Skip to content

Deposits, no-show, and the real-gateway design

Design notes for the real payment connector (Worldline), and for any gateway that has to hold a deposit or charge a no-show fee for bookings made far in advance. The Worldline connector implements this; the simulator does not exercise any of it and is a deliberately naive stand-in. Read Deposits and payment first for the held/confirmed model these notes build on.

This replaces a legacy full-custom booking system on yoyaku + orchestra. The constraints below come from how real card rails behave, not from anything in the engine.

How the later charge is anchored: payment id, not a stored card

The later deposit hold and no-show fee both charge the card with the cardholder gone, as an unscheduled merchant-initiated transaction (MIT). A scheme MIT must be chained to an initial, SCA-authenticated cardholder-initiated transaction (CIT), and the connector charges the MIT against that CIT's payment id (Worldline's "subsequent payment"), not against a separately stored card token. The token path only stores a card the merchant cannot reliably charge later without that CIT chain; the payment id carries it.

  • A paid booking already is that CIT: its hosted checkout runs 3-D Secure and yields the payment id, so no extra step and no stored card.
  • A free booking that needs a card has no payment to anchor on, so the connector runs a small card-verification hosted checkout (configurable verification_amount, default 1.00): it runs 3-D Secure, yields the payment id, and the held amount is released on return so the payer is not charged.

Two distinct instruments, never merged

A "deposit" in everyday speech covers two different needs with different timing. The model keeps them separate, and so must the gateway.

Instrument Purpose When it must exist Card operation
Caution (security deposit) Cover damage During the stay only Authorization hold, released or captured after the stay
No-show fee Deter booking-and-ghosting Committed at booking, charged only on no-show Subsequent MIT against the booking's authorized payment id, on the no-show outcome

Caution is aimed at damage. It is placed near arrival and does not deter a no-show made months earlier, so it can never double as a no-show deterrent. The two are configured and operated independently.

Caution: authorize near arrival, sized to the stay

A card pre-authorization hold expires after roughly 7 to 30 days (set by the card scheme and the merchant category). The crucial point:

The hold has to be valid during the stay, not across the gap between booking and arrival.

So the booking lead time is irrelevant to the hold. Nothing is held during the months between booking and arrival. The gateway places one authorization a few days before arrival, sized to cover the stay, and releases or captures it after departure. For a single-night stay (or a stay shorter than the hold window), a single authorization covers the whole thing and never approaches expiry, no matter how far ahead the booking was made.

sequenceDiagram
    actor U as Guest
    participant App as Booking flow
    participant GW as Gateway (Worldline)
    participant Sched as Scheduler (cron/queue)

    U->>App: book (e.g. 2 months ahead)
    App->>GW: hosted checkout (3DS) -> authorized payment id
    Note over GW: nothing held yet (a free booking verifies, then releases)
    Sched->>GW: a few days before arrival: authorize caution (subsequent MIT)
    alt auth ok
        GW-->>App: caution held for the stay
        Note over GW: after departure: release (no claim) or capture (damage)
    else auth declined
        GW-->>App: early warning, several days to chase guest or cancel
    end

Re-authorization ("refreshing" a hold)

You cannot extend an authorization in place. "Refreshing" means re-authorization: a new authorization (a subsequent MIT against the booking's payment id) plus a reversal of the old hold. The card schemes define this for lodging and car rental (Visa reauthorization / incremental authorization, Mastercard equivalents).

Re-auth is only needed when:

  • the gateway holds the caution from booking time (we do not), or
  • the stay itself is longer than the hold window (a multi-week continuous booking).

Single-night stays hit neither, so re-auth is an edge case, not the main path. The connector should support it for genuinely long stays, but the bulk of short bookings should not rely on it. Re-auth risks (decline at refresh, double-holds if the old auth is not reversed promptly, issuer variance) are exactly why deferring one short auth is preferred.

No-show: charge-on-no-show against the booking's payment id

Free resources have a real no-show problem: zero cost to flake, and a ghosted booking denies a scarce free slot to someone else. A damage caution does not help (wrong timing, wrong purpose, and a free slot may have little to damage). The deterrent must bite at booking time and fire only on a no-show.

Chosen approach, which keeps the resource genuinely free for anyone who shows up:

  1. At booking: authenticate the card with 3-D Secure and keep the resulting payment id (the card-on-file chain anchor). A paid booking's checkout already is that authenticated transaction; a free booking runs a small verification hosted checkout and releases the held amount, so nothing is charged. No card token is stored.
  2. Attendance is an orchestra workflow outcome (a post-stay task with attended / no-show, the same shape as the example workflow's validate task). In the legacy system this lives in custom code; in the target stack it is a workflow task.
  3. On the no-show branch, a workflow action charges the no-show fee as an unscheduled subsequent merchant-initiated transaction against that payment id (3-D Secure is skipped, as the cardholder is gone).

So the gateway adds a "charge against the booking's payment id" operation wired to an event the workflow already emits. It is not new attendance machinery.

Will the card still be valid at the slot date?

There is no Worldline call that takes a future date and validates the card against it. Authorization is real-time. Future validity is assembled from three pieces:

  1. Expiry is known at booking, and enforced. The authenticated checkout returns the card expiry, so the connector compares it to the booking's latest slot and rejects a booking whose card expires before then (the later deposit or no-show could not be charged). No gateway round-trip needed.
  2. Network tokens survive reissuance. When the cardholder is reissued a card during the gap, scheme-managed credentials (network tokens, or Account Updater: Visa VAU / Mastercard ABU) keep the card-on-file chain chargeable. This is the closest thing to "guaranteed chargeable later" and is configured on the Worldline account, not in this connector.
  3. Live validity is only knowable at the time. A card can be cancelled, blocked, or out of funds at the slot date, and nothing at booking can know that. A zero-amount account-verification a few days before arrival can serve as an early warning before the real caution auth, leaving several days to ask for a new card or cancel.

What the Worldline connector therefore provides

  • Authenticated booking transaction with 3-D Secure, keeping the payment id as the card-on-file chain anchor: the paid checkout itself, or a small released card verification hosted checkout for a free booking that needs a card. No card token is stored.
  • Expiry check at booking against the latest slot (reject early).
  • Deferred caution authorization (a subsequent MIT), scheduled from the stay dates (cron or queue), sized to the stay, released or captured after departure.
  • No-show fee as a subsequent MIT against the payment id, on the workflow no-show outcome.
  • Re-authorization (new auth + reverse old) for stays longer than the hold window. Edge case, but supported.
  • Capture / refund / release for the caution and any real payment, mapped to the existing PaymentManager operations and the BookingPayment kind (payment | caution) and modes (authorize | charge).
  • Server-to-server webhook (HMAC-verified) that settles the booking payment even when the payer never returns from the hosted page; idempotent with the browser return, so whichever arrives first wins.

During the transition, while the legacy custom system still owns show/no-show, that system must signal yoyaku (via the client API, booking_api) for the no-show charge to fire. Once orchestra owns the attendance task, that becomes internal.