mirror of
https://github.com/google-wallet/rest-samples.git
synced 2026-01-10 13:58:14 -05:00
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:
73
nodejs/README.md
Normal file
73
nodejs/README.md
Normal 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');
|
||||
```
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user