@lunora/payment 1.0.0-alpha.96 → 1.0.0-alpha.98
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 +73 -2
- package/dist/index.d.ts +73 -2
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/MemoryPaymentStore-Cu9J-zf9.mjs +1 -0
- package/dist/packem_shared/constantTimeEqual-D_ynru-O.mjs +1 -0
- package/dist/packem_shared/createDatabasePaymentStore-CVrAhWjP.mjs +1 -0
- package/dist/packem_shared/lunoraDatabaseToPaymentDatabase-5WkBd3aZ.mjs +1 -0
- package/dist/packem_shared/reconcile-Cm45pURT.mjs +1 -0
- package/dist/providers/autumn.mjs +1 -1
- package/dist/providers/creem.mjs +1 -1
- package/dist/providers/dodopayments.mjs +1 -1
- package/dist/providers/polar.mjs +1 -1
- package/package.json +4 -4
- package/dist/packem_shared/MemoryPaymentStore-C2iTSo5g.mjs +0 -1
- package/dist/packem_shared/constantTimeEqual-Bj5tU-zT.mjs +0 -1
- package/dist/packem_shared/createDatabasePaymentStore-BlMXuCW_.mjs +0 -1
- package/dist/packem_shared/lunoraDatabaseToPaymentDatabase-DBveGQxx.mjs +0 -1
- package/dist/packem_shared/reconcile-Dq48_gHU.mjs +0 -1
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
|
-
|
|
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
|
-
|
|
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-
|
|
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-D_ynru-O.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{LunoraPaymentError as s}from"./LunoraPaymentError-BSxyhWgu.mjs";const m=e=>{let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)},l=e=>{const t=atob(e),n=new Uint8Array(t.length);for(let o=0;o<t.length;o+=1)n[o]=t.codePointAt(o)??0;return n},I=(e,t)=>{const n=Math.max(e.length,t.length);let o=e.length^t.length;for(let r=0;r<n;r+=1){const a=r<e.length?e.charCodeAt(r):0,i=r<t.length?t.charCodeAt(r):0;o|=a^i}return o===0},h=new TextEncoder,f=e=>[...new Uint8Array(e)].map(t=>t.toString(16).padStart(2,"0")).join(""),d="whsec_",A=async(e,t)=>{const n=await crypto.subtle.importKey("raw",e,{hash:"SHA-256",name:"HMAC"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",n,h.encode(t));return m(new Uint8Array(o))},g=I,y=async(e,t)=>{const n=await crypto.subtle.importKey("raw",h.encode(e),{hash:"SHA-256",name:"HMAC"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",n,h.encode(t));return f(o)},u=async e=>{if(!e.secret)throw new s("CONFIG_INVALID","webhook secret not configured");const t=e.toleranceSeconds??300,n=e.now??Date.now(),o=Number(e.webhookTimestamp);if(!e.webhookId||!e.webhookSignature||!Number.isFinite(o))throw new s("WEBHOOK_SIGNATURE_INVALID","missing standard-webhooks headers");if(Math.abs(Math.floor(n/1e3)-o)>t)throw new s("WEBHOOK_TIMESTAMP_INVALID","signature timestamp outside tolerance");const r=e.secret.startsWith(d)?e.secret.slice(d.length):e.secret;if(!r)throw new s("CONFIG_INVALID","webhook secret not configured");const a=l(r);if(a.length===0)throw new s("CONFIG_INVALID","webhook secret not configured");const i=await A(a,`${e.webhookId}.${e.webhookTimestamp}.${e.payload}`);if(!e.webhookSignature.split(" ").map(c=>{const w=c.indexOf(",");return w===-1?"":c.slice(w+1)}).filter(Boolean).some(c=>g(c,i)))throw new s("WEBHOOK_SIGNATURE_INVALID","no matching signature")},N=async e=>{if(!e.secret)throw new s("CONFIG_INVALID","webhook secret not configured");if(!e.signature)throw new s("WEBHOOK_SIGNATURE_INVALID","missing creem-signature header");const t=await y(e.secret,e.payload);if(!g(e.signature,t))throw new s("WEBHOOK_SIGNATURE_INVALID","no matching signature")};export{g as constantTimeEqual,y as hmacSha256Hex,N as verifyCreemSignature,u as verifyStandardWebhook};
|
|
@@ -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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraPaymentError as O}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{a as i,r as x,c as o,d as p,e as u}from"../packem_shared/json-BJJPJVYj.mjs";import{money as h}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as C}from"../packem_shared/constantTimeEqual-
|
|
1
|
+
import{LunoraPaymentError as O}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{a as i,r as x,c as o,d as p,e as u}from"../packem_shared/json-BJJPJVYj.mjs";import{money as h}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as C}from"../packem_shared/constantTimeEqual-D_ynru-O.mjs";import{m as D}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as E}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const P={active:"active",canceled:"canceled",expired:"canceled",past_due:"past_due",scheduled:"paused",trialing:"trialing"},N={activated:"active",canceled:"canceled",cancelled:"canceled",expired:"canceled",scheduled:"paused"},w="::",y=D("autumn"),f=(a,c)=>`${a}${w}${c}`,g=a=>{const c=a.lastIndexOf(w);if(c===-1)throw new O("PROVIDER_ERROR",`malformed autumn subscription id "${a}" (expected "<customerId>::<productId>")`);return{customerId:a.slice(0,c),productId:a.slice(c+w.length)}},A=a=>u(a,"canceled_at","canceledAt")!==void 0||o(a,"status")==="scheduled",k=(a,c)=>{const r=Date.now(),e=o(c,"id","product_id","productId","plan_id","planId")??"",n=o(c,"status")??"active",t=p(c,"past_due")??p(c,"pastDue")??!1;return{cancelAtPeriodEnd:A(c),createdAt:r,currentPeriodEnd:u(c,"current_period_end","currentPeriodEnd")??void 0,currentPeriodStart:u(c,"current_period_start","currentPeriodStart")??void 0,id:f(a,e),priceId:e,provider:"autumn",quantity:u(c,"quantity")??1,referenceId:a,state:t?"past_due":P[n]??"past_due",updatedAt:r}},S=a=>Array.isArray(a)?a.map(c=>i(c)):[],T=(a,c)=>[...S(a.products),...S(a.subscriptions)].find(e=>(o(e,"id","product_id","productId","plan_id","planId")??"")===c),b=a=>({balance:u(a,"remaining","balance"),limit:u(a,"granted","included_usage","limit"),unlimited:p(a,"unlimited")??!1,used:u(a,"usage","used")}),R=(a,c,r,e)=>{const n=Date.now();return{cancelAtPeriodEnd:e,createdAt:n,id:f(a,c),priceId:c,provider:"autumn",quantity:1,referenceId:a,state:r,updatedAt:n}},v=async(a,c,r)=>{const e=i(await a.customers.get({customerId:c})),n=T(e,r);return n?k(c,n):R(c,r,"canceled",!1)},I=a=>o(a,"customer_id","customerId"),q=(a,c)=>{const r={eventId:a,provider:"autumn",raw:{object:c,type:"billing.updated"}},e=I(c),n=S(c.plan_changes)[0]??c,t=n.subscription?i(n.subscription):n,d=o(t,"plan_id","planId","product_id","productId","id"),s=o(t,"status"),m=o(n,"action"),l=p(t,"past_due")??p(t,"pastDue")??!1,_=s===void 0?void 0:P[s],U=m===void 0?void 0:N[m],B=l?"past_due":_??U;return{...r,cancelAtPeriodEnd:p(t,"cancel_at_period_end")??A(t),currentPeriodEnd:u(t,"current_period_end","currentPeriodEnd"),currentPeriodStart:u(t,"current_period_start","currentPeriodStart"),customerId:e,priceId:d,referenceId:e,subscriptionId:e===void 0||d===void 0?void 0:f(e,d),type:E(B)}},M=(a,c,r)=>{const e={eventId:a,provider:"autumn",raw:{object:r,type:c}},n=o(r,"currency")??"usd";switch(c){case"billing.auto_topup_succeeded":{const t=i(r.invoice),d=u(t,"total","amount")??u(r,"total","amount"),s=o(t,"currency")??n;return{...e,amount:d===void 0?void 0:h(BigInt(Math.round(d)),s),customerId:I(r),referenceId:I(r),sessionId:o(t,"id","stripe_id","invoice_id")??o(r,"id"),type:"payment.captured"}}case"billing.updated":return q(a,r);case"customer.product.added":case"customer.product.canceled":case"customer.product.expired":case"customer.product.updated":case"product.attached":{const t=r.product?i(r.product):r,d=c==="customer.product.canceled"||c==="customer.product.expired"?"canceled":o(t,"status"),s=I(r)??I(t);return{...e,cancelAtPeriodEnd:p(t,"cancel_at_period_end")??A(t),currentPeriodEnd:u(t,"current_period_end","currentPeriodEnd"),currentPeriodStart:u(t,"current_period_start","currentPeriodStart"),customerId:s,priceId:o(t,"id","product_id","productId"),referenceId:s,subscriptionId:s===void 0?void 0:f(s,o(t,"id","product_id","productId")??""),type:E(P[d??""]??"past_due")}}case"invoice.paid":case"payment.succeeded":{const t=u(r,"total","amount","amount_paid");return{...e,amount:t===void 0?void 0:h(BigInt(Math.round(t)),n),customerId:I(r),referenceId:I(r),sessionId:o(r,"id","invoice_id","stripe_id"),type:"payment.captured"}}default:return{...e,type:"unhandled"}}},$=a=>o(a,"checkout_url","checkoutUrl","payment_url","paymentUrl","url")??"",z=a=>{const{webhookSecret:c}=a,r=a.client;return{cancelPayment:()=>y("manual payment cancellation"),cancelSubscription:async(e,n)=>{const{customerId:t,productId:d}=g(e);return await r.billing.update({cancelAction:n?.atPeriodEnd?"cancel_end_of_cycle":"cancel_immediately",customerId:t,planId:d}),n?.atPeriodEnd?{...await v(r,t,d),cancelAtPeriodEnd:!0}:R(t,d,"canceled",!1)},capabilities:{merchantOfRecord:!1,portal:!0,usageMetering:!0},capturePayment:e=>y("manual capture"),checkEntitlement:async e=>{if(e.featureId===void 0){const m=i(await r.customers.get({customerId:e.referenceId})),l=T(m,e.priceId??""),_=l?k(e.referenceId,l).state:void 0;return{allowed:_==="active"||_==="trialing",unlimited:!1}}const n=i(await r.check({customerId:e.referenceId,featureId:e.featureId,requiredBalance:e.quantity??1})),t=p(n,"allowed")??!1,d=n.balance,s=typeof d=="object"&&d!==null?i(d):n;return{allowed:t,...b(s)}},createCheckout:async e=>{const n=i(await r.billing.attach({customerId:e.referenceId,planId:e.priceId}));return{id:f(e.referenceId,e.priceId),provider:"autumn",url:$(n)}},createPortalSession:async e=>{const n=i(await r.billing.openCustomerPortal({customerId:e.customerId}));return{url:o(n,"url")??""}},getBalances:async e=>{const n=i(await r.customers.get({customerId:e})),t=i(n.balances??n.features);return Object.entries(t).map(([d,s])=>{const m=i(s),l=b(m);return{allowed:l.unlimited||(l.balance??0)>0,featureId:o(m,"featureId","feature_id")??d,...l}})},getOrCreateCustomer:async e=>{const n=i(await r.customers.getOrCreate({customerId:e.referenceId,email:e.email,name:e.metadata?.name}));return{createdAt:Date.now(),email:o(n,"email")??e.email,id:o(n,"id")??e.referenceId,provider:"autumn",referenceId:e.referenceId}},getPaymentStatus:()=>y("payment-session reconciliation"),getSubscriptionStatus:async e=>{const{customerId:n,productId:t}=g(e);return v(r,n,t)},identifier:"autumn",parseWebhook:async({headers:e,payload:n})=>{const t=e.get("svix-id")??e.get("webhook-id")??"";await C({payload:n,secret:c,toleranceSeconds:a.webhookToleranceSeconds,webhookId:t,webhookSignature:e.get("svix-signature")??e.get("webhook-signature")??"",webhookTimestamp:e.get("svix-timestamp")??e.get("webhook-timestamp")??""});const d=i(JSON.parse(n));return M(t,x(d,"type")??"",i(d.data))},refundPayment:()=>y("refunds"),reportUsage:async e=>{await r.track({customerId:e.referenceId,featureId:e.featureId,value:e.quantity})},resumeSubscription:async e=>{const{customerId:n,productId:t}=g(e);return await r.billing.update({cancelAction:"uncancel",customerId:n,planId:t}),{...await v(r,n,t),cancelAtPeriodEnd:!1}},updateSubscription:async(e,n)=>{const{customerId:t,productId:d}=g(e),s=n.priceId??d;return await r.billing.attach({customerId:t,planId:s}),v(r,t,s)}}};export{z as createAutumnAdapter};
|
package/dist/providers/creem.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as o,c as u,f as m,r as c,b as p,p as f,d as v,e as w}from"../packem_shared/json-BJJPJVYj.mjs";import{money as I,zeroMoney as g}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyCreemSignature as A}from"../packem_shared/constantTimeEqual-
|
|
1
|
+
import{a as o,c as u,f as m,r as c,b as p,p as f,d as v,e as w}from"../packem_shared/json-BJJPJVYj.mjs";import{money as I,zeroMoney as g}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyCreemSignature as A}from"../packem_shared/constantTimeEqual-D_ynru-O.mjs";import{m as E}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as P}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const k=/already exists/iu,C={canceled:"canceled",completed:"captured",expired:"canceled",paid:"captured",partially_refunded:"partially_refunded",pending:"initiated",refunded:"refunded"},h={active:"active",canceled:"canceled",cancelled:"canceled",expired:"canceled",incomplete:"past_due",paid:"active",past_due:"past_due",paused:"paused",scheduled_cancel:"active",trialing:"trialing",unpaid:"past_due"},_=E("creem (merchant-of-record)"),i=a=>typeof a=="string"?a:c(o(a),"id"),T=a=>u(a,"checkout_url","checkoutUrl")??"",S=a=>u(a,"canceled_at","canceledAt")!==void 0||c(a,"status")==="scheduled_cancel",b=a=>{if(!(a instanceof Error))return!1;const{statusCode:r}=a;return typeof r=="number"&&r!==400&&r!==409?!1:k.test(a.message)},l=a=>{const r=o(a),t=Date.now(),e=c(r,"status")??"";return{cancelAtPeriodEnd:S(r),createdAt:t,currentPeriodEnd:f(u(r,"current_period_end_date","currentPeriodEndDate")),currentPeriodStart:f(u(r,"current_period_start_date","currentPeriodStartDate")),id:c(r,"id")??"",priceId:i(r.product)??"",provider:"creem",quantity:p(r,"units")??1,referenceId:m(r)??i(r.customer)??"",state:h[e]??"past_due",updatedAt:t}},B=a=>{const r=o(a),t=Date.now(),e=o(r.order),n=c(e,"currency")??c(r,"currency")??"usd",s=I(BigInt(Math.round(p(e,"amount")??p(r,"amount")??0)),n),d=C[c(e,"status")??c(r,"status")??""]??"initiated";return{amount:s,capturedAmount:d==="captured"||d==="partially_refunded"||d==="refunded"?s:g(n),createdAt:t,id:c(r,"id")??"",provider:"creem",referenceId:m(r)??"",refundedAmount:d==="refunded"?s:g(n),state:d,updatedAt:t}},D=(a,r,t)=>{const e={eventId:a,provider:"creem",raw:{object:t,type:r}},n=o(t.order),s=c(n,"currency")??c(t,"currency")??"usd";switch(r){case"checkout.completed":{const d=p(n,"amount")??p(t,"amount");return{...e,amount:d===void 0?void 0:I(BigInt(Math.round(d)),s),customerId:i(t.customer),referenceId:m(t),sessionId:c(t,"id"),subscriptionId:i(t.subscription),type:"payment.captured"}}case"refund.created":{const d=w(t,"refund_amount","refundAmount","amount")??p(n,"amount"),y=u(t,"refund_currency","refundCurrency")??s;return{...e,amount:d===void 0?void 0:I(BigInt(Math.round(d)),y),referenceId:m(t),sessionId:i(t.transaction)??i(t.subscription)??i(t.order)??i(t.checkout)??c(t,"id"),type:"payment.refunded"}}case"subscription.active":case"subscription.canceled":case"subscription.expired":case"subscription.paid":case"subscription.past_due":case"subscription.paused":case"subscription.scheduled_cancel":case"subscription.trialing":case"subscription.unpaid":case"subscription.update":{const d=r==="subscription.scheduled_cancel"?"scheduled_cancel":c(t,"status");return{...e,cancelAtPeriodEnd:v(t,"cancel_at_period_end")??S(t),currentPeriodEnd:f(u(t,"current_period_end_date","currentPeriodEndDate")),currentPeriodStart:f(u(t,"current_period_start_date","currentPeriodStartDate")),customerId:i(t.customer),priceId:i(t.product),referenceId:m(t)??i(t.customer),subscriptionId:c(t,"id"),type:P(h[d??""]??"past_due")}}default:return{...e,type:"unhandled"}}},O=a=>{const{webhookSecret:r}=a,t=a.client;return{cancelPayment:()=>_("manual payment cancellation"),cancelSubscription:async(e,n)=>l(await t.subscriptions.cancel(e,{mode:n?.atPeriodEnd===!0?"scheduled":"immediate"})),capabilities:{merchantOfRecord:!0,portal:!0,usageMetering:!1},capturePayment:e=>_("manual capture"),createCheckout:async e=>{const n=await t.checkouts.create({customer:e.customerId?{id:e.customerId}:void 0,metadata:{...e.metadata,referenceId:e.referenceId},productId:e.priceId,requestId:e.idempotencyKey,successUrl:e.successUrl,units:e.quantity});return{id:c(n,"id")??"",provider:"creem",url:T(n)}},createPortalSession:async e=>{const n=await t.customers.generateBillingLinks({customerId:e.customerId});return{url:u(n,"customer_portal_link","customerPortalLink")??""}},getOrCreateCustomer:async e=>{const n=s=>({createdAt:Date.now(),email:c(s,"email")??e.email,id:c(s,"id")??"",provider:"creem",referenceId:e.referenceId});try{return n(o(await t.customers.create({email:e.email??"",metadata:{...e.metadata,referenceId:e.referenceId},name:e.metadata?.name??e.referenceId})))}catch(s){if(e.email!==void 0&&b(s)){const d=o(await t.customers.retrieve(void 0,e.email));if(m(d)!==e.referenceId)throw new Error(`Creem customer for email "${e.email}" already belongs to a different reference; refusing to bind it to "${e.referenceId}".`,{cause:s});return n(d)}throw s}},getPaymentStatus:async e=>B(await t.checkouts.retrieve(e)),getSubscriptionStatus:async e=>l(await t.subscriptions.get(e)),identifier:"creem",parseWebhook:async({headers:e,payload:n})=>{await A({payload:n,secret:r,signature:e.get("creem-signature")??""});const s=o(JSON.parse(n));return D(u(s,"id","event_id","eventId")??"",u(s,"eventType","type")??"",o(s.object))},refundPayment:()=>_("programmatic refunds"),resumeSubscription:async e=>l(await t.subscriptions.resume(e)),updateSubscription:async(e,n)=>n.priceId?l(await t.subscriptions.upgrade(e,{productId:n.priceId,updateBehavior:"proration-charge-immediately"})):l(await t.subscriptions.get(e))}};export{O as createCreemAdapter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraPaymentError as v}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as S}from"../packem_shared/idempotencyKey-BjxjMMna.mjs";import{a as i,b as o,r as a,f as u,p as l,d as w}from"../packem_shared/json-BJJPJVYj.mjs";import{money as _,zeroMoney as f}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as b}from"../packem_shared/constantTimeEqual-
|
|
1
|
+
import{LunoraPaymentError as v}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as S}from"../packem_shared/idempotencyKey-BjxjMMna.mjs";import{a as i,b as o,r as a,f as u,p as l,d as w}from"../packem_shared/json-BJJPJVYj.mjs";import{money as _,zeroMoney as f}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as b}from"../packem_shared/constantTimeEqual-D_ynru-O.mjs";import{m as h}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as A}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const P={cancelled:"canceled",failed:"failed",partially_captured:"captured",partially_captured_and_capturable:"captured",processing:"initiated",requires_capture:"authorized",requires_confirmation:"initiated",requires_customer_action:"authorized",requires_merchant_action:"authorized",requires_payment_method:"initiated",succeeded:"captured"},g={active:"active",cancelled:"canceled",expired:"canceled",failed:"past_due",on_hold:"past_due",paused:"paused",pending:"past_due"},I=h("dodopayments (merchant-of-record)"),p=d=>a(i(d.customer),"customer_id")??a(d,"customer_id"),m=d=>{const n=i(d),t=Date.now(),e=a(n,"status")??"";return{cancelAtPeriodEnd:w(n,"cancel_at_next_billing_date")??!1,createdAt:t,currentPeriodEnd:l(a(n,"next_billing_date")),currentPeriodStart:l(a(n,"previous_billing_date")),id:a(n,"subscription_id")??"",priceId:a(n,"product_id")??"",provider:"dodopayments",quantity:o(n,"quantity")??1,referenceId:u(n)??p(n)??"",state:g[e]??"past_due",updatedAt:t}},q=d=>{const n=i(d),t=Date.now(),e=a(n,"currency")??"usd",r=_(BigInt(Math.round(o(n,"total_amount")??0)),e),s=P[a(n,"status")??""]??"initiated";return{amount:r,capturedAmount:s==="captured"?r:f(e),createdAt:t,id:a(n,"payment_id")??"",provider:"dodopayments",referenceId:u(n)??"",refundedAmount:f(e),state:s,updatedAt:t}},k=(d,n,t)=>{const e={eventId:d,provider:"dodopayments",raw:{object:t,type:n}},r=a(t,"currency")??"usd";switch(n){case"dispute.lost":case"refund.succeeded":return{...e,amount:_(BigInt(Math.round(o(t,"amount")??0)),r),referenceId:u(t),sessionId:a(t,"payment_id"),type:"payment.refunded"};case"payment.cancelled":case"payment.failed":return{...e,referenceId:u(t),sessionId:a(t,"payment_id"),type:"payment.failed"};case"payment.succeeded":return{...e,amount:_(BigInt(Math.round(o(t,"total_amount")??0)),r),customerId:p(t),referenceId:u(t),sessionId:a(t,"payment_id"),subscriptionId:a(t,"subscription_id"),type:"payment.captured"};case"subscription.active":case"subscription.cancelled":case"subscription.expired":case"subscription.failed":case"subscription.on_hold":case"subscription.paused":case"subscription.plan_changed":case"subscription.renewed":case"subscription.updated":{const s=a(t,"status");return{...e,cancelAtPeriodEnd:w(t,"cancel_at_next_billing_date"),currentPeriodEnd:l(a(t,"next_billing_date")),currentPeriodStart:l(a(t,"previous_billing_date")),customerId:p(t),priceId:a(t,"product_id"),quantity:o(t,"quantity"),referenceId:u(t)??p(t),subscriptionId:a(t,"subscription_id"),type:A(g[s??""]??"past_due")}}default:return{...e,type:"unhandled"}}},R=d=>{const{webhookSecret:n}=d,t=d.client;return{cancelPayment:()=>I("manual payment cancellation"),cancelSubscription:async(e,r)=>{const s=r?.atPeriodEnd?{cancel_at_next_billing_date:!0}:{status:"cancelled"};return m(await t.subscriptions.update(e,s))},capabilities:{merchantOfRecord:!0,portal:!0,usageMetering:!0},capturePayment:e=>I("manual capture"),createCheckout:async e=>{const r=i(await t.checkoutSessions.create({customer:e.customerId?{customer_id:e.customerId}:void 0,metadata:{...e.metadata,referenceId:e.referenceId},product_cart:[{product_id:e.priceId,quantity:e.quantity??1}],return_url:e.successUrl}));return{id:a(r,"session_id")??"",provider:"dodopayments",url:a(r,"checkout_url")??""}},createPortalSession:async e=>{const r=i(await t.customers.customerPortal.create(e.customerId,{return_url:e.returnUrl}));return{url:a(r,"link")??a(r,"url")??""}},getOrCreateCustomer:async e=>{const r=i(await t.customers.create({email:e.email??"",name:e.metadata?.name??e.referenceId},{idempotencyKey:S("customer","dodopayments",e.referenceId)}));return{createdAt:Date.now(),email:a(r,"email")??e.email,id:a(r,"customer_id")??"",provider:"dodopayments",referenceId:e.referenceId}},getPaymentStatus:async e=>q(await t.payments.retrieve(e)),getSubscriptionStatus:async e=>m(await t.subscriptions.retrieve(e)),identifier:"dodopayments",parseWebhook:async({headers:e,payload:r})=>{const s=e.get("webhook-id")??"";await b({payload:r,secret:n,toleranceSeconds:d.webhookToleranceSeconds,webhookId:s,webhookSignature:e.get("webhook-signature")??"",webhookTimestamp:e.get("webhook-timestamp")??""});const c=i(JSON.parse(r));return k(s,a(c,"type")??"",i(c.data))},refundPayment:async e=>{if(e.amount!==void 0)throw new v("PROVIDER_ERROR","dodopayments refunds a payment in full; partial refunds require line items and aren't supported here");const r=i(await t.refunds.create({payment_id:e.sessionId,reason:e.reason})),s=a(r,"currency")??"usd",c=_(BigInt(Math.round(o(r,"amount")??0)),s);let y="captured";return a(r,"status")==="succeeded"&&(y="refunded"),{amount:c,capturedAmount:c,createdAt:Date.now(),id:e.sessionId,provider:"dodopayments",referenceId:"",refundedAmount:c,state:y,updatedAt:Date.now()}},reportUsage:async e=>{await t.usageEvents.ingest({events:[{customer_id:e.customerId??e.referenceId,event_id:e.idempotencyKey,event_name:e.featureId,metadata:{value:e.quantity},timestamp:e.timestamp===void 0?void 0:new Date(e.timestamp).toISOString()}]})},resumeSubscription:async e=>{const r=await t.subscriptions.update(e,{cancel_at_next_billing_date:!1});return m(r)},updateSubscription:async(e,r)=>{if(r.priceId!==void 0||r.quantity!==void 0){const s=i(r.priceId!==void 0&&r.quantity!==void 0?void 0:await t.subscriptions.retrieve(e));await t.subscriptions.changePlan(e,{product_id:r.priceId??a(s,"product_id")??"",proration_billing_mode:"prorated_immediately",quantity:r.quantity??o(s,"quantity")??1})}return m(await t.subscriptions.retrieve(e))}}};export{R as createDodoPaymentsAdapter};
|
package/dist/providers/polar.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as p,r as n,f as c,d as w,p as f,b as i}from"../packem_shared/json-BJJPJVYj.mjs";import{money as m,zeroMoney as I}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as P}from"../packem_shared/constantTimeEqual-
|
|
1
|
+
import{a as p,r as n,f as c,d as w,p as f,b as i}from"../packem_shared/json-BJJPJVYj.mjs";import{money as m,zeroMoney as I}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as P}from"../packem_shared/constantTimeEqual-D_ynru-O.mjs";import{m as g}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as b}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const y=s=>s instanceof Date?s.getTime():f(typeof s=="string"?s:void 0),S={draft:"initiated",paid:"captured",partially_refunded:"partially_refunded",pending:"initiated",refunded:"refunded",void:"canceled"},A={active:"active",canceled:"canceled",incomplete:"past_due",incomplete_expired:"canceled",past_due:"past_due",trialing:"trialing",unpaid:"past_due"},_=g("polar (merchant-of-record)"),u=s=>{const a=p(s),r=Date.now();return{cancelAtPeriodEnd:w(a,"cancelAtPeriodEnd")??!1,createdAt:r,currentPeriodEnd:y(a.currentPeriodEnd),currentPeriodStart:y(a.currentPeriodStart),id:n(a,"id")??"",priceId:n(a,"productId")??"",provider:"polar",quantity:1,referenceId:c(a)??"",state:A[n(a,"status")??""]??"past_due",updatedAt:r}},v=s=>{const a=p(s),r=Date.now(),e=n(a,"currency")??"usd",t=m(BigInt(Math.round(i(a,"totalAmount")??i(a,"amount")??0)),e),d=S[n(a,"status")??""]??"initiated";return{amount:t,capturedAmount:d==="captured"||d==="partially_refunded"||d==="refunded"?t:I(e),createdAt:r,id:n(a,"id")??"",provider:"polar",referenceId:c(a)??"",refundedAmount:d==="refunded"?t:I(e),state:d,updatedAt:r}},h=(s,a,r)=>{const e={eventId:s,provider:"polar",raw:{object:r,type:a}},t=n(r,"currency")??"usd";switch(a){case"order.created":case"order.paid":return a==="order.created"&&S[n(r,"status")??""]!=="captured"?{...e,type:"unhandled"}:{...e,amount:m(i(r,"total_amount")??i(r,"amount")??0,t),customerId:n(r,"customer_id"),referenceId:c(r),sessionId:n(r,"id"),subscriptionId:n(r,"subscription_id"),type:"payment.captured"};case"refund.created":return{...e,amount:m(i(r,"amount")??0,t),referenceId:c(r),sessionId:n(r,"order_id")??n(r,"id"),type:"payment.refunded"};case"subscription.active":case"subscription.canceled":case"subscription.created":case"subscription.revoked":case"subscription.uncanceled":case"subscription.updated":{const d=a==="subscription.revoked"?"subscription.canceled":b(A[n(r,"status")??""]??"past_due");return{...e,cancelAtPeriodEnd:w(r,"cancel_at_period_end"),currentPeriodEnd:f(n(r,"current_period_end")),currentPeriodStart:f(n(r,"current_period_start")),customerId:n(r,"customer_id"),priceId:n(r,"product_id"),referenceId:c(r),subscriptionId:n(r,"id"),type:d}}default:return{...e,type:"unhandled"}}},M=s=>{const{webhookSecret:a}=s,r=s.client;return{cancelPayment:()=>_("manual payment cancellation"),cancelSubscription:async(e,t)=>{const d=t?.atPeriodEnd?await r.subscriptions.update({id:e,subscriptionUpdate:{cancelAtPeriodEnd:!0}}):await r.subscriptions.revoke({id:e});return u(d)},capabilities:{merchantOfRecord:!0,portal:!0,usageMetering:!0},capturePayment:e=>_("manual capture"),createCheckout:async e=>{const t=await r.checkouts.create({customerEmail:e.customerId?void 0:e.email,customerId:e.customerId,externalCustomerId:e.referenceId,metadata:{...e.metadata,referenceId:e.referenceId},products:[e.priceId],returnUrl:e.cancelUrl,successUrl:e.successUrl});return{id:t.id,provider:"polar",url:t.url}},createPortalSession:async e=>({url:(await r.customerSessions.create({customerId:e.customerId})).customerPortalUrl}),getOrCreateCustomer:async e=>{const t=await r.customers.create({email:e.email??"",externalId:e.referenceId,metadata:{...e.metadata,referenceId:e.referenceId},type:"individual"});return{createdAt:Date.now(),email:t.email??void 0,id:t.id,provider:"polar",referenceId:e.referenceId}},getPaymentStatus:async e=>v(await r.orders.get({id:e})),getSubscriptionStatus:async e=>u(await r.subscriptions.get({id:e})),identifier:"polar",parseWebhook:async({headers:e,payload:t})=>{const d=e.get("webhook-id")??"";await P({payload:t,secret:a,toleranceSeconds:s.webhookToleranceSeconds,webhookId:d,webhookSignature:e.get("webhook-signature")??"",webhookTimestamp:e.get("webhook-timestamp")??""});const o=p(JSON.parse(t));return h(d,n(o,"type")??"",p(o.data))},refundPayment:async e=>{const t=e.amount?void 0:await r.orders.get({id:e.sessionId}),d=e.amount?.currency??t?.currency??"usd",o=e.amount?Number(e.amount.minorUnits):t?.totalAmount??0;await r.refunds.create({amount:o,orderId:e.sessionId,reason:e.reason??"customer_request"});const l=e.amount??m(BigInt(Math.round(o)),d);return{amount:l,capturedAmount:l,createdAt:Date.now(),id:e.sessionId,provider:"polar",referenceId:"",refundedAmount:l,state:"refunded",updatedAt:Date.now()}},reportUsage:async e=>{await r.events.ingest({events:[{externalCustomerId:e.referenceId,metadata:{value:e.quantity},name:e.featureId,timestamp:e.timestamp===void 0?void 0:new Date(e.timestamp)}]})},resumeSubscription:async e=>{const t=await r.subscriptions.update({id:e,subscriptionUpdate:{cancelAtPeriodEnd:!1}});return u(t)},updateSubscription:async(e,t)=>{const d=await r.subscriptions.update({id:e,subscriptionUpdate:t.priceId?{productId:t.priceId}:{}});return u(d)}}};export{M as createPolarAdapter};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/payment",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.98",
|
|
4
4
|
"description": "Provider-agnostic payments for Lunora: Stripe-first adapter, webhook sync, and subscription/payment state machine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"billing",
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
75
|
-
"@lunora/server": "1.0.0-alpha.
|
|
76
|
-
"@lunora/values": "1.0.0-alpha.
|
|
74
|
+
"@lunora/errors": "1.0.0-alpha.27",
|
|
75
|
+
"@lunora/server": "1.0.0-alpha.96",
|
|
76
|
+
"@lunora/values": "1.0.0-alpha.35",
|
|
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{LunoraPaymentError as r}from"./LunoraPaymentError-BSxyhWgu.mjs";const g=(e,t)=>{const s=Math.max(e.length,t.length);let o=e.length^t.length;for(let n=0;n<s;n+=1){const a=n<e.length?e.charCodeAt(n):0,i=n<t.length?t.charCodeAt(n):0;o|=a^i}return o===0},h=new TextEncoder,I=e=>[...new Uint8Array(e)].map(t=>t.toString(16).padStart(2,"0")).join(""),d="whsec_",l=e=>new Uint8Array(Array.from(atob(e),t=>t.codePointAt(0)??0)),A=e=>btoa(String.fromCodePoint(...new Uint8Array(e))),f=async(e,t)=>{const s=await crypto.subtle.importKey("raw",e,{hash:"SHA-256",name:"HMAC"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,h.encode(t));return A(o)},m=g,y=async(e,t)=>{const s=await crypto.subtle.importKey("raw",h.encode(e),{hash:"SHA-256",name:"HMAC"},!1,["sign"]),o=await crypto.subtle.sign("HMAC",s,h.encode(t));return I(o)},N=async e=>{if(!e.secret)throw new r("CONFIG_INVALID","webhook secret not configured");const t=e.toleranceSeconds??300,s=e.now??Date.now(),o=Number(e.webhookTimestamp);if(!e.webhookId||!e.webhookSignature||!Number.isFinite(o))throw new r("WEBHOOK_SIGNATURE_INVALID","missing standard-webhooks headers");if(Math.abs(Math.floor(s/1e3)-o)>t)throw new r("WEBHOOK_TIMESTAMP_INVALID","signature timestamp outside tolerance");const n=e.secret.startsWith(d)?e.secret.slice(d.length):e.secret;if(!n)throw new r("CONFIG_INVALID","webhook secret not configured");const a=l(n);if(a.length===0)throw new r("CONFIG_INVALID","webhook secret not configured");const i=await f(a,`${e.webhookId}.${e.webhookTimestamp}.${e.payload}`);if(!e.webhookSignature.split(" ").map(c=>{const w=c.indexOf(",");return w===-1?"":c.slice(w+1)}).filter(Boolean).some(c=>m(c,i)))throw new r("WEBHOOK_SIGNATURE_INVALID","no matching signature")},E=async e=>{if(!e.secret)throw new r("CONFIG_INVALID","webhook secret not configured");if(!e.signature)throw new r("WEBHOOK_SIGNATURE_INVALID","missing creem-signature header");const t=await y(e.secret,e.payload);if(!m(e.signature,t))throw new r("WEBHOOK_SIGNATURE_INVALID","no matching signature")};export{m as constantTimeEqual,y as hmacSha256Hex,E as verifyCreemSignature,N as verifyStandardWebhook};
|
|
@@ -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};
|