@openreceive/elements 0.4.9 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OMG254JS.js → chunk-DQ6FMWVX.js} +71 -11
- package/dist/index.js +1 -1
- package/dist/wrapper-shared.js +1 -1
- package/package.json +2 -2
- package/skills/debug-openreceive-payment/SKILL.md +8 -0
- package/skills/integrate-openreceive/SKILL.md +11 -0
- package/skills/integrate-openreceive/references/btcpay.md +9 -7
- package/skills/integrate-openreceive/references/django.md +12 -3
- package/skills/integrate-openreceive/references/fastapi.md +13 -4
- package/skills/integrate-openreceive/references/fastify.md +13 -4
- package/skills/integrate-openreceive/references/laravel.md +12 -3
- package/skills/integrate-openreceive/references/next.md +13 -4
- package/skills/integrate-openreceive/references/node.md +13 -4
- package/skills/integrate-openreceive/references/php.md +12 -3
- package/skills/integrate-openreceive/references/rails.md +12 -3
- package/skills/integrate-openreceive/references/woocommerce.md +3 -1
|
@@ -244,11 +244,13 @@ function createElementCheckoutSession(host) {
|
|
|
244
244
|
const session = createCheckoutSession({
|
|
245
245
|
snapshot: () => host.latestCheckoutSnapshot(),
|
|
246
246
|
reference: currentReference,
|
|
247
|
-
|
|
247
|
+
prefix: currentPrefix,
|
|
248
|
+
requestCheckout: (reference, signal) => {
|
|
248
249
|
const metadata = host.createMetadata();
|
|
249
250
|
const csrfHeader = currentCsrfHeader();
|
|
250
251
|
return requestCheckout({
|
|
251
252
|
prefix: currentPrefix(),
|
|
253
|
+
signal,
|
|
252
254
|
reference,
|
|
253
255
|
...metadata === void 0 ? {} : { metadata },
|
|
254
256
|
...csrfHeader === void 0 ? {} : { csrfHeader },
|
|
@@ -290,17 +292,21 @@ function createElementCheckoutSession(host) {
|
|
|
290
292
|
creating = true;
|
|
291
293
|
createdKey = key;
|
|
292
294
|
session.resetLightningRequest();
|
|
295
|
+
const action = session.capture();
|
|
293
296
|
try {
|
|
294
297
|
createError = void 0;
|
|
295
298
|
host.syncResumePath(reference);
|
|
296
299
|
const prepared = await prepareCheckout({
|
|
300
|
+
signal: action.signal,
|
|
297
301
|
prefix,
|
|
298
302
|
reference,
|
|
299
303
|
...csrfHeader === void 0 ? {} : { csrfHeader },
|
|
300
304
|
fetch: globalThis.fetch
|
|
301
305
|
});
|
|
306
|
+
if (!action.isCurrent()) return;
|
|
302
307
|
const resumeHash = host.resumePaymentHash();
|
|
303
308
|
const checkout = resumeHash === void 0 ? prepared : await resumeSwapAttempt({
|
|
309
|
+
signal: action.signal,
|
|
304
310
|
fetch: globalThis.fetch,
|
|
305
311
|
prefix,
|
|
306
312
|
...csrfHeader === void 0 ? {} : { csrfHeader },
|
|
@@ -308,7 +314,7 @@ function createElementCheckoutSession(host) {
|
|
|
308
314
|
paymentHash: resumeHash,
|
|
309
315
|
snapshot: prepared
|
|
310
316
|
});
|
|
311
|
-
if (
|
|
317
|
+
if (!action.isCurrent()) return;
|
|
312
318
|
if (checkout.active?.swap !== void 0) {
|
|
313
319
|
host.swapSelection.setSelectedAsset(checkout.active.swap.pay_in_asset);
|
|
314
320
|
host.swapSelection.setDismissedInvoiceId(null);
|
|
@@ -323,15 +329,17 @@ function createElementCheckoutSession(host) {
|
|
|
323
329
|
host.render();
|
|
324
330
|
host.startCheckoutController();
|
|
325
331
|
} catch (error) {
|
|
326
|
-
if (
|
|
332
|
+
if (!action.isCurrent()) return;
|
|
327
333
|
createError = error instanceof Error && error.message.length > 0 ? error.message : "Could not start checkout.";
|
|
328
334
|
host.dispatchError(error);
|
|
329
335
|
host.render();
|
|
330
336
|
} finally {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
void
|
|
337
|
+
if (action.isCurrent()) {
|
|
338
|
+
creating = false;
|
|
339
|
+
const current = currentCreateKey();
|
|
340
|
+
if (host.element.isConnected && current !== void 0 && current !== key && host.isCreateMode()) {
|
|
341
|
+
void createCheckout();
|
|
342
|
+
}
|
|
335
343
|
}
|
|
336
344
|
}
|
|
337
345
|
}
|
|
@@ -370,9 +378,19 @@ function createElementCheckoutSession(host) {
|
|
|
370
378
|
retryCreateCheckout,
|
|
371
379
|
ensureLightning: () => session.ensureLightning(),
|
|
372
380
|
startSwap: (payInAsset) => session.startSwap(payInAsset),
|
|
381
|
+
clearSwapStartError: () => session.clearSwapStartError(),
|
|
373
382
|
applyOwnAttributes,
|
|
374
383
|
writeOwnAttributes,
|
|
384
|
+
capture: () => session.capture(),
|
|
385
|
+
dispose() {
|
|
386
|
+
session.dispose();
|
|
387
|
+
if (creating) createdKey = void 0;
|
|
388
|
+
creating = false;
|
|
389
|
+
},
|
|
375
390
|
forgetCreateKey() {
|
|
391
|
+
session.reset();
|
|
392
|
+
creating = false;
|
|
393
|
+
createError = void 0;
|
|
376
394
|
createdKey = void 0;
|
|
377
395
|
}
|
|
378
396
|
};
|
|
@@ -1471,7 +1489,7 @@ function renderCheckoutHtml(view) {
|
|
|
1471
1489
|
const settled = statusLabel === "settled";
|
|
1472
1490
|
const swapFocused = (view.wizard?.selectedSwapAsset ?? null) !== null;
|
|
1473
1491
|
const methodGridShowing = view.payment_wizard !== false && view.wizard?.selectedMethod == null && !swapFocused;
|
|
1474
|
-
const hideLightning = !settled && (view.lightningRequested === false || swapFocused
|
|
1492
|
+
const hideLightning = !settled && (view.lightningRequested === false || swapFocused || methodGridShowing && !expired);
|
|
1475
1493
|
const wizard = expired && !swapFocused || settled || view.payment_wizard === false ? "" : renderPaymentWizardHtml(view.wizard);
|
|
1476
1494
|
const copyButton = `<button part="${OPENRECEIVE_CHECKOUT_ELEMENT_PARTS.copy}" class="${orClasses6.btn}" type="button">${COPY_INVOICE_ICON}<span ${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES6.swapCopyLabel}>${escapeHtml7(checkoutLabels6.copyInvoice)}</span></button>`;
|
|
1477
1495
|
const decodeInvoice = view.invoice.trim() !== "" ? view.invoice : typeof view.wizard?.lightningInvoice === "string" && view.wizard.lightningInvoice.trim() !== "" ? view.wizard.lightningInvoice : void 0;
|
|
@@ -1721,8 +1739,12 @@ function defineElements(options = {}) {
|
|
|
1721
1739
|
this.startCheckoutController();
|
|
1722
1740
|
}
|
|
1723
1741
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
1724
|
-
if (
|
|
1742
|
+
if (this.session.applyingOwnAttributes) return;
|
|
1743
|
+
const identityChanged = name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.prefix;
|
|
1744
|
+
if (!this.isConnected && !identityChanged) return;
|
|
1725
1745
|
if (oldValue === newValue) return;
|
|
1746
|
+
if (name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.prefix && oldValue?.replace(/\/+$/, "") === newValue?.replace(/\/+$/, ""))
|
|
1747
|
+
return;
|
|
1726
1748
|
const displayOnly = name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.theme || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.syncUrl || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.resumePathPrefix || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.routeReference || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.resumable || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.decodeLinkUrl;
|
|
1727
1749
|
if (displayOnly) {
|
|
1728
1750
|
this.render();
|
|
@@ -1738,8 +1760,40 @@ function defineElements(options = {}) {
|
|
|
1738
1760
|
}
|
|
1739
1761
|
const createInputChanged = name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.prefix || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice;
|
|
1740
1762
|
if (createInputChanged) {
|
|
1763
|
+
const managed = this.latestCheckoutSnapshot !== void 0;
|
|
1764
|
+
this.stopCheckoutController();
|
|
1741
1765
|
this.session.forgetCreateKey();
|
|
1766
|
+
this.latestCheckoutSnapshot = void 0;
|
|
1767
|
+
this.lastCheckoutState = void 0;
|
|
1768
|
+
this.lastSnapshotDisplayKey = void 0;
|
|
1769
|
+
this.startedSwapInvoice = void 0;
|
|
1770
|
+
this.dismissedSwapInvoiceId = null;
|
|
1771
|
+
this.selectedSwapAsset = null;
|
|
1772
|
+
this.selectedPickerKey = null;
|
|
1773
|
+
this.selectedSwapAssetByGroup = {};
|
|
1774
|
+
this.selection = createPaymentWizardSelection();
|
|
1775
|
+
this.swapOptions = [];
|
|
1776
|
+
this.swapOptionsLoaded = false;
|
|
1777
|
+
this.clearRefundAddressDraft();
|
|
1778
|
+
if (managed && name !== OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice) {
|
|
1779
|
+
this.session.writeOwnAttributes(() => {
|
|
1780
|
+
for (const key of [
|
|
1781
|
+
"invoice",
|
|
1782
|
+
"invoiceId",
|
|
1783
|
+
"paymentHash",
|
|
1784
|
+
"rail",
|
|
1785
|
+
"amountMsats",
|
|
1786
|
+
"fiatCurrency",
|
|
1787
|
+
"fiatValue",
|
|
1788
|
+
"status",
|
|
1789
|
+
"expiresAt"
|
|
1790
|
+
]) {
|
|
1791
|
+
this.removeAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3[key]);
|
|
1792
|
+
}
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1742
1795
|
}
|
|
1796
|
+
if (!this.isConnected) return;
|
|
1743
1797
|
if (this.isCreateMode()) {
|
|
1744
1798
|
this.render();
|
|
1745
1799
|
this.syncThemeAncestorObserver();
|
|
@@ -1753,6 +1807,7 @@ function defineElements(options = {}) {
|
|
|
1753
1807
|
this.startCheckoutController();
|
|
1754
1808
|
}
|
|
1755
1809
|
disconnectedCallback() {
|
|
1810
|
+
this.session.dispose();
|
|
1756
1811
|
this.stopCheckoutController();
|
|
1757
1812
|
this.stopThemeAncestorObserver();
|
|
1758
1813
|
}
|
|
@@ -2219,6 +2274,7 @@ function defineElements(options = {}) {
|
|
|
2219
2274
|
button.addEventListener("click", () => {
|
|
2220
2275
|
const target = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.breadcrumb);
|
|
2221
2276
|
if (target === "swap-asset") {
|
|
2277
|
+
this.session.clearSwapStartError();
|
|
2222
2278
|
this.selectedSwapAsset = null;
|
|
2223
2279
|
this.selectedPickerKey = null;
|
|
2224
2280
|
this.selectedSwapAssetByGroup = {};
|
|
@@ -2279,6 +2335,7 @@ function defineElements(options = {}) {
|
|
|
2279
2335
|
});
|
|
2280
2336
|
root.querySelector(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapBack)?.addEventListener("click", () => {
|
|
2281
2337
|
const current = this.currentSwapInvoice();
|
|
2338
|
+
this.session.clearSwapStartError();
|
|
2282
2339
|
this.dismissedSwapInvoiceId = current?.invoice_id ?? null;
|
|
2283
2340
|
this.selectedSwapAsset = null;
|
|
2284
2341
|
this.selectedPickerKey = null;
|
|
@@ -2426,12 +2483,15 @@ function defineElements(options = {}) {
|
|
|
2426
2483
|
async refundSwap(attemptId, refundAddress, confirm) {
|
|
2427
2484
|
const controller = this.controller;
|
|
2428
2485
|
if (controller === void 0) return;
|
|
2486
|
+
const action = this.session.capture();
|
|
2429
2487
|
try {
|
|
2430
|
-
|
|
2488
|
+
const invoice = confirm ? await controller.confirmSwapRefund({ attemptId, refundAddress }) : await controller.stageSwapRefund({ attemptId, refundAddress });
|
|
2489
|
+
if (!action.isCurrent() || this.controller !== controller) return;
|
|
2490
|
+
this.startedSwapInvoice = invoice;
|
|
2431
2491
|
this.dismissedSwapInvoiceId = null;
|
|
2432
2492
|
this.render();
|
|
2433
2493
|
} catch (error) {
|
|
2434
|
-
this.dispatchError(error);
|
|
2494
|
+
if (action.isCurrent() && this.controller === controller) this.dispatchError(error);
|
|
2435
2495
|
}
|
|
2436
2496
|
}
|
|
2437
2497
|
currentSwapInvoice() {
|
package/dist/index.js
CHANGED
package/dist/wrapper-shared.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openreceive/elements",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"description": "Bitcoin Lightning checkout for any website using custom elements, with optional USDT, USDC, SOL and ETH swaps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"main": "./dist/index.js",
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@openreceive/browser": "0.4.
|
|
24
|
+
"@openreceive/browser": "0.4.11"
|
|
25
25
|
},
|
|
26
26
|
"exports": {
|
|
27
27
|
".": {
|
|
@@ -70,6 +70,14 @@ same diagnostics redacted, always exit 0 — safe to share.
|
|
|
70
70
|
- Refunds exist only for swap deposits from `refund_required`. There is **no
|
|
71
71
|
Lightning refund** — the wallet cannot spend. Do not chase one.
|
|
72
72
|
https://openreceive.org/guides/swap-refunds.md
|
|
73
|
+
- "Payer reports two different amounts on a stablecoin checkout" (50.05 or
|
|
74
|
+
50.03?): the deposit amount is a token quantity, `fee.pay_in_fiat` is its
|
|
75
|
+
fiat valuation. Only `swap.deposit_amount` is an instruction. From 0.4.10 the
|
|
76
|
+
packaged checkout renders a USD stablecoin's breakdown in the token and never
|
|
77
|
+
shows `pay_in_fiat`; on an older bundle, upgrade `@openreceive/*`. To verify,
|
|
78
|
+
read the row's `deposit_amount` and `fee` and confirm the UI shows only the
|
|
79
|
+
deposit amount. A custom UI must call `createSwapFeeBreakdown(fee, swap)`
|
|
80
|
+
with the swap, not the fee alone.
|
|
73
81
|
|
|
74
82
|
## 6. Checkout UI shows nothing
|
|
75
83
|
|
|
@@ -86,6 +86,17 @@ state; do not retry-loop it, and do not build an idempotency store around it —
|
|
|
86
86
|
that serialization is the library's job. (A hook failure while persisting an
|
|
87
87
|
attempt is a **503 retryable**, deliberately distinct.)
|
|
88
88
|
|
|
89
|
+
## Amounts on the deposit panel
|
|
90
|
+
|
|
91
|
+
`swap.deposit_amount` is the ONLY amount a payer is ever told to send, in the
|
|
92
|
+
pay-in token. `swap.fee.pay_in_fiat` / `payout_fiat` are fiat valuations that
|
|
93
|
+
explain the spread (why the deposit exceeds the cart total); they are not
|
|
94
|
+
instructions. For a stablecoin pegged to the fee currency (USDT, USDC) the
|
|
95
|
+
packaged checkout expresses the breakdown in the token and never renders
|
|
96
|
+
`pay_in_fiat` — "$50.03" under "50.05 USDC" reads as the same number with a
|
|
97
|
+
typo. A custom UI gets the same rule from `createSwapFeeBreakdown(fee, swap)`;
|
|
98
|
+
pass the swap, not just the fee.
|
|
99
|
+
|
|
89
100
|
## Secrets
|
|
90
101
|
|
|
91
102
|
`NWC_URI` and `LSC_URI_*` are server-only. Never put them in browser code,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (BTCPay Server)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Connect a BTCPay Server store to a receive-only NWC wallet with the OpenReceive
|
|
6
6
|
plugin, and optionally let payers pay BTCPay invoices with USDT, USDC, ETH or
|
|
@@ -32,7 +32,7 @@ refund path on the same checkout screen.
|
|
|
32
32
|
|
|
33
33
|
## Step 0 — check the deployment before you change anything
|
|
34
34
|
|
|
35
|
-
1. Confirm the BTCPay Server version is 2.4.
|
|
35
|
+
1. Confirm the BTCPay Server version is 2.4.4 or later (Server Settings →
|
|
36
36
|
About, or `GET /api/v1/server/info`). The plugin declares that minimum and
|
|
37
37
|
BTCPay refuses to load it below.
|
|
38
38
|
2. Check whether the plugin is installed (Server Settings → Plugins, or the
|
|
@@ -119,6 +119,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
119
119
|
Questions, or a problem with the plugin itself:
|
|
120
120
|
https://openreceive.org/contact
|
|
121
121
|
|
|
122
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
123
|
+
|
|
122
124
|
---
|
|
123
125
|
|
|
124
126
|
## The quickstart, in full
|
|
@@ -128,7 +130,7 @@ passes. The page it comes from is https://openreceive.org/guides/quickstart-btcp
|
|
|
128
130
|
|
|
129
131
|
## BTCPay Server quickstart
|
|
130
132
|
|
|
131
|
-
Requires BTCPay Server ≥ 2.4.
|
|
133
|
+
Requires BTCPay Server ≥ 2.4.4.
|
|
132
134
|
|
|
133
135
|
The OpenReceive plugin makes a receive-only NWC wallet the Lightning node of a
|
|
134
136
|
BTCPay store. BTCPay mints every Lightning invoice in that wallet and records
|
|
@@ -143,7 +145,7 @@ invoices, checkout, webhooks and Greenfield API are the host.
|
|
|
143
145
|
|
|
144
146
|
### 1. Prerequisites
|
|
145
147
|
|
|
146
|
-
- A BTCPay Server, version 2.4.
|
|
148
|
+
- A BTCPay Server, version 2.4.4 or later, on any network (mainnet, testnet,
|
|
147
149
|
signet, regtest). The wallet must be on the same network.
|
|
148
150
|
- A receive-only NWC code for the wallet you want to receive into
|
|
149
151
|
([get one here](https://openreceive.org/get_a_nwc_code_to_receive_payments)).
|
|
@@ -157,9 +159,9 @@ invoices, checkout, webhooks and Greenfield API are the host.
|
|
|
157
159
|
|
|
158
160
|
In BTCPay, open **Server Settings → Plugins**, search the plugin directory
|
|
159
161
|
for **OpenReceive**, click **Install**, and restart BTCPay when prompted.
|
|
160
|
-
BTCPay creates the plugin's
|
|
161
|
-
`BTCPayServer.Plugins.OpenReceive`) in its own
|
|
162
|
-
else is created.
|
|
162
|
+
BTCPay creates the plugin's two tables (`openreceive_invoices` and
|
|
163
|
+
`openreceive_swaps`, schema `BTCPayServer.Plugins.OpenReceive`) in its own
|
|
164
|
+
Postgres at startup; nothing else is created.
|
|
163
165
|
|
|
164
166
|
To build the plugin from source instead, follow
|
|
165
167
|
[the .NET workspace README](https://github.com/OpenReceive/openreceive/blob/master/packages/dotnet/README.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Django)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Django project — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the Python package is on PyPI
|
|
@@ -130,7 +130,7 @@ itself, and they hold for every integration.
|
|
|
130
130
|
a placeholder that allows everything (`manage.py check` warns
|
|
131
131
|
`openreceive.W002` while it is set) — replace it with this app's real
|
|
132
132
|
ownership check, same as `on_paid`.
|
|
133
|
-
- `on_paid` must be idempotent.
|
|
133
|
+
- `on_paid` must be idempotent. Its database fulfillment commits once per `reference` — your order
|
|
134
134
|
id, one per thing you fulfill, created before checkout, kept across retries,
|
|
135
135
|
never reused. A fresh id per page load lets one order be paid twice.
|
|
136
136
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -231,6 +231,13 @@ built on `@openreceive/browser/headless`. Read that before writing components.
|
|
|
231
231
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
232
232
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
233
233
|
the model gives it.
|
|
234
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
235
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
236
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
237
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
238
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
239
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
240
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
234
241
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
235
242
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
236
243
|
`startSwap` reports through `onError`.
|
|
@@ -300,6 +307,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
300
307
|
Questions, or a problem with the library itself:
|
|
301
308
|
https://openreceive.org/contact
|
|
302
309
|
|
|
310
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
311
|
+
|
|
303
312
|
---
|
|
304
313
|
|
|
305
314
|
## The quickstart, in full
|
|
@@ -473,7 +482,7 @@ The host class needs three things: authorization, the trusted price, and
|
|
|
473
482
|
fulfillment. All three receive the `reference` — a string you choose, and the
|
|
474
483
|
fulfillment identity: your order id, one per thing you fulfill, created before
|
|
475
484
|
checkout, kept across retries, never reused. OpenReceive never looks inside
|
|
476
|
-
it, but `on_paid`
|
|
485
|
+
it, but `on_paid` commits fulfillment once per reference, a new checkout under a reference
|
|
477
486
|
that already settled is refused with 409, and a fresh id per page load lets
|
|
478
487
|
one order be paid twice.
|
|
479
488
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (FastAPI)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a FastAPI application — the app you are already working in.
|
|
6
6
|
You do not need a copy of the OpenReceive source: the engine is on PyPI
|
|
@@ -116,7 +116,7 @@ itself, and they hold for every integration.
|
|
|
116
116
|
- `authorize` runs on every request, and the `resource` it receives is a CLAIM
|
|
117
117
|
the payer made, not proof. Read the Starlette request's session, cookie or
|
|
118
118
|
auth dependency; never trust a body field.
|
|
119
|
-
- `on_paid` must be idempotent.
|
|
119
|
+
- `on_paid` must be idempotent. Its database fulfillment commits once per `reference` — your order id, one
|
|
120
120
|
per thing you fulfill, created before checkout, kept across retries, never
|
|
121
121
|
reused. A fresh id per page load lets one order be paid twice.
|
|
122
122
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -228,6 +228,13 @@ components.
|
|
|
228
228
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
229
229
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
230
230
|
the model gives it.
|
|
231
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
232
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
233
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
234
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
235
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
236
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
237
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
231
238
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
232
239
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
233
240
|
`startSwap` reports through `onError`.
|
|
@@ -294,6 +301,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
294
301
|
Questions, or a problem with the library itself:
|
|
295
302
|
https://openreceive.org/contact
|
|
296
303
|
|
|
304
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
305
|
+
|
|
297
306
|
---
|
|
298
307
|
|
|
299
308
|
## The quickstart, in full
|
|
@@ -468,7 +477,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
468
477
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
469
478
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
470
479
|
across retries, never reused. OpenReceive never looks inside it, but `on_paid`
|
|
471
|
-
|
|
480
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
472
481
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
473
482
|
paid twice.
|
|
474
483
|
|
|
@@ -517,7 +526,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
517
526
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
518
527
|
|
|
519
528
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
520
|
-
an invoice, and `onPaid` runs
|
|
529
|
+
an invoice, and `onPaid` runs inside the settlement transaction. Rolled-back transactions may retry the callback; use a host outbox for external delivery.
|
|
521
530
|
|
|
522
531
|
A page without a bundler renders the same checkout as a custom element:
|
|
523
532
|
`<openreceive-checkout reference="…" prefix="/openreceive">` from
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Fastify)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Fastify application — the app you are already working in.
|
|
6
6
|
You do not need a copy of the OpenReceive source: the packages are on npm, and
|
|
@@ -107,7 +107,7 @@ itself, and they hold for every integration.
|
|
|
107
107
|
payer-supplied amounts.
|
|
108
108
|
- `authorize` runs on every request, and the `resource` it receives is a CLAIM
|
|
109
109
|
the payer made, not proof. Read a framework session; never trust a body field.
|
|
110
|
-
- `onPaid` must be idempotent.
|
|
110
|
+
- `onPaid` must be idempotent. Its database fulfillment commits once per `reference` — your order id, one
|
|
111
111
|
per thing you fulfill, created before checkout, kept across retries, never
|
|
112
112
|
reused. A fresh id per page load lets one order be paid twice.
|
|
113
113
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -211,6 +211,13 @@ components.
|
|
|
211
211
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
212
212
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
213
213
|
the model gives it.
|
|
214
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
215
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
216
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
217
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
218
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
219
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
220
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
214
221
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
215
222
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
216
223
|
`startSwap` reports through `onError`.
|
|
@@ -277,6 +284,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
277
284
|
Questions, or a problem with the library itself:
|
|
278
285
|
https://openreceive.org/contact
|
|
279
286
|
|
|
287
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
288
|
+
|
|
280
289
|
---
|
|
281
290
|
|
|
282
291
|
## The quickstart, in full
|
|
@@ -470,7 +479,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
470
479
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
471
480
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
472
481
|
across retries, never reused. OpenReceive never looks inside it, but `onPaid`
|
|
473
|
-
|
|
482
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
474
483
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
475
484
|
paid twice.
|
|
476
485
|
|
|
@@ -519,7 +528,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
519
528
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
520
529
|
|
|
521
530
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
522
|
-
an invoice, and `onPaid` runs
|
|
531
|
+
an invoice, and `onPaid` runs inside the settlement transaction. Rolled-back transactions may retry the callback; use a host outbox for external delivery.
|
|
523
532
|
|
|
524
533
|
A runnable illustration of this boundary — not a template to copy models from —
|
|
525
534
|
is Buy a Button
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Laravel)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Laravel application — the app you are already working in.
|
|
6
6
|
You do not need a copy of the OpenReceive source: the package is on Packagist
|
|
@@ -121,7 +121,7 @@ itself, and they hold for every integration.
|
|
|
121
121
|
scaffolds `use AllowAllAuthorize;`, a placeholder trait that allows
|
|
122
122
|
everything (the engine warns at boot while it is there) — replace it with
|
|
123
123
|
this app's real ownership check, same as `onPaid`.
|
|
124
|
-
- `onPaid` must be idempotent.
|
|
124
|
+
- `onPaid` must be idempotent. Its database fulfillment commits once per `reference` — your order
|
|
125
125
|
id, one per thing you fulfill, created before checkout, kept across retries,
|
|
126
126
|
never reused. A fresh id per page load lets one order be paid twice.
|
|
127
127
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -216,6 +216,13 @@ built on `@openreceive/browser/headless`. Read that before writing components.
|
|
|
216
216
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
217
217
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
218
218
|
the model gives it.
|
|
219
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
220
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
221
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
222
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
223
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
224
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
225
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
219
226
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
220
227
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
221
228
|
`startSwap` reports through `onError`.
|
|
@@ -284,6 +291,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
284
291
|
Questions, or a problem with the library itself:
|
|
285
292
|
https://openreceive.org/contact
|
|
286
293
|
|
|
294
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
295
|
+
|
|
287
296
|
---
|
|
288
297
|
|
|
289
298
|
## The quickstart, in full
|
|
@@ -449,7 +458,7 @@ variable as set/unset only.
|
|
|
449
458
|
price, and fulfillment. All three receive the `reference` — a string you
|
|
450
459
|
choose, and the fulfillment identity: your order id, one per thing you
|
|
451
460
|
fulfill, created before checkout, kept across retries, never reused.
|
|
452
|
-
OpenReceive never looks inside it, but `onPaid`
|
|
461
|
+
OpenReceive never looks inside it, but `onPaid` commits fulfillment once per reference, a new
|
|
453
462
|
checkout under a reference that already settled is refused with 409, and a
|
|
454
463
|
fresh id per page load lets one order be paid twice.
|
|
455
464
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Next.js)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Next.js App Router application — the app you are already
|
|
6
6
|
working in. You do not need a copy of the OpenReceive source: the packages are
|
|
@@ -109,7 +109,7 @@ itself, and they hold for every integration.
|
|
|
109
109
|
payer-supplied amounts.
|
|
110
110
|
- `authorize` runs on every request, and the `resource` it receives is a CLAIM
|
|
111
111
|
the payer made, not proof. Read a framework session; never trust a body field.
|
|
112
|
-
- `onPaid` must be idempotent.
|
|
112
|
+
- `onPaid` must be idempotent. Its database fulfillment commits once per `reference` — your order id, one
|
|
113
113
|
per thing you fulfill, created before checkout, kept across retries, never
|
|
114
114
|
reused. A fresh id per page load lets one order be paid twice.
|
|
115
115
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -217,6 +217,13 @@ components.
|
|
|
217
217
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
218
218
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
219
219
|
the model gives it.
|
|
220
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
221
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
222
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
223
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
224
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
225
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
226
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
220
227
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
221
228
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
222
229
|
`startSwap` reports through `onError`.
|
|
@@ -283,6 +290,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
283
290
|
Questions, or a problem with the library itself:
|
|
284
291
|
https://openreceive.org/contact
|
|
285
292
|
|
|
293
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
294
|
+
|
|
286
295
|
---
|
|
287
296
|
|
|
288
297
|
## The quickstart, in full
|
|
@@ -489,7 +498,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
489
498
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
490
499
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
491
500
|
across retries, never reused. OpenReceive never looks inside it, but `onPaid`
|
|
492
|
-
|
|
501
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
493
502
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
494
503
|
paid twice.
|
|
495
504
|
|
|
@@ -569,7 +578,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
569
578
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
570
579
|
|
|
571
580
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
572
|
-
an invoice, and `onPaid` runs
|
|
581
|
+
an invoice, and `onPaid` runs inside the settlement transaction. Rolled-back transactions may retry the callback; use a host outbox for external delivery.
|
|
573
582
|
|
|
574
583
|
A runnable illustration of this boundary — not a template to copy models from —
|
|
575
584
|
is Buy a Button
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Node.js)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Node application — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the packages are on npm, and the
|
|
@@ -104,7 +104,7 @@ itself, and they hold for every integration.
|
|
|
104
104
|
payer-supplied amounts.
|
|
105
105
|
- `authorize` runs on every request, and the `resource` it receives is a CLAIM
|
|
106
106
|
the payer made, not proof. Read a framework session; never trust a body field.
|
|
107
|
-
- `onPaid` must be idempotent.
|
|
107
|
+
- `onPaid` must be idempotent. Its database fulfillment commits once per `reference` — your order id, one
|
|
108
108
|
per thing you fulfill, created before checkout, kept across retries, never
|
|
109
109
|
reused. A fresh id per page load lets one order be paid twice.
|
|
110
110
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -203,6 +203,13 @@ components.
|
|
|
203
203
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
204
204
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
205
205
|
the model gives it.
|
|
206
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
207
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
208
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
209
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
210
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
211
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
212
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
206
213
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
207
214
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
208
215
|
`startSwap` reports through `onError`.
|
|
@@ -269,6 +276,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
269
276
|
Questions, or a problem with the library itself:
|
|
270
277
|
https://openreceive.org/contact
|
|
271
278
|
|
|
279
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
280
|
+
|
|
272
281
|
---
|
|
273
282
|
|
|
274
283
|
## The quickstart, in full
|
|
@@ -447,7 +456,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
447
456
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
448
457
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
449
458
|
across retries, never reused. OpenReceive never looks inside it, but `onPaid`
|
|
450
|
-
|
|
459
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
451
460
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
452
461
|
paid twice.
|
|
453
462
|
|
|
@@ -496,7 +505,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
496
505
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
497
506
|
|
|
498
507
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
499
|
-
an invoice, and `onPaid` runs
|
|
508
|
+
an invoice, and `onPaid` runs inside the settlement transaction. Rolled-back transactions may retry the callback; use a host outbox for external delivery.
|
|
500
509
|
|
|
501
510
|
A runnable illustration of this boundary — not a template to copy models from —
|
|
502
511
|
is Buy a Button
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (PHP)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a PHP application — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the engine is on Packagist
|
|
@@ -125,7 +125,7 @@ itself, and they hold for every integration.
|
|
|
125
125
|
is a placeholder that allows everything (the engine warns at boot while a
|
|
126
126
|
host uses it) — replace it with this app's real ownership check, same as
|
|
127
127
|
`onPaid`'s `Hosts\LoggingOnPaid`.
|
|
128
|
-
- `onPaid` must be idempotent.
|
|
128
|
+
- `onPaid` must be idempotent. Its database fulfillment commits once per `reference` — your order id, one
|
|
129
129
|
per thing you fulfill, created before checkout, kept across retries, never
|
|
130
130
|
reused. A fresh id per page load lets one order be paid twice.
|
|
131
131
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -229,6 +229,13 @@ of https://openreceive.org/guides/checkout-ux.md, for a UI built on
|
|
|
229
229
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
230
230
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
231
231
|
the model gives it.
|
|
232
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
233
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
234
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
235
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
236
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
237
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
238
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
232
239
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
233
240
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
234
241
|
`startSwap` reports through `onError`.
|
|
@@ -294,6 +301,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
294
301
|
Questions, or a problem with the library itself:
|
|
295
302
|
https://openreceive.org/contact
|
|
296
303
|
|
|
304
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
305
|
+
|
|
297
306
|
---
|
|
298
307
|
|
|
299
308
|
## The quickstart, in full
|
|
@@ -505,7 +514,7 @@ prices with exact decimal math, and returns the order id the page will pass as
|
|
|
505
514
|
the `reference`. OpenReceive never prices from payer input. The `reference` is
|
|
506
515
|
a string you choose, and it is the fulfillment identity: your order id — one
|
|
507
516
|
per thing you fulfill, created before checkout, kept across retries, never
|
|
508
|
-
reused. `onPaid`
|
|
517
|
+
reused. `onPaid` commits fulfillment once per reference, a new checkout under a reference
|
|
509
518
|
that already settled is refused with 409, and a fresh id per page load lets
|
|
510
519
|
one order be paid twice.
|
|
511
520
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Rails)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Rails application — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the gem is on RubyGems, the
|
|
@@ -116,7 +116,7 @@ itself, and they hold for every integration.
|
|
|
116
116
|
body field. The generator installs `OpenReceive::ALLOW_ALL_AUTHORIZE`, a
|
|
117
117
|
placeholder that allows everything (the engine warns at boot while it is
|
|
118
118
|
set) — replace it with this app's real ownership check, same as `on_paid`.
|
|
119
|
-
- `config.on_paid` must be idempotent.
|
|
119
|
+
- `config.on_paid` must be idempotent. Its database fulfillment commits once per `reference` — your order
|
|
120
120
|
id, one per thing you fulfill, created before checkout, kept across retries,
|
|
121
121
|
never reused. A fresh id per page load lets one order be paid twice.
|
|
122
122
|
- Receive-only NWC is required; a spend-capable code fails closed at boot unless
|
|
@@ -210,6 +210,13 @@ built on `@openreceive/browser/headless`. Read that before writing components.
|
|
|
210
210
|
- `createSwapDisplayModel` → `display.copyRows` for deposits: address, memo,
|
|
211
211
|
and the bare amount each get a copy row. Render `swap.networkWarning*` as
|
|
212
212
|
the model gives it.
|
|
213
|
+
- `swap.deposit_amount` is the only amount a payer is told to send. Never put
|
|
214
|
+
a fiat valuation of it (`swap.fee.pay_in_fiat`) next to a stablecoin amount:
|
|
215
|
+
"$50.03" under "50.05 USDC" reads as a typo, and the payer asks which one to
|
|
216
|
+
send. The one fiat figure on a USDT/USDC deposit panel is the cart total
|
|
217
|
+
(`payout_fiat`); express "you send" and the fee in the token. Use
|
|
218
|
+
`createSwapFeeBreakdown(fee, swap)` — with the swap, not the fee alone — and
|
|
219
|
+
it applies this for you; SOL and ETH keep a fiat breakdown.
|
|
213
220
|
- `createCheckoutSession` owns mint and swap start. To start swaps, pass its
|
|
214
221
|
`swap` option (`selection`, `prefix`, `fetch`) together. Without it
|
|
215
222
|
`startSwap` reports through `onError`.
|
|
@@ -278,6 +285,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
278
285
|
Questions, or a problem with the library itself:
|
|
279
286
|
https://openreceive.org/contact
|
|
280
287
|
|
|
288
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
289
|
+
|
|
281
290
|
---
|
|
282
291
|
|
|
283
292
|
## The quickstart, in full
|
|
@@ -425,7 +434,7 @@ The initializer needs three things: authorization, the trusted price, and
|
|
|
425
434
|
fulfillment. All three receive the `reference` — a string you choose, and the
|
|
426
435
|
fulfillment identity: your order id, one per thing you fulfill, created before
|
|
427
436
|
checkout, kept across retries, never reused. OpenReceive never looks inside
|
|
428
|
-
it, but `on_paid`
|
|
437
|
+
it, but `on_paid` commits fulfillment once per reference, a new checkout under a reference
|
|
429
438
|
that already settled is refused with 409, and a fresh id per page load lets
|
|
430
439
|
one order be paid twice.
|
|
431
440
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (WordPress + WooCommerce)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.11.
|
|
4
4
|
|
|
5
5
|
Install and configure the OpenReceive gateway in the existing WooCommerce
|
|
6
6
|
store. Preserve its theme, checkout, customer accounts, order model and prices.
|
|
@@ -73,6 +73,8 @@ flows; a receive-only NWC wallet cannot send payments.
|
|
|
73
73
|
- [Agent Directions: BTCPay Server](https://openreceive.org/guides/agent-directions-btcpay.md)
|
|
74
74
|
- [WordPress + WooCommerce Quickstart](https://openreceive.org/guides/quickstart-woocommerce.md)
|
|
75
75
|
|
|
76
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
77
|
+
|
|
76
78
|
---
|
|
77
79
|
|
|
78
80
|
## The quickstart, in full
|