สร้าง Payment Chatbot ด้วย SCB Open Banking API (Part 3 : เขียน Fulfillment เพื่อสร้าง Payment Deeplink)
สวัสดีครัช เรามาเริ่มเขียน Fulfillment ต่อกันดีกว่า ความเดิมตอนที่แล้วคือเราได้จัดการ Intent ของ “Maemanee.Payment” บน Dialogflow กันไปเรียบร้อยแล้ว ทีนี้เจ้า Dialogflow มันก้อจะส่ง Request เข้ามาพร้อมไปกับ Extract Parameters มาให้เราเป็นอย่างดีส่งมาในแอพของเรา เราก้อเพียงเอาข้อมูลเหล่านี้ไปส่งไปเรียก SCB Payment API ได้ซักทีเน้อ เอ้าเริ่มมมม ..
สร้าง Fulfillment ง่ายๆด้วย NodeJS
ภาษาหนึ่งที่ผมมีความชอบเป็นการส่วนตัว เวลาผมต้องการที่จะ POC อะไรผมไม่รีรอที่จะเริ่มจาก NodeJS เลย เพราะตัวภาษามีความง่าย ทันสมัยอยู่ตลอดเวลา มีของเล่นให้ใช้ได้เยอะ ว่าแล้วเราก้อสร้างกันเลยด้วย Express (ผมอาจไม่ลง Detail ทุกขั้นตอนนะครับไม่งั้นจะไปไม่ถึงไหนแน่เลย)
app.post('/dialogflow/fulfillment', async (req, res) => {
const body = get(req, ['body']);
console.log('body => ', JSON.stringify(body));
return res.status(HTTP_OK).send('');
});
อันดับแรกเลยผมจะสร้าง API เพื่อทดลองรับ Request จาก Dialogflow ดูก่อนว่าหน้าตาที่เข้ามาเป็นยังไงหน๋อ มันเป็นยังไงหน๋อ — อันนี้ Base on spec ของ Dialogflow V2 นะครับ (อาจจะขอตัดส่วนยาวๆออกไปเอาเฉพาะใจความสำคัญเข้ามานะครับ เพราะ Payload ที่ Dialogflow ยิงเข้ามาค่อนข้างยาว)
{
"responseId": "d2af2259-dc08-4b36-b657-65e3582f06ff-5b26cf67",
"queryResult": {
"queryText": "แม่มณีออกบิล 100 บาท",
"action": "",
"parameters": {
"amount": 100,
"currency": "THB",
"decimal": "0"
},
"allRequiredParamsPresent": true,
"fulfillmentMessages": [
{
"text": {
"text": [
"##PAYMENT-DEEPLINK##"
]
},
"platform": "LINE"
}
],
"outputContexts": [
],
"intent": {
"name": "projects/<agentname>/agent/intents/f9d9e8a2-cc2a-448d-9ffe-8664ca91fcbf",
"displayName": "Maemanee.Payment"
},
"intentDetectionConfidence": 0.8140354,
"languageCode": "th"
},
"originalDetectIntentRequest": {
"source": "line",
"payload": {
"data": {
"type": "message",
"source": {
"userId": "<LINE-USER-ID>",
"roomId": "<LINE-ROOM-ID>",
"type": "room"
},
"message": {
"id": "10208976675600",
"type": "text",
"text": "แม่มณีออกบิล 100 บาท"
},
"replyToken": "90c6eb6003924a538c67a920196532ba",
"timestamp": 1563065853027
},
"source": "line"
}
},
"session": "projects/<agentname>/agent/sessions/8890c1ff-ba1f-4802-90a7-0524ef5eb9d9"
}
อันแรกเลยที่เราจะพาไปรู้จักคือ
- queryResult.queryText คือสิ่งที่ผู้ใช้งานส่งเข้ามาครับ
- queryResult.parameters คือ Parameters ที่เราแยกไว้ตอน Training ใน Dialogflow แหละครับ เห็นไหมว่ามันแยกเป็นชิ้นๆมาให้เลย ดีงามมมมม!
- queryResult.fulfillmentMessages คือ list ของข้อความที่จะใช้ตอบกลับที่เราได้ Config ไว้ใน Dialogflow ครับ จำตรงนี้ไว้ให้ดีนะครับ เพราะเราต้องนำมา Replace ค่า ‘##PAYMENT-DEEPLINK##’ ที่เราจะใช้ตอบกลับผู้ใช้งานที่จะให้เข้าไปจ่ายที่ SCB EASY ครับ
- queryResult.intent.displayName คือ Intent ที่ Dialogflow ตัดสินใจให้ครับว่าเป็น Intent ไหน
- queryResult.intentDetectionConfidence ระดับความมั่นใจว่า Dialogflow มั่นใจแค่ไหนครับ ซึ่งถ้าเกิน 0.8 นี่ค่อนข้างดีเลยทีเดียว
- originalDetectIntentRequest ถ้าเราได้ Config ให้ Dialogflow ต่อ Integration กับ LINE โดยตรงเราจะได้อันนี้กลับมาครับ ซึ่งมันจะนำ Payload จาก LINE ส่งตรงมาให้เราเลย (ส่งเฉพาะที่ Dialogflow Support นะครับ เราจะไม่ได้พวก Follow/Unfollow อยู่ดี)
หลังจากที่เราพาไปรู้จักหน้าตา Payload ของ Dialogflow แล้วเราก้อมาแกะกันสิฮ่ะ รอไร แกะจาก JSON เอาของที่เราสนใจกัน (ถ้าดูจากใน code ผมจะใช้ library lodash มาใช้แกะ JSON นะครับ)
app.post('/dialogflow/fulfillment', async (req, res) => {
const body = get(req, ['body']);
const action = get(body, ['queryResult', 'intent']);
const fulfillmentMessages = get(body, ['queryResult', 'fulfillmentMessages']);
const parameters = get(body, ['queryResult', 'parameters']);
const sourcePayload = get(body, ['originalDetectIntentRequest', 'payload']);
const result = await fulfillmentAction(intent, fulfillmentMessages, parameters, sourcePayload);
return res.status(HTTP_OK).send(result);
});
ของครบ! ไปเรียก Payment API เลยฮ่ะ อันดับแรกเราต้องการ OAuth Client Credential Token ก่อน เราก้อสร้าง Function กันไปเอามาเลยฮ่ะ
getAccessToken() {
var options = {
method: 'POST',
url: 'https://api.scb.co.th/partners/sandbox/v1/oauth/token',
headers: {
resourceOwnerId: <SCB-API-KEY>,
requestUId: generateGUID(),
'Content-Type': 'application/json'
},
body: {
applicationKey: <SCB-API-KEY>,
applicationSecret: <SCB-API-SECRET>
},
json: true
};
return new Promise(function (resolve, reject) {
request(options, function (error, res, body) {
if (!error && res.statusCode == 200) {
resolve(body.data.accessToken);
} else {
reject(error);
}
});
});
}
ต่อไปก้อคือพระเอกของเรา เอามาเรียกเพื่อของ Payment Deeplink ต่อกันเลยครัช
getPaymentDeeplink(accessToken, amount, ref1, ref2) {
var options = {
method: 'POST',
url: 'https://api.scb.co.th/partners/sandbox/v2/deeplink/transactions',
headers: {
resourceOwnerId: <SCB-API-KEY>,
requestUId: generateGUID(),
channel: 'scbeasy',
authorization: 'Bearer ' + accessToken,
'accept-language': 'EN',
'Content-Type': 'application/json'
},
body: {
paymentAmount: amount,
transactionType: 'PAYMENT',
transactionSubType: 'BPA',
ref1: ref1,
ref2: ref2,
ref3: 'SCB',
accountTo: <SCB-MERCHANT-BILLERID>
},
json: true
};
return new Promise(function (resolve, reject) {
request(options, function (error, res, body) {
if (!error && res.statusCode == 201) {
resolve(body.data.deeplinkUrl);
} else {
reject(error);
}
});
});
}
เมื่อ Function พร้อมเราก้อเอามาร้อยเรียงเป็นทอผืนเดียวกันเลยฮ่ะ และเมื่อได้ Payment Deeplink จาก SCB Payment API มาแล้วเราก้อเพียงแต่ Modify ข้อความของเรา โดยที่หน้าที่หลักของเราคือการตอบ Dialogflow กลับไปด้วย Spec แบบนี้ครับ
const result = {
text: {
text: [
"scbeasysim://billpayment-anonymous/2dd86d7d-9476-4de1-8d20-e9d56b4b926e"
]
},
platform: "LINE"
};
return { fulfillmentMessages: [result] };
เสร็จแล้วครัช ทีนี้ Chatbot เราก้อสามารถที่จะส่ง Payment Deeplink กลับไปให้ผู้ใช้งานเพื่อชำระเงินผ่าน SCB EASY (Sandbox version) ได้แล้วววว เก๋กู๊ดดดด
Showcase กันหน่อยยย
มาดูผลงานกันซักหน่อยซิ๊ .. เริ่มจากเราทัก Chatbot เราไปตามที่เราได้สร้าง Intent ไว้เลย
“แม่มณีออกบิลห้าบาทให้หน่อย”
ทีนี้ Chatbot เราก้อตอบรับกลับมาด้วยช่องทางการชำระเงินมาให้เราโดยที่เราสามารถเลือกที่จะจ่ายผ่าน SCB EASY ได้ สำเร็จไปหนึ่งเปราะ
เราจะเห็นว่าการ Navigate จาก LINE ไปยังแอพ SCB EASY (Sandbox) นั้นผมใช้วิธีการเปิด LIFF ของ LINE ขึ้นมา เพราะว่าต้องการเช็คว่ามีแอพในเครื่องหรือเปล่าครับ เพื่อไม่ให้ลิงค์มันแป๊ก ซึ่งหากมีแอพแล้วก้อจะสามารถเด้งไปยังแอพ SCB EASY ได้ทันที
เมื่อทำการจ่ายเงินเสร็จแล้วยังสามารถ Callback กลับมาได้อีกด้วยจากการที่เราใส่ Callback URL ไปด้วยทำให้ User Journey ของผู้ใช้งานมีความเนียนนุ่มไร้รอยต่อกันไปเลยครัช
แต่การ Navigate กลับมานี้เป็นเพียงแค่ Callback จากแอพสู่แอพเท่านั้นนะครับ ทาง SCB Payment API ยังได้เตรียมเส้นที่สามารถส่งกลับมาที่ Server ของเราได้อีกด้วยเพื่อเป็นการ Confirm ว่าผู้ใช้งานได้ทำการชำระเงินเรียบร้อยแล้ว ซึ่งเด๋วเราจะมาติดตามกันในตอนต่อไปของ Series นี้นะครับ :)