💻
Docs
  • Introduction
  • VIRTUAL CARD
    • Page Integration
      • Quick Page Integration
      • Login-free Page Integration
      • About The Sign
      • Explanation of Return Codes
    • API Integration
      • Things Need To Know
      • About The Sign
      • API For Fetching Usable Card Segments
      • API For Retrieve Enumerations Of The States In The United States
      • API For Creating Virtual Card
      • API For Virtual Card Deposit (recharge)
      • API For Obtaining Card Details
      • API For Updating Virtual Card (update cardholder address)
      • API For Card Refund
      • API For Deleting Card
      • API For Querying Order History
      • Successful card opening callback
      • Fail card opening callback (COPY)
      • Risk Control webhook
      • Error Code
  • Physical card
    • Page Integration
      • Page customization
    • API Integration
      • Create Cardholder
      • Modify Cardholder Information
      • Cardholder Document Upload
      • Cardholder Query
      • Bank Card Application
      • Bank Card Application Result Query
      • Bank Card Activation
      • Bank Card Top-up
      • Bank Card Top-up Order Query
      • Bank Card Query
      • Bank Card Transaction Details Query
      • Bank Card Replacement
      • Bank Card Unloss
      • Bank Card Password Retrieval
      • Bank Card Authorized Transaction Details
      • Partner Account Balance Inquiry
      • Partner Top-up Account Information Inquiry
      • Cardholder Audit Result Notification
      • Bank Card Transaction Notification
  • KYC Sharing API
    • Integration Options
    • Signature Description
    • API
      • Submit basic information
      • Upload photo
      • Submit for approval
      • Query Approval Results
      • Approval Results[Webhook]
Powered by GitBook
On this page
  • Sign String Example
  • Please note the following guidelines:
  • Sign Example
  1. VIRTUAL CARD
  2. API Integration

About The Sign

  • ach-access-sign request header

  • The request header "ach-access-sign" is generated by encrypting the concatenation of timestamp + method + requestPath + body using the HMAC SHA256 method with a SecretKey. The result is then encoded using Base-64.

  • In the requestPath, the parameters follow the same rules as the body. The values in a list are sorted in the following order: integers, floats/doubles, strings, and then lists and objects. Within each type, such as integers or doubles, sorting is done in ascending dictionary order. Sorting between lists and objects is based on their position in the array. Substructures like objects and lists within the main structure are recursively sorted using the same rules. Null values, empty strings (''), empty lists ([]), and empty dictionaries ({}) are excluded before generating the signature.

Sign String Example

  • Example: { {“x”: 1, “y”: 2}, 1, 3, 2, -4, 1.1, “xxxxx”, “yyyy”, “jscx”, 0, “sss”,{“z”:2,”x”:1,”a”:””}}

  • After sorting: { -4,0,1,2,3,1.1,”jscx”,”sss”,”xxxxx”,”yyy”,{“x”: 1, “y”: 2},{“x”: 1, “z”: 2}}

Please note the following guidelines:

  • Please note that, in general, the sorting of data in lists during transmission should not be related to the content being transmitted. In the case where both the path and body contain parameters, each should be sorted individually. Then, concatenate them in the order of timestamp + method + requestPath + body for signing. Here's an example: Timestamp: 1538054050234 HTTP Method: GET Path: /api/v1/crypto/order?order_no=sdf23&token=ETH Body: Empty The signature content would be generated as follows: "1538054050234" + "GET" + "/api/v1/crypto/order?token=ETH&order_no=sdf23"

  • For the clarification you provided: The timestamp value should match the "ach-access-timestamp" request header and follow the ISO format, representing Unix time in milliseconds with a 13-digit timestamp. For example: 1538054050231. The method refers to the HTTP request method and should be in uppercase letters. For instance: GET/POST. The requestPath represents the API endpoint path and is case-sensitive. If the URL already ends with a '/', it should still be included in the signing process. For example: /api/v1/crypto/order.

  • The body refers to the string representation of the request payload. If the request does not have a body (typically for GET requests), the body can be omitted in the signing process. When present, the body should be sorted in dictionary order, and its internal structure should also follow the dictionary ordering. Empty values are excluded and not considered in the signing process. For example: '1538054051230' + 'GET' + '/api/v1/crypto/token/price' + body Both the secretKey and apiKey are case-sensitive. The HMAC SHA256 algorithm is used with a secret key to sign the hashed string. The resulting signature is encoded in Base64 format.

Sign Example

Python

import base64
import hmac
import json
import requests
from datetime import datetime
from typing import Dict, Any, List, Union, Type


# 签名
class SignatureUtility:
    def generate_signature(self, secret_key: str, message: str) -> str:
        """Generate the HMAC SHA256 signature for a given message."""
        signature = hmac.new(
            secret_key.encode(), message.encode(), digestmod="sha256"
        ).digest()
        return base64.b64encode(signature).decode()

    def verify_signature(
        self, secret_key: str, message: str, received_signature: str
    ) -> bool:
        """Verify the received signature against the computed one."""
        computed_signature = self.generate_signature(secret_key, message)
        return hmac.compare_digest(computed_signature, received_signature)

    def clean_and_sort_dict(
        self, data: Union[Dict[Any, Union[Dict, Any]], List[Union[Dict, Any]]]
    ) -> Union[Dict[Any, Union[Dict, Any]], List[Union[Dict, Any]]]:
        if isinstance(data, dict):
            sorted_dict = {}
            for key, value in sorted(data.items()):
                if isinstance(value, (dict, list)):
                    value = self.clean_and_sort_dict(value)

                # Checking for non-empty values, including non-empty lists and non-empty dictionaries
                if value or value == 0:
                    sorted_dict[key] = value
            return (
                sorted_dict if sorted_dict else None
            )  # Return None if the dictionary is empty
        elif isinstance(data, list):
            int_list = sorted([item for item in data if isinstance(item, int)])
            float_list = sorted([item for item in data if isinstance(item, float)])
            str_list = sorted([item for item in data if isinstance(item, str)])
            complex_data_types = [
                item for item in data if isinstance(item, (dict, list))
            ]

            sorted_complex_data = [
                self.clean_and_sort_dict(item) for item in complex_data_types
            ]
            sorted_complex_data = [
                item for item in sorted_complex_data if item
            ]  # Filter out None values

            result = int_list + float_list + str_list + sorted_complex_data
            return result if result else None  # Return None if the list is empty





#
def post_request(URL: str, body: Type):
    sign_utility = SignatureUtility()
    query_string = ""
    method = "POST"
    #
    # 请求签名
    body_str = ""
    timestamp = str(int(datetime.utcnow().timestamp() * 1000))
    req_cleaned_body = sign_utility.clean_and_sort_dict(body)
    if req_cleaned_body is not None:
        body_str = json.dumps(
            req_cleaned_body,
            sort_keys=True,
            separators=(",", ":"),
            ensure_ascii=False,
        )
    message = f"{timestamp}{method}{URL}{query_string}{body_str}"
    print(message)
    sign = sign_utility.generate_signature(secret_key=SECRET_KEY, message=message)
    print(sign)
    # POST REQUEST
    headers = {
        "ach-access-key": API_KEY,
        "ach-access-sign": sign,
        "ach-access-timestamp": timestamp,
    }
    response = requests.post(url=HOST + URL, headers=headers, json=body)
    return response.text



API_KEY = "service000-local-apikey"
SECRET_KEY = "service000-local-secretkey"
HOST = "http://127.0.0.1:8072"

#
if __name__ == "__main__":
    url = "/open/api/card/create"
    body = {
        "callbackUrl": "http://baidu.com",
        "cardHolder": {
            "address": {
                "city": "string",
                "country": "string",
                "state": "string",
                "street": "string",
                "zipCode": "string"
            },
            "firstName": "string",
            "lastName": "string"
        },
        "customerId": "user_id_123",
        "deposit": "100",
        "orderNo": "12165456165441",
        "tagNameList": [
            "string"
        ],
        "vid": "vab_069af8a792ad"
    }
    response = post_request(url, body)
    print(f"response: {response}")

Last updated 1 year ago