Search…
Manage Virtual Cards
Provides a virtual credit card that a user can use to make purchases
Virtual cards are the cornerstone of the Virtual Cards SDK. Virtual cards are used as a proxy between a user's personal Funding Source and online merchants.
In order to provision and use a virtual card, a Sudo and Funding Source are required.
Virtual cards make an important distinction between two subtypes: ProvisionalCard and Card.
A ProvisionalCard is a card that is currently being provisioned. This will typically exist in 3 different states: PROVISIONING, COMPLETED, and FAILED. It also contains a property card which contains the provisioned card, when it has COMPLETED. This type is transitory and should only be used for determining whether a card is provisioning, or has failed provisioning. Accessing provisioned cards should be done using methods that return Card types.
A Card is a card that has successfully finished provisioning. This is the card type that represents virtual cards.

Closed Cards

Cards will be closed and set to a CLOSED state under the following circumstances:
    The card has expired; this occurs when your card reaches its expiry date.
    The card has been closed via the cancel card API.
    The Sudo owning the card has been deleted.
While provisional card records are transient, a card record is not. Cards will always be available for access via access methods even when closed. It is important to maintain a record of closed cards so that users can still examine the transaction history and also see any refunds that may still occur against the closed card from merchants where the card was used before the card was closed.

Orphan Cards

When a Sudo is deleted, any virtual cards owned by that Sudo will be closed and the entry for the Sudo in the owners property of any virtual cards owned by that Sudo will be removed. Such cards are called orphan cards and can be identified by looking for cards with no Sudo owner in the owners property. Sudo owners are identified as owners with issuer of value sudoplatform.sudoservice.

Creating a Virtual Card

A virtual card is provisioned by calling the provision card API.
The input parameter (ProvisionCardInput) requires some fields in order to correctly provision the card. sudoId and fundingSourceId are the identifiers associated with a Sudo and FundingSource, respectively.
Input properties cardHolder, alias, billingAddress, and currency are indicative of the user's information that they wish to be associated with the card. cardHolder is the name that will appear on the card and is needed to perform transactions. alias is the name that the user can use to personalize/categorize their card (e.g: Shopping, Work, Clothes, etc). billingAddress is an optional property that the user can provide. If this is left nil/null, the billingAddress will be set by the platform itself. The currency is the three character ISO 4217 alphabetic currency code of the currency in which the card is to be denominated.
Currently only USD is supported.
iOS
Android
When provisioning a card for the first time, a key pair is generated on the Apple Keychain using the RSA algorithm. It is important to encourage a user to backup this key to ensure that they can access their data in the event that the key is somehow lost. This key is linked to the keyNamespace property used when initializing a DefaultSudoVirtualCardsClient (for more info, see here).
The provisionCardWithInput method contains two event handling properties: completion and observer. The completion returns the result of the provision card call.
To check the provisioning state after calling this API, calls to getCardsWithFilter can be used, however the recommended way is to use a ProvisionCardObservable.
The ProvisionCardObservable is a protocol that facilitates the ability to track the state changes of a card during its provisioning cycle. This observer is required if expecting to gain access to the card once it has finished provisioning. Alternatively, you could also query a list of the user's cards to see if it has finished provisioning.
An example implementation on your view controller ProvisionVirtualCardViewControllerfor observer conformance may look like this:
ProvisionVirtualCardViewController.swift
1
class ProvisionVirtualCardViewController: UIViewController, ProvisionCardObserable {
2
​
3
// ... Other view controller details
4
​
5
func provisioningStateDidChange(_ state: ProvisionalCard.State, card: Card?) {
6
switch state {
7
case .provisioning:
8
// Notify the user that their card is currently provisioning
9
case .completed:
10
// if completed, card should not be nil
11
if let card = card {
12
// Handle success response
13
} else {
14
// Handle error
15
}
16
case .failed, .unknown:
17
// Handle error
18
}
19
}
20
​
21
func errorOccurred(_ error: Error) {
22
// Handle error
23
}
24
}
Copied!
You may now provision a virtual card:
ProvisionVirtualCardViewController.swift
1
class ProvisionVirtualCardViewController: UIViewController, ProvisionCardObserable {
2
​
3
// ... Other view controller details
4
​
5
// Ensure you have a reference somehow to a `DefaultSudoVirtualCardsClient` instance.
6
let virtualCardsClient: SudoVirtualCardsClient
7
​
8
func provisionVirtualCard() {
9
// Collect the input details on your form however makes sense for your use case.
10
let provisionInput = ProvisionCardInput(
11
sudoId: sudo.id,
12
fundingSourceId: fundingSource.id,
13
cardHolder: input.cardHolder,
14
alias: input.alias,
15
billingAddress: input.billingAddress,
16
currency: input.currency)
17
self.virtualCardsClient.provisionCardWithInput(
18
provisionInput,
19
completion: { result in
20
switch result {
21
case let .failure(cause):
22
// Handle error
23
case .success:
24
// If using an observer, we don't need to do any extra work here.
25
break
26
},
27
observer: self)
28
}
29
}
Copied!
A call to the provisionCard method initiates the process of provisioning a virtual card and will return a ProvisionalCard on completion. During this process the ProvisionalCard traverses between a PROVISIONING and COMPLETED state.
To check the provisioning state after calling this API, calls to getProvisionalCard is used to poll for state changes of a card during its provisioning cycle. Once the ProvisionalCard has reached a COMPLETED state, the fully provisioned card can be accessed.
An example implementation of creating a virtual card is:
1
// val sudo: Sudo
2
// val fundingSource: FundingSource
3
val billingAddress = BillingAddress(
4
addressLine1 = "123 Street Rd",
5
addressLine2 = null,
6
city = "Salt Lake City",
7
state = "UT",
8
postalCode = "84044",
9
country = "US"
10
)
11
val provisionInput = ProvisionCardInput(
12
sudoId = sudo.id,
13
fundingSourceId = fundingSource.id,
14
cardHolder = "John Smith",
15
alias = "Shopping",
16
billingAddress = billingAddress,
17
currency = "USD"
18
)
19
launch {
20
try {
21
val initialProvisionalCard = withContext(Dispatchers.IO) {
22
virtualCardsClient.provisionCard(
23
provisionInput
24
)
25
}
26
var state = initialProvisionalCard.state
27
var card: Card? = null
28
while (state == ProvisionalCard.State.PROVISIONING) {
29
val provisionalCard = withContext(Dispatchers.IO) {
30
virtualCardsClient.getProvisionalCard(initialProvisionalCard.id)
31
}
32
if (provisionalCard?.state == ProvisionalCard.State.COMPLETED) {
33
card = provisionalCard.card
34
break
35
}
36
state = provisionalCard?.state ?: ProvisionalCard.State.PROVISIONING
37
}
38
// The returned [card] has been created.
39
} catch (e: CardException) {
40
// Handle/notify user of exception
41
}
42
}
Copied!

Updating a Virtual Card

The user may want to update details on their virtual card. To do so, the update card API is used.
The modifiable fields of a card are contained in the UpdateCardInput:
    cardHolder
    alias
    billingAddress
The id property must match the id of the card that is being updated.
When updating a card, ensure that all properties that are remaining unchanged are populated. For example, if you have a card (userCard) that has the following properties:
1
{
2
"id": "card-id-1",
3
"cardHolder": "John Smith",
4
"alias": "Shopping",
5
"billingAddress": nil
6
}
Copied!
If you only want to update the alias to "Banking", then the API should be called like so:
Swift
Kotlin
1
let input = UpdateCardInput(
2
id: userCard.id, // card-id-1
3
cardHolder: userCard.cardHolder, // John Smith
4
alias: "Banking",
5
billingAddress: userCard.billingAddress) // nil
6
​
7
client.updateCardWithInput(input) { result in
8
switch result {
9
case let .success(updatedCard):
10
/*
11
* `updatedCard` contains the updated card, with the previous details intact
12
* and `alias` updated to "Banking".
13
*/
14
case let .failure(error):
15
// Handle error.
16
}
17
}
Copied!
1
val input = UpdateCardInput(
2
id = userCard.id, // card-id-1
3
cardHolder = userCard.cardHolder, // John Smith
4
alias = "Banking",
5
billingAddress = userCard.billingAddress // null
6
)
7
launch {
8
try {
9
val updatedCard = withContext(Dispatchers.IO) {
10
virtualCardsClient.updateCard(input)
11
}
12
/*
13
* [updatedCard] contains the updated card, with the previous details intact
14
* and [alias] updated to "Banking".
15
*/
16
} catch (e: CardException) {
17
// Handle exception
18
}
19
}
Copied!

Cancelling a Virtual Card

If a user decides they want to close a virtual card, this is performed by calling the cancel card API.
This API call is idempotent, so any subsequent call to cancel with the same id will always yield the same result.
When cancelling a card, the card will not be deleted, but instead will be transitioned to the CLOSED state and remain accessible via retrieval methods. Closed cards remain available so that transaction history can be retained and so that refunds that happen after the card is closed can be shown.
Once a card is cancelled and its state is set to CLOSED, it cannot be reactivated - a new card will need to be provisioned.
iOS
Android
To cancel a card, the id of a provisioned card must be passed into the cancelCardWithId method. As this API is asynchronous, the result will be returned via the closure completion. If the input id does not match a previously provisioned card, a SudoVirtualCardsError.cardNotFound will be returned.
An example implementation of cancelling a card on your CancelVirtualCardViewController may look like this:
CancelVirtualCardViewController.swift
1
class CancelVirtualCardViewController: UIViewController {
2
​
3
// .. Implementation details go here
4
​
5
// Ensure you have a reference somehow to a `DefaultSudoVirtualCardsClient` instance.
6
let virtualCardsClient: SudoVirtualCardsClient
7
​
8
func cancelVirtualCard() {
9
// Collect the id on your form however makes sense for your use case.
10
self.virtualCardsClient.cancelCardWithId(id) { result in
11
switch result {
12
case let .failure(cause):
13
// Handle/notify user of error
14
case let .success(card):
15
// The returned card is now cancelled.
16
}
17
}
18
}
Copied!
If a call to cancelCardWithId returns a success result from the completion handler, the virtual card with the associated id will now be in a closed state and no longer usable. If a failure is returned instead, the cause.localizedDescription can be presented to the user to notify them as to what caused the error.
To cancel a card, the id of a provisioned card must be passed into the cancelCard method. If the input id does not match a previously provisioned card, a SudoVirtualCardsClient.CardException.CardNotFoundException will be thrown.
An example implementation of cancelling a virtual card is:
1
// Collect the id on your form however makes sense for your use case.
2
launch {
3
try {
4
val card = withContext(Dispatchers.IO) {
5
virtualCardsClient.cancelCard(id)
6
}
7
// The returned [card] is now cancelled.
8
} catch (e: CardException) {
9
// Handle/notify user of exception
10
}
11
}
Copied!
The call to cancelCard will return the card with the associated id in a CLOSED state and will no longer be usable. If an exception occurs, the e.localizedMessage can be presented to the user to notify them as to what caused the exception.

Accessing Virtual Cards

To provide the ability for a user to access previously provisioned (or closed and suspended) virtual cards, the get and list card APIs are used.
The former is used to access a specific virtual card by its id, whereas the latter is used to access a list of virtual cards. Both of these APIs support the use of data Caching through the use of the cachePolicy property.

Accessing a Single Card

iOS
Android
To access a single virtual card record, getCardWithId can be used. If the input id property does not match an existing record, a nil response will be returned.
An example use case for accessing the virtual card may exist in a view controller named AccessVirtualCardsViewController:
AccessVirtualCardsViewController.swift
1
class AccessVirtualCardsViewController: UIViewController {
2
​
3
// .. Implementation details go here
4
​
5
// Ensure you have a reference somehow to a `DefaultSudoVirtualCardsClient` instance.
6
let virtualCardsClient: SudoVirtualCardsClient
7
​
8
func accessVirtualCardWithId(_ id: String) {
9
self.virtualCardsClient.getCardWithId(
10
id,
11
cachePolicy: .useOnline
12
) { result in
13
switch result {
14
case let .failure(cause):
15
// Handle/notify user of error
16
case let .success(card):
17
// If the id matches a card, `card` will be returned, else nil
18
}
19
}
20
}
Copied!
The getCard method is used to retrieve details of a virtual card given its id. If the input id property does not match an existing virtual card, a null response will be returned.
An example implementation of accessing a virtual card is:
1
launch {
2
try {
3
val card = withContext(Dispatchers.IO) {
4
virtualCardsClient.getCard(
5
id,
6
cachePolicy = CachePolicy.REMOTE_ONLY
7
)
8
}
9
// If the [id] matches a card, [card] will be returned, else [null]
10
} catch (e: CardException) {
11
// Handle/notify user of exception
12
}
13
}
Copied!

Accessing Multiple Cards

iOS
Android
A user can have multiple cards across multiple sudos, and some may be issued, while some are closed or suspended etc. To access multiple records at once, getCardsWithFilter may be used. If no results matching the input are found, an empty ListOutput will be returned.
This method can be used to restrict the results by providing a GetCardsFilterInput. For more general details on filtering, see here.
Cards support filtering on the state property only. Provisional Cards support filtering on clientRefId property only.
Multiple access does support Pagination. For more details, see here.
An example use case for accessing multiple virtual cards may exist in the same view controller above named AccessVirtualCardsViewController:
AccessVirtualCardsViewController.swift
1
class AccessVirtualCardsViewController: UIViewController {
2
​
3
// .. Implementation details go here
4
​
5
// Ensure you have a reference somehow to a `DefaultSudoVirtualCardsClient` instance.
6
let virtualCardsClient: SudoVirtualCardsClient
7
​
8
func accessVirtualCards() {
9
virtualCardsClient.getCardsWithFilter(
10
filter: nil,
11
limit: 10,
12
nextToken: nextToken,
13
cachePolicy: .useOnline
14
) { result in
15
switch result {
16
case let .failure(cause):
17
// Handle/notify user of error
18
case let .success(cards):
19
// `cards` contains the virtual cards that match the input.
20
}
21
}
22
}
Copied!
A user can have multiple cards across multiple sudos, and some may be issued, while some are closed or suspended etc. To access multiple cards at once, listCards is used. If no results matching the input are found, an empty ListOutput will be returned.
Provide a CardsFilter parameter to restrict results. For more general details on filtering, see here.
Cards support filtering on the state property only.
Multiple access does support Pagination. For more details, see here.
An example implementation of accessing multiple virtual cards is:
1
launch {
2
try {
3
val cards = withContext(Dispatchers.IO) {
4
virtualCardsClient.listCards(
5
filterCardsBy {
6
state beginsWith "ISSUED"
7
},
8
limit = 20,
9
nextToken = nextToken,
10
cachePolicy = CachePolicy.REMOTE_ONLY
11
)
12
}
13
// [cards] contains the cards that match the input.
14
} catch (e: CardException) {
15
// Handle/notify user of exception
16
}
17
}
Copied!
Last modified 1yr ago