Stripe Connect(express)でCheckoutを利用したデスティネーション支払い(買い切り・定期課金)を受け付ける


Stripe Connectを利用してオンライン決済を処理する場合、さまざまな方法を選ぶことができます。

今回は、Expressアカウントで、Stripe Checkoutを利用して、デスティネーション支払いを行うケースの実装について、簡単に紹介します。

前提条件の再確認

StripeのExpressアカウントを利用する

Stripe Connectでは、3種類の子アカウント(連結アカウント)を作成できます。

このうちExpressアカウントでは、「最小限の組み込み実装」で、「Stripeがホストする連結アカウント管理者向けのダッシュボードを提供」することができます。

そのため、最小限の実装でプラットフォームアプリを構築することができます。

アカウントタイプごとの詳細については、Stripeドキュメントに記載の表をご確認ください。

デスティネーション支払いを利用する

Stripe Connectを利用したサービスで顧客が決済を行う場合、その金額はStripe / プラットフォーム / 連結アカウントユーザーの3者に分配されます。

そしてその分配方法や、決済された金額がどのアカウントで処理されるかなどを、Stripeでは3種類から選ぶことができます。

Expressアカウント向けの決済方法として推奨されているのが、「デスティネーション支払い」です。

この支払い方法では、顧客が支払った金額はプラットフォーム側で処理されます。そして連結アカウント側に移動した後、プラットフォーム利用手数料とプラットフォームユーザーの売上に分割します。

どのアカウントタイプ・どの支払い方法が向いているかのアドバイスを受ける

もしどのタイプを設定すべきかの判断に悩んだ場合は、Dashboardで推奨事項を確認することができます。

設定画面に移動し、[推奨事項を表示]をクリックしましょう。

すると、Connectを有効化した際に入力した情報などをもとに、推奨の設定事項が表示されます。

もちろん実際に想定しているビジネスモデルと一致しないケースも存在しますが、ドキュメントを調べる際のスタート地点にも使えますので、ぜひお試しください。

Stripe Checkoutセッションの作成方法

Stripe Checkoutを利用する場合、APIを利用してセッションを開始します。この方法はConnectを利用する場合でも変わりなく、設定するパラメータを追加するだけで対応できます。

1回きりの支払いで、Connectのデスティネーション支払いに対応する

    const APPLICATION_FEE_PERCENT = 10;
  const price = await stripe.prices.retrieve(priceId);
  const applicationFeeAmount =
        (price.unit_amount || 0) * quantity * (APPLICATION_FEE_PERCENT / 100);
  const request = {
    success_url: 'http://localhost:3000/success?session_id={CHECKOUT_SESSION_ID}',
    cancel_url: 'http://localhost:3000/cancel',
    mode: 'payment',
    line_items: [
      {
        price: priceId,
        quantity,
      },
    ],
    payment_intent_data: {
      application_fee_amount: applicationFeeAmount,
      transfer_data: {
        destination: accountId,
      },
    }
  };
  const session = await stripe.checkout.sessions.create(request);

1回きりの支払いの場合、payment_intent_dataパラメタを追加します。
この中には、「プラットフォームが徴収する手数料金額」と「売上を送金する対象のアカウントID」の2つを指定する必要があります。

手数料を「商品代金の10%」と設定している場合、line_itemsに渡している料金データの金額と数量をもとに「合計金額」を出します。その金額をもとに手数料を設定することで、手数料金額を決定することができます。

定期課金で、Connectのデスティネーション支払いに対応する

続いて定期課金での実装方法です。

    const APPLICATION_FEE_PERCENT = 10;
  const price = await stripe.prices.retrieve(priceId);

  const request = {
    success_url: 'http://localhost:3000/success?session_id={CHECKOUT_SESSION_ID}',
    cancel_url: 'http://localhost:3000/cancel',
    mode: 'subscription',
    line_items: [
      {
        price: priceId,
        quantity,
      },
    ],
    subscription_data: {
      application_fee_percent: APPLICATION_FEE_PERCENT,
      transfer_data: {
        destination: accountId,
      },
    }
  };
  const session = await stripe.checkout.sessions.create(request);

定期課金の場合は、payment_intent_dataではなくsubscription_dataを設定します。
また、プラットフォーム手数料が合計金額ではなくパーセントに変わります。
そのため、合計金額の計算などを行う必要はなくなります。

応用篇: 1回きり・定期課金両方を処理する関数を実装する

2つのサンプルを見ると、payment_intent_data / mode / subscription_dataの3つをハンドルする必要があることがわかります。

また、システムによっては、連結アカウントではなくプラットフォームが直接販売を行うケースも考えられます。

これらのケースをまとめて処理するコードの例を最後に紹介します。

const stripe = require('stripe')('YOUR-API-KEY-HERE');

const APPLICATION_FEE_PERCENT = 10;

function createNewCheckoutSession(priceId, quantity, accountId) {
  const price = await stripe.prices.retrieve(priceId);
  const request = {
    success_url: 'http://localhost:3000',
    cancel_url: 'http://localhost:3000',
    customer_creation: price.recurring ? undefined : 'if_required',
    mode: price.recurring ? 'subscription' : 'payment',
    line_items: [
      {
        price: priceId,
        quantity,
      },
    ],
  };
  /**
   * If the connected account provided, should add the connect information
   **/
  if (accountId) {
    if (request.mode === 'payment') {
      /**
       * If the session is one time payment,
       * we should add the payment_intent_data.
       **/
      const applicationFeeAmount =
        (price.unit_amount || 0) * quantity * (APPLICATION_FEE_PERCENT / 100);
      request.payment_intent_data = {
        application_fee_amount: applicationFeeAmount,
        transfer_data: {
          destination: accountId,
        },
      };
    } else {
      /**
       * If the session is subscription,
       * we should add the subscription_data.
       **/
      request.subscription_data = {
        application_fee_percent: APPLICATION_FEE_PERCENT,
        transfer_data: {
          destination: accountId,
        },
      };
    }
  }
  const session = await stripe.checkout.sessions.create(request);
  return session;
}

ダイレクト支払いや支払いと送金を別途処理する場合などのケースを処理する場合や、段階的な料金の定期課金を処理する場合など、上記のコードではサポートできないケースも少なくありません。
そのため、実案件での実装では、想定していている料金・支払い体系全てでのテストを行うことをお勧めします。

参考ドキュメント

[PR] Stripe開発者向け情報をQiitaにて配信中!

2021年12月よりQiitaにて、Stripe開発者のためのブログ記事更新を開始しました。

  • [Stripe Updates]:開発者向けStripeアップデート紹介・解説
  • ユースケース別のStripe製品や実装サンプルの紹介
  • Stripeと外部サービス・OSSとの連携方法やTipsの紹介
  • 初心者向けのチュートリアル(予定)

など、Stripeを利用してオンラインビジネスを始める方法について随時更新してまいります。

-> Stripe Organizationsをフォローして最新情報をQiitaで受け取る