# 🎯 Hủy OneQR - Hủy QR động

## 📱 Tổng quan

Để hủy một mã QR động đã tạo, bạn gọi API **Cancel QR Code** của OneQR. Khi hủy QR, giao dịch sẽ không thể thanh toán được nữa.

---

## 🔌 Endpoint

```
POST http://api.pay2s.vn/api/{bank_code}/v1/qr/cancel
```

| Tham số | Ví dụ | Mô tả |
|--------|-------|-------|
| `{bank_code}` | `vcb` | Mã ngân hàng (vcb = Vietcombank, acb = ACB Bank, ...) |

---

## 📤 Request

### Headers

```json
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
```

> **Lưu ý**: `<token>` là Bearer token được cấp từ Pay2S

### Body Parameters (JSON)

| Tham số | Loại | Bắt buộc | Mô tả |
|--------|------|---------|-------|
| `orderId` | String | ✓ | ID đơn hàng trong hệ thống của bạn |
| `traceNumber` | String | ✓ | Mã định danh QR được trả về từ API tạo QR |

### ✅ Ví dụ Request (cURL)

```bash
curl --location 'http://api.pay2s.vn/api/vcb/v1/qr/cancel' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_BEARER_TOKEN' \
  --data '{
    "orderId": "123456",
    "traceNumber": "QRPP2S26159194HHKZE"
  }'
```

---

## 📥 Response

### ✅ Thành công (200 OK)

```json
{
  "success": true,
  "message": "QR transaction cancelled successfully",
  "data": {
    "transactionId": "da58fde344a1e0c9280207e1143c6f40",
    "orderId": "DEMO17916599748852455",
    "status": "cancelled",
    "cancelResponse": {
      "code": "00",
      "message": "Succeed",
      "data": {
        "subCode": "00",
        "subMessage": "Succeed",
        "requestId": "0368e7f9-902c-4bd3-adb6-c5766e328856",
        "checksum": "3E808EBC44F716562EE1290FF9C2A5F0",
        "vcbResponse": {
          "code": "00",
          "message": "Succeed",
          "requestId": "0368e7f9-902c-4bd3-adb6-c5766e328856",
          "subCode": "00",
          "subMessage": "Succeed",
          "serverTime": "15:46:33 08/06/2026",
          "nodeOut": "10.1.60.186",
          "nodeIn": "",
          "operation": "CancelInvoice",
          "success": true,
          "idQrCode": "31928425",
          "checksum": "25B3AFCB0ABA8EF2DADE66255156653C"
        },
        "success": true
      }
    }
  }
}
```

| Trường | Mô tả |
|--------|-------|
| `success` | Trạng thái: `true` = Hủy thành công |
| `message` | Mô tả: `QR transaction cancelled successfully` |
| `status` | Trạng thái: `cancelled` = QR đã bị hủy |
| `transactionId` | Mã giao dịch duy nhất |
| `orderId` | ID đơn hàng được hủy |


---

## 💻 Code mẫu - Hủy QR

::: code-group

```php [PHP]
<?php
$bearerToken = "YOUR_BEARER_TOKEN";
$url = "http://api.pay2s.vn/api/vcb/v1/qr/cancel";

$data = [
    'orderId' => "123456",
    'traceNumber' => "QRPP2S26159194HHKZE"
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer " . $bearerToken,
    "Content-Type: application/json"
]);

$response = curl_exec($ch);
$result = json_decode($response, true);

if ($result['success'] === true) {
    $orderId = $result['data']['orderId'];
    $status = $result['data']['status'];
    echo "Order " . $orderId . " cancelled with status: " . $status;
} else {
    echo "Error: " . $result['message'];
}

curl_close($ch);
?>
```

```javascript [Node.js]
const axios = require('axios');

const bearerToken = "YOUR_BEARER_TOKEN";

const payload = {
  orderId: "123456",
  traceNumber: "QRPP2S26159194HHKZE"
};

axios.post("http://api.pay2s.vn/api/vcb/v1/qr/cancel", payload, {
  headers: {
    "Authorization": `Bearer ${bearerToken}`,
    "Content-Type": "application/json"
  }
})
  .then(response => {
    if (response.data.success === true) {
      console.log("Order cancelled:", response.data.data.orderId);
      console.log("Status:", response.data.data.status);
    } else {
      console.error("Error:", response.data.message);
    }
  })
  .catch(err => console.error(err));
```

```python [Python]
import requests
import json

bearer_token = "YOUR_BEARER_TOKEN"

payload = {
    'orderId': "123456",
    'traceNumber': "QRPP2S26159194HHKZE"
}

headers = {
    "Authorization": f"Bearer {bearer_token}",
    "Content-Type": "application/json"
}

response = requests.post("http://api.pay2s.vn/api/vcb/v1/qr/cancel", 
                         data=json.dumps(payload), 
                         headers=headers)
result = response.json()

if result['success'] == True:
    print("Order cancelled:", result['data']['orderId'])
    print("Status:", result['data']['status'])
else:
    print("Error:", result['message'])
```

:::

---

## ⚡ Best Practices

1. **Validate parameters**: Kiểm tra `orderId` và `traceNumber` không rỗng
2. **Error handling**: Kiểm tra `success` trước xử lý `data`
3. **Timing**: Chỉ hủy QR khi khách hàng từ chối thanh toán
4. **Status check**: Không hủy QR đã được thanh toán
5. **Retry logic**: Nếu request fail, retry sau 2-3 giây
6. **Update database**: Cập nhật status = 'cancelled' trong database
7. **Trace number**: Lưu trữ traceNumber để audit log
8. **User notification**: Thông báo cho khách hàng khi QR bị hủy
9. **Timeout handling**: Đặt timeout hợp lý (30-60 giây)
10. **Logging**: Log toàn bộ cancel request/response

---

## 📌 Luồng hủy QR

```
1. Khách từ chối thanh toán → 2. Hệ thống gọi API hủy QR → 
3. Gửi orderId + traceNumber → 4. Pay2S xác nhận hủy → 
5. QR không thể scan nữa → 6. Cập nhật status = cancelled → 
7. Thông báo khách hàng
```

---

> **Tiếp theo**: Xem [Tạo OneQR](/oneqr/khoi-tao-oneqr) để tạo QR mới hoặc [Webhook](/webhook/tai-lieu-ky-thuat.html) để nhận thông báo thanh toán


## 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.

### OneQR · Hủy QR

<div class="doc-api-actions" data-doc-api-actions><a class="try-button" data-doc-playground href="/playground/?api=oneqr-cancel">Thử API trong Playground ↗</a><a class="try-button experience-button" data-doc-experience hidden>Trải nghiệm ↗</a></div>

**Mô phỏng; chưa xác minh với API nhà cung cấp.** Giữ mô phỏng theo cấu hình demo. Chưa có API OneQR thật để xác minh lại hợp đồng nhà cung cấp.

`POST /api/{bank_code}/v1/qr/cancel`

Body:

```json
{
  "transactionId": "da58fde344a1e0c9280207e1143c6f40"
}
```

HTTP 200. Response:

```json
{
  "success": true,
  "message": "QR transaction cancelled successfully",
  "data": {
    "transactionId": "da58fde344a1e0c9280207e1143c6f40",
    "orderId": "DEMO17916599748852455",
    "status": "cancelled",
    "cancelResponse": {
      "code": "00",
      "message": "Succeed",
      "data": {
        "subCode": "00",
        "subMessage": "Succeed",
        "requestId": "0368e7f9-902c-4bd3-adb6-c5766e328856",
        "checksum": "3E808EBC44F716562EE1290FF9C2A5F0",
        "vcbResponse": {
          "code": "00",
          "message": "Succeed",
          "requestId": "0368e7f9-902c-4bd3-adb6-c5766e328856",
          "subCode": "00",
          "subMessage": "Succeed",
          "serverTime": "15:46:33 08/06/2026",
          "nodeOut": "10.1.60.186",
          "nodeIn": "",
          "operation": "CancelInvoice",
          "success": true,
          "idQrCode": "31928425",
          "checksum": "25B3AFCB0ABA8EF2DADE66255156653C"
        },
        "success": true
      }
    }
  }
}
```
