How to connect to Vietnam Payment API? Development of practical tutorials

Vietnam Payment API Access Development Practical Guide

I. Overview of mainstream payment platforms in Vietnam

The main platforms to consider for payment API integration in Vietnam are as follows:

  1. Momo Wallet - The most popular e-wallets in Vietnam
  2. ZaloPay - Payment solutions under the Zalo social software umbrella
  3. VNPAY - Gateway services supported by the Banking Union
  4. Payoo - Payment methods with many offline outlets
  5. OnePay - Visa/Mastercard localisation programme

II. Access preparations

1. Register for a merchant account

2. API documentation access

The latest API documentation is available in the Developer Centre of each platform:

  • Momo Developer Portal: https://developers.momo.vn/
  • VNPAY technical documentation: https://vnpay.vn/category/technical/

III. Momo API access example (PHP)

<?php
function execPostRequest($url, $data) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen(json_encode($data)))
);
$result = curl_exec($ch);
return json_decode($result); // decode response JSON to object PHP
}

// Momo配置参数 (从商户后台获取)
$partnerCode = "MOMOXXXX";
$accessKey = "XXXXXXXXXX";
$secretKey = "XXXXXXXXXXXXXXXXXXXXXXXX";

//订单信息设置
$orderId = time() .""; // unique order ID
$orderInfo = "Thanh toán qua MoMo"; // payment content description
$amount = $_POST['total_amount']; // total amount need pay
$notifyUrl = "https://yourdomain.com/momo_notify.php"; // callback URL after payment success/fail
$returnUrl = "https://yourdomain.com/thankyou.php"; // redirect URL after payment complete

//生成签名(SHA256加密)
$requestId= time()."";
$rawHash= "accessKey=".$accessKey."&amount=".$amount."&extraData=&ipnUrl=".$notifyUrl."&orderId=".$orderId."&orderInfo=".$orderInfo."&partnerCode=".$partnerCode."&redirectUrl=".$returnUrl."&requestId=".$requestId;

if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
$base_url_payment='https';
} else {
$base_url_payment='http';
}

//拼接完整请求数据
array (
'partnerCode' => $partnerCode,
'accessKey' => $accessKey,
'requestId' => $requestId,
'amount' => strval ($amount),
'orderId' => strval ($order_id),
...
);

try {
/* call api to momo server */
if(!empty($_POST)){
header('Location : '.execPostRequest("...",...));
exit();
}
} catch(Exception ex){ ... }
?>

IV. VNPAY Integration Key Steps (Java example)

public class VNPayConfig {

public static String vnp_PayUrl = "https://sandbox.vnpayment.vn/paymentv2/vpcpay.html";
public static String vnp_Returnurl = "http://localhost:8080/vnp_return.jsp";
public static String vnp_TmnCode = "YourTmnCodeHere";
...

// Generate secure hash with HMAC-SHA512 algorithm
private static String hmacSHA512(final String key,final String data){
try{
Mac sha512_HMAC=Mac.getInstance("HmacSHA512");
SecretKeySpec secret_key=new Secret KeySpec(key.getBytes(), "HmacSHA512");
sha512_HMAC.init(secret_key);
byte[] binary_data=sha512_HMAC.doFinal(data.getBytes());

StringBuilder sb=new StringBuilder(binary_data.length*2);
for(byte b : binary_data){
sb.append(String.format("%02x",b &0xff));
}
return sb.toString();
}catch(Exception e){throw new RuntimeException(e);}
}

public Map createPayment(Long amount,String bankcode) throws UnsupportedEncodingException{
Calendar cld=Calendar.getInstance();
SimpleDateFormat formatter=new SimpleDateFormat("yyyyMMddHHmmss");

Map params=new HashMap();
params.put("vnp_Version", "2.1.0");
params.put("vnp_Command", "pay");
...

List fieldNames=new ArrayList(params.keySet());
Collections.sort(fieldNames).

StringBuilder hashDataBuilder=new StringBuilder();
for(String fieldName : fieldNames){
if(params.get(fieldName)! =null&&!params.get(fieldName).isEmpty()){
hashDataBuilder.append((fieldName+""));
...
}
}

SecureRandom randomSecureRandom=SecureRandom.getInstanceStrong();
int randomNumber=(int)(randomSecure Random.nextDouble()*100000000)+10000000;

params.put(... ,String.valueOf(randomNumber));

return signedParams;
}

V. Common Problems and Debugging Tips

1.Signature verification failed

  • Is the SHA256/HMAC-SHA512 algorithm implementation correct?
  • Is the order of the key and parameters strictly according to the documentation?

2.callback processing

    if verify_signature(request.json): # Custom Signature Verification Function			
if result_code==0.
logger.info(f "Payment successful:{trans_id}")
else.
logger.warning("Invalid signature detected!")
return Response(status.HTTP_200_OK)

3.Test Environment Recommendations
All platforms provide sandbox environments, as recommended:

Vietnam Payment API Access Development Practical Guide (cont'd)

V. Test environment recommendations and debugging techniques (continued)

3.1 Addresses of sandbox environments by platform

3.2 Postman Debugging Example

// Momo payment request example (POST /v2/gateway/api/create)
{
"partnerCode": "MOMOXKXX".
"partnerName": "Test Merchant",
"storeId": "Test Store",
"requestType": "captureWallet".
"ipnUrl": "https://yourdomain.com/momo_callback",
//... Other required fields...
}

VI. ZaloPay Integration Key Code (Node.js example)

const crypto = require('crypto');
const axios = require('axios');

// ZaloPay配置参数
const config = {
app_id: '2554',
key1: 'sdngKKJmqEMzvh5QQcdD2A9XBSKUNaYn',
endpoint: 'https://sb-openapi.zalopay.vn/v2/create'
};

//生成MAC签名(HMAC-SHA256)
function generateMac(data, key) {
return crypto.createHmac('sha256', key)
.update(data)
.digest('hex');
}

async function createZaloPayment(order) {

const timestamp = Date.now();
const uid = `${timestamp}${Math.floor(Math.random() *999)}`;

const params = {
app_id: config.app_id,
app_trans_id: `${moment().format('YYMMDD')}_${order.id}`,
app_user: order.userId,
amount: order.amount,
item: JSON.stringify(order.items),
description:`Thanh toan don hang #${order.id}`,
};

//按文档要求排序并拼接签名字符串
let dataStr= Object.keys(params).sort()
.map(key=>`${key}=${params[key]}`).join("&");

params.mac= generateMac(dataStr,config.key1);

try{
const response= await axios.post(config.endpoint,params);
if(response.data.return_code===1){
return response.data.order_url; //跳转到ZaloPay的支付页面URL
}else{
throw new Error(`ZaloPay error:[${response.data.sub_return_code}] ${response.data.sub_return_message}`);
}
}catch(error){
console.error("调用ZALOPAY API失败:",error);
throw error;
}
}

VII. Notes on the deployment of the production environment

7.1 SSL Certificate Requirements
All callback interfaces must.

  • HTTPS protocol (HTTP not supported)
  • TLS version ≥ 1.2 (as specified by the Central Bank of Vietnam)

7.2 IP Whitelist Settings
Add the server IP to the whitelist in the merchant backend:

# VNPAY Merchant Back Office Example.
120.72.*. * (your server's IP segment)
14.. *. * (Alternate IP segment)

7.3 Transaction limit management

flat-roofed building Single Minimum Single Maximum
MOMO 10,000₫ 50,000,000₫
VNPAY Unlimited Maximum bank account balance
Zalo Pay Corporate Account ₫20 billion/day

VIII. Multilingual error handling programme

public class PaymentErrorHandler {

private static final Map MOMO_ERRORS=new HashMap(){{
put(0, "Giao dịch thành công"); put(-6, "Signature không hợp lệ");}};

private static final Map VNPAY_ERRORS=new HashMap(){{
put("00", "GD thanh cong");put("07", "Trừ tiền thành công GD bị nghi ngờ");}};

public String localizeError(String provider,int code,String locale){
switch(locale.toLowerCase()){
case "vi".
return "vi".equals(locale)?MOMO_ERRORS.get(code):getEnglishMessage(code);
default.
return getEnglishMessage(code);}}
}

IX. Performance optimisation recommendations

Database design optimisation:

 id BIGSERIAL PRIMARY KEY ,
gateway VARCHAR(20)/* momo/vnpay/zalopay */ ,
trans_id VARCHAR(64)/* Gateway Transaction Number*/ ,
status SMALLINT/*0=pending, 1=success**/ ,
created_at TIMESTAMPT NOT NULL DEFAULT NOW(),
INDEX idx_gateway_status(gateway.status));`

Redis caching application:

 cache_key=f'momo_callback_{transid}' if cache.get(cache_key).
logger.warning(f'Duplicate callback detected:{transid}')
else.
cache.set(cache_key,'processed',timeout=300)#5 minutes expires`

For a more detailed implementation of a particular platform or if you encounter specific problems, you can tell me the areas of concern.