LoginSignup
4
1

Stripeを利用して、2回目以降の支払いに備えてクレジットカード情報の保存と再利用を行う方法

Posted at

ECサイトや予約サービスなど、顧客が複数回商品やサービスを注文するユースケースでは、「クレジットカード情報を保存する仕組み」が重要です。2回目以降の注文で、クレジットカード情報を入力する必要がないようにシステムを構築することで、顧客が注文フローの途中で離脱する「カゴ落ち」のリスクを下げることができます。

スクリーンショット 2024-02-07 14.00.13.png

Stripeによる「EC サイトの決済に関する調査レポート」では、「注文・決済フローが3分以上かかると、49%のユーザーが注文を諦める」との結果がでています。

スクリーンショット 2024-02-07 13.57.05.png
https://stripe.com/jp/guides/state-of-asia-pacific-checkouts-2022#konbaziyonlu-niguan-suruzhan-lue

この記事では、Stripeを利用してクレジットカード決済を提供する場合の、「カード情報の保存方法」と「利用方法」について紹介します。

Stripeで、クレジットカード情報を2回目以降の注文に利用する方法

Stripeを利用している場合、Stripe上にカード情報を保存することができます。2回目以降の注文では、Stripeアカウント内にPayment Methodとして保存されたカード情報を利用することで、顧客にクレジットカード情報の入力を求めることなく、注文・決済を完了させることができます。

Stripeを利用した場合の購入フロー

StripeのPayment Methodを利用する場合、1回目の注文と2回目以降の注文でフローが少し変わります。

1回目の購入: 支払い情報などを、ECサイトとStripeアカウントに登録する

1回目の購入では、ユーザーはECサイトのログインアカウントを作る必要があります。作成したユーザー情報とStripe内に保存される顧客情報(Customer)や支払い情報(Payment Method)を紐づけることで、2回目以降の注文フローを効率化できます。

  • ECサイトのユーザーアカウントを作成する
  • Stripeの決済フォーム(ElementsまたはCheckout)を利用して、支払い情報を送信する
  • ECサイトのユーザー情報と、Stripeに送信された支払い情報(Payment Method ID)や、作成された顧客情報(Customer ID)を紐づける

「注文完了後にユーザーアカウントを作成する仕組み」を用意する場合は、StripeのCustomer IDやPayment Method IDをどのようにシステム内に保存し、紐付けさせるかを検討する必要があります。

2回目以降の購入: 保存された支払い情報を利用して注文する

2回目以降の注文では、「登録済みの支払い情報」を利用して決済が行えます。ただし個人カードと家族カード・法人用カードのように、商品によって利用するカードを変更するケースや、何かしらの理由(不正利用の被害にあった・紛失したなど)で登録済みのカードが利用できなくなっている場合に備えて、「登録済みの支払い情報以外での決済」もサポートするようにしましょう。

  • ECサイトのユーザーアカウントでログインする
  • 登録済みの支払い情報を一覧表示し、利用する決済情報を選択する
  • (新しい支払い手段を利用する場合は、1回目の購入の流れに入る)
  • 選択した支払い情報を利用して決済を行う

Stripe APIで見る、支払い情報の保存方法

ここからは、Stripeを使ってどのように支払い情報の保存を行うかをみていきます。Stripe APIをcURLで呼び出すフローにて紹介しますが、実際の組み込みでは言語ごとに用意されているSDKを利用することをお勧めします。

1回目の注文フローを構築する

まずは「支払い情報を保存する」ための、1回目の注文フローを作りましょう。

Step:1 Stripeに顧客( Customer )データを作成する

Stripe上でクレジットカードなどの支払い情報を保存・利用するには、CustomerデータをStripe上に作成する必要があります。

$ curl -X POST "https://api.stripe.com/v1/customers" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "email"="test@stirpe.com"

Customerデータを作成すると、IDを含むデータがレスポンスで取得できます。このIDをECサイト側に作成されているユーザーデータと紐づけましょう。

{
   "id":"cus_P5SHoCX0um98G4",
   "object":"customer",
   ...
}

Step2: 支払いを処理するためのデータ( PaymentIntent )を作成する

Stripeの埋め込み決済フォーム(Stripe Elements)では、支払いを処理するためにPayment Intentを作成します。Payment Intents APIへのリクエストBodyには、先ほど作成したCustomerデータや注文金額・通貨などの情報を渡しましょう。

$ curl -X POST "https://api.stripe.com/v1/payment_intents" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "amount"=1099 \
  -d "currency"="jpy" \
  -d "customer"="cus_P5SHoCX0um98G4" \
  -d "payment_method_options[card][setup_future_usage]"="on_session"

「支払い情報を保存するCustomerのID」と「次回以降の決済にもこの支払い情報を利用すること」を示すsetup_future_usageパラメータを渡すことで、入力された支払い情報をStripe上に保存できるようにします。

2回目以降の注文をシステム側で処理する場合
IoT機器などを利用した自動注文など、「顧客が注文処理を行わずに決済を処理する」ケースがある場合は、setup-future-usageの値をoff_sessionに変更します。

$ curl -X POST "https://api.stripe.com/v1/payment_intents" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "amount"=1099 \
  -d "currency"="jpy" \
  -d "customer"="cus_P5SHoCX0um98G4" \
-  -d "payment_method_options[card][setup_future_usage]"="on_session"
+  -d "payment_method_options[card][setup_future_usage]"="off_session"
[Tips] Stripe Checkoutを利用する場合は、Checkout Session経由でPayment Intentを作成する

リダイレクト型決済フォームのStripe Checkoutを利用する場合、Payment IntentはCheckout Session経由で作成します。こちらの場合も、「次回以降の決済に支払い情報を利用すること」を示すため、setup-future-usageをAPIのリクエストBodyに設定しましょう。

$ curl -X POST "https://api.stripe.com/v1/checkout" \
  -u  'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d  "customer"="cus_P5SHoCX0um98G4" \
  -d  "mode"="payment" \
  -d  "success_url"="https://example.com"\
  -d  "line_items[0][price]"="price_1OFp2iL6R1kGwUF4Cbe0C5fw"\
  -d  "line_items[0][quantity]"="1"\
  -d  "payment_method_options[card][setup_future_usage]"="on_session"
[Tips] 利用する決済手段をプログラムで制御したい場合

StripeのAPIをversion: 2023-08-16より新しいもので構築している場合、ダッシュボード上で利用する決済手段を設定できます。今回のサンプルコードでは、この機能を利用する前提になっていますが、従来通りプログラムコードで決済手段を制御するには、payment_method_typesパラメータをAPIリクエストに追加しましょう。

Payment Intens API

$ curl -X POST "https://api.stripe.com/v1/payment_intents" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "amount"=1099 \
  -d "currency"="jpy" \
  -d "customer"="cus_P5SHoCX0um98G4" \
  -d "payment_method_options[card][setup_future_usage]"="on_session" \
+  -d "payment_method_types[0]"="card" \
+  -d "payment_method_types[1]"="link"

Checkout Sessions API

$ curl -X POST "https://api.stripe.com/v1/payment_intents" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "customer"="cus_P5SHoCX0um98G4" \
  -d  "mode"="payment" \
  -d  "success_url"="https://example.com"\
  -d  "line_items[0][price]"="price_1OFp2iL6R1kGwUF4Cbe0C5fw"\
  -d  "line_items[0][quantity]"="1"\
  -d  "payment_intent_data[setup_future_usage]"="on_session" \
+  -d "payment_method_types[0]"="card" \
+  -d "payment_method_types[1]"="link"

Step3: アプリケーションに決済フォームを埋め込む

作成したPayment IntentまたはCheckout Sessionsの情報を使って、注文フローに決済フォームを組み込みましょう。Payment Intentsでは、APIレスポンスに含まれるClient secretを決済フォーム生成します。Checkout Sessionでは、APIレスポンスにセッションURLが含まれていますので、そのURLにリダイレクトすることで実装できます。

Payment Intentsを利用する場合、まずは決済フォームを表示するHTMLを用意しましょう。

<head>
  <title>Checkout</title>
  <script src="https://js.stripe.com/v3/"></script>
</head>
<body>
  <form id="payment-form">
    <div id="link-authentication-element"></div>
    <div id="payment-element"></div>
    <button type="submit">注文する</button>
  </form>
</div>

続いて、決済フォームを生成するためのJavaScriptコードを追加します。Stripe APIを利用してPayment Intentを生成するAPIを事前に用意して、fetchでアプリケーションから呼び出すようにしましょう。

<script>
document.addEventListener('DOMContentLoaded', async () => {

  const stripe = Stripe("pk_から始まるStripeの公開可能APIキーを入力する");

  const {
    clientSecret
  } = await fetch("Payment Intentsを生成するAPIのURL", {
    method: "Payment Intentsを生成するAPIのメソッド"
  }).then(r => r.json());
});
</script>

APIレスポンスのClient secretを利用して、Stripe Elementsをセットアップします。

  const {
    clientSecret
  } = await fetch("Payment Intentsを生成するAPIのURL", {
    method: "Payment Intentsを生成するAPIのメソッド"
  }).then(r => r.json());

 
+  const elements = stripe.elements({
+    clientSecret: "${clientSecret}",
+    appearance: {
+        theme: "stripe"
+    }
+  });
});

その後、elements.create()を利用して決済フォーム要素を生成しましょう。 defaultValuesを利用することで、ECアプリケーションなどが事前に収集・保存している顧客情報を初期値として渡すことができます。顧客が入力する情報の量を減らすことで、よりカゴ落ちリスクを減らしたり、コンバージョン率を高めたりすることが期待できます。

  const elements = stripe.elements({
    clientSecret: "${paymentIntent.client_secret}",
    appearance: {
        theme: "stripe"
    }
  });
+  const linkAuthenticationElement = elements.create(
+    "linkAuthentication",
+    {
+      defaultValues: {
+        email: "stripe@exampe.com"
+      }
+    }
+  );
+  linkAuthenticationElement.mount("#link-authentication-element");
+
+  // paymentのmount
+  const paymentElement = element.create("payment",{
+    layout: "accordion",
+    defaultValues: {
+      billingDetails: {
+        email: "stripe@example.com",
+        name: "Taro Yamada",
+        phone: "09012345678",
+        address: {
+          line1: "東京都千代田区丸の内1丁目",
+          line2: "",
+          city: "渋谷",
+          state: "東京都",
+          postal_code: "100-0005",
+          country: "JP"
+        }
+      }
+    }
+  });
+  paymentElement.mount("#payment-element");
});
</script>

最後に決済を完了させる処理を追加しましょう。

  paymentElement.mount("payment-element");
+  const form = document.getElementById('payment-form');
+  let submitted = false;
+  form.addEventListener('submit', async (e) => {
+    e.preventDefault();
+
+    // Disable double submission of the form
+    if(submitted) { return; }
+    submitted = true;
+    form.querySelector('button').disabled = true;
+    const {error: stripeError} = await stripe.confirmPayment({
+      elements,
+      confirmParams: {
+        return_url: `${window.location.origin}/index.html`,
+      }
+    });
+
+    if (stripeError) {
+      alert(stripeError.message);
+
+      // reenable the form.
+      submitted = false;
+      form.querySelector('button').disabled = false;
+      return;
+    }
+  });

});
</script>

これでテストのカード情報を入力するためのUIが用意できました。

スクリーンショット 2024-02-07 14.20.06.png

Step4: テストのクレジットカード情報をフォームに入力する

実際にテストのカード情報を送信してみましょう。Stripeのテスト環境では、Stripeが用意する「テスト用の支払い情報」を利用して決済を試みることができます。

  • カード番号: 4242 4242 4242 4242など、テストケースに応じたカード番号
  • 有効な将来の日付 (12/34 など)
  • 任意の 3 桁 (American Express カードの場合は 4 桁) のセキュリティーコード

の3つを利用して、決済処理を完了させましょう。

スクリーンショット 2023-12-04 17.28.09.png

決済に成功すると、Stripeダッシュボードの支払いページに成功した決済として情報が表示されます。

スクリーンショット 2023-12-04 17.31.44.png

支払いの詳細情報ページでは、「この支払いにより、今後の on-session の支払いに pm_xxx が設定されました」とのテキストが表示されています。このメッセージがある場合、支払いに利用したカード情報などが、次回以降のon-session(顧客が注文操作を自ら行うフロー)で再利用できます。

スクリーンショット 2023-12-04 17.31.09.png

Step5: 支払い情報を、顧客のデフォルト決済手段に設定する

次回以降の支払いに利用できる決済手段の登録が完了しました。Stripeでは「この顧客の、デフォルトで利用する決済手段」を設定することもできます。決済手段は、pm_から始まるPayment MethodオブジェクトとしてStripeに保存されます。このPayment Methodデータを、Customerオブジェクトの「デフォルトの支払い手段」として登録させましょう。

登録は、Customerオブジェクトのinvoice_settings.default_payment_methodに設定します。顧客情報を更新するAPIを利用して更新できます。

$ curl -X POST "https://api.stripe.com/v1/customers/{customer}" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "invoice_settings[default_payment_method]"="pm_1OHIFoEzgtKktpOykZPfaUAD"

Customerデータを取得した際に、invoice_settings[default_payment_method]pm_から始まるIDが設定されていれば、設定完了です。

{
  "id":"cus_P5SHoCX0um98G4",
  "object":"customer",
  ...
  "invoice_settings":{
    "custom_fields":null,
    "default_payment_method":"pm_1OHIFoEzgtKktpOykZPfaUAD",
  },
  ...
}

デフォルトの支払い方法に設定する処理は、Webhookで

アプリケーションに組み込む際は、 Stripe Webhookと連携したAPIでこの処理を実行します。Webhook経由で実装することで、「支払いに成功した場合のみ設定する」のような条件付けがしやすくなります。また、決済処理を行うコードの中ではなく、専用のWebhook APIを用意することにより、「アプリケーションコードをよりシンプルにすること」や、「サブスクリプションや請求書・見積書などに対応した場合にも、この処理を追加作業なしで利用できる」などのメリットもあります。

決済が成功した時のみ処理を行う場合は、payment_intent.succeededイベントを利用しましょう。

https://stripe.com/docs/webhooks?locale=ja-JP

これで初めて注文した顧客が、2回目以降でも同じ支払い手段を利用できるようにする準備ができました。

2回目以降の注文フローを構築する

今後は保存した支払い手段を利用して注文するフローを作りましょう。2回目以降は初回購入で登録されたPaymentMethodを使用して、決済フローを作成します。

Step0: ログインユーザー情報などから、StripeのCustomer情報を取得する

2回目以降のリピート注文を処理するには、以前の注文時に作成されたCustomerデータが必要です。そのため、ECサイトなどのユーザーDB情報を利用して、StripeのCustomerデータのIDを取得しましょう。

メールアドレスをキーにして情報を取得したい場合は、StripeのSearch APIを利用することもできます。ただしSearch APIは「1 秒あたり最大 20 件の読み取り操作」と通常のAPIよりも厳しいレート制限が設けられていることに注意が必要です。

curl -G https://api.stripe.com/v1/customers/search \
  -u "REPLACE_WITH_YOUR_SECRET_KEY:" \
  --data-urlencode query="email:'stripe@example.com'"

cus_から始まるCustomer IDを取得する」ことが目的ですので、ECサイト側のユーザーDBやmetadataなどに保存する形でも対応できます。

Step1: 登録済みのPaymentMethodを取得する

取得したCustomerのIDを利用して、デフォルトの支払い手段として設定したPayment Methodを取得しましょう。ここではCustomer IDを利用してCustomerデータを取得します。すでにSearch APIなどを利用してデータを取得している場合には、API呼び出しをスキップできます。

$ curl "https://api.stripe.com/v1/customers/cus_P5SHoCX0um98G4" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY': 

APIレスポンスに含まれるinvoice_settings.default_payment_mehothodが、デフォルトの支払い手段です。pm_から始まるPayment Method IDを取得しましょう。

{
   "id":"cus_P5SHoCX0um98G4",
   "object":"customer",
   ...
   "invoice_settings":{
     "default_payment_method":"pm_1OHIFoEzgtKktpOykZPfaUAD",
   },
    ...
}

Step2: 支払い手段とCustomerを指定してPaymentIntentを作成する

登録済みの支払い手段を利用する場合でも、決済フローを開始するにはPayment Intentを作成します。今回はPaymentMethodとCustomerそれぞれのIDもリクエストパラメータに設定しましょう。

$ curl -X POST "https://api.stripe.com/v1/payment_intents" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "amount"=1099 \
  -d "currency"="jpy" \
  -d "payment_method"="pm_1OHIFoEzgtKktpOykZPfaUAD" \
  -d "customer"="cus_P5SHoCX0um98G4" \
  -d "expand[]"="payment_method"

APIのレスポンスに含まれるPayment Intentは、statusrequires_confirmationになっています。これは「この支払い手段を利用して決済を行うこと」を顧客に確認する必要があることを示しています。

{
  "id":"pi_3OHIT3EzgtKktpOy0hGf06Qy",
  "object":"payment_intent",
 
 "client_secret":"pi_3OHIT3EzgtKktpOy0hGf06Qy_secret_ONJxi884ITsDcMBL1TCmSedzt",
  "payment_method":"pm_1OHIFoEzgtKktpOykZPfaUAD",
  ...
  "status":"requires_confirmation",
  ...
}

Step3: 確認画面に、利用するカード情報を表示する

2回目以降の注文でも、最終確認画面などで「どのカードを利用して決済を行うか」を表示しましょう。ユーザーが誤ったカードを利用して注文を行うことを予防し、返金処理などのサポート問い合わせを減少できます。

PaymentIntent APIやPayment Method APIから取得したカード情報では、last4brandなどの値を利用してユーザーに表示する情報を取得できます。

<ul>
    <li>カードブランド: ${paymentMethod.card?.brand}</li>
    <li>カード番号(下4桁): ${paymentMethod.card?.last4}</li>
    <li>有効期限: ${paymentMethod.card?.exp_month} / ${paymentMethod.card?.exp_year}</li>
</ul>

スクリーンショット 2024-02-07 14.36.33.png

Step4: Payment Intent作成時に指定したカードで、決済を実施する

設定したカード情報を利用して決済を行う場合、Stripe.jsのconfirmCardPaymentを利用します。

stripe
  .confirmCardPayment('${paymentIntent.client_secret}')
  .then(function(result) {
    // Handle result.error or result.paymentIntent
  });

confirmCardPayment利用時の、3Dセキュア認証の動き

2回目以降の支払いでも、3Dセキュアを利用した追加認証を要求できます。
confirmCardPaymentを実行した際に、3Dセキュアの認証フローを表示させる必要が出た場合、Stripe.jsが自動的に認証のためのモーダル画面を立ち上げます。

スクリーンショット 2024-02-07 14.50.20.png

3Dセキュア認証を常に要求するテスト用のカード番号(4000002760003184)などを利用することで、この画面の表示やフローの動作確認を行うことができます。

カード情報を変更して決済を行う場合

Payment Intent作成時にPayment Methodを指定した場合でも、別のクレジットカードや支払い手段を利用して決済を行うことができます。
その場合、1回目の注文フローを構築するで紹介した方法と同様に、PaymentElementを利用して決済手段を選択・入力するUIを表示します。

<form id="payment-form">
    <div id="payment-element"></div>
    <button type="submit">注文する</button>
</form>
<script>
    const stripe = Stripe("${c.env.STRIPE_PUBLISHABLE_KEY}")
    const elements = stripe.elements({
        clientSecret: "${paymentIntent.client_secret}",
        appearance: {
            theme: "stripe"
        }
    })
    const paymentElement = elements.create("payment", {
        layout: 'accordion',
    })
    paymentElement.mount("#payment-element")
</script>

その後confirmPaymentを利用して決済を完了させましょう。

<form id="payment-form">
    <div id="payment-element"></div>
    <button type="submit">注文する</button>
</form>
<script>
    const stripe = Stripe("${c.env.STRIPE_PUBLISHABLE_KEY}")
    const elements = stripe.elements({
        clientSecret: "${paymentIntent.client_secret}",
        appearance: {
            theme: "stripe"
        }
    })
    const paymentElement = elements.create("payment", {
        layout: 'accordion',
    })
    paymentElement.mount("#payment-element")
+    const form = document.getElementById("payment-form")
+    form.addEventListener("submit", async e => {
+        e.preventDefault()
+        const result = await stripe.confirmPayment({
+            elements,
+            redirect: "if_required",
+        })
+        console.log(result)
+    })
</script>

confirmCardPayment('Payment_Intent.client_secret')の代わりにconfirmPaymentを実行することで、ユーザーが入力した支払い情報を利用した決済を行えます。

スクリーンショット 2024-02-07 15.00.28.png

おわりに

Stripeを利用する場合、Payment Intent APIStripe.jsを利用することで、一度ユーザーが決済に利用したカード情報を2回目以降の決済に備えて保存できます。2回目以降の支払いでは、StripeにPayment Methodとして保存されている決済手段のIDをPayment IntentやconfirmCardPaymentの引数に設定することで、ユーザーがカードを入力する手間を省くことが可能です。
また、3Dセキュア認証の処理や、決済に利用するカード情報の一部(下4桁のカード番号やブランド名など)をユーザーに表示する処理などの実装にも簡単に対応できます。

この記事ではcurlとJavaScriptを利用したコードサンプルで手順を紹介しましたが、皆様がお使いの言語やフレームワークにて、この記事の内容を実践してみたレポート記事やブログ投稿を作成していただけると、幸いです。

参考記事

 

関連する開発ドキュメント記事

紹介したAPI

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1