RCS가 지원되는 Google 메시지와 Google 월렛을 사용하여 원활한 체크인 흐름을 설계할 수 있습니다. 사용자는 체크인을 완료하고, 탑승권을 수신하고, 메시지 앱에서 Google 월렛에 직접 추가합니다. 월렛에 추가되면 항공편 세부정보가 변경될 경우 패스가 자동으로 업데이트됩니다. 사용자는 휴대전화에서 최신 탑승권을 빠르게 확인할 수 있습니다.
이 문서에서는 Google 월렛에 탑승권을 구현하는 기술적 단계를 설명합니다. 또한 RBM을 사용한 원활하고 효율적인 체크인 환경을 위한 디자인 팁이 포함된 샘플 대화도 제공됩니다.
기술적 구현
탑승권을 Google 월렛 흐름에 구현하려면 Google 월렛 API 및 RBM API를 사용해야 합니다.
기본 요건
Google Wallet API를 시작하려면 다음 필수 단계를 따르세요.
- Google 월렛용 패스를 만들고 배포할 수 있도록 월렛 발급기관 계정에 가입하세요.
- 아직 Google Cloud (GCP) 프로젝트가 없는 경우 만듭니다.
- Google Wallet API를 사용 설정합니다.
- Google 월렛 API를 호출할 수 있도록 서비스 계정과 키를 만듭니다.
- Google Pay 및 Wallet 콘솔에서 서비스 계정을 승인합니다.
- 탑승권 템플릿을 사용하여 새 Boarding 클래스를 만듭니다.
Google Wallet 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 코드가 포함된 탑승권이 표시됩니다.