Documentation Index
Fetch the complete documentation index at: https://developers-sandbox.uqpaytech.com/llms.txt
Use this file to discover all available pages before exploring further.
本指南介绍如何集成 UQPAY POS API 以处理刷卡交易,涵盖终端注册、PIN 密钥管理、PIN 块加密和支付处理。
- 概览
1.1 前置条件
开始集成前,请确保:
- 已向 UQPAY 提供你的账户 ID,以启用 POS API 功能
1.2 集成流程
┌─────────────────────────────────────────────────────────────────────────┐
│ POS API Integration Flow │
└─────────────────────────────────────────────────────────────────────────┘
Step 1: Terminal Registration (One time setup)
POS Terminal ──► Register Terminal API ──► Receive terminal_id (TID)
Step 2: PIN Key Setup
Generate AES Key ──► Get PIN Key API ──► Decrypt to get PINKEY
Step 3: Process Payment
Read Card ──► Encrypt PIN ──► Create Payment Intent ──► Receive Result
- 终端注册
每台 POS 终端在处理交易前都必须先在 UQPAY 完成注册。这是一次性操作,每台终端只需注册一次。
2.1 API Endpoint
POST /v2/terminal/register
API Reference:Register Terminal
2.2 请求参数
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|
firm_code | string | 是 | 终端厂商代码 | "03" |
firm_sn | string | 是 | 厂商提供的终端序列号 | "TG676SX1764902333" |
terminal_model | string | 是 | 终端型号 | "P1000" |
支持的厂商代码:
| 代码 | 厂商 |
|---|
01 | Shengben |
02 | Newland |
03 | Xinguodu |
04 | iMin |
05 | PAX |
2.3 请求示例
{
"firm_code": "03",
"firm_sn": "TG676SX1764902333",
"terminal_model": "P1000"
}
2.4 响应
| 字段 | 类型 | 说明 |
|---|
terminal_id | string | UQPAY 分配的终端标识(TID) |
firm_sn | string | 终端序列号 |
create_time | string | 注册时间 |
{
"create_time": "2025-12-05 10:38:53",
"firm_sn": "TG676SX1764902333",
"terminal_id": "10000254"
}
注意: 请妥善保存 terminal_id,后续所有 API 调用都需要用到。
- PIN 密钥管理
要安全处理基于 PIN 的交易,你需要获取并管理 PIN 加密密钥。
3.1 生成 AES 私钥
请求 PIN 密钥前,先在终端或服务器上生成一个 AES 私钥:
| 要求 | 规格 |
|---|
| 算法 | AES |
| 密钥长度 | 最长 256 位(64 个十六进制字符) |
| 格式 | 十六进制字符串 |
AES 密钥示例(256 位):
5214abf357f2cc47645c4d942c02fd0180acc6d8de8fae3708849b6a459cba00
重要: 请妥善保存该私钥。后续需要用它解密 Get PIN Key API 返回的 encrypt_pin_key。
3.2 请求 PIN 密钥
API Endpoint:
POST /v2/terminal/getPinKey
API Reference:Get PIN Key
请求参数:
| 字段 | 类型 | 必填 | 说明 | 示例 |
|---|
terminal_id | string | 是 | 注册时返回的终端 ID | "10000254" |
prv_key | string | 是 | 你的 AES 私钥(十六进制格式,最长 64 字符) | "5214abf357f2cc47645c4d942c02fd0180acc6d8de8fae3708849b6a459cba00" |
请求示例:
{
"terminal_id": "10000254",
"prv_key": "5214abf357f2cc47645c4d942c02fd0180acc6d8de8fae3708849b6a459cba00"
}
响应:
| 字段 | 类型 | 说明 |
|---|
terminal_id | string | 终端标识 |
encrypt_pin_key | string | 加密后的 PIN 密钥(Base64 编码) |
pin_key_expire | integer | PIN 密钥过期时间戳(毫秒) |
{
"encrypt_pin_key": "LASDho1ILHoYRf/5YEyIgieoc+SXJkUsHZElXOMNGv7WnC3fZzFPYDH8mJaDbnwvom3QEtdv3NjvaEUtVeetWdQsv2RtQw4XEYh/Cg==",
"pin_key_expire": 1764990056170,
"terminal_id": "10000254"
}
3.3 解密 PIN 密钥
使用你的 AES 私钥解密 encrypt_pin_key,得到实际的 PINKEY。
解密参数:
| 参数 | 值 |
|---|
| 算法 | AES-256-GCM |
| 输入 | encrypt_pin_key |
| 密钥 | 你的 prv_key |
| 输出 | PINKEY |
示例:
- encrypt_pin_key:
LASDho1ILHoYRf/5YEyIgieoc+SXJkUsHZElXOMNGv7WnC3fZzFPYDH8mJaDbnwvom3QEtdv3NjvaEUtVeetWdQsv2RtQw4XEYh/Cg==
- 实际 PINKEY:
26e1734e4d52b905e74574b7bf0897daeec7e217c61b3ac0
解密代码示例见附录。
3.4 密钥过期
留意 pin_key_expire 时间戳,在过期前刷新 PIN 密钥。按相同流程重新请求新的 PIN 密钥即可。
- PIN 块加密
处理基于 PIN 的交易时,你需要将持卡人 PIN 加密为 PIN 块。
4.1 概览
PIN 块由以下内容组合生成:
- 卡 PAN(Primary Account Number)
- 持卡人 PIN
- PINKEY
4.2 示例
输入:
| 参数 | 值 |
|---|
| 卡号 | 5554748800260229 |
| PIN | 302312 |
| PINKEY | 26e1734e4d52b905e74574b7bf0897daeec7e217c61b3ac0 |
输出:
| 参数 | 值 |
|---|
| PINBLOCK | 4F5D12303995DD06 |
PIN 块加密代码示例见附录。
- 创建 Payment Intent
完成终端设置并获取 PINKEY 后,即可处理刷卡交易。
5.1 API Endpoint
POST /v2/payment_intents/create
API Reference:Create Payment Intent
5.2 请求示例
POS 交易使用 card_present 支付方式类型。需要注意的关键字段:
terminal_id:设置为终端注册时返回的 TID
encrypted_pin:设置为 PIN 加密生成的 PINBLOCK
system_trace_audit_number:每笔新交易必须唯一(STAN)
{
"amount": "2.22",
"currency": "SGD",
"payment_method": {
"type": "card_present",
"card_present": {
"card_name": "mastercard 0229",
"card_number": "5554748800260229",
"expiry_month": "03",
"expiry_year": "2026",
"cardholder_verification_method": "online_pin",
"encrypted_pin": "4F5D12303995DD06",
"pan_entry_mode": "contactless_chip",
"fallback": false,
"fallback_reason": "",
"emv_tags": "9F39010757135554748800260229D26032211837752200000F820219809F360202CE9F1E0831303030303030319F10120110A04003240000000000000000000000FF9F3303E060C89F350122950500000480019F0106A000000000019F02060000000500005F24032603315A0855547488002602295F3401009F150258129F160F3130303030303031303030303030309F1A0207029F1C08313030303030303181040000C3505F2A0207029A032512029F21031418059C01005F280204409F120A4D6173746572636172649F1101015F2D02656E9F42020978940810010101200104008701019F0A04000101049F080200028C279F02069F03069F1A0295055F2A029A039C019F37049F35019F45029F4C089F34039F21039F7C149F4C08CF57740F41E613609F4A01825F300202219F26088BD046AF7A2D8D9A9F2701809F34030203009F37041DF853909F03060000000000005F25032303019F4104000000009F0702FFC08E0E000000000000000002031E031F039F0D05B4508400009F0E0500000000009F0F05B4708480004F07A00000000410108407A00000000410109F090200029B02E0009F4E083130303030303031500A4D6173746572636172649F4005F000F0F0019F0607A00000000410109F6E0704400000323000D4030000029908EA53B2F5A7D54E309F72080000000200000000",
"track1": "",
"track2": "5554748800260229D26032211837752200000",
"terminal_info": {
"mobile_device": false,
"system_trace_audit_number": "000003",
"terminal_id": "10000254",
"use_embedded_reader": true
}
}
},
"merchant_order_id": "{{$guid}}",
"description": "pos api order from sandbox",
"metadata": {
"request_id": "{{$guid}}"
},
"return_url": "https://return-url/api/v2/callback"
}
5.3 响应示例
{
"amount": "2.22",
"available_payment_method_types": null,
"cancel_time": "",
"cancellation_reason": "",
"captured_amount": "2.22",
"client_secret": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYXN0ZXJfaWQiOiIwIiwiYWNjb3VudF9pZCI6ImIxYjg5Njg0LWMyYzQtNGQ1NC1iOGE4LTM1NzI3MjdmZDEyMCIsImludGVudF9pZCI6IlBJMTk5Njc5NTM0MjE0OTkxNDYyNCIsImV4cCI6MTc2NDkwOTg4MiwiaWF0IjoxNzY0OTA4MDgyfQ.tUAF1IHWm-payNAES4qOv_Vngbm3mn5z217DayihgLI",
"complete_time": "2025-12-05T12:14:42+08:00",
"create_time": "2025-12-05T12:14:42+08:00",
"currency": "SGD",
"description": "pos api order from sandbox",
"intent_status": "SUCCEEDED",
"latest_payment_attempt": {
"amount": "2.22",
"attempt_id": "PA1996795342275743744",
"attempt_status": "CAPTURE_REQUESTED",
"captured_amount": "2.22",
"complete_time": "2025-12-05T12:14:42+08:00",
"create_time": "2025-12-05T12:14:42+08:00",
"currency": "SGD",
"failure_code": "",
"refunded_amount": "0",
"update_time": "2025-12-05T12:14:42+08:00"
},
"merchant_order_id": "76bcc9a2-0aad-467a-aded-06c0c03bdced",
"metadata": {
"request_id": "3e635c58-8774-4425-84a9-42a602299576"
},
"next_action": null,
"payment_intent_id": "PI1996795342149914624",
"return_url": "https://return-url/api/v2/callback",
"update_time": "2025-12-05T12:14:42+08:00"
}
- 错误处理
6.1 POS API 错误码
| 错误码 | 错误信息 |
|---|
account_pos_api_no_permission | Account pos api no permission |
terminal_not_belong_to_account | Terminal not belong to account |
terminal_register_failed | Terminal register failed |
terminal_info_not_found | Terminal info not found |
pinkey_retrieve_failed | Pinkey retrieve failed |
- 测试
7.1 测试卡
在沙盒环境使用以下测试卡数据:
| 字段 | 值 |
|---|
| 卡号 | 5554748800161559 |
| 有效期月 | 07 |
| 有效期年 | 2027 |
| PIN(明文) | “ |
| 卡组织 | Mastercard |
用于测试的 card_present 对象示例:
{
"type": "card_present",
"card_present": {
"card_name": "uqpay cardholder",
"card_number": "5554748800161559",
"expiry_month": "07",
"expiry_year": "2027",
"cardholder_verification_method": "online_pin",
// "encrypted_pin": "D6CD73498446A0C9",
"pan_entry_mode": "contactless_chip",
"fallback": false,
"fallback_reason": "",
"emv_tags": "9F39010757135554748800161559D25042211103236300000F820219809F3602051B9F1E0831303030303135319F10120110A00003240000000000000000000000FF9F3303E008C89F350122950504400080019F0106A000000000019F02060000000000015F24032504305A0855547488001615595F3401009F150258129F160F3130303030313531303030303030309F1A0207029F1C0831303030303135318104000000015F2A0207029A032512059F21031652269C01005F280204409F42020978940810010101200103008701019F0A04000101049F080200028C279F02069F03069F1A0295055F2A029A039C019F37049F35019F45029F4C089F34039F21039F7C149F4A01825F300202219F260817DE07EBE32C04859F2701809F34031F03029F3704C18B521B9F03060000000000005F25032204019F4104000000009F0702FFC08E0E000000000000000002031E031F039F0D05B4508400009F0E0500000000009F0F05B4708480004F07A00000000410108407A00000000410109F090200029B0260009F4E083130303030313531500A4D6173746572636172649F4005F000F0F0019F0607A00000000410109F6E0704400000323000D4034000019F72080000000000000000",
"track1": "",
"track2": "5554748800161559D25042211103236300000",
"terminal_info": {
"mobile_device": false,
"system_trace_audit_number": "000014",
"terminal_id": "10000267",
"use_embedded_reader": true
}
}
}
注意: 每笔测试交易必须使用唯一的 system_trace_audit_number。
- 附录
8.1 代码示例
AES-256-GCM 解密(Python)
# aes_decrypt.py
import sys
import base64
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
def aes_decrypt(prv_key: str, encrypt_pin_key: str) -> str:
key = bytes.fromhex(prv_key)
ciphertext = base64.b64decode(encrypt_pin_key)
nonce = ciphertext[:12]
tag = ciphertext[-16:]
ct = ciphertext[12:-16]
cipher = Cipher(algorithms.AES(key), modes.GCM(nonce, tag), backend=default_backend())
decryptor = cipher.decryptor()
plaintext = decryptor.update(ct) + decryptor.finalize()
return plaintext.decode("utf-8")
if __name__ == "__main__":
print(aes_decrypt(sys.argv[1], sys.argv[2]))
# Usage
python3 aes_decrypt.py <prv_key> <encrypt_pin_key>
# Example
python3 aes_decrypt.py 5214abf357f2cc47645c4d942c02fd0180acc6d8de8fae3708849b6a459cba00 "LASDho1ILHoYRf/5YEyIgieoc+SXJkUsHZElXOMNGv7WnC3fZzFPYDH8mJaDbnwvom3QEtdv3NjvaEUtVeetWdQsv2RtQw4XEYh/Cg=="
# Output: 26e1734e4d52b905e74574b7bf0897daeec7e217c61b3ac0
PIN 块加密(Python)
# pinblock.py
import sys
import binascii
from Crypto.Cipher import DES3
from Crypto.Util.strxor import strxor
def create_pin_block(card_number: str, pin: str, pinkey: str) -> str:
pin_field_hex = f"0{len(pin)}{pin}".ljust(16, 'F')
pin_field_bytes = binascii.unhexlify(pin_field_hex)
pan_part = card_number[-13:-1]
pan_field_hex = "0000" + pan_part
pan_field_bytes = binascii.unhexlify(pan_field_hex)
pin_block = strxor(pin_field_bytes, pan_field_bytes)
pek_bytes = binascii.unhexlify(pinkey)
cipher = DES3.new(pek_bytes, DES3.MODE_ECB)
encrypted = cipher.encrypt(pin_block)
return binascii.hexlify(encrypted).decode('utf-8').upper()
if __name__ == "__main__":
print(create_pin_block(sys.argv[1], sys.argv[2], sys.argv[3]))
# Usage
python3 pinblock.py <card_number> <pin> <pinkey>
# Example
python3 pinblock.py 5554748800260229 302312 26e1734e4d52b905e74574b7bf0897daeec7e217c61b3ac0
# Output: 4F5D12303995DD06
8.2 术语表
| 术语 | 定义 |
|---|
| TID | 终端标识,由 UQPAY 分配的唯一 ID |
| PINKEY | 用于加密持卡人 PIN 的密钥 |
| PIN Block | 持卡人 PIN 的加密表示 |
| STAN | 系统跟踪审计号,交易的唯一跟踪编号 |
| EMV | Europay、Mastercard、Visa;芯片卡标准 |
| PAN | Primary Account Number(卡号) |
| CVM | 持卡人验证方式(Cardholder Verification Method) |