Overview
The guide below will help you configure co-badged cards with Headless Checkout.
We’ve introduced a new way to integrate our Checkout for Web.Learn how to build a fully customizable checkout using modular components in our documentation here:
Primer Checkout (Web Components)
Example projects
Feel free to use these example projects to get started quicker:
Pre-requisites
This guide assumes that you have already integrated a card form with Headless Checkout.
Make sure to follow the “Get Started with Headless Checkout” first.
Step 1. Set the list of supported card networks in the client session
When creating a client session, it is recommended to pass the list of the card networks supported for this particular session. This ensures that the customer is not asked to choose a card network that you do not accept.
The card network should be sorted in order of preference. This enables us to identify the default card network to consider if the user enters a co-badged card.
// POST /client-session
{
"paymentMethod": {
"orderedAllowedCardNetworks": ["VISA", "CARTES_BANCAIRES", "MASTERCARD"]
}
}
With the example above:
- If the user enters a “Visa & Cartes Bancaires” card, the default network will be Visa
- If the user enters a “Mastercard & Cartes Bancaires” card, the default network will be Cartes Bancaires
Learn more about orderedAllowedCardNetworks in the API Reference.
Web
iOS
Android
React Native
Step 2. Detect the card networks
On Web, the card form is implemented using PCI-compliant hosted fields that can be put anywhere on the web page.When the customer enters their card number, Headless Checkout retrieves the list of card networks available on the card. The list of card networks is passed to the developers through a callback called onCardNetworksChange.const cardManager = await headless.createPaymentMethodManager('PAYMENT_CARD',
{
onCardNetworksChange({
source,
detectedCardNetworks,
selectableCardNetworks
}) {
// Handle the detected card networks
}
})
This callback returns the following data:
source
Where the card networks come from.
LOCAL if the card networks come from the SDK, usually by leveraging the first few digits.
REMOTE if the card networks come from Primer’s server
LOCAL_FALLBACK if Primer’s server fails to be reached. This works similarly to LOCAL.
detectedCardNetworks.items
The list of all the detected card networks, including the ones that are not allowed.
detectedCardNetworks.preferred
The first allowed card network in the detected card networks. Can be undefined if the user entered a card that is not allowed.
selectableCardNetworks.items
The list of all the detected card networks that can be selected by the user. selectableCardNetworks is undefined if Headless Checkout does not detect a co-badged card.
selectableCardNetworks.preferred
The card network selected by default, according to the order set in orderedAllowedCardNetworks. selectableCardNetworks is undefined if Headless Checkout does not detect a co-badged card.
The card networks returned by onCardNetworksChange are objects that contain the network id, its display name, and a flag allowed stating if the card network is in orderedAllowedCardNetworks.Less than 8 digits
If the user enters less than 8 digits, Headless Checkout cannot detect co-badged cards. Instead, Headless Checkout tries to detect major card networks using data available locally.In this case:
source is set to LOCAL
detectedCardNetworks.preferred contains the card network you should present to the user
detectedCardNetworks.items contains all the detected card networks
selectableCardNetworks is always undefined
8 digits or more
If the user enters 8 digits or more, Headless Checkout attempts to make a request to Primer to retrieve the list of card networks available on the card using our BIN data.In this case:
source is set to REMOTE
selectableCardNetworks is only available if the user has entered a co-badged card and both its detected networks are allowed for this session
selectableCardNetworks.items contains the list of card networks from which the user can select
If Headless Checkout fails to contact Primer, Headless Checkout falls back to a local detection (similar to when less than 8 digits are entered). In this case, source is set to LOCAL_FALLBACK .Let’s say orderedAllowedCardNetworks is ["CARTES_BANCAIRES", "VISA", "MASTERCARD"]If the user enters a card that is co-badged Visa and Cartes Bancaires:
detectedCardNetworks.items is CARTES_BANCAIRES and VISA
detectedCardNetworks.preferred is CARTES_BANCAIRES
selectableCardNetworks.items is CARTES_BANCAIRES and VISA
selectableCardNetworks.preferred is CARTES_BANCAIRES
If the user enters a card that is only Visa:
detectedCardNetworks.items is only VISA
detectedCardNetworks.preferred is VISA
selectableCardNetworks is undefined
If the user enters an Amex card (not allowed for this session):
detectedCardNetworks.items is only AMEX
detectedCardNetworks.preferred is undefined
selectableCardNetworks is undefined
Example
const cardManager = await headless.createPaymentMethodManager("PAYMENT_CARD", {
onCardNetworksChange({
source,
detectedCardNetworks,
selectableCardNetworks,
}) {
if (source === "LOCAL_FALLBACK") {
// feel free to send an event to your backend
console.warn("Cannot detect card networks");
}
if (selectableCardNetworks) {
// if `selectableCardNetworks` is non-nullish,
// then ask the user to select a card network
// you can use `selectableCardNetworks.preferred` to pre-select the card network
return showCardNetworkSelect(selectableCardNetworks.items);
}
// show the detected card network
showAllowedCardNetwork(
detectedCardNetworks.preferred || detectedCardNetworks.items[0]
);
},
});
Step 3. Present the list of card networks and handle the selection
It is now your responsibility to present the list of card networks to the customer and enable them to choose the network they want to pay with.To support you, Headless Checkout exposes the logos and human readable names of each card network via the AssetsManager.// After headless checkout is started
const assetsManager = headless.createAssetsManager();
const { cardUrl } = await assetsManager.getCardNetworkAsset('CARTES_BANCAIRES')
Step 4. Create a payment with the selected card network
When the user clicks on “Pay”, call the submit function with the selected card network to let us know which card network the customer chose.cardManager.submit({
cardNetwork: "CARTES_BANCAIRES"
});
If cardNetwork is not present, Primer follows the order of preference given by the option orderedAllowedCardNetworks passed in the client session API.Step 5. (Optional) Show the list of the supported card networks
We highly recommend you to present the list of allowed card networks close to the card form.You can retrieve orderedAllowedCardNetworks on the Web SDK by listening to onClientSessionUpdate. After that, you can useconst primer = await Primer.createHeadless(clientToken, {
// Other options and callbacks
onClientSessionUpdate({ paymentMethod: { orderedAllowedCardNetworks } }) {
showCardNetworks(orderedAllowedCardNetworks)
},
})
Step 2. Detect the card networks
When you pass a full or partial card number by setting rawData on the RawDataManager object, Headless Checkout retrieves the list of card networks available for the card.Retrieving the cards requires a network call, so primerRawDataManager(_:willFetchMetadataForState:) is called to indicate that the network request has started.After a response is received, the list of card networks is passed to your integration through a callback called
primerRawDataManager(_:didReceiveMetadata:forState:). @objc optional
func primerRawDataManager(_ rawDataManager: PrimerHeadlessUniversalCheckout.RawDataManager,
willFetchMetadataForState state: PrimerValidationState) {
// ... Do nothing, or show a loading spinner ...
}
@objc optional
func primerRawDataManager(_ rawDataManager: PrimerHeadlessUniversalCheckout.RawDataManager,
didReceiveMetadata metadata: PrimerPaymentMethodMetadata,
forState state: PrimerValidationState) {
if let cardMetadata = metadata as? PrimerCardNumberEntryMetadata {
// ... Display received card networks to the user ...
if let cardState = state as? PrimerCardNumberEntryState {
// ... If you want to check which card number caused the returned card networks ...
}
}
}
This callback returns the following data:
source
Enum value indicating how the card networks were retrieved
.local if the card networks come from the SDK, usually by leveraging the first few digits.
.remote if the card networks come from Primer’s server.
.localFallback if Primer’s server fails to be reached. This works similarly to .local.
detectedCardNetworks.items
The list of all the detected card networks, including the ones that are not allowed.
detectedCardNetworks.preferred
The first allowed card network in the detected card networks. Can be nil if the user entered a card that is not allowed.
selectableCardNetworks?.items
The list of all the detected card networks that can be selected by the user. selectableCardNetworks is nil if Headless Checkout does not detect a co-badged card.
selectableCardNetworks?.preferred
The card network selected by default, according to the order set in orderedAllowedCardNetworks. selectableCardNetworks is nil if Headless Checkout does not detect a co-badged card.
The card networks returned by primerRawDataManager(_:didReceiveMetadata:forState:) are PrimerCardNetwork objects that contain the network id, its display name, and a flag, allowed, that indicates whether the card network is in orderedAllowedCardNetworks.Learn more about primerRawDataManager(_:didReceiveMetadata:forState:) in the API reference. Less than 8 digits
If the user enters less than 8 digits, Headless Checkout cannot detect co-badged cards. Instead, Headless Checkout tries to detect major card networks using data available locally.In this case:
source is set to .local
detectedCardNetworks.preferred contains the card network you should present to the user
detectedCardNetworks.items contains all the detected card networks
selectableCardNetworks is always nil
8 digits or more
If the user enters 8 digits or more, Headless Checkout attempts to make a request to Primer to retrieve the list of card networks available on the card using our BIN data.In this case:
source is set to .remote
selectableCardNetworks is only available if the user has entered a co-badged card and both its detected networks are allowed for this session
selectableCardNetworks.items contains the list of card networks from which the user can select
If Headless Checkout fails to contact Primer, Headless Checkout falls back to a local detection (similar to when less than 8 digits are entered). In this case, source is set to .localFallback .Let’s say orderedAllowedCardNetworks is ["CARTES_BANCAIRES", "VISA", "MASTERCARD"]If the user enters a card that is co-badged Visa and Cartes Bancaires:
detectedCardNetworks.items is CARTES_BANCAIRES and VISA
detectedCardNetworks.preferred is CARTES_BANCAIRES
selectableCardNetworks?.items is CARTES_BANCAIRES and VISA
selectableCardNetworks?.preferred is CARTES_BANCAIRES
If the user enters a card that is only Visa:
detectedCardNetworks.items is only VISA
detectedCardNetworks.preferred is VISA
selectableCardNetworks is nil
If the user enters an Amex card (not allowed for this session):
detectedCardNetworks.items is only AMEX
detectedCardNetworks.preferred is nil
selectableCardNetworks is nil
Example
extension MyViewController: PrimerHeadlessUniversalCheckoutRawDataManagerDelegate {
func primerRawDataManager(_ rawDataManager: PrimerHeadlessUniversalCheckout.RawDataManager,
willFetchMetadataForState cardState: PrimerValidationState) {
guard let state = cardState as? PrimerCardNumberEntryState else {
logger.error("Received non-card metadata. Ignoring ...")
return
}
/* ... display spinner ... */
}
func primerRawDataManager(_ rawDataManager: PrimerHeadlessUniversalCheckout.RawDataManager,
didReceiveMetadata metadata: PrimerPaymentMethodMetadata,
forState cardState: PrimerValidationState) {
guard let metadata = metadata as? PrimerCardNumberEntryMetadata,
let cardState = cardState as? PrimerCardNumberEntryState else {
logger.error("Received non-card metadata. Ignoring ...")
return
}
var networksForDisplay: [PrimerCardNetwork] = []
var isAllowedNetwork: Bool = true
if metadata.source == .remote, let selectableNetworks = metadata.selectableCardNetworks?.items, !selectableNetworks.isEmpty {
networksForDisplay = selectableNetworks
} else if let preferredDetectedNetwork = metadata.detectedCardNetworks.preferred {
networksForDisplay = [preferredDetectedNetwork]
} else if let cardNetwork = metadata.detectedCardNetworks.items.first {
networksForDisplay = [cardNetwork]
isAllowedNetwork = false
}
/* ... networksForDisplay contains the models to display to the user 🎉 ... */
}
}
Step 3. Present the list of card networks and handle the selection
It is now your responsibility to present the list of card networks to the customer and enable them to choose the network they want to pay with.To support you, Headless Checkout exposes the logos and human readable names of each card network via the AssetsManager.let cardAssets: [PrimerCardNetworkAsset] = networksForDisplay.compactMap { network in
return PrimerHeadlessUniversalCheckout.AssetsManager.getCardNetworkAsset(for: network)
}
cardAssets.forEach { asset in
// ... display the card, e.g. ... */
showCard(title: asset.displayName, image: asset.cardImage)
}
When the user selects a card network, set rawData, passing the relevant card network to let us know which the customer chose.let selectedCardNetwork: CardNetwork = primerCardNetwork.value // Enum value for the network
rawDataManager?.rawData = PrimerCardData(
cardNumber: cardNumber,
expiryDate: expiryDate,
cvv: cvvNumber,
cardholderName: cardholderName,
cardNetwork: selectedCardNetwork // pass the enum value here
)
Step 4. Create a payment with the selected card network
When the user clicks on “Pay”, call the submit function to start the payment.If the card network was not provided via rawData, Primer follows the order of preference given by the option orderedAllowedCardNetworks passed in the client session API.Step 2. Detect the card networks
When you pass the card number using setRawData,
Headless Checkout retrieves the list of card networks available on the card.
The list of card networks is passed to the developers through a callback called onMetadataStateChanged.val rawDataManagerListener =
object : PrimerHeadlessUniversalCheckoutRawDataManagerListener {
override fun onMetadataStateChanged(metadataState: PrimerPaymentMethodMetadataState) {
when (metadataState) {
is PrimerCardMetadataState ->
when (metadataState) {
is PrimerCardMetadataState.Fetched -> // handle metadata fetched state
is PrimerCardMetadataState.Fetching -> // handle metadata fetching state
}
}
}
}
This callback yields the PrimerCardMetadataState and encompasses two distinct states:
PrimerCardMetadataState.Fetching Indicates that the card metadata is currently being retrieved from the Primer server.
PrimerCardMetadataState.Fetching Signifies the state when the card metadata has been fetched, encompassing the following cardNumberEntryMetadata:
source
Enum value indicating how the card networks were retrieved:
LOCAL if the card networks come from the SDK, usually by leveraging the first few digits.
REMOTE if the card networks come from Primer’s server.
LOCAL_FALLBACK if Primer’s server fails to be reached. This works similarly to LOCAL.
detectedCardNetworks.items
The list of all the detected card networks, including the ones that are not allowed.
detectedCardNetworks?.preferred
The first allowed card network in the detected card networks. Can be null if the user entered a card that is not allowed.
selectableCardNetworks?.items
The list of all the detected card networks that can be selected by the user. selectableCardNetworks is null if Headless Checkout does not detect a co-badged card.
selectableCardNetworks?.preferred
The card network selected by default, according to the order set in orderedAllowedCardNetworks. selectableCardNetworks is null if Headless Checkout does not detect a co-badged card.
The card networks returned by onMetadataStateChanged are objects that contain the network type, its display name, and a flag allowed stating if the card network is in orderedAllowedCardNetworks.Less than 8 digits
If the user enters less than 8 digits, Headless Checkout cannot detect co-badged cards.
Instead, Headless Checkout tries to detect major card networks using data available locally.In this case:
source is set to LOCAL
detectedCardNetworks.preferred contains the card network you should present to the user
detectedCardNetworks.items contains all the detected card networks
selectableCardNetworks is always null
8 digits or more
If the user enters 8 digits or more, Headless Checkout attempts to make a request to Primer to retrieve the list of card networks available on the card using our BIN data.In this case:
source is set to REMOTE
selectableCardNetworks is only available if the user has entered a co-badged card and both its detected networks are allowed for this session
selectableCardNetworks.items contains the list of card networks from which the user can select
If Headless Checkout fails to contact Primer, Headless Checkout falls back to a local detection (similar to when less than 8 digits are entered). In this case, source is set to LOCAL_FALLBACK .Let’s say orderedAllowedCardNetworks is ["CARTES_BANCAIRES", "VISA", "MASTERCARD"]If the user enters a card that is co-badged Visa and Cartes Bancaires:
detectedCardNetworks.items contains networks with identifiers CARTES_BANCAIRES and VISA
detectedCardNetworks.preferred is a network with identifier CARTES_BANCAIRES
selectableCardNetworks.items contains networks with identifiers CARTES_BANCAIRES and VISA
selectableCardNetworks.preferred is a network with identifier CARTES_BANCAIRES
If the user enters a card that is only Visa:
detectedCardNetworks.items is only a network with identifier VISA
detectedCardNetworks.preferred is only a network with identifier VISA
selectableCardNetworks is null
If the user enters an Amex card (not allowed for this session):
detectedCardNetworks.items is only a network with identifier AMEX
detectedCardNetworks.preferred is null
selectableCardNetworks is null
Example
val rawDataManagerListener =
object : PrimerHeadlessUniversalCheckoutRawDataManagerListener {
override fun onValidationChanged(isValid: Boolean, errors: List<PrimerInputValidationError>) {
// use the `isValid` to enable/disable pay button
// use the `errors` list and handle validation errors respectively
}
override fun onMetadataStateChanged(metadataState: PrimerPaymentMethodMetadataState) {
when (metadataState) {
is PrimerCardMetadataState ->
when (metadataState) {
is PrimerCardMetadataState.Fetched -> {
val cardNumberMetadata = metadataState.cardNumberEntryMetadata
if (cardNumberMetadata.source === ValidationSource.LOCAL_FALLBACK) {
// feel free to send an event to your backend
Log.w(MY_TAG, "Cannot detect card networks.")
}
cardNumberMetadata.selectableCardNetworks?.let { networksMetadata ->
// if `selectableCardNetworks` is non-null,
// then ask the user to select a card network
// you can use `selectableCardNetworks.preferred` to pre-select the
// card network
val cardNetworks: List<PrimerCardNetwork> = networksMetadata.items
showCardNetworkSelect(cardNetworks)
} ?: run {
cardNumberMetadata.detectedCardNetworks.let { networksMetadata ->
// show the detected card network
val cardNetwork: PrimerCardNetwork? =
networksMetadata.preferred ?: networksMetadata.items.firstOrNull()
showAllowedCardNetwork(cardNetwork)
}
}
}
is PrimerCardMetadataState.Fetching -> showLoadingView()
}
}
}
}
val cardManager = PrimerHeadlessUniversalCheckoutRawDataManager.newInstance("PAYMENT_CARD")
cardManager.setListener(rawDataManagerListener)
Step 3. Present the list of card networks and handle the selection
It is now your responsibility to present the list of card networks to the customer and enable them to choose the network they want to pay with.To support you, Headless Checkout exposes the logos and human readable names of each card network via the PrimerHeadlessUniversalCheckoutAssetsManager.// After headless checkout is started
val cardNetworkAsset = PrimerHeadlessUniversalCheckoutAssetsManager.getCardNetworkAsset(
requireContext(),
CardNetwork.Type.CARTES_BANCAIRES
)
When the user selects a card network, call setRawData
with the relevant card network to let us know which card network the customer chose.cardManager.setRawData(
PrimerCardData(
cardNumber = cardNumber,
expiryDate = expiryDate,
cvv = cvvNumber,
cardHolderName = cardholderName,
cardNetwork = CardNetwork.Type.CARTES_BANCAIRES
)
)
Step 4. Create a payment with the selected card network
When the user clicks on “Pay”, call the submit function to start the payment.If the card network was not provided with setRawData,
Primer follows the order of preference given by the option orderedAllowedCardNetworks passed in the client session API.This feature is currently not available on React Native.
Notes
Slow and unreliable internet connection
When the customer’s network connection is slow or unreliable, it is possible that the customer finishes entering their card details before Universal Checkout retrieves the card networks.
In order to keep the friction low, it is important to note that Universal Checkout does not block the creation of a payment while the card networks are being retrieved or if the card networks could not be retrieved at all.
This means that, if a co-badged card is entered, the customer is not guaranteed to be presented a card choice before they submit their card details.
Selected card network on the payment method data
In the case of a card payment, you can get the selected network by looking at paymentMethodData.network.
We recommend against using paymentMethodData.binData.network as it only contains the international card network.