Web API 시작하기

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

Web API를 시작하기 전에 기본 요건을 완료했는지 확인하세요. Web API를 계속 사용하려면 서비스 계정 및 서비스 계정 키가 있어야 하고 Pass API를 호출하기 위해 서비스 계정에 액세스 권한을 부여해야 합니다.

GitHub에서 샘플 코드를 다운로드하여 아래 단계에 참조된 코드 스니펫을 실행합니다.

인증 및 승인

Google Wallet API에 대한 요청은 애플리케이션에서 요청을 하고 있다는 것을 Google Wallet API에서 식별할 수 있도록 인증해야 합니다. 이 작업은 서비스 계정 키를 사용하여 액세스 토큰을 가져오는 방식으로 수행할 수 있습니다.

먼저 필요한 라이브러리 가져오기를 실행하고 서비스 계정 JSON의 일부 변수와 저장할 발급기관, 클래스, 순 사용자, 객체를 위한 ID를 정의합니다.

자바

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 Offers in Google Wallet. */
public class DemoOffer {
  /**
   * 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 DemoOffer() {
    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 Offers in Google Wallet. */
class DemoOffer
{
  /**
   * 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 DemoOffer:
    """Demo class for creating and managing Offers 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 Offers in Google Wallet.
/// </summary>
class DemoOffer
{
  /// <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 DemoOffer()
  {
    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 Offers in Google Wallet.
 */
class DemoOffer {
  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'
  }

다음으로, 프레임워크 라이브러리 중 하나를 사용하여 Google 월렛 API를 호출하는 데 필요한 사용자 인증 정보를 검색합니다.

자바

/**
 * 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',
  });
}

Passes 객체 만들기

A Passes 객체는 패스 클래스의 인스턴스입니다. 패스 객체를 만들려면 다음 속성을 제공해야 합니다.

  • classId: Passes 클래스의 id
  • id: 고객의 고유 ID입니다. 쿠폰에 이러한 속성이 표시되는 방식에 대한 자세한 내용은 템플릿 가이드라인을 참조하세요.

Passes 객체를 만드는 코드 샘플:

HTTP

POST /walletobjects/v1/offerObject 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
    }
  ],
  "validTimeInterval": {
    "start": {
      "date": "2023-06-12T23:20:50.52Z"
    },
    "end": {
      "date": "2023-12-12T23:20:50.52Z"
    }
  }
}

자바

/**
 * 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 CreateOfferObject(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
    OfferObject response = service.offerobject().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 link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
  OfferObject offerObject =
      new OfferObject()
          .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)))
          .setValidTimeInterval(
              new TimeInterval()
                  .setStart(new DateTime().setDate("2023-06-12T23:20:50.52Z"))
                  .setEnd(new DateTime().setDate("2023-12-12T23:20:50.52Z")));

  OfferObject response = service.offerobject().insert(offerObject).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: "{issuer_id}.{user_id}"
 */
public function createOfferObject(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 link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
  $offerObject = new Google_Service_Walletobjects_OfferObject([
    '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,
      ]),
    ],
    'validTimeInterval' => new Google_Service_Walletobjects_TimeInterval([
      'start' => new Google_Service_Walletobjects_DateTime([
        'date' => '2023-06-12T23:20:50.52Z',
      ]),
      'end' => new Google_Service_Walletobjects_DateTime([
        'date' => '2023-12-12T23:20:50.52Z',
      ]),
    ]),
  ]);

  try {
    $response = $this->service->offerobject->insert($offerObject);

    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_offer_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}/offerObject'

    # 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/retail/offers/rest/v1/offerobject
    offer_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,
        },],
        'validTimeInterval': {
            'start': {
                'date': '2023-06-12T23:20:50.52Z',
            },
            'end': {
                'date': '2023-12-12T23:20:50.52Z',
            },
        },
    }

    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=offer_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 CreateOfferObject(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.Offerclass
      .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 link below for more information on required properties
      // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
      OfferObject offerObject = new OfferObject
      {
        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
          }
        },
        ValidTimeInterval = new TimeInterval
        {
          Start = new Google.Apis.Walletobjects.v1.Data.DateTime
          {
            Date = "2023-06-12T23:20:50.52Z"
          },
          End = new Google.Apis.Walletobjects.v1.Data.DateTime
          {
            Date = "2023-12-12T23:20:50.52Z"
          }
        }
      };

      responseStream = service.Offerobject
          .Insert(offerObject)
          .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 createOfferObject(issuerId, classSuffix, userId) {
  const offerObjectUrl = `${this.baseUrl}/offerObject`;

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

  // See link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
  let offerObject = {
    '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,
      },
    ],
    'validTimeInterval': {
      'start': {
        'date': '2023-06-12T23:20:50.52Z',
      },
      'end': {
        'date': '2023-12-12T23:20:50.52Z',
      },
    },
  };

  let response;
  try {
    response = await this.httpClient.request({
      url: `${offerObjectUrl}/${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: offerObjectUrl,
        method: 'POST',
        data: offerObject,
      });

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

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

완료되면 고객의 패스 객체가 서버에 생성됩니다. 하지만 이 단계에서 패스 객체는 Google 사용자 또는 기기에 연결되지 않았습니다. 패스를 Google 월렛 사용자와 연결하려면 사용자가 먼저 패스를 Google 월렛에 추가해야 합니다.

Google 월렛에 추가 중

Google 월렛에 패스를 추가하면 패스 객체가 Google 사용자에게 연결되며 Google ID에 로그인한 상태에서만 시작할 수 있습니다. 이렇게 하려면 사용자를 Google 월렛에 추가 URL로 안내하면 됩니다.

'Google 월렛에 추가' URL은 이전 단계에서 생성된 패스 객체 ID에 대한 다음 정보를 포함하는 동적으로 생성된 URL입니다. 이 정보는 JSON 웹 토큰 (JWT)으로 인코딩됩니다.

JSON 웹 토큰(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": {
    "offerObjects": [
      {
        "id": "PASSES_OBJECT_ID_1234567890"
      }
    ]
  }
}

JWT claims (1단계)을 생성하고 서비스 계정 키 (private_key)로 클레임에 서명하여 token를 가져옵니다.

자바

/**
 * 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 link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerclass
  OfferClass offerClass =
      new OfferClass()
          .setId(String.format("%s.%s", issuerId, classSuffix))
          .setIssuerName("Issuer name")
          .setReviewStatus("UNDER_REVIEW")
          .setProvider("Provider name")
          .setTitle("Offer title")
          .setRedemptionChannel("ONLINE");

  // See link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
  OfferObject offerObject =
      new OfferObject()
          .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)))
          .setValidTimeInterval(
              new TimeInterval()
                  .setStart(new DateTime().setDate("2023-06-12T23:20:50.52Z"))
                  .setEnd(new DateTime().setDate("2023-12-12T23:20:50.52Z")));

  // 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("offerClasses", Arrays.asList(offerClass));
  payload.put("offerObjects", Arrays.asList(offerObject));
  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 link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerclass
  $offerClass = new Google_Service_Walletobjects_OfferClass(['id' => "{$issuerId}.{$classSuffix}",
    'issuerName' => 'Issuer name',
    'reviewStatus' => 'UNDER_REVIEW',
    'provider' => 'Provider name',
    'title' => 'Offer title',
    'redemptionChannel' => 'ONLINE',
  ]);

  // See link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
  $offerObject = new Google_Service_Walletobjects_OfferObject([
    '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,
      ]),
    ],
    'validTimeInterval' => new Google_Service_Walletobjects_TimeInterval([
      'start' => new Google_Service_Walletobjects_DateTime([
        'date' => '2023-06-12T23:20:50.52Z',
      ]),
      'end' => new Google_Service_Walletobjects_DateTime([
        'date' => '2023-12-12T23:20:50.52Z',
      ]),
    ]),
  ]);

  // 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' => [
      'offerClasses' => [
        $offerClass,
      ],
      'offerObjects' => [
        $offerObject,
      ],
    ],
  ];

  // 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/retail/offers/rest/v1/offerclass
    offer_class = {
        'id': f'{issuer_id}.{class_suffix}',
        'issuerName': 'Issuer name',
        'reviewStatus': 'UNDER_REVIEW',
        'provider': 'Provider name',
        'title': 'Offer title',
        'redemptionChannel': 'ONLINE',
    }

    # See below for more information on required properties
    # https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
    offer_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,
        },],
        'validTimeInterval': {
            'start': {
                'date': '2023-06-12T23:20:50.52Z',
            },
            'end': {
                'date': '2023-12-12T23:20:50.52Z',
            },
        },
    }

    # 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
            'offerClasses': [offer_class,],
            'offerObjects': [offer_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 link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerclass
  OfferClass offerClass = new OfferClass
  {
    Id = $"{issuerId}.{classSuffix}",
    IssuerName = "Issuer name",
    ReviewStatus = "UNDER_REVIEW",
    Provider = "Provider name",
    Title = "Offer title",
    RedemptionChannel = "ONLINE"
  };

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

  // See link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
  OfferObject offerObject = new OfferObject
  {
    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
      }
    },
    ValidTimeInterval = new TimeInterval
    {
      Start = new Google.Apis.Walletobjects.v1.Data.DateTime
      {
        Date = "2023-06-12T23:20:50.52Z"
      },
      End = new Google.Apis.Walletobjects.v1.Data.DateTime
      {
        Date = "2023-12-12T23:20:50.52Z"
      }
    }
  };

  // Create a JSON representation of the pass
  JObject serializedObject = JObject.Parse(
      JsonConvert.SerializeObject(offerObject, 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
      offerClasses = new JObject[]
      {
        serializedClass
      },
      offerObjects = 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 link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerclass
  let offerClass = {
    'id': `${issuerId}.${classSuffix}`,
    'issuerName': 'Issuer name',
    'reviewStatus': 'UNDER_REVIEW',
    'provider': 'Provider name',
    'title': 'Offer title',
    'redemptionChannel': 'ONLINE',
  };

  // See link below for more information on required properties
  // https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
  let offerObject = {
    '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,
      },
    ],
    'validTimeInterval': {
      'start': {
        'date': '2023-06-12T23:20:50.52Z',
      },
      'end': {
        'date': '2023-12-12T23:20:50.52Z',
      },
    },
  };

  // 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
      offerClasses: [offerClass,],
      offerObjects: [offerObject,],
    },
  };

  // 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}`;
}

서명된 JWT를 가져온 후 이 정보를 사용하여 Google 월렛에 추가 링크를 구성할 수 있습니다.

Google 월렛에 추가 링크 형식은 다음과 같습니다.

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

이 링크는 웹페이지에 삽입하거나 이메일로 하이퍼링크로 삽입될 수 있습니다. 채팅 및 SMS와 같은 다른 채널을 사용하여 고객에게 전송할 수도 있습니다.

인코딩된 JWT의 안전한 길이는 1,800자이므로 JWT가 이 한도보다 낮으면 전체 객체를 서명된 JWT에 포함할 수 있습니다. JWT를 이 한도 미만으로 유지해야 합니다. 길이가 1, 800자를 넘으면 웹브라우저에서 잘린 상태로 표시되어 저장 기능이 작동하지 않을 수 있습니다.

Google 월렛에 추가 버튼

최상의 결과를 얻으려면 웹페이지, 이메일 또는 Android 애플리케이션에서 Google 월렛 버튼 애셋을 활용하세요.

Google 월렛 버튼은 다음 두 가지 방법으로 렌더링할 수 있습니다.

  • 자바스크립트 웹 버튼은 웹사이트에 사용합니다.
  • Google 월렛 버튼이 있는 JWT 링크를 이메일, SMS, 앱, 웹사이트에 사용할 수 있습니다.

다음 단계

  • 혜택 업데이트
  • 쿠폰 표시 맞춤설정
  • 더 궁금한 점이 있으시면 FAQ를 검토해 주세요.