LogoLogo
  • Welcome!
  • Integrating Klasha
    • Getting Started
    • Prerequisites
    • Integration Checklist
    • Wrap Up
  • Overview
    • Introduction
    • Parameters
    • Token Generation
    • Errors
    • Countries & Payment Methods
  • Accepting Payments
    • Test Payments
    • Payments API
      • Mobile money
      • USSD
    • Payment Link
      • Dashboard
      • Payment Link API
    • Klasha Inline
    • HTML Checkout
  • PLUGINS AND SDKS
    • Plugins
    • Mobile SDKs
    • Web SDKs
  • MISC
    • Transaction Status
    • Webhook
    • API Status
  • Features
    • One-time Payments
    • Recurrent Payment
  • Transfers
    • Payout
      • ZAR Payout(new encryption)
      • KES Payout(new encryption)
      • ZMW Payout(new encryption)
      • TZS Payout(new encryption)
      • CNY Payout(new encryption)
    • Klasha Wire API
    • Swap API
    • Currency Coverage
  • Bank Account Collection
    • Virtual Account Creation
    • VA balance and statement
    • Business Identification Service
Powered by GitBook
On this page
  • Integration steps
  • Generate a token bearer
  • Create Quotation
  • Encryption algorithm
  • Get Bank Codes
  • Upload attachment
  • Initiate a transfer
  • Listen for a status response
  • Query payout status
  1. Transfers
  2. Payout

CNY Payout(new encryption)

PreviousTZS Payout(new encryption)NextKlasha Wire API

Last updated 1 month ago

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

  • Generate bearer token

  • Create a quotation

  • Get bank codes (BANK_ACCOUNT and BANK_CARD only)

  • Upload Attachment (B2B only)

  • Initiate a transfer

  • Listen for status

Before you begin!

  • Find your keys on the Klasha Dashboard → Settings → Generate API Keys ()

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 token bearer

Create Quotation

Payout quotation is created based on the category. Here is the list of categories

Transfer Type
Service
Service Code

B2B

BANK_ACCOUNT

Not applicable

C2C

BANK_ACCOUNT

Not applicable

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

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

Key
Value

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": "UNIONPAY",
    "service": "BANK_CARD",
    "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

Key
Value

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 (中国农业银行)"
        }
   ...]
}    
{
    "message": "success",
    "error": null,
    "data": [
        {
            "code": "03010000",
            "name": "Bank of Communications"
        },
        {
            "code": "01040000",
            "name": "Bank of China"
        },
        {
            "code": "03033320",
            "name": "Guang Da Bank"
        },
        {
            "code": "14505800",
            "name": "GuangDong Rural Credit Bank"
        }
    ...]
}

Upload attachment

Supported Attachment Types

Values

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

Key
Value

Context-Type

multipart/form-data

Authorization

Bearer <token here>

x-auth-token

MERCHANT PUBLIC TOKEN

Form request:

{
    --form 'file=@"/path/to/file"' \
    --form 'purpose="Party"' \
    --form 'attachmentType="COMMERCIAL_DOCUMENTS"'
}

Initiate a transfer

Make a POST call to the initiate transfer request API

POST {{env_url}}//wallet/merchant/bank/transfer/request/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

Key
Value

Content-Type

application/json

x-auth-token

MERCHANT PUBLIC KEY

Authorization

Bearer <token here>

ID TYPE

Values

Description

ID_CARD

national id card

UNIFIED_SOCIAL_CREDIT_ID

unified social credit id of China registered company

PASSPORT

passport

BUSINESS_REGISTRATION_CERT

business registration certificate

ACCOUNT ID

Values

Description

MOBILE

ALIPAY and WECHATPAY

EMAIL

ALIPAY only

Relationship

Values

SELF

SPOUSE

PARENTS

SONS_AND_DAUGHTERS

BROTHERS_AND_SISTERS

GRANDPARENTS

GRANDPARENTS_IN_LAW

GRANDCHILDREN

MATERNAL_GRANDCHILDREN

Account Type

Values

INDIVIDUAL

COMPANY

TradeType

Values

GOODS

Purpose

Values

Description

GOODS_PURCHASE

B2B

SALARY

C2C

FAMILY_SUPPORT

C2C

TRAVEL

C2C

INSURANCE

C2C

INVESTMENT

C2C

SERVICE_CHARGES

C2C

PATENT_ROYALTY

C2C

OTHER

C2C

Request body (encrypted):

{ 
  "message": "encrypted-request-body"
}

Request body (plain)

{
    "receiverLastName": "杜",
    "bankCode": "03010000",
    "receiverBusinessIdType": "ID_CARD",
    "purpose": "GOODS_PURCHASE",
    "quotationId": 1,
    "accountName": "约翰·杜",
    "creditAccountCountry": "CN",
    "document": {
        "totalAmount": "1500",
        "attachments": [
            {
                "fileId": "1"
            }
        ],
        "orderNumber": "982f38-C",
        "orderTime": "100",
        "orderCurrency": "CNY",
        "logistics": {
            "company": "China pay",
            "orderNo": "123456789"
        },
        "tradeType": "GOODS",
        "products": [
            {
                "name": "Motors",
                "quantity": "1"
            }
        ]
    },
    "accountType": "INDIVIDUAL",
    "senderFirstName": "Sender",
    "senderBusinessAddress": {
        "city": "Beijing",
        "streetAddress": "West Street",
        "countryCode": "CN",
        "postcode": "065001",
        "state": "Heilongjiang"
    },
    "bankName": "Bank of communications",
    "receiverBusinessRegisteredName": "圣公司",
    "receiverBusinessAddress": {
        "city": "Beijing",
        "streetAddress": "West Street",
        "countryCode": "CN",
        "postcode": "065001",
        "state": "Heilongjiang"
    },
    "accountNumber": "10000000000",
    "receiverBusinessIdNumber": "1000000000",
    "senderBusinessRegisteredName": "圣公司",
    "receiverBusinessMobileNumber": "+86 18612345678",
    "requestId": "China_payout",
    "receiverFirstName": "约翰",
    "senderLastName": "China"
}
{
    "receiverIdType": "ID_CARD",
    "receiverLastName": "杜",
    "bankCode": "03010000",
    "senderAddress": {
        "city": "Beijing",
        "streetAddress": "West Street",
        "countryCode": "CN",
        "postcode": "065001",
        "state": "Heilongjiang"
    },
    "senderNationality": "CN",
    "senderIdType": "ID_CARD",
    "purpose": "FAMILY_SUPPORT",
    "quotationId": 1,
    "accountName": "约翰·杜",
    "creditAccountCountry": "CN",
    "accountType": "INDIVIDUAL",
    "senderFirstName": "Sender",
    "receiverIdNumber": "1000000000",
    "senderIdNumber": "1000000000",
    "bankName": "Bank of communications",
    "accountNumber": "10000000000",
    "requestId": "China_payout",
    "receiverMobileNumber": "+86 18612345678",
    "receiverFirstName": "约翰",
    "senderLastName": "China",
    "receiverRelationship": "SPOUSE",
    "receiverEmail": "test@gmail.com"
}
{
    "receiverLastName": "杜",
    "bankCode": "01020000",
    "senderAddress": {
        "city": "Beijing",
        "streetAddress": "West Street",
        "countryCode": "CN",
        "postcode": "065001",
        "state": "Heilongjiang"
    },
    "senderNationality": "CN",
    "senderIdType": "ID_CARD",
    "purpose": "FAMILY_SUPPORT",
    "quotationId": 1,
    "cardHolderName": "Cui Xia",
    "creditAccountCountry": "CN",
    "senderFirstName": "Sender",
    "senderIdNumber": "1000000000",
    "bankName": "Insdustrial and Commercial bank of China",
    "requestId": "China_payout",
    "receiverFirstName": "约翰",
    "senderLastName": "China",
    "cardNumber": "6222040000030009"
}
{
    "receiverLastName": "杜",
    "senderAddress": {
        "city": "Beijing",
        "streetAddress": "West Street",
        "countryCode": "CN",
        "postcode": "065001",
        "state": "Heilongjiang"
    },
    "senderNationality": "CN",
    "senderIdType": "ID_CARD",
    "purpose": "FAMILY_SUPPORT",
    "quotationId": 1,
    "creditAccountCountry": "CN",
    "senderFirstName": "Sender",
    "senderIdNumber": "1000000000",
    "accountNumber": "10000000000",
    "accountId": "MOBILE",
    "requestId": "China_payout",
    "receiverFirstName": "约翰",
    "senderBirthDate": "2024-08-13",
    "senderLastName": "China",
    "receiverRelationship": "SPOUSE"
}

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

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"
}
{
  "data": {
    "reference": "kbtr-3857-011-null-166993253334816",
    "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": "failed"
  },
  "event": "payout"
}

The possible statuses are listed and described in the following table:

Status
Description

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 to fetch status of initiated payouts.

Token Generation
here
call