跳到主要内容

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",
"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. 验签步骤

  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支付币种,目前只支持 BUSD。
coin_amountstring支付币种的数量。需要是数字的字符串格式。
out_trade_nostring商户订单号。

输出参数说明

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

输入示例

{
"coin": "BUSD",
"coin_amount": "100.00",
"out_trade_no": "123456789",
}

成功返回示例

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

获取支付订单列表

接口说明

获取支付订单信息

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

输入参数说明

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

输出参数说明

参数名类型描述
totalstring总数。
paymentslist付款列表。
-- payment_idstring支付订单 ID。
-- out_trade_nostring商户订单号,用于标识支付订单。
-- coinstring支付币种,目前只支持 BUSD。
-- coin_amountstring支付币种的数量。需要是数字的字符串格式。
-- statusstring支付订单的状态,
INIT:初始化,
FAIL:失败,
PAYING:付款中,
PAID:已支付,
REFUNDING:退款中,
REFUNDED:已退款,
SETTLMENTING:结算中,
SETTLMENTED:已结算,
TIMEOUT:超时,
ARBITRAE:仲裁,
FREEZE:冻结
-- transaction_hashstring支付交易的Hash。
-- refund_transaction_hashstring退款交易的Hash。
-- settlement_transaction_hashstring结算交易的Hash。
-- cross_transaction_hashstring跨境交易的Hash。
-- pre_transaction_hashstring预交易的Hash。
-- arbitrae_transaction_hashstring仲裁交易Hash。
-- freeze_transaction_hashstring冻结交易Hash。
-- timeout_transaction_hashstring超时交易Hash。
-- settlement_amountstring结算金额
-- account_period_timestring账户期时间戳。
-- created_atstring支付订单创建时间的时间戳。

输入示例

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

获取支付订单详情

接口说明

获取支付订单信息

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

输入参数说明

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

输出参数说明

参数名类型描述
payment_idstring支付订单 ID。
out_trade_nostring订单号,用于标识支付订单。
coinstring支付币种,目前只支持 BUSD。
coin_amountstring支付币种的数量。需要是数字的字符串格式。
statusstring支付订单的状态,
INIT:初始化,
FAIL:失败,
PAYING:付款中,
PAID:已支付,
REFUNDING:退款中,
REFUNDED:已退款,
SETTLEMENTED:结算中,
SETTLEMENTED:已结算,
TIMEOUT:超时,
ARBITRATE:仲裁,
FREEZE:冻结
transaction_hashstring支付交易的Hash。
refund_transaction_hashstring退款交易的Hash。
settlement_transaction_hashstring结算交易的Hash。
cross_transaction_hashstring跨境交易的Hash。
pre_transaction_hashstring预交易的Hash。
arbitrate_transaction_hashstring仲裁交易Hash。
freeze_transaction_hashstring冻结交易Hash。
time_out_transaction_hashstring超时交易Hash。
settlement_amountstring结算金额
account_period_timestring账户期时间戳。
created_atstring支付订单创建时间的时间戳。

输入示例

{
"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_idstring通知id
notify_typestring通知类型,
PAID:已支付,REFUNDED:已退款,
SETTLEMENTED:已结算,
TIMEOUT:超时,
ARBITRATE:仲裁,
FREEZE:冻结
notify_dataobject通知数据

通知数据

参数名类型描述
payment_idstring付款id
chainstring键名
mechant_idstring商户id
out_trade_nostring商户单号
coinstring币种,目前只有BUSD
coin_amountstring币金额
statusstring状态
transaction_hashstring交易hash,付款时返回
refund_transaction_hashstring退款hash,退款时返回
settlement_transaction_hashstring结算hash,结算时返回
arbitrate_transaction_hashstring仲裁hash,仲裁时返回
freeze_transaction_hashstring冻结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

  • 创建文档