openreceive-server 0.4.10 → 0.4.13
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 +33 -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/debug-openreceive-payment/SKILL.md +1 -1
- data/skills/integrate-openreceive/SKILL.md +4 -3
- data/skills/integrate-openreceive/references/btcpay.md +67 -36
- data/skills/integrate-openreceive/references/django.md +291 -244
- data/skills/integrate-openreceive/references/fastapi.md +206 -155
- data/skills/integrate-openreceive/references/fastify.md +200 -147
- data/skills/integrate-openreceive/references/laravel.md +294 -234
- data/skills/integrate-openreceive/references/next.md +210 -162
- data/skills/integrate-openreceive/references/node.md +188 -138
- data/skills/integrate-openreceive/references/php.md +248 -193
- data/skills/integrate-openreceive/references/rails.md +262 -218
- data/skills/integrate-openreceive/references/woocommerce.md +68 -51
- 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: c33517376bf957f25550f721694be4652218083b8026847ce2836c7dbd586464
|
|
4
|
+
data.tar.gz: 14988254c0ec4cb7cbccc4a44733c20ef7a3860f870242b0184e1f3a895f4173
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a95e7527885746968412329f1bcde0a810533dc6c970f4aa7fb620e3fad02087d97c99551771f7c7c08e108044a1501a650f6e234e3da3d073ae3e0a1fe65e76
|
|
7
|
+
data.tar.gz: 493ae52bcf9179b3af779d50697f8b9fe03e62dfd0277797056ad50f268e80a6a2f5f77f51583e642a4d07b8387c41960a295aa33065165b36c97d65ac74800c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.13 - 2026-10-01
|
|
4
|
+
|
|
5
|
+
Release in lockstep with the 0.4.13 reconciliation fixes (in
|
|
6
|
+
`openreceive-rails`: a slow wallet no longer livelocks the reconcile pass,
|
|
7
|
+
host-clock attempts get their own scan window, the gate floor is 3 s). No
|
|
8
|
+
change to this gem's API.
|
|
9
|
+
|
|
10
|
+
## 0.4.12 - 2026-10-01
|
|
11
|
+
|
|
12
|
+
Release in lockstep with the 0.4.12 checkout fixes. The swap deposit
|
|
13
|
+
warning text the browser shows no longer carries the "pay with one method
|
|
14
|
+
only" sentence; it is built in the browser package, not here. No Ruby server
|
|
15
|
+
API or payment behavior changes.
|
|
16
|
+
|
|
17
|
+
## 0.4.11 - 2026-09-21
|
|
18
|
+
|
|
19
|
+
The mounted HTTP routes mint invoices with the host's own description. The
|
|
20
|
+
display string `amount_for` returns beside the price is now the default
|
|
21
|
+
`make_invoice` memo, so a host that writes no invoice code stops minting
|
|
22
|
+
BOLT11s with an empty description. An explicit request-body `memo` still wins
|
|
23
|
+
and still carries the length cap; a host that returns no description still
|
|
24
|
+
mints without one.
|
|
25
|
+
|
|
26
|
+
Payment safety and disclosure fixes: a checkout's `created_at_source` (whether
|
|
27
|
+
the creation time came from the wallet or from us) is internal and is stripped
|
|
28
|
+
from every public body, error responses carry a redacted message and no
|
|
29
|
+
arbitrary internal `details`, unexpected errors are reported to `Rails.error`
|
|
30
|
+
detached from their cause and backtrace, a host callback that returns an
|
|
31
|
+
invalid amount raises `InternalHostError` instead of leaking a validation
|
|
32
|
+
failure, and a refund address is rejected unless the attempt has a saved,
|
|
33
|
+
supported pay-in asset. A host that refuses an attempt now answers 409 with
|
|
34
|
+
instructions withheld, while genuine storage failure stays a retryable 503.
|
|
35
|
+
|
|
3
36
|
## 0.4.10 - 2026-09-16
|
|
4
37
|
|
|
5
38
|
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
|
|
@@ -49,7 +49,7 @@ same diagnostics redacted, always exit 0 — safe to share.
|
|
|
49
49
|
## 4. Paid but never settles
|
|
50
50
|
|
|
51
51
|
- Settlement is opportunistic: any OpenReceive request runs one reconcile pass
|
|
52
|
-
through a durable gate (min
|
|
52
|
+
through a durable gate (min 3s between wallet scans, stretched by invoice
|
|
53
53
|
age). A quiet server settles on the next request — or run the optional
|
|
54
54
|
notification worker. No timer is missing; that is the design.
|
|
55
55
|
- An unpaid attempt closes only after a successful wallet scan at/after expiry
|
|
@@ -34,9 +34,10 @@ code** (`NWC_URI`).
|
|
|
34
34
|
- BTCPay Server: [references/btcpay.md](references/btcpay.md) — a plugin,
|
|
35
35
|
configured in BTCPay's store UI or Greenfield API; no application code,
|
|
36
36
|
no npm packages, no gem. The rest of this file is about the library.
|
|
37
|
-
3. Follow its **Step 0** first:
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
3. Follow its **Step 0** first: before writing code or searching the machine,
|
|
38
|
+
ask the user for the receive-only NWC code (then the swap URI), one question
|
|
39
|
+
per message, and store each pasted code in the project's env file yourself.
|
|
40
|
+
Never print the value; never invent a placeholder.
|
|
40
41
|
|
|
41
42
|
Install, per adapter — Express: `npm install @openreceive/express @openreceive/react`;
|
|
42
43
|
Fastify: `npm install @openreceive/fastify @openreceive/react`; Next.js:
|
|
@@ -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.13.
|
|
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
|
|
@@ -35,10 +35,12 @@ refund path on the same checkout screen.
|
|
|
35
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
|
-
2. Check whether the plugin is installed (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
2. Check whether the plugin is installed (the Plugins menu — the plug icon in
|
|
39
|
+
the top-right corner — under Installed Plugins, or the store navigation
|
|
40
|
+
shows an "OpenReceive" entry). If not, install it from the BTCPay plugin
|
|
41
|
+
directory (the same Plugins menu → Plugin Directory, search "openreceive",
|
|
42
|
+
then Install and Restart now), as the quickstart says; do not invent an
|
|
43
|
+
installer command.
|
|
42
44
|
3. Check whether the store already has an OpenReceive connection:
|
|
43
45
|
`GET /api/v1/stores/{storeId}/openreceive/settings` returns
|
|
44
46
|
`lightningNodeIsOpenReceive`. If true, the wallet step is done — go to
|
|
@@ -119,6 +121,8 @@ enough; drop the `.md` for the same page a person would read.
|
|
|
119
121
|
Questions, or a problem with the plugin itself:
|
|
120
122
|
https://openreceive.org/contact
|
|
121
123
|
|
|
124
|
+
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
125
|
+
|
|
122
126
|
---
|
|
123
127
|
|
|
124
128
|
## The quickstart, in full
|
|
@@ -131,15 +135,15 @@ passes. The page it comes from is https://openreceive.org/guides/quickstart-btcp
|
|
|
131
135
|
Requires BTCPay Server ≥ 2.4.4.
|
|
132
136
|
|
|
133
137
|
The OpenReceive plugin makes a receive-only NWC wallet the Lightning node of a
|
|
134
|
-
BTCPay store. BTCPay
|
|
135
|
-
payments
|
|
136
|
-
BTCPay invoice with USDT, USDC, ETH or SOL through a Lightning Swap Connect
|
|
137
|
-
provider
|
|
138
|
+
BTCPay store. BTCPay creates every Lightning invoice in that wallet. It records
|
|
139
|
+
payments the same way it records any other payment. You can also let payers pay
|
|
140
|
+
a BTCPay invoice with USDT, USDC, ETH or SOL through a Lightning Swap Connect
|
|
141
|
+
provider. The swap pays into the same wallet. The store's internal node is
|
|
138
142
|
never used.
|
|
139
143
|
|
|
140
144
|
This is not the Node or Rails library. There are no hooks, no
|
|
141
|
-
`openreceive_payments` table and no OpenReceive HTTP routes
|
|
142
|
-
invoices, checkout, webhooks and Greenfield API
|
|
145
|
+
`openreceive_payments` table and no OpenReceive HTTP routes. BTCPay's own
|
|
146
|
+
invoices, checkout, webhooks and Greenfield API do that work.
|
|
143
147
|
|
|
144
148
|
### 1. Prerequisites
|
|
145
149
|
|
|
@@ -155,39 +159,66 @@ invoices, checkout, webhooks and Greenfield API are the host.
|
|
|
155
159
|
|
|
156
160
|
### 2. Install the plugin
|
|
157
161
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
162
|
+
Sign in as a **server administrator**. If someone else hosts your server, ask
|
|
163
|
+
them to install the plugin for you.
|
|
164
|
+
|
|
165
|
+
**1. Open the Plugins menu.** It is the plug icon in the top-right corner.
|
|
166
|
+
|
|
167
|
+
**2. Click Plugin Directory.**
|
|
168
|
+
|
|
169
|
+
**3. Search for `openreceive`** and click the **OpenReceive** result.
|
|
170
|
+
|
|
171
|
+
**4. Click Install in BTCPay Server.** Confirm when prompted, then click
|
|
172
|
+
**Restart now** and wait for BTCPay to come back.
|
|
173
|
+
|
|
174
|
+
At startup, BTCPay creates the plugin's two tables in its own Postgres
|
|
175
|
+
database: `openreceive_invoices` and `openreceive_swaps`, in the schema
|
|
176
|
+
`BTCPayServer.Plugins.OpenReceive`. Nothing else is created.
|
|
163
177
|
|
|
164
178
|
To build the plugin from source instead, follow
|
|
165
179
|
[the .NET workspace README](https://github.com/OpenReceive/openreceive/blob/master/packages/dotnet/README.md).
|
|
166
180
|
|
|
167
181
|
### 3. Connect the wallet
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
node.
|
|
183
|
+
1. Select your store and open **OpenReceive** in its sidebar, under Wallets.
|
|
184
|
+
2. Paste your receive-only NWC code. To see what the wallet supports first,
|
|
185
|
+
click **Test connection**.
|
|
186
|
+
3. Click **Save NWC Code**.
|
|
187
|
+
4. To turn swaps on, paste a Lightning Swap Connect code and click **Save swap
|
|
188
|
+
settings**.
|
|
176
189
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
190
|
+
The page then shows **Wallet connected**. If you set up a provider, it also
|
|
191
|
+
shows **Swaps on**. There is nothing else to configure. You never open BTCPay's
|
|
192
|
+
Lightning node screen, and the plugin never reads the internal node.
|
|
193
|
+
|
|
194
|
+
Screenshots for each of those steps, and for creating a first test invoice,
|
|
195
|
+
are in the plugin's
|
|
196
|
+
[README](https://github.com/OpenReceive/openreceive/blob/master/packages/dotnet/BTCPayServer.Plugins.OpenReceive/README.md).
|
|
197
|
+
|
|
198
|
+
The plugin refuses to save a code whose wallet advertises a spend method such
|
|
199
|
+
as `pay_invoice`. Create a receive-only code instead. If your wallet cannot
|
|
200
|
+
make one, there is an override, but using it is a deliberate choice and the
|
|
201
|
+
plugin logs it.
|
|
202
|
+
|
|
203
|
+
Turning swaps on raises the store's invoice expiration to 60 minutes if it is
|
|
204
|
+
shorter. A swap needs the invoice to stay open for at least 45 minutes.
|
|
182
205
|
|
|
183
206
|
### 4. Check it
|
|
184
207
|
|
|
185
|
-
**Run a health check** on the OpenReceive page runs every
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
Click **Run a health check** on the OpenReceive page. It runs every check
|
|
209
|
+
right there:
|
|
210
|
+
|
|
211
|
+
- the connection
|
|
212
|
+
- the wallet preflight
|
|
213
|
+
- payment notifications
|
|
214
|
+
- the last wallet scan
|
|
215
|
+
- the swap provider and its assets
|
|
216
|
+
- the invoice expiration
|
|
217
|
+
- swaps that need a human
|
|
218
|
+
|
|
219
|
+
Each failing check comes with a link to the fix.
|
|
189
220
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
`nostr+walletconnect://` string in BTCPay's Lightning node screen.
|
|
221
|
+
The [BTCPay plugin reference](https://openreceive.org/guides/btcpay-reference.md) lists every setting,
|
|
222
|
+
Greenfield route, swap state, log event and check. It also lists what the
|
|
223
|
+
plugin does not support by design: every send-side feature, top-up invoices,
|
|
224
|
+
and a bare `nostr+walletconnect://` string in BTCPay's Lightning node screen.
|