@happyvertical/smrt-commerce 0.51.29 → 0.51.31

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.
@@ -1,17 +1,20 @@
1
1
  import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
2
  import { DatabaseInterface } from '@happyvertical/smrt-core/migrations';
3
3
  import { JournalCollection } from '@happyvertical/smrt-ledgers';
4
- import { BillingAdjustmentCollection, BillingRelationshipReader, ClientChargeCollection, PriceBookCollection, RetailChargeCollection, SpendingPolicyCollection, SubscriptionPlanCollection, TenantSubscriptionCollection } from '@happyvertical/smrt-subscriptions';
4
+ import { AutoTopUpHook, BillingAdjustmentCollection, BillingRelationshipReader, ClientChargeCollection, PriceBookCollection, RetailChargeCollection, SpendingPolicyCollection, SubscriptionPlanCollection, TenantSubscriptionCollection } from '@happyvertical/smrt-subscriptions';
5
5
  import { CustomerCollection } from '../collections/CustomerCollection.js';
6
6
  import { InvoiceCollection } from '../collections/InvoiceCollection.js';
7
7
  import { InvoiceLineItemCollection } from '../collections/InvoiceLineItemCollection.js';
8
+ import { PaymentInstrumentCollection } from '../collections/PaymentInstrumentCollection.js';
8
9
  import { BillingAccount, BillingAccountCollection, BillingCloseKind, BillingLineSourceCollection, BillingPeriodCloseCollection, BillingStanding } from '../models/billing.js';
9
10
  import { Invoice } from '../models/Invoice.js';
10
11
  import { Address } from '../types/index.js';
12
+ import { CreateCardSetupCheckoutInput } from './cards.js';
11
13
  import { CreateCreditCheckoutInput } from './credits.js';
12
14
  import { ScheduledBillingPeriod } from './cycles.js';
13
15
  import { ClosePeriodInput, PeriodCloseResult } from './period-close.js';
14
16
  import { BillingProvider, BillingProviderCheckoutSession } from './provider.js';
17
+ import { AutoTopUpFailureHook, AutoTopUpHookOptions } from './top-up.js';
15
18
  /** Ledger accounts billing posts to, in the seller's books. */
16
19
  export interface BillingLedgerAccounts {
17
20
  /** Accounts receivable (debited when an invoice is issued). */
@@ -60,6 +63,22 @@ export interface BillingRuntimeOptions extends SmrtClassOptions {
60
63
  leaseMs?: number;
61
64
  /** Page size for charge scans (default 500). */
62
65
  pageSize?: number;
66
+ /**
67
+ * Bill payers whose default card with this provider is on file (saved by
68
+ * {@link BillingRuntime.createCardSetupCheckout} or a credit purchase with
69
+ * `savePaymentMethod`) with automatically charged invoices (#3139): the
70
+ * provider charges the card after the invoice is sent, and the `paid` or
71
+ * `payment_failed` event settles it. Default false (every invoice is sent
72
+ * for payment). Activate service on the `paid` event (a `current`
73
+ * standing), not on send: collection happens on the provider's schedule.
74
+ */
75
+ autoChargeInvoices?: boolean;
76
+ /**
77
+ * Called when an automatic top-up charge ends without collecting (a
78
+ * decline, or the issuer requiring the payer to authenticate), from the
79
+ * hook or the provider's `payment` event (#3139).
80
+ */
81
+ onAutoTopUpFailed?: AutoTopUpFailureHook;
63
82
  }
64
83
  export interface UpsertBillingAccountInput {
65
84
  payerTenantId: string;
@@ -89,6 +108,15 @@ export interface SyncedBillingAccount {
89
108
  account: BillingAccount;
90
109
  providerCustomerId: string;
91
110
  automaticTax: boolean;
111
+ /** The customer has a billing address country (its tax location). */
112
+ hasTaxLocation: boolean;
113
+ }
114
+ export interface EnsureProviderCustomerOptions {
115
+ /**
116
+ * Refuse a taxed account without a tax location (default true). Card setup
117
+ * turns it off because the address is collected at checkout.
118
+ */
119
+ requireTaxLocation?: boolean;
92
120
  }
93
121
  /**
94
122
  * Billing for one seller. Construct once per process with
@@ -116,6 +144,7 @@ export declare class BillingRuntime {
116
144
  readonly subscriptions: TenantSubscriptionCollection;
117
145
  readonly plans: SubscriptionPlanCollection;
118
146
  readonly policies: SpendingPolicyCollection;
147
+ readonly instruments: PaymentInstrumentCollection;
119
148
  readonly sellerTenantId: string;
120
149
  readonly kind: BillingCloseKind;
121
150
  readonly provider: BillingProvider;
@@ -125,6 +154,8 @@ export declare class BillingRuntime {
125
154
  readonly onPayerStanding?: PayerStandingHook;
126
155
  readonly leaseMs: number;
127
156
  readonly pageSize: number;
157
+ readonly autoChargeInvoices: boolean;
158
+ readonly onAutoTopUpFailed?: AutoTopUpFailureHook;
128
159
  /** The inbox provider namespace for this runtime's events (per seller). */
129
160
  readonly eventProvider: string;
130
161
  private constructor();
@@ -139,10 +170,15 @@ export declare class BillingRuntime {
139
170
  upsertAccount(input: UpsertBillingAccountInput): Promise<BillingAccount>;
140
171
  /**
141
172
  * Create or update the provider customer, including its tax location.
142
- * Creating a customer is not idempotent at the provider; an attempt that
143
- * dies between creation and this save leaves an unused provider customer.
173
+ * Creation is idempotent per account at the provider (sdk#1268): an
174
+ * attempt that dies between creation and this save finds the same
175
+ * customer on retry.
144
176
  */
145
- ensureProviderCustomer(account: BillingAccount): Promise<SyncedBillingAccount>;
177
+ ensureProviderCustomer(account: BillingAccount, options?: EnsureProviderCustomerOptions): Promise<SyncedBillingAccount>;
178
+ /** Whether the account's customer has a tax location (address country). */
179
+ hasTaxLocation(account: BillingAccount): Promise<boolean>;
180
+ /** Whether provider-calculated tax applies to this account's charges. */
181
+ accountIsTaxed(account: BillingAccount): Promise<boolean>;
146
182
  /**
147
183
  * Close billing periods. Without a period, each payer's last ended period
148
184
  * on its own schedule (the previous UTC calendar month unless its account
@@ -183,6 +219,29 @@ export declare class BillingRuntime {
183
219
  * parent that set a delegated balance.
184
220
  */
185
221
  createCreditCheckout(input: CreateCreditCheckoutInput): Promise<BillingProviderCheckoutSession>;
222
+ /**
223
+ * Start a provider checkout that saves the payer's card without charging
224
+ * it. When the checkout completes, event processing makes the card the
225
+ * provider customer's default, records it as the payer's default
226
+ * `PaymentInstrument`, and adopts the billing address collected at
227
+ * checkout as its tax location. Callable by the payer or in a system
228
+ * context. Throws when the provider cannot save payment methods.
229
+ */
230
+ createCardSetupCheckout(input: CreateCardSetupCheckoutInput): Promise<BillingProviderCheckoutSession>;
231
+ /**
232
+ * The `autoTopUp` hook for smrt-subscriptions' `SpendingPolicyEvaluator`:
233
+ * charges the payer's saved card off-session and credits the balance once
234
+ * the charge succeeds. Without a provider that can charge a saved card (or
235
+ * without a card) it never tops up. Enable `payment_intent.*` events on
236
+ * the provider webhook: charges that settle later are credited from them.
237
+ */
238
+ autoTopUpHook(options?: AutoTopUpHookOptions): AutoTopUpHook;
239
+ /**
240
+ * Write off a sent, unpaid invoice at the provider (#3139). The provider's
241
+ * `uncollectible` event then moves the payer to `uncollectible` standing.
242
+ * Call only from trusted operator code. Throws when the provider cannot.
243
+ */
244
+ markInvoiceUncollectible(invoiceId: string): Promise<void>;
186
245
  /** Run in a system context (cross-tenant reads and payer-owned writes). */
187
246
  system<T>(operation: () => Promise<T>): Promise<T>;
188
247
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/billing/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/billing/runtime.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../src/billing/stripe.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,wBAAwB,EAExB,YAAY,EACb,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAEL,KAAK,eAAe,EAIpB,KAAK,oBAAoB,EAM1B,MAAM,eAAe,CAAC;AAQvB,MAAM,WAAW,4BAA4B;IAC3C,6EAA6E;IAC7E,MAAM,EAAE,wBAAwB,CAAC;IACjC,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;CACvB;AAqDD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,eAAe,CAkJjB;AAED,qEAAqE;AACrE,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,oBAAoB,CAAC,CAoE/B"}
1
+ {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../src/billing/stripe.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EACV,wBAAwB,EAExB,YAAY,EACb,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAGL,KAAK,eAAe,EAOpB,KAAK,oBAAoB,EAO1B,MAAM,eAAe,CAAC;AAQvB,MAAM,WAAW,4BAA4B;IAC3C,6EAA6E;IAC7E,MAAM,EAAE,wBAAwB,CAAC;IACjC,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;CACvB;AAsED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,eAAe,CA0SjB;AAED,qEAAqE;AACrE,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,oBAAoB,CAAC,CAuG/B"}
@@ -0,0 +1,102 @@
1
+ import { DatabaseInterface } from '@happyvertical/smrt-core/migrations';
2
+ import { AutoTopUpGrant, AutoTopUpHook, AutoTopUpRequest } from '@happyvertical/smrt-subscriptions';
3
+ import { BillingChargeStatus } from './provider.js';
4
+ import { BillingRuntime } from './runtime.js';
5
+ /** Prefix of every auto top-up charge key (the provider idempotency key). */
6
+ export declare const AUTO_TOP_UP_CHARGE_PREFIX = "smrt-auto-top-up:";
7
+ /** `Payment.reference` of an auto top-up attempt: prefix + policy id. */
8
+ export declare const AUTO_TOP_UP_REFERENCE_PREFIX = "auto-top-up:";
9
+ /** A top-up charge that did not succeed. Nothing was credited. */
10
+ export interface AutoTopUpFailure {
11
+ sellerTenantId: string;
12
+ payerTenantId: string;
13
+ spendingPolicyId: string;
14
+ /** The attempt's commerce `Payment` id. */
15
+ paymentId: string;
16
+ /**
17
+ * `requires_action`: the issuer wants the payer present (re-save the card
18
+ * with a card setup checkout); `failed`: declined or no usable card;
19
+ * `canceled`: the provider canceled it.
20
+ */
21
+ status: Exclude<BillingChargeStatus, 'succeeded' | 'processing'>;
22
+ failureCode?: string;
23
+ amount: number;
24
+ currency: string;
25
+ /**
26
+ * The settlement transaction. Host writes through it commit or roll back
27
+ * with the attempt's closing.
28
+ */
29
+ db: DatabaseInterface;
30
+ }
31
+ /**
32
+ * Host hook for a failed top-up, for example to ask the payer to update their
33
+ * card. It runs inside the transaction that closes the attempt, once per
34
+ * attempt, but may run again if that transaction is retried, so it must be
35
+ * idempotent.
36
+ */
37
+ export type AutoTopUpFailureHook = (failure: AutoTopUpFailure) => void | Promise<void>;
38
+ export interface AutoTopUpHookOptions {
39
+ /**
40
+ * How much to charge, in integer minor units of the policy currency.
41
+ * Default: the request's shortfall, recomputed against the current balance. Return 0 or nothing to skip the top-up
42
+ * (the policy's own behavior then applies).
43
+ */
44
+ amount?: (request: AutoTopUpRequest) => number | null | undefined | Promise<number | null | undefined>;
45
+ /**
46
+ * After a failed charge, wait this long before charging the policy again
47
+ * (default one hour), so a declined card is not retried on every request.
48
+ */
49
+ retryAfterMs?: number;
50
+ /**
51
+ * An attempt still `processing` (or interrupted before its outcome was
52
+ * saved) is re-read from the provider at most this often (default one
53
+ * minute); its outcome also arrives as a `payment` webhook.
54
+ */
55
+ recheckAfterMs?: number;
56
+ /** Charge description shown to the payer where supported. */
57
+ description?: string;
58
+ /**
59
+ * A payer whose account is taxed (`automaticTax`, not tax-exempt) is not
60
+ * topped up by default (`'skip'`): an off-session charge carries no
61
+ * provider-calculated tax, while a checkout purchase of the same credit
62
+ * does. `'charge_untaxed'` charges them without tax, for sellers that
63
+ * account for tax on this credit elsewhere.
64
+ */
65
+ taxedAccounts?: 'skip' | 'charge_untaxed';
66
+ }
67
+ /** The outcome of a charge, from the provider's result or its webhook. */
68
+ export interface AutoTopUpChargeOutcome {
69
+ status: BillingChargeStatus;
70
+ providerPaymentId?: string;
71
+ providerCustomerId?: string;
72
+ /** Minor units, when reported. */
73
+ amount?: number;
74
+ currency?: string;
75
+ failureCode?: string;
76
+ }
77
+ export declare function autoTopUpChargeKey(paymentId: string): string;
78
+ /** The attempt payment id in a charge key, or null for any other key. */
79
+ export declare function autoTopUpPaymentId(chargeKey: string): string | null;
80
+ /**
81
+ * Build the `autoTopUp` hook for `SpendingPolicyEvaluator.create()`.
82
+ *
83
+ * The hook returns a grant only when the charge `succeeded`. A `processing`
84
+ * charge returns nothing now and is credited when its `payment` webhook is
85
+ * processed; `requires_action`, `failed`, and `canceled` credit nothing. A
86
+ * provider without `chargeSavedPaymentMethod` (a push-payment rail) never
87
+ * tops up. Provider errors other than a charge outcome propagate.
88
+ */
89
+ export declare function createAutoTopUpHook(runtime: BillingRuntime, options?: AutoTopUpHookOptions): AutoTopUpHook;
90
+ /**
91
+ * Apply a charge outcome to its attempt: credit and record the payment when it
92
+ * succeeded, close the attempt when it failed. Idempotent, and safe to run
93
+ * concurrently from the hook and the `payment` webhook: `db` must be a
94
+ * transaction (the event transaction, or one the hook opens), and the first
95
+ * statement takes the attempt's row lock with a conditional update, so only
96
+ * one caller settles a pending attempt and the rest see it settled. Runs in
97
+ * the seller's tenant context (payments and journals are the seller's).
98
+ */
99
+ export declare function applyAutoTopUpOutcome(runtime: BillingRuntime, db: DatabaseInterface, paymentId: string, outcome: AutoTopUpChargeOutcome): Promise<{
100
+ grant: AutoTopUpGrant | null;
101
+ }>;
102
+ //# sourceMappingURL=top-up.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"top-up.d.ts","sourceRoot":"","sources":["../../src/billing/top-up.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EAEtB,MAAM,mCAAmC,CAAC;AAM3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,6EAA6E;AAC7E,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAC7D,yEAAyE;AACzE,eAAO,MAAM,4BAA4B,iBAAiB,CAAC;AAK3D,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC,mBAAmB,EAAE,WAAW,GAAG,YAAY,CAAC,CAAC;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,EAAE,EAAE,iBAAiB,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,OAAO,EAAE,gBAAgB,KACtB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,CACP,OAAO,EAAE,gBAAgB,KACtB,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACpE;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC3C;AAED,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGnE;AAUD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,oBAAyB,GACjC,aAAa,CA4Cf;AAmJD;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC;IAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAAA;CAAE,CAAC,CAgI3C"}
@@ -8,7 +8,10 @@
8
8
  */
9
9
  /** Upper-case, validate, and return an ISO 4217-shaped currency code. */
10
10
  export declare function normalizeCurrency(currency: string): string;
11
- /** The ISO 4217 minor-unit exponent of a currency (2 for USD, 0 for JPY). */
11
+ /**
12
+ * The ISO 4217 minor-unit exponent of a currency (2 for USD and RSD, 0 for
13
+ * JPY, 3 for IQD). Any code not listed as 0, 3, or 4 is 2.
14
+ */
12
15
  export declare function currencyMinorUnitExponent(currency: string): number;
13
16
  /** Integer minor units → major units (`1999` USD → `19.99`). */
14
17
  export declare function minorToMajorUnits(amount: number, currency: string): number;
@@ -1 +1 @@
1
- {"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/billing/units.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOlE;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY1E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AAKD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAc,GAAG,MAAM,CAKzE;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE/D"}
1
+ {"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/billing/units.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAuCD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY1E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AAKD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAc,GAAG,MAAM,CAKzE;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE/D"}
@@ -5,13 +5,36 @@ function normalizeCurrency(currency) {
5
5
  if (!CURRENCY_PATTERN.test(code)) throw new Error(`Currency '${currency}' must be a three-letter code.`);
6
6
  return code;
7
7
  }
8
+ var ISO_EXPONENTS = {
9
+ BIF: 0,
10
+ CLP: 0,
11
+ DJF: 0,
12
+ GNF: 0,
13
+ ISK: 0,
14
+ JPY: 0,
15
+ KMF: 0,
16
+ KRW: 0,
17
+ PYG: 0,
18
+ RWF: 0,
19
+ UGX: 0,
20
+ UYI: 0,
21
+ VND: 0,
22
+ VUV: 0,
23
+ XAF: 0,
24
+ XOF: 0,
25
+ XPF: 0,
26
+ BHD: 3,
27
+ IQD: 3,
28
+ JOD: 3,
29
+ KWD: 3,
30
+ LYD: 3,
31
+ OMR: 3,
32
+ TND: 3,
33
+ CLF: 4,
34
+ UYW: 4
35
+ };
8
36
  function currencyMinorUnitExponent(currency) {
9
- const code = normalizeCurrency(currency);
10
- const digits = new Intl.NumberFormat("en", {
11
- style: "currency",
12
- currency: code
13
- }).resolvedOptions().maximumFractionDigits;
14
- return typeof digits === "number" ? digits : 2;
37
+ return ISO_EXPONENTS[normalizeCurrency(currency)] ?? 2;
15
38
  }
16
39
  function minorToMajorUnits(amount, currency) {
17
40
  if (!Number.isSafeInteger(amount)) throw new Error(`Amount ${amount} must be a safe integer number of minor units.`);
@@ -52,4 +75,4 @@ function tenantKey(id) {
52
75
  //#endregion
53
76
  export { majorToMinorUnits as a, tenantKey as c, isTenantId as i, currencyMinorUnitExponent as n, minorToMajorUnits as o, deterministicId as r, normalizeCurrency as s, canonicalTenantId as t };
54
77
 
55
- //# sourceMappingURL=units-Bk_o7kRV.js.map
78
+ //# sourceMappingURL=units-ROB39hIC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"units-ROB39hIC.js","names":[],"sources":["../../src/billing/units.ts"],"sourcesContent":["/**\n * Money-unit and identity helpers for billing-period close (#3060).\n *\n * Commerce stores money as integer minor units. `@happyvertical/accounting`\n * takes and returns currency *major* units and converts to the provider's own\n * representation itself, so the only conversion this package performs is the\n * ISO 4217 one: minor units ÷ 10^exponent.\n */\n\nconst CURRENCY_PATTERN = /^[A-Z]{3}$/;\n\n/** Upper-case, validate, and return an ISO 4217-shaped currency code. */\nexport function normalizeCurrency(currency: string): string {\n const code = String(currency ?? '')\n .trim()\n .toUpperCase();\n if (!CURRENCY_PATTERN.test(code)) {\n throw new Error(`Currency '${currency}' must be a three-letter code.`);\n }\n return code;\n}\n\n/**\n * ISO 4217 minor-unit exponents other than 2, matching\n * `@happyvertical/accounting`'s table (sdk#1269). An explicit table, not the\n * runtime's `Intl` display digits: CLDR rounds some currencies for display\n * (IQD, MGA, and ALL show 0 decimals) and its data changes between ICU\n * releases (RSD has shown both 0 and 2), which would put minor units off by a\n * power of ten against the SDK.\n */\nconst ISO_EXPONENTS: Record<string, number> = {\n BIF: 0,\n CLP: 0,\n DJF: 0,\n GNF: 0,\n ISK: 0,\n JPY: 0,\n KMF: 0,\n KRW: 0,\n PYG: 0,\n RWF: 0,\n UGX: 0,\n UYI: 0,\n VND: 0,\n VUV: 0,\n XAF: 0,\n XOF: 0,\n XPF: 0,\n BHD: 3,\n IQD: 3,\n JOD: 3,\n KWD: 3,\n LYD: 3,\n OMR: 3,\n TND: 3,\n CLF: 4,\n UYW: 4,\n};\n\n/**\n * The ISO 4217 minor-unit exponent of a currency (2 for USD and RSD, 0 for\n * JPY, 3 for IQD). Any code not listed as 0, 3, or 4 is 2.\n */\nexport function currencyMinorUnitExponent(currency: string): number {\n return ISO_EXPONENTS[normalizeCurrency(currency)] ?? 2;\n}\n\n/** Integer minor units → major units (`1999` USD → `19.99`). */\nexport function minorToMajorUnits(amount: number, currency: string): number {\n if (!Number.isSafeInteger(amount)) {\n throw new Error(\n `Amount ${amount} must be a safe integer number of minor units.`,\n );\n }\n return amount / 10 ** currencyMinorUnitExponent(currency);\n}\n\n/**\n * Major units → integer minor units. Rejects a value that is not a whole\n * number of minor units instead of rounding it: a provider amount that does\n * not round-trip exactly means the two sides disagree about the currency.\n */\nexport function majorToMinorUnits(amount: number, currency: string): number {\n if (!Number.isFinite(amount)) {\n throw new Error(`Provider amount ${amount} is not a finite number.`);\n }\n const scaled = amount * 10 ** currencyMinorUnitExponent(currency);\n const minor = Math.round(scaled);\n if (Math.abs(scaled - minor) > 1e-6 || !Number.isSafeInteger(minor)) {\n throw new Error(\n `Provider amount ${amount} ${normalizeCurrency(currency)} is not a whole number of minor units.`,\n );\n }\n return minor;\n}\n\n/**\n * A stable UUID derived from its parts (SHA-256, version/variant bits set).\n * Deterministic ids are what make every period-close write replay-safe: a\n * retry recomputes the same identity instead of creating a second row.\n */\nexport async function deterministicId(parts: string[]): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify(parts));\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n const uuid = digest.slice(0, 16);\n uuid[6] = (uuid[6] & 0x0f) | 0x50;\n uuid[8] = (uuid[8] & 0x3f) | 0x80;\n const hex = Array.from(uuid, (byte) => byte.toString(16).padStart(2, '0'));\n const joined = hex.join('');\n return [\n joined.slice(0, 8),\n joined.slice(8, 12),\n joined.slice(12, 16),\n joined.slice(16, 20),\n joined.slice(20),\n ].join('-');\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Validate and lower-case a tenant UUID. */\nexport function canonicalTenantId(id: string, label = 'Tenant ID'): string {\n if (typeof id !== 'string' || !UUID_PATTERN.test(id)) {\n throw new Error(`${label} must be a UUID.`);\n }\n return id.toLowerCase();\n}\n\n/** Whether `id` is a tenant UUID (any case). */\nexport function isTenantId(id: string): boolean {\n return typeof id === 'string' && UUID_PATTERN.test(id);\n}\n\n/** Lower-case a possibly-empty tenant id for comparison. */\nexport function tenantKey(id: string | null | undefined): string {\n return id ? String(id).toLowerCase() : '';\n}\n"],"mappings":";AASA,IAAM,mBAAmB;AAGlB,SAAS,kBAAkB,UAA0B;CAC1D,MAAM,OAAO,OAAO,YAAY,EAAE,CAAA,CAC/B,KAAK,CAAA,CACL,YAAY;CACf,IAAI,CAAC,iBAAiB,KAAK,IAAI,GAC7B,MAAM,IAAI,MAAM,aAAa,SAAQ,+BAAgC;CAEvE,OAAO;AACT;AAUA,IAAM,gBAAwC;CAC5C,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAMO,SAAS,0BAA0B,UAA0B;CAClE,OAAO,cAAc,kBAAkB,QAAQ,MAAM;AACvD;AAGO,SAAS,kBAAkB,QAAgB,UAA0B;CAC1E,IAAI,CAAC,OAAO,cAAc,MAAM,GAC9B,MAAM,IAAI,MACR,UAAU,OAAM,+CAClB;CAEF,OAAO,SAAS,MAAM,0BAA0B,QAAQ;AAC1D;AAOO,SAAS,kBAAkB,QAAgB,UAA0B;CAC1E,IAAI,CAAC,OAAO,SAAS,MAAM,GACzB,MAAM,IAAI,MAAM,mBAAmB,OAAM,yBAA0B;CAErE,MAAM,SAAS,SAAS,MAAM,0BAA0B,QAAQ;CAChE,MAAM,QAAQ,KAAK,MAAM,MAAM;CAC/B,IAAI,KAAK,IAAI,SAAS,KAAK,IAAI,QAAQ,CAAC,OAAO,cAAc,KAAK,GAChE,MAAM,IAAI,MACR,mBAAmB,OAAM,GAAI,kBAAkB,QAAQ,EAAC,uCAC1D;CAEF,OAAO;AACT;AAOA,eAAsB,gBAAgB,OAAkC;CACtE,MAAM,QAAQ,IAAI,YAAY,CAAA,CAAE,OAAO,KAAK,UAAU,KAAK,CAAC;CAE5D,MAAM,OAAO,IADM,WAAW,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK,CAC5D,CAAA,CAAO,MAAM,GAAG,EAAE;CAC/B,KAAK,KAAM,KAAK,KAAK,KAAQ;CAC7B,KAAK,KAAM,KAAK,KAAK,KAAQ;CAE7B,MAAM,SADM,MAAM,KAAK,OAAO,SAAS,KAAK,SAAS,EAAE,CAAA,CAAE,SAAS,GAAG,GAAG,CACzD,CAAA,CAAI,KAAK,EAAE;CAC1B,OAAO;EACL,OAAO,MAAM,GAAG,CAAC;EACjB,OAAO,MAAM,GAAG,EAAE;EAClB,OAAO,MAAM,IAAI,EAAE;EACnB,OAAO,MAAM,IAAI,EAAE;EACnB,OAAO,MAAM,EAAE;CACjB,CAAA,CAAE,KAAK,GAAG;AACZ;AAEA,IAAM,eACJ;AAGK,SAAS,kBAAkB,IAAY,QAAQ,aAAqB;CACzE,IAAI,OAAO,OAAO,YAAY,CAAC,aAAa,KAAK,EAAE,GACjD,MAAM,IAAI,MAAM,GAAG,MAAK,iBAAkB;CAE5C,OAAO,GAAG,YAAY;AACxB;AAGO,SAAS,WAAW,IAAqB;CAC9C,OAAO,OAAO,OAAO,YAAY,aAAa,KAAK,EAAE;AACvD;AAGO,SAAS,UAAU,IAAuC;CAC/D,OAAO,KAAK,OAAO,EAAE,CAAA,CAAE,YAAY,IAAI;AACzC"}