@openreceive/react 0.4.10 → 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/index.js +75 -40
- package/package.json +3 -3
- package/skills/integrate-openreceive/references/btcpay.md +6 -4
- package/skills/integrate-openreceive/references/django.md +5 -3
- package/skills/integrate-openreceive/references/fastapi.md +6 -4
- package/skills/integrate-openreceive/references/fastify.md +6 -4
- package/skills/integrate-openreceive/references/laravel.md +5 -3
- package/skills/integrate-openreceive/references/next.md +6 -4
- package/skills/integrate-openreceive/references/node.md +6 -4
- package/skills/integrate-openreceive/references/php.md +5 -3
- package/skills/integrate-openreceive/references/rails.md +5 -3
- package/skills/integrate-openreceive/references/woocommerce.md +3 -1
package/dist/index.js
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
mergeAttemptIntoCheckout,
|
|
7
7
|
mergeAttemptIntoSnapshot,
|
|
8
8
|
OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES as OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES3,
|
|
9
|
-
OPENRECEIVE_STYLE_ROOT_ATTRIBUTE as OPENRECEIVE_STYLE_ROOT_ATTRIBUTE2,
|
|
10
9
|
OPENRECEIVE_DEFAULT_PREFIX,
|
|
10
|
+
OPENRECEIVE_STYLE_ROOT_ATTRIBUTE as OPENRECEIVE_STYLE_ROOT_ATTRIBUTE2,
|
|
11
11
|
orClasses as orClasses7,
|
|
12
12
|
prepareCheckout,
|
|
13
13
|
requestCheckout,
|
|
@@ -30,7 +30,8 @@ function useCheckoutSession(options) {
|
|
|
30
30
|
sessionRef.current = createCheckoutSession({
|
|
31
31
|
snapshot: () => optionsRef.current.snapshot(),
|
|
32
32
|
reference: () => optionsRef.current.reference(),
|
|
33
|
-
|
|
33
|
+
prefix: () => optionsRef.current.prefix?.(),
|
|
34
|
+
requestCheckout: (reference, signal) => optionsRef.current.requestCheckout?.(reference, signal),
|
|
34
35
|
onSnapshot: (snapshot) => optionsRef.current.onSnapshot?.(snapshot),
|
|
35
36
|
// The inner `swap` is always present because the session is built once,
|
|
36
37
|
// on the first render, and the host may only gain swap options later.
|
|
@@ -57,7 +58,10 @@ function useCheckoutSession(options) {
|
|
|
57
58
|
onChange: rerender
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
+
const session = sessionRef.current;
|
|
62
|
+
session.syncIdentity();
|
|
63
|
+
React.useEffect(() => () => session.dispose(), [session]);
|
|
64
|
+
return session;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
// src/components.ts
|
|
@@ -712,15 +716,15 @@ function TransactionDetailCopyButton(props) {
|
|
|
712
716
|
}
|
|
713
717
|
|
|
714
718
|
// src/use-checkout.ts
|
|
715
|
-
import * as React6 from "react";
|
|
716
719
|
import {
|
|
717
720
|
copyInvoice as copyInvoiceHelper2,
|
|
718
721
|
createCheckoutController,
|
|
719
722
|
createCheckoutState,
|
|
720
723
|
createCheckoutStatusModel as createCheckoutStatusModel2,
|
|
721
|
-
|
|
722
|
-
|
|
724
|
+
deriveStatus,
|
|
725
|
+
openWallet as openWalletHelper2
|
|
723
726
|
} from "@openreceive/browser/headless";
|
|
727
|
+
import * as React6 from "react";
|
|
724
728
|
function useCheckout(options) {
|
|
725
729
|
const checkout = options.checkout;
|
|
726
730
|
const [copied, showCopied] = useTransientValue(false);
|
|
@@ -857,8 +861,9 @@ function useCheckout(options) {
|
|
|
857
861
|
}, [logContext, state.invoice, options.open, options.logger]);
|
|
858
862
|
const reloadState = React6.useCallback(async () => {
|
|
859
863
|
try {
|
|
860
|
-
const
|
|
861
|
-
|
|
864
|
+
const controller = controllerRef.current;
|
|
865
|
+
const next = await controller?.reloadState();
|
|
866
|
+
if (controllerRef.current === controller && next !== void 0) setState(next);
|
|
862
867
|
} catch (error) {
|
|
863
868
|
onErrorRef.current?.(error);
|
|
864
869
|
throw error;
|
|
@@ -878,10 +883,11 @@ function useCheckout(options) {
|
|
|
878
883
|
}, []);
|
|
879
884
|
const stageSwapRefund = React6.useCallback(
|
|
880
885
|
async (refund) => {
|
|
886
|
+
const controller = swapRefundController();
|
|
881
887
|
try {
|
|
882
|
-
return await
|
|
888
|
+
return await controller.stageSwapRefund(refund);
|
|
883
889
|
} catch (error) {
|
|
884
|
-
onErrorRef.current?.(error);
|
|
890
|
+
if (controllerRef.current === controller) onErrorRef.current?.(error);
|
|
885
891
|
throw error;
|
|
886
892
|
}
|
|
887
893
|
},
|
|
@@ -889,10 +895,11 @@ function useCheckout(options) {
|
|
|
889
895
|
);
|
|
890
896
|
const confirmSwapRefund = React6.useCallback(
|
|
891
897
|
async (refund) => {
|
|
898
|
+
const controller = swapRefundController();
|
|
892
899
|
try {
|
|
893
|
-
return await
|
|
900
|
+
return await controller.confirmSwapRefund(refund);
|
|
894
901
|
} catch (error) {
|
|
895
|
-
onErrorRef.current?.(error);
|
|
902
|
+
if (controllerRef.current === controller) onErrorRef.current?.(error);
|
|
896
903
|
throw error;
|
|
897
904
|
}
|
|
898
905
|
},
|
|
@@ -1861,24 +1868,20 @@ function renderKeepOrderNote(options) {
|
|
|
1861
1868
|
)
|
|
1862
1869
|
);
|
|
1863
1870
|
}
|
|
1864
|
-
var REFUND_ADDRESS_DRAFT_LIMIT = 8;
|
|
1865
|
-
var refundAddressDraftByAttempt = /* @__PURE__ */ new Map();
|
|
1866
|
-
function setRefundAddressDraft(attemptId, value) {
|
|
1867
|
-
refundAddressDraftByAttempt.delete(attemptId);
|
|
1868
|
-
refundAddressDraftByAttempt.set(attemptId, value);
|
|
1869
|
-
while (refundAddressDraftByAttempt.size > REFUND_ADDRESS_DRAFT_LIMIT) {
|
|
1870
|
-
const oldest = refundAddressDraftByAttempt.keys().next().value;
|
|
1871
|
-
if (oldest === void 0) break;
|
|
1872
|
-
refundAddressDraftByAttempt.delete(oldest);
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
1871
|
function SwapRefundForm(props) {
|
|
1876
|
-
const [refundAddress, setRefundAddress] = React8.useState(
|
|
1877
|
-
|
|
1872
|
+
const [refundAddress, setRefundAddress] = React8.useState("");
|
|
1873
|
+
const [draftAttemptId, setDraftAttemptId] = React8.useState(props.attemptId);
|
|
1874
|
+
if (draftAttemptId !== props.attemptId) {
|
|
1875
|
+
setDraftAttemptId(props.attemptId);
|
|
1876
|
+
setRefundAddress("");
|
|
1877
|
+
}
|
|
1878
|
+
const generation = React8.useRef(0);
|
|
1879
|
+
React8.useEffect(
|
|
1880
|
+
() => () => {
|
|
1881
|
+
generation.current += 1;
|
|
1882
|
+
},
|
|
1883
|
+
[props.attemptId]
|
|
1878
1884
|
);
|
|
1879
|
-
React8.useEffect(() => {
|
|
1880
|
-
setRefundAddress(refundAddressDraftByAttempt.get(props.attemptId) ?? "");
|
|
1881
|
-
}, [props.attemptId]);
|
|
1882
1885
|
const [submitting, setSubmitting] = React8.useState(false);
|
|
1883
1886
|
const [showAddressError, setShowAddressError] = React8.useState(false);
|
|
1884
1887
|
const address = refundAddress.trim();
|
|
@@ -1898,7 +1901,12 @@ function SwapRefundForm(props) {
|
|
|
1898
1901
|
return;
|
|
1899
1902
|
}
|
|
1900
1903
|
setSubmitting(true);
|
|
1901
|
-
|
|
1904
|
+
const captured = generation.current;
|
|
1905
|
+
void props.onRefund(props.attemptId, address, confirm).catch((error) => {
|
|
1906
|
+
if (captured === generation.current) props.onError?.(error);
|
|
1907
|
+
}).finally(() => {
|
|
1908
|
+
if (captured === generation.current) setSubmitting(false);
|
|
1909
|
+
});
|
|
1902
1910
|
}
|
|
1903
1911
|
},
|
|
1904
1912
|
// The form says what it is and why it is here before it asks for anything:
|
|
@@ -1934,7 +1942,6 @@ function SwapRefundForm(props) {
|
|
|
1934
1942
|
className: showError ? orClasses5.swapRefundInputInvalid : orClasses5.swapRefundInput,
|
|
1935
1943
|
onChange: (event) => {
|
|
1936
1944
|
const value = event.currentTarget.value;
|
|
1937
|
-
setRefundAddressDraft(props.attemptId, value);
|
|
1938
1945
|
setRefundAddress(value);
|
|
1939
1946
|
},
|
|
1940
1947
|
onBlur: () => {
|
|
@@ -1998,6 +2005,12 @@ function SwapPayloadQRCode(props) {
|
|
|
1998
2005
|
|
|
1999
2006
|
// src/wizard.ts
|
|
2000
2007
|
function PaymentWizard(props) {
|
|
2008
|
+
return React9.createElement(PaymentWizardSession, {
|
|
2009
|
+
...props,
|
|
2010
|
+
key: JSON.stringify([props.checkout?.reference, props.prefix?.replace(/\/+$/, "")])
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
function PaymentWizardSession(props) {
|
|
2001
2014
|
const [selection, setSelection] = React9.useState(
|
|
2002
2015
|
() => createPaymentWizardController().getSelection()
|
|
2003
2016
|
);
|
|
@@ -2014,6 +2027,7 @@ function PaymentWizard(props) {
|
|
|
2014
2027
|
const session = useCheckoutSession({
|
|
2015
2028
|
snapshot: () => checkout,
|
|
2016
2029
|
reference: () => reference,
|
|
2030
|
+
prefix: () => props.prefix,
|
|
2017
2031
|
swap: {
|
|
2018
2032
|
selection: {
|
|
2019
2033
|
started: () => startedSwapInvoice ?? void 0,
|
|
@@ -2076,6 +2090,7 @@ function PaymentWizard(props) {
|
|
|
2076
2090
|
if (prefix === void 0 || reference === void 0 || fetcher === void 0) {
|
|
2077
2091
|
return;
|
|
2078
2092
|
}
|
|
2093
|
+
const action = session.capture();
|
|
2079
2094
|
try {
|
|
2080
2095
|
const invoice = swapRefund !== void 0 ? await (confirm ? swapRefund.confirmSwapRefund : swapRefund.stageSwapRefund).call(
|
|
2081
2096
|
swapRefund,
|
|
@@ -2093,10 +2108,11 @@ function PaymentWizard(props) {
|
|
|
2093
2108
|
confirm,
|
|
2094
2109
|
...props.logger === void 0 ? {} : { logger: props.logger }
|
|
2095
2110
|
});
|
|
2111
|
+
if (!action.isCurrent()) return;
|
|
2096
2112
|
setStartedSwapInvoice(invoice);
|
|
2097
2113
|
setDismissedSwapInvoiceId(null);
|
|
2098
2114
|
} catch (error) {
|
|
2099
|
-
props.onError?.(error);
|
|
2115
|
+
if (action.isCurrent()) props.onError?.(error);
|
|
2100
2116
|
}
|
|
2101
2117
|
},
|
|
2102
2118
|
[
|
|
@@ -2108,7 +2124,8 @@ function PaymentWizard(props) {
|
|
|
2108
2124
|
props.logger,
|
|
2109
2125
|
swapRefund,
|
|
2110
2126
|
startedSwapInvoice,
|
|
2111
|
-
checkout?.invoices
|
|
2127
|
+
checkout?.invoices,
|
|
2128
|
+
session
|
|
2112
2129
|
]
|
|
2113
2130
|
);
|
|
2114
2131
|
const updateWizardSelection = React9.useCallback(
|
|
@@ -2917,11 +2934,21 @@ function Checkout(props) {
|
|
|
2917
2934
|
if (checkout !== void 0) {
|
|
2918
2935
|
return React10.createElement(CheckoutSnapshotMode, {
|
|
2919
2936
|
...props,
|
|
2937
|
+
key: JSON.stringify([
|
|
2938
|
+
checkout.reference,
|
|
2939
|
+
(props.prefix ?? OPENRECEIVE_DEFAULT_PREFIX).replace(/\/+$/, "")
|
|
2940
|
+
]),
|
|
2920
2941
|
checkout,
|
|
2921
2942
|
prefix: props.prefix ?? OPENRECEIVE_DEFAULT_PREFIX
|
|
2922
2943
|
});
|
|
2923
2944
|
}
|
|
2924
|
-
return React10.createElement(CheckoutCreate,
|
|
2945
|
+
return React10.createElement(CheckoutCreate, {
|
|
2946
|
+
...props,
|
|
2947
|
+
key: JSON.stringify([
|
|
2948
|
+
props.reference,
|
|
2949
|
+
(props.prefix ?? OPENRECEIVE_DEFAULT_PREFIX).replace(/\/+$/, "")
|
|
2950
|
+
])
|
|
2951
|
+
});
|
|
2925
2952
|
}
|
|
2926
2953
|
function CheckoutSnapshotMode(props) {
|
|
2927
2954
|
const { checkout } = props;
|
|
@@ -2976,8 +3003,10 @@ function CheckoutCreate(props) {
|
|
|
2976
3003
|
const session = useCheckoutSession({
|
|
2977
3004
|
snapshot: () => createdCheckoutRef.current,
|
|
2978
3005
|
reference: () => reference,
|
|
2979
|
-
|
|
3006
|
+
prefix: () => resolvedPrefix,
|
|
3007
|
+
requestCheckout: (id, signal) => requestCheckout({
|
|
2980
3008
|
prefix: resolvedPrefix,
|
|
3009
|
+
signal,
|
|
2981
3010
|
reference: id,
|
|
2982
3011
|
...csrfHeader === void 0 ? {} : { csrfHeader },
|
|
2983
3012
|
...metadataRef.current === void 0 ? {} : { metadata: metadataRef.current },
|
|
@@ -2998,14 +3027,17 @@ function CheckoutCreate(props) {
|
|
|
2998
3027
|
}, [syncUrl, reference, resumePathPrefix, routeReference]);
|
|
2999
3028
|
React10.useEffect(() => {
|
|
3000
3029
|
let cancelled = false;
|
|
3030
|
+
const action = session.capture();
|
|
3001
3031
|
setCreated({ status: "pending" });
|
|
3002
3032
|
prepareCheckout({
|
|
3003
3033
|
prefix: resolvedPrefix,
|
|
3034
|
+
signal: action.signal,
|
|
3004
3035
|
reference,
|
|
3005
3036
|
...csrfHeader === void 0 ? {} : { csrfHeader },
|
|
3006
3037
|
...createFetchRef.current === void 0 ? {} : { fetch: createFetchRef.current }
|
|
3007
3038
|
}).then(
|
|
3008
|
-
(checkout) => resumePaymentHash === void 0 ? checkout : resumeSwapAttempt({
|
|
3039
|
+
(checkout) => cancelled || !action.isCurrent() || resumePaymentHash === void 0 ? checkout : resumeSwapAttempt({
|
|
3040
|
+
signal: action.signal,
|
|
3009
3041
|
fetch: createFetchRef.current ?? globalThis.fetch,
|
|
3010
3042
|
prefix: resolvedPrefix,
|
|
3011
3043
|
...csrfHeader === void 0 ? {} : { csrfHeader },
|
|
@@ -3014,9 +3046,9 @@ function CheckoutCreate(props) {
|
|
|
3014
3046
|
snapshot: checkout
|
|
3015
3047
|
})
|
|
3016
3048
|
).then((checkout) => {
|
|
3017
|
-
if (!cancelled) setCreated({ status: "ready", checkout });
|
|
3049
|
+
if (!cancelled && action.isCurrent()) setCreated({ status: "ready", checkout });
|
|
3018
3050
|
}).catch((error) => {
|
|
3019
|
-
if (cancelled) return;
|
|
3051
|
+
if (cancelled || !action.isCurrent()) return;
|
|
3020
3052
|
onErrorRef.current?.(error);
|
|
3021
3053
|
setCreated({
|
|
3022
3054
|
status: "error",
|
|
@@ -3025,6 +3057,7 @@ function CheckoutCreate(props) {
|
|
|
3025
3057
|
});
|
|
3026
3058
|
return () => {
|
|
3027
3059
|
cancelled = true;
|
|
3060
|
+
session.reset();
|
|
3028
3061
|
};
|
|
3029
3062
|
}, [reference, resolvedPrefix, csrfHeader, resumePaymentHash, attempt]);
|
|
3030
3063
|
const onSwapStarted = React10.useCallback(
|
|
@@ -3176,7 +3209,9 @@ function CheckoutView(props) {
|
|
|
3176
3209
|
});
|
|
3177
3210
|
const stampsTheme = !theme.fromScope;
|
|
3178
3211
|
const ownsTheme = themeToggle && !theme.fromScope && lockedTheme === void 0;
|
|
3179
|
-
const [swapFocused, setSwapFocused] = React10.useState(
|
|
3212
|
+
const [swapFocused, setSwapFocused] = React10.useState(
|
|
3213
|
+
() => checkout.invoices.some((invoice) => invoice.rail === "swap" && invoice.swap !== void 0)
|
|
3214
|
+
);
|
|
3180
3215
|
const [lightningFocused, setLightningFocused] = React10.useState(false);
|
|
3181
3216
|
const QRCodeComponent = components?.QRCode ?? QRCode;
|
|
3182
3217
|
const InvoiceSummaryComponent = components?.InvoiceSummary ?? InvoiceSummary;
|
|
@@ -3188,7 +3223,7 @@ function CheckoutView(props) {
|
|
|
3188
3223
|
const expired = checkoutModel.status === "expired";
|
|
3189
3224
|
const settled = checkoutModel.status === "settled";
|
|
3190
3225
|
const showLightning = !!checkoutModel.invoice && (!paymentWizard || lightningFocused) && !swapFocused && !expired;
|
|
3191
|
-
const hideLightning = !
|
|
3226
|
+
const hideLightning = !settled && (swapFocused || !showLightning && !expired);
|
|
3192
3227
|
const showSummaryMeta = checkoutModel.status === "settled" || checkoutModel.status === "expired";
|
|
3193
3228
|
const fiatCurrency = checkoutModel.fiat_quote?.fiat?.currency;
|
|
3194
3229
|
const decodeInvoiceHref = createLightningInvoiceDecodeUrl2(checkoutModel.invoice, decodeLinkUrl);
|
|
@@ -3397,7 +3432,7 @@ function CheckoutView(props) {
|
|
|
3397
3432
|
)
|
|
3398
3433
|
)
|
|
3399
3434
|
),
|
|
3400
|
-
paymentWizard && !settled && (!expired || swapFocused) ? React10.createElement(PaymentWizard, {
|
|
3435
|
+
paymentWizard && !settled && (!expired || swapFocused || checkoutModel.checkout?.invoices.some((invoice) => invoice.swap !== void 0)) ? React10.createElement(PaymentWizard, {
|
|
3401
3436
|
key: "wizard",
|
|
3402
3437
|
// Only pass invoice when it's a real bolt11 (non-empty, non-deferred).
|
|
3403
3438
|
invoice: checkoutModel.invoice || void 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openreceive/react",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"description": "React components and hooks for Bitcoin Lightning checkout and optional USDT, USDC, SOL and ETH swaps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"main": "./dist/index.js",
|
|
20
20
|
"types": "./dist/index.d.ts",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@openreceive/browser": "0.4.
|
|
23
|
-
"@openreceive/core": "0.4.
|
|
22
|
+
"@openreceive/browser": "0.4.11",
|
|
23
|
+
"@openreceive/core": "0.4.11"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": ">=18.0.0"
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -307,6 +307,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
307
307
|
Questions, or a problem with the library itself:
|
|
308
308
|
https://openreceive.org/contact
|
|
309
309
|
|
|
310
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
311
|
+
|
|
310
312
|
---
|
|
311
313
|
|
|
312
314
|
## The quickstart, in full
|
|
@@ -480,7 +482,7 @@ The host class needs three things: authorization, the trusted price, and
|
|
|
480
482
|
fulfillment. All three receive the `reference` — a string you choose, and the
|
|
481
483
|
fulfillment identity: your order id, one per thing you fulfill, created before
|
|
482
484
|
checkout, kept across retries, never reused. OpenReceive never looks inside
|
|
483
|
-
it, but `on_paid`
|
|
485
|
+
it, but `on_paid` commits fulfillment once per reference, a new checkout under a reference
|
|
484
486
|
that already settled is refused with 409, and a fresh id per page load lets
|
|
485
487
|
one order be paid twice.
|
|
486
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
|
|
@@ -301,6 +301,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
301
301
|
Questions, or a problem with the library itself:
|
|
302
302
|
https://openreceive.org/contact
|
|
303
303
|
|
|
304
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
305
|
+
|
|
304
306
|
---
|
|
305
307
|
|
|
306
308
|
## The quickstart, in full
|
|
@@ -475,7 +477,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
475
477
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
476
478
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
477
479
|
across retries, never reused. OpenReceive never looks inside it, but `on_paid`
|
|
478
|
-
|
|
480
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
479
481
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
480
482
|
paid twice.
|
|
481
483
|
|
|
@@ -524,7 +526,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
524
526
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
525
527
|
|
|
526
528
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
527
|
-
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.
|
|
528
530
|
|
|
529
531
|
A page without a bundler renders the same checkout as a custom element:
|
|
530
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
|
|
@@ -284,6 +284,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
284
284
|
Questions, or a problem with the library itself:
|
|
285
285
|
https://openreceive.org/contact
|
|
286
286
|
|
|
287
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
288
|
+
|
|
287
289
|
---
|
|
288
290
|
|
|
289
291
|
## The quickstart, in full
|
|
@@ -477,7 +479,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
477
479
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
478
480
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
479
481
|
across retries, never reused. OpenReceive never looks inside it, but `onPaid`
|
|
480
|
-
|
|
482
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
481
483
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
482
484
|
paid twice.
|
|
483
485
|
|
|
@@ -526,7 +528,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
526
528
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
527
529
|
|
|
528
530
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
529
|
-
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.
|
|
530
532
|
|
|
531
533
|
A runnable illustration of this boundary — not a template to copy models from —
|
|
532
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
|
|
@@ -291,6 +291,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
291
291
|
Questions, or a problem with the library itself:
|
|
292
292
|
https://openreceive.org/contact
|
|
293
293
|
|
|
294
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
295
|
+
|
|
294
296
|
---
|
|
295
297
|
|
|
296
298
|
## The quickstart, in full
|
|
@@ -456,7 +458,7 @@ variable as set/unset only.
|
|
|
456
458
|
price, and fulfillment. All three receive the `reference` — a string you
|
|
457
459
|
choose, and the fulfillment identity: your order id, one per thing you
|
|
458
460
|
fulfill, created before checkout, kept across retries, never reused.
|
|
459
|
-
OpenReceive never looks inside it, but `onPaid`
|
|
461
|
+
OpenReceive never looks inside it, but `onPaid` commits fulfillment once per reference, a new
|
|
460
462
|
checkout under a reference that already settled is refused with 409, and a
|
|
461
463
|
fresh id per page load lets one order be paid twice.
|
|
462
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
|
|
@@ -290,6 +290,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
290
290
|
Questions, or a problem with the library itself:
|
|
291
291
|
https://openreceive.org/contact
|
|
292
292
|
|
|
293
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
294
|
+
|
|
293
295
|
---
|
|
294
296
|
|
|
295
297
|
## The quickstart, in full
|
|
@@ -496,7 +498,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
496
498
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
497
499
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
498
500
|
across retries, never reused. OpenReceive never looks inside it, but `onPaid`
|
|
499
|
-
|
|
501
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
500
502
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
501
503
|
paid twice.
|
|
502
504
|
|
|
@@ -576,7 +578,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
576
578
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
577
579
|
|
|
578
580
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
579
|
-
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.
|
|
580
582
|
|
|
581
583
|
A runnable illustration of this boundary — not a template to copy models from —
|
|
582
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
|
|
@@ -276,6 +276,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
276
276
|
Questions, or a problem with the library itself:
|
|
277
277
|
https://openreceive.org/contact
|
|
278
278
|
|
|
279
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
280
|
+
|
|
279
281
|
---
|
|
280
282
|
|
|
281
283
|
## The quickstart, in full
|
|
@@ -454,7 +456,7 @@ the `reference`. OpenReceive never prices from payer input.
|
|
|
454
456
|
The `reference` is a string you choose, and it is the fulfillment identity:
|
|
455
457
|
your order id — one per thing you fulfill, created before checkout, kept
|
|
456
458
|
across retries, never reused. OpenReceive never looks inside it, but `onPaid`
|
|
457
|
-
|
|
459
|
+
commits fulfillment once per reference, a new checkout under a reference that already
|
|
458
460
|
settled is refused with 409, and a fresh id per page load lets one order be
|
|
459
461
|
paid twice.
|
|
460
462
|
|
|
@@ -503,7 +505,7 @@ Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
|
503
505
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
504
506
|
|
|
505
507
|
That is the whole loop: your server owns the price and the order, the payer gets
|
|
506
|
-
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.
|
|
507
509
|
|
|
508
510
|
A runnable illustration of this boundary — not a template to copy models from —
|
|
509
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
|
|
@@ -301,6 +301,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
301
301
|
Questions, or a problem with the library itself:
|
|
302
302
|
https://openreceive.org/contact
|
|
303
303
|
|
|
304
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
305
|
+
|
|
304
306
|
---
|
|
305
307
|
|
|
306
308
|
## The quickstart, in full
|
|
@@ -512,7 +514,7 @@ prices with exact decimal math, and returns the order id the page will pass as
|
|
|
512
514
|
the `reference`. OpenReceive never prices from payer input. The `reference` is
|
|
513
515
|
a string you choose, and it is the fulfillment identity: your order id — one
|
|
514
516
|
per thing you fulfill, created before checkout, kept across retries, never
|
|
515
|
-
reused. `onPaid`
|
|
517
|
+
reused. `onPaid` commits fulfillment once per reference, a new checkout under a reference
|
|
516
518
|
that already settled is refused with 409, and a fresh id per page load lets
|
|
517
519
|
one order be paid twice.
|
|
518
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
|
|
@@ -285,6 +285,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
285
285
|
Questions, or a problem with the library itself:
|
|
286
286
|
https://openreceive.org/contact
|
|
287
287
|
|
|
288
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
289
|
+
|
|
288
290
|
---
|
|
289
291
|
|
|
290
292
|
## The quickstart, in full
|
|
@@ -432,7 +434,7 @@ The initializer needs three things: authorization, the trusted price, and
|
|
|
432
434
|
fulfillment. All three receive the `reference` — a string you choose, and the
|
|
433
435
|
fulfillment identity: your order id, one per thing you fulfill, created before
|
|
434
436
|
checkout, kept across retries, never reused. OpenReceive never looks inside
|
|
435
|
-
it, but `on_paid`
|
|
437
|
+
it, but `on_paid` commits fulfillment once per reference, a new checkout under a reference
|
|
436
438
|
that already settled is refused with 409, and a fresh id per page load lets
|
|
437
439
|
one order be paid twice.
|
|
438
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
|