Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
集成与开发阅读约 11 分钟

Crypto Pay API 集成:构建可靠的支付流程

一份实用的工程指南,介绍如何集成加密支付 API,涵盖从创建发票、验证 webhook 到链上确认和对账的全过程。

TL;DR

加密支付 API 是将区块链转账转化为可追踪订单的编程层:它创建支付请求,监控链上匹配的转账,并在资金到达时通知你的后端。难点不在于最初的 API 调用,而在于围绕它的生命周期:幂等性、webhook 真实性、确认深度、少付和多付,以及与你自有账本的对账。本指南介绍一个与提供商无关的集成模型,包含可运行的请求和 webhook 示例、可在沙盒中复现的验证流程,以及托管网关与自管理节点基础设施之间的权衡。具体端点和提供商选择有意留给 RPC Assistant;本页重点介绍如何构建和测试该流程。

范围与加密支付 API 背后的思维模型

加密支付 API 位于你的订单系统和区块链之间。你的后端请求它为已知金额和币种创建支付请求,客户从自己的钱包发送资金,当观察到匹配的转账时,API 会回报。与卡组织通道不同,加密转账是推送支付:客户发起资金移动,没有发行方可以撤销它。这一单一特性解释了你会遇到的大部分设计差异。

有用的思维模型是一个四阶段生命周期。创建:你的系统注册一张发票,包含金额、参考号和有效期。观察:提供商监控地址或内存池,并将传入转账匹配到该发票。确认:只有在策略定义的区块数或最终性信号之后,转账才被计入。结算:你的账本将订单标记为已支付,随后可选进行出款或兑换步骤。你调试的每个集成 bug 都存在于这四个阶段之一。

本文与提供商无关。字段名因网关而异,但生命周期、安全要求和验证流程是稳定的。当你需要选择特定端点或网络时,请使用 RPC Assistant,而不是硬编码教程中的假设。

  • 推送支付:客户发送资金;你的系统从不从他们的钱包拉取。
  • 不可逆性:退款是新的出站交易,而不是撤销。
  • 异步性:支付确认通过 webhook 或轮询到达,而不是在创建响应中。
  • 最终性差异:比特币、以太坊和快速最终性链需要不同的确认策略。

编写代码前的先决条件和假设

你需要一个支付提供商的沙盒或测试网账户、一对 API 密钥(通常是公钥加上用于签名的密钥)、一个可公开访问的 HTTPS webhook 端点,以及一个用于发票状态的持久化存储。在集成之前,你还需要对两个策略问题有明确答案:你将接受哪些资产和网络,以及每个资产和网络需要多少次确认才算结算。

假设你的后端是唯一受信任的组件。浏览器客户端绝不应持有密钥,webhook 处理器必须将每个传入请求视为不可信,直到签名被验证。如果你运营自己的链基础设施进行验证,你还需要一个可靠的 RPC 端点;多链 RPC 端点指南介绍了如何组织这种访问,OnFinality 的 API 服务描述了托管访问方式,如果你不想运行节点。

使用幂等请求创建发票

创建调用是流程中唯一的同步步骤,也是最可能被你自己的基础设施重试的步骤。始终发送从内部订单 ID 派生的幂等键,这样网络超时不会为一个订单产生两张发票。下面的示例使用占位凭据和通用 REST 结构;请根据提供商的参考调整字段名。

curl -X POST "$PAY_API_BASE/v1/invoices" \
  -H "Authorization: Bearer $PAY_API_KEY" \
  -H "Idempotency-Key: order-10482" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "49.00",
    "currency": "USD",
    "asset": "USDT",
    "network": "ethereum",
    "reference": "order-10482",
    "expires_in": 900,
    "callback_url": "https://api.example.com/webhooks/pay"
  }'

处理 webhook:先验证再信任

Webhook 负载是资金到达的主要信号,但也是欺诈和重复入账 bug 的最常见来源。针对原始请求体验证签名,而不是解析并重新序列化的对象,因为重新序列化会改变字节顺序并破坏 HMAC 校验。然后,在应用任何状态变更之前,检查事件 ID 是否在去重存储中。

下面的处理器展示了 Node.js 中的模式:读取原始请求体,使用共享密钥计算 HMAC,以恒定时间比较,去重,然后转换订单状态。确切的头名称和签名方案因提供商而异;请替换为你自己的。

import crypto from 'node:crypto';
import express from 'express';

const app = express();
const seen = new Set(); // replace with a durable store

app.post('/webhooks/pay', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.get('X-Pay-Signature') || '';
  const expected = crypto
    .createHmac('sha256', process.env.PAY_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  const ok = signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  if (!ok) return res.status(401).send('bad signature');

  const event = JSON.parse(req.body.toString('utf8'));
  if (seen.has(event.id)) return res.status(200).send('duplicate');
  seen.add(event.id);

  // Only act on the terminal state, not intermediate ones.
  if (event.type === 'payment.confirmed') {
    // enqueue order transition; do not do heavy work inline
  }
  res.status(200).send('ok');
});

确认策略与验证流程

当转账出现在内存池中时,支付并未结算;只有当你的策略说它已结算时,它才结算。为每个网络定义确认阈值,并将其与每张发票一起记录,以便日后审计为什么订单被标记为已支付。对于快速最终性链,阈值可能是最终性小工具信号而不是区块数;对于工作量证明链,则是深度。

要端到端验证你的集成,请在沙盒中运行此流程,并将观察到的值记录在表格中。不要从任何指南(包括本指南)复制数字;测量你自己的。

  • 创建一张发票,并确认响应包含唯一 ID 和支付地址或 URI。
  • 发送低于发票金额的测试转账,并确认 API 报告少付状态,而不是已支付。
  • 发送剩余金额,并确认状态变为待处理,然后在达到阈值后变为已确认。
  • 将同一 webhook 重放两次,并确认你的处理器恰好为订单入账一次。
  • 让未支付的发票过期,并确认迟到的转账被标记为人工审核,而不是自动入账。

将提供商账本与你自己的账本对账

Webhook 可能被遗漏、延迟或乱序送达。一个按时间窗口列出发票并与你的订单表比较的定期对账任务是安全网。将提供商视为链上观察的真相来源,将你的数据库视为履约的真相来源;该任务的目的是发现并修复分歧。

下面的 Python 草图拉取最近的发票,并标记在提供商处已确认但本地未履约的发票。按计划运行它,并在输出非空时发出警报,而不是静默自动履约。

import os, requests

base = os.environ["PAY_API_BASE"]
headers = {"Authorization": f"Bearer {os.environ['PAY_API_KEY']}"}

resp = requests.get(f"{base}/v1/invoices", headers=headers,
                    params={"status": "confirmed", "limit": 100}, timeout=15)
resp.raise_for_status()

for inv in resp.json().get("data", []):
    if not local_order_is_fulfilled(inv["reference"]):
        print("DIVERGENCE", inv["id"], inv["reference"], inv["amount"])

排查实际发生的故障

大多数生产事故都属于少数几种模式。签名失败通常意味着请求体在验证之前被解析,或者密钥属于不同的环境。重复入账通常意味着去重存储是内存中的,并在部署时重置。卡在待处理的发票通常意味着客户在发票指定的不同网络上发送,或者金额低于提供商的粉尘阈值。

少付和多付需要明确处理。提前决定是否接受容差范围内的少付,以及多付是退款还是入账。过期后的迟到转账应路由到人工审核,绝不自动履约。如果你自己的链上读取是流程的一部分,间歇性 RPC 错误可能看起来像支付缺失;RPC 端点指南解释了重试和故障转移模式,可防止这类假阴性。

  • Webhook 返回 401:原始请求体未保留,或环境密钥错误。
  • 订单被重复入账:去重基于负载哈希而不是事件 ID,或存储非持久化。
  • 发票从未确认:客户选择了错误的网络,或金额低于阈值。
  • 对账噪音:提供商时间戳与你的窗口查询之间存在时区不匹配。

托管网关与自管理节点基础设施

托管的加密支付 API 在一个合同中提供发票管理、地址派生、确认跟踪,通常还有兑换或出款。你以控制权和每笔交易成本换取集成速度和转移的运营风险。自管理方法,即你自己运行节点并监控地址,让你完全掌控观察层,确认逻辑不依赖第三方,但你需要承担正常运行时间、重组处理和链特定怪癖。

决策通常取决于你支持多少网络以及结算逻辑在多大程度上是你产品的核心。如果支付是一个功能,网关通常是正确的。如果支付观察是产品本身,那么具有可靠 RPC 层的自管理基础设施值得付出成本。对于走自管理路线的团队,专用节点容量消除了导致错过区块的嘈杂邻居效应,RPC 定价有助于在承诺之前对每个网络的成本建模。

需要预先接受的限制和权衡

没有任何集成能消除区块链最终性风险;它只是转移了你衡量风险的位置。确认阈值以延迟换取安全性,正确的值取决于交易大小,而不仅仅是链。在大多数系统中,webhook 投递是至少一次,因此幂等性是强制性的,而不是可选的。如果提供货币兑换,会引入一个时间点的汇率,必须与发票一起存储以解决争议。

还有一个工程无法设计掉的合规表面:了解你的客户义务、制裁筛查和记录保留因司法管辖区以及你是否托管资金而异。将这些视为限制你可以接受哪些提供商和网络的产品需求,而不是事后考虑。

生产就绪流程的后续步骤

分阶段从沙盒迁移到生产:在一个网络上启用一种资产,针对真实的低价值交易运行验证流程,并观察对账输出一周后再扩展。为 webhook 签名失败、对账分歧以及发票在预期确认时间后仍卡在待处理状态添加警报。记录每个网络的确认策略,以便支持人员可以解释为什么订单尚未履约。

当你需要选择具体端点或确认提供商支持哪些网络时,向 RPC Assistant 询问直接建议,而不是从营销页面推断。有关底层基础设施的背景,区块链节点托管指南和网络目录是有用的起点。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始