Refunds — Reference
The Refunds surface is the money-back plane for every SGEN ecommerce site. It owns the refund record, the scope-and-amount composer that drives each refund, the refund-reason taxonomy, the restock decision, the tax-recovery logic, and the handoff to the configured payment provider that moves funds. Anything that returns value to a customer after capture traces back to a record managed here.
This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or reconciling against an external accounting system. Customer-facing how-tos live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.
Overview
Refunds live under the Refunds sub-module inside the ecommerce surface in SG-Admin. The module renders three primary views — the refund list, the refund composer (scope, amount, reason, restock, tax), and the refund detail page (status, provider response, ledger entries) — and exposes a small set of write operations for issue, retry, cancel-pending, and soft delete.
The module is paired by convention with the Orders surface and, by extension, with Invoices. Every refund record links back to a parent order (and optionally to an invoice); the parent's refundable balance is the upper bound on what a refund can return. Refunds are append-only against the parent — a failed refund records a failure but does not consume any refundable balance; a successful refund consumes the refunded amount permanently.
A second surface — payment-provider handoff — sits underneath every refund. The refund record initiates the operation; the configured payment provider determines whether the operation completes. Providers vary in their support for partial refunds, line-level refunds, and asynchronous-vs-synchronous response — the surface normalizes the response into a stable refund-status lifecycle regardless of provider.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Ecommerce → Refunds (and inline on the order detail page)
- URL prefix:
/sg-admin/refunds/ - View templates:
application/views/Admin/Refunds/
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Ecommerce → Refunds [+ New refund] │├──────────────────────────────────────────────────────────────────────┤│ # Order Amount Scope Status Issued ││ ───── ────── ──────── ──────────── ────────── ────────────────││ RF-308 1042 $148.00 Full Completed 2 min ago ││ RF-307 1040 $ 75.50 Partial-line Completed 18 min ago ││ RF-306 1039 $ 14.00 Tax-only Completed yesterday ││ RF-305 1038 $ 32.50 Partial-amt Pending 3 hours ago ││ RF-304 1037 $ 89.00 Full Failed 1 day ago ││ ││ Filter: [Status ▼] [Reason ▼] [Date range] Bulk: [Export] │└──────────────────────────────────────────────────────────────────────┘Actions
The Refunds surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.
List and search
Returns the refund records visible to the current operator, paginated, with refund number, parent order, amount, scope, status, and issued-at columns. Supports column sort, free-text filter, status filter, scope filter, reason filter, and date-range filter. Driven by the data-table contract used across SG-Admin modules.
View refund detail
Loads the full refund record into a read-mostly detail page — parent order summary, scope and amount breakdown, reason and operator note, restock decision, tax-recovery line, payment-provider response, and the running status history. Editable fields are gated behind separate actions; the detail page itself does not write.
Issue refund
Opens the refund composer against a named parent order. Operator picks scope (full / partial-line / partial-amount / tax-only / restock-only), enters reason from the configured taxonomy, optionally adds an operator note, and decides whether to restock the affected line items. The surface validates the refund against the parent's remaining refundable balance, computes the tax-recovery portion based on the configured tax rules, calls the configured payment provider, writes the refund record, and updates the parent order's refund ledger.
Engineering note: the surface treats the provider call as the authoritative truth. A successful provider response advances the refund tocompleted; a failure marks the refundfailedwith the provider error attached and leaves the parent's refundable balance unchanged. No partial state is recorded on the parent — the refund record holds the failure detail in isolation.
Issue refund against invoice
A variant entry. Refunds against a paid invoice (rather than directly against an order) record the same refund shape but link to the invoice instead of an order. The invoice's payment-event ledger gets a reversal entry; the invoice's paid total drops; status may transition back to partially-paid or sent depending on the resulting balance.
Retry failed refund
A focused write path. Re-runs the provider call against an existing refund record in failed status. Useful when the original failure was transient (provider downtime, rate limiting, expired authorization). The record carries a retry counter visible in the detail view; provider configuration determines the maximum allowed retries.
Cancel pending refund
Aborts a refund that is in pending status (the surface is awaiting the provider's asynchronous response). Cancel issues a cancellation call to the provider where supported and marks the local record cancelled. Providers that do not support cancellation return a structured error and the record remains pending until the provider finalizes.
Set refund reason
Updates the reason on an existing refund record without changing amount or scope. Useful for correcting a misclassified reason after the fact. Only the reason taxonomy slug is editable; the human-readable description on the operator note is editable separately. Both edits are tracked in the audit log.
Restock line items
A focused write path that runs alongside issue refund. When the operator opts in at refund time, the surface adjusts inventory levels for the affected line items by the refunded quantity. Restocking can be skipped (operator opts out) for damaged returns or for service refunds where no physical good moves. Restock decisions are immutable after the refund completes; correcting a missed or unwanted restock requires a manual inventory adjustment.
Soft delete
Marks the record inactive without removing it from the database. Soft-deleted refunds disappear from the default list view but remain queryable via the archive filter and continue to back reporting and reconciliation queries. Available only for refunds in failed or cancelled status; completed refunds cannot be soft-deleted (they represent real money movement and must remain visible).
Restore
Reverses a soft delete. The record returns to the active list with its prior scope, status, and provider response intact.
Data model
A refund record carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
refund_number | string | Operator-facing identifier. Configurable prefix. |
order_id | integer | References the parent order. Required unless invoice_id is set. |
invoice_id | integer | References the parent invoice. Optional. |
scope | enum | full, partial-line, partial-amount, tax-only, restock-only. |
amount | decimal | The cash-recovery portion of the refund. |
tax_recovery | decimal | The tax-recovery portion. Sum of amount + tax_recovery is the total returned to the customer. |
currency | string | Inherits from the parent. Immutable after create. |
line_targets | array | Set for partial-line scope — per-line refund quantities and amounts. |
reason_slug | string | One of the configured reason taxonomy slugs. |
reason_note | string | Optional human-readable operator note. |
restock | boolean | True if affected line items should be returned to inventory. |
status | enum | pending, completed, failed, cancelled. |
provider_reference | string | Returned by the payment provider on success. |
provider_response | object | Raw provider response cached for audit. |
retry_count | integer | Incremented on each retry attempt. |
issued_at | timestamp | Set when the refund leaves pending. |
completed_at | timestamp | Set when status reaches completed. |
created_at | timestamp | Set on create, immutable. |
updated_at | timestamp | Touched on any edit. |
full returns the parent's full grand total; partial-line returns a subset of line items at specified quantities; partial-amount returns a fixed amount independent of line items; tax-only returns the tax portion of a prior amount (used for tax-correction after the fact); restock-only returns no money but adjusts inventory.Tax-recovery semantics: the surface computes the tax portion of a refund using the same tax rules that were applied to the parent at capture time, not the current live tax rules. This matters for jurisdictions that change tax rates after a sale — the refund returns the customer's exact paid tax, not a recomputation against a different rate.
Provider response semantics: provider_status is whatever the provider returned; the surface maps it to the normalized refund-status enum but preserves the raw response in provider_response for reconciliation. Failed refunds keep the provider error attached for as long as the record exists.
REFUND RECORD├── id integer primary key├── refund_number string operator-facing identifier├── parent order_id OR invoice_id (one required)├── scope enum full | partial-line | partial-amount | tax-only | restock-only├── amount decimal cash-recovery portion├── tax_recovery decimal tax portion (against original tax rules, not live)├── line_targets array set for partial-line scope├── reason reason_slug + reason_note├── restock boolean inventory cascade decision├── status enum pending → completed | failed | cancelled├── provider provider_reference + provider_response (raw)├── retry_count integer└── timestamps issued_at, completed_at, created_at, updated_at↓ updates parentORDER / INVOICE REFUND LEDGERrefunded_total += amount (on completion only)Permissions
Access to the Refunds surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Refunds surface.
Layer 2 — per-action capability. Within SG-Admin, each Refunds action checks a capability associated with the calling operator's role. The default role configuration ships with three refund-relevant roles — Store Manager, Support Operator, Accounts Receivable — layered on top of the platform's base roles. The capability map is:
| Capability | Administrator | Store Manager | Support | A/R |
|---|---|---|---|---|
| List refunds | ✔ | ✔ | ✔ | ✔ |
| View detail | ✔ | ✔ | ✔ | ✔ |
| Issue refund | ✔ | ✔ | ✔ | ✔ |
| Issue refund against invoice | ✔ | ✔ | — | ✔ |
| Retry failed refund | ✔ | ✔ | ✔ | ✔ |
| Cancel pending refund | ✔ | ✔ | — | ✔ |
| Set refund reason | ✔ | ✔ | ✔ | — |
| Restock decision | ✔ | ✔ | — | — |
| Soft delete (failed/cancelled only) | ✔ | ✔ | — | — |
| Export | ✔ | ✔ | ✔ | ✔ |
Self-protection rules. A refund cannot exceed the parent's remaining refundable balance (parent's captured amount minus the sum of completed refunds). A completed refund cannot be soft-deleted — completed refunds represent real money movement and remain visible. A refund whose parent order is itself soft-deleted can still be viewed but cannot be retried; the parent must be restored first. Restock decisions on restock-only scope are mandatory; the surface rejects the record otherwise.
Audit. Every write — issue, retry, cancel, reason update, restock cascade, soft delete, restore — emits an Activity Log entry and an entry to the refund's own status history. The log records the acting operator, the target refund, the parent reference, and the change shape. Provider responses are cached on the record itself for reconciliation. Activity Log retention is governed by the site's general settings.
REQUEST│▼┌───────────────────────────┐│ Admin gate │ unauth → reject└─────────────┬─────────────┘│ authed▼┌───────────────────────────┐│ Capability check │ role lacks cap → reject│ (per-action, per-role) │└─────────────┬─────────────┘│ allowed▼┌───────────────────────────┐│ Balance + scope guards │ amount > remaining → reject│ │ delete-completed → reject└─────────────┬─────────────┘│ passes▼┌───────────────────────────┐│ Payment provider handoff │ pending → completed | failed└─────────────┬─────────────┘│ on completion▼Parent refund ledger updatedInventory adjusted (if restock)Activity Log entry writtenRelated references
- Orders — Reference. Refunds against orders read from the order's captured-amount and produce entries in the order's refund ledger. Most refund traffic enters via the order detail page.
- Invoices — Reference. Refunds against paid invoices record a reversal in the invoice's payment-event ledger and drop the paid total.
- Inventory — Reference. Restock decisions on a refund cascade into inventory-level adjustments per line item.
- Settings — Reference. Owns the refund reason taxonomy, the payment provider configuration, the retry limits, and the tax rules that drive tax-recovery calculations.
- Users — Reference. Operator identity on every refund record is the acting
user_id; re-attribution applies on permanent user delete. - Custom Codes — Reference. Refund confirmation email templates are themed through the template surfaces.
/docs/refunds.