When user buy goods on the Merchant App and choose ZaloPay Payment method, Merchant's Website will direct users to ZaloPay app to processing payment through Mobile Web to App method. When users have done with payment processing, ZaloPay app will let Merchant server know the final result.
Giá trị | Ghi chú |
---|---|
MerchantSite | Merchant's web/app. |
MerchantServer | Merchant's back-end. |
ZaloPayServer | ZaloPay's back-end. |
ZaloPaySite | ZaloPay's web/app. |
When the Merchant server send a request to create order to ZaloPay server, ZaloPay server will response a redirect link in the orderurl
field to Merchant side.
Example:
{
"return_code": 1,
"return_message": "Giao dịch thành công",
"sub_return_code": 1,
"sub_return_message": "Giao dịch thành công",
"zp_trans_token": "AC-YGb2kvGFAmLzTmQD-PdjA",
"order_url": "https://qcgateway.zalopay.vn/openinapp?order=eyJ6cHRyYW5zdG9rZW4iOiJBQy1ZR2Iya3ZHRkFtTHpUbVFELVBkakEiLCJhcHBpZCI6MjAwMDAwfQ==",
"order_token": "AC-YGb2kvGFAmLzTmQD-PdjA",
"qr_code": "00020101021226520010vn.zalopay0203001010627000503173916458628013369138620010A00000072701320006970454011899ZP23237O000015410208QRIBFTTA5204739953037045405690005802VN622108173916458628013369163043895"
}
bankcode
must be valued: zalopayapp
When users do a payment processing, merchant website call the redirect link like below to direct users to ZaloPay Payment site.
Redirect link (orderurl
value) is the link for ZaloPay App do a payment process purpose.
After the user has finished paying, they will be redirected to the Merchant page (according to RedirectURL Merchant provided to ZaloPay) to display the results.
Parameter | Datatype | Description |
---|---|---|
appid |
int | appid of order |
apptransid |
String | apptransid of order |
pmcid |
int | Payment channel |
bankcode |
String | Bank code |
amount |
long | Amount of order (VND) |
discountamount |
long | Discount (VND) |
status |
int | Error code |
checksum |
String | Use to check redirect is valid or not HMAC(hmac_algorithm, key2, appid +"|"+ apptransid +"|"+ pmcid +"|"+ bankcode +"|"+ amount +"|"+ discountamount +"|"+ status) |
/*
ASP.Net core
*/
using Microsoft.AspNetCore.Mvc;
using ZaloPay.Helper; // HmacHelper, RSAHelper, HttpHelper, Utils (download at DOWNLOADS page)
using ZaloPay.Helper.Crypto;
namespace ZaloPayExample.Controllers
{
[Route("[controller]")]
[ApiController]
public class RedirectController: ControllerBase
{
private string key2 = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3";
[HttpGet]
public IActionResult Get()
{
var data = Request.Query;
var checksumData = data["appid"] +"|"+ data["apptransid"] +"|"+ data["pmcid"] +"|"+
data["bankcode"] +"|"+ data["amount"] +"|"+ data["discountamount"] +"|"+ data["status"];
var checksum = HmacHelper.Compute(ZaloPayHMAC.HMACSHA256, key2, checksumData);
if (!checksum.Equals(data["checksum"])) {
return StatusCode(400, "Bad Request");
}
else {
// check if the callback has been received, if not Merchant should use getOrderStatus API to get final result
return StatusCode(200, "OK");
}
}
}
}
import org.json.JSONObject;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.util.Map;
import java.util.logging.Logger;
@Controller
public class RedirectController {
private Logger logger = Logger.getLogger(this.getClass().getName());
private String key2 = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3";
private Mac HmacSHA256;
public RedirectController() throws Exception {
HmacSHA256 = Mac.getInstance("HmacSHA256");
HmacSHA256.init(new SecretKeySpec(key2.getBytes(), "HmacSHA256"));
}
@GetMapping("/redirect-from-zalopay")
public ResponseEntity redirect(@RequestParam Map<String, String> data) {
String checksumData = data.get("appid") +"|"+ data.get("apptransid") +"|"+ data.get("pmcid") +"|"+ data.get("bankcode") +"|"+
data.get("amount") +"|"+ data.get("discountamount") +"|"+ data.get("status");
byte[] checksumBytes = HmacSHA256.doFinal(checksumData.getBytes());
String checksum = DatatypeConverter.printHexBinary(checksumBytes).toLowerCase();
JSONObject result = new JSONObject();
if (!checksum.equals(data.get("checksum"))) {
return ResponseEntity.badRequest().body("Bad Request");
} else {
// check if the callback has been received, if not Merchant should use getOrderStatus API to get final result
return ResponseEntity.ok("OK");
}
}
}
// go version go1.11.1 linux/amd64
package main
import (
"fmt"
"log"
"net/http"
"github.com/zpmep/hmacutil"
)
// App config
var (
key2 = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3"
)
func main() {
mux := http.DefaultServeMux
mux.HandleFunc("/redirect-from-zalopay", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
data := r.Form
checksumData := data.Get("appid") + "|" + data.Get("apptransid") + "|" + data.Get("pmcid") + "|" + data.Get("bankcode") + "|" + data.Get("amount") + "|" + data.Get("discountamount") + "|" + data.Get("status")
checksum := hmacutil.HexStringEncode(hmacutil.SHA256, key2, checksumData)
if checksum != data.Get("checksum") {
w.WriteHeader(400)
fmt.Fprint(w, "Bad Request")
} else {
// check if the callback has been received, if not Merchant should use getOrderStatus API to get final result
fmt.Fprint(w, "Ok")
}
})
log.Println("Server is listening at port :8001")
http.ListenAndServe(":8001", mux)
}
// Node v10.15.3
const CryptoJS = require('crypto-js');
const express = require('express');
const app = express();
const config = {
key2: "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3"
};
app.get('/redirect-from-zalopay', (req, res) => {
let data = req.query;
let checksumData = data.appid + '|' + data.apptransid + '|' + data.pmcid + '|' + data.bankcode + '|' + data.amount + '|' + data.discountamount + '|' + data.status;
let checksum = CryptoJS.HmacSHA256(checksumData, config.key2).toString();
if (checksum != data.checksum) {
res.sendStatus(400);
} else {
// check if the callback has been received, if not Merchant should use getOrderStatus API to get final result
res.sendStatus(200);
}
});
app.listen(8001, function () {
console.log('Server is listening at port :8001');
});
<?php
// PHP Version 7.3.3
$key2 = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3";
$data = $GET;
$checksumData = $data["appid"] ."|". $data["apptransid"] ."|". $data["pmcid"] ."|". $data["bankcode"] ."|". $data["amount"] ."|". $data["discountamount"] ."|". $data["status"];
$checksum = hash_hmac("sha256", $checksumData, $key2);
if (strcmp($mac, $data["checksum"]) != 0) {
http_response_code(400);
echo "Bad Request";
} else {
// check if the callback has been received, if not Merchant should use getOrderStatus API to get final result
http_response_code(200);
echo "Ok";
}
# ruby 2.5.1p57
# rails 5.2.3
# config/routes.rb
# Rails.application.routes.draw do
# match '/redirect-from-zalopay' => 'redirect#handle', via: :get
# end
# app/controllers/redirect_controller.rb
require 'json'
require 'openssl'
class RedirectController < ApplicationController
def initialize
super
@config = {
key2: 'Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3'
}
end
# POST /callback
def handle
data = request.query_parameters
checksumData = data["appid"] +"|"+ data["apptransid"] +"|"+ data["pmcid"] +"|"+ data["bankcode"] +"|"+ data["amount"] +"|"+ data["discountamount"] +"|"+ data["status"]
checksum = OpenSSL::HMAC.hexdigest('sha256', @config[:key2], checksumData)
if checksum != data['checksum']
render text: 'Bad Request', status: :bad_request
else
# check if the callback has been received, if not Merchant should use getOrderStatus API to get final result
render text: 'OK', status: :ok
end
end
end
# coding=utf-8
# Python 3.6
from flask import Flask, request, json
import hmac, hashlib
app = Flask(__name__)
config = {
'key2': 'Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3'
}
@app.route('/redirect-from-zalopay', methods=['GET'])
def redirect():
data = request.args
checksumData = "{}|{}|{}|{}|{}|{}|{}".format(data.get('appid'), data.get('apptransid'), data.get('pmcid'), data.get('bankcode'), data.get('amount'), data.get('discountamount'), data.get('status'))
checksum = hmac.new(config['key2'].encode(), checksumData, hashlib.sha256).hexdigest()
if checksum != data.get('checksum'):
return "Bad Request", 400
else:
# check if the callback has been received, if not Merchant should use getOrderStatus API to get final result
return "Ok", 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8001)