ANCV Chèque-Vacances
ANCV Connect lets customers pay part of an order with their Chèque-Vacances balance. In Purse Headless Checkout it is a secondary payment method (partner: "ancv", method: "connect"): it is combined with a primary method that covers the rest of the order.
ANCV differs from a gift card in one key way: there is no card number and no CVV. The customer enters their ANCV account identifier, then confirms the payment in the ANCV Chèque-Vacances mobile app. The SDK waits for that confirmation before resolving the token.
This guide covers the ANCV specifics on top of the generic Gift Cards guide.
How the flow works
The two-phase exchange with ANCV is handled inside the SDK. From your code, getSecondaryToken() is a single promise — it just takes longer to resolve, because it only settles once the customer has acted in the app.
Timings enforced by the SDK:
| Behaviour | Value |
|---|---|
| Poll interval | 10 seconds |
| Timeout | 5 minutes (matches the ANCV notification lifetime) |
| Amount sent to the app | checkout.remainingAmountToPay at the moment getSecondaryToken() is called |
The amount the customer sees and confirms in the ANCV app is the remaining amount at the moment you call getSecondaryToken(). Request the ANCV token after the cart amount is final and after any other secondary method has been applied. If the amount to pay drops afterwards, the surplus authorized on the ANCV account is lost for this payment.
Prerequisites
- ANCV Connect enabled on your entity — ask your account manager.
- The customer has the ANCV Chèque-Vacances mobile app installed and is logged in.
- Test accounts and app setup instructions: ANCV partner page.
1. Find the ANCV method
ANCV is exposed like any other secondary method, in the paymentMethods reactive list. Filter it on partner / method, using the Partners and Methods constants exported by the SDK rather than raw strings:
import { Partners, Methods } from '@upstreampay/headless-checkout';
const isAncv = (item) => item.partner === Partners.ancv && item.method === Methods.connect;
paymentMethods is reactive: the entry appears, disappears, or becomes disabled when the session or the amount changes. Subscribe instead of reading .value once, and render the ANCV block from the callback:
checkout.paymentMethods.subscribe((methods) => {
const method = methods.find(isAncv);
if (!method) {
// ANCV is not available for this session
hideAncvBlock();
return;
}
renderAncvBlock(method);
});
Each method also exposes a disabled reactive state — for example when the remaining amount is below the method minimum. Subscribe to it to disable the ANCV form instead of letting the customer start an app confirmation that cannot succeed.
Secondary methods have no payment element. getPaymentElement() returns undefined and logs a warning for ANCV: the whole UI is yours, and the token is obtained through getSecondaryToken().
2. Build the form
ANCV needs a single field: the customer's ANCV Chèque-Vacances account identifier (digits only).
<form id="ancv-form">
<label for="ancv-account">Your ANCV Chèque-Vacances ID</label>
<input type="text" id="ancv-account" inputmode="numeric" placeholder="12345678910" required />
<button type="submit">Use ANCV</button>
</form>
Do not render a CVV field: method.requiresCVV(pan) returns false for ANCV. Keep calling it rather than hardcoding, so the same component keeps working for other secondary methods.
Validate the identifier before submitting with method.validatePan(accountId) — it rejects empty and non-numeric values. Calling getSecondaryToken() with an invalid value throws SECONDARY_METHOD_WRONG_PAN_FORMAT.
3. Request the token and wait for the app confirmation
Pass an AbortSignal so the customer (or a component unmount) can cancel the wait.
const controller = new AbortController();
let token;
try {
showWaitingOverlay(); // "Action required in the ANCV app"
token = await method.getSecondaryToken(accountId, undefined, controller.signal);
} catch (error) {
handleAncvError(error.code);
return;
} finally {
hideWaitingOverlay();
}
While the promise is pending:
- Keep the form disabled — a second call for the same account fails with
OTHER_TRANSACTION_PENDING_FOR_ACCOUNT. - Offer a Cancel action calling
controller.abort(). - Abort the controller when the component unmounts, otherwise the poll keeps running.
Never poll on your side. The SDK owns the polling loop and rejects the promise on expiry, customer refusal, or abort.
4. Apply the authorized amount
ANCV uses the max take policy: the amount is decided by ANCV at confirmation time, not by your UI. token.balance holds the authorized amount and take() always applies the full authorized amount (capped by the method limits) whatever value you pass.
// takePolicy === 'max' — do not render an editable amount input
await token.take(token.balance.value);
Do not render an amount field for ANCV. Read token.takePolicy to decide, instead of testing the partner name:
if (token.takePolicy === 'max') {
await token.take(token.balance.value);
} else {
// gift cards, vouchers: let the customer choose an amount
}
Warn about a possible loss
If the authorized amount is larger than the part the token actually covers in the payment split, the difference is consumed but not used. Compare the authorized amount with the token's contribution and warn the customer before submitting.
const contribution = checkout.amountSplit.value
.filter((split) => split.source.id === token.id)
.reduce((total, split) => total + split.amount, 0);
const loss = token.balance.value - contribution;
if (loss > 0) {
showWarning(`${loss} will not be used on this order.`);
}
5. Remove or replace the token
token.delete() removes the temporary token from the session and clears its split. The customer must restart the app confirmation from scratch to add ANCV again — there is nothing to "re-open" on the ANCV side.
6. Submit
Nothing ANCV-specific: call checkout.submitPayment(). The ANCV transaction reference obtained during the confirmation is attached to the payment automatically.
Saving ANCV for later is not supported
An ANCV authorization is tied to a single transaction, so ANCV tokens cannot be registered in the wallet. token.canBeRegistered is false and calling token.register() throws TOKEN_NOT_REGISTERABLE. Hide any "save for later" UI when canBeRegistered is false.
Error handling
All these errors reject the getSecondaryToken() promise. Only the code changes — see Error Codes.
| Code | Meaning | Suggested treatment |
|---|---|---|
SECONDARY_METHOD_WRONG_PAN_FORMAT | Identifier empty or not numeric | Inline field error, before any network call |
USER_ACTION_REQUEST_HAS_EXPIRED | No confirmation within 5 minutes | Warning, invite to retry |
USER_ACTION_REQUEST_ABORTED_BY_CUSTOMER | Customer refused in the app | Warning, invite to retry |
OTHER_TRANSACTION_PENDING_FOR_ACCOUNT | Another ANCV transaction is already pending for this account | Warning, ask the customer to cancel it in the app first |
SECONDARY_METHOD_REQUEST_ABORTED | Your AbortSignal fired | Silent — the customer cancelled |
SECONDARY_TOKEN_EMPTY_BALANCE | Authorized amount is zero | Error, ANCV balance unusable for this order |
TOKEN_ALREADY_EXISTS | This ANCV account is already applied to the payment | Error, one entry per account |
FAILED_TO_INIT_SECONDARY_TOKEN | Unexpected ANCV or network failure | Generic error, invite to retry |
function handleAncvError(code) {
switch (code) {
case 'USER_ACTION_REQUEST_HAS_EXPIRED':
return warn('The confirmation delay has expired. Please try again.');
case 'USER_ACTION_REQUEST_ABORTED_BY_CUSTOMER':
return warn('The confirmation was cancelled in the ANCV app.');
case 'OTHER_TRANSACTION_PENDING_FOR_ACCOUNT':
return warn('Another transaction is pending on this ANCV account. Cancel it and retry.');
case 'SECONDARY_METHOD_REQUEST_ABORTED':
return; // cancelled on purpose
default:
return error('Invalid ANCV account. Please check the information entered.');
}
}
Treat USER_ACTION_REQUEST_HAS_EXPIRED, USER_ACTION_REQUEST_ABORTED_BY_CUSTOMER and OTHER_TRANSACTION_PENDING_FOR_ACCOUNT as warnings, not as errors: the customer can retry immediately. The Purse Drop-in renders them with a warning style for that reason.
UX guidelines
These rules mirror the Drop-in ANCV experience. Follow them to keep completion rates high on a flow that depends on a second device.
Before the request
- Tell the customer upfront that the mobile app is required: "The ANCV Chèque-Vacances app is required to complete the payment." Show the ANCV logo next to the notice.
- One field only, labelled with the account identifier wording (
Your ANCV Chèque-Vacances ID), numeric keyboard on mobile, sample value as placeholder. - Never show a CVV, expiry, or amount input.
During the wait (up to 5 minutes)
- Show a blocking overlay on the ANCV block only — the rest of the checkout stays readable.
- Overlay content: ANCV logo, "Action required in the ANCV Chèque-Vacances app", a spinner, and a Cancel text button.
- Never show a progress bar or a countdown: the SDK gives no progress signal, and a countdown pressures the customer.
- Block resubmission and prevent the customer from leaving the step by accident.
After confirmation
- Display the authorized amount and the remaining amount to pay in the same view.
- Show a warning when part of the authorized amount will not be used.
- Provide an explicit remove action on the applied ANCV entry.
Limits
- Respect
method.limit(uses per session). Hide or disable the ANCV entry once the limit is reached;take()throwsUSAGE_LIMIT_ERRORotherwise. - ANCV cannot cover the whole order in every configuration — always keep a primary method selectable.
Testing
Use the test accounts and the ANCV app procedure listed on the ANCV partner page. Confirmation must be done on a mobile device logged into an ANCV test account: there is no way to simulate the confirmation from the browser.