CNY Payout(new encryption)
This documentation provides a comprehensive guide to integrating with Klasha's CNY Payout service. You can use this API to send money to China via various methods, including wallets (Alipay, WeChat),
Summary
Unlock seamless cross-border payments with the Payout API for CNY, enabling businesses/customers to send funds to recipients in China efficiently.
Integration steps
The integration process involves the following key steps:
Generate a Bearer Token: Authenticate to get your access token.
Create a Quotation: Get a quote for the amount you wish to transfer. This quote will provide the exchange rate, fees, and a unique
quotationId
.Get Bank Codes (for BANK_ACCOUNT and BANK_CARD): Retrieve a list of supported banks and their codes.
Upload Attachment (B2B only): For certain B2B transfers, you may need to upload supporting documents.
Initiate a Transfer: Use the
quotationId
to execute the payout.Listen for Status: Use webhooks to receive real-time updates on the transaction status.
Source wallet
All transfers would be debited from the respective payout currency wallet balance. Ensure you have sufficient balance before making any transfer request.
Generate a Bearer Token
Token GenerationCreate a Quote
To begin a transfer, you first need to create a quote. This will lock in the exchange rate and fees for a specific period. Payout quotation is created based on the category. Here is the list of categories
B2C
BANK_ACCOUNT
Not applicable
B2B
BANK_ACCOUNT
Not applicable
C2C
BANK_ACCOUNT
Not applicable
B2B
BANK_CARD
UNIONPAY
C2C
BANK_CARD
UNIONPAY
C2C
WALLET
ALIPAY
C2C
WALLET
WECHATPAY
Make a POST call to the create quotation API
POST
{{env_url}}/wallet/merchant/quotation/v2
You would need to pass, as a header the x-auth-token. This can be obtained from your merchant dashboard → Settings → Generate API keys → Merchant public key.
Headers
Content-Type
application/json
Authorization
Bearer <token here>
x-auth-token
MERCHANT PUBLIC KEY
Request body (encrypted):
{
"message": "encrypted-request-body"
}
Request body (plain)
{
"serviceCode": "ALIPAY",
"service": "WALLET",
"transferType": "C2C",
"destinationCurrency": "CNY",
"sourceCurrency": "USD",
"fundSource": "CASH",
"destinationAmount": "10"
}
Response
{
"id": 472,
"sourceAmount": 9.40,
"sourceCurrency": "USD",
"destinationAmount": 70.00,
"destinationCurrency": "CNY",
"fxRate": 7.44,
"fee": 6.00,
"expiration": 1734490800000,
"reference": "f7c32b34-ec79-4de2-ae8a-42529",
"expired": false
}
Encryption algorithm
public static String encryptCBC(String stringToEncrypt, String encryptionkey) {
try {
SecureRandom sr = new SecureRandom();
byte[] salt = new byte[8];
sr.nextBytes(salt);
final byte[][] keyAndIV =GenerateKeyAndIV(32, 16, 1, salt, encryptionkey.getBytes(StandardCharsets.UTF_8),
MessageDigest.getInstance("MD5"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyAndIV[0], "AES"), new IvParameterSpec(keyAndIV[1]));
byte[] encryptedData = cipher.doFinal(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
byte[] prefixAndSaltAndEncryptedData = new byte[16 + encryptedData.length];
// Copy prefix (0-th to 7-th bytes)
System.arraycopy("Salted__".getBytes(StandardCharsets.UTF_8), 0, prefixAndSaltAndEncryptedData, 0, 8);
// Copy salt (8-th to 15-th bytes)
System.arraycopy(salt, 0, prefixAndSaltAndEncryptedData, 8, 8);
// Copy encrypted data (16-th byte and onwards)
System.arraycopy(encryptedData, 0, prefixAndSaltAndEncryptedData, 16, encryptedData.length);
return Base64.getEncoder().encodeToString(prefixAndSaltAndEncryptedData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte) 0);
}
}
Get Bank Codes
Get Bank codes by BANK_ACCOUNT
GET
{{env_url}}/
wallet/merchant/bank/transfer/request/banks/CNY
Get Bank codes by BANK_CARD
GET
{{env_url}}/
wallet/merchant/bank/transfer/request/banks/CNY?type=bank_card
Headers
Content-Type
application/json
Authorization
Bearer <token here>
{
"message": "success",
"error": null,
"data": [
{
"code": "403100000004",
"name": "Postal Savings Bank of China (中国邮政储蓄银行)"
},
{
"code": "102100099996",
"name": "Industrial and Commercial Bank of China (中国工商银行)"
},
{
"code": "103100000026",
"name": "Agricultural Bank of China (中国农业银行)"
}
...]
}
Upload attachment
For B2B transfers, such as ECOMMERCE
or GOODS_PURCHASE
, you may need to upload supporting documents like invoices or customs declarations.
Supported Attachment Types
COMMERCIAL_DOCUMENTS
LOGISTICS_DOCUMENTS
LOGISTICS_COMMUNICATION_DOCUMENTS
CONTRACT_DOCUMENTS
ORDER_DOCUMENTS
CUSTOMS_DECLARATION
OTHERS
Make a POST call to upload file
POST
{{env_url}}/
wallet/merchant/file
You would need to pass, as a header the x-auth-token. This can be obtained from your merchant dashboard → Settings → Generate API keys → Merchant public key.
Headers
Context-Type
multipart/form-data
Authorization
Bearer <token here>
x-auth-token
MERCHANT PUBLIC TOKEN
Form Data:
file
(file)
The actual file to be uploaded (e.g., invoice.pdf
, image.png
)
purpose
Order Delivery
The type of document being uploaded.
attachmentType
CUSTOMS_DECLARATION
The type of document being uploaded.
Response
{
"id": 628,
"purpose": "Order Delivery",
"attachmentType": "CUSTOMS_DECLARATION"
}
Initiate a transfer
Once you have a quotationId
(and a bankCode
if applicable), you can initiate the transfer. The request body will vary depending on the payout method and transfer type.
Make a POST call to the initiate transfer request API
POST
{{env_url}}
/wallet/merchant/{businessId}/bank/transfer/v2/request
You would need to pass, as a header the x-auth-token. This can be obtained from your merchant dashboard → Settings → Generate API keys → Merchant public key.
Headers
Content-Type
application/json
x-auth-token
MERCHANT PUBLIC KEY
Authorization
Bearer <token here>
ID TYPE
SENDER/SENDER_BUSINESS ID
Allowed Types
Transfer Types
Services
ID_CARD PASSPORT
C2C / B2C
BANK_CARD
ID_CARD
PASSPORT HONGKONG_ID_CARD
DRIVER_LICENSE
HONGKONG_MACAO_TAIWAN_PERMIT
ALIEN_RESIDENT_PERMIT EMPLOYEE_CARD OTHER
C2C
BANK_ACCOUNT
ID_CARD PASSPORT
C2C
WALLET
BUSINESS_REGISTRATION_CERT
OTHER
B2C
BANK_ACCOUNT
BUSINESS_REGISTRATION_CERT
B2B
BANK_ACCOUNT
RECEIVER/RECEIVER_BUSINESS ID
Allowed Types
Transfer Types
Services
Receiver ID not compulsory
C2C / B2C
BANK_CARD
ID_CARD HONGKONG_MACAO_TAIWAN_PERMIT
C2C
BANK_ACCOUNT
Receiver ID not compulsory
C2C
WALLET
ID_CARD HONGKONG_MACAO_TAIWAN_PERMIT
B2C
BANK_ACCOUNT
UNIFIED_SOCIAL_CREDIT_ID
ID_CARD
B2B
BANK_ACCOUNT
ACCOUNT ID
Values
Description
MOBILE
ALIPAY and WECHATPAY
ALIPAY only
Relationship
RELATIONSHIP not compulsory
C2C / B2C
BANK_CARD
SELF
SPOUSE
PARENTS SONS_AND_DAUGHTERS BROTHERS_AND_SISTERS
GRANDPARENTS
GRANDPARENTS_IN_LAW GRANDCHILDREN
MATERNAL_GRANDCHILDREN
C2C
BANK_ACCOUNT
SPOUSE
PARENTS
SONS_AND_DAUGHTERS
C2C
WALLET
EMPLOYEE
B2C
BANK_ACCOUNT
RELATIONSHIP not compulsory
B2B
BANK_ACCOUNT
Account Type
INDIVIDUAL
COMPANY
TradeType
GOODS
Purpose
Allowed Values
Transfer Type
Services
ECOMMERCE
GOODS_PURCHASE
B2B
BANK _ACCOUNT
SALARY FAMILY_SUPPORT TRAVEL INSURANCE SERVICE_CHARGES PATENT_ROYALTY OTHER
C2C/B2C
BANK_CARD
FAMILY_SUPPORT
C2C
BANK_ACCOUNT WALLET
SALARY
B2C
BANK_ACCOUNT
Supported Product Categories for GOODS_PURCHASE
When the purpose is GOODS_PURCHASE
, you must provide a productCategory
.
The supported values are:
APPAREL
, BEAUTY_PERSONAL_CARE
, ELEC_COMPONENTS
, TOYS_MODELS
, GARDEN
, FOOD
, BOOKS_MEDIA
, BABY
, GIFTS_CRAFTS
, AUTO_ACCESSORIES
, HOME_GOODS
, SPORTS_OUTDOORS
, JEWELRY
, ENTERTAINMENT
, MUSICAL_INSTRUMENTS
, ELECTRONICS
, ANTIQUES_ART
, WATCHES_EYEWEAR
, MECHANICAL_PARTS
.
Request body (encrypted):
{
"message": "encrypted-request-body"
}
Request body (plain)
{
"bankCode": "01020000",
"bankName": "Industrial and Commercial bank of China",
"cardHolderName": "Cui Xia",
"cardNumber": "6222040000030009",
"creditAccountCountry": "CN",
"message": "bypass",
"purpose": "FAMILY_SUPPORT",
"quotationId": 795,
"receiverFirstName": "Receiver",
"receiverLastName": "China",
"requestId": "China_payout-union-10",
"senderAddress": {
"city": "Beijing",
"countryCode": "CN",
"postcode": "065001",
"state": "Heilongjiang",
"streetAddress": "West Street"
},
"senderFirstName": "Sender",
"senderIdNumber": "1000000000",
"senderIdType": "ID_CARD",
"senderLastName": "China",
"senderNationality": "CN"
}
Fields specification: C2C, BANK_CARD
Field
Description
receiverFirstName
Receiver first name (必须是中文)
receiverLastName
Receiver last name (必须是中文)
senderFirstName
Sender first name
senderLastName
Sender last name
senderNationality
Sender nationality
senderIdType
Sender ID type. Please refer to the supported ID type list
senderIdNumber
Sender ID number
senderAddress
Sender’s address. Refer to the address object
bankName
Name of the bank
bankCode
Bank ID
creditAccountCountry
Bank’s country
cardNumber
Card number
cardHolderName
Name on card
quotationId
Quotation ID obtained after creating a quotation
purpose
Reason for the payout. Please refer to the supported purpose list
requestId
It’s a unique transaction reference will be used to uniquely identify a transfer
Fields specification: C2C, BANK_ACCOUNT
Field
Description
receiverFirstName
Receiver first name (必须是中文)
receiverLastName
Receiver last name (必须是中文)
receiverMobileNumber
Receiver mobile number. Format: +86 18612345678
receiverEmail
Receiver email
receiverRelationship
Relationship with the receiver
receiverIdType
Receiver ID type. Please refer to the supported ID type list
receiverIdNumber
Receiver ID number
senderFirstName
Sender first name
senderLastName
Sender last name
senderNationality
Sender nationality
senderIdType
Sender ID type. Please refer to the supported ID type list
senderIdNumber
Sender ID number
senderAddress
Sender address. Refer to the address object
bankName
Name of the bank
bankCode
Bank ID
creditAccountCountry
Credit account bank’s country
accountNumber
Credit account number
accountName
Credit account name (必须是中文)
accountType
Credit account type. Please refer to the supported account type list
quotationId
Quotation ID obtained after creating a quotation
purpose
Reason for the payout. Please refer to the supported purpose list
requestId
It’s a unique transaction reference will be used to uniquely identify a transfer
Fields specification: C2C, WALLET (ALIPAY, WECHATPAY)
Field Name
Description
receiverIdNumber
Receive ID number
receiverFirstName
Receiver first name (必须是中文)
receiverLastName
Receiver last name (必须是中文)
receiverRelationship
Relationship with the receiver
senderFirstName
Sender first name
senderLastName
Sender last name
senderBirthDate
Sender date of birth
senderNationality
Sender nationality
senderIdType
Sender ID type. Please refer to the supported ID type list
senderIdNumber
Sender ID number
senderAddress
Sender address. Refer to the address object
creditAccountCountry
Credit account bank’s country
accountNumber
Credit account number - this is either an email or mobile number for ALIPAY or a mobile number for WECHAT PAY. The login account ID to WeChat or Alipay account.
accountId
The account ID type of either the WeChat or Alipay account. Please refer to the supported account ID type.
quotationId
Quotation ID obtained after creating a quotation
purpose
Reason for the payout. Please refer to the supported purpose list
requestId
It’s a unique transaction reference will be used to uniquely identify a transfer
Fields specification: B2B, BANK_ACCOUNT
Field
Description
receiverFirstName
Receiver first name (必须是中文)
receiverLastName
Receiver last name (必须是中文)
receiverBusinessMobileNumber
Receiver mobile number. Format: +86 18612345678
receiverEmail
Receiver email
receiverBusinessIdType
Receiver ID type. Please refer to the supported ID type list
receiverBusinessIdNumber
Receiver ID number
receiverBusinessRegisteredName
Receiver business name. Chinese characters are required if it's in China
receiverBusinessAddress
Receiver business address
senderFirstName
Sender first name
senderLastName
Sender last name
senderBusinessAddress
Sender business address. Refer to the address object
senderBusinessRegisteredName
Sender business name
bankName
Name of the bank
bankCode
Bank ID
creditAccountCountry
Credit account bank’s country
accountNumber
Credit account number
accountName
Credit account name (必须是中文)
accountType
Credit account type. Please refer to the supported account type list
quotationId
Klasha quotation ID
purpose
Reason for the payout. Please refer to the supported purpose list
requestId
It’s a unique transaction reference will be used to uniquely identify a transfer
document
See: Document
Fields specification: Document
Field
Description
totalAmount
Total amount
orderNumber
Order number
orderTime
Order time
orderCurrency
Order currency
logistics
See: Logistics
tradeType
Please refer to the supported TradeType
products
List<Product>
Product
attachments
List<Attachment>
See: Attachment
Fields specification: Product
Field
Description
name
Product name
quantity
Quantity
Fields specification: Attachment
Field
Description
fileId
Klasha file ID
Fields specification: Logistics
Field
Description
company
Logistics company
orderNo
Logistics order number
Fields specification: Address
Field
Description
city
City
streetAddress
Street address
countryCode
Country code
postcode
Postcode
state
Province or state
Response
{
"id": 1,
"amount": 70.00,
"payoutStatus": "PENDING",
"requestId": "ed5fa-a33ed-fc4dc",
"narration": "GOODS_PURCHASE",
"country": "CN",
"fee": 6.00,
"bankCode": "03010000",
"bankName": "Bank of communications",
"accountNumber": "10000000000",
"accountName": "Test Flow"
}
Listen for a status response
When a transfer is initiated, it could take a few seconds or minutes to be processed. This is why we recommend relying on webhooks for verification as opposed to polling.
Once a transfer is processed, we send the final status of the transfer as a POST request to your webhook URL
{
"data": {
"reference": "kbtr-3857-011-null-166993253331236",
"createdAt": "2023-03-28T23:01:45.336",
"amount": 1000,
"accountName": "Pastor Bright",
"narration": "certification",
"name": "Steph and sons",
"currency": "NGN",
"bankName": "ACCESS BANK NIGERIA",
"accountNumber": "0690000032",
"status": "successful"
},
"event": "payout"
}
The possible statuses are listed and described in the following table:
successful
This is sent when the transfer is successful
failed
This is sent when the transfer fails
pending
This transfer is still in progress. Please wait for either success or failed.
Query payout status
Make a call to fetch status of initiated payouts.
Last updated