WavePay Payment Gateway is an online payment solution by Digital Money Myanmar Limited (Wave Money) that allows online merchants to accept payments securely and instantly.
This document covers the essential information, processes, and other relevant aspects for merchant technical integration with the Pay with Wave Platform. This document contains confidential information and is not intended to be viewed by unapproved external parties. All information and processes in the document are subject to edits and changes from Wave Money.
WavePay Payment Gateway allows Merchants to accept payments through the user’s WavePay account with minimum effort and enjoy the full benefits of enhanced security and full suite of payment options. Accepting payment is easier with WavePay Payment Gateway.
Below is the state diagram that explains the flow of WavePay Payment Gateway.
Merchants who wish to have access to the API will be provided with a merchantID and Hash Secret Key. Certificates used for HTTPS endpoints for callback URL need to be from recognized Certificate Authorities (CAs), i.e., they are not self-signed and must be implemented with standard port 443.
The API is using HMAC SHA256 algorithm for the hash value between messages. The hash formula for each request & response are provided below. Please note that the null values in the message will be hash as "null" String. For example - when the transactionId is null and the hash formula is (msisdn+transactionId+merchantId) - the String to be hashed would be 9791009039nulltestmerchantID
(String message = "9791009039"+"null"+"testmerchnatID"
).
There are some example snippets for the hash function for the reference.
Javascript
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/hmac-sha256.min.js"></script>
<script>
var hash = CryptoJS.HmacSHA256("HelloMessage", "secret-key-1234");
document.write(hash);
</script>
PHP
<?php
$key = 'secret-key-1234';
$message = 'HelloMessage';
// to lowercase hexits
echo(hash_hmac('sha256', $message, $key));
Merchants can use the testing environment to do their functional integrations. Once the integration testing in test environment is finished, the switch to our production system can be made. This means that all endpoints for both environments and the credentials must be obtained from Wave Money.
Environment | URL |
---|---|
Testing | https://testpayments.wavemoney.io:8107/payment |
Production | https://payments.wavemoney.io/payment |
We will be providing the Client ID and Client Secret to access both environments after successful onboarding.
The Header of Payment Request Header will have the following content.
Name | Description | Type | Mandatory |
---|---|---|---|
merchant_id | Merchant ID provided by Wave Money | string | Mandatory |
order_id | Order ID provided by Merchant | string | Mandatory |
merchant_reference_id | Unique ID for every transaction by Merchant | string | Mandatory |
frontend_result_url | Merchant's Website URL | string | Mandatory |
backend_result_url | Merchant's Web Service callback URL | string | Mandatory |
amount | Total Amount | positive integers | Mandatory |
time_to_live_in_seconds | Time to Live for transaction (in seconds ) | string | Mandatory |
payment_description | Payment Description to display on Payment Screen | string | Mandatory |
merchant_name | Merchant Name to display on Payment Screen | string | Mandatory |
items | Items to display on Payment Screen | JSON string array | Mandatory |
hash | Needed for Hash Validation | string | Mandatory |
This part of the document explains the different steps involved in integrating WavePay Payment Gateway.
First, setup Merchant Credentials and Payload that are required for Request Creation
$data = [
// Time to Live for Transaction in seconds
'time_to_live_in_seconds' => "number of seconds e.g. 5000",
// string - Merchant Name for Payment Screen
'merchant_name' => "<<Merchant Name e.g. MerchantX>>",
// string - Merchant id provided by Wave Money
'merchant_id' => "<<MerchantID>>",
// unsigned integer - Order id provided Merchant
'order_id' => "<<order_id e.g. 78346729>>",
// unsigned integer - Total Amount of transaction
'amount' => "<<amount e.g. 1000>>",
// string - mendatory backend url for Payment Service
'backend_result_url' => "<<Call Back URL - e.g. https://wave-merchant.com.mm/backend-callback>>",
// string - mandatory frontend url for Payment Service
'frontend_result_url' => "Caller URL - e.g. <<https://wave-merchant.com.mm>>",
// string - Unique Merchant Reference ID for Transaction, reference_id is unsigned integer
'merchant_reference_id' => "<<reference_id e.g. 8973423>>",
// string - Payment Description for Payment Screen from Merchant
'payment_description' => "<<Merchant Payment Description e.g. Purchase of Item X>>"
];
Next prepare the items to display in WavePay Payment Screen.
$items = json_encode([
['name' => "Product 1", 'amount' => 1000],
['name' => "Product 2", 'amount' => 500]
]);
Secret Key provided by Wave Money
$secret_key = "provided by WaveMoney";
Generate hash that is required for Payload verification.
$hash = hash_hmac('sha256', implode("", [
$data['time_to_live_in_seconds'],
$data['merchant_id'],
$data['order_id'],
$data['amount'],
$data['backend_result_url'],
$data['merchant_reference_id'],
]), $secret_key);
Use these Payload items and required Parameters in an HTML Form to initiate the Payment with WavePay Payment Gateway. Payment Request can be done by Ajax Request or Web Form.
$client = new \GuzzleHttp\Client([
'http_errors' => false,
'verify' => false
]);
$response = $client->request('post', "https://testpayments.wavemoney.io:8107/payment", [
'headers' => [
'Accept' => "application/json",
],
'form_params' => [
"time_to_live_in_seconds" => $data['time_to_live_in_seconds'],
"merchant_id" => $data['merchant_id'],
"order_id" => $data['order_id'],
"merchant_reference_id" => $data['merchant_reference_id'],
"frontend_result_url" => $data['frontend_result_url'],
"backend_result_url" => $data['backend_result_url'],
"amount" => $data['amount'],
"payment_description" => $data['payment_description'],
"merchant_name" => $data['merchant_name'],
"items" => $items,
"hash" => $hash
]
]);
Endpoint: /payment
HTTP Status Code: 200
Description: Response when Payment request is successfully created.
Response Body
{
"message": "success",
"transaction_id": "<response with encrypted_transaction_id>"
}
Endpoint: /payment
HTTP Status Code: 409
Description: Response when Payment request is already created.
Response Body
{
"message": "Record already exists"
}
Endpoint: /payment
HTTP Status Code: 400
Description: Response when Hash in payload is invalid.
Response Body
{
"message": "INVALID_HASH"
}
Endpoint: /payment
HTTP Status Code: 422
Description: Response when fields in Payload are null or invalid.
Response Body
{
"errors": {
"time_to_live_in_seconds": [
"The time to live in seconds field is required."
],
"merchant_id": [
"The merchant id field is required."
],
"order_id": [
"The order id field is required."
],
"merchant_reference_id": [
"The merchant reference id field is required."
],
"frontend_result_url": [
"The frontend result url field is required."
],
"backend_result_url": [
"The backend result url field is required."
],
"amount": [
"The amount field is required."
],
"payment_description": [
"The payment description field is required."
],
"merchant_name": [
"The merchant name field is required."
],
"items": [
"The items field is required."
],
"hash": [
"The hash field is required."
]
}
}
Endpoint: /payment
HTTP Status Code: 404
Description: Response when Merchant Account is invalid.
Response Body
{
"message": "No record found"
}
If ajax request successful, merchant will get the response including transaction_id which may need to use in requesting below WavePay Payment Gateway endpoint to authenticate and perform payment.
Complete redirection inside server-side is prefer.
Types | Values |
---|---|
HTTP Method | GET |
PATH | /authenticate |
Param | transaction_id |
Example Path: https://testpayments.wavemoney.io/authenticate?transaction_id=<transaction_id from response>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Wave Merchant Integration</title>
</head>
<body>
<form action="https://testpayments.wavemoney.io:8107/payment" method="POST">
<input type="hidden" name="time_to_live_in_seconds" value="<?php echo $data['time_to_live_in_seconds']; ?>">
<input type="hidden" name="merchant_id" value="<?php echo $data['merchant_id']; ?>">
<input type="hidden" name="order_id" value="<?php echo $data['order_id']; ?>">
<input type="hidden" name="merchant_reference_id" value="<?php echo $data['merchant_reference_id']; ?>">
<input type="hidden" name="frontend_result_url" value="<?php echo $data['frontend_result_url']; ?>">
<input type="hidden" name="backend_result_url" value="<?php echo $data['backend_result_url']; ?>">
<input type="hidden" name="amount" value="<?php echo $data['amount']; ?>">
<input type="hidden" name="payment_description" value="<?php echo $data['payment_description']; ?>">
<input type="hidden" name="merchant_name" value="<?php echo $data['merchant_name']; ?>">
<input type="hidden" name="items" value='<?php echo $items; ?>'>
<input type="hidden" name="hash" value="<?php echo $hash; ?>">
<button class="btn btn-primary">Pay with Wave</button>
</form>
</body>
</html>
In web form, we don't need to call authenticate request.
WavePay Payment Gateway will call the Call-back URL which is the "backend_result_url" that is provided in the request form.
There are two types of callBack
-
Scheduled CallBack - If the customer stops the action during the payment process (leaves the screen or close the screen) until time-out, the system assume that transaction as 'time-out' and will make the call back with that status "SCHEDULER_TRANSACTION_TIMED_OUT". It can be between 1 second to 900 seconds after the time-out time.
-
Immediate CallBack - All the rest of the callBacks will be called right after the customer actions.
The following statuses are immediate callBacks
-
PAYMENT_CONFIRMED - Only Status to take as the successful transaction. which will be called when customer complete the payment process successfully,
-
TRANSACTION_TIMED_OUT - which will be called when customer tyirng to continue the expired payment process,
-
INSUFFICIENT_BALANCE,
-
ACCOUNT_LOCKED,
-
BILL_COLLECTION_FAILED - which will be called when there is some failure in bill payment process at Wave,
-
PAYMENT_REQUEST_CANCELLED - which will be called when customer cancel the payment before the payment,
** IMPORTANT : Please be informed that merchant side must only take action and assume as a "successful transaction" only when the callBack status is "PAYMENT_CONFIRMED". The other callBack statuses such as SCHEDULER_TRANSACTION_TIMED_OUT , TRANSACTION_TIMED_OUT and others are just for reporting purposes. The status will hashed along with other values as in the formula. Please also validate the hash values before accepting the transaction as the "successful one".
All the transaction status
HTTP Method | POST |
---|---|
Type | application/json |
{
"status": "status codes as per table below",
"merchantId": "<<MerchantID>>",
"orderId": "<<order_id>>",
"merchantReferenceId": "<<reference_id>>",
"frontendResultUrl": "<<https://wave-merchant.com.mm>>",
"backendResultUrl": "<<https://wave-merchant.com.mm/backend-callback>>",
"initiatorMsisdn": "Myanmar Phone number - 10 digit",
"amount": "<<amount>>",
"timeToLiveSeconds": 300,
"paymentDescription": "<<Merchant Payment Description e.g. Purchase of Item X>>",
"currency": "MMK",
"hashValue": "29e9486e727ac0e4f185c3b757cf8892e59eb8d292c23f11d13926bb0bdae798",
"additionalField1": null,
"additionalField2": null,
"additionalField3": null,
"additionalField4": null,
"additionalField5": null,
"transactionId": "360",
"paymentRequestId": "360",
"requestTime": "2019-11-06T15:38:56"
}
Property Name | Property | Description |
---|---|---|
Status | mandatory | Please see the status codes in table below |
merchantId | mandatory | merchant_id that will be defined and agreed on both sides |
orderId | optional | order id or invoice_id unique to the transaction generated by merchant side. there will be only one order_id for one order whilst there can be many merchantReferenceId |
merchantReferenceId | mandatory, unique | It should be a unique string for every payment request. |
frontendResultUrl | mandatory | Web UI redirect url called after payment confirmation |
backendResultUrl | mandatory | Merchant Backend Call back url for transaction response |
initiatorMsisdn | mandatory | User/ Purchaser Msisdn (Phone Number) |
amount | mandatory | total amount |
timeToLiveSeconds | mandatory | time out amount. Limit 10 minutes |
paymentDescription | optional | a brief title or description of the buying item. example - 3 Tuna Sandwiches |
currency | optional | By default it is MMK if not provided |
transactionId | mandatory | MFS billcollect transactionId provided by Wave |
paymentRequestId | mandatory | Payment Request id which is primary tracking id for every payment |
requestTime | mandatory | now() -- serverTime |
hashValue | mandatory | hash_hmac(status+timeToLiveSeconds+merchantId+orderId+amount+backendResultUrl+merchantReferenceId+initiatorMsisdn+transactionId+paymentRequestId+requestTime) -- with hash secret key |
Status | Description | Transaction Status |
---|---|---|
PAYMENT_CONFIRMED | Payment completed successully | Success |
TRANSACTION_TIMED_OUT | Transaction has timed out when committing the payment | Failed/Cancelled Transaction |
INSUFFICIENT_BALANCE | Customer's Balance is insufficient for the payment | Pending Transaction |
ACCOUNT_LOCKED | Customer's Account is locked. | Failed/Cancelled Transaction |
BILL_COLLECTION_FAILED | Wave Services failed to collect the payment from Customer 's Wallet. | Failed/Cancelled Transaction |
PAYMENT_REQUEST_CANCELLED | Customer's Payment Request is cancelled by Wave Service. | Failed/Cancelled Transaction |
SCHEDULER_TRANSACTION_TIMED_OUT | Transaction Timeout after expired "time to live" | Failed/Cancelled Transaction |
-
When I am at checkout/confirmation page and select “Pay with Wave Money”, do I need to enter full Wave Money Account Mobile Number?
-
No, customer only required to enter Mobile Number prefix with ‘9’, mobile number prefix ‘0’ and country code ‘(+95)’ are not required.
-
For e.g. 979100XXX
-
-
Is it Wave Money's responsibility for the communication between merchant client app & Merchant server app ?
- No. Communication between the client (merchant app) and the server (merchant server app) is the developer responsibility.
-
Can I share Client_ID and Client_secret to public ?
- No, you should not share your Client_ID and Client_Secret to public, you must be kept secret and these details should never appear anywhere publicly, otherwise anyone could make transactions on your behalf. Wavemoney will not be held responsible for transactions not originating from your server.