Google ウォレットに搭乗券を追加する

Google メッセージを RCS と Google ウォレットで使用すると、シームレスなチェックイン フローを設計できます。ユーザーはチェックインを完了し、搭乗券を受け取ってから、メッセージ アプリから Google ウォレットに直接追加します。ウォレットに追加されたパスは、フライトの詳細が変更されると自動的に更新されます。ユーザーはスマートフォンで最新の搭乗券にすぐにアクセスできます。

このドキュメントでは、搭乗券を Google ウォレット フローに実装するための技術的な手順について説明します。また、RBM でスムーズかつ効率的なチェックイン エクスペリエンスを実現するための設計に関するヒントが記載された会話のサンプルも用意されています。

技術的な実装

搭乗券から Google ウォレットへのフローを実装するには、Google Wallet API と RBM API を使用します。

前提条件

Google Wallet API の使用を開始するには、次の必要な手順を行ってください。

  1. ウォレット発行者アカウントを登録すると、Google ウォレットのパスを作成して配布できます。
  2. Google Cloud(GCP)プロジェクトを作成します(まだ作成していない場合)。
  3. Google Wallet API を有効にします。
  4. Google Wallet API を呼び出すために、サービス アカウントとキーを作成します。
  5. Google Pay & ウォレット コンソールでサービス アカウントを承認します。
  6. 搭乗券テンプレートを使用して、新しい搭乗クラスを作成します。

Google ウォレット API

搭乗券を作成し、RBM の [Google ウォレットに追加] URL を生成する手順はこちらをご覧ください。

  1. 必要な認証と承認を行います。
  2. パス オブジェクトを作成します。
  3. 署名付き JSON Web Token(JWT)を取得します。エンコードされた JWT の最大長は 2,048 文字です。
  4. JWT を使用して、[Google ウォレットに追加] の URL を生成します。

RBM API

RBM から「Google ウォレットに追加」の提案を送信するには、URL を開くアクションを送信します。メッセージ ペイロードで、次の操作を行います。

  1. text に「Google ウォレットに追加」と入力します。
  2. url に、Google ウォレットに追加の URL を入力します。

Google ウォレットのアイコンは、候補のラベルに自動的に表示されます。

[Google ウォレットに追加] の候補に表示される Google ウォレット アイコン

会話デザイン

このサンプルでは、会話の独自の機能を活用して、チェックイン フローの全体をユーザーに案内します。自然な会話と豊富な機能(ワンタップによる候補の提示やリッチカードなど)を使用して、ユーザーが目標を達成できるようにする方法を示しています。この場合の目標は、(1)フライトをカスタマイズする、(2)搭乗券を受け取る、(3)空港ですぐにアクセスできるように Google ウォレットに追加する、です。

次は会話の概要です。その後、設計のヒントとフローの手順が説明されます。エージェントに同様の設計を実装するには、手順の後のコードサンプルをご覧ください。

会話の図

デザインに関するアドバイス

チェックイン フローを設計する際は、次の原則に留意してください。

  • 最初のメッセージが最も重要です。ユーザーが会話をする理由がわかるように、会話の目的を簡潔に伝えます。
  • 各メッセージには、少量の情報を提供し、ユーザーに返信を求めるプロンプトを表示する必要があります。返信の候補操作の候補は、ユーザーが次のステップに進む際に役立ちます。
  • エージェントは機械的ではなく、迅速に対応する必要があります。ブランドのトーンと一致する言葉を使用します。理想的なブランド担当者は、顧客とどのようにチャットしますか。
  • 人々は特別な存在になりたいと思っています。ユーザーのフライト履歴に基づいて座席や食事を提案することで、チェックイン エクスペリエンスをパーソナライズできます。
  • リッチカードカルーセルを使用すると、会話がより動的に行われます。画像や詳細を共有して、ユーザーが選択する際に役立つ情報を使用します。
  • 良い会話は良い結びで終わる。搭乗券を送信する前に、ユーザーのチェックインの詳細を確認します。親しみやすい雰囲気で送ってください。

チェックイン フロー

  1. エージェントは、フライトのチェックインが開始されたことをお客様に伝えます。

    チェックインの詳細と返信文の候補が表示された応答メッセージ

    コードサンプル

    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);      
    
  2. 定型返信文をタップしてチェックインします。

    チェックイン候補がタップされている

  3. エージェントは、チェックイン プロセスの見通しをお客様に伝えます。

    メッセージの状態: ありがとうございます。わずか 3 ステップで状況を確認できます。登録手続きの最初のステップ

    コードサンプル

    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);
    });   
    

  4. エージェントは、安全に関するポリシーに同意するようお客様に依頼します。

    安全性に関するポリシーのインフォグラフィックと、同意または拒否するための候補が表示されたリッチカード。カードに表示されるテキスト: 安全なフライトを実現するため、安全に関するポリシーを確認し、同意したことをお知らせください。

    コードサンプル

    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);
    

  5. ユーザーが候補の返信をタップして同意します。

    同意を求める候補をタップしました

  6. エージェントはお客様に感謝の意を伝え、次のステップを説明します。

    メッセージ: ありがとう。無事な乗客は幸せな乗客だ!次のステップ

    コードサンプル

    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);
    });     
    

  7. エージェントはユーザーに座席の選択を促します。

    座席図のインフォグラフィックを含むリッチカード。カードのテキスト: ゆったりとくつろぎましょう。前回のフライトに基づいて、おすすめの座席をいくつかご用意しました。ご希望の座席を選択するか、座席番号を入力してご希望の座席をお知らせください。カードの後に、いくつかの座席オプションが表示される

    コードサンプル

    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);
    

  8. ユーザーが、選択した座席の候補の返信をタップします。

    座席 18A の候補をタップしました

  9. エージェントはお客様の選択を確認します。

    メッセージには、座席 18A、到着しました。

    コードサンプル

    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);
    }); 
    

  10. エージェントは、機内食を選択するようお客様に伝えます。

    メッセージの内容: 次に、食事について説明します。機内食は事前予約できます。ベジタリアンのメインディッシュ、チキンの前菜、どちらがいいですか?

    コードサンプル

    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);
    });
    

  11. エージェントに食事のオプションが表示されます。

    リッチカード カルーセルには、サラダの画像とローストチキンの画像の 2 つのカードが表示されています。どちらのカードにも、食材リストとその食事を選択するための候補が表示されます。

    コードサンプル

    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);
    

  12. ユーザーが、選択した食事に対する返信候補をタップします。

    ベジタリアンを選択する候補がタップされています。

  13. エージェントがお客様の選択を確認します。

    メッセージ: ベジタリアンです。

    コードサンプル

    const params = {
      messageText: `Vegetarian it is 💚`,
      msisdn: msisdn,
    };  
    
    let self = this;  
    
    rbmApiHelper.sendMessage(params, function (response, err) {
      self.sendAskConfirmation(msisdn);  
    });
    

  14. エージェントはチェックインの詳細を要約します。

    メッセージの内容: 座席 18A とベジタリアン メニューをご希望とのことです。選択内容を確認してください。メッセージの下に、詳細の確認、食事の変更、座席の変更の提案が表示されます。

    コードサンプル

    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);  
    

  15. ユーザーが候補の返信をタップして、チェックインの詳細を確認します。

    詳細を確認するよう求める候補がタップされています。

  16. チェックインが完了したことをエージェントが伝えます。

    メッセージには次のように表示されます。フライトのチェックインが完了しました。搭乗券をご確認ください。ご利用を心よりお待ちしております。

    コードサンプル

    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);       
    }); 
    

  17. エージェントはお客様の搭乗券を送信します。

    リッチカードには、QR コードとフライトの詳細が記載された搭乗券の画像が表示されます。カードには、「最新情報をお届けします」と記載されています。フライトの詳細が変更された場合は、通知が届きます。カードには、「Google ウォレットに追加」という候補が表示されています。

    コードサンプル

    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 ウォレットに追加しないユーザーも、最新の状態に保つ必要があります。リッチカードに表示される搭乗情報の変更について、ユーザーにメッセージを送信します。

  18. ユーザーが候補のアクションをタップして、Google ウォレットにパスを追加します。

    Google ウォレット アイコンと [Google ウォレットに追加] の候補

  19. Google ウォレット アプリが開きます。ユーザーがボタンをタップしてパスをウォレットに追加します。

    ウォレット アプリに、簡素化された搭乗券と [追加] ボタンが表示されます。

  20. ユーザーがボタンをタップしてパスを表示します。

    パスの上にチェックマークが表示されます。[Google ウォレットで表示] ボタンが表示されます。

  21. QR コード付きの搭乗券が表示されます。

    フライトの詳細と QR コードがすべて記載されている搭乗券