Digital Wallet Tokens
Overview
Digital Wallet Tokens allow you to tokenize accounts within a third-party digital wallet, such as Google Pay, Apple Pay and Samsung Pay. Your customers can pay anywhere the digital wallet is accepted, such as in stores using NFC Tap and Pay.
Open Fabric provides a standalone SDK to enable push provisioning into digital wallets, please refer to our Getting Started guide to learn how to set up the SDK within your project.
In this guide we'll explain:
- How to request push provisioning access with Apple, Google or Samsung
- How to display the Add to Wallet button
- How to provision an account token into the digital wallet
Request push provisioning access with the digital wallet (prerequisite)
Before you can start, you need to make sure that your app is registered with the digital wallet and approved to provision cards into their wallet.
Apple Pay
In order for you to enable in-app provisioning of your customers' virtual cards into their Apple Wallet, you need to obtain approval from Apple, which includes granting the required Apple Pay entitlements. This is initiated by sending a request to applepayentitlements@apple.com with the following information:
- Request to enable Apple Pay push provisioning in your app.
- Issuer Name, or the name of your BIN Sponsor.
- Country Code: e.g. SG, ID
- App Name
- Adam ID: available in the "General: App Information" tab on your App page in App Store Connect.
- Team ID: available on your Account page in Apple Developer Portal
Once approved by Apple, you will need to share the following information with Open Fabric
- Adam ID
- App Bundle ID
There may be a need for a commercial agreement/contract with Apple before they release the entitlements.
Google Pay
In order for you to enable in-app provisioning of your customers' virtual cards into their Google Pay Wallet, you need to obtain approval from Google by following this Get Started guide https://developers.google.com/pay/issuers/overview/get-started
Samsung Pay
In order for you to enable in-app provisioning of your customers' virtual cards into their Samsung Pay Wallet, you need to obtain approval from Samsung by following these steps:
- Sign Up for Samsung Pay Developers account: https://pay.samsung.com/developers/tour/memberguide
- Create Service for App to App card enrollment: https://pay.samsung.com/developers/tour/svcguide
- Download Samsung Pay SDK and register app: https://pay.samsung.com/developers/tour/appsguide
Displaying the "Add to Wallet" button
In order to push a virtual card into the respective wallet, you will need to present an "Add to Wallet" button as an option for your customers in your application.
Display this button only if the customer’s token is not yet pushed to the wallet. See Checking provisioned token status in wallet on how to verify this.
- Add to Apple Wallet
- Add to Google Pay
- Add to Samsung Pay
import PassKit
class PaymentCardProvisioningViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Check if the device supports adding payment passes
if PKAddPaymentPassViewController.canAddPaymentPass() {
// Create the "Add to Apple Wallet" button
let addPassButton = PKAddPassButton()
// Add handler for the button
addPassButton.addTarget(
self, action: #selector(addPaymentPassButtonTapped), for: .touchUpInside)
// Add code to position the button
// Add the button to the view
view.addSubview(addPassButton)
} else {
print("This device doesn't support adding payment passes.")
}
}
// other code
}
class PushProvisioningActivity: AppCompatActivity() {
private lateinit var addToGooglePayButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_push_provisioning)
addToGooglePayButton = findViewById(R.id.button_add_to_gpay)
addToGooglePayButton.setOnClickListener {
pushProvisionPaymentCard()
}
}
// other code
}
class PushProvisioningActivity: AppCompatActivity() {
private lateinit var addToSamsungPayButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_push_provisioning)
addToSamsungPayButton = findViewById(R.id.button_add_to_spay)
addToSamsungPayButton.setOnClickListener {
pushProvisionPaymentCard()
}
// Check eligibility and show/hide "Add to Samsung Pay" button
checkEligibilityAndUpdateUI()
}
private fun checkEligibilityAndUpdateUI() {
val partnerInfo = PartnerInfoHolder.getInstance(this@PushProvisioningActivity).partnerInfo
val samsungPay = SamsungPay(this@PushProvisioningActivity, partnerInfo)
samsungPay.getSamsungPayStatus(object : StatusListener {
override fun onSuccess(status: Int, bundle: Bundle?) {
if (status == SpaySdk.SPAY_READY) {
addToSamsungPayButton.visibility = View.VISIBLE
} else {
addToSamsungPayButton.visibility = View.GONE
}
}
})
}
// other code
}
Provisioning account token into the wallet
When a customer clicks on "Add to Google Pay" button, you will need to:
- Tokenize a customer's account
- Push the token into the wallet
Tokenizing a customer's account
To initiate a push provisioning for a given customer’s account, request a one-time push provisioning token (similar to an access token) for that account from your backend.
This provisioning token is passed to Open Fabric’s Push Provisioning SDK in the next step.
For security reasons, it is essential to make the API call from the backend.
Use the Provision API, with the following parameters:
typeset topush_provisioninstrumentset toapple_pay,google_payorsamsung_pay
The API will respond back to you with a provision_token which then needs to be handed back to the SDK for the next step.
The tenant_customer_ref and tenant_account_ref are required fields and need to be filled with a reference to your customer and their account.
Use the tenant_account_ref if you want to distinguish different funding sources. But if customers do not have multiple accounts in your system, you may also choose to copy the value of tenant_customer_ref in this field.
Sample request
Headers:
Idempotency-Key: 40709949-0712-487c-a2fe-1dd2e64af506
- Apple Pay
- Google Pay
- Samsung Pay
{
"type": "push_provision",
"instrument": "apple_pay",
"tenant_customer_ref": "customer-00000000000001",
"tenant_account_ref": "FR00000000000001",
"tenant_token_ref": "TOK000000000000001",
"billing_currency": "PHP",
"billing_address": {
"address_line_1": "8751 Paseo de Roxas St.",
"address_line_2": "",
"city": "Makati City",
"state": "Metro Manila",
"post_code": "1226",
"country_code": "PH"
}
}
{
"type": "push_provision",
"instrument": "google_pay",
"tenant_customer_ref": "customer-00000000000001",
"tenant_account_ref": "FR00000000000001",
"tenant_token_ref": "TOK000000000000001",
"billing_currency": "PHP",
"billing_address": {
"address_line_1": "8751 Paseo de Roxas St.",
"address_line_2": "",
"city": "Makati City",
"state": "Metro Manila",
"post_code": "1226",
"country_code": "PH"
}
}
{
"type": "push_provision",
"instrument": "samsung_pay",
"tenant_customer_ref": "customer-00000000000001",
"tenant_account_ref": "FR00000000000001",
"tenant_token_ref": "TOK000000000000001",
"billing_currency": "PHP",
"billing_address": {
"address_line_1": "8751 Paseo de Roxas St.",
"address_line_2": "",
"city": "Makati City",
"state": "Metro Manila",
"post_code": "1226",
"country_code": "PH"
}
}
Sample response
- Apple Pay
- Google Pay
- Samsung Pay
{
"digital_token_id": "c2319421-f0e8-4dd6-a099-124c5a3c6592",
"provision_token": "eyJraWQiOiIySGhTQUNV...",
"customer_id": "24ae8cd0-301d-4b2b-807b-09683f334bdd",
"instrument": "apple_pay",
"locked": false,
"status": "inactive",
"tenant_account_ref": "FR00000000000001",
"tenant_customer_ref": "customer-00000000000001",
"tenant_token_ref": "TOK000000000000001",
"account_id": "436605a1-271d-49a7-80f0-bcaf095cdd6a",
"created_at": "2024-03-20T14:05:14.711+08:00",
"updated_at": "2024-03-20T14:05:14.711+08:00"
}
{
"digital_token_id": "c2319421-f0e8-4dd6-a099-124c5a3c6592",
"provision_token": "eyJraWQiOiIySGhTQUNV...",
"customer_id": "24ae8cd0-301d-4b2b-807b-09683f334bdd",
"instrument": "google_pay",
"locked": false,
"status": "inactive",
"tenant_account_ref": "FR00000000000001",
"tenant_customer_ref": "customer-00000000000001",
"tenant_token_ref": "TOK000000000000001",
"account_id": "436605a1-271d-49a7-80f0-bcaf095cdd6a",
"created_at": "2024-03-20T14:05:14.711+08:00",
"updated_at": "2024-03-20T14:05:14.711+08:00"
}
{
"digital_token_id": "c2319421-f0e8-4dd6-a099-124c5a3c6592",
"provision_token": "eyJraWQiOiIySGhTQUNV...",
"customer_id": "24ae8cd0-301d-4b2b-807b-09683f334bdd",
"instrument": "samsung_pay",
"locked": false,
"status": "inactive",
"tenant_account_ref": "FR00000000000001",
"tenant_customer_ref": "customer-00000000000001",
"tenant_token_ref": "TOK000000000000001",
"account_id": "436605a1-271d-49a7-80f0-bcaf095cdd6a",
"created_at": "2024-03-20T14:05:14.711+08:00",
"updated_at": "2024-03-20T14:05:14.711+08:00"
}
Push the provisioned token into the wallet
After acquiring the provision_token, it can be pushed into the wallet, along with additional provisioning configuration such as cardholder/customer name, card display name, etc.
- Apple Pay
- Google Pay
- Samsung Pay
class PaymentCardProvisioningViewController: UIViewController {
// other code
@objc func addPaymentPassButtonTapped() {
// Call backend to request push provisioning token from OF
let provisionToken = "..."
// Initialize OFPushProvisioningSDK
let ofPushProvisionSdk = OfPushProvisionSdk()
// Configure provisioning parameters
let provisionConfig = ProvisionConfiguration()
provisionConfig.provisionToken = provisionToken
provisionConfig.cardHolderName = "..."
provisionConfig.cardDisplayName = "..."
// Start provisioning
ofPushProvisionSdk.startProvisioning(
viewController: self,
configuration: provisionConfig,
completion: {
result in
switch result {
case .success(let message):
print("Static Token is successfully added into the wallet.")
case .failure(let err):
print(err.localizedDescription)
}
}
)
}
}
class PushProvisioningActivity: AppCompatActivity() {
private lateinit var ofPushProvisionSdk: OfPushProvisionSdk;
// other code
// Handle onActivityResult with OfPushProvisionSdk.PUSH_PROVISIONING_REQUEST_CODE
override fun onActivityResult(requestCode: Int,resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Handle the push provisioning callback from Google Pay
val status = sdk.onActivityResult(this, requestCode, resultCode, data);
if (status === PushProvisionActivityResultStatus.TOKENIZE_SUCCESS) {
issuerTokenId = data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID)
} else if (status === PushProvisionActivityResultStatus.DELETE_TOKEN_SUCCESS) {
Log.i("TapAndPay", "Account has been removed from Google Wallet.")
} else if(status === PushProvisionActivityResultStatus.TOKENIZE_FAIL || status === PushProvisionActivityResultStatus.DELETE_TOKEN_FAIL) {
val errorMessage = data?.getStringExtra(OfPushProvisionSdk.EXTRA_ERROR_MESSAGE) ?: "Unknown error"
Toast.makeText(this, "Google Wallet Error: $errorMessage", Toast.LENGTH_SHORT).show()
}
}
private fun pushProvisionPaymentCard() {
// Call backend to request wallet provisioning token from OF
val provisionToken = "..."
// Initialize OfWalletProvisioningSDK
ofPushProvisionSdk = OfPushProvisionSdk.getInstance(
environment = OfEnvironment.Prod
)
// Configure provisioning parameters
val provisionConfig = ProvisionConfiguration(
provisionToken = provisionToken,
cardHolderName = "...",
cardDisplayName = "..."
)
// Start provisioning
ofPushProvisionSdk.startProvisioning(
activity = this@PushProvisioningActivity,
configuration = provisionConfig,
callback = object: ProvisioningCallback {
override fun onSuccess(message: String) {
Log.i("TapAndPay", "Start provisioning account into Google Wallet.")
}
override fun onFailure(e: Exception) {
Log.e("TapAndPay", "Error provisioning", e)
}
})
}
}
class PushProvisioningActivity: AppCompatActivity() {
// other code
private fun pushProvisionPaymentCard() {
// Call backend to request wallet provisioning token from OF
val provisionToken = "..."
// Initialize OfWalletProvisioningSDK
val ofPushProvisionSdk = OfPushProvisionSdk.getInstance(
digitalWallet: DigitalWallet.SAMSUNG_PAY,
environment = OfEnvironment.Prod
)
// Configure provisioning parameters
val provisionConfig = ProvisionConfiguration(
provisionToken = provisionToken
)
// Start provisioning
ofPushProvisionSdk.startProvisioning(
activity = this@PushProvisioningActivity,
configuration = provisioningConfig,
callback = object: ProvisioningCallback {
override fun onSuccess(message: String) {
Log.i("SamsungPay", "Static Token is successfully added into the wallet.")
}
override fun onFailure(e: Exception) {
Log.e("SamsungPay", "Error provisioning", e)
}
})
}
}
At this point, the digital wallet view will be presented and the customer will go through the process of confirming the addition of the provisioned token into the wallet.
Checking provisioned token status in the Digital Wallet
After a customer’s provisioned token is pushed into the digital wallet, its status can be queried at any time. This status can be used to conditionally display the "Add to Wallet" button or for any necessary user experience design.
- Apple Pay
- Google Pay
- Samsung Pay
ofPushProvisionSdk.checkTokenStatus(
issuerTokenId: issuerTokenId,
completion: {
result in
switch result {
case .success(let status):
if status {
print("Static Token is already in the wallet.")
} else {
// Static Token is not in the wallet.
// Show "Add to Apple Wallet" button
}
case .failure(let err):
// Handle check failure
print(err.localizedDescription)
}
}
)
ofPushProvisionSdk.checkTokenStatus(
activity = this@PushProvisioningActivity,
last4DigitsOrIssuerTokenId = issuerTokenId, // or last 4 digits of the account number
cardNetwork = CardNetwork.MASTERCARD,
callback = object: CheckTokenStatusCallback {
override fun onSuccess(tokenized: Boolean) {
if (status) {
Log.i("TapAndPay", "Account Token is already added into the wallet.")
} else {
// Account Token is not in the wallet.
// Show "Add to Google Pay" button
}
}
override fun onFailure(e: Exception) {
Log.e("TapAndPay", "Error provisioning", e)
}
}
)
ofPushProvisionSdk.checkTokenStatus(
activity = this@PushProvisioningActivity,
issuerTokenId = issuerTokenId,
callback = object: CheckTokenStatusCallback {
override fun onSuccess(tokenized: Boolean) {
if (status) {
Log.i("TapAndPay", "Static Token is already added into the wallet.")
} else {
// Static Token is not in the wallet.
// Show "Add to Google Pay" button
}
}
override fun onFailure(e: Exception) {
Log.e("TapAndPay", "Error provisioning", e)
}
}
)
Removing a token from the digital wallet
After a customer’s account is pushed to a digital wallet, you can request that the digital wallet removes the token using the issuerTokenId.
- Google Pay
ofPushProvisionSdk.requestDeleteToken(
activity = this@PushProvisioningActivity,
issuerTokenId = issuerTokenId,
cardNetwork = CardNetwork.MASTERCARD,
callback = object: = object: ProvisioningCallback {
override fun onSuccess(message: String) {
Log.i("TapAndPay", "Start requesting deleting account token from Google Wallet.")
}
override fun onFailure(e: Exception) {
Log.e("TapAndPay", "Error requesting delete token", e)
}
}
)
The delete request will be processed by the wallet and the result will be sent back to your app via the onActivityResult() method.