Skip to main content

Prepaid Cards

Introduction

The Card service exposes features for managing cards related operations and eligible programs operations:

  • Get customer prepaid cards.
  • Create a prepaid card.
  • Activate a prepaid physical card.
  • Update а card.
  • Get prepaid card details.
  • Retrieve customer eligible prepaid cards programs.
  • Tokenize a prepaid card.
  • Update card tokenization status.

Use the following code to obtain an instance of CardService:

val cardService = Wallet.getInstance().getCardService()

Get customer prepaid cards

Retrieve a list of all prepaid cards for the current customer. CardParameters are the parameters for filtering the customer's cards. Use include parameter to add additional information into the response. TOKENIZATIONS - include information about the mobile wallets' tokenization. AVAILABLE_ACTIONS - include information about available actions on the card.

CardParameters

ParameterTypeDescriptionExample
cardTypeCardTypeOptional parameter to filter cards by card type.CardType.VIRTUAL / .virtual
limitIntOptional parameter to limit the number of results.20
offsetIntOptional parameter to specify the starting position, where 1 is the first page.0
statusList<CardStatus> / [Card.CardStatus]Optional parameter. List of card statuses which will be returned. Optional parameter, if not set all statuses will be returned.listOf(CardStatus.ACTIVE, CardStatus.CANCELLED) / [.active]
includeList<CardIncludesParam> / [CardIncludesParam]Optional parameter. Include listed properties in the response.listOf(CardIncludesParam.TOKENIZATIONS) / [.tokenizations]

CardIncludesParam

AndroidiOSDescription
TOKENIZATIONS.tokenizationsInclude additional information in card.
AVAILABLE_ACTIONS.availableActionsInclude information about available actions on the card.

CardType

Enum class representing the type of card.

AndroidiOSDescription
PHYSICAL.physicalThe card is physical.
VIRTUAL.virtualThe card is virtual.
UNKNOWN.unknownThe type of the card is unknown or cannot be determined.

CardStatus

Enum class representing the status of a card.

AndroidiOSDescription
ACTIVE.activeThe card is currently active and can be used for transactions.
APPLIED.appliedThe card application is in progress or awaiting approval.
EXPIRED.expiredThe card has expired and is no longer valid for use.
DIGITAL.digitalThe card is in a digital format, such as a virtual or electronic card.
UNKNOWN.unknownThe status of the card is unknown or cannot be determined.

CardScheme

Enum class representing the scheme of a card.

AndroidiOSDescription
MC.mastercardThe card is Mastercard.
VISA.visaThe card is Visa.
UNKNOWN.unknownThe scheme of the card is unknown or cannot be determined.

The response is a CardList object which contains List<Card> / [Card] and PagingResultMeta objects.

  • PagingResultMeta contains information about:
    • numberOfRecords - The total number of records available.
    • limit - The maximum number of records per page.
    • page - The current page number.
  • Card contains information about the card such as cardId, status, expiry, lastFour, bin, customerId, cardType, programName, currency, mobile,deliveryAddress,createdDate, accountId, scheme,activationDate, isPinSet, externalId, tokenizations.
    • tokenizations contains object MobileWalletTokenizationList which contains list of MobileWalletTokenization with information such as dpanRef, walletId, walletType, status.
    • '.availableActions' contains an array of CardAvailableActions which has an available action field.

MobileWalletType

Enum class representing the type of mobile wallet.

AndroidiOSDescription
APPLE_PAY.applePayThe wallet type is Apple Pay.
GOOGLE_PAY.googlePayThe wallet type is Google Pay.
SAMSUNG_PAY.samsungPayThe wallet type is Samsung Pay.
UNKNOWN.unknownThe wallet type is unknown or cannot be determined.

MobileWalletStatus

Enum class representing the tokenization status of a mobile wallet.

AndroidiOSDescription
COMPLETED.completedThe tokenization request is completed.
INITIATED.initiatedThe tokenization request is initiated.
UNKNOWN.unknownThe type of the mobile wallet is unknown or cannot be determined.

CardAction

Enum class representing an available action that could be performed on a card.

AndroidiOSDescription
ACTIVATE.activateActivates newly received card. Can be used only for PHYSICAL card
CANCEL.cancelCancel card for all the operations. The status is IRREVERSIBLE.
UNLOCK.unlockUnlocks locked card.
LOCK.lockLocks the card for all operations. It is reversible by UNLOCK action.
SHOW_DETAILS.showDetailsShows sensitive card details (PAN, CVV, expiry date).
SHOW_PIN.showPinShows pin of the card.
SET_PIN.setPinUpdates pin of the card.
val cardParameters = CardParameters(
cardType = CardType.ACTIVE,
limit = null,
offset = null,
status = listOf(CardStatus.ACTIVE, CardStatus.EXPIRED),
include = listof(CardIncludesParam.TOKENIZATIONS, CardIncludesParam.AVAILABLE_ACTIONS)
)

try {
val cards = cardService.getAll(cardParameters)
Log.d(TAG, cards.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

You can achieve pagination by combining the limit and offset filters. For instance, implementing pagination with a page size of 2 results per page would involve configuring:

  • Page 1: limit=2, offset=0
  • Page 2: limit=2, offset=2
  • Page 3: limit=2, offset=4

Retrieve single prepaid card

Provides detailed information about specific card by cardId. The response is a single card object.

val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
val include = listOf(CardIncludesParam.TOKENIZATIONS)

try {
val card = cardService.get(cardId, include)
Log.d(TAG, card.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

Create a prepaid card

Create a new Physical or Virtual prepaid card for a customer, based on the provided programName.

CardRequest

ParameterTypeDescriptionExample
programNameStringRequired parameter. Program name retrieved from CardService."SKRILL-VIRTUAL-MC-EEA"
currencyStringRequired parameter. Currency code in three-letter format ("Alpha-3")."EUR"
mobileStringOptional parameter. If not provided, we will try to use the mobile phone provided during the customer onboarding. Mobile phone number must be in format +1 123456789."+1 123456789"
cardPinStringOptional parameter. For EU Customers: The Card pin should be 4 digits. US customers must set their card PIN via a separate REST HTTP call."1234"
externalIdStringOptional external identifier in merchant system."a2322550-af91-417f-867e-681efad44b9d"
deliveryAddressDeliveryAddressOptional parameter. The DeliveryAddress object will be used for the PHYSICAL card delivery. It can be null in case of VIRTUAL card.
termsAndConditionsAcceptedBoolean / BoolThe field must be present for US customers. For other countries it is not required.True
eDisclosureAcceptedBoolean / BoolThe field must be present for US customers. For other countries it is not required.True

DeliveryAddress

ParameterTypeDescriptionExample
address1StringRequired parameter mandatory for address."Tsarigradsko Shose 73"
address2StringOptional parameter for address line 2"Apartment 2C"
address3StringOptional parameter for address line 3."Cityville, State 78901"
cityStringRequired parameter for specifying the city. Max 30 characters: letters, spaces, hyphen and period."Sofia"
countryCodeStringRequired parameter in ISO-3166 Alpha 2, representing a country code"BG"
stateStringOptional parameter. For US Customers: 2,3-character state or province abbreviation."NL"
postalCodeStringRequired parameter. For EU customers: maximum length 16. For US customers: Pattern: ^[a-zA-Z0-9-\ ]*$"1000"
val cardRequest = CardRequest(
programName = "SKRILL-VIRTUAL-MC-EEA",
currency = "BGN",
mobile = "+1 123456789",
cardPin = null,
externalId = null,
deliveryAddress = null
)

try {
val card = cardService.create(cardRequest)
Log.d(TAG, card.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

The parameters for PHYSICAL / .physical card application are the same, but you need to specify the deliveryAddress parameter.

val cardRequest = CardRequest(
programName = "SKRILL-PHYSICAL-MC-EEA",
currency = "BGN",
mobile = "+1 123456789",
cardPin = null,
externalId = null,
deliveryAddress = DeliveryAddress(
address1 = "Tsarigradsko Shose 73",
address2 = null,
address3 = null,
city = "Sofia",
countryCode = "BG",
state = null,
postalCode = "1000"
)
)

try {
val card = cardService.create(cardRequest)
Log.d(TAG, card.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

Activate a prepaid physical card

This endpoint facilitates the activation of a specific prepaid physical card, identified by its unique cardId. The card can be activated only once and only while it is with status ISSUED or DIGITAL. Once activated, the card's status transitions to ACTIVE, enabling users to conveniently utilize their physical card.

CardActivationRequest

ParameterTypeDescriptionExample
lastFourDigitsStringRequired parameter containing the last four-digit of the PAN."1234"
cvvStringRequired parameter containing the CVV of the card."567"
val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
val cardActivationRequest = CardActivationRequest(
lastFourDigits = "1234",
cvv = "567"
)

try {
val card = cardService.activate(cardId, cardActivationRequest)
Log.d(TAG, card.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

Update prepaid card

The customer can change/update:

  • The status of their prepaid cards, and as a result, lock/unlock or cancel their prepaid card.
  • The card's pin.

In the table below, you can find information about the statuses that the user can change.

From \ ToACTIVECANCELLEDLOCKEDDIGITAL
ACTIVE-
CANCELLED-
LOCKED-
DIGITAL✅ (By PIN & CHIP)-

To update card use CardUpdateRequest.

// Status update request
val cardUpdateRequest = CardUpdateRequest(
status = Status.ACTIVE,
statusReason = "User Activate Card from LOCKED status"
)

// Pin change request
val cardUpdateRequest = CardUpdateRequest(
pin = "1243"
)

val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"

try {
val card = cardService.update(cardId, cardUpdateRequest)
Log.d(TAG, card.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

Get prepaid card details

Card sensitive information can be viewed by the customers via specialized custom views. This can be either one view containing the whole card with the information on it, or separate views for each sensitive information: card PAN, CVV, expiry date and cardholder's name.

info

This operation might require Strong Customer Authentication (SCA). Please read Strong Customer Authentication for more information on the process.

Get card details views

Call getSecureDetailsViews method of the CardService interface with the card ID to obtain a CardSecureDetailsViews object containing specialized secure views for the card PAN, CVV, expiry date and cardholder's name. For security reasons these details cannot be obtained in plain text.

note

This operation requires the SDK to be authenticated with a customer token with pci-dss-1 scope. The scope can be requested during the token exchange process as described in SDK User Authentication.

The getSecureDetailsViews method has an optional parameter viewsConfiguration that can be used to set a custom PAN separator and which view should have a copy button. By default the PAN separator is an empty space ("1234 5678 9012 3456") and the copy button is disabled for all views. The copy button copies the text from the respective view to the clipboard with the ClipDescription.EXTRA_IS_SENSITIVE extra set to true.


val viewsConfiguration = SecureDetailsViewsConfiguration(
shouldShowPanCopyButton = true,
shouldShowCvvCopyButton = true,
shouldShowExpiryDateCopyButton = true,
shouldShowCardholderNameCopyButton = true,
panSeparator = "-"
)

val request = CardSecureDetailsViewRequest(
cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24",
viewsConfiguration = viewsConfiguration
)

val cardSecureDetailsViews: CardSecureDetailsViews = try {
// call getSecureDetailsViews with the card ID and the viewsConfiguration
cardService.getSecureDetailsViews(request )
} catch (e: ScaRequiredException) {
// solve SCA challenge using e.authenticationEvent
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// repeat getSecurePinView with the SCA details
cardService.getSecureDetailsViews(
CardSecureDetailsViewRequest(
cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24",
viewsConfiguration = viewsConfiguration,
scaDetails = ScaAuthenticationEventRequest(
eventId = e.authenticationEvent.eventId,
walletOperationId = e.authenticationEvent.walletOperationId
)
)
)
}

// Classic Views: Add the cardSecureDetailsViews to your view hierarchy
container.addView(cardSecureDetailsViews.panView)
container.addView(cardSecureDetailsViews.cvvView)
container.addView(cardSecureDetailsViews.expiryDateView)
container.addView(cardSecureDetailsViews.cardHolderNameView)

// Jetpack Compose: Create a composable function to display the cardSecureDetailsViews
@Composable
fun SecureDetailsComposable(request: CardSecureDetailsViewRequest) {
val cardSecureDetailsViews = remember { mutableStateOf<CardSecureDetailsViews?>(null) }

LaunchedEffect(true) {
cardSecureDetailsViews.value =
Wallet.getInstance().getCardService().getSecureDetailsViews(request)
}

cardSecureDetailsViews.value?.let { detailViews ->
val panView = detailViews.panView
val cvvView = detailViews.cvvView
val expiryDateView = detailViews.expiryDateView
val cardHolderNameView = detailViews.cardHolderNameView

Column {
AndroidView(factory = { panView })
AndroidView(factory = { cvvView })
AndroidView(factory = { expiryDateView })
AndroidView(factory = { cardHolderNameView })
}
}
}

Customize card details views

The copy button icon color is based on the text color of the respective view. A custom icon for the copy button can be used by setting the pswIconCopyButton attribute as explained in UI Theme Configuration.

By default the card detail views use font sans-serif with text size 24sp for the PAN and 20sp for CVV, Expiry and Cardholder's name. The text style for each view can be changed by setting the pswTextAppearanceCardPan, pswTextAppearanceCardCvv, pswTextAppearanceCardExpiry and pswTextAppearanceCardCardholderName attributes as explained in UI Theme Configuration.

warning

When the views are placed over card image, please make sure that the card design aligns with the approved guidelines from the Card Scheme.

Get card fragment (Legacy)

On Android sensitive card information is displayed in a custom Fragment. Call getSecureDetailsFragment method of the CardService interface with the card ID to obtain a new instance of the fragment and use supportFragmentManager to display it. The sensitive data loads automatically when the fragment is visualized.

class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
val fragment = cardService.getSecureDetailsFragment(
CardSecureDetailsRequest(cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24")
)
supportFragmentManager.commit {
replace(R.id.fragment_container_view, fragment)
}
}
}
}
note

The fragment transaction is only created when savedInstanceState is null. This is to ensure that the fragment is added only once, when the activity is first created. When a configuration change occurs and the activity is recreated, savedInstanceState is no longer null, and the fragment does not need to be added a second time, as it is automatically restored from the savedInstanceState.

Handle Strong Customer Authentication

A ScaAuthenticationEventListener should be attached to this fragment using the listenScaAuthenticationEvent method. In the case when a Strong Customer Authentication (SCA) is required, this listener will be triggered. Follow the steps outlined in Submit the SCA Authentication to confirm the event. After the SCA event is accepted, call authenticateScaEvent with the scaDetails obtained from the listener.

// use the fragment created with cardService.getSecureDetailsFragment()
cardService.listenScaAuthenticationEvent(fragment) { scaDetails ->
// solve SCA challenge using scaDetails
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// call authenticateScaEvent to obtain the card details
cardService.authenticateScaEvent(fragment, ScaAuthenticationEventRequest(...))
}

Get prepaid card PIN

A card PIN can be viewed by the customers via specialized secure view. This secure view is obtained asynchronously with the getSecurePinView method of the CardService interface and then attached to the screen. For security reasons, the PIN cannot be obtained in plain text.

note

This operation requires the SDK to be authenticated with a customer token with pci-dss-1 scope. The scope can be requested during the token exchange process as described in SDK User Authentication.

info

This operation might require Strong Customer Authentication (SCA). Please read Strong Customer Authentication for more information on the process.

Customize PIN font and color

By default the PIN is displayed on a transparent background using Title 1 text style and Text Primary color. These values can be customized as explained in UI Theme Configuration for Android and UI Appearance configuration for iOS.

val pinView: View = try {
// call getSecurePinView with the card ID
cardService.getSecurePinView(CardSecureViewRequest(cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"))
} catch (e: ScaRequiredException) {
// solve SCA challenge using e.authenticationEvent
// as described in https://docs.paysafe.com/docs/embedded-wallets/strong-customer-authentication
// repeat getSecurePinView with the SCA details
cardService.getSecurePinView(
CardSecureViewRequest(
cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24",
scaDetails = ScaAuthenticationEventRequest(
eventId = e.authenticationEvent.eventId,
walletOperationId = e.authenticationEvent.walletOperationId
)
)
)
}

// Jetpack Compose: Create a composable function to display the pinView
@Composable
fun CardPin(cardId: String) {
val pinView = remember { mutableStateOf<View?>(null) }

LaunchedEffect(true) {
pinView.value = cardService.getSecurePinView(CardSecureViewRequest(cardId))
}

pinView.value?.let { view ->
AndroidView(factory = { view })
}
}

// Classic Views: Add the pinView to your view hierarchy
container.addView(pinView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)

Retrieve customer eligible prepaid cards programs

Retrieve eligible programs for a customer. The result list can be filtered by program type. If the customer is eligible for a Prepaid card, the response will contain a non-empty programs array. The number of cards that can be issued of a given type can be seen in the allowableCards field.

try {
val eligiblePrograms = cardService.getPrograms()
Log.d(TAG, eligiblePrograms.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

Tokenize a prepaid card

Tokenize given card for specific mobile wallet.

CardTokenizationRequest

Card Tokenization by which the client mobile application requests the needed info for card tokenization for a specific mobile wallet. Depending on the mobile wallet, device type, and card schema, some of the request fields are optional.

ParameterTypeDescriptionExample
walletTypeMobileWalletTypeRequired parameter containing supported wallet types.MobileWalletType.GOOGLE_PAY / .applePay
clientDeviceId / clientDeviceIDStringRequired for cards with VISA schema + Samsung/Google Pay. Not required for VISA + Apple Wallet."WNkcsfZPiKfa5PrH3jilkQYT"
clientWalletAccountId / clientWalletAccountIDStringRequired for cards with VISA schema + Samsung/Google Pay."40exmLiWV1iV55ZVstOAiMf7"
leafCertificateStringRequired for iOS. The leaf certificate returned by Apple Wallet, that was signed by using the subordinateCertificate. Should be Base-64 encoded binary data of the certificate.
subordinateCertificateStringRequired for iOS. The subordinate certificate returned by Apple Wallet that was signed using the Wallet Provider’s Certificate Authority (CA) certificate. Should be Base-64 encoded binary data of the certificate.
nonceStringRequired for iOS. Base-64 encoded nonce value returned by Apple Wallet."w1koQg=="
nonceSignatureStringRequired for iOS. Base-64 encoded nonce signature value returned by Apple Wallet."QF7lLU3rlO...iuscoJE9PdX"

The response is a CardTokenization object which contains the needed information in order specific card to be added to APPLE/GOOGLE Pay. Depending on the mobile device, some of the fields are optional.

  • opaquePaymentCard - It will be returned only for Google Pay.
  • encryptedPassData - Will be returned only for Apple Pay.
  • ephemeralPublicKey - Will be returned only for Apple Pay. Some other information that this object contains is: activationData,cardNetwork, tokenProvider, customer, cardLastDigits.
  • customer - An object containing information about specific cardholder such as: address1, address2, address3, city, countryCode, state, postalCode, name, mobile

CardNetworkType

Enum class for representing cardNetwork.

AndroidiOSDescription
CARD_NETWORK_VISA.cardNetworkVisaVisa card network.
CARD_NETWORK_MASTERCARD.cardNetworkMastercardMastercard card network.
UNKNOWN.unknownThe network of the card is unknown or cannot be determined.

CardTokenProvider

Enum class for representing tokenProvider.

AndroidiOSDescription
TOKEN_PROVIDER_VISA.tokenProviderVisaVisa token provider.
TOKEN_PROVIDER_MASTERCARD.tokenProviderMastercardMastercard token provider.
UNKNOWN.unknownThe token provider is unknown or cannot be determined.
val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
val cardTokenizationRequest = CardTokenizationRequest(
walletType = MobileWalletType.GOOGLE_PAY,
clientDeviceId = "WNkcsfZPiKfa5PrH3jilkQYT",
clientWalletAccountId = "40exmLiWV1iV55ZVstOAiMf7"
)
try {
val cardTokenizationResponse = cardService.tokenize(cardId, cardTokenizationRequest)
Log.d(TAG, cardTokenizationResponse.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

Update card tokenization status

Updates card tokenization status by which tokenization can be marked as COMPLETED. Tokenization marked as COMPLETED means that the given card will be not able to be tokenized for the specific device and profile (in case of Google Pay).

MobileWalletTokenization

Contains information about card enrolment for specific Mobile Wallet.

ParameterTypeDescriptionExample
dpanRefStringRequired parameter. dpan (Device Pan) reference."DAPLMC00002125433c0c34a2821f4f86866e7576963baf8b"
walletIdStringOptional parameter. Wallet Id used only with Google Pay."NMZlGi8-DezZZKaU06orvl0f"
walletTypeMobileWalletTypeRequired parameter. The card pin should be 4 digits.MobileWalletType.GOOGLE_PAY / .applePay
statusMobileWalletStatusRequired parameter. The user agent of the device used for card creation.MobileWalletStatus.COMPLETED / .completed

The response is the updated MobileWallet object.

val mobileWalletRequest = MobileWallet(
dpanRef = "DAPLMC00002125433c0c34a2821f4f86866e7576963baf8b",
walletId = walletId,
walletType = MobileWalletType.GOOGLE_PAY,
status = MobileWalletStatus.COMPLETED
)
val cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24"
try {
val mobileWalletResponse = cardService.updateTokenizationStatus(cardId, mobileWalletRequest)
Log.d(TAG, mobileWalletResponse.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}

Apple card tokenization

This section includes both sequence diagrams and a technical guide outlining the process of adding a card to Apple Wallet through the In-App Provisioning flow.

Prior to initiating the card addition implementation for Apple Wallet, it is essential to submit a request for access to Apple Pay in accordance with the latest guidelines provided in the Apple Developer documentation. For information on the prerequisites and required steps, please refer to Setting up Apple Pay documentation page.

Apple Pay button

"Add to Apple Wallet" Button should only be displayed if a card does not exist in Apple Wallet on either the iPhone or Apple Watch.

  • Verify device eligibility using canAddPaymentPass().
  • Check PKPassLibrary that card does not exist in passes() and remoteSecureElementPasses.
  • Display "Add to Apple Wallet" button using PKAddPassButton if above conditions are met.
  • Tapping the button triggers In-App provisioning.
Display Apple Pay ButtonDisplay Apple Pay ButtonDisplay Apple Pay ButtonDisplay Apple Pay Button

In-App provisioning

Once the user taps the 'Add to Apple Wallet' button, In-App Provisioning starts by initializing and presenting PKAddPaymentPassViewController by providing PKAddPaymentPassRequestConfiguration.

func createPaymentPassRequestConfiguration(customerInfo: Wallet.CustomerInfo, card: Wallet.Card) -> PKAddPaymentPassRequestConfiguration? {
guard let requestConfiguration = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2) else {
return nil
}

requestConfiguration.cardholderName = "\(customerInfo.firstName) \(customerInfo.lastName)"
requestConfiguration.primaryAccountSuffix = card.lastFour
// Filter the devices that already have this card provisioned, by setting requestConfiguration.primaryAccountIdentifier
requestConfiguration.paymentNetwork = card.scheme.paymentNetwork
return requestConfiguration
}

Subsequently, Apple Wallet requests creation of PKAddPaymentPassRequest through PKAddPaymentPassViewControllerDelegate. The received Apple Public Certificates, along with nonce and nonceSignature, are used to make cardService.tokenize() request to the SDK.

Below code block demonstrates how CardTokenizationRequest is created from data returned by the delegate.

func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController,
generateRequestWithCertificateChain certificates: [Data],
nonce: Data,
nonceSignature: Data,
completionHandler handler: @escaping (PKAddPaymentPassRequest) -> Void) {
let certificatesBase64EncodedString = certificates.map { $0.base64EncodedString() }
let nonceBase64EncodedString = nonce.base64EncodedString()
let nonceSignatureBase64EncodedString = nonceSignature.base64EncodedString()

guard let leafCertificate = certificatesBase64EncodedString.first,
let subordinateCertificate = certificatesBase64EncodedString.last else {
// Handle error
return
}

let tokenizationRequest = Wallet.CardTokenizationRequest(leafCertificate: leafCertificate,
subordinateCertificate: subordinateCertificate,
nonce: nonceBase64EncodedString,
nonceSignature: nonceSignatureBase64EncodedString)

Wallet.instance.cardService.tokenize(cardID: card.cardID,
request: tokenizationRequest,
completion: { result in
switch result {
case .success(let cardTokenization):
let addPaymentPassRequest = PKAddPaymentPassRequest()
addPaymentPassRequest.activationData = cardTokenization.activationData.flatMap { Data(base64Encoded: $0) }
addPaymentPassRequest.encryptedPassData = cardTokenization.encryptedPassData.flatMap { Data(base64Encoded: $0) }
addPaymentPassRequest.ephemeralPublicKey = cardTokenization.ephemeralPublicKey.flatMap { Data(base64Encoded: $0) }

handler(addPaymentPassRequest)
case .failure(let error):
// Handle error
}
})
}

Apple Wallet further forwards the encryptedPassData and ephemeralPublicKey to the PNO (Payment Network Operator) or service provider, where validation checks are conducted. The PNO validates the activationData and performs final approval checks, subsequently sending the successful activation status to Apple Wallet. PKAddPaymentPassViewControllerDelegate notifies success/failure.

func addPaymentPassViewController(
_ controller: PKAddPaymentPassViewController,
didFinishAdding pass: PKPaymentPass?,
error: Error?
)

Successful activation is communicated to the SDK with cardService.updateTokenizationStatus() which finalizes the process.

func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController,
didFinishAdding pass: PKPaymentPass?,
error: (any Error)?) {
if let pass = pass {
let request = Wallet.MobileWalletTokenization(dpanRef: pass.deviceAccountIdentifier,
status: .completed)
Wallet.instance.cardService.updateTokenizationStatus(cardID: card.cardID,
request: request,
completion: { result in
// Handle result and refresh UI
})
} else if let error = error {
// Handle error
}
}
In-App Provisioning FlowIn-App Provisioning FlowIn-App Provisioning FlowIn-App Provisioning Flow

Google card tokenization

This section offers sequence diagrams and a detailed technical step-by-step guide for integrating card tokenization into Google Wallet using Android Push Provisioning API.

Card tokenization involves generating a secure digital replica of an existing physical or virtual card. This replica is subsequently integrated into a token network, such as Google Pay. For Google Pay, token provisioning can occur either manually, where a tokenization request is initiated from Google Wallet, or through push provisioning, where the request originates from your application.

Before initiating the card tokenization process on Google Wallet, the first step is creating a Google account if one has not already been created. After setting up your Google account, you can initiate the access request through this form, as gaining access involves submitting a request to Google Android Push Provisioning API.

After obtaining access, you can download and import the latest version of play-services-tapandpay.aar enabling you to use the TapAndPay SDK. TapAndPay SDK is a library developed by Google which streamlines the necessary calls to integrate with Google Wallet.

Google button display

The initial step is to display the Google button with the appropriate logo and dimensions. Google Pay button should be only displayed if a card does not exist in Google Pay/Wallet. Further information about this can be found in the official documentation provided by Google once access to the page is granted.

The sequence diagram below illustrates the essential checks required to initialize and display the Google button, in conjunction with the Paysafe mobile SDK, Android OS, and TapAndPay SDK.

Display google button flowDisplay google button flowDisplay google button flowDisplay google button flow

Google button click

After completing the aforementioned steps to display and initialize the button, a subsequent click on the button will initiate one of the three specified flows below.

Push Tokenization

This flow will be initiated under two conditions: firstly, if the attempt to list tokens from TapAndPay SDK results in a failure with exception code TapAndPayStatusCode.TAP_AND_PAY_NO_ACTIVE_WALLET and secondly, if there is no match between the tokens retrieved from TapAndPay SDK and the tokens retrieved from Paysafe Mobile SDK.

The following sequence diagram outlines the necessary steps for Push Tokenization.

Push tokenization flowPush tokenization flowPush tokenization flowPush tokenization flow

Manual Tokenization

This flow is initiated when listing the tokens affiliated with a particular wallet using tapAndPayClient.listTokens().

Upon discovering a token in the TapAndPay.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION state related to the selected card, manual verification becomes necessary. This additional step is required to authenticate a card for a specific user.

To proceed with manual tokenization, the issuerTokenId from the token that requires identity verification is used as tokenRefId for the CardManualTokenizationConfiguration object.

The following sequence diagram outlines the necessary steps for Manual Tokenization.

Manual tokenization flowManual tokenization flowManual tokenization flowManual tokenization flow

Create wallet

This flow is initiated when no wallet has been previously created on the device. It can be triggered either through the Push Tokenization or Manual Tokenization flows, where a call to activeWalletId and stableHardwareId may throw an exception in the absence of a wallet.

The following sequence diagram outlines the necessary steps to add a wallet to a device.

Create wallet flowCreate wallet flowCreate wallet flowCreate wallet flow