跳转到主要内容

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

  1. 终端注册

每台 POS 终端在处理交易前都必须先在 UQPAY 完成注册。这是一次性操作,每台终端只需注册一次。

2.1 API Endpoint

POST /v2/terminal/register
API Reference:Register Terminal

2.2 请求参数

字段类型必填说明示例
firm_codestring终端厂商代码"03"
firm_snstring厂商提供的终端序列号"TG676SX1764902333"
terminal_modelstring终端型号"P1000"
支持的厂商代码:
代码厂商
01Shengben
02Newland
03Xinguodu
04iMin
05PAX

2.3 请求示例

{
    "firm_code": "03",
    "firm_sn": "TG676SX1764902333",
    "terminal_model": "P1000"
}

2.4 响应

字段类型说明
terminal_idstringUQPAY 分配的终端标识(TID)
firm_snstring终端序列号
create_timestring注册时间
{
    "create_time": "2025-12-05 10:38:53",
    "firm_sn": "TG676SX1764902333",
    "terminal_id": "10000254"
}
注意: 请妥善保存 terminal_id,后续所有 API 调用都需要用到。

  1. 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_idstring注册时返回的终端 ID"10000254"
prv_keystring你的 AES 私钥(十六进制格式,最长 64 字符)"5214abf357f2cc47645c4d942c02fd0180acc6d8de8fae3708849b6a459cba00"
请求示例:
{
    "terminal_id": "10000254",
    "prv_key": "5214abf357f2cc47645c4d942c02fd0180acc6d8de8fae3708849b6a459cba00"
}
响应:
字段类型说明
terminal_idstring终端标识
encrypt_pin_keystring加密后的 PIN 密钥(Base64 编码)
pin_key_expireintegerPIN 密钥过期时间戳(毫秒
{
    "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 密钥即可。

  1. PIN 块加密

处理基于 PIN 的交易时,你需要将持卡人 PIN 加密为 PIN 块。

4.1 概览

PIN 块由以下内容组合生成:
  • 卡 PAN(Primary Account Number)
  • 持卡人 PIN
  • PINKEY

4.2 示例

输入:
参数
卡号5554748800260229
PIN302312
PINKEY26e1734e4d52b905e74574b7bf0897daeec7e217c61b3ac0
输出:
参数
PINBLOCK4F5D12303995DD06
PIN 块加密代码示例见附录。

  1. 创建 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"
}

  1. 错误处理

6.1 POS API 错误码

错误码错误信息
account_pos_api_no_permissionAccount pos api no permission
terminal_not_belong_to_accountTerminal not belong to account
terminal_register_failedTerminal register failed
terminal_info_not_foundTerminal info not found
pinkey_retrieve_failedPinkey retrieve failed

  1. 测试

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

  1. 附录

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系统跟踪审计号,交易的唯一跟踪编号
EMVEuropay、Mastercard、Visa;芯片卡标准
PANPrimary Account Number(卡号)
CVM持卡人验证方式(Cardholder Verification Method)