Skip to content

独立站 / 外贸电商接入 ​

这套接入适合谁 ​

用 Shopify、WooCommerce 或自建商城做外贸生意的商家,想在结账页加一个"USDT 付款"选项, 买家选它之后看到收款信息,付款成功后订单自动标记为已支付。

前置准备 ​

  • 已拿到 API KEY(见立即接入),并配置好收款地址。
  • 你的服务端能接收 webhook(平台的支付回调),并且能调用平台的开放接口(带 KEY)。
  • 想清楚"谁触发发货":建议由平台的支付回调驱动订单状态变更,而不是靠买家点击。

服务端下单,绝不放到结账页

结账页是浏览器。所有下单请求都从你的商城后端发出(KEY 在后端), 结账页只负责展示收款信息或跳转。

接入形态:自定义支付方式 + webhook ​

三个平台的接法本质相同,都是这一套:

买家在结账页选 "Pay with USDT"
        │
        ▼
你的商城后端 ──(带 API KEY)──▶ 平台 createOrder
        │
        ▼
展示收款信息(收款地址 + 应付金额 + 链)或跳平台收银台
        │
        ▼
买家付款 ──▶ 链上确认 ──▶ 平台回调你的 webhook
        │
        ▼
你的商城后端把订单标记为已支付 / 触发发货

WooCommerce:自定义支付网关 ​

WooCommerce 通过支付网关插件接入。核心是 process_payment 里下单、以及一个接收回调的 REST 端点:

php
<?php
// 精简示意:WooCommerce 自定义支付网关
class WC_Gateway_Web3SDK extends WC_Payment_Gateway {
    public function __construct() {
        $this->id   = 'web3sdk';
        $this->method_title = 'USDT (Web3SDK)';
        $this->has_fields = false;
        parent::__construct();
    }

    public function process_payment($order_id) {
        $order = wc_get_order($order_id);

        // 1) 在商城后端下单:带 API KEY 调平台开放接口
        $resp = wp_remote_post(getenv('API_BASE') . '/api/pay/open/createOrder', [
            'headers' => [
                'X-Pay-Api-Key' => getenv('PAY_API_KEY'),   // 只在服务端
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode([
                'merchantOrderNo' => (string) $order_id,     // 幂等键
                'chainId'         => 2,
                'tokenId'         => 1,
                'amount'          => (string) $order->get_total(),  // 代币单位小数字符串
                'subject'         => 'Order #' . $order_id,
                'notifyUrl'       => home_url('/?wc-api=web3sdk_notify'),
                'returnUrl'       => $this->get_return_url($order),
            ]),
            'timeout' => 15,
        ]);

        $data = json_decode(wp_remote_retrieve_body($resp), true);
        if (empty($data['code']) && !isset($data['data']['order'])) {
            wc_add_notice('USDT 下单失败,请重试', 'error');
            return ['result' => 'failure'];
        }

        $o = $data['data']['order'];
        // 2) 把收款信息存到订单上,结账后页面展示
        $order->update_meta_data('_web3_order_no',     $o['orderNo']);
        $order->update_meta_data('_web3_pay_address',  $o['payAddress']);
        $order->update_meta_data('_web3_pay_amount',   $o['payAmountDisplay']);
        $order->update_meta_data('_web3_chain',        $o['chainKey']);
        $order->save();

        return ['result' => 'success', 'redirect' => $this->get_return_url($order)];
    }
}

接收回调(验签 + 幂等 + 改订单状态):

php
add_action('woocommerce_api_web3sdk_notify', function () {
    $raw = file_get_contents('php://input');                       // 原始正文
    $ts  = $_SERVER['HTTP_X_PAY_TIMESTAMP'] ?? '';
    $sig = $_SERVER['HTTP_X_PAY_SIGNATURE'] ?? '';
    $key = getenv('PAY_API_KEY');

    $want = hash_hmac('sha256', $ts . $raw, $key);                 // 时间戳 + 原始正文
    if (!hash_equals($want, $sig)) {                               // 常数时间比较
        status_header(401); exit;
    }

    $p = json_decode($raw, true);
    $order_id = intval($p['merchantOrderNo'] ?? 0);
    $order = $order_id ? wc_get_order($order_id) : null;
    if (!$order) { status_header(200); exit; }                     // 不是我们的单,仍回 2xx

    // 幂等:已处理过就直接应答
    if ($order->get_meta('_web3_tx_hash') === ($p['txHash'] ?? '')) {
        status_header(200); exit;
    }

    // 只认已确认,且不要只信回调 —— 可选:再查一次平台查询接口核对
    if (($p['status'] ?? 0) >= 3) {
        $order->update_meta_data('_web3_tx_hash', $p['txHash'] ?? '');
        $order->payment_complete($p['txHash'] ?? '');              // 触发 WooCommerce 的已支付流程
    }
    status_header(200); exit;                                      // 2xx 让平台停止重试
});

Shopify:自定义支付方式 ​

Shopify 的自定义支付方式(Custom payment method)在结账时把买家跳到一个你控制的 URL, 你在那里展示收款信息,付款成功后通过 Order API 把订单标记为已支付:

js
// 1) 你的服务端:跳转落地页里下单
app.get('/shopify/pay', async (req, res) => {
  const { orderId, total } = req.query
  const r = await fetch(`${process.env.API_BASE}/api/pay/open/createOrder`, {
    method: 'POST',
    headers: { 'X-Pay-Api-Key': process.env.PAY_API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      merchantOrderNo: String(orderId),
      chainId: 2, tokenId: 1, amount: String(total),
      subject: `Shopify Order ${orderId}`,
      notifyUrl: 'https://your-shop.com/shopify/notify',
    }),
  }).then((x) => x.json())
  res.render('pay', { order: r.data.order })
})

// 2) 收到平台回调后,把 Shopify 订单标记为已支付
app.post('/shopify/notify', express.raw({ type: '*/*' }), async (req, res) => {
  const raw = req.body.toString('utf8')
  if (!verify(raw, req.get('X-Pay-Timestamp'), req.get('X-Pay-Signature'))) {
    return res.sendStatus(401)
  }
  const p = JSON.parse(raw)
  if (isProcessed(p.merchantOrderNo)) return res.sendStatus(200)   // 幂等

  await fetch(`https://${process.env.SHOP}/admin/api/2024-07/orders/${p.merchantOrderNo}/transactions.json`, {
    method: 'POST',
    headers: {
      'X-Shopify-Access-Token': process.env.SHOPIFY_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      transaction: { kind: 'capture', status: 'success', amount: p.amountDisplay },
    }),
  })
  markPaid(p.merchantOrderNo, p.txHash)
  res.sendStatus(200)
})

Shopify 的 API 版本与字段会变,请以官方文档为准;这里给的是接入形状。

回调处理 ​

无论哪个平台,回调都遵循同一套规则:

  • 验签:HMAC-SHA256(apiKey, timestamp + 原始正文),常数时间比较。
  • 幂等:用 merchantOrderNo(在你的商城里通常就是订单号)去重,别重复发货。
  • 快返回:约 10 秒超时;改订单状态这类动作如果慢,先落库再异步做。
  • 2xx 才停重试:返回 2xx 表示你收到了;其它状态码平台会按退避序列重试。
  • 以查询为准:发货前用查询订单复核真实状态。

常见问题 ​

问题说明
能不能直接放一段 JS 在结账页下单不能,KEY 会暴露。必须服务端下单
支付回调与商城 webhook 冲突吗不冲突。平台的回调驱动"订单已支付",商城自己的 webhook 照常
用 amount 还是商城总额开放接口要代币单位小数字符串,与商城总额同单位即可
币价波动怎么办稳定币计价,金额按订单金额直接给;不涉及换汇
回调丢了怎么办平台会重试;此外你的商城可以定时用查询接口对账补状态
库存锁定多久由订单有效期(expiresAt)决定,过期即作废,不必再等

相关 ​

面向全球中小商家的 Web3 支付基础设施