Update to make examples easily importable/testable

* Create language-specific READMEs with setup and usage instructions
* Add links to each language-specific README from main README
* Converted examples to use importable classes and functions
* Added class- and function-level docstrings and type hinting for IDEs
* Removed use of WALLET_* environment variables (now function arguments)
* General formatting updates (line length, indentation, etc.)
* Switched Java, .NET, and PHP to use Walletobjects libraries
* Updated JWT examples to show creating a class and object at once
* Fixed error with missing status parameter for certain pass types
* Added batch creation example for raw HTTP requests
* Removed generator scripts (doesn't work with the Wallet libraries)
This commit is contained in:
Nick Alteen
2022-10-05 09:50:23 -04:00
parent a1a185d568
commit 1a91242dc8
59 changed files with 21216 additions and 14314 deletions

73
nodejs/README.md Normal file
View File

@@ -0,0 +1,73 @@
# Google Wallet Node.js samples
## Overview
The files in this directory each implement a demo class for a specific Google
Wallet pass type. Each class implements methods for performing tasks such as
creating a pass class, updating issuer permissions, and more.
| Pass type | File |
|-----------|------|
| Event tickets | [demo-eventticket.js](./demo-eventticket.js) |
| Flight boarding passes | [demo-flight.js](./demo-flight.js) |
| Generic passes | [demo-generic.js](./demo-generic.js) |
| Gift cards | [demo-giftcard.js](./demo-giftcard.js) |
| Loyalty program membership | [demo-loyalty.js](./demo-loyalty.js) |
| Offers and promotions | [demo-offer.js](./demo-offer.js) |
| Transit passes | [demo-transit.js](./demo-transit.js) |
## Prerequisites
* Node.js 18.x
* NPM 8.x
* Follow the steps outlined in the
[Google Wallet prerequisites](https://developers.google.com/wallet/generic/web/prerequisites)
to create the Google Wallet issuer account and Google Cloud service account
## Environment variables
The following environment variables must be set. Alternatively, you can update
the code files to set the values directly. They can be found in the constructor
for each class file.
| Enviroment variable | Description | Example |
|---------------------|-------------|---------|
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to a Google Cloud service account key file | `/path/to/key.json` |
## How to use the code samples
1. Use `npm` to install the dependencies in the [package.json](./package.json)
```bash
# This must be run from the same location as the package.json
npm install
```
2. In your Node.js code, import a demo class and call its method(s). An example
can be found below
```javascript
// Import the demo class
const { DemoEventTicket } = require('./demo-eventticket');
// Create a demo class instance
let demo = new DemoEventTicket();
// Create the authenticated HTTP client
demo.auth();
// Create a pass class
demo.createEventTicketClass('issuer_id', 'class_suffix');
// Create a pass object
demo.createEventTicketObject('issuer_id', 'class_suffix', 'user_id');
// Create an Add to Google Wallet link
demo.createJwtSaveUrl('issuer_id', 'class_suffix', 'user_id');
// Create an issuer account
demo.createIssuerAccount('issuer_name', 'issuer_email');
// Create pass objects in batch
demo.batchCreateEventTicketObjects('issuer_id', 'class_suffix');
```

View File

@@ -14,423 +14,588 @@
* limitations under the License.
*/
async function main() {
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
/**
* 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';
/*
* keyFilePath - Path to service account key file from Google Cloud Console
* - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
*/
const keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';
/*
* issuerId - The issuer ID being updated in this request
* - Environment variable: WALLET_ISSUER_ID
*/
const issuerId = process.env.WALLET_ISSUER_ID || 'issuer-id';
/*
* classId - Developer-defined ID for the wallet class
* - Environment variable: WALLET_CLASS_ID
*/
const classId = process.env.WALLET_CLASS_ID || 'test-eventTicket-class-id';
/*
* userId - Developer-defined ID for the user, such as an email address
* - Environment variable: WALLET_USER_ID
*/
let userId = process.env.WALLET_USER_ID || 'user-id';
/*
* objectId - ID for the wallet object
* - Format: `issuerId.identifier`
* - Should only include alphanumeric characters, '.', '_', or '-'
* - `identifier` is developer-defined and unique to the user
*/
let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}-${classId}`;
/**
* Base URL for Google Wallet API requests.
*/
this.baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1'
}
// [END setup]
///////////////////////////////////////////////////////////////////////////////
// Create authenticated HTTP client, using service account file.
///////////////////////////////////////////////////////////////////////////////
// [START auth]
const credentials = require(keyFilePath);
/**
* Create authenticated HTTP client using a service account file.
*/
auth() {
this.credentials = require(this.keyFilePath);
const httpClient = new GoogleAuth({
credentials: credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer'
});
this.httpClient = new GoogleAuth({
credentials: this.credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer',
});
}
// [END auth]
///////////////////////////////////////////////////////////////////////////////
// Create a class via the API (this can also be done in the business console).
///////////////////////////////////////////////////////////////////////////////
// [START class]
const classUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/eventTicketClass/';
const classPayload = {
"id": `${issuerId}.${classId}`,
"issuerName": "test issuer name",
"eventName": {
"defaultValue": {
"language": "en-US",
"value": "Test event name"
}
},
"reviewStatus": "underReview"
};
/**
* Create a class via the API. This can also be done in the Google Pay and
* Wallet console.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*
* @returns {string} The pass class ID: `${issuerId}.${classSuffix}`
*/
async createEventTicketClass(issuerId, classSuffix) {
const eventTicketClassUrl = `${this.baseUrl}/eventTicketClass`;
let classResponse = await httpClient.request({
url: classUrl,
method: 'POST',
data: classPayload
});
console.log('class POST response: ', classResponse);
// [END class]
///////////////////////////////////////////////////////////////////////////////
// Get or create an object via the API.
///////////////////////////////////////////////////////////////////////////////
// [START object]
const objectUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/eventTicketObject/';
const objectPayload = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
// 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',
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"state": "active",
"seatInfo": {
"kind": "walletobjects#eventSeat",
"seat": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "42"
}
},
"row": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "G3"
}
},
"section": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "5"
}
},
"gate": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "A"
}
}
},
"ticketHolderName": "Test ticket holder name",
"ticketNumber": "Test ticket number",
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
let objectResponse;
try {
objectResponse = await httpClient.request({
url: objectUrl + objectId,
method: 'GET'
});
} catch (err) {
if (err.response && err.response.status === 404) {
// Object does not yet exist
// Send POST request to create it
objectResponse = await httpClient.request({
url: objectUrl,
method: 'POST',
data: objectPayload
});
} else {
objectResponse = err;
}
}
console.log('object GET or POST response:', objectResponse);
// [END object]
///////////////////////////////////////////////////////////////////////////////
// Create a JWT for the object, and encode it to create a 'Save' URL.
///////////////////////////////////////////////////////////////////////////////
// [START jwt]
const claims = {
iss: credentials.client_email,
aud: 'google',
origins: ['www.example.com'],
typ: 'savetowallet',
payload: {
eventTicketObjects: [{
id: objectId
}],
}
};
const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;
console.log(saveUrl);
// [END jwt]
///////////////////////////////////////////////////////////////////////////////
// Create a new Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START createIssuer]
// New issuer name
const issuerName = 'name';
// New issuer email address
const issuerEmail = 'email-address';
// Issuer API endpoint
const issuerUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/issuer';
// New issuer information
let issuerPayload = {
name: issuerName,
contactInfo: {
email: issuerEmail
}
};
let issuerResponse = await httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuerPayload
});
console.log('issuer POST response:', issuerResponse);
// [END createIssuer]
///////////////////////////////////////////////////////////////////////////////
// Update permissions for an existing Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START updatePermissions]
// Permissions API endpoint
permissionsUrl = `https://walletobjects.googleapis.com/walletobjects/v1/permissions/${issuerId}`;
// New issuer permissions information
permissionsPayload = {
issuerId: issuerId,
permissions: [
// Copy as needed for each email address that will need access
{
emailAddress: 'email-address',
role: 'READER | WRITER | OWNER'
}
]
};
let permissionsResponse = await httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: permissionsPayload
});
console.log('permissions PUT response:', permissionsResponse);
// [END updatePermissions]
///////////////////////////////////////////////////////////////////////////////
// Batch create Google Wallet objects from an existing class
///////////////////////////////////////////////////////////////////////////////
//[START batch]
// The request body will be a multiline string
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let batchObject;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\\w.-]', '_');
// Generate an object ID with the user ID
objectId = `${issuerId}.${userId}-${classId}`;
batchObject = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"state": "active",
"seatInfo": {
"kind": "walletobjects#eventSeat",
"seat": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "42"
}
},
"row": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "G3"
}
},
"section": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "5"
}
},
"gate": {
"kind": "walletobjects#localizedString",
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "en-us",
"value": "A"
}
}
},
"ticketHolderName": "Test ticket holder name",
"ticketNumber": "Test ticket number",
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/eventTicketObject/\n\n';
let response = await this.httpClient.request({
url: eventTicketClassUrl,
method: 'POST',
data: eventTicketClass,
});
data += JSON.stringify(batchObject) + '\n\n';
console.log('Class insert response');
console.log(response);
return response.data.id;
}
data += '--batch_createobjectbatch--';
// [END class]
// Invoke the batch API calls
let batchResponse = await httpClient.request({
url: 'https://walletobjects.googleapis.com/batch',
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
// [START object]
/**
* 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);
}
}
});
}
// [END object]
console.log('batch POST response:', batchResponse);
// [START jwt]
/**
* 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}`;
}
// [END jwt]
// [START createIssuer]
/**
* Create a new Google Wallet issuer account.
*
* @param {string} issuerName The issuer's name.
* @param {string} issuerEmail The issuer's email address.
*/
async createIssuerAccount(issuerName, issuerEmail) {
// Issuer API endpoint
const issuerUrl = `${this.baseUrl}/issuer`;
// New issuer information
let issuer = {
name: issuerName,
contactInfo: {
email: issuerEmail,
},
};
let response = await this.httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuer
});
console.log('Issuer insert response');
console.log(response);
}
// [END createIssuer]
// [START updatePermissions]
/**
* Update permissions for an existing Google Wallet issuer account.
* **Warning:** This operation overwrites all existing permissions!
*
* Example permissions list argument below. Copy the dict entry as
* needed for each email address that will need access. Supported
* values for role are: 'READER', 'WRITER', and 'OWNER'
*
* let permissions = [
* {
* 'emailAddress': 'email-address',
* 'role': 'OWNER',
* },
* ];
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {Array} permissions The list of email addresses and roles to assign.
*/
async updateIssuerPermissions(issuerId, permissions) {
// Permissions API endpoint
const permissionsUrl = `${this.baseUrl}/permissions/${issuerId}`;
let response = await this.httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: {
issuerId: issuerId,
permissions: permissions,
}
});
console.log('Permissions update response');
console.log(response);
}
// [END updatePermissions]
// [START batch]
/**
* Batch create Google Wallet objects from an existing class.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*/
async batchCreateEventTicketObjects(issuerId, classSuffix) {
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let eventTicketObject;
let userId;
let objectId;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\w.-]', '_');
// Generate an object ID with the user ID
// Should only include alphanumeric characters, '.', '_', or '-'
objectId = `${issuerId}.${userId}`;
// See below for more information on required properties
// https://developers.google.com/wallet/tickets/events/rest/v1/eventticketobject
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',
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/eventTicketObject\n\n';
data += JSON.stringify(eventTicketObject) + '\n\n';
}
data += '--batch_createobjectbatch--';
// Invoke the batch API calls
let response = await this.httpClient.request({
url: `${this.baseUrl}/batch`,
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
}
});
console.log('Batch insert response');
console.log(response);
}
// [END batch]
};
}
module.exports = { DemoEventTicket };

View File

@@ -14,376 +14,550 @@
* limitations under the License.
*/
async function main() {
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
/**
* Demo class for creating and managing Flights in Google Wallet.
*/
class DemoFlight {
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';
/*
* keyFilePath - Path to service account key file from Google Cloud Console
* - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
*/
const keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';
/*
* issuerId - The issuer ID being updated in this request
* - Environment variable: WALLET_ISSUER_ID
*/
const issuerId = process.env.WALLET_ISSUER_ID || 'issuer-id';
/*
* classId - Developer-defined ID for the wallet class
* - Environment variable: WALLET_CLASS_ID
*/
const classId = process.env.WALLET_CLASS_ID || 'test-flight-class-id';
/*
* userId - Developer-defined ID for the user, such as an email address
* - Environment variable: WALLET_USER_ID
*/
let userId = process.env.WALLET_USER_ID || 'user-id';
/*
* objectId - ID for the wallet object
* - Format: `issuerId.identifier`
* - Should only include alphanumeric characters, '.', '_', or '-'
* - `identifier` is developer-defined and unique to the user
*/
let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}-${classId}`;
/**
* Base URL for Google Wallet API requests.
*/
this.baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1'
}
// [END setup]
///////////////////////////////////////////////////////////////////////////////
// Create authenticated HTTP client, using service account file.
///////////////////////////////////////////////////////////////////////////////
// [START auth]
const credentials = require(keyFilePath);
/**
* Create authenticated HTTP client using a service account file.
*/
auth() {
this.credentials = require(this.keyFilePath);
const httpClient = new GoogleAuth({
credentials: credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer'
});
this.httpClient = new GoogleAuth({
credentials: this.credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer',
});
}
// [END auth]
///////////////////////////////////////////////////////////////////////////////
// Create a class via the API (this can also be done in the business console).
///////////////////////////////////////////////////////////////////////////////
// [START class]
const classUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/flightClass/';
const classPayload = {
"id": `${issuerId}.${classId}`,
"issuerName": "test issuer name",
"destination": {
"airportIataCode": "SFO",
"gate": "C3",
"terminal": "2"
},
"flightHeader": {
"carrier": {
"carrierIataCode": "LX"
},
"flightNumber": "123"
},
"origin": {
"airportIataCode": "LAX",
"gate": "A2",
"terminal": "1"
},
"localScheduledDepartureDateTime": "2023-07-02T15:30:00",
"reviewStatus": "underReview"
};
/**
* Create a class via the API. This can also be done in the Google Pay and
* Wallet console.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*
* @returns {string} The pass class ID: `${issuerId}.${classSuffix}`
*/
async createFlightClass(issuerId, classSuffix) {
const flightClassUrl = `${this.baseUrl}/flightClass`;
let classResponse = await httpClient.request({
url: classUrl,
method: 'POST',
data: classPayload
});
console.log('class POST response: ', classResponse);
// [END class]
///////////////////////////////////////////////////////////////////////////////
// Get or create an object via the API.
///////////////////////////////////////////////////////////////////////////////
// [START object]
const objectUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/flightObject/';
const objectPayload = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
// See link below for more information on required properties
// https://developers.google.com/wallet/tickets/boarding-passes/rest/v1/flightclass
let flightClass = {
'id': `${issuerId}.${classSuffix}`,
'issuerName': 'Issuer name',
'reviewStatus': 'UNDER_REVIEW',
'localScheduledDepartureDateTime': '2023-07-02T15:30:00',
'flightHeader': {
'carrier': {
'carrierIataCode': 'LX',
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"state": "active",
"passengerName": "Test passenger name",
"reservationInfo": {
"confirmationCode": "Test confirmation code"
},
"boardingAndSeatingInfo": {
"seatNumber": "42",
"boardingGroup": "B"
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
let objectResponse;
try {
objectResponse = await httpClient.request({
url: objectUrl + objectId,
method: 'GET'
});
} catch (err) {
if (err.response && err.response.status === 404) {
// Object does not yet exist
// Send POST request to create it
objectResponse = await httpClient.request({
url: objectUrl,
method: 'POST',
data: objectPayload
});
} else {
objectResponse = err;
}
}
console.log('object GET or POST response:', objectResponse);
// [END object]
///////////////////////////////////////////////////////////////////////////////
// Create a JWT for the object, and encode it to create a 'Save' URL.
///////////////////////////////////////////////////////////////////////////////
// [START jwt]
const claims = {
iss: credentials.client_email,
aud: 'google',
origins: ['www.example.com'],
typ: 'savetowallet',
payload: {
flightObjects: [{
id: objectId
}],
}
};
const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;
console.log(saveUrl);
// [END jwt]
///////////////////////////////////////////////////////////////////////////////
// Create a new Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START createIssuer]
// New issuer name
const issuerName = 'name';
// New issuer email address
const issuerEmail = 'email-address';
// Issuer API endpoint
const issuerUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/issuer';
// New issuer information
let issuerPayload = {
name: issuerName,
contactInfo: {
email: issuerEmail
}
};
let issuerResponse = await httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuerPayload
});
console.log('issuer POST response:', issuerResponse);
// [END createIssuer]
///////////////////////////////////////////////////////////////////////////////
// Update permissions for an existing Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START updatePermissions]
// Permissions API endpoint
permissionsUrl = `https://walletobjects.googleapis.com/walletobjects/v1/permissions/${issuerId}`;
// New issuer permissions information
permissionsPayload = {
issuerId: issuerId,
permissions: [
// Copy as needed for each email address that will need access
{
emailAddress: 'email-address',
role: 'READER | WRITER | OWNER'
}
]
};
let permissionsResponse = await httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: permissionsPayload
});
console.log('permissions PUT response:', permissionsResponse);
// [END updatePermissions]
///////////////////////////////////////////////////////////////////////////////
// Batch create Google Wallet objects from an existing class
///////////////////////////////////////////////////////////////////////////////
//[START batch]
// The request body will be a multiline string
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let batchObject;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\\w.-]', '_');
// Generate an object ID with the user ID
objectId = `${issuerId}.${userId}-${classId}`;
batchObject = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
'flightNumber': '123',
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
'origin': {
'airportIataCode': 'LAX',
'terminal': '1',
'gate': 'A2',
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
'destination': {
'airportIataCode': 'SFO',
'terminal': '2',
'gate': 'C3',
},
"state": "active",
"passengerName": "Test passenger name",
"reservationInfo": {
"confirmationCode": "Test confirmation code"
},
"boardingAndSeatingInfo": {
"seatNumber": "42",
"boardingGroup": "B"
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/flightObject/\n\n';
let response = await this.httpClient.request({
url: flightClassUrl,
method: 'POST',
data: flightClass,
});
data += JSON.stringify(batchObject) + '\n\n';
console.log('Class insert response');
console.log(response);
return response.data.id;
}
data += '--batch_createobjectbatch--';
// [END class]
// Invoke the batch API calls
let batchResponse = await httpClient.request({
url: 'https://walletobjects.googleapis.com/batch',
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
// [START object]
/**
* 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 createFlightObject(issuerId, classSuffix, userId) {
const flightObjectUrl = `${this.baseUrl}/flightObject`;
// 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/tickets/boarding-passes/rest/v1/flightobject
let flightObject = {
'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,
},
],
'passengerName': 'Passenger name',
'boardingAndSeatingInfo': {
'boardingGroup': 'B',
'seatNumber': '42',
},
'reservationInfo': {
'confirmationCode': 'Confirmation code',
},
};
let response;
try {
response = await this.httpClient.request({
url: `${flightObjectUrl}/${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: flightObjectUrl,
method: 'POST',
data: flightObject,
});
console.log('Object insert response');
console.log(response);
return response.data.id;
} else {
// Something else went wrong
console.log(err);
}
}
});
}
// [END object]
console.log('batch POST response:', batchResponse);
// [START jwt]
/**
* 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/tickets/boarding-passes/rest/v1/flightclass
let flightClass = {
'id': `${issuerId}.${classSuffix}`,
'issuerName': 'Issuer name',
'reviewStatus': 'UNDER_REVIEW',
'localScheduledDepartureDateTime': '2023-07-02T15:30:00',
'flightHeader': {
'carrier': {
'carrierIataCode': 'LX',
},
'flightNumber': '123',
},
'origin': {
'airportIataCode': 'LAX',
'terminal': '1',
'gate': 'A2',
},
'destination': {
'airportIataCode': 'SFO',
'terminal': '2',
'gate': 'C3',
},
};
// See link below for more information on required properties
// https://developers.google.com/wallet/tickets/boarding-passes/rest/v1/flightobject
let flightObject = {
'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,
},
],
'passengerName': 'Passenger name',
'boardingAndSeatingInfo': {
'boardingGroup': 'B',
'seatNumber': '42',
},
'reservationInfo': {
'confirmationCode': 'Confirmation code',
},
};
// 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
flightClasses: [flightClass,],
flightObjects: [flightObject,],
},
};
// 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}`;
}
// [END jwt]
// [START createIssuer]
/**
* Create a new Google Wallet issuer account.
*
* @param {string} issuerName The issuer's name.
* @param {string} issuerEmail The issuer's email address.
*/
async createIssuerAccount(issuerName, issuerEmail) {
// Issuer API endpoint
const issuerUrl = `${this.baseUrl}/issuer`;
// New issuer information
let issuer = {
name: issuerName,
contactInfo: {
email: issuerEmail,
},
};
let response = await this.httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuer
});
console.log('Issuer insert response');
console.log(response);
}
// [END createIssuer]
// [START updatePermissions]
/**
* Update permissions for an existing Google Wallet issuer account.
* **Warning:** This operation overwrites all existing permissions!
*
* Example permissions list argument below. Copy the dict entry as
* needed for each email address that will need access. Supported
* values for role are: 'READER', 'WRITER', and 'OWNER'
*
* let permissions = [
* {
* 'emailAddress': 'email-address',
* 'role': 'OWNER',
* },
* ];
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {Array} permissions The list of email addresses and roles to assign.
*/
async updateIssuerPermissions(issuerId, permissions) {
// Permissions API endpoint
const permissionsUrl = `${this.baseUrl}/permissions/${issuerId}`;
let response = await this.httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: {
issuerId: issuerId,
permissions: permissions,
}
});
console.log('Permissions update response');
console.log(response);
}
// [END updatePermissions]
// [START batch]
/**
* Batch create Google Wallet objects from an existing class.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*/
async batchCreateFlightObjects(issuerId, classSuffix) {
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let flightObject;
let userId;
let objectId;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\w.-]', '_');
// Generate an object ID with the user ID
// Should only include alphanumeric characters, '.', '_', or '-'
objectId = `${issuerId}.${userId}`;
// See link below for more information on required properties
// https://developers.google.com/wallet/tickets/boarding-passes/rest/v1/flightobject
flightObject = {
'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,
},
],
'passengerName': 'Passenger name',
'boardingAndSeatingInfo': {
'boardingGroup': 'B',
'seatNumber': '42',
},
'reservationInfo': {
'confirmationCode': 'Confirmation code',
},
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/flightObject\n\n';
data += JSON.stringify(flightObject) + '\n\n';
}
data += '--batch_createobjectbatch--';
// Invoke the batch API calls
let response = await this.httpClient.request({
url: `${this.baseUrl}/batch`,
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
}
});
console.log('Batch insert response');
console.log(response);
}
// [END batch]
};
}
module.exports = { DemoFlight };

View File

@@ -14,376 +14,542 @@
* limitations under the License.
*/
async function main() {
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
/**
* Demo class for creating and managing Generic passes in Google Wallet.
*/
class DemoGeneric {
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';
/*
* keyFilePath - Path to service account key file from Google Cloud Console
* - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
*/
const keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';
/*
* issuerId - The issuer ID being updated in this request
* - Environment variable: WALLET_ISSUER_ID
*/
const issuerId = process.env.WALLET_ISSUER_ID || 'issuer-id';
/*
* classId - Developer-defined ID for the wallet class
* - Environment variable: WALLET_CLASS_ID
*/
const classId = process.env.WALLET_CLASS_ID || 'test-generic-class-id';
/*
* userId - Developer-defined ID for the user, such as an email address
* - Environment variable: WALLET_USER_ID
*/
let userId = process.env.WALLET_USER_ID || 'user-id';
/*
* objectId - ID for the wallet object
* - Format: `issuerId.identifier`
* - Should only include alphanumeric characters, '.', '_', or '-'
* - `identifier` is developer-defined and unique to the user
*/
let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}-${classId}`;
/**
* Base URL for Google Wallet API requests.
*/
this.baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1'
}
// [END setup]
///////////////////////////////////////////////////////////////////////////////
// Create authenticated HTTP client, using service account file.
///////////////////////////////////////////////////////////////////////////////
// [START auth]
const credentials = require(keyFilePath);
/**
* Create authenticated HTTP client using a service account file.
*/
auth() {
this.credentials = require(this.keyFilePath);
const httpClient = new GoogleAuth({
credentials: credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer'
});
this.httpClient = new GoogleAuth({
credentials: this.credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer',
});
}
// [END auth]
///////////////////////////////////////////////////////////////////////////////
// Create a class via the API (this can also be done in the business console).
///////////////////////////////////////////////////////////////////////////////
// [START class]
const classUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/genericClass/';
const classPayload = {
"id": `${issuerId}.${classId}`,
"issuerName": "test issuer name"
};
/**
* Create a class via the API. This can also be done in the Google Pay and
* Wallet console.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*
* @returns {string} The pass class ID: `${issuerId}.${classSuffix}`
*/
async createGenericClass(issuerId, classSuffix) {
const genericClassUrl = `${this.baseUrl}/genericClass`;
let classResponse = await httpClient.request({
url: classUrl,
method: 'POST',
data: classPayload
});
console.log('class POST response: ', classResponse);
// [END class]
///////////////////////////////////////////////////////////////////////////////
// Get or create an object via the API.
///////////////////////////////////////////////////////////////////////////////
// [START object]
const objectUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/genericObject/';
const objectPayload = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"genericType": "GENERIC_TYPE_UNSPECIFIED",
"hexBackgroundColor": "#4285f4",
"logo": {
"sourceUri": {
"uri": "https://storage.googleapis.com/wallet-lab-tools-codelab-artifacts-public/pass_google_logo.jpg"
}
},
"cardTitle": {
"defaultValue": {
"language": "en-US",
"value": "Testing Generic Title"
}
},
"header": {
"defaultValue": {
"language": "en-US",
"value": "Testing Generic Header"
}
},
"subheader": {
"defaultValue": {
"language": "en",
"value": "Testing Generic Sub Header"
}
}
};
let objectResponse;
try {
objectResponse = await httpClient.request({
url: objectUrl + objectId,
method: 'GET'
});
} catch (err) {
if (err.response && err.response.status === 404) {
// Object does not yet exist
// Send POST request to create it
objectResponse = await httpClient.request({
url: objectUrl,
method: 'POST',
data: objectPayload
});
} else {
objectResponse = err;
}
}
console.log('object GET or POST response:', objectResponse);
// [END object]
///////////////////////////////////////////////////////////////////////////////
// Create a JWT for the object, and encode it to create a 'Save' URL.
///////////////////////////////////////////////////////////////////////////////
// [START jwt]
const claims = {
iss: credentials.client_email,
aud: 'google',
origins: ['www.example.com'],
typ: 'savetowallet',
payload: {
genericObjects: [{
id: objectId
}],
}
};
const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;
console.log(saveUrl);
// [END jwt]
///////////////////////////////////////////////////////////////////////////////
// Create a new Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START createIssuer]
// New issuer name
const issuerName = 'name';
// New issuer email address
const issuerEmail = 'email-address';
// Issuer API endpoint
const issuerUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/issuer';
// New issuer information
let issuerPayload = {
name: issuerName,
contactInfo: {
email: issuerEmail
}
};
let issuerResponse = await httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuerPayload
});
console.log('issuer POST response:', issuerResponse);
// [END createIssuer]
///////////////////////////////////////////////////////////////////////////////
// Update permissions for an existing Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START updatePermissions]
// Permissions API endpoint
permissionsUrl = `https://walletobjects.googleapis.com/walletobjects/v1/permissions/${issuerId}`;
// New issuer permissions information
permissionsPayload = {
issuerId: issuerId,
permissions: [
// Copy as needed for each email address that will need access
{
emailAddress: 'email-address',
role: 'READER | WRITER | OWNER'
}
]
};
let permissionsResponse = await httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: permissionsPayload
});
console.log('permissions PUT response:', permissionsResponse);
// [END updatePermissions]
///////////////////////////////////////////////////////////////////////////////
// Batch create Google Wallet objects from an existing class
///////////////////////////////////////////////////////////////////////////////
//[START batch]
// The request body will be a multiline string
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let batchObject;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\\w.-]', '_');
// Generate an object ID with the user ID
objectId = `${issuerId}.${userId}-${classId}`;
batchObject = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"genericType": "GENERIC_TYPE_UNSPECIFIED",
"hexBackgroundColor": "#4285f4",
"logo": {
"sourceUri": {
"uri": "https://storage.googleapis.com/wallet-lab-tools-codelab-artifacts-public/pass_google_logo.jpg"
}
},
"cardTitle": {
"defaultValue": {
"language": "en-US",
"value": "Testing Generic Title"
}
},
"header": {
"defaultValue": {
"language": "en-US",
"value": "Testing Generic Header"
}
},
"subheader": {
"defaultValue": {
"language": "en",
"value": "Testing Generic Sub Header"
}
}
// See link below for more information on required properties
// https://developers.google.com/wallet/generic/rest/v1/genericclass
let genericClass = {
'id': `${issuerId}.${classSuffix}`,
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/genericObject/\n\n';
let response = await this.httpClient.request({
url: genericClassUrl,
method: 'POST',
data: genericClass,
});
data += JSON.stringify(batchObject) + '\n\n';
console.log('Class insert response');
console.log(response);
return response.data.id;
}
data += '--batch_createobjectbatch--';
// [END class]
// Invoke the batch API calls
let batchResponse = await httpClient.request({
url: 'https://walletobjects.googleapis.com/batch',
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
// [START object]
/**
* 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 createGenericObject(issuerId, classSuffix, userId) {
const genericObjectUrl = `${this.baseUrl}/genericObject`;
// 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/generic/rest/v1/genericobject
let genericObject = {
'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',
},
'cardTitle': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic card title',
},
},
'header': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic header',
},
},
'hexBackgroundColor': '#4285f4',
'logo': {
'sourceUri': {
'uri': 'https://storage.googleapis.com/wallet-lab-tools-codelab-artifacts-public/pass_google_logo.jpg',
},
'contentDescription': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic card logo',
},
},
},
};
let response;
try {
response = await this.httpClient.request({
url: `${genericObjectUrl}/${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: genericObjectUrl,
method: 'POST',
data: genericObject,
});
console.log('Object insert response');
console.log(response);
return response.data.id;
} else {
// Something else went wrong
console.log(err);
}
}
});
}
// [END object]
console.log('batch POST response:', batchResponse);
// [START jwt]
/**
* 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/generic/rest/v1/genericclass
let genericClass = {
'id': `${issuerId}.${classSuffix}`,
};
// See link below for more information on required properties
// https://developers.google.com/wallet/generic/rest/v1/genericobject
let genericObject = {
'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',
},
'cardTitle': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic card title',
},
},
'header': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic header',
},
},
'hexBackgroundColor': '#4285f4',
'logo': {
'sourceUri': {
'uri': 'https://storage.googleapis.com/wallet-lab-tools-codelab-artifacts-public/pass_google_logo.jpg',
},
'contentDescription': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic card logo',
},
},
},
};
// 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
genericClasses: [genericClass,],
genericObjects: [genericObject,],
},
};
// 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}`;
}
// [END jwt]
// [START createIssuer]
/**
* Create a new Google Wallet issuer account.
*
* @param {string} issuerName The issuer's name.
* @param {string} issuerEmail The issuer's email address.
*/
async createIssuerAccount(issuerName, issuerEmail) {
// Issuer API endpoint
const issuerUrl = `${this.baseUrl}/issuer`;
// New issuer information
let issuer = {
name: issuerName,
contactInfo: {
email: issuerEmail,
},
};
let response = await this.httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuer
});
console.log('Issuer insert response');
console.log(response);
}
// [END createIssuer]
// [START updatePermissions]
/**
* Update permissions for an existing Google Wallet issuer account.
* **Warning:** This operation overwrites all existing permissions!
*
* Example permissions list argument below. Copy the dict entry as
* needed for each email address that will need access. Supported
* values for role are: 'READER', 'WRITER', and 'OWNER'
*
* let permissions = [
* {
* 'emailAddress': 'email-address',
* 'role': 'OWNER',
* },
* ];
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {Array} permissions The list of email addresses and roles to assign.
*/
async updateIssuerPermissions(issuerId, permissions) {
// Permissions API endpoint
const permissionsUrl = `${this.baseUrl}/permissions/${issuerId}`;
let response = await this.httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: {
issuerId: issuerId,
permissions: permissions,
}
});
console.log('Permissions update response');
console.log(response);
}
// [END updatePermissions]
// [START batch]
/**
* Batch create Google Wallet objects from an existing class.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*/
async batchCreateGenericObjects(issuerId, classSuffix) {
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let genericObject;
let userId;
let objectId;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\w.-]', '_');
// Generate an object ID with the user ID
// Should only include alphanumeric characters, '.', '_', or '-'
objectId = `${issuerId}.${userId}`;
// See link below for more information on required properties
// https://developers.google.com/wallet/generic/rest/v1/genericobject
genericObject = {
'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',
},
'cardTitle': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic card title',
},
},
'header': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic header',
},
},
'hexBackgroundColor': '#4285f4',
'logo': {
'sourceUri': {
'uri': 'https://storage.googleapis.com/wallet-lab-tools-codelab-artifacts-public/pass_google_logo.jpg',
},
'contentDescription': {
'defaultValue': {
'language': 'en-US',
'value': 'Generic card logo',
},
},
},
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/genericObject\n\n';
data += JSON.stringify(genericObject) + '\n\n';
}
data += '--batch_createobjectbatch--';
// Invoke the batch API calls
let response = await this.httpClient.request({
url: `${this.baseUrl}/batch`,
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
}
});
console.log('Batch insert response');
console.log(response);
}
// [END batch]
};
}
module.exports = { DemoGeneric };

View File

@@ -14,363 +14,519 @@
* limitations under the License.
*/
async function main() {
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
/**
* Demo class for creating and managing Gift cards in Google Wallet.
*/
class DemoGiftCard {
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';
/*
* keyFilePath - Path to service account key file from Google Cloud Console
* - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
*/
const keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';
/*
* issuerId - The issuer ID being updated in this request
* - Environment variable: WALLET_ISSUER_ID
*/
const issuerId = process.env.WALLET_ISSUER_ID || 'issuer-id';
/*
* classId - Developer-defined ID for the wallet class
* - Environment variable: WALLET_CLASS_ID
*/
const classId = process.env.WALLET_CLASS_ID || 'test-giftCard-class-id';
/*
* userId - Developer-defined ID for the user, such as an email address
* - Environment variable: WALLET_USER_ID
*/
let userId = process.env.WALLET_USER_ID || 'user-id';
/*
* objectId - ID for the wallet object
* - Format: `issuerId.identifier`
* - Should only include alphanumeric characters, '.', '_', or '-'
* - `identifier` is developer-defined and unique to the user
*/
let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}-${classId}`;
/**
* Base URL for Google Wallet API requests.
*/
this.baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1'
}
// [END setup]
///////////////////////////////////////////////////////////////////////////////
// Create authenticated HTTP client, using service account file.
///////////////////////////////////////////////////////////////////////////////
// [START auth]
const credentials = require(keyFilePath);
/**
* Create authenticated HTTP client using a service account file.
*/
auth() {
this.credentials = require(this.keyFilePath);
const httpClient = new GoogleAuth({
credentials: credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer'
});
this.httpClient = new GoogleAuth({
credentials: this.credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer',
});
}
// [END auth]
///////////////////////////////////////////////////////////////////////////////
// Create a class via the API (this can also be done in the business console).
///////////////////////////////////////////////////////////////////////////////
// [START class]
const classUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/giftCardClass/';
const classPayload = {
"id": `${issuerId}.${classId}`,
"issuerName": "test issuer name",
"merchantName": "Test merchant name",
"allowMultipleUsersPerObject": "true",
"reviewStatus": "underReview"
};
/**
* Create a class via the API. This can also be done in the Google Pay and
* Wallet console.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*
* @returns {string} The pass class ID: `${issuerId}.${classSuffix}`
*/
async createGiftCardClass(issuerId, classSuffix) {
const giftCardClassUrl = `${this.baseUrl}/giftCardClass`;
let classResponse = await httpClient.request({
url: classUrl,
method: 'POST',
data: classPayload
});
console.log('class POST response: ', classResponse);
// [END class]
///////////////////////////////////////////////////////////////////////////////
// Get or create an object via the API.
///////////////////////////////////////////////////////////////////////////////
// [START object]
const objectUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/giftCardObject/';
const objectPayload = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"cardNumber": "Test card number",
"cardPin": "Test card pin",
"balance": {
"kind": "walletobjects#money",
"micros": 20000000,
"currencyCode": "USD"
},
"balanceUpdateTime": {
"date": "2020-04-12T16:20:50.52Z"
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
let objectResponse;
try {
objectResponse = await httpClient.request({
url: objectUrl + objectId,
method: 'GET'
});
} catch (err) {
if (err.response && err.response.status === 404) {
// Object does not yet exist
// Send POST request to create it
objectResponse = await httpClient.request({
url: objectUrl,
method: 'POST',
data: objectPayload
});
} else {
objectResponse = err;
}
}
console.log('object GET or POST response:', objectResponse);
// [END object]
///////////////////////////////////////////////////////////////////////////////
// Create a JWT for the object, and encode it to create a 'Save' URL.
///////////////////////////////////////////////////////////////////////////////
// [START jwt]
const claims = {
iss: credentials.client_email,
aud: 'google',
origins: ['www.example.com'],
typ: 'savetowallet',
payload: {
giftCardObjects: [{
id: objectId
}],
}
};
const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;
console.log(saveUrl);
// [END jwt]
///////////////////////////////////////////////////////////////////////////////
// Create a new Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START createIssuer]
// New issuer name
const issuerName = 'name';
// New issuer email address
const issuerEmail = 'email-address';
// Issuer API endpoint
const issuerUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/issuer';
// New issuer information
let issuerPayload = {
name: issuerName,
contactInfo: {
email: issuerEmail
}
};
let issuerResponse = await httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuerPayload
});
console.log('issuer POST response:', issuerResponse);
// [END createIssuer]
///////////////////////////////////////////////////////////////////////////////
// Update permissions for an existing Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START updatePermissions]
// Permissions API endpoint
permissionsUrl = `https://walletobjects.googleapis.com/walletobjects/v1/permissions/${issuerId}`;
// New issuer permissions information
permissionsPayload = {
issuerId: issuerId,
permissions: [
// Copy as needed for each email address that will need access
{
emailAddress: 'email-address',
role: 'READER | WRITER | OWNER'
}
]
};
let permissionsResponse = await httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: permissionsPayload
});
console.log('permissions PUT response:', permissionsResponse);
// [END updatePermissions]
///////////////////////////////////////////////////////////////////////////////
// Batch create Google Wallet objects from an existing class
///////////////////////////////////////////////////////////////////////////////
//[START batch]
// The request body will be a multiline string
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let batchObject;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\\w.-]', '_');
// Generate an object ID with the user ID
objectId = `${issuerId}.${userId}-${classId}`;
batchObject = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"cardNumber": "Test card number",
"cardPin": "Test card pin",
"balance": {
"kind": "walletobjects#money",
"micros": 20000000,
"currencyCode": "USD"
},
"balanceUpdateTime": {
"date": "2020-04-12T16:20:50.52Z"
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
// See link below for more information on required properties
// https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardclass
let giftCardClass = {
'id': `${issuerId}.${classSuffix}`,
'issuerName': 'Issuer name',
'reviewStatus': 'UNDER_REVIEW',
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/giftCardObject/\n\n';
let response = await this.httpClient.request({
url: giftCardClassUrl,
method: 'POST',
data: giftCardClass,
});
data += JSON.stringify(batchObject) + '\n\n';
console.log('Class insert response');
console.log(response);
return response.data.id;
}
data += '--batch_createobjectbatch--';
// [END class]
// Invoke the batch API calls
let batchResponse = await httpClient.request({
url: 'https://walletobjects.googleapis.com/batch',
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
// [START object]
/**
* 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 createGiftCardObject(issuerId, classSuffix, userId) {
const giftCardObjectUrl = `${this.baseUrl}/giftCardObject`;
// 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/gift-cards/rest/v1/giftcardobject
let giftCardObject = {
'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,
},
],
'cardNumber': 'Card number',
'pin': '1234',
'balance': {
'micros': 20000000,
'currencyCode': 'USD',
},
'balanceUpdateTime': {
'date': '2020-04-12T16:20:50.52-04:00',
},
};
let response;
try {
response = await this.httpClient.request({
url: `${giftCardObjectUrl}/${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: giftCardObjectUrl,
method: 'POST',
data: giftCardObject,
});
console.log('Object insert response');
console.log(response);
return response.data.id;
} else {
// Something else went wrong
console.log(err);
}
}
});
}
// [END object]
console.log('batch POST response:', batchResponse);
// [START jwt]
/**
* 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/gift-cards/rest/v1/giftcardclass
let giftCardClass = {
'id': `${issuerId}.${classSuffix}`,
'issuerName': 'Issuer name',
'reviewStatus': 'UNDER_REVIEW',
};
// See link below for more information on required properties
// https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardobject
let giftCardObject = {
'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,
},
],
'cardNumber': 'Card number',
'pin': '1234',
'balance': {
'micros': 20000000,
'currencyCode': 'USD',
},
'balanceUpdateTime': {
'date': '2020-04-12T16:20:50.52-04:00',
},
};
// 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
giftCardClasses: [giftCardClass,],
giftCardObjects: [giftCardObject,],
},
};
// 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}`;
}
// [END jwt]
// [START createIssuer]
/**
* Create a new Google Wallet issuer account.
*
* @param {string} issuerName The issuer's name.
* @param {string} issuerEmail The issuer's email address.
*/
async createIssuerAccount(issuerName, issuerEmail) {
// Issuer API endpoint
const issuerUrl = `${this.baseUrl}/issuer`;
// New issuer information
let issuer = {
name: issuerName,
contactInfo: {
email: issuerEmail,
},
};
let response = await this.httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuer
});
console.log('Issuer insert response');
console.log(response);
}
// [END createIssuer]
// [START updatePermissions]
/**
* Update permissions for an existing Google Wallet issuer account.
* **Warning:** This operation overwrites all existing permissions!
*
* Example permissions list argument below. Copy the dict entry as
* needed for each email address that will need access. Supported
* values for role are: 'READER', 'WRITER', and 'OWNER'
*
* let permissions = [
* {
* 'emailAddress': 'email-address',
* 'role': 'OWNER',
* },
* ];
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {Array} permissions The list of email addresses and roles to assign.
*/
async updateIssuerPermissions(issuerId, permissions) {
// Permissions API endpoint
const permissionsUrl = `${this.baseUrl}/permissions/${issuerId}`;
let response = await this.httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: {
issuerId: issuerId,
permissions: permissions,
}
});
console.log('Permissions update response');
console.log(response);
}
// [END updatePermissions]
// [START batch]
/**
* Batch create Google Wallet objects from an existing class.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*/
async batchCreateGiftCardObjects(issuerId, classSuffix) {
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let giftCardObject;
let userId;
let objectId;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\w.-]', '_');
// Generate an object ID with the user ID
// Should only include alphanumeric characters, '.', '_', or '-'
objectId = `${issuerId}.${userId}`;
// See link below for more information on required properties
// https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardobject
giftCardObject = {
'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,
},
],
'cardNumber': 'Card number',
'pin': '1234',
'balance': {
'micros': 20000000,
'currencyCode': 'USD',
},
'balanceUpdateTime': {
'date': '2020-04-12T16:20:50.52-04:00',
},
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/giftCardObject\n\n';
data += JSON.stringify(giftCardObject) + '\n\n';
}
data += '--batch_createobjectbatch--';
// Invoke the batch API calls
let response = await this.httpClient.request({
url: `${this.baseUrl}/batch`,
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
}
});
console.log('Batch insert response');
console.log(response);
}
// [END batch]
};
}
module.exports = { DemoGiftCard };

View File

@@ -14,367 +14,540 @@
* limitations under the License.
*/
async function main() {
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
/**
* Demo class for creating and managing Loyalty cards in Google Wallet.
*/
class DemoLoyalty {
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';
/*
* keyFilePath - Path to service account key file from Google Cloud Console
* - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
*/
const keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';
/*
* issuerId - The issuer ID being updated in this request
* - Environment variable: WALLET_ISSUER_ID
*/
const issuerId = process.env.WALLET_ISSUER_ID || 'issuer-id';
/*
* classId - Developer-defined ID for the wallet class
* - Environment variable: WALLET_CLASS_ID
*/
const classId = process.env.WALLET_CLASS_ID || 'test-loyalty-class-id';
/*
* userId - Developer-defined ID for the user, such as an email address
* - Environment variable: WALLET_USER_ID
*/
let userId = process.env.WALLET_USER_ID || 'user-id';
/*
* objectId - ID for the wallet object
* - Format: `issuerId.identifier`
* - Should only include alphanumeric characters, '.', '_', or '-'
* - `identifier` is developer-defined and unique to the user
*/
let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}-${classId}`;
/**
* Base URL for Google Wallet API requests.
*/
this.baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1'
}
// [END setup]
///////////////////////////////////////////////////////////////////////////////
// Create authenticated HTTP client, using service account file.
///////////////////////////////////////////////////////////////////////////////
// [START auth]
const credentials = require(keyFilePath);
/**
* Create authenticated HTTP client using a service account file.
*/
auth() {
this.credentials = require(this.keyFilePath);
const httpClient = new GoogleAuth({
credentials: credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer'
});
this.httpClient = new GoogleAuth({
credentials: this.credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer',
});
}
// [END auth]
///////////////////////////////////////////////////////////////////////////////
// Create a class via the API (this can also be done in the business console).
///////////////////////////////////////////////////////////////////////////////
// [START class]
const classUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/loyaltyClass/';
const classPayload = {
"id": `${issuerId}.${classId}`,
"issuerName": "test issuer name",
"programName": "test program name",
"programLogo": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm8.staticflickr.com/7340/11177041185_a61a7f2139_o.jpg"
}
},
"reviewStatus": "underReview"
};
/**
* Create a class via the API. This can also be done in the Google Pay and
* Wallet console.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*
* @returns {string} The pass class ID: `${issuerId}.${classSuffix}`
*/
async createLoyaltyClass(issuerId, classSuffix) {
const loyaltyClassUrl = `${this.baseUrl}/loyaltyClass`;
let classResponse = await httpClient.request({
url: classUrl,
method: 'POST',
data: classPayload
});
console.log('class POST response: ', classResponse);
// [END class]
///////////////////////////////////////////////////////////////////////////////
// Get or create an object via the API.
///////////////////////////////////////////////////////////////////////////////
// [START object]
const objectUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/loyaltyObject/';
const objectPayload = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
// See link below for more information on required properties
// https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyclass
let loyaltyClass = {
'id': `${issuerId}.${classSuffix}`,
'issuerName': 'Issuer name',
'reviewStatus': 'UNDER_REVIEW',
'programName': 'Program name',
'programLogo': {
'sourceUri': {
'uri': 'http://farm8.staticflickr.com/7340/11177041185_a61a7f2139_o.jpg',
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"state": "active",
"accountId": "Test account id",
"accountName": "Test account name",
"loyaltyPoints": {
"balance": {
"string": "800"
},
"label": "Points"
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
let objectResponse;
try {
objectResponse = await httpClient.request({
url: objectUrl + objectId,
method: 'GET'
});
} catch (err) {
if (err.response && err.response.status === 404) {
// Object does not yet exist
// Send POST request to create it
objectResponse = await httpClient.request({
url: objectUrl,
method: 'POST',
data: objectPayload
});
} else {
objectResponse = err;
}
}
console.log('object GET or POST response:', objectResponse);
// [END object]
///////////////////////////////////////////////////////////////////////////////
// Create a JWT for the object, and encode it to create a 'Save' URL.
///////////////////////////////////////////////////////////////////////////////
// [START jwt]
const claims = {
iss: credentials.client_email,
aud: 'google',
origins: ['www.example.com'],
typ: 'savetowallet',
payload: {
loyaltyObjects: [{
id: objectId
}],
}
};
const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;
console.log(saveUrl);
// [END jwt]
///////////////////////////////////////////////////////////////////////////////
// Create a new Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START createIssuer]
// New issuer name
const issuerName = 'name';
// New issuer email address
const issuerEmail = 'email-address';
// Issuer API endpoint
const issuerUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/issuer';
// New issuer information
let issuerPayload = {
name: issuerName,
contactInfo: {
email: issuerEmail
}
};
let issuerResponse = await httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuerPayload
});
console.log('issuer POST response:', issuerResponse);
// [END createIssuer]
///////////////////////////////////////////////////////////////////////////////
// Update permissions for an existing Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START updatePermissions]
// Permissions API endpoint
permissionsUrl = `https://walletobjects.googleapis.com/walletobjects/v1/permissions/${issuerId}`;
// New issuer permissions information
permissionsPayload = {
issuerId: issuerId,
permissions: [
// Copy as needed for each email address that will need access
{
emailAddress: 'email-address',
role: 'READER | WRITER | OWNER'
}
]
};
let permissionsResponse = await httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: permissionsPayload
});
console.log('permissions PUT response:', permissionsResponse);
// [END updatePermissions]
///////////////////////////////////////////////////////////////////////////////
// Batch create Google Wallet objects from an existing class
///////////////////////////////////////////////////////////////////////////////
//[START batch]
// The request body will be a multiline string
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let batchObject;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\\w.-]', '_');
// Generate an object ID with the user ID
objectId = `${issuerId}.${userId}-${classId}`;
batchObject = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
'contentDescription': {
'defaultValue': {
'language': 'en-US',
'value': 'Logo description',
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"kind": "walletobjects#barcode",
"type": "qrCode",
"value": "Test QR Code"
},
"state": "active",
"accountId": "Test account id",
"accountName": "Test account name",
"loyaltyPoints": {
"balance": {
"string": "800"
},
"label": "Points"
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/loyaltyObject/\n\n';
let response = await this.httpClient.request({
url: loyaltyClassUrl,
method: 'POST',
data: loyaltyClass,
});
data += JSON.stringify(batchObject) + '\n\n';
console.log('Class insert response');
console.log(response);
return response.data.id;
}
data += '--batch_createobjectbatch--';
// [END class]
// Invoke the batch API calls
let batchResponse = await httpClient.request({
url: 'https://walletobjects.googleapis.com/batch',
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
// [START object]
/**
* 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 createLoyaltyObject(issuerId, classSuffix, userId) {
const loyaltyObjectUrl = `${this.baseUrl}/loyaltyObject`;
// 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/loyalty-cards/rest/v1/loyaltyobject
let loyaltyObject = {
'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,
},
],
'accountId': 'Account id',
'accountName': 'Account name',
'loyaltyPoints': {
'label': 'Points',
'balance': {
'int': 800,
},
},
};
let response;
try {
response = await this.httpClient.request({
url: `${loyaltyObjectUrl}/${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: loyaltyObjectUrl,
method: 'POST',
data: loyaltyObject,
});
console.log('Object insert response');
console.log(response);
return response.data.id;
} else {
// Something else went wrong
console.log(err);
}
}
});
}
// [END object]
console.log('batch POST response:', batchResponse);
// [START jwt]
/**
* 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/loyalty-cards/rest/v1/loyaltyclass
let loyaltyClass = {
'id': `${issuerId}.${classSuffix}`,
'issuerName': 'Issuer name',
'reviewStatus': 'UNDER_REVIEW',
'programName': 'Program name',
'programLogo': {
'sourceUri': {
'uri': 'http://farm8.staticflickr.com/7340/11177041185_a61a7f2139_o.jpg',
},
'contentDescription': {
'defaultValue': {
'language': 'en-US',
'value': 'Logo description',
},
},
},
};
// See link below for more information on required properties
// https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyobject
let loyaltyObject = {
'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,
},
],
'accountId': 'Account id',
'accountName': 'Account name',
'loyaltyPoints': {
'label': 'Points',
'balance': {
'int': 800,
},
},
};
// 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
loyaltyClasses: [loyaltyClass,],
loyaltyObjects: [loyaltyObject,],
},
};
// 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}`;
}
// [END jwt]
// [START createIssuer]
/**
* Create a new Google Wallet issuer account.
*
* @param {string} issuerName The issuer's name.
* @param {string} issuerEmail The issuer's email address.
*/
async createIssuerAccount(issuerName, issuerEmail) {
// Issuer API endpoint
const issuerUrl = `${this.baseUrl}/issuer`;
// New issuer information
let issuer = {
name: issuerName,
contactInfo: {
email: issuerEmail,
},
};
let response = await this.httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuer
});
console.log('Issuer insert response');
console.log(response);
}
// [END createIssuer]
// [START updatePermissions]
/**
* Update permissions for an existing Google Wallet issuer account.
* **Warning:** This operation overwrites all existing permissions!
*
* Example permissions list argument below. Copy the dict entry as
* needed for each email address that will need access. Supported
* values for role are: 'READER', 'WRITER', and 'OWNER'
*
* let permissions = [
* {
* 'emailAddress': 'email-address',
* 'role': 'OWNER',
* },
* ];
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {Array} permissions The list of email addresses and roles to assign.
*/
async updateIssuerPermissions(issuerId, permissions) {
// Permissions API endpoint
const permissionsUrl = `${this.baseUrl}/permissions/${issuerId}`;
let response = await this.httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: {
issuerId: issuerId,
permissions: permissions,
}
});
console.log('Permissions update response');
console.log(response);
}
// [END updatePermissions]
// [START batch]
/**
* Batch create Google Wallet objects from an existing class.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*/
async batchCreateLoyaltyObjects(issuerId, classSuffix) {
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let loyaltyObject;
let userId;
let objectId;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\w.-]', '_');
// Generate an object ID with the user ID
// Should only include alphanumeric characters, '.', '_', or '-'
objectId = `${issuerId}.${userId}`;
// See link below for more information on required properties
// https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyobject
loyaltyObject = {
'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,
},
],
'accountId': 'Account id',
'accountName': 'Account name',
'loyaltyPoints': {
'label': 'Points',
'balance': {
'int': 800,
},
},
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/loyaltyObject\n\n';
data += JSON.stringify(loyaltyObject) + '\n\n';
}
data += '--batch_createobjectbatch--';
// Invoke the batch API calls
let response = await this.httpClient.request({
url: `${this.baseUrl}/batch`,
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
}
});
console.log('Batch insert response');
console.log(response);
}
// [END batch]
};
}
module.exports = { DemoLoyalty };

View File

@@ -14,362 +14,522 @@
* limitations under the License.
*/
async function main() {
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
// [START setup]
// [START imports]
const { GoogleAuth } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
// [END imports]
/**
* 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';
/*
* keyFilePath - Path to service account key file from Google Cloud Console
* - Environment variable: GOOGLE_APPLICATION_CREDENTIALS
*/
const keyFilePath = process.env.GOOGLE_APPLICATION_CREDENTIALS || '/path/to/key.json';
/*
* issuerId - The issuer ID being updated in this request
* - Environment variable: WALLET_ISSUER_ID
*/
const issuerId = process.env.WALLET_ISSUER_ID || 'issuer-id';
/*
* classId - Developer-defined ID for the wallet class
* - Environment variable: WALLET_CLASS_ID
*/
const classId = process.env.WALLET_CLASS_ID || 'test-offer-class-id';
/*
* userId - Developer-defined ID for the user, such as an email address
* - Environment variable: WALLET_USER_ID
*/
let userId = process.env.WALLET_USER_ID || 'user-id';
/*
* objectId - ID for the wallet object
* - Format: `issuerId.identifier`
* - Should only include alphanumeric characters, '.', '_', or '-'
* - `identifier` is developer-defined and unique to the user
*/
let objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, '_')}-${classId}`;
/**
* Base URL for Google Wallet API requests.
*/
this.baseUrl = 'https://walletobjects.googleapis.com/walletobjects/v1'
}
// [END setup]
///////////////////////////////////////////////////////////////////////////////
// Create authenticated HTTP client, using service account file.
///////////////////////////////////////////////////////////////////////////////
// [START auth]
const credentials = require(keyFilePath);
/**
* Create authenticated HTTP client using a service account file.
*/
auth() {
this.credentials = require(this.keyFilePath);
const httpClient = new GoogleAuth({
credentials: credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer'
});
this.httpClient = new GoogleAuth({
credentials: this.credentials,
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer',
});
}
// [END auth]
///////////////////////////////////////////////////////////////////////////////
// Create a class via the API (this can also be done in the business console).
///////////////////////////////////////////////////////////////////////////////
// [START class]
const classUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/offerClass/';
const classPayload = {
"id": `${issuerId}.${classId}`,
"issuerName": "test issuer name",
"provider": "test provider",
"reviewStatus": "underReview",
"title": "test title",
"redemptionChannel": "online"
};
/**
* Create a class via the API. This can also be done in the Google Pay and
* Wallet console.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*
* @returns {string} The pass class ID: `${issuerId}.${classSuffix}`
*/
async createOfferClass(issuerId, classSuffix) {
const offerClassUrl = `${this.baseUrl}/offerClass`;
let classResponse = await httpClient.request({
url: classUrl,
method: 'POST',
data: classPayload
});
console.log('class POST response: ', classResponse);
// [END class]
///////////////////////////////////////////////////////////////////////////////
// Get or create an object via the API.
///////////////////////////////////////////////////////////////////////////////
// [START object]
const objectUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/offerObject/';
const objectPayload = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"type": "qrCode",
"value": "Testing Offers QR Code"
},
"state": "active",
"validTimeInterval": {
"kind": "walletobjects#timeInterval",
"start": {
"date": "2023-06-12T23:20:50.52Z"
},
"end": {
"date": "2023-12-12T23:20:50.52Z"
}
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
};
let objectResponse;
try {
objectResponse = await httpClient.request({
url: objectUrl + objectId,
method: 'GET'
});
} catch (err) {
if (err.response && err.response.status === 404) {
// Object does not yet exist
// Send POST request to create it
objectResponse = await httpClient.request({
url: objectUrl,
method: 'POST',
data: objectPayload
});
} else {
objectResponse = err;
}
}
console.log('object GET or POST response:', objectResponse);
// [END object]
///////////////////////////////////////////////////////////////////////////////
// Create a JWT for the object, and encode it to create a 'Save' URL.
///////////////////////////////////////////////////////////////////////////////
// [START jwt]
const claims = {
iss: credentials.client_email,
aud: 'google',
origins: ['www.example.com'],
typ: 'savetowallet',
payload: {
offerObjects: [{
id: objectId
}],
}
};
const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;
console.log(saveUrl);
// [END jwt]
///////////////////////////////////////////////////////////////////////////////
// Create a new Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START createIssuer]
// New issuer name
const issuerName = 'name';
// New issuer email address
const issuerEmail = 'email-address';
// Issuer API endpoint
const issuerUrl = 'https://walletobjects.googleapis.com/walletobjects/v1/issuer';
// New issuer information
let issuerPayload = {
name: issuerName,
contactInfo: {
email: issuerEmail
}
};
let issuerResponse = await httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuerPayload
});
console.log('issuer POST response:', issuerResponse);
// [END createIssuer]
///////////////////////////////////////////////////////////////////////////////
// Update permissions for an existing Google Wallet issuer account
///////////////////////////////////////////////////////////////////////////////
// [START updatePermissions]
// Permissions API endpoint
permissionsUrl = `https://walletobjects.googleapis.com/walletobjects/v1/permissions/${issuerId}`;
// New issuer permissions information
permissionsPayload = {
issuerId: issuerId,
permissions: [
// Copy as needed for each email address that will need access
{
emailAddress: 'email-address',
role: 'READER | WRITER | OWNER'
}
]
};
let permissionsResponse = await httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: permissionsPayload
});
console.log('permissions PUT response:', permissionsResponse);
// [END updatePermissions]
///////////////////////////////////////////////////////////////////////////////
// Batch create Google Wallet objects from an existing class
///////////////////////////////////////////////////////////////////////////////
//[START batch]
// The request body will be a multiline string
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let batchObject;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\\w.-]', '_');
// Generate an object ID with the user ID
objectId = `${issuerId}.${userId}-${classId}`;
batchObject = {
"id": objectId,
"classId": `${issuerId}.${classId}`,
"heroImage": {
"sourceUri": {
"uri": "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg",
"description": "Test heroImage description"
}
},
"textModulesData": [
{
"header": "Test text module header",
"body": "Test text module body"
}
],
"linksModuleData": {
"uris": [
{
"kind": "walletobjects#uri",
"uri": "http://maps.google.com/",
"description": "Test link module uri description"
},
{
"kind": "walletobjects#uri",
"uri": "tel:6505555555",
"description": "Test link module tel description"
}
]
},
"imageModulesData": [
{
"mainImage": {
"kind": "walletobjects#image",
"sourceUri": {
"kind": "walletobjects#uri",
"uri": "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg",
"description": "Test image module description"
}
}
}
],
"barcode": {
"type": "qrCode",
"value": "Testing Offers QR Code"
},
"state": "active",
"validTimeInterval": {
"kind": "walletobjects#timeInterval",
"start": {
"date": "2023-06-12T23:20:50.52Z"
},
"end": {
"date": "2023-12-12T23:20:50.52Z"
}
},
"locations": [
{
"kind": "walletobjects#latLongPoint",
"latitude": 37.424015499999996,
"longitude": -122.09259560000001
}
]
// 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',
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/offerObject/\n\n';
let response = await this.httpClient.request({
url: offerClassUrl,
method: 'POST',
data: offerClass,
});
data += JSON.stringify(batchObject) + '\n\n';
console.log('Class insert response');
console.log(response);
return response.data.id;
}
data += '--batch_createobjectbatch--';
// [END class]
// Invoke the batch API calls
let batchResponse = await httpClient.request({
url: 'https://walletobjects.googleapis.com/batch',
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
// [START object]
/**
* 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);
}
}
});
}
// [END object]
console.log('batch POST response:', batchResponse);
// [START jwt]
/**
* 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}`;
}
// [END jwt]
// [START createIssuer]
/**
* Create a new Google Wallet issuer account.
*
* @param {string} issuerName The issuer's name.
* @param {string} issuerEmail The issuer's email address.
*/
async createIssuerAccount(issuerName, issuerEmail) {
// Issuer API endpoint
const issuerUrl = `${this.baseUrl}/issuer`;
// New issuer information
let issuer = {
name: issuerName,
contactInfo: {
email: issuerEmail,
},
};
let response = await this.httpClient.request({
url: issuerUrl,
method: 'POST',
data: issuer
});
console.log('Issuer insert response');
console.log(response);
}
// [END createIssuer]
// [START updatePermissions]
/**
* Update permissions for an existing Google Wallet issuer account.
* **Warning:** This operation overwrites all existing permissions!
*
* Example permissions list argument below. Copy the dict entry as
* needed for each email address that will need access. Supported
* values for role are: 'READER', 'WRITER', and 'OWNER'
*
* let permissions = [
* {
* 'emailAddress': 'email-address',
* 'role': 'OWNER',
* },
* ];
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {Array} permissions The list of email addresses and roles to assign.
*/
async updateIssuerPermissions(issuerId, permissions) {
// Permissions API endpoint
const permissionsUrl = `${this.baseUrl}/permissions/${issuerId}`;
let response = await this.httpClient.request({
url: permissionsUrl,
method: 'PUT',
data: {
issuerId: issuerId,
permissions: permissions,
}
});
console.log('Permissions update response');
console.log(response);
}
// [END updatePermissions]
// [START batch]
/**
* Batch create Google Wallet objects from an existing class.
*
* @param {string} issuerId The issuer ID being used for this request.
* @param {string} classSuffix Developer-defined unique ID for this pass class.
*/
async batchCreateOfferObjects(issuerId, classSuffix) {
// See below for more information
// https://cloud.google.com/compute/docs/api/how-tos/batch#example
let data = '';
let offerObject;
let userId;
let objectId;
// Example: Generate three new pass objects
for (let i = 0; i < 3; i++) {
// Generate a random user ID
userId = uuidv4().replace('[^\w.-]', '_');
// Generate an object ID with the user ID
// Should only include alphanumeric characters, '.', '_', or '-'
objectId = `${issuerId}.${userId}`;
// See link below for more information on required properties
// https://developers.google.com/wallet/retail/offers/rest/v1/offerobject
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',
},
},
};
data += '--batch_createobjectbatch\n';
data += 'Content-Type: application/json\n\n';
data += 'POST /walletobjects/v1/offerObject\n\n';
data += JSON.stringify(offerObject) + '\n\n';
}
data += '--batch_createobjectbatch--';
// Invoke the batch API calls
let response = await this.httpClient.request({
url: `${this.baseUrl}/batch`,
method: 'POST',
data: data,
headers: {
// `boundary` is the delimiter between API calls in the batch request
'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
}
});
console.log('Batch insert response');
console.log(response);
}
// [END batch]
};
}
module.exports = { DemoOffer };

File diff suppressed because it is too large Load Diff