訂閱
使用 StableOps 建立穩定幣訂閱方案、為商戶使用者開通和管理訂閱、自動生成帳單,並讓終端使用者透過託管結帳頁或自有錢包流程付款;再根據已驗證的訂閱事件更新服務狀態。完整覆蓋方案變更、取消、恢復、帳單支付和付款狀態查詢,幫助業務可靠維護訂閱生命週期。
StableOps 訂閱把商戶側管理和終端使用者支付分開:你的後端用金鑰管理方案、訂閱、帳單設定和 Portal 會話;終端使用者拿 Portal 令牌檢視自己的帳單,並透過託管結帳頁或你的自有錢包支付流程完成付款。
生命週期
- 建立一個或多個方案,指定週期和金額,方案金額以美元計價。
- 為你的
merchantUserId建立訂閱。 - 讀取首期未支付帳單,併為該使用者建立 Portal 會話。
- 在 Portal 上下文為帳單建立結帳頁會話,跳轉到結帳頁支付,或拿到支付指令,交給你自己的錢包支付。
- 以 Webhook 和帳單狀態為準完成業務邏輯。
successUrl只是瀏覽器返回路徑。
結算幣種
方案和帳單不繫結某一種穩定幣。帳單發起支付時,StableOps 會按帳單金額生成一張收款單,發起支付請求傳入 acceptedAssets,StableOps 會按這些鏈和資產組合分配收款地址。付款人在結帳頁裡選擇一條鏈和一種幣,或你的錢包從返回的支付指令裡選擇一項;實際打出的那種幣就成為帳單的結算幣種。在此之前,帳單的 asset 為 null,表示還沒有已結算幣種。
acceptedAssets 需要與你已匯入的地址池可收款範圍一致。如果請求的鏈/資產沒有可用地址,建立收款單會像單獨建立 payment order 一樣失敗。
支付方式選擇
託管結帳頁是最快的整合方式:呼叫 portal.invoices.checkoutSession(invoice.id, { acceptedAssets, ... }),把使用者重定向到返回的 checkoutUrl。
如果你已經有自己的錢包支付流程,可以改為呼叫 portal.invoices.pay(invoice.id, { acceptedAssets })。回應裡包含 paymentOrder.paymentInstructions,欄位形狀和單獨建立 payment order 一致。你選擇其中一條指令,讓使用者錢包在對應鏈和資產上把帳單金額轉到該地址,然後以 StableOps webhook 和帳單狀態作為最終結算依據。
結算模型
帳單結帳頁會話包住的是帳單自己的收款單。收款單後設資料裡有 invoice_id,帳單記錄也回寫了 paymentOrderId。收款單到達 FINALIZED 後,StableOps 透過這對欄位結算帳單,把付款人實際打出的穩定幣回填到帳單的 asset,再把訂閱狀態推進到 active 或完成方案升級。
角色
- 後端或沙箱工具用金鑰建立方案、訂閱和 Portal 會話。
- 終端使用者瀏覽器用 Portal 令牌讀取自己的訂閱和帳單。
- 控制台用於管理方案、訂閱、帳單以及商戶級訂閱設定。
線上測試
下面的面板會在瀏覽器裡使用你的沙箱 API 金鑰,準備演示方案、建立演示訂閱、建立 Portal 會話,並開啟帳單對應的託管結帳頁。
开启时会在创建账单收银台会话前导入一个确定性 burner 地址,适合 org 还没有任何收款地址的场景。若只想使用自己管理的地址,请关闭。
SDK 示例
import { StableOps } from '@stableops/api-sdk'
const stableops = new StableOps({
apiKey: process.env.STABLEOPS_API_KEY!,
})
const plan = await stableops.merchantSubscriptions.plans.create(
{
code: 'starter',
name: 'Starter',
groupKey: 'starter',
// 金額以美元計價;不指定幣種,終端使用者在結帳頁裡選擇 USDC 或 USDT。
amount: '9.00',
interval: 'month',
intervalCount: 1,
},
{ idempotencyKey: 'plan_starter' },
)
const created = await stableops.merchantSubscriptions.subscriptions.create(
{
planId: plan.id,
merchantUserId: 'user_123',
},
{ idempotencyKey: 'sub_user_123' },
)
const portalSession = await stableops.merchantSubscriptions.portalSessions.create({
merchantUserId: 'user_123',
})
const portal = stableops.portal(portalSession.portalToken)
const invoice = created.invoice!
const acceptedAssets = [{ chain: 'base-sepolia', asset: 'USDC' }] as const
const checkout = await portal.invoices.checkoutSession(invoice.id, {
acceptedAssets,
// 可選。省略時預設 exact;auto 會在共享地址金額衝突時自動微調應付金額。
amountMode: 'auto',
successUrl: 'https://merchant.example/success',
cancelUrl: 'https://merchant.example/cancel',
})
return Response.redirect(checkout.checkoutUrl, 303)也可以使用你自己的錢包支付:
const payment = await portal.invoices.pay(invoice.id, {
acceptedAssets,
// 可選。省略時預設 exact;auto 時請始終使用返回的 paymentOrder.amount 發起轉帳。
amountMode: 'auto',
})
const instruction = payment.paymentOrder.paymentInstructions[0]
// 交給你的錢包層:傳送 payment.paymentOrder.amount 數量的
// instruction.asset 到 instruction.chain 上的 instruction.address。
await payWithYourWallet({
chain: instruction.chain,
asset: instruction.asset,
amount: payment.paymentOrder.amount,
to: instruction.address,
})這篇文件怎麼樣?
最後更新