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
- 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()
import PaysafeWallet
let cardService = Wallet.instance.cardService
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 tokenizations.
CardParameters
Parameter | Type | Description | Example |
---|---|---|---|
cardType |
CardType | Optional parameter to filter cards by card type. | CardType.VIRTUAL / .virtual |
limit |
Int | Optional parameter to limit the number of results. | 20 |
offset |
Int | Optional parameter to specify the starting position, where 1 is the first page. | 0 |
status |
List<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] |
include |
List<CardIncludesParam> / [CardIncludesParam] |
Optional parameter. Include listed properties in the response. | listOf(CardIncludesParam.TOKENIZATIONS) / [.tokenizations] |
CardIncludesParam
Android | iOS | Description |
---|---|---|
TOKENIZATIONS |
.tokenizations |
Include additional information in card. |
CardType
Enum class representing the type of a card.
Android | iOS | Description |
---|---|---|
PHYSICAL |
.physical |
The card is physical. |
VIRTUAL |
.virtual |
The card is virtual. |
UNKNOWN |
.unknown |
The type of the card is unknown or cannot be determined |
CardStatus
Enum class representing the status of a card.
Android | iOS | Description |
---|---|---|
ACTIVE |
.active |
The card is currently active and can be used for transactions. |
APPLIED |
.applied |
The card application is in progress or awaiting approval. |
EXPIRED |
.expired |
The card has expired and is no longer valid for use. |
DIGITAL |
.digital |
The card is in a digital format, such as a virtual or electronic card. |
UNKNOWN |
.unknown |
The status of the card is unknown or cannot be determined |
CardScheme
Enum class representing the scheme of a card.
Android | iOS | Description |
---|---|---|
MC |
.mastercard |
The card is Mastercard. |
VISA |
.visa |
The card is Visa. |
UNKNOWN |
.unknown |
The scheme of the card is unknown or cannot be determined. |
The response is aCardList
object which contains List<Card>
/ [Card]
and PagingResultMeta
objects.
PagingResultMeta
contains information about:numberOfRecords
- the total number of records availablelimit
- the maximum number of records per pagepage
- the current page number.
Card
contains information about the card such ascardId
,status
,expiry
,lastFour
,bin
,customerId
,cardType
,programName
,currency
,mobile
,deliveryAddress
,createdDate
,accountId
,scheme
,activationDate
,isPinSet
,externalId
,tokenizations
tokenizations
contains objectMobileWalletTokenizationList
which contains list ofMobileWalletTokenization
with information such asdpanRef
,walletId
,walletType
,status
MobileWalletType
Enum class representing the type of a mobile wallet.
Android | iOS | Description |
---|---|---|
APPLE_PAY |
.applePay |
The wallet type is Apple Pay. |
GOOGLE_PAY |
.googlePay |
The wallet type is Google Pay. |
SAMSUNG_PAY |
.samsungPay |
The wallet type is Samsung Pay. |
UNKNOWN |
.unknown |
The wallet type is unknown or cannot be determined. |
MobileWalletStatus
Enum class representing the tokenization status of a mobile wallet.
Android | iOS | Description |
---|---|---|
COMPLETED |
.completed |
The tokenization request is completed. |
INITIATED |
.initiated |
The tokenization request is initiated. |
UNKNOWN |
.unknown |
The type of the mobile wallet is unknown or cannot be determined. |
val cardParameters = CardParameters(
cardType = CardType.ACTIVE,
limit = null,
offset = null,
status = listOf(CardStatus.ACTIVE, CardStatus.EXPIRED),
include = listof(CardIncludesParam.TOKENIZATIONS)
)
try {
val cards = cardService.getAll(cardParameters)
Log.d(TAG, cards.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}
let parameters = Wallet.CardParameters(cardType: .virtual,
limit: nil,
offset: nil,
status: [.active, .expired],
include: [.tokenizations])
cardService.getAll(parameters: parameters, completion: { result in
switch result {
case .success(let cards):
// Display cards
case .failure(let error):
// Handle error
}
})
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())
}
cardService.get(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24", include: [.tokenizations], completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
Create a prepaid card
Create a new Physical or Virtual prepaid card for a customer, based on the provided programName
.
CardRequest
Parameter | Type | Description | Example |
---|---|---|---|
programName |
String | Required parameter. Program name retrieved from CardService. | "SKRILL-VIRTUAL-MC-EEA" |
currency |
String | Required parameter. Currency code in three-letter format ("Alpha-3"). | "EUR" |
mobile |
String | Optional 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" |
cardPin |
String | Optional 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" |
externalId |
String | Optional external identifier in merchant system. | "a2322550-af91-417f-867e-681efad44b9d" |
deliveryAddress |
DeliveryAddress | Optional parameter. The DeliveryAddress object will be used for the PHYSICAL card delivery. It can be null in case of VIRTUAL card. | |
termsAndConditionsAccepted |
Boolean / Bool | The field must be present for US customers. For other countries it is not required. | True |
eDisclosureAccepted |
Boolean / Bool | The field must be present for US customers. For other countries it is not required. | True |
DeliveryAddress
Parameter | Type | Description | Example |
---|---|---|---|
address1 |
String | Required parameter mandatory for address. | "Tsarigradsko Shose 73" |
address2 |
String | Optional parameter for address line 2 | "Apartment 2C" |
address3 |
String | Optional parameter for address line 3. | "Cityville, State 78901" |
city |
String | Required parameter for specifying the city. Max 30 characters: letters, spaces, hyphen and period. | "Sofia" |
countryCode |
String | Required parameter in ISO-3166 Alpha 2, representing a country code | "BG" |
state |
String | Optional parameter. For US Customers: 2,3-character state or province abbreviation. | "NL" |
postalCode |
String | Required 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())
}
let request = Wallet.CreateCardRequest(programName: "SKRILL-VIRTUAL-MC-EEA",
currency: "EUR",
mobile: "+1 123456789",
cardPin: nil,
externalId: nil,
deliveryAddress: nil)
cardService.create(request: request, completion: { result in
switch result {
case .success(let card):
// Display created card
case .failure(let error):
// Handle error
}
})
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())
}
let request = Wallet.CreateCardRequest(programName: "SKRILL-PHYSICAL-MC-EEA",
currency: "EUR",
mobile: "+1 123456789",
cardPin: nil,
externalId: nil,
deliveryAddress: .init(address1: "Tsarigradsko Shose 73",
address2: nil,
address3: nil,
city: "Sofia",
countryCode: "BG",
state: nil,
postalCode: "1000"))
cardService.create(request: request, completion: { result in
switch result {
case .success(let card):
// Display created card
case .failure(let error):
// Handle error
}
})
Update prepaid card status
The customer can change/update the status of their prepaid cards, and as a result, lock/unlock or cancel their prepaid card.
CardUpdateRequest
Parameter | Type | Description | Example |
---|---|---|---|
status |
CardStatus | Optional parameter containing information about the different card statuses. | CardStatus.ACTIVE / .active |
statusReason |
String | Optional parameter for specifying the reason for card status change. | "User Activate Card from LOCKED status" |
pin |
String | Optional parameter. The card pin should be 4 digits. | "1243" |
val cardUpdateRequest = CardUpdateRequest(
status = Status.ACTIVE,
statusReason = "User Activate Card from LOCKED status",
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())
}
let request = Wallet.CardUpdateRequest(status: .active,
statusReason: "User Activate Card from LOCKED status",
pin: "1234"
cardService.update(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24", request: request, completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
Get prepaid card details
Card sensitive information can be retrieved.
The response contains card sensitive information such as pan
, cvv
, expiry
, cardHolderName
.
try {
val cardSensitiveDetails = cardService.getDetails(cardId = "f16ba382-eb42-481a-b08f-c57bdc9aae24")
Log.d(TAG, cardSensitiveDetails.toString())
} catch (exception: Exception) {
Log.d(TAG, exception.toString())
}
cardService.getDetails(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24", completion: { result in
switch result {
case .success(let cardDetails):
// Display card details
case .failure(let error):
// Handle error
}
})
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())
}
cardService.getPrograms(completion: { result in
switch result {
case .success(let programs):
// Display prepaid programs
case .failure(let error):
// Handle error
}
})
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.
Parameter | Type | Description | Example |
---|---|---|---|
walletType |
MobileWalletType | Required parameter containing supported wallet types. | MobileWalletType.GOOGLE_PAY / .applePay |
clientDeviceId / clientDeviceID |
String | Required for cards with VISA schema + Samsung/Google Pay. Not required for VISA + Apple Pay. | "WNkcsfZPiKfa5PrH3jilkQYT" |
clientWalletAccountId / clientWalletAccountID |
String | Required for cards with VISA schema + Samsung/Google Pay. | "40exmLiWV1iV55ZVstOAiMf7" |
leafCertificate |
String | Required for iOS. The leaf certificate returned by the wallet provider, that was signed using subordinateCertificate. Should be converted to hexadecimal (case insensitive) binary data of the certificate. If the certificate is in PEM encoding, then it should follow the typical standards for PEM. In particular, the tags -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- should be present and the Base64-encoded value should have lines of length 64. | |
subordinateCertificate |
String | Required for iOS. The subordinate certificate returned by the wallet provider that was signed using the Wallet Provider’s Certificate Authority (CA) certificate. Should be converted to hexadecimal (case insensitive) binary data of the certificate. If the certificate is in PEM encoding, then it should follow the typical standards for PEM. In particular, the tags -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- should be present and the Base64-encoded value should have lines of length 64. | |
nonce |
String | Required for iOS. Hexadecimal (case insensitive) nonce value returned by the Apple Pay SDK | "w1koQg==" |
nonceSignature |
String | Required for iOS. Hexadecimal (case insensitive) nonce signature value returned by the Apple Pay SDK. | "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 are:activationData
,cardNetwork
,tokenProvider
,customer
,cardLastDigits
.customer
is an object containing information about specific card holder such as:address1
,address2
,address3
,city
,countryCode
,state
,postalCode
,name
,mobile
CardNetworkType
Enum class for representing cardNetwork
.
Android | iOS | Description |
---|---|---|
CARD_NETWORK_VISA |
.cardNetworkVisa |
Visa card network. |
CARD_NETWORK_MASTERCARD |
.cardNetworkMastercard |
Mastercard card network. |
UNKNOWN |
.unknown |
The network of the card is unknown or cannot be determined. |
CardTokenProvider
Enum class for representing tokenProvider
.
Android | iOS | Description |
---|---|---|
TOKEN_PROVIDER_VISA |
.tokenProviderVisa |
Visa token provider. |
TOKEN_PROVIDER_MASTERCARD |
.tokenProviderMastercard |
Mastercard token provider. |
UNKNOWN |
.unknown |
The 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())
}
let leafCertificate = "<base64 encoded certificate>"
let subordinateCertificate = "<base64 encoded certificate>"
let nonce = "<encoded value>"
let nonceSignature = "<encoded value>"
let request = Wallet.CardTokenizationRequest(leafCertificate: leafCertificate,
subordinateCertificate: subordinateCertificate,
nonce: nonce,
nonceSignature: nonceSignature)
cardService.tokenize(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24", request: request, completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
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
Parameter | Type | Description | Example |
---|---|---|---|
dpanRef |
String | Required parameter. dpan (Device Pan) reference. | "DAPLMC00002125433c0c34a2821f4f86866e7576963baf8b" |
walletId |
String | Optional parameter. Wallet Id used only with Google Pay. | "NMZlGi8-DezZZKaU06orvl0f" |
walletType |
MobileWalletType | Required parameter. The card pin should be 4 digits. | MobileWalletType.GOOGLE_PAY / .applePay |
status |
MobileWalletStatus | Required 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())
}
let request = Wallet.MobileWallet(dpanRef: "DAPLMC00002125433c0c34a2821f4f86866e7572963baf8b",
status: .completed)
cardService.updateTokenizationStatus(cardID: "f16ba382-eb42-481a-b08f-c57bdc9aae24", request: request, completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
Apple card tokenzation
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 the link provided below: https://developer.apple.com/documentation/passkit_apple_pay_and_wallet/apple_pay/setting_up_apple_pay/
Apple Pay button
"Add to Apple Wallet" Button should be only 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 exists in
passes()
andremoteSecureElementPasses
. - Display "Add to Apple Wallet" button using PKAddPassButton if conditions are met.
- Tapping the button triggers In-App provisioning.
In-App provisioning
Once the user taps the 'Add to Apple Wallet' button, In-App Provisioning starts by initializing and presenting PKAddPaymentPassViewController.
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.instace.cardService.tokenize(cardID: card.cardID,
request: tokenizationRequest,
completion: { result in
switch result {
case .success(let cardTokenization):
let pkAddPaymentPassRequest = PKAddPaymentPassRequest()
pkAddPaymentPassRequest.activationData = Data(base64Encoded: cardTokenization.activationData)
pkAddPaymentPassRequest.encryptedPassData = Data(base64Encoded: cardTokenization.encryptedPassData)
pkAddPaymentPassRequest.ephemeralPublicKey = Data(base64Encoded: cardTokenization.ephemeralPublicKey)
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.
let request = Wallet.MobileWallet(dpanRef: tokenization.dpanRef,
status: .completed)
cardService.updateTokenizationStatus(cardID: card.cardID,
request: request,
completion: { result in
switch result {
case .success(let card):
// Display card
case .failure(let error):
// Handle error
}
})
Google card tokenzation
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.
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_NAOCTIVE_WALLET and secondly, if there is no match between the tokens retrieved from TapAndPay SDK and the tokens retrived from Paysafe Mobile SDK.
The following sequence diagram outlines the necessary steps for Push Tokenization.
Manual Tokenization
This flow is initiated when listing the tokens affiliated with a particular wallet and discovering a token in the TapAndPay.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION state related to the selected card.
When a token is in TapAndPay.TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION state, manual verification becomes necessary as an additional step to authenticate a card for a specific user.
The following sequence diagram outlines the necessary steps for Manual Tokenization.
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.