Stripe Link入門: 迅速かつセキュアな決済手段を、数行のコードで実現する方法

この記事では、Stripeが提供するシンプルかつセキュアな決済手段 Link を決済フォームに導入し、テストする方法を紹介します。Linkによる支払いをサポートすることで、顧客はクレジットカード情報を入力することなく、支払いフローを完了させることができます。決済体験をスムーズにすることで、決済フォームでのカゴ落ちを防止でき、コンバージョン率（ CVR ）の向上や、それに伴う売り上げの増加が期待できます。

![image7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/0d2479c4-3473-7ad2-97c7-5446492bda29.png)

実装に必要なコードの量もとても少なく、テスト環境を使った決済体験のテストや、表示非表示の制御もとても簡単です。この記事とサンプルコードを元に、少ない手間で実現できる新しいCVR改善・売り上げ向上施策のアイディアをお試しください。

## Linkとは

Linkは、Stripeが提供する支払いサービスです。Linkにクレジットカード情報を保存することで、顧客はECサイトやウェブサービスの支払いフォームにカード情報を入力することなく決済やサブスクリプション申し込みを完了できます。

![スクリーンショット-2024-05-01-15.50.29.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/de5655bf-e359-8af1-36a5-6ba914362eff.jpeg)

Linkの特徴は、Stripeの決済フォームやリンクを採用していて、Linkを有効化しているサービスであればどこでも保存したクレジットカード情報を使えることです。そのため、例えばOpen AI社のChat GPT Proに申し込みした後、Anthropic社が提供するClaude Proを申し込もうとした場合にも、Linkで保存した支払い情報を利用して簡単に決済を完了できます。

Linkを導入することでカゴ落ちのリスクを減らし、コンバージョン率を高めることができます。これはカード情報を入力するステップを省略できるためで、[過去にLinkを使ってクレジットカード情報を保存しているユーザーの**コンバージョン率が7％ほど改善した**との結果](https://stripe.com/jp/resources/more/ecommerce-conversion-rate-optimization#%E6%B1%BA%E6%B8%88%E6%99%82%E3%81%AE%E3%83%95%E3%83%AA%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%BB%BD%E6%B8%9B%E3%81%99%E3%82%8B)も出ています。

## Linkを決済フォームに追加する

Linkの導入方法はとてもシンプルです。まず[Stripeダッシュボード](https://dashboard.stripe.com/test/settings/payment_methods)にてLinkを有効化します。これによって[Stripe Checkout](https://stripe.com/jp/checkout)や[Stripe Payment Links](https://stripe.com/jp/payments/payment-links)、[Stripe Invoicing](https://stripe.com/jp/invoicing)・[Stripe Billing](https://stripe.com/jp/billing)などのStripeが提供する決済ページにで支払いを行うフローではLinkが利用できるようになります。

![スクリーンショット 2024-10-25 13.28.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/7fd987b8-76b4-bbdf-54df-36aadf2b04dc.png)

もしCheckoutを利用していてLink決済が表示されない場合は、APIリクエスト時に`payment_method_types`を指定していないかをご確認ください。

```diff_javascript
const ch = await stripe.checkout.sessions.create({
-  payment_method_types: ['card'],
  line_items: [{
    price: 'price_1PvUxTIDF6qBhttt4ZaEsTYm',
    quantity: 1
  }],
  mode: 'payment',
  success_url: 'https://example.com',
  cancel_url: 'https://example.com'
})

```

### Stripe Elementsを使ってLinkの決済をサポートする

アプリケーションに決済フォームを埋め込む場合、フロントエンドのJavaScriptコードをすこし追加する必要があります。まずサーバー側の処理にてPayment Intentを作成しましょう。

```js
app.post('/payment-intent', async c => {
  const stripe = new Stripe('skまたはrkから始まるAPIキー')
  const paymentIntent = await stripe.paymentIntents.create({
    amount: 1000,
    currency: 'jpy'
  })
  return c.json({
    client_secret: paymentIntent.client_secret,
  })
})
```

もし表示する決済手段の制御をサーバー側の処理で行なっている場合は、次のように`payment_method_types`の配列に`link`を追加してください。


```diff_javascript
const paymentIntent = await stripe.paymentIntents.create({
  amount: 1000,
  currency: 'jpy',
-  payment_method_types: ['card'],
+  payment_method_types: ['card', 'link'], // 利用する支払い方法を指定
  // ...その他必要なパラメータ
})
```

続いてフロントエンド側のJavaScriptを少し変更します。今回は、Link導入前の決済フォームが次のようなHTMLで実装されていると想定して変更箇所を紹介します。

```html
<form id="payment-form">

  <h3>Payment</h3>
  <div id="payment-element"></div>

  <button id="submit-button" type="submit">Submit</button>
  <div id="error-message"></div>
</form>

<script>
  const stripe = Stripe('${STRIPE_PUB_API_KEY}');
  window.onload = async () => {
    const response = await fetch('/payment_intent', {
      method: 'POST'
    })
    const result = await response.json()

    // Stripe Elementsを初期化
    const elements = stripe.elements({
      clientSecret: result.client_secret
    })
    const paymentElement = elements.create('payment')
    paymentElement.mount('#payment-element')

    // フォームの送信イベントを処理
    const form = document.getElementById('payment-form')
    form.addEventListener('submit', async (event) => {
      event.preventDefault()
      // Use the clientSecret and Elements instance to confirm the setup
      const result = await stripe.confirmPayment({
        elements,
        confirmParams: {
          return_url: 'http://127.0.0.1:8787/',
        },
      })
    
      console.log(result)
      if (result.error) {
        const errorMessage = document.getElementById('error-message')
        errorMessage.textContent = result.error.code + ':' + result.error.message
      }
    })
  }
</script>
```

まず、Link用のフォーム要素を埋め込むためのHTMLタグを追加します。追加するフォーム要素には、メールアドレスを入力する欄が含まれています。そのため、ラベルを追加する場合は、「連絡先情報」などとするとよいでしょう。

```diff_html
<form id="payment-form">
+  <h3>Contact info</h3>
+  <div id="link-authentication-element"></div>

  <h3>Payment</h3>
  <div id="payment-element"></div>

  <button id="submit-button" type="submit">Submit</button>
  <div id="error-message"></div>
</form>
```

その後、追加したHTMLタグにフォーム要素をマウントするコードを追加します。

```diff_javascript
    // Stripe Elementsを初期化
    const elements = stripe.elements({
      clientSecret: result.client_secret
    })
    const paymentElement = elements.create('payment')
    paymentElement.mount('#payment-element')
    
+    const linkAuthenticationElement = elements.create('linkAuthentication');
+    linkAuthenticationElement.mount("#link-authentication-element");
```

これで実装完了です。4行コードを追加するだけで、新しい決済手段のサポートが完了しました。

この状態でアプリケーションにアクセスすると、次のスクリーンショットのような画面が表示されます。

![スクリーンショット 2024-10-25 13.44.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/af7b3cde-4e54-081d-1fea-753801a33cf2.png)

メールアドレスの入力欄が表示されていれば、実装成功です。

## Link決済の動作をテストする

実装が完了しましたので、動作を確認してみましょう。Link決済は、すでにLinkへログインしているかいないかによってフォームの動作が変わります。

Linkにログインしていない状態では、メールアドレス入力欄が表示されています。ここにメールアドレスを入力すると、そのメールアドレスがLinkに登録されているかいないかをStripe.jsが自動で判定します。

まだLinkアカウントがない場合は、クレジットカード情報の入力後に、決済フォームの下部にLinkを使って入力したカード情報を保存できるフォームが自動表示されます。ここで電話番号を入力し、支払いを完了させると、電話番号・メールアドレスを利用してLinkのアカウントが作成されます。これで次回以降の支払いで保存したカード情報が使えるようになります。

![非Linkユーザーの通常のカード情報入力とLink登録オプション](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/fd335b4f-4f8d-531b-b9f4-3ef8d7d2cd4d.png)

もし過去にLinkで登録したことがある場合は、メールアドレスを入力するとSMS認証画面が表示されます。ここでSMSにて受信したコードを入力すると、過去に保存した決済情報を選択できるUIが自動表示されます。

![SMS認証による未ログインLinkユーザーの決済プロセス](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/20e3bdab-6468-6d4b-a35d-3ca0bc946fc3.png)

:::note
**テスト環境でのLinkの認証について**
テスト環境では、`000000`を入力すると自動で認証が完了します。もし認証に失敗したケースなどもテストしたい場合は、[ドキュメントに記載されているテスト用コード](https://docs.stripe.com/payments/link/add-link-elements-integration?link-integration-type=at-payment#web-test-the-integration)をお使いください。
:::

もちろん保存したクレジットカードを使わずに決済を進めることもできます。

![スクリーンショット 2024-10-25 13.50.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/31ac72c1-eeed-4c86-c88d-6920e5611017.png)

このようにして、Linkの決済フローを簡単にテストすることができます。

### 3Dセキュア認証について

2025年3月から義務化される3Dセキュア認証にもLinkは対応しています。テストモードで登録するカード番号を`4000000000003220`にすると、Linkを使った決済処理でも3Dセキュア認証のテスト画面が表示されます。

![スクリーンショット 2024-10-25 14.15.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/98b352a0-fdc5-4a67-b5a6-6a6a0d1b75a5.png)

## ワンクリックで決済するフローを商品詳細ページに実装する

もう１つのLink決済導入方法が、[Express Checkout Element](https://docs.stripe.com/elements/express-checkout-element)を利用することです。これは1クリックで決済を完了できる決済手段だけを顧客に提示する要素で、商品詳細ページや商品を紹介するブログ記事のCTAなどに利用できます。

![スクリーンショット 2024-10-25 14.25.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/bae71894-786d-2be4-4e1f-dd0e09e9be36.png)

この決済手段を組み込む場合は、ユーザーがボタンをクリックするまでサーバー側の処理は実行しません。まずHTML側で決済要素を表示させる実装を追加しましょう。

```html
<div id="express-checkout-element"></div>
<div id="error-message"></div>

<script>
  const stripe = Stripe('${STRIPE_PUB_API_KEY}');
  window.onload = async () => {
    const elements = stripe.elements({
      mode: 'payment',
      amount: 1000,
      currency: 'jpy',
    })
    const expressCheckoutElement = elements.create('expressCheckout')
    expressCheckoutElement.mount('#express-checkout-element')  
  }
</script>
```

これだけでサイトやアプリケーションに1クリックで決済できるボタンを表示できます。ユーザーがボタンをクリックした後は、「決済操作が完了したこと」をトリガーにJavaScriptを実行し、サーバー側でPayment Intentを作成することで決済を完了させます。


```diff_javascript
const stripe = Stripe('${STRIPE_PUB_API_KEY}');
window.onload = async () => {
  const elements = stripe.elements({
    mode: 'payment',
    amount: 1000,
    currency: 'jpy',
  })
  const expressCheckoutElement = elements.create('expressCheckout')
  expressCheckoutElement.mount('#express-checkout-element')

+  expressCheckoutElement.on('confirm', async e => {
+    const { error: submitError } = await elements.submit()
+    const errorMessage = document.getElementById('error-message')
+    if (submitError) {
+      errorMessage.textContent = submitError.message
+      return
+    }
+
+    const response = await fetch('/payment_intent', {
+      method: 'POST'
+    })
+    const { client_secret: clientSecret } = await response.json()
+
+    const result = await stripe.confirmPayment({
+      elements,
+      clientSecret,
+      confirmParams: {
+        return_url: 'http://127.0.0.1:8787/cs',
+      },
+    });
+  
+    if (result.error) {
+      const errorMessage = document.getElementById('error-message');
+      errorMessage.textContent = result.error.code + ':' + result.error.message;
+    }
+  })    
}
```

これによって、LinkやApple Pay / Google Payを利用した1クリックで注文できるフローを簡単に実装できます。

![スクリーンショット 2024-10-25 14.34.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/d5351b0b-df8c-7976-619d-7466679a930f.png)


:::note warn
**フロントエンドからは決済金額を送信しない**
ユーザーが開発者ツールなどを用いて、支払い金額を書き換えられるリスクがあります。そのため、Payment Intentを作成するAPIリクエストのBodyには金額などに関する情報を含めないようにしましょう。

商品ページでの決済処理であれば、ページのURLなどに含まれる商品IDを送信し、IDを利用してDBから決済金額を集計します。
:::

デザインのカスタマイズや配送料金等の処理については、ドキュメントをご確認ください。

https://docs.stripe.com/elements/express-checkout-element/accept-a-payment

## まとめ

Stripeが提供するLinkをサポートすると、リピート客やLinkユーザーが注文を完了させるための手間を大幅に削減できます。Linkを利用した場合は、決済操作を6秒程度で完了できるとも言われており、その手軽さからコンバージョン率の改善やカゴ落ちリスクを削減することが期待できます。また、クレジットカード情報を入力するステップを省略できるため、決済フォームを改ざんするタイプの攻撃からもユーザーを保護できます。

すでにOpen AI社やAnthropic社なども導入している決済手段で、数行コードを追加するだけで対応が可能ですので、まだ導入されていない方は、ぜひお試しの上導入をご検討ください。

### Stripe PaymentElementとLinkの統合に関する関連ドキュメント:

- Stripe Linkについての詳細は[こちら](https://stripe.com/payments/link)
- Stripe Elementsのセットアップに関する情報は[こちら](https://stripe.com/docs/payments/elements)
- サーバーサイドのコード設定についてのガイドは[PaymentIntents API documentation](https://stripe.com/docs/api/payment_intents)にあります
- Stripe Payment Elementを利用した再利用可能な支払いのセットアップに関する情報は[Future payments with Elements and Link](https://stripe.com/docs/payments/link/save-and-reuse)で確認できます
- 支払いを受け付ける方法については[Accept a payment guide](https://stripe.com/docs/payments/accept-a-payment)を参照してください

https://stripe.com/docs/payments/link/save-and-reuse

https://stripe.com/docs/payments/link/add-link-elements-integration