NAV

Mô hình tích hợp

Chọn mô hình tích hợp ZaloPay phù hợp

Mobile Web to App

Giới thiệu Demo API

Mô tả

Người dùng mua sản phẩm trên Mobile Web của Merchant, chọn phương thức thanh toán đơn hàng là ZaloPay. Website của Merchant chuyển tiếp người dùng sang ZaloPay App thông qua đường Mobile Web to App. Sau khi người dùng thực hiện thanh toán xong, ZaloPay Server sẽ thông báo kết quả giao dịch cho Merchant Server.

Luồng xử lý

Sequence Flow

Đặc tả API

Khi Merchant Server gọi request tạo đơn hàng cho ZaloPay Server, ZaloPay Server sẽ trả về một đoạn link chuyển tiếp đã được build sẵn nằm trong field orderurl.

Ví dụ:

{
  "returncode": 1,
  "returnmessage": "Thành công",
  "orderurl":"https://sbgateway.zalopay.vn/openinapp?order=eyJ6cHRyYW5zdG9rZW4iOiJ4dGd1SEs1YnU0VDJkSHE3TUFwTFFnIiwiYXBwaWQiOjN9"
}

Khi nguời dùng thực hiện thanh toán, website phía merchant gọi link chuyển tiếp này để điều hướng user về trang thanh toán bằng ZaloPay App.

Link chuyển tiếp (giá trị của orderurl) dùng để ZaloPay App thực hiện thanh toán:

Redirect

Sau khi người dùng hoàn thành thanh toán, sẽ được chuyển về trang hiển thị kết quả của Merchant (theo RedirectURL Merchant đã cung cấp cho ZaloPay).

Dữ liệu truyền vào query string khi ZaloPay redirect về trang của Merchant:

Tham số Kiểu dữ liệu Ý nghĩa
appid int appid của đơn hàng
apptransid String apptransid của đơn hàng
pmcid int Kênh thanh toán
bankcode String Mã ngân hàng
amount long Giá trị của đơn hàng VND
discountamount long Giảm giá VND
status int Mã lỗi
checksum String Dùng để kiểm tra redirect có hợp lệ hay không, = HMAC(hmac_algorithm, key2, appid +"|"+ apptransid +"|"+ pmcid +"|"+ bankcode +"|"+ amount +"|"+ discountamount +"|"+ status)

Code mẫu kiểm tra Redirect hợp lệ

/*
    ASP.Net core
*/
using Microsoft.AspNetCore.Mvc;
using ZaloPay.Helper; // HmacHelper, RSAHelper, HttpHelper, Utils (tải về ở mục DOWNLOADS)
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 {
                // kiểm tra xem đã nhận được callback hay chưa, nếu chưa thì tiến hành gọi API truy vấn trạng thái thanh toán của đơn hàng để lấy kết quả cuối cùng
                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 {
            // kiểm tra xem đã nhận được callback hay chưa, nếu chưa thì tiến hành gọi API truy vấn trạng thái thanh toán của đơn hàng để lấy kết quả cuối cùng
            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 {
            // kiểm tra xem đã nhận được callback hay chưa, nếu chưa thì tiến hành gọi API truy vấn trạng thái thanh toán của đơn hàng để lấy kết quả cuối cùng
            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 {
    // kiểm tra xem đã nhận được callback hay chưa, nếu chưa thì tiến hành gọi API truy vấn trạng thái thanh toán của đơn hàng để lấy kết quả cuối cùng
    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 {
  // kiểm tra xem đã nhận được callback hay chưa, nếu chưa thì tiến hành gọi API truy vấn trạng thái thanh toán của đơn hàng để lấy kết quả cuối cùng
  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
      # kiểm tra xem đã nhận được callback hay chưa, nếu chưa thì tiến hành gọi API truy vấn trạng thái thanh toán của đơn hàng để lấy kết quả cuối cùng
      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:
    # kiểm tra xem đã nhận được callback hay chưa, nếu chưa thì tiến hành gọi API truy vấn trạng thái thanh toán của đơn hàng để lấy kết quả cuối cùng
    return "Ok", 200

if __name__ == '__main__':
  app.run(host='0.0.0.0', port=8001)
Không tìm thấy kết quả phù hợp