Returns and RMA
Hyvä POS is in closed beta
Hyvä POS is currently in a closed beta (pilot phase) with a small group of merchants. It is not yet generally available: the App Store release follows the pilot, and features and configuration may still change, possibly in backwards-incompatible ways, before the general release. Want to take part? Sign up at hyva.io/pos.
The free tier refunds and exchanges directly at the register. PRO routes returns through a full RMA workflow (request, approve, receive, settle) and combines those steps at the counter, so a walk-in return keeps the audit trail of a staged one.
The Lifecycle at the Register
A full RMA has four steps: the return is requested, approved, the goods are received and inspected, and the refund is settled. Online returns need all four because they happen days apart. At the counter, the customer and the product are both in front of you, so the register runs create, approve and receive as one step and goes straight to settling.
The cashier walks a five-step wizard: Select Items, Return Details, Refund, Confirm, Processing. Each returned line carries a reason (Wrong Item, Defective, Damaged in Shipping, No Longer Needed, Wrong Size/Color, Other), the condition it came back in, and its resolution (refund or exchange). All of it lands on the RMA in Magento, with a comment trail recording every step.
Before the return is created, the register asks the RMA provider which lines can still be returned and for which resolutions. A line outside the store's return period is refused with the period named: "... is outside the store's return period of 30 days and cannot be returned at the register. A return outside the period can be made in the Magento admin." The register has no override for the period; a return outside it is made in the Magento admin. Where the backend keeps a period per resolution (Amasty's Return Rules, for example a refund within 14 days and an exchange within 30), a line past one window is refused for that resolution and the message names the resolutions still open.
When the situation does call for stages (the customer will bring the product next week; the item needs inspection in the back), the register creates the RMA and leaves it open. The pending-returns inbox on the Orders tab shows every open return, oldest first, including returns the customer started online and walked into the store, and any register at the location can pick one up and finish it.
Partial Receive: Refund Follows the Request, Restock Follows the Goods
When fewer items come back sellable than were returned, the two quantities differ:
- The refund follows the requested quantity. A customer returning 5 items is refunded for 5, even if only 3 passed inspection. Inspection results affect the inventory routing, not the refund amount.
- The restock follows the received count. Only the 3 sellable items re-enter stock. The other 2 are routed by their condition.
Where the Stock Goes
Receiving records each line's physical condition, and the condition decides the destination:
- Pristine items restock to the location's main source.
- Damaged items route to the damaged-stock source you configure, so they cannot be resold by accident. With no damaged source configured, they land on the location's main source with a stock note.
A location that names no source of its own restocks to Magento's default source, where a single-source store keeps all of its stock. A store without Magento Inventory has no sources: received items go back into the product's plain stock figure, and the condition decides only whether a line restocks. - Dispose writes the line off entirely.
The cashier can also override the destination per line (useful for a "to refurbish" or transit source) or opt a line out of restocking altogether, which the RMA records.
Where the Money Goes
Settling creates the credit memo in Magento and closes the RMA. The destinations:
- Original payment: web orders refund through the payment gateway (Mollie and others); the PSP refund reference is stamped into Magento's transaction records, so the refund shows in the admin's Transactions view and accounting exports like any online refund. Open pay-by-invoice orders settle as a credit note against the receivable; no money moves at the register. An order paid through Mollie's Orders API (Mollie's web checkout) is refused before anything moves: "Refund this order from the Magento admin: its Mollie payment was made through the Orders API, which the register cannot track." A refund of such an order in cash or to store credit is not affected.
- Cash or card at the counter: the RMA parks as awaiting refund, the cashier hands the money back through checkout (drawer pay-out or terminal reversal), and the settlement books the memo with the tender used. Until the money is handed back, the order detail offers to resume the refund or to cancel it: cancelling puts the RMA back to received, with the items still in stock and no memo, and Process in Store lets the cashier choose the settlement again.
- Exchange: the returned value credits the new sale in the same cart. When the exchange order places, the RMA closes automatically.
- Store credit: the refund lands on the customer's account balance instead. Requires an active store-credit provider and a registered customer; when no provider can issue credit, the wizard shows the option disabled with the reason rather than hiding it. The order detail's Create Refund screen offers the same channel as Store Credit, beside Refund Online and Refund In Store, and settles it through the same return route.
When the Gateway Does Not Answer
The module records every refund through the original payment's gateway before it calls the gateway. When the call fails in a way that leaves open whether the gateway refunded (a timeout, a lost connection), the return is parked and nothing more is refunded for it: the register shows the refund as uncertain. It stays parked until it is reconciled, by asking the gateway again, or resolved by hand as refunded or not refunded, with the staff member's name and a note recorded on the return. Resolved as refunded, the credit memo is booked without a second gateway call. Mollie's and PAY.'s Magento modules keep nothing a refund could be found by afterwards, so an uncertain refund on those methods is resolved by hand: check the provider's dashboard first.
On PRO every refund the register books goes through the module's return and settle routes, the Create Refund screen's included. Those memos give back the store credit, gift card value and reward points the sale spent: the module hands each one to the active credit provider. A memo created through Magento's own POST /V1/order/:id/refund or the admin carries no such context and is left to the vendor.
Orders Paid With Store Credit, Gift Cards or Points
What a refund gives back to each balance depends on the credit vendor: Adobe Commerce and Amasty gift cards return the credit first, Amasty store credit returns the share the refunded lines spent. The register does not work this out. Module 1.3.0 answers a refund preview: the credit memo the vendors' collectors would build for the selected lines, never saved. The register asks it when the refund screen opens, on every change to the selection, and once more right before the tender. From it come:
- the most the order can still refund, which counts the credit the order can still give back;
- one summary row per credit kind (Store Credit, Gift Card, and Reward Points as a points quantity), so the Grand Total is the one Magento books and no adjustment fee is needed to make the figures agree;
- what goes back by the original payment. The checkout reads "€0.00 to hand back, €71.39 back to the balance" for a sale the credit paid in full, and keeps the drawer closed.
The invoice and the order totals show each credit as its own row.
A register on a module older than 1.3.0 refuses to refund an order that carries store credit, a gift card or points, and names the module version it needs. Orders without credit refund as before.
When the Store Books a Refund Differently
At the counter the money goes back first and the credit memo is created after, so another refund on the same order can land in between. The settle sends the figures the cashier acted on; the store books what its collectors produce and says how that differs. When it differs, when the settle fails after the money was handed back, or when another refund already used up the order, the register opens a refund difference on the return's sale record:
- It stays open across retries and app restarts, is listed under Reports → Sync Issues, and blocks the return from being closed or refunded again.
- The success screen names the difference while the customer is still at the counter.
- It closes as recovered only when money moves: the cashier hands back the rest, or takes the excess back, in cash through the open register session or on the card terminal, and records the amount and tender.
- It closes as written off only by a manager or an admin, with a reason. The register has no manager-override approval, so the signed-in staff member's role decides.
Each change is added to the return's history in Magento as a comment.
Stock follows the goods, not the paperwork. The register restocks each line when it receives the items, to the source the inspection verdict points at; the credit memo created at settlement returns nothing to stock a second time, whatever the store's automatic-return setting says. Closing a received return takes the restocked quantity back out. A credit memo created by hand in the admin for such an order is outside this: ticking Return to Stock there restocks the items twice.
On top of the item refund, the wizard supports a goodwill adjustment (extra credit for the inconvenience), a restocking or handling fee (subtracted), and a shipping refund. Each lands on the corresponding credit memo field, so the Magento books match what was refunded.
The Online-Refund Window
Payment providers stop accepting refunds against a payment after a provider- and method-specific window, from 90 days to a year or more, and some methods have no limit at all. When a return is older than the configured Online Refund Validity, the register shows a hint under the Original Payment and Terminal options: the provider may no longer accept the refund, while cash and store credit always work. It is a hint, not a gate: the option stays selectable, because the window differs per provider and per payment method, and the provider decides whether the refund is accepted. The same hint appears on the order-detail Refund sheet and on the return summary at checkout. The window is configured globally, can be overridden per location, and 0 turns it off.
RMA Backends
The staged workflow runs on an RMA provider, and exactly one is active per store:
| Backend | Ships as |
|---|---|
| Built-in POS returns | Included in the Hyva_Pos module, works out of the box |
Adobe Commerce RMA (Magento_Rma) |
Bridge: magento2-hyva-pos-adobe-commerce |
| Amasty RMA | Bridge: magento2-hyva-pos-amasty |
| Aheadworks RMA | Bridge: magento2-hyva-pos-aheadworks |
With a vendor bridge installed (see bridge modules for their state), the POS drives your existing RMA module: in-store and online returns share one system, one numbering, one admin view. One caveat that comes from the vendor modules themselves: they only accept returns on shipped items, so counter sales that never had a shipment are served by the built-in provider. Agencies can build a bridge for other vendors.
Configuration
- Stores → Configuration → Hyvä POS → Advanced → Credit & Return Providers → Return (RMA) Provider: pick the backend, or leave it on auto-detect; an installed vendor bridge wins over the built-in provider.
- Stores → Configuration → Hyvä POS → Terminal Defaults → Returns Defaults → Damaged-Stock MSI Source: where damaged returns go. Overridable per location.
- On the register, the return flow is gated by the issue refunds permission on the staff role, so who may hand money back is a Magento decision, not a device setting.
Returns for sales made on another terminal, or before the device existed, work through receipt sync: the register reconstructs the receipt from the central archive.
Offline
Starting a return needs a connection: the order and the RMA live in Magento. If the connection drops after the cashier has already handed the money back, the server-side half (credit memo and RMA closure) queues on the device and completes on reconnect. Settlement is idempotent on the server: a retry after a timeout that did succeed is a no-op rather than a second refund.