openreceive 0.4.4__py3-none-any.whl
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.
- openreceive/__init__.py +27 -0
- openreceive/_generated/fulfillment_note.py +59 -0
- openreceive/_generated/tables.py +426 -0
- openreceive/_version.py +4 -0
- openreceive/cli.py +598 -0
- openreceive/django/__init__.py +13 -0
- openreceive/django/admin.py +59 -0
- openreceive/django/apps.py +21 -0
- openreceive/django/checks.py +75 -0
- openreceive/django/conf.py +203 -0
- openreceive/django/management/__init__.py +0 -0
- openreceive/django/management/commands/__init__.py +0 -0
- openreceive/django/management/commands/openreceive_doctor.py +70 -0
- openreceive/django/management/commands/openreceive_install.py +211 -0
- openreceive/django/management/commands/openreceive_notifications.py +62 -0
- openreceive/django/management/commands/openreceive_reconcile.py +20 -0
- openreceive/django/migrations/0001_initial.py +91 -0
- openreceive/django/migrations/__init__.py +0 -0
- openreceive/django/models.py +111 -0
- openreceive/django/repository.py +404 -0
- openreceive/django/static/openreceive/MANIFEST.json +20 -0
- openreceive/django/static/openreceive/openreceive-checkout.css +4 -0
- openreceive/django/static/openreceive/openreceive-checkout.js +628 -0
- openreceive/django/static/openreceive/openreceive-checkout.js.map +7 -0
- openreceive/django/urls.py +23 -0
- openreceive/django/views.py +129 -0
- openreceive/fastapi/__init__.py +10 -0
- openreceive/fastapi/binding.py +216 -0
- openreceive/fastapi/lifespan.py +69 -0
- openreceive/fastapi/requests.py +71 -0
- openreceive/fastapi/router.py +127 -0
- openreceive/money.py +76 -0
- openreceive/nwc/__init__.py +39 -0
- openreceive/nwc/client.py +39 -0
- openreceive/nwc/errors.py +196 -0
- openreceive/nwc/info.py +77 -0
- openreceive/nwc/receive_client.py +103 -0
- openreceive/nwc/requests.py +169 -0
- openreceive/nwc/transport/__init__.py +6 -0
- openreceive/nwc/transport/errors.py +25 -0
- openreceive/nwc/transport/nip01.py +86 -0
- openreceive/nwc/transport/nip04.py +34 -0
- openreceive/nwc/transport/nip44.py +94 -0
- openreceive/nwc/transport/receive_client.py +271 -0
- openreceive/nwc/transport/relay.py +90 -0
- openreceive/nwc/uri.py +105 -0
- openreceive/payments/__init__.py +10 -0
- openreceive/payments/reconciliation.py +49 -0
- openreceive/payments/scan.py +105 -0
- openreceive/rates/__init__.py +194 -0
- openreceive/rates/cached_feed.py +204 -0
- openreceive/rates/simple_price.py +69 -0
- openreceive/rates/static.py +26 -0
- openreceive/server/__init__.py +31 -0
- openreceive/server/app.py +208 -0
- openreceive/server/client_ip.py +67 -0
- openreceive/server/doctor.py +150 -0
- openreceive/server/errors.py +184 -0
- openreceive/server/handler.py +827 -0
- openreceive/server/hosts.py +84 -0
- openreceive/server/notifications.py +100 -0
- openreceive/server/rate_limit.py +76 -0
- openreceive/server/reconcile.py +351 -0
- openreceive/server/service.py +795 -0
- openreceive/settlement.py +48 -0
- openreceive/storage/__init__.py +49 -0
- openreceive/storage/repository.py +310 -0
- openreceive/storage/sql/__init__.py +16 -0
- openreceive/storage/sql/ddl.py +68 -0
- openreceive/storage/sql/repository.py +471 -0
- openreceive/storage/sql/tables.py +134 -0
- openreceive/swap/__init__.py +55 -0
- openreceive/swap/address.py +112 -0
- openreceive/swap/assets.py +46 -0
- openreceive/swap/base58.py +23 -0
- openreceive/swap/budget.py +94 -0
- openreceive/swap/cache.py +148 -0
- openreceive/swap/fixedfloat.py +873 -0
- openreceive/swap/http.py +53 -0
- openreceive/swap/keccak.py +81 -0
- openreceive/swap/lsc_uri.py +105 -0
- openreceive/swap/rates_feed.py +271 -0
- openreceive/swap/state.py +79 -0
- openreceive/testing/__init__.py +9 -0
- openreceive/testing/fake_swap_provider.py +251 -0
- openreceive/testing/fake_wallet.py +221 -0
- openreceive/testing/fixtures.py +42 -0
- openreceive/values.py +65 -0
- openreceive-0.4.4.dist-info/METADATA +77 -0
- openreceive-0.4.4.dist-info/RECORD +93 -0
- openreceive-0.4.4.dist-info/WHEEL +4 -0
- openreceive-0.4.4.dist-info/entry_points.txt +2 -0
- openreceive-0.4.4.dist-info/licenses/LICENSE +21 -0
openreceive/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""OpenReceive for Python: receive-only Lightning checkout over NWC.
|
|
2
|
+
|
|
3
|
+
The kernel (money, settlement, NIP-47 normalization, the wallet walk, the
|
|
4
|
+
closure decision, swaps) is pure functions over dicts; the server
|
|
5
|
+
(`openreceive.server`) binds it to a wallet client, a price feed and a payment
|
|
6
|
+
repository, and `openreceive.django` / `openreceive.fastapi` mount the
|
|
7
|
+
framework-free handler. This module re-exports only the kernel entry points
|
|
8
|
+
hosts reach for directly.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from openreceive._version import __version__
|
|
12
|
+
from openreceive.money import quote_fiat_to_msats
|
|
13
|
+
from openreceive.nwc.uri import NWC_CODE_HELP_URL, NwcUriParseError, parse_uri, redact_uri
|
|
14
|
+
from openreceive.settlement import is_settled
|
|
15
|
+
|
|
16
|
+
parse_nwc_uri = parse_uri
|
|
17
|
+
redact_nwc_uri = redact_uri
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"NWC_CODE_HELP_URL",
|
|
21
|
+
"NwcUriParseError",
|
|
22
|
+
"__version__",
|
|
23
|
+
"is_settled",
|
|
24
|
+
"parse_nwc_uri",
|
|
25
|
+
"quote_fiat_to_msats",
|
|
26
|
+
"redact_nwc_uri",
|
|
27
|
+
]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""GENERATED FILE — DO NOT EDIT.
|
|
2
|
+
|
|
3
|
+
Source: spec/data/fulfillment-note.txt (npm run generate:models).
|
|
4
|
+
Twins: packages/js/core/src/generated/fulfillment-note-text.ts, packages/ruby/openreceive-rails/lib/openreceive/generated/fulfillment_note.rb,
|
|
5
|
+
packages/php/openreceive/src/Generated/FulfillmentNote.php; all render the same text, so no host scaffold can
|
|
6
|
+
give different advice.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Final
|
|
10
|
+
|
|
11
|
+
# The note's lines, with "{{table}}" awaiting the caller's table name.
|
|
12
|
+
FULFILLMENT_NOTE_TEMPLATE: Final = (
|
|
13
|
+
"Fulfilling exactly once",
|
|
14
|
+
"",
|
|
15
|
+
"WHAT OPENRECEIVE GUARANTEES",
|
|
16
|
+
"",
|
|
17
|
+
"Across every settlement path OpenReceive itself owns (wallet notifications,",
|
|
18
|
+
"the opportunistic reconcile pass, an explicit reconcile job), the settlement",
|
|
19
|
+
"hook runs AT MOST ONCE per reference. The library serializes on its own",
|
|
20
|
+
"`{{table}}` rows, decides the winner there, and runs your hook",
|
|
21
|
+
"inside that same transaction. A second payment to a second invoice for the",
|
|
22
|
+
"same order is still recorded - with `status_reason = 'duplicate_settlement'`",
|
|
23
|
+
"- but never fulfills a second time. You do not need to add a lock for this.",
|
|
24
|
+
"",
|
|
25
|
+
"That makes the reference the unit of fulfillment: give every payable order",
|
|
26
|
+
"its own reference, created before checkout, kept across retries, and never",
|
|
27
|
+
"reused. A new checkout under a reference that has already settled is refused",
|
|
28
|
+
"with a 409 rather than fulfilled again; a fresh reference per page load",
|
|
29
|
+
"leaves one order payable twice.",
|
|
30
|
+
"",
|
|
31
|
+
"WHAT YOU MUST GUARANTEE",
|
|
32
|
+
"",
|
|
33
|
+
"OpenReceive cannot see fulfillment that happens outside it. If ANY other",
|
|
34
|
+
"path can also mark this order fulfilled - an admin action, a second payment",
|
|
35
|
+
"processor, a support tool, a replayed webhook, a retried background job -",
|
|
36
|
+
"then those paths race each other, not OpenReceive, and you must make",
|
|
37
|
+
"fulfillment idempotent yourself.",
|
|
38
|
+
"",
|
|
39
|
+
"The usual way is to make the transition itself the lock: guard it with a",
|
|
40
|
+
"conditional write that only one transaction can win.",
|
|
41
|
+
"",
|
|
42
|
+
" -- Idempotent by construction: the WHERE clause is the guard. Whoever",
|
|
43
|
+
" -- flips 'awaiting_payment' -> 'paid' first is the only one who fulfills;",
|
|
44
|
+
" -- every later attempt updates 0 rows and must do nothing.",
|
|
45
|
+
" UPDATE orders",
|
|
46
|
+
" SET state = 'paid', paid_at = :paid_at",
|
|
47
|
+
" WHERE id = :reference",
|
|
48
|
+
" AND state = 'awaiting_payment';",
|
|
49
|
+
" -- then: if 0 rows were affected, return without shipping anything.",
|
|
50
|
+
"",
|
|
51
|
+
"If your fulfillment is a read-modify-write that cannot be expressed as one",
|
|
52
|
+
"conditional UPDATE, take a row lock for the duration instead:",
|
|
53
|
+
"",
|
|
54
|
+
" SELECT * FROM orders WHERE id = :reference FOR UPDATE; -- postgres/mysql",
|
|
55
|
+
" -- ...check state, ship, write the new state, all before COMMIT.",
|
|
56
|
+
"",
|
|
57
|
+
"Run either one inside the transaction OpenReceive hands your settlement",
|
|
58
|
+
"hook, so the order transition and the payment record commit together.",
|
|
59
|
+
)
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"""GENERATED FILE — DO NOT EDIT.
|
|
2
|
+
|
|
3
|
+
Source: spec/data/kernel-tables.json, spec/data/swap-state-table.json,
|
|
4
|
+
spec/schemas/error.schema.json, the OpenAPI and AsyncAPI documents
|
|
5
|
+
(npm run generate:models). Twins: packages/ruby/openreceive/lib/openreceive/generated/tables.rb,
|
|
6
|
+
packages/dotnet/BTCPayServer.Plugins.OpenReceive/Generated/OpenReceiveTables.cs,
|
|
7
|
+
packages/php/openreceive/src/Generated/Tables.php.
|
|
8
|
+
Every engine reads the same vocabularies from its rendering, so none can drift.
|
|
9
|
+
|
|
10
|
+
The closed vocabularies and fixed numbers every OpenReceive engine shares, plus
|
|
11
|
+
the FixedFloat status decision table. Read these; never restate them.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Final
|
|
15
|
+
|
|
16
|
+
# The OpenAPI info.version this engine was built from.
|
|
17
|
+
HTTP_CONTRACT_VERSION: Final = "0.4.1"
|
|
18
|
+
|
|
19
|
+
# The AsyncAPI info.version this engine was built from.
|
|
20
|
+
EVENT_CONTRACT_VERSION: Final = "0.2.0"
|
|
21
|
+
|
|
22
|
+
ERROR_CODES: Final = (
|
|
23
|
+
"NOT_IMPLEMENTED",
|
|
24
|
+
"RESTRICTED",
|
|
25
|
+
"UNAUTHORIZED",
|
|
26
|
+
"FORBIDDEN",
|
|
27
|
+
"RATE_LIMITED",
|
|
28
|
+
"QUOTA_EXCEEDED",
|
|
29
|
+
"INTERNAL",
|
|
30
|
+
"UNSUPPORTED_ENCRYPTION",
|
|
31
|
+
"OTHER",
|
|
32
|
+
"NOT_FOUND",
|
|
33
|
+
"TIMEOUT",
|
|
34
|
+
"INVALID_REQUEST",
|
|
35
|
+
"WALLET_UNAVAILABLE",
|
|
36
|
+
"INVOICE_EXPIRED",
|
|
37
|
+
"UNSUPPORTED_METHOD",
|
|
38
|
+
"CONFLICT",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
RETRYABLE_ERROR_CODES: Final = (
|
|
42
|
+
"RATE_LIMITED",
|
|
43
|
+
"QUOTA_EXCEEDED",
|
|
44
|
+
"TIMEOUT",
|
|
45
|
+
"WALLET_UNAVAILABLE",
|
|
46
|
+
"INTERNAL",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
PAYMENT_STATUSES: Final = (
|
|
50
|
+
"pending",
|
|
51
|
+
"settled",
|
|
52
|
+
"expired",
|
|
53
|
+
"failed",
|
|
54
|
+
"not_found",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
PAYMENT_HASH_PATTERN: Final = "^[0-9a-f]{64}$"
|
|
58
|
+
|
|
59
|
+
MIN_AMOUNT_MSATS: Final = 1000
|
|
60
|
+
|
|
61
|
+
MAX_AMOUNT_MSATS: Final = 9007199254740991
|
|
62
|
+
|
|
63
|
+
NWC_REQUIRED_RECEIVE_METHODS: Final = (
|
|
64
|
+
"make_invoice",
|
|
65
|
+
"list_transactions",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
NWC_SPEND_METHODS: Final = (
|
|
69
|
+
"pay_invoice",
|
|
70
|
+
"multi_pay_invoice",
|
|
71
|
+
"pay_keysend",
|
|
72
|
+
"multi_pay_keysend",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Preference order: the first mode the wallet advertises wins.
|
|
76
|
+
NWC_ENCRYPTION_MODES: Final = (
|
|
77
|
+
"nip44_v2",
|
|
78
|
+
"nip04",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
NWC_NOTIFICATION_TYPES: Final = (
|
|
82
|
+
"payment_received",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
NWC_METADATA_MAX_BYTES: Final = 3900
|
|
86
|
+
|
|
87
|
+
# The page size every wallet-history walk requests.
|
|
88
|
+
TRANSACTION_PAGE_LIMIT: Final = 20
|
|
89
|
+
|
|
90
|
+
# Seconds past an attempt's expiry during which reconciliation still scans
|
|
91
|
+
# for a settlement before closing the attempt.
|
|
92
|
+
ATTEMPT_EXPIRY_GRACE_SECONDS: Final = 900
|
|
93
|
+
|
|
94
|
+
SWAP_PAY_IN_ASSETS: Final = (
|
|
95
|
+
"SOL_SOL",
|
|
96
|
+
"USDT_TRON",
|
|
97
|
+
"USDT_SOL",
|
|
98
|
+
"USDC_SOL",
|
|
99
|
+
"ETH_ETH",
|
|
100
|
+
"USDT_ETH",
|
|
101
|
+
"USDC_ETH",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
SWAP_ASSET_INFO: Final = {
|
|
105
|
+
"SOL_SOL": {
|
|
106
|
+
"pay_in_asset": "SOL_SOL",
|
|
107
|
+
"label": "SOL",
|
|
108
|
+
"network_label": "Solana",
|
|
109
|
+
"coin": "SOL",
|
|
110
|
+
"network": "SOL",
|
|
111
|
+
},
|
|
112
|
+
"USDT_TRON": {
|
|
113
|
+
"pay_in_asset": "USDT_TRON",
|
|
114
|
+
"label": "USDT",
|
|
115
|
+
"network_label": "Tron",
|
|
116
|
+
"coin": "USDT",
|
|
117
|
+
"network": "TRX",
|
|
118
|
+
},
|
|
119
|
+
"USDT_SOL": {
|
|
120
|
+
"pay_in_asset": "USDT_SOL",
|
|
121
|
+
"label": "USDT",
|
|
122
|
+
"network_label": "Solana",
|
|
123
|
+
"coin": "USDT",
|
|
124
|
+
"network": "SOL",
|
|
125
|
+
},
|
|
126
|
+
"USDC_SOL": {
|
|
127
|
+
"pay_in_asset": "USDC_SOL",
|
|
128
|
+
"label": "USDC",
|
|
129
|
+
"network_label": "Solana",
|
|
130
|
+
"coin": "USDC",
|
|
131
|
+
"network": "SOL",
|
|
132
|
+
},
|
|
133
|
+
"ETH_ETH": {
|
|
134
|
+
"pay_in_asset": "ETH_ETH",
|
|
135
|
+
"label": "ETH",
|
|
136
|
+
"network_label": "Ethereum",
|
|
137
|
+
"coin": "ETH",
|
|
138
|
+
"network": "ETH",
|
|
139
|
+
},
|
|
140
|
+
"USDT_ETH": {
|
|
141
|
+
"pay_in_asset": "USDT_ETH",
|
|
142
|
+
"label": "USDT",
|
|
143
|
+
"network_label": "Ethereum",
|
|
144
|
+
"coin": "USDT",
|
|
145
|
+
"network": "ETH",
|
|
146
|
+
},
|
|
147
|
+
"USDC_ETH": {
|
|
148
|
+
"pay_in_asset": "USDC_ETH",
|
|
149
|
+
"label": "USDC",
|
|
150
|
+
"network_label": "Ethereum",
|
|
151
|
+
"coin": "USDC",
|
|
152
|
+
"network": "ETH",
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# phase: coarse UI bucket; terminal: the attempt will not change again.
|
|
157
|
+
# "completed" is deliberately NOT terminal: provider completion is not wallet
|
|
158
|
+
# settlement.
|
|
159
|
+
SWAP_STATES: Final = {
|
|
160
|
+
"creating_provider_order": {
|
|
161
|
+
"phase": "preparing",
|
|
162
|
+
"terminal": False,
|
|
163
|
+
},
|
|
164
|
+
"awaiting_deposit": {
|
|
165
|
+
"phase": "awaiting_deposit",
|
|
166
|
+
"terminal": False,
|
|
167
|
+
},
|
|
168
|
+
"confirming": {
|
|
169
|
+
"phase": "processing",
|
|
170
|
+
"terminal": False,
|
|
171
|
+
},
|
|
172
|
+
"exchanging": {
|
|
173
|
+
"phase": "processing",
|
|
174
|
+
"terminal": False,
|
|
175
|
+
},
|
|
176
|
+
"paying_invoice": {
|
|
177
|
+
"phase": "processing",
|
|
178
|
+
"terminal": False,
|
|
179
|
+
},
|
|
180
|
+
"completed": {
|
|
181
|
+
"phase": "settling",
|
|
182
|
+
"terminal": False,
|
|
183
|
+
},
|
|
184
|
+
"expired": {
|
|
185
|
+
"phase": "terminal",
|
|
186
|
+
"terminal": True,
|
|
187
|
+
},
|
|
188
|
+
"refund_required": {
|
|
189
|
+
"phase": "refund",
|
|
190
|
+
"terminal": False,
|
|
191
|
+
},
|
|
192
|
+
"refund_pending": {
|
|
193
|
+
"phase": "refund",
|
|
194
|
+
"terminal": False,
|
|
195
|
+
},
|
|
196
|
+
"refunded": {
|
|
197
|
+
"phase": "terminal",
|
|
198
|
+
"terminal": True,
|
|
199
|
+
},
|
|
200
|
+
"attention": {
|
|
201
|
+
"phase": "attention",
|
|
202
|
+
"terminal": True,
|
|
203
|
+
},
|
|
204
|
+
"failed": {
|
|
205
|
+
"phase": "terminal",
|
|
206
|
+
"terminal": True,
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
SWAP_PROVIDER_STATES: Final = (
|
|
211
|
+
"creating_provider_order",
|
|
212
|
+
"awaiting_deposit",
|
|
213
|
+
"confirming",
|
|
214
|
+
"exchanging",
|
|
215
|
+
"paying_invoice",
|
|
216
|
+
"completed",
|
|
217
|
+
"expired",
|
|
218
|
+
"refund_required",
|
|
219
|
+
"refund_pending",
|
|
220
|
+
"refunded",
|
|
221
|
+
"attention",
|
|
222
|
+
"failed",
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
SWAP_ATTENTION_REASONS: Final = (
|
|
226
|
+
"provider_reported_emergency",
|
|
227
|
+
"provider_status_unrecognized",
|
|
228
|
+
"provider_completed_without_wallet_settlement",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
SWAP_REFUND_REASONS: Final = (
|
|
232
|
+
"underpaid",
|
|
233
|
+
"overpaid",
|
|
234
|
+
"late_deposit",
|
|
235
|
+
"underpaid_and_late",
|
|
236
|
+
"overpaid_and_late",
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
SWAP_AVAILABILITY_REASONS: Final = (
|
|
240
|
+
"provider_unconfigured",
|
|
241
|
+
"amount_too_small",
|
|
242
|
+
"amount_too_large",
|
|
243
|
+
"pair_temporarily_unavailable",
|
|
244
|
+
"region_unsupported",
|
|
245
|
+
"provider_rate_limited",
|
|
246
|
+
"provider_unreachable",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# spec/data/swap-state-table.json: ordered, first-match-wins; the last row is a
|
|
250
|
+
# catch-all. "status" is the upper-cased provider status or "*" (narrowed by
|
|
251
|
+
# "status_contains"); "refund_tx_present" is True, False or "any"; "choice" is
|
|
252
|
+
# the upper-cased emergency choice, "absent" or "any". A non-None
|
|
253
|
+
# "attention_reason" means the result also carries attention: True. Pinned by
|
|
254
|
+
# spec/test-vectors/swap-state.json; how to read it lives once, in the JSON's
|
|
255
|
+
# how_to_read.
|
|
256
|
+
SWAP_STATUS_ROWS: Final = (
|
|
257
|
+
{
|
|
258
|
+
"status": "DONE",
|
|
259
|
+
"status_contains": None,
|
|
260
|
+
"refund_tx_present": True,
|
|
261
|
+
"choice": "any",
|
|
262
|
+
"state": "refunded",
|
|
263
|
+
"attention_reason": None,
|
|
264
|
+
"refund_reason_from_emergency": False,
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"status": "FINISHED",
|
|
268
|
+
"status_contains": None,
|
|
269
|
+
"refund_tx_present": True,
|
|
270
|
+
"choice": "any",
|
|
271
|
+
"state": "refunded",
|
|
272
|
+
"attention_reason": None,
|
|
273
|
+
"refund_reason_from_emergency": False,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"status": "NEW",
|
|
277
|
+
"status_contains": None,
|
|
278
|
+
"refund_tx_present": "any",
|
|
279
|
+
"choice": "any",
|
|
280
|
+
"state": "awaiting_deposit",
|
|
281
|
+
"attention_reason": None,
|
|
282
|
+
"refund_reason_from_emergency": False,
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"status": "PENDING",
|
|
286
|
+
"status_contains": None,
|
|
287
|
+
"refund_tx_present": "any",
|
|
288
|
+
"choice": "any",
|
|
289
|
+
"state": "confirming",
|
|
290
|
+
"attention_reason": None,
|
|
291
|
+
"refund_reason_from_emergency": False,
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"status": "EXCHANGE",
|
|
295
|
+
"status_contains": None,
|
|
296
|
+
"refund_tx_present": "any",
|
|
297
|
+
"choice": "any",
|
|
298
|
+
"state": "exchanging",
|
|
299
|
+
"attention_reason": None,
|
|
300
|
+
"refund_reason_from_emergency": False,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"status": "WITHDRAW",
|
|
304
|
+
"status_contains": None,
|
|
305
|
+
"refund_tx_present": "any",
|
|
306
|
+
"choice": "any",
|
|
307
|
+
"state": "paying_invoice",
|
|
308
|
+
"attention_reason": None,
|
|
309
|
+
"refund_reason_from_emergency": False,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"status": "DONE",
|
|
313
|
+
"status_contains": None,
|
|
314
|
+
"refund_tx_present": "any",
|
|
315
|
+
"choice": "any",
|
|
316
|
+
"state": "completed",
|
|
317
|
+
"attention_reason": None,
|
|
318
|
+
"refund_reason_from_emergency": False,
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"status": "EXPIRED",
|
|
322
|
+
"status_contains": None,
|
|
323
|
+
"refund_tx_present": "any",
|
|
324
|
+
"choice": "any",
|
|
325
|
+
"state": "expired",
|
|
326
|
+
"attention_reason": None,
|
|
327
|
+
"refund_reason_from_emergency": False,
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
"status": "EMERGENCY",
|
|
331
|
+
"status_contains": None,
|
|
332
|
+
"refund_tx_present": True,
|
|
333
|
+
"choice": "REFUND",
|
|
334
|
+
"state": "refunded",
|
|
335
|
+
"attention_reason": None,
|
|
336
|
+
"refund_reason_from_emergency": True,
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"status": "EMERGENCY",
|
|
340
|
+
"status_contains": None,
|
|
341
|
+
"refund_tx_present": "any",
|
|
342
|
+
"choice": "REFUND",
|
|
343
|
+
"state": "refund_pending",
|
|
344
|
+
"attention_reason": None,
|
|
345
|
+
"refund_reason_from_emergency": True,
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
"status": "EMERGENCY",
|
|
349
|
+
"status_contains": None,
|
|
350
|
+
"refund_tx_present": "any",
|
|
351
|
+
"choice": "EXCHANGE",
|
|
352
|
+
"state": "attention",
|
|
353
|
+
"attention_reason": "provider_reported_emergency",
|
|
354
|
+
"refund_reason_from_emergency": False,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"status": "EMERGENCY",
|
|
358
|
+
"status_contains": None,
|
|
359
|
+
"refund_tx_present": "any",
|
|
360
|
+
"choice": "any",
|
|
361
|
+
"state": "refund_required",
|
|
362
|
+
"attention_reason": None,
|
|
363
|
+
"refund_reason_from_emergency": True,
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"status": "*",
|
|
367
|
+
"status_contains": "FAIL",
|
|
368
|
+
"refund_tx_present": "any",
|
|
369
|
+
"choice": "any",
|
|
370
|
+
"state": "failed",
|
|
371
|
+
"attention_reason": None,
|
|
372
|
+
"refund_reason_from_emergency": False,
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"status": "*",
|
|
376
|
+
"status_contains": None,
|
|
377
|
+
"refund_tx_present": "any",
|
|
378
|
+
"choice": "any",
|
|
379
|
+
"state": "attention",
|
|
380
|
+
"attention_reason": "provider_status_unrecognized",
|
|
381
|
+
"refund_reason_from_emergency": False,
|
|
382
|
+
},
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Emergency status spellings folded onto their canonical name before matching.
|
|
386
|
+
SWAP_EMERGENCY_STATUS_ALIASES: Final = {
|
|
387
|
+
"OVER": "MORE",
|
|
388
|
+
"OVERPAID": "MORE",
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
# Ordered; a row matches when every "all_of" status is present. No match, no
|
|
392
|
+
# refund_reason.
|
|
393
|
+
SWAP_REFUND_REASON_ROWS: Final = (
|
|
394
|
+
{
|
|
395
|
+
"all_of": (
|
|
396
|
+
"LESS",
|
|
397
|
+
"EXPIRED",
|
|
398
|
+
),
|
|
399
|
+
"refund_reason": "underpaid_and_late",
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
"all_of": (
|
|
403
|
+
"MORE",
|
|
404
|
+
"EXPIRED",
|
|
405
|
+
),
|
|
406
|
+
"refund_reason": "overpaid_and_late",
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"all_of": (
|
|
410
|
+
"LESS",
|
|
411
|
+
),
|
|
412
|
+
"refund_reason": "underpaid",
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"all_of": (
|
|
416
|
+
"MORE",
|
|
417
|
+
),
|
|
418
|
+
"refund_reason": "overpaid",
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
"all_of": (
|
|
422
|
+
"EXPIRED",
|
|
423
|
+
),
|
|
424
|
+
"refund_reason": "late_deposit",
|
|
425
|
+
},
|
|
426
|
+
)
|
openreceive/_version.py
ADDED