Open2
Stripe SDK (React)小ネタ集
Use Render Hook
目的
- Payment Intent取得とStripe Elementのセットアップ系をカスタムフックに押し込める。
- 親(決済フォームを表示させる操作をする画面)に、Stripe系の実装を含めずに済む
参考記事
use-stripe-elements.tsx
Elementの見た目や初期表示(fallback)の設定など、Payment Elementsを使うために親でやらないといけないことを一通りまとめたHook。
import { ComponentProps, FC, ReactNode, useCallback, useState } from "react";
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
export const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_API_KEY as string,
);
const useStripeElement = ({children, fallback, elementsOptions = {appearance: {
theme: "flat",
}}}: {
children: ReactNode;
fallback?: ReactNode;
elementsOptions?: Omit<ComponentProps<typeof Elements>['options'], 'clientSecret'>
}) => {
const [clientSecret, setClientSecret] = useState('')
const createPaymentIntent = async () => {
const response = await fetch('/api/payment_intent', {
method: "POST",
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
amount: 100
})
})
const data = await response.json()
setClientSecret(data.client_secret)
}
const renderPaymentElement = useCallback(():JSX.Element | null => {
if (!clientSecret) {
if (fallback) return <>{fallback}</>
return null;
}
return (
<Elements
stripe={stripePromise}
options={{
...elementsOptions,
clientSecret: clientSecret,
}}
>
{children}
</Elements>
)
},[clientSecret,children, fallback])
return {
createPaymentIntent,
renderPaymentElement
}
}
paymentForm.tsx
Payment Element本体。
useStripe
, useElements
を使っているので、これは別にしておいた方がよさそう。
import { FC } from "react";
import { PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";
const PaymentForm: FC = () => {
const stripe = useStripe()
const elements = useElements()
return (
<form onSubmit={async (e) => {
e.preventDefault()
if (!stripe || !elements) return ;
stripe.confirmPayment({
elements,
confirmParams: {
return_url: 'https://example.com/success'
}
})
}}>
<PaymentElement />
<button type="submit">process order</button>
</form>
)
}
cart.tsx
呼び出し元、いわゆる親要素。
PaymentForm
を呼んでいる以外は基本的に見た目にしかフォーカスしていない。
const App:FC = () => {
const { createPaymentIntent, renderPaymentElement } = useStripeElement({
children: <PaymentForm />
})
return (
<div>
<button onClick={() => createPaymentIntent()}>Order</button>
{renderPaymentElement()}
</div>
)
}
@stripe/react-stripe-js
のElementsの引数の型を取得する
Elements
タグの型定義
type Elements = FunctionComponent<PropsWithChildren<ElementsProps>>
ただしElementsProps
がexportされていない。
options
の型だけを取りたいなどの場合は、こう書く。
import { ComponentProps} from "react";
import { Elements } from "@stripe/react-stripe-js";
type ElementsOptions = ComponentProps<typeof Elements>['options']
const elementOptions: ElementsOptions = {
appearance: {
theme: 'flat'
}
}