Open14

Stripeについて

西川信行西川信行

無料トライアルを開始すると、Webhookのcustomer.subscription.createdが開始される。
stripeEvent.data.objectcustomerSubscriptionCreatedとして定義すると、
customerSubscriptionCreated.status === "trialing"となり、そのitems.dataに購入している商品の情報が格納されている。

西川信行西川信行

無料トライアルをそのまま完了した場合

無料トライアル期間が終了した際に、Webhookのcustomer.subscription.updatedが開始される。
stripeEvent.data.objectcustomerSubscriptionUpdatedとして定義すると、
customerSubscriptionUpdated.status === "active"で判断できる。

西川信行西川信行

サブスクリプションがキャンセルされると、customer.subscription.deletedが実行される。

西川信行西川信行

課金が発生した場合は、invoice.payment_succeededを使うのがよさそう。

西川信行西川信行

サブスクリプションの請求が失敗したことが分かるWebhookは、invoice.payment_failedです

西川信行西川信行

サブスクリプションの請求が失敗すると、Stripeは複数回自動的に請求を試みます。試行回数の上限に達すると、サブスクリプションのステータスが past_due に変更されます。past_due ステータスのサブスクリプションがある場合、顧客には指定された期限までに請求を支払うように通知されます。期限を過ぎると、サブスクリプションのステータスが unpaid に変更されます。unpaid ステータスの場合、顧客にはサブスクリプションがキャンセルされることが通知されます。

西川信行西川信行

past_due になった時には customer.subscription.updated が走り、該当サブスクリプションの status が past_due に更新される。

また、subscription_items と latest_invoice に含まれる情報を参照することで、該当のイベントを判別することができる。

西川信行西川信行

サブスクリプションの作成(無料トライアル有)

サブスクリプションが作成されたときにcustomer.subscription.createdが実行される。

statusがtriallingになっている。その際に、サービスに購入されたプランと有効期限を記録する。

サブスクリプションが有料ステータスに移行

invoice.paidの状態をみて、サービスの有効期限を更新する。

invoice.paidをもとにサービスの有効期限を更新し、customer.subscription.updatedをもとにサービスのプランの状態を更新する。

無料トライアル期間中にプランを変更

無料期間中にプランを変更した場合は、customer.subscription.updatedをもとにプランを更新する。

invoice.paidの状態をみて、サービスの有効期限を更新する。

サブスクリプションが有料ステータスに移行した場合と、やることは変わらない。

毎月のサブスクリプションを請求(成功)

invoice.paid状態を見て、サービスの有効期限を更新する。

プランの変更

customer.subscription.updatedをもとにサービスのプランの状態を更新する。

毎月のサブスクリプションを請求(失敗)

西川信行西川信行

customer idから、アクティブなサブスクリプションを全て削除する

import Stripe from 'stripe';

const stripe = new Stripe('ストライプのシークレットキー', {
  apiVersion: '2020-08-27',
});

const cancelActiveSubscriptions = async (customerId: string) => {
  try {
    // 顧客情報を取得する
    const customer = await stripe.customers.retrieve(customerId, {
      expand: ['subscriptions.data'],
    });

    // アクティブなサブスクリプションをキャンセルする
    const cancelSubscriptionPromises = customer.subscriptions.data
      .filter((subscription) => subscription.status === 'active')
      .map((subscription) => stripe.subscriptions.del(subscription.id));

    const results = await Promise.all(cancelSubscriptionPromises);

    console.log(`Canceled ${results.length} active subscriptions for customer ${customerId}`);
  } catch (err) {
    console.error(err);
  }
};

西川信行西川信行

StripeのカスタマーIDから、従量課金のsubscriptionIdを取得する方法。

const subscriptions = await stripe.subscriptions.list({ customer: customerId });
const subscriptionItems = subscriptions.data.find(
  (subscriptionData) =>
    subscriptionData.status === 'active' || subscriptionData.status === 'trialing',
)?.items.data;

const payPerUnitItem = subscriptionItems?.find(
  (subscriptionItem) => subscriptionItem.plan.billing_scheme === 'tiered',
);
西川信行西川信行

subscriptionIdを指定して、使用料を更新する方法

await stripe.subscriptionItems.createUsageRecord(subscriptionItemId, {
  quantity,
  timestamp,
  action: 'set',
});
西川信行西川信行

顧客IDを指定して、次回の請求予定金額を取得する方法

const upcomingInvoice = await stripe.invoices.retrieveUpcoming({
  customer: customerId,
});
// 次回請求金額
const amountDue = upcomingInvoice.amount_due;
西川信行西川信行

顧客IDから、次回の請求予定日を取得する方法

const nextBillingDate = subscriptions.data.find(
  (subscriptionData) =>
    subscriptionData.status === 'active' || subscriptionData.status === 'trialing',
)?.current_period_end;