Implementation

Sequence Diagram

Sequence Diagram.

Requirements

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

Main Interface

Initialize

To begin Issuer app should initialize MeaPushProvisioning instance.

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

Card Data Parameters

Push provisioning flow is started by initializing MppCardDataParameters object with CardId, CardSecret or EncryptedPan. After initializing card data parameters push provisioning can be started with 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

Guide for encryptedCardData generation can be found in Card Data Encryption manual.

Loading Configuration

By default MPP SDK is loading mea_config configuration file, however a custom name can be used. When defining a custom name for the configuration file use MeaPushProvisioning.Configuration.loadConfig(String) to load the configuration. Configuration file should be placed in application resources res/raw folder.

MeaPushProvisioning.Configuration.loadConfig("custom_config_name");

ℹ️

INFO

MPP SDK throws MppException if configuration file is missing, broken or otherwise fails to load.

GooglePay Interface

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 you can not open link with yours specific one, then just try to add ?authuser=1 OR &authuser=1 to end of link. You may change 1 to any index of your account. Example: https://developers.google.com/pay/issuers/apis/push-provisioning/android/whitelisting?authuser=3

Push Card

When MPP SDK is initialized, Issuer app can push provision card using MeaPushProvisioning.GooglePay.pushCard(...) and MppCardDataParameters. This method checks the state of Google Pay wallet and creates a new instance when necessary. Backend generated opaque payment card (OPC) is received, and if the specific card is not added to Google Pay wallet already, it pushes the card to the 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 Activity.onActivityResult(int, int, Intent) method. Application should forward these intermediate results to the MeaPushProvisioning.GooglePay.handlePushCardOnActivityResult(int, int, Intent, Activity) method. Final result of push provisioning is returned returned to 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 {
        // Optionaly 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" 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

App is responsible to check and decide if "Add to Google Pay" should be shown or hidden for the user. Button should be shown only when card is not added to Google Pay already.
MPP SDK provides the following methods to check if the specific card is already added to Google Pay wallet:

  • MeaPushProvisioning.GooglePay.checkWalletForCardSuffix(String cardSuffix, GooglePayRegisteredTokensListener listener) - using card suffix, does not require network connection.
  • MeaPushProvisioning.GooglePay.checkWalletForToken(MppPaymentNetwork paymentNetwork, String tokenId, GooglePayTokenListener listener) - using pre-fetched token data, does not 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 Google Pay wallet, these methods return a single item or a list of GooglePayTokenInfo items that contains information about the specific Google Pay token, use getTokenId(), getTokenState(), getPaymentNetwork() and isSelectedAsDefault() methods to get the values.
Issuer app should hide "Add to Google Pay" button when card exists in Google Pay wallet. If methods above return null, Issuer app should show "Add to Google Pay" button.

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

Managing Default NFC Payment App

Getting Registered Tokens from Google Pay Wallet

There might be some use cases when Issuer app needs to get all registered tokens from Google Pay wallet, for example, yellow path use case. Method MeaPushProvisioning.GooglePay.getRegisteredTokens(...) returns 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 how to find 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) {
        ...
    }
});

Continuing Yellow Path Through Push Provisioning

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

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

In general GooglePayTokenInfo of the card can be fetched using MeaPushProvisioning.GooglePay.checkWalletForCardToken(...), however for some issuers or processors backend only provides active tokens and does not provide pending tokens that have entered yellow path. See sections below for each of the cases.

Pending Tokens Available via Backend

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

Show "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 Google Pay wallet. Iterate all the TokenInfo entities and match the selected card based on the last four digits. Once a matching token is found, check the state of the token. If token state matches TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION, card push provisioning has entered yellow path and requires additional user authentication.

Show "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 token(s) 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 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 an issuer app to re-query information about their digitized cards whenever the user makes a change in Google Pay wallet.
MPP SDK will immediately call an 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 the data changed events. Therefore, each application should update the 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 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 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