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",
"amount": "0.1",
"merchant_order_no": "202501010001"
}
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:90ff8d49d2e646b7b57099b12cfb12c7Benpay-Timestamp:1724170900177Benpay-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 | 是 | 支付币种,参考支付币种说明,只能传商户后面配置的币种。 |
| amount | string | 是 | 支付币种的数量。需要是数字的字符串格式。 |
| merchant_order_no | string | 是 | 商户订单号。 |
| merchant_note | string | 否 | 商户备注。 |
支付币种
| 支付币种 | 支付订单下限 | 支付订单上限 |
|---|---|---|
| BUSD | 0.01 | 1,000,000 |
| BJPY | 1 | 100,000,000 |
| BKRW | 15 | 1,500,000,000 |
| BTHB | 0.5 | 50,000,000 |
| BMYR | 0.05 | 5,000,000 |
| BTWD | 0.5 | 50,000,000 |
| BINR | 1 | 100,000,000 |
| BEUR | 0.01 | 1,000,000 |
| BCAD | 0.01 | 1,000,000 |
| BAUD | 0.01 | 1,000,000 |
| BMXN | 0.2 | 20,000,000 |
| BIDR | 1 | 100,000,000 |
输出参数说明
| 参数名 | 类型 | 描述 |
|---|---|---|
| payment_id | string | 生成的支付订单 ID,用于后续跟踪和查询支付状态。 |
输入示例
{
"coin": "BUSD",
"amount": "100.00",
"merchant_order_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 |
| merchant_order_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 | 支付订单唯一标识 |
| -- merchant_order_no | string | 商户订单号 |
| -- coin | string | 支付使用的数字货币币种(如:BUSD) |
| -- amount | string | 支付金额(字符串格式的数字) |
| -- status | string | 订单状态: • paying:支付中 • paid:支付成功 • tosettle:待结算 • settling:结算中 • settled:结算完成 • refunding:退款中 • refunded:已退款 • contractpaid:合约支付完成 • timeout:支付超时 |
| -- created_at | string | 订单创建时间戳 |
| -- merchant_note | string | 商户附加的订单备注信息 |
| -- pay_at | string | 支付成功时间戳 |
| -- settle_at | string | 资金结算完成时间戳 |
| -- settle_amount | string | 实际结算金额 |
| -- settle_tx_hash | string | 结算交易哈希 |
| -- refund_amount | string | 退款金额 |
| -- refund_at | string | 退款处理完成时间戳 |
| -- refund_tx_hash | string | 退款交易哈希 |
| -- refund_address | string | 资金退回的地址 |
| -- bill_due_to | string | 订单结算时间 |
| -- expire_at | string | 订单失效时间戳 |
输入示例
{
"payment_id": "abc123def456"
}
成功返回示例
{
"code": 0,
"msg": "ok",
"data": {
"total": "0",
"list": [
{
"payment_id": "47f4078757a245e3b11c84a66e20cec8",
"merchant_order_no": "order_789012",
"coin": "BUSD",
"amount": "100.50",
"status": "paying",
"created_at": "1731919028",
"merchant_note": "VIP customer monthly subscription",
"pay_at": "2023-07-15T12:35:12Z",
"settle_at": "2023-07-16T00:00:00Z",
"settle_amount": "99.50",
"settle_tx_hash": "0x4a6b8c...3d2e1f",
"refund_amount": "0",
"refund_at": "1731919028",
"refund_tx_hash": "",
"refund_address": "",
"bill_due_to": "1731919028",
"expire_at": "1731919028"
},
{
"payment_id": "1f7d8a94b3714e4ba371db5316871bbb",
"merchant_order_no": "order_59855",
"coin": "BUSD",
"amount": "100.",
"status": "paid",
"created_at": "1731919028",
"merchant_note": "VIP customer monthly subscription",
"pay_at": "1731919028",
"settle_at": "1731919028",
"settle_amount": "99.50",
"settle_tx_hash": "0x4a6b8c...3d2e1f",
"refund_amount": "0",
"refund_at": "1731919028",
"refund_tx_hash": "",
"refund_address": "",
"bill_due_to": "1731919028",
"expire_at": "1731919028"
}
]
}
}
获取支付订单详情
接口说明
获取支付订单信息
| 接口类型 | URL | Method |
|---|---|---|
| restful | /v1/payment/info | POST |
输入参数说明
| 参数名 | 类型 | 是否必需 | 描述 |
|---|---|---|---|
| payment_id | string | 是 | 支付订单 ID,用于查询订单信息。 |
输出参数说明
| 参数名 | 类型 | 描述 |
|---|---|---|
| payment_id | string | 支付订单唯一标识 |
| merchant_order_no | string | 商户订单号 |
| coin | string | 支付使用的数字货币币种(如:BUSD) |
| amount | string | 支付金额(字符串格式的数字) |
| status | string | 订单状态: • paying:支付中 • paid:支付成功 • settling:结算中 • settled:结算完成 • refunding:退款中 • refunded:已退款 • contractpaid:合约支付完成 • timeout:支付超时 |
| created_at | string | 订单创建时间戳 |
| merchant_note | string | 商户附加的订单备注信息 |
| pay_at | string | 支付成功时间戳 |
| settle_at | string | 资金结算完成时间戳 |
| settle_amount | string | 实际结算金额 |
| settle_tx_hash | string | 结算交易哈希 |
| refund_amount | string | 退款金额 |
| refund_at | string | 退款处理完成时间戳 |
| refund_tx_hash | string | 退款交易哈希 |
| refund_address | string | 资金退回的地址 |
| bill_due_to | string | 订单结算时间 |
| expire_at | string | 订单失效时间戳 |
输入示例
{
"payment_id": "abc123def456"
}
成功返回示例
{
"code": 0,
"msg": "ok",
"data": {
"payment_id": "1f7d8a94b3714e4ba371db5316871bbb",
"merchant_order_no": "order_59855",
"coin": "BUSD",
"amount": "100.",
"status": "paid",
"created_at": "1731919028",
"merchant_note": "VIP customer monthly subscription",
"pay_at": "1731919028",
"settle_at": "1731919028",
"settle_amount": "99.50",
"settle_tx_hash": "0x4a6b8c...3d2e1f",
"refund_amount": "0",
"refund_at": "1731919028",
"refund_tx_hash": "",
"refund_address": "",
"bill_due_to": "1731919028",
"expire_at": "1731919028"
}
}
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。
| 参数名 | 类型 | 描述 |
|---|---|---|
| id | string | 通知 id |
| type | string | 通知类型; paid:已支付; refunded:已退款; settled:已结算; timeout:超时 |
| data | object | 通知数据 |
通知数据
| 参数名 | 类型 | 描述 |
|---|---|---|
| payment_id | string | 付款 id |
| chain | string | 键名 |
| mechant_id | string | 商户 id |
| merchant_order_no | string | 商户单号 |
| coin | string | 币种 |
| amount | string | 币金额 |
| status | string | 状态 |
| pay_at | string | 支付时间,支付通知返回 |
| settle_at | string | 结算时间,结算通知返回 |
| settle_amount | string | 结算金额,结算通知返回 |
| settle_tx_hash | string | 结算 hash,结算通知返回 |
| refund_amount | string | 退款金额,退款通知返回 |
| refund_at | string | 退款时间,退款通知返回 |
| refund_tx_hash | string | 退款 hash,退款通知返回 |
| refund_address | string | 退款地址,退款通知返回 |
| expire_at | string | 超时时间,超时通知返回 |
错误码
| 错误码 | 错误描述 |
|---|---|
| 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
- 创建文档