# Send money back exactly once: implementation spec

Follow these steps in order. Each step ends with a check. Stop and ask the site owner when a step says "ask".

- Guide: https://nano133.com/guides/send-money-back-exactly-once · as Markdown: https://nano133.com/guides/send-money-back-exactly-once.md
- Stage: **Alpha**. We ran its code and its checks pass. It read a real node (read only), or, if it needs no node, it ran on real signatures or real files. Where it copies code that nano133.com runs, a test shows both act the same. Last checked 2026-10-25.
- Use at your own risk, for education. Real Nano moves: keep amounts small.
- Code files: https://nano133.com/guides/send-money-back-exactly-once/code/return-once.mjs · https://nano133.com/guides/send-money-back-exactly-once/code/safe-send.mjs

## Rules that must hold

1. Decide from the payment's own block, never from the list alone. When the node does not answer, skip the payment for this run. Never return a payment that is not confirmed, that is younger than the grace of your checkouts, that a checkout can still claim, or that came from your own wallets.
2. Record the return, the used mark and the counters in one transaction. Your claim code must refuse a payment whose hash is used. The address to send back to is read from the payment's block.
3. Run returns from a timer, never from a visitor's request, under the wallet's one lock. A failed return stays pending and is tried again by the next run. Never send a return that a record says went out.

## Steps

The guide has the how for each step. This list has what must be true at its end.

1. **Find the payments that wait.** Check: pay a mock wallet 0.002 XNO, three hours old. `node return-once.mjs candidates <the wallet's address>` prints it, and writes nothing. `node return-once.mjs scan ./returns.db 0` prints `recorded 1, returned 0`, the ledger holds no send, and the wallet received nothing.
2. **Decide that it must go back.** Check: `node return-once.mjs why <hash> <wallet>` answers `goes back to <sender>`, or `stays: <the reason>`: not confirmed, not a send, sent to another address, below the smallest payment, a sender that never gets a return, too young, or an open checkout can still claim it. Each reason has its own answer.
3. **Record the decision, and mark the payment used.** Check: run `scan` twice: one return, not two. A payment that a checkout claimed is not recorded. A payment that a return recorded cannot be claimed (`claim` answers `refused`). A sender's third payment is `kept` when the cap per sender is 2.
4. **Receive it, and send exactly its amount back.** Check: run `node return-once.mjs run ./returns.db 0` for that payment: it prints `recorded 1, returned 1`. The ledger holds one send, of exactly 0.002 XNO, to the address that paid, and the wallet holds none of it. Run it again: `recorded 0, returned 0`, and still one send.
5. **Survive a stop, and run on a timer.** Check: stop a run after it signed the send: the ledger holds nothing, and a later run sends the recorded block, once. Stop one after the node took the block: the later run sends nothing more. Start two runs at once: one works, the other answers `busy`.
6. **Keep the limits, and tell people.** Check: `release` turns a *kept* payment into a pending one, and the next run sends it back, once. `release` leaves a payment that is not kept alone, and answers `missing` for one it does not know.

## Done when

- [ ] Step 1 check passes.
- [ ] Step 2 check passes.
- [ ] Step 3 check passes.
- [ ] Step 4 check passes.
- [ ] Step 5 check passes.
- [ ] Step 6 check passes.
- [ ] `node test.mjs` passes in mock mode (put the code files next to it). It never sends money.
- [ ] You told the owner the stage of this guide and of the track you used.

## What breaks, and what it costs

| Failure | Loss in Nano | Check |
| --- | --- | --- |
| A payment is returned and also pays a checkout | The shop gives the goods and returns the money | Step 3: one transaction for the return and the used mark, and a claim that refuses a used hash |
| A return is sent twice | The buyer gets the amount twice | Steps 4 and 5: the payment's hash is the job of the safe send, and each stage is recorded |
| A payment is returned while its checkout can still claim it | The buyer's good payment goes back, and they get nothing, or the checkout is paid and the payment goes back too | Step 2: wait 2 hours, and ask your checkouts |
| A payment that is not confirmed is returned | A send of money that the wallet may not have | Step 2: confirmed blocks only |
| The return goes to an address that came from a request | The money goes to whoever wrote the request | Step 3: the sender's address is read from the payment's block |
| The sender is an exchange | The money goes to the exchange's address, not to the person's account there, and may not reach them | Step 6: tell buyers |
| An owner's top-up is returned | You send your own money away | Step 2: your own wallets and the owner's address are on the never list |
| A flood of small payments | Two blocks of work for each one, and the wallet's work grows with the flood | Steps 2, 3 and 6: the smallest payment, and the caps for each run, day and sender |
| The node errs during the scan and the code decides anyway | A payment is returned that should stay, or the other way | Step 2: skip the payment this run |
| A return waits with an error for ever | The buyer's money stays in the wallet | Step 5: the next run tries again, and failures are logged |

## Notes for agents

Ask the site owner for these five things before you start. Never guess them:

1. The wallet that the checkouts pay into, and the confirmation that the server holds its key in a secret manager. If the owner pays into a wallet that the server has no key for (a phone wallet, an exchange), stop: returns are not possible.
2. The place where the site records each claimed payment by its send block hash, and the confirmation that the claim code refuses a hash that is used. Change the claim code so that it does, in the same transaction that credits the checkout.
3. The grace of every checkout (how long a late payment still counts), so that the minimum age is longer than the longest grace. The default is 2 hours.
4. The limits: the smallest payment that goes back, the caps for each run, day and sender, and the senders that never get a return (their own wallets, an owner's top-ups).
5. The words for their terms about returns, kept payments and exchanges. Never write legal text for them.

All code that signs for the wallet shares the wallet's one lock. Never send on a real network without the owner. Test with the throwaway secret that `node test.mjs` makes, and send no Nano. The address a return goes to is read from the payment's block, never from a request.
