Next.js / React.jsでStripe実装
2022.05.06
目次
こんにちわ!
WebエンジニアのSekineです。
概要
- タイトルにある通り、ReactでStripe決済するためのクレジットカード登録の処理実装をしました
- 処理方式としては、フロントエンドとStripeで完結させるのではなく、Stripeから生成されたトークンを取得し、それをバックエンドのAPIに渡します
- バックエンド処理にて、そのトークンでStripeへクレジットカード登録という方式にしたかったので、そのように実装してみました
- UIに関しては、デザイナーがデザインした内容で実装したいため、ライブラリが提供している
CardElement
をそのままコンポーネントとして、使うのではなく、分割されている下記のコンポーネントを組み合わせて実装しましたCardNumberElement
:カード番号CardExpiryElement
:有効期限CardCvcElement
:セキュリティコード
開発ドキュメント
- 公式サイトを参考にしながら、実装しました
UI

技術スタック
- Next.js
- React.js
- Tailwind CSS
ソースコード抜粋
ライブラリから必要なものをインポート
import {
CardCvcElement,
CardExpiryElement,
CardNumberElement,
useElements,
useStripe,
} from "@stripe/react-stripe-js";
import {
StripeCardCvcElementChangeEvent,
StripeCardExpiryElementChangeEvent,
StripeCardNumberElementChangeEvent,
} from "@stripe/stripe-js";
チェンジイベントでエラーを捕捉し、エラーメッセージ表示用のローカルstateを定義
const [state, setErrors] = useState<{
cardNumberElementErrorMessage: string;
cardExpiryElementErrorMessage: string;
cardCvcElementErrorMessage: string;
formSubmitErrorMessage: string;
}>({
cardNumberElementErrorMessage: "",
cardExpiryElementErrorMessage: "",
cardCvcElementErrorMessage: "",
formSubmitErrorMessage: "",
});
const onChangeCardNumberElement = (e: StripeCardNumberElementChangeEvent) => {
if (e.error) {
const cardNumberElementErrorMessage = e.error.message;
setErrors((prev) => ({
...prev,
cardNumberElementErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
cardNumberElementErrorMessage: "",
}));
}
};
const onChangeCardExpiryElement = (e: StripeCardExpiryElementChangeEvent) => {
if (e.error) {
const cardExpiryElementErrorMessage = e.error.message;
setErrors((prev) => ({
...prev,
cardExpiryElementErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
cardExpiryElementErrorMessage: "",
}));
}
};
const onChangeCardCvcElement = (e: StripeCardCvcElementChangeEvent) => {
if (e.error) {
const cardCvcElementErrorMessage = e.error.message;
setErrors((prev) => ({
...prev,
cardCvcElementErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
cardCvcElementErrorMessage: "",
}));
}
};
カード情報入力用のパーツ
<CardNumberElement
onChange={onChangeCardNumberElement}
className={
"col-span-12 md:col-span-8 xl:col-span-5 xl:col-start-4 mt-1 xl:mt-0 h-8 border-0.5 form-input border-gray-darkest"
}
/>
{state.cardNumberElementErrorMessage && (
<div
className={
"col-span-12 md:col-span-8 xl:col-span-3 mt-1 text-xs text-red-vivid"
}
>
{state.cardNumberElementErrorMessage}
</div>
)}
...
<CardExpiryElement
onChange={onChangeCardExpiryElement}
className={
"col-span-3 md:col-span-4 xl:col-span-2 xl:col-start-4 mt-1 xl:mt-0 md:mr-10 h-8 border-0.5 form-input border-gray-darkest"
}
/>
{state.cardExpiryElementErrorMessage && (
<div
className={
"col-span-12 md:col-span-8 xl:col-span-3 mt-1 text-xs text-red-vivid"
}
>
{state.cardExpiryElementErrorMessage}
</div>
)}
...
<CardCvcElement
onChange={onChangeCardCvcElement}
className={
"col-span-3 md:col-span-4 xl:col-span-2 xl:col-start-4 mt-1 xl:mt-0 md:mr-10 h-8 border-0.5 form-input border-gray-darkest"
}
/>
{state.cardCvcElementErrorMessage && (
<div
className={
"col-span-12 md:col-span-8 xl:col-span-3 mt-1 text-xs text-red-vivid"
}
>
{state.cardCvcElementErrorMessage}
</div>
)}
ボタン押下時にstripeが提供している要素を取得して、トークン生成依頼メソッドの引数に渡して、実行する
const stripe = useStripe();
const elements = useElements();
- メソッド呼び出し、要素取得時に必要なため、変数へ代入
const onSubmit: FormEventHandler = async (event) => {
event.preventDefault();
const cardNumberElement = elements?.getElement("cardNumber");
if (!stripe || !cardNumberElement) {
return;
}
const { error, token } = await stripe.createToken(cardNumberElement);
console.log("error:", error);
if (error && error.message) {
const formSubmitErrorMessage = error.message;
setErrors((prev) => ({
...prev,
formSubmitErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
formSubmitErrorMessage: "",
}));
}
if (!token) return;
// TODO:トークンをBEへpostする
};
- tokenのレスポンスとしては、下記のようなJSONが返却されますので、BEは、idの値をpostすればOKということになります!
{
"id": "tok_1Kw2Rxxxxxxxxxxxx",
"object": "token",
"card": {
"id": "card_1Kw2Rkxxxxxxxxxxx",
"object": "card",
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"cvc_check": "unchecked",
"dynamic_last4": null,
"exp_month": 12,
"exp_year": 2032,
"funding": "credit",
"last4": "4242",
"name": null,
"tokenization_method": null
},
"client_ip": "xxx.xx.xxx.xx",
"created": 1651748532,
"livemode": false,
"type": "card",
"used": false
}
こんな感じです!
Stripeの公式サイト、APIリファレンス、提供ライブラリをとっても、とてもわかりやすかったです!!
最後までお読みいただきまして、ありがとうございました!
弊社では、エンジニアを募集しています!是非、一緒に良いプロダクトを作りませんか??
株式会社マチス教育システムまで
こんにちわ!
WebエンジニアのSekineです。
概要
- タイトルにある通り、ReactでStripe決済するためのクレジットカード登録の処理実装をしました
- 処理方式としては、フロントエンドとStripeで完結させるのではなく、Stripeから生成されたトークンを取得し、それをバックエンドのAPIに渡します
- バックエンド処理にて、そのトークンでStripeへクレジットカード登録という方式にしたかったので、そのように実装してみました
- UIに関しては、デザイナーがデザインした内容で実装したいため、ライブラリが提供している
CardElement
をそのままコンポーネントとして、使うのではなく、分割されている下記のコンポーネントを組み合わせて実装しましたCardNumberElement
:カード番号CardExpiryElement
:有効期限CardCvcElement
:セキュリティコード
開発ドキュメント
- 公式サイトを参考にしながら、実装しました
UI
技術スタック
- Next.js
- React.js
- Tailwind CSS
ソースコード抜粋
ライブラリから必要なものをインポート
import {
CardCvcElement,
CardExpiryElement,
CardNumberElement,
useElements,
useStripe,
} from "@stripe/react-stripe-js";
import {
StripeCardCvcElementChangeEvent,
StripeCardExpiryElementChangeEvent,
StripeCardNumberElementChangeEvent,
} from "@stripe/stripe-js";
チェンジイベントでエラーを捕捉し、エラーメッセージ表示用のローカルstateを定義
const [state, setErrors] = useState<{
cardNumberElementErrorMessage: string;
cardExpiryElementErrorMessage: string;
cardCvcElementErrorMessage: string;
formSubmitErrorMessage: string;
}>({
cardNumberElementErrorMessage: "",
cardExpiryElementErrorMessage: "",
cardCvcElementErrorMessage: "",
formSubmitErrorMessage: "",
});
const onChangeCardNumberElement = (e: StripeCardNumberElementChangeEvent) => {
if (e.error) {
const cardNumberElementErrorMessage = e.error.message;
setErrors((prev) => ({
...prev,
cardNumberElementErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
cardNumberElementErrorMessage: "",
}));
}
};
const onChangeCardExpiryElement = (e: StripeCardExpiryElementChangeEvent) => {
if (e.error) {
const cardExpiryElementErrorMessage = e.error.message;
setErrors((prev) => ({
...prev,
cardExpiryElementErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
cardExpiryElementErrorMessage: "",
}));
}
};
const onChangeCardCvcElement = (e: StripeCardCvcElementChangeEvent) => {
if (e.error) {
const cardCvcElementErrorMessage = e.error.message;
setErrors((prev) => ({
...prev,
cardCvcElementErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
cardCvcElementErrorMessage: "",
}));
}
};
カード情報入力用のパーツ
<CardNumberElement
onChange={onChangeCardNumberElement}
className={
"col-span-12 md:col-span-8 xl:col-span-5 xl:col-start-4 mt-1 xl:mt-0 h-8 border-0.5 form-input border-gray-darkest"
}
/>
{state.cardNumberElementErrorMessage && (
<div
className={
"col-span-12 md:col-span-8 xl:col-span-3 mt-1 text-xs text-red-vivid"
}
>
{state.cardNumberElementErrorMessage}
</div>
)}
...
<CardExpiryElement
onChange={onChangeCardExpiryElement}
className={
"col-span-3 md:col-span-4 xl:col-span-2 xl:col-start-4 mt-1 xl:mt-0 md:mr-10 h-8 border-0.5 form-input border-gray-darkest"
}
/>
{state.cardExpiryElementErrorMessage && (
<div
className={
"col-span-12 md:col-span-8 xl:col-span-3 mt-1 text-xs text-red-vivid"
}
>
{state.cardExpiryElementErrorMessage}
</div>
)}
...
<CardCvcElement
onChange={onChangeCardCvcElement}
className={
"col-span-3 md:col-span-4 xl:col-span-2 xl:col-start-4 mt-1 xl:mt-0 md:mr-10 h-8 border-0.5 form-input border-gray-darkest"
}
/>
{state.cardCvcElementErrorMessage && (
<div
className={
"col-span-12 md:col-span-8 xl:col-span-3 mt-1 text-xs text-red-vivid"
}
>
{state.cardCvcElementErrorMessage}
</div>
)}
ボタン押下時にstripeが提供している要素を取得して、トークン生成依頼メソッドの引数に渡して、実行する
const stripe = useStripe();
const elements = useElements();
- メソッド呼び出し、要素取得時に必要なため、変数へ代入
const onSubmit: FormEventHandler = async (event) => {
event.preventDefault();
const cardNumberElement = elements?.getElement("cardNumber");
if (!stripe || !cardNumberElement) {
return;
}
const { error, token } = await stripe.createToken(cardNumberElement);
console.log("error:", error);
if (error && error.message) {
const formSubmitErrorMessage = error.message;
setErrors((prev) => ({
...prev,
formSubmitErrorMessage,
}));
} else {
setErrors((prev) => ({
...prev,
formSubmitErrorMessage: "",
}));
}
if (!token) return;
// TODO:トークンをBEへpostする
};
- tokenのレスポンスとしては、下記のようなJSONが返却されますので、BEは、idの値をpostすればOKということになります!
{
"id": "tok_1Kw2Rxxxxxxxxxxxx",
"object": "token",
"card": {
"id": "card_1Kw2Rkxxxxxxxxxxx",
"object": "card",
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"cvc_check": "unchecked",
"dynamic_last4": null,
"exp_month": 12,
"exp_year": 2032,
"funding": "credit",
"last4": "4242",
"name": null,
"tokenization_method": null
},
"client_ip": "xxx.xx.xxx.xx",
"created": 1651748532,
"livemode": false,
"type": "card",
"used": false
}
こんな感じです!
Stripeの公式サイト、APIリファレンス、提供ライブラリをとっても、とてもわかりやすかったです!!
最後までお読みいただきまして、ありがとうございました!
弊社では、エンジニアを募集しています!是非、一緒に良いプロダクトを作りませんか??
株式会社マチス教育システムまで