Collection Link V2 – Tạo đơn có dữ liệu HĐĐT
Nội dung Markdown đầy đủ của trang tài liệu.
# Collection Link V2 – Tạo đơn có dữ liệu HĐĐT
Collection Link V2 dùng để tạo liên kết thanh toán và gửi kèm thông tin cần thiết để Pay2S lập hóa đơn điện tử sau khi đơn được thanh toán thành công.
Đã có [SDK Node.js/TypeScript và PHP](/api/collection-link-v2-sdk) để đóng gói chữ ký V2, metadata và xác thực IPN. Trang SDK có file tải, cách cài đặt và hướng dẫn dành cho AI coding assistant tích hợp đầy đủ luồng thanh toán.
::: info V2 không đổi endpoint
Endpoint vẫn là `https://payment.pay2s.vn/v1/gateway/api/create`. V2 được nhận diện bằng `signatureVersion=2` và metadata Base64 trong `extraData`.
:::
## Luồng xử lý
Với tài khoản và ngân hàng demo, có thể dùng [API mô phỏng thanh toán](/api/simulate-payment) để kiểm thử tự động sau bước tạo đơn.
Để hủy đơn chưa thanh toán, xem [API hủy đơn hàng](/api/cancel-order). API hủy dùng cùng bộ khóa với API create, với chuỗi ký riêng cho thao tác `cancel`.
1. Website tạo Collection Link V2, kèm người mua và hàng hóa.
2. Pay2S tự sinh `orderInfo` chuẩn, dùng mã này trong QR và trả về cùng `payUrl`.
3. Khách hàng thanh toán.
4. Khi thanh toán thành công, Pay2S gửi IPN đến `ipnUrl` và ghi nhận yêu cầu HĐĐT.
5. Đơn xuất hiện trong **Hàng chờ**, hoặc được tự tạo nháp/phát hành theo cấu hình HĐĐT của tài khoản.
Tạo Collection Link thành công **không đồng nghĩa hóa đơn đã được phát hành**.
## Điều kiện sử dụng HĐĐT
Tài khoản Pay2S cần:
- Có kết nối nhà cung cấp HĐĐT đang bật và hoạt động.
- Có mẫu hóa đơn mặc định, hoặc cửa hàng tương ứng đã được gán mẫu.
- Bật tự động hóa nếu muốn tự tạo nháp hoặc tự phát hành.
- Gửi `invoiceOptions.requested=true` trong đơn cần xuất hóa đơn.
Nếu không sử dụng Cửa hàng, Pay2S dùng kết nối và mẫu mặc định của tài khoản.
## HTTP Request
- **URL:** `https://payment.pay2s.vn/v1/gateway/api/create`
- **Method:** `POST`
- **Content-Type:** `application/json; charset=UTF-8`
## Toàn bộ tham số Request
| Trường | Kiểu dữ liệu | Bắt buộc | Quy định và ý nghĩa |
|---|---|---:|---|
| `accessKey` | String | Có | Access Key của tài khoản Pay2S. Phải thuộc cùng tài khoản với `partnerCode` và `secretKey` dùng để ký. |
| `partnerCode` | String | Có | Mã đối tác do Pay2S cấp. Lấy trong phần cấu hình tích hợp của tài khoản. |
| `partnerName` | String | Không | Tên website, ứng dụng hoặc doanh nghiệp tạo đơn; tối đa 180 ký tự. |
| `requestId` | String | Có | Mã duy nhất của mỗi lần gọi API; tối đa 80 ký tự. Chỉ dùng chữ, số và các ký tự `.`, `_`, `:`, `-`. Không tái sử dụng cho request khác. |
| `amount` | Number hoặc String số | Có | Số tiền cần thanh toán bằng VND, không âm. Phải bằng tổng thanh toán của các dòng trong `extraData`. |
| `orderId` | String | Có | Mã đơn hàng duy nhất tại hệ thống của đối tác; tối đa 80 ký tự. Chỉ dùng chữ, số và `.`, `_`, `:`, `-`. |
| `orderInfo` | String | Có | Thông tin tham chiếu do đối tác gửi và dùng khi tạo chữ ký request. Với V2, Pay2S sẽ thay bằng một mã `P2S...` duy nhất trước khi tạo QR; hãy lấy `orderInfo` thực tế từ response. |
| `orderType` | String | Có | Loại đơn hàng. Gửi cùng giá trị với `requestType`, hiện dùng `pay2s`. |
| `bankAccounts` | Array | Có | Danh sách từ 1 đến 10 tài khoản ngân hàng nhận tiền đã liên kết với tài khoản Pay2S. |
| `redirectUrl` | String URL | Có | URL HTTP/HTTPS chuyển khách hàng về sau khi thanh toán. Tối đa 500 ký tự. |
| `ipnUrl` | String URL | Có | URL HTTP/HTTPS trên backend của đối tác dùng nhận kết quả thanh toán. Tối đa 500 ký tự. |
| `requestType` | String | Có | Loại yêu cầu, hiện dùng `pay2s`. Trường này nằm trong chuỗi ký. |
| `signatureVersion` | String | Có | Luôn gửi `"2"` khi sử dụng metadata hóa đơn điện tử. |
| `extraData` | String Base64 | Có | JSON UTF-8 chứa `invoiceType`, `customerInfo`, `items` và `invoiceOptions`, sau đó mã hóa Base64 đúng một lần. Tối đa 32 KB. |
| `signature` | String | Có | HMAC SHA-256 của chuỗi ký V2 bằng `secretKey`, kết quả hex chữ thường. |
| `lang` | String | Không | Ngôn ngữ giao diện thanh toán, ví dụ `vi`; nếu bỏ trống Pay2S dùng ngôn ngữ mặc định của tài khoản. |
### Cấu trúc `bankAccounts`
Mỗi phần tử trong `bankAccounts` gồm:
| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---:|---|
| `account_number` | String | Có | Số tài khoản hoặc số tài khoản định danh đã liên kết trên Pay2S, từ 4 đến 40 ký tự. |
| `bank_id` | String | Có | Mã ngân hàng, ví dụ `ACB`, `VCB`, `MBB`; từ 2 đến 20 chữ hoặc số. |
Ví dụ:
```json
[
{
"account_number": "P2S99999999",
"bank_id": "ACB"
}
]
```
::: warning Không gửi Secret Key trong Request
`secretKey` chỉ dùng tại backend của đối tác để tạo chữ ký. Tuyệt đối không đưa trường này vào payload hoặc mã JavaScript chạy trên trình duyệt.
:::
## Cấu trúc metadata trước khi Base64
```json
{
"invoiceType": "vat",
"customerInfo": {
"buyerContactName": "NGUYỄN VĂN A",
"buyerCompanyName": "CÔNG TY TNHH ABC",
"taxCode": "0312345678",
"citizenId": "",
"address": "123 Nguyễn Huệ, TP.HCM",
"email": "[email protected]",
"phone": "0901234567"
},
"items": [
{
"itemCode": "DV001",
"itemGroupCode": "API_GROUP_SOFTWARE",
"externalItemId": "ORDER_LINE_001",
"sourceProductName": "Gói phần mềm Pro",
"itemName": "Dịch vụ phần mềm",
"unit": "Tháng",
"quantity": 1,
"unitPrice": 100000,
"taxRate": 10
}
],
"invoiceOptions": {
"requested": true,
"buyerNotTakingInvoice": false,
"paymentMethod": "Chuyển khoản",
"note": "",
"source": "api"
}
}
```
Metadata gồm bốn nhóm:
| Nhóm | Bắt buộc | Ý nghĩa |
|---|---:|---|
| `invoiceType` | Có | Dùng `vat` cho đơn có yêu cầu HĐĐT; `regular` cho đơn thông thường; `none` khi không sử dụng hóa đơn |
| `customerInfo` | Có | Thông tin cá nhân hoặc doanh nghiệp mua hàng |
| `items` | Có | Danh sách tối đa 50 dòng hàng hóa, dịch vụ |
| `invoiceOptions` | Có | Yêu cầu xuất hóa đơn, nguồn tích hợp, hình thức thanh toán và ghi chú |
Để đơn xuất hiện trong luồng HĐĐT, sử dụng `invoiceType="vat"` đồng thời đặt `invoiceOptions.requested=true`.
`invoiceType` trong API tạo thanh toán chỉ thể hiện đơn có tham gia luồng HĐĐT hay không. Không gửi `invoiceType="sales"`. Pay2S xác định **hóa đơn GTGT** hay **hóa đơn bán hàng** từ mẫu hóa đơn đã được gán cho cửa hàng hoặc kết nối.
## Thông tin người mua
| Trường | Mô tả |
|---|---|
| `buyerContactName` | Họ tên người trực tiếp mua hàng |
| `buyerCompanyName` | Tên doanh nghiệp hoặc tổ chức |
| `taxCode` | Mã số thuế của đơn vị mua hàng |
| `citizenId` | CCCD của người mua nếu cần |
| `address` | Địa chỉ đầy đủ; không chỉ gửi mã quốc gia như `VN` |
| `email` | Email nhận hóa đơn |
| `phone` | Số điện thoại người mua |
Thông tin hợp lệ được xác định như sau:
- **Cá nhân:** có họ tên và địa chỉ.
- **Doanh nghiệp:** có tên đơn vị, mã số thuế và địa chỉ.
Nếu không đủ thông tin, Pay2S sử dụng **Bán cho người tiêu dùng** để tránh phát hành thông tin người mua không đầy đủ.
### Tra cứu mã số thuế và thứ tự ưu tiên
Website có thể gọi [API tra cứu mã số thuế](/hoa-don-dien-tu/api-tra-cuu-ma-so-thue) từ backend để điền `customerInfo.taxCode`, `buyerCompanyName`, `address` trước khi tạo đơn. API tra cứu dùng header `pay2s-token = Base64(Secret Key)`; không dùng chữ ký Collection Link V2 cho endpoint này.
Nếu tài khoản đã bật **Tự động kiểm tra mã số thuế**, Pay2S kiểm tra `taxCode` trước khi tự động tạo nháp hoặc phát hành:
- Chỉ trạng thái `00` – **NNT đang hoạt động, đã được cấp MST** – được tiếp tục tự động.
- Kết quả tra cứu chỉ bổ sung tên đơn vị và địa chỉ còn thiếu.
- Dữ liệu hợp lệ đã gửi trong `customerInfo` được ưu tiên.
- Nếu bật dùng tên người thanh toán, tên chuyển khoản chỉ được dùng cho `buyerContactName`; không ghi đè `buyerCompanyName`, `taxCode` hoặc `address`.
- Kết quả tra cứu không được lưu cache.
Xem quy tắc đầy đủ tại [Tra cứu mã số thuế và dữ liệu người mua](/hoa-don-dien-tu/tra-cuu-ma-so-thue).
### Người mua không lấy hóa đơn
Đặt:
```json
{
"requested": true,
"buyerNotTakingInvoice": true
}
```
Pay2S sẽ dùng **Bán cho người tiêu dùng**. Danh sách hàng hóa vẫn phải được gửi đầy đủ.
## Danh sách hàng hóa
| Trường | Bắt buộc | Mô tả |
|---|---:|---|
| `itemName` | Có | Tên hàng hóa hoặc dịch vụ; dùng làm giá trị dự phòng khi Quy tắc sản phẩm khớp theo tên |
| `itemCode` | Không | Mã ổn định để khớp quy tắc hàng hóa |
| `itemGroupCode` | Không | Mã nhóm để áp dụng chung một quy tắc |
| `externalItemId` | Không | ID phục vụ đối chiếu với hệ thống nguồn |
| `sourceProductName` | Không | Tên nguyên bản trên website; được ưu tiên đối chiếu khi Quy tắc sản phẩm khớp theo tên |
| `unit` | Không | Đơn vị tính, ví dụ `Tháng`, `Cái`, `Lần` |
| `quantity` | Có | Số lượng lớn hơn 0 |
| `unitPrice` | Có | Đơn giá trước thuế, không âm |
| `taxRate` | Có | `-2`, `-1`, `0`, `3.5`, `5`, `8` hoặc `10` |
| `vatReductionEligible` | Không | `true` nếu dòng hàng thuộc diện giảm thuế GTGT theo Nghị quyết 204/2025/QH15; mặc định `false` |
| `directVatRate` | Có điều kiện | Tỷ lệ tính thuế GTGT trực tiếp trên doanh thu: `1`, `2`, `3` hoặc `5`; bắt buộc khi `vatReductionEligible=true` |
Các giá trị thuế đặc biệt:
- `-2`: không kê khai, tính nộp thuế.
- `-1`: không chịu thuế.
### Kiểm tra tổng tiền
```text
Tiền hàng = quantity × unitPrice
Tiền thuế = Tiền hàng × taxRate / 100
Tổng thanh toán = Tổng tiền hàng + Tổng tiền thuế
```
Với thuế `-1` hoặc `-2`, tiền thuế bằng 0. Tổng tất cả dòng hàng phải khớp `amount` của đơn.
Ví dụ `unitPrice=100000`, `quantity=1`, `taxRate=10` thì `amount` phải bằng `110000`.
### Hóa đơn GTGT và hóa đơn bán hàng
Loại hóa đơn được lấy từ mẫu đã gán trên Pay2S, không lấy từ một trường do client tự khai báo:
- **Hóa đơn GTGT:** gửi `taxRate` đúng thuế suất của từng dòng. Không cần gửi `vatReductionEligible` và `directVatRate`.
- **Hóa đơn bán hàng không giảm theo Nghị quyết 204:** thông thường gửi `taxRate=0` và bỏ hai trường giảm thuế, hoặc gửi `vatReductionEligible=false`.
- **Hóa đơn bán hàng có giảm theo Nghị quyết 204:** gửi `taxRate=0`, `vatReductionEligible=true` và `directVatRate` bằng `1`, `2`, `3` hoặc `5` trên từng dòng đủ điều kiện.
`directVatRate` là **tỷ lệ tính thuế GTGT trực tiếp trên doanh thu**, không phải thuế suất hiển thị của hóa đơn GTGT. Pay2S tính số tiền được giảm của từng dòng như sau:
```text
Tiền hàng trước giảm = quantity × unitPrice
Tiền được giảm = Tiền hàng trước giảm × directVatRate / 100 × 20%
Tiền thanh toán của dòng = Tiền hàng trước giảm - Tiền được giảm
```
Ví dụ một đơn có một món được giảm và một món không được giảm:
```json
{
"items": [
{
"itemCode": "MON_A",
"itemName": "Món A",
"unit": "Phần",
"quantity": 1,
"unitPrice": 100000,
"taxRate": 0,
"vatReductionEligible": true,
"directVatRate": 5
},
{
"itemCode": "MON_B",
"itemName": "Món B",
"unit": "Phần",
"quantity": 1,
"unitPrice": 50000,
"taxRate": 0,
"vatReductionEligible": false
}
]
}
```
Món A được giảm `1.000` đồng, món B không giảm nên `amount` của request phải là `149000`. `unitPrice` phải là giá trước khoản giảm theo Nghị quyết 204; không gửi giá đã giảm rồi yêu cầu Pay2S giảm thêm lần nữa.
Nếu hệ thống nguồn hiện đang tách thuế và đã gửi `taxRate` thực tế, client cũ có thể tiếp tục gửi như trước. Khi mẫu đã gán là hóa đơn bán hàng, Pay2S gộp phần thuế nguồn vào giá bán để giữ đúng số tiền giao dịch.
::: warning Khả năng hỗ trợ của nhà cung cấp
Pay2S chỉ phát hành dữ liệu giảm thuế trực tiếp khi API của nhà cung cấp đã được xác nhận hỗ trợ. Nếu nhà cung cấp chưa hỗ trợ, hóa đơn được giữ ở bản nháp và không gửi đi để tránh phát hành sai.
:::
## Tùy chọn hóa đơn
| Trường | Mô tả |
|---|---|
| `requested` | `true` khi đơn này cần xuất HĐĐT |
| `buyerNotTakingInvoice` | `true` khi người mua không lấy hoặc không cung cấp đủ thông tin |
| `paymentMethod` | Ví dụ `Chuyển khoản`, mặc định có thể dùng `TM/CK` |
| `note` | Ghi chú hóa đơn, tối đa 500 ký tự |
| `source` | Nguồn ổn định như `api`, `whmcs`, `woocommerce`, `hostbill` |
Không đặt `requested=true` cho mọi đơn nếu chỉ một số khách yêu cầu hóa đơn.
## Mã hóa `extraData`
Thực hiện đúng thứ tự:
1. Chuyển metadata thành JSON UTF-8.
2. Base64 chuỗi JSON đúng **một lần**.
3. Dùng chính chuỗi Base64 đó trong payload và chuỗi ký.
Ví dụ Node.js:
```js
const extraData = Buffer
.from(JSON.stringify(metadata), 'utf8')
.toString('base64');
```
Ví dụ PHP:
```php
$extraData = base64_encode(
json_encode($metadata, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
);
```
## Tạo chữ ký V2
Ghép đúng thứ tự dưới đây, không URL encode `extraData` trước khi ký:
```text
accessKey={accessKey}&amount={amount}&bankAccounts=Array&extraData={extraData}&ipnUrl={ipnUrl}&orderId={orderId}&orderInfo={orderInfo}&partnerCode={partnerCode}&redirectUrl={redirectUrl}&requestId={requestId}&requestType={requestType}&signatureVersion=2
```
Tạo chữ ký bằng HMAC SHA-256 với `secretKey`, xuất kết quả dạng hex chữ thường.
::: danger Dữ liệu ký phải giống dữ liệu gửi
Chỉ cần khác một ký tự trong `amount`, `extraData`, URL hoặc `orderInfo`, API sẽ trả về **Chữ ký không hợp lệ**.
:::
## Request mẫu đầy đủ
```json
{
"accessKey": "YOUR_KEY",
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"partnerName": "Cửa hàng trải nghiệm",
"requestId": "DEVd33eb13817916599723201625c7",
"amount": 250000,
"orderId": "DEVd33eb13817916599723201625c7",
"orderInfo": "TT817916599723201625c7",
"bankAccounts": [
{
"account_number": "P2S99999999",
"bank_id": "ACB"
}
],
"redirectUrl": "https://developer.example.com/demos/?mode=live",
"ipnUrl": "https://developer.example.com/sandbox/live-receiver/ipn",
"requestType": "pay2s",
"signature": "<SIGNATURE>",
"signatureVersion": "2",
"extraData": "eyJpbnZvaWNlVHlwZSI6InZhdCIsImN1c3RvbWVySW5mbyI6eyJidXllckNvbnRhY3ROYW1lIjoiQsOhbiBjaG8gbmfGsOG7nWkgdGnDqnUgZMO5bmcifSwiaXRlbXMiOlt7Iml0ZW1OYW1lIjoiU+G6o24gcGjhuqltIGRlbW8iLCJxdWFudGl0eSI6MSwidW5pdFByaWNlIjoyNTAwMDAsInRheFJhdGUiOjB9XSwiaW52b2ljZU9wdGlvbnMiOnsicmVxdWVzdGVkIjp0cnVlLCJidXllck5vdFRha2luZ0ludm9pY2UiOnRydWV9fQ=="
}
```
## `orderInfo` do Pay2S tự sinh
Trong request V2, đối tác vẫn gửi và ký trường `orderInfo` để request có cấu trúc tương thích. Sau khi chữ ký hợp lệ, Pay2S tự thay giá trị này bằng mã thanh toán chuẩn, ví dụ:
```text
P2S8A4F21C09D7B
```
Mã do Pay2S sinh:
- Chỉ gồm tiền tố `P2S`, chữ in hoa và số.
- Dài 15 ký tự, phù hợp nội dung chuyển khoản.
- Được kiểm tra không trùng trước khi sử dụng.
- Được dùng thống nhất trong QR, dữ liệu đơn hàng và IPN.
- Được tái sử dụng nếu đối tác gọi lại cùng `orderId` chưa thanh toán.
Response tạo đơn trả lại `orderInfo` thực tế:
```json
{
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"requestId": "DEVd33eb13817916599723201625c7",
"orderId": "DEVd33eb13817916599723201625c7",
"orderInfo": "TT817916599723201625c7",
"amount": 250000,
"message": "Thành công",
"lang": "vi",
"resultCode": 0,
"qrList": [
{
"bank_id": "ACB",
"account_number": "P2S99999999",
"account_name": "TAI KHOAN DEMO ACB",
"qrCode": "<QRCODE_OMITTED>",
"qrData": "00020101021238550010A000000727012500069704160111P2S999999990208QRIBFTTA52040000530370454062500005802VN62260822TT817916599723201625c7630495CF",
"qrUrl": "https://sandbox-payment.pay2s.vn/quicklink/ACB/P2S99999999/TAI%20KHOAN%20DEMO%20ACB?amount=250000&memo=TT817916599723201625c7&is_mask=0"
}
],
"payUrl": "https://sandbox-payment.pay2s.vn/v2/gateway/pay?t=YOUR_KEY",
"storeId": null,
"credentialScope": "owner",
"isDemo": true,
"environment": "demo"
}
```
::: warning Giá trị request và response khác nhau
Khi tạo chữ ký, dùng đúng `orderInfo` đang gửi trong request. Khi hiển thị nội dung thanh toán hoặc lưu để đối soát, dùng `orderInfo` do response của Pay2S trả về. Hệ thống đối tác nên nhận diện đơn bằng `orderId`.
:::
## 💻 Code mẫu hoàn chỉnh
Các ví dụ dưới đây đều thực hiện cùng một quy trình: tạo metadata HĐĐT, mã hóa Base64 UTF-8, ký V2 và gửi request JSON đến Pay2S. Hãy đặt khóa bí mật trong biến môi trường trên backend.
::: code-group
```php [PHP]
<?php
$endpoint = 'https://payment.pay2s.vn/v1/gateway/api/create';
$accessKey = getenv('PAY2S_ACCESS_KEY');
$secretKey = getenv('PAY2S_SECRET_KEY');
$partnerCode = getenv('PAY2S_PARTNER_CODE');
$requestId = (string) round(microtime(true) * 1000);
$orderId = 'ORDER-' . $requestId;
$amount = '110000';
$orderInfo = 'TT' . $requestId;
$requestType = 'pay2s';
$redirectUrl = 'https://merchant.example/return';
$ipnUrl = 'https://merchant.example/pay2s/ipn';
$bankAccounts = [['account_number' => '99999999', 'bank_id' => 'ACB']];
$metadata = [
'invoiceType' => 'vat',
'customerInfo' => [
'buyerContactName' => 'NGUYỄN VĂN A',
'buyerCompanyName' => 'CÔNG TY TNHH ABC',
'taxCode' => '0312345678',
'citizenId' => '',
'address' => '123 Nguyễn Huệ, TP.HCM',
'email' => '[email protected]',
'phone' => '0901234567',
],
'items' => [[
'itemCode' => 'DV001',
'itemGroupCode' => 'API_GROUP_SOFTWARE',
'sourceProductName' => 'Gói phần mềm Pro',
'itemName' => 'Dịch vụ phần mềm',
'unit' => 'Tháng',
'quantity' => 1,
'unitPrice' => 100000,
'taxRate' => 10,
]],
'invoiceOptions' => [
'requested' => true,
'buyerNotTakingInvoice' => false,
'paymentMethod' => 'Chuyển khoản',
'note' => '',
'source' => 'api',
],
];
$metadataJson = json_encode(
$metadata,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR
);
$extraData = base64_encode($metadataJson);
$rawSignature = "accessKey={$accessKey}"
. "&amount={$amount}"
. '&bankAccounts=Array'
. "&extraData={$extraData}"
. "&ipnUrl={$ipnUrl}"
. "&orderId={$orderId}"
. "&orderInfo={$orderInfo}"
. "&partnerCode={$partnerCode}"
. "&redirectUrl={$redirectUrl}"
. "&requestId={$requestId}"
. "&requestType={$requestType}"
. '&signatureVersion=2';
$payload = [
'accessKey' => $accessKey,
'partnerCode' => $partnerCode,
'partnerName' => 'CÔNG TY TNHH ABC',
'requestId' => $requestId,
'amount' => $amount,
'orderId' => $orderId,
'orderInfo' => $orderInfo,
'orderType' => $requestType,
'bankAccounts' => $bankAccounts,
'redirectUrl' => $redirectUrl,
'ipnUrl' => $ipnUrl,
'requestType' => $requestType,
'signatureVersion' => '2',
'extraData' => $extraData,
'signature' => hash_hmac('sha256', $rawSignature, $secretKey),
];
$ch = curl_init($endpoint);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json; charset=UTF-8'],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
$response = curl_exec($ch);
if ($response === false) {
throw new RuntimeException(curl_error($ch));
}
curl_close($ch);
echo $response;
```
```js [Node.JS]
const crypto = require('crypto');
const axios = require('axios');
const endpoint = 'https://payment.pay2s.vn/v1/gateway/api/create';
const accessKey = process.env.PAY2S_ACCESS_KEY;
const secretKey = process.env.PAY2S_SECRET_KEY;
const partnerCode = process.env.PAY2S_PARTNER_CODE;
const requestId = Date.now().toString();
const orderId = `ORDER-${requestId}`;
const amount = '110000';
const orderInfo = `TT${requestId}`;
const requestType = 'pay2s';
const redirectUrl = 'https://merchant.example/return';
const ipnUrl = 'https://merchant.example/pay2s/ipn';
const bankAccounts = [{ account_number: '99999999', bank_id: 'ACB' }];
const metadata = {
invoiceType: 'vat',
customerInfo: {
buyerContactName: 'NGUYỄN VĂN A',
buyerCompanyName: 'CÔNG TY TNHH ABC',
taxCode: '0312345678',
citizenId: '',
address: '123 Nguyễn Huệ, TP.HCM',
email: '[email protected]',
phone: '0901234567'
},
items: [{
itemCode: 'DV001',
itemGroupCode: 'API_GROUP_SOFTWARE',
externalItemId: 'ORDER_LINE_001',
sourceProductName: 'Gói phần mềm Pro',
itemName: 'Dịch vụ phần mềm',
unit: 'Tháng',
quantity: 1,
unitPrice: 100000,
taxRate: 10
}],
invoiceOptions: {
requested: true,
buyerNotTakingInvoice: false,
paymentMethod: 'Chuyển khoản',
note: '',
source: 'api'
}
};
const extraData = Buffer.from(JSON.stringify(metadata), 'utf8').toString('base64');
const rawSignature =
`accessKey=${accessKey}` +
`&amount=${amount}` +
`&bankAccounts=Array` +
`&extraData=${extraData}` +
`&ipnUrl=${ipnUrl}` +
`&orderId=${orderId}` +
`&orderInfo=${orderInfo}` +
`&partnerCode=${partnerCode}` +
`&redirectUrl=${redirectUrl}` +
`&requestId=${requestId}` +
`&requestType=${requestType}` +
`&signatureVersion=2`;
const signature = crypto
.createHmac('sha256', secretKey)
.update(rawSignature, 'utf8')
.digest('hex');
const payload = {
accessKey,
partnerCode,
partnerName: 'CÔNG TY TNHH ABC',
requestId,
amount,
orderId,
orderInfo,
orderType: requestType,
bankAccounts,
redirectUrl,
ipnUrl,
requestType,
signatureVersion: '2',
extraData,
signature
};
axios.post(endpoint, payload, {
headers: { 'Content-Type': 'application/json; charset=UTF-8' }
}).then(({ data }) => {
console.log(data);
});
```
```csharp [ASP/.NET]
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
const string endpoint = "https://payment.pay2s.vn/v1/gateway/api/create";
string accessKey = Environment.GetEnvironmentVariable("PAY2S_ACCESS_KEY")!;
string secretKey = Environment.GetEnvironmentVariable("PAY2S_SECRET_KEY")!;
string partnerCode = Environment.GetEnvironmentVariable("PAY2S_PARTNER_CODE")!;
string requestId = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
string orderId = $"ORDER-{requestId}";
string amount = "110000";
string orderInfo = $"TT{requestId}";
string requestType = "pay2s";
string redirectUrl = "https://merchant.example/return";
string ipnUrl = "https://merchant.example/pay2s/ipn";
var bankAccounts = new[] { new { account_number = "99999999", bank_id = "ACB" } };
var metadata = new
{
invoiceType = "vat",
customerInfo = new
{
buyerContactName = "NGUYỄN VĂN A",
buyerCompanyName = "CÔNG TY TNHH ABC",
taxCode = "0312345678",
citizenId = "",
address = "123 Nguyễn Huệ, TP.HCM",
email = "[email protected]",
phone = "0901234567"
},
items = new[]
{
new
{
itemCode = "DV001",
itemGroupCode = "API_GROUP_SOFTWARE",
sourceProductName = "Gói phần mềm Pro",
itemName = "Dịch vụ phần mềm",
unit = "Tháng",
quantity = 1,
unitPrice = 100000,
taxRate = 10
}
},
invoiceOptions = new
{
requested = true,
buyerNotTakingInvoice = false,
paymentMethod = "Chuyển khoản",
note = "",
source = "api"
}
};
string metadataJson = JsonSerializer.Serialize(metadata);
string extraData = Convert.ToBase64String(Encoding.UTF8.GetBytes(metadataJson));
string rawSignature = $"accessKey={accessKey}"
+ $"&amount={amount}"
+ "&bankAccounts=Array"
+ $"&extraData={extraData}"
+ $"&ipnUrl={ipnUrl}"
+ $"&orderId={orderId}"
+ $"&orderInfo={orderInfo}"
+ $"&partnerCode={partnerCode}"
+ $"&redirectUrl={redirectUrl}"
+ $"&requestId={requestId}"
+ $"&requestType={requestType}"
+ "&signatureVersion=2";
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
string signature = Convert.ToHexString(
hmac.ComputeHash(Encoding.UTF8.GetBytes(rawSignature))
).ToLowerInvariant();
var payload = new
{
accessKey,
partnerCode,
partnerName = "CÔNG TY TNHH ABC",
requestId,
amount,
orderId,
orderInfo,
orderType = requestType,
bankAccounts,
redirectUrl,
ipnUrl,
requestType,
signatureVersion = "2",
extraData,
signature
};
using var http = new HttpClient();
var response = await http.PostAsJsonAsync(endpoint, payload);
Console.WriteLine(await response.Content.ReadAsStringAsync());
```
```python [Python]
import base64
import hashlib
import hmac
import json
import os
import time
import requests
endpoint = "https://payment.pay2s.vn/v1/gateway/api/create"
access_key = os.environ["PAY2S_ACCESS_KEY"]
secret_key = os.environ["PAY2S_SECRET_KEY"]
partner_code = os.environ["PAY2S_PARTNER_CODE"]
request_id = str(int(time.time() * 1000))
order_id = f"ORDER-{request_id}"
amount = "110000"
order_info = f"TT{request_id}"
request_type = "pay2s"
redirect_url = "https://merchant.example/return"
ipn_url = "https://merchant.example/pay2s/ipn"
bank_accounts = [{"account_number": "99999999", "bank_id": "ACB"}]
metadata = {
"invoiceType": "vat",
"customerInfo": {
"buyerContactName": "NGUYỄN VĂN A",
"buyerCompanyName": "CÔNG TY TNHH ABC",
"taxCode": "0312345678",
"citizenId": "",
"address": "123 Nguyễn Huệ, TP.HCM",
"email": "[email protected]",
"phone": "0901234567",
},
"items": [{
"itemCode": "DV001",
"itemGroupCode": "API_GROUP_SOFTWARE",
"sourceProductName": "Gói phần mềm Pro",
"itemName": "Dịch vụ phần mềm",
"unit": "Tháng",
"quantity": 1,
"unitPrice": 100000,
"taxRate": 10,
}],
"invoiceOptions": {
"requested": True,
"buyerNotTakingInvoice": False,
"paymentMethod": "Chuyển khoản",
"note": "",
"source": "api",
},
}
metadata_json = json.dumps(
metadata, ensure_ascii=False, separators=(",", ":")
).encode("utf-8")
extra_data = base64.b64encode(metadata_json).decode("ascii")
raw_signature = (
f"accessKey={access_key}"
f"&amount={amount}"
"&bankAccounts=Array"
f"&extraData={extra_data}"
f"&ipnUrl={ipn_url}"
f"&orderId={order_id}"
f"&orderInfo={order_info}"
f"&partnerCode={partner_code}"
f"&redirectUrl={redirect_url}"
f"&requestId={request_id}"
f"&requestType={request_type}"
"&signatureVersion=2"
)
signature = hmac.new(
secret_key.encode("utf-8"), raw_signature.encode("utf-8"), hashlib.sha256
).hexdigest()
payload = {
"accessKey": access_key,
"partnerCode": partner_code,
"partnerName": "CÔNG TY TNHH ABC",
"requestId": request_id,
"amount": amount,
"orderId": order_id,
"orderInfo": order_info,
"orderType": request_type,
"bankAccounts": bank_accounts,
"redirectUrl": redirect_url,
"ipnUrl": ipn_url,
"requestType": request_type,
"signatureVersion": "2",
"extraData": extra_data,
"signature": signature,
}
response = requests.post(endpoint, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
```
```java [Java]
// Yêu cầu Java 11+ và thư viện Gson.
import com.google.gson.Gson;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Pay2SCollectionLinkV2 {
private static String hex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte value : bytes) result.append(String.format("%02x", value));
return result.toString();
}
public static void main(String[] args) throws Exception {
String endpoint = "https://payment.pay2s.vn/v1/gateway/api/create";
String accessKey = System.getenv("PAY2S_ACCESS_KEY");
String secretKey = System.getenv("PAY2S_SECRET_KEY");
String partnerCode = System.getenv("PAY2S_PARTNER_CODE");
String requestId = String.valueOf(Instant.now().toEpochMilli());
String orderId = "ORDER-" + requestId;
String amount = "110000";
String orderInfo = "TT" + requestId;
String requestType = "pay2s";
String redirectUrl = "https://merchant.example/return";
String ipnUrl = "https://merchant.example/pay2s/ipn";
List<Map<String, Object>> bankAccounts = List.of(
Map.of("account_number", "99999999", "bank_id", "ACB")
);
Map<String, Object> metadata = Map.of(
"invoiceType", "vat",
"customerInfo", Map.of(
"buyerContactName", "NGUYỄN VĂN A",
"buyerCompanyName", "CÔNG TY TNHH ABC",
"taxCode", "0312345678",
"citizenId", "",
"address", "123 Nguyễn Huệ, TP.HCM",
"email", "[email protected]",
"phone", "0901234567"
),
"items", List.of(Map.of(
"itemCode", "DV001",
"itemGroupCode", "API_GROUP_SOFTWARE",
"sourceProductName", "Gói phần mềm Pro",
"itemName", "Dịch vụ phần mềm",
"unit", "Tháng",
"quantity", 1,
"unitPrice", 100000,
"taxRate", 10
)),
"invoiceOptions", Map.of(
"requested", true,
"buyerNotTakingInvoice", false,
"paymentMethod", "Chuyển khoản",
"note", "",
"source", "api"
)
);
Gson gson = new Gson();
String extraData = Base64.getEncoder().encodeToString(
gson.toJson(metadata).getBytes(StandardCharsets.UTF_8)
);
String rawSignature = "accessKey=" + accessKey
+ "&amount=" + amount
+ "&bankAccounts=Array"
+ "&extraData=" + extraData
+ "&ipnUrl=" + ipnUrl
+ "&orderId=" + orderId
+ "&orderInfo=" + orderInfo
+ "&partnerCode=" + partnerCode
+ "&redirectUrl=" + redirectUrl
+ "&requestId=" + requestId
+ "&requestType=" + requestType
+ "&signatureVersion=2";
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = hex(mac.doFinal(rawSignature.getBytes(StandardCharsets.UTF_8)));
Map<String, Object> payload = Map.ofEntries(
Map.entry("accessKey", accessKey),
Map.entry("partnerCode", partnerCode),
Map.entry("partnerName", "CÔNG TY TNHH ABC"),
Map.entry("requestId", requestId),
Map.entry("amount", amount),
Map.entry("orderId", orderId),
Map.entry("orderInfo", orderInfo),
Map.entry("orderType", requestType),
Map.entry("bankAccounts", bankAccounts),
Map.entry("redirectUrl", redirectUrl),
Map.entry("ipnUrl", ipnUrl),
Map.entry("requestType", requestType),
Map.entry("signatureVersion", "2"),
Map.entry("extraData", extraData),
Map.entry("signature", signature)
);
HttpRequest request = HttpRequest.newBuilder(URI.create(endpoint))
.header("Content-Type", "application/json; charset=UTF-8")
.POST(HttpRequest.BodyPublishers.ofString(gson.toJson(payload)))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
request, HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}
```
```go [Go]
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func main() {
endpoint := "https://payment.pay2s.vn/v1/gateway/api/create"
accessKey := os.Getenv("PAY2S_ACCESS_KEY")
secretKey := os.Getenv("PAY2S_SECRET_KEY")
partnerCode := os.Getenv("PAY2S_PARTNER_CODE")
requestID := strconv.FormatInt(time.Now().UnixMilli(), 10)
orderID := "ORDER-" + requestID
amount := "110000"
orderInfo := "TT" + requestID
requestType := "pay2s"
redirectURL := "https://merchant.example/return"
ipnURL := "https://merchant.example/pay2s/ipn"
bankAccounts := []map[string]any{{"account_number": "99999999", "bank_id": "ACB"}}
metadata := map[string]any{
"invoiceType": "vat",
"customerInfo": map[string]any{
"buyerContactName": "NGUYỄN VĂN A",
"buyerCompanyName": "CÔNG TY TNHH ABC",
"taxCode": "0312345678",
"citizenId": "",
"address": "123 Nguyễn Huệ, TP.HCM",
"email": "[email protected]",
"phone": "0901234567",
},
"items": []map[string]any{{
"itemCode": "DV001", "itemGroupCode": "API_GROUP_SOFTWARE",
"sourceProductName": "Gói phần mềm Pro",
"itemName": "Dịch vụ phần mềm", "unit": "Tháng",
"quantity": 1, "unitPrice": 100000, "taxRate": 10,
}},
"invoiceOptions": map[string]any{
"requested": true, "buyerNotTakingInvoice": false,
"paymentMethod": "Chuyển khoản", "note": "", "source": "api",
},
}
metadataJSON, _ := json.Marshal(metadata)
extraData := base64.StdEncoding.EncodeToString(metadataJSON)
rawSignature := "accessKey=" + accessKey +
"&amount=" + amount +
"&bankAccounts=Array" +
"&extraData=" + extraData +
"&ipnUrl=" + ipnURL +
"&orderId=" + orderID +
"&orderInfo=" + orderInfo +
"&partnerCode=" + partnerCode +
"&redirectUrl=" + redirectURL +
"&requestId=" + requestID +
"&requestType=" + requestType +
"&signatureVersion=2"
mac := hmac.New(sha256.New, []byte(secretKey))
mac.Write([]byte(rawSignature))
signature := hex.EncodeToString(mac.Sum(nil))
payload := map[string]any{
"accessKey": accessKey, "partnerCode": partnerCode,
"partnerName": "CÔNG TY TNHH ABC", "requestId": requestID,
"amount": amount, "orderId": orderID, "orderInfo": orderInfo,
"orderType": requestType, "bankAccounts": bankAccounts,
"redirectUrl": redirectURL, "ipnUrl": ipnURL,
"requestType": requestType, "signatureVersion": "2",
"extraData": extraData, "signature": signature,
}
body, _ := json.Marshal(payload)
response, err := http.Post(endpoint, "application/json; charset=UTF-8", bytes.NewReader(body))
if err != nil { panic(err) }
defer response.Body.Close()
result, _ := io.ReadAll(response.Body)
fmt.Println(string(result))
}
```
```ruby [Ruby]
require 'base64'
require 'json'
require 'net/http'
require 'openssl'
require 'time'
endpoint = URI('https://payment.pay2s.vn/v1/gateway/api/create')
access_key = ENV.fetch('PAY2S_ACCESS_KEY')
secret_key = ENV.fetch('PAY2S_SECRET_KEY')
partner_code = ENV.fetch('PAY2S_PARTNER_CODE')
request_id = (Time.now.to_f * 1000).to_i.to_s
order_id = "ORDER-#{request_id}"
amount = '110000'
order_info = "TT#{request_id}"
request_type = 'pay2s'
redirect_url = 'https://merchant.example/return'
ipn_url = 'https://merchant.example/pay2s/ipn'
bank_accounts = [{ account_number: '99999999', bank_id: 'ACB' }]
metadata = {
invoiceType: 'vat',
customerInfo: {
buyerContactName: 'NGUYỄN VĂN A',
buyerCompanyName: 'CÔNG TY TNHH ABC',
taxCode: '0312345678',
citizenId: '',
address: '123 Nguyễn Huệ, TP.HCM',
email: '[email protected]',
phone: '0901234567'
},
items: [{
itemCode: 'DV001',
itemGroupCode: 'API_GROUP_SOFTWARE',
sourceProductName: 'Gói phần mềm Pro',
itemName: 'Dịch vụ phần mềm',
unit: 'Tháng',
quantity: 1,
unitPrice: 100000,
taxRate: 10
}],
invoiceOptions: {
requested: true,
buyerNotTakingInvoice: false,
paymentMethod: 'Chuyển khoản',
note: '',
source: 'api'
}
}
extra_data = Base64.strict_encode64(JSON.generate(metadata).encode('UTF-8'))
raw_signature = "accessKey=#{access_key}" \
"&amount=#{amount}" \
'&bankAccounts=Array' \
"&extraData=#{extra_data}" \
"&ipnUrl=#{ipn_url}" \
"&orderId=#{order_id}" \
"&orderInfo=#{order_info}" \
"&partnerCode=#{partner_code}" \
"&redirectUrl=#{redirect_url}" \
"&requestId=#{request_id}" \
"&requestType=#{request_type}" \
'&signatureVersion=2'
signature = OpenSSL::HMAC.hexdigest('SHA256', secret_key, raw_signature)
payload = {
accessKey: access_key,
partnerCode: partner_code,
partnerName: 'CÔNG TY TNHH ABC',
requestId: request_id,
amount: amount,
orderId: order_id,
orderInfo: order_info,
orderType: request_type,
bankAccounts: bank_accounts,
redirectUrl: redirect_url,
ipnUrl: ipn_url,
requestType: request_type,
signatureVersion: '2',
extraData: extra_data,
signature: signature
}
request = Net::HTTP::Post.new(endpoint)
request['Content-Type'] = 'application/json; charset=UTF-8'
request.body = JSON.generate(payload)
response = Net::HTTP.start(endpoint.host, endpoint.port, use_ssl: true) do |http|
http.request(request)
end
puts response.body
```
:::
## Postman Pre-request Script
Đặt các biến `accessKey`, `secretKey`, `partnerCode`, `amount`, `redirectUrl`, `ipnUrl` và `bankAccounts` trong Environment. Sau đó dùng script:
```js
const CryptoJS = pm.require('npm:[email protected]');
const accessKey = pm.environment.get('accessKey');
const secretKey = pm.environment.get('secretKey');
const partnerCode = pm.environment.get('partnerCode');
const amount = String(pm.environment.get('amount'));
const redirectUrl = pm.environment.get('redirectUrl');
const ipnUrl = pm.environment.get('ipnUrl');
const bankAccounts = JSON.parse(pm.environment.get('bankAccounts'));
const requestId = Date.now().toString();
const orderId = `ORDER-${requestId}`;
const orderInfo = `TT${requestId}`;
const requestType = 'pay2s';
const metadata = {
invoiceType: 'vat',
customerInfo: {
buyerContactName: 'NGUYỄN VĂN A',
buyerCompanyName: 'CÔNG TY TNHH ABC',
taxCode: '0312345678',
citizenId: '',
address: '123 Nguyễn Huệ, TP.HCM',
email: '[email protected]',
phone: '0901234567'
},
items: [{
itemCode: 'DV001',
itemName: 'Dịch vụ phần mềm',
unit: 'Tháng',
quantity: 1,
unitPrice: 100000,
taxRate: 10
}],
invoiceOptions: {
requested: true,
buyerNotTakingInvoice: false,
paymentMethod: 'Chuyển khoản',
note: '',
source: 'api'
}
};
const extraData = CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(JSON.stringify(metadata))
);
const rawSignature =
`accessKey=${accessKey}` +
`&amount=${amount}` +
`&bankAccounts=Array` +
`&extraData=${extraData}` +
`&ipnUrl=${ipnUrl}` +
`&orderId=${orderId}` +
`&orderInfo=${orderInfo}` +
`&partnerCode=${partnerCode}` +
`&redirectUrl=${redirectUrl}` +
`&requestId=${requestId}` +
`&requestType=${requestType}` +
`&signatureVersion=2`;
const signature = CryptoJS
.HmacSHA256(rawSignature, secretKey)
.toString(CryptoJS.enc.Hex);
const payload = {
accessKey,
partnerCode,
partnerName: 'CÔNG TY TNHH ABC',
requestId,
amount,
orderId,
orderInfo,
orderType: requestType,
bankAccounts,
redirectUrl,
ipnUrl,
requestType,
signatureVersion: '2',
extraData,
signature
};
pm.request.body.update(JSON.stringify(payload));
```
Body của Postman phải chọn **raw → JSON**. Không để các placeholder dạng hai dấu ngoặc nhọn ghi đè lại payload sau khi script chạy.
## Tương thích với V1
- Request V1 cũ không gửi `extraData` và không gửi `signatureVersion` vẫn hoạt động.
- Không thêm `extraData` vào chuỗi ký V1 của client cũ.
- Khi gửi metadata hóa đơn, luôn chuyển sang chữ ký V2 và ký cả `extraData`.
- Có thể triển khai V2 dưới dạng tùy chọn bật/tắt để tránh ảnh hưởng khách đang dùng module cũ.
## Lỗi thường gặp
| Thông báo | Cách kiểm tra |
|---|---|
| Chữ ký không hợp lệ | So sánh từng ký tự trong raw signature với payload thực tế |
| `extraData` không đúng Base64 | JSON UTF-8 rồi Base64 đúng một lần |
| `extraData` không chứa JSON hợp lệ | Giải mã Base64 và kiểm tra lại JSON |
| Thông tin hàng hóa không hợp lệ | Kiểm tra tên, số lượng, đơn giá và thuế suất |
| Đơn ở mãi trong Hàng chờ | Kiểm tra thanh toán, mẫu hóa đơn, tự động hóa và tổng tiền |
| Người mua thành “Bán cho người tiêu dùng” | Bổ sung họ tên + địa chỉ, hoặc tên đơn vị + MST + địa chỉ |
| Đơn có MST không được tự động xử lý | Kiểm tra trạng thái MST; chỉ mã trạng thái `00` được tiếp tục tự động |
::: warning Bảo mật
Chỉ tạo chữ ký ở backend. Không đặt `secretKey` trong JavaScript chạy trên trình duyệt, ứng dụng public hoặc kho mã nguồn công khai.
:::
## 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.
### Collection Link V2 · Thanh toán & HĐĐT
<div class="doc-api-actions" data-doc-api-actions><a class="try-button" data-doc-playground href="/playground/?api=collection-v2">Thử API trong Playground ↗</a><a class="try-button experience-button" data-doc-experience href="/demos/?flow=payment&api=collection-v2">Trải nghiệm thanh toán & IPN ↗</a></div>
**Đã gọi API demo/UAT.**
`POST /v1/gateway/api/create`
Body:
```json
{
"accessKey": "[REDACTED]",
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"partnerName": "Cửa hàng trải nghiệm",
"requestId": "DEVd33eb13817916599723201625c7",
"amount": 250000,
"orderId": "DEVd33eb13817916599723201625c7",
"orderInfo": "TT817916599723201625c7",
"bankAccounts": [
{
"account_number": "P2S99999999",
"bank_id": "ACB"
}
],
"redirectUrl": "https://developer.example.com/demos/?mode=live",
"ipnUrl": "https://developer.example.com/sandbox/live-receiver/ipn",
"requestType": "pay2s",
"signature": "<SIGNATURE>",
"signatureVersion": "2",
"extraData": "eyJpbnZvaWNlVHlwZSI6InZhdCIsImN1c3RvbWVySW5mbyI6eyJidXllckNvbnRhY3ROYW1lIjoiQsOhbiBjaG8gbmfGsOG7nWkgdGnDqnUgZMO5bmcifSwiaXRlbXMiOlt7Iml0ZW1OYW1lIjoiU+G6o24gcGjhuqltIGRlbW8iLCJxdWFudGl0eSI6MSwidW5pdFByaWNlIjoyNTAwMDAsInRheFJhdGUiOjB9XSwiaW52b2ljZU9wdGlvbnMiOnsicmVxdWVzdGVkIjp0cnVlLCJidXllck5vdFRha2luZ0ludm9pY2UiOnRydWV9fQ=="
}
```
HTTP 200. Response:
```json
{
"partnerCode": "PAY2S7EPF0SB1ZP27W71",
"requestId": "DEVd33eb13817916599723201625c7",
"orderId": "DEVd33eb13817916599723201625c7",
"orderInfo": "TT817916599723201625c7",
"amount": 250000,
"message": "Thành công",
"lang": "vi",
"resultCode": 0,
"qrList": [
{
"bank_id": "ACB",
"account_number": "P2S99999999",
"account_name": "TAI KHOAN DEMO ACB",
"qrCode": "<QRCODE_OMITTED>",
"qrData": "00020101021238550010A000000727012500069704160111P2S999999990208QRIBFTTA52040000530370454062500005802VN62260822TT817916599723201625c7630495CF",
"qrUrl": "https://sandbox-payment.pay2s.vn/quicklink/ACB/P2S99999999/TAI%20KHOAN%20DEMO%20ACB?amount=250000&memo=TT817916599723201625c7&is_mask=0"
}
],
"payUrl": "https://sandbox-payment.pay2s.vn/v2/gateway/pay?t=<REDACTED>",
"storeId": null,
"credentialScope": "owner",
"isDemo": true,
"environment": "demo"
}
```