Implementation

Sequence Diagram

Sequence Diagram.

Requirements

Before starting implementation, you should have completed the Installation of the MPP (Mea Push Provisioning) SDK.

Main Interface

Initialize

To begin, your issuer app should initialize the MeaPushProvisioning instance.

if (!MeaPushProvisioning.isInitialized()) {
    MeaPushProvisioning.initialize(this);
}

Card Data Parameters

Start the push provisioning flow by initializing an MppCardDataParameters object with CardId, CardSecret, or EncryptedPan. After initializing card data parameters, you can start push provisioning with the push card method.

CardId & CardSecret

String cardId = "<value>";
String cardSecret = "<value>";

MppCardDataParameters cardParams = MppCardDataParameters.withCardSecret(cardId, cardSecret);

EncryptedCardData

String encryptedCardData = "<value>";
String publicKeyFingerprint = "<value>";
String encryptedKey = "<value>";
String initialVector = "<value>";

MppCardDataParameters cardParams = MppCardDataParameters.withEncryptedPan(encryptedCardData,
                                                                          publicKeyFingerprint,
                                                                          encryptedKey,
                                                                          initialVector);

ℹ️

INFO

You can find the guide for encryptedCardData generation in the Card Data Encryption manual.

Loading Configuration

The MPP SDK loads the mea_config configuration file by default, but you can use a custom name. When defining a custom name for the configuration file, use MeaPushProvisioning.Configuration.loadConfig(String) to load the configuration. Place the configuration file in your application resources res/raw folder.

MeaPushProvisioning.Configuration.loadConfig("custom_config_name");

ℹ️

INFO

The MPP SDK throws MppException if the configuration file is missing, corrupted, or fails to load.

GooglePay Interface

The MeaPushProvisioning.GooglePay helper interface provides methods to interact with Google Pay wallet.

Google Pay Push Provisioning API documentation:

❇️ Useful Information (Only authorized Google accounts can view this content.)
If you have multiple Google accounts and can't open a link with your specific one, try adding ?authuser=1 OR &authuser=1 to the end of the link. You can change 1 to any index of your account. Example: https://developers.google.com/pay/issuers/apis/push-provisioning/android/whitelisting?authuser=3

Push Card

Once the MPP SDK is initialized, your issuer app can push provision a card using MeaPushProvisioning.GooglePay.pushCard(...) and MppCardDataParameters. This method checks the Google Pay wallet state and creates a new instance when necessary. A backend-generated opaque payment card (OPC) is received, and if the specific card isn't already added to Google Pay wallet, it pushes the card to Google Pay wallet via Google Push Provisioning API.

MeaPushProvisioning.GooglePay.pushCard(cardParams, "card display name", userAddress, getActivity(), new MppPushCardToGooglePayListener() {
    @Override
    public void onSuccess(String tokenReferenceId, String cardLastFourDigits, MppPaymentNetwork cardNetwork) {
        ...
    }

    @Override
    public void onFailure(MppError mppError, int tapAndPayStatusCode) {
        ...
    }
});

Handle Google Pay Result Callbacks

Google Pay push provisioning results are returned through the Activity.onActivityResult(int, int, Intent) method. Your application should forward these intermediate results to the MeaPushProvisioning.GooglePay.handlePushCardOnActivityResult(int, int, Intent, Activity) method. The final push provisioning result is returned to the MppPushCardToGooglePayListener callback.

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (MeaPushProvisioning.GooglePay.handleOnActivityResult(requestCode, resultCode, data, this)) {
        // Handled by MPP SDK, no action needed.
    } else {
        // Optionally handle results for other request codes.
    }
}

Add to Google Pay Button

The Add to Google Pay button is used exclusively to initiate the Google Pay card provisioning flow from an issuer app.
The "Add to Google Pay" buttons are available as resizable bitmaps (NinePatch files) suitable for including in your layout: Google Pay Brand guidelines - Assets

Note: Using Flutter, React Native, or another cross-platform app development framework? Check out Flutter-specific instructions here.

Add to Google Pay button.

Show or Hide Add to Google Pay Button

Your app determines whether to show or hide the "Add to Google Pay" button for users. Only show the button when the card hasn't been added to Google Pay yet.
The MPP SDK provides the following methods to check if a specific card is already added to Google Pay wallet:

  • MeaPushProvisioning.GooglePay.checkWalletForCardSuffix(String cardSuffix, GooglePayRegisteredTokensListener listener) - using card suffix, doesn't require network connection.
  • MeaPushProvisioning.GooglePay.checkWalletForToken(MppPaymentNetwork paymentNetwork, String tokenId, GooglePayTokenListener listener) - using pre-fetched token data, doesn't require network connection.
  • MeaPushProvisioning.GooglePay.checkWalletForCardToken(MppCardDataParameters cardData, GooglePayTokenListener listener) - using card data, requires network connection.

Example:

MppCardDataParameters cardParams = MppCardDataParameters.withCardSecret(cardId, cardSecret);

MeaPushProvisioning.GooglePay.checkWalletForCardToken(
        cardParams,
        new GooglePayTokenListener() {
            @Override
            public void onSuccess(@NonNull GooglePayTokenInfo googlePayTokenInfo) {
                ...
                String tokenId = googlePayTokenInfo.getTokenId();
                Boolean isTokenStateNeedIdentityVerification = googlePayTokenInfo.getTokenState() == GooglePayTokenState.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION;
            }

            @Override
            public void onFailure(@NonNull MppError mppError) {
                ...
            }
        }
);

If the card exists in the Google Pay wallet, these methods return a single item or a list of GooglePayTokenInfo items containing information about the specific Google Pay token. Use getTokenId(), getTokenState(), getPaymentNetwork(), and isSelectedAsDefault() methods to get the values.
Your issuer app should hide the "Add to Google Pay" button when the card exists in the Google Pay wallet. If the methods above return null, your issuer app should show the "Add to Google Pay" button.

Use GooglePayTokenInfo.getTokenState() to get additional information about the token state.

Managing Default NFC Payment App

Getting Registered Tokens from Google Pay Wallet

There might be use cases when your issuer app needs to get all registered tokens from the Google Pay wallet, for example during yellow path cases. The method MeaPushProvisioning.GooglePay.getRegisteredTokens(...) returns a list of TokenInfo items related to the active wallet.

MeaPushProvisioning.GooglePay.getRegisteredTokens(new GooglePayRegisteredTokensListener() {
    @Override
    public void onSuccess(List<TokenInfo> tokenList) {
        // Iterate list.
    }

    @Override
    public void onFailure(MppError error) {
        // Handle error.
    }
});

Example of how to find a specific card by last 4 digits:

String cardLastFourDigits = "1234";

MeaPushProvisioning.GooglePay.getRegisteredTokens(new GooglePayRegisteredTokensListener() {
    @Override
    public void onSuccess(List<TokenInfo> tokenList) {
        for (TokenInfo tokenInfo : tokenList) {
            // Find matching card based on the last 4 digits.
            if (cardLastFourDigits.equals(tokenInfo.getFpanLastFour())) {
                GooglePayTokenState tokenState = GooglePayTokenState.getTokenState(tokenInfo.getTokenState());

                MppPaymentNetwork paymentNetwork = MppPaymentNetwork.getPaymentNetwork(tokenInfo.getNetwork());
                GooglePayTokenInfo googlePayTokenInfo = new GooglePayTokenInfo(tokenInfo.getIssuerTokenId(), tokenState, paymentNetwork, tokenInfo.getIsDefaultToken());

                String tokenId = googlePayTokenInfo.getTokenId();
                Boolean isTokenStateNeedIdentityVerification = googlePayTokenInfo.getTokenState() == GooglePayTokenState.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION;
                break;
            }
        }
    }

    @Override
    public void onFailure(MppError error) {
        ...
    }
});

Handling The Yellow Path During Push Provisioning

Your issuer app should allow users to continue yellow path through push provisioning.

Your issuer app should get the token of the selected card and check the token state. If the token state matches the value TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION, card provisioning has entered yellow path. Once a matching GooglePayTokenInfo is available, use MeaPushProvisioning.GooglePay.tokenize(GooglePayTokenInfo, String, Activity, GooglePayTokenizeListener) to continue provisioning.

Generally, you can fetch the GooglePayTokenInfo of the card using MeaPushProvisioning.GooglePay.checkWalletForCardToken(...). However, for some issuers or processors, their backend only provides active tokens and doesn't provide pending tokens that have entered yellow path. See the sections below for each case.

Pending Tokens Available via Backend

After finding a card with MeaPushProvisioning.GooglePay.checkWalletForCardToken(MppCardDataParameters cardData, GooglePayTokenListener listener) in the Google Pay wallet, check the token state. If the token state is TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION, card push provisioning has entered the yellow path and requires additional user authentication.

Show the "Add to Google Pay" button and call MeaPushProvisioning.GooglePay.tokenize(GooglePayTokenInfo, String, Activity, GooglePayTokenizeListener) to continue push provisioning when the button is tapped.

MeaPushProvisioning.GooglePay.checkWalletForCardToken(cardDataParameters, new GooglePayTokenListener() {
    @Override
    public void onSuccess(@NonNull GooglePayTokenInfo googlePayTokenInfo) {
        if (googlePayTokenInfo.getTokenState() == GooglePayTokenState.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION) {
            // Card token requires additional user authentication for yellow path, show Add to Google Pay button.
            // When tapping button, call MeaPushProvisioning.GooglePay.tokenize(googlePayTokenInfo, ...).
        }
        else {
            // Token already exists in Google Pay wallet and no action required from Issuer app, hide Add to Google Pay button.
        }
    }

    @Override
    public void onFailure(@NonNull MppError mppError) {
        // Card token not found in Google Pay wallet, show Add to Google Pay button.
        // When tapping button, call MeaPushProvisioning.GooglePay.pushCard(...).
    }
});

Pending Tokens Not Available via Backend

Use MeaPushProvisioning.GooglePay.getRegisteredTokens(...) to get all tokens provisioned in the Google Pay wallet. Iterate through all the TokenInfo entities and match the selected card based on the last four digits. Once you find a matching token, check the token state. If the token state is TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION, card push provisioning has entered yellow path and requires additional user authentication.

Show the "Add to Google Pay" button and call MeaPushProvisioning.GooglePay.tokenize(GooglePayTokenInfo, String, Activity, GooglePayTokenizeListener) to continue push provisioning when the button is tapped.

MeaPushProvisioning.GooglePay.getRegisteredTokens(new GooglePayRegisteredTokensListener() {
    @Override
    public void onSuccess(@NonNull List<TokenInfo> tokenList) {

        for (TokenInfo tokenInfo : tokenList) {

            // Find matching card based on the last four digits.
            if (CARD_LAST_FOUR_DIGITS.equals(tokenInfo.getFpanLastFour())) {
                GooglePayTokenState tokenState = GooglePayTokenState.getTokenState(tokenInfo.getTokenState());

                if (tokenState == GooglePayTokenState.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION) {
                    // Card token requires additional user authentication for yellow path, show Add to Google Pay button.
                    // When tapping button, call MeaPushProvisioning.GooglePay.tokenize(googlePayTokenInfo, ...).

                    MppPaymentNetwork paymentNetwork = MppPaymentNetwork.getPaymentNetwork(tokenInfo.getNetwork());
                    GooglePayTokenInfo googlePayTokenInfo = new GooglePayTokenInfo(tokenInfo.getIssuerTokenId(), tokenState, paymentNetwork, tokenInfo.getIsDefaultToken());
                }
                else {
                    // Hide Add to Google Pay button.
                }

                break;
            }
        }
    }

    @Override
    public void onFailure(@NonNull MppError error) {
        // Handle error.
    }
});

Alternatively, use MeaPushProvisioning.GooglePay.checkWalletForCardSuffix(cardSuffix, listener) to get tokens with matching PAN suffix.

MeaPushProvisioning.GooglePay.checkWalletForCardSuffix(CARD_LAST_FOUR_DIGITS, new GooglePayRegisteredTokensListener() {
    @Override
    public void onSuccess(@NonNull List<TokenInfo> tokenInfoList) {
        for (TokenInfo tokenInfo : tokenInfoList) {
            GooglePayTokenState tokenState = GooglePayTokenState.getTokenState(tokenInfo.getTokenState());
            if (tokenState == GooglePayTokenState.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION) {
                // Card token requires additional user authentication for yellow path, show Add to Google Pay button.
                // When tapping button, call MeaPushProvisioning.GooglePay.tokenize(googlePayTokenInfo, ...).

                MppPaymentNetwork paymentNetwork = MppPaymentNetwork.getPaymentNetwork(tokenInfo.getNetwork());
                GooglePayTokenInfo googlePayTokenInfo = new GooglePayTokenInfo(tokenInfo.getIssuerTokenId(), tokenState, paymentNetwork, tokenInfo.getIsDefaultToken());
            } else {
                // Hide Add to Google Pay button.
            }
        }
    }

    @Override
    public void onFailure(@NonNull MppError mppError) {
        // Handle error
    }
});

Using Tokenize

ℹ️

INFO

You should have implemented Handling Google Pay Result Callbacks.

// When Add to Google Pay button tapped AND card token requires additional user authentication for the yellow path.
MeaPushProvisioning.GooglePay.tokenize(googlePayTokenInfo, "card name", getActivity(), new GooglePayTokenizeListener() {
    @Override
    public void onSuccess() {
        ...
    }

    @Override
    public void onFailure(@NonNull MppError mppError) {
        ...
    }
});

App-to-App Verification with Activation Code

Handle Google Pay Data Changed Events

Registering for data changed events allows your issuer app to re-query information about digitized cards whenever the user makes a change in Google Pay wallet.
The MPP SDK will immediately call your issuer app callback whenever the following events occur:

  • The active wallet changes (by changing the active account)
  • The selected card of the active wallet changes
  • Tokenized cards are added or removed from the active wallet
  • The status of a token in the active wallet changes

ℹ️

INFO

GooglePayDataChangedListener
Only foreground applications are notified of data changed events. Therefore, each application should update token statuses not only by receiving GooglePayDataChangedListener, but also when the application launches or returns to the foreground.

Register listener:

GooglePayDataChangedListener listener = new GooglePayDataChangedListener() {
    @Override
    public void onDataChanged() {
        // Reload data in UI.
    }
};

MeaPushProvisioning.GooglePay.registerDataChangedListener(listener);

Stop listening for data changed events by removing GooglePayDataChangedListener:

MeaPushProvisioning.GooglePay.removeDataChangedListener(listener);

Testing in Sandbox Mode

By default, Google Pay works in production mode with real payments. During development and pre-production testing, you can reconfigure Google Pay to work in sandbox mode by placing a special file on your device. Once connected to sandbox, requests will be routed to Google's sandbox environment which connects to the TSP's sandbox environment.

Working in sandbox mode: Google Pay sandbox mode

Use the adb command to toggle sandbox mode. To turn sandbox mode on, add an empty file and reboot:

$ adb shell touch /sdcard/Download/android_pay_env_override_sandbox
$ adb reboot

To switch back to production mode, delete the file and reboot the device:

$ adb shell rm /sdcard/Download/android_pay_env_override_sandbox
$ adb reboot

References