Web API のスタートガイド

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Web API の使用を開始する前に、前提条件を満たしていることを確認してください。Web API を引き続き使用するには、サービス アカウントとサービス アカウント キーが必要です。また、サービス アカウントに Google ウォレット API を呼び出すためのアクセス権を付与する必要があります。

GitHub からサンプルコードをダウンロードして、次の手順で参照されるコード スニペットを実行します。

認証と承認

Google ウォレット API へのリクエストは、アプリケーションが送信しているのを Google ウォレット API が識別できるように、認証を受ける必要があります。これを行うには、サービス アカウント キーを使用してアクセス トークンを取得します。

まず、必要なライブラリのインポートを行い、サービス アカウント JSON の変数と、保存される発行者、クラス、一意のユーザー、オブジェクトの ID を定義します。

Java

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.http.*;
import com.google.api.client.http.json.JsonHttpContent;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonParser;
import com.google.api.client.json.gson.GsonFactory;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.common.collect.Lists;

import java.io.*;
import java.security.interfaces.RSAPrivateKey;
import java.util.*;

// Only include if you are using the Google Wallet client library
// https://developers.google.com/wallet/retail/loyalty-cards/resources/libraries
import com.google.api.services.walletobjects.Walletobjects;
import com.google.api.services.walletobjects.model.*;

public class DemoEventTicket {
  public static void main(String[] args) throws Exception {
    /*
     * keyFilePath - Path to service account key file from Google Cloud Console
     * - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
     */
    final String keyFilePath = System.getenv().getOrDefault(
        "GOOGLE_APPLICATION_CREDENTIALS",
        "/path/to/key.json");

    /*
     * issuerId - The issuer ID being updated in this request
     * - Environment variable: WALLET_ISSUER_ID
     */
    String issuerId = System.getenv().getOrDefault(
        "WALLET_ISSUER_ID",
        "issuer-id");

    /*
     * classId - Developer-defined ID for the wallet class
     * - Environment variable: WALLET_CLASS_ID
     */
    String classId = System.getenv().getOrDefault(
        "WALLET_CLASS_ID",
        "test-eventTicket-class-id");

    /*
     * userId - Developer-defined ID for the user, such as an email address
     * - Environment variable: WALLET_USER_ID
     */
    String userId = System.getenv().getOrDefault(
        "WALLET_USER_ID",
        "user-id");

    /*
     * objectId - ID for the wallet object
     * - Format: `issuerId.identifier`
     * - Should only include alphanumeric characters, '.', '_', or '-'
     * - `identifier` is developer-defined and unique to the user
     */
    String objectId = String.format("%s.%s-%s",
        issuerId, userId.replaceAll("[^\\w.-]", "_"), classId);

PHP

use Firebase\JWT\JWT;
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Auth\Middleware\AuthTokenMiddleware;
use Google\Client as Google_Client;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Exception\ClientException;


/*
 * keyFilePath - Path to service account key file from Google Cloud Console
 *             - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
 */
$keyFilePath = getenv('GOOGLE_APPLICATION_CREDENTIALS') ?: '/path/to/key.json';

/*
 * issuerId - The issuer ID being updated in this request
 *          - Environment variable: WALLET_ISSUER_ID
 */
$issuerId = getenv('WALLET_ISSUER_ID') ?: 'issuer-id';

/*
 * classId - Developer-defined ID for the wallet class
 *         - Environment variable: WALLET_CLASS_ID
 */
$classId = getenv('WALLET_CLASS_ID') ?: 'test-eventTicket-class-id';

/*
 * userId - Developer-defined ID for the user, such as an email address
 *        - Environment variable: WALLET_USER_ID
 */
$userId = getenv('WALLET_USER_ID') ?: 'user-id';

/*
 * objectId - ID for the wallet object
 *          - Format: `issuerId.identifier`
 *          - Should only include alphanumeric characters, '.', '_', or '-'
 *          - `identifier` is developer-defined and unique to the user
 */
$objectId = "{$issuerId}." . preg_replace('/[^\w.-]/i', '_', $userId) . "-{$classId}";

Python

import json
import os
import re
import uuid

from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account
from google.auth import jwt, crypt

# KEY_FILE_PATH - Path to service account key file from Google Cloud Console
#               - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
KEY_FILE_PATH = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS",
                               "/path/to/key.json")

# ISSUER_ID - The issuer ID being updated in this request
#           - Environment variable: WALLET_ISSUER_ID
ISSUER_ID = os.environ.get("WALLET_ISSUER_ID", "issuer-id")

# CLASS_ID - Developer-defined ID for the wallet class
#         - Environment variable: WALLET_CLASS_ID
CLASS_ID = os.environ.get("WALLET_CLASS_ID", "test-eventTicket-class-id")

# USER_ID - Developer-defined ID for the user, such as an email address
#        - Environment variable: WALLET_USER_ID
USER_ID = os.environ.get("WALLET_USER_ID", "test@example.com")

# objectId - ID for the wallet object
#          - Format: `issuerId.identifier`
#          - Should only include alphanumeric characters, '.', '_', or '-'
#          - `identifier` is developer-defined and unique to the user
OBJECT_ID = "%s.%s-%s" % (ISSUER_ID, re.sub(r"[^\w.-]", "_", USER_ID), CLASS_ID)

C#

using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using Google.Apis.Auth.OAuth2;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;


class DemoEventTicket
{
  /*
  * keyFilePath - Path to service account key file from Google Cloud Console
  *             - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
  */
  static string keyFilePath = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS") ?? "/path/to/key.json";

  /*
  * issuerId - The issuer ID being used in this request
  *          - Environment variable: WALLET_ISSUER_ID
  */
  static string issuerId = Environment.GetEnvironmentVariable("WALLET_ISSUER_ID") ?? "issuer-id";

  /*
  * classId - Developer-defined ID for the wallet class
  *         - Environment variable: WALLET_CLASS_ID
  */
  static string classId = Environment.GetEnvironmentVariable("WALLET_CLASS_ID") ?? "test-eventTicket-class-id";

  /*
  * userId - Developer-defined ID for the user, such as an email address
  *        - Environment variable: WALLET_USER_ID
  */
  static string userId = Environment.GetEnvironmentVariable("WALLET_USER_ID") ?? "user-id";

  /*
  * objectId - ID for the wallet object
  *          - Format: `issuerId.identifier`
  *          - Should only include alphanumeric characters, '.', '_', or '-'
  *          - `identifier` is developer-defined and unique to the user
  */
  static string objectId = $"{issuerId}.{new Regex(@"[^\w.-]", RegexOptions.Compiled).Replace(userId, "_")}-{classId}";

Node.js

const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');

/*
 * keyFilePath - Path to service account key file from Google Cloud Console
 *             - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
 */
const keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';

/*
 * issuerId - The issuer ID being updated in this request
 *          - Environment variable: WALLET_ISSUER_ID
 */
const issuerId = process.env.WALLET_ISSUER_ID || 'issuer-id';

/*
 * classId - Developer-defined ID for the wallet class
 *         - Environment variable: WALLET_CLASS_ID
 */
const classId = process.env.WALLET_CLASS_ID || 'test-eventTicket-class-id';

/*
 * userId - Developer-defined ID for the user, such as an email address
 *        - Environment variable: WALLET_USER_ID
 */
let userId = process.env.WALLET_USER_ID || 'user-id';

/*
 * objectId - ID for the wallet object
 *          - Format: `issuerId.identifier`
 *          - Should only include alphanumeric characters, '.', '_', or '-'
 *          - `identifier` is developer-defined and unique to the user
 */
let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}-${classId}`;

次に、いずれかのフレームワーク ライブラリを使用して、Google ウォレット API の呼び出しに必要な認証情報を取得します。

Java

GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(keyFilePath))
    .createScoped(Lists.newArrayList("https://www.googleapis.com/auth/wallet_object.issuer"));
credentials.refresh();

HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
HttpRequestFactory httpRequestFactory = httpTransport.createRequestFactory(new HttpCredentialsAdapter(credentials));

PHP

$credentials = new ServiceAccountCredentials(
  'https://www.googleapis.com/auth/wallet_object.issuer',
  $keyFilePath
);

$middleware = new AuthTokenMiddleware($credentials);
$stack = HandlerStack::create();
$stack->push($middleware);
$httpClient = new Client([
  'handler' => $stack,
  'auth' => 'google_auth'
]);

Python

credentials = service_account.Credentials.from_service_account_file(
    KEY_FILE_PATH,
    scopes=["https://www.googleapis.com/auth/wallet_object.issuer"])

http_client = AuthorizedSession(credentials)

C#

credentials = (ServiceAccountCredential)GoogleCredential.FromFile(keyFilePath)
    .CreateScoped(new[] { "https://www.googleapis.com/auth/wallet_object.issuer" })
    .UnderlyingCredential;

httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer",
    await credentials.GetAccessTokenForRequestAsync()
);

Node.js

const credentials = require(keyFilePath);

const httpClient = new GoogleAuth({
  credentials: credentials,
  scopes: 'https://www.googleapis.com/auth/wallet_object.issuer'
});

パス オブジェクトの作成

Passes オブジェクトはパスクラスのインスタンスです。パス オブジェクトを作成するには、次の属性を指定する必要があります。

  • classId: Passes クラスの id
  • id: 顧客の一意の ID

これらの属性がチケットでどのように表現されるかの詳細については、テンプレート ガイドラインをご覧ください。

パス オブジェクトを作成するコードサンプル:

HTTP

POST /walletobjects/v1/eventTicketObject HTTP/1.1
Host: walletobjects.googleapis.com
Content-Type: application/json
Authorization: Bearer <ACCESS_TOKEN>;
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

{
  "id": "issuer-id.user-id",
  "classId": "issuer-id.class-id",
  "heroImage": {
    "sourceUri": {
      "uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
      "description": "Test heroImage description"
    }
  },
  "textModulesData": [
    {
      "header": "Test text module header",
      "body": "Test text module body"
    }
  ],
  "linksModuleData": {
    "uris": [
      {
        "kind": "walletobjects#uri",
        "uri": "http://maps.google.com/",
        "description": "Test link module uri description"
      },
      {
        "kind": "walletobjects#uri",
        "uri": "tel:6505555555",
        "description": "Test link module tel description"
      }
    ]
  },
  "imageModulesData": [
    {
      "mainImage": {
        "kind": "walletobjects#image",
        "sourceUri": {
          "kind": "walletobjects#uri",
          "uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
          "description": "Test image module description"
        }
      }
    }
  ],
  "barcode": {
    "kind": "walletobjects#barcode",
    "type": "qrCode",
    "value": "Test QR Code"
  },
  "state": "active",
  "seatInfo": {
    "kind": "walletobjects#eventSeat",
    "seat": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "42"
      }
    },
    "row": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "G3"
      }
    },
    "section": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "5"
      }
    },
    "gate": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "A"
      }
    }
  },
  "ticketHolderName": "Test ticket holder name",
  "ticketNumber": "Test ticket number",
  "locations": [
    {
      "kind": "walletobjects#latLongPoint",
      "latitude": 37.424015499999996,
      "longitude": -122.09259560000001
    }
  ]
}

Java

HttpRequest objectRequest;
HttpResponse objectResponse;

GenericUrl objectUrl = new GenericUrl(
    "https://walletobjects.googleapis.com/walletobjects/v1/eventTicketObject/" + objectId);
String objectPayload = String.format(
    "{"
  + "  \"id\": \"%s\","
  + "  \"classId\": \"%s.%s\","
  + "  \"heroImage\": {"
  + "    \"sourceUri\": {"
  + "      \"uri\": \"https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg\","
  + "      \"description\": \"Test heroImage description\""
  + "    }"
  + "  },"
  + "  \"textModulesData\": ["
  + "    {"
  + "      \"header\": \"Test text module header\","
  + "      \"body\": \"Test text module body\""
  + "    }"
  + "  ],"
  + "  \"linksModuleData\": {"
  + "    \"uris\": ["
  + "      {"
  + "        \"kind\": \"walletobjects#uri\","
  + "        \"uri\": \"http://maps.google.com/\","
  + "        \"description\": \"Test link module uri description\""
  + "      },"
  + "      {"
  + "        \"kind\": \"walletobjects#uri\","
  + "        \"uri\": \"tel:6505555555\","
  + "        \"description\": \"Test link module tel description\""
  + "      }"
  + "    ]"
  + "  },"
  + "  \"imageModulesData\": ["
  + "    {"
  + "      \"mainImage\": {"
  + "        \"kind\": \"walletobjects#image\","
  + "        \"sourceUri\": {"
  + "          \"kind\": \"walletobjects#uri\","
  + "          \"uri\": \"http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg\","
  + "          \"description\": \"Test image module description\""
  + "        }"
  + "      }"
  + "    }"
  + "  ],"
  + "  \"barcode\": {"
  + "    \"kind\": \"walletobjects#barcode\","
  + "    \"type\": \"qrCode\","
  + "    \"value\": \"Test QR Code\""
  + "  },"
  + "  \"state\": \"active\","
  + "  \"seatInfo\": {"
  + "    \"kind\": \"walletobjects#eventSeat\","
  + "    \"seat\": {"
  + "      \"kind\": \"walletobjects#localizedString\","
  + "      \"defaultValue\": {"
  + "        \"kind\": \"walletobjects#translatedString\","
  + "        \"language\": \"en-us\","
  + "        \"value\": \"42\""
  + "      }"
  + "    },"
  + "    \"row\": {"
  + "      \"kind\": \"walletobjects#localizedString\","
  + "      \"defaultValue\": {"
  + "        \"kind\": \"walletobjects#translatedString\","
  + "        \"language\": \"en-us\","
  + "        \"value\": \"G3\""
  + "      }"
  + "    },"
  + "    \"section\": {"
  + "      \"kind\": \"walletobjects#localizedString\","
  + "      \"defaultValue\": {"
  + "        \"kind\": \"walletobjects#translatedString\","
  + "        \"language\": \"en-us\","
  + "        \"value\": \"5\""
  + "      }"
  + "    },"
  + "    \"gate\": {"
  + "      \"kind\": \"walletobjects#localizedString\","
  + "      \"defaultValue\": {"
  + "        \"kind\": \"walletobjects#translatedString\","
  + "        \"language\": \"en-us\","
  + "        \"value\": \"A\""
  + "      }"
  + "    }"
  + "  },"
  + "  \"ticketHolderName\": \"Test ticket holder name\","
  + "  \"ticketNumber\": \"Test ticket number\","
  + "  \"locations\": ["
  + "    {"
  + "      \"kind\": \"walletobjects#latLongPoint\","
  + "      \"latitude\": 37.424015499999996,"
  + "      \"longitude\": -122.09259560000001"
  + "    }"
  + "  ]"
  + "}", objectId, issuerId, classId);

try {
  // Create and send the request
  objectRequest = httpRequestFactory.buildGetRequest(objectUrl);
  objectResponse = objectRequest.execute();

  System.out.println("object GET response: " + objectResponse.parseAsString());
} catch (HttpResponseException ex) {
  if (ex.getStatusCode() == 404) {
    // Object does not yet exist
    // Send POST request to create it
    // Convert body to JSON
    JsonParser objectParser = GsonFactory.getDefaultInstance().createJsonParser(objectPayload);
    GenericJson objectJson = objectParser.parseAndClose(GenericJson.class);
    HttpContent objectBody = new JsonHttpContent(GsonFactory.getDefaultInstance(), objectJson);

    // Create and send the request
    objectRequest = httpRequestFactory.buildPostRequest(objectUrl, objectBody);
    objectResponse = objectRequest.execute();

    System.out.println("object POST response: " + objectResponse.parseAsString());
  } else {
    // Something else went wrong
    throw ex;
  }
}

PHP

$objectUrl = "https://walletobjects.googleapis.com/walletobjects/v1/eventTicketObject/";
$objectPayload = <<<EOD
{
  "id": "{$objectId}",
  "classId": "{$issuerId}.{$classId}",
  "heroImage": {
    "sourceUri": {
      "uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
      "description": "Test heroImage description"
    }
  },
  "textModulesData": [
    {
      "header": "Test text module header",
      "body": "Test text module body"
    }
  ],
  "linksModuleData": {
    "uris": [
      {
        "kind": "walletobjects#uri",
        "uri": "http://maps.google.com/",
        "description": "Test link module uri description"
      },
      {
        "kind": "walletobjects#uri",
        "uri": "tel:6505555555",
        "description": "Test link module tel description"
      }
    ]
  },
  "imageModulesData": [
    {
      "mainImage": {
        "kind": "walletobjects#image",
        "sourceUri": {
          "kind": "walletobjects#uri",
          "uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
          "description": "Test image module description"
        }
      }
    }
  ],
  "barcode": {
    "kind": "walletobjects#barcode",
    "type": "qrCode",
    "value": "Test QR Code"
  },
  "state": "active",
  "seatInfo": {
    "kind": "walletobjects#eventSeat",
    "seat": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "42"
      }
    },
    "row": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "G3"
      }
    },
    "section": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "5"
      }
    },
    "gate": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "A"
      }
    }
  },
  "ticketHolderName": "Test ticket holder name",
  "ticketNumber": "Test ticket number",
  "locations": [
    {
      "kind": "walletobjects#latLongPoint",
      "latitude": 37.424015499999996,
      "longitude": -122.09259560000001
    }
  ]
}
EOD;

try {
  $objectResponse = $httpClient->get($objectUrl . $objectId);
} catch (ClientException $err) {
  if ($err->getResponse()->getStatusCode() == 404) {
    // Object does not yet exist
    // Send POST request to create it
    $objectResponse = $httpClient->post(
      $objectUrl,
      ['json' => json_decode($objectPayload)]
    );
  }
}

echo 'object GET or POST response: ' . $objectResponse->getBody();

Python

OBJECT_URL = "https://walletobjects.googleapis.com/walletobjects/v1/eventTicketObject/"
object_payload = {
    "id": OBJECT_ID,
    "classId": f"{ISSUER_ID}.{CLASS_ID}",
    "heroImage": {
        "sourceUri": {
            "uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
            "description": "Test heroImage description"
        }
    },
    "textModulesData": [
        {
            "header": "Test text module header",
            "body": "Test text module body"
        }
    ],
    "linksModuleData": {
        "uris": [
            {
                "kind": "walletobjects#uri",
                "uri": "http://maps.google.com/",
                "description": "Test link module uri description"
            },
            {
                "kind": "walletobjects#uri",
                "uri": "tel:6505555555",
                "description": "Test link module tel description"
            }
        ]
    },
    "imageModulesData": [
        {
            "mainImage": {
                "kind": "walletobjects#image",
                "sourceUri": {
                    "kind": "walletobjects#uri",
                    "uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
                    "description": "Test image module description"
                }
            }
        }
    ],
    "barcode": {
        "kind": "walletobjects#barcode",
        "type": "qrCode",
        "value": "Test QR Code"
    },
    "state": "active",
    "seatInfo": {
        "kind": "walletobjects#eventSeat",
        "seat": {
            "kind": "walletobjects#localizedString",
            "defaultValue": {
                "kind": "walletobjects#translatedString",
                "language": "en-us",
                "value": "42"
            }
        },
        "row": {
            "kind": "walletobjects#localizedString",
            "defaultValue": {
                "kind": "walletobjects#translatedString",
                "language": "en-us",
                "value": "G3"
            }
        },
        "section": {
            "kind": "walletobjects#localizedString",
            "defaultValue": {
                "kind": "walletobjects#translatedString",
                "language": "en-us",
                "value": "5"
            }
        },
        "gate": {
            "kind": "walletobjects#localizedString",
            "defaultValue": {
                "kind": "walletobjects#translatedString",
                "language": "en-us",
                "value": "A"
            }
        }
    },
    "ticketHolderName": "Test ticket holder name",
    "ticketNumber": "Test ticket number",
    "locations": [
        {
            "kind": "walletobjects#latLongPoint",
            "latitude": 37.424015499999996,
            "longitude": -122.09259560000001
        }
    ]
}

object_response = http_client.get(OBJECT_URL + OBJECT_ID)
if object_response.status_code == 404:
    # Object does not yet exist
    # Send POST request to create it
    object_response = http_client.post(
        OBJECT_URL,
        json=object_payload
    )

print("object GET or POST response:", object_response.text)

C#

string objectUrl = "https://walletobjects.googleapis.com/walletobjects/v1/eventTicketObject/";
var objectPayload = new
{
  id = objectId,
  classId = $"{issuerId}.{classId}",
  heroImage = new
  {
    sourceUri = new
    {
      uri = "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
      description = "Test heroImage description"
    }
  },
  textModulesData = new object[]
  {
    new
    {
      header = "Test text module header",
      body = "Test text module body"
    }
  },
  linksModuleData = new
  {
    uris = new object[]
    {
      new
      {
        kind = "walletobjects#uri",
        uri = "http://maps.google.com/",
        description = "Test link module uri description"
      },
      new
      {
        kind = "walletobjects#uri",
        uri = "tel:6505555555",
        description = "Test link module tel description"
      }
    }
  },
  imageModulesData = new object[]
  {
    new
    {
      mainImage = new
      {
        kind = "walletobjects#image",
        sourceUri = new
        {
          kind = "walletobjects#uri",
          uri = "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
          description = "Test image module description"
        }
      }
    }
  },
  barcode = new
  {
    kind = "walletobjects#barcode",
    type = "qrCode",
    value = "Test QR Code"
  },
  state = "active",
  seatInfo = new
  {
    kind = "walletobjects#eventSeat",
    seat = new
    {
      kind = "walletobjects#localizedString",
      defaultValue = new
      {
        kind = "walletobjects#translatedString",
        language = "en-us",
        value = "42"
      }
    },
    row = new
    {
      kind = "walletobjects#localizedString",
      defaultValue = new
      {
        kind = "walletobjects#translatedString",
        language = "en-us",
        value = "G3"
      }
    },
    section = new
    {
      kind = "walletobjects#localizedString",
      defaultValue = new
      {
        kind = "walletobjects#translatedString",
        language = "en-us",
        value = "5"
      }
    },
    gate = new
    {
      kind = "walletobjects#localizedString",
      defaultValue = new
      {
        kind = "walletobjects#translatedString",
        language = "en-us",
        value = "A"
      }
    }
  },
  ticketHolderName = "Test ticket holder name",
  ticketNumber = "Test ticket number",
  locations = new object[]
  {
    new
    {
      kind = "walletobjects#latLongPoint",
      latitude = 37.424015499999996,
      longitude = -122.09259560000001
    }
  }
};

HttpRequestMessage objectRequest = new HttpRequestMessage(HttpMethod.Get, $"{objectUrl}{objectId}");
HttpResponseMessage objectResponse = httpClient.Send(objectRequest);
if (objectResponse.StatusCode == HttpStatusCode.NotFound)
{
  // Object does not yet exist
  // Send POST request to create it
  objectRequest = new HttpRequestMessage(HttpMethod.Post, objectUrl);
  objectRequest.Content = new StringContent(JsonConvert.SerializeObject(objectPayload));
  objectResponse = httpClient.Send(objectRequest);
}

string objectContent = await objectResponse.Content.ReadAsStringAsync();
Console.WriteLine($"object GET or POST response: {objectContent}");

Node.js

const objectUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/eventTicketObject/';
const objectPayload = {
  "id": objectId,
  "classId": `${issuerId}.${classId}`,
  "heroImage": {
    "sourceUri": {
      "uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
      "description": "Test heroImage description"
    }
  },
  "textModulesData": [
    {
      "header": "Test text module header",
      "body": "Test text module body"
    }
  ],
  "linksModuleData": {
    "uris": [
      {
        "kind": "walletobjects#uri",
        "uri": "http://maps.google.com/",
        "description": "Test link module uri description"
      },
      {
        "kind": "walletobjects#uri",
        "uri": "tel:6505555555",
        "description": "Test link module tel description"
      }
    ]
  },
  "imageModulesData": [
    {
      "mainImage": {
        "kind": "walletobjects#image",
        "sourceUri": {
          "kind": "walletobjects#uri",
          "uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
          "description": "Test image module description"
        }
      }
    }
  ],
  "barcode": {
    "kind": "walletobjects#barcode",
    "type": "qrCode",
    "value": "Test QR Code"
  },
  "state": "active",
  "seatInfo": {
    "kind": "walletobjects#eventSeat",
    "seat": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "42"
      }
    },
    "row": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "G3"
      }
    },
    "section": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "5"
      }
    },
    "gate": {
      "kind": "walletobjects#localizedString",
      "defaultValue": {
        "kind": "walletobjects#translatedString",
        "language": "en-us",
        "value": "A"
      }
    }
  },
  "ticketHolderName": "Test ticket holder name",
  "ticketNumber": "Test ticket number",
  "locations": [
    {
      "kind": "walletobjects#latLongPoint",
      "latitude": 37.424015499999996,
      "longitude": -122.09259560000001
    }
  ]
};
let objectResponse;

try {
  objectResponse = await httpClient.request({
    url: objectUrl + objectId,
    method: 'GET'
  });
} catch (err) {
  if (err.response && err.response.status === 404) {
    // Object does not yet exist
    // Send POST request to create it
    objectResponse = await httpClient.request({
      url: objectUrl,
      method: 'POST',
      data: objectPayload
    });
  } else {
    objectResponse = err;
  }
}

console.log('object GET or POST response:', objectResponse);

このステップを完了すると、お客様のパスオブジェクトがサーバーに作成されます。ただし、この段階では、パス オブジェクトは Google ユーザーまたはデバイスにリンクされていません。パスを Google ウォレット ユーザーに関連付けるには、まず Google ウォレットにパスを追加する必要があります。

Google ウォレットに追加中

Google ウォレットにパスを追加すると、パス オブジェクトが Google ユーザーにリンクされ、Google の ID でログインしている場合にのみ開始できます。そのためには、「Google ウォレットに追加」の URL にユーザーを誘導します。

「Google ウォレットに追加」URL は、前の手順で作成したパス オブジェクト ID に関する次の情報を含む、動的に生成される URL です。この情報は、JSON Web Token(JWT)としてエンコードされます。

JSON Web Token(JWT)

この JWT には、ユーザーが保存するパス オブジェクトに関して、カード発行会社が行うクレームが含まれています。JWT は、サービス アカウントの作成の手順で取得したサービス アカウント キーの private_key を使用して署名する必要があります。Google は、JWT 署名を検証して、これらのクレームを検証します。

JWT クレームの構造は次のようになります。

{
  "aud": "google",
  "origins": ["https://example.com"],
  "iss": "my-service-account@my-project-id.iam.gserviceaccount.com",
  "typ": "savetowallet",
  "payload": {
    "eventTicketObjects": [
      {
        "id": "PASSES_OBJECT_ID_1234567890"
      }
    ]
  }
}

JWT claims を作成し(ステップ 1)、サービス アカウント キーの private_key を使用してクレームに署名することで token を取得します(ステップ 2)。

Java

HashMap<String, String> objectIdMap = new HashMap<String, String>();
objectIdMap.put("id", objectId);

HashMap<String, Object> payload = new HashMap<String, Object>();
payload.put("eventTicketObjects", new ArrayList<>(Arrays.asList(objectIdMap)));

HashMap<String, Object> claims = new HashMap<String, Object>();
claims.put("iss", ((ServiceAccountCredentials) credentials).getClientEmail());
claims.put("aud", "google");
claims.put("origins", new ArrayList<>(Arrays.asList("www.example.com")));
claims.put("typ", "savetowallet");
claims.put("payload", payload);

Algorithm algorithm = Algorithm.RSA256(
    null,
    (RSAPrivateKey) ((ServiceAccountCredentials) credentials).getPrivateKey());
String token = JWT.create()
    .withPayload(claims)
    .sign(algorithm);
String saveUrl = "https://pay.google.com/gp/v/save/" + token;

System.out.println(saveUrl);

PHP

$serviceAccount = json_decode(file_get_contents($keyFilePath), true);
$claims = [
  'iss' => $serviceAccount['client_email'],
  'aud' => 'google',
  'origins' => ['www.example.com'],
  'typ' => 'savetowallet',
  'payload' => [
    'eventTicketObjects' => [
      ['id' => $objectId]
    ]
  ]
];

$token = JWT::encode($claims, $serviceAccount['private_key'], 'RS256');
$saveUrl = "https://pay.google.com/gp/v/save/${token}";

echo $saveUrl;

Python

claims = {
    "iss": http_client.credentials.service_account_email,
    "aud": "google",
    "origins": ["www.example.com"],
    "typ": "savetowallet",
    "payload": {
        "eventTicketObjects": [
            {
                "id": OBJECT_ID
            }
        ]
    }
}

signer = crypt.RSASigner.from_service_account_file(KEY_FILE_PATH)
token = jwt.encode(signer, claims).decode("utf-8")
save_url = f"https://pay.google.com/gp/v/save/{token}"

print(save_url)

C#

JwtPayload claims = new JwtPayload();
claims.Add("iss", credentials.Id);
claims.Add("aud", "google");
claims.Add("origins", new string[] { "www.example.com" });
claims.Add("typ", "savetowallet");
claims.Add("payload", new
{
  eventTicketObjects = new object[]
  {
    new
    {
      id = objectId
    }
  }
});

RsaSecurityKey key = new RsaSecurityKey(credentials.Key);
SigningCredentials signingCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256);
JwtSecurityToken jwt = new JwtSecurityToken(new JwtHeader(signingCredentials), claims);
string token = new JwtSecurityTokenHandler().WriteToken(jwt);
string saveUrl = $"https://pay.google.com/gp/v/save/{token}";

Console.WriteLine(saveUrl);

Node.js

const claims = {
  iss: credentials.client_email,
  aud: 'google',
  origins: ['www.example.com'],
  typ: 'savetowallet',
  payload: {
    eventTicketObjects: [{
      id: objectId
    }],
  }
};

const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;

console.log(saveUrl);

署名付き JWT を取得したら、この情報を使用して Google ウォレットに追加リンクを作成できます。

[Google ウォレットに追加] リンクの形式は次のとおりです。

https://pay.google.com/gp/v/save/{token}

このリンクはウェブページに埋め込んだり、ハイパーリンクとしてメールで送信したりできます。チャットや SMS などの他のチャネルを使用してお客様に送信することもできます。

エンコードされた JWT の安全な長さは 1,800 文字までです。JWT がこの上限を下回る場合は、署名された JWT にオブジェクト全体を含めることができます。この上限を超えないようにしてください。1, 800 文字を超えると、ウェブブラウザによる切り捨てが発生し、Google ウォレットにパスを追加できないことがあります。

[Google ウォレットに追加] ボタン

ウェブページ、メール、Android アプリで Google ウォレット ボタンのアセットを使用すると、最適な結果が得られます。

Google ウォレット ボタンは次の 2 つの方法でレンダリングできます。

  • JavaScript ウェブボタンはウェブサイトに使用できます。
  • ボタン付きの JWT リンクは、メール、SMS、アプリ、ウェブサイトに使用できます。

次のステップ

  • チケットの外観をカスタマイズする
  • Google Pay and Wallet Consoleサポートへのお問い合わせフォームを使用して、最終的な審査のために UX フローを送信します。