【Next.js】FirebaseでStripeの単発決済の実装

f:id:nekorokkekun:20191014121813p:plain:w1000
以前まとめたこちらの記事の続きとなります。

nekorokkekun.hatenablog.com
nekorokkekun.hatenablog.com

以前の段階で、StripeとFirestoreとFirebase Authenticationのユーザーを同時に作成することができました。

こちらの記事では、Next.js上にFirebaseでStripeの単発決済の実装を行っていきます。

図解

f:id:nekorokkekun:20191014120030p:plain

上記の仕組みを以下の流れで実装していきます。

  • submitNewChargeというイベントを持った決済ボタン作成
  • ボタンを押すとFirestore上に stripe_customers > user_id > charges に決済情報が記録される
  • Firebase FunctionsのcreateStripeChargeが起動し、Stripe上に決済情報を送信
  • Stripe上で決済が行われる

ユーザーが行うのはボタンのクリックだけとなります。

実装

決済ボタン作成

まずは「submitNewChargeというイベントを持った決済ボタン作成」を行います。

イベントは以下の通りです。

    submitNewCharge = async (evt) => {
        evt.preventDefault();
        await
        db.collection('stripe_customers')
        .doc(firebase.auth().currentUser.uid)
        .collection('charges')
        .add({
            amount: 2000
          })
    }

また、ボタン自体は以下の通りです。

<button onClick={this.submitNewCharge}>決済</button>

amountを2000としていますが、実際には動的に商品の値段などをはめ込むシチュエーションが多いと思いますので、その場合は、以下のように記述しましょう。

    submitNewCharge = async (evt) => {
        evt.preventDefault();
        await
        db.collection('stripe_customers')
        .doc(firebase.auth().currentUser.uid)
        .collection('charges')
        .add({
            amount: this.props.detail.monthlyFee
          })
    }

createStripeChargeの作成

次にFirebase Functionsの作成を行います。

functions/index.js

exports.createStripeCharge = functions.firestore.document('stripe_customers/{userId}/charges/{id}').onCreate(async (snap, context) => {
  const val = snap.data();
  try {
    // Look up the Stripe customer id written in createStripeCustomer
    const snapshot = await admin.firestore().collection(`stripe_customers`).doc(context.params.userId).get()
    const snapval = snapshot.data();
    const customer = snapval.customer_id
    // Create a charge using the pushId as the idempotency key
    // protecting against double charges
    const amount = val.amount;
    const idempotencyKey = context.params.id;
    const charge = {amount, currency, customer};
    if (val.source !== null) {
      charge.source = val.source;
    }
    const response = await stripe.charges.create(charge, {idempotency_key: idempotencyKey});
    // If the result is successful, write it back to the database
    return snap.ref.set(response, { merge: true });
  } catch(error) {
    // We want to capture errors and render them in a user-friendly way, while
    // still logging an exception with StackDriver
    console.log(error);
    await snap.ref.set({error: userFacingMessage(error)}, { merge: true });
    return reportError(error, {user: context.params.userId});
  }
});

長いですが、要は「Firestoreの stripe_customers/{userId}/charges にドキュメントが作成されたら、その中から必要なデータを取得して、Stripeで決済処理をする」というだけです。

忘れずFunctionsをデプロイ しておきましょう。

$ cd functions
$cd yarn deploy

確認

実装自体は以上で完了です。実際にページから動作の確認をしてみましょう。

ボタンを押すとFirestoreとStripeに反映されています。
Firestore
f:id:nekorokkekun:20191014121257p:plain
Stripe
f:id:nekorokkekun:20191014121302p:plain

ちなみに以前の記事でご紹介したクレジットカード登録処理していない状態で決済を行うと、Firestoreに以下のように反映されます。
f:id:nekorokkekun:20191014121454p:plain

クレジットカード登録時にsourceが生成され、sourceの紐付きがある状態でなければStripeでの決済はできないからです。

ちなみにこのようにFirestoreに反映された決済はStripeには表示されません。

以上で単発決済の実装は完了です。お疲れ様でした!