Google ウォレットのパスの開発フロー

Google Wallet API には、ギフトカード、搭乗券、イベント チケットなどの特定のユースケース向けに最適化された、事前定義されたパスタイプが用意されています。特定のパスタイプが利用できないユースケース向けの汎用パスタイプもあります。

この記事では、Google Wallet API を使用してパスを作成し、発行するために必要な基本的な手順について説明します。以下で説明するステップの一部を実施する方法は複数ありますが、大まかに言えば、すべてのパスタイプは同じ基本的な開発フローに沿って作成されます。

パスの作成に関する詳細なチュートリアルについては、ウェブ、メール、SMS または Android アプリのガイドをご覧ください。

目的

パスクラスは、テンプレートと同様に、複数のパスに共通する一連のプロパティを定義します。たとえば、イベントのチケットを発行する場合、パスクラスは、イベント名、日付、時間など、すべてのチケットで共通するフィールドを定義します。

発行するすべてのパスでパスクラスを参照する必要があります。また、作成するすべてのパスクラスに一意の ID を割り当てる必要があります。この ID は、パスの作成時に参照するために使用されます。

仕組み

パスクラスは JSON 形式で定義され、Google Wallet REST API、Android SDK、Google ウォレット Business Console で作成できます。

パスクラスの例を表示

{
  "id": "ISSUER_ID.EVENT_CLASS_ID",
  "issuerName": "[TEST ONLY] Heraldic Event",
  "localizedIssuerName": {
    "defaultValue": {
      "language": "en-US",
      "value": "[TEST ONLY] Heraldic Event"
    }
  },
  "logo": {
    "sourceUri": {
      "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660"
    },
    "contentDescription": {
      "defaultValue": {
        "language": "en-US",
        "value": "LOGO_IMAGE_DESCRIPTION"
      }
    }
  },
  "eventName": {
    "defaultValue": {
      "language": "en-US",
      "value": "Google Live"
    }
  },
  "venue": {
    "name": {
      "defaultValue": {
        "language": "en-US",
        "value": "Shoreline Amphitheater"
      }
    },
    "address": {
      "defaultValue": {
        "language": "en-US",
        "value": "ADDRESS_OF_THE_VENUE"
      }
    }
  },
  "dateTime": {
    "start": "2023-04-12T11:30"
  },
  "reviewStatus": "UNDER_REVIEW",
  "hexBackgroundColor": "#264750",
  "heroImage": {
    "sourceUri": {
      "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336"
    },
    "contentDescription": {
      "defaultValue": {
        "language": "en-US",
        "value": "HERO_IMAGE_DESCRIPTION"
      }
    }
  }
}
    

目的

パス オブジェクトは、特定のユーザーに発行される一意のパスのプロパティを定義します。たとえば、イベント チケットのパス オブジェクトは、座席番号やそのチケットの QR コードなど、特定のチケットに固有のフィールドを定義します。

パス オブジェクトが作成されると、Google Wallet API によって新しいパスが保存され、発行者アカウントに関連付けられます。この保存されたパスは、パス オブジェクトの一意のプロパティと、関連するパスクラスのテンプレート プロパティを組み合わせたものです。

また、各パス オブジェクトに一意の ID を割り当てる必要があります。この ID は、パスの発行時に参照するために使用されます。

仕組み

パス オブジェクトは JSON 形式で定義されており、Google Wallet REST API または Android SDK で作成できます。

パス オブジェクトの例を表示

{
  "id": "ISSUER_ID.OBJECT_ID",
  "classId": "ISSUER_ID.EVENT_CLASS_ID",
  "state": "ACTIVE",
  "seatInfo": {
    "seat": {
      "defaultValue": {
        "language": "en-us",
        "value": "5"
      }
    },
    "row": {
      "defaultValue": {
        "language": "en-us",
        "value": "G"
      }
    },
    "section": {
      "defaultValue": {
        "language": "en-us",
        "value": "40"
      }
    },
    "gate": {
      "defaultValue": {
        "language": "en-us",
        "value": "3A"
      }
    }
  },
  "barcode": {
    "type": "QR_CODE",
    "value": "BARCODE_VALUE",
    "alternateText": ""
  }
}
    

目的

ユーザーにパスを発行するには、パスクラスとパス オブジェクトを JSON Web Token(JWT)でエンコードする必要があります。JWT 形式は、2 つの当事者間のクレームを表すための一般的かつオープン スタンダードです。Google Wallet API でパスを発行する場合、JWT を使用して、発行元アカウントに関連付けられている特定のパスにアクセスする権限をユーザーを持っているというクレームを送信します。

JWT が Google Wallet API に送信されると、エンコードされたデータを使用して特定のパスが識別され、ユーザーに発行されます。パスがすでに発行されている場合は、このデータによって Google Wallet API でパスが重複していることを識別できるため、ユーザーの Google ウォレットに複数回追加されることがなくなります。

仕組み

JWT は、JWT の仕様に基づいて JSON 形式で定義されます。Google Wallet API でパスを発行するための JWT を定義するには、発行するパスの情報を JWT の payload プロパティに指定します。

JWT の例を表示

{
  "iss": "issuer@example.com",
  "aud": "google",
  "typ": "savetowallet",
  "iat": 1696877738,
  "origins": [
    "www.example.com"
  ],
  "payload": {
    "eventTicketObjects": [
      {
        "id": "ISSUER_ID.LOYALTY_OBJECT_SUFFIX"
      }
    ]
  }
}
    

目的

パスを発行するために Google Wallet API に送信される JWT はすべて、Google Wallet Business Console で以前に提供した認証情報で署名する必要があります。署名は認証情報を使用して JWT を暗号化し、パスのセキュリティを確保し、エンコードされたパスの詳細が有効で発行者アカウントに関連付けられていることを Google Wallet API が認証できるようにします。

仕組み

Google ウォレット クライアント ライブラリと Android SDK には、JWT に署名するための便利なメソッドが用意されています。コード署名の複雑さに対処できるオープンソース ライブラリも数多く用意されており、その中から選ぶことができます。

Google Wallet REST API を使用してパスを発行する場合、JWT は Google Cloud サービス アカウント キーで署名されます。Google Wallet Android SDK を使用している場合、SDK はアプリ署名証明書の SHA-1 フィンガープリントを使用して JWT の署名を自動的に処理します。

認証情報を保護するため、JWT への署名はサーバーでのみ行うか、アプリで Google ウォレット Android SDK を使用してください。

コード署名の例を表示

Java

  // Create the JWT as a HashMap object
  HashMap claims = new HashMap();
  claims.put("iss", ((ServiceAccountCredentials) credentials).getClientEmail());
  claims.put("aud", "google");
  claims.put("origins", Arrays.asList("www.example.com"));
  claims.put("typ", "savetowallet");

  // Create the Google Wallet payload and add to the JWT
  HashMap payload = new HashMap();
  payload.put("eventTicketObjects", Arrays.asList(newObject));
  claims.put("payload", payload);

  // Google Cloud service account credentials are used to sign the JWT
  Algorithm algorithm =
      Algorithm.RSA256(
          null, (RSAPrivateKey) ((ServiceAccountCredentials) credentials).getPrivateKey());
  String token = JWT.create().withPayload(claims).sign(algorithm);
        

Node.JS

  // Create the JWT claims
  let claims = {
    iss: this.credentials.client_email,
    aud: 'google',
    origins: ['www.example.com'],
    typ: 'savetowallet',
    payload: {
      eventTicketObjects: [newObject]
    },
  };

  // The service account credentials are used to sign the JWT
  let token = jwt.sign(claims, this.credentials.private_key, { algorithm: 'RS256' });
        

Python

  # Create the JWT claims
  claims = {
      'iss': self.credentials.service_account_email,
      'aud': 'google',
      'origins': ['www.example.com'],
      'typ': 'savetowallet',
      'payload': {
          # The listed classes and objects will be created
          'eventTicketObjects': [new_object]
      }
  }

  # The service account credentials are used to sign the JWT
  signer = crypt.RSASigner.from_service_account_file(self.key_file_path)
  token = jwt.encode(signer, claims).decode('utf-8')
        

目的

署名付き JWT を作成したら、Google ウォレット ユーザーにパスを発行できます。これを行うには、[Google ウォレットに追加] ボタンまたはリンクを表示します。ユーザーがボタンまたはハイパーリンクをクリックすると、署名付き JWT が Google Wallet API に送信され、保存された認証情報を使用して復号されます。JWT 署名が認証されると、ユーザーにパスが発行され、Google ウォレットに保存されます。

仕組み

Android アプリ用の [Google ウォレットに追加] ボタンを作成するには、ボタンを生成するメソッドを提供する Google ウォレット Android SDK を使用します。ウェブ、メール、テキスト メッセージなど、他のすべてのプラットフォームでは、https://pay.google.com/gp/v/save/<signed_jwt> の形式でハイパーリンクを作成します。可能であれば、このリンクは [Google ウォレットに追加] ボタンとしてユーザーに提供することをおすすめします。

[Google ウォレットに追加] ボタンの使用方法については、Google Wallet API のブランド ガイドラインをご覧ください。

サンプルコードを表示

  https://pay.google.com/gp/v/save/<signed_jwt>
        

Android SDK

  private lateinit var walletClient: PayClient
  private val addToGoogleWalletRequestCode = 1000
  private lateinit var addToGoogleWalletButton: View

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    walletClient = Pay.getClient(this)
    addToGoogleWalletButton.setOnClickListener {
      walletClient.savePasses(newObjectJson, this, addToGoogleWalletRequestCode)
    }
  }
        

ユーザーが発行したパスを保存すると、保存した他のパスとともに Google ウォレット アプリに表示されます。

JWT でパス オブジェクトとパスクラスを作成する

パスのクラスとパスのオブジェクトは、Google Wallet REST API または Android SDK を使用して事前に作成できます。作成されたパスは、ID を参照してパスを発行するために使用されます。

または、ユーザーにパスを発行する際に使用する JWT に JSON を直接埋め込むことで、パスクラス オブジェクトとパス オブジェクトを「ジャスト イン タイム」で作成することもできます。このメソッドでは、署名付き JWT が [Google ウォレットに追加] ボタンまたはリンクを使用して送信されたときに、Google Wallet API によってパスクラス オブジェクトとパス オブジェクトが作成されます。

たとえば、payload.eventTicketClasses プロパティと payload.eventTicketObjects プロパティを使用して新しいパスクラスとパス オブジェクトが定義された JWT を以下に示します。これらのプロパティは配列であるため、1 つ以上のパスクラスまたはパス オブジェクトを受け入れることができます。既存のパスクラスを参照する新しいパス オブジェクトを ID で JWT で指定することもできます。

JWT の例を表示

  {
    "iss": "issuer@example.com",
    "aud": "google",
    "typ": "savetowallet",
    "iat": 1696877738,
    "origins": [
      "www.example.com"
    ],
    "payload": {
      "eventTicketClasses": [{
        "id": "ISSUER_ID.EVENT_CLASS_ID",
        "issuerName": "[TEST ONLY] Heraldic Event",
        "localizedIssuerName": {
          "defaultValue": {
            "language": "en-US",
            "value": "[TEST ONLY] Heraldic Event"
          }
        },
        "logo": {
          "sourceUri": {
            "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660"
          },
          "contentDescription": {
            "defaultValue": {
              "language": "en-US",
              "value": "LOGO_IMAGE_DESCRIPTION"
            }
          }
        },
        "eventName": {
          "defaultValue": {
            "language": "en-US",
            "value": "Google Live"
          }
        },
        "venue": {
          "name": {
            "defaultValue": {
              "language": "en-US",
              "value": "Shoreline Amphitheater"
            }
          },
          "address": {
            "defaultValue": {
              "language": "en-US",
              "value": "ADDRESS_OF_THE_VENUE"
            }
          }
        },
        "dateTime": {
          "start": "2023-04-12T11:30"
        },
        "reviewStatus": "UNDER_REVIEW",
        "hexBackgroundColor": "#264750",
        "heroImage": {
          "sourceUri": {
            "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336"
          },
          "contentDescription": {
            "defaultValue": {
              "language": "en-US",
              "value": "HERO_IMAGE_DESCRIPTION"
            }
          }
        }
      }],
      "eventTicketObjects": [{
        "id": "ISSUER_ID.OBJECT_ID",
        "classId": "ISSUER_ID.EVENT_CLASS_ID",
        "state": "ACTIVE",
        "seatInfo": {
          "seat": {
            "defaultValue": {
              "language": "en-us",
              "value": "5"
            }
          },
          "row": {
            "defaultValue": {
              "language": "en-us",
              "value": "G"
            }
          },
          "section": {
            "defaultValue": {
              "language": "en-us",
              "value": "40"
            }
          },
          "gate": {
            "defaultValue": {
              "language": "en-us",
              "value": "3A"
            }
          }
        },
        "barcode": {
          "type": "QR_CODE",
          "value": "BARCODE_VALUE",
          "alternateText": ""
        }
      }]
    }
  }