Manage Funding Sources
Provides the essentials for a user to link their real payment methods with a virtual card.
Funding sources supply the means for a user to link a real funding source with a virtual card. A user can supply their checking or savings bank account, or their credit or debit card as a method to fund a transaction performed on the virtual card.
Creating a Funding Source
A funding source is created by invoking a series of steps from the retrieval of funding source configuration to setup and completion of the creation process.
To begin the process of creating a funding source, call the setupFundingSource
method. The funding source providers that your client application supports will inform which provider names you provide to the setupFundingSource
method call. Funding source providers supported by the Virtual Cards service include stripe
and checkout
. You must also provide the name of your application to the setupFundingSource
method. This name forms part of a shared configuration between your application, the Virtual Cards service, and any third party funding source providers you have a relationship with. Contact us if you are not certain what application name to use.
Bank Account based Funding Source
Bank account funding sources must be created using checkout
as the funding source provider.
try {
const provisionalFundingSource = await virtualCardsClient.setupFundingSource({
currency: 'USD',
type: FundingSourceType.BankAccount,
applicationName: 'yourApplicationName',
// include only the provider(s) you will support
supportedProviders: ['checkout'],
})
} catch (error) {
// Handle/notify user of errors
}
The call to setupFundingSource
will return a ProvisionalFundingSource
containing provisioning data required to continue the funding source creation process.
Plaid Financial Institution Selection
In order to continue the setup of a bank account funding source, a financial instiution containing the bank account used to fund the funding source must be selected. Plaid is the provider used to perform this process.
You must use the Plaid Link Web SDK in order to launch the Plaid Link flow to select a financial institution to fund the bank account funding source. The ProvisionalFundingSource.provisioningData
property contains a linkToken
required to instantiate the Plaid Link flow.
The provisioning data also contains and authorizationText
agreement which must be displayed and accepted by the user before creating the bank account funding source as governed by the NACHA organization.
Completing Funding Source Setup with Provider Specific Data
Once the Plaid Link flow has completed and data surrounding the selected bank account has been obtained, construct a provider specific completion data object and call the completeFundingSource
method to finish the process of provisioning a funding :
const linkSuccess: LinkSuccess = /** Result from Plaid Link flow */
const completionData: CompleteFundingSourceCheckoutBankAccountCompletionDataInput =
{
provider: 'checkout',
type: FundingSourceType.BankAccount,
publicToken: linkSuccess.public_token,
institutionId: linkSuccess.metadata.institution?.institution_id,
accountId: linkSuccess.metadata.accounts[0].id,
authorizationText: provisioningData.authorizationText[0],
}
try {
const fundingSource = await virtualCardsClient.completeFundingSource({
id: provisionalFundingSource.id,
completionData
})
} catch (error) {
// Handle error
}
Testing bank account funding sources in sandbox
During application development, you will be using a sandbox Sudo Platform environment. The sandbox environment allows you to test bank account funding sources without requiring the full integration of the Plaid Link user interface.
The sandboxGetPlaidData
method allows you to obtain the account ID and public token required to submit as completion data without the user interface.
This API is not available in production environments.
Plaid documents IDs of institutions available for sandbox testing here.
Sudo Platform pre-defines test accounts with particular behaviours:
custom_checking_500
User with a single checking account that always reports a balance of $500.00.
custom_checking_low
User with a single checking account that always reports a balance of $10.00
custom_identity_mismatch
User with a single account where the owner does not sufficiently match the details of the user able to pass identity verification in the sandbox environment.
The following example shows how to use the sandboxGetPlaidData
API on each platform:
try {
const institutionId = 'ins_109508'
const data = await virtualCardsClient.sandboxGetPlaidData({
institutionId,
plaidUsername: 'custom_checking_500'
})
const completionData: CompleteFundingSourceCheckoutBankAccountCompletionDataInput =
{
provider: 'checkout',
type: FundingSourceType.BankAccount,
authorizationText: provisioningData.authorizationText[0],
// Must match institution ID passed to sandboxGetPlaidData
institutionId,
// Returned by sandboxGetPlaidData
publicToken: data.publicToken,
accountId: data.accountMetadata[0].accountId,
}
// Complete funding source ...
}
catch (err) {
// handle error
}
Card based Funding Source
You can create a credit card funding source using either stripe
or checkout
as the funding source provider.
try {
const provisionalFundingSource = await virtualCardsClient.setupFundingSource({
currency: 'USD',
type: FundingSourceType.CreditCard,
applicationName: 'yourApplicationName',
// include only the provider(s) you will support
supportedProviders: ['stripe', 'checkout'],
})
} catch (error) {
// Handle/notify user of errors
}
The call to setupFundingSource
will return a ProvisionalFundingSource
containing provisioning data required to continue the funding source creation process. To continue, you must use either the Stripe API or the checkout.com API to progress the funding source setup; the chosen provider must match that returned in the provisionalFundingSource.provisioningData
property.
Retrieve the provider API key as well as other information pertaining to the funding source provider using the getFundingSourceClientConfiguration
method:
try {
const configuration =
await virtualCardsClient.getFundingSourceClientConfiguration()
// [configuration] contains the list of funding source client configuration.
} catch (error) {
// Handle/notify user of errors
}
Stripe Funding Source Preparation
To setup a payment intent in Stripe, call StripeClient.confirmSetupIntent(confirmSetupIntentParams: ConfirmSetupIntentParams)
and ensure that the ConfirmSetupIntentParams
input parameter is populated with card details and billing details that you wish to add as a funding source.
Card and Billing Details are required.
Once a Setup Intent has been setup, call completeFundingSource
to finish provisioning the FundingSource
.
checkout.com Funding Source Preparation
To retrieve the tokenized card details as a payment token, handle the cardTokenized
event in the checkout.com Frames integration. The token is returned in the token
property of the event.
Completing Funding Source Setup with Provider Specific Data
Once the provider-specific data has been obtained, construct a provider specific completion data object and call the completeFundingSource
method to finish the process of provisioning a funding source:
The completeFundingSource
call accepts an optional updateCardFundingSource
parameter (default value is true). This parameter is a flag to indicate whether to automatically update unfunded cards' funding source when a new funding source is created:
// For Stripe
const completionData: CompleteFundingSourceStripeCardCompletionDataInput = {
provider: 'stripe',
type: FundingSourceType.CreditCard,
paymentMethod: setupIntentPaymentMethodAsString, // Retrieve from Stripe API
}
/// OR
// For Checkout.com
const completionData: CompleteFundingSourceCheckoutCardCompletionDataInput = {
provider: 'checkout',
type: FundingSourceType.CreditCard,
paymentToken: paymentTokenReturnedFromFrames, // Retrieve from Checkout.com API
}
try {
const fundingSource = await virtualCardsClient.completeFundingSource({
id: provisionalFundingSource.id,
completionData,
// Optional flag to indicate whether to automatically update unfunded cards' funding source when a new funding source is created (defaults to true)
updateCardFundingSource: true,
} catch (error) {
if (error instanceof FundingSourceRequiresUserInteractionError) {
// Handle additional user interaction
}
else {
// Handle error
}
}
Continuing User Interaction
The completeFundingSource
method can throw a FundingSourceRequiresUserInteraction
exception. This indicates that further interaction with the user is required before the funding source setup process can be completed.
The thrown exception contains data indicating what action needs to be taken along with the source of the interaction requirement. The need for further interaction can arise because a particular payment processor needs to perform strong authentication (for example 3DS2). The requirement to handle this case is dependent on which provider(s) you are supporting in your application.
The following table indicates the types of user interaction that may be returned and how they should be handled:
Checkout.com strong authentication
Interaction data type is CheckoutCardInteractionData
Mobile applications should launch a web view targeting the URL specified in the redirectUrl
property of the interaction data contained in the error.
Web applications should navigate to the URL specified in the redirectUrl
property of the interaction data contained in the error.
Call completeFundingSource
again, on successful completion of the authentication, with no payment token specified in the completion data.
Failure during payment processor interaction
If there is any failure returned from the payment processor (Stripe or Checkout.com) prior to calling completeFundingSource
, then the provisional funding source returned by setupFundingSource
method may be re-used for multiple attempts at completeFundingSource
.
Examples of failures include the user entering incorrect expiry, billing address or security code information for a card.
Cancelling a Provisional Funding Source
If a user would like to cancel a provisional funding source before it is completed, they can call the cancelProvisionalFundingSource
method. This method cancels setting up the funding source at the funding source provider and places the provisional funding source in the failed
state.
// Collect the input provisionalFundingSourceId as appropriate
// for your implementation.
try {
const provisionalFundingSource = await virtualCardsClient.cancelProvisionalFundingSource(
provisionalFundingSourceId
)
// The returned `provisionalFundingSource` is now cancelled and is in a Failed state.
} catch {
// Handle/notify user of errors
}
Unfunded Funding Sources
If a user incurs a debit on a virtual card backed by a Bank Account funding source that cannot be fulfilled - generally due to insufficient funds in the bank account at the time the debit is presented - the funding source will be flagged as Unfunded. Funding sources in this condition will have the value UNFUNDED
in the flags
field of the funding source returned from the listFundingSources
or getFundingSource
methods.
If a user has an Unfunded funding source, very limited transaction velocities based on system policy will apply to all virtual cards owned by that user until the funding source is funded again.
Additionally, the user is not permitted to cancel an Unfunded funding source.
Unfunded funding sources also include the attribute unfundedAmount
which indicates the amount by which the funding source is unfunded in the currency of the funding source.
In order to remove this flag from the funding source, the user should add the appropriate value of funds to their bank account and invoke the reviewUnfundedFundingSource
endpoint with the appropriate funding source identifier.
The funding source will be reviewed and if the funds are available, will be marked as funded, restoring normal permitted transaction velocity for the user.
Transactions which cause a funding source to become unfunded may incur an additional service fee applied against that funding source.
try {
const fundingSource = await virtualCardsClient.reviewUnfundedFundingSource({
id,
})
// `fundingSource` contains the fundingSource object. Note that the review process
// occurs asynchronously and may take some time to complete so even if the
// immediately-returned funding source is still unfunded, the client may wait for
// notification of funding source update or poll to detect a successful review.
} catch {
// Handle/notify user of errors
}
Cancelling a Funding Source
If a user decides they want to remove a funding source from their account, the ability to provide them with the means to do so is applied with the cancelFundingSource
method.
When a funding source is cancelled, any associated active virtual cards will no longer be able to process transactions. Any such transactions will be declined.
This API call is idempotent, so cancelling an already-cancelled funding source will yield the same result.
When a funding source is cancelled, the record of the funding source will not be deleted, but instead its state will be set to INACTIVE
and will no longer be usable for provisioning virtual cards. This is because refunds are always returned to the same original funding source and so clients will need to be able to retain details about cancelled funding in case any such future refunds refer to them.
Any existing virtual cards funded by a cancelled funding source will become unfunded. These can be funded by creating a new funding source and setting the updateCardFundingSource
parameter in the completeFundingSource
method call to true.
Clients should check the result of the cancelFundingSource
request to ensure that the funding source was able to be cancelled.
Unfunded funding sources are not permitted to be cancelled and such an attempt will result in a FundingSourceStateError
being returned.
// Collect the input id however makes sense for your implementation.
// const fundingSourceId = fundingSource.id
try {
const fundingSource = await virtualCardsClient.cancelFundingSource(
fundingSourceId
)
// The returned `fundingSource` is now cancelled and in an INACTIVE state.
} catch {
// Handle/notify user of errors
}
Refreshing a Funding Source
Bank account funding sources may periodically require the user to re-authorize access to them or for the user to login again. When this is necessary, the funding source have a REFRESH flag set on it. Funding sources requiring a refresh continue to be usable in a limited manner; virtual cards backed by these funding sources may be used for transactions but no new virtual cards will be available. Commonly, requiring a refresh is a precursor to the funding source being cancelled by the provider and so action should be taken in a reasonable time frame to restore such funding sources.
In order to restore a funding source to a fully usable, active state, clients should refresh any such funding sources using the refreshFundingSource
method.
try {
const fundingSource = await virtualCardsClient.refreshFundingSource({
id: fundingSourceId,
refreshData: providerSpecificRefreshData,
})
} catch (error) {
if (error instanceof FundingSourceRequiresUserInteractionError) {
// Handle additional user interaction
}
else {
// Handle error
}
}
Continuing User Interaction
As with completeFundingSource
, the refreshFundingSource
method can throw a FundingSourceRequiresUserInteraction
exception. This indicates that further interaction with the user is required before the funding source refresh process can be completed.
The thrown exception contains data indicating what action needs to be taken along with the source of the interaction requirement. The need for further interaction can arise because a particular payment processor needs to perform separate authentication (for example OAuth). The requirement to handle this case is dependent on which provider(s) you are supporting in your application.
The following table indicates the types of user interaction that may be returned and how they should be handled:
Plaid bank account authentication and linkage.
Interaction data type is CheckoutBankAccountRefreshFundingSourceInteractionData
Mobile and web applications should invoke Plaid Link, providing the token specified in the linkToken
property of the interaction data contained in the error.
The user must then enter their credentials to complete the authentication with their financial institution.
Call refreshFundingSource
again, on successful completion of the Link, providing authentication data as with completeFundingSource
.
Testing funding source refresh
During application development, you will be using a sandbox Sudo Platform environment. The sandbox environment allows you to test bank account funding sources without requiring occurrence of events that would normally put a funding source in to the refresh state.
The sandboxSetFundingSourceToRequireRefresh
method allows you to set a bank account funding source to this state.
This API is not available in production environments.
Once invoked, a short time in the future the funding source will have the refresh flag set.
The following examples show how to invoke the sandboxSetFundingSourceToRequireRefresh
method on each platform:
try {
await virtualCardsClient.sandboxSetFundingSourceToRequireRefresh({
fundingSourceId: 'bankAccountFundingSourceid'
})
}
catch (err) {
// Handle error
}
Retrieving Funding Sources
Previously created (or cancelled) funding sources can be accessed in two ways: via its identifier (Single Funding Source by Id), or via a list method (List Funding Sources).
A fetch of single or multiple funding sources can be performed remotely or locally by specifying the appropriate CachePolicy as part of the input.
Single Funding Source by Id
To retrieve a single funding source given its unique id
, use the getFundingSource
method. This method will return the record in an active or inactive state if it exists.
try {
const fundingSource = await virtualCardsClient.getFundingSource({
id,
cachePolicy: CachePolicy.RemoteOnly,
})
// `fundingSource` contains the fundingSource object, else `undefined` if not found.
} catch {
// Handle/notify user of errors
}
List Funding Sources
The ability to retrieve multiple or all funding sources available to the user is supported. Returned funding sources are sorted based on time of last update, with the default ordering being most recent first. The results can also be filtered and/or paginated and can contain funding sources which may be active whilst others are inactive. To return least recently updated first, provide the sortOrder parameter with a value of ascending.
A call to a list API will return a ListOutput
object containing a list of matching items
and a nextToken
to support pagination. If no results matching the input are found, the result will contain empty items. Note that an empty items list does not necessarily mean that all items have been retrieved. Always check the value of the returned nextToken
, there are no more results to retrieve when nextToken
is null.
try {
const filter: FundingSourceFilterInput = {
and: [
{ id: { eq: idToGet } },
{ state: { eq: FundingSourceState.Active } },
],
}
const listOutput = await virtualCardsClient.listFundingSources({
filter,
sortOrder: SortOrder.Asc,
cachePolicy: CachePolicy.RemoteOnly,
limit: 20,
nextToken,
})
// `listOutput.items` contains the list of items matching the input.
// Page through the results if `listOutput.nextToken` != undefined.
} catch {
// Handle/notify user of errors
}
Listing Provisional Funding Sources
Provisional funding sources are created by calls to the setupFundingSource
method. The ability to retrieve multiple or all provisional funding sources available to the user is supported. Returned provisional funding sources are sorted based on time of last update, with the default ordering being most recent first. The results can also be filtered and/or paginated. To return least recently updated first, provide the sortOrder parameter with a value of ascending.
A call to a list API will return a ListOutput
object containing a list of matching items
and a nextToken
to support pagination. If no results matching the input are found, the result will contain empty items. Note that an empty items list does not necessarily mean that all items have been retrieved. Always check the value of the returned nextToken
, there are no more results to retrieve when nextToken
is null.
try {
const filter: ProvisionalFundingSourceFilterInput = {
and: [
{ id: { eq: idToGet } },
{ state: { eq: ProvisionalFundingSourceState.Provisioning } },
],
}
const listOutput = await virtualCardsClient.listProvisionalFundingSources(
{
filter,
sortOrder: SortOrder.Asc,
cachePolicy: CachePolicy.RemoteOnly,
limit: 5,
nextToken,
},
)
// `listOutput.items` contains the list of items matching the input.
// Page through the results if `listOutput.nextToken` != undefined.
} catch {
// Handle/notify user of errors
}
Subscribing to Funding Source Changes
Consumers of the SDK may choose to subscribe to notifications of changes to Funding Sources from the Sudo Platform rather than polling. This is done via the subscribeToFundingSourceChanges
method. Subscribing is done across all Funding Sources associated with the client, not on a per-funding-source basis.
This method accepts a unique subscription identifier and a subscriber object which must implement the fundingSourceChanged(fundingSource: FundingSource): Promise<void>
method. This handler should contain application-specific implementation of behavior in the event of a funding source change. The subscription object should also implement the connectionStatusChanged(state: ConnectionState): void
method which is invoked in the event of subscription connection state changes between CONNECTED
and DISCONNECTED
. This handler allows the consumer to detect when the subscription connection has been lost and take appropriate corrective action.
await instanceUnderTest.subscribeToFundingSourceChanges(
'subscription-id',
{
fundingSourceChanged(fundingSource: FundingSource): Promise<void> {
// Implement handling for funding source changed behaviour
},
connectionStatusChanged(state: ConnectionState): void {
// Implement handling for subscription connection status changed
},
},
)
If the subscribeToFundingSourceChanges
method is called more than once with the same subscription id, subsequent invocations will replace the earlier subscriptions.
Unsubscribing
Once no longer interested in funding source changes, clients may unsubscribe by calling the unsubscribeFromFundingSourceChanges
method, passing the same subscription identifier.
sudoVirtualCardClient.unsubscribeFromFundingSourceChanges(
'subscription-id'
)
Last updated