指南
Next.js
按照本指南在 Next.js App Router 中集成 StableOps:在服务端创建幂等支付订单,使用钱包 SDK 发起链上转账,接收并验证签名 Webhook,再以可靠的状态查询完成履约;同时覆盖密钥隔离、错误处理和本地测试。
本指南使用 App Router 和当前的 TypeScript SDK。
安装
pnpm add @stableops/api-sdk配置
STABLEOPS_API_KEY=sk_sandbox_...
STABLEOPS_WEBHOOK_SECRET=whsec_...创建 lib/stableops.ts:
import { StableOps } from '@stableops/api-sdk'
export const stableops = new StableOps({
apiKey: process.env.STABLEOPS_API_KEY!,
})本指南使用沙盒密钥和 Base Sepolia 测试网。请先在沙盒环境导入 base-sepolia 收款地址,并准备测试 USDC。上线时需同时切换为生产密钥、主网资产和生产地址池,不能只修改密钥。
以下示例省略了业务鉴权。生产环境必须验证当前用户及订单归属,从服务端商品或账单记录读取应付金额,不得直接信任表单或请求体中的金额;查询与取消订单也必须检查权限。
创建订单
创建 app/actions/payment.ts:
'use server'
import { revalidatePath } from 'next/cache'
import { stableops } from '@/lib/stableops'
export async function createPaymentOrder(formData: FormData) {
const amount = String(formData.get('amount') ?? '')
const merchantOrderId = String(formData.get('orderId') ?? '')
const order = await stableops.paymentOrders.create(
{
merchantOrderId,
amount,
acceptedAssets: [{ chain: 'base-sepolia', asset: 'USDC' }],
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
metadata: { source: 'nextjs_checkout' },
},
{ idempotencyKey: merchantOrderId },
)
revalidatePath('/payments')
return order
}把返回的一条候选支付指令展示给用户:
{
order.paymentInstructions[0] ? (
<div>
在 {order.paymentInstructions[0].chain} 上向以下地址发送 {order.amount}{' '}
{order.paymentInstructions[0].asset}:<code>{order.paymentInstructions[0].address}</code>
</div>
) : null
}验证 Webhook
创建 app/api/webhooks/stableops/route.ts:
import { SIGNATURE_HEADER, verifySignature } from '@stableops/api-sdk/webhooks'
const WEBHOOK_SECRET = process.env.STABLEOPS_WEBHOOK_SECRET!
export async function POST(req: Request) {
const rawBody = await req.text()
const result = verifySignature({
secrets: [WEBHOOK_SECRET],
header: req.headers.get(SIGNATURE_HEADER) ?? undefined,
rawBody,
})
if (!result.ok) {
return new Response(`invalid signature: ${result.reason}`, { status: 400 })
}
const event = JSON.parse(rawBody) as {
type: string
data: { payment_order_id?: string }
}
switch (event.type) {
case 'payment.detected':
break
case 'payment.confirmed':
break
case 'payment.finalized':
// 做完自家幂等检查后再履约。
break
case 'payment.expired':
case 'payment.reverted':
break
}
return new Response('ok')
}示例中的分支仅演示事件路由,不能直接用作生产履约处理。返回成功前应可靠保存事件及待处理任务;业务更新与事件处理完成标记应在同一事务中提交,或采用可恢复的处理状态。具体见 Webhook 可靠处理。
这篇文档怎么样?
最后更新