@lunora/payment 1.0.0-alpha.95 → 1.0.0-alpha.97

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/dist/index.d.mts CHANGED
@@ -11,6 +11,21 @@ interface PaymentStore {
11
11
  getPaymentSession: (provider: ProviderId, id: string) => Promise<PaymentSession | undefined>;
12
12
  getSubscription: (provider: ProviderId, id: string) => Promise<Subscription | undefined>;
13
13
  listSubscriptionsByReference: (referenceId: string) => Promise<Subscription[]>;
14
+ /**
15
+ * Usage events that still owe an upstream forward, oldest first, at most `limit`.
16
+ *
17
+ * "Owes a forward" is narrower than `reportedToProvider === false`: only an
18
+ * ADDITIVE event with a positive `quantity` qualifies. A `"set"` event's
19
+ * upstream delta was measured against the period total at the moment it was
20
+ * recorded and is not recoverable afterwards, and a non-positive quantity was
21
+ * never sent, so neither is a retry candidate — returning them would make the
22
+ * sweep re-send the same rows forever (or double-count on an additive meter).
23
+ *
24
+ * Read by `reconcile` to retry a forward the provider rejected transiently;
25
+ * without it a single 5xx loses that metered unit upstream for good, which for
26
+ * a provider that owns entitlements under-bills and over-entitles the customer.
27
+ */
28
+ listUnreportedUsage: (provider: ProviderId, limit: number) => Promise<UsageEvent[]>;
14
29
  /**
15
30
  * Claims a provider event id for processing. Resolves `true` the first time an event is seen
16
31
  * and `false` for a duplicate — the inbound-idempotency primitive.
@@ -69,6 +84,7 @@ declare class MemoryPaymentStore implements PaymentStore {
69
84
  getPaymentSession(provider: ProviderId, id: string): Promise<PaymentSession | undefined>;
70
85
  getSubscription(provider: ProviderId, id: string): Promise<Subscription | undefined>;
71
86
  listSubscriptionsByReference(referenceId: string): Promise<Subscription[]>;
87
+ listUnreportedUsage(provider: ProviderId, limit: number): Promise<UsageEvent[]>;
72
88
  markEventProcessed(provider: ProviderId, eventId: string): Promise<boolean>;
73
89
  releaseEvent(provider: ProviderId, eventId: string): Promise<void>;
74
90
  markUsageReported(provider: ProviderId, idempotencyKey: string): Promise<void>;
@@ -273,6 +289,30 @@ declare const createPayment: (options: CreatePaymentOptions) => LunoraPayment;
273
289
  interface PaymentRow extends Record<string, unknown> {
274
290
  readonly _id: string;
275
291
  }
292
+ /**
293
+ * Bounded-read arguments for {@link PaymentDatabase.findMany}, mirroring what
294
+ * Lunora's `ctx.db.findMany` already accepts. `where` stays equality-only — these
295
+ * are the knobs that let a caller bound the number of rows FETCHED rather than
296
+ * fetching everything and slicing afterwards.
297
+ * @experimental
298
+ */
299
+ interface PaymentPageArgs {
300
+ /** Keyset cursor from a previous page's {@link PaymentPage.cursor}. */
301
+ cursor?: string;
302
+ /** Maximum rows to FETCH. Omit to read every match. */
303
+ limit?: number;
304
+ /** Sort keys, pushed down to the store so the keyset cursor is well-defined. */
305
+ orderBy?: Record<string, "asc" | "desc">[];
306
+ }
307
+ /**
308
+ * One page of rows plus the cursor that continues it.
309
+ * @experimental
310
+ */
311
+ interface PaymentPage {
312
+ /** Cursor for the next page, or `undefined` when this was the last one. */
313
+ readonly cursor: string | undefined;
314
+ readonly rows: PaymentRow[];
315
+ }
276
316
  /**
277
317
  * Minimal write/read surface this store needs; `ctx.db` satisfies it structurally.
278
318
  * @experimental
@@ -280,7 +320,12 @@ interface PaymentRow extends Record<string, unknown> {
280
320
  interface PaymentDatabase {
281
321
  delete: (id: string) => Promise<void>;
282
322
  findFirst: (table: string, where: Record<string, unknown>) => Promise<PaymentRow | null>;
283
- findMany: (table: string, where: Record<string, unknown>) => Promise<PaymentRow[]>;
323
+ /**
324
+ * Equality-only `where`, with optional order/limit/cursor pushed DOWN to the
325
+ * store (see {@link PaymentPageArgs}). Omitting `page` reads every match — do
326
+ * that only where the match set is inherently small (one reference's rows).
327
+ */
328
+ findMany: (table: string, where: Record<string, unknown>, page?: PaymentPageArgs) => Promise<PaymentPage>;
284
329
  insert: (table: string, document: Record<string, unknown>) => Promise<string>;
285
330
  patch: (id: string, patch: Record<string, unknown>) => Promise<void>;
286
331
  }
@@ -291,6 +336,12 @@ interface PaymentDatabase {
291
336
  declare const createDatabasePaymentStore: (database: PaymentDatabase) => PaymentStore;
292
337
  /**
293
338
  * Structural subset of Lunora's `ctx.db` (the `findFirst`/`findMany(tableName, { where })` form).
339
+ *
340
+ * `findMany` models the order/limit/cursor knobs too — {@link PaymentDatabase}
341
+ * pushes them down so a sweep over a large match set reads bounded chunks
342
+ * instead of materialising the lot. `continueCursor` is REQUIRED here (`ctx.db`
343
+ * always returns it): a double that omitted it would page exactly once and then
344
+ * silently report the rest of the table as absent.
294
345
  * @experimental
295
346
  */
296
347
  interface LunoraDatabaseLike {
@@ -299,8 +350,12 @@ interface LunoraDatabaseLike {
299
350
  where?: Record<string, unknown>;
300
351
  }) => Promise<Record<string, unknown> | null>;
301
352
  findMany: (table: string, args?: {
353
+ cursor?: string;
354
+ limit?: number;
355
+ orderBy?: Record<string, "asc" | "desc">[];
302
356
  where?: Record<string, unknown>;
303
357
  }) => Promise<{
358
+ continueCursor: null | string;
304
359
  page: Record<string, unknown>[];
305
360
  }>;
306
361
  insert: (table: string, document: Record<string, unknown>) => Promise<string>;
@@ -420,6 +475,12 @@ interface ReconcileInput {
420
475
  readonly paymentSessionIds?: ReadonlyArray<string>;
421
476
  readonly store: PaymentStore;
422
477
  readonly subscriptionIds?: ReadonlyArray<string>;
478
+ /**
479
+ * How many unreported usage events to retry forwarding upstream this sweep
480
+ * (oldest first). Default {@link DEFAULT_USAGE_REPORT_LIMIT}; `0` skips the
481
+ * usage sweep entirely. Ignored by an adapter that does not meter usage.
482
+ */
483
+ readonly usageReportLimit?: number;
423
484
  }
424
485
  /**
425
486
  * `ReconcileResult` is part of the experimental `@lunora/payment` API and may change without a major version bump.
@@ -428,10 +489,15 @@ interface ReconcileInput {
428
489
  interface ReconcileResult {
429
490
  readonly checkedPayments: number;
430
491
  readonly checkedSubscriptions: number;
492
+ readonly checkedUsage: number;
431
493
  readonly failedPayments: number;
432
494
  readonly failedSubscriptions: number;
495
+ /** Usage events whose retried forward failed again — still pending for the next sweep. */
496
+ readonly failedUsage: number;
433
497
  readonly updatedPayments: number;
434
498
  readonly updatedSubscriptions: number;
499
+ /** Usage events successfully forwarded upstream on this sweep. */
500
+ readonly updatedUsage: number;
435
501
  }
436
502
  /**
437
503
  * `reconcile` is part of the experimental `@lunora/payment` API and may change without a major version bump.
@@ -439,6 +505,11 @@ interface ReconcileResult {
439
505
  */
440
506
  declare const reconcile: (input: ReconcileInput) => Promise<ReconcileResult>;
441
507
  /**
508
+ * The canonical column reference for the payment tables — a value to READ (in a test, a migration
509
+ * check, or your editor), not one to spread. `defineSchema({ ...paymentTables })` does NOT work:
510
+ * codegen discovers tables by parsing your `lunora/schema.ts` AST and cannot resolve a
511
+ * cross-package spread, so declare the same columns inline there (see the module docstring).
512
+ *
442
513
  * `paymentTables` is part of the experimental `@lunora/payment` API and may change without a major version bump.
443
514
  * @experimental
444
515
  */
@@ -541,4 +612,4 @@ interface VerifyCreemSignatureInput {
541
612
  * replay-window check. Throws a {@link LunoraPaymentError} on any failure.
542
613
  */
543
614
  declare const verifyCreemSignature: (input: VerifyCreemSignatureInput) => Promise<void>;
544
- export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, zeroMoney };
615
+ export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentPage, type PaymentPageArgs, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, zeroMoney };
package/dist/index.d.ts CHANGED
@@ -11,6 +11,21 @@ interface PaymentStore {
11
11
  getPaymentSession: (provider: ProviderId, id: string) => Promise<PaymentSession | undefined>;
12
12
  getSubscription: (provider: ProviderId, id: string) => Promise<Subscription | undefined>;
13
13
  listSubscriptionsByReference: (referenceId: string) => Promise<Subscription[]>;
14
+ /**
15
+ * Usage events that still owe an upstream forward, oldest first, at most `limit`.
16
+ *
17
+ * "Owes a forward" is narrower than `reportedToProvider === false`: only an
18
+ * ADDITIVE event with a positive `quantity` qualifies. A `"set"` event's
19
+ * upstream delta was measured against the period total at the moment it was
20
+ * recorded and is not recoverable afterwards, and a non-positive quantity was
21
+ * never sent, so neither is a retry candidate — returning them would make the
22
+ * sweep re-send the same rows forever (or double-count on an additive meter).
23
+ *
24
+ * Read by `reconcile` to retry a forward the provider rejected transiently;
25
+ * without it a single 5xx loses that metered unit upstream for good, which for
26
+ * a provider that owns entitlements under-bills and over-entitles the customer.
27
+ */
28
+ listUnreportedUsage: (provider: ProviderId, limit: number) => Promise<UsageEvent[]>;
14
29
  /**
15
30
  * Claims a provider event id for processing. Resolves `true` the first time an event is seen
16
31
  * and `false` for a duplicate — the inbound-idempotency primitive.
@@ -69,6 +84,7 @@ declare class MemoryPaymentStore implements PaymentStore {
69
84
  getPaymentSession(provider: ProviderId, id: string): Promise<PaymentSession | undefined>;
70
85
  getSubscription(provider: ProviderId, id: string): Promise<Subscription | undefined>;
71
86
  listSubscriptionsByReference(referenceId: string): Promise<Subscription[]>;
87
+ listUnreportedUsage(provider: ProviderId, limit: number): Promise<UsageEvent[]>;
72
88
  markEventProcessed(provider: ProviderId, eventId: string): Promise<boolean>;
73
89
  releaseEvent(provider: ProviderId, eventId: string): Promise<void>;
74
90
  markUsageReported(provider: ProviderId, idempotencyKey: string): Promise<void>;
@@ -273,6 +289,30 @@ declare const createPayment: (options: CreatePaymentOptions) => LunoraPayment;
273
289
  interface PaymentRow extends Record<string, unknown> {
274
290
  readonly _id: string;
275
291
  }
292
+ /**
293
+ * Bounded-read arguments for {@link PaymentDatabase.findMany}, mirroring what
294
+ * Lunora's `ctx.db.findMany` already accepts. `where` stays equality-only — these
295
+ * are the knobs that let a caller bound the number of rows FETCHED rather than
296
+ * fetching everything and slicing afterwards.
297
+ * @experimental
298
+ */
299
+ interface PaymentPageArgs {
300
+ /** Keyset cursor from a previous page's {@link PaymentPage.cursor}. */
301
+ cursor?: string;
302
+ /** Maximum rows to FETCH. Omit to read every match. */
303
+ limit?: number;
304
+ /** Sort keys, pushed down to the store so the keyset cursor is well-defined. */
305
+ orderBy?: Record<string, "asc" | "desc">[];
306
+ }
307
+ /**
308
+ * One page of rows plus the cursor that continues it.
309
+ * @experimental
310
+ */
311
+ interface PaymentPage {
312
+ /** Cursor for the next page, or `undefined` when this was the last one. */
313
+ readonly cursor: string | undefined;
314
+ readonly rows: PaymentRow[];
315
+ }
276
316
  /**
277
317
  * Minimal write/read surface this store needs; `ctx.db` satisfies it structurally.
278
318
  * @experimental
@@ -280,7 +320,12 @@ interface PaymentRow extends Record<string, unknown> {
280
320
  interface PaymentDatabase {
281
321
  delete: (id: string) => Promise<void>;
282
322
  findFirst: (table: string, where: Record<string, unknown>) => Promise<PaymentRow | null>;
283
- findMany: (table: string, where: Record<string, unknown>) => Promise<PaymentRow[]>;
323
+ /**
324
+ * Equality-only `where`, with optional order/limit/cursor pushed DOWN to the
325
+ * store (see {@link PaymentPageArgs}). Omitting `page` reads every match — do
326
+ * that only where the match set is inherently small (one reference's rows).
327
+ */
328
+ findMany: (table: string, where: Record<string, unknown>, page?: PaymentPageArgs) => Promise<PaymentPage>;
284
329
  insert: (table: string, document: Record<string, unknown>) => Promise<string>;
285
330
  patch: (id: string, patch: Record<string, unknown>) => Promise<void>;
286
331
  }
@@ -291,6 +336,12 @@ interface PaymentDatabase {
291
336
  declare const createDatabasePaymentStore: (database: PaymentDatabase) => PaymentStore;
292
337
  /**
293
338
  * Structural subset of Lunora's `ctx.db` (the `findFirst`/`findMany(tableName, { where })` form).
339
+ *
340
+ * `findMany` models the order/limit/cursor knobs too — {@link PaymentDatabase}
341
+ * pushes them down so a sweep over a large match set reads bounded chunks
342
+ * instead of materialising the lot. `continueCursor` is REQUIRED here (`ctx.db`
343
+ * always returns it): a double that omitted it would page exactly once and then
344
+ * silently report the rest of the table as absent.
294
345
  * @experimental
295
346
  */
296
347
  interface LunoraDatabaseLike {
@@ -299,8 +350,12 @@ interface LunoraDatabaseLike {
299
350
  where?: Record<string, unknown>;
300
351
  }) => Promise<Record<string, unknown> | null>;
301
352
  findMany: (table: string, args?: {
353
+ cursor?: string;
354
+ limit?: number;
355
+ orderBy?: Record<string, "asc" | "desc">[];
302
356
  where?: Record<string, unknown>;
303
357
  }) => Promise<{
358
+ continueCursor: null | string;
304
359
  page: Record<string, unknown>[];
305
360
  }>;
306
361
  insert: (table: string, document: Record<string, unknown>) => Promise<string>;
@@ -420,6 +475,12 @@ interface ReconcileInput {
420
475
  readonly paymentSessionIds?: ReadonlyArray<string>;
421
476
  readonly store: PaymentStore;
422
477
  readonly subscriptionIds?: ReadonlyArray<string>;
478
+ /**
479
+ * How many unreported usage events to retry forwarding upstream this sweep
480
+ * (oldest first). Default {@link DEFAULT_USAGE_REPORT_LIMIT}; `0` skips the
481
+ * usage sweep entirely. Ignored by an adapter that does not meter usage.
482
+ */
483
+ readonly usageReportLimit?: number;
423
484
  }
424
485
  /**
425
486
  * `ReconcileResult` is part of the experimental `@lunora/payment` API and may change without a major version bump.
@@ -428,10 +489,15 @@ interface ReconcileInput {
428
489
  interface ReconcileResult {
429
490
  readonly checkedPayments: number;
430
491
  readonly checkedSubscriptions: number;
492
+ readonly checkedUsage: number;
431
493
  readonly failedPayments: number;
432
494
  readonly failedSubscriptions: number;
495
+ /** Usage events whose retried forward failed again — still pending for the next sweep. */
496
+ readonly failedUsage: number;
433
497
  readonly updatedPayments: number;
434
498
  readonly updatedSubscriptions: number;
499
+ /** Usage events successfully forwarded upstream on this sweep. */
500
+ readonly updatedUsage: number;
435
501
  }
436
502
  /**
437
503
  * `reconcile` is part of the experimental `@lunora/payment` API and may change without a major version bump.
@@ -439,6 +505,11 @@ interface ReconcileResult {
439
505
  */
440
506
  declare const reconcile: (input: ReconcileInput) => Promise<ReconcileResult>;
441
507
  /**
508
+ * The canonical column reference for the payment tables — a value to READ (in a test, a migration
509
+ * check, or your editor), not one to spread. `defineSchema({ ...paymentTables })` does NOT work:
510
+ * codegen discovers tables by parsing your `lunora/schema.ts` AST and cannot resolve a
511
+ * cross-package spread, so declare the same columns inline there (see the module docstring).
512
+ *
442
513
  * `paymentTables` is part of the experimental `@lunora/payment` API and may change without a major version bump.
443
514
  * @experimental
444
515
  */
@@ -541,4 +612,4 @@ interface VerifyCreemSignatureInput {
541
612
  * replay-window check. Throws a {@link LunoraPaymentError} on any failure.
542
613
  */
543
614
  declare const verifyCreemSignature: (input: VerifyCreemSignatureInput) => Promise<void>;
544
- export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, zeroMoney };
615
+ export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentPage, type PaymentPageArgs, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, zeroMoney };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-DyCsry1Q.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-DBveGQxx.mjs";import{createPayment as y}from"./packem_shared/createPayment-Dmh6IWLZ.mjs";import{createDatabasePaymentStore as p}from"./packem_shared/createDatabasePaymentStore-BlMXuCW_.mjs";import{entitlementsForReference as s,featureNames as c,hasActivePrice as x,resolveEntitlements as S,usagePeriodStart as l}from"./packem_shared/entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as T}from"./packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as P}from"./packem_shared/idempotencyKey-BjxjMMna.mjs";import{addMoney as d,allocateMoney as A,compareMoney as E,formatMoney as N,fromMoneyJSON as h,isZeroDecimalCurrency as R,isZeroMoney as v,money as C,subtractMoney as D,toMoneyJSON as I,zeroMoney as _}from"./packem_shared/addMoney-B4ufWAnD.mjs";import{reconcile as L}from"./packem_shared/reconcile-Dq48_gHU.mjs";import{default as k}from"./packem_shared/paymentTables-Be-_11ux.mjs";import{PAYMENT_TERMINAL_STATES as J,SUBSCRIPTION_TERMINAL_STATES as W,canTransitionPayment as Z,canTransitionSubscription as q,nextPaymentState as z,nextSubscriptionState as B}from"./packem_shared/PAYMENT_TERMINAL_STATES-DVerjPR6.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-C2iTSo5g.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-daHKTG_Q.mjs";import{constantTimeEqual as w,hmacSha256Hex as G,verifyCreemSignature as Q,verifyStandardWebhook as V}from"./packem_shared/constantTimeEqual-Bj5tU-zT.mjs";export{T as LunoraPaymentError,K as MemoryPaymentStore,J as PAYMENT_TERMINAL_STATES,W as SUBSCRIPTION_TERMINAL_STATES,d as addMoney,A as allocateMoney,Y as applyWebhookAction,Z as canTransitionPayment,q as canTransitionSubscription,E as compareMoney,w as constantTimeEqual,r as createAdapterRegistry,p as createDatabasePaymentStore,y as createPayment,s as entitlementsForReference,c as featureNames,N as formatMoney,h as fromMoneyJSON,x as hasActivePrice,G as hmacSha256Hex,P as idempotencyKey,R as isZeroDecimalCurrency,v as isZeroMoney,a as lunoraDatabaseToPaymentDatabase,C as money,z as nextPaymentState,B as nextSubscriptionState,k as paymentTables,n as paymentsFromContext,L as reconcile,S as resolveEntitlements,D as subtractMoney,I as toMoneyJSON,l as usagePeriodStart,Q as verifyCreemSignature,V as verifyStandardWebhook,_ as zeroMoney};
1
+ import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-DyCsry1Q.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-5WkBd3aZ.mjs";import{createPayment as y}from"./packem_shared/createPayment-Dmh6IWLZ.mjs";import{createDatabasePaymentStore as p}from"./packem_shared/createDatabasePaymentStore-CVrAhWjP.mjs";import{entitlementsForReference as s,featureNames as c,hasActivePrice as x,resolveEntitlements as S,usagePeriodStart as l}from"./packem_shared/entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as T}from"./packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as P}from"./packem_shared/idempotencyKey-BjxjMMna.mjs";import{addMoney as d,allocateMoney as A,compareMoney as E,formatMoney as N,fromMoneyJSON as h,isZeroDecimalCurrency as R,isZeroMoney as v,money as C,subtractMoney as D,toMoneyJSON as I,zeroMoney as _}from"./packem_shared/addMoney-B4ufWAnD.mjs";import{reconcile as L}from"./packem_shared/reconcile-Cm45pURT.mjs";import{default as k}from"./packem_shared/paymentTables-Be-_11ux.mjs";import{PAYMENT_TERMINAL_STATES as J,SUBSCRIPTION_TERMINAL_STATES as W,canTransitionPayment as Z,canTransitionSubscription as q,nextPaymentState as z,nextSubscriptionState as B}from"./packem_shared/PAYMENT_TERMINAL_STATES-DVerjPR6.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-Cu9J-zf9.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-daHKTG_Q.mjs";import{constantTimeEqual as w,hmacSha256Hex as G,verifyCreemSignature as Q,verifyStandardWebhook as V}from"./packem_shared/constantTimeEqual-Bj5tU-zT.mjs";export{T as LunoraPaymentError,K as MemoryPaymentStore,J as PAYMENT_TERMINAL_STATES,W as SUBSCRIPTION_TERMINAL_STATES,d as addMoney,A as allocateMoney,Y as applyWebhookAction,Z as canTransitionPayment,q as canTransitionSubscription,E as compareMoney,w as constantTimeEqual,r as createAdapterRegistry,p as createDatabasePaymentStore,y as createPayment,s as entitlementsForReference,c as featureNames,N as formatMoney,h as fromMoneyJSON,x as hasActivePrice,G as hmacSha256Hex,P as idempotencyKey,R as isZeroDecimalCurrency,v as isZeroMoney,a as lunoraDatabaseToPaymentDatabase,C as money,z as nextPaymentState,B as nextSubscriptionState,k as paymentTables,n as paymentsFromContext,L as reconcile,S as resolveEntitlements,D as subtractMoney,I as toMoneyJSON,l as usagePeriodStart,Q as verifyCreemSignature,V as verifyStandardWebhook,_ as zeroMoney};
@@ -0,0 +1 @@
1
+ const a=(i,e)=>`${i}:${e}`,n=(i,e)=>`${i}:${e}`,u=i=>{const e=i.toSorted((t,r)=>t.createdAt-r.createdAt||t.idempotencyKey.localeCompare(r.idempotencyKey));let s=0;for(const t of e)s=t.mode==="set"?t.quantity:s+t.quantity;return s};class d{customers=new Map;processedEvents=new Set;sessions=new Map;subscriptions=new Map;usageEvents=new Map;getCustomerByReference(e,s){return Promise.resolve(this.customers.get(a(e,s)))}getPaymentSession(e,s){return Promise.resolve(this.sessions.get(n(e,s)))}getSubscription(e,s){return Promise.resolve(this.subscriptions.get(n(e,s)))}listSubscriptionsByReference(e){return Promise.resolve([...this.subscriptions.values()].filter(s=>s.referenceId===e))}listUnreportedUsage(e,s){const t=[...this.usageEvents.values()].filter(r=>r.provider===e&&!r.reportedToProvider&&r.mode!=="set"&&r.quantity>0).toSorted((r,o)=>r.createdAt-o.createdAt||r.idempotencyKey.localeCompare(o.idempotencyKey));return Promise.resolve(t.slice(0,Math.max(0,s)))}markEventProcessed(e,s){const t=n(e,s);return this.processedEvents.has(t)?Promise.resolve(!1):(this.processedEvents.add(t),Promise.resolve(!0))}releaseEvent(e,s){return this.processedEvents.delete(n(e,s)),Promise.resolve()}markUsageReported(e,s){const t=n(e,s),r=this.usageEvents.get(t);return r&&this.usageEvents.set(t,{...r,reportedToProvider:!0}),Promise.resolve()}recordUsage(e){const s=n(e.provider,e.idempotencyKey);return this.usageEvents.has(s)?Promise.resolve(!1):(this.usageEvents.set(s,e),Promise.resolve(!0))}sumUsage(e,s,t){const r=[];for(const o of this.usageEvents.values())o.referenceId===e&&o.featureId===s&&o.createdAt>=t&&r.push(o);return Promise.resolve(u(r))}sumUsageByFeature(e,s,t){const r=new Map(s.map(o=>[o,[]]));for(const o of this.usageEvents.values())o.referenceId===e&&o.createdAt>=t&&r.get(o.featureId)?.push(o);return Promise.resolve(new Map([...r].map(([o,c])=>[o,u(c)])))}upsertCustomer(e){return this.customers.set(a(e.provider,e.referenceId),e),Promise.resolve()}upsertPaymentSession(e){return this.sessions.set(n(e.provider,e.id),e),Promise.resolve()}upsertSubscription(e){return this.subscriptions.set(n(e.provider,e.id),e),Promise.resolve()}}export{d as MemoryPaymentStore,u as foldUsage};
@@ -0,0 +1 @@
1
+ import{money as y}from"./addMoney-B4ufWAnD.mjs";import{foldUsage as m}from"./MemoryPaymentStore-Cu9J-zf9.mjs";const o=(e,d)=>typeof e[d]=="string"?e[d]:"",l=(e,d)=>typeof e[d]=="string"?e[d]:void 0,c=(e,d)=>typeof e[d]=="number"?e[d]:0,v=(e,d)=>typeof e[d]=="number"?e[d]:void 0,A=(e,d)=>e[d]===!0,f=(e,d)=>{const t=e[d];return typeof t=="bigint"?t:typeof t=="number"||typeof t=="string"?BigInt(t):0n},S=e=>({createdAt:e.createdAt,email:e.email,provider:e.provider,providerCustomerId:e.id,referenceId:e.referenceId}),P=e=>({createdAt:c(e,"createdAt"),email:l(e,"email"),id:o(e,"providerCustomerId"),provider:o(e,"provider"),referenceId:o(e,"referenceId")}),E=e=>({amountMinor:e.amount.minorUnits,capturedMinor:e.capturedAmount.minorUnits,createdAt:e.createdAt,currency:e.amount.currency,provider:e.provider,providerSessionId:e.id,referenceId:e.referenceId,refundedMinor:e.refundedAmount.minorUnits,state:e.state,updatedAt:e.updatedAt}),M=e=>{const d=o(e,"currency");return{amount:y(f(e,"amountMinor"),d),capturedAmount:y(f(e,"capturedMinor"),d),createdAt:c(e,"createdAt"),id:o(e,"providerSessionId"),provider:o(e,"provider"),referenceId:o(e,"referenceId"),refundedAmount:y(f(e,"refundedMinor"),d),state:o(e,"state"),updatedAt:c(e,"updatedAt")}},T=e=>({cancelAtPeriodEnd:e.cancelAtPeriodEnd,createdAt:e.createdAt,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart,priceId:e.priceId,provider:e.provider,providerSubscriptionId:e.id,quantity:e.quantity,referenceId:e.referenceId,state:e.state,updatedAt:e.updatedAt}),I=e=>({cancelAtPeriodEnd:A(e,"cancelAtPeriodEnd"),createdAt:c(e,"createdAt"),currentPeriodEnd:v(e,"currentPeriodEnd"),currentPeriodStart:v(e,"currentPeriodStart"),id:o(e,"providerSubscriptionId"),priceId:o(e,"priceId"),provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),state:o(e,"state"),updatedAt:c(e,"updatedAt")}),q=e=>({createdAt:c(e,"createdAt"),featureId:o(e,"featureId"),idempotencyKey:o(e,"idempotencyKey"),mode:e.mode==="set"?"set":"add",provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),reportedToProvider:A(e,"reportedToProvider")}),K=e=>({createdAt:e.createdAt,featureId:e.featureId,idempotencyKey:e.idempotencyKey,...e.mode==="set"?{mode:"set"}:{},provider:e.provider,quantity:e.quantity,referenceId:e.referenceId,reportedToProvider:e.reportedToProvider}),F=e=>{const d=async(t,i,r)=>{const s=await e.findFirst(t,i);if(s){await e.patch(s._id,r);return}await e.insert(t,r)};return{getCustomerByReference:async(t,i)=>{const r=await e.findFirst("customers",{provider:t,referenceId:i});return r?P(r):void 0},getPaymentSession:async(t,i)=>{const r=await e.findFirst("paymentSessions",{provider:t,providerSessionId:i});return r?M(r):void 0},getSubscription:async(t,i)=>{const r=await e.findFirst("subscriptions",{provider:t,providerSubscriptionId:i});return r?I(r):void 0},listSubscriptionsByReference:async t=>{const{rows:i}=await e.findMany("subscriptions",{referenceId:t});return i.map(r=>I(r))},listUnreportedUsage:async(t,i)=>{const r=Math.max(0,Math.floor(i));if(r===0)return[];const s=[{createdAt:"asc"},{idempotencyKey:"asc"}],a=[];let n;do{const u=await e.findMany("usageEvents",{provider:t,reportedToProvider:!1},{cursor:n,limit:r,orderBy:s});for(const g of u.rows){const p=q(g);if(p.mode!=="set"&&p.quantity>0&&(a.push(p),a.length===r))return a}n=u.cursor}while(n!==void 0);return a},markEventProcessed:async(t,i)=>await e.findFirst("events",{provider:t,providerEventId:i})?!1:(await e.insert("events",{processedAt:Date.now(),provider:t,providerEventId:i,type:""}),!0),releaseEvent:async(t,i)=>{const r=await e.findFirst("events",{provider:t,providerEventId:i});r&&await e.delete(r._id)},markUsageReported:async(t,i)=>{const r=await e.findFirst("usageEvents",{idempotencyKey:i,provider:t});r&&await e.patch(r._id,{reportedToProvider:!0})},recordUsage:async t=>await e.findFirst("usageEvents",{idempotencyKey:t.idempotencyKey,provider:t.provider})?!1:(await e.insert("usageEvents",K(t)),!0),sumUsage:async(t,i,r)=>{const{rows:s}=await e.findMany("usageEvents",{featureId:i,referenceId:t}),a=s.filter(n=>c(n,"createdAt")>=r).map(n=>({createdAt:c(n,"createdAt"),idempotencyKey:typeof n.idempotencyKey=="string"?n.idempotencyKey:"",mode:n.mode==="set"?"set":"add",quantity:c(n,"quantity")}));return m(a)},sumUsageByFeature:async(t,i,r)=>{const{rows:s}=await e.findMany("usageEvents",{referenceId:t}),a=new Map(i.map(n=>[n,[]]));for(const n of s)c(n,"createdAt")<r||a.get(typeof n.featureId=="string"?n.featureId:"")?.push({createdAt:c(n,"createdAt"),idempotencyKey:typeof n.idempotencyKey=="string"?n.idempotencyKey:"",mode:n.mode==="set"?"set":"add",quantity:c(n,"quantity")});return new Map([...a].map(([n,u])=>[n,m(u)]))},upsertCustomer:async t=>d("customers",{provider:t.provider,referenceId:t.referenceId},S(t)),upsertPaymentSession:async t=>d("paymentSessions",{provider:t.provider,providerSessionId:t.id},E(t)),upsertSubscription:async t=>d("subscriptions",{provider:t.provider,providerSubscriptionId:t.id},T(t))}};export{F as createDatabasePaymentStore};
@@ -0,0 +1 @@
1
+ import{createPayment as i}from"./createPayment-Dmh6IWLZ.mjs";import{createDatabasePaymentStore as s}from"./createDatabasePaymentStore-CVrAhWjP.mjs";const o=r=>({delete:async t=>r.delete(t),findFirst:async(t,e)=>await r.findFirst(t,{where:e}),findMany:async(t,e,a)=>{const n=await r.findMany(t,{...a,where:e});return{cursor:n.continueCursor??void 0,rows:n.page}},insert:async(t,e)=>r.insert(t,e),patch:async(t,e)=>r.patch(t,e)}),y=(r,t)=>{const e=r.auth?.userId??void 0;return i({adapter:t.adapter,authorize:t.authorize??(a=>a.trim()!==""&&e!==void 0&&a===e),entitlements:t.entitlements,observability:t.observability,store:s(o(r.db))})};export{o as lunoraDatabaseToPaymentDatabase,y as paymentsFromContext};
@@ -0,0 +1 @@
1
+ import{compareMoney as y}from"./addMoney-B4ufWAnD.mjs";import{n as u}from"./observability-BteZ2dJS.mjs";const l=100,f=(e,t)=>e.currency===t.currency&&y(e,t)===0,m=(e,t)=>e?.state!==t.state||e.cancelAtPeriodEnd!==t.cancelAtPeriodEnd||e.currentPeriodEnd!==t.currentPeriodEnd||e.priceId!==t.priceId||e.quantity!==t.quantity,A=(e,t)=>e?.state!==t.state||!f(e.capturedAmount,t.capturedAmount)||!f(e.refundedAmount,t.refundedAmount),I=new Set(["partially_refunded","refunded"]),S=(e,t)=>{if(!e)return t;const s=e.refundedAmount.currency===t.refundedAmount.currency&&y(e.refundedAmount,t.refundedAmount)>0?e.refundedAmount:t.refundedAmount,i=t.state==="captured"&&I.has(e.state)?e.state:t.state,r=t.referenceId===""?e.referenceId:t.referenceId;return{...t,referenceId:r,refundedAmount:s,state:i}},b=async(e,t,n,s)=>{const i=await e.getSubscriptionStatus(n),r=await t.getSubscription(e.identifier,n);return m(r,i)?(await t.upsertSubscription({...i,createdAt:r?.createdAt??i.createdAt}),u(s,{id:n,kind:"subscription",provider:e.identifier,type:"reconcile.drift"}),!0):!1},w=async(e,t,n,s)=>{const i=await e.getPaymentStatus(n),r=await t.getPaymentSession(e.identifier,n),a=S(r,i);return A(r,a)?(await t.upsertPaymentSession({...a,createdAt:r?.createdAt??a.createdAt}),u(s,{id:n,kind:"payment",provider:e.identifier,type:"reconcile.drift"}),!0):!1},P=async(e,t,n,s)=>{const{reportUsage:i}=e;if(n<=0||i===void 0||!e.capabilities.usageMetering)return{checked:0,failed:0,updated:0};const r=await t.listUnreportedUsage(e.identifier,n);let a=0,c=0;for(const d of r)try{const o=await t.getCustomerByReference(e.identifier,d.referenceId);await i({customerId:o?.id,featureId:d.featureId,idempotencyKey:d.idempotencyKey,quantity:d.quantity,referenceId:d.referenceId}),await t.markUsageReported(e.identifier,d.idempotencyKey),a+=1}catch{c+=1,u(s,{featureId:d.featureId,provider:e.identifier,referenceId:d.referenceId,type:"usage.report_failed"})}return{checked:r.length,failed:c,updated:a}},p=async(e,t,n,s,i)=>{const r=await Promise.allSettled(e.map(d=>n(d)));let a=0,c=0;for(const[d,o]of r.entries())o.status==="fulfilled"?o.value&&(a+=1):(c+=1,u(i,{error:o.reason,id:e[d]??"",kind:t,provider:s.identifier,type:"reconcile.error"}));return{failed:c,updated:a}},U=async e=>{const{adapter:t,observability:n,store:s}=e,i=e.subscriptionIds??[],r=e.paymentSessionIds??[],a=await p(i,"subscription",o=>b(t,s,o,n),t,n),c=await p(r,"payment",o=>w(t,s,o,n),t,n),d=await P(t,s,e.usageReportLimit??l,n);return u(n,{failedPayments:c.failed,failedSubscriptions:a.failed,provider:t.identifier,type:"reconcile.completed",updatedPayments:c.updated,updatedSubscriptions:a.updated}),{checkedPayments:r.length,checkedSubscriptions:i.length,checkedUsage:d.checked,failedPayments:c.failed,failedSubscriptions:a.failed,failedUsage:d.failed,updatedPayments:c.updated,updatedSubscriptions:a.updated,updatedUsage:d.updated}};export{U as reconcile};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/payment",
3
- "version": "1.0.0-alpha.95",
3
+ "version": "1.0.0-alpha.97",
4
4
  "description": "Provider-agnostic payments for Lunora: Stripe-first adapter, webhook sync, and subscription/payment state machine",
5
5
  "keywords": [
6
6
  "billing",
@@ -72,8 +72,8 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@lunora/errors": "1.0.0-alpha.26",
75
- "@lunora/server": "1.0.0-alpha.93",
76
- "@lunora/values": "1.0.0-alpha.33",
75
+ "@lunora/server": "1.0.0-alpha.95",
76
+ "@lunora/values": "1.0.0-alpha.34",
77
77
  "dinero.js": "2.0.2"
78
78
  },
79
79
  "peerDependencies": {
@@ -1 +0,0 @@
1
- const u=(i,e)=>`${i}:${e}`,n=(i,e)=>`${i}:${e}`,a=i=>{const e=i.toSorted((r,o)=>r.createdAt-o.createdAt||r.idempotencyKey.localeCompare(o.idempotencyKey));let s=0;for(const r of e)s=r.mode==="set"?r.quantity:s+r.quantity;return s};class d{customers=new Map;processedEvents=new Set;sessions=new Map;subscriptions=new Map;usageEvents=new Map;getCustomerByReference(e,s){return Promise.resolve(this.customers.get(u(e,s)))}getPaymentSession(e,s){return Promise.resolve(this.sessions.get(n(e,s)))}getSubscription(e,s){return Promise.resolve(this.subscriptions.get(n(e,s)))}listSubscriptionsByReference(e){return Promise.resolve([...this.subscriptions.values()].filter(s=>s.referenceId===e))}markEventProcessed(e,s){const r=n(e,s);return this.processedEvents.has(r)?Promise.resolve(!1):(this.processedEvents.add(r),Promise.resolve(!0))}releaseEvent(e,s){return this.processedEvents.delete(n(e,s)),Promise.resolve()}markUsageReported(e,s){const r=n(e,s),o=this.usageEvents.get(r);return o&&this.usageEvents.set(r,{...o,reportedToProvider:!0}),Promise.resolve()}recordUsage(e){const s=n(e.provider,e.idempotencyKey);return this.usageEvents.has(s)?Promise.resolve(!1):(this.usageEvents.set(s,e),Promise.resolve(!0))}sumUsage(e,s,r){const o=[];for(const t of this.usageEvents.values())t.referenceId===e&&t.featureId===s&&t.createdAt>=r&&o.push(t);return Promise.resolve(a(o))}sumUsageByFeature(e,s,r){const o=new Map(s.map(t=>[t,[]]));for(const t of this.usageEvents.values())t.referenceId===e&&t.createdAt>=r&&o.get(t.featureId)?.push(t);return Promise.resolve(new Map([...o].map(([t,c])=>[t,a(c)])))}upsertCustomer(e){return this.customers.set(u(e.provider,e.referenceId),e),Promise.resolve()}upsertPaymentSession(e){return this.sessions.set(n(e.provider,e.id),e),Promise.resolve()}upsertSubscription(e){return this.subscriptions.set(n(e.provider,e.id),e),Promise.resolve()}}export{d as MemoryPaymentStore,a as foldUsage};
@@ -1 +0,0 @@
1
- import{money as u}from"./addMoney-B4ufWAnD.mjs";import{foldUsage as y}from"./MemoryPaymentStore-C2iTSo5g.mjs";const o=(e,n)=>typeof e[n]=="string"?e[n]:"",A=(e,n)=>typeof e[n]=="string"?e[n]:void 0,c=(e,n)=>typeof e[n]=="number"?e[n]:0,f=(e,n)=>typeof e[n]=="number"?e[n]:void 0,I=(e,n)=>e[n]===!0,p=(e,n)=>{const t=e[n];return typeof t=="bigint"?t:typeof t=="number"||typeof t=="string"?BigInt(t):0n},g=e=>({createdAt:e.createdAt,email:e.email,provider:e.provider,providerCustomerId:e.id,referenceId:e.referenceId}),S=e=>({createdAt:c(e,"createdAt"),email:A(e,"email"),id:o(e,"providerCustomerId"),provider:o(e,"provider"),referenceId:o(e,"referenceId")}),E=e=>({amountMinor:e.amount.minorUnits,capturedMinor:e.capturedAmount.minorUnits,createdAt:e.createdAt,currency:e.amount.currency,provider:e.provider,providerSessionId:e.id,referenceId:e.referenceId,refundedMinor:e.refundedAmount.minorUnits,state:e.state,updatedAt:e.updatedAt}),P=e=>{const n=o(e,"currency");return{amount:u(p(e,"amountMinor"),n),capturedAmount:u(p(e,"capturedMinor"),n),createdAt:c(e,"createdAt"),id:o(e,"providerSessionId"),provider:o(e,"provider"),referenceId:o(e,"referenceId"),refundedAmount:u(p(e,"refundedMinor"),n),state:o(e,"state"),updatedAt:c(e,"updatedAt")}},l=e=>({cancelAtPeriodEnd:e.cancelAtPeriodEnd,createdAt:e.createdAt,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart,priceId:e.priceId,provider:e.provider,providerSubscriptionId:e.id,quantity:e.quantity,referenceId:e.referenceId,state:e.state,updatedAt:e.updatedAt}),m=e=>({cancelAtPeriodEnd:I(e,"cancelAtPeriodEnd"),createdAt:c(e,"createdAt"),currentPeriodEnd:f(e,"currentPeriodEnd"),currentPeriodStart:f(e,"currentPeriodStart"),id:o(e,"providerSubscriptionId"),priceId:o(e,"priceId"),provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),state:o(e,"state"),updatedAt:c(e,"updatedAt")}),M=e=>({createdAt:e.createdAt,featureId:e.featureId,idempotencyKey:e.idempotencyKey,...e.mode==="set"?{mode:"set"}:{},provider:e.provider,quantity:e.quantity,referenceId:e.referenceId,reportedToProvider:e.reportedToProvider}),T=e=>{const n=async(t,d,r)=>{const a=await e.findFirst(t,d);if(a){await e.patch(a._id,r);return}await e.insert(t,r)};return{getCustomerByReference:async(t,d)=>{const r=await e.findFirst("customers",{provider:t,referenceId:d});return r?S(r):void 0},getPaymentSession:async(t,d)=>{const r=await e.findFirst("paymentSessions",{provider:t,providerSessionId:d});return r?P(r):void 0},getSubscription:async(t,d)=>{const r=await e.findFirst("subscriptions",{provider:t,providerSubscriptionId:d});return r?m(r):void 0},listSubscriptionsByReference:async t=>(await e.findMany("subscriptions",{referenceId:t})).map(r=>m(r)),markEventProcessed:async(t,d)=>await e.findFirst("events",{provider:t,providerEventId:d})?!1:(await e.insert("events",{processedAt:Date.now(),provider:t,providerEventId:d,type:""}),!0),releaseEvent:async(t,d)=>{const r=await e.findFirst("events",{provider:t,providerEventId:d});r&&await e.delete(r._id)},markUsageReported:async(t,d)=>{const r=await e.findFirst("usageEvents",{idempotencyKey:d,provider:t});r&&await e.patch(r._id,{reportedToProvider:!0})},recordUsage:async t=>await e.findFirst("usageEvents",{idempotencyKey:t.idempotencyKey,provider:t.provider})?!1:(await e.insert("usageEvents",M(t)),!0),sumUsage:async(t,d,r)=>{const s=(await e.findMany("usageEvents",{featureId:d,referenceId:t})).filter(i=>c(i,"createdAt")>=r).map(i=>({createdAt:c(i,"createdAt"),idempotencyKey:typeof i.idempotencyKey=="string"?i.idempotencyKey:"",mode:i.mode==="set"?"set":"add",quantity:c(i,"quantity")}));return y(s)},sumUsageByFeature:async(t,d,r)=>{const a=await e.findMany("usageEvents",{referenceId:t}),s=new Map(d.map(i=>[i,[]]));for(const i of a)c(i,"createdAt")<r||s.get(typeof i.featureId=="string"?i.featureId:"")?.push({createdAt:c(i,"createdAt"),idempotencyKey:typeof i.idempotencyKey=="string"?i.idempotencyKey:"",mode:i.mode==="set"?"set":"add",quantity:c(i,"quantity")});return new Map([...s].map(([i,v])=>[i,y(v)]))},upsertCustomer:async t=>n("customers",{provider:t.provider,referenceId:t.referenceId},g(t)),upsertPaymentSession:async t=>n("paymentSessions",{provider:t.provider,providerSessionId:t.id},E(t)),upsertSubscription:async t=>n("subscriptions",{provider:t.provider,providerSubscriptionId:t.id},l(t))}};export{T as createDatabasePaymentStore};
@@ -1 +0,0 @@
1
- import{createPayment as n}from"./createPayment-Dmh6IWLZ.mjs";import{createDatabasePaymentStore as i}from"./createDatabasePaymentStore-BlMXuCW_.mjs";const s=r=>({delete:async t=>r.delete(t),findFirst:async(t,e)=>await r.findFirst(t,{where:e}),findMany:async(t,e)=>(await r.findMany(t,{where:e})).page,insert:async(t,e)=>r.insert(t,e),patch:async(t,e)=>r.patch(t,e)}),m=(r,t)=>{const e=r.auth?.userId??void 0;return n({adapter:t.adapter,authorize:t.authorize??(a=>a.trim()!==""&&e!==void 0&&a===e),entitlements:t.entitlements,observability:t.observability,store:i(s(r.db))})};export{s as lunoraDatabaseToPaymentDatabase,m as paymentsFromContext};
@@ -1 +0,0 @@
1
- import{compareMoney as m}from"./addMoney-B4ufWAnD.mjs";import{n as c}from"./observability-BteZ2dJS.mjs";const f=(t,e)=>t.currency===e.currency&&m(t,e)===0,l=(t,e)=>t?.state!==e.state||t.cancelAtPeriodEnd!==e.cancelAtPeriodEnd||t.currentPeriodEnd!==e.currentPeriodEnd||t.priceId!==e.priceId||t.quantity!==e.quantity,y=(t,e)=>t?.state!==e.state||!f(t.capturedAmount,e.capturedAmount)||!f(t.refundedAmount,e.refundedAmount),A=new Set(["partially_refunded","refunded"]),S=(t,e)=>{if(!t)return e;const a=t.refundedAmount.currency===e.refundedAmount.currency&&m(t.refundedAmount,e.refundedAmount)>0?t.refundedAmount:e.refundedAmount,d=e.state==="captured"&&A.has(t.state)?t.state:e.state,r=e.referenceId===""?t.referenceId:e.referenceId;return{...e,referenceId:r,refundedAmount:a,state:d}},b=async(t,e,n,a)=>{const d=await t.getSubscriptionStatus(n),r=await e.getSubscription(t.identifier,n);return l(r,d)?(await e.upsertSubscription({...d,createdAt:r?.createdAt??d.createdAt}),c(a,{id:n,kind:"subscription",provider:t.identifier,type:"reconcile.drift"}),!0):!1},P=async(t,e,n,a)=>{const d=await t.getPaymentStatus(n),r=await e.getPaymentSession(t.identifier,n),s=S(r,d);return y(r,s)?(await e.upsertPaymentSession({...s,createdAt:r?.createdAt??s.createdAt}),c(a,{id:n,kind:"payment",provider:t.identifier,type:"reconcile.drift"}),!0):!1},p=async(t,e,n,a,d)=>{const r=await Promise.allSettled(t.map(i=>n(i)));let s=0,o=0;for(const[i,u]of r.entries())u.status==="fulfilled"?u.value&&(s+=1):(o+=1,c(d,{error:u.reason,id:t[i]??"",kind:e,provider:a.identifier,type:"reconcile.error"}));return{failed:o,updated:s}},v=async t=>{const{adapter:e,observability:n,store:a}=t,d=t.subscriptionIds??[],r=t.paymentSessionIds??[],s=await p(d,"subscription",i=>b(e,a,i,n),e,n),o=await p(r,"payment",i=>P(e,a,i,n),e,n);return c(n,{failedPayments:o.failed,failedSubscriptions:s.failed,provider:e.identifier,type:"reconcile.completed",updatedPayments:o.updated,updatedSubscriptions:s.updated}),{checkedPayments:r.length,checkedSubscriptions:d.length,failedPayments:o.failed,failedSubscriptions:s.failed,updatedPayments:o.updated,updatedSubscriptions:s.updated}};export{v as reconcile};