flutterwalletgoogle-wallet

Problems integrating an Add to Google Wallet button with Flutter


I am using the flutter_google_wallet plugin to add an Add to Google Wallet button to my app. In the process I've made several mistakes that I wasn't able to figure out without contacting Google's developer support for Google Pay and Wallet console.

Some of the error message I dealt with during the process were:

I did not find solutions to these issues online, so I'll document them in an answer to this "question".


Solution

  • My first attempt at using the plugin resulted in getting an error displayed that said: "Something went wrong. Please try again" and in the adb log I saw an error which said "Validate RPC failed". Support indicated that I needed to get my app added to their "allow-list" by sending them my Issuer ID, App package name, and SHA1 fingerprint. After sending Google this info, it was a couple days before they let me know that I could proceed.

    The documentation for the flutter_google_wallet shows that you pass a json string to its savePasses method but it doesn't show an example of that string, so you have to dig around the google documentation for that format. Initially, I was just sending the "payload" value without wrapping that in an element that included the iss, aud, typ, iat, and origins properties.

    The next error I made was in copying my issuerId from the Google Pay Console. Don't confuse your alpha-numeric merchant ID for your issuerId. The issuerId should be only digits such as: 3388000000012345678. You'll find this at the center of the Google Pay & Wallet console page near the center top after it says Google Wallet API Issuer ID:. This value gets used in two places in your json. The first is as a prefix to your object's "id" value. The second place is as a prefix to the object's "classId" value.

    I was specifying the "iat" value in the wrong format. It should be a numeric "unix_time" (seconds since epoch) value.

    Another error I ran into was that one of the images I referenced had been moved. If you are using a heroImage property, double check and make sure the URI is valid.

    The final mistake I realized I had made was that I was trying to use a "genericObjects" payload with a classId that was an "Event Ticket" type. Make sure to use "eventTicketObjects" json with "Event Ticket" type classes and "genericObjects" with "Generic" type classes!

    Although eventually, I'll create this json on a server, I'm currently mocking that out and creating it within my Flutter app using the code below:

        // "email address" that google created for your "service account" which you can 
        // find on https://console.cloud.google.com/apis/credentials
        const issuerEmail = 'google-wallet-my-proj@company-proj.iam.gserviceaccount.com';
    
        // update this with the issuerId found on pay.google.com/business/console
        const issuerId = '3388000000012345678';
    
        // name of Google Wallet class you created on pay.google.com/business/console
        const className = 'GeneralAdmission';
        final passId = const Uuid().v4(); // random uuid
    
        final iat = DateTime.now().millisecondsSinceEpoch ~/ 1000;
        final json = '''
    {
        "iss": "$issuerEmail",
        "aud": "google",
        "typ": "savetowallet",
        "iat": "$iat",
        "origins": [],
        "payload": {
            "genericObjects": [{
                "id": "$issuerId.$passId",
                "classId": "$issuerId.$className",
                "genericType": "GENERIC_TYPE_UNSPECIFIED",
                "cardTitle": {
                    "defaultValue": {
                        "language": "en",
                        "value": "Card title"
                    }
                },
                "header": {
                    "defaultValue": {
                        "language": "en",
                        "value": "header"
                    }
                },
                "subheader": {
                    "defaultValue": {
                        "language": "en",
                        "value": "subheader"
                    }
                }
            }]
        }
    }
    ''';