Skip to main content

Enabling Cards for Mobile Wallet

All cards (physical and virtual) can be configured for Mobile Wallet. Mobile Wallet enablement allows you to pay using your phone, via Apple, Google, or Samsung Pay. This setup can take around six weeks and requires some design and legal work.

Setup Process

  1. Notify your Customer Success Manager that you would like your cards to have Mobile Wallet functionality
  2. Send Mobile Wallet Metadata to Unit
    1. Mobile Wallet Card Art: The simplest way to do this is using the same card art you use for your physical card design. If you choose this route, you do not need to provide us with any new card art.
    2. If you choose to, you can design a new graphic for the Mobile Wallet cards. If you do this, please send a 1536x969px .png file in landscape orientation. Do not include the chip, cardholder name, PAN, expiration date, or rounded edges. Please include a white or Visa-blue Visa logo.
    3. Color Codes: The RGB color codes used in your card design [ex. White (255, 255, 255)]
    4. App Icon: A 100x100px .png file of your app icon, with the color going all the way to the edges (no rounded corners)
    5. Card Label: What would you like the card to be called when it’s added to the wallet?
  3. Sign or View Apple Mobile Wallet Terms and Conditions
  4. Unit sends T&C’s for you to sign. If you are issuing cards to businesses, you don’t need to sign, only view these terms. We get these T&C's directly from Apple and they are standard for all consumer BINs. There are no T&C's for Google or Samsung Pay, but we will set these up for you.
  5. Complete your Visa Cardholder Agreement
  6. Visa requires all customers to agree to these terms before adding a card to Mobile Wallet
  7. Publish your Privacy Policy and Terms and Conditions on your website
  8. Visa uses these in the setup of your Mobile Wallet and customers can access them via Mobile Wallet by clicking “Additional Information” on their cards
  9. Your Customer Success Manager will inform you when your cards are ready to add to wallet

Add to Mobile Wallet

Adding a card to a Mobile Wallet (e.g. Apple Pay, Google Pay) without the "Add to Mobile Wallet" integration requires scanning a physical card with a device camera or manually typing a card number into the Wallet. By implementing the "Add to Mobile Wallet" flow described below, your end customers can add their card to a digital wallet with one tap, from within your mobile app.

Benefits

Allowing your end customers to add their card to a digital wallet in seconds from within your app, immediately after creating a debit card (or any point thereafter), contributes to a decrease time-to-first-use and increases the potential interchange revenue by making it easier to use the card.

Customer Experience

  1. Within your mobile app, you'll add a button that will allow your end customer to add their card to their relevant mobile wallet.
  2. The customer will review and agree to terms and conditions, and then immediately be able to use the card from within the wallet.

Pre-requisites

  1. You’ll need to have an executed NDA with Unit.

  2. The functionality is only available for mobile applications (native iOS/Android, or using a technology like React native or Flutter)

  3. If your cards have not been previously enabled for mobile wallet support, there are a few preliminary steps Unit can help you with.

    1. Your BINs will need to be configured to allow tokenization. If this is not already complete, contact your Customer Success Manager. Setting up your BIN for tokenization can take up to 4 weeks.
    2. You’ll need to provide mobile wallet card art (see the Card Issuance Guide for more information).
  4. Your BINs will need to also be installed and configured for the mobile wallet SDK with Visa. Contact your Customer Success Manager to ensure this is configured. Setting up your BIN for SDK use takes 10-20 business days and must be complete before testing can begin.

Getting Started

info

Contact your Customer Success Manager to begin implementation on Add to Mobile Wallet functionality, including getting your BINs configured for the mobile wallet SDK with Visa.

Compliance

  • Deposit Agreement: If your cards already support mobile wallets, no additional language needs to be incorporated.
  • End Customer: Unit and Visa will ensure the correct terms and conditions are presented to and consented by your end customer before adding the card to their digital wallet.
  • Partner Agreements: You may need to sign agreements with each digital wallet provider, see Tech Integration details below.

Tech Integration

  • End Customer User Interface

    • Your user interface will need to instruct an end customer to add their debit card to a mobile wallet, according to their device type (iOS or Android).
    • The correct terms for each wallet will be presented to and consented by your end customer.
  • Backend

    • Review Unit’s API documentation for Add to Mobile Wallet.
    • Contact Unit for Visa's In-App Provisioning SDK binaries, App ID
  • Sample Apps

    • Unit provides three sample apps: Android, iOS and React Native
    • Sample apps cannot be used with a simulator
    • Available upon request
  • Testing: Once Unit has confirmed your BIN is configured for SDK use at Visa,

    • Visa requires testing in production.
    • The SDK imposes a limit of 7 provisioning attempts per device in a 24 hour period. Contact your Customer Success Manager if you encounter an issue.
    • iOS apps should be distributed using TestFlight, no simulators.

iOS

Apple Pay Prerequirements

Request Apple Pay In-app provisioning entitlement

  1. For each production instance of the app that will be used, get the Mobile App Name, Team ID, and Adam ID.

  2. The Mobile App Name and the Adam ID can be found in the App Store Connect after selecting one of your apps:

  3. The Team ID can be found in the Apple developer account under "Membership" section:

  4. Email applepayentitlements@apple.com to request Apple Pay in-app provisioning entitlement.

    1. In the email, send Mobile App name, Team ID, and Adam ID for each production instance of the app.
    2. Once entitlement is granted by Apple, include the entitlement into provisioning profile

Setup your App in Apple Developer Portal once entitlement is granted by Apple

Add the in-app provisioning entitlement
  1. Sign in to your Apple developer account.
  2. Navigate to 'Certificates, Identifier & Profiles'.
  3. Select "Distribution" underneath the "Profiles" heading on the sidebar.
  4. Select the distribution iOS provisioning profile that will be used to deploy the app to the iTunes App Store.
  5. Select "Apple Pay In-App Provisioning Distribution" entitlement from the drop down to add the entitlement to the profile.
  6. Once the entitlement is added, follow the standard process to release the app into the App Store.
Add Wallet Capability
  1. Sign in to your Apple developer account.
  2. Navigate to 'Certificates, Identifier & Profiles'.
  3. Select "App IDs" underneath the "Identifiers" heading on the sidebar.
  4. Select the app identifier that will be used to deploy the app to the iTunes App Store.
  5. Under "Capabilities" tab, select the "Wallet" capability:

Integration steps

This section describes the technical steps needed to integrate with Visa's SDK and add a Unit card to the mobile Wallet.

Xcode setup verification

Wallet Capability

  1. Go to "Signing & Capabilities" tab of your target.
  2. Verify that Wallet capability is there.

In-App Provisioning entitlement

  1. Open your project .entitlement file in Xcode.
  2. Add the Boolean key com.apple.developer.payment-pass-provisioning is set to 1 (true).

Add Visa SDK to your Xcode project

  1. Drag and drop the following frameworks into the XCode project:

    • VisaPushProvisioning.xcframework
    • VisaInAppModuleCore.xcframework
    • VisaAnalytics.xcframework
    • VisaFeatureModuleCore.xcframework
    • VisaMobileFoundation.xcframework
    • RLTMXProfiling.xcframework
    • RLTMXProfilingConnections.xcframework
  2. Select "Copy items if needed" (if not already selected) and click on "Finish".

  3. Make sure "Embed & Sign" is selected for all the above XCFrameworks.

Visa SDK Configuration

Inside the application didFinishLaunchingWithOptions method in the AppDelegate.swift, configure VisaInAppCore as follows:

// Setup Visa SDK Config
let visaInAppConfig = VisaInAppConfig(environment: .Production, appId: "b8186bd9-fcba-b77a-11e3-1c0d2e5a3501")
do {
try VisaInAppCore.configure(config: visaInAppConfig)
} catch {
// handle error
}

Setup the environment (.Production or .Sandbox).

Add to Wallet flow

The following diagram demonstrates the Add to Wallet flow. Below the diagram, you will find an explanation for each step in the flow.

Step 1 - Hold a strong reference to VisaPushProvisioningInterface
var vpInterface: VisaPushProvisioningInterface?
Step 2 - Initialize the VisaPushProvisioningInterface instance and implement the VisaPushProvisioningListener

Initialize the VisaPushProvisioningInterface instance in the init() method of your class:

vpInterface = VisaPushProvisioningInterfaceFactory.createPushProvisioningInterface(listener: self)

Declare the VisaPushProvisioningListener methods, we will implement them later:

func initializationSuccess(pushProvisioningInterface: VisaPushProvisioningInterface, response: VPInitResponse) { }
func initializationFailure(pushProvisioningInterface: VisaPushProvisioningInterface, error: VPError) { }

func supportedWalletSuccess(pushProvisioningInterface: VisaPushProvisioningInterface, response: VPSupportedWalletResponse) { }
func supportedWalletFailure(pushProvisioningInterface: VisaPushProvisioningInterface, error: VPError) { }

func cardProvisioningSuccess(pushProvisioningInterface: VisaPushProvisioningInterface, response: VPCardProvisioningResponse) { }
func cardProvisioningFailure(pushProvisioningInterface: VisaPushProvisioningInterface, error: VPError) { }
Step 3 - Initialize Visa SDK and get the signedNonce

Call to initialize() method

self.vpInterface?.initialize()

Handle initialization callbacks of the VisaPushProvisioningListener

func initializationSuccess(pushProvisioningInterface: VisaPushProvisioningInterface, response: VPInitResponse) {
// Get the signedNonce
let signedNonce = response.signedNonce
}

func initializationFailure (pushProvisioningInterface: VisaPushProvisioningInterface, error: VPError) {
// handle error
}
Step 4 - Get wallet payload from Unit API

Get the mobile wallet encrypted payload for a specified Unit card from Unit API. Read more details in Unit get mobile wallet payload docs.

Step 5 - Get supported wallets

Call to getSupportedWallets(request: VPSupportedWalletRequest) method using the payload from step 4

let request = VPSupportedWalletRequest(encPayload: payload)
self.vpInterface?.getSupportedWallets(request: request)

Handle getSupportedWallets callbacks of the VisaPushProvisioningListener

func supportedWalletSuccess(pushProvisioningInterface: VisaPushProvisioningInterface, response: VPSupportedWalletResponse) {
let wallet = response.wallets[0]
}

func supportedWalletFailure(pushProvisioningInterface: VisaPushProvisioningInterface, error: VPError) {
// handle error
}
Step 6 - Start card provisioning

Call to startCardProvisioning(request: VPSupportedWalletRequest, initialView: UIViewController) method

// use a supported wallet to initialize the VPCardProvisioningRequest
let request = VPCardProvisioningRequest(walletCode: wallet.code, walletName: wallet.name)

// call to startCardProvisioning() with the request and a UIViewController to show the Apple Pay screen on
// if your current class is a UIViewController, you can pass 'self'
self.vpInterface?.startCardProvisioning(request: request, initialView: self)

Handle startCardProvisioning callbacks of the VisaPushProvisioningListener

func cardProvisioningSuccess(pushProvisioningInterface: VisaPushProvisioningInterface, response: VPCardProvisioningResponse) {
// update wallet status for Wallet
// provisioning flow completed, you can update the UI
}

func cardProvisioningFailure(pushProvisioningInterface: VisaPushProvisioningInterface, error: VPError) {
// handle error
}

iOS - Add to Wallet Sample App

Steps Before running the sample app

  1. Open the UnitAddToWalletSampleApp.xcworkspace project located in UnitAddToWalletSampleApp folder.
  2. Open UNConstants.swift file.
  3. Set the customerToken variable with your Unit customer token:
static let customerToken: String? = YOUR_CUSTOMER_TOKEN
  1. Set the cardId variable with your Unit's cardId:
static let cardId: String? = YOUR_CARD_ID
  1. In UnitAddToWalletSampleApp folder, run pod install in the command line.

Apple In-App Provisioning Entitlement

Important: In order to fully demonstrate the add to wallet flow, you should set your app bundle identifier that granted access of In-App provisioning from apple. If you didn't grant it yet, you can follow the following steps to simulate the flow on a simulator:

Setup to run on a simulator

  1. Open UnitAddToWalletSampleApp.entitlements file.
  2. Add the com.apple.developer.payment-pass-provisioning entitlement to the dictionary.
  3. Set the added entitlement type to Boolean and set its value to 1.

For example:

<dict>
<key>com.apple.developer.payment-pass-provisioning</key>
<true/>
...
</dict>

Running the sample app

  • After running the sample app, your customer card should appear on screen.
  • Tap on the "Add To Apple Wallet" button.
  • See the logs printed in your screen for more details.

Android

If you elect to support Google Pay push provisioning, the following represents some of the steps that are required by Google.

Google Pay Prerequirements

  1. Create an account in the Google Developer Portal, if one does not already exist, and make sure you have access to this link. if you don't have access, then you should request access.
  2. Ensure that you (and any mobile app providers/developers) have executed any required CTA agreements, as well as the Google NDA, via the Google Developer Portal
  3. Verify that your client BIN(s) are token eligible and set up to support the Google Pay wallet type (Unit can assist with this step)

Google's step-by-step instructions for apps that intend to use Google Pay

Steps that are required before the start of the Visa Digital Enablement SDK integration

Step 1: UX/branding review

  • Optional: You may download Google's sample app to further understand the branding guidelines

  • Review and adhere to Google's branding and user experience requirements

    • Option 1: Use Unit's common Visa presentation, located in the provided zip directory under unit-common-README.md.

    • Option 2: Set the assets independently:

  • Create a slide deck of images with one screen per page of the full user journey of your app starting from the login screen. Here's an example of an app that follows the guidelines: image

  • Complete the user experience branding review with Google (watch the requirements and then fill this form) with the following information:

  • image

You should receive a response to your request within 2 business day.

Step 2: Push Provisioning API access

Get your app's SHA256 fingerprint
  • Press on the Gradle icon on the top right of your screen image
  • Type 'signingReport' and press enter image
  • Copy your SHA-256

Complete this form with the following information:

image

image

image

image

  • Enter your Bank Partner's name (not Unit)

You should receive a response to your request within 2 business day.

After Step 2 you should move to the Visa SDK integration steps. When you are done with the integration, complete the following steps

Folow these steps after app development (when ready to begin testing)

Step 3: Completion of Mobile App review

If you are using the Unit's common Visa presentation you need to get the wallets every time your app goes to the background, and then display the view according to the wallet's status (see step 4 of unit-common-README.md - located in the provided zip directory). You will need to do it for the button to always be updated according to the current Google Wallet status.

Step 4: Completion of Mobile App field testing

Request launch approval from Google

Once approved by Google, follow the standard process to release the app to the Google Play Store.

Steps to support Samsung Pay

If you elect to support Samsung Pay push provisioning, the following represents some of the steps that are required by Samsung.

Steps that are required before the start of the Visa Digital Enablement SDK integration

  1. Ensure that you have executed any required Samsung agreements

  2. Collect the 'TSP Issuer Name(s)' which are registered with Samsung

  3. Verify that your client BIN(s) are token eligible and set up to support the Samsung Pay wallet type (Unit can assist with this step)

  4. Create an account in the Samsung Developer Portal, if one does not already exist:

  5. Review all detailed tasks required for push provisioning outlined in the Samsung Developer Portal

  6. Create a New App in the Samsung Developer Portal, following their guidelines

  7. Create a New Service in the Samsung Developer Portal, following their guidelines

  8. Link the New Service with the App created in Samsung Developer Portal

  9. Copy and save the Service ID, which is used by mobile app during push provisioning

  10. Review and adhere to Samsung's branding and user experience requirements

After these steps you should move to the Visa SDK integration steps. When you are done with the integration, complete the following steps

Folow these steps after app development (when ready to begin testing)

Complete all required steps for the activation of push provisioning as outlined in the Samsung Developer Portal, which may include the following:

  1. Whitelist and release the new Mobile App a. Navigate to Projects > App Management > App b. Add New Release Version, upload the APK file and Register
  2. Submit the release for approval in the Samsung Developer Portal Once approved by Samsung, follow the standard process to release the app to the Google Play Store.

Integration Steps

This section describes the technical steps needed to integrate with Visa's SDK and add a Unit card to the mobile Wallet.

Prerequirements

Before you start with the technical integration, please follow the prerequirements below:

  • The minimum Android Operating System (OS) validated for use with the VDE SDK is 9.0 (Pie).
  • Due to the usage of Google's libraries and U.S. government regulations, the Android VDE SDK cannot function on certain Huawei devices.
  • The minimum supported Android SDK API version is 28.
  • The deployment target is Android SDK API version is 32.
  • The minimum supported Kotlin version is 1.7.0.
  • The minimum supported Java version is 11.
  • The SDK requires to use a physical Android device and not a virtual device.
  • Make sure you have the relevant wallets to which you will add cards installed on your device updated with the newest version.

Add VDE (Visa Digital Enablement) SDK to your project

  1. Get the push provisioning package from Unit, which consists of the following dependencies:

    • VisaPushProvisioning-3.6.2.aar
    • VisaInAppCore-3.6.2.aar
    • TMXProfiling-6.3-81.aar
    • TMXProfilingConnections-6.3-81.aar
    • samsungpaysdk-2.6.00.jar
    • play-services-tapandpay-18.1.0.aar
  2. Add the dependencies from the previous step under the libs folder of your project:

    • In Android Studio switch your folder structure from Android to Project.

    image

    • Now search for the libs folder inside the app folder.

    image

    • paste the dependencies inside the libs folder

    • In case the libs folder doesn't exist you can create it:

      • Make sure the folder structure is Project (it was explained above)
      • Right click on your app name in the directory tree and go to "New" => "Directory" and name it "libs"
  3. Gradle Dependencies:

    a. Declare the following Gradle dependencies. Make sure to add the open source dependencies mentioned:

//Only if App is developed in kotlin and using kotlin-plugins for development then below can be
//used for kotlin coroutines dependencies as well, no need to add separate dependency.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
}

dependencies {
//In-App Provisioning SDK
implementation files('libs/VisaPushProvisioning-3.6.2.aar')
implementation files('libs/VisaInAppCore-3.6.2.aar')
implementation files('libs/TMXProfiling-6.3-81.aar')
implementation files('libs/TMXProfilingConnections-6.3-81.aar')
//Samsung Pay
implementation files('libs/samsungpaysdk-2.6.00.jar')
//Google Pay
implementation files('libs/play-services-tapandpay-18.1.0.aar')
implementation "com.google.android.gms:play-services-base:18.0.0"
//okhttp covers for okttp, okio dependency
implementation "com.squareup.okhttp3:okhttp:4.7.2"
implementation "com.squareup.okhttp3:logging-interceptor:4.7.2"
implementation "net.minidev:json-smart:2.4.11"
//For TrustDefender
implementation "commons-io:commons-io:2.13.0"
implementation "commons-codec:commons-codec:1.16.0"
//Nimbus
implementation "com.nimbusds:nimbus-jose-jwt:8.2.1"
implementation "com.google.code.gson:gson:2.8.5"
implementation "androidx.lifecycle:lifecycle-process:2.5.1"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
//Analytics Database
implementation "androidx.room:room-runtime:2.4.3"
implementation "androidx.room:room-ktx:2.4.3"
kapt "androidx.room:room-compiler:2.4.3"
//Instant apps
implementation "com.google.android.gms:play-services-instantapps:18.0.0"
//Play Integrity
implementation "com.google.android.play:integrity:1.0.1"

//If kotlin plugins are not added then below dependencies require to add to kotlin coroutines
//used by SDK.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$any-latest-version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$any-latest-version"
}
  1. Additional instructions for Samsung Pay

    a. If the target device includes Android 11 and above, add the below in the app's AndroidManifest.xml file. If this is not added, the VDE SDK may return a 'NOT_INSTALLED' status.

<queries>
<package android:name="com.samsung.android.spay" />
</queries>

b. Add the below in AndroidManifest.xml file, if VDE SDK has returned a 'NOT_AVAILABLE' status.

<meta-data android:name="spay_sdk_api_level" android:value="2.6" />
<meta-data android:name="debug_mode" android:value="N" />

Configure VDE SDK in your custom application file

Import the following libraries:

import com.visa.mobileEnablement.inAppCore.*
import com.visa.mobileEnablement.pushProvisioning.*
import androidx.lifecycle.ProcessLifecycleOwner

inside your init() configure VisaInAppCore as follows:

val visaInAppConfig = VisaInAppConfig(VisaInAppEnvironment.Production, "b8186bd9-fcba-b77a-11e3-1c0d2e5a3501")
VisaInAppCore.configure(context, visaInAppConfig)
//recognizes lifecycles changes
ProcessLifecycleOwner.get().lifecycle.addObserver(VisaInAppCoreApplicationObserver())

Setup the environment (.Production or .Sandbox).

Add to Wallet flow

Overview

The following diagram demonstrates the Add to Wallet flow. Below the diagram, you will find an explanation for each step in the flow.

Unit_Android_Add to_wallet_flow_diagram

Step 1 - Hold a reference to VisaPushProvisioningInterface

var pushProvisioningInterface: VisaPushProvisioningInterface? = null

Step 2 - Initialize the VisaPushProvisioningInterface instance and implement the VisaPushProvisioningListener interface in your class

Initialize the VisaPushProvisioningInterface instance in the init() method of your class:

//'this' is the object that implements VisaPushProvisioningListener
pushProvisioningInterface = VisaPushProvisioningInterfaceFactory.createPushProvisioningInterface(this)

Declare the VisaPushProvisioningListener methods, we will implement them later:

override fun initializationSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPInitResponse
) { }

override fun initializationFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) { }

override fun cardProvisioningSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPCardProvisioningResponse
) { }

override fun cardProvisioningFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) { }

override fun supportedWalletSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPSupportedWalletResponse
) { }

override fun supportedWalletFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) { }

Step 3 - Initialize Visa SDK and get the signedNonce

Call to initialize() method

pushProvisioningInterface?.initialize()

Handle initialization callbacks of the VisaPushProvisioningListener

override fun initializationSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPInitResponse
) {
val signedNonce = response.signedNonce
}

override fun initializationFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
//handle error
}

Step 4 - Get wallet payload from Unit API

Get the mobile wallet encrypted payload for a specified Unit card from Unit API. Read more details in Unit get mobile wallet payload docs.

Step 5 - Get supported wallets

Call to getSupportedWallets(request: VPSupportedWalletRequest) method using the payload from step 4

val vpSupportedWalletRequest = VPSupportedWalletRequest(payload)
pushProvisioningInterface?.getSupportedWallets(vpSupportedWalletRequest)

Handle getSupportedWallets callbacks of the VisaPushProvisioningListener

override fun supportedWalletSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPSupportedWalletResponse
) {
val wallet = response.wallets
}

override fun supportedWalletFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
// handle error
}

Step 6 - Start card provisioning

Call to startCardProvisioning(context: Context, request: VPCardProvisioningRequest) method:

val vpCardProvisioningRequest = VPCardProvisioningRequest(wallet.code, wallet.name)
pushProvisioningInterface?.startCardProvisioning(context, vpCardProvisioningRequest)

Handle startCardProvisioning callbacks of the VisaPushProvisioningListener

override fun cardProvisioningSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPCardProvisioningResponse
) {
// update wallet status for Wallet
// provisioning flow completed, you can update the UI}
}

override fun cardProvisioningFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
// handle error
}
  1. Execute the necessary agreements. The below are required, even if you already support Google Pay for manual card provisioning, to establish a relationship with you as a mobile app provider.

    1. Push Provisioning API Access Request

      1. Select "BIN Sponsor or Program Manager working on behalf of a financial company or card issuer
      2. Enter your Bank Partner's name (not Unit)
      3. Answer "Does the financial company or card issuer have a signed CTA agreement with Google?" with Yes
      4. Network will be Visa
    2. Google NDA

  2. Verify that your BIN(s) are token eligible and set up to support Google Pay. Unit can assist with this step.

  3. Create an account in the Google Developer Portal, if you do not already have one

  4. Review all detailed tasks required for push provisioning outlined in the Google Developer Portal

  5. Follow Google’s step-by-step instructions for apps that intend to use Google Pay.

    1. Review and adhere to Google’s branding and user experience requirements
    2. Complete the UX/branding review with Google
  6. When you’re ready to release to production, continue with Google’s step-by-step instructions

    1. Completion of App whitelisting
    2. Completion of App review
    3. Completion of App field testing
  7. Request launch approval from Google

  8. Once approved by Google, follow the standard process to release the app into the Google Play Store. gReport' and press enter

Android - Add to Wallet Sample App

This sample app demonstrates how to integrate with Visa's SDK in your app.

Steps Before running the sample app

  1. Open the project inside UnitWalletDemo folder in the Android Studio.
  2. Open UNConstants file.
  3. Set the customerToken variable with your Unit customer token:
val customerToken: String? = YOUR_CUSTOMER_TOKEN
  1. Set the cardId variable with your Unit's card id:
val cardId: String? = YOUR_CARD_ID
  1. Set the appId variable with your Visa's app id:
val appId: String? = YOUR_VISA_APP_ID

Running the sample app

  • After running the sample app, your customer card should appear on screen.
  • Tap on "Add To G Pay" button.
  • See the logs printed in your screen for more details.

React Native

Prerequirements

  • To support Apple Pay first complete the Prerequirements section in Steps to support Apple Pay. Then carry out the Steps to integrate with the Visa SDK.

  • To support Google Pay first complete step 1 and step 2 at the Steps to support Google Pay. After successfully completing the Steps to integrate with the Visa SDK, move on to stages 3 and 4.

Steps to integrate with the Visa SDK

iOS

Follow the following steps in iOS Xcode setup verification:

  1. Xcode setup verification.
  2. Add Visa SDK to your Xcode project.

Android

Follow the following steps in Android integration steps:

  1. Prerequirements.
  2. Add VDE (Visa Digital Enablement) SDK to your project.

To make a Kotlin module, additional Gradle dependencies are required. The file android/build.gradle has to contain the following additional lines:

buildscript {
ext {
kotlin_version = “[version]” //(ex:1.6.0)
}
}
dependencies {
classpath(“org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version”)
}

Visa SDK Configuration Module

Please note this configuration should be made only once in an application integrating with the VDE SDK. If the mobile app configures VisaInAppConfig more than once, nothing will happen if it is called with the same parameters, otherwise the VDE SDK will produce an exception as VisaInAppConfigException and the configs will not be updated.

iOS

Export a Swift module

Create a .swift file named VdeSdkConfigurationModule:

Agree to create a Bridging Header:

In the Bridge file:

#import <React/RCTBridgeModule.h>

In the .swift file:

//
// VdeSdkConfigurationModule.swift
//

import Foundation
import VisaInAppModuleCore

@objc(VdeSdkConfigurationModule)
class VdeSdkConfigurationModule: NSObject {
var configureSdkPromise: (resolve: RCTPromiseResolveBlock?, reject: RCTPromiseRejectBlock?)

@objc static func requiresMainQueueSetup() -> Bool {
return false
}

@objc
func configureSdk(
_ appId: String, environment: String, resolve: @escaping RCTPromiseResolveBlock,
rejecter: @escaping RCTPromiseRejectBlock
) {
configureSdkPromise.resolve = resolve
configureSdkPromise.reject = rejecter
let visaInAppConfig = VisaInAppConfig(
environment: getEnvironment(environment: environment),
appId: appId)
do {
try VisaInAppCore.configure(config: visaInAppConfig)
configureSdkPromise.resolve?(["Success"])
} catch {
configureSdkPromise.reject?("configureSdk_error", "Failed to configure sdk", nil)
print(error)
}
}

// Get proper environment constant
func getEnvironment(environment: String) -> VisaInAppEnvironment {
if environment.lowercased().contains("prod") {
return VisaInAppEnvironment.Production
} else {
return VisaInAppEnvironment.Sandbox
}
}
}

Create an objective-c file named VdeSdkConfigurationModule (the same name as .swift file)

  • An empty file

To expose our swift methods to RN add:

//
// VdeSdkConfigurationModule.m
//

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(VdeSdkConfigurationModule, NSObject)
RCT_EXTERN_METHOD(configureSdk: (NSString *)appId
environment: (NSString *)appId
resolve: (RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)

@end

Android

Android Module Creation

The module integrating the VDE SDK Configuration methods is implemented as a native class inside the android folder, under the directory tree corresponding to the project name and package. As an example: android/app/src/main/java/com/your-app-name/VDEConfigurationModule.kotlin

Create a kotlin class named VdeSdkConfigurationModule. In this kotlin class:

class VdeSdkConfigurationModule (reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

private lateinit var visaInAppConfig: VisaInAppConfig

@ReactMethod
fun configureSdk(appId: String = "b8186bd9-fcba-b77a-11e3-1c0d2e5a3501",
environment: String = "Production",
configureSdkPromise: Promise
){
visaInAppConfig = VisaInAppConfig(getEnvironment(environment), appId)
VisaInAppCore.configure(reactApplicationContext.applicationContext, visaInAppConfig)
UiThreadUtil.runOnUiThread {
// recognizes when the application is in the background or foreground.
ProcessLifecycleOwner.get()
.lifecycle
.addObserver(VisaInAppCoreApplicationObserver())
}
configureSdkPromise.resolve("Success")
}

private fun getEnvironment(env: String): VisaInAppEnvironment {
if (env.lowercase(Locale.ROOT).contains("prod")) {
return VisaInAppEnvironment.Production
}
return VisaInAppEnvironment.Sandbox
}

override fun getName(): String {
return "VdeSdkConfigurationModule"
}
}

Android Module Registration

Once a native module is written, it needs to be registered with React Native. To do so, the native module is added to a ReactPackage which is then registered with React Native. React Native invokes the method createNativeModules() on a ReactPackage in order to get the list of native modules to register. For Android, if a module is not instantiated and returned in createNativeModules, it will not be available from JavaScript.

To add the Native Module to ReactPackage, create a new Class named MyAppPackage that implements ReactPackage inside the android/app/src/main/java/com/your-app-name/ folder, and add the following code:


import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager

class MyAppPackage : ReactPackage {

override fun createViewManagers(
reactContext: ReactApplicationContext
): MutableList<ViewManager<View, ReactShadowNode<*>>> = mutableListOf()

override fun createNativeModules(
reactContext: ReactApplicationContext
): MutableList<NativeModule> {
var modules: ArrayList<NativeModule> = ArrayList()
modules.add(VdeSdkConfigurationModule(reactContext))
return modules
}
}

Once the module is imported with this code, it needs to be registered to the list of packages returned in ReactNativeHost's getPackages() method.

In android/app/src/main/java/com/your-app-name/MainApplication.java, locate ReactNativeHost’s getPackages() method and add your package to the packages list getPackages() returns:

  @Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new MyAppPackage());

return packages;
}

The module is now successfully registered.

Module access from Javascript

In order to access your native module from JavaScript you need to first import NativeModules from React Native:

import { NativeModules } from 'react-native';

You can then access the VdeSdkConfigurationModule native module off of NativeModules:

const { VdeSdkConfigurationModule } = NativeModules;

Now that you have the VdeSdkConfigurationModule available, you can invoke your native method configureSdk:

const configureSdk = async () => {
try {
const response = await VdeSdkConfigurationModule.configureSdk(AppId, "production");
} catch (error) {
console.error(error);
}
};

Before you try to access the native modules, make sure you rebuild the iOS/Android project.

Visa SDK Push Provisioning Module

iOS

Export a Swift module

Create a .swift file named PushProvisioningModule

  • In the .swift file:
//
// PushProvisioningModule.swift
//

import Foundation
import UIKit
import VisaPushProvisioning

@objc(PushProvisioningModule)
class PushProvisioningModule: NSObject, VisaPushProvisioningListener {

var provisioningInterface: VisaPushProvisioningInterface?
var APPLE_WALLET_CODE = "ApplePayPushProvision"

var launchInitializePromise: (resolve: RCTPromiseResolveBlock?, reject: RCTPromiseRejectBlock?)
var launchGetWalletsPromise: (resolve: RCTPromiseResolveBlock?, reject: RCTPromiseRejectBlock?)
var launchStartCardProvisioningPromise:
(resolve: RCTPromiseResolveBlock?, reject: RCTPromiseRejectBlock?)

@objc
static func requiresMainQueueSetup() -> Bool {
return false
}

// SDK Initialize
@objc
func launchInitialize(
_ resolve: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock
) {
launchInitializePromise.resolve = resolve
launchInitializePromise.reject = rejecter
provisioningInterface =
VisaPushProvisioningInterfaceFactory.createPushProvisioningInterface(listener: self)
provisioningInterface?.initialize()
}

// Get Eligible Wallets
@objc
func launchGetWallets(
_ params: String, resolve: @escaping RCTPromiseResolveBlock,
rejecter: @escaping RCTPromiseRejectBlock
) {
launchGetWalletsPromise.resolve = resolve
launchGetWalletsPromise.reject = rejecter
// convert JSON param
if let data = params.data(using: .utf8) {
do {
if let args = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let encPayload = args["encPayload"] as? String
{
let request = VPSupportedWalletRequest(encPayload: encPayload)
provisioningInterface?.getSupportedWallets(request: request)
}
} catch {
print(error.localizedDescription)
}
}
}

// Start Card Provisioning
@objc
func launchStartCardProvisioning(
_ params: String, resolve: @escaping RCTPromiseResolveBlock,
rejecter: @escaping RCTPromiseRejectBlock
) {
launchStartCardProvisioningPromise.resolve = resolve
launchStartCardProvisioningPromise.reject = rejecter
// convert JSON param
if let data = params.data(using: .utf8) {
do {
if let args = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let walletCode = args["walletCode"] as? String,
let walletName = args["walletName"] as? String
{
let code =
walletCode == APPLE_WALLET_CODE
? VPSupportedWalletCode.ApplePayPushProvision : VPSupportedWalletCode.VCEHPushProvision
let request = VPCardProvisioningRequest(walletCode: code, walletName: walletName)
guard let window = UIApplication.shared.delegate?.window as? UIWindow,
let rootViewController = window.rootViewController
else {
return
}
provisioningInterface?.startCardProvisioning(
request: request,
initialView: rootViewController)
}
} catch {
print(error.localizedDescription)
}
}
}

// Success callback after SDK initialize method call
func initializationSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response:
VPInitResponse
) {

launchInitializePromise.resolve?([response.toJsonString() as Any])
}
// Failure callback after SDK initialize method call
func initializationFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error:
VPError
) {
launchInitializePromise.reject?("initializationFailure", error.toJsonString(), nil)
}

// Success callback after SDK getSupportedWallets method call
func supportedWalletSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPSupportedWalletResponse
) {
launchGetWalletsPromise.resolve?([response.toJsonString() as Any])
}
// Failure callback after SDK getSupportedWallets method call
func supportedWalletFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error:
VPError
) {
launchGetWalletsPromise.reject?("supportedWalletFailure", error.toJsonString(), nil)
}

// Success callback after SDK startCardProvisioning method call
func cardProvisioningSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPCardProvisioningResponse
) {
launchStartCardProvisioningPromise.resolve?([response.toJsonString() as Any])
}
// Failure callback after SDK startCardProvisioning method call
func cardProvisioningFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error:
VPError
) {
launchStartCardProvisioningPromise.reject?("cardProvisioningFailure", error.toJsonString(), nil)
}
}

Create an objective-c file named PushProvisioningModule (the same name as .swift file)

  • An empty file

In the Objective-c file:

  • To expose our swift methods to RN add:
//
// PushProvisioningModule.m
//

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(PushProvisioningModule, NSObject)
// SDK Initialize
RCT_EXTERN_METHOD(launchInitialize: (RCTPromiseResolveBlock)resolve
rejecter: (RCTPromiseRejectBlock)reject)

// Get Eligible Wallets
RCT_EXTERN_METHOD(launchGetWallets: (NSString *)params
resolve: (RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)

// Start Card Provisioning
RCT_EXTERN_METHOD(launchStartCardProvisioning: (NSString *)params
resolve: (RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
@end

Android

Android Module Creation

Create a kotlin class named PushProvisioningModule. In this kotlin class:

For Module declaration:
  • Hold a reference to VisaPushProvisioningInterface.
  • Prepare the SDK initialize, getSupportedWallets and startCardProvisioning, with result assignments:
class PushProvisioningModule (reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext),
VisaPushProvisioningListener {

private lateinit var visaPushProvisioningInterface: VisaPushProvisioningInterface
private lateinit var launchInitializePromise: Promise
private lateinit var launchGetWalletsPromise: Promise
private lateinit var launchStartCardProvisioningPromise: Promise
private lateinit var gson: Gson

override fun getName(): String {
return "PushProvisioningModule"
}

@ReactMethod
fun launchInitialize(promise: Promise) {
gson = Gson()
this.launchInitializePromise = promise
visaPushProvisioningInterface =
VisaPushProvisioningInterfaceFactory.createPushProvisioningInterface(
this)
visaPushProvisioningInterface.initialize()
}

@ReactMethod
fun launchGetWallets(params: String, promise: Promise){
this.launchGetWalletsPromise = promise
visaPushProvisioningInterface.getSupportedWallets(gson.fromJson(params,
VPSupportedWalletRequest::class.java))
}

@ReactMethod
fun launchStartCardProvisioning(params: String, promise: Promise){
this.launchStartCardProvisioningPromise = promise
visaPushProvisioningInterface.startCardProvisioning(reactApplicationContext.applicationContext,
gson.fromJson(params, VPCardProvisioningRequest::class.java))
}

override fun initializationFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
this.launchInitializePromise.reject("initializationFailure", gson.toJson(error));

}

override fun initializationSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPInitResponse
) {
Log.d("PushProvisioningModule", "initialization success, got signedNonce")
this.launchInitializePromise.resolve(gson.toJson(response));
}

override fun supportedWalletFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
Log.d("PushProvisioningModule", "initialization failed")
this.launchGetWalletsPromise.reject("supportedWalletFailure", gson.toJson(error));
}

override fun supportedWalletSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPSupportedWalletResponse
) {
this.launchGetWalletsPromise.resolve(gson.toJson(response));
}

override fun cardProvisioningFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
this.launchStartCardProvisioningPromise.reject("cardProvisioningFailure", gson.toJson(error));

}

override fun cardProvisioningSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPCardProvisioningResponse
) {
this.launchStartCardProvisioningPromise.resolve(gson.toJson(response));
}
}
Android Module Registration

Add the next line to the MyAppPackage class, just as you did for the VdeSdkConfigurationModule:

modules.add(PushProvisioningModule(reactContext))

Module access from Javascript

In order to access your native module from JavaScript you need to first import NativeModules from React Native:

import { NativeModules } from 'react-native';

You can then access the PushProvisioningModule native module off of NativeModules:

const { PushProvisioningModule } = NativeModules;

Now that you have the PushProvisioningModule available, you can invoke your native methods:

// To call the module method to use SDK method initialize
const launchInitialize = async () => {
try {
// To call the module method to use SDK method initialize
const response = await PushProvisioningModule.launchInitialize();
} catch (error) {
// Manage errors
}
};
// To call the module method to use SDK method getSupportedWallets
const launchGetWallets = async () => {
try {
// To call the module method to use SDK method initialize
const response = await PushProvisioningModule.launchGetWallets(
JSON.stringify({
encPayload: encryptedPayload
});
}
catch (error) {
// Manage errors
}
}
};
// To call the module method to use SDK method startCardProvisioning
const launchStartCardProvisioning = async () => {
try {
// To call the module method to use SDK method initialize
const response = await PushProvisioningModule.launchStartCardProvisioning(
JSON.stringify({
walletCode: walletCode,
walletName: walletDescription
});
}
catch (error) {
// Manage errors
}
}
};

React Native - Add to Wallet Sample App

This sample app demonstrates how to integrate with Visa's SDK in your app.

Steps Before running the sample app

  1. Open the UnitWalletDemo project with your code editor.
  2. Open the constants file under src/shared.
  3. Set the UNIT_CONSTANTS variable with your Unit's card id and customer token:
export const UNIT_CONSTANTS = {
CARD_ID: <YOUR_CARD_ID>,
CUSTOMER_TOKEN: <YOUR_CUSTOMER_TOKEN>
}
  1. Set the VISA_CONSTANTS variable with your Visa's App id:
export const VISA_CONSTANTS = {
APP_ID: <YOUR_VISA_APP_ID>
}

Additional steps only for iOS:

Running the sample app

  1. Start Metro

Using npm:

npm start

Using yarn:

yarn start
  1. Start your application by running it directly from within Xcode/Android Studio.
  • After running the sample app, you should see a button that will launch the push provisioning flow once it is clicked.
  • See the logs printed in your terminal for more details.