stripe
twilio

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


はじめに

これは、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

よろしければ、こちらもどうぞ。