Skip to main content

Gift cards and co-payment

Gift cards, loyalty cards and vouchers are secondary methods: they draw a slice of the amount and a primary method pays the rest. Several can stack, and gift cards covering everything make the payment fulfilled with no primary.

Setup​

Artifacteu.purse:headless-checkout-android. Illicado, Buybox, Ogloba, MyBeezBox, Easy2Play (gift card and voucher) and Maxxing loyalty ship in it
DashboardThe issuer activated on the entity as a secondary method
Manifest, keysNone
UIYou render the PAN and CVV fields yourself: there is no payment element for a secondary method

Check the balance and apply​

val giftCard = checkout.secondaryMethods.single { it.partner == Partner.Illicado }
val rules = checkout.secondaryConfig(giftCard) // requiresCvv(pan), validatePan(pan), validateCvv(cvv)

val token = checkout.getSecondaryToken(giftCard, pan, cvv) // suspend: /init, reads the balance, split untouched
checkout.applySecondary(token) // as much as the order needs…
checkout.applySecondary(token, BigDecimal("2.00")) // …or a capped slice (splittable issuers only)
checkout.removeSecondary(token)

secondaryConfig gives you the issuer's rules to validate the form before calling the network: whether this PAN needs a CVV, the PAN and CVV formats. getSecondaryToken tokenizes the card and reads its balance; it throws SecondaryTokenizationFailed (FAILED_TO_INIT_SECONDARY_TOKEN, or SECONDARY_TOKEN_EMPTY_BALANCE for an empty card) and InvalidSecondaryCard for a client-side format error.

applySecondary with an explicit amount throws InvalidTake:

CodeMeaning
INVALID_TAKE_AMOUNTNegative, above the balance, or above what is left to pay
MAX_AMOUNT_REACHEDAbove the issuer's maximum per payment
USAGE_LIMIT_ERRORThe issuer's per-session usage limit is spent by other cards in the split

remainingAmountToPay, amountSplit and isPaymentFulfilled follow every change. When gift cards cover the whole amount, submitPayment() sends them alone; element.submit() on a primary sends them together with the primary.

Keep the gift card​

When the issuer lets the shopper choose, offer to keep the card for the next session:

if (token.canBeRegistered) checkout.registerSecondary(token, registered = true, name = "gift one")

Saving needs a wallet session in the client session, as for saved cards.

Saved gift cards​

Cards kept earlier arrive on savedSecondaryTokens, their balance fetched in the background:

val saved by checkout.savedSecondaryTokens.collectAsState()
saved.forEach { card ->
SavedGiftCardRow(
balance = card.balance.takeIf { card.balanceResolved }, // null → "checking…"
unavailable = card.disabled != null, // TOKEN_INIT_FAILED: the issuer refused it
onUse = { checkout.applySecondary(card) },
onRemove = { checkout.removeSecondary(card) },
onRename = { name -> scope.launch { checkout.editToken(card, name) } },
onDelete = { scope.launch { checkout.deleteToken(card) } },
)
}

An issuer the SDK does not ship​

Implement SecondaryConfig and register it once at app start; the session still decides whether the method is offered.

object AcmeVoucherConfig : SecondaryConfig {
override val takePolicy = SecondaryTakePolicy.MAX
override fun requiresCvv(pan: String) = false
override fun validatePan(pan: String) = pan.startsWith("ACME-")
override fun buildInitPaymentData(pan: String, cvv: String?) = JSONObject().put("voucher_code", pan)
override fun buildValidatePaymentData(token: SecondaryToken) = JSONObject().put("voucher_code", token.pan)
}
SecondaryConfigs.register(Partner.Other("acme"), Method.Other("voucher"), AcmeVoucherConfig)
ANCV Connect

The ANCV Connect flow (the shopper confirms in the ANCV app while the checkout polls) ships on the web SDK only. The SecondaryConfig hooks pollForToken and classifyInitError exist so an app can register such an issuer.

Troubleshooting​

SymptomCause
getSecondaryToken throws SECONDARY_METHOD_REQUIRED_CVVThis PAN needs a CVV: check rules.requiresCvv(pan) before calling
applySecondary throws USAGE_LIMIT_ERRORToo many cards of this issuer in the split
A saved gift card shows disabledIts balance could not be read this session (TOKEN_INIT_FAILED). Let the shopper enter it again
registerSecondary throws TOKEN_NOT_REGISTERABLEThe issuer's policy does not let the shopper choose (canBeRegistered is false), or the card is already saved. Check canBeRegistered before offering to save