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 SOURCE CODE
Full code and its testing web application are available in the following link. The code is fully documented and easy to follow.
The packge is also available on NuGet here.
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.
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.
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.
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,
Model |
Endpoint |
Server-Hosted |
vpcpay |
Merchant-Hosted |
vpcdps |
PAYMENT TRANSACTIONS
Now let us get to work. To issue a payment request to the server you should include the following parameters,
Group |
Parameter |
Description |
Common |
vpc_Version |
API version. Currently covered version is ‘1’. |
vpc_Command |
The command name, which will be ‘pay’ for payment requests. |
Authentication |
vpc_Merchant |
Your merchant ID. |
vpc_AccessCode |
Your access code. |
Order Details |
vpc_MerchTxnRef |
Your unique transaction reference. |
vpc_OrderInfo |
Your order ID. |
|
vpc_Amount |
Amount 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. |
|
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.)
Parameter |
Description |
vpc_ReturnURL |
The 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:
|
vpc_Locale |
The ISO two-letter language code for the Mastercard server pages. Examples are: en (English), ar (Arabic), and es (Spanish.) |
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.)
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,
Parameter |
Description |
vpc_Card |
Card number |
vpc_CardExp |
Card expiry date expressed in yyMM format. For example, Jan 2021 is expressed as 2101. |
vpc_CardSecurityCode |
The card security code. |
Now let us put the pieces together.
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,
Parameter |
Description |
vpc_MerchTxnRef |
Our unique transaction reference. |
vpc_OrderInfo |
Our order number. |
vpc_TxnResponseCode |
Transaction response code. A value of ‘0’ indicates a successful transaction. |
vpc_Message |
Indicates any error messages the transaction may have encountered. |
vpc_ReceiptNo |
Transaction unique identifier. Also called the Reference Retrieval Number (RRN). |
vpc_AuthorizeId |
An identifying code issued by the bank to approve or deny the transaction. |
vpc_BatchNo |
A date supplied by the bank to indicate when this transaction will be settled. |
vpc_CardType |
Card type. For example, ‘MC’ for Mastercard cards. |
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,
- VPC requires the hashed parameters to be ordered by ASCII ordinal values before hashing, e.g., the capital ‘E’ is before the small ‘a’.
- VPC requires the usage of either HMAC SHA-256 or MD5 hashing algorithm.
- 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,
Parameter |
Description |
vpc_SecureHash |
The generated hash. |
vpc_SecureHashType |
The hashing mechanism, which is ‘SHA256’ or ‘MD5’. |
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,
On the other hand, here is the response integrity checking mechanism for SHA-256.
MD5 HASHING
For MD5 request hashes use the following procedure,
For response checking, use the following procedure,
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,
Group |
Parameter |
Description |
Common |
vpc_Version |
API version. Currently covered version is ‘1’. |
vpc_Command |
The command name, which will be ‘queryDR’. |
Authentication |
vpc_Merchant |
Your merchant ID. |
vpc_AccessCode |
Your access code. |
vpc_User |
AMA username. |
vpc_Password |
AMA password. |
Request Details |
vpc_MerchTxnRef |
Your unique transaction reference. |
For the response, we are going to have the following parameters,
Parameter |
Description |
vpc_DRExists |
Returns ‘Y’ if the requested transaction reference has been found. |
vpc_FoundMultipleDRs |
Returns ‘Y’ if there’s multiple transactions with the requested merchant transaction reference. |
vpc_Amount |
If one transaction found, we are going to have its amount here. |
vpc_BatchNo |
If one transaction found, we are going to have its batch number here. |
vpc_TransactionNo |
If one transaction found, we are going to have its unique transaction number here. |
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.
Item |
Value |
Merchant ID |
TESTEGPTEST |
Access Code |
77426638 |
Secure Hash |
7E5C2F4D270600C61F5386167ECB8DA6 |
TEST CARDS
Here are a set of test cards that you can use,
Item |
Card #1 |
Card #2 |
Type |
Mastercard |
Visa |
Number |
5123456789012346 |
4987654321098769 |
Expiry Date |
05/21 |
05/21 |
Security Code |
100 |
100 |
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
- {
-
-
-
- public string Name { get; set; }
-
-
-
-
- public bool IsRequired { get; set; }
- }
Next, we started defining our class hierarchy and parameter properties,
- public abstract class VpcCommand
- {
-
-
-
- [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;
-
-
- queryMembers = targetObject.GetType().GetProperties();
-
- queryMembers = queryMembers.Concat(targetObject.GetType().GetFields());
-
-
- 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();
- }