Download Latest Version OpenReceive v0.4.33 source code.zip (14.5 MB)
Home / v0.3.2
Name Modified Size InfoDownloads / Week
Parent folder
openreceive-rails-0.3.2.gem 2026-08-29 38.9 kB
openreceive-server-0.3.2.gem 2026-08-29 49.2 kB
openreceive-0.3.2.gem 2026-08-29 24.1 kB
openreceive-react-0.3.2.tgz 2026-08-29 49.4 kB
openreceive-svelte-0.3.2.tgz 2026-08-29 4.0 kB
openreceive-vue-0.3.2.tgz 2026-08-29 3.5 kB
openreceive-node-0.3.2.tgz 2026-08-29 54.9 kB
openreceive-provider-data-0.3.2.tgz 2026-08-29 451.7 kB
openreceive-express-0.3.2.tgz 2026-08-29 4.9 kB
openreceive-fastify-0.3.2.tgz 2026-08-29 5.3 kB
openreceive-http-0.3.2.tgz 2026-08-29 39.4 kB
openreceive-next-0.3.2.tgz 2026-08-29 4.1 kB
openreceive-0.3.2.tgz 2026-08-29 2.0 kB
openreceive-angular-0.3.2.tgz 2026-08-29 10.6 kB
openreceive-browser-0.3.2.tgz 2026-08-29 142.4 kB
openreceive-core-0.3.2.tgz 2026-08-29 28.3 kB
openreceive-elements-0.3.2.tgz 2026-08-29 49.6 kB
OpenReceive v0.3.2 source code.tar.gz 2026-08-29 2.7 MB
OpenReceive v0.3.2 source code.zip 2026-08-29 3.1 MB
README.md 2026-08-29 7.1 kB
Totals: 20 Items   6.8 MB 0

An overpaid swap deposit is a refund, not a support ticket

A payer who sent more than deposit_amount landed on attention — terminal, no form, "This payment needs support review." The provider does not treat it that way: lightning-swap opens refund_required on MORE and takes a full choice=REFUND for it, exactly as it does for LESS and EXPIRED. The client was the only thing calling it an incident, and the payer's money sat behind a sentence with no next step.

MORE / OVER / OVERPAID now map to refund_required in both engines, so the deposit comes back through the same two-step form as every other emergency. The refund is the WHOLE deposit — the payout is a fixed-amount bolt11, so there is nothing to exchange a surplus into and choice=EXCHANGE is not a path this client takes. The order stays unpaid and the payer can pay again afterwards.

refund_reason gains overpaid and overpaid_and_late beside the three that were there, with payer-facing copy for both. LIMIT, which the provider pairs with LESS/MORE when a deposit falls outside the pair's limits, names no reason of its own — it says nothing the payer can act on beyond the amount. attention still holds what genuinely needs a human: choice=EXCHANGE, a provider status we do not recognize, and the states OpenReceive itself sets.

Attempts already sitting in attention heal on their next status read, which is the next time the payer opens the checkout.

The attention screen says what to do

Both renderers printed the same sentence twice — once as the heading detail, once as the warning banner underneath it — and then hid the ids behind a "Payment details" caret. The banner now carries the next step, the amounts sent and required sit under it, and the deposit transaction and provider order render open: on a screen whose whole remaining job is quoting an id to a human, a disclosure triangle is the wrong shape. The demo shops stop rendering a payable QR under an attention status for the same reason.

A payer can get back to a deposit that is already in flight

A stablecoin deposit has no account behind it and sends no email. The order's reference is the whole of the payer's claim on it, and POST /checkouts/prepare answers with the amount and the pay-in catalog and NO attempts — so a checkout rebuilt from a reference alone opened on the payment-method grid. That is the wrong screen for someone who was told to bookmark a refund.

resumePaymentHash (attribute resume-payment-hash) is a create-mode prop on the React, element, Vue, Svelte and Angular checkouts: pass the payment hash your application stored beside the order and the deposit — or its refund form — comes back as the payer left it. A hash the server will not serve is ignored, because a stale note in host storage is not a reason to put an error in front of a payer who can still start a fresh payment; they land on the method grid exactly as they would have.

The hash and not the asset, deliberately: POST /swaps/status addresses one attempt and applies no reuse test, so it still answers for a deposit that stopped being payable hours ago, while re-selecting the asset re-serves the committed attempt only until it expires and then mints a second address.

Four additive names on @openreceive/browser (and /headless) for hosts driving their own UI: requestSwapStatus reads one attempt by hash and rejects with a status-carrying error; resumeSwapAttempt is the forgiving wrapper the renderers share, folding the attempt into a prepared snapshot and returning the snapshot unchanged on any failure; currentCheckoutUrl is the one definition of "its URL" that the two refund screens copy; formatMethodNetworkDetail joins network labels for a method tile. SwapDisplayModel gains resumable, the boolean refundReturnLabel was already chosen from, so a UI can render the copy button that sentence names without string-matching the label.

The guides say less

Thirteen guides were rewritten against what the packages actually export, and the set shrank by roughly 1,800 lines while gaining a page. swap-refunds.md is new and is the one that was missing: when refund_required happens, which refund_reason means what, and — the part every stack got wrong — that a refund form is only a promise if the payer can reach it again after closing the tab. headless-checkout.md, storage.md, security.md and checkout-ux.md lost the most; three internal notes (architecture.md, checkout-design.md, scope-lock.md) now hold the reasoning that was scattered through them.

Every demo stack runs with no wallet, and every order has a URL

The checkout lives at /checkout/:reference on all four Buy a Button stacks, which serve the SPA there, put the reference in the address bar the moment the order exists, and take a pasted one back in from the catalog. The two custom-UI stacks reopen the attempt with resumePaymentHash; the two that mount the packaged checkout let the payer re-pick their coin.

DEMO_WALLET=testkit now boots the Rails stack too — examples/buttons/server/rails/lib/button_shop/testkit/ is a port of packages/js/testkit down to the fixtures, so one Playwright suite drives all four with the same assertions. It fakes the wallet, the swap provider and the price feed, and nothing else: the engine, the hooks, the migrations and Postgres are the production paths. The /__testkit route is declared unconditionally and 404s unconditionally without the variable, which its own test asserts in an environment that does not set it, and which npm run check:demo-containers enforces against every compose file.

All four stacks — node-express, static-html-small-api, nextjs-fullstack and rails — were rebuilt against the 0.3.2 packages, and the Playwright suite runs against node-express. Of the gems, only openreceive-server changed; openreceive and openreceive-rails are byte-identical to 0.3.1 and ship to keep the one-version-for-everything rule.

Two release gates that were lying are fixed. release:prepare now refreshes the Rails demo's Gemfile.lock alongside package-lock.json — the v0.3.1 tag's CI died at bundle install with exit 16 because the three VERSION constants moved and a lockfile pinning them by path did not, and local test:ci never sees it because nothing here sets frozen mode. And Release Dry Run, which had never passed on either tag it ever ran for, now builds the packages before smoking them: @openreceive/angular resolves @openreceive/elements/wrapper-shared to a .d.ts that exists only once elements is built, and a fresh checkout with no build step cannot. ci.yml hid it by running npm test — and so pretest:js — first.

The live wallet smoke (npm run test:live) was NOT run for this release: it needs a funded NWC connection.

This is a PATCH bump: nothing left the public API. resumePaymentHash and the four new browser exports are additive, and the swap-status mapping change moves attempts out of a terminal state into a recoverable one.

Source: README.md, updated 2026-08-29