@openreceive/http 0.4.10 → 0.4.12
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/README.md +4 -2
- package/dist/{adapter-surface-DHngkTKQ.d.ts → adapter-surface-Cs4g29Ez.d.ts} +95 -54
- package/dist/adapter-surface.d.ts +1 -1
- package/dist/adapter-surface.js +1 -1
- package/dist/{chunk-TJY2HBVU.js → chunk-PGZAFYN6.js} +440 -88
- package/dist/index.d.ts +11 -8
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/skills/integrate-openreceive/SKILL.md +4 -3
- package/skills/integrate-openreceive/references/btcpay.md +67 -36
- package/skills/integrate-openreceive/references/django.md +291 -244
- package/skills/integrate-openreceive/references/fastapi.md +206 -155
- package/skills/integrate-openreceive/references/fastify.md +200 -147
- package/skills/integrate-openreceive/references/laravel.md +294 -234
- package/skills/integrate-openreceive/references/next.md +210 -162
- package/skills/integrate-openreceive/references/node.md +188 -138
- package/skills/integrate-openreceive/references/php.md +248 -193
- package/skills/integrate-openreceive/references/rails.md +262 -218
- package/skills/integrate-openreceive/references/woocommerce.md +68 -51
package/README.md
CHANGED
|
@@ -38,8 +38,10 @@ write-once settlement with first-attempt-only fulfillment, and the
|
|
|
38
38
|
machine (`attention` reads as `pending` on the wire; operators see it only in
|
|
39
39
|
`openreceive_payments.status`). `onPaid` is the settlement hook in both modes: with `db` it receives
|
|
40
40
|
`PaymentSettlement` (`reference` plus a `query` that runs inside the
|
|
41
|
-
settlement transaction); with a custom repository it receives
|
|
42
|
-
`SettlementEvent
|
|
41
|
+
settlement transaction); with a custom repository it receives
|
|
42
|
+
`SettlementEvent<Transaction>` (`reference`, `paymentHash`, `paidAt`, `details`,
|
|
43
|
+
and `transaction`). The repository awaits fulfillment before commit; failure
|
|
44
|
+
rolls back both payment and host writes. Settlement
|
|
43
45
|
piggybacks on mounted routes by default through the durable `openreceive_meta`
|
|
44
46
|
gate (`opportunisticReconcile: false` disables, `{ minIntervalSeconds }`
|
|
45
47
|
tunes); `startNotificationWorker` is the optional
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Checkout, SwapData, NodeSettlementActionHook, CreateCheckoutAmount,
|
|
2
|
-
import { ErrorBody, ErrorCode, TransactionSettlementStatus, PaymentDetails } from '@openreceive/core';
|
|
1
|
+
import { Checkout, SwapData, NodeSettlementActionHook, CreateCheckoutAmount, OpenReceive } from '@openreceive/node';
|
|
2
|
+
import { ErrorBody, ErrorCode, TransactionSettlementStatus, PaymentDetails, PaymentScanWindow } from '@openreceive/core';
|
|
3
3
|
|
|
4
4
|
type AuthorizeAction = "checkout.prepare" | "checkout.create" | "payment.check" | "swap.quote" | "swap.create" | "swap.read" | "swap.refund";
|
|
5
5
|
/**
|
|
@@ -152,7 +152,9 @@ interface ReconcilableAttempt {
|
|
|
152
152
|
readonly paymentHash: string;
|
|
153
153
|
/** Exact NIP-47 invoice creation time returned by make_invoice. */
|
|
154
154
|
readonly createdAt: number;
|
|
155
|
-
|
|
155
|
+
readonly createdAtSource?: "wallet" | "host";
|
|
156
|
+
/** Actual wallet invoice deadline from the saved checkout, never the deposit deadline.
|
|
157
|
+
* Closure also requires a covering scan after this deadline plus the expiry grace. */
|
|
156
158
|
readonly expiresAt: number;
|
|
157
159
|
}
|
|
158
160
|
/** A terminal (non-settled) state transition observed by reconciliation. */
|
|
@@ -185,19 +187,38 @@ interface SettlementRecord {
|
|
|
185
187
|
* `recordReconciliation` must apply the transition only while the row is still
|
|
186
188
|
* `pending` — it must never overwrite a settled attempt.
|
|
187
189
|
*
|
|
188
|
-
* `
|
|
189
|
-
*
|
|
190
|
-
* call reports the claim won, so a redelivered settlement fulfills once.
|
|
190
|
+
* `recordSettlementWithFulfillment` locks the reference, records settlement,
|
|
191
|
+
* and awaits host fulfillment in one transaction. Failure rolls back both writes.
|
|
191
192
|
*/
|
|
192
|
-
interface
|
|
193
|
+
interface SettlementContext<Transaction = unknown> extends SettlementRecord {
|
|
194
|
+
readonly reference: string;
|
|
195
|
+
/** Handle owned by the settlement transaction; never a root connection. */
|
|
196
|
+
readonly transaction: Transaction;
|
|
197
|
+
}
|
|
198
|
+
interface ReconcileCursor {
|
|
199
|
+
readonly created_at: number;
|
|
200
|
+
readonly payment_hash: string;
|
|
201
|
+
}
|
|
202
|
+
interface ReconcileScheduler {
|
|
203
|
+
cursor: ReconcileCursor | null;
|
|
204
|
+
windows: PaymentScanWindow[];
|
|
205
|
+
}
|
|
206
|
+
interface ReconcileGateClaim {
|
|
207
|
+
readonly token: string;
|
|
208
|
+
readonly scheduler: ReconcileScheduler;
|
|
209
|
+
}
|
|
210
|
+
interface PaymentRepository<Transaction = unknown> {
|
|
193
211
|
listForReference(reference: string): Promise<readonly PaymentRecord[]>;
|
|
212
|
+
/** Durable acknowledgment for settlement outcomes, including concurrent writes. */
|
|
213
|
+
findByPaymentHash(paymentHash: string): Promise<PaymentRecord | undefined>;
|
|
194
214
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
215
|
+
* A bounded keyset page of `pending` attempts ordered by createdAt and hash,
|
|
216
|
+
* strictly after the supplied cursor; terminal rows are excluded. Return at
|
|
217
|
+
* OPENRECEIVE_RECONCILE_BATCH_SIZE (200) when that many remain, otherwise
|
|
218
|
+
* all remaining rows. A short page signals exhaustion; the durable scheduler
|
|
219
|
+
* wraps immediately so new arrivals cannot postpone old fulfillment retries.
|
|
199
220
|
*/
|
|
200
|
-
listReconcilableAttempts(): Promise<readonly ReconcilableAttempt[]>;
|
|
221
|
+
listReconcilableAttempts(after?: ReconcileCursor | null): Promise<readonly ReconcilableAttempt[]>;
|
|
201
222
|
/**
|
|
202
223
|
* The `pending` attempt for one payment hash, or undefined when the hash is
|
|
203
224
|
* unknown or already terminal. Direct settlement from an NWC notification
|
|
@@ -210,14 +231,15 @@ interface PaymentRepository {
|
|
|
210
231
|
commitAttempt(input: CheckoutCreatedInput): void | Promise<void>;
|
|
211
232
|
recordReconciliation(transition: ReconciliationTransition): void | Promise<void>;
|
|
212
233
|
/**
|
|
213
|
-
*
|
|
234
|
+
* Record this pending attempt's settlement under the reference lock, await
|
|
235
|
+
* fulfillment for the first settled sibling, then commit both writes.
|
|
214
236
|
* Returns true only for the call that won the claim — the attempt was still
|
|
215
237
|
* unsettled AND no sibling attempt on the order had settled. Later calls for
|
|
216
238
|
* the same or a sibling attempt must record the settlement (a genuine second
|
|
217
239
|
* payment is not discarded) and return false. A settled attempt is never
|
|
218
240
|
* overwritten, and an unknown payment hash is a no-op returning false.
|
|
219
241
|
*/
|
|
220
|
-
|
|
242
|
+
recordSettlementWithFulfillment(settlement: SettlementRecord, fulfill: (context: SettlementContext<Transaction>) => void | Promise<void>): boolean | Promise<boolean>;
|
|
221
243
|
/**
|
|
222
244
|
* Count attempt rows recorded for this client IP at or after `sinceUnixSeconds`.
|
|
223
245
|
* Backs the handler's opt-in `rateLimiting` option; when a custom repository
|
|
@@ -226,9 +248,9 @@ interface PaymentRepository {
|
|
|
226
248
|
*/
|
|
227
249
|
countAttemptsFromIp?(clientIp: string, sinceUnixSeconds: number): number | Promise<number>;
|
|
228
250
|
/**
|
|
229
|
-
* Claim the durable global reconcile gate: return
|
|
230
|
-
*
|
|
231
|
-
*
|
|
251
|
+
* Claim the durable global reconcile gate: return a token and scheduler when
|
|
252
|
+
* this caller owns the lease, or null while another lease/interval is active
|
|
253
|
+
* (`gate_busy`). Persist progress only through the matching unexpired token. The claim MUST be a durable compare-and-set
|
|
232
254
|
* shared by every process on the host database (the built-in SQL repository
|
|
233
255
|
* uses the `openreceive_meta` key/value/rev table) — process-local memory
|
|
234
256
|
* cannot coordinate multiple workers and must never back this. Backs the
|
|
@@ -239,6 +261,14 @@ interface PaymentRepository {
|
|
|
239
261
|
claimReconcileGate?(input: {
|
|
240
262
|
readonly now: number;
|
|
241
263
|
readonly intervalSeconds: number;
|
|
264
|
+
readonly leaseSeconds?: number;
|
|
265
|
+
}): ReconcileGateClaim | null | Promise<ReconcileGateClaim | null>;
|
|
266
|
+
checkpointReconcileGate?(input: {
|
|
267
|
+
readonly claim: ReconcileGateClaim;
|
|
268
|
+
readonly scheduler: ReconcileScheduler;
|
|
269
|
+
readonly now: number;
|
|
270
|
+
readonly release?: boolean;
|
|
271
|
+
readonly intervalSeconds?: number;
|
|
242
272
|
}): boolean | Promise<boolean>;
|
|
243
273
|
}
|
|
244
274
|
|
|
@@ -328,7 +358,33 @@ interface PaymentSettlement {
|
|
|
328
358
|
readonly query: SqlQuery;
|
|
329
359
|
}
|
|
330
360
|
type PaymentSettlementHook = (settlement: PaymentSettlement) => void | Promise<void>;
|
|
331
|
-
|
|
361
|
+
/** Nonsecret dry-run report for explicit host/operator review. */
|
|
362
|
+
interface PaymentRepairCandidate {
|
|
363
|
+
readonly paymentHash: string;
|
|
364
|
+
readonly reference: string;
|
|
365
|
+
readonly status: "expired" | "attention";
|
|
366
|
+
readonly statusReason: string | null;
|
|
367
|
+
readonly updatedAt: number;
|
|
368
|
+
readonly instructionExpiresAt: number;
|
|
369
|
+
readonly settlementExpiresAt: number;
|
|
370
|
+
readonly category: "early_swap_closure" | "attention";
|
|
371
|
+
}
|
|
372
|
+
interface SqlPaymentRepository extends PaymentRepository<SqlClient> {
|
|
373
|
+
/** Read-only, hash-keyset candidate report; normal routes never call this. */
|
|
374
|
+
listRepairCandidates(input?: {
|
|
375
|
+
after?: string;
|
|
376
|
+
limit?: number;
|
|
377
|
+
}): Promise<{
|
|
378
|
+
candidates: readonly PaymentRepairCandidate[];
|
|
379
|
+
nextCursor: string | null;
|
|
380
|
+
}>;
|
|
381
|
+
/** Requeue one reviewed row only if its reported status and version still match. */
|
|
382
|
+
requeueAttempt(input: {
|
|
383
|
+
paymentHash: string;
|
|
384
|
+
expectedStatus: "expired" | "attention";
|
|
385
|
+
expectedUpdatedAt: number;
|
|
386
|
+
reason: string;
|
|
387
|
+
}): Promise<boolean>;
|
|
332
388
|
/**
|
|
333
389
|
* Replay-safe settlement transaction: set the attempt's `paid_at`/`settled`
|
|
334
390
|
* status once, and run `fulfill` inside the same transaction only for the
|
|
@@ -378,35 +434,18 @@ interface CreateHostDbOptions extends CreateOpenReceiveHostBaseOptions {
|
|
|
378
434
|
readonly onPaid: PaymentSettlementHook;
|
|
379
435
|
readonly payments?: never;
|
|
380
436
|
}
|
|
381
|
-
/**
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Advanced escape hatch: the host implements the full
|
|
391
|
-
* `PaymentRepository` contract, including commit locking, write-once
|
|
392
|
-
* settlement, and reconciliation transitions.
|
|
393
|
-
*
|
|
394
|
-
* The settlement hook is `onPaid` in this mode too, but its context type
|
|
395
|
-
* differs from db mode: it receives the raw {@link SettlementEvent}
|
|
396
|
-
* (`paymentHash`, `paidAt`, `details`), with no `reference` and no transactional
|
|
397
|
-
* `query` — unlike db-mode `onPaid`, which runs inside the library's settlement
|
|
398
|
-
* transaction. Write-once is still the library's: repository-mode `onPaid`
|
|
399
|
-
* runs only for the settlement whose `payments.recordSettlement` claim was
|
|
400
|
-
* won, so a redelivered settlement never fulfills twice.
|
|
401
|
-
*/
|
|
402
|
-
interface CreateHostRepositoryOptions extends CreateOpenReceiveHostBaseOptions {
|
|
403
|
-
readonly payments: PaymentRepository;
|
|
404
|
-
/** Host settlement handler; runs once, for the winning first-settlement claim. */
|
|
405
|
-
readonly onPaid: SettlementEventHook;
|
|
437
|
+
/** Custom fulfillment receives the resolved reference and its transaction handle. */
|
|
438
|
+
type SettlementEvent<Transaction = unknown> = SettlementContext<Transaction>;
|
|
439
|
+
type SettlementEventHook<Transaction = unknown> = (settlement: SettlementEvent<Transaction>) => void | Promise<void>;
|
|
440
|
+
/** The custom repository awaits onPaid before committing; any failure rolls back both writes. */
|
|
441
|
+
interface CreateHostRepositoryOptions<Transaction = unknown> extends CreateOpenReceiveHostBaseOptions {
|
|
442
|
+
readonly payments: PaymentRepository<Transaction>;
|
|
443
|
+
/** Awaited before the winning first-reference settlement transaction commits. */
|
|
444
|
+
readonly onPaid: SettlementEventHook<Transaction>;
|
|
406
445
|
readonly db?: never;
|
|
407
446
|
readonly tableName?: never;
|
|
408
447
|
}
|
|
409
|
-
type CreateHostOptions = CreateHostDbOptions | CreateHostRepositoryOptions
|
|
448
|
+
type CreateHostOptions<Transaction = unknown> = CreateHostDbOptions | CreateHostRepositoryOptions<Transaction>;
|
|
410
449
|
interface Host {
|
|
411
450
|
readonly resolveCheckout: ResolveCheckoutHook;
|
|
412
451
|
readonly onCheckoutCreated: CheckoutCreatedHook;
|
|
@@ -420,7 +459,7 @@ interface Host {
|
|
|
420
459
|
* settlement write-once, and reconciliation transitions are library-owned in
|
|
421
460
|
* `db` mode.
|
|
422
461
|
*/
|
|
423
|
-
declare function createHost(options: CreateHostOptions): Host;
|
|
462
|
+
declare function createHost<Transaction = unknown>(options: CreateHostOptions<Transaction>): Host;
|
|
424
463
|
/**
|
|
425
464
|
* What the payer is buying, in the host's own words — one optional display
|
|
426
465
|
* string returned beside the price by `amountFor`. It is peeled off here so the
|
|
@@ -528,8 +567,10 @@ interface ResolvedHostCheckout {
|
|
|
528
567
|
* What the payer is buying, in the host's own words — one display string,
|
|
529
568
|
* echoed on the prepare and create responses so the shipped checkout can show
|
|
530
569
|
* something other than a QR and a number. OpenReceive owns no line items and
|
|
531
|
-
* never will; this is the whole of what it carries.
|
|
532
|
-
*
|
|
570
|
+
* never will; this is the whole of what it carries. Never read from a request
|
|
571
|
+
* body: the payer does not write the copy next to the amount. On create it is
|
|
572
|
+
* also the default invoice memo, so the payer's wallet shows the same words
|
|
573
|
+
* the checkout did.
|
|
533
574
|
*/
|
|
534
575
|
readonly description?: string;
|
|
535
576
|
/** Return the selected host payment attempt's hash to reuse or inspect its checkout. */
|
|
@@ -697,14 +738,14 @@ type StackWallet = {
|
|
|
697
738
|
* Where attempts live, which decides what `onPaid` receives. With the host
|
|
698
739
|
* database handle (`db`, the default mode) it is the per-reference
|
|
699
740
|
* `PaymentSettlement`, with `reference` and the transactional `query`;
|
|
700
|
-
* with a custom repository (`payments`, advanced) it
|
|
701
|
-
*
|
|
741
|
+
* with a custom repository (`payments`, advanced) it includes the resolved
|
|
742
|
+
* reference and the repository transaction handle. The branch carries the hook's type, so the
|
|
702
743
|
* wrong signature is a type error rather than a runtime surprise.
|
|
703
744
|
*/
|
|
704
|
-
type StackStorage = Pick<CreateHostDbOptions, "db" | "tableName" | "onPaid" | "payments"> | Pick<CreateHostRepositoryOptions
|
|
705
|
-
interface CreateStackOptions extends Omit<CreateHttpHandlerOptions, "service" | "host">, Omit<CreateHostDbOptions, "db" | "tableName" | "onPaid" | "payments"> {
|
|
745
|
+
type StackStorage<Transaction = unknown> = Pick<CreateHostDbOptions, "db" | "tableName" | "onPaid" | "payments"> | Pick<CreateHostRepositoryOptions<Transaction>, "payments" | "onPaid" | "db" | "tableName">;
|
|
746
|
+
interface CreateStackOptions<Transaction = unknown> extends Omit<CreateHttpHandlerOptions, "service" | "host">, Omit<CreateHostDbOptions, "db" | "tableName" | "onPaid" | "payments"> {
|
|
706
747
|
readonly wallet: StackWallet;
|
|
707
|
-
readonly storage: StackStorage
|
|
748
|
+
readonly storage: StackStorage<Transaction>;
|
|
708
749
|
/**
|
|
709
750
|
* Where the one boot-failure line goes. Boot happens before any service
|
|
710
751
|
* exists, so this is the only sink @openreceive/http can offer; it defaults
|
|
@@ -725,7 +766,7 @@ interface Stack {
|
|
|
725
766
|
/** Closes the service if the stack created it. */
|
|
726
767
|
close(): Promise<void>;
|
|
727
768
|
}
|
|
728
|
-
declare function createStack(options: CreateStackOptions): Stack;
|
|
769
|
+
declare function createStack<Transaction = unknown>(options: CreateStackOptions<Transaction>): Stack;
|
|
729
770
|
/**
|
|
730
771
|
* True when adapter options are the flat all-in-one form (no prebuilt `host`).
|
|
731
772
|
* The composed form always carries `host`; the flat form carries the host
|
|
@@ -735,4 +776,4 @@ declare function createStack(options: CreateStackOptions): Stack;
|
|
|
735
776
|
*/
|
|
736
777
|
declare function isStackOptions(options: CreateHttpHandlerOptions | CreateStackOptions): options is CreateStackOptions;
|
|
737
778
|
|
|
738
|
-
export {
|
|
779
|
+
export { createProxyRateLimitingConfig as $, type AttemptStatus as A, type ResolveCheckoutHook as B, type CheckoutCreatedHook as C, type ResolvedHostCheckout as D, type ServiceErrorShape as E, type SettlementContext as F, type SettlementEvent as G, type Host as H, type IpRateLimitConfig as I, type SettlementEventHook as J, type SettlementRecord as K, type SqlClient as L, type SqlDatabase as M, type NotificationListener as N, OPENRECEIVE_RECONCILE_BATCH_SIZE as O, type PaymentInsert as P, type SqlPaymentRepository as Q, type ReconcilableAttempt as R, type SqlAdapter as S, type SqlPaymentsOptions as T, type SqlQuery as U, type Stack as V, type StackStorage as W, type StackWallet as X, createHost as Y, createHttpHandler as Z, createIpRateLimit as _, type Authorize as a, createRequestId as a0, createSqlPayments as a1, createStack as a2, errorResponse as a3, hostError as a4, isServiceErrorShape as a5, isStackOptions as a6, jsonResponse as a7, mapHostRouteError as a8, paymentsSchemaSql as a9, resolveClientIp as aa, startNotificationListener as ab, startNotificationWorker as ac, type AuthorizeAction as b, type AuthorizeContext as c, type AuthorizeResource as d, type CheckoutCreatedInput as e, type CreateHostDbOptions as f, type CreateHostOptions as g, type CreateHostRepositoryOptions as h, type CreateHttpHandlerOptions as i, type CreateStackOptions as j, type HostCheckoutPrice as k, HostError as l, HttpError as m, type HttpHandler as n, type NotificationWorker as o, type PaymentRecord as p, type PaymentRepairCandidate as q, type PaymentRepository as r, type PaymentSettlement as s, type PaymentSettlementHook as t, type RateLimit as u, type ReconcileCursor as v, type ReconcileGateClaim as w, type ReconcileScheduler as x, type ReconciliationTransition as y, type ResolveCheckoutContext as z };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Checkout, CreateCheckoutAmount, OpenReceive, PaymentCheck, SwapCheckout } from '@openreceive/node';
|
|
2
|
-
export { a as Authorize, b as AuthorizeAction, c as AuthorizeContext, d as AuthorizeResource, C as CheckoutCreatedHook, e as CheckoutCreatedInput, i as CreateHttpHandlerOptions, j as CreateStackOptions, H as Host, l as HostError, m as HttpError, n as HttpHandler, I as IpRateLimitConfig, o as NotificationWorker,
|
|
2
|
+
export { a as Authorize, b as AuthorizeAction, c as AuthorizeContext, d as AuthorizeResource, C as CheckoutCreatedHook, e as CheckoutCreatedInput, i as CreateHttpHandlerOptions, j as CreateStackOptions, H as Host, l as HostError, m as HttpError, n as HttpHandler, I as IpRateLimitConfig, o as NotificationWorker, r as PaymentRepository, s as PaymentSettlement, t as PaymentSettlementHook, u as RateLimit, z as ResolveCheckoutContext, B as ResolveCheckoutHook, D as ResolvedHostCheckout, E as ServiceErrorShape, G as SettlementEvent, J as SettlementEventHook, V as Stack, W as StackStorage, X as StackWallet, Z as createHttpHandler, a2 as createStack, a4 as hostError, a5 as isServiceErrorShape, a8 as mapHostRouteError, ac as startNotificationWorker } from './adapter-surface-Cs4g29Ez.js';
|
|
3
3
|
import '@openreceive/core';
|