订阅


订阅是经常性收入的基石,允许您按重复的计划向客户收取产品和服务的费用。在 PaymentKit 中,当客户购买具有经常性价格的产品时,就会创建订阅。

本指南将引导您了解整个订阅生命周期,从通过结账会话创建订阅,到管理其状态和处理基于用量的计费。在继续之前,请确保您已创建产品价格

订阅生命周期#

订阅会根据支付事件、试用期和管理操作经历各种状态。了解此生命周期是有效管理客户的关键。

Subscriptions

以下是订阅可能具有的主要状态:

  • active:订阅状态良好,付款及时。
  • trialing:客户处于免费试用期。
  • past_due:最近一次支付尝试失败,PaymentKit 正在重试收费。
  • canceled:订阅已被取消,将不会续订。
  • incomplete:订阅的初始支付失败。
  • incomplete_expired:初始支付失败,且重试期已过。
  • paused:订阅已暂时暂停,不会生成发票。

创建订阅#

订阅不是直接创建的。而是通过创建一个 modesubscription结账会话来创建。此会话引导客户完成支付流程,成功完成后,将自动创建一个订阅。

Create a Subscription via Checkout

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);
  }
}

See all 1 lines

在此示例中,为一个具有经常性价格的产品创建了一个结账会话。一旦客户完成支付,将创建一个新的订阅,并在首次收费前处于 14 天的 trialing 状态。

管理现有订阅#

订阅创建后,您可以使用 payment.subscriptions 资源对其进行管理。

检索订阅#

使用订阅 ID 获取其详细信息。

Retrieve a Subscription

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 或状态等属性进行筛选。

List Subscriptions

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

取消订阅#

取消订阅是一个常见的需求。您可以选择立即取消,也可以在当前计费周期结束时取消。

Cancel a Subscription

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

暂停和恢复订阅#

对于临时暂停服务,您可以暂停订阅,之后再恢复。

Pause and Resume

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_typemetered 的价格,您必须在整个计费周期内报告用量。这通过为特定的订阅项目创建用量记录来完成。

Report Usage

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 文档,并了解如何监听与订阅相关的事件。