THÔNG SỐ KỸ THUẬT API HOÀN TIỀN GLODIPAY
PHIÊN BẢN 2.0.0
Mục lục
- Giới thiệu
- Điểm cuối (Endpoints)
- Chữ ký (Signature)
- REFUND API (API Hoàn tiền)
- REFUND QUERY (Truy vấn hoàn tiền)
- REFUND NOTIFICATION (Thông báo hoàn tiền)
- Phụ lục
- Giá trị trạng thái hoàn tiền
- Mã trạng thái
- Ví dụ mã nguồn
- PHP
- Node.js
Giới thiệu
Tài liệu này mô tả GLODIPAY Refund API v2, cho phép người bán khởi tạo hoàn tiền cho các giao dịch đã hoàn tất, truy vấn trạng thái hoàn tiền và nhận thông báo hoàn tiền qua IPN.
Các điểm chính:
- Hoàn tiền có thể là toàn bộ hoặc một phần (tùy thuộc vào các hạn chế của từng nhà cung cấp -- một số nhà cung cấp chỉ hỗ trợ hoàn tiền toàn bộ số tiền).
- Việc hoàn tiền có thể được xử lý tự động ngay lập tức hoặc tạm dừng để phê duyệt thủ công, tùy thuộc vào cấu hình của người bán.
notificationUrltừ giao dịch gốc được sử dụng cho các thông báo IPN hoàn tiền.- Số tiền hoàn lại tối đa cho mỗi lần hoàn tiền là:
paidAmounttrừ đi tổng số tiền đã được hoàn lại trước đó cho giao dịch.
Điểm cuối (Endpoints)
| Thử nghiệm | Sẽ được cung cấp |
| Sản xuất | Lấy từ trang API Keys trong Dashboard của người bán |
Chữ ký (Signature)
Tất cả các yêu cầu gửi đến GLODIPAY phải bao gồm trường signature. Tất cả các phản hồi và payload IPN từ GLODIPAY cũng bao gồm trường signature để xác minh tính xác thực.
Chữ ký sử dụng RSA với MD5 (md5WithRSAEncryption).
Tạo chữ ký (Người bán -> GLODIPAY)
Sử dụng khóa riêng của người bán (có sẵn trong Dashboard của người bán).
Các bước:
- Thu thập tất cả các tham số yêu cầu ngoại trừ
signaturedưới dạng đối tượng khóa-giá trị phẳng. - Sắp xếp chỉ các khóa cấp cao nhất theo thứ tự tăng dần tự nhiên (
SORT_NATURALtrong PHP /localeComparevới{ numeric: true }trong JavaScript). Không sắp xếp các khóa bên trong các đối tượng lồng nhau. - Cắt bỏ khoảng trắng ở đầu và cuối tất cả các giá trị chuỗi một cách đệ quy.
- Tuần tự hóa thành một chuỗi JSON.
- Ký bằng
openssl_sign(..., 'md5WithRSAEncryption'). - Mã hóa Base64 đầu ra nhị phân.
Xác minh chữ ký (GLODIPAY -> Người bán)
Sử dụng khóa công khai của GLODIPAY (có sẵn từ Cổng thông tin).
Các bước:
- Loại bỏ
signaturekhỏi payload. - Sắp xếp chỉ các khóa cấp cao nhất theo thứ tự tăng dần tự nhiên. Không sắp xếp các khóa bên trong các đối tượng lồng nhau.
- Cắt bỏ khoảng trắng ở đầu và cuối tất cả các giá trị chuỗi một cách đệ quy.
- Tuần tự hóa thành một chuỗi JSON.
- Xác minh bằng
openssl_verify(..., base64_decode($signature), $publicKey, 'md5WithRSAEncryption'). - Giá trị trả về
1= hợp lệ.
Lưu ý (Node.js): Chuyển đổi tất cả các giá trị số thành chu ỗi trước khi sắp xếp/tuần tự hóa. Thoát các dấu gạch chéo xuôi trong chuỗi JSON: .replace(///g, '\/').
REFUND API (API Hoàn tiền)
Khởi tạo hoàn tiền cho một giao dịch đã hoàn tất.
Endpoint: POST /v2/refund
Phương thức: POST
Content-Type: application/json
Yêu cầu (Request)
| transactionId | String (ULID) | M | ID giao dịch nhận được từ IPN thanh toán hoặc truy vấn giao dịch |
| amount | Float | M | Số tiền hoàn lại. Tối thiểu: 0.10. Tối đa: số tiền còn lại có thể hoàn trả (paidAmount trừ đi số tiền đã hoàn trả) |
| reason | String(max:1000) | O | Mô tả ngắn gọn lý do hoàn tiền |
| signature | String(max:750) | M | Chữ ký RSA-MD5 |
Lưu ý: Một số nhà cung cấp nhất định (ví dụ: PayAgency, SmartPay, ClisaPay, FinvyPay, WPay) chỉ hỗ trợ hoàn tiền toàn bộ số tiền. Hệ thống sẽ thực thi số tiền hoàn lại tối thiểu tương ứng.
Phản hồi (Response)
Content-Type: application/json
| status | String | success |
| message | String | Thông báo dễ hiểu cho người dùng |
| data | JSON | Chi tiết hoàn tiền. Xem đối tượng data bên dưới |
đối tượng data:
| refundId | String (ULID) | ID hoàn tiền của GLODIPAY |
| refundNumber | String | Số hoàn tiền dễ hiểu của GLODIPAY |
Hoàn tiền đã được tạo và xử lý ngay lập tức (đã bật tự động hoàn tiền):
{
"status": "success",
"message": null,
"data": {
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"refundNumber": "2507-1751420414"
}
}
Hoàn tiền đã được tạo và đang chờ phê duyệt thủ công:
{
"status": "success",
"message": "Refund created and waiting for approval.",
"data": {
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"refundNumber": "2507-1751420414"
}
}
Lỗi xác thực (HTTP 422):
{
"status": "error",
"message": "Invalid request data.",
"errors": [
{
"field": "amount",
"message": ["The amount must be between 0.1 and 100."]
}
]
}
REFUND QUERY (Truy vấn hoàn tiền)
Truy vấn trạng thái mới nhất của một khoản hoàn tiền.
Endpoint: POST /v2/refund/query
Phương thức: POST
Content-Type: application/json
Yêu cầu (Request)
| refundId | String (ULID) | M | ID hoàn tiền của GLODIPAY (từ phản hồi Refund API hoặc Refund IPN) |
| signature | String(max:750) | M | Chữ ký RSA-MD5 |
Phản hồi (Response)
Content-Type: application/json
| status | String | success |
| message | String | Thông báo dễ hiểu cho người dùng |
| data | JSON | Đối tượng chi tiết hoàn tiền. Xem các trường bên dưới |
Các trường trong đối tượng data:
| transactionId | String | ID giao dịch gốc của GLODIPAY |
| ref | String | orderRef của người bán |
| refundId | String | ID hoàn tiền của GLODIPAY |
| currency | String | Mã tiền tệ ISO 4217 |
| refundAmount | Float | Số tiền hoàn lại |
| status | String | Trạng thái hoàn tiền. Xem phần Giá trị trạng thái hoàn tiền |
| statusCode | Number | Mã trạng thái số. Xem phần Mã trạng thái |
| metadata | JSON | Các cặp khóa-giá trị từ phiên thanh toán gốc |
| reason | String | Lý do hoàn tiền |
| message | String | Thông báo trạng thái dễ hiểu |
| originalRefundCreatedAt | ISO 8601 datetime | Thời gian tạo hoàn tiền tại PSP |
| refundCreatedAt | ISO 8601 datetime | Thời gian tạo hoàn tiền trong hệ thống GLODIPAY |
| transactionCreatedAt | ISO 8601 datetime | Thời gian tạo giao dịch gốc |
| signature | String | Chữ ký RSA-MD5 -- xác minh bằng khóa công khai của GLODIPAY |
Ví dụ:
{
"status": "success",
"message": "",
"data": {
"transactionId": "01jza90dy6w82dfrrqvadn5vs4",
"ref": "ORDER-001",
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"currency": "USD",
"refundAmount": 50.00,
"status": "refund_successful",
"statusCode": 11,
"metadata": { "orderId": "12345" },
"reason": "Customer request",
"message": null,
"originalRefundCreatedAt": "2026-04-14T11:00:00+00:00",
"refundCreatedAt": "2026-04-14T11:00:01+00:00",
"transactionCreatedAt": "2026-04-14T10:00:00+00:00",
"signature": "base64-encoded-rsa-signature"
}
}
REFUND NOTIFICATION (Thông báo hoàn tiền)
GLODIPAY gửi một HTTP POST đến notificationUrl của giao dịch gốc khi trạng thái hoàn tiền thay đổi.
Phương thức: POST
Content-Type: application/json
Chính sách gửi lại (Retry policy): Nếu máy chủ của bạn không trả về {"returnCode":"100"} trong vòng 30 giây, GLODIPAY sẽ thử gửi lại.
Payload
| transactionId | String | M | ID giao dịch gốc của GLODIPAY |
| ref | String | M | orderRef của người bán |
| refundId | String | M | ID hoàn tiền của GLODIPAY |
| currency | String | M | Mã tiền tệ ISO 4217 |
| refundAmount | Float | M | Số tiền hoàn lại |
| status | String | M | Trạng thái hoàn tiền. Xem phần Giá trị trạng thái hoàn tiền |
| statusCode | Number | M | Mã trạng thái số. Xem phần Mã trạng thái |
| metadata | JSON | O | Các cặp khóa-giá trị từ phiên thanh toán gốc |
| reason | String | O | Lý do hoàn tiền |
| message | String | O | Thông báo trạng thái dễ hiểu |
| originalRefundCreatedAt | ISO 8601 datetime | M | Thời gian tạo hoàn tiền tại PSP |
| refundCreatedAt | ISO 8601 datetime | M | Thời gian tạo hoàn tiền trong hệ thống GLODIPAY |
| transactionCreatedAt | ISO 8601 datetime | M | Thời gian tạo giao dịch gốc |
| signature | String | M | Chữ ký RSA-MD5 -- xác minh bằng khóa công khai của GLODIPAY |
Ví dụ Payload IPN:
{
"transactionId": "01jza90dy6w82dfrrqvadn5vs4",
"ref": "ORDER-001",
"refundId": "01jzabk09xc4pbgwe8hyg4cwbf",
"currency": "USD",
"refundAmount": 50.00,
"status": "refund_successful",
"statusCode": 11,
"metadata": { "orderId": "12345" },
"reason": "Customer request",
"message": null,
"originalRefundCreatedAt": "2026-04-14T11:00:00+00:00",
"refundCreatedAt": "2026-04-14T11:00:01+00:00",
"transactionCreatedAt": "2026-04-14T10:00:00+00:00",
"signature": "base64-encoded-rsa-signature"
}
Phản hồi (Người bán -> GLODIPAY)
Máy chủ của bạn phải phản hồi trong vòng 30 giây:
{
"returnCode": "100",
"description": "Received"
}
| returnCode | String | R | Phải là "100" để xác nhận đã nhận |
| description | String(1,1500) | O | Mô tả tùy chọn |
Phụ lục
Giá trị trạng thái hoàn tiền (Refund Status Values)
Giá trị chuỗi được trả về trong trường status của payload IPN hoàn tiền và các phản hồi truy vấn hoàn tiền.
| refund_initiated | Yêu cầu hoàn tiền đã khởi tạo |
| refund_under_review | Hoàn tiền đang xem xét |
| refund_successful | Hoàn tiền hoàn tất thành công |
| refund_failed | Hoàn tiền thất bại |
| refund_partially_successful | Hoàn tiền một phần thành công |
| refund_partially_failed | Hoàn tiền một phần thất bại |
| void_initiated | Việc hủy bỏ đã khởi tạo |
| void_under_review | Việc hủy bỏ đang xem xét |
| void_successful | Việc hủy bỏ hoàn tất thành công |
| void_failed | Việc hủy bỏ thất bại |
| void_partially_successful | Việc hủy bỏ một phần thành công |
| void_partially_failed | Việc hủy bỏ một phần thất bại |
Mã trạng thái (Status Codes)
Mã số trong trường statusCode của payload IPN hoàn tiền và các phản hồi truy vấn hoàn tiền.
| 8 | refund_initiated | Hoàn tiền đã khởi tạo |
| 9 | refund_failed | Hoàn tiền thất bại |
| 10 | refund_under_review | Hoàn tiền đang xem xét |
| 11 | refund_successful | Hoàn tiền thành công |
| 12 | refund_partially_failed | Hoàn tiền một phần thất bại |
| 13 | refund_partially_successful | Hoàn tiền một phần thành công |
| 18 | void_initiated | Void đã khởi tạo |
| 19 | void_under_review | Void đang xem xét |
| 20 | void_successful | Void thành công |
| 21 | void_failed | Void thất bại |
| 22 | void_partially_successful | Void một phần thành công |
| 23 | void_partially_failed | Void một phần thất bại |
Ví dụ mã nguồn
PHP
<?php
function generateSignature(array $data): string
{
$privateKey = openssl_pkey_get_private("-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END PRIVATE KEY-----
");
ksort($data, SORT_NATURAL);
array_walk_recursive(
$data,
static function (&$field) {
$field = trim($field);
}
);
openssl_sign(json_encode($data), $signature, $privateKey, 'md5WithRSAEncryption');
return base64_encode($signature);
}
function verifySignature(array $data): bool
{
$publicKey = openssl_pkey_get_public("-----BEGIN PUBLIC KEY-----
YOUR_GLODIPAY_PUBLIC_KEY_HERE
-----END PUBLIC KEY-----
");
$dataWithoutSignature = array_filter($data, static function ($key) {
return $key !== 'signature';
}, ARRAY_FILTER_USE_KEY);
$signature = $data['signature'];
ksort($dataWithoutSignature, SORT_NATURAL);
array_walk_recursive(
$dataWithoutSignature,
static function (&$field) {
$field = trim($field);
}
);
$result = openssl_verify(
json_encode($dataWithoutSignature),
base64_decode($signature),
$publicKey,
'md5WithRSAEncryption'
);
return $result === 1;
}
// Ví dụ: khởi tạo hoàn tiền
$payload = [
'transactionId' => '01jza90dy6w82dfrrqvadn5vs4',
'amount' => '50.00',
'reason' => 'Customer request',
];
$payload['signature'] = generateSignature($payload);
$ch = curl_init('https://payment-sandbox.gpayprocessing.com/v2/refund');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
echo 'Refund ID: ' . $result['data']['refundId'];
Node.js
// Save as script.mjs and run: node script.mjs
import { createSign } from 'crypto';
import https from 'https';
const PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END PRIVATE KEY-----`;
function phpCast(v) {
if (typeof v === 'number') return String(v);
if (typeof v === 'boolean') return v ? '1' : '';
if (typeof v === 'string') return v.trim();
if (Array.isArray(v)) return v.map(phpCast);
if (v && typeof v === 'object') return Object.fromEntries(Object.entries(v).map(([k, val]) => [k, phpCast(val)]));
return v;
}
function generateSignature(data) {
const sorted = {};
Object.keys(data)
.filter(k => k !== 'signature')
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }))
.forEach(k => { sorted[k] = data[k]; });
const canonical = JSON.stringify(phpCast(sorted))
.replace(/\//g, '\\/')
.replace(/[\u0080-\uffff]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'));
const sign = createSign('md5WithRSAEncryption');
sign.update(canonical);
return sign.sign(PRIVATE_KEY, 'base64');
}
// Ví dụ: khởi tạo hoàn tiền
const payload = {
transactionId: '01jza90dy6w82dfrrqvadn5vs4',
amount: '50.00',
reason: 'Customer request',
};
payload.signature = generateSignature(payload);
const postData = JSON.stringify(payload);
const options = {
hostname: 'payment-sandbox.gpayprocessing.com',
path: '/v2/refund',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
},
};
const req = https.request(options, (res) => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
const result = JSON.parse(body);
console.log('Refund ID:', result.data.refundId);
});
});
req.on('error', console.error);
req.write(postData);
req.end();