備忘録。
react で stripe 決済を実装する
実装には、推奨されている paymentIntent を使う。
実装のフローとしては、
1:フロントで決済画面に移行する
2:フロントからサーバーに paymentIntent を create する通信を投げる
3:サーバー側で paymentIntent を create ( paymentIntent を参照) し、client_secret をフロントに返す
4:サーバーから返ってきた client_secret と 顧客が入力したカード情報で、決済を行う ( confirmCardPayment を参照)。
のようになる。
サーバー側 (Express)
app.js
import express from "express"
import Stripe from "stripe"
const stripeSecret = new Stripe(STRIPE_SECRET_KEY)
app.post("/api/payment/create", async (request, response) => {
const total = calculate_total(request.body.total) // 合計金額を計算する関数を作る
const paymentIntent = await stripeSecret.paymentIntents.create({
amount: total,
currency: "JPY"
})
response.status(201).send({
clientSecret: paymentIntent.client_secret
})
})
フロント側 (React)
App,js
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
const stripePromise = loadStripe(STRIPE_PUBLISHABLE_KEY)
function App() {
return(
<Elements stripe={stripePromise}>
<CheckoutPage/>
</Elements>
)
}
export default App
CheckoutPage.js
import React, {useEffect} from "react"
import {CardElement, useElements, useStripe} from "@stripe/react-stripe-js"
function CheckoutPage () {
const stripe = useStripe()
const elements = useElements()
const [clientSecret, setClientSecret] = useState("")
const [errorMsg, setErrorMsg] = useState("")
const [processing, setProcessing] = useState(false)
const [success, setSuccess] = useState(false)
useEffect(() => {
async function getClientSecret(total) {
try {
const {data} = await axios.post("/api/payment/create", {total: hogehoge})
setClientSecret(data.clientSecret)
} catch(error) {
setErrorMsg(error.message)
}
}
getClientSecret(total)
}, [total])
async function paymentHandler(e) {
e.preventDefault()
if (!stripe || !elements) {
return
}
setProcessing(true)
const cardElement = elements.getElement(CardElement);
if (!cardElement) { return }
await stripe.confirmCardPayment(clientSecret, {
payment_methods: {
card: cardElement,
}
})
.then(({paymentIntent}) => {
setErrorMsg(false)
setProcessing(false)
setSuccess(true)
})
.catch((error) => {
setErrorMsg(error.message)
setProcessing(false)
setSuccess(false)
})
}
return (
<div>
<Form onSubmit={paymentHandler}>
<CardElement/>
{errorMsg && <div className="errorMsg">{errorMsg}</div>}
<button disabled={!stripe || !elements}>Pay Now</button>
</Form>
</div>
)
}
export default CheckoutPage;
参考資料
を参考にしました