@happyvertical/smrt-commerce 0.51.32 → 0.51.34

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.
Files changed (79) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +88 -0
  3. package/dist/__tests__/foreign-key-fixtures.d.ts +13 -0
  4. package/dist/__tests__/foreign-key-fixtures.d.ts.map +1 -0
  5. package/dist/__tests__/helpers/anchor-fixture.d.ts +36 -0
  6. package/dist/__tests__/helpers/anchor-fixture.d.ts.map +1 -0
  7. package/dist/__tests__/helpers/billing-fixture.d.ts +51 -0
  8. package/dist/__tests__/helpers/billing-fixture.d.ts.map +1 -0
  9. package/dist/__tests__/helpers/consumer-license-sale.d.ts +13 -0
  10. package/dist/__tests__/helpers/consumer-license-sale.d.ts.map +1 -0
  11. package/dist/__tests__/helpers/crypto-rail.d.ts +69 -0
  12. package/dist/__tests__/helpers/crypto-rail.d.ts.map +1 -0
  13. package/dist/__tests__/helpers/fake-stripe.d.ts +147 -0
  14. package/dist/__tests__/helpers/fake-stripe.d.ts.map +1 -0
  15. package/dist/billing/btcpay.d.ts +40 -0
  16. package/dist/billing/btcpay.d.ts.map +1 -0
  17. package/dist/billing/cards.d.ts +14 -4
  18. package/dist/billing/credits.d.ts +7 -2
  19. package/dist/billing/credits.d.ts.map +1 -1
  20. package/dist/billing/crypto.d.ts +32 -0
  21. package/dist/billing/crypto.d.ts.map +1 -0
  22. package/dist/billing/cycles.d.ts +21 -1
  23. package/dist/billing/events.d.ts +23 -4
  24. package/dist/billing/events.d.ts.map +1 -1
  25. package/dist/billing/index.d.ts +5 -1
  26. package/dist/billing/index.d.ts.map +1 -1
  27. package/dist/billing/invoice-payments.d.ts +29 -0
  28. package/dist/billing/invoice-payments.d.ts.map +1 -0
  29. package/dist/billing/jobs.d.ts +3 -3
  30. package/dist/billing/payment-attempts.d.ts +143 -0
  31. package/dist/billing/payment-attempts.d.ts.map +1 -0
  32. package/dist/billing/period-close.d.ts +1 -1
  33. package/dist/billing/provider.d.ts +109 -3
  34. package/dist/billing/provider.d.ts.map +1 -1
  35. package/dist/billing/runtime.d.ts +122 -14
  36. package/dist/billing/runtime.d.ts.map +1 -1
  37. package/dist/billing/stripe.d.ts +16 -2
  38. package/dist/billing/stripe.d.ts.map +1 -1
  39. package/dist/billing/top-up.d.ts +4 -4
  40. package/dist/collections/ContractCollection.d.ts +4 -0
  41. package/dist/collections/ContractLineItemCollection.d.ts +4 -0
  42. package/dist/collections/CustomerCollection.d.ts +4 -0
  43. package/dist/collections/FulfillmentCollection.d.ts +5 -1
  44. package/dist/collections/FulfillmentLineItemCollection.d.ts +4 -0
  45. package/dist/collections/InvoiceCollection.d.ts +4 -0
  46. package/dist/collections/InvoiceLineItemCollection.d.ts +5 -1
  47. package/dist/collections/PaymentAllocationCollection.d.ts +4 -0
  48. package/dist/collections/PaymentCollection.d.ts +5 -1
  49. package/dist/collections/PaymentInstrumentCollection.d.ts +4 -0
  50. package/dist/collections/PaymentIntentCollection.d.ts +12 -1
  51. package/dist/collections/PayoutCollection.d.ts +12 -1
  52. package/dist/collections/VendorCollection.d.ts +4 -0
  53. package/dist/index.d.ts +64 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4055 -2859
  56. package/dist/index.js.map +1 -1
  57. package/dist/manifest.json +920 -91
  58. package/dist/migrations/moneyMinorUnits.d.ts +61 -1
  59. package/dist/models/Contract.d.ts +6 -2
  60. package/dist/models/ContractLineItem.d.ts +5 -1
  61. package/dist/models/Customer.d.ts +5 -1
  62. package/dist/models/Fulfillment.d.ts +5 -1
  63. package/dist/models/FulfillmentLineItem.d.ts +5 -1
  64. package/dist/models/Invoice.d.ts +6 -2
  65. package/dist/models/InvoiceLineItem.d.ts +5 -1
  66. package/dist/models/Payment.d.ts +6 -2
  67. package/dist/models/PaymentAllocation.d.ts +5 -1
  68. package/dist/models/PaymentInstrument.d.ts +5 -1
  69. package/dist/models/PaymentIntent.d.ts +24 -1
  70. package/dist/models/Payout.d.ts +24 -1
  71. package/dist/models/Vendor.d.ts +5 -1
  72. package/dist/models/billing.d.ts +111 -1
  73. package/dist/models/billing.d.ts.map +1 -1
  74. package/dist/models/index.d.ts +1 -1
  75. package/dist/models/index.d.ts.map +1 -1
  76. package/dist/smrt-knowledge.json +320 -9
  77. package/dist/types/index.d.ts +5 -1
  78. package/dist/ui.d.ts +14 -1
  79. package/package.json +11 -10
package/AGENTS.md CHANGED
@@ -100,6 +100,23 @@ for usage.
100
100
  `WRITTEN_OFF` (a later payment is recorded; the status stays).
101
101
  - **`Invoice.providerTaxAmount`** is added to line-item tax; it is
102
102
  server-managed (not API-writable). `toAccountingInput()` emits major units.
103
+ - **Payment rails (#3138).** A runtime has one issuing `provider` and any
104
+ number of `paymentProviders` (unique names, one inbox namespace each; a
105
+ provider with `capabilities.issuesInvoices === false` can never issue).
106
+ `observe()` resolves the provider from the delivery namespace, never the
107
+ payload. Crypto rails implement the port over `@happyvertical/payments`'
108
+ `CryptoCheckoutGateway`; never call BTCPay (or any gateway) from here.
109
+ Settlement is decided by the pure `decideAttempt()` from gateway state and
110
+ `paymentPolicy` — the gateway's `settled` is authoritative and nothing here
111
+ counts confirmations. A `BillingPaymentAttempt` (`(provider, checkout_id)`
112
+ deterministic id) records every state and settles once; grants and payments
113
+ are keyed by checkout id. Invoice payments close the issuer's invoice out of
114
+ band in `observe()` (idempotent) before `project()` records the payment, and
115
+ `settlePaidInvoice` skips (records nothing for) a `paidOutOfBand` invoice
116
+ the rail latched with `outOfBandRequestedAt`; other out-of-band closes are
117
+ recorded as `OTHER` payments. Rails never save cards or charge tax: a taxed
118
+ credit purchase on a rail needs an explicit `automaticTax: false`. Exceptions are flags for operators;
119
+ nothing reverses or refunds automatically.
103
120
  - **Known limits**: write-offs post no bad-debt journal (AR stays until paid
104
121
  or journaled by an operator); discounts still reach Stripe as negative lines (the SDK's
105
122
  line `discount` would change the lines a replayed push reconciles, so it
package/README.md CHANGED
@@ -373,6 +373,94 @@ const evaluator = await SpendingPolicyEvaluator.create({
373
373
  `automaticTax: false` to keep them untaxed), and Stripe customers are
374
374
  created idempotently.
375
375
 
376
+ ### Payment rails: BTC and other crypto (#3138)
377
+
378
+ A seller runtime can take payments on more than one **rail**. The issuing
379
+ `provider` (Stripe) still issues, taxes, and emails every invoice; extra rails
380
+ in `paymentProviders` let a payer choose, per payment, to pay an invoice or buy
381
+ prepaid credit another way. The crypto rail runs short-lived, fiat-priced
382
+ checkouts through `@happyvertical/payments`' provider-neutral
383
+ `CryptoCheckoutGateway` — BTCPay Server at launch — so another gateway can
384
+ replace BTCPay without changing this package or its consumers.
385
+
386
+ ```ts
387
+ import {
388
+ BillingRuntime,
389
+ createBtcPayBillingProvider,
390
+ createStripeBillingProvider,
391
+ } from '@happyvertical/smrt-commerce';
392
+
393
+ const btcpay = createBtcPayBillingProvider({
394
+ baseUrl, apiKey, storeId, // one BTCPay store per seller entity
395
+ webhookSecret, metadataSecret, // from the host's secret store, by name
396
+ speedPolicy: 'LowMediumSpeed', // settle after 2 confirmations (BTCPay offers 0/1/2/6)
397
+ rateSource: 'kraken',
398
+ minimumAmount: { CAD: 500, USD: 500 },
399
+ });
400
+ const billing = await BillingRuntime.create({
401
+ db, sellerTenantId, kind: 'provider',
402
+ provider: createStripeBillingProvider({ stripe, webhookSecret }),
403
+ paymentProviders: [btcpay],
404
+ paymentPolicy: { latePaymentPolicy: 'review', refundBasis: 'original_native' },
405
+ billingRelationships,
406
+ ledger: { ...ledger, cryptoHoldingsAccountId, fxGainLossAccountId, feesAccountId },
407
+ });
408
+
409
+ // Prepaid credit or an issued invoice, paid with BTC:
410
+ await billing.createCreditCheckout({ ...purchase, provider: 'btcpay' });
411
+ await billing.createInvoicePayment({ invoiceId, provider: 'btcpay', purchaseId, successUrl, cancelUrl });
412
+
413
+ // Webhook route for the rail (BTCPay signs with its own header):
414
+ await billing.acceptWebhook(rawBody, '', { provider: 'btcpay', headers: request.headers });
415
+ await billing.processEvents();
416
+ ```
417
+
418
+ - **Settlement is the gateway's.** Credit is granted and invoices are paid
419
+ only when the gateway reports `settled` under its own confirmation policy;
420
+ this package never counts confirmations. BTCPay offers 0, 1, 2 or 6
421
+ confirmations; the default is `LowMediumSpeed` (2 confirmations).
422
+ - **Payment attempts.** Every rail checkout is a `BillingPaymentAttempt`
423
+ recording the locked fiat price, fiat and native amounts received, rate and
424
+ rate source, each payment's txid, rail and fee, a timeline of status changes,
425
+ and what settlement produced. `listPaymentAttempts()` feeds "payment
426
+ confirming" displays and operator queues; `refreshPaymentAttempts()` is the
427
+ polling fallback for missed webhooks.
428
+ - **Dunning pauses** for an invoice while a payment for it is confirming
429
+ (0-conf seen); if the payment then expires or is invalidated the payer's
430
+ standing is re-applied.
431
+ - **Invoices paid on a rail** are closed at the issuer with
432
+ `markInvoicePaidOutOfBand` (Stripe `paid_out_of_band`), so Stripe stops
433
+ collecting; Stripe's own `paid` event then records nothing more.
434
+ `createInvoicePayment` refuses when the issuing provider cannot do that.
435
+ - **Exceptions never auto-refund.** Underpaid, overpaid, late, manually marked
436
+ (at the gateway), invalidated-after-settlement, and paid-twice attempts are
437
+ flagged for an operator (`resolvePaymentAttempt`). Per-seller
438
+ `paymentPolicy` sets under/overpayment tolerances (basis points), late
439
+ payments (`review` | `accept`), manually marked checkouts, hold vs convert
440
+ (`conversionHandler`), and the refund basis.
441
+ - **Ledger.** Receipts are booked at the locked fiat amount into
442
+ `cryptoHoldingsAccountId` (default `cashAccountId`); money received above
443
+ the price is booked as customer credit (prepaid-credit liability).
444
+ `recordManualRefund()` refunds from that excess first, then from purchased
445
+ credit (or an invoice payment kept as credit because the invoice was paid
446
+ elsewhere) — capped cumulatively, once per reference; an invoice payment
447
+ applied to its invoice is reversed at the issuer, not here.
448
+ `recordCryptoConversion()` posts a conversion with FX gain/loss.
449
+ - **Upgrading.** Additive schema: `_smrt_billing_payment_attempts` (run
450
+ `smrt db:migrate`). A runtime without `paymentProviders` never reads it.
451
+ - **One live payment per invoice.** The issuer's own `paid` event for an
452
+ invoice a rail closed out of band records nothing (the rail records it); an
453
+ out-of-band close made by someone else (wire, cheque) is recorded as an
454
+ `OTHER`-method payment; a real card payment made while a rail payment
455
+ confirms is recorded and the rail money is flagged `invoice_already_paid`.
456
+ An issuer close whose rail payment never settles is flagged
457
+ `out_of_band_without_settlement`: reopen the invoice at the issuer, then
458
+ `resolvePaymentAttempt()` it, after which a later out-of-band close is
459
+ recorded normally.
460
+ - **Entities.** Each legal entity is its own seller runtime with its own
461
+ store, keys, and ledger. Inter-entity resale is ordinary reseller billing
462
+ between two sellers, payable on any rail the selling entity offers.
463
+
376
464
  See [`AGENTS.md`](./AGENTS.md#billing-period-close-3060) for invariants and
377
465
  known limits.
378
466
 
@@ -0,0 +1,13 @@
1
+ interface CommerceForeignKeyFixtures {
2
+ db: {
3
+ type: 'sqlite';
4
+ url: string;
5
+ };
6
+ customerIds?: string[];
7
+ vendorIds?: string[];
8
+ contractIds?: string[];
9
+ }
10
+ /** Seed only the real same-package parents referenced by a test scenario. */
11
+ export declare function seedCommerceForeignKeyFixtures({ db, customerIds, vendorIds, contractIds, }: CommerceForeignKeyFixtures): Promise<void>;
12
+ export {};
13
+ //# sourceMappingURL=foreign-key-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foreign-key-fixtures.d.ts","sourceRoot":"","sources":["../../src/__tests__/foreign-key-fixtures.ts"],"names":[],"mappings":"AAIA,UAAU,0BAA0B;IAClC,EAAE,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,6EAA6E;AAC7E,wBAAsB,8BAA8B,CAAC,EACnD,EAAE,EACF,WAAgB,EAChB,SAAc,EACd,WAAgB,GACjB,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA2B5C"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Helpers for billing-cycle anchor tests (#3116), shared by the SQLite suite
3
+ * and the PostgreSQL lane. Scenarios run at fixed future dates with no usage
4
+ * charges, so only flat plans are billed.
5
+ */
6
+ import type { TenantSubscription } from '@happyvertical/smrt-subscriptions';
7
+ import type { BillingRuntime } from '../../billing/runtime.js';
8
+ import { type BillingLineSource } from '../../models/billing.js';
9
+ import { type BillingWorld } from './billing-fixture.js';
10
+ export declare const at: (iso: string) => Date;
11
+ export declare const DAY = 86400000;
12
+ export declare function subscriptionOf(world: BillingWorld, tenantId: string): Promise<TenantSubscription>;
13
+ export declare function updateSubscription(world: BillingWorld, tenantId: string, patch: Partial<Pick<TenantSubscription, 'startedAt' | 'canceledAt' | 'trialEndsAt' | 'status'>>): Promise<TenantSubscription>;
14
+ /** Put SITE (billed to NETWORK) out of the way so NETWORK has nothing due. */
15
+ export declare function parkNetwork(world: BillingWorld): Promise<void>;
16
+ /** Subscribe another tenant to the provider's site plan, billed to NETWORK. */
17
+ export declare function addNetworkSite(world: BillingWorld, tenantId: string, startedAt: Date): Promise<TenantSubscription>;
18
+ /** A payer's invoices from the provider runtime, oldest period first. */
19
+ export declare function invoicesOf(world: BillingWorld, payer: string, runtime?: BillingRuntime): Promise<import("../../index.js").Invoice[]>;
20
+ export declare function flatClaims(world: BillingWorld, subscriptionId?: string): Promise<BillingLineSource[]>;
21
+ /** Claimed windows as ISO pairs with amounts, in time order. */
22
+ export declare function claimedWindows(world: BillingWorld, subscriptionId: string): Promise<Array<[string, string, number]>>;
23
+ /** Assert that no two flat claims of one subscription cover the same time. */
24
+ export declare function overlappingClaims(world: BillingWorld): Promise<string[]>;
25
+ /**
26
+ * Hold a runtime's first flat-plan claim insert for `lineKey` until
27
+ * `release()`: `reached` resolves once the claim has read the existing
28
+ * coverage and is about to insert, so a test can interleave a second worker
29
+ * deterministically.
30
+ */
31
+ export declare function holdFlatClaim(runtime: BillingRuntime, lineKey: string): {
32
+ reached: Promise<void>;
33
+ release: () => void;
34
+ restore: () => void;
35
+ };
36
+ //# sourceMappingURL=anchor-fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-fixture.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/anchor-fixture.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,YAAY,EAIlB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,EAAE,GAAI,KAAK,MAAM,SAAkB,CAAC;AACjD,eAAO,MAAM,GAAG,WAAa,CAAC;AAE9B,wBAAsB,cAAc,CAClC,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CAM7B;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,CACZ,IAAI,CACF,kBAAkB,EAClB,WAAW,GAAG,YAAY,GAAG,aAAa,GAAG,QAAQ,CACtD,CACF,GACA,OAAO,CAAC,kBAAkB,CAAC,CAO7B;AAED,8EAA8E;AAC9E,wBAAsB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED,+EAA+E;AAC/E,wBAAsB,cAAc,CAClC,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,IAAI,GACd,OAAO,CAAC,kBAAkB,CAAC,CAgB7B;AAED,yEAAyE;AACzE,wBAAsB,UAAU,CAC9B,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,cAA+B,+CAUzC;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,YAAY,EACnB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAW9B;AAED,gEAAgE;AAChE,wBAAsB,cAAc,CAClC,KAAK,EAAE,YAAY,EACnB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAM1C;AAED,8EAA8E;AAC9E,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBnB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM;;mBAGhD,IAAI;;EAqBzB"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * A small reseller world for billing-period close tests (#3060), shared by
3
+ * the SQLite suite and the PostgreSQL lane.
4
+ *
5
+ * PROVIDER sells to NETWORK (a reseller that pays for SITE's usage and plan)
6
+ * and to SOLO (billed for itself). NETWORK resells to SITE (retail usage) and
7
+ * to SITE2 (NETWORK's own flat plan).
8
+ */
9
+ import type { DatabaseInterface } from '@happyvertical/smrt-core/migrations';
10
+ import { BillingAdjustmentCollection, ClientChargeCollection, CommercialUsageService, ResellerBillingService, SubscriptionPlanCollection, TenantSubscriptionCollection } from '@happyvertical/smrt-subscriptions';
11
+ import { BillingRelationshipService } from '@happyvertical/smrt-tenancy';
12
+ import { type BillingLedgerAccounts, BillingRuntime, type PayerStandingChange } from '../../billing/runtime.js';
13
+ import { FakeStripe } from './fake-stripe.js';
14
+ export declare const PROVIDER = "00000000-0000-4000-8000-00000000000a";
15
+ export declare const NETWORK = "aaaaaaaa-1111-4111-8111-aaaaaaaaaaaa";
16
+ export declare const SITE = "cccccccc-3333-4333-8333-cccccccccccc";
17
+ export declare const SITE2 = "eeeeeeee-5555-4555-8555-eeeeeeeeeeee";
18
+ export declare const SOLO = "dddddddd-4444-4444-8444-dddddddddddd";
19
+ export declare const STRANGER = "ffffffff-6666-4666-8666-ffffffffffff";
20
+ /** The current UTC calendar month, closable by passing `now: periodEnd + 1d`. */
21
+ export declare function currentMonth(): {
22
+ periodStart: Date;
23
+ periodEnd: Date;
24
+ now: Date;
25
+ };
26
+ export declare function nextMonth(period: {
27
+ periodEnd: Date;
28
+ }): {
29
+ periodStart: Date;
30
+ periodEnd: Date;
31
+ now: Date;
32
+ };
33
+ export interface BillingWorld {
34
+ db: DatabaseInterface;
35
+ stripe: FakeStripe;
36
+ provider: BillingRuntime;
37
+ reseller: BillingRuntime;
38
+ commercial: CommercialUsageService;
39
+ relationships: BillingRelationshipService;
40
+ resellerBilling: ResellerBillingService;
41
+ ledger: BillingLedgerAccounts;
42
+ standingChanges: PayerStandingChange[];
43
+ subscriptions: TenantSubscriptionCollection;
44
+ plans: SubscriptionPlanCollection;
45
+ charges: ClientChargeCollection;
46
+ adjustments: BillingAdjustmentCollection;
47
+ /** Record and rate one usage event (10 tokens) for a tenant. */
48
+ usage(tenantId: string, quantity?: number): Promise<void>;
49
+ }
50
+ export declare function createBillingWorld(db: DatabaseInterface): Promise<BillingWorld>;
51
+ //# sourceMappingURL=billing-fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing-fixture.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/billing-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,OAAO,EACL,2BAA2B,EAC3B,sBAAsB,EACtB,sBAAsB,EAGtB,sBAAsB,EACtB,0BAA0B,EAC1B,4BAA4B,EAE7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,0BAA0B,EAG3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,EACd,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,QAAQ,yCAAyC,CAAC;AAC/D,eAAO,MAAM,OAAO,yCAAyC,CAAC;AAC9D,eAAO,MAAM,IAAI,yCAAyC,CAAC;AAC3D,eAAO,MAAM,KAAK,yCAAyC,CAAC;AAC5D,eAAO,MAAM,IAAI,yCAAyC,CAAC;AAC3D,eAAO,MAAM,QAAQ,yCAAyC,CAAC;AAE/D,iFAAiF;AACjF,wBAAgB,YAAY,IAAI;IAC9B,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,GAAG,EAAE,IAAI,CAAC;CACX,CAaA;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE;;;;EAUpD;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,iBAAiB,CAAC;IACtB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,EAAE,sBAAsB,CAAC;IACnC,aAAa,EAAE,0BAA0B,CAAC;IAC1C,eAAe,EAAE,sBAAsB,CAAC;IACxC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,eAAe,EAAE,mBAAmB,EAAE,CAAC;IACvC,aAAa,EAAE,4BAA4B,CAAC;IAC5C,KAAK,EAAE,0BAA0B,CAAC;IAClC,OAAO,EAAE,sBAAsB,CAAC;IAChC,WAAW,EAAE,2BAA2B,CAAC;IACzC,gEAAgE;IAChE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D;AAID,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,iBAAiB,GACpB,OAAO,CAAC,YAAY,CAAC,CA6OvB"}
@@ -0,0 +1,13 @@
1
+ import { type SmrtObject } from '@happyvertical/smrt-core';
2
+ export declare const CONSUMER_PACKAGE = "@fixture/market-core";
3
+ export declare function defineConsumerLicenseSale(): Promise<{
4
+ ctor: typeof SmrtObject;
5
+ dispose: () => void;
6
+ }>;
7
+ /**
8
+ * Each `LicenseSale` keeps its own identity and table, the schema planner
9
+ * keeps commerce's `contracts` free of the consumer's class, and commerce's
10
+ * `LicenseSale` still round-trips through its own table.
11
+ */
12
+ export declare function assertLicenseSalesCoexist(consumer: typeof SmrtObject): Promise<void>;
13
+ //# sourceMappingURL=consumer-license-sale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumer-license-sale.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/consumer-license-sale.ts"],"names":[],"mappings":"AAwBA,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,0BAA0B,CAAC;AAKlC,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAEvD,wBAAsB,yBAAyB,IAAI,OAAO,CAAC;IACzD,IAAI,EAAE,OAAO,UAAU,CAAC;IACxB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC,CAgCD;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,OAAO,UAAU,GAC1B,OAAO,CAAC,IAAI,CAAC,CA2Cf"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * A crypto payment rail world for #3138: the billing fixture's Stripe
3
+ * issuing provider plus an in-memory `CryptoCheckoutGateway`, wired into one
4
+ * seller runtime. Shared by the SQLite suite and the PostgreSQL lane.
5
+ */
6
+ import { type CreateCryptoCheckoutInput, type CryptoCheckout, type CryptoCheckoutException, type CryptoCheckoutGateway, type CryptoCheckoutPayment, type CryptoCheckoutStatus } from '@happyvertical/payments';
7
+ import type { DatabaseInterface } from '@happyvertical/smrt-core/migrations';
8
+ import type { BillingPaymentPolicy } from '../../billing/payment-attempts.js';
9
+ import type { BillingProvider } from '../../billing/provider.js';
10
+ import { type BillingLedgerAccounts, BillingRuntime } from '../../billing/runtime.js';
11
+ import { type BillingWorld } from './billing-fixture.js';
12
+ export declare const RAIL_METADATA_SECRET = "rail-metadata-secret";
13
+ export declare const RAIL_SIGNATURE = "fake-rail-ok";
14
+ interface FakeCheckout {
15
+ id: string;
16
+ orderId: string;
17
+ amount: number;
18
+ currency: string;
19
+ metadata: Record<string, string>;
20
+ status: CryptoCheckoutStatus;
21
+ exception: CryptoCheckoutException;
22
+ amountPaid: number;
23
+ payments: CryptoCheckoutPayment[];
24
+ owned: boolean;
25
+ /** Settlement asset override (default BTC). */
26
+ asset?: string;
27
+ }
28
+ /** An in-memory gateway: tests move checkouts through their states. */
29
+ export declare class FakeCryptoGateway implements CryptoCheckoutGateway {
30
+ readonly id = "btcpay";
31
+ readonly checkouts: Map<string, FakeCheckout>;
32
+ created: number;
33
+ private counter;
34
+ createCheckout(input: CreateCryptoCheckoutInput): Promise<CryptoCheckout>;
35
+ getCheckout(checkoutId: string): Promise<CryptoCheckout>;
36
+ listCheckouts(input: {
37
+ orderId: string;
38
+ }): Promise<CryptoCheckout[]>;
39
+ verifyWebhook(rawBody: string, headers: Headers | Record<string, string | undefined>): {
40
+ eventId: string;
41
+ checkoutId: string | null;
42
+ type: string;
43
+ redelivery: boolean;
44
+ };
45
+ /** Move a checkout: `paid` is fiat minor units received. */
46
+ set(checkoutId: string, status: CryptoCheckoutStatus, options?: {
47
+ exception?: CryptoCheckoutException;
48
+ paid?: number;
49
+ paymentStatus?: CryptoCheckoutPayment['status'];
50
+ }): void;
51
+ private view;
52
+ }
53
+ export interface RailWorld extends BillingWorld {
54
+ gateway: FakeCryptoGateway;
55
+ rail: BillingProvider;
56
+ runtime: BillingRuntime;
57
+ railLedger: BillingLedgerAccounts;
58
+ /** Provider invoice ids closed as paid out of band. */
59
+ outOfBand: string[];
60
+ /** Deliver a signed rail webhook for a checkout and process events. */
61
+ railEvent(checkoutId: string, eventId?: string): Promise<void>;
62
+ }
63
+ export declare function createRailWorld(db: DatabaseInterface, options?: {
64
+ paymentPolicy?: Partial<BillingPaymentPolicy>;
65
+ /** Give Stripe the out-of-band method (default true). */
66
+ outOfBand?: boolean;
67
+ }): Promise<RailWorld>;
68
+ export {};
69
+ //# sourceMappingURL=crypto-rail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto-rail.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/crypto-rail.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAE1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAE1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAI7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,EACf,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,KAAK,YAAY,EAGlB,MAAM,sBAAsB,CAAC;AAG9B,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAC3D,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAG7C,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,uBAAuB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,uEAAuE;AACvE,qBAAa,iBAAkB,YAAW,qBAAqB;IAC7D,QAAQ,CAAC,EAAE,YAAY;IACvB,QAAQ,CAAC,SAAS,4BAAmC;IACrD,OAAO,SAAK;IACZ,OAAO,CAAC,OAAO,CAAK;IAEd,cAAc,CAClB,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,cAAc,CAAC;IAyBpB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAOxD,aAAa,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAM1E,aAAa,CACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;;;;;;IAsBvD,4DAA4D;IAC5D,GAAG,CACD,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;QACP,SAAS,CAAC,EAAE,uBAAuB,CAAC;QACpC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;KAC5C,GACL,IAAI;IA6BP,OAAO,CAAC,IAAI;CAsBb;AAED,MAAM,WAAW,SAAU,SAAQ,YAAY;IAC7C,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,qBAAqB,CAAC;IAClC,uDAAuD;IACvD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,uEAAuE;IACvE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAID,wBAAsB,eAAe,CACnC,EAAE,EAAE,iBAAiB,EACrB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9C,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAC;CAChB,GACL,OAAO,CAAC,SAAS,CAAC,CAmFpB"}
@@ -0,0 +1,147 @@
1
+ import type { StripeAccountingProvider } from '@happyvertical/accounting';
2
+ export declare const WEBHOOK_SECRET = "whsec_test_billing";
3
+ interface FakeCustomer {
4
+ id: string;
5
+ created: number;
6
+ name?: string;
7
+ email?: string;
8
+ address?: Record<string, string>;
9
+ tax_exempt?: string;
10
+ default_payment_method?: string;
11
+ metadata: Record<string, string>;
12
+ }
13
+ interface FakeInvoiceItem {
14
+ id: string;
15
+ customer: string;
16
+ currency: string;
17
+ quantity: number;
18
+ unit_amount: number;
19
+ description: string;
20
+ invoice: string | null;
21
+ metadata: Record<string, string>;
22
+ }
23
+ interface FakeInvoice {
24
+ id: string;
25
+ customer: string;
26
+ currency: string;
27
+ status: 'draft' | 'open' | 'paid' | 'void' | 'uncollectible';
28
+ collection_method: 'send_invoice' | 'charge_automatically';
29
+ auto_advance: boolean;
30
+ finalized: number;
31
+ due_date: number | null;
32
+ created: number;
33
+ automatic_tax: boolean;
34
+ items: string[];
35
+ subtotal: number;
36
+ tax: number;
37
+ total: number;
38
+ amount_paid: number;
39
+ amount_remaining: number;
40
+ metadata: Record<string, string>;
41
+ sent: number;
42
+ paid_out_of_band?: boolean;
43
+ }
44
+ interface FakeSession {
45
+ id: string;
46
+ url: string;
47
+ mode: string;
48
+ currency: string | null;
49
+ amount_subtotal: number | null;
50
+ amount_tax: number;
51
+ amount_total: number | null;
52
+ customer: string | null;
53
+ status: 'open' | 'complete' | 'expired';
54
+ payment_status: 'paid' | 'unpaid' | 'no_payment_required';
55
+ automatic_tax: boolean;
56
+ customer_update_address: boolean;
57
+ billing_address_collection: string | null;
58
+ setup_future_usage: string | null;
59
+ payment_method: string | null;
60
+ metadata: Record<string, string>;
61
+ }
62
+ /** How a saved payment method answers an off-session charge. */
63
+ export type FakeCardBehavior = 'succeed' | 'processing' | 'decline' | 'authentication_required';
64
+ interface FakePaymentIntent {
65
+ id: string;
66
+ object: 'payment_intent';
67
+ created: number;
68
+ amount: number;
69
+ currency: string;
70
+ customer: string;
71
+ payment_method: string;
72
+ status: string;
73
+ last_payment_error: Record<string, string> | null;
74
+ metadata: Record<string, string>;
75
+ }
76
+ export declare class FakeStripe {
77
+ readonly customers: Map<string, FakeCustomer>;
78
+ readonly invoiceItems: Map<string, FakeInvoiceItem>;
79
+ readonly invoices: Map<string, FakeInvoice>;
80
+ readonly sessions: Map<string, FakeSession>;
81
+ readonly paymentIntents: Map<string, FakePaymentIntent>;
82
+ /** False while Stripe's search index lags behind writes. */
83
+ searchable: boolean;
84
+ /** Behavior of each saved payment method (default `succeed`). */
85
+ readonly cards: Map<string, FakeCardBehavior>;
86
+ readonly subscriptions: Map<string, Record<string, unknown>>;
87
+ readonly requests: Array<{
88
+ method: string;
89
+ path: string;
90
+ key?: string;
91
+ }>;
92
+ private readonly idempotent;
93
+ private failures;
94
+ private counter;
95
+ private nextId;
96
+ /** Make the next matching request(s) fail. */
97
+ fail(method: string, path: RegExp, when?: 'before' | 'after', times?: number): void;
98
+ provider(): Promise<StripeAccountingProvider>;
99
+ readonly fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
100
+ private route;
101
+ private applyCustomer;
102
+ private recalculate;
103
+ private customerView;
104
+ private sessionView;
105
+ private view;
106
+ /** Customer pays an open invoice in full. */
107
+ pay(invoiceId: string): void;
108
+ /**
109
+ * Stripe's automatic collection of a finalized `charge_automatically`
110
+ * invoice: charges the customer's default card. Returns the outcome.
111
+ */
112
+ collect(invoiceId: string): 'paid' | 'failed';
113
+ /**
114
+ * The customer finishes a checkout: a payment session is paid with tax
115
+ * from the collected (or customer) address; a setup session saves
116
+ * `paymentMethod`. A collected address is saved to the customer when the
117
+ * session asked to.
118
+ */
119
+ completeSession(sessionId: string, options?: {
120
+ paymentMethod?: string;
121
+ address?: Record<string, string>;
122
+ }): FakeSession;
123
+ private createPaymentIntent;
124
+ /** A processing PaymentIntent settles (Stripe then sends its event). */
125
+ settlePaymentIntent(intentId: string, status: 'succeeded' | 'failed'): FakePaymentIntent;
126
+ setInvoiceStatus(invoiceId: string, status: FakeInvoice['status']): void;
127
+ lineItemsOf(invoiceId: string): FakeInvoiceItem[];
128
+ }
129
+ /** A signed Stripe webhook delivery for `event`. */
130
+ export declare function signedEvent(event: Record<string, unknown>): {
131
+ payload: string;
132
+ signature: string;
133
+ };
134
+ export declare function invoiceEvent(type: string, invoiceId: string): Record<string, unknown>;
135
+ export declare function checkoutEvent(session: {
136
+ id: string;
137
+ mode?: string;
138
+ currency: string | null;
139
+ amount_subtotal: number | null;
140
+ amount_total?: number | null;
141
+ metadata: Record<string, string>;
142
+ }, paymentStatus?: string, type?: string): Record<string, unknown>;
143
+ /** A `payment_intent.*` event for a PaymentIntent the fake holds. */
144
+ export declare function paymentIntentEvent(type: string, intent: Record<string, unknown>): Record<string, unknown>;
145
+ export declare function subscriptionEvent(type: string, subscriptionId: string): Record<string, unknown>;
146
+ export {};
147
+ //# sourceMappingURL=fake-stripe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-stripe.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/fake-stripe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * BTCPay Server as a crypto payment rail (#3138): sugar over
3
+ * {@link createCryptoBillingProvider} with the SDK's BTCPay checkout gateway.
4
+ * One BTCPay store per seller; keys and secrets come from the host's secret
5
+ * store, never from this package.
6
+ */
7
+ import { type BtcpayClientOptions, type BtcpaySpeedPolicy } from '@happyvertical/payments/btcpay';
8
+ import type { BillingProvider } from './provider.js';
9
+ export interface BtcPayBillingProviderOptions {
10
+ /** Server origin, for example `https://btcpay.example.com`. */
11
+ baseUrl: string;
12
+ /** Store-scoped key: `canviewinvoices` + `cancreateinvoice` suffice. */
13
+ apiKey: string;
14
+ storeId: string;
15
+ /** The store webhook's secret. */
16
+ webhookSecret: string;
17
+ /** Secret signing the `smrt_*` checkout metadata. */
18
+ metadataSecret: string;
19
+ /**
20
+ * Confirmations before BTCPay settles on-chain: `HighSpeed` 0,
21
+ * `MediumSpeed` 1, `LowMediumSpeed` 2 (default, the owner's settlement
22
+ * policy), `LowSpeed` 6.
23
+ */
24
+ speedPolicy?: BtcpaySpeedPolicy;
25
+ /** Default `['BTC-CHAIN']`; add `BTC-LN` once the store has Lightning. */
26
+ paymentMethods?: string[];
27
+ expirationMinutes?: number;
28
+ monitoringMinutes?: number;
29
+ /** BTCPay's own underpayment tolerance, percent (default 0). */
30
+ paymentTolerance?: number;
31
+ /** Label of the store's rate source, recorded on every payment. */
32
+ rateSource?: string;
33
+ /** Minimum checkout per currency, minor units. */
34
+ minimumAmount?: Record<string, number>;
35
+ /** Provider name (default `btcpay`). */
36
+ name?: string;
37
+ fetch?: BtcpayClientOptions['fetch'];
38
+ }
39
+ export declare function createBtcPayBillingProvider(options: BtcPayBillingProviderOptions): BillingProvider;
40
+ //# sourceMappingURL=btcpay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"btcpay.d.ts","sourceRoot":"","sources":["../../src/billing/btcpay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEvB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,4BAA4B;IAC3C,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;CACtC;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,eAAe,CAsBjB"}
@@ -1,7 +1,17 @@
1
- import { DatabaseInterface } from '@happyvertical/smrt-core/migrations';
2
- import { BillingAccount } from '../models/billing.js';
3
- import { BillingProviderCheckoutSession, BillingProviderCheckoutState } from './provider.js';
4
- import { BillingRuntime } from './runtime.js';
1
+ /**
2
+ * Card on file (#3139): a provider checkout that saves a payment method
3
+ * without charging, and the settlement that makes it the payer's default.
4
+ *
5
+ * A completed setup (or a credit purchase that saved its card) is applied
6
+ * from the provider's checkout event: `observe()` re-reads the session and
7
+ * makes the saved method the provider customer's default; `project()`
8
+ * records it as the payer customer's default commerce `PaymentInstrument`
9
+ * and adopts the billing address collected at checkout as the tax location.
10
+ */
11
+ import type { DatabaseInterface } from '@happyvertical/smrt-core/migrations';
12
+ import type { BillingAccount } from '../models/billing.js';
13
+ import type { BillingProviderCheckoutSession, BillingProviderCheckoutState } from './provider.js';
14
+ import type { BillingRuntime } from './runtime.js';
5
15
  export declare const CARD_SETUP_PURPOSE = "card_setup";
6
16
  /** Checkout metadata flag: the session saves its payment method. */
7
17
  export declare const SAVE_CARD_METADATA = "smrt_save_card";
@@ -1,5 +1,5 @@
1
- import { BillingProviderCheckoutSession } from './provider.js';
2
- import { BillingRuntime } from './runtime.js';
1
+ import { type BillingProviderCheckoutSession } from './provider.js';
2
+ import type { BillingRuntime } from './runtime.js';
3
3
  export declare const CREDIT_PURCHASE_PURPOSE = "credit_purchase";
4
4
  export interface CreateCreditCheckoutInput {
5
5
  /** The `period: 'balance'` spending policy to credit. */
@@ -27,6 +27,11 @@ export interface CreateCreditCheckoutInput {
27
27
  * automatically charged invoices (#3139).
28
28
  */
29
29
  savePaymentMethod?: boolean;
30
+ /**
31
+ * The payment rail to pay with (a provider name from the runtime's
32
+ * `provider` or `paymentProviders`; default the issuing provider, #3138).
33
+ */
34
+ provider?: string;
30
35
  }
31
36
  /** The checkout metadata a completed purchase is settled from. */
32
37
  export interface CreditPurchaseMetadata {
@@ -1 +1 @@
1
- {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../../src/billing/credits.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAEzD,MAAM,WAAW,yBAAyB;IACxC,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuBD,uEAAuE;AACvE,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,sBAAsB,GAAG,IAAI,CAwB/B;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,8BAA8B,CAAC,CA4FzC"}
1
+ {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../../src/billing/credits.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,KAAK,8BAA8B,EAEpC,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAEzD,MAAM,WAAW,yBAAyB;IACxC,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuBD,uEAAuE;AACvE,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,sBAAsB,GAAG,IAAI,CAwB/B;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,8BAA8B,CAAC,CAwHzC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * A crypto payment rail for the {@link BillingProvider} port (#3138).
3
+ *
4
+ * The rail runs short-lived, fiat-priced checkouts through a
5
+ * `CryptoCheckoutGateway` from `@happyvertical/payments` — BTCPay at launch,
6
+ * any other implementation of the port later. It never issues invoices: the
7
+ * runtime's issuing provider (Stripe) does, and this rail pays them or buys
8
+ * prepaid credit. Settlement is the gateway's decision (its confirmation
9
+ * policy); this package never counts confirmations.
10
+ */
11
+ import { type CryptoCheckoutGateway } from '@happyvertical/payments';
12
+ import { type BillingProvider } from './provider.js';
13
+ export interface CryptoBillingProviderOptions {
14
+ /** A checkout gateway, for example `createBtcpayCheckoutGateway()`. */
15
+ gateway: CryptoCheckoutGateway;
16
+ /**
17
+ * Secret the `smrt_*` checkout metadata is signed with. Rotating it
18
+ * strands checkouts created before the rotation.
19
+ */
20
+ metadataSecret: string;
21
+ /** Provider name on local rows and in the inbox (default the gateway id). */
22
+ name?: string;
23
+ /**
24
+ * Smallest checkout accepted, per currency, in minor units (fee and dust
25
+ * economics). A currency without an entry has no minimum.
26
+ */
27
+ minimumAmount?: Record<string, number>;
28
+ }
29
+ export declare function createCryptoBillingProvider(options: CryptoBillingProviderOptions): BillingProvider;
30
+ /** Exact decimal string → integer atomic units; rejects excess precision. */
31
+ export declare function decimalToAtomic(value: string, exponent: number): number;
32
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/billing/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAEL,KAAK,qBAAqB,EAG3B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,eAAe,EAMrB,MAAM,eAAe,CAAC;AASvB,MAAM,WAAW,4BAA4B;IAC3C,uEAAuE;IACvE,OAAO,EAAE,qBAAqB,CAAC;IAC/B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAKD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,eAAe,CAqHjB;AAkED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYvE"}