この記事は、Twilio Advent Calendar 2022 16日目の記事です。
ECやサブスクリプションビジネスにおいて、顧客との連絡手段を確保することはとても重要です。
中でも電話番号は、商品の発送時や、本人確認などのSMSを送信する時にも利用することがあります。
Stripeの場合、「リダイレクト型決済」のCheckoutでは、電話番号の情報入力もStripe側に任せることができます。
ただしElementsでサイトやアプリに埋め込む場合には、フィールドの作成などある程度の開発が必要です。
この記事では、ユーザーの操作ミスで、誤った電話番号が入力されたまま申し込みが完了しないように、TwilioのLookup APIを利用したバリデーションを組み込む方法を紹介します。
電話番号の検証付き決済フォームを実装する
Step1: クイックスタートのアプリをDLする
今回はStripeの組み込み作業を省力化するため、クイックスタートのサンプルアプリを利用します。
記事と同じ設定で進めたい方は、以下の設定で[アプリ全体をダウンロード]ボタンをクリックしましょう。
- プラットフォーム: Web
- フロントエンド: React
- バックエンド: Node
- フロー: カスタムの決済フロー
stripe.com/docs のサンプルアプリは、ダッシュボードにログインした状態でDLしましょう。
サンプルコード内のAPIキーが、現在ログインしているアカウントのものに自動で切り替わります。
DLしたアプリのライブラリインストールと、サービスの起動を行いましょう。
% npm install
% npm start
http://localhost:3000/checkout にアクセスすると、決済フォームが表示されます。
Step2: 電話番号入力フォームを追加する
サンプルアプリの用意ができましたので、早速電話番号の入力フォームを追加します。
まずは必要なライブラリを追加します。
$ npm install validator twilio
src/CheckoutForm.jsx
を次のように変更しましょう。
<PaymentElement id="payment-element" options={paymentElementOptions} />
+ <div
+ style={{
+ display: "flex",
+ justifyContent: "start",
+ flexDirection: "column",
+ margin: "10px auto",
+ }}
+ >
+ <label htmlFor="phone">Phone:</label>
+ <input
+ type="tel"
+ id="phone"
+ style={{
+ border: "1px solid rgb(230, 230, 230)",
+ width: "100%",
+ padding: "7px",
+ boxSizing: "border-box",
+ }}
+ />
+ </div>
<button disabled={isLoading || !stripe || !elements} id="submit">
電話番号入力フォームが表示されました。
入力された番号を、React Hookでstateに格納しましょう。
const [isLoading, setIsLoading] = useState(false);
+ const [phoneNumber, setPhoneNumber] = useState('')
useEffect(() => {
...
<input
type="tel"
id="phone"
style={{
border: "1px solid rgb(230, 230, 230)",
width: "100%",
padding: "7px",
boxSizing: "border-box",
}}
+ onChange={e => {
+ setPhoneNumber(e.target.value)
+ }}
/>
Step3: 入力された電話番号を検証する
決済処理の中で、電話番号の検証を行いましょう。
まずは検証のためのAPIを用意します。
server.js
を次のように変更します。
const express = require("express");
+const { Twilio } = require("twilio");
+const client = new Twilio('username', 'password')
...
+app.post("/verify-phone-number", async (req, res) => {
+ const { 'phone_number': phoneNumber } = req.body;
+ try {
+ await client.lookups
+ .phoneNumbers(`+81${phoneNumber}`)
+ .fetch({ type: ['carrier'] });
+ res.status(200).send()
+ } catch (e) {
+ if (e.code === 20404) {
+ res.status(400).json({
+ message: "Invalid phone number"
+ })
+ return
+ }
+ res.status(e.status).json(e)
+ }
+});
app.listen(4242, () => console.log("Node server listening on port 4242!"));
TwilioのLookup APIを利用して、電話番号の検索を行います。
存在しない電話番号の場合には、エラーを返します。
今回は便宜上国コード(+81
)を直接設定しています。
実際の運用では、入力フォーム側でコードを設定するか、自動判定するように実装しましょう。
続いてsrc/CheckoutForm.jsx
で、追加したAPIを呼び出す処理を追加しましょう。
+ const [errorMessage, setErrorMessage] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
// Stripe.js has not yet loaded.
// Make sure to disable form submission until Stripe.js has loaded.
return;
}
setIsLoading(true);
+ const result = await fetch('/verify-phone-number', {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ phone_number: phoneNumber
+ })
+ });
+ if (!result.ok) {
+ const { message } = await result.json();
+ setErrorMessage(message);
+ setIsLoading(false);
+ return;
+ }
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "http://localhost:3000",
},
});
これで、フォームのSubmit処理の中で、電話番号の検証も行えるようになりました。
日本以外または存在しない電話番号を入力すると、エラーが発生し、決済が中断されるようになります。
Verify APIでの、より強力な検証
Twilioでは、電話番号の確認だけでなくSMSや音声通話で確認コードを送信することもできます。
この機能を利用することで、より強力な本人確認を実装できます。