Twilio Pay を使って電話決済を体験する(中級編)

# はじめに
これは、2019/03/21(木・祝)開催 [JP_Stripes Connect 2019](https://connect2019.jpstripes.com/)のワークショップ『「電話でペイ！」(Twilio Pay)を試してみよう！』で使用する資料です。

- [Twilio \<Pay> を使って電話決済を体験する(初級編)](https://qiita.com/takeshifurusato/private/b3e74383113c9723e11c)
- Twilio \<Pay> を使って電話決済を体験する(中級編) ←こちら
- [Twilio \<Pay> を使って電話決済を体験する(上級編)](https://qiita.com/takeshifurusato/private/a3c4b0e2fdfba63186dd)

Twilio <Pay> を使って電話決済を体験する(中級編)では、電話決済時のクレジットカード情報入力状況を、Webページにリアルタイムに表示させるWebアプリケーションを作成します。
以下のような電話決済の様子を確認できるWebページを作ります。
![スクリーンショット 2019-03-10 20.55.04.png](https://qiita-image-store.s3.amazonaws.com/0/110436/b849697e-7139-57c4-2401-b07f849a9f35.png)

# 前提
こちらは、[Twilio \<Pay> を使って電話決済を体験する(初級編)](https://qiita.com/takeshifurusato/private/b3e74383113c9723e11c)にて、StripeやTwilioの設定・構築ができていることが前提となります。

# 注意事項
[Twilio \<Pay> を使って電話決済を体験する(初級編)](https://qiita.com/takeshifurusato/private/b3e74383113c9723e11c)の注意事項に記載した費用が発生します。
また、[TwilioFunctions](https://twilio.kddi-web.com/price/functions/)、[TwilioSync](https://twilio.kddi-web.com/price/sync/)を利用します。ワークショップでは、無料枠の範囲内で作業ができますが、それを超えると費用が発生します。

# 得られるもの
- Twilio\<Pay>の状況通知(StatusCallback)の動作を体験できる

# ざっくり構成
<img width="800" alt="スクリーンショット 2019-03-14 17.25.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/110436/abc7e5f3-f17d-5bbe-b950-76c9a3537135.png">
Twilio\<Pay>は、状況が変化するたびに、指定したURLに通知をする機能(statusCallback)が用意されています。

1. Webhook：\<Pay>は、予めTwilioFunctionsで作成した通知用URLに対し状況通知をする。
2. ステータス保存：TwilioFuncitonにて、TwilioSyncというものを利用し、状況を保存します。
3. ステータス通知：TwilioAssetに登録してある状況確認ページは、TwilioSyncから状況の通知を受ける。
4. 閲覧：画面が更新される。

# 事前準備
[Twilio \<Pay> を使って電話決済を体験する(初級編)](https://qiita.com/takeshifurusato/private/b3e74383113c9723e11c)を実施していること。

# ワークショップ
以下の手順になります。

1. 決済の進捗状態を保存するためTwilioSyncを設定する
2. Twilio\<Pay>の決済状況をTwilioSyncに保存する処理を作成する
3. 決済状況を表示させるWebページを作成する
4. テストする。

## 進捗状態を保存するためTwilioSyncを設定する
Twilioにはリアルタイムに複数のデバイスに渡って状態を維持するためのAPIが用意されています。それがTwilioSyncです。今回は、TwilioSyncを利用して、決済の進捗状況を保存します。
### TwilioSyncで新しいServiceを作成
Twilioの管理コンソールから**Sync**を選択します。※下の方にあります。
![スクリーンショット 2019-03-01 16.48.07.png](https://qiita-image-store.s3.amazonaws.com/0/110436/528c12a3-e86c-ecbc-3ac0-719809ae6bec.png)
**Services**を選択します。
![スクリーンショット 2019-03-01 16.49.26.png](https://qiita-image-store.s3.amazonaws.com/0/110436/20aae105-f477-355c-d981-b8941a096068.png)
TwilioSyncは初期状態で、**Default Service**というServiceが登録されています。今回は、Twilio\<Pay>のために、新規のServiceを作成します。画面上部の赤いプラスボタンをクリックします。
以下の画面が表示されるので、「PaySyncService」と入力します。※わかりやすい任意の名前でよいです。
![スクリーンショット 2019-03-01 17.00.12.png](https://qiita-image-store.s3.amazonaws.com/0/110436/355aa532-57a7-284a-9fc3-6dbc005b56ec.png)
作成に成功すると以下の画面が表示されますので、**SESRVICE SID**をテキストエディタなどにメモしておきます。※後で**PAY_SYNC_SERVICE_SID**として使います。
メモできたら**\< Back**で戻ります。
![スクリーンショット 2019-03-01 17.01.07.png](https://qiita-image-store.s3.amazonaws.com/0/110436/03584336-461d-a62b-891a-c04c6057b04f.png)
### TwilioSyncにアクセスするためのAPI Keyを作成する
**ツール** → **新しいAPIキーを作成する**をクリックします。
![スクリーンショット 2019-03-01 17.11.03.png](https://qiita-image-store.s3.amazonaws.com/0/110436/cad89744-5059-cf79-f7a3-db222243abce.png)
わかりやすい名前に「PayStatus API Key」と入力します。※わかりやすい任意の名前で良いです。
入力が終わったら、**APIキーを作成する**をクリックします。
![スクリーンショット 2019-03-01 17.13.39.png](https://qiita-image-store.s3.amazonaws.com/0/110436/fe55a92e-a3ad-6825-eba3-2393aac034b2.png)
APIキーの作成に成功すると、以下の画面が表示されます。
表示されている**SID**と**SECRET**をテキストエディタなどにメモしてきます。※後で**SID**は**PAY_TWILIO_API_KEY**として、**SECRET**は**PAY_TWILIO_API_SECRET**として使います。この画面から遷移すると**SECRET**は二度と表示されません。必ずメモしてください。
メモができたら**完了しました！**にチェックを入れて、**終了**をクリックします。
![スクリーンショット 2019-03-01 17.17.42.png](https://qiita-image-store.s3.amazonaws.com/0/110436/91abcc4d-c512-5384-df3e-146553f47aa2.png)
### TwilioSyncへのAccessToken生成用のFunctionを作成
TwilioSyncを利用するためには、APIキーを使って、AccessTokenを動的に生成する必要があります。TwilioFunctionsを使って、TwilioSyncのAccessTokenを生成します。

まず、Function内で利用可能な環境変数に、さきほどのAPIキーなどを登録します。
Twilioの管理コンソールから**Runtime** → **Functions** → **設定**を選択します。
![スクリーンショット 2019-03-01 17.24.00.png](https://qiita-image-store.s3.amazonaws.com/0/110436/fe304d9e-ab01-33d6-429c-b5efa96f25ca.png)
**Environmental Variables**にある赤いボタンをクリックし、以下のKEY-VALUEを追加します。※上記でそれぞれメモした値になります。
入力が終わったら、画面下の**Save**をクリックします。

|KEY|VALUE|
|:--|:--|
|PAY_SYNC_SERVICE_SID|ISから始まるSyncサービスのSID|
|PAY_TWILIO_API_KEY|SKから始まるAPIキー|
|PAY_TWILIO_API_SECRET|APIキーとセットで設定されたSECRET|
また、**Enable ACCOUNT_SID and AUTH_TOKEN**にはチェックを入れておきます。

![スクリーンショット 2019-03-01 22.18.14.png](https://qiita-image-store.s3.amazonaws.com/0/110436/bcd4bdd8-a39b-47c9-8a4e-f6fe6e7b3377.png)

AccessToken生成用のFunctionを作成します。左のメニューから**Functions**を選択し、赤色のプラスボタンをクリックします。
![スクリーンショット 2019-03-01 17.34.35.png](https://qiita-image-store.s3.amazonaws.com/0/110436/240931ff-7ad2-68aa-a99d-5fe527d5d7cf.png)
以下の画面が表示されるので**Blank**を選択し、**Create**をクリックします。
![スクリーンショット 2019-03-01 17.36.55.png](https://qiita-image-store.s3.amazonaws.com/0/110436/280b7f7e-7c09-23cd-fc36-69c922b665c7.png)
表示された画面で、以下のように入力し、**Save**をクリックします。

|項目名|設定値|
|:--|:--|
|**FUNCTION NAME**|PayStatusSyncToken|
|**PATH**|/pay-status-sync-token|
|**ACCESS CONTROL**| チェックなし |
|**EVENT**| (未選択) |
|**CODE**| 以下参照 |

```javascript
exports.handler = function(context, event, callback) {
    const ACCOUNT_SID = context.ACCOUNT_SID;
    const SERVICE_SID = context.PAY_SYNC_SERVICE_SID;
    const API_KEY = context.PAY_TWILIO_API_KEY;
    const API_SECRET = context.PAY_TWILIO_API_SECRET;
    const IDENTITY = context.DOMAIN_NAME;
    const AccessToken = Twilio.jwt.AccessToken;
    const SyncGrant = AccessToken.SyncGrant;

    const syncGrant = new SyncGrant({
        serviceSid: SERVICE_SID
    });

    const accessToken = new AccessToken(
        ACCOUNT_SID,
        API_KEY,
        API_SECRET
    );

    accessToken.addGrant(syncGrant);
    accessToken.identity = IDENTITY;
    callback(null, {
        token: accessToken.toJwt()
    });
};
```
![スクリーンショット 2019-03-01 17.43.50.png](https://qiita-image-store.s3.amazonaws.com/0/110436/2e1b21ba-76ed-36f9-671d-b774f99515ed.png)
保存できたら、**PATH**の後ろにあるコピーボタンでURLをコピー、ブラウザを別ダブで開き、そのURLにアクセスしてみてください。JSON形式のtokenが取得できていればOKです。

## Twilio\<Pay>の決済状況をTwilioSyncに保存する処理を作成する
Twilio\<Pay>には **statusCallback**というものがあり、こちらにURLを指定しておくと、状態が変わるたびに指定したURLに通知をしてくれます。
まずは、**statusCallback**を受けて決済状況をTwilioSyncに保存する処理をTwilioFunctionsで作成します。
### **statusCallback**を受けるFunctionを作成
Twilioの管理コンソールから**Runtime** → **Functions**を選択します。
![スクリーンショット 2019-03-01 17.59.56.png](https://qiita-image-store.s3.amazonaws.com/0/110436/cc9844cd-371c-ebbc-cbae-ef7b862cd5fc.png)
赤色のプラスボタンをクリックします。
以下の画面が表示されるので**Blank**を選択し、**Create**をクリックします。
![スクリーンショット 2019-03-01 17.36.55.png](https://qiita-image-store.s3.amazonaws.com/0/110436/280b7f7e-7c09-23cd-fc36-69c922b665c7.png)
表示された画面で、以下のように入力し、**Save**をクリックします。

|項目名|設定値|
|:--|:--|
|**FUNCTION NAME**|PayStatus|
|**PATH**|/pay-status|
|**ACCESS CONTROL**| チェックなし |
|**EVENT**| (未選択) |
|**CODE**| 以下参照 |

```javascript
exports.handler = function(context, event, callback) {
    let payload = {
        PaymentCardNumber : event.PaymentCardNumber,
        ExpirationDate    : event.ExpirationDate,
        SecurityCode      : event.SecurityCode,
        Status            : 'in-progress',
	};

    let sync = Runtime.getSync({serviceName: context.PAY_SYNC_SERVICE_SID});
    sync.documents("PayStatus")
        .update({
            data: payload,
        }).then(response => {
            callback(null, 'OK');
        }).catch(error => {
            sync.documents.create({
                uniqueName: "PayStatus",
                data: payload
            }).then(response => {
                callback(null, 'OK');
            }).catch(error => {
                console.log(error);
                callback(error);
            });
        });
};
```
![スクリーンショット 2019-03-01 21.43.10.png](https://qiita-image-store.s3.amazonaws.com/0/110436/77d5205e-5b1a-ee71-0b66-b49250017ffb.png)

保存できたら**PATH**の横にあるコピーボタンでURLをコピしておきます。

### **statusCallback**を受ける
\<Pay>のstatusCallbackパラメーターに、先程のFunctionのURLを設定します。
Twilioの管理コンソールから**Runtime** → **TwiML Bins**を選択します。
Twilio <Pay> を使って電話決済を体験する(初級編)にて作成した**TwiML Bins**をクリックします。今回は「stripe_demo」です。
![スクリーンショット 2019-03-01 21.36.36.png](https://qiita-image-store.s3.amazonaws.com/0/110436/c275dcd4-d3c0-5598-0ee0-fb087a618484.png)
4行目の\<Pay>にstatusCallbackパラメータの設定を追加します。

```javascript
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say language="ja-JP" voice="Polly.Mizuki">こんにちは。クレジットカード決済にようこそ。料金は100円です。</Say>   
  <Pay chargeAmount="100" currency='jpy' action="[stripe-result用FunctionのURL" postalCode="false" paymentConnector="stripe_test" statusCallback="[pay-status用FunctionのURL]" >
    <Prompt for="payment-card-number">
      <Say language="ja-JP" voice="Polly.Mizuki">クレジットカード番号を入力してください。</Say>
    </Prompt>    
    <Prompt for="expiration-date">
      <Say language="ja-JP" voice="Polly.Mizuki">有効期限を、月と年のそれぞれ2桁の数字で入力してください。</Say>
    </Prompt>    
    <Prompt for="security-code">
      <Say language="ja-JP" voice="Polly.Mizuki">セキュリティーコードを入力してください。カードの裏に記載されています。</Say>
    </Prompt> 
  </Pay>
</Response>
```
![スクリーンショット 2019-03-01 21.48.28.png](https://qiita-image-store.s3.amazonaws.com/0/110436/c7599f9b-76db-cf66-044a-2cd89e7e3565.png)
**Save**をクリックし、保存します。

## 決済状況を表示させるWebページを作成する
ブラウザに表示するHTMLを作成します。
Twilioには**Assets**というファイルを保存、Webサイトとして公開できる機能があります。今回はこちらを利用します。
### アップロードするファイルを作成します。
今回は、以下にアップロードするファイルを用意しています。以下のファイルをダウンロードし、zipを展開してください。
https://heady-sea-5045.twil.io/assets/paystatus.zip
### Assetsにアップロードする
Twilioの管理コンソールから**Runtime** → **Assets**を選択します。
**Add an Assets**をクリックし、上記zipファイルを展開したものをアップロードします。
![スクリーンショット 2019-03-01 21.54.13.png](https://qiita-image-store.s3.amazonaws.com/0/110436/5a52fd30-55f0-3b86-19ad-0fa7e21bf661.png)
そのまま**Upload**をクリックします。
![スクリーンショット 2019-03-01 22.03.18.png](https://qiita-image-store.s3.amazonaws.com/0/110436/d03dfc1c-499d-1bca-e9e5-83aec5942711.png)
アップロード後、htmlファイルの方の、**PATH**のコピーボタンをクリックします。

# テストする
新しくブラウザのタブを開き、上記htmlファイルのURLにアクセスします。
Twilio <Pay> を使って電話決済を体験する(初級編)にて作成した決済用電話番号に電話をかけます。
入力する毎に、画面の表示が変わっていることを確認します。
![スクリーンショット 2019-03-01 22.22.01.png](https://qiita-image-store.s3.amazonaws.com/0/110436/4b5c1ff3-a720-f952-f87a-80d3be275048.png)

# おまけ
決済が完了したら、Webページの表示を「completed」にしたいと思います。
Twilioの管理コンソールから**Runtime** → **Functions**を選択します。
Twilio <Pay> を使って電話決済を体験する(初級編)で作成した、決済完了後に呼び出されるFunction(今回は「stripeResult)を選択します。
![スクリーンショット 2019-03-01 22.25.26.png](https://qiita-image-store.s3.amazonaws.com/0/110436/8bef13f0-e247-00af-0ee9-1442f3ec91bb.png)
Functionの**CODE**を以下の内容に書き換えます。
※処理の最後に、TwilioSyncを更新する処理を追加しました。

```javascript
exports.handler = function(context, event, callback) {
	let twiml = new Twilio.twiml.VoiceResponse();
	
	switch (event.Result) {
    case "success":
        status_text = "completed";
        text = "100円の決済が完了しました。ご利用ありがとうございました。";
        break;
    case "payment-connector-error":
        status_text = "error";
        text = "エラーが発生しました。決済に失敗しました。";
        console.log(decodeURIComponent(event.PaymentError));
        break;
    
    default: 
        status_text = "error";
        text = "決済に失敗しました。";
    }

	twiml.say({ language: 'ja-JP', voice: 'Polly.Mizuki' },text);

    let payload = {
        PaymentCardNumber : event.PaymentCardNumber,
        ExpirationDate    : event.ExpirationDate,
        SecurityCode      : event.SecurityCode,
        Status            : status_text,
	};
    let sync = Runtime.getSync({serviceName: context.PAY_SYNC_SERVICE_SID});
    sync.documents("PayStatus")
        .update({
            data: payload,
        }).then(response => {
        	callback(null, twiml);
        }).catch(error => {
            sync.documents.create({
                uniqueName: "PayStatus",
                data: payload
            }).then(response => {
            	callback(null, twiml);
            }).catch(error => {
                console.log(error);
                callback(error);
            });
        });
};
```
**Save**をクリックします。
![スクリーンショット 2019-03-01 22.58.36.png](https://qiita-image-store.s3.amazonaws.com/0/110436/df5f38cb-566e-72d3-fd50-d26e9254bfc7.png)


決済番号に電話をかけ、決済後、StatusがCompletedになることを確認します。
![スクリーンショット 2019-03-01 22.37.57.png](https://qiita-image-store.s3.amazonaws.com/0/110436/96b6547c-d5c6-013f-c646-3bac171b8646.png)


# 時間がある人は
[Twilio <Pay> を使って電話決済を体験する(上級編)](https://qiita.com/takeshifurusato/private/a3c4b0e2fdfba63186dd)をお試しください。

# お礼
今回の資料を作成するにあたり、以下を参考にさせていただきました。ありがとうございます。
https://qiita.com/mobilebiz/items/89d8c0aa6a80dac1793f

# 追記
こちらについて、複数同時着信対応をしたものをつくりました。
https://qiita.com/takeshifurusato/items/2fd5fc09ebaeca666677
よろしければ、こちらもどうぞ。