Stripe.jsを利用した決済フォームで、オートコンプリート付きの住所フォームを埋め込む方法 [React編]

Stripe.jsを利用して、サイトに決済フォームを埋め込む際、`<AddressElement>`を利用することで、配送先や請求先住所を入力するフォームを**オートコンプリート付き**で実装できます。

今回は、`AddressElement`を`PaymentElement`と併せて使う方法を紹介します。

## ライブラリのインストール・アップグレード

まずはJSライブラリをインストールまたはアップグレードしましょう。

```bash
$ npm i @stripe/stripe-js @stripe/react-stripe-js
```

`@stripe/react-stripe-js`のバージョンが`1.11.0`**よりも前**の場合は、アップグレードしましょう。

```bash
$ npm i @stripe/react-stripe-js@latest
```

## ReactでAddressElementを配置する

要素の配置は、Payment Elementなどの決済フォーム要素と同じです。

Reactの場合、`Elements`の子要素で配置する必要がある点に注意しましょう。

```js
import { loadStripe } from '@stripe/stripe-js'
import { AddressElement, Elements } from '@stripe/react-stripe-js'

export default function Home() {
  return (
        <Elements
          stripe={loadStripe('pk_test_xxx')}
        >
          <AddressElement
            options={{
              mode: 'billing',
            }}
            onChange={e => {
              if (e.complete) {
                console.log(e.value)
              }
            }}
          />
        </Elements>
  )
}
```

埋め込みに成功すると、次のように表示されます。

![スクリーンショット 2022-11-19 10.05.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/d62a36c8-44ee-3c68-ab1e-66e44481a60e.png)

国を選択すると、住所入力フォームが動的に変化します。

![スクリーンショット 2022-11-19 10.06.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/1f035708-20aa-6c5b-d97c-0b614def2579.png)

## 入力できる住所を、日本のみ（または任意の国のみ）に指定する方法

ECサイトでは、商品の発送を国内限定にしたり、特定地域のみに絞る必要があります。

その場合は、`AddressElement`の`allowedCountries`を指定しましょう。

### 住所要素を「日本のみ」にする

`allowedCountries`に`['JP']`を設定すると、日本の住所のみを受け付けます。

```diff_javascript
          <AddressElement
            options={{
              mode: 'billing',
+              allowedCountries: ['JP'],
            }}
          />
```

氏名と郵便番号フォームが表示され、国を指定する要素が非表示になりました。

![スクリーンショット 2022-11-21 13.18.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/a90e37d8-edc0-dc0e-33ac-175c3d9b2976.png)

郵便番号をセットすると、残りのフォームが表示されます。

![スクリーンショット 2022-11-21 13.19.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/fb35dd1e-9cd5-0a20-6d0a-04f05bb32eef.png)

## オートコンプリートを有効にする

`AddressElement`では、一定の条件を満たすとオートコンプリートが利用できます。

最も簡単な方法は、「同一の`Elements`要素内に、`PaymentElements`要素を配置すること」です。

**オートコンプリートが有効になる例**

```tsx
        <Elements
          stripe={loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_API_KEY)}
          options={{
            clientSecret: 'pi_xxxx_secret_xxxx'
          }}
        >
          <PaymentElement />
          <AddressElement
            options={{
              mode: 'billing',
              allowedCountries: ['JP'],
            }}
          />
        </Elements>
```

この場合、Checkout同様郵便番号を入力すると、住所のオートコンプリートが利用できます。

![スクリーンショット 2022-11-21 13.20.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/be132584-4893-5330-9016-de74c07b995d.png)


## 入力された住所を`useState`などで管理する

`AddressElement`の`onChange`イベントで、入力内容を取得できます。

```jsx
  <AddressElement
    onChange={e => {
      // 全ての項目が埋まっている場合、trueになる
      if (e.complete) {
        // 住所や氏名などの値
        console.log(e.value)
      }
   }}
 />
```

`onChange`の引数は、次のようなデータが含まれています。

```js
{
  elementType: 'address',
  elementMode: 'billing',
  value: {
    name: 'name',
    address: {
      line1: '千代田区千代田',
      line2: '11',
      city: '千代田区',
      country: 'JP',
      postal_code: '100-0001',
      state: '東京都'
    }
  },
  empty: false,
  complete: true,
  isNewAddress: true
}
```

`empty`や`complete`などを利用することで、フォームの入力状態についての状態管理も`AddressElement`に任せやすくなります。

なお、TypeScriptを利用している場合、次のようにして、型情報を取得できます。

```ts
import { StripeAddressElementChangeEvent } from '@stripe/stripe-js'

const onAddressChange = (e: StripeAddressElementChangeEvent) => {
  const { name, address } = e.value
}
```

## 別システムが持つ住所情報を利用する方法

既存のシステムで住所情報をすでに保存している場合、顧客が住所を再び入力する手間を省略することができます。

### デフォルト値として設定する方法

1つ目の方法は、`defaultValues`を使います。

すでにシステムが持っている住所情報を、デフォルト値として設定することができます。

```jsx
  <AddressElement
    options={{
      mode: 'billing',
      defaultValues: {
        name: "John due",
        address: {
          line1: '千代田区千代田',
          line2: '11',
          city: '千代田区',
          country: 'JP',
          postal_code: '100-0001',
          state: '東京都'
        }
      }
    }}
 />
```

### 複数の住所を選択できるようにする方法

複数の住所を保存している場合は、`contacts`も利用できます。

```jsx
        <AddressElement
          options={{
            mode: 'billing',
            allowedCountries: ['JP'],
            contacts: [{
              name: "John due",
              address: {
                line1: '千代田区千代田',
                line2: '11',
                city: '千代田区',
                country: 'JP',
                postal_code: '100-0001',
                state: '東京都'
              }
            }, {
              name: "Jane due",
              address: {
                line1: '千代田区丸の内',
                line2: '11',
                city: '千代田区',
                country: 'JP',
                postal_code: '100-0005',
                state: '東京都'
              }
            }]
          }}
```

`contact`を利用すると、複数の住所を顧客が指定できるUIが表示されます。

![スクリーンショット 2022-11-21 13.50.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/51a9027c-d059-2005-25c1-f60ab9ebe55e.png)


## 決済フォームの住所フォームに、AddressElementを利用するメリット

通常のフォーム要素を利用する場合、配送先・請求先住所を決済と紐づけるには、明示的にデータを渡す必要があります。

```js

          const result = await stripe.confirmPayment({
            elements,
            confirmParams: {
              payment_method_data: {
                billing_details: {
                  name: "John due",
                  address: {
                    line1: '千代田区千代田',
                    line2: '11',
                    city: '千代田区',
                    country: 'JP',
                    postal_code: '100-0001',
                    state: '東京都'
                  }
                }
              },
              shipping: {
                name: "Jane due",
                address: {
                  line1: '千代田区千代田',
                  line2: '22',
                  city: '千代田区',
                  country: 'JP',
                  postal_code: '100-0001',
                  state: '東京都'
                }
              }
            },
            redirect: 'if_required',
          })
```

しかし`PaymentElement`と`AddressElement`が同一の`Elements`プロバイダーに存在する場合、`AddressElement`の情報はStripe.js側で自動的にStripeへ送信されます。

![スクリーンショット 2022-11-21 14.27.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/eac8181b-db6c-6525-a1e3-9994674c8ed7.png)

そのため、フォーム周りの状態管理などのコード量を削減することができます。

## 関連記事

https://stripe.com/docs/elements/address-element

https://stripe.com/docs/elements/address-element/collect-addresses

https://stripe.com/docs/js/element/address_element

## [PR] Stripe開発者向け情報をQiitaにて配信中！

- [Stripe Updates]：開発者向けStripeアップデート紹介・解説
- ユースケース別のStripe製品や実装サンプルの紹介
- Stripeと外部サービス・OSSとの連携方法やTipsの紹介
- 初心者向けのチュートリアル(予定)

など、Stripeを利用してオンラインビジネスを始める方法について週に２〜３本ペースで更新中です。

-> [Stripe Organizationsをフォローして最新情報をQiitaで受け取る](https://qiita.com/organizations/stripe)