@lunora/payment 1.0.0-alpha.124 → 1.0.0-alpha.125

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/index.d.mts +14 -3
  2. package/dist/index.d.ts +14 -3
  3. package/dist/index.mjs +1 -1
  4. package/dist/packem_shared/{PAYMENT_TERMINAL_STATES-Dxxpk_br.mjs → PAYMENT_TERMINAL_STATES-C-ICZkun.mjs} +1 -1
  5. package/dist/packem_shared/{adapter.d-CeLGlHv_.d.mts → adapter.d-DytKm_IF.d.mts} +25 -0
  6. package/dist/packem_shared/{adapter.d-CeLGlHv_.d.ts → adapter.d-DytKm_IF.d.ts} +25 -0
  7. package/dist/packem_shared/applyWebhookAction-CvLpZjKP.mjs +1 -0
  8. package/dist/packem_shared/createDatabasePaymentStore-BGcHX1uq.mjs +1 -0
  9. package/dist/packem_shared/createPayment-Dimez2zY.mjs +1 -0
  10. package/dist/packem_shared/entitlementsForReference-PMmhAOWa.mjs +1 -0
  11. package/dist/packem_shared/idempotencyKey-qQJLcEQX.mjs +1 -0
  12. package/dist/packem_shared/{lunoraDatabaseToPaymentDatabase-CZmZ5LeG.mjs → lunoraDatabaseToPaymentDatabase-CFPYSEu0.mjs} +1 -1
  13. package/dist/packem_shared/paymentTables-Ve_lJxAt.mjs +1 -0
  14. package/dist/packem_shared/reconcile-fbxX8j9k.mjs +1 -0
  15. package/dist/providers/autumn.d.mts +1 -1
  16. package/dist/providers/autumn.d.ts +1 -1
  17. package/dist/providers/creem.d.mts +1 -1
  18. package/dist/providers/creem.d.ts +1 -1
  19. package/dist/providers/creem.mjs +1 -1
  20. package/dist/providers/dodopayments.d.mts +1 -1
  21. package/dist/providers/dodopayments.d.ts +1 -1
  22. package/dist/providers/dodopayments.mjs +1 -1
  23. package/dist/providers/polar.d.mts +1 -1
  24. package/dist/providers/polar.d.ts +1 -1
  25. package/dist/providers/polar.mjs +1 -1
  26. package/dist/providers/stripe.d.mts +3 -1
  27. package/dist/providers/stripe.d.ts +3 -1
  28. package/dist/providers/stripe.mjs +1 -1
  29. package/package.json +3 -3
  30. package/dist/packem_shared/applyWebhookAction-DqM4qNr3.mjs +0 -1
  31. package/dist/packem_shared/createDatabasePaymentStore-DWe1Vz4V.mjs +0 -1
  32. package/dist/packem_shared/createPayment-Cmyk1BcJ.mjs +0 -1
  33. package/dist/packem_shared/entitlementsForReference-Bq-9UUL8.mjs +0 -1
  34. package/dist/packem_shared/idempotencyKey-DBL6bpmj.mjs +0 -1
  35. package/dist/packem_shared/paymentTables-Be-_11ux.mjs +0 -1
  36. package/dist/packem_shared/reconcile-Cm45pURT.mjs +0 -1
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as ProviderId, C as Customer, b as PaymentSession, S as Subscription, U as UsageEvent, W as WebhookActionType, A as ApplyResult, P as PaymentAdapter, c as AttachInput, d as CheckoutResult, e as CancelSubscriptionOptions, f as CaptureInput, g as CheckInput, h as CheckResult, i as CheckoutInput, F as FeatureBalance, R as RefundInput, T as TrackInput, j as TrackResult, k as CurrencyCode, M as Money, l as PaymentState, m as SubscriptionState, n as WebhookAction } from "./packem_shared/adapter.d-CeLGlHv_.mjs";
2
- export { type o as AdapterRegistry, type p as CustomerRef, type q as PortalInput, type r as ProviderCapabilities, type s as RefundAmountKind, type t as RefundResult, type u as ReportUsageInput, type v as SubscriptionPatch, type w as WebhookHeaders, type x as WebhookInput, y as createAdapterRegistry } from "./packem_shared/adapter.d-CeLGlHv_.mjs";
1
+ import { a as ProviderId, C as Customer, b as PaymentSession, S as Subscription, U as UsageEvent, W as WebhookActionType, A as ApplyResult, P as PaymentAdapter, c as AttachInput, d as CheckoutResult, e as CancelSubscriptionOptions, f as CaptureInput, g as CheckInput, h as CheckResult, i as CheckoutInput, F as FeatureBalance, R as RefundInput, T as TrackInput, j as TrackResult, k as CurrencyCode, M as Money, l as PaymentState, m as SubscriptionState, n as WebhookAction } from "./packem_shared/adapter.d-DytKm_IF.mjs";
2
+ export { type o as AdapterRegistry, type p as CustomerRef, type q as PortalInput, type r as ProviderCapabilities, type s as RefundAmountKind, type t as RefundResult, type u as ReportUsageInput, type v as SubscriptionPatch, type w as WebhookHeaders, type x as WebhookInput, y as createAdapterRegistry } from "./packem_shared/adapter.d-DytKm_IF.mjs";
3
3
  import { LunoraError } from '@lunora/errors';
4
4
  import { TableDefinition } from '@lunora/server';
5
5
  /**
@@ -296,13 +296,24 @@ interface LunoraPayment {
296
296
  /** Resolve every configured feature's allowance for a reference in one call. Requires `entitlements`. */
297
297
  listBalances: (referenceId: string) => Promise<FeatureBalance[]>;
298
298
  listSubscriptions: (referenceId: string) => Promise<Subscription[]>;
299
- /** Refund the caller's own captured payment (authorized, derived idempotency key, store synced). */
299
+ /**
300
+ * Refund the caller's own captured payment (authorized, derived idempotency key, store synced).
301
+ *
302
+ * The derived key makes "same session, same amount, same reason" ONE operation, so a retried
303
+ * request cannot refund twice. Two *intentional* refunds of the same amount on one session are
304
+ * therefore indistinguishable from that retry: pass a distinct `RefundInput.idempotencyKey` for
305
+ * the second one, or the provider replays the first and the second moves no money.
306
+ */
300
307
  refundPayment: (input: RefundInput) => Promise<PaymentSession>;
301
308
  readonly store: PaymentStore;
302
309
  /**
303
310
  * Record metered usage for a reference's feature — durably (exactly-once by idempotency key) and,
304
311
  * when the provider supports it, forwarded to its metering API. Best-effort upstream: a reporting
305
312
  * failure is observed, never thrown, and the local ledger that `check` reads is always updated.
313
+ *
314
+ * `mode: "set"` is rejected with `VALIDATION_ERROR` on a provider that meters usage: its meter is
315
+ * additive, so a period total is not expressible on it and a lowering set would leave the provider
316
+ * billing more than the local ledger holds. Use `mode: "add"` there.
306
317
  */
307
318
  track: (input: TrackInput) => Promise<TrackResult>;
308
319
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as ProviderId, C as Customer, b as PaymentSession, S as Subscription, U as UsageEvent, W as WebhookActionType, A as ApplyResult, P as PaymentAdapter, c as AttachInput, d as CheckoutResult, e as CancelSubscriptionOptions, f as CaptureInput, g as CheckInput, h as CheckResult, i as CheckoutInput, F as FeatureBalance, R as RefundInput, T as TrackInput, j as TrackResult, k as CurrencyCode, M as Money, l as PaymentState, m as SubscriptionState, n as WebhookAction } from "./packem_shared/adapter.d-CeLGlHv_.js";
2
- export { type o as AdapterRegistry, type p as CustomerRef, type q as PortalInput, type r as ProviderCapabilities, type s as RefundAmountKind, type t as RefundResult, type u as ReportUsageInput, type v as SubscriptionPatch, type w as WebhookHeaders, type x as WebhookInput, y as createAdapterRegistry } from "./packem_shared/adapter.d-CeLGlHv_.js";
1
+ import { a as ProviderId, C as Customer, b as PaymentSession, S as Subscription, U as UsageEvent, W as WebhookActionType, A as ApplyResult, P as PaymentAdapter, c as AttachInput, d as CheckoutResult, e as CancelSubscriptionOptions, f as CaptureInput, g as CheckInput, h as CheckResult, i as CheckoutInput, F as FeatureBalance, R as RefundInput, T as TrackInput, j as TrackResult, k as CurrencyCode, M as Money, l as PaymentState, m as SubscriptionState, n as WebhookAction } from "./packem_shared/adapter.d-DytKm_IF.js";
2
+ export { type o as AdapterRegistry, type p as CustomerRef, type q as PortalInput, type r as ProviderCapabilities, type s as RefundAmountKind, type t as RefundResult, type u as ReportUsageInput, type v as SubscriptionPatch, type w as WebhookHeaders, type x as WebhookInput, y as createAdapterRegistry } from "./packem_shared/adapter.d-DytKm_IF.js";
3
3
  import { LunoraError } from '@lunora/errors';
4
4
  import { TableDefinition } from '@lunora/server';
5
5
  /**
@@ -296,13 +296,24 @@ interface LunoraPayment {
296
296
  /** Resolve every configured feature's allowance for a reference in one call. Requires `entitlements`. */
297
297
  listBalances: (referenceId: string) => Promise<FeatureBalance[]>;
298
298
  listSubscriptions: (referenceId: string) => Promise<Subscription[]>;
299
- /** Refund the caller's own captured payment (authorized, derived idempotency key, store synced). */
299
+ /**
300
+ * Refund the caller's own captured payment (authorized, derived idempotency key, store synced).
301
+ *
302
+ * The derived key makes "same session, same amount, same reason" ONE operation, so a retried
303
+ * request cannot refund twice. Two *intentional* refunds of the same amount on one session are
304
+ * therefore indistinguishable from that retry: pass a distinct `RefundInput.idempotencyKey` for
305
+ * the second one, or the provider replays the first and the second moves no money.
306
+ */
300
307
  refundPayment: (input: RefundInput) => Promise<PaymentSession>;
301
308
  readonly store: PaymentStore;
302
309
  /**
303
310
  * Record metered usage for a reference's feature — durably (exactly-once by idempotency key) and,
304
311
  * when the provider supports it, forwarded to its metering API. Best-effort upstream: a reporting
305
312
  * failure is observed, never thrown, and the local ledger that `check` reads is always updated.
313
+ *
314
+ * `mode: "set"` is rejected with `VALIDATION_ERROR` on a provider that meters usage: its meter is
315
+ * additive, so a period total is not expressible on it and a lowering set would leave the provider
316
+ * billing more than the local ledger holds. Use `mode: "add"` there.
306
317
  */
307
318
  track: (input: TrackInput) => Promise<TrackResult>;
308
319
  }
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-CZmZ5LeG.mjs";import{createPayment as y,webhookResponse as i}from"./packem_shared/createPayment-Cmyk1BcJ.mjs";import{createDatabasePaymentStore as s}from"./packem_shared/createDatabasePaymentStore-DWe1Vz4V.mjs";import{entitlementsForReference as c,featureNames as x,hasActivePrice as S,resolveEntitlements as l,usagePeriodStart as M}from"./packem_shared/entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as u}from"./packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as b}from"./packem_shared/idempotencyKey-DBL6bpmj.mjs";import{addMoney as A,allocateMoney as E,compareMoney as N,formatMoney as h,fromMoneyJSON as R,isZeroDecimalCurrency as v,isZeroMoney as C,money as D,subtractMoney as I,toMoneyJSON as _,zeroMoney as g}from"./packem_shared/addMoney-B4ufWAnD.mjs";import{reconcile as L}from"./packem_shared/reconcile-Cm45pURT.mjs";import{default as F}from"./packem_shared/paymentTables-Be-_11ux.mjs";import{PAYMENT_TERMINAL_STATES as W,SUBSCRIPTION_TERMINAL_STATES as Z,canTransitionPayment as q,canTransitionSubscription as w,nextPaymentState as z,nextSubscriptionState as B}from"./packem_shared/PAYMENT_TERMINAL_STATES-Dxxpk_br.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-CZbrrEb1.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-DqM4qNr3.mjs";import{constantTimeEqual as G,hmacSha256Hex as Q,verifyCreemSignature as V,verifyStandardWebhook as X}from"./packem_shared/constantTimeEqual-ggrwGvRa.mjs";export{u as LunoraPaymentError,K as MemoryPaymentStore,W as PAYMENT_TERMINAL_STATES,Z as SUBSCRIPTION_TERMINAL_STATES,A as addMoney,E as allocateMoney,Y as applyWebhookAction,q as canTransitionPayment,w as canTransitionSubscription,N as compareMoney,G as constantTimeEqual,r as createAdapterRegistry,s as createDatabasePaymentStore,y as createPayment,c as entitlementsForReference,x as featureNames,h as formatMoney,R as fromMoneyJSON,S as hasActivePrice,Q as hmacSha256Hex,b as idempotencyKey,v as isZeroDecimalCurrency,C as isZeroMoney,a as lunoraDatabaseToPaymentDatabase,D as money,z as nextPaymentState,B as nextSubscriptionState,F as paymentTables,n as paymentsFromContext,L as reconcile,l as resolveEntitlements,I as subtractMoney,_ as toMoneyJSON,M as usagePeriodStart,V as verifyCreemSignature,X as verifyStandardWebhook,i as webhookResponse,g as zeroMoney};
1
+ import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-DyCsry1Q.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-CFPYSEu0.mjs";import{createPayment as y,webhookResponse as i}from"./packem_shared/createPayment-Dimez2zY.mjs";import{createDatabasePaymentStore as s}from"./packem_shared/createDatabasePaymentStore-BGcHX1uq.mjs";import{entitlementsForReference as c,featureNames as x,hasActivePrice as S,resolveEntitlements as l,usagePeriodStart as M}from"./packem_shared/entitlementsForReference-PMmhAOWa.mjs";import{LunoraPaymentError as u}from"./packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as b}from"./packem_shared/idempotencyKey-qQJLcEQX.mjs";import{addMoney as A,allocateMoney as E,compareMoney as N,formatMoney as h,fromMoneyJSON as R,isZeroDecimalCurrency as v,isZeroMoney as C,money as D,subtractMoney as I,toMoneyJSON as _,zeroMoney as g}from"./packem_shared/addMoney-B4ufWAnD.mjs";import{reconcile as L}from"./packem_shared/reconcile-fbxX8j9k.mjs";import{default as F}from"./packem_shared/paymentTables-Ve_lJxAt.mjs";import{PAYMENT_TERMINAL_STATES as W,SUBSCRIPTION_TERMINAL_STATES as Z,canTransitionPayment as q,canTransitionSubscription as w,nextPaymentState as z,nextSubscriptionState as B}from"./packem_shared/PAYMENT_TERMINAL_STATES-C-ICZkun.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-CZbrrEb1.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-CvLpZjKP.mjs";import{constantTimeEqual as G,hmacSha256Hex as Q,verifyCreemSignature as V,verifyStandardWebhook as X}from"./packem_shared/constantTimeEqual-ggrwGvRa.mjs";export{u as LunoraPaymentError,K as MemoryPaymentStore,W as PAYMENT_TERMINAL_STATES,Z as SUBSCRIPTION_TERMINAL_STATES,A as addMoney,E as allocateMoney,Y as applyWebhookAction,q as canTransitionPayment,w as canTransitionSubscription,N as compareMoney,G as constantTimeEqual,r as createAdapterRegistry,s as createDatabasePaymentStore,y as createPayment,c as entitlementsForReference,x as featureNames,h as formatMoney,R as fromMoneyJSON,S as hasActivePrice,Q as hmacSha256Hex,b as idempotencyKey,v as isZeroDecimalCurrency,C as isZeroMoney,a as lunoraDatabaseToPaymentDatabase,D as money,z as nextPaymentState,B as nextSubscriptionState,F as paymentTables,n as paymentsFromContext,L as reconcile,l as resolveEntitlements,I as subtractMoney,_ as toMoneyJSON,M as usagePeriodStart,V as verifyCreemSignature,X as verifyStandardWebhook,i as webhookResponse,g as zeroMoney};
@@ -1 +1 @@
1
- const c={authorized:{cancel:"canceled",capture:"captured",fail:"failed"},canceled:{},captured:{partial_refund:"partially_refunded",refund:"refunded"},failed:{authorize:"authorized",capture:"captured",fail:"failed"},initiated:{authorize:"authorized",cancel:"canceled",capture:"captured",fail:"failed"},partially_refunded:{partial_refund:"partially_refunded",refund:"refunded"},refunded:{refund:"refunded"}},t={active:{cancel:"canceled",mark_past_due:"past_due",pause:"paused",renew:"active"},canceled:{},past_due:{activate:"active",cancel:"canceled",pause:"paused",renew:"active"},paused:{cancel:"canceled",resume:"active"},trialing:{activate:"active",cancel:"canceled",mark_past_due:"past_due"}},r=new Set(["canceled","refunded"]),i=new Set(["canceled"]),n=(e,a)=>c[e][a],u=(e,a)=>n(e,a)!==void 0,d=(e,a)=>t[e][a],l=(e,a)=>d(e,a)!==void 0;export{r as PAYMENT_TERMINAL_STATES,i as SUBSCRIPTION_TERMINAL_STATES,u as canTransitionPayment,l as canTransitionSubscription,n as nextPaymentState,d as nextSubscriptionState};
1
+ const c={authorized:{cancel:"canceled",capture:"captured",fail:"failed"},canceled:{},captured:{partial_refund:"partially_refunded",refund:"refunded"},failed:{authorize:"authorized",capture:"captured",fail:"failed"},initiated:{authorize:"authorized",cancel:"canceled",capture:"captured",fail:"failed"},partially_refunded:{partial_refund:"partially_refunded",refund:"refunded"},refunded:{refund:"refunded"}},t={active:{cancel:"canceled",mark_past_due:"past_due",pause:"paused",renew:"active"},canceled:{},past_due:{activate:"active",cancel:"canceled",pause:"paused",renew:"active"},paused:{cancel:"canceled",resume:"active"},trialing:{activate:"active",cancel:"canceled",mark_past_due:"past_due",pause:"paused",renew:"active"}},r=new Set(["canceled","refunded"]),i=new Set(["canceled"]),n=(e,a)=>c[e][a],u=(e,a)=>n(e,a)!==void 0,d=(e,a)=>t[e][a],l=(e,a)=>d(e,a)!==void 0;export{r as PAYMENT_TERMINAL_STATES,i as SUBSCRIPTION_TERMINAL_STATES,u as canTransitionPayment,l as canTransitionSubscription,n as nextPaymentState,d as nextSubscriptionState};
@@ -85,7 +85,17 @@ interface Subscription {
85
85
  /** Start of the current billing period — the window `check` sums metered usage over. */
86
86
  readonly currentPeriodStart?: number;
87
87
  readonly id: string;
88
+ /** The primary (first) price/product id — `priceIds[0]`. Display and single-item plan changes. */
88
89
  readonly priceId: string;
90
+ /**
91
+ * EVERY price/product id the subscription bills, not just the primary one. A Stripe subscription
92
+ * can carry an add-on or a metered price alongside the base plan, and a customer paying for one is
93
+ * entitled to it — so entitlements test membership here (see `hasActivePrice`).
94
+ *
95
+ * Optional because the webhook path and any pre-existing stored row carry only `priceId`; absent
96
+ * reads as `[priceId]`, which is exactly right for the single-item case every other provider has.
97
+ */
98
+ readonly priceIds?: ReadonlyArray<string>;
89
99
  readonly provider: ProviderId;
90
100
  readonly quantity: number;
91
101
  readonly referenceId: string;
@@ -195,6 +205,11 @@ interface TrackInput {
195
205
  * resets to, rather than reading the current total and appending a delta. Concurrent `"set"` calls
196
206
  * for the same reference therefore resolve last-writer-wins instead of over- or under-counting, and
197
207
  * a replayed `"set"` is idempotent — neither mode needs a serialized context or a per-reference lock.
208
+ *
209
+ * `"set"` reconciles the LOCAL period total only, so `track` rejects it with `VALIDATION_ERROR` on a
210
+ * provider that meters usage upstream: those meters are additive and cannot take a period total, and
211
+ * a set that LOWERS usage has no negative delta to forward — the provider would keep billing the
212
+ * higher figure while the local ledger holds the lower one. Use `"add"` with a metered provider.
198
213
  */
199
214
  readonly mode?: "add" | "set";
200
215
  /**
@@ -372,6 +387,16 @@ interface WebhookAction {
372
387
  /** Provider event id — the inbound idempotency key. */
373
388
  readonly eventId: string;
374
389
  readonly priceId?: string;
390
+ /**
391
+ * EVERY price/product id the subscription bills, when the adapter could establish the whole set.
392
+ * `sync.ts` applies it as a WHOLESALE replacement, so it must be complete or absent — never a
393
+ * subset, which would silently drop prices the stored row already had.
394
+ *
395
+ * `undefined` leaves the stored set standing (see {@link Subscription.priceIds}). That is the
396
+ * single-price providers' case, and the fail-closed answer for a provider whose embedded item
397
+ * list is paginated and whose event carries only the first page.
398
+ */
399
+ readonly priceIds?: ReadonlyArray<string>;
375
400
  readonly provider: ProviderId;
376
401
  readonly quantity?: number;
377
402
  /** Raw provider event, retained for the events log / debugging. */
@@ -85,7 +85,17 @@ interface Subscription {
85
85
  /** Start of the current billing period — the window `check` sums metered usage over. */
86
86
  readonly currentPeriodStart?: number;
87
87
  readonly id: string;
88
+ /** The primary (first) price/product id — `priceIds[0]`. Display and single-item plan changes. */
88
89
  readonly priceId: string;
90
+ /**
91
+ * EVERY price/product id the subscription bills, not just the primary one. A Stripe subscription
92
+ * can carry an add-on or a metered price alongside the base plan, and a customer paying for one is
93
+ * entitled to it — so entitlements test membership here (see `hasActivePrice`).
94
+ *
95
+ * Optional because the webhook path and any pre-existing stored row carry only `priceId`; absent
96
+ * reads as `[priceId]`, which is exactly right for the single-item case every other provider has.
97
+ */
98
+ readonly priceIds?: ReadonlyArray<string>;
89
99
  readonly provider: ProviderId;
90
100
  readonly quantity: number;
91
101
  readonly referenceId: string;
@@ -195,6 +205,11 @@ interface TrackInput {
195
205
  * resets to, rather than reading the current total and appending a delta. Concurrent `"set"` calls
196
206
  * for the same reference therefore resolve last-writer-wins instead of over- or under-counting, and
197
207
  * a replayed `"set"` is idempotent — neither mode needs a serialized context or a per-reference lock.
208
+ *
209
+ * `"set"` reconciles the LOCAL period total only, so `track` rejects it with `VALIDATION_ERROR` on a
210
+ * provider that meters usage upstream: those meters are additive and cannot take a period total, and
211
+ * a set that LOWERS usage has no negative delta to forward — the provider would keep billing the
212
+ * higher figure while the local ledger holds the lower one. Use `"add"` with a metered provider.
198
213
  */
199
214
  readonly mode?: "add" | "set";
200
215
  /**
@@ -372,6 +387,16 @@ interface WebhookAction {
372
387
  /** Provider event id — the inbound idempotency key. */
373
388
  readonly eventId: string;
374
389
  readonly priceId?: string;
390
+ /**
391
+ * EVERY price/product id the subscription bills, when the adapter could establish the whole set.
392
+ * `sync.ts` applies it as a WHOLESALE replacement, so it must be complete or absent — never a
393
+ * subset, which would silently drop prices the stored row already had.
394
+ *
395
+ * `undefined` leaves the stored set standing (see {@link Subscription.priceIds}). That is the
396
+ * single-price providers' case, and the fail-closed answer for a provider whose embedded item
397
+ * list is paginated and whose event carries only the first page.
398
+ */
399
+ readonly priceIds?: ReadonlyArray<string>;
375
400
  readonly provider: ProviderId;
376
401
  readonly quantity?: number;
377
402
  /** Raw provider event, retained for the events log / debugging. */
@@ -0,0 +1 @@
1
+ import{LunoraPaymentError as P}from"./LunoraPaymentError-BSxyhWgu.mjs";import{localRefundKey as A,LOCAL_REFUND_CLAIM_TYPE as E}from"./idempotencyKey-qQJLcEQX.mjs";import{zeroMoney as o,compareMoney as l,addMoney as _}from"./addMoney-B4ufWAnD.mjs";import{n as a}from"./observability-BteZ2dJS.mjs";import{nextPaymentState as S,nextSubscriptionState as w}from"./PAYMENT_TERMINAL_STATES-C-ICZkun.mjs";const b={"payment.authorized":"authorize","payment.captured":"capture","payment.failed":"fail","payment.refunded":"refund"},h={"subscription.active":"active","subscription.canceled":"canceled","subscription.past_due":"past_due","subscription.paused":"paused"},T="orphan-retry:",R="marker.orphan_retry",k={"subscription.active":"activate","subscription.canceled":"cancel","subscription.past_due":"mark_past_due","subscription.paused":"pause"},O=(t,e)=>l(t,e)>0?t:e,Y=new Set(["authorized","initiated"]),N=async(t,e,r)=>{if(!r||!e.sessionId||!e.amount||e.amountKind==="absolute")return e;const d=A(e.sessionId,e.refundId,e.amount),n=await t.markEventProcessed(e.provider,d,E);return await t.releaseEvent(e.provider,d),n?e:{...e,amount:o(e.amount.currency)}},v=(t,e)=>{if(!e.amount||t.refundedAmount.currency!==e.amount.currency||t.capturedAmount.currency!==e.amount.currency)return;const r=e.amountKind==="absolute"?O(e.amount,t.refundedAmount):_(t.refundedAmount,e.amount);if(!(l(r,t.capturedAmount)>0))return r},M=(t,e)=>{if(!t)return"refund";const r=v(t,e);return r&&l(r,t.capturedAmount)<0?"partial_refund":"refund"},q=async(t,e,r)=>{if(!e.sessionId)return{applied:!1,reason:"unhandled"};const d=await t.getPaymentSession(e.provider,e.sessionId),n=d?.state??"initiated",u=Date.now(),s=e.amount?.currency??d?.amount.currency??"USD",c=r==="refund"?await N(t,e,d):e,p=r==="refund"?M(d,c):r,I=S(n,p);if(!I)return{applied:!1,reason:r==="refund"&&Y.has(n)?"orphaned":"illegal_transition"};const i=d??{amount:e.amount??o(s),capturedAmount:o(s),createdAt:u,id:e.sessionId,provider:e.provider,referenceId:e.referenceId??"",refundedAmount:o(s),state:n,updatedAt:u};let{capturedAmount:m,refundedAmount:y}=i;if(p==="capture"&&e.amount&&(m=e.amount),(p==="partial_refund"||p==="refund")&&c.amount){const f=v(i,c);if(!f)return{applied:!1,reason:"invalid_refund_amount"};y=f}return await t.upsertPaymentSession({...i,capturedAmount:m,referenceId:e.referenceId??i.referenceId,refundedAmount:y,state:I,updatedAt:u}),{applied:!0,reason:"ok"}},C=(t,e,r)=>{if(e==="active"){if(t==="active")return"renew";if(t==="paused")return"resume"}return k[r]},g=async(t,e)=>{if(!e.subscriptionId)return{applied:!1,reason:"unhandled"};const r=await t.getSubscription(e.provider,e.subscriptionId),d=Date.now();if(e.type==="subscription.updated")return r?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,priceIds:e.priceIds??r.priceIds,quantity:e.quantity??r.quantity,updatedAt:d}),{applied:!0,reason:"ok"}):{applied:!1,reason:"orphaned"};const n=h[e.type];if(!n)return{applied:!1,reason:"unhandled"};if(!r)return await t.upsertSubscription({cancelAtPeriodEnd:e.cancelAtPeriodEnd??!1,createdAt:d,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??d,id:e.subscriptionId,priceId:e.priceId??"",priceIds:e.priceIds,provider:e.provider,quantity:e.quantity??1,referenceId:e.referenceId??"",state:n,updatedAt:d}),{applied:!0,reason:"ok"};const u=C(r.state,n,e.type),s=u?w(r.state,u):void 0;return s?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,priceIds:e.priceIds??r.priceIds,quantity:e.quantity??r.quantity,state:s,updatedAt:d}),{applied:!0,reason:"ok"}):{applied:!1,reason:"illegal_transition"}},U=async(t,e,r)=>{if(e.type==="unhandled")return{applied:!1,reason:"unhandled"};if(!e.eventId?.trim())throw new P("WEBHOOK_EVENT_ID_MISSING",`webhook event id is missing or blank for provider "${e.provider}"`);if(!await t.markEventProcessed(e.provider,e.eventId,e.type))return a(r,{eventId:e.eventId,provider:e.provider,type:"webhook.duplicate"}),{applied:!1,reason:"duplicate"};const n=b[e.type];let u;try{u=n?await q(t,e,n):await g(t,e)}catch(s){throw await t.releaseEvent(e.provider,e.eventId),s}return u.reason==="orphaned"&&(await t.markEventProcessed(e.provider,`${T}${e.eventId}`,R)?await t.releaseEvent(e.provider,e.eventId):u={applied:!1,reason:"unhandled"}),a(r,{action:e.type,eventId:e.eventId,provider:e.provider,reason:u.reason,type:"webhook.applied"}),e.type==="payment.failed"?a(r,{provider:e.provider,referenceId:e.referenceId,sessionId:e.sessionId,type:"payment.failed"}):e.type==="subscription.past_due"&&a(r,{provider:e.provider,referenceId:e.referenceId,subscriptionId:e.subscriptionId,type:"subscription.past_due"}),u};export{U as default};
@@ -0,0 +1 @@
1
+ import{money as y}from"./addMoney-B4ufWAnD.mjs";import{foldUsage as m}from"./MemoryPaymentStore-CZbrrEb1.mjs";const o=(e,n)=>typeof e[n]=="string"?e[n]:"",l=(e,n)=>typeof e[n]=="string"?e[n]:void 0,c=(e,n)=>typeof e[n]=="number"?e[n]:0,v=(e,n)=>typeof e[n]=="number"?e[n]:void 0,A=(e,n)=>e[n]===!0,S=(e,n)=>{const t=e[n];return Array.isArray(t)?t.filter(d=>typeof d=="string"):void 0},f=(e,n)=>{const t=e[n];return typeof t=="bigint"?t:typeof t=="number"||typeof t=="string"?BigInt(t):0n},P=e=>({createdAt:e.createdAt,email:e.email,provider:e.provider,providerCustomerId:e.id,referenceId:e.referenceId}),E=e=>({createdAt:c(e,"createdAt"),email:l(e,"email"),id:o(e,"providerCustomerId"),provider:o(e,"provider"),referenceId:o(e,"referenceId")}),M=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}),T=e=>{const n=o(e,"currency");return{amount:y(f(e,"amountMinor"),n),capturedAmount:y(f(e,"capturedMinor"),n),createdAt:c(e,"createdAt"),id:o(e,"providerSessionId"),provider:o(e,"provider"),referenceId:o(e,"referenceId"),refundedAmount:y(f(e,"refundedMinor"),n),state:o(e,"state"),updatedAt:c(e,"updatedAt")}},q=e=>({cancelAtPeriodEnd:e.cancelAtPeriodEnd,createdAt:e.createdAt,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart,priceId:e.priceId,priceIds:e.priceIds===void 0?void 0:[...e.priceIds],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"),priceIds:S(e,"priceIds"),provider:o(e,"provider"),quantity:c(e,"quantity"),referenceId:o(e,"referenceId"),state:o(e,"state"),updatedAt:c(e,"updatedAt")}),K=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")}),U=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}),h=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?E(r):void 0},getPaymentSession:async(t,d)=>{const r=await e.findFirst("paymentSessions",{provider:t,providerSessionId:d});return r?T(r):void 0},getSubscription:async(t,d)=>{const r=await e.findFirst("subscriptions",{provider:t,providerSubscriptionId:d});return r?I(r):void 0},listSubscriptionsByReference:async t=>{const{rows:d}=await e.findMany("subscriptions",{referenceId:t});return d.map(r=>I(r))},listUnreportedUsage:async(t,d)=>{const r=Math.max(0,Math.floor(d));if(r===0)return[];const a=[{createdAt:"asc"},{idempotencyKey:"asc"}],s=[];let i;do{const u=await e.findMany("usageEvents",{provider:t,reportedToProvider:!1},{cursor:i,limit:r,orderBy:a});for(const g of u.rows){const p=K(g);if(p.mode!=="set"&&p.quantity>0&&(s.push(p),s.length===r))return s}i=u.cursor}while(i!==void 0);return s},markEventProcessed:async(t,d,r)=>await e.findFirst("events",{provider:t,providerEventId:d})?!1:(await e.insert("events",{processedAt:Date.now(),provider:t,providerEventId:d,type:r}),!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",U(t)),!0),sumUsage:async(t,d,r)=>{const{rows:a}=await e.findMany("usageEvents",{featureId:d,referenceId:t}),s=a.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 m(s)},sumUsageByFeature:async(t,d,r)=>{const{rows: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,u])=>[i,m(u)]))},upsertCustomer:async t=>n("customers",{provider:t.provider,referenceId:t.referenceId},P(t)),upsertPaymentSession:async t=>n("paymentSessions",{provider:t.provider,providerSessionId:t.id},M(t)),upsertSubscription:async t=>n("subscriptions",{provider:t.provider,providerSubscriptionId:t.id},q(t))}};export{h as createDatabasePaymentStore};
@@ -0,0 +1 @@
1
+ import{toErrorBody as N}from"@lunora/errors";import{usagePeriodStart as g,resolveEntitlements as A,featureNames as U,hasActivePrice as _}from"./entitlementsForReference-PMmhAOWa.mjs";import{LunoraPaymentError as f}from"./LunoraPaymentError-BSxyhWgu.mjs";import{derivedIdempotencyKey as b,localRefundKey as D,LOCAL_REFUND_CLAIM_TYPE as E,idempotencyKey as k}from"./idempotencyKey-qQJLcEQX.mjs";import{subtractMoney as K,addMoney as T,compareMoney as v,isZeroMoney as B}from"./addMoney-B4ufWAnD.mjs";import{n as C}from"./observability-BteZ2dJS.mjs";import F from"./applyWebhookAction-CvLpZjKP.mjs";const y=(o,n=200,s)=>{const u=new Headers({"content-type":"application/json"});for(const[l,w]of Object.entries({}))u.set(l,w);return Response.json(o,{headers:u,status:n})};let h=0;const $=()=>{const o=Date.now();return h=o>h?o:h+1,h},O=o=>o?`${o.currency}:${String(o.minorUnits)}`:"full",q=o=>o&&"referenceId"in o?Object.fromEntries(Object.entries(o).filter(([n])=>n!=="referenceId")):o,G=o=>y({applied:o.applied},o.status),H=o=>{const{adapter:n,store:s}=o,u=async e=>{if(!o.authorize)return;let t;try{t=await o.authorize(e)}catch{throw new f("FORBIDDEN",`caller not authorized for reference "${e}"`)}if(t!==!0)throw new f("FORBIDDEN",`caller not authorized for reference "${e}"`)},l=async e=>{await u(e.referenceId);const t=q(e.metadata);let r;const a=await s.getCustomerByReference(n.identifier,e.referenceId);if(a)r=a.id;else{const d=await n.getOrCreateCustomer({email:e.email,referenceId:e.referenceId});r=d.id,await s.upsertCustomer(d)}const i=e.idempotencyKey??await b("checkout",n.identifier,e.referenceId,e.priceId,e.mode,String(e.quantity??1),e.successUrl,e.cancelUrl,t?JSON.stringify(t):"");return n.createCheckout({...e,customerId:r,idempotencyKey:i,metadata:t})},w=(e,t,r)=>{const a=e-t;return{allowed:a>=r,balance:a,limit:e,unlimited:!1,used:t}},S=async(e,t,r,a,i)=>{const d=e.limit(a);if(d!==void 0){const c=await s.sumUsage(r,a,g(t));return w(d,c,i)}return{allowed:e.has(a),unlimited:e.has(a)}},p=async e=>{const t=()=>new f("NOT_FOUND",`payment session "${e}" not found`);let r=await s.getPaymentSession(n.identifier,e);if(!r)try{r=await n.getPaymentStatus(e)}catch{throw t()}if(!r.referenceId)throw t();try{await u(r.referenceId)}catch{throw t()}return r},I=async(e,t)=>{const r={...e,...t,updatedAt:Date.now()};return await s.upsertPaymentSession(r),r};return{adapter:n,attach:async e=>l({...e,mode:e.mode??"subscription"}),cancelPayment:async(e,t)=>{const r=await p(e),a=t?.idempotencyKey??k("cancel_payment",n.identifier,e),i=await n.cancelPayment(e,{...t,idempotencyKey:a});return I(r,{state:i.state})},cancelSubscription:async(e,t)=>{const r=await s.getSubscription(n.identifier,e);if(!r)throw new f("NOT_FOUND",`subscription "${e}" not found`);try{await u(r.referenceId)}catch{throw new f("NOT_FOUND",`subscription "${e}" not found`)}const a=t?.idempotencyKey??k("cancel_subscription",n.identifier,e),i=await n.cancelSubscription(e,{...t,idempotencyKey:a}),d={...i,referenceId:r.referenceId===""?i.referenceId:r.referenceId};return await s.upsertSubscription(d),d},capturePayment:async e=>{const t=await p(e.sessionId),r=e.idempotencyKey??await b("capture_payment",n.identifier,e.sessionId,O(e.amount)),a=await n.capturePayment({...e,idempotencyKey:r});return I(t,{capturedAmount:a.capturedAmount,state:a.state})},check:async e=>{if(await u(e.referenceId),e.featureId===void 0&&e.priceId===void 0)throw new f("VALIDATION_ERROR","check() requires a featureId or priceId");if(n.checkEntitlement)return n.checkEntitlement(e);const t=await s.listSubscriptionsByReference(e.referenceId);if(e.priceId!==void 0)return{allowed:_(t,e.priceId),unlimited:!1};if(e.featureId===void 0)throw new f("VALIDATION_ERROR","check() requires a featureId or priceId");if(!o.entitlements)throw new f("CONFIG_INVALID","check() requires `entitlements` to be configured");const r=A(o.entitlements,t);return S(r,t,e.referenceId,e.featureId,e.quantity??1)},createCheckout:async e=>l(e),createPortalSession:async(e,t)=>{await u(e);const r=await s.getCustomerByReference(n.identifier,e);if(!r)throw new f("NOT_FOUND",`no customer for reference "${e}"`);return n.createPortalSession({customerId:r.id,returnUrl:t})},handleWebhook:async e=>{let t;try{const a=await e.text();t=await n.parseWebhook({headers:e.headers,payload:a})}catch(a){if(a instanceof f){const{body:i,status:d}=N(a);return y({error:i.message},d)}return y({error:"webhook error"},400)}const r=await F(s,t,o.observability);return r.reason==="orphaned"?y({applied:r.applied,reason:r.reason},500):y({applied:r.applied,reason:r.reason},200)},listBalances:async e=>{if(await u(e),n.getBalances)return n.getBalances(e);if(!o.entitlements)throw new f("CONFIG_INVALID","listBalances() requires `entitlements` to be configured");const t=await s.listSubscriptionsByReference(e),r=A(o.entitlements,t),a=U(o.entitlements),i=a.filter(c=>r.limit(c)!==void 0),d=i.length===0?new Map:await s.sumUsageByFeature(e,i,g(t));return a.map(c=>{const m=r.limit(c);return m!==void 0?{featureId:c,...w(m,d.get(c)??0,1)}:{featureId:c,allowed:r.has(c),unlimited:r.has(c)}})},listSubscriptions:async e=>(await u(e),s.listSubscriptionsByReference(e)),refundPayment:async e=>{const t=await p(e.sessionId),r=e.amount??K(t.capturedAmount,t.refundedAmount),a=T(t.refundedAmount,r);if(v(a,t.capturedAmount)>0)throw new f("VALIDATION_ERROR",`refundPayment(): refunding ${String(e.amount?.minorUnits)} would exceed the captured amount on session "${e.sessionId}"`);if(B(r))return t;const i=e.amount??(v(r,t.capturedAmount)===0?void 0:r),d=e.idempotencyKey??await b("refund_payment",n.identifier,e.sessionId,O(i),e.reason??""),c=await n.refundPayment({...e,amount:i,idempotencyKey:d});if(c.pending)return t;const m=D(e.sessionId,c.refundId,r);if(!await s.markEventProcessed(n.identifier,m,E)&&c.refundId!==void 0)return t;try{return await I(t,{refundedAmount:a,state:v(a,t.capturedAmount)<0?"partially_refunded":"refunded"})}catch(P){throw await s.releaseEvent(n.identifier,m),P}},store:s,track:async e=>{await u(e.referenceId);const t=e.quantity===void 0?1:e.quantity;if(!Number.isSafeInteger(t)||t<0)throw new f("VALIDATION_ERROR",`track(): \`quantity\` must be a non-negative safe integer (got ${String(e.quantity)})`);const r=e.idempotencyKey??crypto.randomUUID(),a=e.mode==="set";if(a&&n.capabilities.usageMetering&&n.reportUsage)throw new f("VALIDATION_ERROR",`track(): \`mode: "set"\` is not supported on "${n.identifier}", whose meter is additive — a lowering set cannot be forwarded, so the provider would over-bill against the local period total. Use \`mode: "add"\`.`);const i=a?await s.listSubscriptionsByReference(e.referenceId):void 0,d=i===void 0?0:await s.sumUsage(e.referenceId,e.featureId,g(i));if(a?t===d:t===0)return{recorded:!1,reportedToProvider:!1};const c=t;if(!await s.recordUsage({createdAt:$(),featureId:e.featureId,idempotencyKey:r,...a?{mode:"set"}:{},provider:n.identifier,quantity:t,referenceId:e.referenceId,reportedToProvider:!1}))return{recorded:!1,reportedToProvider:!1};if(c<=0||!n.capabilities.usageMetering||!n.reportUsage)return{recorded:!0,reportedToProvider:!1};try{const R=await s.getCustomerByReference(n.identifier,e.referenceId);return await n.reportUsage({customerId:R?.id,featureId:e.featureId,idempotencyKey:r,quantity:c,referenceId:e.referenceId}),await s.markUsageReported(n.identifier,r),{recorded:!0,reportedToProvider:!0}}catch{return C(o.observability,{featureId:e.featureId,provider:n.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{H as createPayment,G as webhookResponse};
@@ -0,0 +1 @@
1
+ const f=new Set(["active","trialing"]),u=s=>s.priceIds??[s.priceId],p=s=>{let t=0;for(const e of s)f.has(e.state)&&e.currentPeriodStart!==void 0&&(t=Math.max(t,e.currentPeriodStart));return t},S=s=>{const t=new Set;for(const e of Object.values(s.plans)){for(const a of e.features??[])t.add(a);for(const a of Object.keys(e.limits??{}))t.add(a)}return[...t].toSorted((e,a)=>e.localeCompare(a))},h=(s,t)=>s.some(e=>f.has(e.state)&&u(e).includes(t)),m=(s,t)=>{const e=new Set(t.filter(n=>f.has(n.state)).flatMap(n=>u(n))),a=[],r=new Set,c=new Map;for(const[n,i]of Object.entries(s.plans))if(i.priceIds.some(o=>e.has(o))){a.push(n);for(const o of i.features??[])r.add(o);for(const[o,l]of Object.entries(i.limits??{})){const d=c.get(o);c.set(o,d===void 0?l:Math.max(d,l))}}return{features:r,has:n=>r.has(n),limit:n=>c.get(n),plans:a}},v=async(s,t,e)=>m(t,await s.listSubscriptionsByReference(e));export{v as entitlementsForReference,S as featureNames,h as hasActivePrice,m as resolveEntitlements,p as usagePeriodStart};
@@ -0,0 +1 @@
1
+ const r=new TextEncoder,c=new Set(["dodopayments","polar","stripe"]),a=async n=>[...new Uint8Array(await crypto.subtle.digest("SHA-256",r.encode(n)))].map(t=>t.toString(16).padStart(2,"0")).join(""),s=(n,...t)=>[n,...t.map(String)].join(":"),i=async(n,t,...e)=>{const o=await a(JSON.stringify([t,...e.map(String)]));return`${n}:${t}:${o}`},d=(n,t,e)=>t===void 0?`local-refund:${n}:${e.currency}:${String(e.minorUnits)}`:`local-refund:${n}:id:${t}`,y="marker.local_refund",p=n=>c.has(n);export{y as LOCAL_REFUND_CLAIM_TYPE,i as derivedIdempotencyKey,s as idempotencyKey,d as localRefundKey,p as usageForwardIsIdempotent};
@@ -1 +1 @@
1
- import{createPayment as i}from"./createPayment-Cmyk1BcJ.mjs";import{createDatabasePaymentStore as s}from"./createDatabasePaymentStore-DWe1Vz4V.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};
1
+ import{createPayment as i}from"./createPayment-Dimez2zY.mjs";import{createDatabasePaymentStore as s}from"./createDatabasePaymentStore-BGcHX1uq.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{defineTable as r}from"@lunora/server";import{v as e}from"@lunora/values";const n=r({createdAt:e.number(),email:e.optional(e.string()),provider:e.string(),providerCustomerId:e.string(),referenceId:e.string()}).index("by_provider_customer",["provider","providerCustomerId"],{unique:!0}).index("by_reference",["referenceId"]),i=r({cancelAtPeriodEnd:e.boolean(),createdAt:e.number(),currentPeriodEnd:e.optional(e.number()),currentPeriodStart:e.optional(e.number()),priceId:e.string(),priceIds:e.optional(e.array(e.string())),provider:e.string(),providerSubscriptionId:e.string(),quantity:e.number(),referenceId:e.string(),state:e.string(),updatedAt:e.number()}).index("by_provider_subscription",["provider","providerSubscriptionId"],{unique:!0}).index("by_reference",["referenceId"]),t=r({amountMinor:e.bigint(),capturedMinor:e.bigint(),createdAt:e.number(),currency:e.string(),provider:e.string(),providerSessionId:e.string(),referenceId:e.string(),refundedMinor:e.bigint(),state:e.string(),updatedAt:e.number()}).index("by_provider_session",["provider","providerSessionId"],{unique:!0}).index("by_reference",["referenceId"]),d=r({processedAt:e.number(),provider:e.string(),providerEventId:e.string(),type:e.string()}).index("by_provider_event",["provider","providerEventId"],{unique:!0}),o=r({createdAt:e.number(),featureId:e.string(),idempotencyKey:e.string(),mode:e.optional(e.string()),provider:e.string(),quantity:e.number(),referenceId:e.string(),reportedToProvider:e.boolean()}).index("by_idempotency",["provider","idempotencyKey"],{unique:!0}).index("by_reference_feature",["referenceId","featureId"]),p={customers:n,events:d,paymentSessions:t,subscriptions:i,usageEvents:o};export{p as default};
@@ -0,0 +1 @@
1
+ import{usageForwardIsIdempotent as l}from"./idempotencyKey-qQJLcEQX.mjs";import{compareMoney as y}from"./addMoney-B4ufWAnD.mjs";import{n as u}from"./observability-BteZ2dJS.mjs";const I=100,f=(e,t)=>e.currency===t.currency&&y(e,t)===0,A=(e,t)=>e?.state!==t.state||e.cancelAtPeriodEnd!==t.cancelAtPeriodEnd||e.currentPeriodEnd!==t.currentPeriodEnd||e.priceId!==t.priceId||e.quantity!==t.quantity,b=(e,t)=>e?.state!==t.state||!f(e.capturedAmount,t.capturedAmount)||!f(e.refundedAmount,t.refundedAmount),p={captured:0,partially_refunded:1,refunded:2},w=(e,t)=>{if(!e)return t;const a=e.refundedAmount.currency===t.refundedAmount.currency&&y(e.refundedAmount,t.refundedAmount)>0?e.refundedAmount:t.refundedAmount,d=p[e.state],r=p[t.state],i=d!==void 0&&r!==void 0&&r<d?e.state:t.state,o=t.referenceId===""?e.referenceId:t.referenceId;return{...t,referenceId:o,refundedAmount:a,state:i}},S=(e,t)=>e===void 0||e===""?t:e,P=async(e,t,n,a)=>{const d=await e.getSubscriptionStatus(n),r=await t.getSubscription(e.identifier,n);return A(r,d)?(await t.upsertSubscription({...d,createdAt:r?.createdAt??d.createdAt,referenceId:S(r?.referenceId,d.referenceId)}),u(a,{id:n,kind:"subscription",provider:e.identifier,type:"reconcile.drift"}),!0):!1},v=async(e,t,n,a)=>{const d=await e.getPaymentStatus(n),r=await t.getPaymentSession(e.identifier,n),i=w(r,d);return b(r,i)?(await t.upsertPaymentSession({...i,createdAt:r?.createdAt??i.createdAt}),u(a,{id:n,kind:"payment",provider:e.identifier,type:"reconcile.drift"}),!0):!1},g=async(e,t,n,a)=>{const{reportUsage:d}=e;if(n<=0||d===void 0||!e.capabilities.usageMetering||!l(e.identifier))return{checked:0,failed:0,updated:0};const r=await t.listUnreportedUsage(e.identifier,n);let i=0,o=0;for(const c of r)try{const s=await t.getCustomerByReference(e.identifier,c.referenceId);await d({customerId:s?.id,featureId:c.featureId,idempotencyKey:c.idempotencyKey,quantity:c.quantity,referenceId:c.referenceId}),await t.markUsageReported(e.identifier,c.idempotencyKey),i+=1}catch{o+=1,u(a,{featureId:c.featureId,provider:e.identifier,referenceId:c.referenceId,type:"usage.report_failed"})}return{checked:r.length,failed:o,updated:i}},m=async(e,t,n,a,d)=>{const r=await Promise.allSettled(e.map(c=>n(c)));let i=0,o=0;for(const[c,s]of r.entries())s.status==="fulfilled"?s.value&&(i+=1):(o+=1,u(d,{error:s.reason,id:e[c]??"",kind:t,provider:a.identifier,type:"reconcile.error"}));return{failed:o,updated:i}},U=async e=>{const{adapter:t,observability:n,store:a}=e,d=e.subscriptionIds??[],r=e.paymentSessionIds??[],i=await m(d,"subscription",s=>P(t,a,s,n),t,n),o=await m(r,"payment",s=>v(t,a,s,n),t,n),c=await g(t,a,e.usageReportLimit??I,n);return u(n,{failedPayments:o.failed,failedSubscriptions:i.failed,provider:t.identifier,type:"reconcile.completed",updatedPayments:o.updated,updatedSubscriptions:i.updated}),{checkedPayments:r.length,checkedSubscriptions:d.length,checkedUsage:c.checked,failedPayments:o.failed,failedSubscriptions:i.failed,failedUsage:c.failed,updatedPayments:o.updated,updatedSubscriptions:i.updated,updatedUsage:c.updated}};export{U as reconcile};
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.mjs";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.mjs";
2
2
  /**
3
3
  * The `autumn-js` SDK surface the adapter uses, as a structural type — a real `Autumn` instance
4
4
  * satisfies it without a cast. Resources/methods are `unknown` (the adapter re-types the client as
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.js";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.js";
2
2
  /**
3
3
  * The `autumn-js` SDK surface the adapter uses, as a structural type — a real `Autumn` instance
4
4
  * satisfies it without a cast. Resources/methods are `unknown` (the adapter re-types the client as
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.mjs";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.mjs";
2
2
  /**
3
3
  * The `creem` SDK surface the adapter uses, as a structural type — a real `Creem` instance satisfies
4
4
  * it without a cast. Resources are `unknown` (the adapter re-types the client as the real `Creem`
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.js";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.js";
2
2
  /**
3
3
  * The `creem` SDK surface the adapter uses, as a structural type — a real `Creem` instance satisfies
4
4
  * it without a cast. Resources are `unknown` (the adapter re-types the client as the real `Creem`
@@ -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-ggrwGvRa.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:t}=a;return typeof t=="number"&&t!==400&&t!==409?!1:k.test(a.message)},l=a=>{const t=o(a),r=Date.now(),e=c(t,"status")??"";return{cancelAtPeriodEnd:S(t),createdAt:r,currentPeriodEnd:f(u(t,"current_period_end_date","currentPeriodEndDate")),currentPeriodStart:f(u(t,"current_period_start_date","currentPeriodStartDate")),id:c(t,"id")??"",priceId:i(t.product)??"",provider:"creem",quantity:p(t,"units")??1,referenceId:m(t)??i(t.customer)??"",state:h[e]??"past_due",updatedAt:r}},B=a=>{const t=o(a),r=Date.now(),e=o(t.order),n=c(e,"currency")??c(t,"currency")??"usd",s=I(BigInt(Math.round(p(e,"amount")??p(t,"amount")??0)),n),d=C[c(e,"status")??c(t,"status")??""]??"initiated";return{amount:s,capturedAmount:d==="captured"||d==="partially_refunded"||d==="refunded"?s:g(n),createdAt:r,id:c(t,"id")??"",provider:"creem",referenceId:m(t)??"",refundedAmount:d==="refunded"?s:g(n),state:d,updatedAt:r}},D=(a,t,r)=>{const e={eventId:a,provider:"creem",raw:{object:r,type:t}},n=o(r.order),s=c(n,"currency")??c(r,"currency")??"usd";switch(t){case"checkout.completed":{const d=p(n,"amount")??p(r,"amount");return{...e,amount:d===void 0?void 0:I(BigInt(Math.round(d)),s),customerId:i(r.customer),referenceId:m(r),sessionId:c(r,"id"),subscriptionId:i(r.subscription),type:"payment.captured"}}case"refund.created":{const d=w(r,"refund_amount","refundAmount","amount")??p(n,"amount"),y=u(r,"refund_currency","refundCurrency")??s;return{...e,amount:d===void 0?void 0:I(BigInt(Math.round(d)),y),referenceId:m(r),refundId:c(r,"id"),sessionId:i(r.checkout)??i(r.transaction)??i(r.subscription)??i(r.order)??c(r,"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=t==="subscription.scheduled_cancel"?"scheduled_cancel":c(r,"status");return{...e,cancelAtPeriodEnd:v(r,"cancel_at_period_end")??S(r),currentPeriodEnd:f(u(r,"current_period_end_date","currentPeriodEndDate")),currentPeriodStart:f(u(r,"current_period_start_date","currentPeriodStartDate")),customerId:i(r.customer),priceId:i(r.product),referenceId:m(r)??i(r.customer),subscriptionId:c(r,"id"),type:P(h[d??""]??"past_due")}}default:return{...e,type:"unhandled"}}},O=a=>{const{webhookSecret:t}=a,r=a.client;return{cancelPayment:()=>_("manual payment cancellation"),cancelSubscription:async(e,n)=>l(await r.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 r.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 r.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 r.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 r.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 r.checkouts.retrieve(e)),getSubscriptionStatus:async e=>l(await r.subscriptions.get(e)),identifier:"creem",parseWebhook:async({headers:e,payload:n})=>{await A({payload:n,secret:t,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 r.subscriptions.resume(e)),updateSubscription:async(e,n)=>n.priceId?l(await r.subscriptions.upgrade(e,{productId:n.priceId,updateBehavior:"proration-charge-immediately"})):l(await r.subscriptions.get(e))}};export{O as createCreemAdapter};
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-ggrwGvRa.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}},D=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}},M=(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":{if(c(t,"status")!=="succeeded")return{...e,type:"unhandled"};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),refundId:c(t,"id"),sessionId:i(t.checkout)??i(t.transaction)??i(t.subscription)??i(t.order)??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=>D(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 M(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,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.mjs";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.mjs";
2
2
  /**
3
3
  * The `dodopayments` SDK surface the adapter uses, as a structural type — a real `DodoPayments`
4
4
  * instance satisfies it without a cast. Resources are `unknown` (the adapter re-types the client as
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.js";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.js";
2
2
  /**
3
3
  * The `dodopayments` SDK surface the adapter uses, as a structural type — a real `DodoPayments`
4
4
  * instance satisfies it without a cast. Resources are `unknown` (the adapter re-types the client as
@@ -1 +1 @@
1
- import{LunoraPaymentError as S}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as b}from"../packem_shared/idempotencyKey-DBL6bpmj.mjs";import{a as i,b as u,r as a,f as c,p as l,d as v}from"../packem_shared/json-BJJPJVYj.mjs";import{money as _,zeroMoney as f}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as h}from"../packem_shared/constantTimeEqual-ggrwGvRa.mjs";import{m as A}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as P}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const q={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=A("dodopayments (merchant-of-record)"),k=/^-?\d+$/,E=(d,n)=>{const t=d[n];return typeof t=="number"?Number.isFinite(t)?BigInt(Math.round(t)):void 0:typeof t=="string"&&k.test(t)?BigInt(t):void 0},m=d=>a(i(d.customer),"customer_id")??a(d,"customer_id"),p=d=>{const n=i(d),t=Date.now(),e=a(n,"status")??"";return{cancelAtPeriodEnd:v(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:u(n,"quantity")??1,referenceId:c(n)??m(n)??"",state:g[e]??"past_due",updatedAt:t}},D=d=>{const n=i(d),t=Date.now(),e=a(n,"currency")??"usd",r=_(BigInt(Math.round(u(n,"total_amount")??0)),e),s=q[a(n,"status")??""]??"initiated";return{amount:r,capturedAmount:s==="captured"?r:f(e),createdAt:t,id:a(n,"payment_id")??"",provider:"dodopayments",referenceId:c(n)??"",refundedAmount:f(e),state:s,updatedAt:t}},O=(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":{const s=E(t,"amount");return{...e,amount:s===void 0?void 0:_(s,r),referenceId:c(t),refundId:a(t,"refund_id")??a(t,"dispute_id"),sessionId:a(t,"payment_id"),type:"payment.refunded"}}case"payment.cancelled":case"payment.failed":return{...e,referenceId:c(t),sessionId:a(t,"payment_id"),type:"payment.failed"};case"payment.succeeded":return{...e,amount:_(BigInt(Math.round(u(t,"total_amount")??0)),r),customerId:m(t),referenceId:c(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=n==="subscription.paused"?"paused":a(t,"status");return{...e,cancelAtPeriodEnd:v(t,"cancel_at_next_billing_date"),currentPeriodEnd:l(a(t,"next_billing_date")),currentPeriodStart:l(a(t,"previous_billing_date")),customerId:m(t),priceId:a(t,"product_id"),quantity:u(t,"quantity"),referenceId:c(t)??m(t),subscriptionId:a(t,"subscription_id"),type:P(g[s??""]??"past_due")}}default:return{...e,type:"unhandled"}}},z=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 p(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:b("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=>D(await t.payments.retrieve(e)),getSubscriptionStatus:async e=>p(await t.subscriptions.retrieve(e)),identifier:"dodopayments",parseWebhook:async({headers:e,payload:r})=>{const s=e.get("webhook-id")??"";await h({payload:r,secret:n,toleranceSeconds:d.webhookToleranceSeconds,webhookId:s,webhookSignature:e.get("webhook-signature")??"",webhookTimestamp:e.get("webhook-timestamp")??""});const o=i(JSON.parse(r));return O(s,a(o,"type")??"",i(o.data))},refundPayment:async e=>{if(e.amount!==void 0)throw new S("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",o=_(BigInt(Math.round(u(r,"amount")??0)),s),y=a(r,"status")==="succeeded",w=y?"refunded":"captured";return{amount:o,capturedAmount:o,createdAt:Date.now(),id:e.sessionId,pending:!y,provider:"dodopayments",referenceId:"",refundedAmount:o,refundId:a(r,"refund_id"),state:w,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 p(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??u(s,"quantity")??1})}return p(await t.subscriptions.retrieve(e))}}};export{z as createDodoPaymentsAdapter};
1
+ import{LunoraPaymentError as w}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as S}from"../packem_shared/idempotencyKey-qQJLcEQX.mjs";import{a as i,b as c,r as n,f as p,p as f,d as v}from"../packem_shared/json-BJJPJVYj.mjs";import{money as m,zeroMoney as b}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as h}from"../packem_shared/constantTimeEqual-ggrwGvRa.mjs";import{m as A}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as P}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const q={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=A("dodopayments (merchant-of-record)"),k=/^-?\d+$/,E=(d,a)=>{const t=d[a];return typeof t=="number"?Number.isFinite(t)?BigInt(Math.round(t)):void 0:typeof t=="string"&&k.test(t)?BigInt(t):void 0},y=d=>n(i(d.customer),"customer_id")??n(d,"customer_id"),_=d=>{const a=i(d),t=Date.now(),e=n(a,"status")??"";return{cancelAtPeriodEnd:v(a,"cancel_at_next_billing_date")??!1,createdAt:t,currentPeriodEnd:f(n(a,"next_billing_date")),currentPeriodStart:f(n(a,"previous_billing_date")),id:n(a,"subscription_id")??"",priceId:n(a,"product_id")??"",provider:"dodopayments",quantity:c(a,"quantity")??1,referenceId:p(a)??y(a)??"",state:g[e]??"past_due",updatedAt:t}},O=d=>{const{refunds:a}=d;if(!Array.isArray(a))return 0n;let t=0n;for(const e of a){const r=i(e),s=c(r,"amount");n(r,"status")==="succeeded"&&s!==void 0&&(t+=BigInt(Math.round(s)))}return t},D=d=>{const a=i(d),t=Date.now(),e=n(a,"currency")??"usd",r=m(BigInt(Math.round(c(a,"total_amount")??0)),e),s=q[n(a,"status")??""]??"initiated",o=s==="captured"?r:b(e),u=O(a);let l=s;return s==="captured"&&u>0n&&(l=u>=o.minorUnits?"refunded":"partially_refunded"),{amount:r,capturedAmount:o,createdAt:t,id:n(a,"payment_id")??"",provider:"dodopayments",referenceId:p(a)??"",refundedAmount:m(u,e),state:l,updatedAt:t}},T=(d,a,t)=>{const e={eventId:d,provider:"dodopayments",raw:{object:t,type:a}},r=n(t,"currency")??"usd";switch(a){case"dispute.lost":case"refund.succeeded":{const s=E(t,"amount");return{...e,amount:s===void 0?void 0:m(s,r),referenceId:p(t),refundId:n(t,"refund_id")??n(t,"dispute_id"),sessionId:n(t,"payment_id"),type:"payment.refunded"}}case"payment.cancelled":case"payment.failed":return{...e,referenceId:p(t),sessionId:n(t,"payment_id"),type:"payment.failed"};case"payment.succeeded":return{...e,amount:m(BigInt(Math.round(c(t,"total_amount")??0)),r),customerId:y(t),referenceId:p(t),sessionId:n(t,"payment_id"),subscriptionId:n(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==="subscription.paused"?"paused":n(t,"status");return{...e,cancelAtPeriodEnd:v(t,"cancel_at_next_billing_date"),currentPeriodEnd:f(n(t,"next_billing_date")),currentPeriodStart:f(n(t,"previous_billing_date")),customerId:y(t),priceId:n(t,"product_id"),quantity:c(t,"quantity"),referenceId:p(t)??y(t),subscriptionId:n(t,"subscription_id"),type:P(g[s??""]??"past_due")}}default:return{...e,type:"unhandled"}}},C=d=>{const{webhookSecret:a}=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 _(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:n(r,"session_id")??"",provider:"dodopayments",url:n(r,"checkout_url")??""}},createPortalSession:async e=>{const r=i(await t.customers.customerPortal.create(e.customerId,{return_url:e.returnUrl}));return{url:n(r,"link")??n(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:n(r,"email")??e.email,id:n(r,"customer_id")??"",provider:"dodopayments",referenceId:e.referenceId}},getPaymentStatus:async e=>D(await t.payments.retrieve(e)),getSubscriptionStatus:async e=>_(await t.subscriptions.retrieve(e)),identifier:"dodopayments",parseWebhook:async({headers:e,payload:r})=>{const s=e.get("webhook-id")??"";await h({payload:r,secret:a,toleranceSeconds:d.webhookToleranceSeconds,webhookId:s,webhookSignature:e.get("webhook-signature")??"",webhookTimestamp:e.get("webhook-timestamp")??""});const o=i(JSON.parse(r));return T(s,n(o,"type")??"",i(o.data))},refundPayment:async e=>{if(e.amount!==void 0)throw new w("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=n(r,"currency")??"usd",o=m(BigInt(Math.round(c(r,"amount")??0)),s),u=n(r,"status")==="succeeded",l=u?"refunded":"captured";return{amount:o,capturedAmount:o,createdAt:Date.now(),id:e.sessionId,pending:!u,provider:"dodopayments",referenceId:"",refundedAmount:o,refundId:n(r,"refund_id"),state:l,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 _(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??n(s,"product_id")??"",proration_billing_mode:"prorated_immediately",quantity:r.quantity??c(s,"quantity")??1})}return _(await t.subscriptions.retrieve(e))}}};export{C as createDodoPaymentsAdapter};
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.mjs";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.mjs";
2
2
  /**
3
3
  * The `@polar-sh/sdk` surface the adapter uses, as a structural type — a real `Polar` instance
4
4
  * satisfies it without a cast. Resources are `unknown` (the adapter re-types the client as the real
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.js";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.js";
2
2
  /**
3
3
  * The `@polar-sh/sdk` surface the adapter uses, as a structural type — a real `Polar` instance
4
4
  * satisfies it without a cast. Resources are `unknown` (the adapter re-types the client as the real
@@ -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 g}from"../packem_shared/constantTimeEqual-ggrwGvRa.mjs";import{m as b}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as v}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"},_=b("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}},h=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}},k=(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),refundId:n(r,"id"),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":v(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"}}},O=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=>h(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 g({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 k(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,P=await r.refunds.create({amount:o,orderId:e.sessionId,reason:e.reason??"customer_request"}),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,refundId:P.id,state:"refunded",updatedAt:Date.now()}},reportUsage:async e=>{await r.events.ingest({events:[{externalCustomerId:e.referenceId,externalId:e.idempotencyKey,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{O as createPolarAdapter};
1
+ import{a as f,r as n,f as p,d as A,p as I,b as c}from"../packem_shared/json-BJJPJVYj.mjs";import{money as m,zeroMoney as _}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as v}from"../packem_shared/constantTimeEqual-ggrwGvRa.mjs";import{m as b}from"../packem_shared/not-supported-Cl0brzhn.mjs";import{s as h}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const w=s=>s instanceof Date?s.getTime():I(typeof s=="string"?s:void 0),g={draft:"initiated",paid:"captured",partially_refunded:"partially_refunded",pending:"initiated",refunded:"refunded",void:"canceled"},P={active:"active",canceled:"canceled",incomplete:"past_due",incomplete_expired:"canceled",past_due:"past_due",trialing:"trialing",unpaid:"past_due"},S=b("polar (merchant-of-record)"),l=s=>{const a=f(s),r=Date.now();return{cancelAtPeriodEnd:A(a,"cancelAtPeriodEnd")??!1,createdAt:r,currentPeriodEnd:w(a.currentPeriodEnd),currentPeriodStart:w(a.currentPeriodStart),id:n(a,"id")??"",priceId:n(a,"productId")??"",provider:"polar",quantity:1,referenceId:p(a)??"",state:P[n(a,"status")??""]??"past_due",updatedAt:r}},k=s=>{const a=f(s),r=Date.now(),e=n(a,"currency")??"usd",t=m(BigInt(Math.round(c(a,"totalAmount")??c(a,"amount")??0)),e),d=g[n(a,"status")??""]??"initiated",o=d==="captured"||d==="partially_refunded"||d==="refunded",i=c(a,"refundedAmount")??c(a,"refunded_amount"),u=d==="refunded"?t:_(e);return{amount:t,capturedAmount:o?t:_(e),createdAt:r,id:n(a,"id")??"",provider:"polar",referenceId:p(a)??"",refundedAmount:i===void 0?u:m(BigInt(Math.round(i)),e),state:d,updatedAt:r}},E=(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"&&g[n(r,"status")??""]!=="captured"?{...e,type:"unhandled"}:{...e,amount:m(c(r,"total_amount")??c(r,"amount")??0,t),customerId:n(r,"customer_id"),referenceId:p(r),sessionId:n(r,"id"),subscriptionId:n(r,"subscription_id"),type:"payment.captured"};case"refund.created":case"refund.updated":return n(r,"status")!=="succeeded"?{...e,type:"unhandled"}:{...e,amount:m(c(r,"amount")??0,t),referenceId:p(r),refundId:n(r,"id"),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":h(P[n(r,"status")??""]??"past_due");return{...e,cancelAtPeriodEnd:A(r,"cancel_at_period_end"),currentPeriodEnd:I(n(r,"current_period_end")),currentPeriodStart:I(n(r,"current_period_start")),customerId:n(r,"customer_id"),priceId:n(r,"product_id"),referenceId:p(r),subscriptionId:n(r,"id"),type:d}}default:return{...e,type:"unhandled"}}},B=s=>{const{webhookSecret:a}=s,r=s.client;return{cancelPayment:()=>S("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 l(d)},capabilities:{merchantOfRecord:!0,portal:!0,usageMetering:!0},capturePayment:e=>S("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=>k(await r.orders.get({id:e})),getSubscriptionStatus:async e=>l(await r.subscriptions.get({id:e})),identifier:"polar",parseWebhook:async({headers:e,payload:t})=>{const d=e.get("webhook-id")??"";await v({payload:t,secret:a,toleranceSeconds:s.webhookToleranceSeconds,webhookId:d,webhookSignature:e.get("webhook-signature")??"",webhookTimestamp:e.get("webhook-timestamp")??""});const o=f(JSON.parse(t));return E(d,n(o,"type")??"",f(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,i=await r.refunds.create({amount:o,orderId:e.sessionId,reason:e.reason??"customer_request"}),u=e.amount??m(BigInt(Math.round(o)),d),y=i.status==="succeeded";return{amount:u,capturedAmount:u,createdAt:Date.now(),id:e.sessionId,pending:!y,provider:"polar",referenceId:"",refundedAmount:u,refundId:i.id,state:y?"refunded":"captured",updatedAt:Date.now()}},reportUsage:async e=>{await r.events.ingest({events:[{externalCustomerId:e.referenceId,externalId:e.idempotencyKey,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 l(t)},updateSubscription:async(e,t)=>{const d=await r.subscriptions.update({id:e,subscriptionUpdate:t.priceId?{productId:t.priceId}:{}});return l(d)}}};export{B as createPolarAdapter};
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.mjs";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.mjs";
2
2
  /**
3
3
  * The Stripe SDK surface the adapter uses, as a structural type — a real `Stripe` instance satisfies
4
4
  * it without a cast. Resources are `unknown` here (the adapter re-types the client as the real
@@ -12,6 +12,8 @@ interface StripeClientLike {
12
12
  readonly customers: unknown;
13
13
  readonly paymentIntents: unknown;
14
14
  readonly refunds: unknown;
15
+ /** Only reached when a subscription's embedded `items` list is paginated — see `itemsComplete`. */
16
+ readonly subscriptionItems: unknown;
15
17
  readonly subscriptions: unknown;
16
18
  readonly webhooks: unknown;
17
19
  }
@@ -1,4 +1,4 @@
1
- import { P as PaymentAdapter } from "../packem_shared/adapter.d-CeLGlHv_.js";
1
+ import { P as PaymentAdapter } from "../packem_shared/adapter.d-DytKm_IF.js";
2
2
  /**
3
3
  * The Stripe SDK surface the adapter uses, as a structural type — a real `Stripe` instance satisfies
4
4
  * it without a cast. Resources are `unknown` here (the adapter re-types the client as the real
@@ -12,6 +12,8 @@ interface StripeClientLike {
12
12
  readonly customers: unknown;
13
13
  readonly paymentIntents: unknown;
14
14
  readonly refunds: unknown;
15
+ /** Only reached when a subscription's embedded `items` list is paginated — see `itemsComplete`. */
16
+ readonly subscriptionItems: unknown;
15
17
  readonly subscriptions: unknown;
16
18
  readonly webhooks: unknown;
17
19
  }
@@ -1 +1 @@
1
- import{LunoraPaymentError as E}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as f}from"../packem_shared/idempotencyKey-DBL6bpmj.mjs";import{a as u,r as s,d as S,b as c}from"../packem_shared/json-BJJPJVYj.mjs";import{compareMoney as P,money as m,zeroMoney as v}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{assertWebhookSecret as T}from"../packem_shared/constantTimeEqual-ggrwGvRa.mjs";import{s as q}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const M={canceled:"canceled",processing:"authorized",requires_action:"authorized",requires_capture:"authorized",requires_confirmation:"initiated",requires_payment_method:"initiated",succeeded:"captured"},h={active:"active",canceled:"canceled",incomplete:"past_due",incomplete_expired:"canceled",past_due:"past_due",paused:"paused",trialing:"trialing",unpaid:"past_due"},o=a=>s(u(a.metadata),"referenceId")??s(a,"client_reference_id"),I=a=>{const n=u(a.items),t=Array.isArray(n.data)?n.data:[];return u(t[0])},w=a=>s(u(I(a).price),"id"),g=a=>c(I(a),"quantity"),A=a=>{const n=c(I(a),"current_period_end")??c(a,"current_period_end");return n===void 0?void 0:n*1e3},K=a=>{const n=c(I(a),"current_period_start")??c(a,"current_period_start");return n===void 0?void 0:n*1e3},_=a=>{const n=u(a),t=s(n,"currency")??"usd",e=c(n,"amount")??0,r=m(BigInt(Math.round(e)),t),i=M[s(n,"status")??""]??"initiated",d=Date.now();return{amount:r,capturedAmount:i==="captured"?m(BigInt(Math.round(c(n,"amount_received")??e)),t):v(t),createdAt:d,id:s(n,"id")??"",provider:"stripe",referenceId:o(n)??"",refundedAmount:v(t),state:i,updatedAt:d}},l=a=>{const n=u(a),t=Date.now();return{cancelAtPeriodEnd:S(n,"cancel_at_period_end")??!1,createdAt:t,currentPeriodEnd:A(n),currentPeriodStart:K(n),id:s(n,"id")??"",priceId:w(n)??"",provider:"stripe",quantity:g(n)??1,referenceId:o(n)??"",state:h[s(n,"status")??""]??"past_due",updatedAt:t}},k=({base:a,currency:n,object:t})=>{const e=s(t,"payment_status"),r=e==="paid"||e==="no_payment_required";if(s(t,"mode")==="subscription")return{...a,customerId:s(t,"customer"),referenceId:o(t),subscriptionId:s(t,"subscription"),type:r?"subscription.active":"subscription.updated"};const i=s(t,"payment_intent");if(i===void 0&&e!=="no_payment_required")return{...a,type:"unhandled"};const d=c(t,"amount_total");return{...a,amount:d===void 0?void 0:m(BigInt(Math.round(d)),n),customerId:s(t,"customer"),referenceId:o(t),sessionId:i??s(t,"id"),type:r?"payment.captured":"payment.authorized"}},B=({base:a,currency:n,object:t})=>s(t,"status")!=="lost"?{...a,type:"unhandled"}:{...a,amount:m(BigInt(Math.round(c(t,"amount")??0)),n),referenceId:o(t),refundId:s(t,"id"),sessionId:s(t,"payment_intent"),type:"payment.refunded"},U=(a,n,t)=>{const e={eventId:a,provider:"stripe",raw:{object:t,type:n}},r=s(t,"currency")??"usd",i={base:e,currency:r,object:t};switch(n){case"charge.dispute.closed":return B(i);case"charge.refunded":return{...e,amount:m(BigInt(Math.round(c(t,"amount_refunded")??0)),r),amountKind:"absolute",referenceId:o(t),sessionId:s(t,"payment_intent")??s(t,"id"),type:"payment.refunded"};case"checkout.session.async_payment_failed":return s(t,"mode")==="subscription"?{...e,customerId:s(t,"customer"),referenceId:o(t),subscriptionId:s(t,"subscription"),type:"subscription.past_due"}:{...e,referenceId:o(t),sessionId:s(t,"payment_intent")??s(t,"id"),type:"payment.failed"};case"checkout.session.async_payment_succeeded":case"checkout.session.completed":return k(i);case"customer.subscription.created":case"customer.subscription.updated":return{...e,cancelAtPeriodEnd:S(t,"cancel_at_period_end"),currentPeriodEnd:A(t),currentPeriodStart:K(t),customerId:s(t,"customer"),priceId:w(t),quantity:g(t),referenceId:o(t),subscriptionId:s(t,"id"),type:q(h[s(t,"status")??""]??"past_due")};case"customer.subscription.deleted":return{...e,referenceId:o(t),subscriptionId:s(t,"id"),type:"subscription.canceled"};case"payment_intent.amount_capturable_updated":return{...e,amount:m(BigInt(Math.round(c(t,"amount")??0)),r),referenceId:o(t),sessionId:s(t,"id"),type:"payment.authorized"};case"payment_intent.payment_failed":return{...e,referenceId:o(t),sessionId:s(t,"id"),type:"payment.failed"};case"payment_intent.succeeded":return{...e,amount:m(BigInt(Math.round(c(t,"amount_received")??c(t,"amount")??0)),r),customerId:s(t,"customer"),referenceId:o(t),sessionId:s(t,"id"),type:"payment.captured"};default:return{...e,type:"unhandled"}}},W=a=>{const{webhookSecret:n}=a,t=a.client;return{cancelPayment:async(e,r)=>{const i=await t.paymentIntents.cancel(e,void 0,{idempotencyKey:r?.idempotencyKey});return _(i)},cancelSubscription:async(e,r)=>{const i=r?.atPeriodEnd?await t.subscriptions.update(e,{cancel_at_period_end:!0},{idempotencyKey:r.idempotencyKey}):await t.subscriptions.cancel(e,void 0,{idempotencyKey:r?.idempotencyKey});return l(i)},capabilities:{merchantOfRecord:!1,portal:!0,usageMetering:!0},capturePayment:async e=>{const r=await t.paymentIntents.capture(e.sessionId,e.amount?{amount_to_capture:Number(e.amount.minorUnits)}:void 0,{idempotencyKey:e.idempotencyKey});return _(r)},createCheckout:async e=>{const r=await t.checkout.sessions.create({cancel_url:e.cancelUrl,client_reference_id:e.referenceId,customer:e.customerId,line_items:[{price:e.priceId,quantity:e.quantity??1}],metadata:{...e.metadata,referenceId:e.referenceId},mode:e.mode,subscription_data:e.mode==="subscription"?{metadata:{referenceId:e.referenceId}}:void 0,success_url:e.successUrl},{idempotencyKey:e.idempotencyKey});return{id:r.id,provider:"stripe",url:r.url??""}},createPortalSession:async e=>({url:(await t.billingPortal.sessions.create({customer:e.customerId,return_url:e.returnUrl})).url}),getOrCreateCustomer:async e=>{const r=await t.customers.create({email:e.email,metadata:{...e.metadata,referenceId:e.referenceId}},{idempotencyKey:f("customer","stripe",e.referenceId)});return{createdAt:Date.now(),email:r.email??void 0,id:r.id,provider:"stripe",referenceId:e.referenceId}},getPaymentStatus:async e=>_(await t.paymentIntents.retrieve(e)),getSubscriptionStatus:async e=>l(await t.subscriptions.retrieve(e)),identifier:"stripe",parseWebhook:async({headers:e,payload:r})=>{T(n);const i=e.get("stripe-signature")??"";let d;try{d=await t.webhooks.constructEventAsync(r,i,n,a.webhookToleranceSeconds)}catch(y){throw new E("WEBHOOK_SIGNATURE_INVALID",y instanceof Error?y.message:"invalid webhook signature")}const p=u(u(d.data).object);return U(d.id,d.type,p)},refundPayment:async e=>{const r=await t.refunds.create({amount:e.amount?Number(e.amount.minorUnits):void 0,payment_intent:e.sessionId,reason:e.reason},{idempotencyKey:e.idempotencyKey}),i=await t.paymentIntents.retrieve(e.sessionId),d=_(i),p=e.amount??d.capturedAmount,y=e.amount!==void 0&&P(e.amount,d.capturedAmount)<0;return{...d,refundedAmount:p,refundId:r.id,state:y?"partially_refunded":"refunded"}},reportUsage:async e=>{await t.billing.meterEvents.create({event_name:e.featureId,identifier:e.idempotencyKey,payload:{stripe_customer_id:e.customerId??"",value:String(e.quantity)},timestamp:e.timestamp===void 0?void 0:Math.floor(e.timestamp/1e3)},{idempotencyKey:e.idempotencyKey})},resumeSubscription:async(e,r)=>{const i=await t.subscriptions.update(e,{cancel_at_period_end:!1},{idempotencyKey:r?.idempotencyKey??f("resume_subscription","stripe",e)});return l(i)},updateSubscription:async(e,r)=>{const i={};if(r.priceId!==void 0||r.quantity!==void 0){const p=await t.subscriptions.retrieve(e);i.items=[{id:p.items.data[0]?.id,price:r.priceId,quantity:r.quantity}]}const d=await t.subscriptions.update(e,i,{idempotencyKey:r.idempotencyKey??f("update_subscription","stripe",e,r.priceId??"",r.quantity??"")});return l(d)}}};export{W as createStripeAdapter};
1
+ import{LunoraPaymentError as T}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as f}from"../packem_shared/idempotencyKey-qQJLcEQX.mjs";import{a as c,r as i,d as v,b as u}from"../packem_shared/json-BJJPJVYj.mjs";import{compareMoney as q,money as y,zeroMoney as h}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{assertWebhookSecret as M}from"../packem_shared/constantTimeEqual-ggrwGvRa.mjs";import{s as k}from"../packem_shared/subscription-event-C8GiQSFM.mjs";const B={canceled:"canceled",processing:"authorized",requires_action:"authorized",requires_capture:"authorized",requires_confirmation:"initiated",requires_payment_method:"initiated",succeeded:"captured"},w={active:"active",canceled:"canceled",incomplete:"past_due",incomplete_expired:"canceled",past_due:"past_due",paused:"paused",trialing:"trialing",unpaid:"past_due"},o=r=>i(c(r.metadata),"referenceId")??i(r,"client_reference_id"),S=r=>{const s=c(r.items);return Array.isArray(s.data)?s.data:[]},I=r=>c(S(r)[0]),g=r=>i(c(I(r).price),"id"),U=r=>{const s=[];for(const t of S(r)){const e=i(c(c(t).price),"id");e!==void 0&&e!==""&&s.push(e)}return s},N=r=>v(c(r.items),"has_more")!==!0,A=r=>N(r)?U(r):void 0,K=r=>u(I(r),"quantity"),P=r=>{const s=u(I(r),"current_period_end")??u(r,"current_period_end");return s===void 0?void 0:s*1e3},E=r=>{const s=u(I(r),"current_period_start")??u(r,"current_period_start");return s===void 0?void 0:s*1e3},_=r=>{const s=c(r),t=i(s,"currency")??"usd",e=u(s,"amount")??0,n=y(BigInt(Math.round(e)),t),a=B[i(s,"status")??""]??"initiated",d=Date.now();return{amount:n,capturedAmount:a==="captured"?y(BigInt(Math.round(u(s,"amount_received")??e)),t):h(t),createdAt:d,id:i(s,"id")??"",provider:"stripe",referenceId:o(s)??"",refundedAmount:h(t),state:a,updatedAt:d}},l=r=>{const s=c(r),t=Date.now();return{cancelAtPeriodEnd:v(s,"cancel_at_period_end")??!1,createdAt:t,currentPeriodEnd:P(s),currentPeriodStart:E(s),id:i(s,"id")??"",priceId:g(s)??"",priceIds:A(s),provider:"stripe",quantity:K(s)??1,referenceId:o(s)??"",state:w[i(s,"status")??""]??"past_due",updatedAt:t}},R=({base:r,currency:s,object:t})=>{const e=i(t,"payment_status"),n=e==="paid"||e==="no_payment_required";if(i(t,"mode")==="subscription")return{...r,customerId:i(t,"customer"),referenceId:o(t),subscriptionId:i(t,"subscription"),type:n?"subscription.active":"subscription.updated"};const a=i(t,"payment_intent");if(a===void 0&&e!=="no_payment_required")return{...r,type:"unhandled"};const d=u(t,"amount_total");return{...r,amount:d===void 0?void 0:y(BigInt(Math.round(d)),s),customerId:i(t,"customer"),referenceId:o(t),sessionId:a??i(t,"id"),type:n?"payment.captured":"payment.authorized"}},z=({base:r,currency:s,object:t})=>i(t,"status")!=="lost"?{...r,type:"unhandled"}:{...r,amount:y(BigInt(Math.round(u(t,"amount")??0)),s),referenceId:o(t),refundId:i(t,"id"),sessionId:i(t,"payment_intent"),type:"payment.refunded"},C=(r,s,t)=>{const e={eventId:r,provider:"stripe",raw:{object:t,type:s}},n=i(t,"currency")??"usd",a={base:e,currency:n,object:t};switch(s){case"charge.dispute.closed":return z(a);case"charge.refunded":return{...e,amount:y(BigInt(Math.round(u(t,"amount_refunded")??0)),n),amountKind:"absolute",referenceId:o(t),sessionId:i(t,"payment_intent")??i(t,"id"),type:"payment.refunded"};case"checkout.session.async_payment_failed":return i(t,"mode")==="subscription"?{...e,customerId:i(t,"customer"),referenceId:o(t),subscriptionId:i(t,"subscription"),type:"subscription.past_due"}:{...e,referenceId:o(t),sessionId:i(t,"payment_intent")??i(t,"id"),type:"payment.failed"};case"checkout.session.async_payment_succeeded":case"checkout.session.completed":return R(a);case"customer.subscription.created":case"customer.subscription.updated":return{...e,cancelAtPeriodEnd:v(t,"cancel_at_period_end"),currentPeriodEnd:P(t),currentPeriodStart:E(t),customerId:i(t,"customer"),priceId:g(t),priceIds:A(t),quantity:K(t),referenceId:o(t),subscriptionId:i(t,"id"),type:k(w[i(t,"status")??""]??"past_due")};case"customer.subscription.deleted":return{...e,referenceId:o(t),subscriptionId:i(t,"id"),type:"subscription.canceled"};case"payment_intent.amount_capturable_updated":return{...e,amount:y(BigInt(Math.round(u(t,"amount")??0)),n),referenceId:o(t),sessionId:i(t,"id"),type:"payment.authorized"};case"payment_intent.payment_failed":return{...e,referenceId:o(t),sessionId:i(t,"id"),type:"payment.failed"};case"payment_intent.succeeded":return{...e,amount:y(BigInt(Math.round(u(t,"amount_received")??u(t,"amount")??0)),n),customerId:i(t,"customer"),referenceId:o(t),sessionId:i(t,"id"),type:"payment.captured"};default:return{...e,type:"unhandled"}}},L=r=>{const{webhookSecret:s}=r,t=r.client;return{cancelPayment:async(e,n)=>{const a=await t.paymentIntents.cancel(e,void 0,{idempotencyKey:n?.idempotencyKey});return _(a)},cancelSubscription:async(e,n)=>{const a=n?.atPeriodEnd?await t.subscriptions.update(e,{cancel_at_period_end:!0},{idempotencyKey:n.idempotencyKey}):await t.subscriptions.cancel(e,void 0,{idempotencyKey:n?.idempotencyKey});return l(a)},capabilities:{merchantOfRecord:!1,portal:!0,usageMetering:!0},capturePayment:async e=>{const n=await t.paymentIntents.capture(e.sessionId,e.amount?{amount_to_capture:Number(e.amount.minorUnits)}:void 0,{idempotencyKey:e.idempotencyKey});return _(n)},createCheckout:async e=>{const n=await t.checkout.sessions.create({cancel_url:e.cancelUrl,client_reference_id:e.referenceId,customer:e.customerId,line_items:[{price:e.priceId,quantity:e.quantity??1}],metadata:{...e.metadata,referenceId:e.referenceId},mode:e.mode,subscription_data:e.mode==="subscription"?{metadata:{referenceId:e.referenceId}}:void 0,success_url:e.successUrl},{idempotencyKey:e.idempotencyKey});return{id:n.id,provider:"stripe",url:n.url??""}},createPortalSession:async e=>({url:(await t.billingPortal.sessions.create({customer:e.customerId,return_url:e.returnUrl})).url}),getOrCreateCustomer:async e=>{const n=await t.customers.create({email:e.email,metadata:{...e.metadata,referenceId:e.referenceId}},{idempotencyKey:f("customer","stripe",e.referenceId)});return{createdAt:Date.now(),email:n.email??void 0,id:n.id,provider:"stripe",referenceId:e.referenceId}},getPaymentStatus:async e=>_(await t.paymentIntents.retrieve(e)),getSubscriptionStatus:async e=>{const n=await t.subscriptions.retrieve(e),a=l(n);if(a.priceIds!==void 0)return a;const d=[];for await(const p of t.subscriptionItems.list({limit:100,subscription:e})){const m=i(c(c(p).price),"id");m!==void 0&&m!==""&&d.push(m)}return{...a,priceIds:d}},identifier:"stripe",parseWebhook:async({headers:e,payload:n})=>{M(s);const a=e.get("stripe-signature")??"";let d;try{d=await t.webhooks.constructEventAsync(n,a,s,r.webhookToleranceSeconds)}catch(m){throw new T("WEBHOOK_SIGNATURE_INVALID",m instanceof Error?m.message:"invalid webhook signature")}const p=c(c(d.data).object);return C(d.id,d.type,p)},refundPayment:async e=>{const n=await t.refunds.create({amount:e.amount?Number(e.amount.minorUnits):void 0,payment_intent:e.sessionId,reason:e.reason},{idempotencyKey:e.idempotencyKey}),a=await t.paymentIntents.retrieve(e.sessionId),d=_(a),p=e.amount??d.capturedAmount,m=e.amount!==void 0&&q(e.amount,d.capturedAmount)<0;return{...d,refundedAmount:p,refundId:n.id,state:m?"partially_refunded":"refunded"}},reportUsage:async e=>{await t.billing.meterEvents.create({event_name:e.featureId,identifier:e.idempotencyKey,payload:{stripe_customer_id:e.customerId??"",value:String(e.quantity)},timestamp:e.timestamp===void 0?void 0:Math.floor(e.timestamp/1e3)},{idempotencyKey:e.idempotencyKey})},resumeSubscription:async(e,n)=>{const a=await t.subscriptions.update(e,{cancel_at_period_end:!1},{idempotencyKey:n?.idempotencyKey??f("resume_subscription","stripe",e)});return l(a)},updateSubscription:async(e,n)=>{const a={};if(n.priceId!==void 0||n.quantity!==void 0){const p=await t.subscriptions.retrieve(e);a.items=[{id:p.items.data[0]?.id,price:n.priceId,quantity:n.quantity}]}const d=await t.subscriptions.update(e,a,{idempotencyKey:n.idempotencyKey??f("update_subscription","stripe",e,n.priceId??"",n.quantity??"")});return l(d)}}};export{L as createStripeAdapter};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/payment",
3
- "version": "1.0.0-alpha.124",
3
+ "version": "1.0.0-alpha.125",
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.36",
75
- "@lunora/server": "1.0.0-alpha.121",
76
- "@lunora/values": "1.0.0-alpha.45",
75
+ "@lunora/server": "1.0.0-alpha.122",
76
+ "@lunora/values": "1.0.0-alpha.46",
77
77
  "dinero.js": "2.0.2"
78
78
  },
79
79
  "peerDependencies": {
@@ -1 +0,0 @@
1
- import{LunoraPaymentError as P}from"./LunoraPaymentError-BSxyhWgu.mjs";import{localRefundKey as A,LOCAL_REFUND_CLAIM_TYPE as E}from"./idempotencyKey-DBL6bpmj.mjs";import{zeroMoney as o,compareMoney as l,addMoney as _}from"./addMoney-B4ufWAnD.mjs";import{n as i}from"./observability-BteZ2dJS.mjs";import{nextPaymentState as S,nextSubscriptionState as w}from"./PAYMENT_TERMINAL_STATES-Dxxpk_br.mjs";const b={"payment.authorized":"authorize","payment.captured":"capture","payment.failed":"fail","payment.refunded":"refund"},h={"subscription.active":"active","subscription.canceled":"canceled","subscription.past_due":"past_due","subscription.paused":"paused"},T="orphan-retry:",R="marker.orphan_retry",k={"subscription.active":"activate","subscription.canceled":"cancel","subscription.past_due":"mark_past_due","subscription.paused":"pause"},O=(t,e)=>l(t,e)>0?t:e,Y=new Set(["authorized","initiated"]),N=async(t,e,r)=>{if(!r||!e.sessionId||!e.amount||e.amountKind==="absolute")return e;const n=A(e.sessionId,e.refundId,e.amount),d=await t.markEventProcessed(e.provider,n,E);return await t.releaseEvent(e.provider,n),d?e:{...e,amount:o(e.amount.currency)}},I=(t,e)=>{if(!e.amount||t.refundedAmount.currency!==e.amount.currency||t.capturedAmount.currency!==e.amount.currency)return;const r=e.amountKind==="absolute"?O(e.amount,t.refundedAmount):_(t.refundedAmount,e.amount);if(!(l(r,t.capturedAmount)>0))return r},M=(t,e)=>{if(!t)return"refund";const r=I(t,e);return r&&l(r,t.capturedAmount)<0?"partial_refund":"refund"},q=async(t,e,r)=>{if(!e.sessionId)return{applied:!1,reason:"unhandled"};const n=await t.getPaymentSession(e.provider,e.sessionId),d=n?.state??"initiated",u=Date.now(),s=e.amount?.currency??n?.amount.currency??"USD",c=r==="refund"?await N(t,e,n):e,a=r==="refund"?M(n,c):r,m=S(d,a);if(!m)return{applied:!1,reason:r==="refund"&&Y.has(d)?"orphaned":"illegal_transition"};const p=n??{amount:e.amount??o(s),capturedAmount:o(s),createdAt:u,id:e.sessionId,provider:e.provider,referenceId:e.referenceId??"",refundedAmount:o(s),state:d,updatedAt:u};let{capturedAmount:y,refundedAmount:v}=p;if(a==="capture"&&e.amount&&(y=e.amount),(a==="partial_refund"||a==="refund")&&c.amount){const f=I(p,c);if(!f)return{applied:!1,reason:"invalid_refund_amount"};v=f}return await t.upsertPaymentSession({...p,capturedAmount:y,referenceId:e.referenceId??p.referenceId,refundedAmount:v,state:m,updatedAt:u}),{applied:!0,reason:"ok"}},C=(t,e,r)=>{if(e==="active"){if(t==="active")return"renew";if(t==="paused")return"resume"}return k[r]},g=async(t,e)=>{if(!e.subscriptionId)return{applied:!1,reason:"unhandled"};const r=await t.getSubscription(e.provider,e.subscriptionId),n=Date.now();if(e.type==="subscription.updated")return r?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,quantity:e.quantity??r.quantity,updatedAt:n}),{applied:!0,reason:"ok"}):{applied:!1,reason:"orphaned"};const d=h[e.type];if(!d)return{applied:!1,reason:"unhandled"};if(!r)return await t.upsertSubscription({cancelAtPeriodEnd:e.cancelAtPeriodEnd??!1,createdAt:n,currentPeriodEnd:e.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??n,id:e.subscriptionId,priceId:e.priceId??"",provider:e.provider,quantity:e.quantity??1,referenceId:e.referenceId??"",state:d,updatedAt:n}),{applied:!0,reason:"ok"};const u=C(r.state,d,e.type),s=u?w(r.state,u):void 0;return s?(await t.upsertSubscription({...r,cancelAtPeriodEnd:e.cancelAtPeriodEnd??r.cancelAtPeriodEnd,currentPeriodEnd:e.currentPeriodEnd??r.currentPeriodEnd,currentPeriodStart:e.currentPeriodStart??r.currentPeriodStart,priceId:e.priceId??r.priceId,quantity:e.quantity??r.quantity,state:s,updatedAt:n}),{applied:!0,reason:"ok"}):{applied:!1,reason:"illegal_transition"}},U=async(t,e,r)=>{if(e.type==="unhandled")return{applied:!1,reason:"unhandled"};if(!e.eventId?.trim())throw new P("WEBHOOK_EVENT_ID_MISSING",`webhook event id is missing or blank for provider "${e.provider}"`);if(!await t.markEventProcessed(e.provider,e.eventId,e.type))return i(r,{eventId:e.eventId,provider:e.provider,type:"webhook.duplicate"}),{applied:!1,reason:"duplicate"};const d=b[e.type];let u;try{u=d?await q(t,e,d):await g(t,e)}catch(s){throw await t.releaseEvent(e.provider,e.eventId),s}return u.reason==="orphaned"&&(await t.markEventProcessed(e.provider,`${T}${e.eventId}`,R)?await t.releaseEvent(e.provider,e.eventId):u={applied:!1,reason:"unhandled"}),i(r,{action:e.type,eventId:e.eventId,provider:e.provider,reason:u.reason,type:"webhook.applied"}),e.type==="payment.failed"?i(r,{provider:e.provider,referenceId:e.referenceId,sessionId:e.sessionId,type:"payment.failed"}):e.type==="subscription.past_due"&&i(r,{provider:e.provider,referenceId:e.referenceId,subscriptionId:e.subscriptionId,type:"subscription.past_due"}),u};export{U as default};
@@ -1 +0,0 @@
1
- import{money as y}from"./addMoney-B4ufWAnD.mjs";import{foldUsage as m}from"./MemoryPaymentStore-CZbrrEb1.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 a=await e.findFirst(t,i);if(a){await e.patch(a._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 a=[{createdAt:"asc"},{idempotencyKey:"asc"}],s=[];let n;do{const u=await e.findMany("usageEvents",{provider:t,reportedToProvider:!1},{cursor:n,limit:r,orderBy:a});for(const g of u.rows){const p=q(g);if(p.mode!=="set"&&p.quantity>0&&(s.push(p),s.length===r))return s}n=u.cursor}while(n!==void 0);return s},markEventProcessed:async(t,i,r)=>await e.findFirst("events",{provider:t,providerEventId:i})?!1:(await e.insert("events",{processedAt:Date.now(),provider:t,providerEventId:i,type:r}),!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:a}=await e.findMany("usageEvents",{featureId:i,referenceId:t}),s=a.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(s)},sumUsageByFeature:async(t,i,r)=>{const{rows:a}=await e.findMany("usageEvents",{referenceId:t}),s=new Map(i.map(n=>[n,[]]));for(const n of a)c(n,"createdAt")<r||s.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([...s].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};
@@ -1 +0,0 @@
1
- import{toErrorBody as P}from"@lunora/errors";import{usagePeriodStart as b,resolveEntitlements as A,featureNames as N,hasActivePrice as U}from"./entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as f}from"./LunoraPaymentError-BSxyhWgu.mjs";import{derivedIdempotencyKey as v,localRefundKey as _,LOCAL_REFUND_CLAIM_TYPE as D,idempotencyKey as k}from"./idempotencyKey-DBL6bpmj.mjs";import{subtractMoney as E,addMoney as K,compareMoney as R,isZeroMoney as B}from"./addMoney-B4ufWAnD.mjs";import{n as T}from"./observability-BteZ2dJS.mjs";import C from"./applyWebhookAction-DqM4qNr3.mjs";const y=(o,n=200,s)=>{const m=new Headers({"content-type":"application/json"});for(const[l,w]of Object.entries({}))m.set(l,w);return Response.json(o,{headers:m,status:n})};let h=0;const F=()=>{const o=Date.now();return h=o>h?o:h+1,h},S=o=>o?`${o.currency}:${String(o.minorUnits)}`:"full",q=o=>o&&"referenceId"in o?Object.fromEntries(Object.entries(o).filter(([n])=>n!=="referenceId")):o,W=o=>y({applied:o.applied},o.status),G=o=>{const{adapter:n,store:s}=o,m=async e=>{if(!o.authorize)return;let t;try{t=await o.authorize(e)}catch{throw new f("FORBIDDEN",`caller not authorized for reference "${e}"`)}if(t!==!0)throw new f("FORBIDDEN",`caller not authorized for reference "${e}"`)},l=async e=>{await m(e.referenceId);const t=q(e.metadata);let r;const a=await s.getCustomerByReference(n.identifier,e.referenceId);if(a)r=a.id;else{const d=await n.getOrCreateCustomer({email:e.email,referenceId:e.referenceId});r=d.id,await s.upsertCustomer(d)}const i=e.idempotencyKey??await v("checkout",n.identifier,e.referenceId,e.priceId,e.mode,String(e.quantity??1),e.successUrl,e.cancelUrl,t?JSON.stringify(t):"");return n.createCheckout({...e,customerId:r,idempotencyKey:i,metadata:t})},w=(e,t,r)=>{const a=e-t;return{allowed:a>=r,balance:a,limit:e,unlimited:!1,used:t}},O=async(e,t,r,a,i)=>{const d=e.limit(a);if(d!==void 0){const c=await s.sumUsage(r,a,b(t));return w(d,c,i)}return{allowed:e.has(a),unlimited:e.has(a)}},p=async e=>{const t=()=>new f("NOT_FOUND",`payment session "${e}" not found`);let r=await s.getPaymentSession(n.identifier,e);if(!r)try{r=await n.getPaymentStatus(e)}catch{throw t()}if(!r.referenceId)throw t();try{await m(r.referenceId)}catch{throw t()}return r},g=async(e,t)=>{const r={...e,...t,updatedAt:Date.now()};return await s.upsertPaymentSession(r),r};return{adapter:n,attach:async e=>l({...e,mode:e.mode??"subscription"}),cancelPayment:async(e,t)=>{const r=await p(e),a=t?.idempotencyKey??k("cancel_payment",n.identifier,e),i=await n.cancelPayment(e,{...t,idempotencyKey:a});return g(r,{state:i.state})},cancelSubscription:async(e,t)=>{const r=await s.getSubscription(n.identifier,e);if(!r)throw new f("NOT_FOUND",`subscription "${e}" not found`);try{await m(r.referenceId)}catch{throw new f("NOT_FOUND",`subscription "${e}" not found`)}const a=t?.idempotencyKey??k("cancel_subscription",n.identifier,e),i=await n.cancelSubscription(e,{...t,idempotencyKey:a});return await s.upsertSubscription(i),i},capturePayment:async e=>{const t=await p(e.sessionId),r=e.idempotencyKey??await v("capture_payment",n.identifier,e.sessionId,S(e.amount)),a=await n.capturePayment({...e,idempotencyKey:r});return g(t,{capturedAmount:a.capturedAmount,state:a.state})},check:async e=>{if(await m(e.referenceId),e.featureId===void 0&&e.priceId===void 0)throw new f("VALIDATION_ERROR","check() requires a featureId or priceId");if(n.checkEntitlement)return n.checkEntitlement(e);const t=await s.listSubscriptionsByReference(e.referenceId);if(e.priceId!==void 0)return{allowed:U(t,e.priceId),unlimited:!1};if(e.featureId===void 0)throw new f("VALIDATION_ERROR","check() requires a featureId or priceId");if(!o.entitlements)throw new f("CONFIG_INVALID","check() requires `entitlements` to be configured");const r=A(o.entitlements,t);return O(r,t,e.referenceId,e.featureId,e.quantity??1)},createCheckout:async e=>l(e),createPortalSession:async(e,t)=>{await m(e);const r=await s.getCustomerByReference(n.identifier,e);if(!r)throw new f("NOT_FOUND",`no customer for reference "${e}"`);return n.createPortalSession({customerId:r.id,returnUrl:t})},handleWebhook:async e=>{let t;try{const a=await e.text();t=await n.parseWebhook({headers:e.headers,payload:a})}catch(a){if(a instanceof f){const{body:i,status:d}=P(a);return y({error:i.message},d)}return y({error:"webhook error"},400)}const r=await C(s,t,o.observability);return r.reason==="orphaned"?y({applied:r.applied,reason:r.reason},500):y({applied:r.applied,reason:r.reason},200)},listBalances:async e=>{if(await m(e),n.getBalances)return n.getBalances(e);if(!o.entitlements)throw new f("CONFIG_INVALID","listBalances() requires `entitlements` to be configured");const t=await s.listSubscriptionsByReference(e),r=A(o.entitlements,t),a=N(o.entitlements),i=a.filter(c=>r.limit(c)!==void 0),d=i.length===0?new Map:await s.sumUsageByFeature(e,i,b(t));return a.map(c=>{const u=r.limit(c);return u!==void 0?{featureId:c,...w(u,d.get(c)??0,1)}:{featureId:c,allowed:r.has(c),unlimited:r.has(c)}})},listSubscriptions:async e=>(await m(e),s.listSubscriptionsByReference(e)),refundPayment:async e=>{const t=await p(e.sessionId),r=e.amount??E(t.capturedAmount,t.refundedAmount),a=K(t.refundedAmount,r);if(R(a,t.capturedAmount)>0)throw new f("VALIDATION_ERROR",`refundPayment(): refunding ${String(e.amount?.minorUnits)} would exceed the captured amount on session "${e.sessionId}"`);if(B(r))return t;const i=e.amount??(R(r,t.capturedAmount)===0?void 0:r),d=e.idempotencyKey??await v("refund_payment",n.identifier,e.sessionId,S(i),e.reason??""),c=await n.refundPayment({...e,amount:i,idempotencyKey:d});if(c.pending)return t;const u=_(e.sessionId,c.refundId,r);await s.markEventProcessed(n.identifier,u,D);try{return await g(t,{refundedAmount:a,state:R(a,t.capturedAmount)<0?"partially_refunded":"refunded"})}catch(I){throw await s.releaseEvent(n.identifier,u),I}},store:s,track:async e=>{await m(e.referenceId);const t=e.quantity===void 0?1:e.quantity;if(!Number.isSafeInteger(t)||t<0)throw new f("VALIDATION_ERROR",`track(): \`quantity\` must be a non-negative safe integer (got ${String(e.quantity)})`);const r=e.idempotencyKey??crypto.randomUUID(),a=e.mode==="set",i=a?await s.listSubscriptionsByReference(e.referenceId):void 0,d=i===void 0?0:await s.sumUsage(e.referenceId,e.featureId,b(i));if(a?t===d:t===0)return{recorded:!1,reportedToProvider:!1};const c=a?t-d:t;if(!await s.recordUsage({createdAt:F(),featureId:e.featureId,idempotencyKey:r,...a?{mode:"set"}:{},provider:n.identifier,quantity:t,referenceId:e.referenceId,reportedToProvider:!1}))return{recorded:!1,reportedToProvider:!1};if(c<=0||!n.capabilities.usageMetering||!n.reportUsage)return{recorded:!0,reportedToProvider:!1};try{const I=await s.getCustomerByReference(n.identifier,e.referenceId);return await n.reportUsage({customerId:I?.id,featureId:e.featureId,idempotencyKey:r,quantity:c,referenceId:e.referenceId}),await s.markUsageReported(n.identifier,r),{recorded:!0,reportedToProvider:!0}}catch{return T(o.observability,{featureId:e.featureId,provider:n.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{G as createPayment,W as webhookResponse};
@@ -1 +0,0 @@
1
- const f=new Set(["active","trialing"]),m=s=>{let t=0;for(const e of s)f.has(e.state)&&e.currentPeriodStart!==void 0&&(t=Math.max(t,e.currentPeriodStart));return t},p=s=>{const t=new Set;for(const e of Object.values(s.plans)){for(const o of e.features??[])t.add(o);for(const o of Object.keys(e.limits??{}))t.add(o)}return[...t].toSorted((e,o)=>e.localeCompare(o))},S=(s,t)=>s.some(e=>e.priceId===t&&f.has(e.state)),d=(s,t)=>{const e=new Set(t.filter(n=>f.has(n.state)).map(n=>n.priceId)),o=[],a=new Set,c=new Map;for(const[n,i]of Object.entries(s.plans))if(i.priceIds.some(r=>e.has(r))){o.push(n);for(const r of i.features??[])a.add(r);for(const[r,l]of Object.entries(i.limits??{})){const u=c.get(r);c.set(r,u===void 0?l:Math.max(u,l))}}return{features:a,has:n=>a.has(n),limit:n=>c.get(n),plans:o}},h=async(s,t,e)=>d(t,await s.listSubscriptionsByReference(e));export{h as entitlementsForReference,p as featureNames,S as hasActivePrice,d as resolveEntitlements,m as usagePeriodStart};
@@ -1 +0,0 @@
1
- const o=new TextEncoder,r=async t=>[...new Uint8Array(await crypto.subtle.digest("SHA-256",o.encode(t)))].map(n=>n.toString(16).padStart(2,"0")).join(""),a=(t,...n)=>[t,...n.map(String)].join(":"),i=async(t,n,...e)=>{const c=await r(JSON.stringify([n,...e.map(String)]));return`${t}:${n}:${c}`},d=(t,n,e)=>n===void 0?`local-refund:${t}:${e.currency}:${String(e.minorUnits)}`:`local-refund:${t}:id:${n}`,s="marker.local_refund";export{s as LOCAL_REFUND_CLAIM_TYPE,i as derivedIdempotencyKey,a as idempotencyKey,d as localRefundKey};
@@ -1 +0,0 @@
1
- import{defineTable as r}from"@lunora/server";import{v as e}from"@lunora/values";const n=r({createdAt:e.number(),email:e.optional(e.string()),provider:e.string(),providerCustomerId:e.string(),referenceId:e.string()}).index("by_provider_customer",["provider","providerCustomerId"],{unique:!0}).index("by_reference",["referenceId"]),i=r({cancelAtPeriodEnd:e.boolean(),createdAt:e.number(),currentPeriodEnd:e.optional(e.number()),currentPeriodStart:e.optional(e.number()),priceId:e.string(),provider:e.string(),providerSubscriptionId:e.string(),quantity:e.number(),referenceId:e.string(),state:e.string(),updatedAt:e.number()}).index("by_provider_subscription",["provider","providerSubscriptionId"],{unique:!0}).index("by_reference",["referenceId"]),t=r({amountMinor:e.bigint(),capturedMinor:e.bigint(),createdAt:e.number(),currency:e.string(),provider:e.string(),providerSessionId:e.string(),referenceId:e.string(),refundedMinor:e.bigint(),state:e.string(),updatedAt:e.number()}).index("by_provider_session",["provider","providerSessionId"],{unique:!0}).index("by_reference",["referenceId"]),d=r({processedAt:e.number(),provider:e.string(),providerEventId:e.string(),type:e.string()}).index("by_provider_event",["provider","providerEventId"],{unique:!0}),o=r({createdAt:e.number(),featureId:e.string(),idempotencyKey:e.string(),mode:e.optional(e.string()),provider:e.string(),quantity:e.number(),referenceId:e.string(),reportedToProvider:e.boolean()}).index("by_idempotency",["provider","idempotencyKey"],{unique:!0}).index("by_reference_feature",["referenceId","featureId"]),p={customers:n,events:d,paymentSessions:t,subscriptions:i,usageEvents:o};export{p as default};
@@ -1 +0,0 @@
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};