DineGlobeDocs

Refunds

Request, approve and execute refunds: thresholds, the fresh-MFA rule, the second-person rule, disputes and pending-versus-settled refunds.

ManagerFinanceOwner

Refunds never happen quietly in DineGlobe. Every refund is requested under the venue’s own lock, bounded by the refundable balance, confirmed with fresh two-step verification and — unless the amount sits at or below the owner’s delegation threshold — approved by a second person who can never be the requester. Until the provider settles the money, a refund is displayed as pending, never as refunded. The pages that matter here are the Refund workflow and Payment status and finance views panels in the operator console at /app/panels.

Who may do what

Act Permission Roles
Request a refund, read the money views payments.refund.execute Owner, org admin, manager, finance
Approve or decline a refund, set the delegation threshold payments.refund.approve Owner, org admin only
Anything with money — Host and door are refused; they see no money views

Two further guards apply to the humans in this workflow:

  • Fresh MFA. Both requesting and deciding demand that the live session was MFA-verified within the last 30 minutes. Without it the action answers 401 MFA_REQUIRED — complete the authenticator prompt and retry promptly; the window is deliberately short.
  • Approver ≠ requester. A decision by the person who requested the refund answers 403 PERMISSION_DENIED. An owner cannot approve their own refund.

Requesting a refund

A refund is requested against a payment attempt whose capture has already been reconciled, with the amount in exact minor units, a mandatory reason and an idempotency request key.

The request runs under the venue lock and is bounded by the refundable balance:

captured net − settled refunds − open disputes − other pending refund intents

If the requested amount exceeds that balance, the request refuses with 422 REFUND_EXCEEDS_AVAILABLE and names the exact remaining amount — refund at most that. Nothing is written.

The request key is idempotent: replaying the same key returns the stored result, and the same key with a different payload answers 409 CONFLICT — nothing is rewritten.

The delegation threshold

The owner sets a per-venue threshold in minor units. It decides who may act alone:

  • At or below the threshold — the intent is born approved: the message reads “within delegated authority (approved at request)”.
  • Above the threshold — the intent waits for a separate ownership-tier decision.
  • No threshold configured (empty) — the fail-closed default: every human refund needs an approval. Clearing a threshold deliberately restores this state; the save confirms with “every human refund now needs owner approval (fail closed)”.

The decision is recorded as an immutable evidence row with its own mandatory reason. There is one decision per intent per approver and the history is append-only. A declined decision cancels the intent.

Intent states

State Meaning
REQUESTED Created and bounded; awaiting a decision when the amount is above the threshold (or no threshold exists).
PENDING_PROVIDER Released to the provider, not settled yet. Displayed everywhere as pending — never as “refunded”.
SUCCEEDED The provider settled the refund; its ledger row is written exactly once.
FAILED The provider permanently refused. Terminal — manual resolution with the provider.
CANCELLED The decision came back declined, or the intent was cancelled.

Execution and recovery

Approved intents (and system-generated ones, such as the compensation refund for money that arrived after a hold died) are executed by the worker sweep — staff can also fire it from the panel. Execution carries the stable idempotency key refund:{intentId} and is retrieval-first: if the create call times out, the next sweep retrieves the SAME provider object instead of issuing a second refund.

  • 503 PAYMENT_REFUND_TIMEOUT — the provider did not answer and may have stored the refund. Retryable: the next sweep recovers the same refund.
  • 422 PAYMENT_REFUND_REJECTED — the provider permanently refused. Terminal; resolve manually. The system never double-collects to work around a refusal.
  • 422 REFUND_INVALID_TRANSITION — the intent is not in a state that accepts that action. Reload it; it may already be settled, failed or cancelled.
  • 409 CONFLICT — this key or this approver already recorded something. Nothing was rewritten.

Reconciling a single refund repairs truth from the provider: if the provider settled, the intent becomes SUCCEEDED with its ledger row (never duplicated); if it is still pending, it stays pending with a reconciliation stamp. This is also how a restored database is brought back in line with provider truth.

Disputes

The transaction ledger has a DISPUTE kind alongside captures and refunds. Disputed money is subtracted from the refundable balance — the chargeback process owns that money, so it can never be paid out a second time. V1 ships no dispute-intake screen; a dispute row reaches the ledger as evidence and immediately reduces what remains refundable on that attempt.

Reading the money views

  • Reservation payment status — the reservation badge and the payment badge are separate lines; receipts carry the provider’s own effect ids; a refund pending at the provider reads PENDING, never “Refunded”.
  • Finance transactions — settled ledger rows by transaction date and currency. Totals aggregate per currency and are never combined across currencies. The pending queue lists every unresolved refund demand: awaiting decisions, pending at the provider, and failed escalations.
  • Refund intent queue — REQUESTED rows await the workflow; nothing in the queue calls a provider.

Aggregate per-currency money also feeds the operational reports — see Reports & metrics.

The pilot posture

No live payment provider is configured in the pilot environment, so the execution sweep has nothing real to run against: it answers 503 PAYMENT_PROVIDER_UNAVAILABLE and fabricates nothing. The request chain, the balance arithmetic, the approval evidence and the ledger are real and exercised; the provider leg waits for the capability gates described in Payments to open.

In other words: configure thresholds, rehearse approvals, audit decisions — and expect an honest refusal the moment a command would have to reach a real provider. The gate posture itself is described in Payments; the full code reference lives in Error codes & fixes.