iVendNextDevelopers Request a sandbox

S51 — Refund rules (pp_rfr): how the till may pay a refund back

The retailer decides how a refund goes back. A return against a sale goes back only by the payment methods that paid that sale, plus the retailer's store credit and any method it always allows. A return without its sale goes back only by the methods the retailer names. When the cashier opens a refund, the till dims the methods the rules leave out, with the reason, or refuses the refund in the app's words.

When to use it: a sale paid by card alone must not be refunded in cash, or a no-receipt return must go to store credit. A payment method's own Allowed in Returns holds for every refund alike; this app looks at the sale being refunded. Each store refunding only its own sales needs no app: leave Use inter-store transactions off in Retail Setting (the default), and a till opens only its own store's sales for a return against a sale (a layaway plan's refund is payable at any store of the company).

What happens

When What the cashier sees
A return against a sale paid by card, with Refund by the sale's own methods The card method, the store credit method and the Always allowed ones are offered; the rest are dimmed: "Not for this sale: Refunded only by the methods your rules keep for this sale."
None of the till's methods would stay (the methods offered are the POS Profile's whole payment table, a method the store switched off for returns included) "This refund can't go ahead" and "None of the methods that paid for this can take its refund here. Get a manager."
A return without its sale, with methods named Only those and the store credit method; none of them offered here: refused, naming them
A return without its sale, no methods named, Refund by the sale's own methods ticked Only the Always allowed methods and the store credit method: a no-receipt return is no way round the rule
An exchange Its returned items are always judged (their credit note is checked alone, whatever the replacement costs) and keep the store credit method, so the exchange goes on; its new items are a sale, never judged
An exchange whose replacement costs less, at a till with a cash drawer ⚠ The till pays the difference in cash; on a sale whose rules leave cash out, the credit note is refused after the exchange is rung. Return the items instead (to the methods kept) and ring the new ones as a sale
An order's or a layaway plan's refund The methods rule as above (every method that paid into the order or plan stays)
A sale, or a return whose total is zero or more Not judged
No settings record, or every rule off As today
The service down, or the retailer's records unreadable The check's down answer: the refund goes on and is flagged on Sales to review
What the retailer and the cashier see (on a demo site, 2026-10-24)
What the install will change, read before you consent
The settings record Default: the methods rule ticked, the store credit method chosen
The check switched on for one POS Profile
The refund of a cash sale: the card dimmed with the app's reason, cash and store credit offered

What the retailer does

Before you install: every host.docker.internal address in manifest.json (in allowed_urls, and where the manifest calls it) is our test bench's own address for the sample's service, and it resolves only where Docker provides it. Replace it with your own service's address first, or iVendNext's calls never reach your service; if this manifest is already installed, raise its version and install it again (the manifest recipe, step 6).

  1. Install the app from the App library (or paste its manifest into Retail Extension Package in Desk), read what it will do and consent. The install adds the record type PP RFR Settings with its table PP RFR Method, the role PP RFR Integration, the check and the feed registration, the check switched off. Nothing changes at the till yet.
  2. Make the app's integration user (the integration user recipe): tick Is Desk User, give it the role PP RFR Integration alone and generate its API key. Choose the user on the feed registration PP RFR Settings feed.
  3. Fill the settings in PP RFR Settings: one record, its title Default. Tick Refund by the sale's own methods, choose your Store credit method: the Mode of Payment of type Store Credit the till pays exchanges with (keep one such method enabled, and choose it here), never taken off, so exchanges go on; add any Always allowed method, and name the methods for a return Without its sale. Whoever keeps the settings needs Select on Mode of Payment. On each POS Profile the rules hold at, tick Allowed in Returns for your store credit method and each card method a refund may go back to: the rules only take methods away, and a till where none of the kept methods is allowed in returns leaves the cashier nothing to choose (and refuses every exchange).
  4. Switch the check on: open Refund rules in Retail Extension Subscription, read what is sent, tick the consent, choose the POS Profiles of the stores the rules hold at, and switch it on. Last, press Connect again on the app's record, which sends the integration user's key to the service.

Where it shows. The settings are a Desk record the retailer keeps (its System Manager, or a role it gives the rights to). At the till the cashier meets them when a refund's choices open.

What leaves the retailer's iVendNext. Before each refund the till pays (and at each Pay, answered at once without a read), the check's context goes to the service's address: each line's item, group, quantity and amounts, the totals, the store, till, POS day and terminal, the cashier's user, the customer and their customer group, the coupons, the payment methods offered, and on a return the sale it refunds with the payment methods that paid it; never a card. The service reads the retailer's settings record with the integration user's key and keeps it one minute. It keeps nothing of a sale and writes nothing. A hosted copy holds each retailer's check secret and its user's API key from Connect again until the retailer removes the app. Say so on your card.

The files

File What it is
manifest.json the record type PP RFR Settings (named by its title, the service reads Default) and its table PP RFR Method (a payment method, used for Always allowed and Without its sale), a feed registration (it names the app's one integration user), a help address, and one check, installed switched off; when the service is down the refund goes on, flagged
refunds.py the pure core: the signature check (verify), the shape check, the retailer's settings (settings_of) and the answer (on_check)
service/app.py the service, standard library only: POST /rfr/check, GET /health, and the app template's handshake for many retailers; one handle answers the web server and the function entry (lambda_handler) alike
Dockerfile · app.json the settings file for hosting ("runs": "function": on CitiXsys hosting it is a function, paid per call) and an image for your own server (built from the kit's root)
tests/ the rules, the service over the wire with signed calls (one retailer and many), iVendNext's reads against a stand-in iVendNext, every answer against the published contract, the manifest; canaries.py breaks each guarded rule and expects its test to fail

Run it

python3 -m unittest discover -s samples/s51_refund_rules/tests -p 'test_*.py'
python3 samples/s51_refund_rules/tests/canaries.py
python3 samples/s51_refund_rules/service/app.py samples/s51_refund_rules/service/.env.local

Settings, one retailer (service/.env.local, never committed): IVN_CHECK_SECRET (handed back at install as pp_rfr:sub:refunds), IVN_SHOP_URL, IVN_SHOP_KEY (<api key>:<api secret> of the integration user), PORT. RFR_PREFIX names the settings record type (default PP RFR).

Many retailers, hosted

The service is the app template's shape: set IVN_LIBRARY_APP and a store (IVN_SHOPS_TABLE on CitiXsys hosting, else IVN_SHOPS_FILE). Each retailer connects itself after its install and hands over the check's secret; Connect again hands over its integration user's key. Until the key arrives the service answers 503 and the down answer applies. Every call names its retailer (X-iVendNext-Site), and each retailer is answered from its own settings with its own key.

Make it your own

What a partner usually changes:

Before you copy it

Technical notes

Why it is built this way

Option Cost Verdict
Do nothing: untick Allowed in Returns (on each POS Profile's payment rows) for every method a refund may not use 0 per profile, never per sale: a card sale and a cash sale are refunded by the same list
A setting in Payment method rules ~60 lines on a listed app; no extra call at each Pay, no extra check slot its rules key on the customer group at the sale and its retailers set it up that way today; these key on the refunded sale. A retailer who wants both apps pays one more call per Pay and one of three slots: chosen for a sample a partner copies alone
A check answered by a function, judging only refunds ~250 lines of core and service on S50's copy picked: holds at every door that pays a refund, on the stores the retailer switches it on for

The answer, exactly

Bounds, stated

Needs this serves

This page in the kit