はじめに
プロダクトのマネタイズには決済機能は欠かせません。
特にオンラインで提供されるサービスであるならばクレジットカード支払いができれば決済のハードルがぐんと下がり、ユーザビリティも向上するでしょう。
今回はStripeという決済代行サービスを使ってWebサービスに決済機能を実装してみます。
フロントエンドはReactで実装し、Firebaseを使うことでサーバーレスで実現します。
Stripe APIを使ったWeb決済機能
Stripe APIを使ってフロントエンドに決済機能を導入する方法は次の3パターンです。
元々はダイアログ型のCheckoutと埋込み型のElementsの2種類だったのですが、Checkoutに関しては最近新しいUIが追加されました。
実現したい機能によって変わりますが、低コストで実装したいならStripeが用意したUIを使うCheckout、サイトデザインに合うようにスタイルをカスタマイズしたいのであればElementsを使います。
今回はStripe Checkout(Legacy)で実装してみます。
Stripe Checkoutとは
Stripe決済用のボタンをWebサイトに配置し、ボタンをクリックすることで決済ダイアログもしくは決済ページに遷移します。
そこで決済情報の入力および決済処理が完了するとWebサイトに戻ります。
Webサイトは決済処理の結果を受け取り、結果によってページの表示を切り替えることができます。
表示される決済ページはカスタマイズすることができて、クレジットカード情報のみならず、メールアドレス、請求書先住所、配送先住所などの入力フォームを追加することができます。

Firebase HostingとCloud Functionを用いてサーバーレスなStripe決済機能を実現する
Stripeはフロントエンドに導入するのは簡単ですが、実際に決済機能まで実装しようとするとサーバサイドのアプリケーションが必要になります。
ですが、決済機能のためにわざわざサーバサイドを準備したくない場合もあります。
そこで、Cloud Functions for FirebaseというFaaS(Function as a Service)を使って、本来サーバサイドで行う処理を任せることでサーバーレスなアーキテクチャを実現することができます。
全体の構成は以下の通りです。

- クレジットカード情報をStripeへ問い合わせる
アプリケーション側では顧客のクレジットカード情報は扱いません。情報を直接Stripe側へ送信するため、個人情報の保護をStripe側に任せられるというメリットがあります。ここではあくまでクレジットカードの認証のみ行います。
-
認証に成功したらトークンを発行
Stripeは受け取ったクレジットカード情報が正しいものであるか認証します。認証が成功したらアプリケーション側にトークンを発行します。
-
トークンと決済情報を渡しCloud Functionを叩く
受け取ったトークンをCloud FunctionのエンドポイントにPOSTで渡し、決済機能を実行します。
-
Stripe APIで決済処理を実行する
受け取ったトークンと決済情報(金額、通貨など)を受け取り、Stripe APIで決済処理を実行します。
-
決済処理の結果を返す
Stripe APIで決済処理の結果を受け取ったら、それをアプリケーション側に返します。
要はStripe APIの決済処理をラップするAPIをCloud Functionsで実装するイメージです。
ReactでFirebaseプロジェクトを作成
それでは、実際にアプリケーションを作成していきます。
まず、Reactアプリケーションを作成します。
$ npx create-react-app react-stripe-checkout
次にWebのFirebase consoleからプロジェクトを作成します。
コンソール上で作成できたら、CLIでFirebaseプロジェクトとして初期化します。
$ npm install -g firebase-tools $ cd react-stripe-checkout $ firebase init
Firebase CLIで機能を選択できるので、HostingとFunctionsを選択します。

先程作成したプロジェクトを選択します。
対話形式で聞かれるので、適当な選択肢を選んで進むと設定が完了します。
最後に、デフォルトのFirebaseプロジェクトではホスティングのフォルダがpublic担っているので、React用(buildフォルダ)に設定を変更します。
firebase.jsonのホスティング設定を以下の通り修正してください。
{
"hosting": {
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
これでReactによるFirebaseプロジェクトが作成されました。
Firebaseプロジェクトのプランを変更
今回の実装ではCloud Functionから外部のAPI(Stripe API)を叩くので、料金プランをSparkからBlazeへ変更する必要があります。

Firebase consoleに戻り料金プランを変更します。

フロントエンド実装
Stripe CheckoutをReactコンポーネントとして使えるパッケージをインストールします。
$ yarn add react-stripe-checkout
次にpublic/index.htmlでstripe.jsを読み込みます。
<script src="https://js.stripe.com/v3/"></script>
Checkoutを配置したコンポーネントを作成します。
ここでStripeの公開鍵が必要になるので、Stripeダッシュボードの開発者>APIキーのページから取得してください。

フロント側を実装します。
src/components/StripeComponent.js
ここでは通貨は日本円、金額は1,000円の決済機能を実装します。
import React, { Component } from 'react';
import StripeCheckout from 'react-stripe-checkout';
const stripeApiKey = "Stripeの公開鍵";
const checkoutUrl = "Firebase Cloud Functionのエンドポイント";
class Checkout extends Component {
handleToken = (token) => {
fetch(checkoutUrl, {
method: "POST",
headers:{
'Content-type':'application/json'
},
body: JSON.stringify({
token,
charge: {
amount: 1000,
currency: 'JPY'
}
}),
})
.then(res => {
console.log(res);
return res.json();
})
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
});
}
render() {
return (
<StripeCheckout
stripeKey={stripeApiKey}
token={this.handleToken}
name="会費"
amount={1000}
panelLabel="{{amount}}支払う"
currency="JPY"
locale="ja"
allowRememberMe={false}
/>
);
}
}
export default Checkout;
StripeCheckoutコンポーネントのpropsにStripeの公開鍵や金額、通貨などを渡します。
token propsには関数をセットし、トークンを受け取った後の処理を記述します。
他にもオプションが設定できるので、詳しくは公式のリファレンスを確認してください。
実装が問題なければビルドしてHostingにデプロイします。
$ yarn build
$ firebase deploy
次にサーバサイドの実装に移ります。
サーバサイド実装(Cloud Functions)
サーバサイドの実装にはにはStripeの秘密鍵が必要になります。
Stripeダッシュボードから開発者>APIキー画面からシークレットキーを確認してください。

秘密鍵は外部に公開してはいけないのでソースに直に書き込まないように注意してください。(Gitにもプッシュしないように)
ここではfirebaseコマンドを使ってCLIで値をセットします。
まずは必要なパッケージをfunctionsディレクトリ内でインストールします。
クロスドメインの通信を行うためcors、Stripe APIを叩くためにstripeパッケージを使用します。
$ cd functions $ yarn add cors stripe
functions/index.jsにcharge関数を実装します。
後述しますが、Firebase CLIでセットしたStripeの秘密鍵をconfigから読み込ませてstripeオブジェクトを生成しています。
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({origin: true});
const stripe = require('stripe')(functions.config().stripe.secret_token);
admin.initializeApp(functions.config().firebase);
const stripeCharge = (request, response) => {
const body = request.body;
const amount = body.charge.amount;
const currency = body.charge.currency;
const description = 'React Stripe Checkout';
const receipt_email = body.token.email;
const source = body.token.id;
const charge = {amount, currency, description, receipt_email, source};
stripe.charges.create(charge)
.then(res => {
send(response, 200, {message: 'success!', result: res});
console.log('charge success.');
})
.catch(err => {
send(response, 500, {error: err.message});
console.log('charge failed.');
});
}
function send(response, statusCode, body) {
response.send({
statusCode,
data: JSON.stringify(body)
});
}
exports.charge = functions.https.onRequest((request, response) => {
cors(request, response, () => {
try {
stripeCharge(request, response);
} catch (error) {
send(response, 500, {error: error.message});
}
});
});
フロントエンドからPOSTされたパラメータを使って、Stripe APIを叩いています。
StripeのCharge APIには他にもオプションが設定できるので詳しくは公式リファレンスを確認してください。
CREATE A CHARGE(API Reference)
いよいよCloud Functionをデプロイします。
firebase CLIでStripe APIで使用するシークレットキーをセットします。
$ firebase functions:config:set stripe.secret_token="シークレットキー"
Hostingは既にデプロイされている前提で、Functionのみデプロイします。
$ firebase deploy --only functions
フロントに決済ボタンが配置され、それをクリックすると決済ダイアログが表示されます。
そこにクレジットカード情報(以下はテスト用)を入力して送信すると決済が実行されます。

Stripeダッシュボードの「支払い」ページで結果を確認することができます。

今回は最低限の実装ですが、結果によってフロントの表示を変えたりすることもできるので、色々試してみてください。
さいごに
StripeとFirebaseはかなり相性がいいと感じました。
Stripeはプロダクトに導入する決済サービスとして頭一つ抜けてると思っています。できることが多い分、なかなか奥が深いので引き続き注目していきたいと思います。