跳到主要内容

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_keytimestampnonce拼接而成的公共参数字符串,参数之间用逗号分隔,参数顺序不限制。
  • 第二行是 HTTP 请求方法(如 POSTGET)。
  • 第三行是请求路径(如 /v1/payment/create)。
  • 第四行是请求体的 JSON 字符串。如果是 GET 请求,这一行是空字符串,但仍需要保留一个空行。
2.3 使用 RSA 私钥对待签名字符串进行签名
  1. 生成哈希值:使用 SHA256 算法对待签名字符串进行哈希计算,得到哈希值。
  2. 对哈希值进行加密:使用 RSA 算法和私钥对生成的哈希值进行加密,得到签名。
  3. 签名编码:对生成的签名进行 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. 验签步骤

  1. 准备数据

    • 公钥字符串:API 提供的 PEM 格式的 RSA 公钥。
    • 响应头参数Benpay-NonceBenpay-TimestampBenpay-Signature
    • 响应 body:响应的 body 内容。
  2. 处理公钥

    将公钥字符串转换为字节串。PEM 格式的公钥需按照 PEM 编码标准处理。

  3. 解码签名

    将 Base64 编码的签名解码为字节串。这是为了将签名恢复为原始二进制形式,以便进行验证。

  4. 构造消息

    消息字符串的构造应包括时间戳(timestamp)、随机字符串(nonce)和 API 响应的正文(response_body),并使用换行符(\n)结尾。

  5. 哈希计算

    使用 SHA256 算法对消息字符串进行哈希计算。该过程将消息内容转换为固定长度的哈希值。

  6. 验证签名

    使用 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"}
  1. 构造消息字符串

    消息字符串的格式为:

    {timestamp}\n{nonce}\n{response_body}

    即:

    1724170900177\n90ff8d49d2e646b7b57099b12cfb12c7\n{"payemnt_id": "c49e792d3e794dffbccd685ed96e24f1"}\n
  2. 验签

    使用提供的公钥和解码后的签名来验证上述消息字符串的签名。

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

https://api.benfenpay.com

支付 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,才能发起支付。

接口类型URLMethod
restful/v1/payment/createPOST

输入参数说明

参数名类型是否必需描述
coinstring支付币种,参考支付币种说明,只能传商户后面配置的币种。
amountstring支付币种的数量。需要是数字的字符串格式。
merchant_order_nostring商户订单号。
merchant_notestring商户备注。

支付币种

支付币种支付订单下限支付订单上限
BUSD0.011,000,000
BJPY1100,000,000
BKRW151,500,000,000
BTHB0.550,000,000
BMYR0.055,000,000
BTWD0.550,000,000
BINR1100,000,000
BEUR0.011,000,000
BCAD0.011,000,000
BAUD0.011,000,000
BMXN0.220,000,000
BIDR1100,000,000

输出参数说明

参数名类型描述
payment_idstring生成的支付订单 ID,用于后续跟踪和查询支付状态。

输入示例

{
"coin": "BUSD",
"amount": "100.00",
"merchant_order_no": "123456789"
}

成功返回示例

{
"code": 0,
"msg": "ok",
"data": {
"payment_id": "042929b0f06a466680a9aae585f7d72b"
}
}

获取支付订单列表

接口说明

获取支付订单信息

接口类型URLMethod
restful/v1/payment/listPOST

输入参数说明

参数名类型是否必需描述
pageint页码,默认 1。
limitint页大小,默认 10。
payment_idint付款 id
merchant_order_nostring商户订单号,用于标识支付订单。
created_at_beginint创建开始时间戳,单位秒
created_at_endint创建结束时间戳,单位秒
order_bystring排序,可填:created_at, created_at desc

输出参数说明

参数名类型描述
totalstring总数
paymentslist付款列表
-- payment_idstring支付订单唯一标识
-- merchant_order_nostring商户订单号
-- coinstring支付使用的数字货币币种(如:BUSD)
-- amountstring支付金额(字符串格式的数字)
-- statusstring订单状态:
• paying:支付中
• paid:支付成功
• tosettle:待结算
• settling:结算中
• settled:结算完成
• refunding:退款中
• refunded:已退款
• contractpaid:合约支付完成
• timeout:支付超时
-- created_atstring订单创建时间戳
-- merchant_notestring商户附加的订单备注信息
-- pay_atstring支付成功时间戳
-- settle_atstring资金结算完成时间戳
-- settle_amountstring实际结算金额
-- settle_tx_hashstring结算交易哈希
-- refund_amountstring退款金额
-- refund_atstring退款处理完成时间戳
-- refund_tx_hashstring退款交易哈希
-- refund_addressstring资金退回的地址
-- bill_due_tostring订单结算时间
-- expire_atstring订单失效时间戳

输入示例

{
"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"
}
]
}
}

获取支付订单详情

接口说明

获取支付订单信息

接口类型URLMethod
restful/v1/payment/infoPOST

输入参数说明

参数名类型是否必需描述
payment_idstring支付订单 ID,用于查询订单信息。

输出参数说明

参数名类型描述
payment_idstring支付订单唯一标识
merchant_order_nostring商户订单号
coinstring支付使用的数字货币币种(如:BUSD)
amountstring支付金额(字符串格式的数字)
statusstring订单状态:
• paying:支付中
• paid:支付成功
• settling:结算中
• settled:结算完成
• refunding:退款中
• refunded:已退款
• contractpaid:合约支付完成
• timeout:支付超时
created_atstring订单创建时间戳
merchant_notestring商户附加的订单备注信息
pay_atstring支付成功时间戳
settle_atstring资金结算完成时间戳
settle_amountstring实际结算金额
settle_tx_hashstring结算交易哈希
refund_amountstring退款金额
refund_atstring退款处理完成时间戳
refund_tx_hashstring退款交易哈希
refund_addressstring资金退回的地址
bill_due_tostring订单结算时间
expire_atstring订单失效时间戳

输入示例

{
"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。

参数名类型描述
idstring通知 id
typestring通知类型;
paid:已支付;
refunded:已退款;
settled:已结算;
timeout:超时
dataobject通知数据

通知数据

参数名类型描述
payment_idstring付款 id
chainstring键名
mechant_idstring商户 id
merchant_order_nostring商户单号
coinstring币种
amountstring币金额
statusstring状态
pay_atstring支付时间,支付通知返回
settle_atstring结算时间,结算通知返回
settle_amountstring结算金额,结算通知返回
settle_tx_hashstring结算 hash,结算通知返回
refund_amountstring退款金额,退款通知返回
refund_atstring退款时间,退款通知返回
refund_tx_hashstring退款 hash,退款通知返回
refund_addressstring退款地址,退款通知返回
expire_atstring超时时间,超时通知返回

错误码

错误码错误描述
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

  • 创建文档