RCS 및 Google 월렛과 함께 Google 메시지를 사용하여 원활한 체크인 흐름을 설계할 수 있습니다. 사용자가 체크인을 완료하고 탑승권을 받은 후 메시지 앱에서 Google 월렛에 직접 추가합니다. 월렛에 들어가면 항공편 세부정보가 변경되면 패스가 자동으로 업데이트됩니다. 사용자는 휴대전화에서 바로 최신 탑승권에 빠르게 액세스할 수 있습니다.
이 문서에서는 Google 월렛 흐름에 탑승권을 구현하는 기술적 단계를 설명합니다. 또한 RBM을 사용한 원활하고 효율적인 체크인 환경을 위한 설계 팁이 포함된 샘플 대화도 제공합니다.
기술적 구현
Google 월렛 흐름에 탑승권을 구현하려면 Google Wallet API와 RBM API를 사용합니다.
기본 요건
Google Wallet API를 시작하려면 다음 필수 단계를 따르세요.
- Google 월렛의 패스를 만들고 배포할 수 있도록 월렛 발급기관 계정에 가입합니다.
- 아직 Google Cloud(GCP) 프로젝트가 없는 경우 만듭니다.
- Google Wallet API를 사용 설정합니다.
- Google 월렛 API를 호출할 수 있도록 서비스 계정과 키를 만듭니다.
- Google Pay 및 지갑 콘솔에서 서비스 계정을 승인합니다.
- 탑승권 템플릿을 사용하여 새 탑승 클래스를 만듭니다.
Google 월렛 API
탑승권을 만들고 RBM용 Google 월렛에 추가 URL을 생성하려면 다음 단계를 따르세요.
- 필요한 인증 및 승인을 실행합니다.
- 패스 객체를 만듭니다.
- 서명된 JSON 웹 토큰(JWT)을 가져옵니다. 인코딩된 JWT의 최대 길이는 2,048자(영문 기준)입니다.
- JWT를 사용하여 Google 월렛에 추가 URL을 생성합니다.
RBM API
RBM에서 Google 월렛에 추가 제안을 보내려면 URL 열기 작업을 전송합니다. 메시지 페이로드에서 다음을 실행합니다.
text
에는 'Google 월렛에 추가'를 입력합니다.url
의 경우 'Google 월렛에 추가' URL을 입력합니다.
Google 월렛 아이콘이 추천 라벨에 자동으로 표시됩니다.
대화 설계
이 샘플에서는 고유한 대화 기능을 사용하여 사용자에게 전체 체크인 흐름을 안내합니다. 자연스러운 대화상자와 풍부한 기능(예: 원탭 추천, 리치 카드)을 사용하여 사용자가 목표를 달성하도록 돕는 방법을 보여줍니다. 이 경우 목표는 (1) 항공편 환경을 맞춤설정하고 (2) 탑승권을 받고, (3) 공항에서 빠르게 액세스할 수 있도록 Google 월렛에 추가하는 것입니다.
다음은 대화의 개요입니다. 그다음에는 디자인 도움말과 흐름의 단계별 분석이 이어집니다. 상담사에게 유사한 디자인을 구현하려면 단계에 나온 코드 샘플을 참고하세요.
디자인 조언
체크인 흐름을 디자인할 때는 다음 원칙을 염두에 두세요.
- 첫 번째 메시지가 가장 중요합니다. 사용자가 응대할 이유가 있도록 대화의 목적을 간략하게 설명합니다.
- 각 메시지는 소량의 정보를 제공하고 사용자에게 응답하라는 메시지를 표시해야 합니다. 추천 답장 및 추천 작업은 사용자가 다음 단계를 수행하는 데 도움이 됩니다.
- 상담사는 기계적이지 않고 응답이 빠르게 이루어져야 합니다. 브랜드의 어조를 반영하는 언어를 사용하세요. 이상적인 브랜드 담당자는 고객과 어떻게 채팅하나요?
- 사람들은 특별 대우를 받고 싶어 합니다. 사용자의 항공편 내역을 기반으로 좌석이나 식사를 추천하여 체크인 환경을 맞춤설정할 수 있습니다.
- 리치 카드 및 캐러셀을 사용하면 대화를 더욱 역동적으로 만들 수 있습니다. 이를 사용하여 사용자가 옵션 중에서 선택하는 데 도움이 되는 이미지와 세부정보를 공유하세요.
- 좋은 대화는 좋은 마무리로 끝납니다. 보딩 패스를 보내기 전에 사용자의 체크인 세부정보를 확인합니다. 친근한 인사를 통해 인간적인 감성을 더하세요.
체크인 흐름
상담사가 사용자에게 항공편 체크인이 열렸다고 알립니다.
코드 샘플
const suggestions = [ { reply: { text: '⚡ Check in', postbackData: 'checkIn', }, }, { reply: { text: '⏰ Remind me later', postbackData: 'remindMe', }, }, { reply: { text: '✈️ View my flight details', postbackData: 'flightDetails', }, }, { reply: { text: '🔀 Change my flight', postbackData: 'flightChange', }, }, ]; const params = { messageText: 'Check-in for your flight', messageDescription: '👏 Happy morning, Jo! Check-in is now open for your flight from London to Mumbai on ' + getFlightTime() + ' at 2:00PM. What would you like to do? ', msisdn: phoneNumber, suggestions: suggestions, imageUrl: getImageUrl('fly.png'), height: 'MEDIUM', }; rbmApiHelper.sendRichCard(params);
사용자가 추천 답장을 탭하여 체크인합니다.
상담사가 체크인 절차에 대한 기대치를 설정합니다.
코드 샘플
const params = { messageText: "OK, great. It's just 3 simple steps to check in. Here's the first step to get you onboard:", msisdn: msisdn, }; let self = this; rbmApiHelper.sendMessage(params, function (response, err) { self.sendPolicyImage(msisdn); });
상담사가 사용자에게 안전 정책에 동의해 달라고 요청합니다.
코드 샘플
const suggestions = [ { reply: { text: 'Yes, I agree', postbackData: 'policy_agree', }, }, { reply: { text: "No, I don't agree", postbackData: 'policy_nack', }, }, ]; const params = { messageText: 'Baggage safety policy', messageDescription: 'To help us ensure a safe flight, please review our safety policy and let us know you agree', msisdn: msisdn, suggestions: suggestions, imageUrl: getImageUrl('policyImage.png'), height: 'MEDIUM', orientation: 'HORIZONTAL', thumbnailImageAlignment: 'LEFT', }; rbmApiHelper.sendRichCard(params);
사용자가 제안된 답장을 탭하여 동의합니다.
상담사는 사용자에게 감사 인사를 하고 다음 단계를 안내합니다.
코드 샘플
const params = { messageText: "Thank you - A safe passenger is a happy passenger! Here's the next step:", msisdn: msisdn, }; let self = this; rbmApiHelper.sendMessage(params, function (response, err) { self.sendPlan(msisdn); });
상담사가 사용자에게 좌석을 선택하라는 메시지를 표시합니다.
코드 샘플
const suggestions = [ { reply: { text: 'View the seat map', postbackData: 'view_seat_map', }, }, ]; const outerSuggestions = [ { reply: { text: '17A', postbackData: 'seat_17A', }, }, { reply: { text: '17C', postbackData: 'seat_17C', }, }, { reply: { text: '18A', postbackData: 'seat_18A', }, }, { reply: { text: 'Show me more', postbackData: 'more', }, }, ]; const params = { messageText: 'Choose your seat', messageDescription: "It's time to sit back and get comfy! 💺 We've recommended some seats based on your last flight. Choose the one you want, or let us know your preferred seat by typing the number.", msisdn: msisdn, imageUrl: getImageUrl('seatMap.png'), height: 'TALL', orientation: 'VERTICAL', outerSuggestions: outerSuggestions }; rbmApiHelper.sendRichCard(params);
사용자가 선택한 좌석에 관한 추천 답변을 탭합니다.
상담사가 사용자의 선택을 확인합니다.
코드 샘플
this.seatmap[msisdn] = seat; const params = { messageText: `Seat ${seat}, you got it`, msisdn: msisdn, }; let self = this; rbmApiHelper.sendMessage(params, function(res) { self.sendFoodOptions(msisdn); });
상담사가 사용자에게 기내식 메뉴를 선택해 달라고 요청합니다.
코드 샘플
const params = { messageText: `Now let's talk food 😋 You can pre-order your in-flight meal. Would you be happy with a vegetarian entree or a chicken entree?`, msisdn: msisdn, }; let self = this; rbmApiHelper.sendMessage(params, function(res) { self.sendFoodDetails(msisdn); });
상담사가 식사 옵션을 보여줍니다.
코드 샘플
const cardContents = [ { title: 'Panzanella salad (v)', description: 'Ingredients: bread, lettuce, onions, tomatoes, olive oil', suggestions: [ { reply: { text: 'Choose vegetarian', postbackData: 'veggie', }, }, ], media: { height: 'MEDIUM', contentInfo: { fileUrl: getImageUrl('salad.jpg'), }, }, }, { title: 'Grilled chicken with greens', description: 'Ingredients: chicken, potatoes, peppers, olive oil', suggestions: [ { reply: { text: 'Choose chicken', postbackData: 'chicken', }, }, ], media: { height: 'MEDIUM', contentInfo: { fileUrl: getImageUrl('chicken.png'), }, }, }, ]; const params = { msisdn: msisdn, cardContents: cardContents, }; rbmApiHelper.sendCarouselCard(params);
사용자가 선택한 식사에 관한 추천 답글을 탭합니다.
상담사가 사용자의 선택을 확인합니다.
코드 샘플
const params = { messageText: `Vegetarian it is 💚`, msisdn: msisdn, }; let self = this; rbmApiHelper.sendMessage(params, function (response, err) { self.sendAskConfirmation(msisdn); });
상담사가 체크인 세부정보를 요약합니다.
코드 샘플
let seat = this.seatmap[msisdn]; const suggestions = [ { reply: { text: "Yes, I'm happy with that", postbackData: 'happy', }, }, { reply: { text: 'Change my seat', postbackData: 'change_seat', }, }, { reply: { text: 'Change my meal', postbackData: 'change_meal', }, }, ]; const params = { messageText: "Here's what we've noted down: You've opted for seat " + seat + " and a vegetarian meal. Please confirm your choices.", msisdn: msisdn, suggestions: suggestions }; rbmApiHelper.sendMessage(params);
사용자가 추천 답장을 탭하여 체크인 세부정보를 확인합니다.
상담사가 체크인이 완료되었음을 알립니다.
코드 샘플
const params = { messageText: "Hooray! You're now checked in for your flight ☑️ Here's your boarding pass. We're so happy to host you soon!", msisdn: msisdn, }; let self = this; rbmApiHelper.sendMessage(params, function (response, err) { self.sendWalletPass(msisdn); });
상담사가 사용자의 탑승권을 전송합니다.
코드 샘플
this.walletHelper.createFlightPassUrl(this.seatmap[msisdn]).then((url) => { let suggestions = [ { action: { text: 'Add to Google Wallet', postbackData: 'addToWallet', openUrlAction: { url: url }, }, }, ]; const params = { messageText: 'HS123 LHR to BOM\nPassenger: Jo Flow', messageDescription: "We'll keep you up to date! You'll get a notification if your flight details change.", msisdn: msisdn, suggestions: suggestions, imageUrl: getImageUrl('boardingPass.png'), height: 'TALL', orientation: 'HORIZONTAL', thumbnailImageAlignment: 'LEFT', }; rbmApiHelper.sendRichCard(params); });
이 가로형 리치 카드의 이미지는 항공사에서 제공하는 완전한 기능을 갖춘 탑승권입니다. 이미지에는 스캔 가능한 바코드를 비롯하여 필요한 모든 탑승 정보가 표시되어야 합니다. 사용자는 이미지를 탭하여 Google의 메시지 앱에서 탑승권을 확인하고 스캔할 수 있습니다.
리치 카드에 Google 월렛에 추가 제안이 표시됩니다. 이 추천은 사용자가 월렛에 탑승권을 추가할 수 있는 Google 월렛 앱을 여는 URL 열기 작업을 트리거합니다. 사용자의 기기에 앱이 없는 경우 설치하라는 메시지가 표시됩니다. 패스가 Google 월렛에 추가되면 항공편 세부정보가 변경되면 사용자에게 자동으로 항공편 알림 및 상태 업데이트가 전송됩니다.
Google 월렛에 패스를 추가하지 않는 사용자도 최신 상태로 유지해야 합니다. 리치 카드에 표시된 탑승 정보 변경에 관해 사용자에게 메시지를 보냅니다.
사용자가 제안된 작업을 탭하여 Google 월렛에 패스를 추가합니다.
Google 월렛 앱이 열립니다. 사용자가 버튼을 탭하여 월렛에 패스를 추가합니다.
사용자가 버튼을 탭하여 패스를 봅니다.
QR 코드가 포함된 탑승권이 표시됩니다.