Benpay api 文档
介绍
欢迎来到 Benpay 开发者文档!
您可以通过 Benpay API:
- 创建付款
- 查询付款
- 付款成功、退款成功 webhook 通知 ......
欢迎使用 Benpay API 管理您的收款,我们提供完善的文档和示例代码,也有专业的技术支持团队为您提供帮助。
接入指南
概述
申请成为商户后进入到商户后台,设置商户公钥、支付回调地址、通知地址、IP白名单、生成平台公钥,签名和验签使用SHA256withRSA2048算法。
请求参数签名
1. 签名算法概述
在API请求中,签名算法用于确保请求数据的完整性和来源的可靠性,没有携带签名或者签名验证不通过,Benpay 将会拒绝处理请求,并返回 401 Unauthorized,拒绝信息将在body返回,验签成功的请求会以http状态200返回,商户需对成功的响应或webhook请求进行验签,以防数据在传输过程中被篡改。以下将详细描述了如何生成签名、构造请求头的流程。
2. 签名生成步骤
签名的生成可以分为以下几个步骤:
2.1 准备签名的公共参数
生成签名时需要以下几个公共参数:
- api_key:商户分配的api唯一标识。
- timestamp:当前时间的时间戳(单位:毫秒),用于标识请求的时间,防止重放攻击,服务器会拒绝5分钟时间偏差的请求。
- nonce:一个随机字符串,每次请求需生成不同的值,确保请求的唯一性。
2.2 构造待签名字符串
将公共参数、HTTP请求方法、请求路径和请求体拼接成一个字符串,拼接的格式如下:
api_key={api_key},timestamp={timestamp},nonce={nonce}\n
{method}\n
{url_path}\n
{body}\n
- 签名串一共有四行,每一行为一个参数。结尾以\n结束,包括最后一行。如果参数本身以\n结束,也需要附加一个\n。
- 第一行是由
api_key
、timestamp
、nonce
拼接而成的公共参数字符串,参数之间用逗号分隔,参数顺序不限制。 - 第二行是HTTP请求方法(如
POST
、GET
)。 - 第三行是请求路径(如
/v1/payment/create
)。 - 第四行是请求体的JSON字符串。如果是GET请求,这一行是空字符串,但仍需要保留一个空行。
2.3 使用RSA私钥对待签名字符串进行签名
- 生成哈希值:使用SHA256算法对待签名字符串进行哈希计算,得到哈希值。
- 对哈希值进行加密:使用RSA算法和私钥对生成的哈希值进行加密,得到签名。
- 签名编码:对生成的签名进行Base64编码,得到最终的签名字符串。
2.4 生成Authorization请求头
将生成的签名与公共参数一起放入Authorization请求头中。格式如下:
Authorization: BENPAY-SHA256-RSA2048 api_key={api_key},timestamp={timestamp},nonce={nonce},signature={signature}
2.5 发送请求
在发送请求时,将生成的Authorization请求头与请求体一并发送给服务器。服务器将根据相同的规则验证签名的合法性。
2.6 示例代码
以下是Python实现的签名算法示例代码:
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-----
(你的私钥内容)
-----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)
响应参数验签
1. 验签过程概述
为了确保API响应的完整性和真实性,需要对响应数据进行签名验证。同样使用SHA256withRSA2048算法对响应参数验签。HTTP状态码非200响应无需验签。
2. 验签步骤
准备数据
- 公钥字符串:API提供的PEM格式的RSA公钥。
- 响应头参数:
Benpay-Nonce
,Benpay-Timestamp
,Benpay-Signature
。 - 响应body:响应的body内容。
处理公钥
将公钥字符串转换为字节串。PEM格式的公钥需按照PEM编码标准处理。
解码签名
将Base64编码的签名解码为字节串。这是为了将签名恢复为原始二进制形式,以便进行验证。
构造消息
消息字符串的构造应包括时间戳(
timestamp
)、随机字符串(nonce
)和API响应的正文(response_body
),并使用换行符(\n
)结尾。哈希计算
使用SHA256算法对消息字符串进行哈希计算。该过程将消息内容转换为固定长度的哈希值。
验证签名
使用RSA公钥对计算出的哈希值和解码后的签名进行验证。如果公钥能成功验证签名,则表示响应数据未被篡改。
- 验证过程:尝试用RSA公钥对哈希值进行解密,验证其是否与原始签名匹配。如果匹配,则签名有效;否则,签名无效。
3. 响应示例
假设API响应头包含以下信息:
Benpay-Nonce
:90ff8d49d2e646b7b57099b12cfb12c7
Benpay-Timestamp
:1724170900177
Benpay-Signature
:adS+OlZXSoIgrK+tCcX7tUy8dWYl5fOIVP5pPadG0GpS/TcmgAiV/NGWRbxIEnoky6yKYLBDevF6xcZcFl7G5ST7mdcBuFIhYdYppjEdqbHCCRmS4e99EWIhBtJMzDBfTqJXYSn4s+aA9QdMgh9vUVj6JfsRlQOO8vrzFBHZ6fHrlN8JS6nIsfUoa+gDAIKxExd28GNQPZIK6rPkFn9Drxz9mhpEa9cWauA7+e/O/hxRg/bCEk863L7hasb8ohgzyCFwYPpKDql91Lm5El0QlJuxUEoxfWTJeMlFdUloDMoRdsgoNsFlHsp2duxuHTJSu/CtaXfDxO814niPvls6Tw==
response_body
:{"payemnt_id": "c49e792d3e794dffbccd685ed96e24f1"}
构造消息字符串
消息字符串的格式为:
{timestamp}\n{nonce}\n{response_body}
即:
1724170900177\n90ff8d49d2e646b7b57099b12cfb12c7\n{"payemnt_id": "c49e792d3e794dffbccd685ed96e24f1"}\n
验签
使用提供的公钥和解码后的签名来验证上述消息字符串的签名。
4. 验签结果
- 验签成功:如果签名验证通过,表明响应数据在传输过程中未被篡改。
- 验签失败:如果签名验证失败,表明响应数据可能被篡改或公钥和签名不匹配。
5. 示例代码
以下是Python实现的签名算法示例代码:
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__":
# 从响应头获取的公共参数
timestamp = "1724170900177"
nonce = "90ff8d49d2e646b7b57099b12cfb12c7"
# 示例签名
signature = "adS+OlZXSoIgrK+tCcX7tUy8dWYl5fOIVP5pPadG0GpS/TcmgAiV/NGWRbxIEnoky6yKYLBDevF6xcZcFl7G5ST7mdcBuFIhYdYppjEdqbHCCRmS4e99EWIhBtJMzDBfTqJXYSn4s+aA9QdMgh9vUVj6JfsRlQOO8vrzFBHZ6fHrlN8JS6nIsfUoa+gDAIKxExd28GNQPZIK6rPkFn9Drxz9mhpEa9cWauA7+e/O/hxRg/bCEk863L7hasb8ohgzyCFwYPpKDql91Lm5El0QlJuxUEoxfWTJeMlFdUloDMoRdsgoNsFlHsp2duxuHTJSu/CtaXfDxO814niPvls6Tw=="
# 示例响应体
response_body = '{"payemnt_id": "c49e792d3e794dffbccd685ed96e24f1"}'
# 服务器提供的公钥(PEM格式)
public_key_pem = """-----BEGIN PUBLIC KEY-----
(平台公钥)
-----END PUBLIC KEY-----"""
# 构造待验证的消息字符串
message = f"{timestamp}\n{nonce}\n{response_body}\n"
# 验证签名
is_valid = verify_signature(public_key_pem, signature, message)
if is_valid:
print("验签成功")
else:
print("验签失败")
IP 白名单
在调用 Benpay API 时,只允许从您设置的 IP 白名单地址发起请求,您需要在设置 API Key 时设置调用发起的 IP 地址。
请求 base URL
支付URL
创建支付订单后,拼上付款id,跳到以下链接进行支付
https://www.benpay.org/paymvp/consumer/order/{your_payment_id}
响应说明
正确的请求会以http状态码200返回以下结构
{
"code": 0,
"msg": "ok",
"data": {}
}
异常的请求会以http状态码200返回以下结构
{
"code": 错误码,
"msg": "error msg",
"data": {}
}
未授权的请求会以http状态码401返回以下结构
{
"code": 错误码,
"msg": "error msg",
"data": {}
}
接口文档
创建支付订单
接口说明
创建支付订单,刚创建的订单状态是INIT,创建的订单的同时会创建一个预交易,要等这个预交易链上成功后,通过查询接口查询到订单状态变为PAYING,才能发起支付。
接口类型 | URL | Method |
---|---|---|
restful | /v1/payment/create | POST |
输入参数说明
参数名 | 类型 | 是否必需 | 描述 |
---|---|---|---|
coin | string | 是 | 支付币种,目前只支持 BUSD。 |
coin_amount | string | 是 | 支付币种的数量。需要是数字的字符串格式。 |
out_trade_no | string | 否 | 商户订单号。 |
输出参数说明
参数名 | 类型 | 描述 |
---|---|---|
payment_id | string | 生成的支付订单 ID,用于后续跟踪和查询支付状态。 |
输入示例
{
"coin": "BUSD",
"coin_amount": "100.00",
"out_trade_no": "123456789",
}
成功返回示例
{
"code": 0,
"msg": "ok",
"data": {
"payment_id": "042929b0f06a466680a9aae585f7d72b"
}
}
获取支付订单列表
接口说明
获取支付订单信息
接口类型 | URL | Method |
---|---|---|
restful | /v1/payment/list | POST |
输入参数说明
参数名 | 类型 | 是否必需 | 描述 |
---|---|---|---|
page | int | 否 | 页码,默认1。 |
limit | int | 否 | 页大小,默认10。 |
payment_id | int | 否 | 付款id |
out_trade_no | string | 否 | 商户订单号,用于标识支付订单。 |
created_at_begin | int | 否 | 创建开始时间戳,单位秒 |
created_at_end | int | 否 | 创建结束时间戳,单位秒 |
order_by | string | 否 | 排序,可填:created_at, created_at desc |
输出参数说明
参数名 | 类型 | 描述 |
---|---|---|
total | string | 总数。 |
payments | list | 付款列表。 |
-- payment_id | string | 支付订单 ID。 |
-- out_trade_no | string | 商户订单号,用于标识支付订单。 |
-- coin | string | 支付币种,目前只支持 BUSD。 |
-- coin_amount | string | 支付币种的数量。需要是数字的字符串格式。 |
-- status | string | 支付订单的状态, INIT:初始化, FAIL:失败, PAYING:付款中, PAID:已支付, REFUNDING:退款中, REFUNDED:已退款, SETTLMENTING:结算中, SETTLMENTED:已结算, TIMEOUT:超时, ARBITRAE:仲裁, FREEZE:冻结 |
-- transaction_hash | string | 支付交易的Hash。 |
-- refund_transaction_hash | string | 退款交易的Hash。 |
-- settlement_transaction_hash | string | 结算交易的Hash。 |
-- cross_transaction_hash | string | 跨境交易的Hash。 |
-- pre_transaction_hash | string | 预交易的Hash。 |
-- arbitrae_transaction_hash | string | 仲裁交易Hash。 |
-- freeze_transaction_hash | string | 冻结交易Hash。 |
-- timeout_transaction_hash | string | 超时交易Hash。 |
-- settlement_amount | string | 结算金额 |
-- account_period_time | string | 账户期时间戳。 |
-- created_at | string | 支付订单创建时间的时间戳。 |
输入示例
{
"payment_id": "abc123def456"
}
成功返回示例
{
"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",
"arbitrae_transaction_hash": "",
"freeze_transaction_hash": "",
"timeout_transaction_hash": "",
"settlement_amount": "0",
"merchant_note": "merchant note 111"
}
]
}
}
获取支付订单详情
接口说明
获取支付订单信息
接口类型 | URL | Method |
---|---|---|
restful | /v1/payment/info | POST |
输入参数说明
参数名 | 类型 | 是否必需 | 描述 |
---|---|---|---|
payment_id | string | 是 | 支付订单 ID,用于查询订单信息。 |
输出参数说明
参数名 | 类型 | 描述 |
---|---|---|
payment_id | string | 支付订单 ID。 |
out_trade_no | string | 订单号,用于标识支付订单。 |
coin | string | 支付币种,目前只支持 BUSD。 |
coin_amount | string | 支付币种的数量。需要是数字的字符串格式。 |
status | string | 支付订单的状态, INIT:初始化, FAIL:失败, PAYING:付款中, PAID:已支付, REFUNDING:退款中, REFUNDED:已退款, SETTLEMENTED:结算中, SETTLEMENTED:已结算, TIMEOUT:超时, ARBITRATE:仲裁, FREEZE:冻结 |
transaction_hash | string | 支付交易的Hash。 |
refund_transaction_hash | string | 退款交易的Hash。 |
settlement_transaction_hash | string | 结算交易的Hash。 |
cross_transaction_hash | string | 跨境交易的Hash。 |
pre_transaction_hash | string | 预交易的Hash。 |
arbitrate_transaction_hash | string | 仲裁交易Hash。 |
freeze_transaction_hash | string | 冻结交易Hash。 |
time_out_transaction_hash | string | 超时交易Hash。 |
settlement_amount | string | 结算金额 |
account_period_time | string | 账户期时间戳。 |
created_at | string | 支付订单创建时间的时间戳。 |
输入示例
{
"payment_id": "abc123def456"
}
成功返回示例
{
"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 概述
通过 Webhook 主动通知商户发起的付款单付款,退款,结算,冻结,仲裁,超时,若用户想接收通知,则可以在商户设置页面配置回调地址Webhook。
请求描述
采用与API key相同的鉴权方案,即Benpay使用平台私钥对请求数据进行验名,商户使用平台公钥对请求数据进行验签。Benpay向用户配置的Webhook回调地址发送发送一个 POST 请求,HTTP 请求头中 Content-Type 为 application/json;charset=UTF-8 。用户收到Webhook 推送通知后,需要返回成功应答(HTTP状态码 200 ),成功应答的内容是success;平台在收到非成功状态码时,会认为此次通知失败,会再次进行重试通知,再次通知的频率为 15s,30s,3m,10m,30m。
参数名 | 类型 | 描述 |
---|---|---|
notify_id | string | 通知id |
notify_type | string | 通知类型, PAID:已支付,REFUNDED:已退款, SETTLEMENTED:已结算, TIMEOUT:超时, ARBITRATE:仲裁, FREEZE:冻结 |
notify_data | object | 通知数据 |
通知数据
参数名 | 类型 | 描述 |
---|---|---|
payment_id | string | 付款id |
chain | string | 键名 |
mechant_id | string | 商户id |
out_trade_no | string | 商户单号 |
coin | string | 币种,目前只有BUSD |
coin_amount | string | 币金额 |
status | string | 状态 |
transaction_hash | string | 交易hash,付款时返回 |
refund_transaction_hash | string | 退款hash,退款时返回 |
settlement_transaction_hash | string | 结算hash,结算时返回 |
arbitrate_transaction_hash | string | 仲裁hash,仲裁时返回 |
freeze_transaction_hash | string | 冻结hash,冻结时返回 |
错误码
错误码 | 错误描述 |
---|---|
40301 | 请求头 Authorization 参数错误 |
40302 | 请求头 Authorization nonce 重复 |
40303 | 请求头 Authorization timestamp 过期 |
40304 | 商户不存在 |
40305 | 商户无效 |
40306 | 商户公钥未设置 |
40307 | 平台公钥未设置 |
40308 | 商户IP白名单未设置 |
40309 | 请求IP不允许 |
40310 | 验签失败 |
40380 | 响应结果签名失败 |
50001 | 请求参数错误 |
50011 | 订单不存在 |
50101 | 服务异常 |
50102 | 网络异常 |
50103 | 数据异常 |
API SDK
我们提供 golang/python 二种语言的 API SDK:
更新日志
2024-09-19 v1.0.0
- 创建文档