openreceive-server 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/lib/openreceive/server/errors.rb +1 -1
- data/lib/openreceive/server/request_handler.rb +31 -11
- data/lib/openreceive/server/service.rb +10 -0
- data/lib/openreceive/server/swap/fixedfloat.rb +7 -8
- data/lib/openreceive/server/version.rb +1 -1
- data/skills/integrate-openreceive/references/btcpay.md +6 -4
- data/skills/integrate-openreceive/references/django.md +5 -3
- data/skills/integrate-openreceive/references/fastapi.md +6 -4
- data/skills/integrate-openreceive/references/fastify.md +6 -4
- data/skills/integrate-openreceive/references/laravel.md +5 -3
- data/skills/integrate-openreceive/references/next.md +6 -4
- data/skills/integrate-openreceive/references/node.md +6 -4
- data/skills/integrate-openreceive/references/php.md +5 -3
- data/skills/integrate-openreceive/references/rails.md +5 -3
- data/skills/integrate-openreceive/references/woocommerce.md +3 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39aa74c7725e2887e61131f418feceb4a03194b5a079ac46307f685600d062f5
|
|
4
|
+
data.tar.gz: 33e89f647cef3990e85ce10990ec868267251001bbf03b280921d522bdd5dce6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 928f356068c9f63b15b9955ea89fc6e77187828eb41a619c4a9bce4fbdd8b2b61c73e432387a777f35005e32a093347531b41e065e28e5b3b48551d813d1946d
|
|
7
|
+
data.tar.gz: fd128c515bd6a1f99c836dae1c57c3fd3d3468a52130d2e5b7ffacc5ba049d3ed84ac1b5e34b4c626fe9f00c91469030bb68f3343debf4f6aff658337fa38ca3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.11 - 2026-09-21
|
|
4
|
+
|
|
5
|
+
The mounted HTTP routes mint invoices with the host's own description. The
|
|
6
|
+
display string `amount_for` returns beside the price is now the default
|
|
7
|
+
`make_invoice` memo, so a host that writes no invoice code stops minting
|
|
8
|
+
BOLT11s with an empty description. An explicit request-body `memo` still wins
|
|
9
|
+
and still carries the length cap; a host that returns no description still
|
|
10
|
+
mints without one.
|
|
11
|
+
|
|
12
|
+
Payment safety and disclosure fixes: a checkout's `created_at_source` (whether
|
|
13
|
+
the creation time came from the wallet or from us) is internal and is stripped
|
|
14
|
+
from every public body, error responses carry a redacted message and no
|
|
15
|
+
arbitrary internal `details`, unexpected errors are reported to `Rails.error`
|
|
16
|
+
detached from their cause and backtrace, a host callback that returns an
|
|
17
|
+
invalid amount raises `InternalHostError` instead of leaking a validation
|
|
18
|
+
failure, and a refund address is rejected unless the attempt has a saved,
|
|
19
|
+
supported pay-in asset. A host that refuses an attempt now answers 409 with
|
|
20
|
+
instructions withheld, while genuine storage failure stays a retryable 503.
|
|
21
|
+
|
|
3
22
|
## 0.4.10 - 2026-09-16
|
|
4
23
|
|
|
5
24
|
Release in lockstep with the 0.4.10 stablecoin checkout fix. The swap fee
|
|
@@ -97,7 +97,7 @@ module OpenReceive
|
|
|
97
97
|
class HostPersistenceError < StandardError
|
|
98
98
|
attr_reader :status, :code, :retryable
|
|
99
99
|
|
|
100
|
-
def initialize(message = "
|
|
100
|
+
def initialize(message = "Payment storage is unavailable; " \
|
|
101
101
|
"payer instructions were withheld. Please retry.")
|
|
102
102
|
super(message)
|
|
103
103
|
@status = 503
|
|
@@ -88,7 +88,7 @@ module OpenReceive
|
|
|
88
88
|
else
|
|
89
89
|
@service.create_checkout(
|
|
90
90
|
"reference" => reference, "amount" => required_amount(resolved),
|
|
91
|
-
"memo" =>
|
|
91
|
+
"memo" => mint_memo(body, resolved), "metadata" => body["metadata"]
|
|
92
92
|
)
|
|
93
93
|
end
|
|
94
94
|
commit(checkout, nil, request) unless resolved["payment_hash"]
|
|
@@ -99,7 +99,7 @@ module OpenReceive
|
|
|
99
99
|
# committed invoice amount — and served on the re-fetch path too, so
|
|
100
100
|
# a repeated create answers with the same shape it first did.
|
|
101
101
|
payment_methods = @service.list_swap_options(amount_msats: checkout["amount_msats"])
|
|
102
|
-
body = { "checkout" => checkout, "payment_methods" => payment_methods }
|
|
102
|
+
body = { "checkout" => public_checkout(checkout), "payment_methods" => payment_methods }
|
|
103
103
|
description = resolved_description(resolved)
|
|
104
104
|
body["description"] = description if description
|
|
105
105
|
success(201, body, request_id)
|
|
@@ -180,7 +180,7 @@ module OpenReceive
|
|
|
180
180
|
"reference" => reference,
|
|
181
181
|
"amount" => required_amount(resolved),
|
|
182
182
|
"pay_in_asset" => asset,
|
|
183
|
-
"memo" =>
|
|
183
|
+
"memo" => mint_memo(body, resolved),
|
|
184
184
|
"metadata" => body["metadata"]
|
|
185
185
|
)
|
|
186
186
|
end
|
|
@@ -255,9 +255,9 @@ module OpenReceive
|
|
|
255
255
|
retryable = Nwc::RETRYABLE_ERROR_CODES.include?(code) if retryable.nil?
|
|
256
256
|
status = retryable ? 503 : 502
|
|
257
257
|
end
|
|
258
|
-
body = { "code" => code, "message" => error.message, "request_id" => request_id }
|
|
258
|
+
body = { "code" => code, "message" => Nwc.redact_error_text(error.message), "request_id" => request_id }
|
|
259
259
|
body["retryable"] = retryable unless retryable.nil?
|
|
260
|
-
|
|
260
|
+
# Arbitrary internal details and causes have no public projection.
|
|
261
261
|
response_headers = headers(request_id)
|
|
262
262
|
# Mirrors the JS handler: a Retry-After hint (whole seconds, minimum 1)
|
|
263
263
|
# rides along with retryable throttling errors.
|
|
@@ -271,6 +271,10 @@ module OpenReceive
|
|
|
271
271
|
|
|
272
272
|
private
|
|
273
273
|
|
|
274
|
+
def public_checkout(checkout)
|
|
275
|
+
checkout.reject { |key, _| key.to_s == "created_at_source" }
|
|
276
|
+
end
|
|
277
|
+
|
|
274
278
|
# Status refresh with no request-level pass: one-attempt reconcile_payments,
|
|
275
279
|
# delivering settlement inline. A truncated walk raises WalletUnavailableError
|
|
276
280
|
# (retryable 503) rather than reporting not_found.
|
|
@@ -391,11 +395,11 @@ module OpenReceive
|
|
|
391
395
|
# (database down, bug): retryable 503, never a payer-blaming conflict
|
|
392
396
|
# — mirrors the JS handler's commit().
|
|
393
397
|
raise e if e.respond_to?(:status) && e.respond_to?(:code)
|
|
394
|
-
raise
|
|
398
|
+
raise ConflictError, "The host did not accept this payment attempt; payer instructions were withheld."
|
|
395
399
|
end
|
|
396
400
|
|
|
397
401
|
def public_swap(swap)
|
|
398
|
-
swap.reject { |key, _| key == "swap_data" }
|
|
402
|
+
swap.reject { |key, _| key == "swap_data" }.merge("checkout" => public_checkout(swap.fetch("checkout")))
|
|
399
403
|
end
|
|
400
404
|
|
|
401
405
|
# Payer-facing subset of a settlement's wallet details, whitelisted
|
|
@@ -442,7 +446,9 @@ module OpenReceive
|
|
|
442
446
|
# preimages.
|
|
443
447
|
def report_unexpected_error(error, request_id)
|
|
444
448
|
if defined?(::Rails) && ::Rails.respond_to?(:error) && ::Rails.error
|
|
445
|
-
|
|
449
|
+
# Detached diagnostic: no cause, stack or attached provider configuration.
|
|
450
|
+
safe_error = RuntimeError.new(OpenReceive::Nwc.redact_error_text(error.message))
|
|
451
|
+
::Rails.error.report(safe_error, handled: true, source: "openreceive")
|
|
446
452
|
else
|
|
447
453
|
origin = Array(error.backtrace).first
|
|
448
454
|
line = "[openreceive] unexpected #{error.class} (request_id=#{request_id})" \
|
|
@@ -550,10 +556,24 @@ module OpenReceive
|
|
|
550
556
|
memo
|
|
551
557
|
end
|
|
552
558
|
|
|
559
|
+
# The description that goes INTO the invoice. An explicit body memo wins,
|
|
560
|
+
# so a client that writes its own copy keeps it; otherwise the host's
|
|
561
|
+
# display string is it. Without the fallback a host on the mounted routes
|
|
562
|
+
# mints BOLT11s with no description at all and the payer's wallet shows a
|
|
563
|
+
# blank line next to the amount. Only the body memo carries the length
|
|
564
|
+
# cap: the cap bounds client input, and the host description is host data
|
|
565
|
+
# exactly like the price beside it.
|
|
566
|
+
def mint_memo(body, resolved)
|
|
567
|
+
memo = validated_memo(body)
|
|
568
|
+
blank = memo.nil? || (memo.is_a?(String) && memo.strip.empty?)
|
|
569
|
+
blank ? resolved_description(resolved) : memo
|
|
570
|
+
end
|
|
571
|
+
|
|
553
572
|
# What the payer is buying, in the host's own words: one optional display
|
|
554
|
-
# string the host returns beside the price.
|
|
555
|
-
#
|
|
556
|
-
#
|
|
573
|
+
# string the host returns beside the price. Never read from a request
|
|
574
|
+
# body, because the payer does not get to write the copy next to the
|
|
575
|
+
# amount; it rides the prepare and create responses and, via mint_memo,
|
|
576
|
+
# defaults the invoice description. Blank is the same as absent.
|
|
557
577
|
def resolved_description(resolved)
|
|
558
578
|
value = resolved["description"]
|
|
559
579
|
return nil unless value.is_a?(String)
|
|
@@ -162,6 +162,7 @@ module OpenReceive
|
|
|
162
162
|
"bolt11" => wallet.fetch("invoice"),
|
|
163
163
|
"amount_msats" => wallet.fetch("amount_msats"),
|
|
164
164
|
"created_at" => created_at,
|
|
165
|
+
"created_at_source" => wallet["created_at"].nil? ? "host" : "wallet",
|
|
165
166
|
"expires_at" => expires_at,
|
|
166
167
|
"fiat_quote" => fiat_quote
|
|
167
168
|
}
|
|
@@ -563,6 +564,12 @@ module OpenReceive
|
|
|
563
564
|
end
|
|
564
565
|
|
|
565
566
|
def resolve_amount(input)
|
|
567
|
+
resolve_amount_value(input)
|
|
568
|
+
rescue ArgumentError, TypeError, KeyError, ValidationError
|
|
569
|
+
raise InternalHostError, "The host supplied an invalid payment amount."
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def resolve_amount_value(input)
|
|
566
573
|
amount = stringify(input)
|
|
567
574
|
if amount.key?("sats")
|
|
568
575
|
return [OpenReceive::Money.direct_to_msats(currency: "SATS", value: amount.fetch("sats")), nil]
|
|
@@ -639,6 +646,9 @@ module OpenReceive
|
|
|
639
646
|
# checksum — a false accept here sends the payer's money somewhere
|
|
640
647
|
# unrecoverable. Mirrors the JS normalizeRefundAddress exactly.
|
|
641
648
|
def normalize_refund_address(value, pay_in_asset)
|
|
649
|
+
unless OpenReceive::Server::Swap::Assets::PAY_IN_ASSETS.include?(pay_in_asset)
|
|
650
|
+
raise InternalHostError, "Swap recovery requires a supported pay-in asset/network."
|
|
651
|
+
end
|
|
642
652
|
normalized = value.to_s.strip
|
|
643
653
|
if normalized.empty? || normalized.length > 300
|
|
644
654
|
raise ValidationError, "refundAddress is invalid."
|
|
@@ -306,9 +306,7 @@ module OpenReceive
|
|
|
306
306
|
def post(path, body)
|
|
307
307
|
@weight_budget&.reserve(path)
|
|
308
308
|
body_string = JSON.generate(body)
|
|
309
|
-
#
|
|
310
|
-
# responsible for sanitizing nested secrets; the API key and HMAC
|
|
311
|
-
# signature live in headers and are deliberately never logged.
|
|
309
|
+
# Host diagnostic sinks receive summaries only, before any provider I/O.
|
|
312
310
|
log_api_request(path, body)
|
|
313
311
|
begin
|
|
314
312
|
response = @http.call(
|
|
@@ -341,9 +339,7 @@ module OpenReceive
|
|
|
341
339
|
message: "FixedFloat #{path} returned invalid JSON."
|
|
342
340
|
)
|
|
343
341
|
end
|
|
344
|
-
#
|
|
345
|
-
# raise. The host sink sanitizes nested secrets — notably the order
|
|
346
|
-
# token in a create/order response — so this must not pre-redact.
|
|
342
|
+
# Summarize before invoking any host sink, including error envelopes.
|
|
347
343
|
log_api_response(path: path, status: status, ok: ok,
|
|
348
344
|
code: parsed["code"], msg: parsed["msg"], data: parsed["data"])
|
|
349
345
|
unless ok
|
|
@@ -369,7 +365,10 @@ module OpenReceive
|
|
|
369
365
|
end
|
|
370
366
|
|
|
371
367
|
def log_api_request(path, body = {})
|
|
372
|
-
@api_request_logger&.call(
|
|
368
|
+
@api_request_logger&.call(
|
|
369
|
+
"provider" => @name, "path" => path, "has_body" => !body.empty?,
|
|
370
|
+
"has_token" => body.is_a?(Hash) && !body["token"].nil?
|
|
371
|
+
)
|
|
373
372
|
rescue StandardError
|
|
374
373
|
nil
|
|
375
374
|
end
|
|
@@ -377,7 +376,7 @@ module OpenReceive
|
|
|
377
376
|
def log_api_response(path:, status:, ok:, code: nil, msg: nil, data: nil)
|
|
378
377
|
@api_response_logger&.call(
|
|
379
378
|
"provider" => @name, "path" => path, "status" => status, "ok" => ok,
|
|
380
|
-
"code" => code
|
|
379
|
+
"code" => code.is_a?(Numeric) ? code : nil, "has_data" => !data.nil?
|
|
381
380
|
)
|
|
382
381
|
rescue StandardError
|
|
383
382
|
nil
|
|
@@ -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
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openreceive-server
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenReceive
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.4.
|
|
18
|
+
version: 0.4.11
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.4.
|
|
25
|
+
version: 0.4.11
|
|
26
26
|
description: |
|
|
27
27
|
Accept Bitcoin Lightning payments directly into a wallet you control, from your
|
|
28
28
|
Ruby or Rack application. OpenReceive provides invoice creation, payment checks,
|