@lunora/payment 1.0.0-alpha.101 → 1.0.0-alpha.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/index.d.mts +34 -5
- package/dist/index.d.ts +34 -5
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/MemoryPaymentStore-CZbrrEb1.mjs +1 -0
- package/dist/packem_shared/PAYMENT_TERMINAL_STATES-tA4tF4im.mjs +1 -0
- package/dist/packem_shared/{adapter.d-Cpy8g96b.d.mts → adapter.d-mtRgt9o6.d.mts} +33 -2
- package/dist/packem_shared/{adapter.d-Cpy8g96b.d.ts → adapter.d-mtRgt9o6.d.ts} +33 -2
- package/dist/packem_shared/applyWebhookAction-DMAgu_yP.mjs +1 -0
- package/dist/packem_shared/{createDatabasePaymentStore-CVrAhWjP.mjs → createDatabasePaymentStore-DWe1Vz4V.mjs} +1 -1
- package/dist/packem_shared/createPayment-CS5Pd2yI.mjs +1 -0
- package/dist/packem_shared/idempotencyKey-DBL6bpmj.mjs +1 -0
- package/dist/packem_shared/{lunoraDatabaseToPaymentDatabase-5WkBd3aZ.mjs → lunoraDatabaseToPaymentDatabase-DcpOgifo.mjs} +1 -1
- package/dist/providers/autumn.d.mts +1 -1
- package/dist/providers/autumn.d.ts +1 -1
- package/dist/providers/creem.d.mts +1 -1
- package/dist/providers/creem.d.ts +1 -1
- package/dist/providers/creem.mjs +1 -1
- package/dist/providers/dodopayments.d.mts +1 -1
- package/dist/providers/dodopayments.d.ts +1 -1
- package/dist/providers/dodopayments.mjs +1 -1
- package/dist/providers/polar.d.mts +1 -1
- package/dist/providers/polar.d.ts +1 -1
- package/dist/providers/polar.mjs +1 -1
- package/dist/providers/stripe.d.mts +1 -1
- package/dist/providers/stripe.d.ts +1 -1
- package/dist/providers/stripe.mjs +1 -1
- package/package.json +4 -4
- package/dist/packem_shared/MemoryPaymentStore-Cu9J-zf9.mjs +0 -1
- package/dist/packem_shared/PAYMENT_TERMINAL_STATES-DVerjPR6.mjs +0 -1
- package/dist/packem_shared/applyWebhookAction-daHKTG_Q.mjs +0 -1
- package/dist/packem_shared/createPayment-Dmh6IWLZ.mjs +0 -1
- package/dist/packem_shared/idempotencyKey-BjxjMMna.mjs +0 -1
package/README.md
CHANGED
|
@@ -98,6 +98,7 @@ Webhook signature verification needs the raw request body, so the endpoint runs
|
|
|
98
98
|
|
|
99
99
|
```ts
|
|
100
100
|
// lunora/http.ts
|
|
101
|
+
import { webhookResponse } from "@lunora/payment";
|
|
101
102
|
import { httpAction, httpRouter } from "lunorash/server";
|
|
102
103
|
import { processWebhook } from "./billing";
|
|
103
104
|
|
|
@@ -109,12 +110,12 @@ app.post(
|
|
|
109
110
|
const body = await request.text();
|
|
110
111
|
const signature = request.headers.get("stripe-signature") ?? "";
|
|
111
112
|
|
|
112
|
-
return
|
|
113
|
+
return webhookResponse(await ctx.runAction(processWebhook, { body, signature }));
|
|
113
114
|
}),
|
|
114
115
|
);
|
|
115
116
|
```
|
|
116
117
|
|
|
117
|
-
`handleWebhook`
|
|
118
|
+
Once the signature verifies, `handleWebhook` acknowledges with `200` — a duplicate or no-op event is not re-applied — with one deliberate exception: an **orphaned** event, one patching a row whose create event has not arrived yet, answers `500` so the provider retries it (bounded to a single retry). That is why the route must use `webhookResponse`: `Response.json(result)` would answer `200` for every outcome, and an out-of-order delivery would be lost for good.
|
|
118
119
|
|
|
119
120
|
> This README covers the basics. For the full API — entitlements, usage metering, reconciliation, and the stored tables — see the [documentation](https://lunora.sh/docs/packages/payment).
|
|
120
121
|
|
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-
|
|
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
|
|
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-mtRgt9o6.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-mtRgt9o6.mjs";
|
|
3
3
|
import { LunoraError } from '@lunora/errors';
|
|
4
4
|
import { TableDefinition } from '@lunora/server';
|
|
5
5
|
/**
|
|
@@ -29,8 +29,13 @@ interface PaymentStore {
|
|
|
29
29
|
/**
|
|
30
30
|
* Claims a provider event id for processing. Resolves `true` the first time an event is seen
|
|
31
31
|
* and `false` for a duplicate — the inbound-idempotency primitive.
|
|
32
|
+
*
|
|
33
|
+
* `type` is recorded on the claim row so the `events` table is a readable audit log rather than
|
|
34
|
+
* bare ids and timestamps: a real delivery passes its `WebhookActionType`, and the
|
|
35
|
+
* internal claim markers (`sync.ts`'s orphan-retry bound, the local-refund ledger) pass a
|
|
36
|
+
* `marker.*` label so they are distinguishable from provider traffic in the studio.
|
|
32
37
|
*/
|
|
33
|
-
markEventProcessed: (provider: ProviderId, eventId: string) => Promise<boolean>;
|
|
38
|
+
markEventProcessed: (provider: ProviderId, eventId: string, type: string) => Promise<boolean>;
|
|
34
39
|
/** Flag a recorded usage event as forwarded to the provider's metering API. */
|
|
35
40
|
markUsageReported: (provider: ProviderId, idempotencyKey: string) => Promise<void>;
|
|
36
41
|
/**
|
|
@@ -85,7 +90,7 @@ declare class MemoryPaymentStore implements PaymentStore {
|
|
|
85
90
|
getSubscription(provider: ProviderId, id: string): Promise<Subscription | undefined>;
|
|
86
91
|
listSubscriptionsByReference(referenceId: string): Promise<Subscription[]>;
|
|
87
92
|
listUnreportedUsage(provider: ProviderId, limit: number): Promise<UsageEvent[]>;
|
|
88
|
-
markEventProcessed(provider: ProviderId, eventId: string): Promise<boolean>;
|
|
93
|
+
markEventProcessed(provider: ProviderId, eventId: string, _type: string): Promise<boolean>;
|
|
89
94
|
releaseEvent(provider: ProviderId, eventId: string): Promise<void>;
|
|
90
95
|
markUsageReported(provider: ProviderId, idempotencyKey: string): Promise<void>;
|
|
91
96
|
recordUsage(event: UsageEvent): Promise<boolean>;
|
|
@@ -208,6 +213,30 @@ type PaymentEvent = {
|
|
|
208
213
|
* @experimental
|
|
209
214
|
*/
|
|
210
215
|
type PaymentObserver = (event: PaymentEvent) => void;
|
|
216
|
+
/**
|
|
217
|
+
* What a `processWebhook`-shaped internal action hands back to the HTTP route: the
|
|
218
|
+
* outcome plus the HTTP status the provider must actually see.
|
|
219
|
+
* @experimental
|
|
220
|
+
*/
|
|
221
|
+
interface WebhookOutcome {
|
|
222
|
+
/** Whether the event advanced a row. A verified no-op/duplicate is `false`. */
|
|
223
|
+
applied: boolean;
|
|
224
|
+
/** The status {@link LunoraPayment.handleWebhook} answered — 500 for an orphaned event. */
|
|
225
|
+
status: number;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Turn a {@link WebhookOutcome} back into the HTTP response the provider must see.
|
|
229
|
+
*
|
|
230
|
+
* The webhook endpoint runs at the Worker edge (signature verification needs the raw
|
|
231
|
+
* body) and forwards into the shard via `ctx.runAction`, so `handleWebhook`'s own
|
|
232
|
+
* `Response` cannot cross the action boundary — only its JSON payload can. A route
|
|
233
|
+
* that answers `Response.json(result)` therefore collapses every outcome to `200`,
|
|
234
|
+
* including the deliberate `500` on an orphaned (out-of-order) event: the provider
|
|
235
|
+
* stops retrying and that event is lost for good. Call this from the route instead
|
|
236
|
+
* of building the response by hand.
|
|
237
|
+
* @experimental
|
|
238
|
+
*/
|
|
239
|
+
declare const webhookResponse: (result: WebhookOutcome) => Response;
|
|
211
240
|
/**
|
|
212
241
|
* Returns whether the current caller may act on `referenceId`. Throwing is also treated as denial.
|
|
213
242
|
* @experimental
|
|
@@ -612,4 +641,4 @@ interface VerifyCreemSignatureInput {
|
|
|
612
641
|
* replay-window check. Throws a {@link LunoraPaymentError} on any failure.
|
|
613
642
|
*/
|
|
614
643
|
declare const verifyCreemSignature: (input: VerifyCreemSignatureInput) => Promise<void>;
|
|
615
|
-
export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentPage, type PaymentPageArgs, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, zeroMoney };
|
|
644
|
+
export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentPage, type PaymentPageArgs, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, type WebhookOutcome, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, webhookResponse, zeroMoney };
|
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-
|
|
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
|
|
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-mtRgt9o6.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-mtRgt9o6.js";
|
|
3
3
|
import { LunoraError } from '@lunora/errors';
|
|
4
4
|
import { TableDefinition } from '@lunora/server';
|
|
5
5
|
/**
|
|
@@ -29,8 +29,13 @@ interface PaymentStore {
|
|
|
29
29
|
/**
|
|
30
30
|
* Claims a provider event id for processing. Resolves `true` the first time an event is seen
|
|
31
31
|
* and `false` for a duplicate — the inbound-idempotency primitive.
|
|
32
|
+
*
|
|
33
|
+
* `type` is recorded on the claim row so the `events` table is a readable audit log rather than
|
|
34
|
+
* bare ids and timestamps: a real delivery passes its `WebhookActionType`, and the
|
|
35
|
+
* internal claim markers (`sync.ts`'s orphan-retry bound, the local-refund ledger) pass a
|
|
36
|
+
* `marker.*` label so they are distinguishable from provider traffic in the studio.
|
|
32
37
|
*/
|
|
33
|
-
markEventProcessed: (provider: ProviderId, eventId: string) => Promise<boolean>;
|
|
38
|
+
markEventProcessed: (provider: ProviderId, eventId: string, type: string) => Promise<boolean>;
|
|
34
39
|
/** Flag a recorded usage event as forwarded to the provider's metering API. */
|
|
35
40
|
markUsageReported: (provider: ProviderId, idempotencyKey: string) => Promise<void>;
|
|
36
41
|
/**
|
|
@@ -85,7 +90,7 @@ declare class MemoryPaymentStore implements PaymentStore {
|
|
|
85
90
|
getSubscription(provider: ProviderId, id: string): Promise<Subscription | undefined>;
|
|
86
91
|
listSubscriptionsByReference(referenceId: string): Promise<Subscription[]>;
|
|
87
92
|
listUnreportedUsage(provider: ProviderId, limit: number): Promise<UsageEvent[]>;
|
|
88
|
-
markEventProcessed(provider: ProviderId, eventId: string): Promise<boolean>;
|
|
93
|
+
markEventProcessed(provider: ProviderId, eventId: string, _type: string): Promise<boolean>;
|
|
89
94
|
releaseEvent(provider: ProviderId, eventId: string): Promise<void>;
|
|
90
95
|
markUsageReported(provider: ProviderId, idempotencyKey: string): Promise<void>;
|
|
91
96
|
recordUsage(event: UsageEvent): Promise<boolean>;
|
|
@@ -208,6 +213,30 @@ type PaymentEvent = {
|
|
|
208
213
|
* @experimental
|
|
209
214
|
*/
|
|
210
215
|
type PaymentObserver = (event: PaymentEvent) => void;
|
|
216
|
+
/**
|
|
217
|
+
* What a `processWebhook`-shaped internal action hands back to the HTTP route: the
|
|
218
|
+
* outcome plus the HTTP status the provider must actually see.
|
|
219
|
+
* @experimental
|
|
220
|
+
*/
|
|
221
|
+
interface WebhookOutcome {
|
|
222
|
+
/** Whether the event advanced a row. A verified no-op/duplicate is `false`. */
|
|
223
|
+
applied: boolean;
|
|
224
|
+
/** The status {@link LunoraPayment.handleWebhook} answered — 500 for an orphaned event. */
|
|
225
|
+
status: number;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Turn a {@link WebhookOutcome} back into the HTTP response the provider must see.
|
|
229
|
+
*
|
|
230
|
+
* The webhook endpoint runs at the Worker edge (signature verification needs the raw
|
|
231
|
+
* body) and forwards into the shard via `ctx.runAction`, so `handleWebhook`'s own
|
|
232
|
+
* `Response` cannot cross the action boundary — only its JSON payload can. A route
|
|
233
|
+
* that answers `Response.json(result)` therefore collapses every outcome to `200`,
|
|
234
|
+
* including the deliberate `500` on an orphaned (out-of-order) event: the provider
|
|
235
|
+
* stops retrying and that event is lost for good. Call this from the route instead
|
|
236
|
+
* of building the response by hand.
|
|
237
|
+
* @experimental
|
|
238
|
+
*/
|
|
239
|
+
declare const webhookResponse: (result: WebhookOutcome) => Response;
|
|
211
240
|
/**
|
|
212
241
|
* Returns whether the current caller may act on `referenceId`. Throwing is also treated as denial.
|
|
213
242
|
* @experimental
|
|
@@ -612,4 +641,4 @@ interface VerifyCreemSignatureInput {
|
|
|
612
641
|
* replay-window check. Throws a {@link LunoraPaymentError} on any failure.
|
|
613
642
|
*/
|
|
614
643
|
declare const verifyCreemSignature: (input: VerifyCreemSignatureInput) => Promise<void>;
|
|
615
|
-
export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentPage, type PaymentPageArgs, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, zeroMoney };
|
|
644
|
+
export { type ApplyResult, type AttachInput, type AuthorizeReference, type CancelSubscriptionOptions, type CaptureInput, type CheckInput, type CheckResult, type CheckoutInput, type CheckoutResult, type CreatePaymentOptions, type CurrencyCode, type Customer, type Entitlements, type EntitlementsConfig, type FeatureBalance, type LunoraDatabaseLike, type LunoraPayment, LunoraPaymentError, MemoryPaymentStore, type Money, type MoneyJSON, PAYMENT_TERMINAL_STATES, type PaymentAction, type PaymentAdapter, type PaymentContextLike, type PaymentDatabase, type PaymentErrorCode, type PaymentEvent, type PaymentObserver, type PaymentPage, type PaymentPageArgs, type PaymentRow, type PaymentSession, type PaymentState, type PaymentStore, type PaymentsFromContextOptions, type PlanDefinition, type ProviderId, type ReconcileInput, type ReconcileResult, type RefundInput, SUBSCRIPTION_TERMINAL_STATES, type Subscription, type SubscriptionAction, type SubscriptionState, type TrackInput, type TrackResult, type UsageEvent, type VerifyCreemSignatureInput, type VerifyStandardWebhookInput, type WebhookAction, type WebhookActionType, type WebhookOutcome, addMoney, allocateMoney, applyWebhookAction, canTransitionPayment, canTransitionSubscription, compareMoney, constantTimeEqual, createDatabasePaymentStore, createPayment, entitlementsForReference, featureNames, formatMoney, fromMoneyJSON, hasActivePrice, hmacSha256Hex, idempotencyKey, isZeroDecimalCurrency, isZeroMoney, lunoraDatabaseToPaymentDatabase, money, nextPaymentState, nextSubscriptionState, paymentTables, paymentsFromContext, reconcile, resolveEntitlements, subtractMoney, toMoneyJSON, usagePeriodStart, verifyCreemSignature, verifyStandardWebhook, webhookResponse, zeroMoney };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-DyCsry1Q.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-
|
|
1
|
+
import{createAdapterRegistry as r}from"./packem_shared/createAdapterRegistry-DyCsry1Q.mjs";import{lunoraDatabaseToPaymentDatabase as a,paymentsFromContext as n}from"./packem_shared/lunoraDatabaseToPaymentDatabase-DcpOgifo.mjs";import{createPayment as y,webhookResponse as i}from"./packem_shared/createPayment-CS5Pd2yI.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-tA4tF4im.mjs";import{MemoryPaymentStore as K}from"./packem_shared/MemoryPaymentStore-CZbrrEb1.mjs";import{default as Y}from"./packem_shared/applyWebhookAction-DMAgu_yP.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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a=(i,e)=>`${i}:${e}`,n=(i,e)=>`${i}:${e}`,u=i=>{const e=i.toSorted((t,r)=>t.createdAt-r.createdAt||t.idempotencyKey.localeCompare(r.idempotencyKey));let s=0;for(const t of e)s=t.mode==="set"?t.quantity:s+t.quantity;return s};class d{customers=new Map;processedEvents=new Set;sessions=new Map;subscriptions=new Map;usageEvents=new Map;getCustomerByReference(e,s){return Promise.resolve(this.customers.get(a(e,s)))}getPaymentSession(e,s){return Promise.resolve(this.sessions.get(n(e,s)))}getSubscription(e,s){return Promise.resolve(this.subscriptions.get(n(e,s)))}listSubscriptionsByReference(e){return Promise.resolve([...this.subscriptions.values()].filter(s=>s.referenceId===e))}listUnreportedUsage(e,s){const t=[...this.usageEvents.values()].filter(r=>r.provider===e&&!r.reportedToProvider&&r.mode!=="set"&&r.quantity>0).toSorted((r,o)=>r.createdAt-o.createdAt||r.idempotencyKey.localeCompare(o.idempotencyKey));return Promise.resolve(t.slice(0,Math.max(0,s)))}markEventProcessed(e,s,t){const r=n(e,s);return this.processedEvents.has(r)?Promise.resolve(!1):(this.processedEvents.add(r),Promise.resolve(!0))}releaseEvent(e,s){return this.processedEvents.delete(n(e,s)),Promise.resolve()}markUsageReported(e,s){const t=n(e,s),r=this.usageEvents.get(t);return r&&this.usageEvents.set(t,{...r,reportedToProvider:!0}),Promise.resolve()}recordUsage(e){const s=n(e.provider,e.idempotencyKey);return this.usageEvents.has(s)?Promise.resolve(!1):(this.usageEvents.set(s,e),Promise.resolve(!0))}sumUsage(e,s,t){const r=[];for(const o of this.usageEvents.values())o.referenceId===e&&o.featureId===s&&o.createdAt>=t&&r.push(o);return Promise.resolve(u(r))}sumUsageByFeature(e,s,t){const r=new Map(s.map(o=>[o,[]]));for(const o of this.usageEvents.values())o.referenceId===e&&o.createdAt>=t&&r.get(o.featureId)?.push(o);return Promise.resolve(new Map([...r].map(([o,c])=>[o,u(c)])))}upsertCustomer(e){return this.customers.set(a(e.provider,e.referenceId),e),Promise.resolve()}upsertPaymentSession(e){return this.sessions.set(n(e.provider,e.id),e),Promise.resolve()}upsertSubscription(e){return this.subscriptions.set(n(e.provider,e.id),e),Promise.resolve()}}export{d as MemoryPaymentStore,u as foldUsage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const c={authorized:{cancel:"canceled",capture:"captured",fail:"failed"},canceled:{},captured:{partial_refund:"partially_refunded",refund:"refunded"},failed:{},initiated:{authorize:"authorized",cancel:"canceled",capture:"captured",fail:"failed"},partially_refunded:{partial_refund:"partially_refunded",refund:"refunded"},refunded:{refund:"refunded"}},n={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"}},i=new Set(["canceled","failed","refunded"]),r=new Set(["canceled"]),t=(e,a)=>c[e][a],u=(e,a)=>t(e,a)!==void 0,d=(e,a)=>n[e][a],l=(e,a)=>d(e,a)!==void 0;export{i as PAYMENT_TERMINAL_STATES,r as SUBSCRIPTION_TERMINAL_STATES,u as canTransitionPayment,l as canTransitionSubscription,t as nextPaymentState,d as nextSubscriptionState};
|
|
@@ -287,6 +287,30 @@ interface RefundInput {
|
|
|
287
287
|
readonly reason?: string;
|
|
288
288
|
readonly sessionId: string;
|
|
289
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* What an adapter's `refundPayment` returns: the provider-shaped session, plus the provider's own id
|
|
292
|
+
* for the refund it just issued.
|
|
293
|
+
*
|
|
294
|
+
* `RefundResult` is part of the experimental `@lunora/payment` API and may change without a major version bump.
|
|
295
|
+
* @experimental
|
|
296
|
+
*/
|
|
297
|
+
interface RefundResult extends PaymentSession {
|
|
298
|
+
/**
|
|
299
|
+
* The provider accepted the refund but has NOT moved the money yet — Dodo answers
|
|
300
|
+
* `refunds.create` with `pending`/`review` and settles later via `refund.succeeded`, or never,
|
|
301
|
+
* via `refund.failed`. The facade leaves its ledger untouched for one of these and lets the
|
|
302
|
+
* confirming webhook carry the money, because a `refund.failed` reverses nothing. Absent (the
|
|
303
|
+
* default) means the refund is already settled — Stripe and Polar refund synchronously.
|
|
304
|
+
*/
|
|
305
|
+
readonly pending?: boolean;
|
|
306
|
+
/**
|
|
307
|
+
* The provider's id for THIS refund — Stripe and Polar `Refund.id`, Dodo `refund_id`. It is the
|
|
308
|
+
* identity the facade keys its local refund marker on, so two in-flight refunds of the same
|
|
309
|
+
* amount on one session stay distinct. `undefined` only where a provider reports no id, which
|
|
310
|
+
* falls the marker back to the (colliding) amount.
|
|
311
|
+
*/
|
|
312
|
+
readonly refundId?: string;
|
|
313
|
+
}
|
|
290
314
|
/**
|
|
291
315
|
* `CancelSubscriptionOptions` is part of the experimental `@lunora/payment` API and may change without a major version bump.
|
|
292
316
|
* @experimental
|
|
@@ -345,6 +369,12 @@ interface WebhookAction {
|
|
|
345
369
|
/** Raw provider event, retained for the events log / debugging. */
|
|
346
370
|
readonly raw?: unknown;
|
|
347
371
|
readonly referenceId?: string;
|
|
372
|
+
/**
|
|
373
|
+
* Provider id of the refund this event reports (refund actions only). Carries the per-refund
|
|
374
|
+
* identity the sync layer matches against the marker `refundPayment` left behind, so a second
|
|
375
|
+
* facade refund of the same amount is not mistaken for the first one's confirmation.
|
|
376
|
+
*/
|
|
377
|
+
readonly refundId?: string;
|
|
348
378
|
readonly sessionId?: string;
|
|
349
379
|
readonly subscriptionId?: string;
|
|
350
380
|
readonly type: WebhookActionType;
|
|
@@ -416,7 +446,8 @@ interface PaymentAdapter {
|
|
|
416
446
|
readonly identifier: ProviderId;
|
|
417
447
|
/** Verify the signature over the raw body, then normalize the event. Throws on invalid signature. */
|
|
418
448
|
parseWebhook: (input: WebhookInput) => Promise<WebhookAction>;
|
|
419
|
-
|
|
449
|
+
/** Issue the refund and report the provider's id for it (see {@link RefundResult.refundId}). */
|
|
450
|
+
refundPayment: (input: RefundInput) => Promise<RefundResult>;
|
|
420
451
|
/**
|
|
421
452
|
* Forward metered usage to the provider's billing API. Optional — present only on providers
|
|
422
453
|
* whose `capabilities.usageMetering` is `true` and that expose an ingestion endpoint. When
|
|
@@ -440,4 +471,4 @@ interface AdapterRegistry {
|
|
|
440
471
|
* @experimental
|
|
441
472
|
*/
|
|
442
473
|
declare const createAdapterRegistry: (adapters: ReadonlyArray<PaymentAdapter>) => AdapterRegistry;
|
|
443
|
-
export { ApplyResult as A, Customer as C, FeatureBalance as F, Money as M, PaymentAdapter as P, RefundInput as R, Subscription as S, TrackInput as T, UsageEvent as U, WebhookActionType as W, ProviderId as a, PaymentSession as b, AttachInput as c, CheckoutResult as d, CancelSubscriptionOptions as e, CaptureInput as f, CheckInput as g, CheckResult as h, CheckoutInput as i, TrackResult as j, CurrencyCode as k, PaymentState as l, SubscriptionState as m, WebhookAction as n, AdapterRegistry as o, CustomerRef as p, PortalInput as q, ProviderCapabilities as r, RefundAmountKind as s,
|
|
474
|
+
export { ApplyResult as A, Customer as C, FeatureBalance as F, Money as M, PaymentAdapter as P, RefundInput as R, Subscription as S, TrackInput as T, UsageEvent as U, WebhookActionType as W, ProviderId as a, PaymentSession as b, AttachInput as c, CheckoutResult as d, CancelSubscriptionOptions as e, CaptureInput as f, CheckInput as g, CheckResult as h, CheckoutInput as i, TrackResult as j, CurrencyCode as k, PaymentState as l, SubscriptionState as m, WebhookAction as n, AdapterRegistry as o, CustomerRef as p, PortalInput as q, ProviderCapabilities as r, RefundAmountKind as s, RefundResult as t, ReportUsageInput as u, SubscriptionPatch as v, WebhookHeaders as w, WebhookInput as x, createAdapterRegistry as y };
|
|
@@ -287,6 +287,30 @@ interface RefundInput {
|
|
|
287
287
|
readonly reason?: string;
|
|
288
288
|
readonly sessionId: string;
|
|
289
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* What an adapter's `refundPayment` returns: the provider-shaped session, plus the provider's own id
|
|
292
|
+
* for the refund it just issued.
|
|
293
|
+
*
|
|
294
|
+
* `RefundResult` is part of the experimental `@lunora/payment` API and may change without a major version bump.
|
|
295
|
+
* @experimental
|
|
296
|
+
*/
|
|
297
|
+
interface RefundResult extends PaymentSession {
|
|
298
|
+
/**
|
|
299
|
+
* The provider accepted the refund but has NOT moved the money yet — Dodo answers
|
|
300
|
+
* `refunds.create` with `pending`/`review` and settles later via `refund.succeeded`, or never,
|
|
301
|
+
* via `refund.failed`. The facade leaves its ledger untouched for one of these and lets the
|
|
302
|
+
* confirming webhook carry the money, because a `refund.failed` reverses nothing. Absent (the
|
|
303
|
+
* default) means the refund is already settled — Stripe and Polar refund synchronously.
|
|
304
|
+
*/
|
|
305
|
+
readonly pending?: boolean;
|
|
306
|
+
/**
|
|
307
|
+
* The provider's id for THIS refund — Stripe and Polar `Refund.id`, Dodo `refund_id`. It is the
|
|
308
|
+
* identity the facade keys its local refund marker on, so two in-flight refunds of the same
|
|
309
|
+
* amount on one session stay distinct. `undefined` only where a provider reports no id, which
|
|
310
|
+
* falls the marker back to the (colliding) amount.
|
|
311
|
+
*/
|
|
312
|
+
readonly refundId?: string;
|
|
313
|
+
}
|
|
290
314
|
/**
|
|
291
315
|
* `CancelSubscriptionOptions` is part of the experimental `@lunora/payment` API and may change without a major version bump.
|
|
292
316
|
* @experimental
|
|
@@ -345,6 +369,12 @@ interface WebhookAction {
|
|
|
345
369
|
/** Raw provider event, retained for the events log / debugging. */
|
|
346
370
|
readonly raw?: unknown;
|
|
347
371
|
readonly referenceId?: string;
|
|
372
|
+
/**
|
|
373
|
+
* Provider id of the refund this event reports (refund actions only). Carries the per-refund
|
|
374
|
+
* identity the sync layer matches against the marker `refundPayment` left behind, so a second
|
|
375
|
+
* facade refund of the same amount is not mistaken for the first one's confirmation.
|
|
376
|
+
*/
|
|
377
|
+
readonly refundId?: string;
|
|
348
378
|
readonly sessionId?: string;
|
|
349
379
|
readonly subscriptionId?: string;
|
|
350
380
|
readonly type: WebhookActionType;
|
|
@@ -416,7 +446,8 @@ interface PaymentAdapter {
|
|
|
416
446
|
readonly identifier: ProviderId;
|
|
417
447
|
/** Verify the signature over the raw body, then normalize the event. Throws on invalid signature. */
|
|
418
448
|
parseWebhook: (input: WebhookInput) => Promise<WebhookAction>;
|
|
419
|
-
|
|
449
|
+
/** Issue the refund and report the provider's id for it (see {@link RefundResult.refundId}). */
|
|
450
|
+
refundPayment: (input: RefundInput) => Promise<RefundResult>;
|
|
420
451
|
/**
|
|
421
452
|
* Forward metered usage to the provider's billing API. Optional — present only on providers
|
|
422
453
|
* whose `capabilities.usageMetering` is `true` and that expose an ingestion endpoint. When
|
|
@@ -440,4 +471,4 @@ interface AdapterRegistry {
|
|
|
440
471
|
* @experimental
|
|
441
472
|
*/
|
|
442
473
|
declare const createAdapterRegistry: (adapters: ReadonlyArray<PaymentAdapter>) => AdapterRegistry;
|
|
443
|
-
export { ApplyResult as A, Customer as C, FeatureBalance as F, Money as M, PaymentAdapter as P, RefundInput as R, Subscription as S, TrackInput as T, UsageEvent as U, WebhookActionType as W, ProviderId as a, PaymentSession as b, AttachInput as c, CheckoutResult as d, CancelSubscriptionOptions as e, CaptureInput as f, CheckInput as g, CheckResult as h, CheckoutInput as i, TrackResult as j, CurrencyCode as k, PaymentState as l, SubscriptionState as m, WebhookAction as n, AdapterRegistry as o, CustomerRef as p, PortalInput as q, ProviderCapabilities as r, RefundAmountKind as s,
|
|
474
|
+
export { ApplyResult as A, Customer as C, FeatureBalance as F, Money as M, PaymentAdapter as P, RefundInput as R, Subscription as S, TrackInput as T, UsageEvent as U, WebhookActionType as W, ProviderId as a, PaymentSession as b, AttachInput as c, CheckoutResult as d, CancelSubscriptionOptions as e, CaptureInput as f, CheckInput as g, CheckResult as h, CheckoutInput as i, TrackResult as j, CurrencyCode as k, PaymentState as l, SubscriptionState as m, WebhookAction as n, AdapterRegistry as o, CustomerRef as p, PortalInput as q, ProviderCapabilities as r, RefundAmountKind as s, RefundResult as t, ReportUsageInput as u, SubscriptionPatch as v, WebhookHeaders as w, WebhookInput as x, createAdapterRegistry as y };
|
|
@@ -0,0 +1 @@
|
|
|
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-tA4tF4im.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 +1 @@
|
|
|
1
|
-
import{money as y}from"./addMoney-B4ufWAnD.mjs";import{foldUsage as m}from"./MemoryPaymentStore-
|
|
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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{toErrorBody as R}from"@lunora/errors";import{usagePeriodStart as b,resolveEntitlements as S,featureNames as O,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 A}from"./idempotencyKey-DBL6bpmj.mjs";import{subtractMoney as K,addMoney as B,compareMoney as k,isZeroMoney as C}from"./addMoney-B4ufWAnD.mjs";import{n as E}from"./observability-BteZ2dJS.mjs";import F from"./applyWebhookAction-DMAgu_yP.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 T=()=>{const o=Date.now();return h=o>h?o:h+1,h},P=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),W=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}},N=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)}},I=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},p=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 I(e),a=t?.idempotencyKey??A("cancel_payment",n.identifier,e),i=await n.cancelPayment(e,{...t,idempotencyKey:a});return p(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??A("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 I(e.sessionId),r=e.idempotencyKey??await v("capture_payment",n.identifier,e.sessionId,P(e.amount)),a=await n.capturePayment({...e,idempotencyKey:r});return p(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("CONFIG_INVALID","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("CONFIG_INVALID","check() requires a featureId or priceId");if(!o.entitlements)throw new f("CONFIG_INVALID","check() requires `entitlements` to be configured");const r=S(o.entitlements,t);return N(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}=R(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 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=S(o.entitlements,t),a=O(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 I(e.sessionId),r=e.amount??K(t.capturedAmount,t.refundedAmount),a=B(t.refundedAmount,r);if(k(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(C(r))return t;const i=e.amount??(k(r,t.capturedAmount)===0?void 0:r),d=e.idempotencyKey??await v("refund_payment",n.identifier,e.sessionId,P(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 p(t,{refundedAmount:a,state:k(a,t.capturedAmount)<0?"partially_refunded":"refunded"})}catch(g){throw await s.releaseEvent(n.identifier,u),g}},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:T(),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 g=await s.getCustomerByReference(n.identifier,e.referenceId);return await n.reportUsage({customerId:g?.id,featureId:e.featureId,idempotencyKey:r,quantity:c,referenceId:e.referenceId}),await s.markUsageReported(n.identifier,r),{recorded:!0,reportedToProvider:!0}}catch{return E(o.observability,{featureId:e.featureId,provider:n.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{W as createPayment,G as webhookResponse};
|
|
@@ -0,0 +1 @@
|
|
|
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 +1 @@
|
|
|
1
|
-
import{createPayment as i}from"./createPayment-
|
|
1
|
+
import{createPayment as i}from"./createPayment-CS5Pd2yI.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,4 +1,4 @@
|
|
|
1
|
-
import { P as PaymentAdapter } from "../packem_shared/adapter.d-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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`
|
package/dist/providers/creem.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as o,c as u,f as m,r as c,b as p,p as f,d as v,e as w}from"../packem_shared/json-BJJPJVYj.mjs";import{money as I,zeroMoney as g}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyCreemSignature as A}from"../packem_shared/constantTimeEqual-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:
|
|
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.transaction)??i(r.subscription)??i(r.order)??i(r.checkout)??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,4 +1,4 @@
|
|
|
1
|
-
import { P as PaymentAdapter } from "../packem_shared/adapter.d-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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
|
|
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 o,r as a,f as u,p as l,d as w}from"../packem_shared/json-BJJPJVYj.mjs";import{money as _,zeroMoney as f}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as 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)"),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:w(n,"cancel_at_next_billing_date")??!1,createdAt:t,currentPeriodEnd:l(a(n,"next_billing_date")),currentPeriodStart:l(a(n,"previous_billing_date")),id:a(n,"subscription_id")??"",priceId:a(n,"product_id")??"",provider:"dodopayments",quantity:o(n,"quantity")??1,referenceId:u(n)??m(n)??"",state:g[e]??"past_due",updatedAt:t}},k=d=>{const n=i(d),t=Date.now(),e=a(n,"currency")??"usd",r=_(BigInt(Math.round(o(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:u(n)??"",refundedAmount:f(e),state:s,updatedAt:t}},D=(d,n,t)=>{const e={eventId:d,provider:"dodopayments",raw:{object:t,type:n}},r=a(t,"currency")??"usd";switch(n){case"dispute.lost":case"refund.succeeded":return{...e,amount:_(BigInt(Math.round(o(t,"amount")??0)),r),referenceId:u(t),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:u(t),sessionId:a(t,"payment_id"),type:"payment.failed"};case"payment.succeeded":return{...e,amount:_(BigInt(Math.round(o(t,"total_amount")??0)),r),customerId:m(t),referenceId:u(t),sessionId:a(t,"payment_id"),subscriptionId:a(t,"subscription_id"),type:"payment.captured"};case"subscription.active":case"subscription.cancelled":case"subscription.expired":case"subscription.failed":case"subscription.on_hold":case"subscription.paused":case"subscription.plan_changed":case"subscription.renewed":case"subscription.updated":{const s=a(t,"status");return{...e,cancelAtPeriodEnd:w(t,"cancel_at_next_billing_date"),currentPeriodEnd:l(a(t,"next_billing_date")),currentPeriodStart:l(a(t,"previous_billing_date")),customerId:m(t),priceId:a(t,"product_id"),quantity:o(t,"quantity"),referenceId:u(t)??m(t),subscriptionId:a(t,"subscription_id"),type:P(g[s??""]??"past_due")}}default:return{...e,type:"unhandled"}}},U=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=>k(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 c=i(JSON.parse(r));return D(s,a(c,"type")??"",i(c.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",c=_(BigInt(Math.round(o(r,"amount")??0)),s),y=a(r,"status")==="succeeded",v=y?"refunded":"captured";return{amount:c,capturedAmount:c,createdAt:Date.now(),id:e.sessionId,pending:!y,provider:"dodopayments",referenceId:"",refundedAmount:c,refundId:a(r,"refund_id"),state:v,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??o(s,"quantity")??1})}return p(await t.subscriptions.retrieve(e))}}};export{U as createDodoPaymentsAdapter};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as PaymentAdapter } from "../packem_shared/adapter.d-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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
|
package/dist/providers/polar.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as p,r as n,f as c,d as w,p as f,b as i}from"../packem_shared/json-BJJPJVYj.mjs";import{money as m,zeroMoney as I}from"../packem_shared/addMoney-B4ufWAnD.mjs";import{verifyStandardWebhook as
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { P as PaymentAdapter } from "../packem_shared/adapter.d-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as PaymentAdapter } from "../packem_shared/adapter.d-
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-mtRgt9o6.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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraPaymentError as E}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as K}from"../packem_shared/idempotencyKey-
|
|
1
|
+
import{LunoraPaymentError as E}from"../packem_shared/LunoraPaymentError-BSxyhWgu.mjs";import{idempotencyKey as K}from"../packem_shared/idempotencyKey-DBL6bpmj.mjs";import{a as m,r as a,d as v,b as o}from"../packem_shared/json-BJJPJVYj.mjs";import{compareMoney as P,money as y,zeroMoney as f}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"},S={active:"active",canceled:"canceled",incomplete:"past_due",incomplete_expired:"canceled",past_due:"past_due",paused:"paused",trialing:"trialing",unpaid:"past_due"},c=s=>a(m(s.metadata),"referenceId")??a(s,"client_reference_id"),I=s=>{const n=m(s.items),t=Array.isArray(n.data)?n.data:[];return m(t[0])},w=s=>a(m(I(s).price),"id"),g=s=>o(I(s),"quantity"),h=s=>{const n=o(I(s),"current_period_end")??o(s,"current_period_end");return n===void 0?void 0:n*1e3},A=s=>{const n=o(I(s),"current_period_start")??o(s,"current_period_start");return n===void 0?void 0:n*1e3},_=s=>{const n=m(s),t=a(n,"currency")??"usd",e=o(n,"amount")??0,r=y(BigInt(Math.round(e)),t),i=M[a(n,"status")??""]??"initiated",d=Date.now();return{amount:r,capturedAmount:i==="captured"?y(BigInt(Math.round(o(n,"amount_received")??e)),t):f(t),createdAt:d,id:a(n,"id")??"",provider:"stripe",referenceId:c(n)??"",refundedAmount:f(t),state:i,updatedAt:d}},l=s=>{const n=m(s),t=Date.now();return{cancelAtPeriodEnd:v(n,"cancel_at_period_end")??!1,createdAt:t,currentPeriodEnd:h(n),currentPeriodStart:A(n),id:a(n,"id")??"",priceId:w(n)??"",provider:"stripe",quantity:g(n)??1,referenceId:c(n)??"",state:S[a(n,"status")??""]??"past_due",updatedAt:t}},B=(s,n,t)=>{const e={eventId:s,provider:"stripe",raw:{object:t,type:n}},r=a(t,"currency")??"usd";switch(n){case"charge.refunded":return{...e,amount:y(BigInt(Math.round(o(t,"amount_refunded")??0)),r),amountKind:"absolute",referenceId:c(t),sessionId:a(t,"payment_intent")??a(t,"id"),type:"payment.refunded"};case"checkout.session.completed":{if(a(t,"mode")==="subscription"){const u=a(t,"payment_status"),p=u==="paid"||u==="no_payment_required";return{...e,customerId:a(t,"customer"),referenceId:c(t),subscriptionId:a(t,"subscription"),type:p?"subscription.active":"subscription.updated"}}const i=a(t,"payment_intent");if(i===void 0&&a(t,"payment_status")!=="no_payment_required")return{...e,type:"unhandled"};const d=o(t,"amount_total");return{...e,amount:d===void 0?void 0:y(BigInt(Math.round(d)),r),customerId:a(t,"customer"),referenceId:c(t),sessionId:i??a(t,"id"),type:"payment.captured"}}case"customer.subscription.created":case"customer.subscription.updated":return{...e,cancelAtPeriodEnd:v(t,"cancel_at_period_end"),currentPeriodEnd:h(t),currentPeriodStart:A(t),customerId:a(t,"customer"),priceId:w(t),quantity:g(t),referenceId:c(t),subscriptionId:a(t,"id"),type:q(S[a(t,"status")??""]??"past_due")};case"customer.subscription.deleted":return{...e,referenceId:c(t),subscriptionId:a(t,"id"),type:"subscription.canceled"};case"payment_intent.amount_capturable_updated":return{...e,amount:y(BigInt(Math.round(o(t,"amount")??0)),r),referenceId:c(t),sessionId:a(t,"id"),type:"payment.authorized"};case"payment_intent.payment_failed":return{...e,referenceId:c(t),sessionId:a(t,"id"),type:"payment.failed"};case"payment_intent.succeeded":return{...e,amount:y(BigInt(Math.round(o(t,"amount_received")??o(t,"amount")??0)),r),customerId:a(t,"customer"),referenceId:c(t),sessionId:a(t,"id"),type:"payment.captured"};default:return{...e,type:"unhandled"}}},C=s=>{const{webhookSecret:n}=s,t=s.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:K("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,s.webhookToleranceSeconds)}catch(p){throw new E("WEBHOOK_SIGNATURE_INVALID",p instanceof Error?p.message:"invalid webhook signature")}const u=m(m(d.data).object);return B(d.id,d.type,u)},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),u=e.amount??d.capturedAmount,p=e.amount!==void 0&&P(e.amount,d.capturedAmount)<0;return{...d,refundedAmount:u,refundId:r.id,state:p?"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=>{const r=await t.subscriptions.update(e,{cancel_at_period_end:!1});return l(r)},updateSubscription:async(e,r)=>{const i={};if(r.priceId!==void 0||r.quantity!==void 0){const u=await t.subscriptions.retrieve(e);i.items=[{id:u.items.data[0]?.id,price:r.priceId,quantity:r.quantity}]}const d=await t.subscriptions.update(e,i);return l(d)}}};export{C as createStripeAdapter};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/payment",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.103",
|
|
4
4
|
"description": "Provider-agnostic payments for Lunora: Stripe-first adapter, webhook sync, and subscription/payment state machine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"billing",
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
75
|
-
"@lunora/server": "1.0.0-alpha.
|
|
76
|
-
"@lunora/values": "1.0.0-alpha.
|
|
74
|
+
"@lunora/errors": "1.0.0-alpha.30",
|
|
75
|
+
"@lunora/server": "1.0.0-alpha.101",
|
|
76
|
+
"@lunora/values": "1.0.0-alpha.38",
|
|
77
77
|
"dinero.js": "2.0.2"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const a=(i,e)=>`${i}:${e}`,n=(i,e)=>`${i}:${e}`,u=i=>{const e=i.toSorted((t,r)=>t.createdAt-r.createdAt||t.idempotencyKey.localeCompare(r.idempotencyKey));let s=0;for(const t of e)s=t.mode==="set"?t.quantity:s+t.quantity;return s};class d{customers=new Map;processedEvents=new Set;sessions=new Map;subscriptions=new Map;usageEvents=new Map;getCustomerByReference(e,s){return Promise.resolve(this.customers.get(a(e,s)))}getPaymentSession(e,s){return Promise.resolve(this.sessions.get(n(e,s)))}getSubscription(e,s){return Promise.resolve(this.subscriptions.get(n(e,s)))}listSubscriptionsByReference(e){return Promise.resolve([...this.subscriptions.values()].filter(s=>s.referenceId===e))}listUnreportedUsage(e,s){const t=[...this.usageEvents.values()].filter(r=>r.provider===e&&!r.reportedToProvider&&r.mode!=="set"&&r.quantity>0).toSorted((r,o)=>r.createdAt-o.createdAt||r.idempotencyKey.localeCompare(o.idempotencyKey));return Promise.resolve(t.slice(0,Math.max(0,s)))}markEventProcessed(e,s){const t=n(e,s);return this.processedEvents.has(t)?Promise.resolve(!1):(this.processedEvents.add(t),Promise.resolve(!0))}releaseEvent(e,s){return this.processedEvents.delete(n(e,s)),Promise.resolve()}markUsageReported(e,s){const t=n(e,s),r=this.usageEvents.get(t);return r&&this.usageEvents.set(t,{...r,reportedToProvider:!0}),Promise.resolve()}recordUsage(e){const s=n(e.provider,e.idempotencyKey);return this.usageEvents.has(s)?Promise.resolve(!1):(this.usageEvents.set(s,e),Promise.resolve(!0))}sumUsage(e,s,t){const r=[];for(const o of this.usageEvents.values())o.referenceId===e&&o.featureId===s&&o.createdAt>=t&&r.push(o);return Promise.resolve(u(r))}sumUsageByFeature(e,s,t){const r=new Map(s.map(o=>[o,[]]));for(const o of this.usageEvents.values())o.referenceId===e&&o.createdAt>=t&&r.get(o.featureId)?.push(o);return Promise.resolve(new Map([...r].map(([o,c])=>[o,u(c)])))}upsertCustomer(e){return this.customers.set(a(e.provider,e.referenceId),e),Promise.resolve()}upsertPaymentSession(e){return this.sessions.set(n(e.provider,e.id),e),Promise.resolve()}upsertSubscription(e){return this.subscriptions.set(n(e.provider,e.id),e),Promise.resolve()}}export{d as MemoryPaymentStore,u as foldUsage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const c={authorized:{cancel:"canceled",capture:"captured",fail:"failed"},canceled:{},captured:{partial_refund:"partially_refunded",refund:"refunded"},failed:{},initiated:{authorize:"authorized",cancel:"canceled",capture:"captured",fail:"failed"},partially_refunded:{partial_refund:"partially_refunded",refund:"refunded"},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"}},i=new Set(["canceled","failed","refunded"]),r=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{i as PAYMENT_TERMINAL_STATES,r as SUBSCRIPTION_TERMINAL_STATES,u as canTransitionPayment,l as canTransitionSubscription,n as nextPaymentState,d as nextSubscriptionState};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraPaymentError as I}from"./LunoraPaymentError-BSxyhWgu.mjs";import{compareMoney as l,zeroMoney as c,addMoney as P}from"./addMoney-B4ufWAnD.mjs";import{n as o}from"./observability-BteZ2dJS.mjs";import{nextPaymentState as A,nextSubscriptionState as E}from"./PAYMENT_TERMINAL_STATES-DVerjPR6.mjs";const S={"payment.authorized":"authorize","payment.captured":"capture","payment.failed":"fail","payment.refunded":"refund"},_={"subscription.active":"active","subscription.canceled":"canceled","subscription.past_due":"past_due","subscription.paused":"paused"},b="orphan-retry:",w={"subscription.active":"activate","subscription.canceled":"cancel","subscription.past_due":"mark_past_due","subscription.paused":"pause"},h=(t,e)=>l(t,e)>0?t:e,v=(t,e)=>{if(!e.amount||t.refundedAmount.currency!==e.amount.currency||t.capturedAmount.currency!==e.amount.currency)return;const r=e.amountKind==="absolute"?h(e.amount,t.refundedAmount):P(t.refundedAmount,e.amount);if(!(l(r,t.capturedAmount)>0))return r},T=async(t,e,r)=>{if(!e.sessionId)return{applied:!1,reason:"unhandled"};const n=await t.getPaymentSession(e.provider,e.sessionId),u=n?.state??"initiated",d=Date.now(),p=e.amount?.currency??n?.amount.currency??"USD";let i=r;if(r==="refund"&&n){const a=v(n,e);a&&l(a,n.capturedAmount)<0&&(i="partial_refund")}const f=A(u,i);if(!f)return{applied:!1,reason:"illegal_transition"};const s=n??{amount:e.amount??c(p),capturedAmount:c(p),createdAt:d,id:e.sessionId,provider:e.provider,referenceId:e.referenceId??"",refundedAmount:c(p),state:u,updatedAt:d};let{capturedAmount:y,refundedAmount:m}=s;if(i==="capture"&&e.amount&&(y=e.amount),(i==="partial_refund"||i==="refund")&&e.amount){const a=v(s,e);if(!a)return{applied:!1,reason:"invalid_refund_amount"};m=a}return await t.upsertPaymentSession({...s,capturedAmount:y,referenceId:e.referenceId??s.referenceId,refundedAmount:m,state:f,updatedAt:d}),{applied:!0,reason:"ok"}},k=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 u=_[e.type];if(!u)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:u,updatedAt:n}),{applied:!0,reason:"ok"};const d=r.state==="active"&&u==="active"?"renew":w[e.type],p=d?E(r.state,d):void 0;return p?(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:p,updatedAt:n}),{applied:!0,reason:"ok"}):{applied:!1,reason:"illegal_transition"}},Y=async(t,e,r)=>{if(e.type==="unhandled")return{applied:!1,reason:"unhandled"};if(!e.eventId?.trim())throw new I("WEBHOOK_EVENT_ID_MISSING",`webhook event id is missing or blank for provider "${e.provider}"`);if(!await t.markEventProcessed(e.provider,e.eventId))return o(r,{eventId:e.eventId,provider:e.provider,type:"webhook.duplicate"}),{applied:!1,reason:"duplicate"};const u=S[e.type];let d;try{d=u?await T(t,e,u):await k(t,e)}catch(p){throw await t.releaseEvent(e.provider,e.eventId),p}return d.reason==="orphaned"&&(await t.markEventProcessed(e.provider,`${b}${e.eventId}`)?await t.releaseEvent(e.provider,e.eventId):d={applied:!1,reason:"unhandled"}),o(r,{action:e.type,eventId:e.eventId,provider:e.provider,reason:d.reason,type:"webhook.applied"}),e.type==="payment.failed"?o(r,{provider:e.provider,referenceId:e.referenceId,sessionId:e.sessionId,type:"payment.failed"}):e.type==="subscription.past_due"&&o(r,{provider:e.provider,referenceId:e.referenceId,subscriptionId:e.subscriptionId,type:"subscription.past_due"}),d};export{Y as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{toErrorBody as R}from"@lunora/errors";import{usagePeriodStart as I,resolveEntitlements as v,featureNames as A,hasActivePrice as U}from"./entitlementsForReference-Bq-9UUL8.mjs";import{LunoraPaymentError as d}from"./LunoraPaymentError-BSxyhWgu.mjs";import{derivedIdempotencyKey as g,idempotencyKey as S}from"./idempotencyKey-BjxjMMna.mjs";import{addMoney as D,compareMoney as k}from"./addMoney-B4ufWAnD.mjs";import{n as _}from"./observability-BteZ2dJS.mjs";import B from"./applyWebhookAction-daHKTG_Q.mjs";const y=(o,a=200,s)=>Response.json(o,{headers:{"content-type":"application/json",...s},status:a});let u=0;const K=()=>{const o=Date.now();return u=o>u?o:u+1,u},N=o=>o?`${o.currency}:${String(o.minorUnits)}`:"full",C=o=>o&&"referenceId"in o?Object.fromEntries(Object.entries(o).filter(([a])=>a!=="referenceId")):o,V=o=>{const{adapter:a,store:s}=o,f=async e=>{if(!o.authorize)return;let t;try{t=await o.authorize(e)}catch{throw new d("FORBIDDEN",`caller not authorized for reference "${e}"`)}if(!t)throw new d("FORBIDDEN",`caller not authorized for reference "${e}"`)},p=async e=>{await f(e.referenceId);const t=C(e.metadata);let r;const n=await s.getCustomerByReference(a.identifier,e.referenceId);if(n)r=n.id;else{const m=await a.getOrCreateCustomer({email:e.email,referenceId:e.referenceId});r=m.id,await s.upsertCustomer(m)}const i=e.idempotencyKey??await g("checkout",a.identifier,e.referenceId,e.priceId,e.mode,String(e.quantity??1),e.successUrl,e.cancelUrl,t?JSON.stringify(t):"");return a.createCheckout({...e,customerId:r,idempotencyKey:i,metadata:t})},b=(e,t,r)=>{const n=e-t;return{allowed:n>=r,balance:n,limit:e,unlimited:!1,used:t}},P=async(e,t,r,n,i)=>{const m=e.limit(n);if(m!==void 0){const c=await s.sumUsage(r,n,I(t));return b(m,c,i)}return{allowed:e.has(n),unlimited:e.has(n)}},l=async e=>{const t=()=>new d("NOT_FOUND",`payment session "${e}" not found`);let r=await s.getPaymentSession(a.identifier,e);if(!r)try{r=await a.getPaymentStatus(e)}catch{throw t()}if(!r.referenceId)throw t();try{await f(r.referenceId)}catch{throw t()}return r},w=async(e,t)=>{const r={...e,...t,updatedAt:Date.now()};return await s.upsertPaymentSession(r),r};return{adapter:a,attach:async e=>p({...e,mode:e.mode??"subscription"}),cancelPayment:async(e,t)=>{const r=await l(e),n=t?.idempotencyKey??S("cancel_payment",a.identifier,e),i=await a.cancelPayment(e,{...t,idempotencyKey:n});return w(r,{state:i.state})},cancelSubscription:async(e,t)=>{const r=await s.getSubscription(a.identifier,e);if(!r)throw new d("NOT_FOUND",`subscription "${e}" not found`);try{await f(r.referenceId)}catch{throw new d("NOT_FOUND",`subscription "${e}" not found`)}const n=t?.idempotencyKey??S("cancel_subscription",a.identifier,e),i=await a.cancelSubscription(e,{...t,idempotencyKey:n});return await s.upsertSubscription(i),i},capturePayment:async e=>{const t=await l(e.sessionId),r=e.idempotencyKey??await g("capture_payment",a.identifier,e.sessionId,N(e.amount)),n=await a.capturePayment({...e,idempotencyKey:r});return w(t,{capturedAmount:n.capturedAmount,state:n.state})},check:async e=>{if(await f(e.referenceId),e.featureId===void 0&&e.priceId===void 0)throw new d("CONFIG_INVALID","check() requires a featureId or priceId");if(a.checkEntitlement)return a.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 d("CONFIG_INVALID","check() requires a featureId or priceId");if(!o.entitlements)throw new d("CONFIG_INVALID","check() requires `entitlements` to be configured");const r=v(o.entitlements,t);return P(r,t,e.referenceId,e.featureId,e.quantity??1)},createCheckout:async e=>p(e),createPortalSession:async(e,t)=>{await f(e);const r=await s.getCustomerByReference(a.identifier,e);if(!r)throw new d("NOT_FOUND",`no customer for reference "${e}"`);return a.createPortalSession({customerId:r.id,returnUrl:t})},handleWebhook:async e=>{let t;try{const n=await e.text();t=await a.parseWebhook({headers:e.headers,payload:n})}catch(n){if(n instanceof d){const{body:i,status:m}=R(n);return y({error:i.message},m)}return y({error:"webhook error"},400)}const r=await B(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 f(e),a.getBalances)return a.getBalances(e);if(!o.entitlements)throw new d("CONFIG_INVALID","listBalances() requires `entitlements` to be configured");const t=await s.listSubscriptionsByReference(e),r=v(o.entitlements,t),n=A(o.entitlements),i=n.filter(c=>r.limit(c)!==void 0),m=i.length===0?new Map:await s.sumUsageByFeature(e,i,I(t));return n.map(c=>{const h=r.limit(c);return h!==void 0?{featureId:c,...b(h,m.get(c)??0,1)}:{featureId:c,allowed:r.has(c),unlimited:r.has(c)}})},listSubscriptions:async e=>(await f(e),s.listSubscriptionsByReference(e)),refundPayment:async e=>{const t=await l(e.sessionId),r=e.amount?D(t.refundedAmount,e.amount):t.capturedAmount;if(k(r,t.capturedAmount)>0)throw new d("VALIDATION_ERROR",`refundPayment(): refunding ${String(e.amount?.minorUnits)} would exceed the captured amount on session "${e.sessionId}"`);const n=e.idempotencyKey??await g("refund_payment",a.identifier,e.sessionId,N(e.amount),e.reason??"");return await a.refundPayment({...e,idempotencyKey:n}),w(t,{state:k(r,t.capturedAmount)<0?"partially_refunded":"refunded"})},store:s,track:async e=>{await f(e.referenceId);const t=e.quantity===void 0?1:e.quantity;if(!Number.isSafeInteger(t)||t<0)throw new d("VALIDATION_ERROR",`track(): \`quantity\` must be a non-negative safe integer (got ${String(e.quantity)})`);const r=e.idempotencyKey??crypto.randomUUID(),n=e.mode==="set",i=n?await s.listSubscriptionsByReference(e.referenceId):void 0,m=i===void 0?0:await s.sumUsage(e.referenceId,e.featureId,I(i));if(n?t===m:t===0)return{recorded:!1,reportedToProvider:!1};const c=n?t-m:t;if(!await s.recordUsage({createdAt:K(),featureId:e.featureId,idempotencyKey:r,...n?{mode:"set"}:{},provider:a.identifier,quantity:t,referenceId:e.referenceId,reportedToProvider:!1}))return{recorded:!1,reportedToProvider:!1};if(c<=0||!a.capabilities.usageMetering||!a.reportUsage)return{recorded:!0,reportedToProvider:!1};try{const O=await s.getCustomerByReference(a.identifier,e.referenceId);return await a.reportUsage({customerId:O?.id,featureId:e.featureId,idempotencyKey:r,quantity:c,referenceId:e.referenceId}),await s.markUsageReported(a.identifier,r),{recorded:!0,reportedToProvider:!0}}catch{return _(o.observability,{featureId:e.featureId,provider:a.identifier,referenceId:e.referenceId,type:"usage.report_failed"}),{recorded:!0,reportedToProvider:!1}}}}};export{V as createPayment};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const a=new TextEncoder,c=async n=>[...new Uint8Array(await crypto.subtle.digest("SHA-256",a.encode(n)))].map(t=>t.toString(16).padStart(2,"0")).join(""),i=(n,...t)=>[n,...t.map(String)].join(":"),r=async(n,t,...e)=>{const o=await c(JSON.stringify([t,...e.map(String)]));return`${n}:${t}:${o}`};export{r as derivedIdempotencyKey,i as idempotencyKey};
|