BESOFINANCE.peer DOCS 首页 商家后台 申请开户
开发者文档 · v1(统一 API,接口以本页为准)

介绍

通过买家已经在用的钱包 App 收款:Venmo、Cash App、Zelle、PayPal、Chime、Revolut、Wise、Monzo、N26。付款经零知识证明验证后,资金点对点直达你指定的链上钱包。买家也可以直接用链上钱包付数字货币,或用 Apple Pay。

besofinance.peer 在 Peer 支付网络之上提供统一 API:你只对接我们一套接口和一套密钥,每个商户在支付网络上有自己独立的收款账户(子账户),资金不经过我们,直接结算到你的钱包。

买家付款方式

方式说明
法币钱包9 个钱包 App,报价支持 33 种法币(见“支持的法币钱包”),实际可用以当时的流动性为准。
数字货币买家选择一条链(12 条 Relay 链 + Zcash),用该网络上的钱包付款,转账桥接到你设置的收款币种与网络。
Apple Pay经 Coinbase Apple Pay 付款,按其结算所走的 Relay 通道记账。

一笔支付如何完成

  1. 你的后端创建订单
    调用 POST /v1/orders(或 SDK 的 createCheckout),传入金额,得到结账页地址 checkoutUrl。
  2. 买家选择钱包
    买家在结账页看到金额,选择自己的付款 App 或数字货币,生成一次支付尝试(payment)。
  3. 买家完成付款
    按页面指引在自己的 App 里转账,无需注册账户。
  4. 零知识证明验证
    系统用零知识证明确认付款真实发生,不泄露买家账户信息。
  5. 结算与通知
    USDC 结算到你的钱包,我们向你的服务器发送签名的 ORDER_FULFILLED Webhook。

两种验证方式

方式说明
买家验证(默认)买家在结账页内(手机 App Clip 或桌面浏览器扩展)确认自己的付款,凭证不经过我们。Zelle 与 N26 需要桌面浏览器扩展。
卖家自动放行(SAR)买家无需安装或操作任何东西,系统按收款方自动核对到账。适用于 Venmo、Cash App、Wise 与 PayPal 中收款方支持的情况,是否开通取决于你的账户配置,由 Besofinance 为你开通。若买家已付款但未验证,可用 fulfill-sar 凭交易号放行(PayPal 除外)。

接入方式

方式适合场景
托管结账页(API / SDK)自有购物车、自定义下单流程。SDK:Node.js、Python、PHP、Go
电商插件WooCommerce、Shopify、Magento 2、OpenCart、PrestaShop、WHMCS(见“电商插件与多语言 SDK”)
支付链接发票、私信收款:商家后台“扫码收款”页生成链接
扫码支付线下柜台收款:同一链接生成二维码

上线前准备

时限说明:订单本身不会过期;会过期的是买家的每次支付尝试:法币约 1 小时,Zcash 20 分钟内需完成充值;Relay 数字货币尝试不会进入 EXPIRED,约 6 小时后未完成会变为 FAILED。尝试过期后买家可以在同一订单上重新发起付款,你也可以用 补救接口 重新打开或延长。

快速开始

1. 拿到密钥

在商家后台“开发者 → API 密钥”创建一把沙盒密钥,得到公钥 beso_pk_test_… 和私钥 beso_sk_test_…。私钥只显示一次,保存到服务器环境变量:

BESO_API_KEY=beso_pk_test_xxxxxxxx
BESO_API_SECRET=beso_sk_test_xxxxxxxx
BESO_WEBHOOK_SECRET=whsec_xxxxxxxx   # 注册 Webhook 时返回

2. 安装 SDK

npm install @besofinance/peer-sdk

需要 Node.js 18 及以上。SDK 会自动为每个请求签名(见 鉴权与请求签名)。Python / PHP / Go 见 多语言 SDK。

3. 创建结账

import { BesoClient } from '@besofinance/peer-sdk';

const beso = new BesoClient({
  apiBaseUrl: 'https://api.peer.besofinance.xyz/v1',
  apiKey: process.env.BESO_API_KEY,
  apiSecret: process.env.BESO_API_SECRET,
});

const order = await beso.createCheckout({
  requestedAmount: '50.00',
  requestedCurrency: 'USD',
  reference: 'ORDER-1001',          // 你的订单号,同时是幂等键
  settleChain: 8453,                  // 可选,数字链 ID;不传用开户时登记的收款钱包
  settleToken: 'USDC',
  successUrl: 'https://yoursite.com/orders/1001',
  notes: { customerId: 'C-88' },
});

4. 跳转到结账页

// 后端把 order.checkoutUrl 返回给前端
window.location.href = order.checkoutUrl;
支付完成后结账页只显示“返回商户”链接(指向 successUrl),不会自动跳转;cancelUrl 目前不会被访问。请以 Webhook 为准判断是否已付款。

5. 处理 Webhook

在你的服务器上提供一个接口,先验证签名,再监听 ORDER_FULFILLED 事件,把订单标记为已支付。详见 验签。

6. 端到端测试

// 沙盒:新建一张 10 美元的测试单并直接结算,触发真实签名的 ORDER_FULFILLED
await beso.sandboxTestOrder({ amount: '10.00' });
// 或者结算你刚用 createCheckout 建的那张单
await beso.sandboxTestOrder({ orderId: order.id });
沙盒与正式环境用不同的密钥,数据完全隔离,但都要求账户已开通、订阅有效。接口调用失败时,SDK 抛出 BesoApiError(旧名 PeerApiError 仍可用),含 statusCode、errorCode、fieldErrors。

开户、订阅与环境

开户流程

  1. 提交申请
    在 login.html 的“申请开户”页填写公司、联系邮箱、网站、行业、预计月交易额、收款钱包和后台登录密码;或调用 POST /v1/onboarding/applications(无需密钥)。
  2. 审核
    Besofinance 审核(KYB)。状态:pending_review → approved / rejected。审核期间可以登录后台查看进度,但不能生成密钥、不能调用 API(403 MERCHANT_NOT_ACTIVE)。
  3. 开设收款账户
    审核通过后,Besofinance 在支付网络上为你开设独立的子账户(沙盒 + 正式各一个)。每个子账户有自己的 API 密钥和结算通知签名密钥,与其他商户完全隔离;收款设置(含手续费由谁承担,默认商户承担)沿用平台账户。
  4. 支付网络店铺审核
    正式收款前,支付网络还会审核你的店铺资料(网站、经营内容等)。审核期间沙盒可正常联调,正式环境状态为 pending_peer_review,不能生成正式密钥,正式环境建单返回 403 PEER_REVIEW_PENDING。审核通过(approved)后自动解除。当前状态见 GET /v1/merchants/me 的 liveReviewStatus。
  5. 开通与首期账单
    开通后状态变为 active,系统生成首期订阅账单(结账链接),付款后即可使用。
  6. 生成密钥
    在后台“开发者”页按环境生成 API 密钥(每个环境最多 5 把有效密钥;正式密钥需店铺审核通过)。

开户申请接口

POST https://api.peer.besofinance.xyz/v1/onboarding/applications
Content-Type: application/json

{
  "companyName": "Acme Ltd",
  "contactEmail": "ops@acme.com",
  "password": "至少 10 位,用于登录商家后台",
  "website": "https://acme.com",
  "industry": "电商",
  "country": "SG",
  "expectedMonthlyVolume": "50000",
  "note": "可选备注",
  "payoutAddress": "0x…",
  "payoutChainId": 8453,
  "payoutToken": "USDC"
}

返回 201 与 { merchantId, onboardingStatus: "pending_review" }。同一邮箱重复申请返回 409 APPLICATION_EXISTS;每个 IP 每分钟最多 10 次。

订阅计费

项目说明
费用每个商户 1,000 美元 / 月(以商务协议为准),按自然月周期,开通日起算。
付款方式后台“账单”页的“续费”按钮生成结账链接,用任一支持的钱包或数字货币支付;也可调用 POST /v1/merchants/me/subscription/invoices。到期前 5 天会自动生成下期账单。
宽限期到期后有 3 天宽限期(状态 past_due),期间接口照常可用。
自动停用宽限期结束仍未付款,状态变为 suspended:除账户/账单类接口外,所有 API 请求返回 402 SUBSCRIPTION_EXPIRED,后台无法新建订单。
自动恢复账单付款结算后立即恢复为 active,无需人工处理。
交易手续费订阅费之外的交易手续费在开户时书面约定,默认由商户承担,从结算金额中扣除(见支付对象的 totalUsdcFeeAmount)。

停用期间仍可用的接口:GET /merchants/me、订阅与账单、/integration/status、Webhook 配置与投递日志、退款申请。被 Besofinance 人工冻结(风控)时所有接口返回 403 MERCHANT_SUSPENDED。

沙盒与正式环境

沙盒正式
密钥前缀beso_pk_test_ / beso_sk_test_beso_pk_live_ / beso_sk_live_
资金不动真钱,支付由模拟结算真实付款与结算
数据订单、支付、Webhook、投递日志、IP 白名单按环境完全隔离;同一个 reference 在两个环境里是两张单
只在该环境可用POST /sandbox/test-order支付补救、退款

环境由密钥决定,无需额外参数。响应里的订单、Webhook 事件都带 environment 字段(SANDBOX / LIVE)。商家后台用右上角的环境开关切换。

SDK 参考

请在后端创建订单,API 私钥只能保存在服务器端。Node.js 包名 @besofinance/peer-sdk(0.2+ 自动签名)。所有函数都有两种写法:函数式 createCheckout(params, opts),或面向对象 new BesoClient(opts).createCheckout(params)。

客户端参数

参数说明
apiKey公钥 beso_pk_test_… / beso_pk_live_…(旧版单一密钥 beso_sk_… 也可,此时不传 apiSecret)
apiSecret私钥,用于请求签名
apiBaseUrl默认 https://api.peer.besofinance.xyz/v1
timeoutMs / signal / fetcher可选:超时(默认 15 秒)、取消信号、自定义 fetch

函数

函数对应接口
createCheckout(params, opts, idempotencyKey?)POST /orders,返回订单(含 checkoutUrl)
getCheckoutUrl(order)从订单对象取结账页地址(重放请求时可能为 null,见“创建订单”)
createCheckoutAndRedirect(params, opts)创建订单;在浏览器环境中立即跳转(仅限你自己的后端代理场景,勿把私钥放进浏览器)
getMerchantOrder / getOrderGET /merchants/me/orders/{id}(对账用) / GET /orders/{id}(免密钥)
listOrders / listPayments / listOrderPayments订单与支付列表
getMerchant / getUsage / getTierUsage / getIntegrationStatus商家资料、用量、套餐额度、接入状态
checkQuoteAvailabilityPOST /merchants/me/quotes/availability
createWebhook / listWebhooks / updateWebhook / rotateWebhookSecret / deleteWebhook / testWebhookWebhook 管理
listDeliveries / getDelivery / resendDelivery投递日志与手动重发
createRefundRequest / listRefundRequests / getRefundRequest / cancelRefundRequest退款申请
paymentAction / getAction支付补救(仅正式环境)
getSubscription / createSubscriptionInvoice订阅与账单
sandboxTestOrder沙盒测试单
verifyWebhook(rawBody, headers, secret)Webhook 验签

错误处理

import { BesoApiError } from '@besofinance/peer-sdk';

try {
  await beso.createCheckout(params);
} catch (error) {
  if (error instanceof BesoApiError) {
    console.error(error.statusCode, error.errorCode, error.fieldErrors);
    if (error.errorCode === 'SUBSCRIPTION_EXPIRED') { /* 提醒续费 */ }
  }
}

BesoApiError 包含:statusCode、errorCode、message、fieldErrors、formErrors、responseObject。PeerApiError 是它的别名,旧代码无需修改;另提供与 Peer SDK 同名的别名 PayApiError,从 @zkp2p/pay-sdk 迁移时只需改 import。

电商插件与多语言 SDK

插件和 SDK 的源码都在交付包的 plugins/ 与 sdk/ 目录中,全部对接本页的统一 API(公钥 + 私钥签名),收到 ORDER_FULFILLED 后把订单置为已支付。

平台目录形式
WooCommerceplugins/woocommerceWordPress 插件(支付网关 + Webhook 回调)
Shopifyplugins/shopify独立小服务(Node.js):订单创建时生成支付链接,收到 Webhook 后通过 Admin API 标记已付款
Magento 2plugins/magento2Magento 模块 Besofinance_Peer
OpenCartplugins/opencartOpenCart 4 扩展
PrestaShopplugins/prestashopPrestaShop 8 支付模块
WHMCSplugins/whmcs第三方支付网关模块 + 回调
Node.js / TypeScriptsdk/@besofinance/peer-sdk
Pythonsdk/python单文件,无第三方依赖(Python 3.8+)
PHPsdk/php单文件,依赖 ext-curl(PHP 7.4+)
Gosdk/go标准库实现(Go 1.20+)

插件统一的配置项:API 基础地址、公钥、私钥、Webhook 签名密钥;可选:结算链 ID、结算币种、收款地址(不填用开户时登记的钱包)。

AI 辅助接入

特别声明:本板块只用于让 AI 帮助开发者快速接入 SDK 与 API 密钥,即用 AI 替你把 SDK 和 Webhook 写进你自己的项目。它不是在 AI 对话里直接收单、下单或付款的功能,买家付款仍然在买家自己的钱包 App 中完成。

如果你习惯用 AI 编程助手,可以把下面这段提示词交给它,让它按我们的文档,在你的项目里完成 SDK 安装、创建结账、跳转和 Webhook 验签。

使用步骤

  1. 准备密钥
    在商家后台“开发者 → API 密钥”生成沙盒密钥(公钥 + 私钥)。
  2. 放进环境变量
    写到项目的 .env:BESO_API_KEY、BESO_API_SECRET、BESO_WEBHOOK_SECRET。
  3. 交给 AI
    把下面的提示词发给你的 AI 编程助手,并让它在你的项目目录里工作。
  4. 本地验证
    用沙盒跑通:创建结账 → 调用沙盒测试单结算 → 收到 ORDER_FULFILLED Webhook。
  5. 切换正式环境
    确认无误后,换成正式密钥上线。
密钥安全:不要把 API 密钥直接粘贴进 AI 对话。请只告诉 AI 读取环境变量。私钥只能放在服务器端,不要写进前端代码、也不要提交到代码仓库。

提示词模板

请帮我在当前项目里接入 besofinance.peer 的支付 API。要求:

1. 安装 npm 包 @besofinance/peer-sdk(Node.js 18+),用 new BesoClient({ apiBaseUrl,
   apiKey, apiSecret }) 创建客户端;SDK 会自动给每个请求加 X-Timestamp / X-Nonce /
   X-Signature 签名。
2. API 基础地址 https://api.peer.besofinance.xyz/v1 ;公钥从环境变量 BESO_API_KEY、
   私钥从 BESO_API_SECRET 读取,不要写进代码,不要让我把密钥贴给你。
3. 在后端新增创建订单接口:调用 createCheckout,传 requestedAmount + requestedCurrency
   (或 requestedUsdcAmount)、reference(我们自己的订单号,用作幂等键)、successUrl、notes。
   把返回的 checkoutUrl 给前端跳转。successUrl 只是“返回商户”链接,不会自动跳转。
4. 新增 Webhook 接收接口:读取原始请求体,用 X-Webhook-Timestamp 与 X-Webhook-Signature
   做 HMAC-SHA256 验签(签名内容为 timestamp + "." + 原始请求体,十六进制;secret 来自
   BESO_WEBHOOK_SECRET),时间戳误差超过 5 分钟就拒绝;用 X-Webhook-Id 去重;先返回 2xx
   再异步处理;只在收到 ORDER_FULFILLED 时才把订单标记为已支付并发货(用 data.order.reference
   或 data.order.id 对应到我们的订单)。
5. 接口调用失败时捕获 BesoApiError,记录 statusCode 与 errorCode;402 SUBSCRIPTION_EXPIRED
   表示订阅到期。
6. 全程使用沙盒密钥测试:调用 POST /v1/sandbox/test-order 结算测试单,并给我一份本地测试步骤。

只做以上 SDK 与 API 密钥的接入,不要改动与支付无关的代码。

它能做什么,不能做什么

可以不可以
帮你安装 SDK、写创建结账与跳转代码在 AI 对话里直接替买家付款
帮你写 Webhook 验签与去重逻辑绕过后台,自行生成或保管你的 API 密钥
帮你写本地测试步骤替你决定是否上线或放行订单

API 参考 · 鉴权与请求签名

API 密钥

请求签名

每个带密钥的请求都必须带以下三个请求头:

请求头说明
X-Timestamp当前 Unix 时间(秒),与服务器时间相差不能超过 300 秒
X-Nonce每个请求唯一的随机串,16–64 位 [A-Za-z0-9_-];同一把密钥的 nonce 在 10 分钟内不能重复(防重放)
X-Signature十六进制 HMAC-SHA256,算法如下
待签名串 = X-Timestamp + "\n"
         + X-Nonce     + "\n"
         + 大写 HTTP 方法(GET / POST / PATCH / DELETE)+ "\n"
         + 请求路径和查询串(以 /v1 开头,如 /v1/merchants/me/orders?page=2)+ "\n"
         + hex(SHA256(原始请求体))      # 没有请求体时是空串的 SHA256

X-Signature = hex(HMAC-SHA256(私钥, 待签名串))
请对实际发送的字节计算 SHA256(先序列化 JSON,再签名,再原样发送)。路径与查询串按发送时的原样,不要重新排序或解码。

curl 示例

BODY='{"requestedAmount":"50.00","requestedCurrency":"USD","reference":"ORDER-1001"}'
TS=$(date +%s); NONCE=$(openssl rand -hex 16)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIG=$(printf '%s\n%s\n%s\n%s\n%s' "$TS" "$NONCE" POST /v1/orders "$BODY_HASH" \
  | openssl dgst -sha256 -hmac "$BESO_API_SECRET" -hex | sed 's/^.* //')

curl -X POST https://api.peer.besofinance.xyz/v1/orders \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $BESO_API_KEY" -H "X-Timestamp: $TS" \
  -H "X-Nonce: $NONCE" -H "X-Signature: $SIG" \
  -d "$BODY"

Python 示例

import hashlib, hmac, json, os, secrets, time, urllib.request

def beso(method, path, body=None):
    raw = json.dumps(body, separators=(",", ":")) if body is not None else ""
    ts, nonce = str(int(time.time())), secrets.token_hex(16)
    msg = "\n".join([ts, nonce, method, path, hashlib.sha256(raw.encode()).hexdigest()])
    sig = hmac.new(os.environ["BESO_API_SECRET"].encode(), msg.encode(), hashlib.sha256).hexdigest()
    req = urllib.request.Request("https://api.peer.besofinance.xyz" + path, data=raw.encode() or None, method=method,
        headers={"X-API-Key": os.environ["BESO_API_KEY"], "X-Timestamp": ts, "X-Nonce": nonce,
                 "X-Signature": sig, "Content-Type": "application/json"})
    return json.load(urllib.request.urlopen(req))

beso("POST", "/v1/orders", {"requestedAmount": "50.00", "requestedCurrency": "USD", "reference": "ORDER-1001"})

密钥轮换与吊销

IP 白名单(可选)

在后台“开发者 → IP 白名单”为每个环境设置允许调用的公网 IPv4 / IPv6 地址或 CIDR 段(IPv4 前缀 /8–/32,IPv6 /32–/128,最多 50 条;单个地址按 /32 或 /128 保存)。空列表表示不限制。白名单只约束 API 密钥调用,不影响商家后台登录。不在名单内的请求返回:

{
  "success": false,
  "message": "IP address 203.0.114.7 is not in this merchant's API key IP allowlist",
  "responseObject": { "ip": "203.0.114.7", "reason": "IP_NOT_ALLOWED" },
  "statusCode": 403,
  "errorCode": "IP_NOT_ALLOWED"
}

无法确定来源 IP 时 reason 为 CLIENT_IP_UNRESOLVED(状态码与 errorCode 相同)。

旧版单一密钥

早期发放的 beso_sk_… 单一密钥(只放 X-API-Key,不签名)在过渡期内继续可用,作用于部署时的默认环境。被平台关闭后返回 401 LEGACY_KEY_DISABLED,请在后台改用公钥 + 私钥。

免密钥接口

GET /v1/orders/{orderId} 无需密钥(订单 ID 本身即凭证),供结账页读取订单;POST /v1/onboarding/applications 用于提交开户申请。

API 参考 · 基础约定

基础地址(沙盒与正式相同,环境由密钥决定):

https://api.peer.besofinance.xyz/v1

统一响应格式

{
  "success": true,
  "message": "Merchant orders retrieved",
  "responseObject": { ... },
  "statusCode": 200
}
字段说明
success2xx 响应为 true
message便于阅读的说明,不要用来做程序判断
responseObject数据主体;失败时为 null 或错误详情
statusCode与 HTTP 状态码一致
errorCode仅失败时出现,机器可读的错误码(见“错误码”)

参数校验失败返回 400、errorCode: "VALIDATION_ERROR",具体字段错误在 responseObject.fieldErrors(对象:字段名 → 错误数组),整体错误在 responseObject.formErrors。

数据格式

API 参考 · 接口列表

路径相对于 https://api.peer.besofinance.xyz。“密钥”= 签名的 API 密钥;“停用时”= 订阅停用(402)期间是否仍可调用。

方法路径鉴权停用时说明
POST/v1/orders密钥402创建结账订单
GET/v1/orders/{orderId}无可用按 ID 读取订单(结账页用)
GET/v1/merchants/me/orders密钥402订单列表(分页;status、displayStatus、chargebackStatus、orderId、reference、search)
GET/v1/merchants/me/orders/{orderId}密钥402订单详情(会先向支付网络刷新)
GET/v1/merchants/me/orders/{orderId}/payments密钥402某订单下的全部支付尝试
GET/v1/merchants/me/payments密钥402支付列表(分页;status、chargebackStatus、orderId)
POST/v1/merchants/me/orders/{orderId}/payments/{paymentId}/recreate正式密钥402补救:重新打开已过期/取消/失败的支付
POST/v1/merchants/me/orders/{orderId}/payments/{paymentId}/extend正式密钥402补救:延长 24 小时
POST/v1/merchants/me/orders/{orderId}/payments/{paymentId}/fulfill-sar正式密钥402补救:凭交易号放行
GET/v1/merchants/me/orders/{orderId}/actions/{actionId}密钥402查询补救动作结果
GET/v1/merchants/me密钥可用商家资料、订阅状态、可用环境
GET/v1/merchants/me/usage密钥402近 30 天用量统计
GET/v1/merchants/me/tier-usage密钥-上游套餐月度额度与已用量(透传 Peer)
POST/v1/merchants/me/quotes/availability密钥402下单前检查某金额是否有流动性
GET/v1/integration/status密钥可用接入自检
POST / GET/v1/webhooks密钥可用注册 / 列出 Webhook
PATCH / DELETE/v1/webhooks/{webhookId}密钥可用更新 / 删除
POST/v1/webhooks/{webhookId}/rotate-secret密钥可用轮换签名密钥
POST/v1/webhooks/{webhookId}/test密钥可用发送测试事件
GET/v1/webhooks/{webhookId}/deliveries密钥可用该端点最近 50 次投递
GET/v1/webhook-deliveries密钥可用全部投递日志(分页)
GET/v1/webhook-deliveries/{deliveryId}密钥可用投递详情(每次尝试 + 原始负载)
POST/v1/webhook-deliveries/{deliveryId}/resend密钥可用手动重发
POST / GET/v1/refund-requests密钥可用提交 / 列出退款申请
GET/v1/refund-requests/{id}密钥可用退款申请详情
POST/v1/refund-requests/{id}/cancel密钥可用撤回尚未审核的申请
GET/v1/merchants/me/subscription密钥可用订阅状态与账单
POST/v1/merchants/me/subscription/invoices密钥可用生成续费账单(返回结账链接)
POST/v1/sandbox/test-order沙盒密钥402测试单:新建并直接结算
POST/v1/onboarding/applications无—提交开户申请

商家后台使用同一套接口(路径前缀为 /portal,登录会话鉴权),另有 API 密钥管理与 IP 白名单设置,只能在后台操作。

API 参考 · 创建订单

POST https://api.peer.besofinance.xyz/v1/orders

请求字段

金额二选一:requestedAmount + requestedCurrency(按法币报价),或 requestedUsdcAmount(按 USDC 报价)。

字段类型说明
requestedAmountstring法币金额,最多 2 位小数,如 "100.00"
requestedCurrencystringISO 4217 代码,默认 USD。建单时按实时汇率换算成 USDC;没有汇率时返回 EXCHANGE_RATE_LOOKUP_FAILED
requestedUsdcAmountstringUSDC 金额,最多 6 位小数。与上两个字段互斥
referencestring你的订单号,1–64 位 [A-Za-z0-9_.:-]。同时是幂等键(同一环境内唯一)。不传时依次取 notes.orderId、Idempotency-Key 请求头,都没有则自动生成
settleAddressstring收款钱包地址;不传用开户时登记的钱包
settleChainnumber | string收款网络的数字链 ID,如 8453(Base)、42161(Arbitrum)、728126428(Tron)。为兼容旧接入也接受链名(base、tron 等)
settleTokenstring收款币种,如 USDC、USDT;默认开户时登记的币种
enabledRailsstring[]本单允许的付款通道,非空数组:法币钱包标识、relay_<chainId>、near_intents_133701、apple_pay(见“支持的钱包 / 数字货币”)。旧写法 crypto_<chainId> 自动换成 relay_<chainId>。不传用账户默认配置
feePayerstring手续费承担方:MERCHANT(商家承担,从到账金额中扣)、PAYEE(买家承担,买家支付总额包含手续费,你收到请求的净额)、SPLIT(按比例分摊)。不传用账户默认
buyerFeeShareBpsinteger买家分摊比例,单位基点,0–10000,必须是 1000 的倍数;feePayer 为 SPLIT 时必填
dynamicOrdersEnabledboolean可选,覆盖账户的动态订单设置(没有报价能落在费用上限内时是否允许调整)
successUrlstring完成页“返回商户”链接的地址(买家点击才会跳转,不会自动跳转;嵌入模式不显示)。链接会附带 order_id、payment_id、tx_hash、status=success 等查询参数,不要据此发货
cancelUrlstring保留字段,目前结账页不会访问
notesobject自定义元数据(客户 ID 等),原样出现在订单与 Webhook 的 order.notes 中

金额限制

幂等与重放

同一环境内用同一个 reference 再次创建,返回 200 和原订单,并带 idempotentReplay: true,不会重复建单;但如果金额、币种或金额模式与原订单不同,返回 409 IDEMPOTENCY_KEY_CONFLICT。首次请求返回 201。如果首次请求在支付网络已建单但链接未能保存(极少见),返回 409 CHECKOUT_URL_UNRECOVERABLE,请换一个 reference 重建。

响应(订单对象)

{
  "id": "ord_2b9c…",
  "environment": "SANDBOX",
  "status": "CREATED",
  "amountMode": "FIXED",
  "requestedAmount": "50.00", "requestedCurrency": "USD",
  "requestedUsdcAmount": "50", "remainingUsdcAmount": "50",
  "netSettledUsdcAmount": null, "fulfillTransaction": null,
  "destinationAddress": "0x…", "destinationChainId": "8453", "destinationToken": "USDC",
  "checkoutUrl": "https://pay.peer.xyz/checkout?…",
  "chargebackStatus": "NONE", "refundStatus": "NONE", "settledPaymentCount": 0,
  "notes": { "customerId": "C-88" },
  "reference": "ORDER-1001",
  "lastEvent": null,
  "createdAt": "2026-10-06T10:15:42.000Z", "updatedAt": "2026-10-06T10:15:42.000Z"
}
字段说明
status见“订单与支付状态”。PENDING_UPSTREAM 表示支付网络尚未确认建单(网络异常时短暂出现)
requestedUsdcAmount / remainingUsdcAmount应收 USDC / 还差多少 USDC
netSettledUsdcAmount扣除手续费后实际到账的 USDC
fulfillTransaction结算交易哈希
chargebackStatusNONE / PARTIALLY_CHARGEBACKED / CHARGEBACKED,不会改变 status
refundStatusNONE / PENDING / COMPLETED,不会改变 status
lastEvent最近一次收到的事件类型
chargebacks拒付明细数组(无则为空数组)
refundAmountUsdc / refundTransactionHash / refundDepositId退款金额 / 退款交易哈希 / 退款存款 ID(无退款时为 null)
feePayer / buyerFeeShareBps / enabledRails本单生效的手续费承担方式、买家分摊比例、允许的通道
successUrl / cancelUrl建单时传入的值(见上文,两者都不会自动跳转)
metadata / requestedFiat上游写入的法币报价快照(requestAmountInputMode、requestedFiatAmount、requestedFiatCurrency、requestedUsdcAmountAtCreation)/ 未被改价的法币单的原始报价。订单被动态改价(ORDER_RESIZED)后请以 requestedUsdcAmount 为准
bridgeInfo跨链结算信息(仅非 Base 结算时)
inPersonCheckout / cancelledAt / completedAt是否线下收款 / 取消时间 / 完成时间

上述上游字段取自最近一次同步的支付网络快照;刚建单、尚未同步时可能为 null。上游的子账户 ID、分成配置与分成明细等内部字段不会返回。

API 参考 · 订单、支付与商家

支付对象

由 /merchants/me/orders/{orderId}/payments、/merchants/me/payments 和 Webhook 的 data.payment 返回:

字段说明
id / orderId支付 ID(支付网络的 ID,补救接口用它)/ 所属订单(我们的 ord_…)
statusCREATED / SETTLED / EXPIRED / FAILED / CANCELLED
rail付款通道,如 venmo、relay_8453
paymentAmount / currency / currencyPerUsdRate买家支付的法币金额、币种、报价汇率
netSettledUsdcAmount / totalUsdcFeeAmount到账 USDC / 手续费 USDC
penalties罚扣明细(如有)
chargebackStatus该笔支付的拒付状态
railIdentifier链上 / 通道侧的参考号;用 id 做外部关联,不要用它
fulfillTransaction结算交易哈希
quoteExpiresAt本次尝试的截止时间
errorMessage / errorCode失败原因(如有)/ 最近一次链上结算失败时为 FULFILL_INTENT_FAILED
chargeback / quote拒付详情 / 报价快照(上游字段,原样透传)
createdAt / completedAt创建 / 完成时间(completedAt 只在结算或买家取消时写入,过期与失败保持 null)

订单列表 GET /v1/merchants/me/orders

按创建时间倒序;page(默认 1)、limit(默认 20,最大 100,超过返回 400 而不是截断)。返回 { orders, total, page, limit }(旧字段 rows 与 orders 相同,保留兼容)。

参数说明
statusCREATED / PARTIALLY_FULFILLED / FULFILLED / CANCELLED;传了 displayStatus 时忽略
displayStatus只看未完成订单的支付活动:ACTIVE(有进行中的付款尝试)、EXPIRED(有过期尝试且无进行中的)、CREATED(两者都没有)
chargebackStatusNONE / PARTIALLY_CHARGEBACKED / CHARGEBACKED
orderId订单号子串匹配,完整的 ord_… 精确命中一单;传了 search 时忽略
reference你的订单号,精确匹配
search至少 3 个字符(更短的会被忽略):在订单号、reference、notes、支付通道和支付 ID 中做不区分大小写的子串匹配;纯数字(可带 $)按金额精确匹配,$20 与 20.00 都命中 20

列表中的每个订单比单笔查询多 5 个由支付记录推导的字段:hasActivePayment、hasExpiredPayment、paymentSelectedRail(进行中尝试的通道,否则最近一次过期尝试的通道)、settledPaymentCount、settledPaymentRails(按支付创建时间去重)。

商家资料 GET /v1/merchants/me

字段说明
id / name / environment商户 ID、名称、当前密钥的环境
evmWalletAddress / destinationChainId / destinationToken默认收款钱包、网络、币种
enabledRails可用的法币通道
subscription订阅对象(见“订阅与账单”)
onboardingStatus / accountStatus开户状态 / ok、suspended_unpaid、suspended_by_admin
liveReviewStatus支付网络对店铺的审核状态:not_submitted、pending_peer_review、approved、rejected。只有 approved 才能在正式环境收款
environments{ sandbox: bool, live: bool },哪些环境已开通
apiKeyIpAllowlist当前环境的 IP 白名单,空数组表示不限制

用量 GET /v1/merchants/me/usage

返回近 30 天(当前环境)的 orders、fulfilledOrders、settledUsdc、successRate(%)和订阅对象。订阅本身不限笔数和金额,cap 恒为 null。

套餐额度 GET /v1/merchants/me/tier-usage

原样透传上游 Peer 的同名接口:{ tier, limits, usage, windowStart, windowEnd }。limits(volumeUsdc、orderCount)为 null 表示没有上限(本平台目前的上游账户与沙盒均为 null);usage 统计本自然月(UTC)创建、且至少有一笔法币支付结算的已完成 / 部分完成订单,按订单的请求 USDC 金额累计。上游不会在接近上限时发通知,建议自行定时查询;达到上限后创建订单会返回 403 MERCHANT_MONTHLY_VOLUME_LIMIT_EXCEEDED 或 MERCHANT_MONTHLY_ORDER_LIMIT_EXCEEDED。

额度检查 POST /v1/merchants/me/quotes/availability

字段说明
amount必填,十进制字符串
quoteModeexact-token(默认,amount 为 USDC)或 exact-fiat(amount 为 fiatCurrency 计价的法币)。请与建单时的金额模式保持一致
fiatCurrency法币代码;省略时使用收款账户的默认币种,再回落到 USD
enabledRails只检查这些通道
nearbyQuotesCount1–10,默认 3;无法成交时每个方向(更低 / 更高)最多返回多少个附近可成交金额
destinationChainId / destinationToken / destinationAddress可选,默认使用开户时登记的收款配置;与建单时的按单覆盖保持一致

收款目的地自动使用你的默认钱包。返回支付网络的可用性结果(是否可成交,以及附近金额建议)。

接入自检 GET /v1/integration/status

返回 environment、authMode(signed / legacy)、subscription(状态)、webhooks(启用的端点数)、lastWebhookDelivery、lastOrderAt、ready(订阅有效且至少有一个 Webhook)。另有与 Peer 同名的接入清单字段:verified(你的接口已成功接收过一次签名的沙盒投递;判断是否接好请看它)、liveWebhookReady、complete(已有正式订单)、steps(每步含 id、actor、done、evidence;actor 为 MERCHANT 的步骤需在商家后台完成,BESOFINANCE 的由我们开通)和 nextAction(下一个未完成步骤)。

API 参考 · 限流与分页

限流

每个响应带 x-rate-limit-remaining。超限返回 429 RATE_LIMITED,响应头含 x-retry-after(多少秒后可重试,同时提供标准 Retry-After)与 x-rate-limiter-resets-at(ISO 时间)。

分页

参数默认上限说明
page1—页码,从 1 开始
limit20100每页条数,超出 1–100 返回 400

列表响应为 { <列表名>, total, page, limit },按创建时间倒序:订单列表是 orders,支付列表是 payments,投递日志是 deliveries,退款申请是 refundRequests。订单列表另带一个与 orders 相同的 rows 字段,仅为兼容旧接入,新代码请读 orders。

API 参考 · 支付补救

买家付款遇到问题时(尝试过期但其实已付款、需要更多时间、已付款但没完成验证),可以用补救接口处理。仅正式环境,沙盒返回 403 SANDBOX_NOT_SUPPORTED;只适用于法币通道。paymentId 来自 GET /v1/merchants/me/orders/{orderId}/payments。

动作路径适用条件结果
重新打开POST …/payments/{paymentId}/recreate支付为 EXPIRED / CANCELLED / FAILED,订单为 CREATED / PARTIALLY_FULFILLED支付回到 CREATED,对同一收款方重新计时;之后结算照常触发 PAYMENT_SETTLED
延长POST …/payments/{paymentId}/extend支付为 CREATED,且距截止不足 6 小时截止时间加 24 小时(自创建起最多 5 天);result.confirmedExpiryTime 为新截止时间
凭交易号放行POST …/payments/{paymentId}/fulfill-sar支付为 CREATED;Venmo / Cash App / Wise 且收款方支持自动放行;请求体需 txId(1–256 位,买家收据上的交易号)支付直接结算,触发 PAYMENT_SETTLED / ORDER_FULFILLED

请求体都可带 idempotencyKey(8–200 位),同一个键重试返回同一个动作。成功返回 202 与动作对象:

{
  "actionId": "…", "actionName": "RECREATE", "paymentId": "…", "orderId": "ord_…",
  "status": "QUEUED",      // PENDING_APPROVAL / QUEUED / RUNNING / SUCCEEDED / BLOCKED / FAILED
  "error": null, "result": null, "payment": { … }, "createdAt": "…", "startedAt": null, "finishedAt": null
}

用 GET /v1/merchants/me/orders/{orderId}/actions/{actionId} 轮询结果。不满足条件返回 422,responseObject.reason 给出原因(如 PAYMENT_STATUS_NOT_ELIGIBLE、RAIL_NOT_SUPPORTED、EXTEND_TOO_EARLY、SAR_NOT_AVAILABLE);同类动作进行中返回 409 ACTION_IN_PROGRESS(带 actionId);功能关闭时 503 FEATURE_DISABLED。错误码同时出现在 errorCode 与 responseObject.code。

API 参考 · 退款申请

退款由 Besofinance 审核后代为执行,全额退回买家原付款账户。你通过 API 或后台“退款”页提交申请,状态变化会以 REFUND_REQUEST_UPDATED 事件通知你;支付网络完成退款时另有 REFUND_PENDING / REFUND_COMPLETED 事件。

可退款条件

提交申请

POST /v1/refund-requests
{ "orderId": "ord_…", "refundTo": "@buyer-venmo", "reason": "客户取消" }
字段说明
orderId必填,我们的订单 ID
refundTo必填,买家在原付款平台上的账户(如 Venmo 用户名、Revolut 标签),2–120 字
reason可选,最多 500 字

返回 201 与退款申请对象:id、orderId、environment、status、type(恒为 FULL)、reason、refundTo、rail、amount、currency、adminNote、peerRefundStatus、refundTransactionHash、createdAt、updatedAt。

错误:传 amount 返回 400 PARTIAL_REFUND_NOT_SUPPORTED;已有进行中的申请返回 409 REFUND_ALREADY_REQUESTED;不满足条件返回 422 REFUND_NOT_ELIGIBLE,responseObject.reason 为 SANDBOX_NOT_SUPPORTED、ORDER_NOT_FULFILLED、ALREADY_REFUNDED、CHARGEBACK_RECORDED、MULTIPLE_OR_NO_SETTLED_PAYMENTS、RAIL_NOT_REFUNDABLE 或 PAYOUT_NOT_BASE_USDC。

状态

状态含义
requested已提交,等待审核;此时可调用 POST /v1/refund-requests/{id}/cancel 撤回
approved审核通过,等待执行
processing已发起退款(peerRefundStatus: PENDING)
completed退款完成(peerRefundStatus: COMPLETED,带交易哈希)
rejected / cancelled被拒绝(见 adminNote)/ 已撤回

API 参考 · 订阅与账单

GET /v1/merchants/me/subscription

{
  "subscription": {
    "plan": "merchant", "monthlyFee": "1000", "currency": "USD",
    "activatedAt": "2026-10-01", "paidThrough": "2026-10-31", "currentPeriodEnd": "2026-10-31",
    "nextChargeDate": "2026-11-01", "cutoffDate": "2026-11-03", "graceDays": 3,
    "status": "active"     // active / past_due / suspended / cancelled / not_started
  },
  "invoices": [ { "id": "inv_…", "periodStart": …, "periodEnd": …, "amount": "1000.00", "currency": "USD",
                   "months": 1, "method": "peer", "status": "open", "checkoutUrl": "https://…", "paidAt": null } ]
}

POST /v1/merchants/me/subscription/invoices

请求体 { "months": 1 }(1–12)。已有未付账单时直接返回它;否则生成新账单并返回带 checkoutUrl 的账单对象。账单在结账页付款结算后自动入账,订阅立即顺延,并发送 SUBSCRIPTION_PAID / SUBSCRIPTION_ACTIVE 事件。

API 参考 · 沙盒测试单

POST /v1/sandbox/test-order
{ "amount": "10.00", "currency": "USD", "rail": "venmo" }
字段说明
orderId可选,结算一张你已经建好的沙盒订单;不传则新建一张
amount / currency新建时的金额,默认 10.00 USD
rail可选,模拟的付款通道
settle默认 true:模拟买家付款并结算,触发真实签名的 PAYMENT_SETTLED / ORDER_FULFILLED,转发到你的沙盒 Webhook;传 false 只建单

只能用沙盒密钥(正式密钥返回 403 SANDBOX_ONLY),每分钟最多 5 次。不动真钱。

Webhooks · 创建与管理

Webhook 以带签名的 POST 请求,把订单、支付、退款和订阅的状态变化推送到你的服务器,每次都附带订单与支付的完整快照。Webhook 按环境注册:用沙盒密钥注册的端点只收沙盒事件,用正式密钥注册的只收正式事件,各有自己的签名密钥。

基本流程

  1. 创建 Webhook 并妥善保存签名密钥(只显示一次)。
  2. 订阅需要的事件。
  3. 每次收到请求都验证签名与时间戳。
  4. 用 X-Webhook-Id 去重,避免重复处理。
  5. 用 data.order.id / data.order.reference 和 data.payment.id 与你自己的订单对账。

创建

POST https://api.peer.besofinance.xyz/v1/webhooks
{
  "url": "https://yoursite.com/webhooks/beso",
  "events": ["ORDER_FULFILLED", "PAYMENT_SETTLED", "REFUND_REQUEST_UPDATED"],
  "customHeaders": [{ "key": "X-Auth", "value": "Bearer 共享口令" }]
}
字段必填说明
url是公网 HTTPS 地址。不接受 HTTP、内网 / 回环 / 保留地址、本机域名(localhost、*.localhost、*.local、*.internal)和带账号密码的地址。每次投递前都会重新解析域名,解析到非公网地址时该次投递直接失败、不再重试
events是一个或多个事件名(见“事件与负载”)
customHeaders否最多 10 组 { key, value },每次投递都会带上(例如共享的 Bearer 口令)。规则见下

返回 201:{ id, url, events, environment, active, customHeaders, secret },其中 secret(whsec_…)仅显示一次。

自定义请求头规则:名称去掉首尾空格并转小写,必须是合法的 HTTP 头名;值不能包含回车、换行或空字符;名称不能重复;保留名不可用:x-webhook-id、x-webhook-timestamp、x-webhook-signature、host、content-type、content-length、connection、expect、forwarded、proxy-authenticate、proxy-authorization、te、trailer、transfer-encoding、upgrade,以及所有 proxy-、x-forwarded- 开头的名称。头的值加密保存,列表接口只回显打码后的值。

你的接口需要做到

重试策略

首次投递失败后按下表重试,最多共 7 次尝试(约 35 小时):

失败次数下次重试间隔
11 分钟
25 分钟
330 分钟
42 小时
58 小时
624 小时

7 次都失败后标记为 FAILED,不再自动重试,但你可以随时手动重发。投递状态:PENDING(待投递或待重试)、DELIVERED(收到 2xx)、FAILED(次数用尽或端点已删除 / 停用)。重试队列持久化保存,服务重启不会丢失。

管理接口

# 列表(当前环境)
GET    /v1/webhooks

# 更新:url / events / active / customHeaders(customHeaders 整体替换,传 [] 清空)
PATCH  /v1/webhooks/{webhookId}      { "active": false }

# 轮换签名密钥:返回新 secret(只显示一次),旧 secret 立即失效
POST   /v1/webhooks/{webhookId}/rotate-secret

# 删除
DELETE /v1/webhooks/{webhookId}

# 发送测试事件:只发给这个端点,类型为 ORDER_CREATED,data.test = true,快照均为 null;只尝试一次、不重试(每 10 分钟最多 5 次)
# 返回 { success, deliveryId, responseCode, error }
POST   /v1/webhooks/{webhookId}/test

# 该端点最近 50 次投递
GET    /v1/webhooks/{webhookId}/deliveries

Webhooks · 事件与负载

负载结构

{
  "id": "evt_…",                 // 同 X-Webhook-Id,用来去重
  "type": "ORDER_FULFILLED",
  "environment": "LIVE",          // SANDBOX / LIVE;订阅类事件为 null
  "timestamp": "2026-10-06T10:20:03.112Z",
  "data": {
    "order": { … },               // 订单对象(同“创建订单”的响应)
    "payment": { … } | null,       // 支付对象
    "refund": { … } | null,        // 退款事件
    "paymentBridge": { … } | null  // 桥接事件
    // 仅特定事件:trigger(拒付)、resize(ORDER_RESIZED)、amountChange(ORDER_AMOUNT_SET)
  }
}

订单与支付事件

事件触发时机data.payment
ORDER_CREATED订单创建成功(测试投递也用这个类型,带 data.test: true)null
ORDER_FULFILLED订单已全额结算,可以发货完成订单的那笔支付
ORDER_CANCELLED没有任何支付尝试的 CREATED 订单被取消null
ORDER_RESIZED动态订单被买家调整为附近可成交的金额(data.resize 含新旧金额)null
ORDER_AMOUNT_SET开放金额订单设定了金额(本 API 暂不创建此类订单)null
PAYMENT_CREATED买家发起一次支付尝试有
PAYMENT_SETTLED一笔支付已结算(部分付款也会触发;与 ORDER_FULFILLED 到达顺序不固定)有
PAYMENT_FAILED尝试无法发起,或数字货币转账失败 / 停滞(停滞的尝试若迟到到账仍可能结算)有
PAYMENT_EXPIRED尝试超过付款时限(非终态,迟到的付款仍可能结算)有
PAYMENT_CANCELLED尝试被取消(例如买家换了钱包)有
PAYMENT_BRIDGE_PENDING / _SUBMITTED / _COMPLETED / _FAILED结算后跨链转到你的收款网络:排队 / 已提交 / 已确认 / 失败(data.paymentBridge)有
REFUND_PENDING / REFUND_COMPLETED退款已发起 / 已完成(data.refund)null
PAYMENT_CHARGEBACKED一笔已结算支付被拒付(data.trigger)有
ORDER_PARTIALLY_CHARGEBACKED / ORDER_CHARGEBACKED订单部分 / 全部已结算支付被拒付有

Besofinance 平台事件

事件触发时机data
REFUND_REQUEST_UPDATED退款申请状态变化(审核、执行、完成、拒绝){ refundRequest }
SUBSCRIPTION_INVOICE生成了新的订阅账单{ invoiceId, amount, currency, period, dueBy, cutoff, checkoutUrl, subscription }
SUBSCRIPTION_PAID订阅账单已付款{ invoiceId, periodEnd, subscription }
SUBSCRIPTION_PAST_DUE / _SUSPENDED / _ACTIVE / _CANCELLED订阅状态变化{ subscription }

订阅类事件与环境无关(environment 为 null),会投递到两个环境中订阅了该事件的端点。

Webhooks · 验签

请求头

请求头说明
X-Webhook-Id事件唯一 ID(重试和手动重发时不变)
X-Webhook-Timestamp本次发送的 Unix 时间戳(秒),每次尝试都会更新
X-Webhook-SignatureHMAC-SHA256 签名,小写十六进制,无前缀

算法

signature = hex(HMAC-SHA256(secret, timestamp + "." + rawBody))

时间戳与当前时间相差超过 5 分钟(300 秒)的请求应当拒绝。

Node.js 示例

const crypto = require('crypto');

function verify(rawBody, headers, secret) {
  const ts  = headers['x-webhook-timestamp'];
  const sig = headers['x-webhook-signature'] || '';
  if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(ts + '.' + rawBody)
    .digest('hex');

  const a = Buffer.from(sig, 'hex'), b = Buffer.from(expected, 'hex');
  return a.length === b.length && crypto.timingSafeEqual(a, b);   // 长度不同时 timingSafeEqual 会抛异常,先比长度
}

使用 SDK 时直接调用 verifyWebhook(rawBody, req.headers, process.env.BESO_WEBHOOK_SECRET)。Express 中请用 express.raw({ type: 'application/json' }) 拿到原始请求体。

Python 示例

import hmac, hashlib, time

def verify(raw_body: bytes, headers, secret: str) -> bool:
    ts, sig = headers.get("X-Webhook-Timestamp", ""), headers.get("X-Webhook-Signature", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        return False
    expected = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(sig, expected)

处理清单

Webhooks · 投递日志与重发

每次投递都会记录:状态、尝试次数、每次尝试的 HTTP 状态码 / 错误 / 耗时、下次重试时间和原始负载。商家后台“开发者 → 投递日志”可查看并一键重发。

# 列表(分页;可按 status=PENDING|DELIVERED|FAILED、type=事件名 过滤)
GET  /v1/webhook-deliveries?status=FAILED&limit=50

# 详情:含 attemptLog(每次尝试)与 payload(原始负载)
GET  /v1/webhook-deliveries/{deliveryId}

# 手动重发:任何状态都可以(包括 DELIVERED),事件 ID 不变,签名用新的时间戳
POST /v1/webhook-deliveries/{deliveryId}/resend

投递对象字段:id、webhookId、eventId、type、environment、status、attempts、lastStatusCode、lastError、nextAttemptAt、deliveredAt、createdAt、updatedAt。手动重发不占用自动重试次数。

服务中断后,也可以用 GET /v1/merchants/me/orders?status=FULFILLED 回读订单状态补齐数据。

参考 · 订单与支付状态

订单代表需要收取的总金额;支付代表买家在某个钱包上的一次付款尝试。一张订单可以有多次支付尝试。

订单状态

状态含义
PENDING_UPSTREAMBesofinance 已受理,支付网络尚未确认建单(网络异常时短暂出现;用同一 reference 重试即可)
CREATED尚无已结算的支付,底下的支付可能进行中、过期、失败或取消。订单不会过期
PARTIALLY_FULFILLED已有支付结算,但剩余金额仍高于完成阈值
FULFILLED剩余金额归零或进入阈值内,订单完成(终态)
CANCELLED没有任何支付尝试的订单被取消(终态)

拒付(chargebackStatus)和退款(refundStatus)单独记录,不会改变订单的 status:被拒付或已退款的订单仍是 FULFILLED。

支付状态

状态含义
CREATED尝试进行中,买家在时限内付款或等待验证。验证被拒也会停留在 CREATED
SETTLED已结算到收款钱包(终态)
EXPIRED付款时限已过(非终态:迟到的付款仍可能结算,也可用补救接口重新打开)
FAILED结算未能发起,或数字货币转账失败 / 停滞
CANCELLED买家放弃了这次尝试,例如更换了钱包

付款时限

通道时限超时后
法币钱包约 1 小时EXPIRED;迟到的付款仍会结算并触发 PAYMENT_SETTLED;可 extend 延长或 recreate 重新打开
Relay 数字货币约 6 小时 10 分钟不会进入 EXPIRED;约 6 小时 20 分钟后仍未完成则 FAILED,迟到的充值仍可能结算
Zcash(NEAR Intents)20 分钟内完成充值过期

状态流转

参考 · 支持的法币钱包

钱包通道标识市场 / 币种可退款可能被拒付
Venmovenmo美国 · USD是是
Cash Appcashapp美国 · USD是否
Zellezelle美国 · USD(Bank of America、Chase、Citi)是否
Chimechime美国 · USD否否
PayPalpaypal多币种是是
Revolutrevolut多币种是否
Wisewise多币种(覆盖最广)是否
Monzomonzo英国 · GBP是否
N26n26欧元区 · EUR否否

报价支持以下 33 种法币(某张订单实际能用哪些,取决于启用的钱包和当时的流动性;没有流动性的钱包在结账页显示为不可用):

AED  ARS  AUD  CAD  CHF  CNY  CZK  DKK  EUR  GBP  HKD
HUF  IDR  ILS  INR  JPY  KES  MXN  MYR  NOK  NZD  PHP
PLN  RON  SAR  SEK  SGD  THB  TRY  UGX  USD  VND  ZAR

每笔支付记录报价所用的汇率 currencyPerUsdRate。

争议风险:Venmo 与 PayPal 的付款在结算后可能被买家在平台上发起争议,成功的争议会以拒付事件(PAYMENT_CHARGEBACKED 等)通知你,订单的 chargebackStatus 随之变化。请为每张订单保留订单详情、沟通记录与履约证明至少 24 个月。其他钱包不会被拒付,买家要退款只能走退款流程。

使用 Zelle 时,买家会选择自己的银行(例如 zelle-chase、zelle-bofa、zelle-citi),对应支付对象里的 paymentMethodId 字段;其他钱包该字段为 null。Zelle 与 N26 的买家验证需要桌面浏览器扩展。

参考 · 支持的数字货币

结账页除法币钱包外,还提供数字货币付款通道。默认收款目的地为 Base 网络上的 USDC;收款目的地由订单的 destinationChainId 与 destinationToken(即请求里的 settleChain / settleToken)单独设置。

买家如何用数字货币付款

  1. 买家选择一条链。
  2. 买家从该网络上的钱包完成转账。
  3. 转账被桥接到你的收款目的地,过程中会收到 PAYMENT_BRIDGE_* 事件。

数字货币通道标识

在建单请求的 enabledRails 中使用,格式为 relay_<chainId>,每条受支持的买家付款链对应一个;另有 Zcash 与 Apple Pay 两个标识。旧写法 crypto_<chainId> 仍被接受并自动转换。

网络通道标识
Ethereumrelay_1
Optimismrelay_10
BNB Smart Chainrelay_56
Polygonrelay_137
World Chainrelay_480
Hyperliquidrelay_999
Arcrelay_5042
Baserelay_8453
Arbitrumrelay_42161
Bitcoinrelay_8253038
Tronrelay_728126428
Solanarelay_792703809
Zcash(经 NEAR Intents)near_intents_133701
Apple Pay(Coinbase)apple_pay(结算后按所走的 Relay 通道记账)

买家可用的数字货币(以结账页实际显示为准)

资产说明
BTCBitcoin
ETH5 条链
USDT8 条链
USDC9 条链
PYUSD2 条链
SOL2 条链
BNBBNB Smart Chain
HYPEHyperliquid
WBTC5 条链
USDHHyperliquid
ZECZcash

网络:Bitcoin、Ethereum、Solana、Base、Optimism、BNB Smart Chain、Polygon、World Chain、Hyperliquid、Arc、Arbitrum、Zcash、Tron。

订单里与数字货币有关的字段

字段说明
destinationAddress收款钱包地址
destinationToken收款币种
destinationChainId收款网络(链 ID,字符串)
netSettledUsdcAmount最终结算到账的 USDC 数量(Relay 支付在 PAYMENT_SETTLED 中可能是估算值,几分钟后回读为准)
currencyPerUsdRate支付对象上的汇率
fulfillTransaction结算交易哈希:钱包付款为 Base 上的哈希,Relay 付款为收款网络上的哈希

默认收款钱包是开户时登记的 EVM 地址(evmWalletAddress);结算到 Solana、Tron 等非 EVM 网络时,请在建单时传入对应网络的 settleAddress。

手续费承担方

通过 feePayer 设置:MERCHANT(商家承担)、PAYEE(买家承担)、SPLIT(分摊);SPLIT 时用 buyerFeeShareBps(0–10000,1000 的倍数)设置买家分摊的比例。

参考 · 错误码

请按 errorCode 做程序判断,message 只用于日志。支付网络返回的业务错误码会原样透传。

状态码errorCode场景
400VALIDATION_ERROR路径、查询或请求体校验失败,详见 fieldErrors / formErrors
400AMOUNT_BELOW_MIN / AMOUNT_ABOVE_MAX / AMOUNT_NON_POSITIVE订单金额低于最小额(默认 10 USDC)/ 高于 10,000 USDC / 换算后不大于 0
400INTENT_ABOVE_MAX买家发起付款时,含手续费超过每笔 10,000 USDC
400DESTINATION_INVALID / PAYOUT_CONFIG_INVALID收款地址与网络不匹配 / 不支持的收款网络或币种
400NO_ELIGIBLE_PAYMENT_RAILS本单允许的通道都不可用
400AMOUNT_BELOW_MERCHANT_MIN / AMOUNT_ABOVE_MERCHANT_MAX超出为你的收款账户单独设置的单笔金额范围(沙盒不检查),需调整请联系 Besofinance
400MERCHANT_CONFIG_MISSING收款账户尚未完成结账配置(由 Besofinance 处理)
400DYNAMIC_ORDERS_DISABLED账户未开启动态订单却传了 dynamicOrdersEnabled: true
400PARTIAL_REFUND_NOT_SUPPORTED退款申请里传了金额(只支持全额)
401UNAUTHORIZED缺少或无效的 API 密钥 / 后台会话
401SIGNATURE_REQUIRED / INVALID_SIGNATURE缺少签名请求头 / 签名不匹配
401TIMESTAMP_OUT_OF_RANGE / INVALID_NONCE / NONCE_REUSED时间戳偏差超过 300 秒 / nonce 格式不对 / nonce 重复(疑似重放)
401API_KEY_REVOKED / API_KEY_EXPIRED密钥已吊销 / 轮换宽限期已过
401LEGACY_KEY_DISABLED旧版单一密钥已停用
402SUBSCRIPTION_EXPIRED订阅到期且宽限期已过,付清账单后自动恢复
403MERCHANT_NOT_ACTIVE账户还在审核中或未开通
403MERCHANT_SUSPENDED账户被 Besofinance 冻结
403PEER_REVIEW_PENDING支付网络尚未通过店铺审核:不能生成正式密钥,正式环境建单(POST /v1/orders)与额度检查被拒绝;读接口、Webhook 配置与沙盒不受影响
403MERCHANT_TIER_FORBIDDEN上游正式收款账户尚未选定套餐(由 Besofinance 处理,沙盒不会出现)
403MERCHANT_MONTHLY_ORDER_LIMIT_EXCEEDED / MERCHANT_MONTHLY_VOLUME_LIMIT_EXCEEDED上游账户的本月法币订单笔数 / 金额已达上限(见“套餐额度”)。目前本平台的上游账户与沙盒均不设上限
403IP_NOT_ALLOWED来源 IP 不在白名单(reason:IP_NOT_ALLOWED / CLIENT_IP_UNRESOLVED)
403SANDBOX_ONLY / SANDBOX_NOT_SUPPORTED该接口只能用沙盒密钥 / 只能在正式环境使用
404NOT_FOUND资源不存在,或不属于当前商户 / 环境
409IDEMPOTENCY_KEY_CONFLICT同一 reference 已用于金额或币种不同的订单
409MERCHANT_OWNERSHIP_CHANGED上游账户在建单过程中变更了所有者,未保存任何数据;用同一 reference 重试即可
409CHECKOUT_URL_UNRECOVERABLE订单已在支付网络创建但链接丢失,请换 reference 重建
409ENVIRONMENT_NOT_AVAILABLE该环境的收款账户尚未开通
409REFUND_ALREADY_REQUESTED / INVALID_TRANSITION已有进行中的退款申请 / 当前状态不能执行该操作
409APPLICATION_EXISTS / KEY_LIMIT_REACHED该邮箱已有申请或账户 / 该环境有效密钥已达 5 把
409ACTION_IN_PROGRESS同类补救动作进行中(responseObject.actionId)
422REFUND_NOT_ELIGIBLE订单不满足退款条件(responseObject.reason)
422PAYMENT_NOT_ELIGIBLE / IDEMPOTENCY_KEY_MISMATCH支付不满足补救条件(reason)/ 补救的幂等键已用于别的请求
429RATE_LIMITED请求过于频繁,按 x-retry-after 等待
500INTERNAL_ERROR服务内部错误,可重试
502UPSTREAM_ERROR / EXCHANGE_RATE_LOOKUP_FAILED支付网络暂时不可用 / 汇率查询失败,可用同一 reference 重试
502UPSTREAM_AUTH_FAILED你的收款账户凭证异常,Besofinance 已收到告警
503FEATURE_DISABLED / TREASURY_NOT_CONFIGURED补救功能暂时关闭 / 平台暂不能生成在线账单

参考 · 与 Peer 原生 API 的差异

如果你之前直接对接过 Peer Pay(api.pay.peer.xyz),迁移到本 API 时注意以下差异:

项目Peer 原生besofinance.peer
基础地址https://api.pay.peer.xyz/api/v1https://api.peer.besofinance.xyz/v1,路径去掉 /api 前缀,其余一致
鉴权单一 X-API-Key,无前缀公钥 + 私钥 HMAC 签名,前缀区分环境;支持轮换宽限期
金额字段requestedFiatAmount / requestedFiatCurrencyrequestedAmount / requestedCurrency(原名也接受;requestedUsdcAmount 相同)
收款字段destinationAddress / destinationChainId / destinationTokensettleAddress / settleChain / settleToken(原名也接受);不传用开户时登记的钱包
幂等请求体 idempotencyKey,重放返回 checkoutUrl: nullreference(或 Idempotency-Key 头 / idempotencyKey),重放返回原订单和原链接
订单 IDPeer 订单 IDord_…;单张订单可用 API 密钥读取(/merchants/me/orders/{id})
开放金额订单支持 openAmount暂不支持
Webhook 负载{ id, type, timestamp, data },订单为 CheckoutOrder同结构,另加 environment;data.order 为本 API 的订单对象(含 reference)
Webhook 创建响应{ webhook, secret }{ id, url, events, environment, active, customHeaders, secret }
投递记录每端点最近 50 条,{ deliveries, hasMore };无手动重发每端点最近 50 条(数组)+ 全量分页日志、每次尝试详情、手动重发;另可轮换签名密钥
退款仅 Owner 在后台发起,无 API退款申请 API + 审核流程,状态通过事件通知
限流每 IP 每分钟 1200 次每把密钥每分钟 600 次
用量/merchants/me/tier-usage(套餐笔数 / 金额上限)/merchants/me/tier-usage 原样透传;另有 /merchants/me/usage(近 30 天统计)
计费上游套餐 + 交易费1,000 美元 / 月订阅 + 交易费;欠费返回 402
订单取消仅后台暂不提供(请联系 Besofinance)

联系我们

接入、费率、退款或合作问题,请发邮件至 xiongfei@besofinance.xyz。