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.

Before creating a funding source, the identity of your user must first be verified via Secure Id Verification.

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.

See the Plaid Link Web SDK documentation for more information:

https://plaid.com/docs/link/web/

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:

UsernameBehavior

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

For Stripe React integration, see the documentation at:

https://stripe.com/docs/stripe-js/react

For Stripe JavaScript integration, see the documentation at:

https://stripe.com/docs/js

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

For checkout.com JavaScript, see the documentation at:

https://www.checkout.com/docs/integrate/frames

A React wrapper for their Frames library is available at:

https://github.com/checkout/frames-react

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:

Interaction typeIdentificationAction to takeCompletion process

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:

Interaction typeIdentificationAction to takeCompletion process

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. The results can be paginated and can contain funding sources which may be active whilst others are inactive.

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.

try {
    const listOutput = await virtualCardsClient.listFundingSources({
        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. The results can be filtered and/or paginated.

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.

try {
  const filter: ProvisionalFundingSourceFilterInput = {
    and: [
      { id: { eq: idToGet } },
      { state: { eq: ProvisionalFundingSourceState.Provisioning } },
    ],
  }
  const listOutput = await virtualCardsClient.listProvisionalFundingSources(
    {
      filter,
      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