Getting started with the Web API

Stay organized with collections Save and categorize content based on your preferences.

Before getting started with the Web API, make sure you’ve completed the prerequisites. To continue with the Web API, you must have a service account and a service account key, and you are required to grant access to your service account to call the Google Wallet API.

Download the sample code on GitHub to run the code snippets referenced in the steps below.

Authentication and authorization

Requests to the Google Wallet API must be authenticated so that the Google Wallet API can identify that the request is being made by your application. This is achieved by using the service account key to obtain an access token.

First, perform the necessary library imports and define some variables for the service account JSON, and IDs for the issuer, class, unique user and object that will be saved.

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.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.*;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.walletobjects.Walletobjects;
import com.google.api.services.walletobjects.model.*;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import java.io.*;
import java.security.interfaces.RSAPrivateKey;
import java.util.*;

/** Demo class for creating and managing Event tickets in Google Wallet. */
public class DemoEventTicket {
  /**
   * Path to service account key file from Google Cloud Console. Environment variable:
   * GOOGLE_APPLICATION_CREDENTIALS.
   */
  public static String keyFilePath;

  /** Service account credentials for Google Wallet APIs. */
  public static GoogleCredentials credentials;

  /** Google Wallet service client. */
  public static Walletobjects service;

  public DemoEventTicket() {
    keyFilePath =
        System.getenv().getOrDefault("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/key.json");
  }

PHP

use Firebase\JWT\JWT;
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Client as Google_Client;

/** Demo class for creating and managing Event tickets in Google Wallet. */
class DemoEventTicket
{
  /**
   * Path to service account key file from Google Cloud Console. Environment
   * variable: GOOGLE_APPLICATION_CREDENTIALS.
   */
  public string $keyFilePath;

  /**
   * Service account credentials for Google Wallet APIs.
   */
  public ServiceAccountCredentials $credentials;

  /**
   * Google Wallet service client.
   */
  public Google_Service_Walletobjects $service;

  public function __construct()
  {
    $this->keyFilePath = getenv('GOOGLE_APPLICATION_CREDENTIALS') ?: '/path/to/key.json';
  }

Python

import json
import os
import re
from typing import List
import uuid

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


class DemoEventTicket:
    """Demo class for creating and managing Event tickets in Google Wallet.

    Attributes:
        key_file_path: Path to service account key file from Google Cloud
            Console. Environment variable: GOOGLE_APPLICATION_CREDENTIALS.
        base_url: Base URL for Google Wallet API requests.
    """

    def __init__(self):
        self.key_file_path = os.environ.get('GOOGLE_APPLICATION_CREDENTIALS',
                                            '/path/to/key.json')
        self.base_url = 'https://walletobjects.googleapis.com/walletobjects/v1'

        # Set up authenticated client
        self.auth()

C#

using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Walletobjects.v1;
using Google.Apis.Walletobjects.v1.Data;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;


/// <summary>
/// Demo class for creating and managing Event tickets in Google Wallet.
/// </summary>
class DemoEventTicket
{
  /// <summary>
  /// Path to service account key file from Google Cloud Console. Environment
  /// variable: GOOGLE_APPLICATION_CREDENTIALS.
  /// </summary>
  public static string keyFilePath;

  /// <summary>
  /// Service account credentials for Google Wallet APIs
  /// </summary>
  public static ServiceAccountCredential credentials;

  /// <summary>
  /// Google Wallet service client
  /// </summary>
  public static WalletobjectsService service;

  public DemoEventTicket()
  {
    keyFilePath = Environment.GetEnvironmentVariable(
        "GOOGLE_APPLICATION_CREDENTIALS") ?? "/path/to/key.json";
  }

Node.js

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

/**
 * Demo class for creating and managing Event tickets in Google Wallet.
 */
class DemoEventTicket {
  constructor() {
    /**
     * Path to service account key file from Google Cloud Console. Environment
     * variable: GOOGLE_APPLICATION_CREDENTIALS.
     */
    this.keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';

    /**
     * Base URL for Google Wallet API requests.
     */
    this.baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1'
  }

Next, use one of the framework libraries to retrieve the necessary credentials to call the Google Wallet API.

Java

/**
 * Create authenticated HTTP client using a service account file.
 *
 * @throws Exception
 */
public void Auth() throws Exception {
  String scope = "https://www.googleapis.com/auth/wallet_object.issuer";

  credentials =
      GoogleCredentials.fromStream(new FileInputStream(keyFilePath))
          .createScoped(Arrays.asList(scope));
  credentials.refresh();

  HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

  service =
      new Walletobjects.Builder(
              httpTransport,
              GsonFactory.getDefaultInstance(),
              new HttpCredentialsAdapter(credentials))
          .setApplicationName("APPLICATION_NAME")
          .build();
}

PHP

/**
 * Create authenticated HTTP client using a service account file.
 */
public function auth()
{
  $scope = 'https://www.googleapis.com/auth/wallet_object.issuer';

  $this->credentials = new ServiceAccountCredentials(
    $scope,
    $this->keyFilePath
  );

  // Initialize Google Wallet API service
  $this->client = new Google_Client();
  $this->client->setApplicationName('APPLICATION_NAME');
  $this->client->setScopes($scope);
  $this->client->setAuthConfig($this->keyFilePath);

  $this->service = new Google_Service_Walletobjects($this->client);
}

Python

def auth(self):
    """Create authenticated HTTP client using a service account file."""
    self.credentials = Credentials.from_service_account_file(
        self.key_file_path,
        scopes=['https://www.googleapis.com/auth/wallet_object.issuer'])

    self.http_client = AuthorizedSession(self.credentials)

C#

/// <summary>
/// Create authenticated service client using a service account file.
/// </summary>
public void Auth()
{
  credentials = (ServiceAccountCredential)GoogleCredential
      .FromFile(keyFilePath)
      .CreateScoped(new[]
      {
        "https://www.googleapis.com/auth/wallet_object.issuer"
      })
      .UnderlyingCredential;

  service = new WalletobjectsService(
      new BaseClientService.Initializer()
      {
        HttpClientInitializer = credentials,
      });
}

Node.js

/**
 * Create authenticated HTTP client using a service account file.
 */
auth() {
  this.credentials = require(this.keyFilePath);

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

Creating a Passes Object

A Passes Object is an instance of a Passes Class. In order to create a Passes Object, you must provide the following attributes:

  • classId: The id of the Passes Class
  • id: A unique ID for your customer

Refer to the template guidelines for more information on how these attributes are represented in the ticket.

Code sample to create a Passes Object:

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.OBJECT_ID",
  "classId": "ISSUER_ID.CLASS_SUFFIX",
  "state": "ACTIVE",
  "heroImage": {
    "sourceUri": {
      "uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"
    },
    "contentDescription": {
      "defaultValue": {
        "language": "en-US",
        "value": "Hero image description"
      }
    }
  },
  "textModulesData": [
    {
      "header": "Text module header",
      "body": "Text module body",
      "id": "TEXT_MODULE_ID"
    }
  ],
  "linksModuleData": {
    "uris": [
      {
        "uri": "http://maps.google.com/",
        "description": "Link module URI description",
        "id": "LINK_MODULE_URI_ID"
      },
      {
        "uri": "tel:6505555555",
        "description": "Link module tel description",
        "id": "LINK_MODULE_TEL_ID"
      }
    ]
  },
  "imageModulesData": [
    {
      "mainImage": {
        "sourceUri": {
          "uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"
        },
        "contentDescription": {
          "defaultValue": {
            "language": "en-US",
            "value": "Image module description"
          }
        }
      },
      "id": "IMAGE_MODULE_ID"
    }
  ],
  "barcode": {
    "type": "QR_CODE",
    "value": "QR code"
  },
  "locations": [
    {
      "latitude": 37.424015499999996,
      "longitude": -122.09259560000001
    }
  ],
  "seatInfo": {
    "seat": {
      "defaultValue": {
        "language": "en-US",
        "value": "42"
      }
    },
    "row": {
      "defaultValue": {
        "language": "en-US",
        "value": "G3"
      }
    },
    "section": {
      "defaultValue": {
        "language": "en-US",
        "value": "5"
      }
    },
    "gate": {
      "defaultValue": {
        "language": "en-US",
        "value": "A"
      }
    }
  },
  "ticketHolderName": "Ticket holder name",
  "ticketNumber": "Ticket number"
}

Java

/**
 * Create an object via the API.
 *
 * @param issuerId The issuer ID being used for this request.
 * @param classSuffix Developer-defined unique ID for this pass class.
 * @param userId Developer-defined user ID for this object.
 * @return The pass object ID: "{issuerId}.{userId}"
 * @throws IOException
 */
public String CreateEventTicketObject(String issuerId, String classSuffix, String userId)
    throws IOException {
  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  String newUserId = userId.replaceAll("[^\\w.-]", "_");
  String objectId = String.format("%s.%s", issuerId, newUserId);

  try {
    // Check if the object exists
    EventTicketObject response = service.eventticketobject().get(objectId).execute();

    System.out.println("Object get response");
    System.out.println(response.toPrettyString());

    return response.getId();
  } catch (GoogleJsonResponseException ex) {
    if (ex.getStatusCode() != 404) {
      // Something else went wrong
      ex.printStackTrace();
      return ex.getMessage();
    }
  }

  // Object doesn't exist, create it now
  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
  EventTicketObject eventTicketObject =
      new EventTicketObject()
          .setId(objectId)
          .setClassId(String.format("%s.%s", issuerId, classSuffix))
          .setState("ACTIVE")
          .setHeroImage(
              new Image()
                  .setSourceUri(
                      new ImageUri()
                          .setUri(
                              "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"))
                  .setContentDescription(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString()
                                  .setLanguage("en-US")
                                  .setValue("Hero image description"))))
          .setTextModulesData(
              Arrays.asList(
                  new TextModuleData()
                      .setHeader("Text module header")
                      .setBody("Text module body")
                      .setId("TEXT_MODULE_ID")))
          .setLinksModuleData(
              new LinksModuleData()
                  .setUris(
                      Arrays.asList(
                          new Uri()
                              .setUri("http://maps.google.com/")
                              .setDescription("Link module URI description")
                              .setId("LINK_MODULE_URI_ID"),
                          new Uri()
                              .setUri("tel:6505555555")
                              .setDescription("Link module tel description")
                              .setId("LINK_MODULE_TEL_ID"))))
          .setImageModulesData(
              Arrays.asList(
                  new ImageModuleData()
                      .setMainImage(
                          new Image()
                              .setSourceUri(
                                  new ImageUri()
                                      .setUri(
                                          "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"))
                              .setContentDescription(
                                  new LocalizedString()
                                      .setDefaultValue(
                                          new TranslatedString()
                                              .setLanguage("en-US")
                                              .setValue("Image module description"))))
                      .setId("IMAGE_MODULE_ID")))
          .setBarcode(new Barcode().setType("QR_CODE").setValue("QR code value"))
          .setLocations(
              Arrays.asList(
                  new LatLongPoint()
                      .setLatitude(37.424015499999996)
                      .setLongitude(-122.09259560000001)))
          .setSeatInfo(
              new EventSeat()
                  .setSeat(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("42")))
                  .setRow(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("G3")))
                  .setSection(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("5")))
                  .setGate(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("A"))))
          .setTicketHolderName("Ticket holder name")
          .setTicketNumber("Ticket number");

  EventTicketObject response = service.eventticketobject().insert(eventTicketObject).execute();

  System.out.println("Object insert response");
  System.out.println(response.toPrettyString());

  return response.getId();
}

PHP

/**
 * Create an object via the API.
 *
 * @param string $issuerId The issuer ID being used for this request.
 * @param string $classSuffix Developer-defined unique ID for this pass class.
 * @param string $userId Developer-defined user ID for this pass object.
 *
 * @return string The pass object ID: "{$issuerId}.{$userId}"
 */
public function createEventTicketObject(string $issuerId, string $classSuffix, string $userId)
{
  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  $newUserId = preg_replace('/[^\w.-]/i', '_', $userId);
  $objectId = "{$issuerId}.{$newUserId}";

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
  $eventTicketObject = new Google_Service_Walletobjects_EventTicketObject([
    'id' => "{$objectId}",
    'classId' => "{$issuerId}.{$classSuffix}",
    'state' => 'ACTIVE',
    'heroImage' => new Google_Service_Walletobjects_Image([
      'sourceUri' => new Google_Service_Walletobjects_ImageUri([
        'uri' => 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg',
      ]),
      'contentDescription' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => 'Hero image description',
        ]),
      ]),
    ]),
    'textModulesData' => [
      new Google_Service_Walletobjects_TextModuleData([
        'header' => 'Text module header',
        'body' => 'Text module body',
        'id' => 'TEXT_MODULE_ID',
      ]),
    ],
    'linksModuleData' => new Google_Service_Walletobjects_LinksModuleData([
      'uris' => [
        new Google_Service_Walletobjects_Uri([
          'uri' => 'http://maps.google.com/',
          'description' => 'Link module URI description',
          'id' => 'LINK_MODULE_URI_ID',
        ]),
        new Google_Service_Walletobjects_Uri([
          'uri' => 'tel:6505555555',
          'description' => 'Link module tel description',
          'id' => 'LINK_MODULE_TEL_ID',
        ]),
      ],
    ]),
    'imageModulesData' => [
      new Google_Service_Walletobjects_ImageModuleData([
        'mainImage' => new Google_Service_Walletobjects_Image([
          'sourceUri' => new Google_Service_Walletobjects_ImageUri([
            'uri' => 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg',
          ]),
          'contentDescription' => new Google_Service_Walletobjects_LocalizedString([
            'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
              'language' => 'en-US',
              'value' => 'Image module description',
            ]),
          ]),
        ]),
        'id' => 'IMAGE_MODULE_ID',
      ])
    ],
    'barcode' => new Google_Service_Walletobjects_Barcode([
      'type' => 'QR_CODE',
      'value' => 'QR code value',
    ]),
    'locations' => [
      new Google_Service_Walletobjects_LatLongPoint([
        'latitude' => 37.424015499999996,
        'longitude' =>  -122.09259560000001,
      ]),
    ],
    'seatInfo' => new Google_Service_Walletobjects_EventSeat([
      'seat' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => '42',
        ])
      ]),
      'row' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => 'G3',
        ])
      ]),
      'section' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => '5',
        ])
      ]),
      'gate' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => 'A',
        ])
      ])
    ]),
    'ticketHolderName' => 'Ticket holder name',
    'ticketNumber' => 'Ticket number',
  ]);

  try {
    $response = $this->service->eventticketobject->insert($eventTicketObject);

    print "Object insert response\n";
    print_r($response);

    return $response->id;
  } catch (Google\Service\Exception $ex) {
    if ($ex->getCode() == 409) {
      print "Object {$objectId} already exists";
      return;
    }

    // Something else went wrong
    print $ex->getTraceAsString();
  }
}

Python

def create_event_ticket_object(self, issuer_id: str, class_suffix: str,
                               user_id: str) -> str:
    """Create an object via the API.

    Args:
        issuer_id (str): The issuer ID being used for this request.
        class_suffix (str): Developer-defined unique ID for this pass class.
        user_id (str): Developer-defined user ID for this pass object.

    Returns:
        The pass object ID: f"{issuer_id}.{user_id}"
    """
    object_url = f'{self.base_url}/eventTicketObject'

    # Generate the object ID
    # Should only include alphanumeric characters, '.', '_', or '-'
    new_user_id = re.sub(r'[^\w.-]', '_', user_id)
    object_id = f'{issuer_id}.{new_user_id}'

    # See below for more information on required properties
    # https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
    event_ticket_object = {
        'id': f'{object_id}',
        'classId': f'{issuer_id}.{class_suffix}',
        'state': 'ACTIVE',
        'heroImage': {
            'sourceUri': {
                'uri':
                    'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg',
            },
            'contentDescription': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': 'Hero image description',
                },
            },
        },
        'textModulesData': [{
            'header': 'Text module header',
            'body': 'Text module body',
            'id': 'TEXT_MODULE_ID',
        },],
        'linksModuleData': {
            'uris': [
                {
                    'uri': 'http://maps.google.com/',
                    'description': 'Link module URI description',
                    'id': 'LINK_MODULE_URI_ID',
                },
                {
                    'uri': 'tel:6505555555',
                    'description': 'Link module tel description',
                    'id': 'LINK_MODULE_TEL_ID',
                },
            ],
        },
        'imageModulesData': [{
            'mainImage': {
                'sourceUri': {
                    'uri':
                        'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg',
                },
                'contentDescription': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': 'Image module description',
                    },
                },
            },
            'id': 'IMAGE_MODULE_ID',
        },],
        'barcode': {
            'type': 'QR_CODE',
            'value': 'QR code',
        },
        'locations': [{
            'latitude': 37.424015499999996,
            'longitude': -122.09259560000001,
        },],
        'seatInfo': {
            'seat': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': '42',
                },
            },
            'row': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': 'G3',
                },
            },
            'section': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': '5',
                },
            },
            'gate': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': 'A',
                },
            },
        },
        'ticketHolderName': 'Ticket holder name',
        'ticketNumber': 'Ticket number',
    }

    response = self.http_client.get(f'{object_url}{object_id}')
    if response.status_code == 404:
        # Object does not yet exist
        # Send POST request to create it
        response = self.http_client.post(
            url=object_url,
            json=event_ticket_object,
        )

        print('Object insert response')
        print(response.text)
    else:
        print('Object get response')
        print(response.text)

    return response.json().get('id')

C#

/// <summary>
/// Create an object via the API.
/// </summary>
/// <param name="issuerId">The issuer ID being used for this request.</param>
/// <param name="classSuffix">Developer-defined unique ID for this pass class.</param>
/// <param name="userId">Developer-defined user ID for this object.</param>
/// <returns>The pass object ID: "{issuerId}.{userId}"</returns>
public string CreateEventTicketObject(string issuerId, string classSuffix, string userId)
{
  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  string newUserId = new Regex(@"[^\w.-]", RegexOptions.Compiled)
      .Replace(userId, "_");
  string objectId = $"{issuerId}.{newUserId}";

  // Check if the object exists
  Stream responseStream = service.Eventticketclass
      .Get(objectId)
      .ExecuteAsStream();
  StreamReader responseReader = new StreamReader(responseStream);
  JObject jsonResponse = JObject.Parse(responseReader.ReadToEnd());

  if (jsonResponse.ContainsKey("error"))
  {
    if (jsonResponse["error"].Value<int>("code") == 404)
    {
      // Object does not exist
      // Send insert request to create it
      // See below for more information on required properties
      // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
      EventTicketObject eventTicketObject = new EventTicketObject
      {
        Id = objectId,
        ClassId = $"{issuerId}.{classSuffix}",
        State = "ACTIVE",
        HeroImage = new Image
        {
          SourceUri = new ImageUri
          {
            Uri = "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"
          },
          ContentDescription = new LocalizedString
          {
            DefaultValue = new TranslatedString
            {
              Language = "en-US",
              Value = "Hero image description"
            }
          }
        },
        TextModulesData = new List<TextModuleData>
        {
          new TextModuleData
          {
            Header = "Text module header",
            Body = "Text module body",
            Id = "TEXT_MODULE_ID"
          }
        },
        LinksModuleData = new LinksModuleData
        {
          Uris = new List<Google.Apis.Walletobjects.v1.Data.Uri>
          {
            new Google.Apis.Walletobjects.v1.Data.Uri
            {
              UriValue = "http://maps.google.com/",
              Description = "Link module URI description",
              Id = "LINK_MODULE_URI_ID"
            },
            new Google.Apis.Walletobjects.v1.Data.Uri
            {
              UriValue = "tel:6505555555",
              Description = "Link module tel description",
              Id = "LINK_MODULE_TEL_ID"
            }
          }
        },
        ImageModulesData = new List<ImageModuleData>
        {
          new ImageModuleData
          {
            MainImage = new Image
            {
              SourceUri = new ImageUri
              {
                Uri = "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"
              },
              ContentDescription = new LocalizedString
              {
                DefaultValue = new TranslatedString
                {
                  Language = "en-US",
                  Value = "Image module description"
                }
              }
            },
            Id = "IMAGE_MODULE_ID"
          }
        },
        Barcode = new Barcode
        {
          Type = "QR_CODE",
          Value = "QR code"
        },
        Locations = new List<LatLongPoint>
        {
          new LatLongPoint
          {
            Latitude = 37.424015499999996,
            Longitude = -122.09259560000001
          }
        },
        SeatInfo = new EventSeat
        {
          Seat = new LocalizedString
          {
            DefaultValue = new TranslatedString
            {
              Language = "en-US",
              Value = "42"
            }
          },
          Row = new LocalizedString
          {
            DefaultValue = new TranslatedString
            {
              Language = "en-US",
              Value = "G3"
            }
          },
          Section = new LocalizedString
          {
            DefaultValue = new TranslatedString
            {
              Language = "en-US",
              Value = "5"
            }
          },
          Gate = new LocalizedString
          {
            DefaultValue = new TranslatedString
            {
              Language = "en-US",
              Value = "A"
            }
          }
        },
        TicketHolderName = "Ticket holder name",
        TicketNumber = "Ticket number"
      };

      responseStream = service.Eventticketobject
          .Insert(eventTicketObject)
          .ExecuteAsStream();
      responseReader = new StreamReader(responseStream);
      jsonResponse = JObject.Parse(responseReader.ReadToEnd());

      Console.WriteLine("Object insert response");
      Console.WriteLine(jsonResponse.ToString());

      return jsonResponse.Value<string>("id");
    }
    else
    {
      Console.WriteLine("Something else went wrong");
      Console.WriteLine(jsonResponse.ToString());

      return jsonResponse.ToString();
    }
  }
  else
  {
    Console.WriteLine("Object get response");
    Console.WriteLine(jsonResponse.ToString());

    return jsonResponse.Value<string>("id");
  }
}

Node.js

/**
 * Create an object via the API.
 *
 * @param {string} issuerId The issuer ID being used for this request.
 * @param {string} classSuffix Developer-defined unique ID for this pass class.
 * @param {string} userId Developer-defined user ID for this object.
 *
 * @returns {string} The pass object ID: `${issuerId}.${userId}`
 */
async createEventTicketObject(issuerId, classSuffix, userId) {
  const eventTicketObjectUrl = `${this.baseUrl}/eventTicketObject`;

  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}`;

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
  let eventTicketObject = {
    'id': `${objectId}`,
    'classId': `${issuerId}.${classSuffix}`,
    'state': 'ACTIVE',
    'heroImage': {
      'sourceUri': {
        'uri': 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg',
      },
      'contentDescription': {
        'defaultValue': {
          'language': 'en-US',
          'value': 'Hero image description',
        },
      },
    },
    'textModulesData': [
      {
        'header': 'Text module header',
        'body': 'Text module body',
        'id': 'TEXT_MODULE_ID',
      },
    ],
    'linksModuleData': {
      'uris': [
        {
          'uri': 'http://maps.google.com/',
          'description': 'Link module URI description',
          'id': 'LINK_MODULE_URI_ID',
        },
        {
          'uri': 'tel:6505555555',
          'description': 'Link module tel description',
          'id': 'LINK_MODULE_TEL_ID',
        },
      ],
    },
    'imageModulesData': [
      {
        'mainImage': {
          'sourceUri': {
            'uri': 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg',
          },
          'contentDescription': {
            'defaultValue': {
              'language': 'en-US',
              'value': 'Image module description',
            },
          },
        },
        'id': 'IMAGE_MODULE_ID',
      },
    ],
    'barcode': {
      'type': 'QR_CODE',
      'value': 'QR code',
    },
    'locations': [
      {
        'latitude': 37.424015499999996,
        'longitude': -122.09259560000001,
      },
    ],
    'seatInfo': {
      'seat': {
        'defaultValue': {
          'language': 'en-US',
          'value': '42',
        },
      },
      'row': {
        'defaultValue': {
          'language': 'en-US',
          'value': 'G3',
        },
      },
      'section': {
        'defaultValue': {
          'language': 'en-US',
          'value': '5',
        },
      },
      'gate': {
        'defaultValue': {
          'language': 'en-US',
          'value': 'A',
        },
      },
    },
    'ticketHolderName': 'Ticket holder name',
    'ticketNumber': 'Ticket number',
  };

  let response;
  try {
    response = await this.httpClient.request({
      url: `${eventTicketObjectUrl}/${objectId}`,
      method: 'GET',
    });

    console.log('Object get response');
    console.log(response);

    return response.data.id;
  } catch (err) {
    if (err.response && err.response.status === 404) {
      // Object does not yet exist
      // Send POST request to create it
      response = await this.httpClient.request({
        url: eventTicketObjectUrl,
        method: 'POST',
        data: eventTicketObject,
      });

      console.log('Object insert response');
      console.log(response);

      return response.data.id;
    } else {
      // Something else went wrong
      console.log(err);
    }
  }
}

Once complete, your customer’s Passes Object will have been created on the server. However, at this stage, the Passes object has not been linked to a Google user or their device. For the pass to be associated with a Google Wallet user, the user must first add the pass to Google Wallet.

Adding to Google Wallet

Adding a pass to Google Wallet links the Passes Object to a Google user and can only be initiated in the context of a logged in Google identity. This can be achieved by directing the user to a Add to Google Wallet URL.

The Add to Google Wallet URL is a dynamically generated URL that contains the following information about the Passes Object id created in the previous step. This information is encoded as a JSON Web Token (JWT).

JSON Web Token (JWT)

The JWT contains claims that you (the issuer) is making about the Passes Object that will be saved by the user. The JWT must be signed using the private_key from the service account key obtained from the Create Service Account step, and Google will validate these claims by verifying the JWT signature.

The JWT claims should have the following structure:

{
  "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"
      }
    ]
  }
}

Construct the JWT claims (step 1), and obtain the token by signing the claims with the service account key's private_key (step 2):

Java

/**
 * Generate a signed JWT that creates a new pass class and object.
 *
 * <p>When the user opens the "Add to Google Wallet" URL and saves the pass to their wallet, the
 * pass class and object defined in the JWT are created. This allows you to create multiple pass
 * classes and objects in one API call when the user saves the pass to their wallet.
 *
 * @param issuerId The issuer ID being used for this request.
 * @param classSuffix Developer-defined unique ID for this pass class.
 * @param userId Developer-defined user ID for this object.
 * @return An "Add to Google Wallet" link.
 */
public String CreateJWTSaveURL(String issuerId, String classSuffix, String userId) {
  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  String newUserId = userId.replaceAll("[^\\w.-]", "_");
  String objectId = String.format("%s.%s", issuerId, newUserId);

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketclass
  EventTicketClass eventTicketClass =
      new EventTicketClass()
          .setId(String.format("%s.%s", issuerId, classSuffix))
          .setIssuerName("Issuer name")
          .setReviewStatus("UNDER_REVIEW")
          .setEventName(
              new LocalizedString()
                  .setDefaultValue(
                      new TranslatedString().setLanguage("en-US").setValue("Event name")));

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
  EventTicketObject eventTicketObject =
      new EventTicketObject()
          .setId(objectId)
          .setClassId(String.format("%s.%s", issuerId, classSuffix))
          .setState("ACTIVE")
          .setHeroImage(
              new Image()
                  .setSourceUri(
                      new ImageUri()
                          .setUri(
                              "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"))
                  .setContentDescription(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString()
                                  .setLanguage("en-US")
                                  .setValue("Hero image description"))))
          .setTextModulesData(
              Arrays.asList(
                  new TextModuleData()
                      .setHeader("Text module header")
                      .setBody("Text module body")
                      .setId("TEXT_MODULE_ID")))
          .setLinksModuleData(
              new LinksModuleData()
                  .setUris(
                      Arrays.asList(
                          new Uri()
                              .setUri("http://maps.google.com/")
                              .setDescription("Link module URI description")
                              .setId("LINK_MODULE_URI_ID"),
                          new Uri()
                              .setUri("tel:6505555555")
                              .setDescription("Link module tel description")
                              .setId("LINK_MODULE_TEL_ID"))))
          .setImageModulesData(
              Arrays.asList(
                  new ImageModuleData()
                      .setMainImage(
                          new Image()
                              .setSourceUri(
                                  new ImageUri()
                                      .setUri(
                                          "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"))
                              .setContentDescription(
                                  new LocalizedString()
                                      .setDefaultValue(
                                          new TranslatedString()
                                              .setLanguage("en-US")
                                              .setValue("Image module description"))))
                      .setId("IMAGE_MODULE_ID")))
          .setBarcode(new Barcode().setType("QR_CODE").setValue("QR code value"))
          .setLocations(
              Arrays.asList(
                  new LatLongPoint()
                      .setLatitude(37.424015499999996)
                      .setLongitude(-122.09259560000001)))
          .setSeatInfo(
              new EventSeat()
                  .setSeat(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("42")))
                  .setRow(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("G3")))
                  .setSection(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("5")))
                  .setGate(
                      new LocalizedString()
                          .setDefaultValue(
                              new TranslatedString().setLanguage("en-US").setValue("A"))))
          .setTicketHolderName("Ticket holder name")
          .setTicketNumber("Ticket number");

  // Create the JWT as a HashMap object
  HashMap<String, Object> claims = new HashMap<String, Object>();
  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<String, Object> payload = new HashMap<String, Object>();
  payload.put("eventTicketClasses", Arrays.asList(eventTicketClass));
  payload.put("eventTicketObjects", Arrays.asList(eventTicketObject));
  claims.put("payload", payload);

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

  System.out.println("Add to Google Wallet link");
  System.out.println(String.format("https://pay.google.com/gp/v/save/%s", token));

  return String.format("https://pay.google.com/gp/v/save/%s", token);
}

PHP

/**
 * Generate a signed JWT that creates a new pass class and object.
 *
 * When the user opens the "Add to Google Wallet" URL and saves the pass to
 * their wallet, the pass class and object defined in the JWT are
 * created.This allows you to create multiple pass classes and objects in
 * one API call when the user saves the pass to their wallet.
 *
 * @param string $issuerId The issuer ID being used for this request.
 * @param string $classSuffix Developer-defined class ID for this class.
 * @param string $userId Developer-defined user ID for this object.
 *
 * @return string An "Add to Google Wallet" link.
 */
public function createJwtSaveUrl(string $issuerId, string $classSuffix, string $userId)
{
  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  $newUserId = preg_replace('/[^\w.-]/i', '_', $userId);
  $objectId = "{$issuerId}.{$newUserId}";

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketclass
  $eventTicketClass = new Google_Service_Walletobjects_EventTicketClass([
    'id' => "{$issuerId}.{$classSuffix}",
    'issuerName' => 'Issuer name',
    'reviewStatus' => 'UNDER_REVIEW',
    'eventName' => new Google_Service_Walletobjects_LocalizedString([
      'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
        'language' => 'en-US',
        'value' => 'Event name',
      ]),
    ]),
  ]);

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
  $eventTicketObject = new Google_Service_Walletobjects_EventTicketObject([
    'id' => "{$objectId}",
    'classId' => "{$issuerId}.{$classSuffix}",
    'state' => 'ACTIVE',
    'heroImage' => new Google_Service_Walletobjects_Image([
      'sourceUri' => new Google_Service_Walletobjects_ImageUri([
        'uri' => 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg',
      ]),
      'contentDescription' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => 'Hero image description',
        ]),
      ]),
    ]),
    'textModulesData' => [
      new Google_Service_Walletobjects_TextModuleData([
        'header' => 'Text module header',
        'body' => 'Text module body',
        'id' => 'TEXT_MODULE_ID',
      ]),
    ],
    'linksModuleData' => new Google_Service_Walletobjects_LinksModuleData([
      'uris' => [
        new Google_Service_Walletobjects_Uri([
          'uri' => 'http://maps.google.com/',
          'description' => 'Link module URI description',
          'id' => 'LINK_MODULE_URI_ID',
        ]),
        new Google_Service_Walletobjects_Uri([
          'uri' => 'tel:6505555555',
          'description' => 'Link module tel description',
          'id' => 'LINK_MODULE_TEL_ID',
        ]),
      ],
    ]),
    'imageModulesData' => [
      new Google_Service_Walletobjects_ImageModuleData([
        'mainImage' => new Google_Service_Walletobjects_Image([
          'sourceUri' => new Google_Service_Walletobjects_ImageUri([
            'uri' => 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg',
          ]),
          'contentDescription' => new Google_Service_Walletobjects_LocalizedString([
            'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
              'language' => 'en-US',
              'value' => 'Image module description',
            ]),
          ]),
        ]),
        'id' => 'IMAGE_MODULE_ID',
      ])
    ],
    'barcode' => new Google_Service_Walletobjects_Barcode([
      'type' => 'QR_CODE',
      'value' => 'QR code value',
    ]),
    'locations' => [
      new Google_Service_Walletobjects_LatLongPoint([
        'latitude' => 37.424015499999996,
        'longitude' =>  -122.09259560000001,
      ]),
    ],
    'seatInfo' => new Google_Service_Walletobjects_EventSeat([
      'seat' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => '42',
        ])
      ]),
      'row' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => 'G3',
        ])
      ]),
      'section' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => '5',
        ])
      ]),
      'gate' => new Google_Service_Walletobjects_LocalizedString([
        'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
          'language' => 'en-US',
          'value' => 'A',
        ])
      ])
    ]),
    'ticketHolderName' => 'Ticket holder name',
    'ticketNumber' => 'Ticket number',
  ]);

  // Create the JWT as an array of key/value pairs
  $serviceAccount = json_decode(file_get_contents($this->keyFilePath), true);
  $claims = [
    'iss' => $serviceAccount['client_email'],
    'aud' => 'google',
    'origins' => ['www.example.com'],
    'typ' => 'savetowallet',
    'payload' => [
      'eventTicketClasses' => [
        $eventTicketClass,
      ],
      'eventTicketObjects' => [
        $eventTicketObject,
      ],
    ],
  ];

  // The service account credentials are used to sign the JWT
  $token = JWT::encode(
    $claims,
    $serviceAccount['private_key'],
    'RS256'
  );

  print "Add to Google Wallet link\n";
  print "https://pay.google.com/gp/v/save/{$token}";

  return "https://pay.google.com/gp/v/save/{$token}";
}

Python

def create_jwt_save_url(self, issuer_id: str, class_suffix: str,
                        user_id: str) -> str:
    """Generate a signed JWT that creates a new pass class and object.

    When the user opens the "Add to Google Wallet" URL and saves the pass to
    their wallet, the pass class and object defined in the JWT are
    created. This allows you to create multiple pass classes and objects in
    one API call when the user saves the pass to their wallet.

    Args:
        issuer_id (str): The issuer ID being used for this request.
        class_suffix (str): Developer-defined unique ID for this pass class.
        user_id (str): Developer-defined user ID for this pass object.

    Returns:
        An "Add to Google Wallet" link.
    """

    # Generate the object ID
    # Should only include alphanumeric characters, '.', '_', or '-'
    new_user_id = re.sub(r'[^\w.-]', '_', user_id)
    object_id = f'{issuer_id}.{new_user_id}'

    # See below for more information on required properties
    # https://developers.google.com/wallet/tickets/events/rest/v1/eventticketclass
    event_ticket_class = {
        'id': f'{issuer_id}.{class_suffix}',
        'issuerName': 'Issuer name',
        'reviewStatus': 'UNDER_REVIEW',
        'eventName': {
            'defaultValue': {
                'language': 'en-US',
                'value': 'Event name',
            },
        },
    }

    # See below for more information on required properties
    # https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
    event_ticket_object = {
        'id': f'{object_id}',
        'classId': f'{issuer_id}.{class_suffix}',
        'state': 'ACTIVE',
        'heroImage': {
            'sourceUri': {
                'uri':
                    'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg',
            },
            'contentDescription': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': 'Hero image description',
                },
            },
        },
        'textModulesData': [{
            'header': 'Text module header',
            'body': 'Text module body',
            'id': 'TEXT_MODULE_ID',
        },],
        'linksModuleData': {
            'uris': [
                {
                    'uri': 'http://maps.google.com/',
                    'description': 'Link module URI description',
                    'id': 'LINK_MODULE_URI_ID',
                },
                {
                    'uri': 'tel:6505555555',
                    'description': 'Link module tel description',
                    'id': 'LINK_MODULE_TEL_ID',
                },
            ],
        },
        'imageModulesData': [{
            'mainImage': {
                'sourceUri': {
                    'uri':
                        'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg',
                },
                'contentDescription': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': 'Image module description',
                    },
                },
            },
            'id': 'IMAGE_MODULE_ID',
        },],
        'barcode': {
            'type': 'QR_CODE',
            'value': 'QR code',
        },
        'locations': [{
            'latitude': 37.424015499999996,
            'longitude': -122.09259560000001,
        },],
        'seatInfo': {
            'seat': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': '42',
                },
            },
            'row': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': 'G3',
                },
            },
            'section': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': '5',
                },
            },
            'gate': {
                'defaultValue': {
                    'language': 'en-US',
                    'value': 'A',
                },
            },
        },
        'ticketHolderName': 'Ticket holder name',
        'ticketNumber': 'Ticket number',
    }

    # 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
            'eventTicketClasses': [event_ticket_class,],
            'eventTicketObjects': [event_ticket_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')

    print('Add to Google Wallet link')
    print(f'https://pay.google.com/gp/v/save/{token}')

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

C#

/// <summary>
/// Generate a signed JWT that creates a new pass class and object.
///
/// When the user opens the "Add to Google Wallet" URL and saves the pass to
/// their wallet, the pass class and object defined in the JWT are created.
/// This allows you to create multiple pass classes and objects in
/// one API call when the user saves the pass to their wallet.
///
/// The Google Wallet C# library uses Newtonsoft.Json.JsonPropertyAttribute
/// to specify the property names when converting objects to JSON. The
/// Newtonsoft.Json.JsonConvert.SerializeObject method will automatically
/// serialize the object with the right property names.
///
/// </summary>
/// <param name="issuerId">The issuer ID being used for this request.</param>
/// <param name="classSuffix">Developer-defined unique ID for this pass class.</param>
/// <param name="userId">Developer-defined user ID for this object.</param>
/// <returns>An "Add to Google Wallet" link.</returns>
public string CreateJWTSaveURL(string issuerId, string classSuffix, string userId)
{
  // Ignore null values when serializing to/from JSON
  JsonSerializerSettings excludeNulls = new JsonSerializerSettings()
  {
    NullValueHandling = NullValueHandling.Ignore
  };

  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  string newUserId = new Regex(@"[^\w.-]", RegexOptions.Compiled)
      .Replace(userId, "_");
  string objectId = $"{issuerId}.{newUserId}";

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketclass
  EventTicketClass eventTicketClass = new EventTicketClass
  {
    Id = $"{issuerId}.{classSuffix}",
    IssuerName = "Issuer name",
    ReviewStatus = "UNDER_REVIEW",
    EventId = classSuffix,
    EventName = new LocalizedString
    {
      DefaultValue = new TranslatedString
      {
        Language = "en-US",
        Value = "Event name"
      }
    }
  };

  // Create a JSON representation of the class
  JObject serializedClass = JObject.Parse(
      JsonConvert.SerializeObject(eventTicketClass, excludeNulls));

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
  EventTicketObject eventTicketObject = new EventTicketObject
  {
    Id = objectId,
    ClassId = $"{issuerId}.{classSuffix}",
    State = "ACTIVE",
    HeroImage = new Image
    {
      SourceUri = new ImageUri
      {
        Uri = "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"
      },
      ContentDescription = new LocalizedString
      {
        DefaultValue = new TranslatedString
        {
          Language = "en-US",
          Value = "Hero image description"
        }
      }
    },
    TextModulesData = new List<TextModuleData>
    {
      new TextModuleData
      {
        Header = "Text module header",
        Body = "Text module body",
        Id = "TEXT_MODULE_ID"
      }
    },
    LinksModuleData = new LinksModuleData
    {
      Uris = new List<Google.Apis.Walletobjects.v1.Data.Uri>
      {
        new Google.Apis.Walletobjects.v1.Data.Uri
        {
          UriValue = "http://maps.google.com/",
          Description = "Link module URI description",
          Id = "LINK_MODULE_URI_ID"
        },
        new Google.Apis.Walletobjects.v1.Data.Uri
        {
          UriValue = "tel:6505555555",
          Description = "Link module tel description",
          Id = "LINK_MODULE_TEL_ID"
        }
      }
    },
    ImageModulesData = new List<ImageModuleData>
    {
      new ImageModuleData
      {
        MainImage = new Image
        {
          SourceUri = new ImageUri
          {
            Uri = "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"
          },
          ContentDescription = new LocalizedString
          {
            DefaultValue = new TranslatedString
            {
              Language = "en-US",
              Value = "Image module description"
            }
          }
        },
        Id = "IMAGE_MODULE_ID"
      }
    },
    Barcode = new Barcode
    {
      Type = "QR_CODE",
      Value = "QR code"
    },
    Locations = new List<LatLongPoint>
    {
      new LatLongPoint
      {
        Latitude = 37.424015499999996,
        Longitude = -122.09259560000001
      }
    },
    SeatInfo = new EventSeat
    {
      Seat = new LocalizedString
      {
        DefaultValue = new TranslatedString
        {
          Language = "en-US",
          Value = "42"
        }
      },
      Row = new LocalizedString
      {
        DefaultValue = new TranslatedString
        {
          Language = "en-US",
          Value = "G3"
        }
      },
      Section = new LocalizedString
      {
        DefaultValue = new TranslatedString
        {
          Language = "en-US",
          Value = "5"
        }
      },
      Gate = new LocalizedString
      {
        DefaultValue = new TranslatedString
        {
          Language = "en-US",
          Value = "A"
        }
      }
    },
    TicketHolderName = "Ticket holder name",
    TicketNumber = "Ticket number"
  };

  // Create a JSON representation of the pass
  JObject serializedObject = JObject.Parse(
      JsonConvert.SerializeObject(eventTicketObject, excludeNulls));

  // Create the JWT as a JSON object
  JObject jwtPayload = JObject.Parse(JsonConvert.SerializeObject(new
  {
    iss = credentials.Id,
    aud = "google",
    origins = new string[]
    {
      "www.example.com"
    },
    typ = "savetowallet",
    payload = JObject.Parse(JsonConvert.SerializeObject(new
    {
      // The listed classes and objects will be created
      // when the user saves the pass to their wallet
      eventTicketClasses = new JObject[]
      {
        serializedClass
      },
      eventTicketObjects = new JObject[]
      {
        serializedObject
      }
    })),
  }));

  // Deserialize into a JwtPayload
  JwtPayload claims = JwtPayload.Deserialize(jwtPayload.ToString());

  // The service account credentials are used to sign the JWT
  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);

  Console.WriteLine("Add to Google Wallet link");
  Console.WriteLine($"https://pay.google.com/gp/v/save/{token}");

  return $"https://pay.google.com/gp/v/save/{token}";
}

Node.js

/**
 * Generate a signed JWT that creates a new pass class and object.
 *
 * When the user opens the "Add to Google Wallet" URL and saves the pass to
 * their wallet, the pass class and object defined in the JWT are
 * created. This allows you to create multiple pass classes and objects in
 * one API call when the user saves the pass to their wallet.
 *
 * @param {string} issuerId The issuer ID being used for this request.
 * @param {string} classSuffix Developer-defined unique ID for this pass class.
 * @param {string} userId Developer-defined user ID for this object.
 *
 * @returns {string} An "Add to Google Wallet" link.
 */
createJwtSaveUrl(issuerId, classSuffix, userId) {
  // Generate the object ID
  // Should only include alphanumeric characters, '.', '_', or '-'
  let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}`;

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketclass
  let eventTicketClass = {
    'id': `${issuerId}.${classSuffix}`,
    'issuerName': 'Issuer name',
    'reviewStatus': 'UNDER_REVIEW',
    'eventName': {
      'defaultValue': {
        'language': 'en-US',
        'value': 'Event name',
      },
    },
  };

  // See below for more information on required properties
  // https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
  let eventTicketObject = {
    'id': `${objectId}`,
    'classId': `${issuerId}.${classSuffix}`,
    'state': 'ACTIVE',
    'heroImage': {
      'sourceUri': {
        'uri': 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg',
      },
      'contentDescription': {
        'defaultValue': {
          'language': 'en-US',
          'value': 'Hero image description',
        },
      },
    },
    'textModulesData': [
      {
        'header': 'Text module header',
        'body': 'Text module body',
        'id': 'TEXT_MODULE_ID',
      },
    ],
    'linksModuleData': {
      'uris': [
        {
          'uri': 'http://maps.google.com/',
          'description': 'Link module URI description',
          'id': 'LINK_MODULE_URI_ID',
        },
        {
          'uri': 'tel:6505555555',
          'description': 'Link module tel description',
          'id': 'LINK_MODULE_TEL_ID',
        },
      ],
    },
    'imageModulesData': [
      {
        'mainImage': {
          'sourceUri': {
            'uri': 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg',
          },
          'contentDescription': {
            'defaultValue': {
              'language': 'en-US',
              'value': 'Image module description',
            },
          },
        },
        'id': 'IMAGE_MODULE_ID',
      },
    ],
    'barcode': {
      'type': 'QR_CODE',
      'value': 'QR code',
    },
    'locations': [
      {
        'latitude': 37.424015499999996,
        'longitude': -122.09259560000001,
      },
    ],
    'seatInfo': {
      'seat': {
        'defaultValue': {
          'language': 'en-US',
          'value': '42',
        },
      },
      'row': {
        'defaultValue': {
          'language': 'en-US',
          'value': 'G3',
        },
      },
      'section': {
        'defaultValue': {
          'language': 'en-US',
          'value': '5',
        },
      },
      'gate': {
        'defaultValue': {
          'language': 'en-US',
          'value': 'A',
        },
      },
    },
    'ticketHolderName': 'Ticket holder name',
    'ticketNumber': 'Ticket number',
  };

  // Create the JWT claims
  let claims = {
    iss: this.credentials.client_email,
    aud: 'google',
    origins: ['www.example.com'],
    typ: 'savetowallet',
    payload: {
      // The listed classes and objects will be created
      eventTicketClasses: [eventTicketClass,],
      eventTicketObjects: [eventTicketObject,],
    },
  };

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

  console.log('Add to Google Wallet link');
  console.log(`https://pay.google.com/gp/v/save/${token}`);

  return `https://pay.google.com/gp/v/save/${token}`;
}

After you’ve obtained a signed JWT, you can use this information to construct a Add to Google Wallet link.

The Add to Google Wallet link has the following format:

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

This link can be embedded into your web page, or email as a hyperlink. It can also be sent to the customer using other channels like chat and SMS.

The safe length of an encoded JWT is 1800 characters. If your JWT is below this limit, the entire object can be included in the signed JWT. Your JWTs should remain below this limit. If the length is over 1800 characters, adding the pass to Google Wallet may not work due to truncation by web browsers.

Add to Google Wallet Button

For best results, leverage the Google Wallet button assets in your web page, email, or Android application.

The Google Wallet button can be rendered in two ways:

  • The JavaScript Web button can be used for websites.
  • The JWT link with a button can be used for email, SMS, and apps, and websites.

Next steps