💵
Next.jsとStripe(Elements)でクレカ決済を実装
2024-12-07
2024-12-08
約5787字
この記事は、「決済を実装した体験談や決済サービス(クレカ、QRコード、何でもOK!)の情報を投稿しよう! by PAY Advent Calendar 2024」8日目の記事です。
Yardでのベータ版をリリースして以降、複数の人(特に個人開発者)から Stripe を使った決済機能の実装について聞かれることがありました。
確かに、自分も決済機能を実装する時に Stripe の仕様やネット上の記事を入念に読み、試行錯誤しながら実装した記憶があるので、初心者にとってはややハードルは高いかもです。(Stripeが複雑というよりは、決済機能自体がミッションクリティカルなので難しい面が多いと思います)
また、副業で決済機能があるプラットフォームサービスの要件定義に関わった際、Stripeの調査をしたこともあり、その時の知見が多少ある感じなので、今回、少し整理してみようと思います。
Stripeは、オンライン決済を簡単かつ安全に行えるプラットフォームです。
ウェブサイトやスマートフォンアプリ上で、クレジットカードなどを使用した決済が可能になります。
また、数百万社以上の企業から利用されており、世界135種類以上の通貨にも対応しています。
Stripeには、多くの機能がありますが、よく使われるものは以下の4つです(個人の見解)。
今回は、Yard で採用しているWebフレームワークの Next.js(TypeScript / Pages Router) で Stripe を使ったクレジットカード決済機能のサンプルを紹介します。
setup_intent
と setup_intent_client_secret
から決済情報を確認し、ステータスが正常なら課金完了、ステータスが無効なら課金失敗の処理をするまず、node.jsの stripe のパッケージをインストールします。
npm install --save stripe @stripe/stripe-js
次に、APIの pages/api/setup-intent.ts を以下のように実装します。
ここでは、まず stripe の Customer を作ります。この Customer は、原則 Yard の会員と1対1で対応することを想定します。そのため、Yard側の会員情報と紐づけるために emailアドレスを入れつつ作成します。また、metadataも任意で入れれるので、ユーザー名なども入れておけます(ここは適宜、セッションやDBから取得した値を入れてください)。
(このCustomer IDをDBなどに保存しておくと、後々、ユーザーにとってクレカ番号を毎回入力しなくて済み使い回すこともできます)
その後、setupIntentsメソッドを使い client_secret
を取得し、クライアントに返します。
payment_method_types に card
を入れるとクレカ決済になります。
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2022-11-15",
});
export default async function handler(
req,
res
) {
const customer = await stripe.customers.create({
email: "xxx@yyy.zz",
metadata: {
app_username: "toitech"
}
})
const response = await stripe.setupIntents.create({
amount: 1000,
currency: 'jpy',
customer: customer.id,
payment_method_types: ['card']
})
return res.status(200).json({
client_secret: setupIntent.client_secret,
customer_id: customer.id,
})
}
pages/payment/index.tsx クライアントサイドの決済ページを以下のように実装します。
CustomFormメソッドの中で、フォームのデザインをカスタマイズしています。
import { loadStripe, StripeElementsOptions } from "@stripe/stripe-js";
import {
Elements,
PaymentElement,
useElements,
useStripe,
} from "@stripe/react-stripe-js";
import { useEffect, useState } from "react";
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
);
const CustomForm = () => {
const stripe = useStripe();
const elements = useElements();
const [message, setMessage] = useState<string | undefined | null>(null);
const [isLoading, setIsLoading] = useState(false);
const handleSubmit: React.FormEventHandler<HTMLFormElement> = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
return;
}
setIsLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: process.env.NEXT_PUBLIC_URL + "/payment/complete",
},
});
if (error.type === "card_error" || error.type === "validation_error") {
setMessage(error.message);
} else {
setMessage("An unexpected error occured.");
}
setIsLoading(false);
};
return (
<form onSubmit={handleSubmit}>
<PaymentElement />
<button type="submit" disabled={isLoading}>
支払いを確定
</button>
{message && <div>{message}</div>}
</form>
);
};
const Payment = () => {
const [clientSecret, setClientSecret] = useState(null);
useEffect(() => {
fetch("/api/setup-intent", {
method: "post",
})
.then((res) => res.json())
.then((data) => {
setClientSecret(data.client_secret);
})
.catch((e) => {
console.log(e);
});
}, [setClientSecret]);
if (!clientSecret) return <p>loading</p>;
const options: StripeElementsOptions = {
appearance: {
theme: "stripe",
},
clientSecret: clientSecret,
};
return (
<Elements stripe={stripePromise} options={options}>
<CustomForm />
</Elements>
);
};
export default Payment;
pages/payment/complete.tsx クライアントサイドの決済完了ページを以下のように実装します。
stripe.setupIntents.retrieveメソッドで、決済が成功したかを確認できます。成功が確認されたら、自前のDBに保存するなりすると履歴が管理できます。
import { GetServerSideProps } from "next";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2022-11-15",
});
const getServerSideProps: GetServerSideProps = async (context) => {
const setup_intent = context.query?.setup_intent;
const setup_intent_client_secret = context.query?.setup_intent_client_secret;
const redirect_status = context.query?.redirect_status;
if (
!setup_intent ||
!setup_intent_client_secret ||
redirect_status != "succeeded"
) {
return {
notFound: true,
};
}
const setupIntent = await stripe.setupIntents.retrieve(
setup_intent as string,
);
if (!setupIntent) {
return {
notFound: true,
};
}
return {
props: { status: setupIntent.status },
};
};
const PaymentComplete = (p: any) => {
return <>{p.status}</>;
};
export default PaymentComplete;
今回は、Stripeの概要とクレカを使ったオンライン決済の機能をNext.jsで実装してみました。
今回は、都度決済の機能を想定して実装しましたが、Stripeでは他にもサブスク、任意の口座への入金(Connectを使う)、オーソリなどの機能もサポートしています。
自分もまだまだ使っていない機能も多いので、今後も機会があれば試してみたいと思います。
Yardは、テック人材に気軽に相談できる情報共有コミュニティです。
ご興味ある方は、登録お願いします!
©︎ 2025 - Yard