I'm trying to integrate and use Google Wallet (API and Android SDK) for generating a Generic Pass with Smart Tap enabled, read it using another mobile phone and decrypt the payload.
PRE:
I can't read my custom passes, or, to be precise, I don't receive the correct payload.
My current passes are in "Demo Mode", because I want to be sure they work before ask for publishing. The google documentation is not clear wether or not this is a blocker or what to do in these case. I tried to upload the pskt (3) public key (with a different version) in my wallet console, but still doesn't work. The collector ID is the one I have in my google wallet console (converted to byte) and my issuerID has been added to redemptionIssuers. it seems that smartTapRedemptionValue is not written in the tag, but if I query wallet API it is there:
{
"cardTitle": {
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "it",
"value": "$TITLE"
},
"kind": "walletobjects#localizedString"
},
"classId": "$ISSUER_ID.$CLASS_NAME",
"genericType": "genericTypeUnspecified",
"hasUsers": true,
"header": {
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "it",
"value": "$HEADER"
},
"kind": "walletobjects#localizedString"
},
"hexBackgroundColor": "#ffffff",
"id": "$ISSUER_ID.newPassObject3",
"smartTapRedemptionValue": "if_you_read_this_it's_great!",
"state": "active",
"subheader": {
"defaultValue": {
"kind": "walletobjects#translatedString",
"language": "it",
"value": "$SUBHEADER"
},
"kind": "walletobjects#localizedString"
}
}
If anyone has any clue, thanks for the help!
I found what was the issue, and it wasn't an issue of the pass.
TL:DR -> Sample app is partial and full of missing pieces.
Digging in documentation and various reverse engineering, I found a 'Service Type' code usage, that was hardcoded in the app: https://github.com/google-pay/smart-tap-sample-app/blob/192d1760bd8f44e8142dda6611c2a1314b35595b/app/src/main/java/com/google/smarttapsample/GetDataCommand.java#L37
Here is set to Loyalty Cards (0x03). If you want to make it work with generic passes, you have to put Generic (0x12).
In addition, there is an update to GetDataResponse method getDecryptedPayload
:
https://github.com/google-pay/smart-tap-sample-app/blob/192d1760bd8f44e8142dda6611c2a1314b35595b/app/src/main/java/com/google/smarttapsample/GetDataResponse.java#L258
You should add an "else if" for managing generic cards:
// Iterate over service NDEF records
for (NdefRecord serviceRecord : serviceNdefRecord.getRecords()) {
// Check for `ly` type.
if (Arrays.equals(serviceRecord.getType(), new byte[]{(byte) 0x6c, (byte) 0x79})) {
//.... processLoyaltyServiceRecord(serviceRecord);
} else if (Arrays.equals(serviceRecord.getType(), new byte[]{(byte) 103, (byte) 114})) {
processGenericServiceRecord(serviceRecord);
}
}
private void processGenericServiceRecord(NdefRecord serviceRecord) throws FormatException {
//in case of general pass `gr` type
// Get the generic record payload
NdefMessage genericRecordPayload = new NdefMessage(serviceRecord.getPayload());
for (NdefRecord generic : genericRecordPayload.getRecords()) {
// Check for `n` ID = 6e
if (Arrays.equals(generic.getId(), new byte[]{(byte) 0x6e})) {
// Get the Smart Tap redemption value
decryptedSmartTapRedemptionValue = new String(Arrays.copyOfRange(generic.getPayload(), 1, generic.getPayload().length));
}
}
}
And now reading the pass works correctly!