Integrate MIGS 2-party payments in PHP.
We needed a solution to handle 2-party payments to a Bendigo bank merchant account for one of our websites. Although I've only implemented 2-party payments, the following notes and code should provide a good foundation for adding additional functionality.
In 2-party payments the application connects directly to the Virtual Payment Client (VPC) using a form POST operation that directly returns a response. This is in contrast to 3-party payments where the payment server manages the payment pages (and collection of the cardholder's card details) from their website.
Like many other banks in Australia, Bendigo bank are using MIGS to facilitate card not present transactions, via their virtual payment client API. MIGS allows payments from other card providers (not just MasterCard), including: Amex, Bankcard, Diners Club and Visa.
At a high level:
- we capture the user's card details via a form on our own website.
- we package up the payment data according to MIGS documentation.
- we send the request to the MIGS payment gateway using cURL.
- we get a response from MIGS containing a response code and message.
- we check the response code and handle successful/invalid requests accordingly.
- "Look ma, it's a MIG!"
An overview of the Migs class:
There are a few constants/variables declared at the top of the Migs class, I don't expect these would need modification. Any variables I thought might need changing are placed in the MigsConfig class.
The MigsConfig class is included in the same file, down the very bottom. This is where your configuration settings go; more on this further down.
function __construct() { ... } - This is the constructor. It is simply here to instantiate the $this->config variable.
function digitalOrder($data) { ... } - This is the 2-party payment method. It takes in the data as an associative array. It expects all of the user's details to be present and following the naming conventions specified in the MIGS manual, however it does not perform any validation of this.
The user's payment details (vpc_CardNum, vpc_CardExp, vpc_vpc_Amount, vpc_MerchTxnRef, etc) are merged in with the merchant account details (vpc_AccessCode, vpc_Merchant - taken from the MigsConfig) as well as the constants (vpc_Version, vpc_Command & vpc_TxSource).
This combined data associative array is then packaged into a query string and processed using cURL. Lastly, the response is unpackaged from a string into an associative array and returned.
function __package($values) { ... } - Takes the associative array and packages it into a query string.
function __process($url, $postfields=false) { ... } - Takes a base url and a query string and makes a cURL request, returning the response as a string.
function __unpackage($queryString) { ... } - Takes a string in the query string format (E.g. 'key=value&foo=bar') and returns it as an associate array.
function getResponseCodeDescription($responseCode) { ... } - Gets the response description for the given response code. This helper function can be called in a static fashion.
Configuration:
Configuration has been extracted to the MigsConfig class (at the bottom of the file); it is the only code that should require modifying. These values are case sensitive.
var $merchant = array(
'test' => array(
'vpc_AccessCode' => '80####77',
'vpc_Merchant' => 'TESTBBL96###74',
),
'production' => array(
'vpc_AccessCode' => '80####77',
'vpc_Merchant' => 'BBL96###74',
)
);
As a side note for Joomla users:
Since I was using the Migs class from a couple of custom developed Joomla components, I wanted the ability to configure the MIGS values through the Joomla admin. This is achieved through a configuration helper method which populates the MigsConfig variables from my component's parameters. It works in an unobtrusive manner so you can ignore it or modify it to suit your framework/CMS. It works because the Migs constructor creates an instance of the MigsConfig class using the values declared in the code, allowing you to directly configure it. Alternatively, after instantiating the Migs class, you can call $migs->getJoomlaParams($component) to read the Joomla component parameters and override your initial MigsConfig values.

Using the Migs class in your code:
You'll want to validate and sanitize the form inputs in your own code. This is typically a responsibility of your model. Some frameworks have utility methods to handle this. For example, I was using Joomla's JRequest class: JRequest::getInt('vpc_cardNum', null, 'post').
If the framework/CMS you are working with has this, you should use it; otherwise do some research, maybe it's time to upgrade.
Now that you've got all of the mandatory fields, valid and ready to go, you can create an instance of the Migs class and make the payment. The following is a very rough example of what your code might look like:
require_once('migs.core.php');
// [ Some code... ]
// Get the user's payment details in a sanatized format.
$data = array(); // [ Your code here... ]
// Validate the user's payment details.
$isValid = true; // [ Your code here... ]
if ($isValid) {
$migs = new Migs();
// [Optional] load the config from a Joomla component parameters.
$migs->config->getJoomlaParams('com_example');
$response = $migs->digitalOrder($data);
if ($response) {
$responseCode = $response['vpc_TxnResponseCode'];
if ($responseCode == '0') {
$msg = $response['vpc_Message'];
$msg .= 'Receipt number: ' . $response['vpc_ReceiptNo'];
}
else {
$msg = Migs::getResponseCodeDescription($responseCode);
$msg .= $response['vpc_Message'];
}
}
else {
$msg = 'Unexpected error connecting to the payment gateway.';
}
// Let the user know what happened with the payment.
echo $msg;
}
else {
// Invalid data, let them know.
echo 'Please ensure all data is entered correctly';
}
Testing the MIGS solution:
Switching between test and production is as easy as changing the mode.
var $mode = 'test'; // 'production'
var $ALLOWED_MODES = array('test', 'production');
As could be expected: when in test mode, test merchant values are used; similarly, when in production mode, production merchant values are used.
When in test mode, the cURL settings specify to skip SSL verification. This was an issue on a different project, so I have gotten into the habit of turning off those settings during development. They should be enabled in production mode (which they are by default).
// Some localhost/test environments might need relaxed security.
if ($mode == 'test') {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
As for test data, here are some test card numbers and expiry dates you can use on the MIGS payment server:
| Card Type: | Number: | Expiry: |
|---|---|---|
| MasterCard | 5123456789012346 | 05/2013 |
| Visa | 4005550000000001 | 05/2013 |
| Amex | 345678901234564 | 05/2013 |
| Bankcard (Australian Domestic) | 5610901234567899 | 05/2013 |
| Diners Club | 30123456789019 | 05/2013 |
Furthermore, the payment amount is translated to cents and needs to be divisible by 100 to be valid. E.g. an amount of $23.00 would work, but $23.50 would return an error response (when using the test payment server).
Get yourself an SSL certificate, srsly!
SSL protects data submitted over the Internet from being intercepted and viewed by unintended recipients. You really should be rocking SSL if you're allowing users to authenticate to your website or you're collecting sensitive information such as a cardholder's credit card number or address.
Even if you are using 3-party payments, you'd still benefit from SSL; it would prevent possible browser alerts about being redirected to an unsecured site (on the return trip from the payment server to your website).
Download the Migs class (and integration guide):
MIGS-Virtual-Payment-Client-Guide-Rev-2.1.0.pdf [1.5mb]
Please note: This code is provided as is, use it at your own discretion.
That said, if you do notice anything that you think is potentially unsecure, or that you think could be improved upon - feel free to let me know.
I'd also be interested in hearing if you have success using this class in any of your own projects.