Stripe Checkout / Elementsで、コンビニ決済のサポートを開始する３つの方法

Stripeを利用することで、顧客は料金の支払いをコンビニで行えます。また、Stripeユーザーは、**コンビニでの決済処理結果を、Stripe DashboardやWebhookを通して受け取る**ことができます。

コンビニ決済を有効化することで、これまでリーチすることが困難だった「**クレジットカードを持たない顧客**」にリーチすることが可能です。また、顧客も公共交通機関での移動中や、信頼性の低いネットワークを利用している状況など、**「クレジットカードを取り出して入力する」操作がやりにくい場面**でも、安心して注文ができるようになります。

![nm7W8u4S.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/8d46c218-ce94-855e-0b7e-f0779018da76.png)

この記事では、Stripeを利用した決済で、コンビニ決済をサポートする方法について、「Checkout」「PaymentElements」そして「Elements(PaymentElements不使用)」の３種類を紹介します。

# コンビニ決済をサポートする３つの方法

この記事では、Node.jsとReactを例に実装方法を紹介します。phpやRubyなど、その他の言語やフレームワークでの実装については、APIドキュメント(https://stripe.com/docs/api) をご確認ください。記事内で紹介しているメソッド・関数を、それぞれの言語で実行する方法を見ることができます。


## 1: Checkoutを利用して、決済処理を行なっている場合
Stripe Checkoutを利用して決済画面を提供している場合、組み込みはとても簡単です。
決済方法を、コードまたはDashboardから管理ができますので、お使いの管理方法に合わせて変更を行いましょう。


### コード側で決済方法を管理する場合

コードで決済方法を指定している場合、以下の例のように、`payment_method_types`に`konbini`を追加します。

```js
         await stripe.checkout.sessions.create({
            line_items: [{
              price: 'price_xxxxxx',
              quantity: 1
            }],
            success_url: 'http://localhost:3000/success',
            cancel_url:'http://localhost:3000/cancel',
            mode: 'payment',
            payment_method_types: ['card', 'konbini'],
            customer_creation: 'if_required'
          })
```

### Dashboardで決済方法を管理する場合

`payment_method_types`を削除して、ダッシュボードから決済方法( https://stripe.com/docs/payments/dashboard-payment-methods )を設定します。

```diff_javascript
         await stripe.checkout.sessions.create({
            line_items: [{
              price: 'price_xxxxxx',
              quantity: 1
            }],
            success_url: 'http://localhost:3000/success',
            cancel_url:'http://localhost:3000/cancel',
            mode: 'payment',
-            payment_method_types: ['card'],
            customer_creation: 'if_required'
          })
```

### 表示を確認する

変更が完了すると、Checkoutの支払い方法に「コンビニ決済」が表示されます。

![YTzCxnsQ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/77ed0fb3-fdec-c9dc-9ece-ba56c387272a.png)


## 2: ElementsのPaymentElementを利用している場合
PaymentElementを利用して決済フォームを実装している場合についても、ほとんど設定の変更が必要ありません。
PaymentIntentを作成する際に、`payment_method_types`を指定している場合のみ、`konbini`を追加しましょう。

```js
        const paymentIntent = await stripe.paymentIntents.create({
            amount: 2000,
            currency: 'jpy',
            payment_method_types: ['konbini', 'card'],
        })
```

以下の例のように、`payment_method_options['konbini']`に追加情報を登録することもできます。

```js
        const paymentIntent = await stripe.paymentIntents.create({
            amount: 2000,
            currency: 'jpy',
            payment_method_types: ['konbini', 'card'],
            payment_method_options: {
                konbini: {
                    product_description: "Tシャツ",
                    expires_after_days: 3,
                    tax_amount: 200
                }
            }
        })
```

あとはPaymentIntentのClient SecretをStripe.jsに渡すだけで、PaymentElementがコンビニ決済を選択できるように表示を変更します。

![BHOfUmLN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/2b0c7f3c-1edb-17e0-43e5-1f5448a7c810.png)

カードなどの処理と同様、`confirmPayment`を実行すると、PaymentIntentがconfirmされます。
![C-NRGZpc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/86477536-3f2d-5210-fb4a-2450015fec63.png)


## 3: ElementsでPayment Elementを利用して「いない」場合
Elementsを利用する場合、基本的にはPaymentElementを利用することをお勧めします。
しかしサイトデザインの要件を満たすために、PaymentElement以外のElementを利用する必要があるケースでは、上記のPaymentElementを利用した実装が行えません。

Payment Elementを利用せずに実装する場合、フロントエンドにも追加の実装が必要です。
最も簡単な方法は、「**コンビニ決済を処理するためのformを、新しく用意すること**」です。

まず、「氏名・メールアドレス・電話番号」を入力するフォームを用意します。

```jsx
    const KonbiniForm: FC = () => {
        const [name, setName] = useState("")
        const [email, setEmail] = useState('')
        const [phone, setPhone] = useState('')
        return (
            <form onSubmit={async (e)=>{
                e.preventDefault()
             }>
                <div>
                    <label>電話番号</label>
                    <input type='tel' value={phone} onChange={e=>setPhone(e.target.value)} /> 
                </div>
                <div>
                    <label>名前</label>
                    <input type='text' value={name} onChange={e=>setName(e.target.value)} /> 
                </div>
                <div>
                    <label>メールアドレス</label>
                    <input type='email' value={email} onChange={e=>setEmail(e.target.value)} /> 
                </div>
                <button type='submit' disabled={status === 'inprogress'}>注文する</button>
            </form>
        )
    }
```

その後、PaymentIntentを作成するAPI呼び出しと、コンビニ決済を処理するメソッドを呼び出す実装をsubmitイベントに設定します。


```jsx
    const KonbiniForm: FC = () => {
        const stripe = useStripe()
        const [name, setName] = useState("")
        const [email, setEmail] = useState('')
        const [phone, setPhone] = useState('')
        return (
            <form onSubmit={async (e)=>{
                e.preventDefault()
                try {
                    if (!stripe) return;            
                    const intent = await fetch('/api/payment_intent', {
                        method: 'post'
                    }).then(data=>data.json())
                    const result = await stripe.confirmKonbiniPayment(intent.client_secret, 
                        {
                          payment_method: {
                            billing_details: {
                              name,
                              email,
                            },
                          },
                          payment_method_options: {
                            konbini: {
                              confirmation_number: phone,
                            },
                          },
                        }
                    )
                    console.log(result)
                } catch(e) {
                    console.log(e)
                }
            }}>
    ...
```

`confirmKonbiniPayment`が成功すると、こちらもコンビニでの支払い情報がモーダル表示されます。
もし独自のUIで情報を表示させたい場合は、以下のように引数を追加します。

```js
                    const result = await stripe.confirmKonbiniPayment(intent.client_secret, 
                        {
                          payment_method: {
                            billing_details: {
                              name,
                              email,
                            },
                          },
                          payment_method_options: {
                            konbini: {
                              confirmation_number: phone,
                            },
                          },
                        },
                        {
                            handleActions: false
                        }
                    )
```
`handleActions: false`を設定すると、モーダルが表示されなくなります。
コンビニでの支払いのために必要な情報は、`result.paymentIntent.next_action`から取得することができます。

![7RRPyAKx.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/9f96c29c-3ef7-ab3d-9a7b-9de1840bcba4.png)


この中には、以下の情報が含まれています。

- 支払い期日（`konbini_display_details.expires_at`）
- Stripeが用意する支払い方法ページのURL（`konbini_display_details.hosted_voucher_url`）
- コンビニチェーンごとの入力情報(確認番号と支払い番号)
    （`konbini_display_details[familymart|lawson|ministop|seicomart]`）

コンビニのロゴ・アイコン画像などは含まれません。実装時に自分で用意する必要がありますので、ご注意ください。


# コンビニ決済をサポートする際の注意点

## 利用開始前に審査を受ける必要があります

コンビニ決済を有効化するには、事前に審査を受ける必要があります。

この審査は最低でも３週間程度かかりますので、なるべく早いタイミングで申請を行うようにしましょう。
また審査では、「特定商取引法に基づく表記がされているか」や「禁止業種に該当しないか」・「Stripeアカウントの本番環境申請が完了しているか」などが確認されます。

情報が不足している場合には、メールにてご連絡いたしますが、審査の完了までに追加の時間が発生しますのでご注意ください。

## 注文完了時点では、決済が完了していません
コンビニ決済での注文の場合、実際に顧客が代金を支払うまでに時間差があります。

そのため、カード決済のみ対応していたサービスなどでは、「決済が完了したことを確認できるまで、発送業務等の後続処理を行わない」実装になっているかを確認しましょう。
もし決済完了を確認せずに後続の業務を行なった場合、実際には決済が完了できていない（コンビニでの支払いが実行されなかった）場合にも商品を発送してしまうなどの問題が発生します。

### Checkoutを利用している場合

Checkoutを利用した決済では、まず`checkout.session.completed`イベントを受け付けます。Webhookイベント内の`"payment_status"`をチェックし、カード決済などで支払い済みの場合(`payment_status=paid`)ならば後続業務を実行します。

コンビニ決済など、支払いが未完了の場合は、`payment_status=unpaid`ですので、在庫の確保を続けるだけにするなど、決済の完了を待つシステムにする必要があります。

顧客の決済が完了した場合には、追加で`checkout.session.async_payment_succeeded`イベントが発生します。こちらのイベントを受け付けた場合にはじめて発送等の業務を開始させましょう。

### Elements(PaymentIntent)を利用している場合

Elements(PaymentIntent)を利用している場合、カード決済でもコンビニ決済でも`payment_intent.succeeded`イベントの発火で後続業務を開始できます。

## Webhookを利用した期限切れの処理が必要になるケースがあります
コンビニ決済では、設定した期限内に支払いが行われないケースも発生します。その場合、確保している在庫を開放するなどの操作が必要になります。
この操作を実装するには、Webhookで以下のイベントを受け付ける必要があります。

- Checkoutを利用したケース：`checkout.session.async_payment_failed`
- Elements(PaymentIntent)を利用したケース: `payment_intent.payment_failed`

また、支払いが完了していない顧客に、メールを送信する仕組みも用意されています。
[設定 > メール]から設定することができますので、こちらも合わせてご利用ください。

![Uvs2KO3-.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/c3ca92d9-8242-9234-bb4b-6dc1e648cd3f.png)

Webhookを利用するコードサンプルなどは、以下の記事をご覧ください。

https://qiita.com/hideokamoto/items/f0304cb005f276e35d43

## 手数料の最低金額が設定されています
コンビニ決済を利用する場合、決済手数料は最低でも120円が発生します。
基本の決済手数料が3.6%ですので、**おおよそ3,300円以下の商品については、決済手数料が通常よりも高く**なります。

## 返金に手数料がかかります

顧客からの要求などで返金を行う場合にも、¥250 + 10% 消費税 (計 ¥ 275)の手数料が発生します。
ご注意ください。


# おわりに

コンビニ決済をサポートすることで、クレジットカードを持たない顧客でもサービスを利用できるようになります。

すでに本番環境で Stripe アカウントをご利用の場合、ダッシュボード上から審査手続きを開始することができます。

「設定」＞「支払い方法」より、「有効にする」ボタンをクリックして、サポートを開始しましょう。


# [関連記事]コンビニ決済入門シリーズ

## Webhookでの処理方法

https://qiita.com/hideokamoto/items/f0304cb005f276e35d43

## Billing（サブスクリプション）への導入方法

https://qiita.com/hideokamoto/items/605096153bdf473895f0

## Connectを利用したマーケットプレイスへの導入方法

https://qiita.com/hideokamoto/items/9a4258ad5a41440274a2