API hủy đơn Collection Link
Nội dung Markdown đầy đủ của trang tài liệu.
# API hủy đơn Collection Link
`POST https://payment.pay2s.vn/v1/gateway/api/cancel`
API dành cho đối tác sử dụng accessKey/partnerCode/secretKey của API create.
API app `POST /api/v1/orders/cancel` dùng Bearer token giữ nguyên.
Áp dụng cho đơn tạo bằng [Collection Link V1](/api/collection-link) và
[Collection Link V2](/api/collection-link-v2). Chỉ hủy đơn chưa thanh toán.
## Request và chữ ký
Gửi JSON, tất cả trường là chuỗi:
```json
{
"accessKey": "YOUR_KEY",
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"orderId": "DEVd33eb1381791659971923e7ba11",
"requestId": "CANCEL17916599722809736db",
"requestType": "cancel",
"signature": "<SIGNATURE>"
}
```
- `orderId`: đúng orderId khách gửi lúc tạo đơn, không phải mã PAY2SJSC do Pay2S sinh.
- `requestId`: mã yêu cầu hủy do khách tạo, được trả lại để đối chiếu. Không phải requestId của lần create.
- `requestType`: bắt buộc `cancel`.
- Không gửi secretKey, extraData hoặc signatureVersion.
- Chuỗi ký nối đúng thứ tự dưới đây, không URL-encode, không thêm khoảng trắng:
```text
accessKey=ACCESS_KEY&orderId=ORDER_123&partnerCode=PARTNER_CODE&requestId=CANCEL_123&requestType=cancel
```
`signature = HMAC-SHA256(rawSignature, secretKey)` dạng hex.
Thực hiện ký từ backend; không đưa secretKey lên trình duyệt.
## Postman pre-request script
Đặt Environment: `accessKey`, `partnerCode`, `secretKey`, `orderId`.
Chọn Body → raw → JSON trước khi chạy:
```js
const CryptoJS = pm.require('npm:[email protected]');
const accessKey = pm.environment.get('accessKey');
const partnerCode = pm.environment.get('partnerCode');
const secretKey = pm.environment.get('secretKey');
const orderId = pm.environment.get('orderId');
if (![accessKey, partnerCode, secretKey, orderId].every(Boolean)) {
throw new Error('Thiếu accessKey, partnerCode, secretKey hoặc orderId');
}
const requestId = 'CANCEL-' + Date.now();
const requestType = 'cancel';
const raw = `accessKey=${accessKey}&orderId=${orderId}&partnerCode=${partnerCode}&requestId=${requestId}&requestType=${requestType}`;
const signature = CryptoJS.HmacSHA256(raw, secretKey).toString(CryptoJS.enc.Hex);
pm.request.headers.upsert({key: 'Content-Type', value: 'application/json'});
pm.request.body.update(JSON.stringify({accessKey, partnerCode, orderId, requestId, requestType, signature}));
```
## Kết quả
HTTP 200 khi hủy thành công hoặc đơn đã hủy trước đó:
```json
{
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"requestId": "CANCEL17916599722809736db",
"orderId": "DEVd33eb1381791659971923e7ba11",
"invoiceNumber": "PAY2SJSC5B7F84ACD116417A",
"status": "cancelled",
"resultCode": 0,
"message": "Đơn hàng đã được hủy thành công."
}
```
Lỗi trả `{"status":false,"message":"..."}`:
| HTTP | Ý nghĩa |
| --- | --- |
| 400 | Trường dữ liệu hoặc requestType không hợp lệ |
| 401 | Thông tin xác thực hoặc chữ ký sai |
| 404 | Không có đơn trong phạm vi khóa đang dùng |
| 409 | Đơn không còn pending, hoặc orderId trùng nhiều đơn |
| 429 | Quá nhiều yêu cầu từ cùng IP |
| 500 | Lỗi xử lý; có thể gọi lại và kiểm tra trạng thái |
Chỉ cập nhật `pending → cancelled`, không xóa đơn hoặc hoàn tiền. Gọi lại đơn đã
hủy trả thành công; không ghi lại trạng thái. Khóa cửa hàng chỉ truy cập đơn của
cửa hàng đó; khóa chủ tài khoản truy cập đơn không gắn cửa hàng, giống phạm vi
create. Nếu legacy tạo nhiều đơn cùng orderId với nội dung khác nhau, trả 409 để
tránh chọn nhầm. Nên dùng orderId duy nhất cho mỗi đơn.
Hủy đơn trên Pay2S không vô hiệu hóa khả năng chuyển khoản tới tài khoản ngân hàng.
API này không gọi hủy QR phía ngân hàng, không phát webhook hủy, không hủy hóa đơn
điện tử. Khách tích hợp nhận kết quả trực tiếp từ response.
## Request và response đối chiếu ngày 11/10/2026
Các mẫu dưới đây giữ cấu trúc trường và kiểu dữ liệu. Khóa, chữ ký, URL phiên đã được thay bằng placeholder; danh sách chỉ giữ tối đa hai phần tử và dữ liệu ảnh/PDF/XML dài được rút gọn. Không sao chép placeholder để gọi API thật.
### Hủy đơn hàng
<div class="doc-api-actions" data-doc-api-actions><a class="try-button" data-doc-playground href="/playground/?api=cancel-order">Thử API trong Playground ↗</a><a class="try-button experience-button" data-doc-experience href="/demos/?flow=payment&api=cancel-order">Trải nghiệm thanh toán & IPN ↗</a></div>
**Đã gọi API demo/UAT.**
`POST /v1/gateway/api/cancel`
Body:
```json
{
"accessKey": "[REDACTED]",
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"orderId": "DEVd33eb1381791659971923e7ba11",
"requestId": "CANCEL17916599722809736db",
"requestType": "cancel",
"signature": "<SIGNATURE>"
}
```
HTTP 200. Response:
```json
{
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"requestId": "CANCEL17916599722809736db",
"orderId": "DEVd33eb1381791659971923e7ba11",
"invoiceNumber": "PAY2SJSC5B7F84ACD116417A",
"status": "cancelled",
"resultCode": 0,
"message": "Đơn hàng đã được hủy thành công."
}
```