Stripe のウェブフックを実装し、取引の各ステップを自動化する方法を探求します。このチャプターでは、ウェブフックの設定から、取引イベントのリアルタイムモニタリング、そして自動応答の仕組みまでを解説。Stripe とサーバーレスアプリケーションの連携を最大限に活かす技術を提供します。
Stripe CLI のインストール
Stripe CLI は Stripe の開発を効率的に行うためのコマンドラインツールです。以下のコマンドでインストールできます。
Stripe CLI のインストール
Mac の場合
brew install stripe/stripe-cli/stripe
Stripe Webhook のテスト
skeet s
でローカルサーバーを起動し、Stripe Webhook のテストを行います。
skeet s
別ウィンドウで Stripe CLI を使って、Stripe Webhook のテストを行います。
新しく追加した stripeRouter
のエンドポイントを指定します。
stripe listen --forward-to http://127.0.0.1:5001/<your-project-id>/<your-region>/stripeRouter
そしてさらに別ウィンドウで、Stripe CLI でトリガーを送信します。
stripe trigger checkout.session.completed
すると、ローカルサーバーにリクエストが送信され、
payment
アクションが実行されました。
無事に stripeRouter
のエンドポイントにリクエストが送信されました 🎉
Firestore との連携、StripeUser モデルの作成
それでは、Stripe と Firestore を連携させるために、
StripeUser モデルを作成します。
functions/skeet/src/models/StripeUser.ts
import { Timestamp, FieldValue } from '@skeet-framework/firestore'
// CollectionId: StripeUser
// DocumentId: StripeUserId
// Path: StripeUser
export const StripeUserCN = 'StripeUser'
export type StripeUser = {
id?: string
customerId: string | null
username: string
email: string
isActivated: boolean
expirationDate: Timestamp | FieldValue | null
createdAt?: Timestamp | FieldValue
updatedAt?: Timestamp | FieldValue
}
トランザクション発生時に StripeUser を作成する
サブスクリプションの購入が完了したら、Webhook を受信して、
StripeUser を作成します。
functions/skeet/src/lib/stripe/webhook/payment.ts
import { StripeUser, StripeUserCN } from '@/models/stripeUserModels'
import { add } from '@skeet-framework/firestore'
import Stripe from 'stripe'
export const subscription = async (
db: FirebaseFirestore.Firestore,
eventObject: Stripe.Checkout.Session
) => {
console.log('subscription event handler')
const stripeUserParams: StripeUser = {
customerId: String(eventObject.customer),
username: eventObject.customer_details?.name || '',
email: eventObject.customer_details?.email || '',
isActivated: true,
expirationDate: null,
}
await add<StripeUser>(db, StripeUserCN, stripeUserParams)
return true
}
それでは subscription
イベントを起こすために、
テスト用のペイメントリンクから支払いを行います。
テスト用のクレジットカードは
4242 4242 4242 4242
期限とCVCは任意のものでテスト用として動作します。
そして Firebase エミュレーターの Firestore を確認します。
http://localhost:4000/
無事に StripeUser が作成されました 🎉
メンバー限定のボタンに変更する
前の章で作成した、bonusAction.ts
を会員限定のコンテンツへ変更します。
functions/skeet/src/lib/discord/actions/bonusAction.ts
import { DiscordUser, DiscordUserCN } from '@/models/discordUserModels'
import { StripeUser, StripeUserCN } from '@/models/stripeUserModels'
import {
DiscordRouterParams,
deferResponse,
updateResponse,
} from '@skeet-framework/discord-utils'
import { get, update } from '@skeet-framework/firestore'
import { Response } from 'firebase-functions/v1'
export const bonusAction = async (
res: Response,
db: FirebaseFirestore.Firestore,
discordToken: string,
body: DiscordRouterParams
) => {
console.log('bonusAction')
await deferResponse(discordToken, body.id, body.token)
const memberId = body.member.user.id
const user = await get<DiscordUser>(db, DiscordUserCN, memberId)
if (!user) {
await updateResponse(discordToken, body.application_id, body.token, {
content: `⚠️ Failed to get user.Please try again in a few seconds.`,
flags: 64,
})
return false
}
const bonus = Math.floor(Math.random() * 100)
const newExp = user.exp + bonus
let lv = user.lv
const expToNextLevel = 100 * lv
if (expToNextLevel >= 300) {
const stripeUser = await get<StripeUser>(db, StripeUserCN, memberId)
if (!stripeUser || !stripeUser.isActivated) {
await updateResponse(discordToken, body.application_id, body.token, {
content: `⚠️ この先はメンバーシップが必要です。`,
flags: 64,
})
return false
}
}
if (newExp >= expToNextLevel) {
lv++
}
await update<DiscordUser>(db, DiscordUserCN, body.member.user.id, {
exp: newExp,
lv,
})
let description = `あなたは${bonus}の EXP を獲得しました!`
if (lv > user.lv) {
description += `\nレベルが${lv}に上がりました!`
}
// Discord Embed Message
const emdbed = {
description,
color: 0x0099ff,
}
await updateResponse(discordToken, body.application_id, body.token, {
embeds: [emdbed],
flags: 64,
})
return true
}
これで、さきほどのボタンを EXP が 300 を超えるまでクリックしてみます。
メンバー限定のコンテンツに変更されました 🎉