{ if (open) return; // ESC/overlay while a charge is in flight must not close the modal — the // charge may still land. ESC while the overflow-confirm prompt is showing // dismisses the prompt (back to the amount-editing form), mirroring the // customer modal, instead of closing the whole flow. if (isChargeInFlight(status)) return; if (overflowConfirm) { cancelOverflowConfirmation(); return; } handleClose(); }} > Take Payment {#if overflowConfirm}
{:else if status === 'idle'}
Services
{#each booking.services ?? [] as service, i (service.service_id ?? `svc-${i}`)}
{service.service_name || 'Unknown Service'}
£ handlePriceInput(service.service_id, e.currentTarget.value)} /> {#if serviceOverrides[service.service_id] && Math.abs(parseFloat(serviceOverrides[service.service_id].price) - serviceOverrides[service.service_id].originalPrice) > 0.01} (was {formatCurrency(serviceOverrides[service.service_id].originalPrice)}) {/if}
{/each}
{#if loyaltyEligible}
{/if} {#if booking.discounts && booking.discounts.length > 0}
Applied Discounts
{((discountSum / subtotal) * 100).toFixed(0)}% Off Total
{#each booking.discounts as d (d.id)}
{#if d.discount_source === 'loyalty'} Loyalty Stamp Card (10% Off) {:else if d.campaign_name} {d.campaign_name} {:else} Promo Campaign ({d.discount_percent}% Off) {/if}
-{formatCurrency(d.discount_amount)}
{/each}
{/if}
{#if tipEnabled} Subtotal (pre-tip)
{#if discountSum > 0.01} {formatCurrency(subtotal)} {/if} {formatCurrency(netTotal)}
{:else} Total
{#if discountSum > 0.01} {formatCurrency(subtotal)} {/if} {formatCurrency(totalDue)}
{/if}
{#if nothingToCharge}

Nothing to charge — the booking is fully covered by discounts or prior payments.

{/if} {#if amountPaidPence > 0}
Already paid {formatCurrency(amountPaidPence / 100)}
{/if} {#if discountPreview?.eligible && discountPreview.discounts.length > 0}
{#each discountPreview.discounts as d (d.name)}
{d.name} -{formatCurrency(Math.round(d.amount * 100) / 100)}
{/each}
{/if} {#if tipEnabled}
Total with Tip ({tipDisplay}) {formatCurrency(totalWithTip)}
Tip amount +{formatCurrency(tipDelta)}
{/if}
{#if savedCards.length > 0} {/if}
{#if savedCards.length > 0} {/if}
{:else if status === 'selecting'}
{#if tipEnabled} Subtotal (pre-tip) {formatCurrency(netTotal)} {:else} Total {formatCurrency(totalDue)} {/if}
{#if campaignDiscountPence(discountPreview) > 0}

Discount {formatCurrency(campaignDiscountPence(discountPreview) / 100)} pending — tip will be calculated on the discounted amount.

{/if}
Add a Tip
{#each tipPercentages as tip (tip.pct)} {/each}
£
{#if tipEnabled}
Total with Tip ({tipDisplay}) {formatCurrency(totalWithTip)}
Tip amount +{formatCurrency(tipDelta)}
{/if}
{#if showPartialCharge}
£
{#if cardValidationError}

{cardValidationError}

{/if}

Leave blank to charge full amount ({formatCurrency(totalDue)})

{/if}
{:else if status === 'card-processing' || status === 'card-polling'}

Waiting for customer to tap card...

This may take a few moments

{:else if status === 'cash-entering'}
Total Due {formatCurrency(cashDue)}
£
{#if cashAmountNum >= cashDue}
Change Due {formatCurrency(changeDue)}
{#if changeDue > 0}
{/if}
{/if}
{:else if status === 'cash-confirming'}

Processing cash payment...

{:else if status === 'gift-entering'}
Total Due {formatCurrency(totalDue)}
{#if (booking.user_id ?? booking.user?.id) && customerBalance > 0}
Source
{/if} {#if useAccountBalance}
(giftCardPaymentAmount = (e.target as HTMLInputElement).value)} class="flex-1 font-mono text-lg" />

Available balance: {formatCurrency(customerBalance)}. Maximum of total due or balance can be used.

{:else}

Enter the 12-character code printed on the gift card

{/if}
{:else if status === 'gift-confirming'}

Processing gift card...

{:else if status === 'saved-card-selecting'}
Total Due {formatCurrency(totalDue)}
{#if loadingSavedCards}
{:else if savedCards.length === 0}

No saved cards found for this customer.

Add a card via Square Dashboard or use another payment method.

{:else}
Select a Saved Card {#each savedCards as card (card.id)} {/each}

Your card issuer will ask you to approve this payment in your banking app.

{/if} { lastSCAOutcome = ''; handleClose(); }} /> {#if selectedSavedCardId}
{#if showPartialCharge}
£
{#if savedCardValidationError}

{savedCardValidationError}

{/if}

Leave blank to charge full amount ({formatCurrency(totalDue)})

{/if}
{/if}
{:else if status === 'saved-card-processing' || status === 'saved-card-waiting-sca'}
{#if status === 'saved-card-waiting-sca'}

Waiting for customer to approve in their banking app…

The customer may need to approve this payment in their banking app

{:else}

Processing saved card payment...

{/if}
{:else if status === 'error' && error}

{error}

{:else if status === 'success' && paymentResult}

Payment Successful

Amount {formatCurrency(paymentResult.amount / 100)}
{#if paymentResult.card_brand}
Card {paymentResult.card_brand} ****{paymentResult.last4}
{/if}
Status Completed
{/if}