feat(payments): persist helcimCustomerCode + skip getOrCreateCustomer on card-on-file

This commit is contained in:
Jennie Robinson Faber 2026-04-27 11:54:54 +01:00
parent 0a41b30db7
commit ac5e979c78
10 changed files with 330 additions and 33 deletions

View file

@ -25,17 +25,45 @@ export const useMemberPayment = () => {
paymentSuccess.value = false
try {
// Skip HelcimPay verify if a card's already on file — Helcim refuses
// to re-save it, breaking retries after a partial-failed signup.
const [, existing] = await Promise.all([
getOrCreateCustomer(),
$fetch('/api/helcim/existing-card').catch((err) => {
// Fast-path: when both Helcim ids are already cached on the member doc
// AND a card's on file, we can skip the paid getOrCreateCustomer round
// trip entirely and go straight to subscription creation.
const hasCachedHelcimIds = Boolean(
memberData.value?.helcimCustomerId && memberData.value?.helcimCustomerCode
)
let existing = null
let probedExistingCard = false
let cardToken = null
if (hasCachedHelcimIds) {
existing = await $fetch('/api/helcim/existing-card').catch((err) => {
console.warn('[payment] existing-card lookup failed, falling back to verify flow:', err)
return null
}),
])
})
probedExistingCard = true
if (existing?.cardToken) {
customerId.value = memberData.value.helcimCustomerId
customerCode.value = memberData.value.helcimCustomerCode
cardToken = existing.cardToken
}
}
let cardToken = existing?.cardToken || null
if (!cardToken) {
// Skip HelcimPay verify if a card's already on file — Helcim refuses
// to re-save it, breaking retries after a partial-failed signup.
const [, existingFromFull] = await Promise.all([
getOrCreateCustomer(),
probedExistingCard
? Promise.resolve(existing)
: $fetch('/api/helcim/existing-card').catch((err) => {
console.warn('[payment] existing-card lookup failed, falling back to verify flow:', err)
return null
}),
])
cardToken = existingFromFull?.cardToken || null
}
if (!cardToken) {
await initializeHelcimPay(

View file

@ -85,21 +85,46 @@ const initialize = async () => {
}
try {
// Skip HelcimPay verify if a card's already on file Helcim refuses
// to re-save it, breaking retries after a partial-failed signup.
const [customer, existing] = await Promise.all([
$fetch('/api/helcim/get-or-create-customer', { method: 'POST' }),
$fetch('/api/helcim/existing-card').catch((err) => {
// Fast-path: when both Helcim ids are already cached on the member doc
// AND a card's on file, skip the paid get-or-create-customer round trip.
const hasCachedHelcimIds = Boolean(
memberData.value?.helcimCustomerId && memberData.value?.helcimCustomerCode
);
let existing = null;
let probedExistingCard = false;
if (hasCachedHelcimIds) {
existing = await $fetch('/api/helcim/existing-card').catch((err) => {
console.warn('[payment-setup] existing-card lookup failed, falling back to verify flow:', err);
return null;
}),
]);
customerId.value = customer.customerId;
customerCode.value = customer.customerCode;
hasExistingCard.value = Boolean(existing?.cardToken);
});
probedExistingCard = true;
if (existing?.cardToken) {
customerId.value = memberData.value.helcimCustomerId;
customerCode.value = memberData.value.helcimCustomerCode;
hasExistingCard.value = true;
}
}
if (!hasExistingCard.value) {
await initializeHelcimPay(customerId.value, customerCode.value, 0);
// Skip HelcimPay verify if a card's already on file Helcim refuses
// to re-save it, breaking retries after a partial-failed signup.
const [customer, existingFromFull] = await Promise.all([
$fetch('/api/helcim/get-or-create-customer', { method: 'POST' }),
probedExistingCard
? Promise.resolve(existing)
: $fetch('/api/helcim/existing-card').catch((err) => {
console.warn('[payment-setup] existing-card lookup failed, falling back to verify flow:', err);
return null;
}),
]);
customerId.value = customer.customerId;
customerCode.value = customer.customerCode;
hasExistingCard.value = Boolean(existingFromFull?.cardToken);
if (!hasExistingCard.value) {
await initializeHelcimPay(customerId.value, customerCode.value, 0);
}
}
step.value = 'ready';
} catch (err) {