Cloud Functions for FirebaseでStripeのWebhook Endpointを作る
StripeのPayment Intents APIを使って決済を行う際に、サーバ側はWebhookの payment_intent.succeeded
イベントで決済完了の判定を行う。
今回はFirebaseを使っていたので、WebhookのエンドポイントをCloud Functionsで用意することにした。
WebhookエンドポイントはHTTPリクエスト経由で呼ばれるので、onCallではなくonRequestを用いて実装を行った。
なお、今回もstripe-firebase-extensionsのコードを参考にさせてもらった。
import * as functions from 'firebase-functions'
import Stripe from 'stripe'
import { getWebhookEvent } from '../util/stripe'
import { getUidFromStripeCustomerId, updatePaymentDocument } from '../util/firestore'
const handleWebhookEvents = functions
.region('asia-northeast1')
.https.onRequest(async (req, resp) => {
const relevantEvents = new Set(['payment_intent.succeeded'])
let event: Stripe.Event
const signature = req.headers['stripe-signature']
? (req.headers['stripe-signature'] as string)
: null
if (!signature) {
console.error('️[Error]: Webhook signature verification failed.')
resp.status(401).send('Webhook Error: Invalid Secret')
return
}
try {
event = getWebhookEvent({
payload: req.rawBody,
signature,
})
} catch (error) {
console.error(
'️[Error]: Webhook signature verification failed. Is your Stripe webhook secret parameter configured correctly?',
error.message
)
resp.status(401).send('Webhook Error: Invalid Secret')
return
}
if (relevantEvents.has(event.type)) {
console.log(`Handling Stripe event [${event.id}] of type [${event.type}].`)
try {
switch (event.type) {
case 'payment_intent.succeeded': {
const paymentIntent = event.data.object as Stripe.PaymentIntent
const customer = paymentIntent.customer
if (!customer) {
throw new Error('customer is null')
}
const uid = await getUidFromStripeCustomerId(customer as string)
if (!uid) {
throw new Error('uid not found')
}
await updatePaymentDocument({
uid,
paymentIntentId: paymentIntent.id,
status: paymentIntent.status,
})
break
}
default:
new Error('Unhandled relevant event!')
}
console.log(`Successfully handled Stripe event [${event.id}] of type [${event.type}].`)
resp.json({
message: 'ok',
})
} catch (error) {
console.error(
`️[Error]: Webhook handler for Stripe event [${event.id}] of type [${event.type}] failed:`,
error.message
)
resp.status(500).json({
error: 'Webhook handler failed. View function logs in Firebase.',
})
return
}
}
})
export default handleWebhookEvents