返回部落格
Engineering
2026-07-158 分鐘閱讀作者:StableOps

USDC 訂閱:無需儲存錢包授權,如何收取週期性穩定幣付款

本文說明如何在不儲存錢包授權或代管資金的前提下建置 USDC 訂閱:按週期生成帳單,由付款人主動連線自託管錢包結帳,再以已驗證的鏈上結算事件更新訂閱狀態;同時覆蓋續費提醒、逾期、方案變更、對帳與完整生命週期管理。

USDC
訂閱支付
穩定幣支付

USDC 訂閱不應被設計成試圖用錢包私鑰或長期錢包授權重現銀行卡自動扣款。更可靠的做法是週期性出帳:你的服務定義方案和帳期;每張發票產生新的付款請求;客戶在 Hosted Checkout 或自有錢包流程中主動發起轉帳;後端只在已驗證的訂閱事件表明發票已經結算、訂閱已經啟用或續期後變更服務權益。

這個模型適合 SaaS、API 積分和數字服務,也能清晰劃分託管邊界:商戶控制收款地址,StableOps 不保管付款人私鑰、錢包憑據或可複用扣款授權,付款人決定何時簽署每一筆鏈上轉帳。

週期性發票不是自動錢包扣款

“加密訂閱計費”可能指兩種完全不同的系統。把它們混為一談,容易讓錢包流程作出無法兌現的承諾。

模型誰發起轉帳商戶儲存什麼何時變更服務狀態
自動扣款已預授權的支付機制可複用授權或支付憑據扣款結果可靠後
週期性穩定幣發票每張發票均由付款人發起訂閱、發票、付款訂單和事件記錄已驗證的訂閱事件記錄啟用或續期後

StableOps 使用第二種模型。方案定義金額和週期,訂閱將方案關聯到 merchantUserId,開放發票可以發起一次付款嘗試。付款人可以在託管頁面中支付發票,也可以使用你建置的錢包介面。首期付款和瀏覽器回跳 URL 都不是以後從該錢包扣取 USDC 的授權。

這個邊界應體現在產品文案、催繳郵件和帳戶頁面中:說明下一張發票何時開出、客戶如何支付;除非另行營運明確授權的支付機制,否則不要承諾錢包會被自動扣款。

將訂閱建模為從出帳到結算的生命週期

以按月收取 USDC 的方案為例,可靠路徑如下:

方案:USD 金額 + 帳單週期
          |
          v
為 merchantUserId 建立訂閱
          |
          v
首期或續期帳單的開放發票
          |
          v
Portal session -> Hosted Checkout(或你的錢包流程)
          |
          v
付款人選擇可接受的 USDC 鏈並按精確付款指令轉帳
          |
          v
payment.detected -> payment.confirmed -> payment.finalized
          |
          v
發票結算 -> end_user_invoice.paid
          |
          v
end_user_subscription.activated 或 end_user_subscription.renewed
          |
          v
商戶冪等開通或續期服務

方案和發票以 USD 計價,並不預先繫結某一種穩定幣。建立結帳時傳入你能夠接收的鏈和資產組合,例如 Base USDC。StableOps 會為發票建立 Payment Order,併為這些可接受組合分配收款地址。只有當 Payment Order 達到 FINALIZED、發票結算事務將其標記為 paid 時,實際結算 asset 才會回填;在此之前,包括付款請求已經建立後,asset 仍可能為 null

如果只准備接收 USDC,建議先支援一條已支援的 USDC 鏈。這樣可減少網路選擇、地址容量和精確付款指令帶來的客服歧義。只有當每個新增組合都有足夠的收款地址和對帳流程時,再增加更多可接受組合。訂閱指南說明了方案、發票、Portal 和結算模型。

建立首張發票並將付款人跳轉到結帳頁

商戶側操作應始終在後端完成:使用 secret API key 建立方案、訂閱和 Portal session。Portal token 有效期較短,且只限定到一個商戶使用者;後端可以用它建立發票 checkout session,再只把返回的 Checkout URL 交給瀏覽器。

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

const stableops = new StableOps({
  apiKey: process.env.STABLEOPS_API_KEY!,
})

const planId = process.env.STABLEOPS_PLAN_ID
if (!planId) throw new Error('STABLEOPS_PLAN_ID is required')

const created = await stableops.merchantSubscriptions.subscriptions.create(
  {
    planId,
    merchantUserId: 'user_123',
  },
  { idempotencyKey: 'subscription:user_123:starter' },
)

// 試用訂閱要到試用結束後才會開出首張發票。
const invoice = created.invoice
if (!invoice) throw new Error('No invoice is due yet')

const portalSession = await stableops.merchantSubscriptions.portalSessions.create({
  merchantUserId: 'user_123',
})

const portal = stableops.portal(portalSession.portalToken)
const checkout = await portal.invoices.checkoutSession(invoice.id, {
  // 演示使用 Base Sepolia 測試網組合;生產環境請換成已支援的主網組合,例如 { chain: 'base', asset: 'USDC' }。
  acceptedAssets: [{ chain: 'base-sepolia', asset: 'USDC' }],
  successUrl: 'https://merchant.example/billing/return',
  cancelUrl: 'https://merchant.example/billing/canceled',
})

return Response.redirect(checkout.checkoutUrl, 303)

STABLEOPS_PLAN_ID 必須是建立方案時返回的真實 ID,而不是便於閱讀的方案 code。建立訂閱時使用穩定的、商戶自有的冪等鍵,並在自己的帳戶記錄旁儲存 StableOps 的訂閱和發票 ID。試用訂閱在首張發票到期前會返回 invoice: null,因此生產程式碼必須處理這個分支,不能強制斷言非空。包含方案建立和自有錢包替代方案的完整公開 SDK 流程可見訂閱指南

successUrl 只是瀏覽器回跳路徑,不是支付收據。客戶可能關閉標籤頁、在鏈上最終性達成前回跳,或在未支付時反覆訪問該 URL。應根據發票狀態和已簽名 Webhook 更新計費介面,而不是在回跳時延長權益。

用最終結算而非結帳完成控制訪問許可權

發票 checkout session 包裝的是發票自己的 Payment Order,因此訂閱發票與一次性付款採用同樣的鏈上狀態機。

訊號適合的用途不能據此做什麼
開啟 Checkout 或瀏覽器回跳展示付款進度標記發票已支付或續期服務
payment.detected展示“已收到付款”不可逆的續期或最終帳務寫入
payment.confirmed進度 UI 或刻意設計為可逆的操作認定所有鏈上風險已經過去
已驗證的 payment.finalized記錄底層鏈上轉帳已經達到最終狀態假定訂閱結算已經完成
end_user_invoice.paid記錄發票結算事務已經提交判斷本次是首次啟用還是續期
end_user_subscription.activatedend_user_subscription.renewed冪等開通或續期服務跳過 Webhook 驗籤或事件去重

普通支付事件描述底層 Payment Order,end_user_invoice.*end_user_subscription.* 才是訂閱狀態的事實來源。監聽 end_user_invoice.open.paidend_user_subscription.activated.renewed;將 end_user_invoice.payment_late 作為需要明確恢復決策的異常處理。催繳流程還應定期查詢訂閱狀態,避免 past_dueexpired 帳戶只依賴某一條通知路徑。對原始 Webhook body 驗籤,按 X-Event-Id 去重,並按內部帳戶或訂閱 ID 使真正的權益寫入冪等。Webhook 指南穩定幣支付 Webhook說明了驗籤、重試和履約邊界。

明確處理續費、變更和恢復

穩定幣計費不包含隱藏的付款許可權,因此面向客戶的生命週期必須清晰可見。

計費情形目前生命週期行為商戶動作
首張發票已支付發票變為 paid;訂閱發出 activated 並變為 active冪等開通首期權益
首張發票未支付到期且沒有在途訂單時,發票變為 uncollectibleincomplete 訂閱變為 expired將記錄視為終態,並轉入商戶自定義的重新入駐或客服流程
續費發票已開出目前訂閱繼續有效,付款人收到新發票建立新的 Portal session 和 Checkout 連結;首期付款不構成後續授權
續費未支付目前週期結束後訂閱進入 past_due;超過寬限期後變為 expired,開放發票變為 uncollectible執行已說明的寬限期和訪問策略,並定期對帳狀態
超過收款期後到帳不可收取發票保持未結算,併發出 end_user_invoice.payment_late透過明確的客服流程決定複核、退款、記帳或恢復服務
升級到更高價格方案立即開出金額為目標價減目前價的 upgrade_proration 差價發票;結算後才切換方案展示並收取差價發票
降級或同價變更目標方案保持 pending,到下一次續費時生效展示未來生效日期
期末取消設定 cancelAtPeriodEnd;進入終態前仍可恢復按產品策略保留權益直到目前週期結束
立即取消開放發票變為 uncollectible,訂閱變為 canceled停止權益;不要假定 resume 可以將其復活

升級或取消時,應使用訂閱 API 或 Merchant Portal,而不是隻修改內部方案標籤。resume 只能清除非終態訂閱上待執行的期末取消,不能復活已經 canceledexpired 的訂閱。應用應保留 StableOps 訂閱 ID、目前方案、發票 ID 和付款訂單引用,使客服和對帳能定位到準確的計費週期。Merchant Portal session只為終端使用者提供其自身訂閱和發票的受限上下文,不能替代後端授權規則。

USDC 訂閱支付的生產檢查清單

  • 將產品描述為付款人主動發起的週期性 USDC 發票,不描述成自動錢包扣款。
  • 在後端建立方案和訂閱;不要在瀏覽器暴露商戶 secret API key。
  • 將帳戶 ID 與 StableOps 訂閱 ID、發票 ID 和關聯的付款訂單 ID 一起儲存。
  • 從明確且已支援的 USDC 鏈和資產組合開始,並保持充足的收款地址容量。
  • 將發票金額與返回的付款指令作為付款人轉帳的事實來源。
  • 僅將 successUrl 用於使用者體驗;透過已驗籤、已去重的訂閱事件和對帳後的訂閱狀態結算權益。
  • 將續費、past_due、升級、取消和恢復當作明確的狀態變更處理。
  • 即使 Webhook 已去重,仍要按內部帳戶或訂閱 ID 讓權益寫入冪等。
  • 擴充套件到更多鏈或方案前,對帳 openpaidvoiduncollectible 發票,以及 past_dueexpired 訂閱。

FAQ

USDC 訂閱可以自動從客戶錢包扣款嗎?

不可以,至少本週期性發票流程不支援。每張發票都由客戶透過 Hosted Checkout 或其主動發起的錢包流程支付。StableOps 不保管客戶私鑰、錢包憑據或可複用扣款授權,也不會把首期付款當成以後劃轉 USDC 的許可。

訂閱計費應支援哪一條 USDC 網路?

先選擇一組客戶常用、產品已支援且你有收款地址的 (chain, asset) 組合。付款人選擇的精確付款指令,包括鏈、USDC 資產、地址和金額,才是發票 Payment Order 的匹配依據。

客戶付款後,何時續期服務?

首次開通應處理已驗籤、已去重的 end_user_subscription.activated,續期應處理 end_user_subscription.renewed,並以 API 狀態查詢作為恢復路徑。底層 payment.finalized 證明鏈上付款達到最終狀態,但它可能早於訂閱結算完成。不要根據錢包交易雜湊、Hosted Checkout 回跳或未經驗證的 Webhook 續期。

從一個方案和一條訂閱事件路徑開始

先建立一個按月方案,在 Sandbox 建立訂閱,並讓測試使用者透過 Hosted Checkout 支付第一張發票。隨後接通已簽名的發票和訂閱 Webhook 路徑,再向真實客戶提供方案。非託管 USDC 收款架構說明了底層 Payment Order 生命週期。當服務能夠收取付款人主動發起的 USDC 轉帳、透過訂閱事件開通或續期,並恢復一次漏付或遲到付款時,就具備了不保管付款人錢包憑據的穩定週期性計費基礎。

相關文章

穩定幣轉帳並不等於支付完成。本文從付款訂單狀態機、鏈與資產精確匹配、確認和重組處理、介面冪等、Webhook 事件去重及可恢復履約出發,說明如何把鏈上轉帳轉化為可安全進入生產環境、能夠審計並支援異常恢復的支付事件。

穩定幣退款必須作為新的鏈上轉帳單獨處理。本文講解 StableOps 如何校驗最終訂單和剩餘額度,由商戶從原收款地址簽署退款交易,再核對目標地址、資產、金額與最終深度,並透過 Webhook、失敗重試和對帳避免重複退款與帳務遺漏。

本文講解交易平台如何用唯一地址、鏈上最終確定、Webhook 冪等處理與每日對帳建置加密貨幣充值監控,在支援客戶選擇多條網路的同時安全增加內部餘額,妥善處理過期、遲到與異常轉帳,並降低重複記帳、錯誤歸屬和鏈重組造成的真實資金損失。

本文介紹完整的 AI Agent 穩定幣支付架構:買方側用策略、預算、審批和客戶自持簽名限制自動付款,賣方側用付款訂單、鏈上最終性、Webhook 與對帳確認收款,並說明如何處理結算未知、資源交付失敗和審計追蹤。