# WorldCard API --- # Integration Preparation WorldCard now recommends starting with [Card API](/en-US/Card/). Previous integration docs are grouped under [Legacy](/en-US/legacy/): - [Legacy Card](/en-US/legacy/card/): previous card and KYC APIs - [Legacy Transfer](/en-US/legacy/transfer/): previous transfer APIs ## Choose Your Integration Path - `Card/*`: current recommended path for new integrations, using `X-App-Id + RSA-PSS + AES-256-GCM` - `legacy/card/*`: previous card system using `appKey/appSecret + MD5` - `legacy/transfer/*`: previous transfer system using the shared legacy parameter and signing rules ## Before Integrating the Current Card API - `baseUrl` - `X-App-Id` - Merchant RSA private key and public key - Merchant AES key - WorldCard public key for webhook verification - Source IP whitelist - Merchant callback URLs (configured per callback type in the merchant portal; see [Webhook Callbacks](/en-US/Card/webhook#callback-url-configuration)): `webhook_card_open_url` `webhook_card_deposit_url` `webhook_card_close_url` `webhook_card_status_change_url` `webhook_card_billing_url` `webhook_card_3ds_url` ## Recommended First Test Flow 1. Read [Signature and Encryption](/en-US/Card/sign) 2. Call [Card BIN List](/en-US/Card/bin_list) to choose an available BIN 3. Call [Card Application](/en-US/Card/apply) 4. Receive `card_open` from [Webhook Callbacks](/en-US/Card/webhook) 5. Call [Card Detail](/en-US/Card/info) to confirm status and sensitive fields 6. Then continue with top-up, freeze/unfreeze, close, and related webhooks ## Important Notes - The current `Card/*` documentation scope is `VIRTUAL-V` / `VIRTUAL-R` / `VIRTUAL-G` - `X-Timestamp` in `Card/*` requests uses Unix seconds - `X-Timestamp` in Card webhooks uses Unix milliseconds - Sensitive fields such as card number, CVV, and expiry are only returned when `status=2` - A successful webhook acknowledgement must be HTTP `200` with plain-text body `ok` - Before using legacy APIs, read [Legacy Common Parameters](/en-US/legacy/parameter) and [Legacy Signing and Decryption](/en-US/legacy/sign) --- # Status Codes ## Common Status Codes ### HTTP Status Codes | Code | Description | |------|-------------| | 0 | Success | | 400 | Bad Request | | 401 | Unauthorized | | 403 | Forbidden | | 404 | Resource Not Found | | 409 | Resource Conflict | | 429 | Too Many Requests | | 500 | Internal Server Error | | 503 | Service Unavailable | ### Common Business Error Codes (1000-1999) | Code | Description | |------|-------------| | 1001 | Request parameter validation failed | | 1004 | Operation too frequent | | 1006 | Invalid operation | | 1075 | Date parsing failed | | 1076 | Invalid date format | | 1101 | Required field missing | | 1102 | Invalid field value | | 1103 | Invalid pagination parameter | | 1104 | Invalid page size parameter | | 1105 | Invalid date range | | 1133 | Owner mismatch | ## Card Module Status Codes (10000-10999) | Code | Description | |------|-------------| | 10001 | Card does not exist | | 10002 | Card has been disabled | | 10005 | Card type does not exist | | 10007 | Insufficient card quota | | 10008 | Card BIN does not exist | | 10009 | Card BIN has been disabled | | 10010 | Card order does not exist | | 10011 | Duplicate card order | | 10013 | Invalid card amount | | 10016 | Card transaction record does not exist | | 10017 | Unsupported card type | | 10018 | Invalid card parameter | | 10019 | Card user mismatch | | 10021 | Card issuance failed | | 10024 | Card cancellation failed | | 10025 | Card freeze failed | | 10026 | Card unfreeze failed | | 10031 | Insufficient card reserve balance | ## Remittance Module Status Codes (16000-16999) | Code | Description | |------|-------------| | 16001 | Remittance order does not exist | | 16002 | Remittance order already exists | | 16003 | Remittance order creation failed | | 16004 | Remittance order update failed | | 16005 | Remittance order deletion failed | | 16006 | Remittance order query failed | | 16021 | Remittance order missing required fields | | 16501 | Order notification does not exist | | 16708 | Invalid quote exchange rate | ## Card Status (status) Values of the card `status` field (returned by card detail / list / application APIs): | status | Description | |--------|-------------| | 1 | Processing | | 2 | Success (activated) | | 3 | Failed | | 4 | Closing | | 5 | Closed | | 6 | Frozen | | 7 | Pending card assignment (PHYSICAL-G physical card) | | 8 | Pending activation (PHYSICAL-G physical card) | | 9 | Pending KYC (PHYSICAL-G physical card) | --- # Changelog API change log, sorted in reverse chronological order. ## 2026-06-04 ### Remittance Fees & Documentation Improvements - **New remittance fee calculation guide**: documents the add-on model `fee = sendAmount × preRatedFee + fixedFee`, with actual charge = sendAmount + fee - **Quote preRatedFee unit unified**: the `preRatedFee` returned by [Get Quote](/en-US/legacy/transfer/V4/quotes) is now a decimal ratio (consistent with order query / notification); downstream computes the fee as sendAmount × preRatedFee + fixedFee - **Webhook card_status enum completed**: the `card_status` in card-close and card-status-change callbacks is one of `activated` / `inactive` / `blocked` / `cancelled` (the previously documented frozen value `frozen` is corrected to `blocked`; please update downstream) - **Webhook retry description corrected**: up to 10 retries, backoff now starts at 1min and is capped at 24h on the 10th retry - **Legacy navigation completed**: legacy index pages now include endpoint navigation; the Card landing page lists the `VIRTUAL-V` / `VIRTUAL-R` / `VIRTUAL-G` card types ::: details Related API [Fee Calculation](/en-US/legacy/transfer/V4/fee) · [Get Quote](/en-US/legacy/transfer/V4/quotes) · [Webhook Callbacks](/en-US/Card/webhook) ::: ## 2026-05-19 ### Card API Type Expansion - **New VIRTUAL-G card type**: Apply / Top-Up / Freeze / Unfreeze / Close endpoints now support `card_type=VIRTUAL-G` - **VIRTUAL-G application field requirements**: required fields are `first_name`/`last_name` (1-40 chars, letters and single spaces between words only), `email`, `phone`/`phone_country_code`, `birthday`, `country_code`/`city`/`address`/`postal_code`; `state` is optional - **Callback card number stability**: `card_open` and `card_close` callbacks guarantee that `card_number` is a 16-digit real PAN; the card number is available when the callback is pushed, so merchants do not need to handle null or masked values ::: details Related API [Card Application](/en-US/Card/apply) · [Freeze/Unfreeze](/en-US/Card/lock_unlock) · [Card Closure](/en-US/Card/close) · [Webhook Callbacks](/en-US/Card/webhook) ::: ## 2026-04-24 ### Documentation Structure - **Legacy namespace cleanup**: former `/A/*` docs moved to `/legacy/card/*`, and former `/Transfer/*` docs moved to `/legacy/transfer/*` - **Shared legacy pages moved to root**: legacy common parameters and signing/decryption docs now live at `/legacy/parameter` and `/legacy/sign` - **Primary entry flow updated**: the homepage, navigation, and getting started guide now point to `Card/*` as the primary path, with Legacy reserved for archived docs ::: details Related Entry Points [Getting Started](/en-US/prepare) · [Legacy Overview](/en-US/legacy/) · [Legacy Card](/en-US/legacy/card/) · [Legacy Transfer](/en-US/legacy/transfer/) ::: ### Card API Clarifications - **Current recommended scope**: new integrations should use `Card/*`, and the current primary docs cover `VIRTUAL-V` / `VIRTUAL-R` - **Status and sensitive fields clarified**: Card API uses the unified `status` lifecycle, and sensitive fields are only returned when `status=2` ## 2026-04-02 ### Card Application - **Billing address support**: Card application endpoint now accepts `state` (state/province) and `address_line_two` (address line 2) fields. `state` is required for VIRTUAL-V cards - **Card details returns address info**: Card details endpoint now returns `state`, `address_line_two`, and full cardholder information fields ::: details Related API [Card Application](/en-US/Card/apply) · [Card Details](/en-US/Card/info) ::: ## 2026-03-31 ### Documentation Platform - **Documentation tool migration**: Migrated from mdbook to VitePress with en-US / zh-CN / zh-HK tri-language support - **llms.txt support**: Added [llms.txt specification](/en-US/llms) support, providing structured documentation access for AI/LLM tools ## 2025-06-30 ### Card Top-Up - **Minimum top-up limit adjustment**: Physical card minimum 200, virtual card minimum 20 ::: details Related API [Top-Up API](/en-US/legacy/card/CARD/topup) ::: ## 2025-04-23 ### VISA-H Card Info - **New response field**: Card info endpoint now returns `status` field ::: details Related API [VISA-H Card Info](/en-US/legacy/card/CARD/VISA_H/info) ::: --- # LLMs.txt This documentation site supports the [llms.txt specification](https://llmstxt.org/), providing structured access for AI/LLM tools. ## What is llms.txt? llms.txt is a standardized format that helps Large Language Models (LLMs) quickly understand website content structure. Similar to how `robots.txt` serves search engines, `llms.txt` serves AI tools. ## Available Files | File | Description | Link | |------|-------------|------| | `llms.txt` | Documentation structure index with links to all pages | [/en-US/llms.txt](/en-US/llms.txt) | | `llms-full.txt` | Complete documentation content in a single file | [/en-US/llms-full.txt](/en-US/llms-full.txt) | ## Usage ### Integrate with AI Assistants Provide the `llms-full.txt` URL to your AI tool so it can understand the full API documentation: ``` https://doc.worldcard.io/en-US/llms-full.txt ``` ### On-Demand Loading Use `llms.txt` to get the document structure, then load specific `.md` source files as needed: ``` https://doc.worldcard.io/en-US/llms.txt ``` ::: tip Per-Language Files Each language (en-US, zh-CN, zh-HK) has its own `llms.txt` and `llms-full.txt`. ::: --- # Card API ## Overview WorldCard Card Management API, supporting the full lifecycle of virtual cards: card issuance, top-up, query, freeze/unfreeze, card closure, as well as transaction record queries and Webhook notifications. It currently supports three virtual card types: **VIRTUAL-V**, **VIRTUAL-R**, and **VIRTUAL-G**; the exact type is determined by the `card_type` field of the card application API. ## API List | API | Description | |-----|-------------| | [Card Application](./apply) | Apply to create a virtual card | | [Card Details](./info) | Query card details | | [Card BIN List](./bin_list) | Query the virtual card BIN list | | [Physical Card BIN List](./physical_bin_list) | Query the physical card BIN list | | [Card Top-Up](./topup) | Top up card balance | | [Top-Up Records](./deposit_list) | Query top-up transaction records | | [Freeze/Unfreeze](./lock_unlock) | Freeze or unfreeze a card | | [Card Closure](./close) | Close a card | | [Transaction Records](./transaction) | Query card spending transaction records | | [Webhook Callbacks](./webhook) | Notes on the 6 types of event notification callbacks | ## General Notes - **Request Method**: All APIs use POST with Content-Type: application/json - **Authentication**: Requests must include `X-App-Id`, `X-Signature`, `X-Timestamp`, `X-Nonce` Headers - **Signature Algorithm**: RSA-PSS + SHA-256 - **Request Body Encryption**: AES-256-GCM - **Response Format**: `{"code": 0, "message": "success", "data": {...}}`, `code=0` indicates success --- # Card Application ## Brief Description Apply to create a virtual card. The result will be delivered via Webhook callback upon successful card issuance. --- ## Request URL ``` ${baseUrl}/api/v1/card/virtual/apply ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Type | VIRTUAL-V | VIRTUAL-R | VIRTUAL-G | Description | |-----------|------|-----------|-----------|-----------|-------------| | merchant_card_id | string | Yes | Yes | Yes | Merchant card ID, unique identifier | | card_type | string | Yes | Yes | Yes | Card type: `VIRTUAL-V` / `VIRTUAL-R` / `VIRTUAL-G` | | card_bin_id | string | Yes | Yes | Yes | Card BIN ID (obtained from the card BIN list API) | | first_recharge_amount | string | Yes | Yes | Yes | Initial top-up amount in base currency unit, minimum 10 | | email | string | No | Yes | Yes | Email address | | first_name | string | Yes | No | Yes | Cardholder first name (VIRTUAL-G: 1-40 chars, letters A-Z/a-z and single spaces between words only) | | last_name | string | Yes | No | Yes | Cardholder last name (VIRTUAL-G: same rule as first_name) | | state | string | Yes | No | No | State/Province | | country_code | string | Yes | No | Yes | Country code, ISO3166-2 standard, e.g. `US` | | city | string | Yes | No | Yes | City | | address | string | Yes | No | Yes | Address | | postal_code | string | Yes | No | Yes | Postal code | | phone | string | No | No | Yes | Phone number | | phone_country_code | string | No | No | Yes | Phone country code | | birthday | string | No | No | Yes | Birthday, format `YYYY-MM-DD` (age ≥ 18 required if provided) | | address_line_two | string | No | No | No | Address line 2 | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456", "merchant_card_id": "mc_001", "status": 1, "created_at": "2026-04-01T10:00:00+08:00" } } ``` --- ## Response Parameter Description ### Response Fields | Parameter | Type | Description | |-----------|------|-------------| | code | int | Response code, 0=success | | message | string | Response message | | data | object | Response data | ### data | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | status | int | Card status: 1=Processing 2=Active 3=Failed 4=Closing 5=Closed 6=Frozen | | created_at | string | Creation time (RFC3339) | --- ## Notes - Card issuance is an asynchronous operation. The API returning `status=1` (Processing) indicates the application has been submitted - The final result of card issuance is delivered via the `card_open` type in [Webhook Callbacks](./webhook) - `first_recharge_amount` will be frozen from the reserve balance; funds are deducted upon successful issuance and unfrozen upon failure --- # Card BIN List ## Brief Description Query available virtual card BINs with pagination, used for selecting a card BIN when applying for a new card. --- ## Request URL ``` ${baseUrl}/api/v1/card/virtual/bin/list ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_type | No | string | Card type. Supports `VIRTUAL-V` / `VIRTUAL-R`. Returns all types if omitted | | page | No | int | Page number, defaults to 1 | | page_size | No | int | Items per page, defaults to 10, maximum 100 | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "total": 15, "page": 1, "page_size": 10, "list": [ { "card_bin_id": "1", "card_bin": "440872", "card_type": "VIRTUAL-V", "card_brand": "VISA", "business_scene": "Online Payments", "currency": "USD", "issuer_country": "US" }, { "card_bin_id": "2", "card_bin": "559292", "card_type": "VIRTUAL-R", "card_brand": "MASTERCARD", "business_scene": "Business Procurement", "currency": "USD", "issuer_country": "US" } ] } } ``` --- ## Response Parameter Description ### Top-level Parameters | Parameter | Type | Description | |-----------|------|-------------| | code | int | Response code. 0 indicates success, non-zero indicates failure | | message | string | Response message. "success" on success, error description on failure | | data | object | Response data. Contains the card BIN list on success, null on failure | ### data Fields | Parameter | Type | Description | |-----------|------|-------------| | total | int | Total number of matching card BINs | | page | int | Current page number | | page_size | int | Items per page | | list | array | Card BIN list | ### list Array Element | Parameter | Type | Description | |-----------|------|-------------| | card_bin_id | string | Card BIN ID, pass this value when applying for a card | | card_bin | string | Card BIN number, e.g. 440872 | | card_type | string | Card type, e.g. `VIRTUAL-V`, `VIRTUAL-R` | | card_brand | string | Card brand, e.g. VISA, MASTERCARD | | business_scene | string | Applicable business scenario, e.g. E-commerce, Advertising | | currency | string | Currency code, ISO 4217 standard, e.g. USD | | issuer_country | string | Issuer country, ISO 3166-2 standard, e.g. US | --- # Card Closure ## Brief Description Close a virtual card. Any remaining balance on the card will be communicated to the merchant via Webhook callback for processing. Currently supports `VIRTUAL-V` / `VIRTUAL-R` / `VIRTUAL-G` card types; the request format is identical across all three. --- ## Request URL ``` ${baseUrl}/api/v1/card/close ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_id | No | string | System card ID (either this or merchant_card_id, this takes priority) | | merchant_card_id | No | string | Merchant card ID | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456", "status": 4 } } ``` --- ## Response Parameter Description ### data | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | status | int | Card status: 1=Processing 2=Active 3=Failed 4=Closing 5=Closed 6=Frozen | --- ## Notes - Card closure is an asynchronous operation. The API returning `status=4` (Closing) indicates the request has been submitted - The closure result is delivered via the `card_close` type in [Webhook Callbacks](./webhook) - The callback includes the remaining balance on the card (`amount`/`currency`); the merchant should refund the user accordingly --- # Top-Up Records ## Brief Description Query top-up transaction records of a virtual card. --- ## Request URL ``` ${baseUrl}/api/v1/card/deposit/list ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_id | No | string | System card ID (either this or merchant_card_id, this takes priority) | | merchant_card_id | No | string | Merchant card ID | | merchant_transaction_id | No | string | Merchant transaction ID (exact match) | | transaction_id | No | string | System transaction ID (exact match) | | page | No | int | Page number, default 1 | | page_size | No | int | Items per page, default 10, maximum 100 | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "total": 2, "page": 1, "page_size": 10, "list": [ { "transaction_id": "9876543210", "card_id": "1234567890123456", "merchant_card_id": "mc_001", "merchant_transaction_id": "tx_001", "amount": "100.00", "currency": "USD", "status": 2, "created_at": "2026-04-01T10:00:00+08:00" } ] } } ``` --- ## Response Parameter Description ### data | Parameter | Type | Description | |-----------|------|-------------| | total | int | Total number of transactions | | page | int | Current page number | | page_size | int | Items per page | | list | array | Transaction list | ### list Items | Parameter | Type | Description | |-----------|------|-------------| | transaction_id | string | System transaction ID | | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | merchant_transaction_id | string | Merchant transaction ID | | amount | string | Top-up amount | | currency | string | Currency code | | status | int | Status: 1=Processing 2=Success 3=Failed | | created_at | string | Creation time | --- # Card Details ## Brief Description Query detailed information of a virtual card, including sensitive fields such as card number, CVV, and expiry date (returned encrypted). --- ## Request URL ``` ${baseUrl}/api/v1/card/virtual/get_info ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_id | No | string | System card ID (either this or merchant_card_id, this takes priority) | | merchant_card_id | No | string | Merchant card ID | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456", "merchant_card_id": "mc_001", "card_type": "VIRTUAL-V", "card_bin_id": "bin_001", "card_number": "", "cvv": "", "expiry_month": "", "expiry_year": "", "available_balance": "90.00", "frozen_balance": "10.00", "pending_balance": "0.00", "currency": "USD", "status": 2, "first_recharge_amount": "100.00", "first_name": "John", "last_name": "Doe", "email": "john@example.com", "phone": "1234567890", "phone_country_code": "1", "birthday": "1990-01-01", "country_code": "US", "city": "New York", "address": "123 Main St", "state": "NY", "address_line_two": "", "postal_code": "10001" } } ``` --- ## Response Parameter Description ### data | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | card_type | string | Card type: `VIRTUAL-V` / `VIRTUAL-R` / `VIRTUAL-G` | | card_bin_id | string | Card BIN ID | | card_number | string | Card number (AES-GCM encrypted, only returned when status=2) | | cvv | string | CVV (AES-GCM encrypted, only returned when status=2) | | expiry_month | string | Expiry month (AES-GCM encrypted, only returned when status=2) | | expiry_year | string | Expiry year (AES-GCM encrypted, only returned when status=2) | | available_balance | string | Available balance in base currency unit | | frozen_balance | string | Frozen balance in base currency unit | | pending_balance | string | Pending settlement balance in base currency unit | | currency | string | Currency code (ISO4217, e.g. USD) | | status | int | Card status: 1=Processing 2=Active 3=Failed 4=Closing 5=Closed 6=Frozen | | first_recharge_amount | string | Initial top-up amount in base currency unit | | first_name | string | Cardholder first name | | last_name | string | Cardholder last name | | email | string | Email address | | phone | string | Phone number | | phone_country_code | string | Phone country code | | birthday | string | Birthday (format: YYYY-MM-DD) | | country_code | string | Country code (ISO3166-2, e.g. US) | | city | string | City | | address | string | Address | | state | string | State/Province | | address_line_two | string | Address line 2 | | postal_code | string | Postal code | --- ## Notes - Card number, CVV, and expiry date are returned encrypted with the merchant's AES key and must be decrypted by the merchant - Sensitive fields (card_number, cvv, expiry_month, expiry_year) are only returned when the card status is Active (status=2); otherwise they are empty strings --- # Freeze/Unfreeze ## Brief Description Freeze or unfreeze a virtual card. Currently supports `VIRTUAL-V` / `VIRTUAL-R` / `VIRTUAL-G` card types; the request format is identical across all three. --- ## Freeze ### Request URL ``` ${baseUrl}/api/v1/card/freeze ``` ### Request Method - Method: POST - Content-Type: application/json ### Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_id | No | string | System card ID (either this or merchant_card_id, this takes priority) | | merchant_card_id | No | string | Merchant card ID | ### Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456", "status": 6 } } ``` ### Response Parameters | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | status | int | Card status: 1=Processing 2=Active 3=Failed 4=Closing 5=Closed 6=Frozen | --- ## Unfreeze ### Request URL ``` ${baseUrl}/api/v1/card/unfreeze ``` ### Request Method - Method: POST - Content-Type: application/json ### Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_id | No | string | System card ID (either this or merchant_card_id, this takes priority) | | merchant_card_id | No | string | Merchant card ID | ### Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456", "status": 2 } } ``` ### Response Parameters | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | status | int | Card status: 1=Processing 2=Active 3=Failed 4=Closing 5=Closed 6=Frozen | --- ## Notes - Freeze/unfreeze operations are notified via the `card_status_change` type in [Webhook Callbacks](./webhook) upon completion - Card status values: 1=Processing 2=Active 3=Failed 4=Closing 5=Closed 6=Frozen --- # Physical Card BIN List ## Description Paginated query of available physical card BINs, used to select a BIN when applying for a physical card. --- ## Request URL ``` ${baseUrl}/api/v1/card/physical/bin/list ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | page | No | int | Page number, default 1 | | page_size | No | int | Items per page, default 10, max 100 | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "total": 3, "page": 1, "page_size": 10, "list": [ { "card_bin_id": "12", "card_bin": "493812", "card_type": "PHYSICAL-G", "card_brand": "VISA", "card_form": 2, "currency": "USD", "issuer_country": "US" } ] } } ``` --- ## Response Fields ### Top-level fields | Name | Type | Description | |------|------|-------------| | code | int | Response code, 0 = success, non-zero = failure | | message | string | Response message, "success" on success, error description on failure | | data | object | Response data, contains physical card BIN list on success, null on failure | ### data fields | Name | Type | Description | |------|------|-------------| | total | int | Total number of matching physical card BINs | | page | int | Current page | | page_size | int | Items per page | | list | array | Physical card BIN list | ### list item fields | Name | Type | Description | |------|------|-------------| | card_bin_id | string | BIN ID, pass this value when applying for a card | | card_bin | string | BIN number, e.g. 493812 | | card_type | string | Card type, fixed as `PHYSICAL-G` for physical cards | | card_brand | string | Card brand, e.g. VISA, MASTERCARD | | card_form | int | Card form factor: 1=virtual / 2=physical (fixed 2 for physical) | | currency | string | Currency code, ISO 4217, e.g. USD | | issuer_country | string | Issuer region, ISO 3166-2, e.g. US | --- # Physical Card — Activate ## Description Activate a physical card that has been [assigned](./physical_card_assign) (`status=8`, pending activation). The merchant submits a 6-digit PIN to activate the card. Activation is **asynchronous**: the response still shows `status=8`; on success the card `status` becomes `2` and a [card-open callback](./webhook) (`card_open`) is pushed. --- ## Request URL ``` ${baseUrl}/api/v1/card/physical/activate ``` ## Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | card_id | Yes | string | Card ID. Either this or merchant_card_id; card_id preferred | | merchant_card_id | Yes | string | Merchant card ID. Either this or card_id | | pin | Yes | string | 6-digit numeric PIN, transmit encrypted | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456789", "merchant_card_id": "mc_001", "status": 8 } } ``` --- ## Response Fields ### data | Name | Type | Description | |------|------|-------------| | card_id | string | Card ID | | merchant_card_id | string | Merchant card ID | | status | int | Card status, **still `8`** (async activation; final state via webhook) | --- ## Notes - Only cards with `status=8` can be activated; already-activated (`status=2`) returns success (idempotent). - If the card is not yet ready, returns a retryable error "card not ready, retry later". - **Asynchronous**: success only means submitted. On success the card `status` becomes `2` and a [card-open callback](./webhook) (`card_open`) is pushed; failure keeps `status=8` and the PIN can be re-submitted. - PIN is used only for activation and is not persisted. --- # Physical Card Application ## Description Apply to create a physical card (PHYSICAL-G). The flow is: application (create cardholder) → [initiate KYC](./physical_card_kyc_init) → complete identity verification → [assign card number](./physical_card_assign) → [activate](./physical_card_activate). This endpoint is step one; on success the card status is `9` (pending KYC). After identity verification passes, the status advances automatically to `7` (pending card assignment) before a card number can be assigned. --- ## Request URL ``` ${baseUrl}/api/v1/card/physical/apply ``` ## Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | merchant_card_id | Yes | string | Merchant card ID, unique | | card_type | Yes | string | Card type, fixed `PHYSICAL-G` | | card_bin_id | Yes | string | Card BIN ID (from [Physical Card BIN List](./physical_bin_list)) | | first_name | Yes | string | Cardholder first name (English) | | last_name | Yes | string | Cardholder last name (English) | | telephone_country_code | Yes | string | Phone country code, e.g. 86 | | phone | Yes | string | Phone number | | email | No | string | Email | | birth_date | Yes | string | Birth date, `YYYY-MM-DD` | | residence_country | No | string | Residence country | | residence_state | No | string | Residence state | | residence_city | No | string | Residence city | | residence_line1/2/3 | No | string | Residence address lines | | residence_postal_code | No | string | Residence postal code | | delivery_* | No | string | Delivery address (country/state/city/line1-3/postal) for card shipping | > Identity verification (ID number, ID photos, liveness) is handled in the KYC step ([initiate KYC](./physical_card_kyc_init)) after application, so this endpoint no longer collects identity-document fields. --- ## Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456789", "merchant_card_id": "mc_001", "card_type": "PHYSICAL-G", "status": 9, "created_at": "2026-06-05T10:00:00+08:00" } } ``` --- ## Response Fields ### data | Name | Type | Description | |------|------|-------------| | card_id | string | System card ID (used in subsequent assign/activate) | | merchant_card_id | string | Merchant card ID | | card_type | string | `PHYSICAL-G` | | status | int | Card status, `9` on success (pending KYC); see [Status Codes](../code) | | created_at | string | Creation time (RFC3339) | --- ## Notes - Synchronous cardholder creation; returns `status=9` (pending KYC) on success. No issuance fee, no reserve freeze. - Next: call [assign card number](./physical_card_assign) with the real PAN → `status=8` (pending activation). - Next: call [initiate KYC](./physical_card_kyc_init); after the end customer passes, the card status advances automatically `9 → 7` (pending card assignment). - After KYC passes, call [assign card number](./physical_card_assign) to submit the real PAN → `status=8` (pending activation). - Calling assign before KYC completes returns `10028` (KYC not completed). - To abandon the application, call [cancel KYC](./physical_card_kyc_cancel) to cancel and refund the opening quota. --- # Physical Card — Assign Card Number ## Description Bind the real card number (PAN) to a physical card that has been [applied](./physical_card_apply) (`status=7`, pending card assignment). On success the card status becomes `8` (pending activation). --- ## Request URL ``` ${baseUrl}/api/v1/card/physical/assign ``` ## Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | card_id | Yes | string | Card ID (from apply). Either this or merchant_card_id; card_id preferred | | merchant_card_id | Yes | string | Merchant card ID. Either this or card_id | | card_number | Yes | string | Real 16-digit PAN, transmit encrypted | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456789", "merchant_card_id": "mc_001", "status": 8 } } ``` --- ## Response Fields ### data | Name | Type | Description | |------|------|-------------| | card_id | string | Card ID | | merchant_card_id | string | Merchant card ID | | status | int | Card status, `8` on success (pending activation) | --- ## Notes - Only cards with `status=7` can be assigned; re-assign returns current status (idempotent). - `card_number` must be a 16-digit numeric real PAN not already used by another card. - Next: call [activate](./physical_card_activate) with the 6-digit PIN. --- # Physical Card — Change PIN ## Description Change the PIN of an activated (`status=2`) physical card. Synchronous. --- ## Request URL ``` ${baseUrl}/api/v1/card/physical/change/pin ``` ## Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | card_id | Yes | string | Card ID. Either this or merchant_card_id; card_id preferred | | merchant_card_id | Yes | string | Merchant card ID. Either this or card_id | | pin | Yes | string | New PIN, 6 digits, transmit encrypted | --- ## Response Example ```json { "code": 0, "message": "success" } ``` --- ## Response Fields | Name | Type | Description | |------|------|-------------| | code | int | Response code, 0=success | | message | string | Response message | --- ## Notes - Only cards with `status=2` (activated) can change PIN. - PIN is used only for this change and is not persisted. --- # Physical Card Application Flow This guide walks merchants through the full lifecycle of a **PHYSICAL-G physical card**, from application to activation, with request/response examples at every step. A physical card must pass **KYC identity verification** before a card number can be assigned. The process has five steps. ## Overview ``` ①Apply ──▶ ②Init KYC ──▶ ③Complete ──▶ ④Assign Number ──▶ ⑤Activate status=9 (launch SDK) status 9→7 status=8 status=2 pending KYC pending assign pending activation activated ``` | Card status | Meaning | Triggered by | |-------------|---------|--------------| | 9 | Pending KYC | Initial state after a successful application | | 7 | Pending card assignment | Advances automatically after KYC passes | | 8 | Pending activation | After the card number is assigned | | 2 | Activated | After activation; top-up and spending enabled | | 3 | Failed | KYC rejected / abandoned / cancelled on timeout | > All endpoints are `POST` with `Content-Type: application/json`; request bodies are **AES-256-GCM encrypted** and **RSA-PSS + SHA-256 signed** (headers `X-App-Id`/`X-Signature`/`X-Timestamp`/`X-Nonce`). See [Getting Started](../prepare) and [Signing](./sign) for the envelope. The examples below show the **decrypted business payload**. Responses are `{"code":0,"message":"success","data":{...}}`; `code=0` means success. --- ## Prerequisite: Pick a BIN Call [Physical Card BIN List](./physical_bin_list) to obtain an available `card_bin_id` (determines the card scheme, currency, card face, etc.). --- ## Step 1: Apply Call [Apply](./physical_card_apply) to create the cardholder, submitting basic cardholder details and delivery address — **no identity documents** (document verification happens in the KYC step). **Request** `POST /api/v1/card/physical/apply` ```json { "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G", "card_bin_id": "100023", "first_name": "SAN", "last_name": "ZHANG", "telephone_country_code": "86", "phone": "13800138000", "email": "sanzhang@example.com", "birth_date": "1990-01-15", "residence_country": "CN", "residence_state": "Guangdong", "residence_city": "Shenzhen", "residence_line1": "Nanshan District", "residence_postal_code": "518000", "delivery_country": "CN", "delivery_state": "Guangdong", "delivery_city": "Shenzhen", "delivery_line1": "Nanshan District, Keyuan Road 1", "delivery_postal_code": "518000" } ``` **Response** ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456789", "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G", "status": 9, "created_at": "2026-06-26T10:00:00+08:00" } } ``` - Card status is `9` (pending KYC). **Save `card_id`** — later steps (assign, activate) can locate the card by it. - No issuance fee, no reserve freeze. See [Apply](./physical_card_apply) for the full field list. --- ## Step 2: Initiate KYC Call [Initiate KYC](./physical_card_kyc_init) to exchange `merchant_card_id` for an `access_token`. **Request** `POST /api/v1/card/physical/kyc/init` ```json { "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G" } ``` **Response** ```json { "code": 0, "message": "success", "data": { "access_token": "_act-sbx-xxxxxxxxxxxxxxxx", "external_user_id": "1234567890123456789", "status": 1 } } ``` - The merchant front end initializes the **Sumsub WebSDK** with `access_token` to guide the end customer through document upload and liveness. See the [KYC Integration Guide](./physical_card_kyc_guide) for the full front-end integration code (init, token refresh, events). - The `access_token` expires (~600s). **On expiry**: call this endpoint again for a fresh token — the endpoint is idempotent, reusing the same verification and only re-issuing the token (it does not restart verification). Fetch it right before launching the flow. --- ## Step 3: Complete Verification and Get the Result After the end customer finishes in the SDK, the result is produced **asynchronously**. There are two ways to learn it (use both). ### Option A: Callback (recommended) Once `webhook_card_kyc_url` is registered in the merchant portal, a [`card_kyc` callback](./webhook) is pushed (encrypted + signed, like other outbound callbacks) when the review completes. Decrypted payload: ```json { "card_id": "1234567890123456789", "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G", "status": 2, "retry_allowed": false, "reviewed_at": "2026-06-26T10:30:00+08:00" } ``` - `status`: `2`=approved, `3`=rejected. - A card may be pushed more than once (result updated); treat the record with the greatest `reviewed_at` as final. - Return HTTP `200` + plain text `ok`, otherwise it retries with exponential backoff. ### Option B: Polling Call [Query KYC Status](./physical_card_kyc_status): **Request** `POST /api/v1/card/physical/kyc/status` ```json { "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G" } ``` **Response** ```json { "code": 0, "message": "success", "data": { "status": 2, "retry_allowed": false, "review_answer": "GREEN", "reviewed_at": "2026-06-26T10:30:00+08:00" } } ``` - `status`: `0`=not started, `1`=pending, `2`=approved, `3`=rejected. - After KYC is approved, the card status advances automatically from `9` to `7` (pending card assignment). --- ## Step 4: Assign Card Number Once KYC has passed (card status `7`), call [Assign Card Number](./physical_card_assign) to submit the real card number (16-digit PAN). **Request** `POST /api/v1/card/physical/assign` ```json { "card_id": "1234567890123456789", "merchant_card_id": "mc_pcard_001", "card_number": "4000123412341234" } ``` **Response** ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456789", "merchant_card_id": "mc_pcard_001", "status": 8 } } ``` - On success the card status becomes `8` (pending activation). - **Calling this before KYC completes returns `10028` (KYC not completed)** — confirm the card status is `7` first. --- ## Step 5: Activate Call [Activate](./physical_card_activate) to set the PIN and finish opening the card. **Request** `POST /api/v1/card/physical/activate` ```json { "card_id": "1234567890123456789", "merchant_card_id": "mc_pcard_001", "pin": "135790" } ``` **Response** ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456789", "merchant_card_id": "mc_pcard_001", "status": 2 } } ``` - On success the card status becomes `2` (activated), ready for [top-up](./topup) and spending. --- ## Exception Handling ### KYC Rejected When the callback/query returns `status=3`: ```json { "status": 3, "retry_allowed": true, "review_answer": "RED", "reviewed_at": "2026-06-26T10:30:00+08:00" } ``` - The card stays at `9`. - If `retry_allowed=true`, the customer may [re-initiate KYC](./physical_card_kyc_init) (repeat Step 2) and submit again. - If `retry_allowed=false`, this verification cannot be retried; guide the customer to [cancel](./physical_card_kyc_cancel). ### Customer Abandons When the customer no longer wishes to continue, call [Cancel KYC](./physical_card_kyc_cancel): **Request** `POST /api/v1/card/physical/kyc/cancel` ```json { "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G" } ``` **Response** ```json { "code": 0, "message": "success", "data": { "status": 3 } } ``` - The card moves to the failed state (`status=3`) and the **opening quota is refunded**. - Only pending-KYC cards (`status=9`) can be cancelled; other states return the current status idempotently without refunding again. ### Timeout Auto-Cancel An application that does not complete KYC within the allowed period is **cancelled automatically and refunded**, and a `card_kyc` (`status=3`) notification is pushed. > After cancellation (manual or timeout), to open a new card use a **new `merchant_card_id`** and start again from Step 1. --- ## Status Transition Summary ``` apply ─▶ [9 pending KYC] ─KYC passed─▶ [7 pending assign] ─assign─▶ [8 pending activate] ─activate─▶ [2 activated] │ ▲ KYC reject│ │re-init KYC ▼ │ (stays 9, retry_allowed=true ⇒ retry) │ cancel / timeout ▼ [3 failed] (quota refunded, needs new merchant_card_id to re-open) ``` --- ## Related Endpoints | Step | Endpoint | |------|----------| | Prerequisite | [Physical Card BIN List](./physical_bin_list) | | ① Apply | [Apply](./physical_card_apply) | | ② Initiate KYC | [Initiate KYC](./physical_card_kyc_init) | | ③ Query/Callback | [Query KYC Status](./physical_card_kyc_status) · [card_kyc callback](./webhook) | | ④ Assign Number | [Assign Card Number](./physical_card_assign) | | ⑤ Activate | [Activate](./physical_card_activate) | | Abandon | [Cancel KYC](./physical_card_kyc_cancel) | | Query details | [Physical Card Details](./physical_card_get_info) | --- # Physical Card — Detail ## Description Query physical card (PHYSICAL-G) detail. **CVV/expiry are not returned for physical cards.** Activated cards (`status=2`) sync the latest balance. --- ## Request URL ``` ${baseUrl}/api/v1/card/physical/get_info ``` ## Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | card_id | Yes | string | Card ID. Either this or merchant_card_id; card_id preferred | | merchant_card_id | Yes | string | Merchant card ID. Either this or card_id | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890123456789", "merchant_card_id": "mc_001", "card_type": "PHYSICAL-G", "status": 2, "card_number": "4937241000003841", "available_balance": "100.00", "currency": "USD", "created_at": "2026-06-05T10:00:00+08:00", "updated_at": "2026-06-05T12:00:00+08:00" } } ``` --- ## Response Fields ### data | Name | Type | Description | |------|------|-------------| | card_id | string | Card ID | | merchant_card_id | string | Merchant card ID | | card_type | string | `PHYSICAL-G` | | status | int | Card status, see [Status Codes](../code) (incl. 7 pending assignment / 8 pending activation / 9 pending KYC) | | card_number | string | Card number (returned after assignment, decrypt) | | available_balance | string | Available balance | | currency | string | Currency, ISO4217, e.g. USD | | created_at / updated_at | string | Creation/update time (RFC3339) | --- ## Notes - **No cvv / expiry fields for physical cards** (hard constraint). - When `status=2` (activated), the latest balance is synced before returning; non-activated states (7/8) return the stored value. --- # Physical Card Cancel KYC ## Description Cancel the card application for a pending-KYC card (`status=9`) when the end customer abandons identity verification. The card moves to the failed state (`status=3`) and the opening quota reserved at apply is refunded. --- ## URL ``` ${baseUrl}/api/v1/card/physical/kyc/cancel ``` ## Method - POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | merchant_card_id | Yes | string | Merchant card ID | | card_type | Yes | string | Card type, fixed `PHYSICAL-G` | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "status": 3 } } ``` --- ## Response Fields ### data | Name | Type | Description | |------|------|-------------| | status | int | Card status after cancellation (`3`-failed) | --- ## Notes - Only pending-KYC cards (`status=9`) can be cancelled; calling this for an approved/assigned/cancelled card returns the current status idempotently without refunding again. - The opening quota is refunded after cancellation; to open a new card, use a new `merchant_card_id`. - Cancellation pushes a `card_open` (`status=3` failed) notification (consistent with other card types concluding an open-card with one `card_open`). - A card that does not complete KYC within the allowed period is cancelled and refunded automatically, and a `card_kyc` callback is sent. --- # Physical Card KYC Integration Guide This guide explains in detail how to integrate KYC identity verification for a **PHYSICAL-G physical card**. After [Apply](./physical_card_apply), the card sits at `9` (pending KYC); it must complete the verification flow in this guide before the status advances to `7` (pending card assignment). For the overall flow see [Physical Card Application Flow](./physical_card_flow). ## Verification Model KYC uses the **Sumsub WebSDK** (hosted mode): - Document images and liveness data are captured by the end customer directly in the **Sumsub WebSDK**, and **do not pass through the merchant backend**, reducing the merchant's compliance and storage burden. - The merchant backend only: initiates verification to obtain a temporary token, receives the asynchronous review result, and advances card opening accordingly. - Applicable card status: `9` (pending KYC). ## End-to-End Sequence ``` Merchant backend ──① init──────────────▶ obtain access_token Merchant frontend ──② init SDK with access_token End customer ──③ upload documents + liveness in the SDK SDK ──④ capture submitted (frontend signal); review result is async Review ──⑤ automated / manual → approved(GREEN) / rejected(RED) Our system ──⑥ card_kyc callback / status query ──▶ merchant learns the result Card ──⑦ on approval, advances automatically 9→7; can assign a number ``` > Key point: step ④ "SDK capture complete" **does not mean approved** — it only means the customer submitted. The final verdict comes from the asynchronous result in step ⑥. --- ## Step 1: Backend Initiates KYC and Obtains an access_token Call [Initiate KYC](./physical_card_kyc_init) to exchange `merchant_card_id` for an `access_token`. ```json // POST /api/v1/card/physical/kyc/init { "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G" } ``` ```json { "code": 0, "message": "success", "data": { "access_token": "_act-sbx-xxxxxxxxxxxxxxxx", "external_user_id": "1234567890123456789", "status": 1 } } ``` | Field | Meaning | |-------|---------| | `access_token` | A one-time, short-lived (~600s) verification session token, passed to the front-end SDK | | `external_user_id` | The card's unique verification identifier (i.e. `card_id`); results are keyed on it | | `status` | KYC status; always `1` (pending) right after init | --- ## Step 2: Frontend Integrates the Sumsub WebSDK After obtaining `access_token`, the merchant front end integrates the **Sumsub WebSDK** as follows. ### 1. Load the SDK Via ` ``` ```bash npm install @sumsub/websdk ``` ### 2. Prepare a container ```html
``` ### 3. Initialize and launch Initialize with the `access_token` from Step 1. **The second argument is the token-expiration handler** — the SDK calls it when the `access_token` is about to expire; it must return a Promise resolving to a fresh `access_token` for seamless renewal. ```js function launchKycSdk(accessToken) { const snsWebSdkInstance = snsWebSdk .init( accessToken, // called when access_token is about to expire: must return Promise () => getNewAccessToken() ) .withConf({ lang: 'en' }) // UI language, optional .withOptions({ addViewportTag: false, adaptIframeHeight: true }) .on('idCheck.onApplicantSubmitted', () => { // customer has submitted everything (≠ approved); final result comes from the async callback/query console.log('applicant submitted') }) .on('idCheck.onError', (error) => { console.error('kyc sdk error', error) }) .onMessage((type, payload) => { console.log('kyc sdk message', type, payload) }) .build() snsWebSdkInstance.launch('#sumsub-websdk-container') } // Token refresh: call your own backend → which re-calls kyc/init → returns the new token async function getNewAccessToken() { const resp = await fetch('/your-backend/kyc/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ merchant_card_id: 'mc_pcard_001' }), }) const data = await resp.json() return data.access_token // from the kyc/init response data.access_token } ``` > `/your-backend/kyc/refresh` above is **the merchant's own backend** endpoint; internally it forwards to this platform's [Initiate KYC](./physical_card_kyc_init) to mint a new token. Never expose the platform credentials used to call init to the front end. ### Key events | Event | Meaning | |-------|---------| | `idCheck.onApplicantSubmitted` | Customer submitted everything; **not yet approved** | | `idCheck.onApplicantStatusChanged` | Applicant review status changed | | `idCheck.onError` | SDK runtime error | > **Important**: front-end events are only for UI hints and flow guidance. **Always rely on the backend `card_kyc` callback / [`kyc/status`](./physical_card_kyc_status) query for the final verdict**; do not use front-end events to decide whether card opening may continue. --- ## access_token Lifecycle and Refresh The `access_token` is valid for about **600 seconds**; after expiry the SDK can no longer use it. Common expiry scenarios: the customer steps away and returns, lingers too long on a step, or the token is issued but the flow is not launched promptly. Renewal is handled by the **second argument** of `snsWebSdk.init(accessToken, () => getNewAccessToken())` above: when the token is about to expire the SDK calls it, and the merchant front end — via its own backend — **re-calls [Initiate KYC](./physical_card_kyc_init)** to mint a new token and returns it to the SDK, transparent to the customer. | Point | Detail | |-------|--------| | Idempotent | Re-calling init **reuses the same verification session** and only re-issues a token — it does **not** reset captured progress or restart verification | | `external_user_id` unchanged | Every init returns the same `external_user_id` for the card (its `card_id`) | | Recommended timing | Fetch the first token **right before launching** the verification flow, to avoid issuing it too early and letting it expire idle | --- ## KYC States and Transitions | `status` | Meaning | Entered when | Card status link | Merchant action | |----------|---------|--------------|------------------|-----------------| | 0 | Not started | init never called | card stays 9 | call init | | 1 | Pending | after init / after capture submitted | card stays 9 | await callback or poll | | 2 | Approved | verdict GREEN | card auto **9→7** | call [Assign Card Number](./physical_card_assign) | | 3 | Rejected | verdict RED | card stays 9 | check `retry_allowed`, retry or cancel | > Review may be **automated** or **manual**; a manual review may **override** an existing verdict (e.g. auto-rejected then manually approved), so a card's result **may be updated and pushed more than once**. --- ## Learning the Review Result The result is produced **asynchronously**. Two channels are provided; **use both** (callback primary, polling fallback). ### Channel 1: card_kyc Callback (recommended) Once `webhook_card_kyc_url` is registered in the merchant portal, the result is pushed when review completes (encrypted + signed, like other outbound callbacks). Decrypted payload: ```json { "card_id": "1234567890123456789", "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G", "status": 2, "retry_allowed": false, "reason": "", "reviewed_at": "2026-06-26T10:30:00+08:00" } ``` | Field | Meaning | |-------|---------| | `status` | `2`=approved, `3`=rejected | | `retry_allowed` | Whether KYC may be re-initiated after rejection | | `reason` | Rejection reason (generic description, optional) | | `reviewed_at` | Review time (RFC3339) | - **Multiple pushes**: a card's result may be pushed more than once (including manual overrides); **treat the record with the greatest `reviewed_at` as final** and ignore earlier ones. - Return HTTP `200` + plain text `ok`, otherwise it retries with exponential backoff. - Field details: [Webhook · card_kyc](./webhook). ### Channel 2: Polling (fallback) When no callback is registered, or none has arrived for a while, call [Query KYC Status](./physical_card_kyc_status) to fetch the latest result: ```json // POST /api/v1/card/physical/kyc/status { "merchant_card_id": "mc_pcard_001", "card_type": "PHYSICAL-G" } ``` ```json { "code": 0, "message": "success", "data": { "status": 2, "retry_allowed": false, "review_answer": "GREEN", "reviewed_at": "2026-06-26T10:30:00+08:00" } } ``` - Suggested cadence: after the customer finishes capture, poll every few to tens of seconds until `status` is `2` or `3`. --- ## Rejection and Retry When review returns `status=3` (rejected), handle by `retry_allowed`: - **`retry_allowed=true`** (retryable — common for blurry document images, poor lighting, failed liveness, and other correctable issues): have the customer **re-[initiate KYC](./physical_card_kyc_init)** (repeat steps 1–2) and submit again. The card stays at `9`; **no need to re-apply**. - **`retry_allowed=false`** (not retryable — e.g. the document does not meet requirements): this verification cannot be retried; guide the customer to [Cancel KYC](./physical_card_kyc_cancel). ```json // rejected and retryable { "status": 3, "retry_allowed": true, "review_answer": "RED", "reviewed_at": "2026-06-26T10:30:00+08:00" } ``` --- ## Abandonment and Timeout - **Active abandonment**: when the customer no longer wishes to continue, call [Cancel KYC](./physical_card_kyc_cancel); the card moves to the failed state (`status=3`) and the opening quota is refunded. - **Timeout auto-cancel**: an application that does not complete KYC within the allowed period is cancelled automatically and refunded, and a `card_kyc` (`status=3`) notification is pushed. - After cancellation (manual or timeout), to open a new card use a **new `merchant_card_id`** starting from Apply. --- ## Card-Open Result Notification (card_open) To stay consistent with other card types (every open-card attempt concludes with one `card_open` success/failure), PHYSICAL-G also pushes `card_open` (`status=3` failed) when the open-card **terminally fails**, **in addition to** `card_kyc` (they do not replace each other): | Scenario | Final card status | Pushed | |----------|-------------------|--------| | KYC approved → activated | 2 activated | `card_open`(status=2 success) | | Non-retryable rejection (`retry_allowed=false`) | 3 failed | `card_kyc`(rejected) + `card_open`(status=3) | | Merchant cancel | 3 failed | `card_open`(status=3) | | Timeout auto-cancel | 3 failed | `card_kyc`(timeout) + `card_open`(status=3) | | **Retryable rejection (`retry_allowed=true`)** | **stays 9** | **only `card_kyc`(rejected, retry_allowed=true); no card_open** (the open-card has not failed; the customer can retry) | > In short: `card_open`(failed) is pushed only on terminal failure; a retryable rejection does not push it. The `card_open` failure payload (`status=3` + `failure_reason`) is documented in [Webhook · card_open](./webhook). --- ## Error Codes (Initiate KYC) | code | Meaning | Action | |------|---------|--------| | 10010 | Card order not found | Call [Apply](./physical_card_apply) first | | 10033 | KYC service unavailable | Retry later; contact support if it persists | | 10044 | Card status does not allow initiating KYC | The card must be at `9` (pending KYC) | --- ## Best Practices - **Fetch the token just in time**: call init only right before launching the verification UI, to avoid issuing it too early and letting it expire idle. - **Use both result channels**: register the `webhook_card_kyc_url` callback and also poll [kyc/status](./physical_card_kyc_status) as a fallback after front-end capture completes. - **Take the latest by `reviewed_at`**: results may be pushed multiple times; always treat the greatest `reviewed_at` as final. - **Branch on rejection**: use `retry_allowed` to separate "guide retry" from "guide cancel", so customers don't retry endlessly when retry is not possible. --- # Physical Card Init KYC ## Description Initiate identity verification (KYC) for a physical card in the pending-KYC state (`status=9`). The endpoint returns an `access_token` the merchant uses to initialize the **Sumsub WebSDK**, letting the end customer complete document upload and liveness checks in the front end. The review result is delivered asynchronously via the `card_kyc` [webhook](./webhook) (you may also [query the KYC status](./physical_card_kyc_status)). **Prerequisite**: the card must first be created via [Apply](./physical_card_apply) and be in `status=9` (pending KYC). --- ## URL ``` ${baseUrl}/api/v1/card/physical/kyc/init ``` ## Method - POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | merchant_card_id | Yes | string | Merchant card ID (a created card pending KYC) | | card_type | Yes | string | Card type, fixed `PHYSICAL-G` | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "access_token": "_act-sbx-xxxxxxxx", "external_user_id": "1234567890123456789", "status": 1 } } ``` --- ## Response Fields ### data | Name | Type | Description | |------|------|-------------| | access_token | string | Temporary token to initialize the Sumsub WebSDK (expires in ~600s) | | external_user_id | string | KYC user identifier | | status | int | KYC status: `1`-pending `2`-approved `3`-rejected (always `1` right after init) | --- ## Integration Notes - The merchant front end initializes the **Sumsub WebSDK** with the returned `access_token` to guide the end customer through document upload and liveness. See the [KYC Integration Guide](./physical_card_kyc_guide) for the full front-end integration code (load, init, token refresh, events). - **`access_token` expires (~600s)**: when it expires, call this endpoint again to obtain a fresh token (the endpoint is idempotent — it only re-issues a token and does not restart the verification). Fetch it right before launching the flow. - The result is asynchronous: delivered via the `card_kyc` [webhook](./webhook), or fetched via [Query KYC Status](./physical_card_kyc_status). - After approval, the card status advances automatically `9 → 7` (pending assignment) and can be [assigned a card number](./physical_card_assign). --- ## Error Codes | code | Description | |------|-------------| | 10010 | Card order not found | | 10033 | KYC service unavailable | | 10044 | Card status does not allow initiating KYC (must be created to pending-KYC first) | --- # Physical Card KYC Status ## Description Query the KYC review status of a physical card. May be called at any time while awaiting the callback to obtain the latest result. --- ## URL ``` ${baseUrl}/api/v1/card/physical/kyc/status ``` ## Method - POST - Content-Type: application/json --- ## Request Parameters | Name | Required | Type | Description | |------|----------|------|-------------| | merchant_card_id | Yes | string | Merchant card ID | | card_type | Yes | string | Card type, fixed `PHYSICAL-G` | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "status": 3, "retry_allowed": true, "review_answer": "RED", "reviewed_at": "2026-06-26T18:00:00+08:00" } } ``` --- ## Response Fields ### data | Name | Type | Description | |------|------|-------------| | status | int | KYC status: `0`-not started `1`-pending `2`-approved `3`-rejected | | retry_allowed | bool | Whether KYC may be re-initiated after rejection | | review_answer | string | Review conclusion (optional) | | reviewed_at | string | Review time (optional, RFC3339) | --- ## Notes - When `status=2` (approved), the card status advances automatically `9 → 7` and can be assigned a card number. - When `status=3` (rejected) and `retry_allowed=true`, the end customer may [re-initiate KYC](./physical_card_kyc_init) and submit again. --- # Signature and Encryption ## Overview Card API uses **RSA-PSS + SHA-256** signature authentication and **AES-256-GCM** request body encryption to ensure request integrity, source authenticity, and data confidentiality. --- ## Key Preparation Merchants need to prepare the following keys and configure them in the dashboard: | Key | Purpose | Format | |-----|---------|--------| | Merchant RSA Public Key | Platform verifies signature | PEM or Base64 encoded | | Merchant RSA Private Key | Merchant signs requests (keep secure, do not upload) | PEM | | Merchant AES Key | Request body encryption/decryption | 32 bytes (Hex or Base64 encoded) | ### AES Key Format The AES key is 32 bytes (256-bit) and supports the following storage formats: | Encoding | String Length | Example | |----------|-------------|---------| | Hex encoded | 64 characters | `a1b2c3d4...` (64 hexadecimal characters) | | Base64 encoded | 44 characters | `obLD1E5G...==` (standard Base64 with padding) | > **Note**: The platform determines the format by string length (64 chars → Hex, 44 chars → Base64). Ensure the key length is correct and do not add spaces or newlines around the key. --- ## Request Signing Flow ### 1. AES-256-GCM Encrypt Request Body Encrypt the JSON request body plaintext with AES-256-GCM, then wrap it in a `data` field: ``` plaintext = '{"merchant_card_id":"mc_001","card_type":"VIRTUAL-V",...}' ciphertext = AES-256-GCM-Encrypt(plaintext, aes_key) // Output format: Base64(nonce[12 bytes] + ciphertext + tag) request_body = '{"data":"' + ciphertext + '"}' ``` The ciphertext format is `Base64(nonce + ciphertext + tag)`, where nonce is a 12-byte random value and tag is the GCM authentication tag. ### 2. Build Signature Input The signature input is a concatenation of three parts (no separator): ``` sign_input = request_body + timestamp + nonce ``` - `request_body`: The complete JSON request body from step 1 (i.e., `{"data":"..."}`) - `timestamp`: Unix timestamp in seconds (string) - `nonce`: Unique random string (UUID or random hex) ### 3. RSA-PSS Signature Sign the input with the merchant's RSA private key using RSA-PSS + SHA-256: ``` hash = SHA-256(sign_input) signature = RSA-PSS-Sign(hash, merchant_private_key) ``` ### 4. Set HTTP Headers | Header | Value | Description | |--------|-------|-------------| | X-App-Id | Merchant App ID | Merchant identifier (assigned by platform) | | X-Signature | Base64(signature) | Base64 encoded signature from step 3 | | X-Timestamp | Unix timestamp (seconds) | Valid within ±5 minutes | | X-Nonce | Unique random string | Anti-replay, must not be reused | | Content-Type | application/json | Fixed value | ### 5. Send Request ``` POST ${baseUrl}/api/v1/card/... Content-Type: application/json X-App-Id: your_app_id X-Signature: Base64EncodedSignature X-Timestamp: 1711929600 X-Nonce: 550e8400-e29b-41d4-a716-446655440000 {"data":""} ``` --- ## Platform Verification Flow 1. Validate `X-Timestamp` (±5 minutes) 2. Read encrypted request body 3. Look up merchant config via `X-App-Id` to get RSA public key and AES key 4. Verify `X-Signature` using merchant's RSA public key 5. Check `X-Nonce` has not been used before (anti-replay) 6. Decrypt request body with AES key to get plaintext JSON 7. Parse JSON parameters and execute business logic --- ## Response Encryption **Sensitive fields** in API responses (card number, CVV, expiry) are encrypted with the merchant's AES key: ```json { "code": 0, "message": "success", "data": { "card_id": "1234567890", "card_number": "", "cvv": "", "expiry_month": "", "expiry_year": "", "status": 2 } } ``` Merchants must decrypt these fields with their AES key. Non-sensitive fields (card_id, status, etc.) are in plaintext. --- ## Security Requirements - **Timestamp validity**: ±5 minutes, expired requests are rejected - **Nonce uniqueness**: Each request must use a different nonce; duplicates within 10 minutes are rejected - **RSA key length**: 2048-bit or above recommended - **AES key length**: Fixed 32 bytes (256-bit) - **Private key security**: RSA private key must only be stored on the merchant side, never uploaded to the platform --- ## IP Whitelist - All API requests are validated against the source IP address - Merchants must configure an IP whitelist in "System Settings" before calling the API - Requests from unconfigured or non-whitelisted IPs will receive a **403** error - Supports individual IP addresses (e.g. `1.2.3.4`) and CIDR notation (e.g. `10.0.0.0/8`) --- ## Error Codes | HTTP Status | Error Message | Description | |------------|---------------|-------------| | 401 | Missing required headers | Header missing | | 401 | Request expired | Timestamp outside ±5 minutes | | 401 | Invalid merchant ID | AppKey not found | | 403 | IP address not in whitelist | Source IP not in whitelist configuration | | 401 | Signature verification failed | RSA-PSS verification failed | | 401 | Request already processed (duplicate nonce) | Nonce replay | | 401 | Request body decryption failed | AES decryption failed | --- ## Code Examples ### Go ```go package main import ( "crypto" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "fmt" "io" "net/http" "strings" "strconv" "time" "github.com/google/uuid" ) // ==================== AES-256-GCM Encryption ==================== // encryptAESGCM encrypts plaintext using AES-256-GCM. // Returns Base64(nonce[12 bytes] + ciphertext + tag[16 bytes]). func encryptAESGCM(plaintext, key []byte) (string, error) { block, err := aes.NewCipher(key) // key must be 32 bytes if err != nil { return "", fmt.Errorf("failed to create AES cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { return "", fmt.Errorf("failed to create GCM: %w", err) } // Generate 12-byte random nonce nonce := make([]byte, gcm.NonceSize()) // 12 bytes if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return "", fmt.Errorf("failed to generate nonce: %w", err) } // Seal output = nonce + ciphertext + tag (nonce prepended as dst prefix) ciphertext := gcm.Seal(nonce, nonce, plaintext, nil) return base64.StdEncoding.EncodeToString(ciphertext), nil } // ==================== AES-256-GCM Decryption ==================== // decryptAESGCM decrypts a Base64-encoded AES-GCM ciphertext. // Input format: Base64(nonce[12 bytes] + ciphertext + tag[16 bytes]). func decryptAESGCM(ciphertextB64 string, key []byte) (string, error) { data, err := base64.StdEncoding.DecodeString(ciphertextB64) if err != nil { return "", fmt.Errorf("Base64 decode failed: %w", err) } block, err := aes.NewCipher(key) if err != nil { return "", fmt.Errorf("failed to create AES cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { return "", fmt.Errorf("failed to create GCM: %w", err) } nonceSize := gcm.NonceSize() // 12 bytes if len(data) < nonceSize { return "", fmt.Errorf("ciphertext too short") } nonce := data[:nonceSize] ciphertext := data[nonceSize:] plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { return "", fmt.Errorf("AES-GCM decryption failed: %w", err) } return string(plaintext), nil } // ==================== RSA-PSS Signing ==================== // signRSAPSS signs data using RSA-PSS + SHA-256. // privateKeyPEM: PEM-formatted RSA private key (PKCS#8 or PKCS#1). // Returns Base64-encoded signature. func signRSAPSS(data []byte, privateKeyPEM string) (string, error) { block, _ := pem.Decode([]byte(privateKeyPEM)) if block == nil { return "", fmt.Errorf("PEM decode failed") } // Support both PKCS#8 and PKCS#1 formats var rsaKey *rsa.PrivateKey key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { rsaKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return "", fmt.Errorf("failed to parse private key: %w", err) } } else { var ok bool rsaKey, ok = key.(*rsa.PrivateKey) if !ok { return "", fmt.Errorf("not an RSA private key") } } // SHA-256 hash hash := sha256.Sum256(data) // RSA-PSS sign (nil opts = salt length equals hash length, 32 bytes) signature, err := rsa.SignPSS(rand.Reader, rsaKey, crypto.SHA256, hash[:], nil) if err != nil { return "", fmt.Errorf("RSA-PSS signing failed: %w", err) } return base64.StdEncoding.EncodeToString(signature), nil } // ==================== Decode AES Key ==================== // decodeAESKey determines AES key encoding by length. // 64 chars → Hex, 44 chars → Base64, 32 chars → raw bytes. func decodeAESKey(keyStr string) ([]byte, error) { switch len(keyStr) { case 64: return hex.DecodeString(keyStr) case 44: return base64.StdEncoding.DecodeString(keyStr) case 32: return []byte(keyStr), nil default: return nil, fmt.Errorf("unsupported AES key length: %d", len(keyStr)) } } // ==================== Send Signed Request ==================== func sendSignedRequest(url, appID, privateKeyPEM string, aesKey, payload []byte) (*http.Response, error) { // 1. AES-GCM encrypt the request body encrypted, err := encryptAESGCM(payload, aesKey) if err != nil { return nil, fmt.Errorf("failed to encrypt request body: %w", err) } // 2. Build the encrypted request body {"data":"..."} body, _ := json.Marshal(map[string]string{"data": encrypted}) // 3. Generate timestamp and nonce timestamp := strconv.FormatInt(time.Now().Unix(), 10) // Unix seconds nonce := uuid.New().String() // 4. Build signature input = body + timestamp + nonce (no separator) signInput := string(body) + timestamp + nonce // 5. RSA-PSS sign signature, err := signRSAPSS([]byte(signInput), privateKeyPEM) if err != nil { return nil, fmt.Errorf("signing failed: %w", err) } // 6. Build HTTP request req, err := http.NewRequest("POST", url, strings.NewReader(string(body))) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-App-Id", appID) req.Header.Set("X-Signature", signature) req.Header.Set("X-Timestamp", timestamp) req.Header.Set("X-Nonce", nonce) return http.DefaultClient.Do(req) } // ==================== Usage Example ==================== func main() { // --- Configuration (replace with actual values) --- appID := "your_app_id" aesKeyStr := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" // 64-char Hex privateKeyPEM := `-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASC...(replace with actual key) -----END PRIVATE KEY-----` baseURL := "https://api.example.com" // 1. Decode AES key aesKey, err := decodeAESKey(aesKeyStr) if err != nil { panic(err) } // 2. Build request payload payload := []byte(`{"merchant_card_id":"mc_001","card_type":"VIRTUAL-V","amount":"100.00"}`) // 3. Send signed request resp, err := sendSignedRequest(baseURL+"/api/v1/card/apply", appID, privateKeyPEM, aesKey, payload) if err != nil { panic(err) } defer resp.Body.Close() // 4. Parse response var result struct { Code int `json:"code"` Message string `json:"message"` Data struct { CardID string `json:"card_id"` CardNumber string `json:"card_number"` // AES-GCM encrypted Cvv string `json:"cvv"` // AES-GCM encrypted ExpiryMonth string `json:"expiry_month"` // AES-GCM encrypted ExpiryYear string `json:"expiry_year"` // AES-GCM encrypted Status int `json:"status"` } `json:"data"` } respBody, _ := io.ReadAll(resp.Body) json.Unmarshal(respBody, &result) // 5. Decrypt sensitive fields if result.Data.CardNumber != "" { cardNumber, err := decryptAESGCM(result.Data.CardNumber, aesKey) if err != nil { panic(err) } fmt.Println("Card number:", cardNumber) } if result.Data.Cvv != "" { cvv, _ := decryptAESGCM(result.Data.Cvv, aesKey) fmt.Println("CVV:", cvv) } } ``` ### Java ```java import javax.crypto.Cipher; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; 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.security.KeyFactory; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Signature; import java.security.MessageDigest; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PSSParameterSpec; import java.util.Base64; import java.util.UUID; /** * Card API signing and encryption example. * Requires: JDK 11+ (standard library only, no third-party dependencies). */ public class CardApiClient { private static final int GCM_NONCE_LENGTH = 12; // 12 bytes private static final int GCM_TAG_LENGTH = 128; // 128 bits // ==================== AES-256-GCM Encryption ==================== /** * AES-256-GCM encryption. * @return Base64(nonce[12 bytes] + ciphertext + tag[16 bytes]) */ public static String encryptAESGCM(String plaintext, byte[] key) throws Exception { // Generate 12-byte random nonce byte[] nonce = new byte[GCM_NONCE_LENGTH]; new SecureRandom().nextBytes(nonce); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, nonce); cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec); byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); // Concatenate: nonce + ciphertext + tag (Java GCM appends tag to ciphertext) byte[] result = new byte[nonce.length + ciphertext.length]; System.arraycopy(nonce, 0, result, 0, nonce.length); System.arraycopy(ciphertext, 0, result, nonce.length, ciphertext.length); return Base64.getEncoder().encodeToString(result); } // ==================== AES-256-GCM Decryption ==================== /** * AES-256-GCM decryption. * @param ciphertextB64 Base64(nonce[12 bytes] + ciphertext + tag[16 bytes]) */ public static String decryptAESGCM(String ciphertextB64, byte[] key) throws Exception { byte[] data = Base64.getDecoder().decode(ciphertextB64); // Split nonce and ciphertext+tag byte[] nonce = new byte[GCM_NONCE_LENGTH]; byte[] ciphertext = new byte[data.length - GCM_NONCE_LENGTH]; System.arraycopy(data, 0, nonce, 0, GCM_NONCE_LENGTH); System.arraycopy(data, GCM_NONCE_LENGTH, ciphertext, 0, ciphertext.length); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, nonce); cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec); byte[] plaintext = cipher.doFinal(ciphertext); return new String(plaintext, StandardCharsets.UTF_8); } // ==================== RSA-PSS Signing ==================== /** * RSA-PSS + SHA-256 signing. * @param privateKeyPEM PEM-formatted RSA private key (PKCS#8) * @return Base64-encoded signature */ public static String signRSAPSS(byte[] data, String privateKeyPEM) throws Exception { // Parse PEM private key String keyContent = privateKeyPEM .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); byte[] keyBytes = Base64.getDecoder().decode(keyContent); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); // SHA-256 hash MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(data); // RSA-PSS sign (salt length = 32 bytes, matching Go default) Signature signature = Signature.getInstance("RSASSA-PSS"); PSSParameterSpec pssSpec = new PSSParameterSpec( "SHA-256", // hash algorithm "MGF1", // mask generation function MGF1ParameterSpec.SHA256, // MGF1 hash algorithm 32, // salt length (= SHA-256 output length) 1 // trailer field (fixed) ); signature.setParameter(pssSpec); signature.initSign(privateKey); signature.update(data); // Note: Java Signature hashes internally, pass raw data return Base64.getEncoder().encodeToString(signature.sign()); } // ==================== Decode AES Key ==================== /** * Determines AES key encoding by length. * 64 chars → Hex, 44 chars → Base64, 32 chars → raw bytes. */ public static byte[] decodeAESKey(String keyStr) { switch (keyStr.length()) { case 64: // Hex encoded byte[] key = new byte[32]; for (int i = 0; i < 32; i++) { key[i] = (byte) Integer.parseInt(keyStr.substring(i * 2, i * 2 + 2), 16); } return key; case 44: // Base64 encoded return Base64.getDecoder().decode(keyStr); case 32: // Raw bytes return keyStr.getBytes(StandardCharsets.UTF_8); default: throw new IllegalArgumentException("Unsupported AES key length: " + keyStr.length()); } } // ==================== Send Signed Request ==================== public static HttpResponse sendSignedRequest( String url, String appId, String privateKeyPEM, byte[] aesKey, String payload ) throws Exception { // 1. AES-GCM encrypt the request body String encrypted = encryptAESGCM(payload, aesKey); // 2. Build the encrypted request body {"data":"..."} String body = "{\"data\":\"" + encrypted + "\"}"; // 3. Generate timestamp and nonce String timestamp = String.valueOf(System.currentTimeMillis() / 1000); // Unix seconds String nonce = UUID.randomUUID().toString(); // 4. Build signature input = body + timestamp + nonce (no separator) String signInput = body + timestamp + nonce; // 5. RSA-PSS sign String signature = signRSAPSS(signInput.getBytes(StandardCharsets.UTF_8), privateKeyPEM); // 6. Send HTTP request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Content-Type", "application/json") .header("X-App-Id", appId) .header("X-Signature", signature) .header("X-Timestamp", timestamp) .header("X-Nonce", nonce) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); return client.send(request, HttpResponse.BodyHandlers.ofString()); } // ==================== Usage Example ==================== public static void main(String[] args) throws Exception { // --- Configuration (replace with actual values) --- String appId = "your_app_id"; String aesKeyStr = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; // 64-char Hex String privateKeyPEM = "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASC...(replace with actual key)\n" + "-----END PRIVATE KEY-----"; String baseUrl = "https://api.example.com"; // 1. Decode AES key byte[] aesKey = decodeAESKey(aesKeyStr); // 2. Build request payload String payload = "{\"merchant_card_id\":\"mc_001\",\"card_type\":\"VIRTUAL-V\",\"amount\":\"100.00\"}"; // 3. Send signed request HttpResponse response = sendSignedRequest( baseUrl + "/api/v1/card/apply", appId, privateKeyPEM, aesKey, payload); System.out.println("Status: " + response.statusCode()); System.out.println("Response: " + response.body()); // 4. Decrypt sensitive fields from response // Assuming the response JSON has been parsed, extract encrypted field values String encryptedCardNumber = "..."; // From response data.card_number String cardNumber = decryptAESGCM(encryptedCardNumber, aesKey); System.out.println("Card number: " + cardNumber); } } ``` ::: tip Notes - **RSA private key format**: The Go example supports both PKCS#8 (`BEGIN PRIVATE KEY`) and PKCS#1 (`BEGIN RSA PRIVATE KEY`) formats. The Java example only supports PKCS#8 format. To convert PKCS#1 to PKCS#8, use: `openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt`. - **Java RSA-PSS signing**: `Signature.update(data)` takes **raw data** (not a hash) — Java performs SHA-256 hashing internally. Go's `rsa.SignPSS` takes **pre-hashed data**. Both are equivalent. - **AES key**: The examples use Hex encoding. Obtain your actual key from the "System Settings" page in the dashboard. ::: --- # Card Top-Up ## Brief Description Top up the balance of a virtual card. Currently supports `VIRTUAL-V` / `VIRTUAL-R` / `VIRTUAL-G` card types; the request format is identical across all three. --- ## Request URL ``` ${baseUrl}/api/v1/card/deposit ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_id | No | string | System card ID (either this or merchant_card_id, this takes priority) | | merchant_card_id | No | string | Merchant card ID | | merchant_transaction_id | Yes | string | Merchant transaction ID, unique (used for idempotency) | | amount | Yes | string | Top-up amount in base currency unit, minimum 10 | | currency | No | string | Currency code, default USD | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "transaction_id": "9876543210", "card_id": "1234567890123456", "merchant_card_id": "mc_001", "merchant_transaction_id": "tx_001", "amount": "100.00", "currency": "USD", "status": 1, "created_at": "2026-04-01T10:00:00+08:00" } } ``` --- ## Response Parameter Description ### data | Parameter | Type | Description | |-----------|------|-------------| | transaction_id | string | System transaction ID | | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | merchant_transaction_id | string | Merchant transaction ID | | amount | string | Top-up amount | | currency | string | Currency code | | status | int | Status: 1=Processing 2=Success 3=Failed | | created_at | string | Creation time | --- ## Notes - The top-up result is delivered via the `card_deposit` type in [Webhook Callbacks](./webhook) - `merchant_transaction_id` must be unique; duplicate submissions return error code `10011` --- # Transaction Records ## Brief Description Query spending transaction records of a virtual card (authorization, settlement, refund, reversal, etc.). --- ## Request URL ``` ${baseUrl}/api/v1/card/transaction/list ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|------|-------------| | card_id | No | string | System card ID (either this or merchant_card_id, this takes priority) | | merchant_card_id | No | string | Merchant card ID | | page | No | int | Page number, default 1 | | page_size | No | int | Items per page, default 10, maximum 100 | --- ## Response Example ```json { "code": 0, "message": "success", "data": { "total": 5, "page": 1, "page_size": 10, "list": [ { "transaction_id": "auth_001", "card_id": "1234567890123456", "merchant_card_id": "mc_001", "transaction_amount": "50.00", "transaction_currency": "USD", "billing_amount": "50.00", "billing_currency": "USD", "settlement_amount": "", "settlement_currency": "", "transaction_type": "Authorization", "transaction_status": "COMPLETE", "transaction_time": "2026-04-01T10:00:00+08:00", "merchant_name": "AMAZON.COM", "merchant_id": "", "merchant_category_code": "5411", "merchant_country": "US", "merchant_city": "SEATTLE" } ] } } ``` --- ## Response Parameter Description ### data | Parameter | Type | Description | |-----------|------|-------------| | total | int | Total number of transactions | | page | int | Current page number | | page_size | int | Items per page | | list | array | Transaction list | ### list Items | Parameter | Type | Description | |-----------|------|-------------| | transaction_id | string | Transaction ID | | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | transaction_amount | string | Transaction amount | | transaction_currency | string | Transaction currency code | | billing_amount | string | Billing amount | | billing_currency | string | Billing currency code | | settlement_amount | string | Settlement amount | | settlement_currency | string | Settlement currency code | | transaction_type | string | Transaction type (Authorization/Settlement/Refund/Reversal) | | transaction_status | string | Transaction status (PENDING/DECLINED/COMPLETE) | | transaction_time | string | Transaction time | | merchant_name | string | Merchant name | | merchant_id | string | Merchant ID | | merchant_category_code | string | Merchant Category Code (MCC) | | merchant_country | string | Merchant country | | merchant_city | string | Merchant city | --- ## Transaction Types | Type | Description | |------|-------------| | Authorization | Spending authorization | | Settlement | Settlement | | Refund | Spending refund | | Reversal | Authorization reversal | --- # Webhook Callbacks ## Overview When card events occur, WorldCard will proactively push notifications to the callback URL configured by the merchant. There are 7 callback types in total. --- ## General Specifications ### Push Method - Method: POST - Content-Type: application/json - The request body is encrypted with **AES-256-GCM**, and the signature is placed in the HTTP Header ### HTTP Header | Header | Description | |--------|-------------| | X-App-Id | Merchant App ID | | X-Signature | RSA-PSS + SHA-256 signature (Base64 encoded) | | X-Timestamp | Timestamp (Unix milliseconds) | | X-Nonce | Unique random string (anti-replay) | ### Request Body Format ```json { "data": "" } ``` `notify_type` and business fields are inside the encrypted data and can be obtained after decryption. ### Signature Algorithm Signature input = `full request body JSON + X-Timestamp + X-Nonce` (identical to the request API signature): ``` sign_input = '{"data":"..."}' + timestamp + nonce hash = SHA-256(sign_input) signature = RSA-PSS-Sign(hash, worldcard_private_key) ``` ### Merchant Verification + Decryption Flow 1. Extract `X-Signature` (Base64 decoded), `X-Timestamp`, and `X-Nonce` from the Header 2. Build signature input = `full request body JSON + X-Timestamp + X-Nonce` 3. Verify the RSA-PSS + SHA-256 signature using the WorldCard RSA public key 4. Decrypt the `data` field using the merchant's AES key (AES-256-GCM) → plaintext JSON 5. Extract `notify_type` and business fields from the plaintext JSON ### Success Response Upon receiving the notification, the merchant must respond with: - HTTP status code: `200` - Body: `ok` (plain text) ### Retry Mechanism - Pushes that fail (non-HTTP 200, or a response body that is not the plain text `ok`) are retried automatically - At most **10 retries**, using **exponential backoff**: interval = min(2ⁿ × 30 seconds, 24 hours), where n is the number of retries already attempted - The backoff intervals are approximately: 1min → 2min → 4min → 8min → 16min → 32min → 1h → 2h → 4.3h → 24h (capped at the 10th retry) - After all 10 retries fail, pushing stops; merchants can reconcile proactively via the corresponding query API --- ## Callback URL Configuration Each callback type corresponds to an independent callback URL configuration item, with no effect on one another: | notify_type | Configuration Field | Description | |-------------|-------------------|-------------| | card_open | webhook_card_open_url | Card issuance result notification | | card_deposit | webhook_card_deposit_url | Top-up result notification | | card_close | webhook_card_close_url | Card closure completion notification | | card_status_change | webhook_card_status_change_url | Card status change notification | | card_billing | webhook_card_billing_url | Transaction billing notification | | card_3ds | webhook_card_3ds_url | 3DS OTP notification | | card_kyc | webhook_card_kyc_url | KYC review result notification | **How to configure**: The callback URLs above are set per callback type in the **merchant portal** (log in to the merchant management console), not via the B2B signed API. Note: - A given type of notification is only pushed when the corresponding callback URL has been configured; if it is not configured, that callback type is skipped. - Configuration changes take effect immediately and apply only to events generated thereafter (historical events are not re-pushed). - It is recommended to configure all 6 callback types to avoid missing critical notifications such as card issuance, top-up, and billing. --- ## 1. Card Issuance Callback (card_open) Pushed after the card application has been processed. ### Decrypted Payload | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | card_number | string | Card number (16-digit real PAN on success; the card number is available when the callback is pushed — merchants do not need to handle null or masked values) | | status | int | Status: 2=Success 3=Failed | | amount | string | Card issuance amount | | currency | string | Currency code | | create_time | string | Creation time (RFC3339) | | complete_time | string | Completion time (RFC3339) | | failure_reason | string | Failure reason (present on failure) | > PHYSICAL-G physical card: a successful activation pushes `status=2`; a **terminal open-card failure** (non-retryable KYC rejection / cancel / timeout, card moves to failed) also pushes one `status=3` + `failure_reason`. See [KYC Integration Guide · Card-Open Result Notification](./physical_card_kyc_guide). --- ## 2. Top-Up Callback (card_deposit) Pushed after the top-up has been processed. ### Decrypted Payload | Parameter | Type | Description | |-----------|------|-------------| | transaction_id | string | System transaction ID | | merchant_transaction_id | string | Merchant transaction ID | | card_id | string | System card ID | | amount | string | Top-up amount | | settle_amount | string | Actual settlement amount | | currency | string | Currency code | | status | int | Status: 1=Success 2=Failed | | create_time | string | Creation time | | complete_time | string | Completion time | | failure_reason | string | Failure reason | --- ## 3. Card Closure Callback (card_close) Pushed after the card closure is complete. `amount`/`currency` represent the remaining balance on the card; the merchant should refund the user accordingly. ### Decrypted Payload | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | card_number | string | Card number (guaranteed to be a 16-digit real PAN; the card number is available when the callback is pushed — merchants do not need to handle null or masked values) | | card_status | string | Card status. Enum: `activated`=Normal / `inactive`=Not activated / `frozen`=Frozen / `cancelled`=Cancelled; usually `cancelled` once card closure is complete | | amount | string | Remaining balance on the card | | currency | string | Currency code | | complete_time | string | Completion time | --- ## 4. Card Status Change Callback (card_status_change) Pushed after a card is frozen, unfrozen, or closed. ### Decrypted Payload | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | card_number | string | Card number | | card_status | string | New status: `frozen`=Frozen / `activated`=Unfrozen / `cancelled`=Cancelled | | change_time | string | Change time | > On card closure, this callback (`card_status`=`cancelled`) and the `card_close` callback (with the refundable balance) are independent and both are pushed. --- ## 5. Billing Callback (card_billing) Pushed when a card spending transaction occurs (authorization, settlement, refund, reversal, etc.). ### Decrypted Payload | Parameter | Type | Description | |-----------|------|-------------| | transaction_id | string | Transaction ID | | card_id | string | System card ID | | card_type_id | int | Card type ID | | transaction_time | string | Transaction time | | transaction_type | string | Transaction type (Authorization/Settlement/Refund/Reversal) | | transaction_status | string | Transaction status (PENDING/DECLINED/COMPLETE) | | result_code | string | Transaction result code | | fail_reason | string | Failure reason | | transaction_amount | string | Transaction amount | | transaction_currency | string | Transaction currency code | | billing_amount | string | Billing amount | | billing_currency | string | Billing currency code | | auth_code | string | Authorization code | | merchant_name | string | Merchant name | | merchant_country | string | Merchant country | | merchant_city | string | Merchant city | | mcc | string | Merchant Category Code | | description | string | Transaction description | --- ## 6. 3DS OTP Callback (card_3ds) Pushed when a 3D Secure verification OTP code is generated for the card. ### Decrypted Payload | Parameter | Type | Description | |-----------|------|-------------| | card_id | string | System card ID | | currency | string | Transaction currency code | | amount | string | Transaction amount | | otp | string | One-time password (present for OTP type) | | detail | string | Transaction details (merchant info, etc.) | | business_type | string | Business type identifier (`ThreeDomainSecureForwarding` for 3DS page forwarding) | | expires_at | string | OTP expiration time (RFC3339) | | url | string | 3DS forwarding URL (only present for `ThreeDomainSecureForwarding` type) | --- ## 7. KYC Review Result Callback (card_kyc) Pushed when a physical card (PHYSICAL-G) completes KYC identity verification (approved / rejected). ### Decrypted payload | Name | Type | Description | |------|------|-------------| | card_id | string | System card ID | | merchant_card_id | string | Merchant card ID | | card_type | string | Card type, `PHYSICAL-G` | | status | int | KYC result: `2`-approved `3`-rejected | | retry_allowed | bool | Whether KYC may be re-initiated after rejection | | reason | string | Rejection reason (generic description, optional) | | reviewed_at | string | Review time (RFC3339) | ### Notes - After `status=2` (approved), the card status advances automatically `9 → 7` (pending card assignment); the merchant may proceed to [assign a card number](./physical_card_assign). - When `status=3` (rejected) and `retry_allowed=true`, the end customer may [re-initiate KYC](./physical_card_kyc_init). - A card's review result may be pushed more than once; treat the record with the greatest `reviewed_at` as the final conclusion. - When a card that does not complete KYC in time is cancelled automatically, a `status=3` notification is also pushed. --- # Legacy `legacy/*` contains archived documentation for the previous integration system. Use [Card API](/en-US/Card/) for new integrations. ## Shared Legacy Docs - [Common Parameters](/en-US/legacy/parameter) - [Signing and Decryption](/en-US/legacy/sign) ## Legacy Groups - [Legacy Card](/en-US/legacy/card/): legacy card and KYC APIs - [Legacy Transfer](/en-US/legacy/transfer/): legacy transfer APIs --- # Common Parameters This page applies to legacy endpoints under `legacy/card/*` and `legacy/transfer/*`. | Parameter | Example | Description | |-----------|---------|-------------| | appKey | 24feuhdi2n2ebd | appKey | | nonce | 1038343423 | A random string | | sign | 346de23ueh2uwbduhru3heu3 | Signature, see the signing method | | timestamp | 1651052350742 | Timestamp in milliseconds | --- # Signing and Decryption This page applies to legacy endpoints under `legacy/card/*` and `legacy/transfer/*`, which use the `appKey/appSecret + MD5` signing contract and AES decryption. ## Signing (sign) - Collect all **non-empty parameters** along with **appSecret**, and sort them by parameter name in ascending order. - Concatenate using `key=value` format, joining multiple pairs with `&`. - Compute a **lowercase 32-character MD5** hash of the entire concatenated string. **Signature format:** ``` md5(key1=value1&key2=value2&...&keyN=valueN) ``` **Example:** Before concatenation: ``` amount=1&appId=YTUvZeeOdx&appSecret=owfwFkDnlCuiUTYz&callbackUrl=http://127.0.0.1:8921/api/callback/test&itemId=100001&outOrderId=2975857684279803×tamp=20200717133601001&uuid=18898810602 ``` MD5 result: ``` 2f64566717836f1b62276519ac71aaf3 ``` > **Tips:** > > - `appSecret` is only used for signing. **Do not submit it as a parameter.** > - Sorting should be in full-byte ascending order. Empty-value parameters should not be included in the signature. > - Common causes of signature failure: inconsistent timestamp, missing `appSecret`, incorrect sorting, empty parameters included in signature, etc. --- ## Signing Code Examples ### Go Example ```go package main import ( "crypto/md5" "encoding/hex" "fmt" "sort" "strings" ) // Build the signature string func BuildSignString(params map[string]string, appSecret string) string { keys := make([]string, 0) for k, v := range params { if v != "" { keys = append(keys, k) } } keys = append(keys, "appSecret") sort.Strings(keys) var list []string for _, k := range keys { var val string if k == "appSecret" { val = appSecret } else { val = params[k] } if val != "" { list = append(list, fmt.Sprintf("%s=%s", k, val)) } } return strings.Join(list, "&") } func Md5Lower(s string) string { h := md5.New() h.Write([]byte(s)) return hex.EncodeToString(h.Sum(nil)) } func main() { params := map[string]string{ "amount": "1", "appId": "YTUvZeeOdx", "callbackUrl": "http://127.0.0.1:8921/api/callback/test", "itemId": "100001", "outOrderId": "2975857684279803", "timestamp": "20200717133601001", "uuid": "18898810602", } appSecret := "owfwFkDnlCuiUTYz" signStr := BuildSignString(params, appSecret) fmt.Println("String to sign:", signStr) fmt.Println("MD5 signature:", Md5Lower(signStr)) } ``` --- ## Decryption (AES) - **Algorithm**: AES/ECB/PKCS5Padding, no salt - **Key**: appSecret - **Input**: Base64-encoded ciphertext **Example:** - Ciphertext: `jvU9Vaz67Gj+Xn/qlaXKHA==` - AES Key: `uDMGUIodDeKcja0nDbIBzpfDJFIHld56` - Decrypted: `110` --- ### Go Example ```go package main import ( "crypto/aes" "crypto/cipher" "encoding/base64" "fmt" ) // Remove PKCS#7 padding func PKCS7Unpad(data []byte) []byte { padlen := int(data[len(data)-1]) if padlen > len(data) { return data } return data[:len(data)-padlen] } func AESDecryptBase64(ciphertextBase64, key string) (string, error) { data, err := base64.StdEncoding.DecodeString(ciphertextBase64) if err != nil { return "", err } // Key length should be 16/24/32 block, err := aes.NewCipher([]byte(key)) if err != nil { return "", err } if len(data)%aes.BlockSize != 0 { return "", fmt.Errorf("ciphertext is not a multiple of block size") } decrypted := make([]byte, len(data)) mode := newECBDecrypter(block) mode.CryptBlocks(decrypted, data) decrypted = PKCS7Unpad(decrypted) return string(decrypted), nil } // -------- ECB mode support below -------------- type ecb struct{ b cipher.Block } type ecbDecrypter ecb func newECBDecrypter(b cipher.Block) cipher.BlockMode { return (*ecbDecrypter)(&ecb{b}) } func (x *ecbDecrypter) BlockSize() int { return x.b.BlockSize() } func (x *ecbDecrypter) CryptBlocks(dst, src []byte) { bs := x.b.BlockSize() for len(src) > 0 { x.b.Decrypt(dst[:bs], src[:bs]) src = src[bs:] dst = dst[bs:] } } func main() { encrypted := "U88v0HHTLxUp9LUkj95AJA==" key := "sBOvCZZurSbbdJiA" // 16 bytes plain, err := AESDecryptBase64(encrypted, key) if err != nil { fmt.Println("Decryption failed:", err) } else { fmt.Println("Decrypted result:", plain) } } ``` --- --- # Legacy Card This section contains the previous card integration system, which still uses the `appKey/appSecret + MD5` contract. The content remains split into `KYC` and `CARD` groups for existing merchants. - Use [Card API](/en-US/Card/) for new integrations - Read [Common Parameters](/en-US/legacy/parameter) and [Signing and Decryption](/en-US/legacy/sign) before using legacy endpoints --- # Card Management The legacy card management APIs, grouped by card type, using `appKey + MD5` signing. ## Common - [Top Up](/en-US/legacy/card/CARD/topup) - [Top Up Query](/en-US/legacy/card/CARD/query) - [Top Up Webhook](/en-US/legacy/card/CARD/webhook) ## Card Types - [MASTER-E](/en-US/legacy/card/CARD/MASTER_E/) - [VISA-H](/en-US/legacy/card/CARD/VISA_H/) - [VIRTUAL-P](/en-US/legacy/card/CARD/VIRTUAL-P/) - [VIRTUAL-L](/en-US/legacy/card/CARD/VIRTUAL-L/) --- # Top-Up Query ## Brief Description Used to query submitted top-up orders. --- ## Request URL ``` ${baseUrl}/api/card/top/up/query ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-------------|----------|--------|------------------------------------------------------| | orderNo | No | string | Platform order number; if not empty, only this field is used for query | | merOrderNo | No | string | Channel order number | --- ## Response Example ```json { "data": { "amount": "20", "cardNumber": "6244810070000117", "completeTime": "", "createTime": "2023-04-02 17:07:09", "ext": "", "merOrderNo": "345342352343", "orderNo": "4534234363464342", "settleAmount": "20.00", "status": 0 }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description ### General Response Fields | Parameter | Example | Description | Type | |-----------|-----------|--------------------------------|-----------| | data | See below | Response data, order info | object | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success (true or false) | bool | ### data - Order Information | Parameter | Example | Description | |--------------|-----------------------|------------------------------------------| | merOrderNo | 436343534234 | Channel order number | | cardNumber | 6244810070000117 | Card number | | orderNo | 35432346345345 | Platform order number | | amount | 20.00 | Top-up amount | | settleAmount | 20.00 | Settlement amount | | status | 0 | Order status: 1-Processing 2-Success 3-Failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | ext | | Order additional information (reserved field) | --- ## Business Status Code Description | Code | Description | |--------|-----------------| | 200 | Success | | 500 | Failure | | 5201 | Order not found | --- # Top-Up ## Brief Description Used to top up a card's balance. --- ## Change Log | Date | Content | Details | | ------------ | ---------------- | ------------------------------------------------ | | 2025-06-30 | Minimum top-up limit | Physical card minimum 200, virtual card minimum 20 | --- ## Request URL ``` ${baseUrl}/api/card/top/up ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | | ----------- | -------- | ------ | -------------------------------------------------------------------- | | merOrderNo | Yes | string | Channel order number | | cardNumber | Yes | string | Card number / Account | | amount | Yes | string | Amount (up to two decimal places, truncated; different cards top up in different currencies) | | notifyUrl | No | string | Order notification URL; if not empty, a notification will be sent to this URL upon completion | | ext | No | string | Reserved field for special business processing | --- ## Response Example ```json { "data": { "cardNumber": "6244810070000117", "completeTime": "", "createTime": "2023-04-02 17:07:09", "ext": "", "merOrderNo": "345342352343", "orderNo": "4534234363464342", "settleAmount": "20.00", "status": 0 }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description ### Response Fields | Parameter | Example | Description | Note | | --------- | ------------------ | -------------------------- | -------- | | data | See below | Response data, order info | object | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success (true/false) | bool | ### data - Order Information | Parameter | Example | Description | | ------------ | -------------------- | ------------------------------------------------------------------------------------- | | merOrderNo | 436343534234 | Channel order number | | cardNumber | 6244810070000117 | Card number | | orderNo | 35432346345345 | Platform order number | | settleAmount | 20.00 | Settlement amount, up to two decimal places; if more than 2 decimals, truncated (e.g. 2.123 becomes 2.12) | | status | 1 | Order status: 1-Processing, 2-Success, 3-Failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | ext | | Order additional information, reserved field | --- ## Business Status Code Description | Code | Description | | ------- | ------------------------------------- | | 200 | Success | | 500 | Failure (query order to confirm) | | 5000 | Operation exception (query order to confirm) | | 5101 | Duplicate order submission | | 5102 | Insufficient reserve balance | --- # Top-Up Webhook ## API Description When a top-up order is completed, if a notification URL (notifyUrl) was provided when submitting the order, the platform will send a notification request to that URL. - **API URL**: Determined by the `notifyUrl` parameter in the order API - **Request Method**: POST - **Content-Type**: application/json --- ## Request Parameters | Parameter | Required | Example | Description | | ------------- | -------- | --------------------- | ---------------------------------------------- | | orderNo | Yes | 1448538596381429760 | Platform order number | | merOrderNo | Yes | 1448535323381429760 | Merchant order number | | status | Yes | 3 | Order status: 1-Processing, 2-Success, 3-Failed | | failReason | No | Processing failed | Failure reason, may be present only when status=3 | | completeTime | Yes | 20220331121212 | Order completion time | | createTime | Yes | 20220331121212 | Order creation time | | settleAmount | Yes | 20 | Settlement amount | | appKey | Yes | edy2378eh23gf29w2 | appKey | | sign | Yes | a87694dbd3be64a356a42be95e123360 | API signature | | ext | No | "" | Order additional information, reserved field | --- ## Response Example The API response body must be the string: `ok` ``` ok ``` --- ## Notes - If the API does not return `ok` as required, the platform will consider the push as failed. - On push failure, an exponential backoff retry mechanism is used: - After the first failure, retries occur every **1 minute**, with a maximum of **5 consecutive retries** (6 total attempts). - If a correct response is still not received, another push will be attempted **10 minutes** after the last failure. - If it still fails, the platform will stop further automatic pushes. Please investigate promptly and handle the related notifications manually. --- # MASTER-E The MASTER-E card management APIs. - [Card Info](/en-US/legacy/card/CARD/MASTER_E/info) - [Lock/Unlock](/en-US/legacy/card/CARD/MASTER_E/lock_unlock) - [Set ATM PIN](/en-US/legacy/card/CARD/MASTER_E/atm) - [Daily Limit](/en-US/legacy/card/CARD/MASTER_E/limit) - [Transactions](/en-US/legacy/card/CARD/MASTER_E/transaction) - [Transactions (Paged)](/en-US/legacy/card/CARD/MASTER_E/transaction-page) --- # Change/Set ATM PIN ## Brief Description Used to change or set the ATM PIN. The PIN is transmitted using AES encryption for security. --- ## Request URL - Secure (PIN encrypted): `${baseUrl}/op/card/E/change/pin/secure` - Standard (PIN in plaintext): `${baseUrl}/op/card/E/change/pin` --- ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | | ---------- | -------- | ------ | -------------------------------------------------------- | | cardNumber | Yes | string | Card number | | pin | Yes | string | 6-digit PIN; secure endpoint requires AES encryption, standard endpoint accepts plaintext | --- ## Response Example ```json { "data": "succeed", "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description | Parameter | Example | Description | Type | | --------- | -------- | -------------------------------------- | ------- | | data | succeed | Response data | string | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success: true for success, false for failure | bool | --- # Card Information ## Brief Description Query card basic information (including balance) --- ## Request URL ``` ${baseUrl}/op/card/E/info ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | | ---------- | -------- | ------ | ----------- | | cardNumber | Yes | string | Card number | --- ## Response Example ```json { "data": { "available": 0, "availableLimit": 0, "balance": 0, "cardLimit": 0, "cardNo": "4242424242459888", "dailyAtmLimit": 0, "dailyPurchaseLimit": 0, "embossedName": "HUIBIN HUANG", "expiryMonth": "12", "expiryYear": "23", "status": 3 }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description | Parameter | Example | Description | Type | | --------- | ------------ | ----------------------- | ------ | | data | See below | Response data (card info) | object | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success or not | bool | ### data - Card Information | Parameter | Example | Description | |--------------------|--------------------|------------------------------------------------------------------| | cardNo | 1234567891234567 | Card number (16 digits) | | embossedName | HUANG BINBIN | Cardholder name | | status | 3 | Card status: 1-New; 2-Assigned; 3-Activated; 4-Expired/Cancelled; 5-Reported Lost | | expiryMonth | 12 | Expiry month | | expiryYear | 2027 | Expiry year | | cardLimit | 20 | Card limit | | dailyAtmLimit | 2300 | Daily ATM limit | | dailyPurchaseLimit | 234 | Daily purchase limit | | availableLimit | 399 | Available limit | | balance | 1000 | Balance | | available | 200 | Available balance | --- # Modify Daily Limits ## Brief Description Used to modify the card's daily purchase limit and ATM limit. --- ## Request URL ``` ${baseUrl}/op/card/E/set/daily/limit ``` --- ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | | ------------------ | -------- | ------ | -------------------- | | cardNumber | Yes | string | Card number | | dailyPurchaseLimit | Yes | int | Daily purchase limit | | dailyAtmLimit | Yes | int | Daily ATM limit | --- ## Response Example ```json { "data": { "availableLimit": "0", "cardLimit": "0", "dailyAtmLimit": "100", "dailyPurchaseLimit": "100", "embossedName": "HUIBIN HUANG", "expiryMonth": "12", "expiryYear": "23" }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description | Parameter | Example | Description | Type | | --------- | -------- | ---------------------------------------- | ------------ | | data | See below | Response data (card information) | object | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success: true for success, false for failure | bool | ### data - Card Information | Parameter | Example | Description | | ------------------ | ------------------ | -------------------- | | embossedName | HUANG BINBIN | Cardholder name | | expiryMonth | 12 | Expiry month | | expiryYear | 2027 | Expiry year | | cardLimit | 20 | Card total limit | | dailyAtmLimit | 2300 | Daily ATM limit | | dailyPurchaseLimit | 234 | Daily purchase limit | | availableLimit | 399 | Available limit | --- # Lock / Unlock ## Brief Description Used for card locking (reporting lost) and unlocking. --- ## Request URL - Lock: `${baseUrl}/op/card/E/lock` - Unlock: `${baseUrl}/op/card/E/un/lock` --- ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | | ----------- | -------- | ------ | ----------- | | cardNumber | Yes | string | Card number | --- ## Response Example ```json { "data": { "availableLimit": 0, "cardLimit": 0, "cardNo": "4242424242459888", "dailyAtmLimit": 0, "dailyPurchaseLimit": 0, "expiryMonth": "12", "expiryYear": "23", "status": 3 }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description | Parameter | Example | Description | Type | | --------- | ------------------ | -------------------------- | ------- | | data | See below | Response data (card info) | object | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success (true/false) | bool | ### data - Card Information | Parameter | Example | Description | | ------------------ | ---------------- | -------------------------------------------------------------------- | | cardNo | 1234567891234567 | Card number (16 digits) | | status | 3 | Card status (1: New, 2: Assigned, 3: Activated, 4: Expired/Cancelled, 5: Reported Lost) | | expiryMonth | 12 | Expiry month | | expiryYear | 2027 | Expiry year | | cardLimit | 20 | Card limit | | dailyAtmLimit | 2300 | Daily ATM limit | | dailyPurchaseLimit | 234 | Daily purchase limit | | availableLimit | 399 | Available limit | --- # Query Transaction Records - Paginated ## Brief Description Used to query card transaction records with pagination support. - Do not pass `pageToken` for the first query - After the query returns, if `nextPageToken` or `prevPageToken` is returned, use those tokens for pagination in subsequent queries --- ## Request URL ``` ${baseUrl}/op/card/E/transaction/page ``` --- ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | | -------------- | -------- | ------ | ---------------------------------------------------------- | | cardNumber | Yes | string | Card number | | pageToken | No | string | Page token; do not pass for first query; use returned token for pagination | | pageSize | Yes | string | Number of records per page | | dateRangeFrom | No | string | Start date, format yyyy-MM-dd | | dateRangeTo | No | string | End date, format yyyy-MM-dd | --- ## Response Example ```json { "data": { "nextPageToken": "eyJpZCI6IjZhNTExYmRiLTU0NmItNGM4Mi1iOGI0LWY5Yzc1NGJlMmI4OSIsInBhZ2VTaXplIjozLCJjdXJyZW50IjoyLCJkaXJlY3Rpb24iOiJuZXh0In0=", "prevPageToken": "", "transactions": [ { "amount": "2", "cardAccountId": "938b56b2-3437-4295-8393-b5e407504c39", "cardEmbossedName": "", "cardId": "", "cardLast4": "", "createdAt": "2024-04-17 14:23:00", "currency": "HKD", "description": "增值", "entryType": "DEBIT", "id": "885d2247-27a8-4405-9db6-e5031d44e6fc", "intent": "transfer", "merchant": "{\"country\":\"\",\"name\":\"\",\"mcc\":\"\",\"category\":\"\"}", "refId": "CT156Z3CV6", "status": "posted", "xid": "accttrans_1780482111940837376" }, { "amount": "22", "cardAccountId": "938b56b2-3437-4295-8393-b5e407504c39", "cardEmbossedName": "", "cardId": "", "cardLast4": "", "createdAt": "2024-03-19 18:56:38", "currency": "HKD", "description": "test", "entryType": "DEBIT", "id": "942a75ad-acf2-4718-b7ce-3b46a71f3804", "intent": "transfer", "merchant": "{\"country\":\"\",\"name\":\"\",\"mcc\":\"\",\"category\":\"\"}", "refId": "CTB7R765KX", "status": "posted", "xid": "accttranstest_1770039366362087424" }, { "amount": "11", "cardAccountId": "938b56b2-3437-4295-8393-b5e407504c39", "cardEmbossedName": "", "cardId": "", "cardLast4": "", "createdAt": "2024-03-19 16:09:07", "currency": "HKD", "description": "test20240319", "entryType": "DEBIT", "id": "6a511bdb-546b-4c82-b8b4-f9c754be2b89", "intent": "transfer", "merchant": "{\"country\":\"\",\"name\":\"\",\"mcc\":\"\",\"category\":\"\"}", "refId": "CTJ4NT62GR", "status": "posted", "xid": "accttrans_1769997341692542976" } ] }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description | Parameter | Example | Description | Type | | --------- | -------- | ---------------------------------------- | ------------ | | data | See below | Response data | object | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success: true for success, false for failure | bool | ### data - Paginated Data | Parameter | Example | Description | | -------------- | -------------------------------------- | ------------------------ | | nextPageToken | dhd2e293jei3hr32he839jdi3nd30-skj | Next page token | | prevPageToken | dhd2e293jei3hr32he839jdi3nd30 | Previous page token | | transactions | Array | Transaction record list | ### transactions - Transaction Information | Parameter | Example | Description | | ---------------- | ------------------------------------- | ---------------------------------------------------------------- | | amount | 23434 | Amount | | cardAccountId | 17c17176-7bfe-4c1e-8bb4-b51f0524d76a | Card account ID | | cardEmbossedName | HUIB | Card embossed name | | cardId | 17c17176-7bfe-4c1e-8bb4-b51f0 | Card ID | | cardLast4 | 8922 | Last 4 digits of card number | | createdAt | 2023-02-26T22:23:26.103Z | Transaction creation date | | currency | HKD | Currency | | description | TOP UP | Description | | entryType | CREDIT | Type: CREDIT-Credit, DEBIT-Debit | | intent | topup | Transaction type: charge-Charge, refund-Refund, topup/transfer-Top-up, repay-Repay, cashback-Cashback, interest-Interest, fee-Fee, other-Other | | status | posted | Transaction status: pending-Pending, posted-Completed, declined-Declined, void-Voided | > Note: > - If the transaction status is posted (Cleared), the original transaction record status remains unchanged, and a refund will create a new transaction record. > - If the transaction status is pending (Locked), the record will become void, and the limit will be returned to the customer. There will be no additional Refund record. --- # Query Transaction Records ## Brief Description Used to query card transaction records. --- ## Request URL ``` ${baseUrl}/op/card/E/transaction ``` --- ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | | -------------- | -------- | ------ | -------------------------------- | | cardNumber | Yes | string | Card number | | pageSize | Yes | string | Number of records | | dateRangeFrom | No | string | Start date, format yyyy-MM-dd | | dateRangeTo | No | string | End date, format yyyy-MM-dd | --- ## Response Example ```json { "data": [ { "amount": "10", "cardAccountId": "938b56b2-3437-4295-8393-b5e407504c39", "cardEmbossedName": "", "cardId": "", "cardLast4": "", "createdAt": "2023-12-13T06:52:55.919Z", "currency": "HKD", "description": "增值", "entryType": "DEBIT", "id": "bb5bbae7-d987-4983-bf75-01b90c8e0f3f", "intent": "transfer", "merchant": "{\"country\":\"\",\"name\":\"\",\"mcc\":\"\",\"category\":\"\"}", "refId": "CTNAUG5382", "status": "posted" }, { "amount": "500", "cardAccountId": "938b56b2-3437-4295-8393-b5e407504c39", "cardEmbossedName": "", "cardId": "", "cardLast4": "", "createdAt": "2023-12-13T03:30:50.390Z", "currency": "HKD", "description": "CREDIT BALANCE ADJUSTMENT", "entryType": "CREDIT", "id": "da8295d2-df35-4c61-b7df-85ea8e740b51", "intent": "topup", "merchant": "{\"country\":\"\",\"name\":\"\",\"mcc\":\"\",\"category\":\"\"}", "refId": "CTD8U71NPV", "status": "posted" } ], "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description | Parameter | Example | Description | Type | | --------- | -------- | -------------------------------------- | ------ | | data | See below | Response data (transaction list) | array | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success: true for success, false for failure | bool | ### data - Transaction Record Information | Parameter | Example | Description | | ---------------- | ------------------------------------- | ---------------------------------------------------------------- | | amount | 23434 | Amount | | cardAccountId | 17c17176-7bfe-4c1e-8bb4-b51f0524d76a | Card account ID | | cardEmbossedName | HUIB | Card embossed name | | cardId | 17c17176-7bfe-4c1e-8bb4-b51f0 | Card ID | | cardLast4 | 8922 | Last 4 digits of card number | | createdAt | 2023-02-26T22:23:26.103Z | Transaction creation date | | currency | HKD | Currency | | description | TOP UP | Description | | entryType | CREDIT | Type: CREDIT-Credit, DEBIT-Debit | | intent | topup | Transaction intent: charge-Charge, refund-Refund, topup/transfer-Top-up, repay-Repay, cashback-Cashback, interest-Interest, fee-Fee, other-Other | | status | posted | Status: pending-Pending, posted-Completed, declined-Declined, void-Voided | --- # VIRTUAL-L The VIRTUAL-L virtual card management APIs. - [Get Card BIN](/en-US/legacy/card/CARD/VIRTUAL-L/head) - [Card Info](/en-US/legacy/card/CARD/VIRTUAL-L/info) - [Transactions](/en-US/legacy/card/CARD/VIRTUAL-L/transaction) - [Freeze/Unfreeze](/en-US/legacy/card/CARD/VIRTUAL-L/lock_unlock) - [Close Card](/en-US/legacy/card/CARD/VIRTUAL-L/close) - [OTP-Webhook](/en-US/legacy/card/CARD/VIRTUAL-L/opt_webhook) - [Bill-Webhook](/en-US/legacy/card/CARD/VIRTUAL-L/bill_webhook) - [close-Webhook](/en-US/legacy/card/CARD/VIRTUAL-L/close-webhook) --- # Bill-Webhook **API Description:** When the card balance changes, the platform pushes a notification to the specified backend address. **Endpoint:** Configured in the admin panel as the notification address **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Example | Description | |----------------------|----------|--------------------------------------|------------------------------------------------------------------------------| | cardNumber | Yes | 1234567812345678 | Card number | | recordNo | Yes | 245346345345 | Transaction record number | | occurTime | Yes | 2024-09-03 17:40:06 | Transaction time | | transCurrency | Yes | CNY | Transaction currency | | transCurrencyAmt | Yes | 38.48 | Transaction currency amount | | localCurrency | Yes | USD | Card currency | | localCurrencyAmt | Yes | 38.48 | Card local currency transaction amount | | respCode | Yes | 000000 | Transaction response code | | respCodeDesc | Yes | 交易成功 | Transaction response code description | | merchantName | No | WEIXIN*Meituan | Merchant information | | merchantCategoryCode | No | 5999 | Merchant category code | | approvalCode | Yes | VWAJ0R | Approval code | | crossBoardType | Yes | 1 | Cross-border type: 0-Domestic, 1-International | | transType | Yes | AUTH | Transaction type: AUTH-Consumption, REFUND-Refund, FEE-Fee, TOP UP-Top-up, REVERSAL-Reversal | | transStatus | Yes | APPROVED | Transaction status: APPROVED-Approved, DECLINED-Declined | | platformType | Yes | 消费 | Description | | appKey | Yes | edy2378eh23gf29w2 | appKey | | sign | Yes | a87694dbd3be64a356a42be95e123360 | API signature | --- ## Request Example ```json { "localCurrencyAmt": "10", "occurTime": "2024-09-03 17:40:06", "platformType": "开卡预存", "sign": "d1fd48a0268506f4a131442a27485385", "localCurrency": "USD", "transCurrency": "USD", "recordNo": "2409031607063343947", "crossBoardType": "1", "transType": "TOP UP", "respCodeDesc": "交易成功", "appKey": "psqxrezci72955wqxlhar898vx2q8lts", "respCode": "000000", "transCurrencyAmt": "10", "transStatus": "APPROVED" } ``` --- ## Response Example `ok` (body content is the string "ok") --- ## Notes 1. If `ok` is not returned as required, the platform will treat it as a push failure and use an **exponential backoff strategy** for retries. After the initial push failure, it retries every 1 minute, up to 5 times per batch. 2. If still no successful response, a new batch of retries begins after 10 minutes. This continues for up to 10 batches (maximum 50 times total). 3. Please ensure timely and correct responses to avoid message loss. --- # close-Webhook **API Description:** Submit a cancellation request first. After successful cancellation, a notification is sent. The refund amount will be returned to the reserve account. The client must handle the refund independently. The notification address must be configured in the admin panel security center. **Endpoint:** Configured in the admin panel **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Example | Description | |------------------|----------|---------------------|--------------------------------------------------------------------------| | cardNumber | Yes | 1234567812345678 | Card number | | recordNo | Yes | 245346345345 | Transaction record number | | occurTime | Yes | 2024-09-03 17:40:06 | Transaction time | | localCurrency | Yes | USD | Card currency | | localCurrencyAmt | Yes | 38.48 | Refund amount, returned to the reserve account. The card issuer must refund the customer independently | | appKey | Yes | edy2378eh23gf29w2 | appKey | | sign | Yes | a87694dbd3be64a356a42be95e123360 | API signature | --- ## Request Example ```json { "localCurrencyAmt": "10", "occurTime": "2024-09-03 17:40:06", "sign": "d1fd48a0268506f4a131442a27485385", "localCurrency": "USD", "recordNo": "2409031607063343947", "appKey": "psqxrezci72955wqxlhar898vx2q8lts", "transCurrencyAmt": "10" } ``` --- ## Response Example On success, the body must return the string `ok`: ``` ok ``` --- ## Notes 1. If `ok` is not returned as required, the platform will treat it as a push failure and use an exponential backoff strategy: after the first failure, it retries every 1 minute, up to 5 times per batch. 2. If 5 retries still receive no response, a new batch begins after a 10-minute interval. This continues for up to 10 batches, totaling 50 attempts. 3. Continued failure to respond may result in delayed processing of card cancellation refunds. Please ensure proper message reception and correct responses. --- # Close Card **API Description:** Cancel a card. If the API returns success, it only means the request has been submitted. The system will process the actual cancellation, which may take some time. An asynchronous notification will be pushed after cancellation is complete, containing refund amount and other information. --- **Request URL:** `${baseUrl}/op/card/P/close` **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |------------|----------|--------|-------------| | cardNumber | Yes | string | Card ID | --- ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|----------------------------------------------------------|---------| | data | true | Response data, true for cancellation success, false for failure | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag, true for success, false for failure | boolean | --- **Notes:** - This API only submits the cancellation request. The actual cancellation and refund process is processed asynchronously. - A dedicated notification will be pushed after cancellation is complete, containing the refund amount (returned to the reserve account). --- # Get Card BIN **API Description:** Get card BIN information for display to customers. --- **Request URL:** `${baseUrl}/op/card/L/head` `${baseUrl}/op/card/L/head/en` **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Response Example ```json { "data": [ { "ChatGPT": [ { "美国": [ { "VISA": [ "440872", "489607", "491090", "485997" ] }, { "万事达": [ "559292", "556371" ] } ] } ], "电商消费": [ { "美国": [ { "VISA": [ "491090", "489607", "485997" ] }, { "万事达": [ "559292", "556371", "556167", "553437" ] } ], "中国香港": [ { "VISA": [ "438357", "493193" ] } ] } ], "广告投放": [ { "美国": [ { "VISA": [ "491090", "489607", "483317" ] }, { "万事达": [ "559292", "556371" ] } ], "中国香港": [ { "VISA": [ "438357", "493193" ] } ] } ] } ], "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|--------------------------------------------------------------------------------|-----------| | data | jsonArray| Response data, card BIN info for display to customers. Format is fixed, content may vary | jsonArray | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag, true for success, false for failure | boolean | --- # Card Information **Brief Description:** Get card information --- **Request URL:** `${baseUrl}/op/card/L/info` **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |------------|----------|--------|-------------| | cardNumber | Yes | string | Card number | --- ## Response Example ```json { "data": { "balance": "12.00", "cardExpiryDate": "08/2026", "cardNo": "5561672373861111", "cardStatus": "1", "cardStatusDesc": "使用中", "cardUserInfo": { "birthDate": "1992-09-20", "email": "00050@gmail.com", "firstName": "dennis", "lastName": "hhb", "mobile": "9375161772" }, "cvv": "1Hqa1r3Kb6nWIpVRn8/odg==", "localCurrency": "USD", "startActiveDate": "2024-08-10" }, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|---------------------------------------------------|---------| | data | json | Response data, see data info below | object | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag: true for success, false for failure | boolean | ### data Field Description | Parameter | Example | Description | Type | |-----------------|------------------|---------------------------------------------------|------------| | balance | 3.234 | Card balance | string | | cardNo | 235344343 | Card number | string | | cvv | 23684823920432 | CVV, decrypt with the key before use. Refer to decryption docs | string | | cardExpiryDate | 09/23 | Card expiry date | string | | localCurrency | USD | Currency | string | | startActiveDate | 02/23 | Activation date | string | | cardStatus | 0 | Card status: 0-Frozen, 1-Active, 2-Cancelled | int/string | | cardUserInfo | jsonObject | Registered user information | object | #### cardUserInfo Field Description | Parameter | Example | Description | Type | |-----------|----------------|--------------------|--------| | birthDate | 1992-09-20 | Date of birth | string | | email | 232478@qq.com | Registered email | string | | firstName | dennis | First name | string | | lastName | huang | Last name | string | | mobile | 169374627 | Registered phone | string | --- # Freeze / Unfreeze **API Description:** Used to lock (freeze) or unlock (unfreeze) a card. --- **Request URL:** `${baseUrl}/op/card/L/lock` — Lock `${baseUrl}/op/card/L/un/lock` — Unlock **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |------------|----------|--------|-------------| | cardNumber | Yes | string | Card number | --- ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|---------------------------------------------------|---------| | data | true | Response data, true for success, false for failure| boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag, true for success, false for failure | boolean | --- # OTP-Webhook **API Description:** OTP notification. When a user uses the card and a verification code is required, the current OTP is pushed through this API. The OTP must be provided to the user promptly. --- **Endpoint:** Configured in the admin panel **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Example | Description | |----------------------|----------|--------------------------------------|------------------| | otp | Yes | 123456 | Verification code| | cardNo | Yes | 493193**1091 | Card number | | transactionCurrency | Yes | CNY | Currency | | transactionAmount | Yes | 23.32 | Transaction amount| | appKey | Yes | edy2378eh23gf29w2 | appKey | | sign | Yes | a87694dbd3be64a356a42be95e123360 | API signature | --- ## Response Example The response body content is the string "ok": ``` ok ``` --- **Notes:** - OTP is time-sensitive and is only notified once --- # Simulate Verification Code **Brief Description:** Used to simulate a customer triggering the verification code function. If a notification address is bound, the notification address will receive this simulated message. Only available in the test environment. --- **Request URL:** `${baseUrl}/op/card/L/simulation/otp` **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |------------|----------|--------|-------------| | cardNumber | Yes | string | Card number | --- ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|-------------------------------------------------------|---------| | data | true | Response data, true for success, false for failure | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag: true for success, false for failure | boolean | --- --- # Simulate Transaction **Brief Description:** Used to simulate a customer initiating a transaction. If a notification address is bound, the notification address will receive this simulated message. Only available in the test environment. --- **Request URL:** `${baseUrl}/op/card/L/simulation/transaction` **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |------------|----------|--------|-------------| | cardNumber | Yes | string | Card number | --- ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|-------------------------------------------------------|---------| | data | true | Response data, true for success, false for failure | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag: true for success, false for failure | boolean | --- --- # Query Transaction Records **Brief Description:** Query transaction records for a specified card number. The maximum query time range per request is 3 months. --- **Request URL:** `${baseUrl}/op/card/L/transaction` **Request Method:** POST **Request Headers:** Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |------------------|----------|--------|--------------------------------| | cardNumber | Yes | string | Card number | | pageToken | Yes | string | Page number | | pageSize | Yes | string | Number of records | | creationTimeFrom | Yes | string | Start time, format yyyy-MM-dd | | creationTimeTo | Yes | string | End time, format yyyy-MM-dd | --- ## Response Example ```json { "data": { "currentPage": 1, "pageSize": 2, "totalRecords": 3, "transactions": [ { "cardId": "2408032111001269724", "crossBoardType": "1", "localCurrency": "USD", "localCurrencyAmt": "", "occurTime": 1722682107000, "platformType": "消费", "recordNo": "2408031848060073804", "respCode": "000000", "respCodeDesc": "交易成功", "transCurrency": "CNY", "transCurrencyAmt": "38.48", "transStatus": "APPROVED", "transType": "AUTH" }, { "cardId": "2408032111001269724", "crossBoardType": "1", "localCurrency": "USD", "localCurrencyAmt": "", "occurTime": 1722682107000, "platformType": "消费", "recordNo": "2408031848060073804", "respCode": "000000", "respCodeDesc": "交易成功", "transCurrency": "CNY", "transCurrencyAmt": "38.48", "transStatus": "APPROVED", "transType": "AUTH" } ] }, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|---------------------------------------------------|---------| | data | json | Response data, see data fields below | object | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag: true for success, false for failure | boolean | ### data Field Description (transactions list items) | Parameter | Example | Description | Type | |----------------------|-------------------|-------------------------------------------------------------------------------------------------------|--------| | recordNo | 245346345345 | Transaction record number | string | | occurTime | 1722682107000 | Transaction time (timestamp) | long | | transCurrency | CNY | Transaction currency | string | | transCurrencyAmt | 38.48 | Transaction currency amount | string | | localCurrency | USD | Card currency | string | | localCurrencyAmt | 38.48 | Card local currency transaction amount | string | | respCode | 000000 | Transaction response code | string | | respCodeDesc | 交易成功 | Transaction response code description | string | | crossBoardType | 1 | Cross-border type: 0-Domestic, 1-International | string | | transType | AUTH | Transaction type (AUTH-Consumption, REFUND-Refund, FEE-Fee, TOP UP-Top-up, REVERSAL-Reversal) | string | | transStatus | APPROVED | Transaction status (APPROVED-Approved, DECLINED-Declined) | string | | platformType | 消费 | Description | string | | merchantName | WEIXIN*Meituan | Merchant information | string | | merchantCategoryCode | 5999 | Merchant category code | string | | approvalCode | VWAJ0R | Approval code | string | --- --- # VIRTUAL-P The VIRTUAL-P virtual card management APIs. For card characteristics, see [Overview](/en-US/legacy/card/CARD/VIRTUAL-P/explanation). - [Overview](/en-US/legacy/card/CARD/VIRTUAL-P/explanation) - [Get Card BIN](/en-US/legacy/card/CARD/VIRTUAL-P/head) - [Card Info](/en-US/legacy/card/CARD/VIRTUAL-P/info) - [Transactions](/en-US/legacy/card/CARD/VIRTUAL-P/transaction) - [Freeze/Unfreeze](/en-US/legacy/card/CARD/VIRTUAL-P/lock_unlock) - [Close Card](/en-US/legacy/card/CARD/VIRTUAL-P/close) - [Submit Cardholder Document](/en-US/legacy/card/CARD/VIRTUAL-P/document) - [Webhook](/en-US/legacy/card/CARD/VIRTUAL-P/webhook) --- # Close Card ## Brief Description Card cancellation operation - A successful response indicates the operation has been accepted. The system will process the card cancellation, which may take some time. - After successful cancellation, a notification will be pushed containing the refund amount information. ## Request URL ``` POST ${baseUrl}/op/card/P/close Content-Type: application/json ``` ## Request Parameters | Parameter | Required | Type | Description | | ---------- | -------- | ------ | ----------- | | cardNumber | Yes | string | Card ID | ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | | --------- | -------- | ---------------------------------------------------- | ------- | | data | true | Response data (true: cancellation successful, false: failed) | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true for success, false for failure) | boolean | --- # Submit Cardholder Document ## Description - Submit cardholder identity document for an existing P card. - Once the document is approved, the card's consumption scenes restricted by risk control will be restored. - The same card can be submitted multiple times (the latest record overwrites the previous one). ## Request URL ``` POST ${baseUrl}/op/card/P/document Content-Type: application/json ``` ## Method - Method: POST - Content-Type: application/json ## Request Parameters | Name | Required | Type | Description | | ---------- | -------- | ------ | -------------------------------------------------------------------------------------------------------- | | cardNumber | Yes | string | Card ID | | idType | Yes | string | Document type. Allowed values: `CN-RIC` (Mainland ID), `PASSPORT`, `HK-HKID` (Hong Kong ID), `DLN` (driver's license), `Government-Issued ID Card` | | faceImage | Yes | string | Front side of the document, base64 encoded. **Single image ≤ 1 MB** | | backImage | No | string | Back side of the document, base64 encoded. **Single image ≤ 1 MB**. Whether required depends on the document type | > Notes: > - `faceImage` / `backImage` do **not** participate in MD5 signature computation. > - **Each image is capped at 1 MB (original file size)**; total request body is capped at 4 MB. Exceeding limits returns `status=5001, msg="faceImage/backImage 单张图片不能超过 1MB"` or `"请求体过大"`. ## Example Response ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Fields | Name | Example | Description | Type | | -------- | -------- | ---------------------------------------------- | ------- | | data | true | Whether the operation succeeded | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Whether the operation succeeded (true / false) | boolean | ## Response Status Codes In addition to native status codes (`200` success, `5001` invalid parameters, `5006` service error, etc.), this endpoint defines two **platform-extended codes**, triggered during ownership verification: | status | Meaning | Trigger condition | | ------ | -------------------------------- | -------------------------------------------------------------------------------------------- | | 5010 | Order not found | `cardNumber` does not match any order record | | 5011 | Card does not belong to the user | The order's user ID does not match the user associated with the signing `appKey` | --- # Notes - This card differs from other card products. - The card number returned at card creation is not the actual card number but a card ID, used only for API operations. - The actual card number, expiry date, and CVV information must be obtained through the `secureUrl` in the card information. - The `secureUrl` must be re-fetched each time and becomes invalid after a single use. - The `secureUrl` can be embedded and displayed to the user. --- # Get Card BIN ## Brief Description - Get card BIN, used when creating a card ## Request URL ``` POST ${baseUrl}/op/card/P/bin Content-Type: application/json ``` ## Update Log | Update Date | Update Description | | ----------- | ------------------------------------- | | 2025-09-16 | Added supported platforms in response | ## Request Method - Method: POST - Content-Type: application/json ## Response Example ```json { "data": [ { "bin": "537100", "issuerCountry": "US", "network": "MasterCard", "platforms": [ { "platformLabel": "Invideo AI", "successRate": "1" }, { "platformLabel": "Notion", "successRate": "1" } ] }, { "bin": "428852", "issuerCountry": "US", "network": "Visa", "platforms": [ { "platformLabel": "Select Media", "successRate": "1" }, { "platformLabel": "Victoria's Secret", "successRate": "1" } ] }, { "bin": "517746", "issuerCountry": "US", "network": "MasterCard", "platforms": [ { "platformLabel": "Replicate", "successRate": "1" }, { "platformLabel": "Whoop", "successRate": "1" } ] } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters ### Top-level Response Parameters | Parameter | Example | Description | Type | |-----------|-----------|--------------------------------------------------------------------------------------|----------------| | data | jsonArray | Response data, card BIN info. Recommended to display directly for customer selection. Content may vary | Card BIN array | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true for success, false for failure) | boolean | ### data Field Structure | Parameter | Example | Description | Type | |---------------|-----------|-----------------------------------------------|----------------| | bin | 428836 | Card BIN info, display for customer to select | string | | issuerCountry | US | Issuing region | string | | network | VISA | Card network | string | | platforms | jsonArray | Supported platforms | Platform array | ### platforms Field Structure | Parameter | Example | Description | Type | |---------------|-----------|---------------|--------| | platformLabel | Replicate | Platform name | string | | successRate | 1 | Success rate | string | --- # Card Information ## Brief Description Get card information, must be used with v1-compatible interface. --- ## Request URL ``` ${baseUrl}/op/card/P/info/v2 ``` ## Request Method - **Method**: POST - **Content-Type**: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |-------------|----------|--------|-------------| | cardNumber | Yes | string | Card ID | --- ## Response Example ```json { "data": { "balance": { "available": "10", "currency": "USD", "frozen": "0", "pending": "0" }, "billingAddress": { "addressLine1": "456 Elm Street San Francisco", "addressLine2": "", "city": "DC", "country": "USA", "postalCode": "94101", "state": "WA" }, "bin": "537100", "firstName": "Dennis", "last4": "0729", "lastName": "Huang", "cardInfo": { "cardNo":"2532hd2ubdebdebdebd", "cvv":"2532hd2ubdebdebdebd", "expYear":"2532hd2ubdebdebdebd", "expMonth":"2532hd2ubdebdebdebd" }, "status": "Active" }, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|---------------------------------------------------|-----------| | data | json | Response data | data info | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag: true for success, false for failure | bool | ### data Field Description | Parameter | Example | Description | |----------------|------------|-------------------------------------------------------------------| | bin | 559292 | Card BIN | | firstName | Eli | First name | | lastName | Davis | Last name | | last4 | 1234 | Last 4 digits of card number | | status | Active | Card status: Active - In use, Frozen - Frozen, Inactive - Cancelled | | billingAddress | jsonObject | Billing address information | | balance | jsonObject | Balance information | | cardInfo | jsonObject | Card information | --- #### billingAddress Field Description | Parameter | Example | Required | Description | |--------------|--------------------|----------|----------------| | addressLine1 | 1 Westwood Drive | No | addressLine1 | | addressLine2 | 1 Westwood Drive | No | addressLine2 | | city | Fort Worth | No | City | | country | US | No | Country | | postalCode | 89328 | No | Postal code | | state | IL | No | State/Province | --- #### balance Field Description | Parameter | Example | Required | Description | |-----------|---------|----------|-------------------| | available | 10 | No | Available balance | | currency | USD | No | Currency | | frozen | 0 | Yes | Frozen | | pending | 1 | Yes | Settling | --- #### cardInfo Field Description | Parameter | Example | Required | Description | |-----------|--------------------|----------|--------------------------------------| | cardNo | 34783hddh3h3ube3e | Yes | Card number, requires AES decryption | | cvv | 34783hddh3h3ube3e | Yes | CVV, requires AES decryption | | expYear | 034783hddh3h3ube3e | Yes | Expiry year, requires AES decryption | | expMonth | 34783hddh3h3ube3e | Yes | Expiry month, requires AES decryption| --- #### status Field Description | Value | Description | |----------|-------------| | Active | In use | | Frozen | Frozen | | Inactive | Cancelled | --- # Freeze / Unfreeze ## Brief Description - Freeze or unfreeze a specified card, used to temporarily lock or unlock usage permissions. ## Request URL **Freeze Card** ``` POST ${baseUrl}/op/card/P/lock Content-Type: application/json ``` **Unfreeze Card** ``` POST ${baseUrl}/op/card/P/un/lock Content-Type: application/json ``` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | ---------- | -------- | ------ | ----------- | | cardNumber | Yes | string | Card ID | ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Type | | --------- | -------- | ------------------------------------------------- | ------- | | data | true | Response data (whether operation was successful) | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true for success, false for failure)| boolean | --- # Query Transaction Records ## Brief Description Query card transaction records. The maximum query time span is 3 months. --- ## Request URL ``` POST ${baseUrl}/op/card/P/transaction Content-Type: application/json ``` --- ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |------------------|----------|--------|------------------------------------| | cardNumber | Yes | string | Card ID | | pageToken | Yes | string | Page token (page cursor, can be empty) | | pageSize | Yes | string | Number of records per page | | creationTimeFrom | Yes | string | Start time, format yyyy-MM-dd | | creationTimeTo | Yes | string | End time, format yyyy-MM-dd | --- ## Response Example ```json { "data": { "currentPage": 1, "pageSize": 2, "totalRecords": 1, "transactions": [ { "amount": "10", "cardNumber": "5592922156696856", "currency": "USD", "debitOrCreditFlag": "D", "id": "ac4cef2d-8d1b-4ee9-a15e-b751ebaefad0", "status": "Closed", "transactionTime": "2024-11-29T02:24:14.578Z", "type": "TopUp" } ] }, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|--------------------------------------------|---------| | data | - | Response data, see table below | object | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true for success) | boolean | ### data Field Description (Transaction Record Info) | Parameter | Example | Description | |--------------------|---------------------------------------|------------------------------------------| | id | ac4cef2d-8d1b-4ee9-a15e-b751ebaefad0 | Transaction ID | | cardNumber | 4383570038788189 | Card ID | | debitOrCreditFlag | D | D: Debit (decrease), C: Credit (increase)| | transactionTime | 2024-11-29T02:24:14.578Z | Transaction time | | currency | CNY | Transaction currency | | amount | 38.48 | Transaction amount | | type | TopUp | Transaction type | | status | APPROVED | Status | | detail | WEIXIN*huidi 5210747029 CN | Transaction details | --- ### type Field Enum Values | Value | Description | |-------------|--------------------| | Consumption | Consumption | | TopUp | Top-up | | Fee | Transaction fee | | Credit | Refund | | Reversal | Reversal | | DeleteCard | Card cancellation | --- ### status Field Enum Values | Value | Description | |---------|-------------| | Approve | Approved | | Pending | Pending | | Fail | Failed | --- # Webhook ## API Description Notification messages, including card balance changes, 3ds messages, etc. ## Endpoint Configured in the admin panel ## Request Method POST Content-Type: application/json ## Exponential Backoff In case of push failure, it is recommended to use exponential backoff for retries, e.g., 1 second after the first failure, then 2 seconds, 4 seconds, 8 seconds, etc., up to a maximum of five retries. ## Request Parameters | Parameter | Required | Type | Example | Description | |-----------|----------|--------|--------------------------------------|--------------------------------| | type | Yes | string | 3ds | Notification type | | data | Yes | string | jsonString | Notification data (JSON string)| | appKey | Yes | string | edy2378eh23gf29w2 | appKey | | sign | Yes | string | a87694dbd3be64a356a42be95e123360 | API signature | ### Request Example ```json { "data": "{\"id\":\"e6a54b44-1a38-4d51-824c-6f7bf6e1fb3e\",\"currency\":\"USD\",\"amount\":\"9\",\"type\":\"TopUp\",\"cardNo\":\"5592922156696856\",\"status\":\"Closed\",\"transactionTime\":\"2024-11-29T07:14:18.157Z\"}", "sign": "dc227220340c874fec3bba43bad08b32", "appKey": "psqxrezci72955wqxlhar898vx2q8lts", "type": "CardTransaction" } ``` ## Response Example Body content returns the string `ok` directly ``` ok ``` --- ## Notification Type Descriptions ### 1. CardTransaction (Card Transaction Notification) | Parameter | Required | Type | Example | Description | |-----------------|----------|--------|----------------------------------------|------------------| | id | Yes | string | fb68813f-61cd-4d17-8265-4b5dd71ec405 | Transaction ID | | cardNo | Yes | string | 5592922156696856 | Card number | | currency | Yes | string | USD | Currency | | amount | Yes | string | 9 | Amount | | type | Yes | string | TopUp | Type | | status | Yes | string | Closed | Status | | detail | No | string | METAPAY*ADS fb.me/ads IE | Transaction details | | transactionTime | Yes | string | 2024-11-29T07:14:18.157Z | Transaction time | #### type Enum Values | Value | Description | |-------------------|--------------------------------------------------| | TopUp | Top-up | | Consumption | Consumption | | Fee | Transaction fee | | Auth | Authorization | | Credit | Refund | | Reversal | Reversal | | FrozenCard | Card frozen | | UnFrozenCard | Card unfrozen | | CardStateChange | Card status change | | DeleteCard | Card cancellation (use this to refund balance and process card closure) | #### status Enum Values | Value | Description | |---------|-------------| | Pending | Processing | | Approve | Success | | Fail | Failed | --- ### 2. DeleteCard (Card Cancellation Notification) | Parameter | Required | Type | Example | Description | |----------------|----------|--------|--------------------------|---------------------| | cardNo | Yes | string | 5592922156696856 | Card number | | currency | Yes | string | USD | Currency | | status | Yes | string | Inactive | Status | | userName | Yes | string | as aa | Username | | createTime | Yes | string | 2021-08-30T11:59:32.935Z | Creation time | | cardNoLastFour | Yes | string | 9990 | Last 4 digits of card | #### status Enum Values | Value | Description | |----------|-------------| | Active | In use | | Frozen | Frozen | | Inactive | Cancelled | --- ### 3. Card3dsOtp (3ds Verification Code) | Parameter | Required | Type | Example | Description | |-----------|----------|--------|------------------|------------------| | cardNo | Yes | string | 5592922156696856 | Card number | | currency | Yes | string | USD | Currency | | amount | Yes | string | 9 | Amount | | otp | Yes | string | 1234321 | OTP | | detail | Yes | string | | Transaction info | --- ### 4. ThreeDomainSecureForwarding (3ds Redirect) > Note: Some cards require redirecting to a specific page to submit the verification code | Parameter | Required | Type | Example | Description | |----------------|----------|--------|------------------|------------------------------------| | cardNo | Yes | string | 5592922156696856 | Card number | | currency | Yes | string | USD | Currency | | amount | Yes | string | 9 | Amount | | detail | Yes | string | | Transaction info | | timestamp | Yes | long | 1725350485682 | Timestamp | | expirationTime | Yes | long | 1725350785682 | Expiration time | | url | Yes | string | 1234321 | Redirect URL to submit verification code | --- # VISA-H The VISA-H card management APIs. - [Card Info](/en-US/legacy/card/CARD/VISA_H/info) - [Lock/Unlock](/en-US/legacy/card/CARD/VISA_H/lock_unlock) - [Set PIN](/en-US/legacy/card/CARD/VISA_H/pin) - [Transactions](/en-US/legacy/card/CARD/VISA_H/transaction) - [OOB](/en-US/legacy/card/CARD/VISA_H/OOB) - [OOB Processing](/en-US/legacy/card/CARD/VISA_H/OOB_process) - [Simulate OOB](/en-US/legacy/card/CARD/VISA_H/OOB_simulate) --- # OOB ## API Description When a customer makes an online purchase, an OOB push notification is triggered. The relevant information is displayed to the customer with a button for confirmation. For details, see the [Visa official documentation](https://developer.visa.com/pages/visa-3d-secure/payment-flows-oob). - **Endpoint**: The receiving address configured in the admin security center - **Method**: POST - **Content-Type**: application/json --- ## Request Parameters | Parameter | Required | Example | Description | | ----------- | -------- | --------------------------------- | ------------------ | | data | Yes | JSON string, see below | Authorization info | | appKey | Yes | edy2378eh23gf29w2 | appKey | | sign | Yes | a87694dbd3be64a356a42be95e123360 | API signature | **data field parameters and example:** ```json { "cardNumber": "7662222584726493096", "acsTransactionId": "41ce7a7d-9d57-4d87-b6e4-310acfa4b276", "transactionInformation": [ { "merchantName": "Amazon", "purchaseAmount": "31.81", "purchaseCurrency": "USD", "purchaseDate": 1722212389000, "merchantCountryCode": 840, "acquirerMerchantID": "784959000762203", "maskedPAN": "451888******8888" } ], "threeDsRequestorAppUrl": "", "status": 1, "createdDate": 1722212395829, "lastModifiedDate": 1722212395829, "expireDate": 1722212495829 } ``` ### data Internal Parameter Description | Field | Example | Description | | --------------------- | -------------------------- | ------------------ | | cardNumber | 7662222584726493096 | Card number | | acsTransactionId | 41ce7a7d-9d57-xxx... | Transaction ID | | settlementDate | 2024-07-17T23:00:20.609Z | Settlement time | | status | 1 | Status | | transactionInformation| Array, see table below | Transaction info | | createdDate | 1722212395829 | Created time | | lastModifiedDate | 1722212395829 | Last modified time | | expireDate | 1722212395829 | Expiry time | #### transactionInformation Field Description | Field | Example | Description | | ------------------- | ------------------ | --------------- | | merchantName | Amazon | Merchant name | | purchaseAmount | 31.81 | Amount | | purchaseCurrency | USD | Currency | | purchaseDate | 1722212389000 | Transaction time| | merchantCountryCode | 840 | Country code | | acquirerMerchantID | 784959000762203 | Merchant ID | | maskedPAN | 451888******8888 | Card number | --- ### status Field Dictionary | Value | Meaning | | ----- | ------------ | | -3 | FAILED | | -2 | CANCELLED | | -1 | TIMEOUT | | 1 | IN_PROGRESS | | 2 | ACCEPTED | | 3 | DECLINED | --- ## Response Example > Body content is the string: "ok" ``` ok ``` --- ## Important Notes - If `ok` is not returned as required, the platform will treat it as a push failure. Due to time sensitivity, failed pushes will not be retried. - If multiple pushes fail, use an **exponential backoff** strategy for retries, i.e., increase the delay between each retry. The maximum number of retries should not exceed 5. --- # OOB Processing ## Brief Description When OOB is triggered, the user receives OOB information and submits a processing action after confirmation. ## Request URL `${baseUrl}/op/card/H/oob` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | ---------------- | -------- | ------ | -------------------------------------------- | | cardNumber | Yes | string | Card number | | acsTransactionId | Yes | string | OOB transaction ID | | reply | Yes | int | Reply action, submit: 2-ACCEPTED, 3-DECLINED | ## Response Example ```json { "data": { "confirmationStatus": 2 }, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | | --------- | -------- | ------------------------- | ------- | | data | object | Response data | object | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true/false) | boolean | ### data Field Description | Parameter | Example | Description | | ------------------- | ------- | ---------------------------- | | confirmationStatus | 2 | Status, see dictionary below | #### confirmationStatus Dictionary | value | key | | ----- | ----------- | | -3 | FAILED | | -2 | CANCELLED | | -1 | TIMEOUT | | 1 | IN_PROGRESS | | 2 | ACCEPTED | | 3 | DECLINED | --- # Simulate OOB Trigger ## Brief Description Used to simulate triggering an OOB (Out of Band) notification. ## Request URL `${baseUrl}/op/card/H/oob/simulate` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | ----------- | -------- | ------ | ----------- | | cardNumber | Yes | string | Card number | ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | | --------- | -------- | --------------------------- | ------- | | data | true | Response data, success flag | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true/false) | boolean | --- # Card Information ## Brief Description Query card information and balance. ## Change Log | Change Date | Change Description | | ----------- | ------------------------------ | | 2025-04-23 | Added status field in response | ## Request URL `${baseUrl}/op/card/H/balance` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | ----------- | -------- | ------ | ----------- | | cardNumber | Yes | string | Card number | ## Response Example ```json { "data": { "availableCredit": 123, "currentBillingEndDate": 1724342400000, "cvv": "WBM3cgTCXY2Yt6ARQ82IVQ==", "expiry": "202707", "overallCreditLimit": 123, "panFirst6": "451888", "panLast4": "0229", "status": "activated", "totalBalance": 0 }, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters ### Common Parameters | Parameter | Example | Description | Schema | | --------- | -------- | ------------------------ | --------- | | data | json | Response data | data info | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag (true/false)| | ### data Field Description | Parameter | Example | Description | | --------------------- | ------------------------------------- | ------------------------------------------------------------------------ | | availableCredit | 11 | Available credit - total balance, equivalent to overallCreditLimit + totalBalance | | overallCreditLimit | 10 | Overall credit limit. Same-day top-up amounts are reflected here first and transferred to totalBalance on T+1 | | totalBalance | 11 | Total balance | | currentBillingEndDate | 1723824000000 | Current billing end date | | expiry | 202707 | Expiry date | | panFirst6 | 451888 | First 6 digits of card number | | panLast4 | 0229 | Last 4 digits of card number | | status | activated | Card status | | cvv | WBM3cgTCXY2Yt6ARQ82IVQ== | CVV, decrypt with the key before use. Refer to the decryption documentation | --- # Lock / Unlock ## Brief Description Card lock (suspend) and unlock (unsuspend) operations. ## Request URL - `POST ${baseUrl}/op/card/H/lock` — Lock - `POST ${baseUrl}/op/card/H/un/lock` — Unlock ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | |-------------|----------|--------|-------------| | cardNumber | Yes | string | Card number | ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Type | |-----------|----------|---------------------------------------------------|---------| | data | true | Response data, whether successful | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag: true for success, false for failure | boolean | --- # Modify / Set PIN ## Brief Description Used to modify or set the ATM PIN. The PIN is transmitted encrypted for enhanced security. The original endpoint `${baseUrl}/op/card/H/change/pin` has been deprecated. ## Request URL ``` ${baseUrl}/op/card/H/change/pin/secure ``` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | ---------- | -------- | ------ | -------------------------------------------------------- | | cardNumber | Yes | string | Card number | | pin | Yes | string | Encrypted value of 6-digit PIN (refer to AES encryption method) | ## Response Example ```json { "data": true, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | | --------- | -------- | -------------------------------- | ------- | | data | true | Response data, true = set successfully | boolean | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true/false) | boolean | --- # Query Transaction Records ## Brief Description Used to query transaction records. Top-up records made before midnight will generate a bill at 7:00 AM the next day. --- ## Update Log | Update Date | Update Description | | ------------ | ------------------------------------------------------------------------ | | 2024-10-16 | Added `debitOrCreditFlag` field in response | | 2024-10-16 | Updated `transactionSource` dictionary values | | 2025-03-26 | Added `isAuthorization` query parameter:
If `isAuthorization=false`, shows unsettled transactions; if `true`, shows settled transactions. Consumer orders initially appear in the `false` list and move to the `true` list after settlement. | --- ## Request URL ``` ${baseUrl}/op/card/H/transactions ``` ## Request Method - Method: POST - Content-Type: application/json --- ## Request Parameters | Parameter | Required | Type | Description | |--------------------|----------|--------|---------------------------------------------------------------| | cardNumber | Yes | string | Card number | | offset | Yes | string | Offset, starting from 0, e.g. 0/10/20 | | pageSize | Yes | string | Number of records | | isAuthorization | No | int | 0: unsettled orders, 1: settled orders | | creationTimeFrom | Yes | string | Start time, format yyyy-MM-dd | | creationTimeTo | Yes | string | End time, format yyyy-MM-dd | --- ## Response Example ```json { "data": [ { "amount": 10, "creationTime": "2024-07-17T23:00:20.609Z", "currency": "HKD", "isATM": false, "isAuthorization": false, "isInCountry": true, "isOnline": false, "isPos": false, "memo": "Repayment for order r-2342342422342343 (501)", "reversal": false, "settlementDate": "2024-07-18T00:00:00Z", "transactionId": 1000001081427, "transactionSource": "6", "transactionType": "5", "txCompleted": true, "acquirerCurrency": "", "acquirerAmount": 0, "cardAcceptorName": "" }, { "amount": 11, "creationTime": "2024-07-18T23:00:43.07Z", "currency": "HKD", "isATM": false, "isAuthorization": false, "isInCountry": true, "isOnline": false, "isPos": false, "memo": "Repayment for order r-240718180106121536 (501)", "reversal": false, "settlementDate": "2024-07-19T00:00:00Z", "transactionId": 1000001081883, "transactionSource": "6", "transactionType": "5", "txCompleted": true, "acquirerCurrency": "HKD", "acquirerAmount": 235, "cardAcceptorName": "AlipayHK*ALIPAY" } ], "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Schema | |-----------|--------------------------------------------|---------------------------------------------------|--------------------| | data | jsonArray | Response data, see table below | Transaction records| | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag: true for success, false for failure | boolean | ### data - Transaction Record Fields | Parameter | Example | Description | |--------------------|----------------------------------------------|------------------------------------------------------------------------------------| | amount | 23434 | Amount | | creationTime | 2024-07-17T23:00:20.609Z | Transaction creation time | | settlementDate | 2024-07-17T23:00:20.609Z | Settlement time | | currency | HKD | Currency | | acquirerCurrency | USD | Acquirer currency | | acquirerAmount | 23.1 | Acquirer amount | | cardAcceptorName | order r-2342342422342343 | Authorized merchant information | | isATM | false | Whether it is an ATM withdrawal | | isAuthorization | false | Whether authorized: when true, amount is 0, acquirerAmount is the actual authorized amount; amount is populated after settlement | | isInCountry | false | Whether in local country | | isOnline | false | Whether online | | isPos | false | Whether POS | | memo | Repayment for order r-2342342422342343 (501) | Transaction information | | transactionId | 1000001081427 | Transaction ID | | debitOrCreditFlag | D | D -> Debit (decrease), C -> Credit (increase) | | transactionSource | 6 | Transaction source, see dictionary below | | transactionType | 5 | Transaction type, see dictionary below | | txCompleted | true | Whether completed. After completion, records with initial amount of 0 will have the actual settlement amount | --- ## Dictionary ### transactionType - Transaction Type Dictionary | Value | Description | Details | |-------|----------------------|---------------------------------------------------------------------------------------------------------------| | 0 | unknown | | | 1 | newAccount | New account creation. | | 2 | cardSale | New card issuance. | | 3 | purchase | POS purchase, online purchase, or mobile purchase. | | 4 | refund | Refund to card or wallet. | | 5 | account funding | TopUp via POS to wallet, regardless of currency support. | | 6 | withdraw | Withdrawal via POS or ATM, or bank transfer without card, etc. | | 7 | balanceInquiry | Balance inquiry. | | 8 | p2p | Peer-to-peer transfer. | | 9 | walletTransfer | Transfer between wallets. | | 10 | exchange | Currency exchange (buyer/seller). | | 11 | exchangeMargin | Exchange margin between different currencies. | | 12 | secondPresentment | Second presentment (e.g., drafts). | | 13 | chargeback | Chargeback processing. | | 14 | adjustment | Fund adjustment. | | 15 | exchangeAdjustment | Exchange rate adjustment. | | 16 | maintenance | Account maintenance. | | 17 | chargeoff | Bad debt write-off. | | 18 | withholding | Withholding / deduction reserve. | | 19 | closeAccount | Account closure. | | 20 | reward | Reward issuance. | | 21 | shipping | Card shipping (standard delivery). | | 22 | expeditedShipping | Card expedited shipping. | | 23 | addressing | FPS (Faster Payment System) ID registration. | | 24 | settlement | Settlement. | | 25 | consolidated | Account consolidation. | | 26 | interest | Interest payment. | | 27 | cashback | Cashback. | | 28 | fee | Fee deduction. | | 29 | monthly fee | Monthly fee. | | 30 | annual fee | Annual fee. | | 31 | fastFund | Visa instant funding. | | 32 | loadReversal | Top-up reversal. | | 33 | expiry | Expired funds returned to original funder. | | 34 | moneyReceive | Mastercard fast funding. | | 35 | latePaymentFee | Late payment fee. | ### transactionSource - Transaction Source Dictionary | Value | Description | |-------|---------------------| | 0 | Unknown | | 1 | InWalletPOS | | 2 | OutOfWalletPOS | | 3 | InWalletATM | | 4 | OutOfWalletATM | | 5 | Online | | 6 | Partner = TopUp | | 7 | Customer | | 8 | Internal | | 9 | Static | | 10 | MobileCommerce | | 11 | OnUs | | 12 | OutOfWalletOnline | --- # KYC The legacy card issuance (KYC application) and query APIs, using `appKey + MD5` signing. ## APIs - [Query](/en-US/legacy/card/KYC/query) - [Webhook](/en-US/legacy/card/KYC/webhook) ## Card Types - [MASTER-E (Discontinued)](/en-US/legacy/card/KYC/MASTER-E) - [VISA-H](/en-US/legacy/card/KYC/VISA-H) - [Virtual Card](/en-US/legacy/card/KYC/VIRTUAL/) --- # MASTER-E ## Brief Description MASTER-E card KYC submission --- ## Request URL `POST ${baseUrl}/api/card/kyc/apply/master/E` Content-Type: `application/json` --- ## Request Parameters | Parameter | Required | Type | Description | | -------------- | -------- | ------- | -------------------------------------------------------------------- | | merOrderNo | Yes | string | Merchant order number | | cardNumber | No | string | Card number requiring KYC; please consult your business representative before using this field | | notifyUrl | No | string | If not empty, notifications will be sent on status changes | | firstName | Yes | string | Cardholder's Chinese first name | | lastName | Yes | string | Cardholder's English last name | | firstNameEn | Yes | string | Cardholder's English first name | | lastNameEN | Yes | string | Cardholder's English last name | | countryCode | Yes | string | Country calling code, e.g. 86 | | phone | Yes | string | Phone number, also used as delivery phone number | | email | Yes | string | Email address | | address | Yes | string | Residential address, also used as delivery address | | certType | Yes | string | ID type: 0-ID card, -1-Passport | | idNumber | Yes | string | ID card number / Passport number | | idExpiryDate | Yes | string | ID expiry date, format yyyy-MM-dd, e.g. 2015-01-01 | | country | Yes | string | Nationality, e.g. 156 | | birthday | Yes | string | Date of birth, format yyyy-MM-dd, e.g. 2015-01-01 | | annualIncome | Yes | string | Annual income in units of 10,000, calculated in HKD | | occupation | Yes | string | Occupation, refer to the "Occupation" dictionary below | | position | Yes | string | Position, refer to the "Position" dictionary below | | faceImage | No | string | Base64 front of ID card (required for Mainland China ID / HK Permanent ID); this field is excluded from signature | | backImage | No | string | Base64 back of ID card (required for Mainland China ID / HK Permanent ID); this field is excluded from signature | | passImage | No | string | Base64 passport page photo (required for passport type); this field is excluded from signature | | signImage | No | string | Base64 handwritten signature, recommended to submit; this field is excluded from signature | --- ## Response Example ```json { "data": { "completeTime": "", "createTime": "2023-04-23 20:08:35", "ext": "", "merOrderNo": "230423200834215712", "orderNo": "230423200835147904", "status": 1 }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description ### General Response | Parameter | Example | Description | Data Type | | --------- | ------------ | ----------------- | ------------- | | data | jsonObject | Response data | See below | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success or not | boolean | ### data Fields | Parameter | Example | Description | | ------------ | -------------------- | ---------------------------------------------- | | merOrderNo | 436343534234 | Merchant order number | | orderNo | 35432346345345 | Platform order number | | status | 1 | Order status: 1-Processing, 2-Success, 3-Failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | ext | | Order additional information | --- ## Position Codes | Value | Description | |-------|---------------------| | 1 | Account manage | | 2 | Senior management | | 3 | Boss | ## Occupation Codes | Value | Occupation | |-------|-------------------| | 1 | Actor | | 2 | Aircraft Mechanic | | 3 | Book Author | | 4 | Businessmen | | 5 | Cashier | | 6 | Cleaner | | 7 | Dancer | | 8 | Dentist | | 9 | Fireman | | 10 | Housewife | | 11 | Journalist | | 12 | Medical | | 13 | Assistant | | 14 | Musician | | 15 | Teacher | | 16 | Painter | | 17 | Policeman | --- # VISA-H ## Brief Description VISA-H KYC verification API --- ## Request URL ``` ${baseUrl}/api/card/kyc/apply/visa/H ``` ## Request Method - Method: `POST` - Content-Type: `application/json` --- ## Request Parameters | Parameter | Required | Type | Description | |--------------------|----------|---------|----------------------------------------------------------------------| | merOrderNo | Yes | string | Merchant order number | | cardNumber | No | string | Card number, can be used when the card number is already known | | notifyUrl | No | string | If not empty, notifications will be sent to this URL on status changes | | firstName | Yes | string | English first name | | lastName | Yes | string | English last name | | countryCode | Yes | string | Phone country code, e.g. 86 | | phone | Yes | string | Phone number | | email | Yes | string | Email address | | line1 | Yes | string | Current address - house/unit number | | line2 | Yes | string | Current address - street | | line3 | Yes | string | Current address - district | | postalCode | Yes | string | Current address - postal code | | city | Yes | string | Current address - city | | addressCountry | Yes | string | Current address - country, e.g. 156 | | deliveryLine1 | Yes | string | Delivery address - house/unit number | | deliveryLine2 | Yes | string | Delivery address - street | | deliveryLine3 | Yes | string | Delivery address - district | | deliveryPostalCode | Yes | string | Delivery address - postal code | | deliveryCity | Yes | string | Delivery address - city | | deliveryCountry | Yes | string | Delivery address - country, e.g. 156 | | certType | Yes | string | ID type: 0-ID card, 1-Passport | | country | Yes | string | ID nationality, e.g. 156 | | idNumber | Yes | string | ID number | | idIssuanceDate | Yes | string | ID issuance date, format yyyy-MM-dd | | idExpiryDate | Yes | string | ID expiry date, format yyyy-MM-dd; use 2099-12-31 for permanent validity | | faceImage | No | string | Base64 front of ID card (required for Mainland China ID / HK Permanent ID; this field is excluded from signature) | | backImage | No | string | Base64 back of ID card (required for Mainland China ID / HK Permanent ID; this field is excluded from signature) | | passImage | No | string | Base64 passport page photo (required for passport type; this field is excluded from signature) | --- ## Response Example ```json { "data": { "completeTime": "", "createTime": "2023-04-23 20:08:35", "ext": "", "merOrderNo": "230423200834215712", "orderNo": "230423200835147904", "status": 1 }, "msg": "Operation successful", "status": 200, "success": true } ``` --- ## Response Parameter Description ### Base Response Fields | Parameter | Example | Description | |-------------|----------------------|------------------------------------------| | data | jsonArray | Response data, see order info below | | msg | Operation successful | Response message | | status | 200 | Response code | | success | true | Success: true for success, false for failure | ### data - Order Information | Parameter | Example | Description | |-------------|----------------------|------------------------------------------------| | merOrderNo | 436343534234 | Merchant order number | | orderNo | 35432346345345 | Platform order number | | status | 1 | Order status: 1-Processing 2-Success 3-Failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime| 2023-04-02 17:07:09 | Order completion time | | ext | | Order additional information, reserved field | --- # Query ## Brief Description Query KYC application status ## Request URL `POST ${baseUrl}/api/card/kyc/query` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | ------------ | -------- | ------ | --------------------- | | orderNo | No | string | Platform order number | | merOrderNo | No | string | Merchant order number | ## Response Example ```json { "data": { "cardNumber": "1234567891234567", "completeTime": "", "createTime": "2023-04-02 17:07:09", "ext": "", "merOrderNo": "345342352343", "orderNo": "4534234363464342", "failReason": "", "status": 0 }, "msg": "Operation successful", "status": 200, "success": true } ``` ## Response Parameter Description | Parameter | Example | Description | Schema | | ----------- | -------------------- | ------------------------------------ | ----------------- | | data | jsonArray | Response data | Product type list | | msg | Operation successful | Response message | | | status | 200 | Response code | | | success | true | Success (true=success/false=failure) | | ### data - Order Information | Parameter | Example | Description | | ------------- | -------------------- | ------------------------------------------------ | | merOrderNo | 436343534234 | Merchant order number | | orderNo | 35432346345345 | Platform order number | | cardNumber | 1234567891234567 | Card number/Card ID upon successful application | | status | 0 | Status: 1-Processing 2-Success 3-Failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | failReason | Incorrect information | May be empty; returns specific reason on failure | | ext | | Order additional information, reserved field | ## Business Status Code Description | Code | Description | | ------ | --------------- | | 200 | Success | | 500 | Failure | | 5201 | Order not found | --- # webhook ## API Description When the KYC status changes, the platform will send a notification with the updated status and the applied card number. ## API URL The specific request URL is provided by the application API. ## Request Method POST ## Content Type Content-Type: application/json ## Request Parameters | Parameter | Required | Example | Description | |---------------|----------|------------------------|--------------------------------------------------| | orderNo | Yes | 1448538596381429760 | Platform order number | | merOrderNo | Yes | 1448535323381429760 | Merchant order number | | cardNumber | No | 1448535323381429760 | Card number/Card ID (returned on successful application) | | status | Yes | 3 | Order status (1-Processing, 2-Success, 3-Failed) | | completeTime | Yes | 20220331121212 | Order completion time | | createTime | Yes | 20220331121212 | Order creation time | | appKey | Yes | edy2378eh23gf29w2 | appKey | | sign | Yes | a87694dbd3be64a356a42be95e123360 | API signature | | ext | No | {"settleAmount":10} | Returned based on order type, reserved field | | failReason | No | Incorrect information | Returns specific failure reason on failure | ## Response Example The body content should be a string: ``` ok ``` ## Notes If `ok` is not returned as required, the platform will treat it as a push failure and will retry using an Exponential Backoff strategy: - After the first failure, retry after 1 minute. Subsequent retry intervals double (2, 4, 8, 16 minutes), for a total of 5 retries. - If all 5 retries fail, another retry will be attempted 10 minutes later. - Only when the string `ok` is returned will the platform consider the push successful and stop retrying. --- # Virtual Card The legacy virtual card KYC issuance APIs. - [VIRTUAL-L](/en-US/legacy/card/KYC/VIRTUAL/VIRTUAL-L) - [VIRTUAL-P](/en-US/legacy/card/KYC/VIRTUAL/VIRTUAL-P) --- # VIRTUAL-L ## Brief Description Here KYC refers to the card issuance API. A card number will be returned upon successful card issuance. Note: If firstRecharge is not submitted, card issuance will deduct 10 USD by default, and the card issuing system will top up the card with 10 USD by default. ## Request URL - `${baseUrl}/api/card/kyc/apply/virtual/L` - `${baseUrl}/api/card/kyc/apply/virtual/L/en` ## Request Method - Method : POST - Content-Type : application/json ## Request Parameters | Parameter | Required | Type | Description | | -------------- | -------- | ------ | ------------------------------------------------------------------------ | | merOrderNo | Yes | string | Merchant order number | | notifyUrl | No | string | If provided, notifications will be sent on status changes; see notification documentation | | cardType | Yes | string | Card type to apply for, e.g. VISA, MASTER; obtain the corresponding card type via card BIN | | businessScene | Yes | string | Business type, e.g. e-commerce; obtain via card BIN | | cardArea | Yes | string | Issuing region, e.g. Hong Kong; obtain via card BIN | | firstRecharge | No | string | Initial top-up amount, e.g. 11; defaults to 10; must be an integer >= 10; the amount will be loaded onto the card and deducted from the reserve balance | | cardHeader | No | string | Card BIN, e.g. 493193; obtain via card BIN query. If not specified, a random one will be assigned | | firstName | No | string | English first name (English characters only) | | lastName | No | string | English last name (English characters only) | | email | No | string | Email address | | birthday | No | string | Date of birth, format yyyy-MM-dd | ## Response Example ```json { "data": { "completeTime":"", "createTime":"2023-04-23 20:08:35", "ext":"", "merOrderNo":"230423200834215712", "orderNo":"230423200835147904", "status":1 }, "msg":"Operation successful", "status":200, "success":true } ``` ## Response Parameter Description ### General Response Fields | Parameter | Example | Description | Type | | --------- | --------- | ---------------------------- | --------- | | data | jsonArray | Response data, order info | See below | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success: true/false | bool | ### data Field Description | Parameter | Example | Description | | ------------- | ------------------- | ---------------------------------------------- | | merOrderNo | 436343534234 | Merchant order number | | orderNo | 35432346345345 | Platform order number | | status | 1 | Order status: 1-Processing, 2-Success, 3-Failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | ext | | Order additional information, reserved field | --- # VIRTUAL-P ## Brief Description Card issuance API. Note: firstRecharge defaults to 10; it can be set to 0 for card issuance without initial top-up. ## Request URL - `${baseUrl}/api/card/kyc/apply/virtual/P` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | -------------- | -------- | ------ | ---------------------------------------------------------------------------------------- | | merOrderNo | Yes | string | Merchant order number | | notifyUrl | No | string | If provided, notifications will be sent on status changes; see notification documentation | | cardBin | Yes | string | Card BIN | | firstRecharge | No | string | Up to two decimal places; the submitted amount will be used for initial top-up and the corresponding reserve balance will be deducted | | phoneCode | No | string | Phone country code | | phone | No | string | Phone number | | email | Yes | string | Email address | | firstName | No | string | First name, English only; combined length of firstName and lastName must not exceed 32 characters | | lastName | No | string | Last name, English only; combined length of firstName and lastName must not exceed 32 characters | | dateOfBirth | No | string | Date of birth, format yyyy-MM-dd | | country | No | string | Country, based on card BIN issuing country, ISO 3166-1 alpha-2; e.g. US | | state | No | string | State/Province; required for US or Canada, use two-letter code e.g. WA; if left empty, randomly generated | | city | No | string | City, e.g. New York | | addressLine2 | No | string | Address line 2 | | addressLine1 | No | string | Address line 1 | | postalCode | No | string | Postal code | ## Response Example ```json { "data": { "cardNumber": "", "completeTime": "", "createTime": "2024-08-29 12:56:36", "ext": "", "merOrderNo": "100007893", "orderNo": "240829125636170528", "status": 1 }, "msg": "Operation successful", "status": 200, "success": true } ``` ## Response Parameter Description ### General Response Fields | Parameter | Example | Description | Type | | --------- | --------------- | ---------------------------- | --------- | | data | jsonArray | Response data, order info | See below | | msg | Operation successful | Response message | string | | status | 200 | Response code | int | | success | true | Success: true/false | bool | ### data Field Description | Parameter | Example | Description | | ------------- | ------------------- | ---------------------------------------------- | | merOrderNo | 100007893 | Merchant order number | | orderNo | 240829125636170528 | Platform order number | | status | 1 | Order status: 1-Processing, 2-Success, 3-Failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | ext | | Order additional information, reserved field | --- # Legacy Transfer This section contains the previous transfer API documentation and remains available for existing integrations. - Shared legacy rules: [Common Parameters](/en-US/legacy/parameter) and [Signing and Decryption](/en-US/legacy/sign) - Transfer endpoints are currently documented under [V4](/en-US/legacy/transfer/V4/) --- # Transfer V4 The Transfer V4 API collection, using the legacy common parameters and `appKey + MD5` signing. Before integrating, please read [Common Parameters](/en-US/legacy/parameter) and [Signing and Decryption](/en-US/legacy/sign). ## Transaction Interfaces - [Get Quote](/en-US/legacy/transfer/V4/quotes) - [Fee Calculation](/en-US/legacy/transfer/V4/fee) - [Create Order](/en-US/legacy/transfer/V4/order) - [Order Query](/en-US/legacy/transfer/V4/order-query) - [Order Webhook](/en-US/legacy/transfer/V4/webhook) ## Reference Data - [Business Modes](/en-US/legacy/transfer/V4/business) - [Source Countries](/en-US/legacy/transfer/V4/sourceCountry) - [Target Countries](/en-US/legacy/transfer/V4/countries) - [Payout Channels](/en-US/legacy/transfer/V4/target-types) - [Channel Countries](/en-US/legacy/transfer/V4/type-countries) - [Currencies & Partners](/en-US/legacy/transfer/V4/currency-partner) - [Supported Banks](/en-US/legacy/transfer/V4/bank) - [Bank Branches](/en-US/legacy/transfer/V4/branch) - [States / Provinces](/en-US/legacy/transfer/V4/states) - [City Codes](/en-US/legacy/transfer/V4/city-code) - [Relationship Codes](/en-US/legacy/transfer/V4/relationship-code) - [Remittance Purpose](/en-US/legacy/transfer/V4/remittancePurpose-code) - [Source of Funds](/en-US/legacy/transfer/V4/sourceOfFunds-code) - [Occupation Codes](/en-US/legacy/transfer/V4/occupation-code) --- # Supported Banks for Current Country ## Brief Description View the list of supported payout banks for the current country. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/banks` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|--------|-----------------------------| | country | Yes | string | Target country, e.g. CHN | ## Response Example ```json { "data": [ { "code": "7083", "name": "Bank Of China Limited" }, { "code": "7126", "name": "The Bank Of Tokyo-Mitsubishi UFJ, Ltd" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|----------------------------------|--------| | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag, true means success | | ### data Info | Parameter | Example | Description | |-----------|---------------------------------------|-------------| | code | 003 | Bank code | | name | The Bank Of Tokyo-Mitsubishi UFJ, Ltd | Bank name | --- # Supported Bank Branches for Current Country ## Brief Description View the list of supported bank branches for the current country or region. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/branch` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|--------|--------------------------| | country | Yes | string | Target country, e.g. CHN | | bankCode | Yes | string | Bank code, e.g. 003 | ## Response Example ```json { "data": [ { "code": "127 000", "name": "127 - DHAKA-SOUTH - TRUNCATION POINT" }, { "code": "127 151", "name": "127 - DHAKA-SOUTH - DHAKA" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|---------------------------------------------------|--------| | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag: true for success, false for failure | | ### data Info | Parameter | Example | Description | |-----------|------------------------------------------------------|------------------------| | code | 003 | Branch code, use this value | | name | The Bank Of Tokyo-Mitsubishi UFJ, Ltd Branch name | Branch bank name | --- # Query Supported Business Modes ## Brief Description Query currently supported business modes. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/business` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|--------|------------------------------------------| | country | Yes | string | Target country, e.g. CHN, from data source | | type | Yes | string | Payout channel, e.g. bank_account, from data source | ## Response Example ```json { "data": [ { "business": "C2C" }, { "business": "B2B" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|------------------------------------|--------| | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag, true means success | | ### data Info | Parameter | Example | Description | |-----------|---------|-------------------------| | business | C2C | Supported business type | --- # Query Cities ## Brief Description Query cities for the current state, currently used for IDN. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/city/code` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|--------|------------------------| | country | Yes | string | Country, e.g. IDN | | stateCode | Yes | string | State code, e.g. 01 | ## Response Example ```json { "data": [ { "code": "0100", "name": "Kepala Daerah Provinsi Jawa Barat" }, { "code": "0102", "name": "Kab. Bekasi" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|----------------------------------|--------| | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag, true means success | | ### data Info | Parameter | Example | Description | |-----------|-------------|-------------| | code | 0102 | City code | | name | Kab. Bekasi | City name | --- # Get Reachable Target Countries ## Brief Description Get the list of reachable target countries. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/countries` ## Request Method - Method: POST - Content-Type: application/json ## Response Example ```json { "data": [ { "code": "ABW", "name": "Aruba", "shortCode": "AW" }, { "code": "AFG", "name": "Afghanistan", "shortCode": "AF" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|----------------------------------|--------| | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag, true means success | | ### data Info | Parameter | Example | Description | |-----------|-------------|--------------------------| | code | CHN | Country, use this value | | name | Afghanistan | Country name | | shortCode | AF | Country short code | --- # Supported Currencies and Channel Partners ## Brief Description Query the currencies and related channel partners supported by the current country and channel. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/currency/partner` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | |-----------|----------|--------|--------------------------------| | type | Yes | string | Channel type, e.g. ewallet | | country | Yes | string | Target country, e.g. IDN | ## Response Example ```json { "data": [ { "country": "IDN", "currency": "IDR", "partner": "partner_artajasa", "type": "ewallet" }, { "country": "IDN", "currency": "IDR", "partner": "partner_dana", "type": "ewallet" }, { "country": "IDN", "currency": "IDR", "partner": "partner_gopay", "type": "ewallet" }, { "country": "IDN", "currency": "IDR", "partner": "partner_linkaja", "type": "ewallet" }, { "country": "IDN", "currency": "IDR", "partner": "partner_ovo", "type": "ewallet" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|----------------------------------|--------| | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag, true means success | | ### data Info | Parameter | Example | Description | |-----------|-----------------|--------------------------| | country | IDN | Destination country | | currency | IDR | Available payout currency| | type | ewallet | Available payout channel | | partner | partner_ovo | Channel-supported partner| --- # Fee Calculation ## Brief Description The remittance fee consists of two parts: a **fixed fee** and a **percentage fee**. It follows an **add-on model**: the fee is charged on top of the send amount (the principal) and is frozen together with the principal when the order is placed. This page explains how to calculate the fee and the actual charged amount based on the values returned by [Get Quote](/en-US/legacy/transfer/V4/quotes). ## Billing Model Let the send amount (the principal you want to remit) be `sendAmount`, the fixed fee returned by the quote be `fixedFee`, and the percentage rate be `preRatedFee` (a decimal ratio, e.g. `0.021` means 2.1%): ``` fee totalFee = sendAmount × preRatedFee + fixedFee charge chargeAmount = sendAmount + totalFee ``` - **Add-on model**: the fee is not deducted from the principal but added on top of it; the amount frozen when placing the order is `chargeAmount` (= principal + fee). - **Fee bearer**: V4 only supports `chargeCode = SHA`, meaning the fee is borne entirely by the sender. - The fee and the exchange-rate conversion are independent: `fixedFee` and `preRatedFee` determine the fee, while `rate` determines the amount credited to the recipient. ## Field Sources The rates required for billing are obtained from the [Get Quote](/en-US/legacy/transfer/V4/quotes) API and are all **effective values** applicable to you: | Field | Meaning | Unit | |-------|---------|------| | rate | Effective exchange rate | Amount of target currency obtainable per 1 unit of source currency | | fixedFee | Effective fixed fee | Amount, in the same currency as the source currency (input) | | preRatedFee | Effective percentage rate | **Decimal ratio**, e.g. `0.021` means 2.1% (multiply it directly by the send amount to obtain the percentage fee) | > The `preRatedFee` returned by the quote API is consistent with the values in order query and order notification: all are decimal ratios. ## Worked Example Sending 1000 USD, with the quote returning `fixedFee = 8` and `preRatedFee = 0.021` (i.e. 2.1%): ``` totalFee = 1000 × 0.021 + 8 = 21 + 8 = 29 (USD) chargeAmount = 1000 + 29 = 1029 (USD) ``` That is: a principal of 1000 USD, a fee of 29 USD, and 1029 USD frozen when the order is placed. ## Precision - The send amount (principal) supports at most **2 decimal places**. - Derived amounts such as the fee and the charged amount are settled to **4 decimal places**. ## About the Fee Fields in the Order APIs - The response of [Create Order](/en-US/legacy/transfer/V4/order) **does not return** the pre-calculated total fee or charged amount. Please calculate them yourself using the quote before placing the order. - The `fixedFee` / `preRatedFee` in [Order Query](/en-US/legacy/transfer/V4/order-query) and [Order Notification](/en-US/legacy/transfer/V4/webhook) are consistent with the quote API (`preRatedFee` is a decimal ratio) and can be used for reconciliation. --- # Sender Occupation Information ## Brief Description Get sender occupation information, required in some scenarios. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/occupation/code` ## Request Method - Method: POST - Content-Type: application/json ## Response Example ```json { "data": [ { "code": "001", "name": "Agriculture" }, { "code": "002", "name": "Doctor" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|----------------------------------|--------| | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag, true means success | | ### data Info | Parameter | Example | Description | |-----------|---------|------------------------| | code | 01 | Code, use this value | | name | Loan | Occupation description | --- # Order Query ## Brief Description Query submitted orders. UETR should be obtained 5-30 minutes after order completion. You can query proactively if needed. ## Update Log | Update Date | Update Description | | ------------- | ----------------------------------------------- | | 2025-09-16 | Added fixedFee, preRatedFee fields in response | | 2025-10-08 | Added chargeCode, fullPayFee fields in response | ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/order/query` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | ----------- | -------- | ------ | ---------------------------------------------------- | | orderNo | No | string | Platform order number, if not empty only this field is used for query | | merOrderNo | No | string | Channel order number | ## Response Example ```json { "data": { "account": "543435", "completeTime": "2024-08-31 15:17:56", "createTime": "2024-08-31 15:03:25", "ext": "", "failReason": "", "merOrderNo": "240831150324112064", "orderNo": "240831150325224032", "sourceAmount": 1586.150, "sendAmount": 0, "sourceCurrency": "USD", "status": 1, "targetAmount": 10000, "targetCurrency": "CNY", "fixedFee": 5, "preRatedFee": 0.012, "chargeCode": "FP", "fullPayFee": 20 }, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|---------------------------------------------------|--------| | data | json | Response data, order information | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag: true for success, false for failure | | ### data - Order Information | Parameter | Example | Description | |----------------|--------------------------------------------------|-----------------------------------------------------------------------------------------------| | merOrderNo | 436343534234 | Merchant order number | | orderNo | 35432346345345 | Platform order number | | account | 6244810070000117 | Target account | | sourceCurrency | USD | Settlement currency | | sourceAmount | 20.00 | Settlement amount, max 2 decimal places. If more than 2 decimals, rounded up (e.g. 2.123 becomes 2.13) | | sendAmount | 20.00 | Send amount | | fixedFee | 12 | Fixed fee, same currency as input | | preRatedFee | 0.012 | Exchange fee, expressed as a **decimal ratio** (consistent with the quote and order-query endpoints), e.g. for a send amount of 100, this fee is 100 × 0.012 = 1.2. See [Fee Calculation](/en-US/legacy/transfer/V4/fee) | | merQuotes | 7.3423 | Settlement exchange rate | | targetCurrency | CNY | Target currency | | targetAmount | 10000 | Target amount | | status | 1 | Order status: 1-Processing, 2-Success, 3-Failed | | failReason | 信息错误 | Failure reason, returned when order fails | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | ext | {"pickupCode":"63742374673463847"} (string) | Order extra information. Note: possible extra info see below | #### ext - Order Extra Information | Type | Example | Description | | ------------ | -------------------------------------- | -------------------------------------------------------- | | cash_pickup | {"pickupCode":"63742374673463847"} | Pickup voucher, present on-site | | wire | {"uetr":"63742374673463847"} | This UETR can be provided to the receiving bank to assist with transaction tracking | --- # Create Order ## Brief Description Initiate a remittance. There are many fields, organized into several sections. You can refer to the admin panel order creation flow for business context. Different payout modes require different information, mainly divided into wire and others (bank_account, cash_pickup, ewallet). Other types are all C2C mode; only wire supports B2B, B2C, C2B. Wire has fewer validations. --- ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/order/create` ## Request Method - Method: POST - Content-Type: application/json --- ## Parameter Description | Parameter | Required | Type | Description | Condition | |---------------------------------|----------|--------|----------------------------------------------------------------------------|------------------------------------------------------------------------| | merOrderNo | Yes | string | Merchant order number, must be unique | | | notifyUrl | No | string | Order notification URL. If provided, notifications will be sent on order status changes | | | ext | No | string | Reserved field for special business needs | | ### Amount Related | Parameter | Required | Type | Description | Condition | |-----------------|----------|--------|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------| | sourceCurrency | Yes | string | Remittance reserve currency, e.g. USD, EUR. Check balance API for available currencies and amounts. | `country=CHN && type=bank_account && currency=CNY` only supports USD | | sendAmount | Yes | string | Send amount, max 2 decimal places | Associated with sourceCurrency | | chargeCode | No | string | Wire fee bearer method, SHA | type=wire | ### Payout Path | Parameter | Required | Type | Description | Condition | |---------------|----------|--------|------------------------------------------------|-----------------------------------------------| | country | Yes | string | Destination country, e.g. HKG | | | business | Yes | string | Business type, e.g. C2C, from reference data | | | type | Yes | string | Payout channel, e.g. wire, ewallet, bank_account| | | currency | Yes | string | Payout currency, e.g. HKD, from reference data | | | partner | No | string | Payout channel partner, e.g. partner_ewallet, from API | | ### Recipient Account | Parameter | Required | Type | Description | Condition | |-------------------|----------|--------|--------------------------------------|---------------------------------------------| | accountNumber | No | string | Bank account number | type=bank_account or type=wire, accountNumber/iban mutually exclusive | | iban | No | string | IBAN | type=bank_account or type=wire, accountNumber/iban mutually exclusive; type=bank_account && currency=EUR | | targetBankCode | No | string | Bank code, e.g. 003, from API | type=bank_account | | targetBankBranch | No | string | Bank branch, e.g. 003-01, query if bankCode exists | type=bank_account | | swiftCode | No | string | SWIFT code | type=wire | | corrSwiftCode | No | string | Correspondent/intermediary bank SWIFT code | type=wire, optional | | ewalletId | No | string | E-wallet account | type=ewallet | | routingCode | No | string | Bank ABA routing code, 9 digits | type=bank_account and country=AUS | | sortCode | No | string | Sort code (UK 6-digit), 3 pairs of digits | type=bank_account && country=GBR && currency=GBP | ### Recipient Information | Parameter | Required | Type | Description | Condition | |-----------------------|----------|--------|---------------------------------------------------------|-------------------------------------------------| | targetAddressLine | Yes | string | Detailed address, do not repeat country and city | | | targetAddressCity | Yes | string | City | | | targetAddressCityCode | No | string | City code, e.g. 0926, from API | country=IDN && business=C2C && type=ewallet | | targetAddressState | No | string | State/province code, e.g. 09. For IDN submit name, from API | | | targetAddressZip | No | string | Postal code | C CAN-bank_account-CAD | | targetAddressCountry | No | string | Country or region, e.g. HKG | | | targetMobileNumber | No | string | Phone number, e.g. +6281329623212 | C, cash_pickup, type=ban_account_new && country=CHN | | targetCompanyName | No | string | Company name | Required for B | | targetNameFirst | No | string | First name | Required for C | | targetNameLast | No | string | Last name | Required for C | | targetNativeNameFirst | No | string | Native first name | C, type=ban_account_new && country=CHN | | targetNativeNameLast | No | string | Native last name | C, type=ban_account_new && country=CHN | | targetNationality | No | string | Nationality, e.g. HKG | Required for C | | targetDateOfBirth | No | string | Date of birth, format 1999-10-01 | C, KOR-bank_account-KRW | | targetEmail | No | string | Email | C, KOR-bank_account-KRW & targetMobileNumber is not +82 number | | targetIdNumber | No | string | ID number | C | | targetIdType | No | string | ID type, national/passport | C, type=cash_pickup && country=VNM, required if targetIdNumber is not empty | | targetIdExpiration | No | string | ID expiry date, format 1999-10-01, use 2099-12-31 for permanent | C | ### Sender Information | Parameter | Required | Type | Description | Condition | |------------------------------------|----------|--------|-------------------------------------------------------|------------------------------------------------------------------------------------------------| | sourceCountry | Yes | string | Sending country or region, must be cross-border, must differ from country | From API | | sourceReferenceNumber | No | string | Reference number, required for C, used to identify the user for retrieval | | | sourceAddressLine | Yes | string | Detailed address | | | sourceAddressCity | Yes | string | City | | | sourceAddressState | No | string | State/Province | C, type=ban_account_new && country=CHN | | sourceAddressCountry | Yes | string | Country | | | sourceAddressZip | No | string | Postal code | | | sourceNameFirst | No | string | First name | Required for C | | sourceNameLast | No | string | Last name | Required for C | | sourceGender | No | string | Gender (M/F) | C, type!=wire && country=NPL | | sourceNationality | No | string | Nationality, e.g. HKG | Required for C | | sourceDateOfBirth | No | string | Date of birth, format 1999-10-01 | Required for C | | sourceIdNumber | No | string | ID number | C, country=IDN && business=C2C && type=ewallet; country=CHN && business=C2C && type=bank_account | | sourceIdType | No | string | ID type, national/passport, required if sourceIdNumber is not empty | C | | sourceIdExpiration | No | string | ID expiry date, format 1999-10-01, use 2099-12-31 for permanent | C | | sourceIdIssueCountry | No | string | ID issuing country, required if sourceIdNumber has value | C, when sourceIdNumber has value | | sourceMobileNumber | No | string | Phone number | C, see rules below | | sourceCompanyName | No | string | Company name | Required for B | | sourceCompanyTradingName | No | string | Company trading name | Required for B | | sourceCompanyRegistrationNumber | No | string | Company registration number | B | | sourceCompanyRegistrationCountry | No | string | Company registration country or region | B | ### Order Supplementary Information | Parameter | Required | Type | Description | |------------------|----------|--------|------------------------------| | remittancePurpose| Yes | string | Transaction purpose, from reference data | | sourceOfFunds | Yes | string | Source of funds, from reference data | | relationship | Yes | string | Transaction relationship, from reference data | | occupation | No | string | Sender occupation, from reference data | | unformattedNote | No | string | Transfer note, max length 64 | --- ## Field Rules - sourceMobileNumber, targetMobileNumber - business=C2C && type=bank_account && country in (CMR, COG, GHA, KEN, UGA, BGD, CAD, IDN, KRW, NPL, USA, VNM) - business=C2C && type=ewallet && country in (BEN, CMR, COG, GHA, MDG, MWI, MOZ, NER, RWA, SEN, TZA, TGO, UGA, ZMB, BGD, IDN, NPL) - business=C2C && type=cash_pickup && country in (BGD, KHM, IDN, KRW, NPL, PHP, VNM) - sourceIdNumber - country=IDN && business=C2C && type=ewallet - country=CHN && business=C2C && type=bank_account - type=wire && country=HKG && currency=HKD - type=wire && country=AUS && currency=AUD - type=wire && country=SGP && currency=SGD - sourceAddressCountry, targetAddressCountry - type=wire && country=HKG && currency=HKD - type=wire && country=AUS && currency=AUD - type=wire && country=SGP && currency=SGD --- ## Response Example > Order creation is processed asynchronously. The response actually returns only the order identifiers and acceptance status (`merOrderNo` / `orderNo` / `sourceCurrency` / `targetCurrency` / `status` / `createTime`). The amount and fee fields in the example below, such as `sourceAmount` / `targetAmount` / `fixedFee` / `preRatedFee`, only have values after the order is accepted; please obtain them via [Order Query](/en-US/legacy/transfer/V4/order-query). For fee calculation, see [Fee Calculation](/en-US/legacy/transfer/V4/fee). ```json { "data": { "account": "543435", "completeTime": "", "createTime": "2024-08-31 23:08:58", "ext": "", "failReason": "", "merOrderNo": "240831230857132800", "orderNo": "240831230858245344", "sendAmount": 0, "chargeCode": "SHA", "fullPayFee": "20", "fixedFee": 5, "preRatedFee": 0.012, "sourceAmount": 1586.15, "sourceCurrency": "USD", "status": 0, "targetAmount": 10000, "targetCurrency": "CNY" }, "msg": "操作成功", "status": 200, "success": true } ``` --- ## Response Parameters | Parameter | Example | Description | Schema | |-----------|----------|---------------------------------------------------|------------------| | data | jsonArray| Response data | Product type list| | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag (true for success, false for failure)| | **data - Order Information** | Parameter | Example | Description | |----------------|---------------------|----------------------------------------------------------| | merOrderNo | 436343534234 | Merchant order number | | orderNo | 35432346345345 | Platform order number | | account | 6244810070000117 | Target account | | sourceCurrency | USD | Settlement currency | | sourceAmount | 20.1234 | Settlement amount, max 4 decimal places | | sendAmount | 20. | Send amount, has value when sendAmount is used, otherwise 0 | | targetCurrency | CNY | Target currency | | targetAmount | 10000 | Target amount | | merQuotes | 7.2634 | Quote at order submission | | chargeCode | SHA | Wire fee bearer method | | fullPayFee | 20 | When FP bears the fee, sourceAmount includes this fee | | fixedFee | 12 | Fixed fee, same currency as sourceCurrency | | preRatedFee | 0.012 | Exchange fee, expressed as a **decimal ratio** (consistent with the quote and order-query endpoints), e.g. for a send amount of 100, this is 100 × 0.012 = 1.2. See [Fee Calculation](/en-US/legacy/transfer/V4/fee) | | status | 1 | Order status (1-Processing, 2-Success, 3-Failed) | | failReason | 信息错误 | Failure reason, returned when failed | | createTime | 2023-04-02 17:07:09 | Order creation time | | completeTime | 2023-04-02 17:07:09 | Order completion time | | ext | | Order extra information, reserved | --- ## Business Status Code Description | Status Code | Description | |-------------|--------------------------------------------------------------| | 200 | Success | | 500 | Operation failed (do not treat as final failure, query order to confirm) | | 5000 | Operation exception (query order to confirm) | | 5101 | Duplicate order | | 5102 | Insufficient reserve balance | --- # Get Quote ## Brief Description Get exchange rate quote information. ## Update Log | Update Date | Update Description | | ------------- | ------------------------------------------ | | 2025-09-16 | Added fixedFee, preRatedFee fields in response | | 2025-10-15 | Added fullPay, fullPayFee fields in response | | 2026-06-04 | Unified preRatedFee to a decimal ratio (consistent with order endpoints); added [Fee Calculation](/en-US/legacy/transfer/V4/fee) | ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/quotes` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | | -------------- | -------- | ------ | ----------------------------------------------------- | | sourceCurrency | Yes | string | Source currency, available currencies from balance API | | country | Yes | string | Target country, e.g. CHN | | business | Yes | string | Business type, e.g. C2C | | type | Yes | string | Payout channel, e.g. ewallet | | targetCurrency | Yes | string | Payout currency, e.g. CNY | ## Response Example ```json { "data": { "input": "USD", "output": "CNY", "rate": 6.402552, "fixedFee": 5, "preRatedFee": 0.012, "fullPay": true, "fullPayFee": 20 }, "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | Schema | | --------- | -------- | ------------------------------------------------- | ------ | | data | json | Response data info | | | msg | 操作成功 | Response message | | | status | 200 | Response code | | | success | true | Success flag: true for success, false for failure | | ### data Info | Parameter | Example | Description | | ----------- | -------- | ----------------------------------------------------------------- | | input | USD | Input currency | | output | CNY | Output currency | | rate | 6.402552 | Exchange rate quote | | fixedFee | 5 | Fixed fee, same currency as input | | preRatedFee | 0.012 | Exchange fee, expressed as a decimal ratio (consistent with the order endpoints), e.g. for a send amount of 100 this fee is 100 × 0.012 = 1.2. See [Fee Calculation](/en-US/legacy/transfer/V4/fee) | | fullPay | true | Whether fullPay is supported | | fullPayFee | 20 | fullPay fee | --- # Get Relationship Codes ## Brief Description Get available transaction relationship codes. ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/relationship/code` ## Request Method - Method: POST - Content-Type: application/json ## Response Example ```json { "data": [ { "code": "01", "name": "父母" }, { "code": "02", "name": "兄弟姐妹" } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Type | Example | Description | | --------- | ------ | -------- | ------------------------- | | data | array | See below| Response data | | msg | string | 操作成功 | Response message | | status | int | 200 | Response code | | success | bool | true | Success flag: true/false | ### data Info | Parameter | Type | Example | Description | | --------- | ------ | -------- | -------------------------------- | | code | string | 01 | Relationship code, use this value| | name | string | 兄弟姐妹 | Relationship description | --- # Get Remittance Purpose Codes ## Brief Description Get available remittance purpose codes ## Request URL `${baseUrl}/api/v1/legacy/tf/v4/remittancePurpose/code` ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Type | Description | Example | |:----------|:--------:|:------:|:-------------------|:--------| | country | Yes | string | Target country | CHN | | business | Yes | string | Business type | C2C | | type | Yes | string | Payout channel | ewallet | | currency | Yes | string | Payout currency | CNY | ## Response Example ```json { "data": [ { "code": "001-01", "name": "Family/living expense" }, { "code": "...", "name": "..." } ], "msg": "操作成功", "status": 200, "success": true } ``` ## Response Parameters | Parameter | Example | Description | |:----------|:---------|:--------------------------------------------------| | data | json | Response data | | msg | 操作成功 | Response message | | status | 200 | Response code | | success | true | Success flag: true for success, false for failure | ### data Info | Parameter | Example | Description | |:----------|:----------------------|:-------------------------------| | code | 001-01 | Code, use this value | | name | Family/living expense | Remittance purpose description | --- # Get Source Countries **Brief Description** Get the list of available source countries, used for the `sourceCountry` field when creating orders. **Request URL** `POST ${baseUrl}/api/v1/legacy/tf/v4/sourceCountry` **Request Method** - Method: `POST` - Content-Type: `application/json` **Response Example** ```json { "data": [ { "code": "ABW", "name": "Aruba", "shortCode": "AW" }, { "code": "AFG", "name": "Afghanistan", "shortCode": "AF" } ], "msg": "操作成功", "status": 200, "success": true } ``` **Response Parameters** | Parameter | Example | Description | Type | |-----------|----------|---------------------------|---------| | data | - | Response data | array | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true/false) | boolean | **data Object Field Description** | Parameter | Example | Description | |-----------|-------------|----------------------------------| | code | CHN | Country code (used for ordering) | | name | Afghanistan | Country name | | shortCode | AF | Country short code | --- # Get Source of Funds Codes **Brief Description** Get available source of funds codes --- **Request URL** `${baseUrl}/api/v1/legacy/tf/v4/sourceOfFunds/code` --- **Request Method** - Method: POST - Content-Type: application/json --- **Request Parameters** | Parameter | Required | Type | Description | Example | |-----------|----------|--------|----------------|---------| | country | Yes | string | Target country | CHN | | business | Yes | string | Business type | C2C | --- **Response Example** ```json { "data": [ { "code": "01", "name": "Bank Deposit" }, { "code": "03", "name": "Redemption of Investment products" }, { "code": "05", "name": "Loan" } ], "msg": "操作成功", "status": 200, "success": true } ``` --- **Response Parameters** | Parameter | Example | Description | Type | |-----------|----------|------------------|--------| | data | json | Response data | object | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag | bool | --- **data Field Description** | Parameter | Example | Description | Type | |-----------|---------|--------------------------|--------| | code | 01 | Source of funds code | string | | name | Loan | Source of funds description | string | --- # Query Country State Information **Brief Description** Query state information for a specified country. After obtaining the state list, you can further query corresponding city information. Currently mainly used for Indonesia (IDN). --- **Request URL** `${baseUrl}/api/v1/legacy/tf/v4/states` --- **Request Method** - Method: POST - Content-Type: application/json --- **Request Parameters** | Parameter | Required | Type | Description | Example | |-----------|----------|--------|----------------|---------| | country | Yes | string | Target country | CHN | --- **Response Example** ```json { "data": [ { "code": "01", "name": "Jawa Barat" }, { "code": "02", "name": "Banten" } ], "msg": "操作成功", "status": 200, "success": true } ``` --- **Response Parameters** | Parameter | Example | Description | Note | |-----------|----------|------------------------------|---------| | data | - | Response data, see data info | array | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true/false) | boolean | **data Object Field Description** | Parameter | Example | Description | |-----------|---------|-------------| | code | 003 | State code | | name | Banten | State name | --- # Query Supported Payout Channels **Brief Description** View the payout channels supported by the current country. --- **Request URL** `${baseUrl}/api/v1/legacy/tf/v4/target/types` --- **Request Method** - Method: POST - Content-Type: application/json --- **Request Parameters** | Parameter | Required | Type | Description | Example | |-----------|----------|--------|--------------------------|---------| | country | Yes | string | Target country, e.g. CHN | CHN | --- **Response Example** ```json { "data": [ { "country": "HKG", "currency": "CNH", "type": "bank_account", "partner": "" }, { "country": "HKG", "currency": "USD", "type": "wire", "partner": "partner_ewallet" }, { "country": "HKG", "currency": "HKD", "type": "ewallet", "partner": "partner_ewallet" } ], "msg": "操作成功", "status": 200, "success": true } ``` --- **Response Parameters** | Parameter | Example | Description | Type | |-----------|----------|-----------------------|--------| | data | json | Response data info | object | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag | bool | --- **data Info** | Parameter | Example | Description | |-----------|-----------------|---------------------------| | country | HKG | Destination country | | currency | USD | Available payout currency | | type | wire | Available payout channel | | partner | partner_ewallet | Channel-supported partner | --- # Countries Supported by Payout Channel **Brief Description** Query the list of countries supported by a payout channel, used to obtain available country information. --- **Request URL** `${baseUrl}/api/v1/legacy/tf/v4/type/countries` --- **Request Method** - Method: POST - Content-Type: application/json --- **Request Parameters** | Parameter | Required | Type | Description | Example | |-----------|:--------:|--------|------------------------------------------------|---------| | type | Yes | string | Payout channel, e.g. wire, see dictionary below| wire | --- **Response Example** ```json { "data": [ { "code": "ABW", "name": "Aruba", "shortCode": "AW" }, { "code": "AFG", "name": "Afghanistan", "shortCode": "AF" } ], "msg": "操作成功", "status": 200, "success": true } ``` --- **Response Parameters** | Parameter | Example | Description | Type | |-----------|----------|--------------------------------|---------| | data | - | Response data, see data info | array | | msg | 操作成功 | Response message | string | | status | 200 | Response code | int | | success | true | Success flag (true/false) | boolean | **data Object Field Description** | Parameter | Example | Description | |-----------|-------------|----------------------------------| | code | CHN | Country code (used for ordering) | | name | Afghanistan | Country name | | shortCode | AF | Country short code | --- **Payout Channel Type Dictionary** | type | Description | |--------------|----------------| | wire | Global transfer| | ewallet | E-wallet | | bank_account | Local bank | | cash_pickup | Cash pickup | --- # Order Notification ## API Description When an order is completed and a notification URL was provided at order submission, the platform will notify this endpoint. ## Update Log | Update Date | Update Description | | ----------- | -------------------------------------- | | 2025-09-16 | Added fixed, proRatedFee fields in response | ## Endpoint The request address is provided by the order API ## Request Method - Method: POST - Content-Type: application/json ## Request Parameters | Parameter | Required | Example | Description | | -------------- | -------- | ------------------------------------ | -------------------------------------------------------- | | orderNo | Yes | 1448538596381429760 | Platform order number | | merOrderNo | Yes | 1448535323381429760 | Integrator order number | | status | Yes | 3 | Order status: 1-Processing, 2-Success, 3-Failed | | completeTime | Yes | 20220331121212 | Order completion time | | createTime | Yes | 20220331121212 | Order creation time | | account | Yes | 235234234 | Target account | | targetAmount | Yes | 1000 | Target amount | | targetCurrency | Yes | CNY | Target currency | | sourceCurrency | Yes | USD | Settlement currency | | sourceAmount | Yes | 1000 | Settlement amount | | fixedFee | No | 12 | Fixed fee, same currency as sourceCurrency | | proRatedFee | No | 0.012 | Per-transaction exchange fee, e.g. for send amount 100, this is 100*0.012 | | failReason | No | 信息错误 | Failure reason, returned only when failure reason is clear| | appKey | Yes | edy2378eh23gf29w2 | appKey | | sign | Yes | a87694dbd3be64a356a42be95e123360 | API signature | | ext | No | {"uetr":"63742374673463847"} | Reserved field, returned based on order type | ### ext Field Description | Order Type | Example | Description | | ------------ | ---------------------------------------- | ------------------------------------------------------------ | | cash_pickup | {"pickupCode":"63742374673463847"} | Pickup voucher, present on-site | | wire | {"uetr":"63742374673463847"} | UETR, can be provided to the receiving bank to assist with transaction tracking | ## Response Example Body content is the string ok: ``` ok ``` ## Notes - If `"ok"` is not returned as required, the platform will treat it as a push failure. - After failure, retries follow an **exponential backoff** strategy: initial interval of 1 minute, doubling after each push failure, up to 5 retries. - If there is still no response after 5 attempts, a final push will be made after 10 minutes.