Nuxt から PAY.JP で Apple Pay のサンプルを利用する

Apple Pay を Web アプリで利用したいと思い利用方法を調べた結果です。

# Apple Pay について

公式に日本語のドキュメントがあるのでそこを見れば大体は理解できると思います。

その中で開発者として気をつけないければならないこととしては、 **ペイメントサービスプロバイダと連携する** ことが強く推奨されています。

https://developer.apple.com/jp/apple-pay/planning/

>SDKやJS APIに関しては、Apple Payをサポートするペイメントサービスプロバイダが提供するものを使用することが強く推奨されています。別の方法としては、AppやWebサイトからの支払いの受け取り、ペイメントトークンの復号、承認のためのペイメントサービスプロバイダとのやり取りといった、一連のプロセスを実行するためのサーバサイドソリューションをデベロッパ自身で用意することもできます。クレジットやデビットカードによる支払いへの対応は複雑になる場合があります。そのため、専門知識を有しているか、すでにシステムが用意されている場合でない限り、ペイメントサービスプロバイダが提供するApple PayのSDKやJS APIを使用することが、AppやWebサイトにApple Payをサポートする上で最も簡単かつ安全な方法です。詳しくは、ご利用のプラットフォームまたはペイメントサービスプロバイダにお問い合わせください

つまり、「素人は下手に手を出さず、専門の方々がラップしたものを利用してくれ」というとこだと察します。

現時点(2019/09/17)では以下の１０社がラッパを提供してくれているようです。

<img width="300" alt="スクリーンショット 2019-09-17 2.09.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49585/8695e8c9-3bd0-f6ff-d793-1d893c59479f.png">

https://developer.apple.com/jp/apple-pay/payment-platforms/

この中から今回は PAY.JP を使用して Apple Pay を実装しようと思います。

# 準備

- create-nuxt-app のインストール
  - Nuxt アプリの雛形を作成してくれるものです。今回はこちらから作成します。
- PAY.JP のアカウント登録

# 問題

[こちらのサンプル]() にある通り、 pay.jp で Apple Pay を利用するためのモジュールは script タグでロードされることを想定しており、 package.json で管理できません。
なので Vue コンポーネントのヘッダーで外部リソースとして読み込む必要がありますが、これが読み込めず...。

以下自分が試したことですが、全て失敗に終わったのでスキップしていただいて構いません。

## ヘッダーで pay.jp のライブラリを外部リソースとして読み込む

こんな実装です。

```index.vue
@Component({
  head: function() {
    return {
      script: [
        { src: 'https://js.pay.jp/' }
      ]
    }
  }
})
export default class index extends Vue {
  mounted() {
    // @ts-ignore
    window.Payjp.setPublicKey('xxxxxxxx')
  }
}
```

ページのヘッダーで `https://js.pay.jp/` をロードして、 DOM に触ることのできる `mounted()` のタイミングで初期化処理をしています。 
`Payjp` を読み込めないとエラーが表示されるので `// @ts-ignore` をしています。本来は `declear global` などで Window に Payjp を定義してあげる方法が綺麗かと思いますが、自分の環境ではなぜかエラーが解消されなかったので汚いですが ignore としました。

Window に Payjp を定義してあげる方法はこちらの記事が参考になると思います。
https://qiita.com/moriyuu/items/f0340a9ed22ea43d7b4c

## 問題点

上記実装で問題なく Payjp モジュールを参照することができたのですが、 `ApplePaySession` を参照することができません。

`ApplePaySession` は Apple Pay のフォームに成功可否を通知して UI を表示するために使用します。
`ApplePaySession.STATUS_SUCCESS` を通知すれば Apple Pay のフォームに成功と表示されて閉じますし、それ以外なら失敗と表示されます。

|成功|失敗(undefined)|
|---|---|
|<img width="200" alt="IMG_1471.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49585/d1426a2c-3438-46d1-d024-83c6b473ddd7.png">|<img width="200" alt="IMG_1470.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49585/67aebbc7-b1a5-955d-7f73-5feb7eb00557.png">|

サンプルの例でいうと、buildSession が成功すると 1つ目の result を引数に持つコールバックが呼ばれます。バックエンドで実装した決済 API をコールして成功したら `ApplePaySession.STATUS_SUCCESS` を通知してあげます。

`result.token.id` は取得できているのに、 `ApplePaySession` が undefined なためエラーとなってしまいます。

```index.vue
    // @ts-ignore
    let session = window.Payjp.applePay.buildSession(req, (result: any) => {
      // TODO: ここで result.token.id を使って backend の決済 API をコールする
      // @ts-ignore
      session.completePayment(window.Payjp.ApplePaySession.STATUS_SUCCESS)
      alert(result.token.id)
    }, (e: any) => {
      alert(e.error.message)
    })
    session.begin()
```

以下のパターンで試してみましたが、どれも undefined でした。
- window.ApplePaySession
- window.Payjp.ApplePaySession
- window.Payjp.applePay.ApplePaySession

# 対応

結果的に[サンプルコード](https://github.com/payjp/payjp.github.io/blob/master/sample/applepay/index.html)を static/ 配下に pay.html として設置して `<a href='/pay.html'>` でリンクするという方法に至りました。

これであれば、 `ApplePaySession` を参照可能です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49585/4c28f61a-3ae9-f390-bbbf-33f39ae24644.gif" width=300>


コード全体は[こちら](https://github.com/shts/nuxt-payjp-applepay)です。

# 所感

- PAY.JP のドキュメントは日本語で内容も充実していると感じた。
- 会員登録前でもドキュメントを参照できるのはすごくありがたい。
- Web コンソールが使いやすい。
- サンプルが充実しているので Nuxt のサンプルもそこに追加してほしい。

以上です。

# 参考
- https://pay.jp/docs/apple-pay-setup-web
- https://pay.jp/docs/apple-pay-integrate-web
- https://github.com/payjp/payjp.github.io/blob/master/sample/applepay/index.html