Skip to content

接入 Taria Pay(给 coding agent)

这页是商家应用接入 Taria Pay 的唯一契约,给 coding agent 和工程师共用。不要自造字段名、路径或结账 URL。

人工文档:快速接入 · 创建支付订单 · 接收支付结果 · API 参考

环境

生产 API originhttps://api.tariapay.com
本地 API originhttp://127.0.0.1:3003
Hosted checkouthttps://checkout.tariapay.com(本地 http://127.0.0.1:3004
密钥环境变量TARIAPAY_SECRET_KEY(商家后台 → Developers)。别名:TARIAPAY_API_KEY
Webhook 密钥TARIAPAY_WEBHOOK_SECRET
API originTARIAPAY_API_BASE只写 origin,不要带路径

商户接口:

调用方URL
规范路径{origin}/v1/payment-intents
兼容别名(同一组 handler){origin}/merchant/v1/payment-intents
SDK baseUrl只传 {origin}。SDK 会追加 /v1

不要叠前缀(/merchant/v1 再加一层 /v1)。公开结账接口仍在 /checkout/v1/...,商户服务端不要调。

最小流程

  1. 服务端用密钥创建 payment intent。
  2. 浏览器跳转到 paymentIntent.checkoutUrl(或用 CheckoutButton 打开)。
  3. 只在 验签通过 的 webhook(payment_intent.confirmed)或服务端 retrieve 之后履约。
  4. 不要因为买家回到了 successUrl 就履约。

Node / Workers 骨架

ts
import { TariaPay, TariaPayError, constructWebhookEvent } from "@tariapay/sdk";

const tariapay = new TariaPay({
  secretKey: process.env.TARIAPAY_SECRET_KEY!,
  baseUrl: process.env.TARIAPAY_API_BASE, // 只传 origin
});

export async function createCheckout(order: {
  orderId: string;
  amount: string;
  email?: string;
  appUrl: string;
}) {
  try {
    return await tariapay.paymentIntents.create({
      orderId: order.orderId,
      amount: order.amount,
      currency: "USDC",
      acceptedCurrencies: ["USDC", "USDT"],
      successUrl: `${order.appUrl}/orders/${order.orderId}?pay=success`,
      cancelUrl: `${order.appUrl}/orders/${order.orderId}?pay=cancel`,
      customer: order.email ? { email: order.email } : undefined,
    });
  } catch (error) {
    if (error instanceof TariaPayError && error.type === "timeout_error") {
      return tariapay.paymentIntents.retrieveByOrderId(order.orderId);
    }
    throw error;
  }
}

export function verifyWebhook(rawBody: string, headers: Headers) {
  return constructWebhookEvent(rawBody, headers, process.env.TARIAPAY_WEBHOOK_SECRET!);
}

Cloudflare Workers:

  • 打开 nodejs_compat
  • SDK 会绑定 fetch。如果你自己注入 fetch,写成 (input, init) => fetch(input, init)
  • constructWebhookEvent 不需要商户 secret key。
  • 密钥放 wrangler secret / .dev.vars,不要放进 [vars]

acceptedCurrencies

currency 是主币种,也是你自己订单上应记录的币。

acceptedCurrencies 控制结账页币种选择。不传则页面 只显示 currency

ts
await tariapay.paymentIntents.create({
  orderId: "order_1001",
  amount: "49.99",
  currency: "USDC",
  acceptedCurrencies: ["USDC", "USDT"],
});

paymentMethods 管的是钱包 vs 转账,不是币种。

Webhook 契约

请求头(规范名与别名都要认):

规范名别名含义
x-tariapay-signaturex-pay-signature${timestamp}.${rawBody} 的 hex HMAC-SHA256
x-tariapay-timestampx-pay-signature-timestampUnix 秒
x-tariapay-eventx-pay-event事件类型
x-tariapay-delivery-idx-pay-delivery-id投递 ID(幂等键)
json
{
  "id": "evt_123",
  "type": "payment_intent.confirmed",
  "createdAt": "2026-03-29T12:00:00.000Z",
  "data": {
    "paymentIntent": {
      "paymentId": "b1985d0b-2026-4f8c-bf75-59ea53fe5466",
      "orderId": "order_1001",
      "status": "confirmed",
      "currency": "USDC",
      "amount": "49.99",
      "checkoutUrl": "https://checkout.tariapay.com/pi_b1985d0b-2026-4f8c-bf75-59ea53fe5466",
      "txHash": "0x..."
    }
  }
}

data.paymentIntent 读字段。用 x-tariapay-delivery-idid 去重。只在 type === "payment_intent.confirmed" 时履约。

本地 E2E(命令)

在 Taria Pay 仓库:

bash
npm run dev:backend          # :3003
npm run dev:checkout         # :3004
./scripts/dev-backfill-loop.sh

在商家应用:

bash
TARIAPAY_API_BASE=http://127.0.0.1:3003
TARIAPAY_SECRET_KEY=tpk_...
# Webhook:用公网 HTTPS 隧道指向本地 handler,再到后台 Developers → Webhooks 注册

已付款的 intent 会停在 processing,直到有人 POST /internal/backfill。不跑 backfill loop,本地不会发商户 webhook。

托管测试网上的测试模式支付需要测试代币:合约地址与领取方式见测试 → 测试代币

错误对照

现象处理
创建返回 401密钥错,或 baseUrl 不是 origin(或旧 listener 没有 /v1 别名)。
创建超时retrieveByOrderId(orderId),不要换新的 orderId
创建 409orderId 已存在,改为查询。
结账页只有一个币acceptedCurrencies
Webhook 验签失败HMAC 必须用原始 body;secret 必须和已注册 endpoint 一致。
本地已付款无 webhook启动 dev-backfill-loop.sh,并用隧道暴露 handler。
Workers 报 Illegal invocation不要传入未绑定的 globalThis.fetch;用 SDK 默认或包一层。

前端

托管跳转:window.location.assign(checkoutUrl)

React 按钮:

tsx
import { CheckoutButton } from "@tariapay/checkout-panel/button";
import "@tariapay/checkout-panel/styles.css";

Vite 额外配置:optimizeDeps.include: ["@tariapay/checkout-panel/button"];如果包是 workspace 源码链接,还要加 server.fs.allow。按钮不会拉钱包栈。

不要做

  • 把 secret key 放进浏览器包。
  • successUrl 履约。
  • 自己拼接 checkout.tariapay.com/...
  • 创建超时时换一个新的 orderId 再试。
  • 猜测 webhook 字段路径(data.payment / snake_case)。上面的 envelope 就是契约。