訂閱是週期性收入的基石,讓您能以重複性的排程向客戶收取產品和服務的費用。在 PaymentKit 中,當客戶購買具有週期性價格的產品時,就會建立訂閱。
本指南將引導您了解整個訂閱生命週期,從透過結帳階段(Checkout Session)建立訂閱,到管理其狀態和處理基於用量的計費。在繼續之前,請確保您已建立您的 產品 和 價格。
訂閱生命週期
訂閱會根據付款事件、試用期和管理操作而經歷各種狀態。了解此生命週期是有效管理客戶的關鍵。
Subscription Lifecycle Flow
direction: down
style: {
stroke-width: 2
font-size: 14
}
Checkout: {
label: "結帳階段"
shape: circle
style: {
fill: "#e6f7ff"
stroke: "#91d5ff"
}
}
Incomplete: {
label: "未完成"
style: {
fill: "#fffbe6"
stroke: "#ffe58f"
}
}
Trialing: {
label: "試用中"
style: {
fill: "#f6ffed"
stroke: "#b7eb8f"
}
}
Active: {
label: "啟用中"
style: {
fill: "#d4edda"
stroke: "#28a745"
}
}
Past-Due: {
label: "逾期"
style: {
fill: "#fff1f0"
stroke: "#ffccc7"
}
}
Canceled: {
label: "已取消"
style: {
fill: "#fafafa"
stroke: "#d9d9d9"
}
}
Paused: {
label: "已暫停"
style: {
fill: "#e6f7ff"
stroke: "#91d5ff"
}
}
Checkout -> Incomplete: "付款失敗"
Checkout -> Trialing: "成功(含試用)"
Checkout -> Active: "成功(無試用)"
Incomplete -> Active: "付款成功"
Trialing -> Active: "試用結束"
Active -> Past-Due: "續訂失敗"
Past-Due -> Active: "付款成功"
Past-Due -> Canceled: "重試失敗"
Active -> Canceled: "由使用者/管理員取消"
Trialing -> Canceled: "由使用者/管理員取消"
Active <-> Paused: "暫停/恢復"以下是訂閱可能具有的主要狀態:
active:訂閱狀態良好且付款皆為最新。trialing:客戶正處於免費試用期。past_due:最近一次付款嘗試失敗,PaymentKit 正在重試收費。canceled:訂閱已被取消且不會續訂。incomplete:訂閱的初始付款失敗。incomplete_expired:初始付款失敗,且重試期已過。paused:訂閱已暫時暫停,不會產生發票。
建立訂閱
訂閱並非直接建立。您需要建立一個 mode 為 'subscription' 的 結帳階段(Checkout Session)。此階段會引導客戶完成付款流程,成功完成後,系統會自動建立一個訂閱。
透過結帳建立訂閱
import payment from '@blocklet/payment-js';
async function createSubscriptionCheckout() {
try {
const session = await payment.checkout.sessions.create({
success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://example.com/cancel',
mode: 'subscription',
line_items: [
{ price_id: 'price_xxx', quantity: 1 } // 請替換為您的週期性價格 ID
],
subscription_data: {
trial_period_days: 14, // 選用:14 天免費試用
metadata: {
project_id: 'proj_123',
},
},
});
console.log('Checkout session created:', session.url);
// 將您的客戶重新導向至 session.url
} catch (error) {
console.error('Error creating checkout session:', error.message);
}
}
createSubscriptionCheckout();在此範例中,我們為一個具有週期性價格的產品建立了一個結帳階段。一旦客戶完成付款,將會建立一個新的訂閱,並在首次收費前處於 trialing(試用中)狀態 14 天。
管理現有訂閱
訂閱建立後,您可以使用 payment.subscriptions 資源來管理它。
檢索訂閱
使用訂閱 ID 來獲取特定訂閱的詳細資訊。
檢索訂閱
async function getSubscription(subscriptionId) {
try {
const subscription = await payment.subscriptions.retrieve(subscriptionId);
console.log(`Status of ${subscription.id}: ${subscription.status}`);
console.log('Current period ends:', new Date(subscription.current_period_end * 1000));
} catch (error) {
console.error('Error retrieving subscription:', error.message);
}
}
getSubscription('sub_xxx'); // 請替換為有效的訂閱 ID列出訂閱
您可以列出所有訂閱,或根據客戶 ID 或狀態等屬性進行篩選。
列出訂閱
async function listActiveSubscriptionsForCustomer(customerId) {
try {
const subscriptions = await payment.subscriptions.list({
customer_id: customerId,
status: 'active',
activeFirst: true,
order: 'created_at:DESC',
});
console.log(`Found ${subscriptions.total} active subscriptions for customer ${customerId}:`);
subscriptions.data.forEach(sub => {
console.log(`- ID: ${sub.id}, Status: ${sub.status}`);
});
} catch (error) {
console.error('Error listing subscriptions:', error.message);
}
}
listActiveSubscriptionsForCustomer('cus_xxx'); // 請替換為有效的客戶 ID取消訂閱
取消訂閱是一個常見的需求。您可以選擇立即取消,或在目前計費週期結束時取消。
取消訂閱
async function cancelSubscription(subscriptionId) {
try {
const subscription = await payment.subscriptions.cancel(subscriptionId, {
at: 'current_period_end', // 訂閱將保持啟用狀態,直到計費週期結束。
// 使用 'now' 可立即取消。
reason: 'cancellation_requested',
feedback: 'User no longer needs the service.',
});
console.log(`Subscription ${subscription.id} scheduled for cancellation.`);
} catch (error) {
console.error('Error canceling subscription:', error.message);
}
}
cancelSubscription('sub_xxx'); // 請替換為有效的訂閱 ID暫停和恢復訂閱
對於需要暫時中止服務的情況,您可以暫停訂閱,並在之後恢復它。
暫停和恢復
async function manageSubscriptionPause(subscriptionId) {
try {
// 暫停訂閱
let subscription = await payment.subscriptions.pause(subscriptionId);
console.log(`Subscription ${subscription.id} is now ${subscription.status}.`);
// 一段時間後,恢復訂閱
subscription = await payment.subscriptions.resume(subscriptionId);
console.log(`Subscription ${subscription.id} is now ${subscription.status}.`);
} catch (error) {
console.error('Error managing subscription pause state:', error.message);
}
}
manageSubscriptionPause('sub_xxx'); // 請替換為有效的訂閱 ID回報計量計費的用量
如果您的訂閱使用的價格 usage_type 為 'metered',您必須在整個計費週期內回報用量。這可以透過為特定的訂閱項目建立用量記錄來完成。
回報用量
async function reportApiUsage(subscriptionItemId) {
try {
const usageRecord = await payment.subscriptionItems.createUsageRecord({
subscription_item_id: subscriptionItemId,
quantity: 100, // 要回報的用量
action: 'increment', // 'increment' 會在現有用量上增加,'set' 則會覆寫。
timestamp: Math.floor(Date.now() / 1000), // 用量發生的時間
});
console.log('Usage record created:', usageRecord.id);
} catch (error) {
console.error('Error reporting usage:', error.message);
}
}
// 您可以在訂閱物件上找到 subscription_item_id。
reportApiUsage('si_xxx'); // 請替換為有效的訂閱項目 ID在計費週期結束時,PaymentKit 會將所有回報的用量加總,並據此向客戶收費。
後續步驟
您現在對如何管理訂閱有了扎實的了解。若要深入研究,請探索詳細的 API 文件,並學習如何監聽與訂閱相關的事件。