StableOps

快速开始

十分钟跑通第一笔稳定币支付并验证 Webhook。

1. 安装 SDK

pnpm add @stableops/api-sdk @stableops/wallet-sdk

API SDK 纯 TypeScript 实现,运行时只依赖 Node crypto。 钱包 SDK 用于浏览器端调用用户的钱包发起链上转账。

2. 配置服务端客户端

import { StableOps } from '@stableops/api-sdk'

const client = new StableOps({
  apiKey: process.env.STABLEOPS_API_KEY!,
  environment: 'sandbox', // 默认 sandbox。测试后再切到 "live"。
})

不要把 STABLEOPS_API_KEY 暴露给浏览器。创建支付单、查询订单等商户 API 调用应放在你的服务端完成。

3. 在 Dashboard 完成准备

创建第一笔订单前,先在 Dashboard 里完成两件事:

  • 导入收款地址:导入你接受的链上收款地址。StableOps 会从地址池分配一个地址给支付单。
  • 注册 Webhook 端点:填写你的回调 URL,例如 https://your-app.example.com/hooks/stableops

Webhook secret 只会在创建或轮换时展示,请保存到服务端环境变量,例如 STABLEOPS_WEBHOOK_SECRET。需要自动化时,也可以使用 收款地址 APIWebhook 端点 API

4. 创建支付单

const order = await client.paymentOrders.create(
  {
    merchantOrderId: 'sub_89231_2026_06',
    amount: '49.00',
    settlementAsset: 'USDC',
    acceptedAssets: [
      { chain: 'base-sepolia', asset: 'USDC' },
      { chain: 'ethereum-sepolia', asset: 'USDC' },
    ],
    // 30 分钟后未支付自动过期,订单状态推进到 expired 并释放收款地址。
    expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
    metadata: { customerId: 'cus_9821', plan: 'pro_monthly' },
  },
  { idempotencyKey: crypto.randomUUID() },
)

console.log(order.paymentInstructions)
// [
//   { chain: 'base-sepolia', asset: 'USDC', address: '0x...' },
//   { chain: 'ethereum-sepolia', asset: 'USDC', address: '0x...' },
// ]

把订单 id、金额和 paymentInstructions 返回给你的前端即可;不要把 API key 一起下发。

5. 调用钱包 SDK 支付

前端拿到服务端创建的订单后,用钱包 SDK 调用浏览器钱包发起链上转账:

import {
  getInjectedWalletProviders,
  selectWalletPaymentInstruction,
  sendWalletPayment,
} from '@stableops/wallet-sdk'

const { instruction, provider } = selectWalletPaymentInstruction(
  order.paymentInstructions,
  getInjectedWalletProviders(),
)

const sent = await sendWalletPayment({
  provider,
  amount: order.amount,
  instruction,
})

console.log(sent.txHash)

上面的例子会按当前浏览器已有的钱包,从候选链中选出一条可支付指令。TRON、Solana 或自定义多链选择器可以参考 钱包 SDK。转账上链后,StableOps 的 scanner 会按 订单收款地址匹配入金,并在状态变化时投递 Webhook。

钱包 SDK 只是为了方便——并非必须。付款方可以用任意方式完成这笔转账:手动从 MetaMask / Phantom / TronLink 等钱包发送、从交易所提币、或用其他后端脚本,只要把 订单金额(order.amount)转到对应链的 paymentInstructions[].address 即可。scanner 一律按 (链, 收款地址) 匹配入金,与资金来源无关。

6. 验证签名

每一次投递都带 X-Product-Signature: t=…,v1=…。在解析 body 之前调用 SDK 的 常量时间验签助手:

import { verifySignature } from '@stableops/api-sdk/webhooks'

export async function POST(req: Request) {
  const rawBody = await req.text()
  const result = verifySignature({
    secrets: [process.env.STABLEOPS_WEBHOOK_SECRET!],
    header: req.headers.get('x-product-signature') ?? undefined,
    rawBody,
  })
  if (!result.ok) {
    return new Response(`invalid: ${result.reason}`, { status: 400 })
  }
  const event = JSON.parse(rawBody)
  // 用 event.data.payment_order_id 与自家账本对账即可。
  return new Response('ok')
}

你刚刚完成

  • 在 Dashboard 导入了收款地址并注册了 Webhook 端点。
  • 创建了一笔带具体收款地址的订单。
  • 通过钱包 SDK 发起了一笔链上支付。
  • 端到端验证了一次签名的 Webhook 投递。

下一步:阅读 概念 → 确认 理解状态机; 或跳转到 API 参考

本页内容