Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

これは、2019/03/21(木・祝)開催 JP_Stripes Connect 2019のワークショップ『「電話でペイ!」(Twilio Pay)を試してみよう!』で使用する資料です。

Twilio を使って電話決済を体験する(中級編)では、電話決済時のクレジットカード情報入力状況を、Webページにリアルタイムに表示させるWebアプリケーションを作成します。
以下のような電話決済の様子を確認できるWebページを作ります。
スクリーンショット 2019-03-10 20.55.04.png

前提

こちらは、Twilio <Pay> を使って電話決済を体験する(初級編)にて、StripeやTwilioの設定・構築ができていることが前提となります。

注意事項

Twilio <Pay> を使って電話決済を体験する(初級編)の注意事項に記載した費用が発生します。
また、TwilioFunctionsTwilioSyncを利用します。ワークショップでは、無料枠の範囲内で作業ができますが、それを超えると費用が発生します。

得られるもの

  • Twilio<Pay>の状況通知(StatusCallback)の動作を体験できる

ざっくり構成

スクリーンショット 2019-03-14 17.25.00.png
Twilio<Pay>は、状況が変化するたびに、指定したURLに通知をする機能(statusCallback)が用意されています。

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

事前準備

Twilio <Pay> を使って電話決済を体験する(初級編)を実施していること。

ワークショップ

以下の手順になります。

  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
Servicesを選択します。
スクリーンショット 2019-03-01 16.49.26.png
TwilioSyncは初期状態で、Default ServiceというServiceが登録されています。今回は、Twilio<Pay>のために、新規のServiceを作成します。画面上部の赤いプラスボタンをクリックします。
以下の画面が表示されるので、「PaySyncService」と入力します。※わかりやすい任意の名前でよいです。
スクリーンショット 2019-03-01 17.00.12.png
作成に成功すると以下の画面が表示されますので、SESRVICE SIDをテキストエディタなどにメモしておきます。※後でPAY_SYNC_SERVICE_SIDとして使います。
メモできたら< Backで戻ります。
スクリーンショット 2019-03-01 17.01.07.png

TwilioSyncにアクセスするためのAPI Keyを作成する

ツール新しいAPIキーを作成するをクリックします。
スクリーンショット 2019-03-01 17.11.03.png
わかりやすい名前に「PayStatus API Key」と入力します。※わかりやすい任意の名前で良いです。
入力が終わったら、APIキーを作成するをクリックします。
スクリーンショット 2019-03-01 17.13.39.png
APIキーの作成に成功すると、以下の画面が表示されます。
表示されているSIDSECRETをテキストエディタなどにメモしてきます。※後でSIDPAY_TWILIO_API_KEYとして、SECRETPAY_TWILIO_API_SECRETとして使います。この画面から遷移するとSECRETは二度と表示されません。必ずメモしてください。
メモができたら完了しました!にチェックを入れて、終了をクリックします。
スクリーンショット 2019-03-01 17.17.42.png

TwilioSyncへのAccessToken生成用のFunctionを作成

TwilioSyncを利用するためには、APIキーを使って、AccessTokenを動的に生成する必要があります。TwilioFunctionsを使って、TwilioSyncのAccessTokenを生成します。

まず、Function内で利用可能な環境変数に、さきほどのAPIキーなどを登録します。
Twilioの管理コンソールからRuntimeFunctions設定を選択します。
スクリーンショット 2019-03-01 17.24.00.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

AccessToken生成用のFunctionを作成します。左のメニューからFunctionsを選択し、赤色のプラスボタンをクリックします。
スクリーンショット 2019-03-01 17.34.35.png
以下の画面が表示されるのでBlankを選択し、Createをクリックします。
スクリーンショット 2019-03-01 17.36.55.png
表示された画面で、以下のように入力し、Saveをクリックします。

項目名 設定値
FUNCTION NAME PayStatusSyncToken
PATH /pay-status-sync-token
ACCESS CONTROL チェックなし
EVENT (未選択)
CODE 以下参照
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
保存できたら、PATHの後ろにあるコピーボタンでURLをコピー、ブラウザを別ダブで開き、そのURLにアクセスしてみてください。JSON形式のtokenが取得できていればOKです。

Twilio<Pay>の決済状況をTwilioSyncに保存する処理を作成する

Twilio<Pay>には statusCallbackというものがあり、こちらにURLを指定しておくと、状態が変わるたびに指定したURLに通知をしてくれます。
まずは、statusCallbackを受けて決済状況をTwilioSyncに保存する処理をTwilioFunctionsで作成します。

statusCallbackを受けるFunctionを作成

Twilioの管理コンソールからRuntimeFunctionsを選択します。
スクリーンショット 2019-03-01 17.59.56.png
赤色のプラスボタンをクリックします。
以下の画面が表示されるのでBlankを選択し、Createをクリックします。
スクリーンショット 2019-03-01 17.36.55.png
表示された画面で、以下のように入力し、Saveをクリックします。

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

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

statusCallbackを受ける

<Pay>のstatusCallbackパラメーターに、先程のFunctionのURLを設定します。
Twilioの管理コンソールからRuntimeTwiML Binsを選択します。
Twilio を使って電話決済を体験する(初級編)にて作成したTwiML Binsをクリックします。今回は「stripe_demo」です。
スクリーンショット 2019-03-01 21.36.36.png
4行目の<Pay>にstatusCallbackパラメータの設定を追加します。

<?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
Saveをクリックし、保存します。

決済状況を表示させるWebページを作成する

ブラウザに表示するHTMLを作成します。
TwilioにはAssetsというファイルを保存、Webサイトとして公開できる機能があります。今回はこちらを利用します。

アップロードするファイルを作成します。

今回は、以下にアップロードするファイルを用意しています。以下のファイルをダウンロードし、zipを展開してください。
https://heady-sea-5045.twil.io/assets/paystatus.zip

Assetsにアップロードする

Twilioの管理コンソールからRuntimeAssetsを選択します。
Add an Assetsをクリックし、上記zipファイルを展開したものをアップロードします。
スクリーンショット 2019-03-01 21.54.13.png
そのままUploadをクリックします。
スクリーンショット 2019-03-01 22.03.18.png
アップロード後、htmlファイルの方の、PATHのコピーボタンをクリックします。

テストする

新しくブラウザのタブを開き、上記htmlファイルのURLにアクセスします。
Twilio を使って電話決済を体験する(初級編)にて作成した決済用電話番号に電話をかけます。
入力する毎に、画面の表示が変わっていることを確認します。
スクリーンショット 2019-03-01 22.22.01.png

おまけ

決済が完了したら、Webページの表示を「completed」にしたいと思います。
Twilioの管理コンソールからRuntimeFunctionsを選択します。
Twilio を使って電話決済を体験する(初級編)で作成した、決済完了後に呼び出されるFunction(今回は「stripeResult)を選択します。
スクリーンショット 2019-03-01 22.25.26.png
FunctionのCODEを以下の内容に書き換えます。
※処理の最後に、TwilioSyncを更新する処理を追加しました。

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

決済番号に電話をかけ、決済後、StatusがCompletedになることを確認します。
スクリーンショット 2019-03-01 22.37.57.png

時間がある人は

Twilio を使って電話決済を体験する(上級編)をお試しください。

お礼

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

追記

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

takeshifurusato
TwilioJP-UG Okayama(Twilio Champions) / JAWS-UG Okayama / JP_Stripes Okayama / Okayama WordPress Meetup / SORACOM UG Okayama
https://takeshi.furusato.blog
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away