Refund
A refund sheet for issuing full or partial refunds on an order. Handles prorated discounts, restocking, shipping, split payments and failed or pending refunds.
Refund is the screen a merchant opens from an order to send money back. RefundSheet wraps it in a wide sheet on desktop and a full-height sheet on phones, and RefundForm is the same form for embedding in your own page. Each line has a quantity stepper from 0 to what’s still refundable, already refunded lines are listed as Refunded, and a summary on the right recalculates as you go.
The math lives in calculateRefund(order, selection), a pure function with tests. Order-level discounts are split across lines in proportion to each line’s price, and tax in proportion to each line’s price after discount, with the largest-remainder method so the parts add up to the cent. Each unit then takes a cumulative slice of its line’s share, so refunding units one at a time adds up to exactly what refunding them together would. When the selection takes everything that’s left, the total is set to the amount still available, so earlier rounding never strands a cent.
Shipping can be refunded up to what was paid minus what was already refunded. The total can be edited by hand, showing how far it is from the calculated amount, and is checked against what’s available. Money goes back to the payment methods in the order you pass them, so a gift card split shows both parts. Currencies without decimals, like yen, are handled in minor units throughout.
onRefund receives a RefundRequest and can return a Refund. A succeeded refund closes the sheet, a pending one shows “Refund pending with the bank”, and a failed one or a thrown error shows its message next to the button with everything still filled in. The button guards against double submits, and refunds over half the order or the whole order ask for confirmation first.
Procurez-vous HextaUI Pro
Un seul paiement débloque tous les blocs, y compris les prochains. Voir les tarifs.
Ajouter le registre Pro à components.json
components.json Ajouter votre token
Créez un token sur votre page de compte et placez-le dans
.env.localsous le nomHEXTAUI_PRO_TOKEN.Ajouter le block
pnpm dlx shadcn@latest add @hextaui-pro/refund
Shopify refundCreate
Ask Shopify’s suggestedRefund for the parent transactions, then send refundCreate with the line items, restock location and shipping from the request. A FAILURE or PENDING transaction comes back as a failed or pending Refund, so the sheet shows the right state.
Stripe refunds API
A server action that creates one Stripe refund per transaction in minor units, with an idempotency key so a retry never refunds twice. Stripe’s failure_reason turns into a message the merchant can act on.
Medusa payments
Map Medusa payments to refund sources and call the admin refund route for each. Reasons come from your refund reason IDs.
Anatomie
Les parties à composer, de l’extérieur vers l’intérieur.
| Partie | Description |
|---|---|
RefundSheet | The sheet with the order name, customer and what they paid, wrapping RefundForm. |
RefundForm | Line steppers, restock, shipping, reason, note, notification and the sticky summary with the submit button. |
RefundQuantity | The 0 to max stepper used on each line. |
RefundHistory | Earlier refunds with amount, date, items and status. |
calculateRefund | Pure refund math: prorated discount and tax, shipping, manual amount, available amount and payment split. |
parseAmount | Reads a typed amount like 1,234.50 or 12,50 into Money, respecting the currency’s decimals. |
RefundSheet
Takes every RefundForm prop except onClose.
| Prop | Type | Par défaut |
|---|---|---|
openControlled open state. | boolean | – |
defaultOpenInitial open state when uncontrolled. | boolean | false |
onOpenChangeCalled when the sheet opens or closes. Closing is ignored while a refund is processing. | (open: boolean) => void | – |
triggerAn element that opens the sheet, such as a Refund button. | React.ReactElement | – |
RefundForm
Also takes form props.
| Prop | Type | Par défaut |
|---|---|---|
orderThe order to refund. Lines, discounts, tax, shipping and earlier refunds drive the math. | Order | – |
onRefundIssues the refund. Return a Refund with status pending or failed to show those states, or throw to show the error. | (request: RefundRequest) => Promise<Refund | void> | – |
onCompleteCalled after a refund succeeds or is pending. | (refund: Refund) => void | – |
paymentsWhere the money goes back, in order. Defaults to the order’s payment method. | RefundPayment[] | – |
locationsRestock locations. Hidden when empty. | { id: string; name: string }[] | [] |
defaultLocationIdThe location selected first. Defaults to the first one. | string | – |
reasonsReasons in the select. | string[] | Customer changed their mind, Damaged, Wrong item, Didn’t arrive, Other |
noteRequiredForReasons that need a note. | string[] | ["Other"] |
restockableReturn false for items that aren’t physical, such as gift cards or downloads. | (line: OrderLine) => boolean | () => true |
defaultRestockWhether restock starts on. | boolean | true |
defaultNotifyWhether the customer notification starts on. | boolean | true |
confirmAboveAsk for confirmation above this share of the order total. Refunding everything always asks. | number | 0.5 |
localeLocale for money and dates. | Intl.LocalesArgument | "en-US" |
labelsEvery user-facing string. | Partial<RefundFormLabels> | – |
RefundRequest
What onRefund receives.
| Prop | Type | Par défaut |
|---|---|---|
linesSelected lines and whether each is restocked. | { lineId; quantity; restock }[] | – |
restockLocationIdWhere items are restocked, or null. | string | null | – |
shippingShipping being refunded. | Money | – |
amountThe refund total, calculated or typed. | Money | – |
calculatedThe calculated total, for auditing a manual amount. | Money | – |
transactionsThe split across payment methods. | { paymentId: string; amount: Money }[] | – |
reason, note, notifyReason, staff note and whether to email the customer. | string | null, string, boolean | – |
calculateRefund
calculateRefund(order, selection, { payments }) returns RefundCalculation.
| Prop | Type | Par défaut |
|---|---|---|
selection.linesUnits to refund per line ID. Clamped to what’s refundable and flagged in errors. | Record<string, number> | – |
selection.shippingShipping to refund. Capped at shipping paid minus shipping refunded. | Money | null | – |
selection.amountA manual total. The result reports the difference from the calculated amount. | Money | null | – |
resultitemsSubtotal, discount, tax, shipping, calculated, total, available, maxShipping, allocations, share, everything and errors. | RefundCalculation | – |
| Touche | Action |
|---|---|
| ArrowUpArrowDown | In a quantity field, refund one more or one less unit. |
| HomeEnd | In a quantity field, jump to none or all refundable units. |
| Enter | Submit the refund, or confirm it in the dialog. |
| Escape | Close the confirmation or the sheet, unless a refund is processing. |
- Each quantity field is a labelled spinbutton with its range, and the buttons say which item they change.
- The selection and total are announced politely as they change. A failed refund is an alert next to the button, and pending or success results are announced as status.
- Refunded lines are marked with text, not only dimmed. Totals roll per digit with tabular figures and stay still with reduced motion.
- Focus moves to the invalid field when submitting with a problem, and returns to the submit button after the confirmation closes.
Construit avec
Les composants HextaUI gratuits dont Refund est constitué. Chacun s’installe séparément.
Code
7 fichiers, ajoutés à components/blocks/refund.