NAV

API Chung

Danh sách API Thông tin mã lỗi

Tạo đơn hàng

App server gửi thông tin đơn hàng tới ZaloPay server để tạo đơn hàng.

Luồng xử lý

Sequence Flow

Đặc tả API

Environment Method Endpoint
Sandbox POST https://sandbox.zalopay.com.vn/v001/tpe/createorder
Real POST https://zalopay.com.vn/v001/tpe/createorder

Thông tin đơn hàng

Đơn hàng gồm có các thông tin sau:

Tham số
Kiểu dữ liệu Kích thước Bắt buộc Ý nghĩa Ví dụ
appid int Định danh cho ứng dụng đã được cấp khi đăng ký ứng dụng với ZaloPay. 1
appuser String 50 Thông tin định danh của người dùng ứng dụng thanh toán đơn hàng: id/username/tên/số điện thoại/email của user. Nếu không định danh được có thể dùng thông tin mặc định, chằng hạn như tên ứng dụng user123
apptime long Thời gian tạo đơn hàng (unix timestamp in milisecond). Thời gian tính đến milisecond, lấy theo current time và không quá 15 phút so với thời điểm thanh toán 1459823610957
amount long Giá trị của đơn hàng VND 50000
apptransid String 40 Mã giao dịch của đơn hàng. Mã giao dịch phải bắt đầu theo format yymmdd của ngày hiện tại. Mã giao dịch nên theo format yymmdd_Mã đơn hàng thanh toán 180208_007242
embeddata String 1024 Dữ liệu riêng của đơn hàng. Dữ liệu này sẽ được callback lại cho AppServer khi thanh toán thành công (Nếu không có thì để chuỗi rỗng) {"promotioninfo":"","merchantinfo":"du lieu rieng cua ung dung"}
item String 256 Item của đơn hàng, do ứng dụng tự định nghĩa [{"itemid":"knb","itename":"kim nguyen bao","itemprice":198400,"itemquantity":1}]
mac String Thông tin chứng thực của đơn hàng, xem cách tạo thông tin chứng thực cho đơn hàng c8f49d523336f0a182586a70b71c20da96 4d37954711de9273152b500df74c0d
bankcode String 20 ✔ (*) Mã ngân hàng, xem cách lấy danh sách các ngân hàng được hỗ trợ VTB
description String 256 Thông tin mô tả về dịch vụ đang được thanh toán dùng để hiển thị cho user trên app ZaloPay và trên tool quản lý Merchant <Tên Merchant/Dịch vụ> - Thanh toán đơn hàng #<Mã đơn hàng>
Ví dụ:
Lazada - Thanh toán đơn hàng #180208_007242
phone String 50 Số điện thoại của người dùng 0934568239
email String 100 Email của người dùng example@gmail.com
address String 1024 Địa chỉ của người dùng TPHCM
subappid String 50 Định danh dịch vụ / nhóm dịch vụ đối tác đăng ký với ZaloPay (chỉ áp dụng với một số đối tác đặc biệt) sub123

Một số trường đặc biệt của embeddata

Tên
Kiểu dữ liệu Định dạng Mô tả Ví dụ
redirecturl String URL Redirect về url này sau khi thanh toán trên cổng ZaloPay (override redirect url lúc đăng ký app với ZaloPay) {"redirecturl": "https://docs.zalopay.vn/result"}
columninfo JSON String {"column_name": "value"} Thêm thông tin hiển thị ở phần Quản lý giao dịch chi tiết trên Merchant site, nếu cột chưa tồn tại cần vào phần Cài đặt hiển thị dữ liệu để cấu hình {"columninfo": "{\"store_id\": \"123\", \"store_name\": \"ABC\"}"}
promotioninfo JSON String {"campaigncode":"code"} Dùng để triển khai chương trình khuyến mãi {"promotioninfo": "{\"campaigncode\":\"blackfriday\"}"}
zlppaymentid String Mã thông tin thanh toán {"zlppaymentid": "P4201372"}

Tạo thông tin chứng thực

mac = HMAC(hmac_algorihtm, key1, hmacinput)

Trong đó:

Ví dụ:

appid=1&apptransid=160405095135-57032837085b5&appuser=pmqc&apptime=1459823610957&description=Mua kim nguyên bảo cho game VLTK&embeddata={"promotioninfo":"{\"campaigncode\":\"yeah\"}","merchantinfo":"embeddata123"}&item=[{"itemid":"knb","itemname":"kim nguyen bao","itemquantity":10,"itemprice":50000}]&amount=500000&&mac=28ecee91f4b32aa1306812f5d74c4ed1f7cbce7b4f2848cf06f23933ae8027e0 

Tham số api trả về

Tham số
Kiểu dữ liệu Ý nghĩa
returncode int

1: Thành công

<>: Thất bại

returnmessage String Mô tả chi tiết thông tin mã lỗi
orderurl String Dùng để tạo QR code hoặc gọi chuyển tiếp sang trang cổng ZaloPay
zptranstoken String Kết hợp với appid để tạo QR code Deprecated

Ví dụ: { "appid": 553, "zptranstoken": "190613000002244_order" }

Ví dụ:

{
  "zptranstoken": "190613000002244_order",
  "orderurl": "https://sbgateway.zalopay.vn/openinapp?order=eyJ6cHRyYW5zdG9rZW4iOiIxOTA2MTMwMDAwMDIyNDRfb3JkZXIiLCJhcHBpZCI6NTUzfQ",
  "returncode": 1,
  "returnmessage": ""
}

Code mẫu

/**
 * .Net core 2.1.505
 */
using System;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using ZaloPay.Helper; // HmacHelper, RSAHelper, HttpHelper, Utils (tải về ở mục DOWNLOADS)
using ZaloPay.Helper.Crypto;
using Newtonsoft.Json; // https://www.newtonsoft.com/json

namespace ZaloPayExample
{
    class Program
    {
        static string appid = "553";
        static string key1 = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q";
        static string createOrderUrl = "https://sandbox.zalopay.com.vn/v001/tpe/createorder";

        static async Task Main(string[] args) {
            var transid = Guid.NewGuid().ToString(); 
            var embeddata = new { merchantinfo = "embeddata123" };
            var items = new []{
                new { itemid = "knb", itemname = "kim nguyen bao", itemprice = 198400, itemquantity = 1 }
            };
            var param = new Dictionary<string, string>();    

            param.Add("appid", appid);
            param.Add("appuser", "demo");
            param.Add("apptime", Utils.GetTimeStamp().ToString());
            param.Add("amount", "50000");
            param.Add("apptransid", DateTime.Now.ToString("yyMMdd") + "_" + transid); // mã giao dich có định dạng yyMMdd_xxxx
            param.Add("embeddata", JsonConvert.SerializeObject(embeddata));
            param.Add("item", JsonConvert.SerializeObject(items));
            param.Add("description", "ZaloPay demo");
            param.Add("bankcode", "zalopayapp");

            var data = appid + "|" + param["apptransid"] + "|" + param["appuser"] + "|" + param["amount"] + "|" 
                + param["apptime"] + "|" + param["embeddata"] + "|" + param["item"];
            param.Add("mac", HmacHelper.Compute(ZaloPayHMAC.HMACSHA256, key1, data));

            var result = await HttpHelper.PostFormAsync(createOrderUrl, param);

            foreach(var entry in result) {
                Console.WriteLine("{0} = {1}", entry.Key, entry.Value);
            }
        }
    }
}
// Java version "1.8.0_201"
import org.apache.http.NameValuePair; // https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONObject; // https://mvnrepository.com/artifact/org.json/json
import vn.zalopay.crypto.HMACUtil; // tải về ở mục DOWNLOADS

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;

public class CreateOrder {
    private static Map<String, String> config = new HashMap<String, String>(){{
        put("appid", "553");
        put("key1", "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q");
        put("key2", "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3");
        put("endpoint", "https://sandbox.zalopay.com.vn/v001/tpe/createorder");
    }};

    public static String getCurrentTimeString(String format) {
        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT+7"));
        SimpleDateFormat fmt = new SimpleDateFormat(format);
        fmt.setCalendar(cal);
        return fmt.format(cal.getTimeInMillis());
    }

    public static void main( String[] args ) throws Exception
    {
        final Map embeddata = new HashMap(){{
            put("merchantinfo", "embeddata123");
        }};

        final Map[] item = {
            new HashMap(){{
                put("itemid", "knb");
                put("itemname", "kim nguyen bao");
                put("itemprice", 198400);
                put("itemquantity", 1);
            }}
        };

        Map<String, Object> order = new HashMap<String, Object>(){{
            put("appid", config.get("appid"));
            put("apptransid", getCurrentTimeString("yyMMdd") +"_"+ UUID.randomUUID()); // mã giao dich có định dạng yyMMdd_xxxx
            put("apptime", System.currentTimeMillis()); // miliseconds
            put("appuser", "demo");
            put("amount", 50000);
            put("description", "ZaloPay Intergration Demo");
            put("bankcode", "zalopayapp");
            put("item", new JSONObject(item).toString());
            put("embeddata", new JSONObject(embeddata).toString());
        }};

        // appid +”|”+ apptransid +”|”+ appuser +”|”+ amount +"|" + apptime +”|”+ embeddata +"|" +item
        String data = order.get("appid") +"|"+ order.get("apptransid") +"|"+ order.get("appuser") +"|"+ order.get("amount")
                +"|"+ order.get("apptime") +"|"+ order.get("embeddata") +"|"+ order.get("item");
        order.put("mac", HMACUtil.HMacHexStringEncode(HMACUtil.HMACSHA256, config.get("key1"), data));

        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(config.get("endpoint"));

        List<NameValuePair> params = new ArrayList<>();
        for (Map.Entry<String, Object> e : order.entrySet()) {
            params.add(new BasicNameValuePair(e.getKey(), e.getValue().toString()));
        }

        // Content-Type: application/x-www-form-urlencoded
        post.setEntity(new UrlEncodedFormEntity(params));

        CloseableHttpResponse res = client.execute(post);
        BufferedReader rd = new BufferedReader(new InputStreamReader(res.getEntity().getContent()));
        StringBuilder resultJsonStr = new StringBuilder();
        String line;

        while ((line = rd.readLine()) != null) {
            resultJsonStr.append(line);
        }

        JSONObject result = new JSONObject(resultJsonStr.toString());
        for (String key : result.keySet()) {
            System.out.format("%s = %s\n", key, result.get(key));
        }
    }
}
// go version go1.11.1 linux/amd64
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "strconv"
    "time"

    "github.com/google/uuid" // go get github.com/google/uuid
    "github.com/zpmep/hmacutil" // go get github.com/zpmep/hmacutil
)

type object map[string]interface{}

var (
    appid = "553"
    key1  = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q"
    key2  = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3"
)

func main() {
    embeddata, _ := json.Marshal(object{
        "merchantinfo": "embeddata123",
    })
    items, _ := json.Marshal([]object{
        object{"itemid": "knb", "itemname": "kim nguyen bao", "itemprice": 198400, "itemquantity": 1},
    })
    // request data
    params := make(url.Values)
    params.Add("appid", appid)                            
    params.Add("amount", "1000")                          
    params.Add("appuser", "demo")                         
    params.Add("embeddata", string(embeddata))            
    params.Add("item", string(items))                     
    params.Add("description", "ZaloPay QR Merchant Demo") 
    params.Add("bankcode", "zalopayapp")                  

    now := time.Now()
    params.Add("apptime", strconv.FormatInt(now.UnixNano()/int64(time.Millisecond), 10)) // miliseconds

    transid := uuid.New().String()                                                                                 // unique id
    params.Add("apptransid", fmt.Sprintf("%02d%02d%02d_%v", now.Year()%100, int(now.Month()), now.Day(), transid)) // mã giao dich có định dạng yyMMdd_xxxx

    // appid|apptransid|appuser|amount|apptime|embeddata|item
    data := fmt.Sprintf("%v|%v|%v|%v|%v|%v|%v", params.Get("appid"), params.Get("apptransid"), params.Get("appuser"),
        params.Get("amount"), params.Get("apptime"), params.Get("embeddata"), params.Get("item"))
    params.Add("mac", hmacutil.HexStringEncode(hmacutil.SHA256, key1, data))

    // Content-Type: application/x-www-form-urlencoded
    res, err := http.PostForm("https://sandbox.zalopay.com.vn/v001/tpe/createorder", params)

    // parse response
    if err != nil {
        log.Fatal(err)
    }
    defer res.Body.Close()

    body, _ := ioutil.ReadAll(res.Body)

    var result map[string]interface{}

    if err := json.Unmarshal(body, &result); err != nil {
        log.Fatal(err)
    }

    for k, v := range result {
        log.Printf("%s = %+v", k, v)
    }
}
// Node v10.15.3
const axios = require('axios').default; // npm install axios
const CryptoJS = require('crypto-js'); // npm install crypto-js
const uuid = require('uuid/v1'); // npm install uuid
const moment = require('moment'); // npm install moment

// APP INFO
const config = {
  appid: "553",
  key1: "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  key2: "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  endpoint: "https://sandbox.zalopay.com.vn/v001/tpe/createorder"
};

const embeddata = {
  merchantinfo: "embeddata123"
};

const items = [{
  itemid: "knb",
  itemname: "kim nguyen bao",
  itemprice: 198400,
  itemquantity: 1
}];

const order = {
  appid: config.appid, 
  apptransid: `${moment().format('YYMMDD')}_${uuid()}`, // mã giao dich có định dạng yyMMdd_xxxx
  appuser: "demo", 
  apptime: Date.now(), // miliseconds
  item: JSON.stringify(items), 
  embeddata: JSON.stringify(embeddata), 
  amount: 50000, 
  description: "ZaloPay Integration Demo",
  bankcode: "zalopayapp", 
};

// appid|apptransid|appuser|amount|apptime|embeddata|item
const data = config.appid + "|" + order.apptransid + "|" + order.appuser + "|" + order.amount + "|" + order.apptime + "|" + order.embeddata + "|" + order.item;
order.mac = CryptoJS.HmacSHA256(data, config.key1).toString();

axios.post(config.endpoint, null, { params: order })
  .then(res => {
    console.log(res.data);
  })
  .catch(err => console.log(err));
<?php

// PHP Version 7.3.3

$config = [
  "appid" => 553,
  "key1" => "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2" => "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint" => "https://sandbox.zalopay.com.vn/v001/tpe/createorder"
];

$embeddata = [
  "merchantinfo" => "embeddata123"
];
$items = [
  [ "itemid" => "knb", "itemname" => "kim nguyen bao", "itemprice" => 198400, "itemquantity" => 1 ]
];
$order = [
  "appid" => $config["appid"],
  "apptime" => round(microtime(true) * 1000), // miliseconds
  "apptransid" => date("ymd")."_".uniqid(), // mã giao dich có định dạng yyMMdd_xxxx
  "appuser" => "demo",
  "item" => json_encode($items, JSON_UNESCAPED_UNICODE),
  "embeddata" => json_encode($embeddata, JSON_UNESCAPED_UNICODE),
  "amount" => 50000,
  "description" => "ZaloPay Intergration Demo",
  "bankcode" => "zalopayapp"
];

// appid|apptransid|appuser|amount|apptime|embeddata|item
$data = $order["appid"]."|".$order["apptransid"]."|".$order["appuser"]."|".$order["amount"]
  ."|".$order["apptime"]."|".$order["embeddata"]."|".$order["item"];
$order["mac"] = hash_hmac("sha256", $data, $config["key1"]);

$context = stream_context_create([
  "http" => [
    "header" => "Content-type: application/x-www-form-urlencoded\r\n",
    "method" => "POST",
    "content" => http_build_query($order)
  ]
]);

$resp = file_get_contents($config["endpoint"], false, $context);
$result = json_decode($resp, true);

foreach ($result as $key => $value) {
  echo "$key: $value<br>";
}
# ruby 2.5.1p57

require 'securerandom'
require 'json'
require 'openssl' # gem install openssl
require 'net/http'

config = {
  appid: '553',
  key1: '9phuAOYhan4urywHTh0ndEXiV3pKHr5Q',
  key2: 'Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3',
  endpoint: 'https://sandbox.zalopay.com.vn/v001/tpe/createorder'
}

apptime = (Time.now.to_f.round(3) * 1000).to_i # miliseconds
apptransid = Time.now.strftime('%y%m%d') + '_' + SecureRandom.uuid

embeddata = {
  merchantinfo: 'embeddata123'
}
items = [{
  itemid: 'knb',
  itemname: 'kim nguyen bao',
  itemprice: 198_400,
  itemquantity: 1
}]

order = {
  appid: config[:appid],
  apptransid: apptransid, # mã giao dich có định dạng yyMMdd_xxxx
  appuser: 'demo',
  apptime: apptime, # miliseconds 
  item: items.to_json, 
  embeddata: embeddata.to_json, 
  amount: 50_000, 
  description: 'ZaloPay Integration Demo', 
  bankcode: 'zalopayapp'
}

# appid|apptransid|appuser|amount|apptime|embeddata|item
data = config[:appid] + '|' + apptransid + '|' + order[:appuser] + '|' + order[:amount].to_s + '|' +
       order[:apptime].to_s + '|' + order[:embeddata].to_s + '|' + order[:item].to_s

order[:mac] = OpenSSL::HMAC.hexdigest('sha256', config[:key1], data)

res = Net::HTTP.post_form(URI.parse(config[:endpoint]), order)
result = JSON.parse(res.body)

result.each do |key, value|
  puts "#{key}: #{value}"
end
# coding=utf-8
# Python 3.6

from time import time
from datetime import datetime
import uuid, json, hmac, hashlib, urllib.request, urllib.parse

config = {
  "appid": 553,
  "key1": "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2": "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint": "https://sandbox.zalopay.com.vn/v001/tpe/createorder"
}

order = {
  "appid": config["appid"],
  "apptransid": "{:%y%m%d}_{}".format(datetime.today(), uuid.uuid4()), # mã giao dich có định dạng yyMMdd_xxxx
  "appuser": "demo",
  "apptime": int(round(time() * 1000)), # miliseconds
  "embeddata": json.dumps({ 
    "merchantinfo": "embeddata123"
  }),
  "item": json.dumps([
    { "itemid": "knb", "itemname": "kim nguyen bao", "itemprice": 198400, "itemquantity": 1 }
  ]),
  "amount": 50000,
  "description": "ZaloPay Integration Demo",
  "bankcode": "zalopayapp"
}

# appid|apptransid|appuser|amount|apptime|embeddata|item
data = "{}|{}|{}|{}|{}|{}|{}".format(order["appid"], order["apptransid"], order["appuser"], 
order["amount"], order["apptime"], order["embeddata"], order["item"])

order["mac"] = hmac.new(config['key1'].encode(), data.encode(), hashlib.sha256).hexdigest()

response = urllib.request.urlopen(url=config["endpoint"], data=urllib.parse.urlencode(order).encode())
result = json.loads(response.read())

for k, v in result.items():
  print("{}: {}".format(k, v))
curl -X POST https://sandbox.zalopay.com.vn/v001/tpe/createorder \
  -H "Content-type: application/x-www-form-urlencoded" \
  -d appid=553 \
  -d apptransid=190419_41c9c3a0-6248-11e9-8a6f-f5f370222ed3 \
  -d appuser=demo \
  -d apptime=1555639809242 \
  -d item='[{"itemid":"knb","itemname":"kim nguyen bao","itemprice":198400,"itemquantity":1}]' \
  -d embeddata='{"merchantinfo":"embeddata123"}' \
  -d amount=50000 \
  -d description='ZaloPay Intergration Demo' \
  -d bankcode=zalopayapp \
  -d mac=f02093505c88081d440b79e9de7da9ff73d27942c63713f07607e13b1987775f

Callback

API đối tác xây dựng sẽ nhận kết quả thanh toán từ ZaloPay. Khi và chỉ khi ZaloPay đã thu tiền khách hàng thành công thì mới gọi API này để thông báo kết quả.

Luồng xử lý

Callback Flow

Đặc tả API

Nếu trừ tiền user thành công ZaloPayServer callback cho AppServer theo CallbackURL lúc đăng ký ứng dụng. Ứng dụng dùng key2 (được cấp lúc đăng ký ứng dụng) để xác thực data do ZaloPayServer POST qua.

Dữ liệu nhận được từ callback:

Tên Kiểu dữ liệu Mô tả
data Json String Dữ liệu giao dịch ZaloPay gọi về cho ứng dụng
mac String Thông tin chứng thực của đơn hàng, dùng key2 được cung cấp để chứng thực đơn hàng

Dữ liệu của trường data:

Tên
Kiểu dữ liệu Mô tả Ví dụ
appid long appid của đơn hàng 5
apptransid String apptransid của đơn hàng 180208181007242
apptime long apptime của đơn hàng 1460543835849
appuser String appuser của đơn hàng pmqc
amount long Số tiền ứng dụng nhận được 50000
embeddata String embeddata của đơn hàng {"promotioninfo":"","merchantinfo":"du lieu rieng cua ung dung"}
item String item của đơn hàng [{"itemid":"knb","itename":"kim nguyen bao","itemprice":198400,"itemquantity":1}]
zptransid long Mã giao dịch của ZaloPay 160413000003083
servertime long Thời gian giao dịch của ZaloPay (unix timestamp in miliseconds) 1460543836370
channel int Kênh thanh toán 38
merchantuserid string ZaloPay user đã thanh toán cho đơn hàng
userfeeamount long Số tiền phí 220
discountamount long Số tiền giảm giá 10000

Ví dụ:

{
  "data":"{\"appid\":2,\"zptransid\":160520000000081,\"apptransid\":\"160520176021926423825\",\"apptime\":1463711618132,\"appuser\":\"160514000002501\",\"item\":\"[{\"itemID\":\"it002\",\"itemName\":\"Color 50K\",\"itemQuantity\":1,\"itemPrice\":50000}]\",\"amount\":1000,\"embeddata\":\"{\"promotioninfo\":\"\",\"merchantinfo\":\"du lieu rieng cua ung dung\"}\",\"servertime\":1463711619269,\"channel\":38,\"merchantuserid\":\"rSVW3nBDryiJ6eN7h4L8ZjFn1OAbTaPoBm0I0JbB9zo\",\"userfeeamount\":220}",
  "mac":"16b369598e86411baf15421cff917610119f37d157c064109618496c937b9bc5"
}

Kiểm tra callback hợp lệ

reqmac = HMAC(hmac_algorithm, key2, callback_data.data)
if (reqmac == callback_data.mac) {
  // callback hợp lệ
} else {
  // callback không hợp lệ
} 

Trong đó:

Các kênh thanh toán hỗ trợ:

Giá trị Kênh thanh toán
36 Visa/Master/JCB
37 Bank Account
38 ZaloPay Wallet
39 ATM
41 Visa/Master Debit

Thông tin AppServer trả về cho ZaloPayServer khi nhận callback

Tham số Kiểu dữ liệu Ý nghĩa
returncode int

1: thành công

2: trùng mã giao dịch ZaloPay zptransid hoặc apptransid ( đã cung cấp dịch vụ cho user trước đó)

0: callback lại (tối đa 3 lần)

3: Refund vào tài khoản ZaloPay của user Deprecated (sử dụng api Hoàn tiền thay thế)

<>: thất bại (không callback lại)

returnmessage String Mô tả chi tiết thông tin mã lỗi

Ví dụ:

{
  "returncode": "[returncode]",
  "returnmessage": "[returnmessage]"
}

Code mẫu

/**
 * .Net core 2.1.505
 */
using System;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ZaloPay.Helper; // HmacHelper, RSAHelper, HttpHelper, Utils (tải về ở mục DOWNLOADS)
using ZaloPay.Helper.Crypto;
using Newtonsoft.Json; // https://www.newtonsoft.com/json

namespace ZaloPayExample.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class CallbackController: ControllerBase
    {
        private string key2 = "eG4r0GcoNtRGbO8";

        [HttpPost]
        public IActionResult Post([FromBody] dynamic cbdata)
        {
            var result = new Dictionary<string, object>();

            try {
              var dataStr = Convert.ToString(cbdata["data"]);
              var reqMac = Convert.ToString(cbdata["mac"]);

              var mac = HmacHelper.Compute(ZaloPayHMAC.HMACSHA256, key2, dataStr);

              Console.WriteLine("mac = {0}", mac);

              // kiểm tra callback hợp lệ (đến từ ZaloPay server)
              if (!reqMac.Equals(mac)) {
                  // callback không hợp lệ
                  result["returncode"] = -1;
                  result["returnmessage"] = "mac not equal";
              }
              else {
                  // thanh toán thành công
                  // merchant cập nhật trạng thái cho đơn hàng
                  var dataJson = JsonConvert.DeserializeObject<Dictionary<string, object>>(dataStr);
                  Console.WriteLine("update order's status = success where apptransid = {0}", dataJson["apptransid"]);

                  result["returncode"] = 1;
                  result["returnmessage"] = "success";
              }
            } catch (Exception ex) {
              result["returncode"] = 0; // ZaloPay server sẽ callback lại (tối đa 3 lần)
              result["returnmessage"] = ex.Message;
            }

            // thông báo kết quả cho ZaloPay server
            return Ok(result); 
        }
    }
}
/**
 * Spring Boot v2.1.4.RELEASE
 * */
import org.json.JSONObject; // https://mvnrepository.com/artifact/org.json/json
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.util.logging.Logger;

@RestController
public class CallbackController {

    private Logger logger = Logger.getLogger(this.getClass().getName());
    private String key2 = "eG4r0GcoNtRGbO8";
    private Mac HmacSHA256;

    public CallbackController() throws Exception  {
        HmacSHA256 = Mac.getInstance("HmacSHA256");
        HmacSHA256.init(new SecretKeySpec(key2.getBytes(), "HmacSHA256"));
    }

    @PostMapping("/callback")
    public String callback(@RequestBody String jsonStr) {
        JSONObject result = new JSONObject();

        try {
          JSONObject cbdata = new JSONObject(jsonStr);
          String dataStr = cbdata.getString("data");
          String reqMac = cbdata.getString("mac");

          byte[] hashBytes = HmacSHA256.doFinal(dataStr.getBytes());
          String mac = DatatypeConverter.printHexBinary(hashBytes).toLowerCase();

          // kiểm tra callback hợp lệ (đến từ ZaloPay server)
          if (!reqMac.equals(mac)) {
              // callback không hợp lệ
              result.put("returncode", -1);
              result.put("returnmessage", "mac not equal");
          } else {
              // thanh toán thành công
              // merchant cập nhật trạng thái cho đơn hàng
              JSONObject data = new JSONObject(dataStr);
              logger.info("update order's status = success where apptransid = " + data.getString("apptransid"));

              result.put("returncode", 1);
              result.put("returnmessage", "success");
          }
        } catch (Exception ex) {
          result.put("returncode", 0); // ZaloPay server sẽ callback lại (tối đa 3 lần)
          result.put("returnmessage", ex.getMessage());
        }

        // thông báo kết quả cho ZaloPay server
        return result.toString();
    }
}
// go version go1.11.1 linux/amd64
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"

    "github.com/zpmep/hmacutil" // go get github.com/zpmep/hmacutil
)

// App config
var (
    key2 = "eG4r0GcoNtRGbO8"
)

func main() {
    mux := http.DefaultServeMux
    mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
        defer r.Body.Close()
        var cbdata map[string]interface{}
        decoder := json.NewDecoder(r.Body)
        decoder.Decode(&cbdata)

        requestMac := cbdata["mac"].(string)
        dataStr := cbdata["data"].(string)
        mac := hmacutil.HexStringEncode(hmacutil.SHA256, key2, dataStr)
        log.Println("mac =", mac)

        result := make(map[string]interface{})

    // kiểm tra callback hợp lệ (đến từ ZaloPay server)
        if mac != requestMac {
            // callback không hợp lệ
            result["returncode"] = -1
            result["returnmessage"] = "mac not equal"
        } else {
            // thanh toán thành công
            result["returncode"] = 1
            result["returnmessage"] = "success"

            // merchant cập nhật trạng thái cho đơn hàng
            var dataJSON map[string]interface{}
            json.Unmarshal([]byte(dataStr), &dataJSON)
            log.Println("update order's status = success where apptransid =", dataJSON["apptransid"])
        }

        // thông báo kết quả cho ZaloPay server
        resultJSON, _ := json.Marshal(result)
        fmt.Fprintf(w, "%s", resultJSON)
    })

    log.Println("Server is listening at port :8888")
    http.ListenAndServe(":8888", mux)
}
// Node v10.15.3
const CryptoJS = require('crypto-js'); // npm install crypto-js
const express = require('express'); // npm install express
const bodyParser = require('body-parser'); // npm install body-parser
const app = express();

const config = {
  key2: "eG4r0GcoNtRGbO8"
};

app.use(bodyParser.json());

app.post('/callback', (req, res) => {
  let result = {};

  try {
    let dataStr = req.body.data;
    let reqMac = req.body.mac;

    let mac = CryptoJS.HmacSHA256(dataStr, config.key2).toString();
    console.log("mac =", mac);


    // kiểm tra callback hợp lệ (đến từ ZaloPay server)
    if (reqMac !== mac) {
      // callback không hợp lệ
      result.returncode = -1;
      result.returnmessage = "mac not equal";
    }
    else {
      // thanh toán thành công
      // merchant cập nhật trạng thái cho đơn hàng
      let dataJson = JSON.parse(dataStr, config.key2);
      console.log("update order's status = success where apptransid =", dataJson["apptransid"]);

      result.returncode = 1;
      result.returnmessage = "success";
    }
  } catch (ex) {
    result.returncode = 0; // ZaloPay server sẽ callback lại (tối đa 3 lần)
    result.returnmessage = ex.message;
  }

  // thông báo kết quả cho ZaloPay server
  res.json(result);
});

app.listen(8888, function (){
  console.log('Server is listening at port :8888');
});
<?php

// PHP Version 7.3.3

$result = [];

try {
  $key2 = "eG4r0GcoNtRGbO8";
  $postdata = file_get_contents('php://input');
  $postdatajson = json_decode($postdata, true);
  $mac = hash_hmac("sha256", $postdatajson["data"], $key2);

  $requestmac = $postdatajson["mac"];

  // kiểm tra callback hợp lệ (đến từ ZaloPay server)
  if (strcmp($mac, $requestmac) != 0) {
    // callback không hợp lệ
    $result["returncode"] = -1;
    $result["returnmessage"] = "mac not equal";
  } else {
    // thanh toán thành công
    // merchant cập nhật trạng thái cho đơn hàng
    $datajson = json_decode($postdatajson["data"], true);
    // echo "update order's status = success where apptransid = ". $dataJson["apptransid"];

    $result["returncode"] = 1;
    $result["returnmessage"] = "success";
  }
} catch (Exception $e) {
  $result["returncode"] = 0; // ZaloPay server sẽ callback lại (tối đa 3 lần)
  $result["returnmessage"] = $e->getMessage();
}

// thông báo kết quả cho ZaloPay server
echo json_encode($result);
# ruby 2.5.1p57
# rails 5.2.3

# config/routes.rb
Rails.application.routes.draw do
  match '/callback' => 'callback#handle', via: :post
end

# app/controllers/callback_controller.rb
require 'json'
require 'openssl' # gem install openssl

class CallbackController < ApplicationController 
  def initialize
    super
    @config = {
      key2: 'eG4r0GcoNtRGbO8'
    }
  end

  # POST /callback
  def handle
    result = {}

    begin
      cbdata = params
      mac = OpenSSL::HMAC.hexdigest('sha256', @config[:key2], cbdata['data'])

      # kiểm tra callback hợp lệ (đến từ ZaloPay server)
      if cbdata['mac'] != mac
        # callback không hợp lệ
        result[:returncode] = -1
        result[:returnmessage] = "mac not equal"
      else
        # thanh toán thành công
        # merchant cập nhật trạng thái cho đơn hàng
        dataJson = JSON.parse(cbdata['data'])
        puts "update order's status = success where apptransid = " + dataJson['apptransid']

        result[:returncode] = 1
        result[:returnmessage] = "success"
      end
    rescue Exception => ex
      result[:returncode] = 0 # ZaloPay server sẽ callback lại (tối đa 3 lần)
      result[:returnmessage] = ex.message
    end

    # thông báo kết quả cho ZaloPay server
    render json: result, status: :ok
  end
end
# coding=utf-8
# Python 3.6

from flask import Flask, request, json # pip3 install Flask 
import hmac, hashlib

app = Flask(__name__)
config = {
  'key2': 'eG4r0GcoNtRGbO8'
}

@app.route('/callback', methods=['POST'])
def callback():
  result = {}

  try
    cbdata = request.json
    mac = hmac.new(config['key2'].encode(), cbdata['data'].encode(), hashlib.sha256).hexdigest()

    # kiểm tra callback hợp lệ (đến từ ZaloPay server)
    if mac != cbdata['mac']:
      # callback không hợp lệ
      result['returncode'] = -1
      result['returnmessage'] = 'mac not equal'
    else:
      # thanh toán thành công
      # merchant cập nhật trạng thái cho đơn hàng
      dataJson = json.loads(cbdata['data'])
      print("update order's status = success where apptransid = " + dataJson['apptransid'])

      result['returncode'] = 1
      result['returnmessage'] = 'success'
  except Exception as e
    result['returncode'] = 0 # ZaloPay server sẽ callback lại (tối đa 3 lần)
    result['returnmessage'] = str(e)

  # thông báo kết quả cho ZaloPay server
  return json.jsonify(result)

Truy vấn trạng thái thanh toán của đơn hàng

Khi user thanh toán thành công thì ZaloPay sẽ gọi callback (notify) về Merchant, khi đó Merchant cập nhật trạng thái đơn hàng Thành Công trên hệ thống của Merchant. Nhưng trong thực tế callback có thể bị miss do lỗi Network timeout, Merchant Service unavailable/Internal error, ... nên Merchant cần hiện thực việc chủ đông gọi API truy vấn trạng thái đơn hàng.

Merchant Server gửi request đến ZaloPay Server để lấy thông tin trạng thái thanh toán của giao dịch.

Luồng xử lý

Get status flow

Đặc tả API

Environment Method Endpoint
Sandbox GET https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid
Real GET https://zalopay.com.vn/v001/tpe/getstatusbyapptransid

Dữ liệu truyền vào api

Tham số
Kiểu dữ liệu Bắt buộc Ý nghĩa
appid int appid của đơn hàng
apptransid String apptransid của đơn hàng
mac String • Thông tin chứng thực dữ liệu
= HMAC(hmac_algorithm, key1, appid+"|"+apptransid+"|"+key1)

Tham số api trả về

Tham số
Kiểu dữ liệu Ý nghĩa
returncode int 1 : thành công
<> : chưa thanh toán / thanh toán thất bại / quá thời hạn truy vấn
returnmessage String Thông tin trạng thái đơn hàng
isprocessing boolean true: giao dịch đang xử lý
false: giao dịch chưa thực hiện / giao dịch đã kết thúc xử lý
amount long Số tiền giao dịch
discountamount long Số tiền giảm giá
zptransid long Mã giao dịch của ZaloPay

Code mẫu

/**
 * .Net core 2.1.505
 */
using System;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using ZaloPay.Helper; // HmacHelper, RSAHelper, HttpHelper, Utils (tải về ở mục DOWNLOADS)
using ZaloPay.Helper.Crypto;
using Newtonsoft.Json; // https://www.newtonsoft.com/json

namespace ZaloPayExample
{
    class Program
    {
        static string appid = "553";
        static string key1 = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q";
        static string queryOrderUrl = "https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid";

        static async Task Main(string[] args)
        {
            var apptransid = "<apptransid>"; 

            var param = new Dictionary<string, string>();
            param.Add("appid", appid);
            param.Add("apptransid", apptransid);
            var data = appid + "|" + apptransid + "|" + key1; 

            param.Add("mac", HmacHelper.Compute(ZaloPayHMAC.HMACSHA256, key1, data));

            var result = await HttpHelper.PostFormAsync(queryOrderUrl, param);

            foreach(var entry in result) {
                Console.WriteLine("{0} = {1}", entry.Key, entry.Value);
            }
        }
    }
}
// Java version "1.8.0_201"

import org.apache.http.NameValuePair; // https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONObject; // https://mvnrepository.com/artifact/org.json/json

import vn.zalopay.crypto.HMACUtil; // tải về ở mục DOWNLOADS

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

public class GetOrderStatus {
    private static Logger logger = Logger.getLogger(App.class.getName());

    private static Map<String, String> config = new HashMap<String, String>(){{
        put("appid", "553");
        put("key1", "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q");
        put("key2", "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3");
        put("endpoint", "https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid");
    }};

    public static void main(String[] args) throws Exception {
        String apptransid = "190308_123456"; 
        String data = config.get("appid") +"|"+ apptransid  +"|"+ config.get("key1"); // appid|apptransid|key1
        String mac = HMACUtil.HMacHexStringEncode(HMACUtil.HMACSHA256, config.get("key1"), data);

        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("appid", config.get("appid")));
        params.add(new BasicNameValuePair("apptransid", apptransid));
        params.add(new BasicNameValuePair("mac", mac));

        URIBuilder uri = new URIBuilder(config.get("endpoint"));
        uri.addParameters(params);

        CloseableHttpClient client = HttpClients.createDefault();
        HttpGet get = new HttpGet(uri.build());

        CloseableHttpResponse res = client.execute(get);
        BufferedReader rd = new BufferedReader(new InputStreamReader(res.getEntity().getContent()));
        StringBuilder resultJsonStr = new StringBuilder();
        String line;

        while ((line = rd.readLine()) != null) {
            resultJsonStr.append(line);
        }

        JSONObject result = new JSONObject(resultJsonStr.toString());
        for (String key : result.keySet()) {
            System.out.format("%s = %s\n", key, result.get(key));
        }
    }
}
// go version go1.11.1 linux/amd64
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"

    "github.com/zpmep/hmacutil" // go get github.com/zpmep/hmacutil
)

var (
    appid = "553"
    key1  = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q"
    key2  = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3"
)

func main() {
    params := make(url.Values)
    params.Add("appid", "553")                
    params.Add("apptransid", "190308_123456")   

    data := fmt.Sprintf("%s|%s|%s", appid, params.Get("apptransid"), key1) // appid|apptransid|key1
    params.Add("mac", hmacutil.HexStringEncode(hmacutil.SHA256, key1, data))

    res, err := http.Get("https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid?" + params.Encode())

    if err != nil {
        log.Fatal(err)
    }
    defer res.Body.Close()

    body, _ := ioutil.ReadAll(res.Body)

    var result map[string]interface{}

    if err := json.Unmarshal(body, &result); err != nil {
        log.Fatal(err)
    }

    for k, v := range result {
        log.Printf("%s = %+v", k, v)
    }
}
// Node v10.15.3
const axios = require('axios').default; // npm install axios
const CryptoJS = require('crypto-js'); // npm install crypto-js

const config = {
  appid: "553",
  key1: "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  key2: "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  endpoint: "https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid"
};

let params = {
  appid: config.appid,
  apptransid: "190308_123456", 
};

let data = config.appid + "|" + params.apptransid + "|" + config.key1; // appid|apptransid|key1
params.mac = CryptoJS.HmacSHA256(data, config.key1).toString();

axios.get(config.endpoint, {
    params
  })
  .then(res => console.log(res.data))
  .catch(err => console.log(err));
<?php

// PHP Version 7.3.3

$config = [
  "appid" => 553,
  "key1" => "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2" => "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint" => "https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid"
];

$apptransid = "190308_123456"; 
$data = $config["appid"]."|".$apptransid."|".$config["key1"]; // appid|apptransid|key1
$params = [
  "appid" => $config["appid"],
  "apptransid" => $apptransid,
  "mac" => hash_hmac("sha256", $data, $config["key1"])
];

$resp = file_get_contents($config["endpoint"]."?".http_build_query($params));
$result = json_decode($resp, true);

foreach ($result as $key => $value) {
  echo "$key: $value<br>";
}
# ruby 2.5.1p57

require 'json'
require 'openssl' # gem install openssl
require 'net/http'

config = {
  appid: '553',
  key1: '9phuAOYhan4urywHTh0ndEXiV3pKHr5Q',
  key2: 'Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3',
  endpoint: 'https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid'
}

params = {
  appid: config[:appid],
  apptransid: "190308_123456",
}

data = config[:appid] +"|"+ params[:apptransid] +"|"+ config[:key1] # appid|apptransid|key1
params[:mac] = OpenSSL::HMAC.hexdigest('sha256', config[:key1], data)

uri = URI(config[:endpoint])
uri.query = URI.encode_www_form(params)

res = Net::HTTP.get_response(uri)
result = JSON.parse(res.body)

result.each do |key, value|
  puts "#{key}: #{value}"
end
# coding=utf-8
# Python 3.6

from time import time
import hmac, hashlib, urllib.parse, urllib.request

config = {
  "appid": 553,
  "key1": "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2": "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint": "https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid"
}

params = {
  "appid": config["appid"],
  "apptransid": "190308_123456"
}

data = "{}|{}|{}".format(config["appid"], params["apptransid"], config["key1"]) # appid|apptransid|key1
params["mac"] = hmac.new(config['key1'].encode(), data.encode(), hashlib.sha256).hexdigest()

resposne = urllib.request.urlopen(url=config["endpoint"], data=urllib.parse.urlencode(params).encode())
result = json.loads(response.read())

for k, v in result.items():
  print("{}: {}".format(k, v))
curl https://sandbox.zalopay.com.vn/v001/tpe/getstatusbyapptransid \
  -d appid=553 \
  -d apptransid=190419-123456 \
  -d mac=440f389ddd834b40f21f9baed9448b530b6d7a9af722d747f3fa0af68c007c2e

Hoàn tiền giao dịch

API cho phép đối tác hoàn tiền toàn phần hoặc từng phần của 1 giao dịch thanh toán thành công qua ZaloPay.

Quy tắc hoàn tiền

HÌNH THỨC THANH TOÁN NGUỒN TIỀN HOÀN TIỀN VỀ THỜI GIAN HOÀN TIỀN NGÂN HÀNG HỖ TRỢ
Ứng dụng ZaloPay Ví ZaloPay Ví ZaloPay Ngay lập tức Vietcombank, Vietinbank, BIDV, Sacombank, Eximbank, SCB, Bản Việt, JCB
ATM / Tài khoản ngân hàng Ví ZaloPay Ngay lập tức
Visa / Master / JCB Thẻ 5 - 7 ngày làm việc
Thanh toán Thẻ/Tài khoản ngân hàng qua cổng ZaloPay Gateway ATM / Tài khoản ngân hàng Tài khoản ngân hàng 3 - 5 ngày làm việc (tùy ngân hàng) ABBank, ACB, Agribank, Bắc Á Bank, Bảo Việt Bank, BIDV, Đông Á Bank, Eximbank, GP Bank, HD Bank, Liên Việt Post Bank, Maritime Bank, MB Bank, Nam Á Bank, NCB, Bản Việt, OCB, Ocean Bank, PG Bank, Sacombank, Saigon Bank, SCB, SeA Bank, SHB, Techcombank, TP Bank, VIB
Visa / Master / JCB Thẻ 5 - 7 ngày làm việc (tùy ngân hàng)

Luồng xử lý

Refund flow

Đặc tả API

Environment Method Endpoint
Sandbox POST https://sandbox.zalopay.com.vn/v001/tpe/partialrefund
Real POST https://zalopay.com.vn/v001/tpe/partialrefund

Dữ liệu truyền vào api

Tham số
Kiểu dữ liệu Kích thước Bắt buộc Ý nghĩa
mrefundid String 45 • Merchant phải tự gen ra mã giao dịch riêng của mình khi submit yêu cầu hoàn tiền.
• Định dạng: yymmdd_appid_xxxxxxxxxx
appid int Merchant app id đã được cấp khi đăng ký với ZaloPay.
zptransid String 15 • Mã giao dịch muốn hoàn tiền.
• Mã này được Zalo Pay trả về khi thanh toán.
amount long Số tiền muốn hoàn lại cho khách hàng
timestamp long 13 • Thời điểm thực hiện việc hoàn tiền (unix timestamp in milisecond).
• Thời gian tính đến milisecond, và lấy theo current time
mac String Thông tin chứng thực (xem cách tạo thông tin chứng thực bên dưới)
description String 100 Lý do hoàn tiền

Tạo thông tin chứng thực

mac = HMAC(hmac_algorithm, key1, hmacinput)

Trong đó:

Tham số api trả về

Tham số Kiểu dữ liệu Ý nghĩa
returncode int 1 : Hoàn tiền giao dịch thành công
<1 : Hoàn tiền thất bại, cần thực hiện lại giao dịch
>1 : Đang hoàn tiền, gọi getRefundStatus api để lấy trạng thái cuối cùng
returnmessage String Thông tin lỗi
refundid String Mã giao dịch hoàn tiền của ZaloPay, cần lưu lại để đối chiếu

Code mẫu

/**
 * .Net core 2.1.505
 */
using System;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using ZaloPay.Helper; // HmacHelper, RSAHelper, HttpHelper, Utils (tải về ở mục DOWNLOADS)
using ZaloPay.Helper.Crypto;
using Newtonsoft.Json; // https://www.newtonsoft.com/json

namespace ZaloPayExample
{
    class Program
    {
        static string appid = "553";
        static string key1 = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q";
        static string refundUrl = "https://sandbox.zalopay.com.vn/v001/tpe/partialrefund";

        static async Task Main(string[] args)
        {
            var timestamp = Utils.GetTimeStamp().ToString();
            var rand = new Random();
            var uid = timestamp+""+rand.Next(111, 999).ToString(); 

            Dictionary<string, string> param = new Dictionary<string, string>();
            param.Add("appid", appid);
            param.Add("mrefundid", DateTime.Now.ToString("yyMMdd") + "_" + appid + "_" + uid); 
            param.Add("zptransid", "1234567");
            param.Add("amount", "1000");
            param.Add("timestamp", timestamp);
            param.Add("description", "demo");

            var data = appid + "|" + param["zptransid"] + "|" + param["amount"] + "|" + param["description"] + "|" + param["timestamp"];
            param.Add("mac", HmacHelper.Compute(ZaloPayHMAC.HMACSHA256, key1, data));

            var result = await HttpHelper.PostFormAsync(refundUrl, param);

            foreach(var entry in result) {
                Console.WriteLine("{0} = {1}", entry.Key, entry.Value);
            }
        }
    }
}
// Java version "1.8.0_201"
import org.apache.http.NameValuePair; // https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONObject; // https://mvnrepository.com/artifact/org.json/json
import vn.zalopay.crypto.HMACUtil; // tải về ở mục DOWNLOADS

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Logger;

public class Refund {
    private static Logger logger = Logger.getLogger(App.class.getName());

    private static Map<String, String> config = new HashMap<String, String>(){{
        put("appid", "553");
        put("key1", "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q");
        put("key2", "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3");
        put("endpoint", "https://sandbox.zalopay.com.vn/v001/tpe/partialrefund");
    }};

    private static String getCurrentTimeString(String format) {
        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT+7"));
        SimpleDateFormat fmt = new SimpleDateFormat(format);
        fmt.setCalendar(cal);
        return fmt.format(cal.getTimeInMillis());
    }

    public static void main( String[] args ) throws Exception
    {
        String appid = config.get("appid");
        Random rand = new Random();
        long timestamp = System.currentTimeMillis(); // miliseconds
        String uid = timestamp + "" + (111 + rand.nextInt(888)); // unique id

        Map<String, Object> order = new HashMap<String, Object>(){{
            put("appid", appid);
            put("zptransid", 123456789);
            put("mrefundid", getCurrentTimeString("yyMMdd") +"_"+ appid +"_"+uid);
            put("timestamp", timestamp);
            put("amount", 50000);
            put("description", "ZaloPay Intergration Demo");
        }};

        // appid|zptransid|amount|description|timestamp
        String data = order.get("appid") +"|"+ order.get("zptransid") +"|"+ order.get("amount")
                +"|"+ order.get("description") +"|"+ order.get("timestamp");
        order.put("mac", HMACUtil.HMacHexStringEncode(HMACUtil.HMACSHA256, config.get("key1"), data));

        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(config.get("endpoint"));

        List<NameValuePair> params = new ArrayList<>();
        for (Map.Entry<String, Object> e : order.entrySet()) {
            params.add(new BasicNameValuePair(e.getKey(), e.getValue().toString()));
        }

        post.setEntity(new UrlEncodedFormEntity(params));

        CloseableHttpResponse res = client.execute(post);
        BufferedReader rd = new BufferedReader(new InputStreamReader(res.getEntity().getContent()));
        StringBuilder resultJsonStr = new StringBuilder();
        String line;

        while ((line = rd.readLine()) != null) {
            resultJsonStr.append(line);
        }

        JSONObject result = new JSONObject(resultJsonStr.toString());
        for (String key : result.keySet()) {
            System.out.format("%s = %s\n", key, result.get(key));
        }
    }
}
// go version go1.11.1 linux/amd64
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "strconv"
    "time"

    "github.com/zpmep/hmacutil" // go get github.com/zpmep/hmacutil
)

var (
    appid = "553"
    key1  = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q"
    key2  = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3"
)

func main() {
    params := make(url.Values)
    params.Add("appid", appid)
    params.Add("zptransid", "190508000000017")
    params.Add("amount", "50000")
    params.Add("description", "ZaloPay Refund Demo")

    now := time.Now()
    timestamp := now.UnixNano() / int64(time.Millisecond) // Miliseconds
    params.Add("timestamp", strconv.FormatInt(timestamp, 10))

  uid := fmt.Sprintf("%d%d", timestamp, 111+rand.Intn(888))

    params.Add("mrefundid", fmt.Sprintf("%02d%02d%02d_%v_%v", now.Year()%100, int(now.Month()), now.Day(), appid, uid))

    // appid|zptransid|amount|description|timestamp
    data := fmt.Sprintf("%v|%v|%v|%v|%v", appid, params.Get("zptransid"), params.Get("amount"), params.Get("description"), params.Get("timestamp"))
    params.Add("mac", hmacutil.HexStringEncode(hmacutil.SHA256, key1, data))

    log.Printf("%+v", params)

    // Content-Type: application/x-www-form-urlencoded
    res, err := http.PostForm("https://sandbox.zalopay.com.vn/v001/tpe/partialrefund", params)

    if err != nil {
        log.Fatal(err)
    }
    defer res.Body.Close()

    body, _ := ioutil.ReadAll(res.Body)

    var result map[string]interface{}

    if err := json.Unmarshal(body, &result); err != nil {
        log.Fatal(err)
    }

    for k, v := range result {
        log.Printf("%s = %v", k, v)
    }
}
// Node v10.15.3
const axios = require('axios').default; // npm install axios
const CryptoJS = require('crypto-js'); // npm install crypto-js
const moment = require('moment'); // npm install moment

const config = {
  appid: "553",
  key1: "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  key2: "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  endpoint: "https://sandbox.zalopay.com.vn/v001/tpe/partialrefund"
};

const timestamp = Date.now();
const uid = `${timestamp}${Math.floor(111 + Math.random() * 999)}`; // unique id

let params = {
  appid: config.appid,
  mrefundid: `${moment().format('YYMMDD')}_${config.appid}_${uid}`,
  timestamp, // miliseconds
  zptransid: '190508000000022',
  amount: '50000',
  description: 'ZaloPay Refund Demo',
};

// appid|zptransid|amount|description|timestamp
let data = params.appid + "|" + params.zptransid + "|" + params.amount + "|" + params.description + "|" + params.timestamp;
params.mac = CryptoJS.HmacSHA256(data, config.key1).toString();

axios.post(config.endpoint, null, { params })
  .then(res => console.log(res.data))
  .catch(err => console.log(err));
<?php

// PHP Version 7.3.3

$config = [
  "appid" => 553,
  "key1" => "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2" => "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint" => "https://sandbox.zalopay.com.vn/v001/tpe/partialrefund"
];

$timestamp = round(microtime(true) * 1000); // miliseconds
$uid = "$timestamp".rand(111,999); // unique id 

$params = [
  "appid" => $config["appid"],
  "mrefundid" => date("ymd")."_".$config["appid"]."_".$uid,
  "timestamp" => $timestamp,
  "zptransid" => 123456789,
  "amount" => 50000,
  "description" => "ZaloPay Intergration Demo"
];

// appid|zptransid|amount|description|timestamp
$data = $params["appid"]."|".$params["zptransid"]."|".$params["amount"]
  ."|".$params["description"]."|".$params["timestamp"];
$params["mac"] = hash_hmac("sha256", $data, $config["key1"]);

$context = stream_context_create([
  "http" => [
    "header" => "Content-type: application/x-www-form-urlencoded\r\n",
    "method" => "POST",
    "content" => http_build_query($params)
  ]
]);

$resp = file_get_contents($config["endpoint"], false, $context);
$result = json_decode($resp, true);

foreach ($result as $key => $value) {
  echo "$key: $value<br>";
}
# ruby 2.5.1p57

require 'json'
require 'openssl'
require 'net/http'

config = {
  appid: '553',
  key1: '9phuAOYhan4urywHTh0ndEXiV3pKHr5Q',
  key2: 'Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3',
  endpoint: 'https://sandbox.zalopay.com.vn/v001/tpe/partialrefund'
}

appid = config[:appid]
timestamp = (Time.now.to_f.round(3) * 1000).to_i
uid = timestamp.to_s + '' + (111 + Random.rand(888)).to_s # unique id
mrefundid = Time.now.strftime('%y%m%d') + '_' + appid + '_' + uid

order = {
  appid: appid,
  zptransid: "123456789",
  mrefundid: mrefundid,
  timestamp: timestamp,
  amount: 50_000,
  description: 'ZaloPay Integration Demo',
}

# appid|zptransid|amount|description|timestamp
data = appid + '|' + order[:zptransid] + '|' + order[:amount].to_s + '|' + order[:description] + '|' + order[:timestamp].to_s

order[:mac] = OpenSSL::HMAC.hexdigest('sha256', config[:key1], data)

res = Net::HTTP.post_form(URI.parse(config[:endpoint]), order)
result = JSON.parse(res.body)

result.each do |key, value|
  puts "#{key}: #{value}"
end
# coding=utf-8
# Python 3.6

from time import time
from datetime import datetime
import random, json, hmac, hashlib, urllib.request, urllib.parse

config = {
  "appid": 553,
  "key1": "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2": "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint": "https://sandbox.zalopay.com.vn/v001/tpe/partialrefund"
}

timestamp = int(round(time() * 1000)) # miliseconds
uid =  "{}{}".format(timestamp, random.randint(111, 999)) # unique id

order = {
  "appid": config["appid"],
  "mrefundid": "{:%y%m%d}_{}_{}".format(datetime.today(), config["appid"], uid),
  "timestamp": timestamp,
  "zptransid": 123456789,
  "amount": 1000,
  "description": "ZaloPay Integration Demo",
}

# appid|zptransid|amount|description|timestamp
data = "{}|{}|{}|{}|{}".format(order["appid"], order["zptransid"], order["amount"], order["description"], order["timestamp"])
order["mac"] = hmac.new(config['key1'].encode(), data.encode(), hashlib.sha256).hexdigest()

response = urllib.request.urlopen(url=config["endpoint"], data=urllib.parse.urlencode(order).encode())
result = json.loads(response.read())

for k, v in result.items():
  print("{}: {}".format(k, v))
curl -X POST https://sandbox.zalopay.com.vn/v001/tpe/partialrefund \
  -H "Content-type: application/x-www-form-urlencoded" \
  -d appid=553 \
  -d mrefundid=190419_553_352a73a0-6249-11e9-b146-9306e752a45c \
  -d timestamp=1555640217561 \
  -d zptransid=1234567 \
  -d amount=1000 \
  -d description='ZaloPay Integration Demo' \
  -d mac=5d826b55da6b97525d3eb80a7484922300ae5e2be7d51a35be8e34763a899d73

Truy vấn trạng thái hoàn tiền - GetRefundStatus

API cho phép đối tác gọi để truy vấn tình trạng hoàn tiền của 1 giao dịch hoàn tiền.

Luồng xử lý

Get refund status flow

Đặc tả API

Environment Method Endpoint
Sandbox GET https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus
Real GET https://zalopay.com.vn/v001/tpe/getpartialrefundstatus

Dữ liệu truyền vào api

Tham số
Kiểu dữ liệu Kích thước Bắt buộc Ý nghĩa
appid String appid của merchant được cung cấp
mrefundid String 45 • Mã giao dịch merchant tự gen đã truyền qua lúc gọi hoàn tiền.
• Định dạng: yymmdd_appid_xxxxxxxxxx
timestamp long Thời điểm gọi api (timestamp in milisecond)
mac String = HMAC(hmac_algorithm, key1, appid+"|"+mrefundid+"|"+timestamp)

Tham số api trả về

Tham số Kiểu dữ liệu Ý nghĩa
returncode int mã lỗi
returnmessage String thông tin lỗi

Thông tin mã lỗi

Mã lỗi Miêu tả ngắn Ý nghĩa
2 IN_REFUND_QUEUE Giao dịch đang được hoàn tiền.
1 REFUND_SUCCESS Refund thành công.
0 EXCEPTION Refund có lỗi.
-1 REFUND_FAIL Refund thất bại.
-3 MAC_INVALID Sai thông tin chứng thực.
-10 APPID_INVALID Sai thông tin app.
-13 REFUND_EXPIRE_TIME Quá thời hạn cho phép hoàn tiền.
-24 INVALID_MERCHANT_REFUNDID_FORMAT Sai định dạng mrefundid.
-25 INVALID_MERCHANT_REFUNDID_DATE mrefundid có thời gian không đúng.
-26 INVALID_MERCHANT_REFUNDID_APPID mrefundid có appId không đúng.

Code mẫu

/**
 * .Net core 2.1.505
 */
using System;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using ZaloPay.Helper; // HmacHelper, RSAHelper, HttpHelper, Utils (tải về ở mục DOWNLOADS)
using ZaloPay.Helper.Crypto;
using Newtonsoft.Json; // https://www.newtonsoft.com/json

namespace ZaloPayExample
{
    class Program
    {
        static string appid = "553";
        static string key1 = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q";
        static string getRefundStatusUrl = "https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus";

        static async Task Main(string[] args)
        {
            Dictionary<string, string> param = new Dictionary<string, string>();
            param.Add("appid", appid);
            param.Add("timestamp", "123456789");
            param.Add("mrefundid", "190308_553_xxxxxx");

            var data = appid + "|" + param["mrefundid"] + "|" + param["timestamp"];
            param.Add("mac", HmacHelper.Compute(ZaloPayHMAC.HMACSHA256, key1, data));

            var result = await HttpHelper.PostFormAsync(getRefundStatusUrl, param);

            foreach(var entry in result) {
                Console.WriteLine("{0} = {1}", entry.Key, entry.Value);
            }
        }
    }
}
// Java version "1.8.0_201"
import org.apache.http.NameValuePair; // https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONObject; // https://mvnrepository.com/artifact/org.json/json
import vn.zalopay.crypto.HMACUtil; // tải về ở mục DOWNLOADS

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

public class GetRefundStatus {
    private static Logger logger = Logger.getLogger(App.class.getName());

    private static Map<String, String> config = new HashMap<String, String>(){{
        put("appid", "553");
        put("key1", "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q");
        put("key2", "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3");
        put("endpoint", "https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus");
    }};


    public static void main(String[] args) throws Exception {
        String mrefundid = "190308_553_123456"; 
        String timestamp = Long.toString(System.currentTimeMillis()); // miliseconds
        String data = config.get("appid") +"|"+ mrefundid  +"|"+ timestamp; // appid|mrefundid|timestamp
        String mac = HMACUtil.HMacHexStringEncode(HMACUtil.HMACSHA256, config.get("key1"), data);

        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("appid", config.get("appid")));
        params.add(new BasicNameValuePair("mrefundid", mrefundid));
        params.add(new BasicNameValuePair("timestamp", timestamp));
        params.add(new BasicNameValuePair("mac", mac));

        URIBuilder uri = new URIBuilder(config.get("endpoint"));
        uri.addParameters(params);

        CloseableHttpClient client = HttpClients.createDefault();
        HttpGet get = new HttpGet(uri.build());

        CloseableHttpResponse res = client.execute(get);
        BufferedReader rd = new BufferedReader(new InputStreamReader(res.getEntity().getContent()));
        StringBuilder resultJsonStr = new StringBuilder();
        String line;

        while ((line = rd.readLine()) != null) {
            resultJsonStr.append(line);
        }

        JSONObject result = new JSONObject(resultJsonStr.toString());
        for (String key : result.keySet()) {
            System.out.format("%s = %s\n", key, result.get(key));
        }
    }
}
// go version go1.11.1 linux/amd64
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "strconv"

    "github.com/zpmep/hmacutil" // go get github.com/zpmep/hmacutil
)

var (
    appid = "553"
    key1  = "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q"
    key2  = "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3"
)

func main() {
    params := make(url.Values)
    params.Add("appid", "553")
    params.Add("mrefundid", "190308_553_123456") 
    params.Add("timestamp", strconv.Itoa(12345678910)) // miliseconds

    data := fmt.Sprintf("%v|%v|%v", appid, params.Get("mrefundid"), params.Get("timestamp")) // appid|mrefundid|timestamp
    params.Add("mac", hmacutil.HexStringEncode(hmacutil.SHA256, key1, data))

    res, err := http.Get("https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus?" + params.Encode())

    if err != nil {
        log.Fatal(err)
    }
    defer res.Body.Close()

    body, _ := ioutil.ReadAll(res.Body)

    var result map[string]interface{}

    if err := json.Unmarshal(body, &result); err != nil {
        log.Fatal(err)
    }

    for k, v := range result {
        log.Printf("%s = %+v", k, v)
    }
}
// Node v10.15.3
const axios = require('axios').default; // npm install axios
const CryptoJS = require('crypto-js'); // npm install crypto-js

const config = {
  appid: "553",
  key1: "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  key2: "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  endpoint: "https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus"
};

const params = {
  appid: config.appid,
  timestamp: Date.now(), // miliseconds
  mrefundid: "190312_553_123456", 
};

const data = config.appid + "|" + params.mrefundid + "|" + params.timestamp; // appid|mrefundid|timestamp
params.mac = CryptoJS.HmacSHA256(data, config.key1).toString()

axios.get(config.endpoint, { params })
  .then(res => console.log(res.data))
  .catch(err => console.log(err));
<?php

// PHP Version 7.3.3

$config = [
  "appid" => 553,
  "key1" => "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2" => "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint" => "https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus"
];

$mrefundid = "190308_553_123456"; 
$timestamp = round(microtime(true) * 1000); // miliseconds
$data = $config["appid"]."|".$mrefundid."|".$timestamp; // appid|mrefundid|timestamp
$params = [
  "appid" => $config["appid"],
  "timestamp" => $timestamp,
  "mrefundid" => $mrefundid,
  "mac" => hash_hmac("sha256", $data, $config["key1"])
];

$resp = file_get_contents($config["endpoint"]."?".http_build_query($params));
$result = json_decode($resp, true);

foreach ($result as $key => $value) {
  echo "$key: $value<br>";
}
# ruby 2.5.1p57

require 'json'
require 'openssl' # gem install openssl
require 'net/http'

config = {
  appid: '553',
  key1: '9phuAOYhan4urywHTh0ndEXiV3pKHr5Q',
  key2: 'Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3',
  endpoint: 'https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus'
}

params = {
  appid: config[:appid],
  mrefundid: "190312_553_123456",
  timestamp: (Time.now.to_f.round(3) * 1000).to_i # miliseconds
}

data = config[:appid] +"|"+ params[:mrefundid] +"|"+ params[:timestamp].to_s # appid|mrefundid|timestamp
params[:mac] = OpenSSL::HMAC.hexdigest('sha256', config[:key1], data)

uri = URI(config[:endpoint])
uri.query = URI.encode_www_form(params)

res = Net::HTTP.get_response(uri)
result = JSON.parse(res.body)

result.each do |key, value|
  puts "#{key}: #{value}"
end
# coding=utf-8
# Python 3.6

from time import time
import hmac, hashlib, urllib.parse, urllib.request

config = {
  "appid": 553,
  "key1": "9phuAOYhan4urywHTh0ndEXiV3pKHr5Q",
  "key2": "Iyz2habzyr7AG8SgvoBCbKwKi3UzlLi3",
  "endpoint": "https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus"
}

params = {
  "appid": config["appid"],
  "timestamp": int(round(time() * 1000)), # miliseconds
  "mrefundid": "190409_553_123456",
}

data = "{}|{}|{}".format(config["appid"], params["mrefundid"], params["timestamp"]) # appid|mrefundid|timestamp
params["mac"] = hmac.new(config["key1"].encode(), data.encode(), hashlib.sha256).hexdigest()

resposne = urllib.request.urlopen(url=config["endpoint"], data=urllib.parse.urlencode(params).encode())
result = json.loads(response.read())

for k, v in result.items():
  print("{}: {}".format(k, v))
curl https://sandbox.zalopay.com.vn/v001/tpe/getpartialrefundstatus \
  -d appid=553 \
  -d timestamp=1555640469224 \
  -d mrefundid=190308_553_123456 \
  -d mac=4255035bae2e65118887bc17110ab4b43a1cdd7c513aad9b25a5ef27e187196e
Không tìm thấy kết quả phù hợp