v26 Standard
← Về trang chủ
Hotline: (028) 710 66 777

Tài liệu nhà phát triển

Chào mừng bạn đến với cổng thông tin dành cho lập trình viên tích hợp vào Aureus Payment Gateway. Tài liệu này cung cấp các thông số kỹ thuật, hướng dẫn cấu hình và mẫu mã nguồn để bạn có thể tích hợp tính năng thanh toán QR tự động vào hệ thống ERP, Web hoặc App của mình.

Aureus Payment Gateway là giải pháp quản trị dòng tiền vô hình, cho phép dòng tiền đi thẳng từ người mua về tài khoản ngân hàng của người bán mà không qua tài khoản trung gian, đảm bảo an toàn tuyệt đối.

Luồng hoạt động hệ thống

BANK (NGÂN HÀNG) Aureus Gateway Processing Engine CLIENT SYSTEM 1. Notify Money In 2. Webhook Callback

Sơ đồ: Quy trình xử lý thông báo gạch nợ tự động qua Aureus Gateway

Dựa trên luồng hoạt động trên, quy trình tích hợp thực tế đã được Aureus Gateway tinh gọn chỉ còn 2 bước kỹ thuật cốt lõi, giúp rút ngắn tối đa thời gian triển khai cho hệ thống của bạn.

Dành cho đối tác đã sẵn sàng: Nếu bạn đã sở hữu X-Api-KeyPrivateKey từ Portal, hãy bắt đầu ngay với hướng dẫn chi tiết tại Bước 1Bước 2 bên dưới.
📱
1. Tạo QR Thanh toán
App gọi API để tạo mã QR động, khách quét mã chuyển khoản thẳng vào tài khoản tòa nhà.
🔔
2. Webhook Gạch nợ
Gateway gửi thông báo tức thì về hệ thống của bạn sau khi ngân hàng xác nhận tiền về.
1 Tạo thanh toán QR — Quy trình chung

Ứng dụng của bạn gọi API này để yêu cầu Gateway sinh mã QR động. Khách hàng quét mã QR và chuyển khoản trực tiếp vào tài khoản ngân hàng của Merchant.

📱

API Tạo lệnh thanh toán mới

X-Api-Key + RSA Signature
POST /api/v1/payments/create

Headers bắt buộc

HeaderYêu cầuMô tả
Content-TypeBắt buộcapplication/json (Gateway chỉ nhận JSON thô).
X-Api-KeyBắt buộcAPI Key được cấp trên Portal.
X-TimestampBắt buộcUnix Timestamp (giây). Sai lệch tối đa 5 phút.
X-SignatureBắt buộcChữ ký RSA-SHA256 (Base64).

1. Request Body (JSON Interface)

TrườngKiểuYêu cầuMô tả
order_codestringBắt buộcMã đơn hàng duy nhất trong hệ thống của bạn.
amountdecimalBắt buộcSố tiền (VNĐ). Tối thiểu 1.000đ.
contentstringBắt buộcNội dung hiển thị khi khách quét mã QR.
📝
Lưu ý quan trọng: Hệ thống sử dụng Raw Request Body (chuỗi JSON thô, không có khoảng trắng thừa) để tính toán và xác thực chữ ký RSA. Bất kỳ lỗi định dạng nào (thừa một dấu cách, sai kiểu dữ liệu...) đều khiến chữ ký bị sai lệch.

Response (200 OK - Standard Snake Case):

{
  "payment_code": "PMT1715077421234",
  "order_code": "APP-2024-ABCD",
  "amount": 2500000.0,
  "qr_link": "https://img.vietqr.io/image/MB-123...png",
  "qr_content": "00020101021238580010A00000072701240006970422011004123456785204701...",
  "deep_link": "https://bankhub.aureus.vn/pay/PMT1715077421234"
}

Giải thích tham số Response:

TrườngKiểuMô tả
payment_codestringMã giao dịch chuyên biệt của hệ thống Aureus Gateway (dùng để đối soát hoặc tra cứu sau).
order_codestringMã đơn hàng gốc do hệ thống của Merchant/Partner đã truyền lên.
amountdecimalSố tiền thanh toán thực tế của giao dịch (VNĐ).
qr_linkstringĐường link trả về ảnh chứa mã QR động. Có thể gắn trực tiếp vào src của thẻ <img>.
qr_contentstringNội dung text thuần chuẩn VietQR (phục vụ cho việc hệ thống hoặc thiết bị POS tự sinh ảnh QR).
deep_linkstringLink chuyển hướng tự động (Intent). Dùng riêng cho nền tảng Mobile web/app nhằm gọi trực tiếp tới App ngân hàng và điền sẵn hóa đơn.
📱
Khuyến nghị cho App Mobile: Ưu tiên dùng deep_link thay vì hiển thị ảnh QR. Link này tự động mở App ngân hàng và điền sẵn 100% thông tin thanh toán cho người dùng.

🔐 Hướng dẫn sinh chữ ký X-Signature

Để đảm bảo tính toàn vẹn và xác thực, mỗi yêu cầu POST gửi đến Gateway cần đi kèm một chữ ký RSA-SHA256 trong Header X-Signature theo các bước sau:

BướcMô tả chi tiết
Bước 1Lấy toàn bộ nội dung Request Body (JSON thô) và giá trị X-Timestamp.
Bước 2Tạo chuỗi dữ liệu cần ký bằng cách nối chúng qua ký tự gạch đứng: {JSON_Body}|{Timestamp}.
Bước 3Sử dụng thuật toán RSA-SHA256Private Key để thực hiện ký chuỗi dữ liệu trên.
Bước 4Chuyển đổi dữ liệu sau khi ký sang định dạng Base64 String để gán vào header X-Signature.

Để tạo chữ ký X-Signature, đối tác cần dùng Khóa bí mật (Private Key) dạng XML được hệ thống cấp.

Mẫu mã nguồn thực hiện:

using System;
using System.Security.Cryptography;
using System.Text;

public class RsaSigner
{
    /// 
    /// Ký số RSA cho yêu cầu tạo thanh toán
    /// 
    /// Nội dung JSON của Request Body
    /// Giá trị X-Timestamp (số giây Unix)
    /// Khóa Private Key (dạng <RSAKeyValue>...</RSAKeyValue>)
    public static string SignRequest(string bodyJson, string timestamp, string privateKeyXml)
    {
        // 1. Tạo chuỗi dữ liệu để ký: {bodyJson}|{timestamp}
        string dataToSign = $"{bodyJson}|{timestamp}";
        byte[] dataBytes = Encoding.UTF8.GetBytes(dataToSign);

        using (var rsa = new RSACryptoServiceProvider())
        {
            // 2. Import Private Key
            rsa.FromXmlString(privateKeyXml);

            // 3. Ký dữ liệu bằng thuật toán SHA256
            byte[] signatureBytes = rsa.SignData(dataBytes, CryptoConfig.MapNameToOID("SHA256"));

            // 4. Trả về mã Base64 để đưa vào Header X-Signature
            return Convert.ToBase64String(signatureBytes);
        }
    }
}
import java.security.*;
import java.security.spec.*;
import java.util.Base64;

public class RsaSigner {
    // Ký số RSA SHA256 cho yêu cầu
    // - bodyJson: Nội dung Request Body
    // - timestamp: Unix Timestamp
    // - privateKey: RSA Private Key
    public static String signRequest(String bodyJson, String timestamp, String privateKey) throws Exception {
        String dataToSign = bodyJson + "|" + timestamp;
        Signature rsa = Signature.getInstance("SHA256withRSA");
        // Khởi tạo và ký dữ liệu
        rsa.initSign(getPrivateKey(privateKey));
        rsa.update(dataToSign.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(rsa.sign());
    }
}
class RsaSigner {
    // Ký số RSA cho yêu cầu thanh toán
    public static function signRequest($bodyJson, $timestamp, $privateKey) {
        $dataToSign = $bodyJson . "|" . $timestamp;
        openssl_sign($dataToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256);
        return base64_encode($signature);
    }
}
const crypto = require('crypto');

class RsaSigner {
    // Ký số RSA-SHA256
    static signRequest(bodyJson, timestamp, privateKey) {
        const dataToSign = `${bodyJson}|${timestamp}`;
        const signer = crypto.createSign('SHA256');
        signer.update(dataToSign);
        return signer.sign(privateKey, 'base64');
    }
}

2 Webhook Gạch nợ — Quy trình chung

Sau khi khách hàng chuyển khoản thành công, Gateway lập tức gửi một HTTP POST về NotifyUrl.

🔔

Thông tin Webhook (Callback)

POST từ Gateway → NotifyUrl

Headers gửi kèm Webhook

HeaderMô tả
Content-Typeapplication/json
X-SignatureMã băm HMAC-SHA256 để xác thực nguồn gốc gói tin.

Cấu trúc dữ liệu Webhook (Payload)

TrườngKiểuMô tả
merchant_idintID duy nhất của Merchant trên hệ thống.
order_codestringMã đơn hàng phía Merchant.
payment_codestringMã thanh toán duy nhất phía Gateway.
payment_idlongID nội bộ của giao dịch (Internal ID).
transaction_idstringMã tham chiếu ngân hàng (Provider ID).
amountdecimalSố tiền thực nhận (VNĐ).
statusstringPAID (Thành công) hoặc MISMATCH (Lệch tiền).
{
  "merchant_id": 105,
  "order_code": "APP-2024-ABCD",
  "payment_code": "PMT1715077421234",
  "payment_id": 12345,
  "transaction_id": "FT2413800881234",
  "amount": 2500000.0,
  "status": "PAID"
}

Công cụ tạo chuỗi rawBody thực tế (7 trường)

Cấu trúc Webhook thực tế gồm 7 trường. Hãy dùng công cụ này để tạo chuỗi test chữ ký.

{"merchant_id":105,"order_code":"DH12345","payment_code":"PMT123456","payment_id":999,"transaction_id":"FT88888","amount":50000,"status":"PAID"}
string rawBody = "{\"merchant_id\":105,\"order_code\":\"DH12345\",\"payment_code\":\"PMT123456\",\"payment_id\":999,\"transaction_id\":\"FT88888\",\"amount\":50000,\"status\":\"PAID\"}";

Mã nguồn tạo Raw Body lập trình

Dùng các hàm này để đảm bảo chuỗi JSON tạo ra luôn là chuỗi Minified (không khoảng trắng thừa), khớp với cơ chế ký của Gateway.

// Dùng Newtonsoft.Json với Formatting.None
var rawBody = JsonConvert.SerializeObject(yourModel, Formatting.None);
// Dùng json_encode (mặc định là minified)
$rawBody = json_encode($yourArray, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// Dùng Jackson ObjectMapper
ObjectMapper mapper = new ObjectMapper();
String rawBody = mapper.writeValueAsString(yourModel); // Mặc định là minified
// Dùng JSON.stringify không kèm tham số indent
const rawBody = JSON.stringify(yourModel);

🔐 Phần giải mã & Xác thực Webhook (HMAC-SHA256)

Để đảm bảo gói tin Webhook thực sự đến từ Aureus Gateway, đối tác cần dùng Secret Key để xác thực chữ ký HMAC-SHA256 gửi trong Header X-Signature.

BướcMô tả chi tiết
Bước 1 Lấy nội dung Raw Request Body.
C#: await Request.Content.ReadAsStringAsync();
PHP: file_get_contents('php://input');
NodeJS: req.body (với raw-body parser)
Bước 2Sử dụng Secret Key để tính toán mã băm HMAC-SHA256.
Bước 3So sánh mã băm vừa tính được với header X-Signature.

Mẫu mã nguồn xác thực:

using System.Security.Cryptography;
using System.Text;

public class WebhookVerifier {
    public static bool Verify(string rawBody, string secretKey, string headerSignature) {
        byte[] keyBytes = Encoding.UTF8.GetBytes(secretKey);
        using (var hmac = new HMACSHA256(keyBytes)) {
            byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody));
            string result = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            return result == headerSignature;
        }
    }
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class WebhookVerifier {
    public static boolean verify(String rawBody, String secretKey, String headerSig) throws Exception {
        Mac hmac = Mac.getInstance("HmacSHA256");
        hmac.init(new SecretKeySpec(secretKey.getBytes(), "HmacSHA256"));
        byte[] rawHmac = hmac.doFinal(rawBody.getBytes());
        // Chuyển sang Hex và so sánh
        return bytesToHex(rawHmac).equals(headerSig);
    }
}
class WebhookVerifier {
    public static function verify($rawBody, $secretKey, $headerSignature) {
        $signature = hash_hmac('sha256', $rawBody, $secretKey);
        return hash_equals($signature, $headerSignature);
    }
}
const crypto = require('crypto');

class WebhookVerifier {
    static verify(rawBody, secretKey, headerSignature) {
        const signature = crypto.createHmac('sha256', secretKey)
                                .update(rawBody)
                                .digest('hex');
        return signature === headerSignature;
    }
}
⚠️
Lưu ý về Idempotency: Gateway có thể gửi lại Webhook nhiều lần nếu không nhận được phản hồi HTTP 200. Bạn KHÔNG ĐƯỢC gạch nợ trùng lặp. Hãy luôn kiểm tra payment_code trong database xem đã xử lý thành công chưa trước khi thực hiện logic gạch nợ.

Tại sao Webhook báo lỗi hoặc sai Chữ ký?

Nếu bạn nhận được Webhook nhưng không thể gạch nợ thành công, hãy kiểm tra 2 vấn đề sau:

1. Lỗi "Unknown Property" (HTTP 400)

Nhiều framework (như Java, Go) sẽ báo lỗi nếu nhận được trường dữ liệu lạ (ví dụ payment_id).
Giải pháp: Cấu hình cho phép bỏ qua các trường không khai báo (Ignore Unknown Properties) trong Model của bạn.

2. Sai lệch chuỗi Raw Body

Tuyệt đối không tự dùng Model để build lại JSON nhằm Verify chữ ký. Chỉ cần sai một dấu cách hoặc thứ tự trường là HMAC sẽ sai.
Giải pháp: Đọc chuỗi thô trực tiếp từ stream của Request.

Danh sách 7 trường bắt buộc có trong Raw Body hiện tại:
merchant_id, order_code, payment_code, payment_id, transaction_id, amount, status

Best Practice: Bền vững hóa API Webhook

Để tránh việc hệ thống bị lỗi (HTTP 400/500) mỗi khi Gateway cập nhật hoặc bổ sung trường dữ liệu mới, đối tác cần tuân thủ nguyên tắc thiết kế sau:

  • KHÔNG dùng Class/Model cố định để nhận tham số trực tiếp tại hàm Webhook.
  • KHÔNG chặn các trường dữ liệu lạ (Unknown properties).
  • CHỈ nên quan tâm đến các trường cốt lõi bạn cần dùng cho logic gạch nợ.

Cấu trúc API Webhook khuyên dùng (C#):

// Thiết kế API "Mở" - Không sợ thay đổi tham số
[HttpPost]
public async Task<IHttpActionResult> ReceiveWebhook() 
{
    // 1. Nhận chuỗi thô (Luôn luôn thành công)
    string rawBody = await Request.Content.ReadAsStringAsync();
    
    // 2. Lấy chữ ký X-Signature từ Header để xác thực (Tùy framework)
    // - Với ASP.NET Web API:
    Request.Headers.TryGetValues("X-Signature", out IEnumerable<string> values);
    string signature = values?.FirstOrDefault() ?? "";
    
    // - Với ASP.NET Core: 
    // string signature = Request.Headers["X-Signature"].FirstOrDefault();
    
    // 3. Parse linh hoạt vào Dictionary
    var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(rawBody);
    
    // 4. Lấy dữ liệu cần thiết
    string orderCode = data.ContainsKey("order_code") ? data["order_code"].ToString() : "";
    string status = data.ContainsKey("status") ? data["status"].ToString() : "";
    
    // ... Kiểm tra chữ ký và gạch nợ ...
    return Ok();
}

🔧 Quy trình mở rộng

A. Lấy Token xác thực (Auth Login)

Dùng để lấy thẻ truy cập (JWT Token) phục vụ cho việc gọi các API quản trị cấp cao (Management API) không yêu cầu chữ ký RSA.

POST/api/v1/auth/login

Cấu trúc Request Body (JSON):

TrườngKiểuYêu cầuMô tả
emailstringBắt buộcTên tài khoản quản trị hệ thống.
passwordstringBắt buộcMật khẩu truy cập.

Ví dụ Request:

{
  "email": "admin@company.com",
  "password": "my-secure-password"
}

Ví dụ Response (200 OK):

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ik...",
  "refresh_token": "fb8b7c3d...",
  "role": "OWNER",
  "route": "/portal",
  "tenant_type": "EXTERNAL"
}
💡
Token sẽ hết hạn sau 60 phút. Kể từ lúc nhận Token, bạn truyền Token này vào các Request Headers tiếp theo thông qua khóa Authorization: Bearer {token}.

B. Đăng ký Merchant tự động

API cho phép các nền tảng ERP/App đối tác tự động khởi tạo điểm bán hoặc tòa nhà (Merchant) mà không cần thao tác tay thủ công trên Portal.

POST/api/v1/merchants/register

Headers bắt buộc:

HeaderMô tả
AuthorizationBearer Token nhận được từ API Xác thực hệ thống (Login).
Content-Typeapplication/json

Cấu trúc Request Body:

TrườngKiểuYêu cầuMô tả
namestringBắt buộcTên tòa nhà, điểm thu phí hoặc công ty (Vd: Tòa nhà The Landmark).
tenant_keystringBắt buộcMã định danh nội bộ (ID đối chiếu hệ thống của đối tác).
notify_urlstringBắt buộcURL Webhook để hệ thống gọi Push sang khi có tiền vào.

Ví dụ Response (200 OK):

{
  "success": true,
  "data": {
    "api_key": "GA_1A2B3C...",
    "secret_key": "7f8b9c...",
    "hosted_link_url": "https://bankhub.aureus.vn/link/...",
    "private_key": "<RSAKeyValue>...</RSAKeyValue>"
  },
  "message": "Merchant registered. Please open HostedLinkUrl to link bank account."
}
🔗
Sau khi đăng ký thành công, hãy lấy hosted_link_url mở Popup để khách hàng liên kết ngân hàng thực tế. Bạn phải lưu lại private_key an toàn ở phía Server vì hệ thống chỉ cung cấp duy nhất 1 lần!

C. Kiểm tra trạng thái giao dịch (Polling)

Trong trường hợp tín hiệu mạng chập chờn khiến Webhook không truyền tới được server của bạn, hoặc bạn cần đối soát độc lập, API này cho phép chủ động hỏi Gateway xem đơn hàng đã được thanh toán xong chưa.

GET/api/v1/payments/status?payment_code={code}
Tham số (Query)Yêu cầuMô tả
payment_codeBắt buộcMã giao dịch (payment_code) sinh ra khi chạy API Tạo lệnh thanh toán.

Ví dụ Response (200 OK):

{
  "status": "COMPLETED"
}

Danh sách trạng thái (Status):

Trạng tháiÝ nghĩa
COMPLETEDGiao dịch thành công trọn vẹn, tiền đã khớp vào tải khoản ngân hàng.
PENDINGĐang trong trạng thái treo, chờ người dùng mở App quét mã QR.
FAILEDGiao dịch thất bại hoặc đã bay QR quá hạn mà chưa có tiền vào.

D. Bảo mật nâng cao

Aureus sử dụng định dạng RSA Key dạng XML (.NET). Vui lòng chuyển đổi từ PEM sang XML khi cập nhật khóa lên hệ thống.

E. Ký quỹ SaaS

Phí giao dịch: 1.000 VNĐ cho mỗi giao dịch thành công. Thuế VAT 8% áp dụng một lần khi nạp tiền.

F. Bảng mã lỗi

Mã lỗiMô tả
AUTH_FAILEDSai API Key hoặc Token xác thực.
INVALID_SIGNATUREChữ ký RSA/HMAC không khớp hoặc sai định dạng.
INSUFFICIENT_BALANCESố dư quỹ SaaS không đủ.