Benpay API Documentation
Introduction
Welcome to the Benpay Developer Documentation!
With the Benpay API, you can:
- Create payments
- Query payments
- Receive webhook notifications for successful payments and refunds ...
Benpay API helps you manage your collections easily, providing comprehensive documentation, sample codes, and professional technical support.
Integration Guide
Overview
After becoming a merchant, you can enter the merchant dashboard to set up your merchant public key, payment callback URL, notification URL, IP whitelist, and generate the platform public key. The signing and verification use the SHA256withRSA2048 algorithm.
Request Parameter Signature
1. Signature Algorithm Overview
In API requests, the signature algorithm ensures the integrity and reliability of the request data. If a request does not include a signature or the signature verification fails, Benpay will reject the request and return a 401 Unauthorized error, with the rejection information in the response body. Successfully verified requests will return with HTTP status 200. Merchants should verify the signatures of both successful responses and webhook requests to prevent data tampering during transmission. The steps to generate signatures and construct the request header are detailed below.
2. Signature Generation Steps
Signature generation involves the following steps:
2.1 Prepare Common Parameters for Signature
The following common parameters are required when generating a signature:
- api_key: The unique identifier assigned to the merchant.
- timestamp: The current timestamp (in milliseconds) used to mark the request time and prevent replay attacks. The server rejects requests with more than 5 minutes of time deviation.
- nonce: A random string generated for each request to ensure the uniqueness of the request.
2.2 Construct the String to Sign
Concatenate the common parameters, HTTP request method, request path, and request body into a single string in the following format:
api_key={api_key},timestamp={timestamp},nonce={nonce}\n
{method}\n
{url_path}\n
{body}\n
- The signature string consists of four lines, each ending with a
\n
, including the last line. - The first line is the common parameter string consisting of
api_key
,timestamp
, andnonce
, separated by commas, with no specific order required. - The second line is the HTTP request method (e.g.,
POST
,GET
). - The third line is the request path (e.g.,
/v1/payment/create
). - The fourth line is the JSON string of the request body. For GET requests, this line is an empty string but must still include an empty line.
2.3 Sign the String Using the RSA Private Key
- Generate Hash: Use the SHA256 algorithm to hash the string to be signed.
- Encrypt the Hash: Use the RSA algorithm and the private key to encrypt the hash, generating the signature.
- Encode the Signature: Base64 encode the generated signature to get the final signature string.
2.4 Generate the Authorization Header
Place the generated signature and common parameters into the Authorization header in the following format:
Authorization: BENPAY-SHA256-RSA2048 api_key={api_key},timestamp={timestamp},nonce={nonce},signature={signature}
2.5 Send the Request
Send the request along with the generated Authorization header and request body to the server. The server will validate the signature's legality based on the same rules.
2.6 Sample Code
Here’s a sample code in Python demonstrating the signature algorithm:
import requests
import base64
import hashlib
import uuid
from datetime import datetime
import time
from Crypto.Signature import pkcs1_15
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
import json
def generate_signature(api_key, timestamp, nonce, method, url_path, body, private_key_string):
public_param_string = f"api_key={api_key},timestamp={timestamp},nonce={nonce}"
sign_string = f"{public_param_string}\n{method.upper()}\n{url_path}\n{body}\n"
private_key = RSA.import_key(private_key_string)
hash_obj = SHA256.new(sign_string.encode('utf-8'))
signature = pkcs1_15.new(private_key).sign(hash_obj)
signature_base64 = base64.b64encode(signature).decode('utf-8')
return signature_base64
def generate_authorization_header(api_key, timestamp, nonce, signature):
return f"BENPAY-SHA256-RSA2048 api_key={api_key},timestamp={timestamp},nonce={nonce},signature={signature}"
if __name__ == "__main__":
api_key = "your api key"
timestamp = int(time.time() * 1000)
nonce = uuid.uuid4().hex
method = "POST"
url_path = "/v1/payment/create"
json_data = {
"coin": "BUDS",
"coin_amount": "0.1"
}
private_key_string = """-----BEGIN PRIVATE KEY-----
(your private key content)
-----END PRIVATE KEY-----
"""
signature = generate_signature(api_key, timestamp, nonce, method, url_path, json.dumps(json_data), private_key_string)
authorization_header = generate_authorization_header(api_key, timestamp, nonce, signature)
url = "http://www.benpay.org/v1/payment/create"
headers = {
"Authorization": authorization_header,
"Content-Type": "application/json"
}
response = requests.post(url, headers=headers, data=json.dumps(json_data).encode('utf-8'))
print(response)
Response Parameter Verification
1. Verification Process Overview
To ensure the integrity and authenticity of the API response, response data must be verified using the SHA256withRSA2048 algorithm. Only HTTP status code 200 responses require signature verification.
2. Verification Steps
Prepare Data
- Public Key String: The RSA public key provided by the API in PEM format.
- Response Header Parameters:
Benpay-Nonce
,Benpay-Timestamp
,Benpay-Signature
. - Response Body: The content of the response body.
Process Public Key
Convert the public key string into a byte array. PEM-formatted public keys should be handled according to PEM encoding standards.
Decode Signature
Decode the Base64-encoded signature into a byte array, restoring the signature to its original binary form for verification.
Construct the Message
The message string should include the timestamp (
timestamp
), nonce (nonce
), and API response body (response_body
), ending with a newline (\n
).Hash Calculation
Use the SHA256 algorithm to hash the message string, converting the message content into a fixed-length hash value.
Verify Signature
Use the RSA public key to verify the calculated hash value and the decoded signature. If the public key successfully verifies the signature, the response data is confirmed as untampered.
- Verification Process: Attempt to decrypt the hash value with the RSA public key to check if it matches the original signature. If it matches, the signature is valid; otherwise, it is invalid.
3. Response Example
Assuming the API response headers contain the following information:
Benpay-Nonce
:90ff8d49d2e646b7b57099b12cfb12c7
Benpay-Timestamp
:1724170900177
Benpay-Signature
:adS+OlZXSoIgrK+tCcX7tUy8dWYl5fOIVP5pPadG0GpS/TcmgAiV/NGWRbxIEnoky6yKYLBDevF6xcZcFl7G5ST7mdcBuFIhYdYppjEdqbHCCRmS4e99EWIhBtJMzDBfTqJXYSn4s+aA9QdMgh9vUVj6JfsRlQOO8vrzFBHZ6fHrlN8JS6nIsfUoa+gDAIKxExd28GNQPZIK6rPkFn9Drxz9mhpEa9cWauA7+e/O/hxRg/bCEk863L7hasb8ohgzyCFwYPpKDql91Lm5El0QlJuxUEoxfWTJeMlFdUloDMoRdsgoNsFlHsp2duxuHTJSu/CtaXfDxO814niPvls6Tw==
response_body
:{"payment_id": "c49e792d3e794dffbccd685ed96e24f1"}
Construct the Message String
The message string format is:
{timestamp}\n{nonce}\n{response_body}
Example:
1724170900177\n90ff8d49d2e646b7b57099b12cfb12c7\n{"payment_id": "c49e792d3e794dffbccd685ed96e24f1"}\n
Signature Verification
Use the provided public key and decoded signature to verify the signature of the above message string.
4. Verification Results
- Verification Success: If the signature verification passes, the response data has not been tampered with during transmission.
- Verification Failure: If the verification fails, the response data may have been altered, or the public key and signature do not match.
5. Sample Code
Here’s a sample code in Python demonstrating the signature verification:
import base64
from Crypto.Signature import pkcs1_15
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
def verify_signature(public_key_pem_str, signature_base64_str, message_str):
try:
public_key = RSA.import_key(public_key_pem_str.encode('utf-8'))
hash_obj = SHA256.new(message_str.encode('utf-8'))
signature = base64.b64decode(signature_base64_str)
pkcs1_15.new(public_key).verify(hash_obj, signature)
return True
except (ValueError, TypeError):
return False
if __name__ == "__main__":
# Common parameters from response headers
timestamp = "1724170900177"
nonce = "90ff8d49d2e646b7b57099b12cfb12c7"
# Sample signature
signature = "adS+OlZXSoIgrK+tCcX7tUy8dWYl5fOIVP5pPadG0GpS/TcmgAiV/NGWRbxIEnoky6yKYLBDevF6xcZcFl7G5ST7mdcBuFIhYdYppjEdqbHCCRmS4e99EWIhBtJMzDBfTqJXYSn4s+aA9QdMgh9vUVj6JfsRlQOO8vrzFBHZ6fHrlN8JS6nIsfUoa+gDAIKxExd28GNQPZIK6rPkFn9Drxz9mhpEa9cWauA7+e/O/hxRg/bCEk863L7hasb8ohgzyCFwYPpKDql91Lm5El0QlJuxUEoxfWTJeMlFdUloDMoRdsgoNsFlHsp2duxuHTJSu/CtaXfDxO814niPvls6Tw=="
# Sample response body
response_body = '{"payment_id": "c49e792d3e794dffbccd685ed96e24f1"}'
# Public key provided by the server (PEM format)
public_key_pem = """-----BEGIN PUBLIC KEY-----
(platform public key)
-----END PUBLIC KEY-----"""
# Construct the message string for verification
message = f"{timestamp}\n{nonce}\n{response_body}\n"
# Verify the signature
is_valid = verify_signature(public_key_pem, signature, message)
if is_valid:
print("Verification Successful")
else:
print("Verification Failed")
IP Whitelist
When calling the Benpay API, requests are only allowed from IP addresses set in your IP whitelist. You need to configure the calling IP address when setting up your API Key.
Request Base URL
Payment URL
After creating a payment order, append the payment ID to the below link to complete the payment:
https://www.benpay.org/paymvp/consumer/order/{your_payment_id}
Response Explanation
A successful request will return the following structure with HTTP status code 200:
{
"code": 0,
"msg": "ok",
"data": {}
}
An error request will return the following structure with HTTP status code 200:
{
"code": error code,
"msg": "error message",
"data": {}
}
An unauthorized request will return the following structure with HTTP status code 401:
{
"code": error code,
"msg": "error message",
"data": {}
}
API Documentation
Create Payment Order
API Description
Create a payment order. The status of a newly created order is INIT. A pre-transaction will be created simultaneously with the order. After this pre-transaction is successfully recorded on the blockchain, you can check the order status through the query api. Once the status changes to PAYING, payment can be initiated.
Type | URL | Method |
---|---|---|
RESTful | /v1/payment/create | POST |
Input Parameter Description
Parameter | Required | Type | Description |
---|---|---|---|
coin | Yes | string | Payment currency, currently only BUSD. |
coin_amount | Yes | string | Amount of the payment currency, as a string. |
out_trade_no | No | string | Merchant order number. |
Output Parameter Description
Parameter | Type | Description |
---|---|---|
payment_id | string | Generated payment order ID for tracking status. |
Input Example
{
"coin": "BUSD",
"coin_amount": "100.00",
"out_trade_no": "123456789"
}
Successful Response Example
{
"code": 0,
"msg": "ok",
"data": {
"payment_id": "042929b0f06a466680a9aae585f7d72b"
}
}
Get Payment Order List
API Description
Get payment order list.
Type | URL | Method |
---|---|---|
RESTful | /v1/payment/list | POST |
Input Parameter Description
Parameter | Type | Required | Description |
---|---|---|---|
page | int | No | Page number, default is 1. |
limit | int | No | Page size, default is 10. |
payment_id | string | No | Payment order ID for querying. |
out_trade_no | string | No | Merchant order number, used to identify payment orders. |
created_at_begin | int | No | Start timestamp for creation time, in seconds. |
created_at_end | int | No | End timestamp for creation time, in seconds. |
order_by | string | No | Order by field,option: created_at, created_at desc |
Output Parameter Description
Parameter | Type | Description |
---|---|---|
total | string | Total count. |
payments | list | List of payments. |
-- payment_id | string | Payment order ID. |
-- out_trade_no | string | Order number for identifying the payment. |
-- coin | string | Payment currency, currently only BUSD. |
-- coin_amount | string | Amount of the payment currency, as a string. |
-- status | string | Payment order status: INIT: Initialized, FAIL: Failed, PAYING: In progress, PAID:Paid, REFUNDING: Refunding, REFUNDED: Refunded, SETTLMENTING: In settlement, SETTLMENTED: Settled, TIMEOUT: Timed out, ARBITRAE: Arbitration, FREEZE: Frozen |
-- transaction_hash | string | Payment transaction hash. |
-- refund_transaction_hash | string | Refund transaction hash. |
-- settlement_transaction_hash | string | Settlement transaction hash. |
-- cross_transaction_hash | string | Cross-border transaction hash. |
-- pre_transaction_hash | string | Pre-transaction hash. |
-- arbitrate_transaction_hash | string | Arbitrate transaction hash. |
-- freeze_transaction_hash | string | Freeze transaction hash. |
-- timeout_transaction_hash | string | Timeout transaction hash. |
-- settlement_amount | string | Settlement amount. |
-- account_period_time | string | Account period timestamp. |
-- created_at | string | Timestamp of when the payment order was created. |
Input Example
{
"payment_id": "abc123def456"
}
Successful Response Example
{
"code": 0,
"msg": "ok",
"data": {
"total": "0",
"payments": [
{
"payment_id": "47f4078757a245e3b11c84a66e20cec8",
"out_trade_no": "20241118163814",
"coin": "BUSD",
"coin_amount": "0.05",
"status": "PAYING",
"transaction_hash": "",
"refund_transaction_hash": "",
"settlement_transaction_hash": "",
"cross_transaction_hash": "",
"account_period_time": "0",
"created_at": "1731919095",
"arbitrate_transaction_hash": "",
"freeze_transaction_hash": "",
"timeout_transaction_hash": "",
"settlement_amount": "0",
"merchant_note": "merchant note 111"
},
{
"payment_id": "eb7dbae231fd492e8928d6423777125b",
"out_trade_no": "20241118163707",
"coin": "BUSD",
"coin_amount": "0.05",
"status": "PAYING",
"transaction_hash": "",
"refund_transaction_hash": "",
"settlement_transaction_hash": "",
"cross_transaction_hash": "",
"account_period_time": "0",
"created_at": "1731919028",
"arbitrate_transaction_hash": "",
"freeze_transaction_hash": "",
"timeout_transaction_hash": "",
"settlement_amount": "0",
"merchant_note": "merchant note 111"
}
]
}
}
Get Payment Order Information
API Description
Get a payment order information.
Type | URL | Method |
---|---|---|
RESTful | /v1/payment/info | POST |
Input Parameter Description
Parameter | Type | Required | Description |
---|---|---|---|
payment_id | string | Yes | Payment order ID for querying. |
Output Parameter Description
Parameter | Type | Description |
---|---|---|
payment_id | string | Payment order ID. |
out_trade_no | string | Order number for identifying the payment. |
coin | string | Payment currency, currently only BUSD. |
coin_amount | string | Amount of the payment currency, as a string. |
status | string | Payment order status: INIT: Initialized, FAIL: Failed, PAYING: In progress, PAID:Paid, REFUNDING: Refunding, REFUNDED: Refunded, SETTLMENTING: In settlement, SETTLMENTED: Settled, TIMEOUT: Timed out, ARBITRAE: Arbitration, FREEZE: Frozen |
transaction_hash | string | Payment transaction hash. |
refund_transaction_hash | string | Refund transaction hash. |
settlement_transaction_hash | string | Settlement transaction hash. |
cross_transaction_hash | string | Cross-border transaction hash. |
pre_transaction_hash | string | Pre-transaction hash. |
arbitrate_transaction_hash | string | Arbitrate transaction hash. |
freeze_transaction_hash | string | Freeze transaction hash. |
timeout_transaction_hash | string | Timeout transaction hash. |
settlement_amount | string | Settlement amount. |
account_period_time | string | Account period timestamp. |
created_at | string | Timestamp of when the payment order was created. |
Input Example
{
"payment_id": "abc123def456"
}
Successful Response Example
{
"code": 0,
"msg": "ok",
"data": {
"payment_id": "d2e209245db341e8b990a70e5a8c5e0d",
"out_trade_no": "",
"coin": "BUSD",
"coin_amount": "100",
"status": "PAYING",
"transaction_hash": "",
"refund_transaction_hash": "",
"settlement_transaction_hash": "",
"cross_transaction_hash": "",
"account_period_time": "0",
"created_at": "1724344957"
}
}
Webhook
Webhook Overview
Webhooks are used to notify merchants of payments, refunds, settlements, freezes, arbitration, timeouts. If you wish to receive notifications, you can configure a callback URL in the merchant settings page.
Request description
The same authentication scheme as the API key is used, where Benpay uses the platform's private key to sign the request data, and the merchant uses the platform's public key to verify the request data. Benpay will send a POST request to the configured webhook URL with Content-Type
set to application/json;charset=UTF-8
. Upon receiving the webhook notification, the merchant needs to respond with a 200 HTTP status code and the content "success." If a non-success status code is received, Benpay will consider the notify failed and retry after 15s,30s,3m,10m,30m.
Parameter | Type | Description |
---|---|---|
notify_id | string | Notification ID |
notify_type | string | Notification type: PAID:Paid, REFUNDED: Refunded, SETTLMENTED: Settled, TIMEOUT: Timed out, ARBITRAE: Arbitration, FREEZE: Frozen |
notify_data | object | Notification data |
Notification Data
Parameter | Type | Description |
---|---|---|
payment_id | string | Payment ID |
chain | string | Chain |
merchant_id | string | Merchant ID |
out_trade_no | string | Merchant order number |
coin | string | Currency, currently BUSD only |
coin_amount | string | Coin amount |
status | string | Status |
transaction_hash | string | Transaction hash, returned at payment time |
refund_transaction_hash | string | Refund hash, return on refund |
settlement_transaction_hash | string | Settlement hash, returned at settlement |
arbitrate_transaction_hash | string | Arbitration hash, returned at arbitration |
freeze_transaction_hash | string | Freeze hash, return at frozen |
Error Codes
Error Code | Error Description |
---|---|
40101 | Incorrect Authorization header |
40102 | Duplicate nonce in Authorization header |
40103 | Expired timestamp in Authorization header |
40104 | Merchant not exist |
40105 | Invalid merchant |
40106 | Merchant public key missing |
40107 | Platform public key missing |
40108 | Merchant IP whitelist missing |
40109 | Unauthorized request IP |
40110 | Signature verification failed |
40180 | Response signature failed |
50001 | Invalid request parameters |
50011 | Order not exist |
50101 | Service error |
50102 | Network error |
50103 | Data error |
API SDK
We provide API SDKs in golang/python:
Change Log
2024-09-19 v
1.0.0
- Created documentation