I have followed all the steps for creating a service account, giving permissions etc. and I have this code trying to implement skinny jwt flow in node.js :
const serviceAccountInfo = {
"type": "service_account",
"project_id": "XXXX",
"private_key_id": "XXXXX",
"private_key": "XXXXX",
"client_email": "[email protected]",
"client_id": "XXX",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": ""
}
// Issuer ID obtained from Google Pay Business Console.
const issuerId = XXXXXXXXXX...
// Developer defined ID for the wallet class, this is the 'test-eventTicket-class-id'
const classUid = "EVENTTICKET_CLASS_4ba7141a-9ac7-4a11-b252-7e2f85ac7953"
const classId = `${issuerId}.${classUid}`
// Developer defined ID for the user, eg an email address: '[email protected]'
const userId = "[email protected]"
// ID for the wallet object, must be in the form `issuerId.userId` where userId is alphanumeric.
const objectId = `${issuerId}.${userId.replace(/[^\w.-]/g, "_")}-${classUid}`
const google_api_base_url= "https://walletobjects.googleapis.com/walletobjects/v1"
//skinny jwt flow
//1.create EventClass (json representation)
const logoUri ="https://www.crowdpolicy.com/wp-content/uploads/2020/01/kit-cp-icon-black.png"
let eventTicketClass
try {
eventTicketClass = createEventClass(logoUri, issuerId)
} catch ( err ) {
console.log("event Ticket Class error: ", err)
}
console.log("event Ticket Class created: ", eventTicketClass)
//2.post request to insert EventClass in the Google Pay API for Passes server
//const credentials = require(serviceAccountFile)
let insertClassResult
try{
const httpClient = new GoogleAuth({
credentials: serviceAccountInfo,
scopes: "https://www.googleapis.com/auth/wallet_object.issuer"
})
insertClassResult = await httpClient.request({url: google_api_base_url+"/eventTicketClass", method: 'POST', data: eventTicketClass})
console.log("insertClassResult: ", insertClassResult)
} catch ( err ) {
console.log("insertClassResult error: ", err)
res.send({
success: false,
error: err
})
return
}
//this event id is unique random str -64 characters- for every pass event e.g. cityon pass, so that the grouping of the passes of the same event will be stable
const eventId =
"kLNIQ02OHDjhbu3xkNp8USxl6nOLSqv5HOLanEtK72I+9Gs9tdAZzFekY4G8DQ=="
function createEventClass(logoUri, issuerId) {
//the function creates and returns EventClass (json representation)
const eventName = {
translatedValues: [
{
kind: "walletobjects#eventTicketClass",
language: "en-US",
value: "RESIDENT CARD",
},
],
defaultValue: {
kind: "walletobjects#eventTicketClass",
language: "el-GR",
value: "ΚΑΡΤΑ ΔΗΜΟΤΗ",
},
}
const logo = {
sourceUri: {
uri: logoUri,
},
contentDescription: {
translatedValues: [
{
kind: "walletobjects#eventTicketClass",
language: "en-US",
value: "Cityon Pass Logo",
},
],
defaultValue: {
kind: "walletobjects#eventTicketClass",
language: "el-GR",
value: "Cityon Pass Logo",
},
},
}
const objectUid = "EVENTTICKET_OBJECT_" + uuidv4()
const objectId = issuerId + "." + objectUid
const localizedIssuerName = {
translatedValues: [
{
language: "en-US",
value: "Crowdpolicy",
},
],
defaultValue: {
language: "el-GR",
value: "Crowdpolicy",
},
}
const eventTicketClass = {
eventName: eventName,
eventId: eventId,
logo: logo,
id: objectId,
issuerName: "Crowdpolicy",
reviewStatus: "UNDER_REVIEW", //"APPROVED" will be when class is sent
review: {
comments: "This is a comment!",
},
countryCode: "en-US",
enableSmartTap: false,
hexBackgroundColor: "#03A5B9",
localizedIssuerName: localizedIssuerName,
multipleDevicesAndHoldersAllowedStatus: "ONE_USER_ALL_DEVICES",
}
return eventTicketClass
}
But I am always getting this error :
insertClassResult error: GaxiosError: Permission denied. at Gaxios._request (/usr/src/app/node_modules/gaxios/build/src/gaxios.js:130:23) at processTicksAndRejections (internal/process/task_queues.js:95:5) at async JWT.requestAsync (/usr/src/app/node_modules/google-auth-library/build/src/auth/oauth2client.js:368:18) at async exports.generateGpayPass (/usr/src/app/src/controllers/generateGpayPass.js:96:29) at async exports.getUserInfo (/usr/src/app/src/controllers/getUserInfo.js:438:25) { response: { config: { url: 'https://walletobjects.googleapis.com/walletobjects/v1/eventTicketClass', method: 'POST', data: [Object], headers: [Object], paramsSerializer: [Function: paramsSerializer], body: '{"eventName":{"translatedValues":[{"kind":"walletobjects#eventTicketClass","language":"en-US","value":"RESIDENT CARD"}],"defaultValue":{"kind":"walletobjects#eventTicketClass","language":"el-GR","value":"ΚΑΡΤΑ ΔΗΜΟΤΗ"}},"eventId":"kLNIQ02OHDjhbu3xkNp8USxl6nOLSqv5HOLanEtK72I+9Gs9tdAZzFekY4G8DQ==","logo":{"sourceUri":{"uri":"https://www.crowdpolicy.com/wp-content/uploads/2020/01/kit-cp-icon-black.png"},"contentDescription":{"translatedValues":[{"kind":"walletobjects#eventTicketClass","language":"en-US","value":"Cityon Pass Logo"}],"defaultValue":{"kind":"walletobjects#eventTicketClass","language":"el-GR","value":"Cityon Pass Logo"}}},"id":"3388000000022115000.EVENTTICKET_OBJECT_0dd423dd-ccfd-4af9-b15a-87684bdf1916","issuerName":"Crowdpolicy","reviewStatus":"UNDER_REVIEW","review":{"comments":"This is a comment!"},"countryCode":"en-US","enableSmartTap":false,"hexBackgroundColor":"#03A5B9","localizedIssuerName":{"translatedValues":[{"language":"en-US","value":"Crowdpolicy"}],"defaultValue":{"language":"el-GR","value":"Crowdpolicy"}},"multipleDevicesAndHoldersAllowedStatus":"ONE_USER_ALL_DEVICES"}', validateStatus: [Function: validateStatus], responseType: 'json' }, data: { error: [Object] }, headers: { 'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"', 'cache-control': 'private', connection: 'close', 'content-encoding': 'gzip', 'content-type': 'application/json; charset=UTF-8', date: 'Tue, 09 Aug 2022 14:11:44 GMT', server: 'ESF', 'transfer-encoding': 'chunked', vary: 'Origin, X-Origin, Referer', 'x-content-type-options': 'nosniff', 'x-frame-options': 'SAMEORIGIN', 'x-xss-protection': '0' }, status: 403, statusText: 'Forbidden', request: { responseURL: 'https://walletobjects.googleapis.com/walletobjects/v1/eventTicketClass' } }, config: { url: 'https://walletobjects.googleapis.com/walletobjects/v1/eventTicketClass', method: 'POST', data: { eventName: [Object], eventId: 'kLNIQ02OHDjhbu3xkNp8USxl6nOLSqv5HOLanEtK72I+9Gs9tdAZzFekY4G8DQ==', logo: [Object], id: '3388000000022115000.EVENTTICKET_OBJECT_0dd423dd-ccfd-4af9-b15a-87684bdf1916', issuerName: 'Crowdpolicy', reviewStatus: 'UNDER_REVIEW', review: [Object], countryCode: 'en-US', enableSmartTap: false, hexBackgroundColor: '#03A5B9', localizedIssuerName: [Object], multipleDevicesAndHoldersAllowedStatus: 'ONE_USER_ALL_DEVICES' }, headers: { Authorization: 'Bearer ya29.c.b0AXv0zTPfam2a9PzXQsnXtbr0rUqlmM3lYY8vuY2CBxtnTwjhyphJRaCfvgFu_SGaB3TEpQrhRap_fc_GOOYxaocgr-DYhdYRdQSPRISSksxDF0PUFlLACh3MhHwt7Iy9yy6QxvaHcz6_YKk0iDst0buF23A3vRj1tEurcd4Flbu6LEl6HPMBgEZHWbbNPAhMpvyMX_NqF4eq7-tQE0BiCVUWJAMYL0w........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................', 'User-Agent': 'google-api-nodejs-client/8.1.0', 'x-goog-api-client': 'gl-node/14.20.0 auth/8.1.0', 'Content-Type': 'application/json', Accept: 'application/json' }, paramsSerializer: [Function: paramsSerializer], body: '{"eventName":{"translatedValues":[{"kind":"walletobjects#eventTicketClass","language":"en-US","value":"RESIDENT CARD"}],"defaultValue":{"kind":"walletobjects#eventTicketClass","language":"el-GR","value":"ΚΑΡΤΑ ΔΗΜΟΤΗ"}},"eventId":"kLNIQ02OHDjhbu3xkNp8USxl6nOLSqv5HOLanEtK72I+9Gs9tdAZzFekY4G8DQ==","logo":{"sourceUri":{"uri":"https://www.crowdpolicy.com/wp-content/uploads/2020/01/kit-cp-icon-black.png"},"contentDescription":{"translatedValues":[{"kind":"walletobjects#eventTicketClass","language":"en-US","value":"Cityon Pass Logo"}],"defaultValue":{"kind":"walletobjects#eventTicketClass","language":"el-GR","value":"Cityon Pass Logo"}}},"id":"3388000000022115000.EVENTTICKET_OBJECT_0dd423dd-ccfd-4af9-b15a-87684bdf1916","issuerName":"Crowdpolicy","reviewStatus":"UNDER_REVIEW","review":{"comments":"This is a comment!"},"countryCode":"en-US","enableSmartTap":false,"hexBackgroundColor":"#03A5B9","localizedIssuerName":{"translatedValues":[{"language":"en-US","value":"Crowdpolicy"}],"defaultValue":{"language":"el-GR","value":"Crowdpolicy"}},"multipleDevicesAndHoldersAllowedStatus":"ONE_USER_ALL_DEVICES"}', validateStatus: [Function: validateStatus], responseType: 'json' }, code: 403, errors: [ { message: 'Permission denied.', domain: 'walletobjects', reason: 'permissionDenied' } ] }