前回の続き。
とりあえず、なんとなくの動きがわかったので、
もう少しコードを書いて試してみる。
試してみたサンプルのソースコードはこちら。
・memory-lovers/example-stripe-extensions
サンプルでできること
簡単なサンプルなので、必要最低限のみ。
- ログインして
- プランを表示して、
- 購入して、
- 購入した情報を表示
サンプルはこんな感じ。
前準備
まずは、FirebaseとStripeが必要なので、
第一回を見ながら、以下を進める。
- Firebaseプロジェクトの作成
- Stripe Extensionの有効化
- Stripeに商品/価格を登録し、カスタマーポータルを設定
https://www.memory-lovers.blog/entry/2021/06/07/050000
その後は、チェックアウトして、
nuxt.config.ts
に設定情報を書く。
const config: NuxtConfig = { env: { // Firebase Config API_KEY: "...", AUTH_DOMAIN: "...", PROJECT_ID: "...", STORAGE_BUCKET: "...", MESSAGING_SENDER_ID: "...", APP_ID: "...", // Stripe Public API Key STRIPE_API_KEY: "..." } };
最後に、起動すればOK
$ npm install $ npm run dev # => http://localhost:3000
ログイン画面(page/index.vue
)
ログイン画面はこんな感じ。
画像
ログインするだけ。
マイページ画面(page/mypage.vue
)
ログインユーザの情報を表示する画面。
Authのカスタムクレームや有効なサブスクの情報を表示
画像
プラン画面
登録した商品と価格の一覧を表示する画面。
購入もここから。
画像
ハマったポイント
実際に動かしてみてハマったポイントがいくつか。。
拡張機能では購入のみで、サブスクの2重購入もできてしまう
なにか購入したあとに、変更とかキャンセルとかどうするのかな?
と思ったら、カスタマーポータル経由で行ってもらうよう。
拡張機能のソースを見ても購入以外にないっぽい。
さらに、すでにサブスクを購入しているかなどの制限もないため、
定期支払の商品をいくつも買えてしまう。
が、カスタマーポータルは定期支払は1つのみしか対応していないので、
ユーザ自身がキャンセルできなくなってしまう。。
なので、一度でも購入していたら、カスタマーポータルで対応してもらうのが良さそう。
カスタムクレームの反映には強制更新が必要
カスタムクレームで購入しているプランがわかるかとおもったけど、
getIdTokenResult(true)
で強制更新ないと反映されないよう。
Firebaseのドキュメントにも書いてあった...
・カスタム クレームとセキュリティ ルールによるアクセスの制御 | Firebase
- カスタム クレームの変更後に、ユーザーがログインまたは再認証する。その結果として発行されたIDトークンには最新のクレームが含まれる。
- 古いトークンが期限切れになると、既存のユーザー セッションでそのIDトークンが更新される。
currentUser.getIdToken(true)
を呼び出して IDトークンが強制的に更新される。
拡張機能が用意する各ドキュメントにはIDがない
Firestoreを使うときにはドキュメントだけでパスが分かるように、
IDを持たせていたけど、ドキュメントIDは各ドキュメントに含まれてない。
Stripeの商品IDや価格IDもドキュメントIDになっているため、
DocumentReferenceなどからidを取得しておく必要がある。
購入しているサブスクの商品名などは別途取得が必要
ログインユーザの定期支払は、/customers/{uid}/subscriptions/{id}
を見ればいいけど、
商品や価格はDocumentReference
が設定されている。
単体だと買っているかどうかはわかるけど、商品名などは、
別途、subscription.prodcut.get()
などで取得しないといけない。
// /customers/{uid}/subscriptions/{id} export interface Subscription { product: FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData>; price: FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData>; }
型定義はstripe-node
のを使わないといけない...
第三回で整理した型定義を使おうと思ったら、
stripe-jsだけではだめで、stripe-nodeが必要に..
stripeInterface.ts
は、stripe-firebase-extensions/firestore-stripe-subscriptionsを利用してる。
import Stripe from "stripe"; // stripe-firebase-extensionsのinterces.tsをコピー import interfaces from "./stripeInterface"; // /customers/{uid}/ export type CustomerDoc = { email: string; stripeId: string; stripeLink: string; }; // /customers/{uid}/checkout_sessions/{id} export type CheckoutSessionDoc = { mode?: "subscription" | "payment" | string; // default: 'subscription' price?: string; success_url: string; cancel_url: string; quantity?: number; // default: 1 payment_method_types?: Stripe.Checkout.SessionCreateParams.PaymentMethodType[]; // default: ['card'] metadata?: Stripe.Checkout.SessionCreateParams.SubscriptionData; // default: {} tax_rates?: Array<string>; // default: [] allow_promotion_codes?: boolean; // default: false trial_from_plan?: boolean; // default: true line_items?: Stripe.Checkout.SessionCreateParams.LineItem[]; billing_address_collection?: Stripe.Checkout.SessionCreateParams.BillingAddressCollection; // default: 'required', collect_shipping_address?: boolean; // default: false, locale?: Stripe.Checkout.SessionCreateParams.Locale; // default: 'auto', promotion_code?: string; client_reference_id?: string; sessionId?: string; error?: { message: string; }; }; // /customers/{uid}/subscriptions/{id} export type SubscriptionDoc = interfaces.Subscription; // /customers/{uid}/payments/{id} export type PaymentDoc = Stripe.PaymentIntent; // /customers/{uid}/subscriptions/{id}/invoices/{id} // /customers/{uid}/payments/{id}/invoices/{id} export type InvoiceDoc = Stripe.Invoice; // /products/{id} export type ProductDoc = interfaces.Product; // /products/{id}/prices/{id} export type PriceDoc = interfaces.Price; // /products/tax_rates/tax_rates/{id} export type TaxRateDoc = interfaces.TaxRate;
とはいえ、ハマるところも少ない気がする(´ω`)
Stripeすごい(´ω`)
以上!!
Stripe試してみたシリーズ
- 第一回: サンプルを試す
- 第二回: 拡張機能のドキュメントを読む
- 第三回: 拡張機能のソースを読む
- 第四回: Nuxtアプリで試してみる
- 第五回: 本番運用する前に