Capturing Payments Using Mastercard MIGS Service and ASP.NET Core

Overview

Good day everyone! Today we will explore Mastercard MIGS payment service and learn how to capture payments from your clients through the provided API using C# and ASP.NET Core.

It is worth mentioning that the payment component we are going to create can be used from any project (not just ASP.NET core projects.) Moreover, it is very easy to port our test code to MVC, WebForms or even console/desktop apps.

Full Code Listing

The package has been built using C# and has been posted to:

Introduction

Mastercard provides a service called MIGS (Mastercard Internet Gateway Service) that merchants can use to capture and collect funds from their clients as well as to issue refunds and monitor reports. This service exposes its functionality through an API called VPC (Virtual Payment Client) that can be easily accessed using some sort of credentials that merchants receive from their acquiring bank.

In other words, the VPC (Virtual Payment Client) acts as an interface that provides a secure method of communication between your online store and the Mastercard payment server, which facilitates the processing of payments.

Mastercard VPC

For this to work, as a merchant you must register for the e-payment service through your bank which will give you two sets of credentials that can be used to communicate with VPC, the testing and the production credentials.

Secure Credentials

Each secure credential (testing/production) that you will receive form the bank consists of five pieces:

  • Merchant ID: which you can think of as the username.
  • Access Code: which you can think of as the password.
  • Secure Hash Secret: which can be used for integrity validation (which we will cover later.) This code must be kept in a safe place.
  • The AMA user: will be covered later in the transaction queries.
  • The AMA password: will be covered later in the transaction queries.

Integration Models

To collect and capture your funds from clients you need to use one of two models:

  • Server-Hosted Model
  • Merchant-Hosted Model

Server-Hosted Model / 3-Party

Server-Hosted Model, also called bank-hosted and 3-party, is where you do not handle client card information directly. Instead, you send the user to a secure page hosted by Mastercard where user can enter their card information and you wait for the page to return to you with the response.

Mastercard MIGS Server-Hosted Model

Pros and cons of this model:

  • Pros:
    • You are totally free of securing user card information.
  • Cons:
    • User must be present.
    • Cannot be used in offline or mail/telephone orders.
    • You must check for pending transactions if the page has not returned to you (e.g., user has closed the browser or an internet issue.)

Merchant-Hosted Model / 2-party

Merchant-Hosted Model, also called 2-party, is where you handle client card information directly. You ask the user for card information or retrieve it from a database and send all the required information to VPC using a web request, where the VPC replies instantly with relevant information.

Mastercard MIGS Merchant-Hosted Model

Pros and cons of the 2-party model:

  • Pros:
    • No waits. You get the response instantly.
    • User may not be present.
    • Can be used with mail/telephone orders.
  • Cons:
    • You must handle the security of credit card page and data being stored in the database (for example.)

Endpoints

To start utilizing a model, you need to use its relevant endpoint which is available in the following table:

ModelEndpoint
Server-Hostedvpcpay
Merchant-Hostedvpcdps
MIGS Model Endpoints

Payment Transactions

Now let us get to work. To issue a payment request to the server you should include the following parameters:

GroupParameterDescription
Commonvpc_VersionAPI version. Currently covered version is ‘1’.
vpc_CommandThe command name, which will be ‘pay’ for payment requests.
Authenticationvpc_MerchantYour merchant ID.
vpc_AccessCodeYour access code.
Order Detailsvpc_MerchTxnRefYour unique transaction reference.
vpc_OrderInfoYour order ID.
vpc_AmountAmount expressed in the smallest currency unit expressed as integer. For example, if the transaction amount is $49.95 then the amount value should be 4995.
Payment Transaction Parameters

Transaction Reference vs. Order Info

Now you might ask what is the difference between transaction reference and order info? And what makes transaction reference strictly recommended to be unique?

The answer is that ever asked yourself what will happen if the user in a server-hosted transaction closes the Mastercard page after a successful payment without returning to your site? The answer is without the unique transaction reference you will lose access to user’s payment transaction.

Using unique transaction references allows you to identify each payment request and to link them to user/order. And if you did not get a response from the payment request, you can use the unique reference to query about payment status later using the QueryDR command (will be covered later.)

You can also pass the order ID in the vpc_OrderInfo field keeping in mind that each order may have multiple payment requests, however, each payment request can only be linked to a single order.

Server-Hosted Payment Transactions

If you are going to use the server-hosted integration model, you will have to point your call to the correct API endpoint, which is vpcpay for server-hosted model, and provide two more parameters besides the parameters mentioned above. One of those two, as you might guess, is the return URL (i.e., callback URL.)

ParameterDescription
vpc_ReturnURLThe URL that Mastercard gateway will navigate to when the transaction completes. In this URL you can define a handler to process the payment transaction results. You may define a Thank You page, a receipt page, or something like this. Keep in mind that not all transactions are successful. This URL must be: AbsoluteSSL-compliant.
vpc_LocaleThe ISO two-letter language code for the Mastercard server pages. Examples are: en (English), ar (Arabic), and es (Spanish.)
Server-Hosted Payment Transaction Extra Parameters

Now the question is: How do we send those parameters to the VPC API? Will we include them in body? Will we encode them as JSON? The answer is no! We simple include them as query string. As we are in server-hosted model, we will prepare our URL that has those parameters in the query string and then we will just navigate to this URL and wait for it to navigate back to our return URL (vpc_ReturnURL.)

Sever-Hosted Model Application

Worth mentioning that you can define your custom parameters as long as they do not start with ‘vpc_’.

Merchant-Hosted Payment Transactions

On the other hand, the merchant-hosted transactions use the endpoint vpcdps. It uses extra 3 parameters that you can easily guess:

ParameterDescription
vpc_CardCustom card number
vpc_CardExpCard expiry date expressed in yyMM format. For example, Jan 2021 is expressed as 2101.
vpc_CardSecurityCodeThe card security code.
Merchant-Hosted Payment Transaction Extra Parameters

Now let us put the pieces together.

Merchant-Hosted Model Application

Response Parameters

If we are using the server-hosted model, we may receive the response as query parameters of our callback URL. On the other hand, if we are using the merchant-hosted model, we may receive the response as query string in the body of the web response. Those parameters include:

ParameterDescription
vpc_MerchTxnRefOur unique transaction reference.
vpc_OrderInfoOur order number.
vpc_TxnResponseCodeTransaction response code. A value of ‘0’ indicates a successful transaction.
vpc_MessageIndicates any error messages the transaction may have encountered.
vpc_ReceiptNoTransaction unique identifier. Also called the Reference Retrieval Number (RRN).
vpc_AuthorizeIdAn identifying code issued by the bank to approve or deny the transaction.
vpc_BatchNoA date supplied by the bank to indicate when this transaction will be settled.
vpc_CardTypeCard type. For example, ‘MC’ for Mastercard cards.
Payment Transaction Response

Request and Response Samples

Here is an example of a request URL:

https://migs.mastercard.com.au/vpcpay?vpc_AccessCode=77426638&vpc_Amount=10025&vpc_Command=pay&vpc_Locale=en&vpc_MerchTxnRef=TX-1&vpc_Merchant=TESTEGPTEST&vpc_OrderInfo=100&vpc_ReturnURL=https://localhost:44376/Home/PaymentCallback&vpc_Version=1

Here in this link, you can see that the endpoint is ‘vpcpay’ which indicates a server-hosted model (i.e., we will navigate the user to this link. We can also see that the amount is 10025 which means $100.25 (or whatever currency we are operating.) We can also see that we have specified our return URL as ‘https://localhost:44376/Home/PaymentCallback. Which the user will be automatically navigated to when he completes the transaction.

When the user completes the transaction, he will be navigated to:

https://localhost:44376/Home/PaymentCallback&vpc_Amount=121300&vpc_AuthorizeId=586587&vpc_BatchNo=20210129&vpc_CSCResultCode=M&vpc_Card=MC&vpc_Command=pay &vpc_MerchTxnRef=TX-1&vpc_Merchant=TESTEGPTEST&vpc_Message=Approved&vpc_OrderInfo=O-1&vpc_ReceiptNo=103006586587&vpc_TxnResponseCode=0&vpc_Version=1

No need for more explanation. The parameters are easy to understand and follow.

Integrity Checking

Ever wondered, what if a user simulates the payment process and just called the above URL of your site with any fake values?! Will you still count it as a valid transaction? How do you differentiate between the true and fake responses? The answer is the integrity checking.

The last third in the secure credentials that we did not cover yet is the secure hash secret. This code is used to prevent the cardholder from modifying a transaction request and response when passing it though cardholder’s browser. This is what we call integrity checking.

Integrity checking works by using the hash secret code as a key to hash all our parameters and to send the generated hash along with our call to the server which in turn validates the hash and proceeds if valid. On the contrary, the server hashes all response parameters using the same hash secret code and returns the response along with the generated hash to our application, which we can validate by regenerating the hash and comparing it to the returned value.

Three things should be noted in this process:

  1. VPC requires the hashed parameters to be ordered by ASCII ordinal values before hashing, e.g., the capital ‘E’ is before the small ‘a’.
  2. VPC requires the usage of either HMAC SHA-256 or MD5 hashing algorithm.
  3. The hash secret must be kept safe, it should not be exposed under any circumstances.

So, for this to work, we have two extra parameters that will be included in the request:

ParameterDescription
vpc_SecureHashThe generated hash.
vpc_SecureHashTypeThe hashing mechanism, which is ‘SHA256’ or ‘MD5’.
Secure Hash Parameters

Beware not to include your original secret hash.

Let us see this in action.

SHA-256 Hashing

For SHA-256 hashing you can use the following procedure for requests:

SHA-256 Request Request Integrity Checking

On the other hand, here is the response integrity checking mechanism for SHA-256.

SHA-256 Response Integrity Checking

MD5 Hashing

For MD5 request hashes use the following procedure:

MD5 Request Integrity Checking

For response checking, use the following procedure:

MD5 Response Integrity Checking

Transaction Queries

Previously we have raised an issue about what will happen in server-hosted transactions if the user has closed the browser (for example) without returning to your site? The answer is in your unique merchant transaction number. You can use this number in the query API to query for transactions that has this number. That is why this number is strictly recommended to be unique.

The query command is based on the merchant-hosted model. And that means there will be no navigations, you will create the request and read its response instantly. It means also that it will use the vpcdps endpoint.

One critical thing to mention is that the transaction query command is part of the Advanced Merchant Administration (AMA) commands. Those advanced commands use an extra security credentials called the AMA username and password. Those credentials must be included as query parameters in the request.

For the transaction query request, we are going to use the following parameters:

GroupParameterDescription
Commonvpc_VersionAPI version. Currently covered version is ‘1’.
vpc_CommandThe command name, which will be ‘queryDR’.
Authenticationvpc_MerchantYour merchant ID.
vpc_AccessCodeYour access code.
vpc_UserAMA username.
vpc_PasswordAMA password.
Request Detailsvpc_MerchTxnRefYour unique transaction reference.
Transaction Query Request Parameters

For the response, we are going to have the following parameters:

ParameterDescription
vpc_DRExistsReturns ‘Y’ if the requested transaction reference has been found.
vpc_FoundMultipleDRsReturns ‘Y’ if there’s multiple transactions with the requested merchant transaction reference.
vpc_AmountIf one transaction found, we are going to have its amount here.
vpc_BatchNoIf one transaction found, we are going to have its batch number here.
vpc_TransactionNoIf one transaction found, we are going to have its unique transaction number here.
Transaction Query Response Parameters

Test Environment

Credentials

Here is a set of test credentials that can be used in the payment process. Normally you will receive your test credentials from your acquiring bank. You can also search the internet and get few test credentials like those.

ItemValue
Merchant IDTESTEGPTEST
Access Code77426638
Secure Hash7E5C2F4D270600C61F5386167ECB8DA6
Test Credentials

Test Cards

Here are a set of test cards that you can use:

ItemCard #1Card #2
TypeMastercardVisa
Number51234567890123464987654321098769
Expiry Date05/2105/21
Security Code100100
Test Cards

Code Glimpse

In this section we will go through the code very quickly. Let us start with query parameters. To get our code generating parameters dynamically and to differentiate between parameter fields and other fields, we created an attribute that will be used to decorate only parameter fields.

  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
  public class QueryParamAttribute : Attribute
  {
    /// <summary>
    /// Target parameter name.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Is parameter required. Indicates whether to include an empty string if parameter value is null/empty.  Default is True.
    /// </summary>
    public bool IsRequired { get; set; }
  }

Next, we started defining our class hierarchy and parameter properties:

  public abstract class VpcCommand
  {
    /// <summary>
    /// Command name.
    /// </summary>
    [QueryParam(Name = "vpc_Command", IsRequired = true)]
    public abstract string Command { get; }

We used reflection to get the parameter query properties and their corresponding values:

private static IEnumerable<MemberInfo> GetObjectQueryMembers(object targetObject)
{
  IEnumerable<MemberInfo> queryMembers;

  // Loads for instance properties
  queryMembers = targetObject.GetType().GetProperties();
  // Loads for instance fields
  queryMembers = queryMembers.Concat(targetObject.GetType().GetFields());

  // Checks QueryParamAttribute existence
  queryMembers = queryMembers.Where(a => a.GetCustomAttributes<QueryParamAttribute>().Any());
  return queryMembers;
}

We used a simple code to generate and concatenate query strings:

    public static string CreateQueryString(IEnumerable<QueryParameter> parameters)
    {
      string queryStr = string.Empty;
      foreach (var param in parameters)
      {
        queryStr += string.Format("{0}={1}&", param.Name, param.Value);
      }

      queryStr = queryStr.TrimEnd('&');
      return queryStr;
    }

And here is our secure hash generating code:

public virtual string SHA256Hash(string hashSecret, IEnumerable<QueryParameter> queryParams)
{
  queryParams = queryParams.OrderBy(a => a.Name, StringComparer.Ordinal);
  string queryStr = QueryManager.CreateQueryString(queryParams);
  return Sha256(queryStr, hashSecret);
}


public virtual string MD5Hash(string hashSecret, IEnumerable<QueryParameter> queryParams)
{
  queryParams = queryParams.OrderBy(a => a.Name, StringComparer.Ordinal);

  string str = hashSecret + string.Join("", queryParams.Select(a => a.Value));

  return MD5(str);
}

And the code to execute a merchant-hosted command is fairly straightforward:

public virtual string ExecuteCommandRaw(VpcCommand cmd)
{
  string reqUrl = ComputeCommand(cmd);

  WebRequest req = HttpWebRequest.Create(reqUrl);

  req.Method = "POST";

  using (var stm = req.GetResponse().GetResponseStream())
  using (var stmReader = new StreamReader(stm))
  {
    return stmReader.ReadToEnd();
  }

Full Source Code

Finally, full code and its testing web application are available in the following link. The code is fully documented and easy to follow:

https://github.com/elsheimy/Elsheimy.Components.ePayment.Migs

The package is also available on Nuget at:

https://www.nuget.org/packages/Elsheimy.Components.ePayment.Migs/