Stripeを導入するためにStripeのドキュメントを確認したけれどどこから何を始めていいのかわからなかったという人を対象にStripeを導入するために必要な組み込み方法を説明しています。Stripeでオンライン決済といってもプログラミングの得意な人向けの方法から全くプラグラミングがわからない人でもお手軽に導入できる方法まで幅広い方法が提供されています。その上、プログラミングを使って組み込む方法も一つではなくシチュエーションによって利用するAPIが異なるため混乱する人も多いと思いますのでぜひ参考にしてみてください。Stripeの料金体系や成り立ちなどの説明はなく組み込み方法を中心にStripeの基本的な仕組みを理解したい技術者向けの内容になっています。

アカウントの作成

Stripeを利用するためにはStripeのアカウントを作成する必要があります。テスト目的で利用する場合は名前、メールアドレスの入力とパスワードの設定で完了します。テストから本番へと移行する際には追加の情報が必要となりますがアカウントの作成は無料で行うことができます。

 Stripeのトップページ
Stripeのトップページ

画面中央にある”今すぐ始める”をクリックします。入力フォームが表示されてるので入力を行ってください。

アカウント作成画面
アカウント作成画面

アカウントの作成を行うと入力したメールアドレスに確認メールが届き、認証が完了するとダッシュボードが表示されます。ダッシュボードでは公開可能キーとシークレットキーを確認することができます。

ダッシュボードの表示
ダッシュボードの表示

3つの支払いの実装方法

Stripeのドキュメントを確認するとオンラインでの支払いの組み込み方法にはStripe Payment Links, Stripe Checkout, Stripe Elementの3つの方法が提供されていることがわかります。

3つの組み込み方法
3つの組み込み方法

3つの組み込み方法には以下の違いがあります。Stripeが推奨されているのはStripe Checkoutです。

  • Stripe Payment Links・・・Stripe の支払いページへのリンクを埋め込むか共有することで、Web サイトなしで支払いを受け付けます。Stripeサイトのみで支払いの処理を完結させることができます。支払いページのリンクをクリックすることで支払い画面が表示され支払いを完了させることができます。
  • Stripe Checkout・・・Webサイトが必要となり支払いについてはStripe がオンラインで提供するチェックアウトページで行います。Stripeのページへは自サイトからリダイレクトを行なってアクセスします。リダイレクト前のページ、支払い処理後のページはすべて自分で作成する必要があります。顧客は支払い時のみStirpeサイトを利用することになります。
  • Stripe Elements・・・カスタマイズ可能な UI コンポーネントを Web サイトまたはモバイルアプリ (iOS または Android) に組み込んで顧客から支払い情報を入力してもらいます。Stripe Payment Links、Stripe CheckoutのようにStripeがオンラインで提供するページを利用することはありません。顧客は一つのWebサイト上で支払い処理を完了させることができます。

文章を読んで3つの違いが理解できても技術者であれば実際に動作を見てみたいはずです。本文書では最も組み込みが簡単なStripe Payment Linksからカスタマイズが可能なStripe Elementsまで3つの方法すべての説明を行っています。読者の人はカスタマイズ可能な支払いの組み込む方法が知りたいという人がほとんどだと思いますがすべての組み込み方法を理解していくことで段階的にStripeの仕組み、用語やダッシュボードの見方/利用方法を理解することができます。

Stripe Payment Links

Stripeのダッシュボード上で支払いページのリンクを作成することができます。作成したリンクをクリックすると支払い画面が表示されるのでその画面から顧客は支払いを行うことができます。自分でプログラミングのコードを作成する必要もない上、自Webサイトを持っている必要もありません。メールやLine、Slackにリンクを貼って支払いを行ってもらうこともも可能です。間違いなく一番手軽で簡単です。

Stripe Payment Linksの設定

ダッシュボードの上部にある”支払い”タブをクリックして左側のサイドバーの支払いのメニューから”支払いのリンク”をクリックしてください。”支払いのリンク”をクリックすると画面に”+支払いリンクを作成”ボタンが表示されるのでクリックしてください。

支払いのリンク
支払いのリンク

初めて支払いのリンクを作成する場合はアカウント名の設定を行う必要があります。ここではテストというアカウント名を設定しています。

アカウント名の設定
アカウント名の設定

支払いリンクから表示される支払いページで販売する商品の登録を行います。商品の入力項目から”+新しい商品を追加”を選択します。

商品の追加
商品の追加

画面上には新しい商品を追加するためのポップアップが表示されるので販売したい商品の名前と価格、あれば画像の設定を行ってください。その他に継続と一括が選択できますが継続ではNeflixのサブスクリプションのように定期的に支払いを行いたい時に利用することができます。ここでは一括を設定します。

商品の追加設定
商品の追加設定

商品情報を入力後に”商品を追加”ボタンを行うとプレビューに表示される画面も更新されます。これが顧客が支払い時に閲覧するページです。

商品追加後のプレビューの更新
商品追加後のプレビューの更新

”確認ページを表示する”を選択するとプレビューが確認ページに変わります。支払いが完了した時に顧客が閲覧するページです。デフォルトのメッセージをカスタムメッセージに置き換えることができます。また確認ページではなく支払い完了後に確認ページを表示せず自分のサイトなどにリダイレクトさせることもできます。

支払い後の確認ページの確認
支払い後の確認ページの確認

設定が完了したら右上にある”リンクを作成”ボタンをクリックします。

支払いのリンクが表示されます。リンクを使用する際のヒントも記述されているので参考にすることができます。

リンクの作成後の画面
リンクの作成後の画面

リンクをコピーして手元のブラウザを使って実際にページから支払いができるか確認します。

支払いページの表示
支払いページの表示

カード情報にはテスト用の番号(4242 4242 4242 4242)を入力します。月と年には将来の日付を入れてCVCには任意の3桁の番号を入れます。すべての情報を入力するとボタンがクリックできるようになります。情報を保存して時間の支払いを迅速化するをクリックすると電話番号を入力する項目が表示され携帯に情報が送信されます。次回の支払いを迅速に行うために情報を保存することができます。

フォームを入力
フォームを入力

支払いに必要なクレジットカード情報を入力後に”¥9,500支払う”をクリックするとプレビューで確認した確認ページが表示されます。これで顧客がクレジットカードを利用してトートバッグを購入したことになります。

確認ページの表示
確認ページの表示

ダッシュボードにアクセスしてダッシュボード上ではどのような情報が追加されているのか確認します。支払いタブをクリックすると支払いを行った情報が表示されます。成功と表示されており支払いが問題なく完了したことがわかります。黒塗りした部分にはメールアドレスが表示されています。

支払い一覧の確認
支払い一覧の確認

支払いの行の右端にある”・・・”をクリックすると返金や領収書を送信することもできます。

返金や領収書送信のメニュー
返金や領収書送信のメニュー

領収書を送信をクリックすると登録されているメールアドレス宛に購入した商品に関する領収書も送信されます。

次に顧客タブをクリックすると購入者の情報が表示されます。

顧客情報
顧客情報

商品タグを開くと支払いリンクで作成した商品も表示されます。

商品情報の確認
商品情報の確認

このようにStripe Payment Linksを利用するとコードを記述することなく簡単に支払いページの作成を行うことができます。支払いページから支払いが完了した情報もダッシュボードから確認することができダッシュボードでは返金などの処理も行うことができます。簡単ですね。

Stripe Checkout

ここではStripe Checkoutを使って支払いページを作成します。Stripeが推奨している方法でStripe Payment Linksとは異なりコードの記述、自サイトも必要となりますが支払いページについてはStripeがオンライン上に提供しているページを利用するため最小限のコーディングで支払い機能をWebサイトに組み込むことができます。

Stripeは特にバックエンドについてはさまざまな言語用の組み込みのサンプルコードを提供しています。本文書ではバックエンドにPHPとNodeを利用して動作確認を行います。言語が異なるので記述するコードは異なりますが処理の流れは同じです。購入する商品の情報と支払い処理後に表示させたいURLを設定し、Stripe上の支払いページへのリダイレクトを行っています。

Stripe Checkoutの設定

Stripeのドキュメントサイト上にStripe Checkoutの組み込みのコードサンプルが公開されているのでサンプルコードを利用して動作確認を行います。

Stripe Checkoutページ
Stripe Checkoutページ

サンプルコードがStripeのドキュメント上で提供されているのはフロントエンドではHTML, React, Next.jsで、バックエンドではNode, Ruby, Python, .Net, Java, Goです。フロントエンドについてはJavaScriptのStripe.jsが提供されているのでReact以外が利用できないわけではありません。最初にフロントエンドHTML, バックエンドPHPの環境で動作確認を行い、その後にバックエンドをNodeに変更します。PHP, Nodeどちらか好きな環境を選択して読んでください。内容は同じなので利用する予定のない片方をスキップしてください。

PHPの場合

PHPを利用するためにはPHPを実行できる環境が必要となります。本文書ではmacOSのBig Surを利用しておりphp -vコマンドを実行するとバージョンが表示されるのでPHPを利用することができます。


 % php -v
PHP 8.0.7 (cli) (built: Jun  4 2021 03:56:55) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.7, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.7, Copyright (c), by Zend Technologies

ダッシュボードへログインした状態で構築済みのCheckoutページの上部でフロントエンドHTML、バックエンドPHPを選んでスクロールを行い、ラインアイテムを定義するでテスト商品を選択するか、”新しい商品を作成する”から販売を行う商品追加してを選択してください。ここでは支払いリンクの時に作成したトートバッグを選択しています。商品を選択すると右側に表示されている情報が更新されます。priceに設定されているのは商品の価格IDです。ダッシュボードからも確認することができます。

事前にダッシュボード上に商品が登録済みがどうかでラインアイテムを定義するの表示がかわります。何も商品が登録されていない場合は登録を行ってください。
fukidashi
商品を選択する
商品を選択する

さらにスクロールするとAlipay, WeChat Payなどカード以外の支払い方法を選択できます。支払い方法の変更をしない場合は再度上部にスクロールして”アプリ全体をダウンロード”をクリックしてください。

アプリ全体のダウンロードをクリックするとstripe-checkout-sampmel-code.zipがダウンロードできるので解凍を行ってください。解凍後に作成されるフォルダに移動します。

composer.jsonファイルに記述されているstripeのバージョンが古いのでcomposer installを実行するのではなくcomposerコマンドでstripe/stripe-phpをインストールします。


 % composer require stripe/stripe-php

インストール完了後、php -Sコマンドで開発サーバを起動します。ポート番号は4242に設定しています。


 % php -S localhost:4242
[Sun Oct 10 11:09:23 2021] PHP 8.0.7 Development Server (http://localhost:4242) started

ブラウザからlocalhost:4242に接続すると404 Not Foundが表示されるのでpublicフォルダにあるcheckout.htmlファイルを指定(localhost:4242/public/checkout.html)します。ブラウザ上に下記の画面が表示されます。

checkoutボタンの表示
checkoutボタンの表示

表示されている金額やCheckoutの文字はcheckout.htmlを更新することで変更することができます。表示されている商品/金額と支払いページで表示される商品とは異なります。

Checkoutボタンをクリックしてください。支払いの画面が表示されます。表示されるのはラインアイテムで定義した商品です。支払いページは起動したPHPのサーバ上ではなくStripeが提供するページです。URLはcheckout.stripe.com/….です。Checkoutボタンをクリックするとリダイレクトされます。

支払いページの表示
支払いページの表示

コードの確認

支払いページが表示されるまでの流れをコードで確認していきます。

checkout.htmlファイルのformタグのactionでcreate-checkout-session.phpが指定されています。CheckoutボタンをクリックするとそのURL(/create-checkout-session.php)にPOSTリクエストが送信されます。


<form action="/create-checkout-session.php" method="POST">
  <button type="submit" id="checkout-button">Checkout</button>
</form>

create-checkout-session.phpファイルの中では支払いが成功した時と失敗した時のURLを設定する必要があります。支払いのページに表示されている商品の情報もline_itemsとしてここで設定を行います。checkout.htmlで記述していた商品名と価格とは異なるので本番では同じ情報にする必要があります。\Stripe\Checkout\Sessionのcreateメソッドにはさまざまなパラメータを設定することができここでは商品の情報と支払い情報が登録されています。createメソッドで作成されるcheckout_sessionのurlにリダイレクトされています。リダイレクト先が先ほど確認したStripe上の支払いページです。


require 'vendor/autoload.php';
\Stripe\Stripe::setApiKey('YOUR_KEY');

header('Content-Type: application/json');

$YOUR_DOMAIN = 'http://localhost:4242/public';

$checkout_session = \Stripe\Checkout\Session::create([
  'line_items' => [[
    'price' => 'price_1JifULC94s8gJpHeALjTjMer',
    'quantity' => 1,
  ]],
  'payment_method_types' => [
    'card',
  ],
  'mode' => 'payment',
  'success_url' => $YOUR_DOMAIN . '/success.html',
  'cancel_url' => $YOUR_DOMAIN . '/cancel.html',
]);

header("HTTP/1.1 303 See Other");
header("Location: " . $checkout_session->url);

表示されている支払いページでテスト用のカード番号(4242 4242 4242 4242)を入力して支払うボタンをクリックするとcreate-checkout-session.phpファイルで設定したsuccess.htmlにリダイレクトされ下記の画面が表示されます。

成功ページの表示
成功ページの表示

その他のテスト用のカード番号を利用して動作を確認します。

支払いに認証が必要な場合には、”4000 0025 0000 3155”を利用します。この場合は認証が必要なので下記の画面が表示されます。

追加の認証が必要な場合
追加の認証が必要な場合

支払いが拒否される場合のカード番号は”4000 0000 0000 9995”を利用します。エラーメッセージが支払い画面に表示されます。

支払いが拒否された場合のエラー
支払いが拒否された場合のエラー

Stripe Checkoutを利用した場合はStripeが提供する支払いページ以外の支払いページ前のページ、支払い後の成功ページ、失敗ページは自サイトで準備する必要があることがわかりました。

Nodeの場合

バックエンドにNodeを利用している場合の動作確認を行います。Nodeを利用して動作確認を行うためにはNodeがインストールされている必要があります。node -vコマンドでバージョンが表示される場合は利用することができます。バージョンが表示されない場合はNodeのインストールを行ってください。


 % node -v 
v14.7.

ダッシュボードへログインした状態で構築済みのCheckoutページの上部でフロントエンドでHTML、バックエンドでNodeを選んでスクロールを行い、ラインアイテムを定義するでテスト商品を選択するか、”新しい商品を作成する”から販売を行う商品追加してを選択してください。ここでは支払いリンクの時に作成したトートバッグを選択しています。商品を選択すると右側に表示されている情報が更新されます。販売を行う商品を選択してください。ここでは支払いリンクの時に作成したトートバッグを選択しています。

事前にダッシュボード上に商品が登録済みがどうかでラインアイテムを定義するの表示がかわります。何も商品が登録されていない場合は登録を行ってください。
fukidashi
商品を選択する
商品を選択する

さらにスクロールするとAlipya, WeChat Payなどカード以外の支払い方法を選択できます。支払い方法の変更をしない場合は再度上部にスクロールして”アプリ全体をダウンロード”をクリックしてください。

アプリ全体のダウンロードをクリックするとstripe-checkout-sampmel-code.zipがダウンロードできるので解凍を行ってください。解凍後に作成されるフォルダに移動します。

package.jsonファイルに記述されているstripeのバージョンが古いのでnpm installを実行するのではなくnpmコマンドでstripeインストールします。Nodeの場合はExpressサーバを利用しているのでexpressのライブラリのインストールも必要になります。


 % npm install --save express
 % npm install --save stripe

インストール完了後、npm startコマンドでExpressサーバを起動します。stripe-checkout-sampmel-codeフォルダ内にあるserver.jsファイルの中でポート番号が4242に設定されています。


 % npm start

> stripe-sample@1.0.0 start
> node server.js

Running on port 4242

ブラウザからlocalhost:4242に接続するとCannot GET /が表示されるのでcheckout.htmlファイル(localhost:4242/checkout.html)を指定します。ブラウザ上に下記の画面が表示されます。

Checkoutボタンの表示
Checkoutボタンの表示

表示されている金額Checkoutの文字はcheckout.htmlを更新することで変更することができます。表示されている商品/金額と支払いページで表示される商品とは異なります。

Checkoutボタンをクリックしてください。支払いの画面が表示されます。表示されるのはラインアイテムで定義した商品です。支払いページは起動したNodeのExpresサーバ上ではなくStripeが提供するページです。Checkoutボタンをクリックするとこのページにリダイレクトされます。

支払いページの表示
支払いページの表示

コードの確認

支払いページが表示されるまでの流れをコードで確認していきます。

checkout.htmlファイルのformタグのactionでcreate-checkout-sessionが指定されているのでCheckoutボタンをクリックするとそのURLにPOSTリクエストが送信されます。


<form action="/create-checkout-session" method="POST">
  <button type="submit" id="checkout-button">Checkout</button>
</form>

server.jsファイルの中で/create-checkout-sessionのルーティングの処理が記述されています。支払いのページに表示されている商品の情報もline_itemsとしてここで設定を行い支払いが成功した時と失敗した時のURLを設定する必要があります。stripe.checkout.sessions.createメソッドにはさまざまなパラメータを設定することができここでは商品の情報と支払い情報が登録されています。createメソッドで作成されるsessonのurlにリダイレクトされています。リダイレクト先が先ほど確認したStripe上の支払いページです。


const stripe = require('stripe')('YOUR_KEY');
const express = require('express');
const app = express();
app.use(express.static('public'));

const YOUR_DOMAIN = 'http://localhost:4242';

app.post('/create-checkout-session', async (req, res) => {
  const session = await stripe.checkout.sessions.create({
    line_items: [
      {
        price: 'price_1JifULC94s8gJpHeALjTjMer',
        quantity: 1,
      },
    ],
    payment_method_types: [
      'card',
    ],
    mode: 'payment',
    success_url: `${YOUR_DOMAIN}/success.html`,
    cancel_url: `${YOUR_DOMAIN}/cancel.html`,
  });

  res.redirect(303, session.url)
});

app.listen(4242, () => console.log('Running on port 4242'));

表示されている支払いページでテスト用のカード番号(4242 4242 4242 4242)を入力して支払うボタンをクリックするとserver.jsファイルで設定したsuccess.htmlにリダイレクトされ下記の画面が表示されます。

成功ページの表示
成功ページの表示

その他のテスト用のカード番号を利用して動作を確認します。

支払いに認証が必要な場合には、”4000 0025 0000 3155”を利用します。この場合は認証が必要なので下記の画面が表示されます。

追加の認証が必要な場合
追加の認証が必要な場合

支払いが拒否される場合のカード番号は”4000 0000 0000 9995”を利用します。エラーメッセージが支払い画面に表示されます。

支払いが拒否された場合のエラー
支払いが拒否された場合のエラー

Stripe Checkoutを利用した場合はStripeが提供する支払いページ以外の支払いページ前のページ、支払い後の成功ページ、失敗ページは自サイトで準備する必要があることがわかりました。

Stripe Elements

3つの支払い方法の設定の中でUIのカスタマイズが可能でStripeの支払いページにリダイレクトすることなく支払いページが作成できるのがStripe Elementsです。

Stripe Checkoutのコードと同様にStripe ElementsのコードについてもStripeのドキュメントサイトからサンプルをダウンロードすることができます。

Stripe Elementsの設定

Stripe Checkoutの場合は”構築済みのCheckoutページ”でしたがStripe Elementsの場合は”カスタムの決済フロー“を選択します。

カスタムの支払いフロー
カスタムの支払いフロー

Stipe Checkoutと同様にStripeのドキュメント上で提供されているはフロントエンドはHTML, React, Next.jsで、バックエンドはNode, Ruby, Python, .Net, Java, Goです。

PHPの場合

バックエンドをPHPを利用した場合から確認を行なっていきます。Stripe ElementsではWebだけではなくiOSやAndroidといったプラットフォームも選択することができます。

プラットフォームをWeb, フロントエンドをHTML, バックエンドをPHPに選択して”アプリ全体をダウンロード”でサンプルコードをダウンロードします。

カスタムの支払いフロー
カスタムの支払いフロー

“アプリ全体をダウンロード”をクリックするとstripe-checkout-sampmel-code.zipがダウンロードできるので解凍を行ってください。解凍後に作成されるフォルダに移動します。

composer.jsonファイルに記述されているstripeのバージョンが古いのでcomposer installを実行するのではなくcomposerコマンドでstripe/stripe-phpをインストールします。


 % composer require stripe/stripe-php

インストール完了後、php -Sコマンドで開発サーバを起動します。ポート番号は4242に設定しています。


 % php -S localhost:4242
[Sun Oct 10 13:20:23 2021] PHP 8.0.7 Development Server (http://localhost:4242) started

ブラウザからlocalhost:4242/public/checkout.htmlにアクセスを行います。Stripe Checkoutとは異なりcheckout.htmlにはカード番号と有効期限、CVCのみが入力できるシンプルな入力フォームが表示されます。

支払い画面の表示
支払い画面の表示

フロントエンド側の共通処理

Stripe Elementsではカスタマイズ可能なUIと説明されているのでcheckout.htmlファイルの中身を確認しますがカードに関するフォームは一切記述されていません。idにcard-elementが設定されているdiv要素にコメントでStripe.js injects the Card Elementを記述されておりコメントからこの場所にJavaScriptによってフォームが挿入されているということがわかります。


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Accept a card payment</title>
    <meta name="description" content="A demo of a card payment on Stripe" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" href="global.css" />
    <script src="https://js.stripe.com/v3/"></script>
    <script src="https://polyfill.io/v3/polyfill.min.js?version=3.52.1&features=fetch"></script>
    <script src="client.js" defer></script>
  </head>

  <body>
    <!-- Display a payment form -->
    <form id="payment-form">
      <div id="card-element"><!--Stripe.js injects the Card Element--></div>
      <button id="submit">
        <div class="spinner hidden" id="spinner"></div>
        <span id="button-text">Pay now</span>
      </button>
      <p id="card-error" role="alert"></p>
      <p class="result-message hidden">
        Payment succeeded, see the result in your
        <a href="" target="_blank">Stripe dashboard.</a> Refresh the page to pay again.
      </p>
    </form>
  </body>
</html>

polyfillを除いてStripe Checkoutではjs.script.com/v3/のみscriptタブで読み込んでいましたがStripe Elementsではclient.jsファイルが読み込まれています。client.jsファイルの中にフォーム、”Pay now”ボタンを押した時の処理が記述されているので中身を確認していきましょう。

ポイントの一つはcheckout.htmlファイルを開いた時にfetch関数を利用してバックエンドサーバにPOSTリクエストが送信されていることです。fetch関数を実行後の処理は少し長いですがこの中で行われていることを理解していないとStripe Elementを使いこなすことはできません。サンプルにも関わらず処理が多く大変なのかという印象をうけるかもしれませんが処理は複数のパートに分かれているので個別に見ていけば難しいことは行っていません。


fetch('/create.php', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(purchase),
})
  .then(function (result) {
    return result.json();
  })
  .then(function (data) {
    var elements = stripe.elements();

    var style = {
      base: {
        color: '#32325d',
        fontFamily: 'Arial, sans-serif',
        fontSmoothing: 'antialiased',
        fontSize: '16px',
        '::placeholder': {
          color: '#32325d',
        },
      },
      invalid: {
        fontFamily: 'Arial, sans-serif',
        color: '#fa755a',
        iconColor: '#fa755a',
      },
    };

    var card = elements.create('card', { style: style });
    // Stripe injects an iframe into the DOM
    card.mount('#card-element');

    card.on('change', function (event) {
      // Disable the Pay button if there are no card details in the Element
      document.querySelector('button').disabled = event.empty;
      document.querySelector('#card-error').textContent = event.error
        ? event.error.message
        : '';
    });

    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function (event) {
      event.preventDefault();
      // Complete payment when the submit button is clicked
      payWithCard(stripe, card, data.clientSecret);
    });
  });

// Calls stripe.confirmCardPayment
// If the card requires authentication Stripe shows a pop-up modal to
// prompt the user to enter authentication details without leaving your page.
var payWithCard = function (stripe, card, clientSecret) {
  loading(true);
  stripe
    .confirmCardPayment(clientSecret, {
      payment_method: {
        card: card,
      },
    })
    .then(function (result) {
      if (result.error) {
        // Show error to your customer
        showError(result.error.message);
      } else {
        // The payment succeeded!
        orderComplete(result.paymentIntent.id);
      }
    });
};

最初にfetch関数でcreate.phpに対してPOSTリクエストを送信しています。送信している中身は支払いを行うために購入する商品の情報です。

サーバ側の処理

create.phpファイルでは受け取った購入予定の商品の金額からpaymentIntentを作成します。file_get_contentsやjson_decodeを使ってPOSTリクエストのbodyに入った商品情報をJSONにして、calculateOrderAmount関数ではただ1400を戻しているだけです。本番では受け取った商品情報からデータベースなどにアクセスを行い正しい金額を取得するといった処理が必要になります。


require 'vendor/autoload.php';

// This is your real test secret API key.
\Stripe\Stripe::setApiKey('YOUR KEY');


function calculateOrderAmount(array $items): int {
  return 1400;
}

header('Content-Type: application/json');

try {
  // retrieve JSON from POST body
  $json_str = file_get_contents('php://input');
  $json_obj = json_decode($json_str);

  $paymentIntent = \Stripe\PaymentIntent::create([
    'amount' => calculateOrderAmount($json_obj->items),
    'currency' => 'usd',
  ]);

  $output = [
    'clientSecret' => $paymentIntent->client_secret,
  ];

  echo json_encode($output);
} catch (Error $e) {
  http_response_code(500);
  echo json_encode(['error' => $e->getMessage()]);
}

PaymentIntentのcreateメソッドを利用して金額からpaymentIntentを作成します。作成したpaymentIntentからclient_secretを取り出して戻しています。paymentIntentを作成する際にはStripeのサーバにアクセスを行っています。paymentIntentを作成するとStripeのダッシュボードには支払い情報が登録されます。支払いを行っていないのでステータスは未完了なので支払い処理は完了していません。一番上の14ドルの行が作成された行です。1400なので1400ドルと思うかもしれませんがドルが単位ではなく最小単位のペニーを基準にしています。

登録された支払い情報
登録された支払い情報
paymentIntentには支払いの金額、支払い方法、支払いステータスなど支払いに関するさまざまな情報を持っているオブジェクトです。client.jsに戻されるclient_secretはカード情報を入力後に支払い情報をStripeに送信する際に一緒に送信されます。この値を使ってstripeに保存されているpaymentIntentと紐づいていると思われます。
fukidashi

フォームの設定(共通)

client.jsではfetch関数でcreate.phpにPOSTリクエストを送信しclient_secretを受け取ったらフォームの作成を行います。フォームにstyleを設定してcard.mountでidはcard-elementの要素にフォームをマウントしています。この時点でブラウザ上にカードの入力フォームが表示されます。


var elements = stripe.elements();

var style = {
  base: {
    color: '#32325d',
    fontFamily: 'Arial, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#32325d',
    },
  },
  invalid: {
    fontFamily: 'Arial, sans-serif',
    color: '#fa755a',
    iconColor: '#fa755a',
  },
};

var card = elements.create('card', { style: style });
// Stripe injects an iframe into the DOM
card.mount('#card-element');

イベントの設定(共通)

フォーム作成後はchangeイベントの設定を行っています。情報が入力されるとdisableで無効化されていたボタンの無効設定を解除しています。


card.on('change', function (event) {
  // Disable the Pay button if there are no card details in the Element
  document.querySelector('button').disabled = event.empty;
  document.querySelector('#card-error').textContent = event.error
    ? event.error.message
    : '';
});

formにもイベントリスナーを通してsubmitイベントが設定されているのでボタンをクリックするとpayWithCardメソッドが実行されます。


var form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
  event.preventDefault();
  // Complete payment when the submit button is clicked
  payWithCard(stripe, card, data.clientSecret);
});

支払いの処理(共通)

payWidhCardでは入力したカード情報とサーバから受け取ったclient_secretを利用してstripeのconfirmCardPaymentを実行してStripeのサーバに支払い処理を依頼しています。


var payWithCard = function (stripe, card, clientSecret) {
  loading(true);
  stripe
    .confirmCardPayment(clientSecret, {
      payment_method: {
        card: card,
      },
    })
    .then(function (result) {
      if (result.error) {
        // Show error to your customer
        showError(result.error.message);
      } else {
        // The payment succeeded!
        orderComplete(result.paymentIntent.id);
      }
    });
};

confirmCardPaymentを実行するとerrorとpaymentIntentの情報が戻されます。エラーがある場合はブラウザ上にエラーを表示するためにshowErrorメソッドを実行し、支払いに成功した場合にはorderCompleteを実行します。

実際にテスト用の正しいカード情報を入力してPay Nowボタンを押すと下記の画面が表示されます。”4242 4242 4242 4242″はアメリカのカード番号なので入力すると郵便番号を入力する必要があります。日本のテストのカード番号を利用したい場合は”4000 0039 2000 0003”です。日本のカード番号を利用すると郵便番号を入力する項目は表示されません。

支払い成功時のメッセージ
支払い成功時のメッセージ
動作確認する際はcreate.phpファイルのPaymentIntentのcreateメソッドのcurrentyはjpyに変更しておきます。支払いがドルから円に変わります。
fukidashi

表示する内容についてはorderCompoleteの中身を書き換えることでメッセージをカスタマイズすることも他のページにリダイレクトすることも可能です。


// Shows a success message when the payment is complete
var orderComplete = function(paymentIntentId) {
  loading(false);
  document
    .querySelector(".result-message a")
    .setAttribute(
      "href",
      "https://dashboard.stripe.com/test/payments/" + paymentIntentId
    );
  document.querySelector(".result-message").classList.remove("hidden");
  document.querySelector("button").disabled = true;
};
// Show the customer the error from Stripe if their card fails to charge
var showError = function(errorMsgText) {
  loading(false);
  var errorMsg = document.querySelector("#card-error");
  errorMsg.textContent = errorMsgText;
  setTimeout(function() {
    errorMsg.textContent = "";
  }, 4000);
};

Pay NowボタンをクリックしてStripeのサーバに支払いに関する情報を送信し待っている場合にボタンにスピナーが表示されます。その処理はloading関数で行っています。loading関数の引数はpayWithCardの実行直後にtrueになり、Stripeから戻り値があると成功、エラーに関わらず引数の値をtrueでloading関数を実行しています。


var loading = function(isLoading) {
  if (isLoading) {
    // Disable the button and show a spinner
    document.querySelector("button").disabled = true;
    document.querySelector("#spinner").classList.remove("hidden");
    document.querySelector("#button-text").classList.add("hidden");
  } else {
    document.querySelector("button").disabled = false;
    document.querySelector("#spinner").classList.add("hidden");
    document.querySelector("#button-text").classList.remove("hidden");
  }
};

支払いが成功するとダッシューボードの支払いに情報が追加されます。

支払いの成功
支払いの成功

ここまでの動作確認でStripe Payment Links, Stripe Checkout, Stripe Elementsの組み込み方法の違いが明確になったと思います。

サンプルコードをそのまま利用しただけなので何もカスタマイズを行っていません。後ほどフォームのカスタマイズ方法について説明を行います。

Nodeの場合

PHP、NodeともにフロントエンドでHTMLを選択しているため、バックエンドのコードの記述方法以外はほぼ同じです。フロントエンドについてはPHPの場合で説明した箇所を参考にしてください。

プラットフォームをWeb, フロントエンドをHTML, バックエンドをNodeに選択して”アプリ全体をダウンロード”でサンプルコードをダウンロードします。

stripe elememts node
stripe elememts node

package.jsonファイルに記述されているstripeのバージョンが低いnpm installを実行するのではなくnpmコマンドでstripeとexpressをインストールします。


 % npm install --save express
 % npm install --save stripe

インストール完了後、npm startコマンドでExpressサーバを起動します。server.jsファイルの中でポート番号は4242に設定されています。


 % npm start

> stripe-sample@1.0.0 start
> node server.js

Running on port 4242

ブラウザからlocalhost:4242に接続するとCannot GET /が表示されるのでcheckout.htmlファイルを指定します。ブラウザ上に下記の画面が表示されます。Stripe Checkoutとは異なりcheckout.htmlにはカード番号と有効期限、CVCのみが入力できるシンプルな入力フォームです。

支払い画面の表示
支払い画面の表示

checkout.htmlに記述されている内容についてはPHPの場合と同じなので説明を行っているフロントエンド側の共通処理を参考にしてください。唯一異なるのはfetch関数でPOSTリクエストを送信する先がNodeでは/create-payment-intentになっている点です。

サーバ側の処理

server.jsファイルには/create-payment-intentのルーティングを持っており受け取ったrequestのbodyから購入予定の商品情報を取り出しcalculateOrderAmount関数で金額に変換してpaymentIntentを作成します。本番では受け取った商品情報からデータベースなどにアクセスして正しい金額を取得するといった処理が必要になります。


const express = require("express");
const app = express();
// This is your real test secret API key.
const stripe = require("stripe")("YOUR KEY");

app.use(express.static("public"));
app.use(express.json());

const calculateOrderAmount = items => {
  // Replace this constant with a calculation of the order's amount
  // Calculate the order total on the server to prevent
  // people from directly manipulating the amount on the client
  return 1400;
};

app.post("/create-payment-intent", async (req, res) => {
  const { items } = req.body;
  // Create a PaymentIntent with the order amount and currency
  const paymentIntent = await stripe.paymentIntents.create({
    amount: calculateOrderAmount(items),
    currency: "usd"
  });

  res.send({
    clientSecret: paymentIntent.client_secret
  });
});

app.listen(4242, () => console.log('Node server listening on port 4242!'));

Paystripe.paymentIntents.createメソッドを利用して金額からpaymentIntentを作成します。作成したpaymentIntentからclient_secretを取り出して送信元であるcheckount.htmlファイルのfetch関数にclient_secretを戻しています。paymentIntentを作成する際にはStripeのサーバにアクセスを行なっておりpaymentIntentの作成が完了するとStripeのダッシュボードには支払い情報が登録されます。支払いを行っていないのでステータスは未完了なので支払い処理は完了していません。

登録された支払い情報
登録された支払い情報
paymentIntentには支払いの金額、支払い方法、支払いステータスなど支払いに関するさまざまな情報を持っているオブジェクトです。client.jsに戻されるclient_secretはカード情報を入力後に支払い情報をStripeに送信する際に一緒に送信されます。この値を使ってstripeに保存されているpaymentIntentと紐づいていると思われます。
fukidashi

フロントエンド側のコードについてはPHPの場合と同じなのでフォームの設定(共通), イベントの設定(共通), 支払いの処理(共通)を参考にしてください。

Webhookによりイベント通知を取得

支払いが成功した場合などStripe側での処理が行われるとWebhookを利用してStripeから自サーバに向けて通知を行うことができます。

サーバ側では通知を受け取る場所を作成する必要がありますがローカルで構築したサーバ上ではStripeから直接通知を受け取ることができません。Stripeでは開発用にStrpie CLIというツールを準備してくれているのでStripe CLIを利用してStripeから通知を受け取る方法を確認します。

Stripe CLIの設定

動作確認をmacOSで行っているのでhomebrewを利用してStripe CLIを行います。WindowsもGithubからツールのダウンロードを行いインストールすることができます。


 % brew install stripe/stripe-cli/stripe

インストールが完了したらstripe loginコマンドを実行します。


 % stripe login
Your pairing code is: happy-affirm-good-savvy
This pairing code verifies your authentication with Stripe.
Press Enter to open the browser or visit https://dashboard.stripe.com/stripecli/confirm_auth?t=ptD9yLSLP2N35'463jogSmUpLCfafeiz7Tl (^C to quit)
⣽ Waiting for confirmation... 

Stripeのアカウントとstripe CLIをペアリングする必要があるためstripe loginを実行後メッセージが出たらEnterキーを押します。ブラウザが起動しstripe login実行後の表示メッセージにあるstripe上のURLにアクセスが行われサインインしていない場合はサインインの画面が表示されるのでメールアドレスとパスワードを入力してください。

認証が完了するとStripe CLIにアクセスの許可を与えるかどうか聞かれるのでstripe login実行時に表示されていたペアリングのコードとブラウザ上に表示されていることコードが一致しているか確認してください。

Stripe上のペアリングコードの確認
Stripe上のペアリングコードの確認

ペアが確立できたら完了画面がブラウザ上に表示され実行したコンソールには90日間有効のキーが作成されたというメッセージが表示されます。


Please note: this key will expire after 90 days, at which point you'll need to re-authenticate.
ペア完了の確認
ペア完了の確認

通知のリッスン

イベントの通知をリッスンするためにstripe listenコマンドを実行します。


 % stripe listen  
⣯ Getting ready... > Ready! Your webhook signing secret is <WEBHOOK KEY> (^C to quit)

listenコマンドを実行後にStripe Elementのサンプルコードを利用して支払い処理を実行すると下記のように通知を受け取ります。このイベントを見るとStripe上でどのような処理が行われいるのかも理解することができます。


 % stripe listen  
⣯ Getting ready... > Ready! Your webhook signing secret is whsec_e6G1G0aTFSNn78C4Db19I0hJhYdvaGbD (^C to quit)
2021-10-10 20:41:48   --> customer.created [evt_1Jj0PYC94s8gJpHejEkMNlXN]
2021-10-10 20:41:48   --> payment_intent.created [evt_3Jj0PYC94s8gJpHe0ShCyAE4]
2021-10-10 20:42:26   --> charge.succeeded [evt_3Jj0PYC94s8gJpHe0guuMmvr]
2021-10-10 20:42:26   --> payment_method.attached [evt_1Jj0QAC94s8gJpHe4kD4y3DW]
2021-10-10 20:42:26   --> payment_intent.succeeded [evt_3Jj0PYC94s8gJpHe0B0vtGJy]

–print-jsonオプジョンをつけて実行するとイベント名ではなく通知で送信されてくるオブジェクトの中身を確認することができます。


 % stripe listen --print-json

画像では字が小さいためわかりにくいですが、stripe listenを実行したコンソール上には以下のようなオブジェクトがイベント毎に表示されます。表示しているのは最後のオブジェクトのpayment_intent.succededイベントなので最後から3番目の行のstatusがsuccededになっていることも確認できます。

コンソールに表示されるオブジェクト
コンソールに表示されるオブジェクト

Stripe CLIからサーバへのフォーワーディング

Stripe CLIで受け取ったイベントをサーバに転送することができます。–forward-toオプションをつけてlocalhost:4242/webookに転送設定を行います。


 % stripe listen --forward-to localhost:4242/webhook

Nodeのサーバを利用して送信されてくるイベントをサーバ上のコンソールに表示させます。新たにserver.jsにルーティング/webhookを追加します。


app.post('/webhook', (req, res) => {
  console.log(req.body);
  res.sendStatus(200);
});
responseでステータスコード200を戻さずconsole.logだけ記述している場合は、 [ERROR] Failed to POST: Post “http://localhost:4242/webhook”: context deadline exceeded (Client.Timeout exceeded while awaiting headers)
のエラーがstripe listenを実行したコンソール上に表示されます。
fukidashi

Stripe Elementsのサンプルコードを利用して支払い処理を行うとnpm startコマンドを実行したコンソール上に通知に含まれるオブジェクトが表示されます。


{
  id: 'evt_3JjOgUC94s8gJpHe1kD7TCUt',
  object: 'event',
  api_version: '2020-08-27',
  created: 1633959488,
  data: {
    object: {
      id: 'pi_3JjOgUC94s8gJpHe1nSVQ3Ym',
      object: 'payment_intent',
      amount: 1400,
      amount_capturable: 0,
      amount_received: 1400,
      application: null,
      application_fee_amount: null,
      canceled_at: null,
      cancellation_reason: null,
//略

Webhookの署名を確認する

Stipeから通知を受け取ることができましたがこの通知が本当にStripeから送られてきたかどうかのチェックは行っていません。Stripeから送られてきたことを確認するために通知のヘッダーに含まれている署名を利用します。

Webhookの署名を確認するためのコードもダッシュボードから確認することができます。ダッシュボードにアクセスして右上にある開発者をクリックしてサイドメニューからWebhookを選択してください。

Webhookを管理する画面が表示されます。Stripe CLIの設定がLocal Listenersに表示されています。

Hosted endpointsにある”+エンドポイントを追加”をクリックしてください。

webhookの設定
webhookの設定

エンドポイントのサンプルコードがバックエンド毎に用意されています。ここではNode.jsを開いています。サンプルコードの中には署名のチェックのコードも含まれています。

本番環境ではエンドポイントURLに通知を受け取ることができるURLを設定することになります。

エンドポイントのサンプルコード
エンドポイントのサンプルコード

そのままの設定ではすべてのイベントを取得することになるのでイベントを選択から取得したイベントを選択することができます。イベントを選択すると右側のコードも更新されます。

ローカル環境でテストするサンプルコードも準備されており、Stripe CLIを利用するとイベントをトリガーすることもできます。

ローカル環境用のテストサンプルコード
ローカル環境用のテストサンプルコード

実際にserver.jsファイルにローカルテスト用のサンプルコードをコピーして、npm startコマンドを実行します。


 % npm start

Stipe CLIでリッスンしてlocalhost:4242にforwardします。


 % stripe listen --forward-to localhost:4242/webhook

別のコンソールから


 % stripe trigger payment_intent.succeeded

Stripe CLIを実行しているコンソールには3つのイベントを受け取っています。


//略
2021-10-11 23:22:09   --> charge.succeeded [evt_3JjPOGC94s8gJpHe0Y0F5w5a]
2021-10-11 23:22:09  <--  [200] POST http://localhost:4242/webhook [evt_3JjPOGC94s8gJpHe0Y0F5w5a]
2021-10-11 23:22:10   -->  payment_intent.succeeded [evt_3JjPOGC94s8gJpHe0KE6jL3G]
2021-10-11 23:22:10  <--  [200] POST http://localhost:4242/webhook [evt_3JjPOGC94s8gJpHe0KE6jL3G]
2021-10-11 23:22:10   -->  payment_intent.created [evt_3JjPOGC94s8gJpHe053xl28l]
2021-10-11 23:22:10  <--  [200] POST http://localhost:4242/webhook [evt_3JjPOGC94s8gJpHe053xl28l]

npm startコマンドを実行したコンソールには2つのイベントのみ表示されます。


Unhandled event type charge.succeeded
Unhandled event type payment_intent.created

その理由はサンプルコードではswtichを利用した分岐によってpayment_intent.succededの場合は何も行わず(サンプルコードでは何も行っていないが本番ではここに処理を追加する)その他のイベントの場合はdefaultでコンソール上に表示するように設定を行っているためです。


switch (event.type) {
  case 'payment_intent.succeeded':
    const paymentIntent = event.data.object;
    // Then define and call a function to handle the event payment_intent.succeeded
    break;
  // ... handle other event types
  default:
    console.log(`Unhandled event type ${event.type}`);
}

Stripe CLIを使ったテスト環境での動作確認でしたがWebhookの設定方法とイベントがどういうものか理解できたかと思います。

フォームのカスタマイズ

Stripe Elementsではドキュメントの中でも”カスタマイズ可能な UI コンポーネントを Web サイトまたはモバイルアプリ (iOS または Android) に組み込んで”と説明がありましたがフォームを作成しているclient.jsファイルのstripe.elementsのcreateメソッドを見てもどのようにカスタマイズができるのかはわかりません。


app.post('/webhook', (req, res) => {
var card = elements.create("card", { style: style });
// Stripe injects an iframe into the DOM
card.mount("#card-element");

Stripeのドキュメントでelements.createメソッドを確認してみると第一引数に作成したい要素の名前を指定することができます。cardの他にcardNumber, cardExpiry, cardCvcなどカード番号、有効期限、CVCの要素を指定することができます。

create_elementsのドキュメント
create_elementsのドキュメント

elements.createメソッドの説明を読んだだけではおそらく組み込むことは難しいと思われます。そのこともあるせいかElementsの例としてStripeのドキュメントではGitHubで公開している”Stripe Elements example“が紹介されています。

Stripe Elements exampleを参考にすれば以下で表示されているフォームへとカスタマイズできることがわかります。

Striple Elementsのカスタマイズ方法
Striple Elementsのカスタマイズ方法

要素の追加

細かなCSSの調整は行いませんが、カードの名義人のinput要素を追加しデフォルトでは横並びになっていたカード番号、有効期限、CVCを横並びにする方法を確認します。

client.jsの更新だけではなくcheckout.htmlの更新も行います。


<form id="payment-form">
  <div id="card-element"><!--Stripe.js injects the Card Element--></div>
  <button id="submit">
    <div class="spinner hidden" id="spinner"></div>
    <span id="button-text">Pay now</span>
  </button>
  <p id="card-error" role="alert"></p>
  <p class="result-message hidden">
    Payment succeeded, see the result in your
    <a href="" target="_blank">Stripe dashboard.</a> Refresh the page to pay
    again.
  </p>
</form>

checkout.htmlにカード名義人とカード番号、有効期限、CVCをJavaScriptから挿入する場所を追加します。元々あったcard-numberは削除しています。


<form id="payment-form">
  <div>
    <input
      type="text"
      name="name"
      placeholder="カード名義"
      id="card-name"
    />
  </div>
  <div id="card-number">
    <!--Stripe.js injects the Card Number Element-->
  </div>
  <div id="card-expiry">
    <!--Stripe.js injects the Card Expiry Element-->
  </div>
  <div id="card-cvc"><!--Stripe.js injects the Card CVC Element--></div>
  <button id="submit">
    <div class="spinner hidden" id="spinner"></div>
    <span id="button-text">Pay now</span>
  </button>
  <p id="card-error" role="alert"></p>
  <p class="result-message hidden">
    Payment succeeded, see the result in your
    <a href="" target="_blank">Stripe dashboard.</a> Refresh the page to pay
    again.
  </p>
</form>

client.jsファイルの更新

client.jsファイルでドキュメントのelements.createメソッドの説明を参考に各要素の作成を行います。作成した要素はcheckout.htmlで追加したdiv要素にマウントします。


var cardNumber = elements.create('cardNumber', {
  style: style,
});
cardNumber.mount('#card-number');

var cardExpiry = elements.create('cardExpiry', {
  style: style,
});
cardExpiry.mount('#card-expiry');

var cardCvc = elements.create('cardCvc', { style: style });
cardCvc.mount('#card-cvc');

changeイベントもcardからcarNumberに変更します。changeイベントによってボタンを有効化しているのでこの設定がないとすべての値を入力してもボタンが有効になりません。cardNumberだけ行っているのでcardNumberに数字を入力するとボタンが有効になります。cardNumberのバリデーションも行われるので入力した情報に不備がある場合はエラーが表示されます。有効期限やCVCにも同じようにchangeイベントを追加するとバリデーションが行われます。


cardNumber.on('change', function (event) {
  // Disable the Pay button if there are no card details in the Element
  document.querySelector('button').disabled = event.empty;
  document.querySelector('#card-error').textContent = event.error
    ? event.error.message
    : '';
});
有効期限、CVCにchangeイベントを設定しなくても”Pay Now”ボタンを押して時に入力した値に不備がある場合はエラーが表示されます。
fukidashi

“Pay Now”ボタンを押すとイベントリスナーで設定していたsubmitイベントの中身が実行されていますがpayWithCardの引数にはデフォルトではcardが設定されています。今回はカードではなくcardNumber, cardExpiry, cardCvcを設定していますがpaywithCard関数の中で実行されるstripe.confirmCardPaymentのpayment_methodのcardではcardの他にcardNumberを指定することができます。

そのためpayWithCardの引数の値をcardからcardNumberに変更します。


form.addEventListener('submit', function (event) {
  event.preventDefault();
  // Complete payment when the submit button is clicked
  payWithCard(stripe, cardNumber, data.clientSecret);
});

ここまでの設定でlocalhost:4242/checkout.htmlにアクセスすると以下の画面が表示されます。カード番号のボーダーなどが表示されていません。

カスタマイズ後のフォーム画面
イズ後のフォーム画面

global.cssを開いてcard-elementに設定されていたCSSをcard-number, card-expiry, card-cvcに設定します。


#card-number,
#card-expiry,
#card-cvc {
  border-radius: 4px 4px 0 0;
  padding: 12px;
  border: 1px solid rgba(50, 50, 93, 0.1);
  height: 44px;
  width: 100%;
  background: white;
}

表示されていなかったボーダーが表示されます。

CSSの設定によりボーダー表示
CSSの設定によりボーダー表示

ここまでの設定で支払いを行うことができます。カード名義を追加しましたがinput要素を追加しただけで何も処理を行っていないため支払い情報にはカード名義の情報は含まれません。

カード名義の値の取得

カードの入力した値を取得するためにpayWithCard関数のstripe.confirmCardPaymentメソッドのオプションの中にbulling_detailsを追加します。


var payWithCard = function (stripe, card, clientSecret) {
  loading(true);
  stripe
    .confirmCardPayment(clientSecret, {
      payment_method: {
        card: card,
        billing_details: {
          name: document.getElementById('card-name').value, //追加
        },
      },
    })
    .then(function (result) {
      if (result.error) {
        // Show error to your customer
        showError(result.error.message);
      } else {
        // The payment succeeded!
        orderComplete(result.paymentIntent.id);
      }
    });
};

追加後に支払いを行うとカード名義で設定した名前がダッシュボードの支払い情報の中に含まれます。カード名義にTEST TAROを入力して支払いを行いました。

カード名義の登録確認
カード名義の登録確認

その他のカスタマイズ

placehoderを変更したい、カードのアイコンを追加したいなどあれば下記のように行うことができます。


var cardNumber = elements.create('cardNumber', {
  style: style,
  showIcon: true,
  placeholder: 'カード番号',
});

カード番号のPlaceholderとアイコンを確認することができます。

アイコンとプレースホルダー変更
アイコンとプレースホルダー変更

カード名義のようにinput要素を追加する方法、カード番号、有効期限、CVCを分割して表示する方法がわかったのでこれらの設定を参考にフォームのカスタマイズ方法を理解することができました。

顧客と支払いカード情報を保存する

ここまでのStripe Elementsのサンプルコードでは顧客情報をStripe上に保存していないため再度同じ顧客から支払いを行いたい時に再度カード情報を入力してもらう必要があります。支払い時に顧客情報を作成しカード情報を保存することで次回の支払い時に保存した情報を利用することでカード情報を再度入力することなく支払いを行うことができます。

Striple Elementsで利用したNode.jsのサンプルコードを元に設定を行なっていきます。

Stripeのドキュメントの支払い時にカードを保存するを参考に動作確認を行なっていきます。

支払い時のカード情報を保存する
支払い時のカード情報を保存する

顧客情報を保存する前にダッシュボード上で顧客情報の確認を行います。Stripe Elementsでテスト支払いを行いましたが顧客情報は登録されていません。

顧客情報は未登録
顧客情報は未登録
Stripe Payment Linksでテストを行っている場合は顧客が登録されています。
fukidashi

顧客/支払い方法の保存

paymentIntentを作成する前に顧客を作成します。作成した顧客のidをpaymentIntentsメソッドのcustomerプロパティに設定します。これでサーバ側の設定は完了です。


app.post('/create-payment-intent', async (req, res) => {
  const { items } = req.body;

  const customer = await stripe.customers.create();
  // Create a PaymentIntent with the order amount and currency
  const paymentIntent = await stripe.paymentIntents.create({
    amount: calculateOrderAmount(items),
    currency: 'jpy',
    customer: customer.id,
  });

  res.send({
    clientSecret: paymentIntent.client_secret,
  });
});

chekcout.htmlファイルを開いただけで空の顧客情報が作成され、paymentIntentを作成すると顧客と支払い情報が紐づけられます。この時点でダッシュボードの顧客を見ると新たに顧客が作成されていることが確認できます。

client.jsファイルの中のcofirmCardPaymentメソッドの中にsetup_future_usageのoff_sessionを利用します。off_sessionに設定することで支払いの処理にユーザが介さなくても支払い処理を行うことができます。

ここまでの設定で支払いを実行すると支払いが行われ、顧客、支払い方法などの情報が保存されます。ダッシュボードを確認すると支払いが完了し、支払い方法が登録された顧客情報を確認することができます。

保存された顧客情報
保存された顧客情報

保存したデータで支払い

保存したデータを利用して支払いを行うことで顧客を介さなくても以前登録したカード情報で支払いが行えることを確認します。

保存したデータを利用するためには顧客データを取得するためのIDが必要となります。ここでは顧客毎に割り当てられる顧客IDを利用します。顧客IDはダッシュボードから確認することができます。

server.jsファイルに新たにルーティングの/chargeを追加します。


const chargeCustomer = async (customerId) => {
  // Lookup the payment methods available for the customer
  const paymentMethods = await stripe.paymentMethods.list({
    customer: customerId,
    type: 'card',
  });
  // Charge the customer and payment method immediately
  const paymentIntent = await stripe.paymentIntents.create({
    amount: 500,
    currency: 'jpy',
    customer: customerId,
    payment_method: paymentMethods.data[0].id,
    off_session: true,
    confirm: true,
  });
  if (paymentIntent.status === 'succeeded') {
    console.log('✅ Successfully charged card off session');
  }
};

app.post('/charge', async (req, res) => {
  await chargeCustomer(req.body.customerId);
  res.sendStatus(200);
});

支払い処理が行われているchargeCustomer関数を確認します。POSTリクエストで送信されてきたCustomerIdを利用してpaymentMethodsを取得しています。paymentMethodsは支払いのためのカード情報を持っています。paymemtIntents.createメソッドではpaymentMethodsのidとcustomerIdを指定しています。off_sessionでは顧客を介さないで処理を行うこと、confirmのtrueを設定することでpaymentIntent作成と同時に支払いを行っています。

サーバの外側から/chargeに対してcustomerIdを含むPOSTリクエストを送信することで支払い処理が実行されることになります。

本文書ではVisual Studio CodeのエクステンションのRest Clientを利用して下記のPOSTリクエストを送信しています。http://localhost:4242/chargeに対してPOSTリクエストでcustomerIdを含むJSONデータを送信しています。


###
POST http://localhost:4242/charge
Content-Type: application/json

{
    "customerId": "cus_KOQ60VAoLcNsK8"
}

実行が完了するとnpm startを実行しているコンソールに”Successfully charged card off session”が表示されれば処理は正常に動作しています。

処理後ダッシュボードを確認すると支払いで指定した500円の支払いデータを確認することができます。

追加の支払いデータを確認
追加の支払いデータを確認

今回は正常に動作が完了しましたがエラーが発生した場合の処理も組み込んでテストを行う必要があります。Stripeのドキュメントの”支払い時にカードを保存する”の後半に記載されているので参考にしてください。

Stripe Elementsでのさまざまな組み込み方法

本文書で説明を行ったStripe Elementsの組み込みサンプルとしてPayment intentsを利用していましたがこれ以外にもSetup Intentsを利用するものやPaymentMethodを利用するものなど複数あります。Payment intentsについては本文書では説明済みなので残りのSetup IntentsとPaymentMethodについて説明を行う予定です。