@lunora/payment 1.0.0-alpha.2 → 1.0.0-alpha.21
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/LICENSE.md +6 -0
- package/README.md +3 -2
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +20 -459
- package/dist/index.d.ts +20 -459
- package/dist/index.mjs +9 -11
- package/dist/packem_shared/{LunoraPaymentError-B3hEzXSs.mjs → LunoraPaymentError-BeQlhkZj.mjs} +4 -7
- package/dist/packem_shared/adapter.d-iFA55DzU.d.mts +312 -0
- package/dist/packem_shared/adapter.d-iFA55DzU.d.ts +312 -0
- package/dist/packem_shared/{addMoney-bCcs1nyw.mjs → addMoney-jSh_7TfF.mjs} +1 -1
- package/dist/packem_shared/{applyWebhookAction-DpAqf3Lw.mjs → applyWebhookAction-CLAx4svt.mjs} +1 -1
- package/dist/packem_shared/{constantTimeEqual-CfY0jYcL.mjs → constantTimeEqual-BQjoW85H.mjs} +14 -38
- package/dist/packem_shared/{createAdapterRegistry-BuDHFCBc.mjs → createAdapterRegistry-Ds7bx_TK.mjs} +1 -1
- package/dist/packem_shared/{createDatabasePaymentStore-bYB_HUE6.mjs → createDatabasePaymentStore-C0NUCj1H.mjs} +1 -1
- package/dist/packem_shared/{createPayment-BccfPGyw.mjs → createPayment-DzX-ji34.mjs} +17 -5
- package/dist/packem_shared/json-DhcPm8EO.mjs +11 -0
- package/dist/packem_shared/{lunoraDatabaseToPaymentDatabase-RlKX3Kcd.mjs → lunoraDatabaseToPaymentDatabase-CL2vdAXq.mjs} +2 -2
- package/dist/packem_shared/not-supported-C0onRyia.mjs +7 -0
- package/dist/packem_shared/{reconcile-CI1ukJF9.mjs → reconcile-Dtn_ErbJ.mjs} +1 -1
- package/dist/packem_shared/subscription-event-CwEsWRCK.mjs +17 -0
- package/dist/providers/autumn-features.d.mts +94 -0
- package/dist/providers/autumn-features.d.ts +94 -0
- package/dist/providers/autumn-features.mjs +95 -0
- package/dist/providers/autumn.d.mts +19 -0
- package/dist/providers/autumn.d.ts +19 -0
- package/dist/providers/autumn.mjs +294 -0
- package/dist/providers/creem.d.mts +17 -0
- package/dist/providers/creem.d.ts +17 -0
- package/dist/providers/creem.mjs +205 -0
- package/dist/providers/dodopayments.d.mts +21 -0
- package/dist/providers/dodopayments.d.ts +21 -0
- package/dist/providers/dodopayments.mjs +259 -0
- package/dist/providers/polar.d.mts +22 -0
- package/dist/providers/polar.d.ts +22 -0
- package/dist/{packem_shared/createPolarAdapter-BJtVGSlF.mjs → providers/polar.mjs} +51 -47
- package/dist/providers/stripe.d.mts +23 -0
- package/dist/providers/stripe.d.ts +23 -0
- package/dist/{packem_shared/createStripeAdapter-D40MVBXg.mjs → providers/stripe.mjs} +56 -55
- package/package.json +46 -5
- package/dist/packem_shared/json-Db337f36.mjs +0 -6
package/dist/packem_shared/{constantTimeEqual-CfY0jYcL.mjs → constantTimeEqual-BQjoW85H.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LunoraPaymentError } from './LunoraPaymentError-
|
|
1
|
+
import { LunoraPaymentError } from './LunoraPaymentError-BeQlhkZj.mjs';
|
|
2
2
|
|
|
3
3
|
const encoder = new TextEncoder();
|
|
4
4
|
const toHex = (buffer) => [...new Uint8Array(buffer)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
@@ -25,42 +25,6 @@ const hmacSha256Hex = async (secret, payload) => {
|
|
|
25
25
|
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
|
|
26
26
|
return toHex(signature);
|
|
27
27
|
};
|
|
28
|
-
const parseStripeSignatureHeader = (header) => {
|
|
29
|
-
let timestamp = Number.NaN;
|
|
30
|
-
const signatures = [];
|
|
31
|
-
for (const part of header.split(",")) {
|
|
32
|
-
const index = part.indexOf("=");
|
|
33
|
-
if (index === -1) {
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
const scheme = part.slice(0, index).trim();
|
|
37
|
-
const value = part.slice(index + 1).trim();
|
|
38
|
-
if (scheme === "t") {
|
|
39
|
-
timestamp = Number(value);
|
|
40
|
-
} else if (scheme === "v1") {
|
|
41
|
-
signatures.push(value);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return { signatures, timestamp };
|
|
45
|
-
};
|
|
46
|
-
const verifyStripeSignature = async (input) => {
|
|
47
|
-
if (!input.secret) {
|
|
48
|
-
throw new LunoraPaymentError("CONFIG_INVALID", "webhook secret not configured");
|
|
49
|
-
}
|
|
50
|
-
const toleranceSeconds = input.toleranceSeconds ?? 300;
|
|
51
|
-
const nowMs = input.now ?? Date.now();
|
|
52
|
-
const { signatures, timestamp } = parseStripeSignatureHeader(input.signatureHeader);
|
|
53
|
-
if (!Number.isFinite(timestamp) || signatures.length === 0) {
|
|
54
|
-
throw new LunoraPaymentError("WEBHOOK_SIGNATURE_INVALID", "malformed signature header");
|
|
55
|
-
}
|
|
56
|
-
if (Math.abs(Math.floor(nowMs / 1e3) - timestamp) > toleranceSeconds) {
|
|
57
|
-
throw new LunoraPaymentError("WEBHOOK_TIMESTAMP_INVALID", "signature timestamp outside tolerance");
|
|
58
|
-
}
|
|
59
|
-
const expected = await hmacSha256Hex(input.secret, `${String(timestamp)}.${input.payload}`);
|
|
60
|
-
if (!signatures.some((candidate) => constantTimeEqual(candidate, expected))) {
|
|
61
|
-
throw new LunoraPaymentError("WEBHOOK_SIGNATURE_INVALID", "no matching signature");
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
28
|
const verifyStandardWebhook = async (input) => {
|
|
65
29
|
if (!input.secret) {
|
|
66
30
|
throw new LunoraPaymentError("CONFIG_INVALID", "webhook secret not configured");
|
|
@@ -91,5 +55,17 @@ const verifyStandardWebhook = async (input) => {
|
|
|
91
55
|
throw new LunoraPaymentError("WEBHOOK_SIGNATURE_INVALID", "no matching signature");
|
|
92
56
|
}
|
|
93
57
|
};
|
|
58
|
+
const verifyCreemSignature = async (input) => {
|
|
59
|
+
if (!input.secret) {
|
|
60
|
+
throw new LunoraPaymentError("CONFIG_INVALID", "webhook secret not configured");
|
|
61
|
+
}
|
|
62
|
+
if (!input.signature) {
|
|
63
|
+
throw new LunoraPaymentError("WEBHOOK_SIGNATURE_INVALID", "missing creem-signature header");
|
|
64
|
+
}
|
|
65
|
+
const expected = await hmacSha256Hex(input.secret, input.payload);
|
|
66
|
+
if (!constantTimeEqual(input.signature, expected)) {
|
|
67
|
+
throw new LunoraPaymentError("WEBHOOK_SIGNATURE_INVALID", "no matching signature");
|
|
68
|
+
}
|
|
69
|
+
};
|
|
94
70
|
|
|
95
|
-
export { constantTimeEqual, hmacSha256Hex,
|
|
71
|
+
export { constantTimeEqual, hmacSha256Hex, verifyCreemSignature, verifyStandardWebhook };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { money } from './addMoney-
|
|
1
|
+
import { money } from './addMoney-jSh_7TfF.mjs';
|
|
2
2
|
|
|
3
3
|
const readString = (row, key) => typeof row[key] === "string" ? row[key] : "";
|
|
4
4
|
const readOptionalString = (row, key) => typeof row[key] === "string" ? row[key] : void 0;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { toErrorBody } from '@lunora/errors';
|
|
1
2
|
import { usagePeriodStart, resolveEntitlements, featureNames, hasActivePrice } from './entitlementsForReference-CzZGXPoZ.mjs';
|
|
2
|
-
import { LunoraPaymentError } from './LunoraPaymentError-
|
|
3
|
+
import { LunoraPaymentError } from './LunoraPaymentError-BeQlhkZj.mjs';
|
|
3
4
|
import idempotencyKey from './idempotencyKey-BFzDCA7g.mjs';
|
|
4
5
|
import { n as notifyObserver } from './observability-CvhJ205g.mjs';
|
|
5
|
-
import applyWebhookAction from './applyWebhookAction-
|
|
6
|
+
import applyWebhookAction from './applyWebhookAction-CLAx4svt.mjs';
|
|
7
|
+
|
|
8
|
+
const jsonResponse = (body, status = 200, headers) => Response.json(body, { headers: { "content-type": "application/json", ...headers }, status });
|
|
6
9
|
|
|
7
|
-
const jsonResponse = (body, status) => Response.json(body, { headers: { "content-type": "application/json" }, status });
|
|
8
10
|
const stripReferenceId = (metadata) => metadata && "referenceId" in metadata ? Object.fromEntries(Object.entries(metadata).filter(([key]) => key !== "referenceId")) : metadata;
|
|
9
11
|
const createPayment = (options) => {
|
|
10
12
|
const { adapter, store } = options;
|
|
@@ -31,7 +33,7 @@ const createPayment = (options) => {
|
|
|
31
33
|
if (existing) {
|
|
32
34
|
customerId = existing.id;
|
|
33
35
|
} else {
|
|
34
|
-
const customer = await adapter.getOrCreateCustomer({ referenceId: input.referenceId });
|
|
36
|
+
const customer = await adapter.getOrCreateCustomer({ email: input.email, referenceId: input.referenceId });
|
|
35
37
|
customerId = customer.id;
|
|
36
38
|
await store.upsertCustomer(customer);
|
|
37
39
|
}
|
|
@@ -78,6 +80,12 @@ const createPayment = (options) => {
|
|
|
78
80
|
},
|
|
79
81
|
check: async (input) => {
|
|
80
82
|
await ensureAuthorized(input.referenceId);
|
|
83
|
+
if (input.featureId === void 0 && input.priceId === void 0) {
|
|
84
|
+
throw new LunoraPaymentError("CONFIG_INVALID", "check() requires a featureId or priceId");
|
|
85
|
+
}
|
|
86
|
+
if (adapter.checkEntitlement) {
|
|
87
|
+
return adapter.checkEntitlement(input);
|
|
88
|
+
}
|
|
81
89
|
const subscriptions = await store.listSubscriptionsByReference(input.referenceId);
|
|
82
90
|
if (input.priceId !== void 0) {
|
|
83
91
|
return { allowed: hasActivePrice(subscriptions, input.priceId), unlimited: false };
|
|
@@ -107,7 +115,8 @@ const createPayment = (options) => {
|
|
|
107
115
|
action = await adapter.parseWebhook({ headers: request.headers, payload });
|
|
108
116
|
} catch (error) {
|
|
109
117
|
if (error instanceof LunoraPaymentError) {
|
|
110
|
-
|
|
118
|
+
const { body, status } = toErrorBody(error);
|
|
119
|
+
return jsonResponse({ error: body.message }, status);
|
|
111
120
|
}
|
|
112
121
|
return jsonResponse({ error: "webhook error" }, 400);
|
|
113
122
|
}
|
|
@@ -116,6 +125,9 @@ const createPayment = (options) => {
|
|
|
116
125
|
},
|
|
117
126
|
listBalances: async (referenceId) => {
|
|
118
127
|
await ensureAuthorized(referenceId);
|
|
128
|
+
if (adapter.getBalances) {
|
|
129
|
+
return adapter.getBalances(referenceId);
|
|
130
|
+
}
|
|
119
131
|
if (!options.entitlements) {
|
|
120
132
|
throw new LunoraPaymentError("CONFIG_INVALID", "listBalances() requires `entitlements` to be configured");
|
|
121
133
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const asRecord = (value) => typeof value === "object" && value !== null ? value : {};
|
|
2
|
+
const readString = (object, key) => typeof object[key] === "string" ? object[key] : void 0;
|
|
3
|
+
const readNumber = (object, key) => typeof object[key] === "number" ? object[key] : void 0;
|
|
4
|
+
const readBoolean = (object, key) => typeof object[key] === "boolean" ? object[key] : void 0;
|
|
5
|
+
const referenceFromMetadata = (object) => readString(asRecord(object.metadata), "referenceId");
|
|
6
|
+
const parseTimestamp = (value) => {
|
|
7
|
+
const parsed = typeof value === "string" ? Date.parse(value) : Number.NaN;
|
|
8
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { asRecord as a, readNumber as b, readBoolean as c, referenceFromMetadata as d, parseTimestamp as p, readString as r };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createPayment } from './createPayment-
|
|
2
|
-
import { createDatabasePaymentStore } from './createDatabasePaymentStore-
|
|
1
|
+
import { createPayment } from './createPayment-DzX-ji34.mjs';
|
|
2
|
+
import { createDatabasePaymentStore } from './createDatabasePaymentStore-C0NUCj1H.mjs';
|
|
3
3
|
|
|
4
4
|
const lunoraDatabaseToPaymentDatabase = (database) => {
|
|
5
5
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compareMoney } from './addMoney-
|
|
1
|
+
import { compareMoney } from './addMoney-jSh_7TfF.mjs';
|
|
2
2
|
import { n as notifyObserver } from './observability-CvhJ205g.mjs';
|
|
3
3
|
|
|
4
4
|
const sameCurrencyAmount = (a, b) => a.currency === b.currency && compareMoney(a, b) === 0;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const stateToEventType = (state) => {
|
|
2
|
+
if (state === "canceled") {
|
|
3
|
+
return "subscription.canceled";
|
|
4
|
+
}
|
|
5
|
+
if (state === "past_due") {
|
|
6
|
+
return "subscription.past_due";
|
|
7
|
+
}
|
|
8
|
+
if (state === "paused") {
|
|
9
|
+
return "subscription.paused";
|
|
10
|
+
}
|
|
11
|
+
if (state === "active" || state === "trialing") {
|
|
12
|
+
return "subscription.active";
|
|
13
|
+
}
|
|
14
|
+
return "subscription.updated";
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export { stateToEventType as s };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** A per-seat / per-workspace sub-customer with its own feature balances. */
|
|
2
|
+
interface AutumnEntity {
|
|
3
|
+
readonly featureId?: string;
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly name?: string;
|
|
6
|
+
/** The raw provider record, for fields this facade does not normalize. */
|
|
7
|
+
readonly raw: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
interface CreateEntityInput {
|
|
10
|
+
/** The feature the entity consumes a seat/allowance of (e.g. `"seats"`). */
|
|
11
|
+
readonly featureId: string;
|
|
12
|
+
/** Caller-chosen stable id — Autumn requires an entity id on create. */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly name?: string;
|
|
15
|
+
}
|
|
16
|
+
/** One point in a usage-events aggregation. */
|
|
17
|
+
interface UsageEventPoint {
|
|
18
|
+
readonly count: number;
|
|
19
|
+
readonly period?: string;
|
|
20
|
+
readonly raw: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
interface EventsListInput {
|
|
23
|
+
/** Feature(s) to report on. */
|
|
24
|
+
readonly featureId: ReadonlyArray<string> | string;
|
|
25
|
+
}
|
|
26
|
+
interface EventsAggregateInput extends EventsListInput {
|
|
27
|
+
/** Time window to aggregate over (aggregate only — `list` ignores it, so it isn't accepted there). */
|
|
28
|
+
readonly range?: "7d" | "24h" | "30d" | "90d" | "last_cycle";
|
|
29
|
+
}
|
|
30
|
+
/** A prepaid feature quantity purchased at checkout (e.g. buy 5 seats up front). */
|
|
31
|
+
interface PrepaidOption {
|
|
32
|
+
readonly featureId: string;
|
|
33
|
+
readonly quantity: number;
|
|
34
|
+
}
|
|
35
|
+
/** Autumn-native checkout — richer than the generic `createCheckout` (trials, prepaid, entities, rewards). */
|
|
36
|
+
interface AutumnCheckoutInput {
|
|
37
|
+
/** Scope the checkout to a specific entity (seat/workspace) rather than the top-level customer. */
|
|
38
|
+
readonly entityId?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Autumn applies a plan's configured free trial automatically. Pass `false` to opt out of that
|
|
41
|
+
* trial for this checkout; leave unset (or `true`) to keep the plan default.
|
|
42
|
+
*/
|
|
43
|
+
readonly freeTrial?: boolean;
|
|
44
|
+
/** Prepaid feature quantities to purchase up front. */
|
|
45
|
+
readonly options?: ReadonlyArray<PrepaidOption>;
|
|
46
|
+
/** The plan to attach (Autumn's plan id — the successor to the classic "product" id). */
|
|
47
|
+
readonly planId: string;
|
|
48
|
+
/** An Autumn reward id to apply as a discount. */
|
|
49
|
+
readonly reward?: string;
|
|
50
|
+
readonly successUrl?: string;
|
|
51
|
+
}
|
|
52
|
+
/** The subset of the Autumn SDK surface the native facade calls. A real `Autumn` instance satisfies it. */
|
|
53
|
+
interface AutumnFeaturesClientLike {
|
|
54
|
+
readonly billing: unknown;
|
|
55
|
+
readonly entities: unknown;
|
|
56
|
+
readonly events: unknown;
|
|
57
|
+
readonly plans: unknown;
|
|
58
|
+
readonly referrals: unknown;
|
|
59
|
+
}
|
|
60
|
+
interface AutumnFeaturesOptions {
|
|
61
|
+
readonly client: AutumnFeaturesClientLike;
|
|
62
|
+
}
|
|
63
|
+
/** The Autumn-native feature facade returned by {@link createAutumnFeatures}. */
|
|
64
|
+
interface AutumnFeatures {
|
|
65
|
+
readonly checkout: (referenceId: string, input: AutumnCheckoutInput) => Promise<{
|
|
66
|
+
raw: Record<string, unknown>;
|
|
67
|
+
url: string;
|
|
68
|
+
}>;
|
|
69
|
+
readonly entities: {
|
|
70
|
+
readonly create: (referenceId: string, input: CreateEntityInput) => Promise<AutumnEntity>;
|
|
71
|
+
readonly delete: (referenceId: string, entityId: string) => Promise<void>;
|
|
72
|
+
readonly get: (referenceId: string, entityId: string) => Promise<AutumnEntity>;
|
|
73
|
+
};
|
|
74
|
+
readonly events: {
|
|
75
|
+
readonly aggregate: (referenceId: string, input: EventsAggregateInput) => Promise<UsageEventPoint[]>;
|
|
76
|
+
readonly list: (referenceId: string, input: EventsListInput) => Promise<UsageEventPoint[]>;
|
|
77
|
+
};
|
|
78
|
+
readonly plans: {
|
|
79
|
+
readonly list: () => Promise<Record<string, unknown>[]>;
|
|
80
|
+
};
|
|
81
|
+
readonly referrals: {
|
|
82
|
+
readonly createCode: (referenceId: string, programId: string) => Promise<{
|
|
83
|
+
code: string;
|
|
84
|
+
raw: Record<string, unknown>;
|
|
85
|
+
}>;
|
|
86
|
+
readonly redeemCode: (referenceId: string, code: string) => Promise<Record<string, unknown>>;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build the Autumn-native feature facade over an injected client. Companion to `createAutumnAdapter`;
|
|
91
|
+
* share the same underlying `autumn-js` `Autumn` client between them.
|
|
92
|
+
*/
|
|
93
|
+
declare const createAutumnFeatures: (options: AutumnFeaturesOptions) => AutumnFeatures;
|
|
94
|
+
export { type AutumnCheckoutInput, type AutumnEntity, type AutumnFeatures, type AutumnFeaturesClientLike, type AutumnFeaturesOptions, type CreateEntityInput, type EventsAggregateInput, type EventsListInput, type PrepaidOption, type UsageEventPoint, createAutumnFeatures };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** A per-seat / per-workspace sub-customer with its own feature balances. */
|
|
2
|
+
interface AutumnEntity {
|
|
3
|
+
readonly featureId?: string;
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly name?: string;
|
|
6
|
+
/** The raw provider record, for fields this facade does not normalize. */
|
|
7
|
+
readonly raw: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
interface CreateEntityInput {
|
|
10
|
+
/** The feature the entity consumes a seat/allowance of (e.g. `"seats"`). */
|
|
11
|
+
readonly featureId: string;
|
|
12
|
+
/** Caller-chosen stable id — Autumn requires an entity id on create. */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly name?: string;
|
|
15
|
+
}
|
|
16
|
+
/** One point in a usage-events aggregation. */
|
|
17
|
+
interface UsageEventPoint {
|
|
18
|
+
readonly count: number;
|
|
19
|
+
readonly period?: string;
|
|
20
|
+
readonly raw: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
interface EventsListInput {
|
|
23
|
+
/** Feature(s) to report on. */
|
|
24
|
+
readonly featureId: ReadonlyArray<string> | string;
|
|
25
|
+
}
|
|
26
|
+
interface EventsAggregateInput extends EventsListInput {
|
|
27
|
+
/** Time window to aggregate over (aggregate only — `list` ignores it, so it isn't accepted there). */
|
|
28
|
+
readonly range?: "7d" | "24h" | "30d" | "90d" | "last_cycle";
|
|
29
|
+
}
|
|
30
|
+
/** A prepaid feature quantity purchased at checkout (e.g. buy 5 seats up front). */
|
|
31
|
+
interface PrepaidOption {
|
|
32
|
+
readonly featureId: string;
|
|
33
|
+
readonly quantity: number;
|
|
34
|
+
}
|
|
35
|
+
/** Autumn-native checkout — richer than the generic `createCheckout` (trials, prepaid, entities, rewards). */
|
|
36
|
+
interface AutumnCheckoutInput {
|
|
37
|
+
/** Scope the checkout to a specific entity (seat/workspace) rather than the top-level customer. */
|
|
38
|
+
readonly entityId?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Autumn applies a plan's configured free trial automatically. Pass `false` to opt out of that
|
|
41
|
+
* trial for this checkout; leave unset (or `true`) to keep the plan default.
|
|
42
|
+
*/
|
|
43
|
+
readonly freeTrial?: boolean;
|
|
44
|
+
/** Prepaid feature quantities to purchase up front. */
|
|
45
|
+
readonly options?: ReadonlyArray<PrepaidOption>;
|
|
46
|
+
/** The plan to attach (Autumn's plan id — the successor to the classic "product" id). */
|
|
47
|
+
readonly planId: string;
|
|
48
|
+
/** An Autumn reward id to apply as a discount. */
|
|
49
|
+
readonly reward?: string;
|
|
50
|
+
readonly successUrl?: string;
|
|
51
|
+
}
|
|
52
|
+
/** The subset of the Autumn SDK surface the native facade calls. A real `Autumn` instance satisfies it. */
|
|
53
|
+
interface AutumnFeaturesClientLike {
|
|
54
|
+
readonly billing: unknown;
|
|
55
|
+
readonly entities: unknown;
|
|
56
|
+
readonly events: unknown;
|
|
57
|
+
readonly plans: unknown;
|
|
58
|
+
readonly referrals: unknown;
|
|
59
|
+
}
|
|
60
|
+
interface AutumnFeaturesOptions {
|
|
61
|
+
readonly client: AutumnFeaturesClientLike;
|
|
62
|
+
}
|
|
63
|
+
/** The Autumn-native feature facade returned by {@link createAutumnFeatures}. */
|
|
64
|
+
interface AutumnFeatures {
|
|
65
|
+
readonly checkout: (referenceId: string, input: AutumnCheckoutInput) => Promise<{
|
|
66
|
+
raw: Record<string, unknown>;
|
|
67
|
+
url: string;
|
|
68
|
+
}>;
|
|
69
|
+
readonly entities: {
|
|
70
|
+
readonly create: (referenceId: string, input: CreateEntityInput) => Promise<AutumnEntity>;
|
|
71
|
+
readonly delete: (referenceId: string, entityId: string) => Promise<void>;
|
|
72
|
+
readonly get: (referenceId: string, entityId: string) => Promise<AutumnEntity>;
|
|
73
|
+
};
|
|
74
|
+
readonly events: {
|
|
75
|
+
readonly aggregate: (referenceId: string, input: EventsAggregateInput) => Promise<UsageEventPoint[]>;
|
|
76
|
+
readonly list: (referenceId: string, input: EventsListInput) => Promise<UsageEventPoint[]>;
|
|
77
|
+
};
|
|
78
|
+
readonly plans: {
|
|
79
|
+
readonly list: () => Promise<Record<string, unknown>[]>;
|
|
80
|
+
};
|
|
81
|
+
readonly referrals: {
|
|
82
|
+
readonly createCode: (referenceId: string, programId: string) => Promise<{
|
|
83
|
+
code: string;
|
|
84
|
+
raw: Record<string, unknown>;
|
|
85
|
+
}>;
|
|
86
|
+
readonly redeemCode: (referenceId: string, code: string) => Promise<Record<string, unknown>>;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build the Autumn-native feature facade over an injected client. Companion to `createAutumnAdapter`;
|
|
91
|
+
* share the same underlying `autumn-js` `Autumn` client between them.
|
|
92
|
+
*/
|
|
93
|
+
declare const createAutumnFeatures: (options: AutumnFeaturesOptions) => AutumnFeatures;
|
|
94
|
+
export { type AutumnCheckoutInput, type AutumnEntity, type AutumnFeatures, type AutumnFeaturesClientLike, type AutumnFeaturesOptions, type CreateEntityInput, type EventsAggregateInput, type EventsListInput, type PrepaidOption, type UsageEventPoint, createAutumnFeatures };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { a as asRecord, r as readString, b as readNumber } from '../packem_shared/json-DhcPm8EO.mjs';
|
|
2
|
+
|
|
3
|
+
const toFeatureId = (featureId) => typeof featureId === "string" ? featureId : [...featureId];
|
|
4
|
+
const entityFrom = (record) => {
|
|
5
|
+
return {
|
|
6
|
+
featureId: readString(record, "featureId") ?? readString(record, "feature_id"),
|
|
7
|
+
id: readString(record, "id") ?? "",
|
|
8
|
+
name: readString(record, "name"),
|
|
9
|
+
raw: record
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const pointsFrom = (result) => {
|
|
13
|
+
const list = result.list ?? result.data ?? result.events;
|
|
14
|
+
const rows = Array.isArray(list) ? list : [];
|
|
15
|
+
return rows.map((entry) => {
|
|
16
|
+
const record = asRecord(entry);
|
|
17
|
+
return { count: readNumber(record, "count") ?? readNumber(record, "value") ?? 0, period: readString(record, "period"), raw: record };
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const createAutumnFeatures = (options) => {
|
|
21
|
+
const client = options.client;
|
|
22
|
+
return {
|
|
23
|
+
/**
|
|
24
|
+
* Native Autumn checkout — supports opting out of a plan's free trial, prepaid feature
|
|
25
|
+
* quantities, entity scoping, and reward codes. Returns the hosted checkout URL (empty when
|
|
26
|
+
* Autumn applied the change directly without a payment step).
|
|
27
|
+
*/
|
|
28
|
+
checkout: async (referenceId, input) => {
|
|
29
|
+
const result = asRecord(
|
|
30
|
+
await client.billing.attach({
|
|
31
|
+
customerId: referenceId,
|
|
32
|
+
// Autumn applies the plan's configured trial by default; `null` opts this checkout out.
|
|
33
|
+
// eslint-disable-next-line unicorn/no-null -- the SDK's customize.freeTrial requires null (not undefined) to disable
|
|
34
|
+
...input.freeTrial === false ? { customize: { freeTrial: null } } : {},
|
|
35
|
+
discounts: input.reward ? [{ rewardId: input.reward }] : void 0,
|
|
36
|
+
entityId: input.entityId,
|
|
37
|
+
featureQuantities: input.options?.map((option) => {
|
|
38
|
+
return { featureId: option.featureId, quantity: option.quantity };
|
|
39
|
+
}),
|
|
40
|
+
planId: input.planId,
|
|
41
|
+
successUrl: input.successUrl
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
const url = readString(result, "paymentUrl") ?? readString(result, "checkoutUrl") ?? readString(result, "url") ?? "";
|
|
45
|
+
return { raw: result, url };
|
|
46
|
+
},
|
|
47
|
+
entities: {
|
|
48
|
+
/** Create a per-seat / per-workspace sub-customer under a reference. */
|
|
49
|
+
create: async (referenceId, input) => {
|
|
50
|
+
const created = await client.entities.create({ customerId: referenceId, entityId: input.id, featureId: input.featureId, name: input.name });
|
|
51
|
+
return entityFrom(asRecord(created));
|
|
52
|
+
},
|
|
53
|
+
/** Remove an entity — frees its seat/allowance. */
|
|
54
|
+
delete: async (referenceId, entityId) => {
|
|
55
|
+
await client.entities.delete({ customerId: referenceId, entityId });
|
|
56
|
+
},
|
|
57
|
+
/** Fetch one entity and its balances. */
|
|
58
|
+
get: async (referenceId, entityId) => {
|
|
59
|
+
const entity = await client.entities.get({ customerId: referenceId, entityId });
|
|
60
|
+
return entityFrom(asRecord(entity));
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
events: {
|
|
64
|
+
/** Bucketed usage aggregation for a reference's feature(s) over a range. */
|
|
65
|
+
aggregate: async (referenceId, input) => {
|
|
66
|
+
const result = await client.events.aggregate({ customerId: referenceId, featureId: toFeatureId(input.featureId), range: input.range });
|
|
67
|
+
return pointsFrom(asRecord(result));
|
|
68
|
+
},
|
|
69
|
+
/** Raw usage events for a reference's feature(s). */
|
|
70
|
+
list: async (referenceId, input) => {
|
|
71
|
+
const result = await client.events.list({ customerId: referenceId, featureId: toFeatureId(input.featureId) });
|
|
72
|
+
return pointsFrom(asRecord(result));
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
plans: {
|
|
76
|
+
/** The configured plan catalog. */
|
|
77
|
+
list: async () => {
|
|
78
|
+
const result = asRecord(await client.plans.list());
|
|
79
|
+
const list = result.list ?? result.data ?? result.plans;
|
|
80
|
+
return Array.isArray(list) ? list.map((entry) => asRecord(entry)) : [];
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
referrals: {
|
|
84
|
+
/** Mint (or fetch) a referral code for a reference in a referral program. */
|
|
85
|
+
createCode: async (referenceId, programId) => {
|
|
86
|
+
const result = asRecord(await client.referrals.createCode({ customerId: referenceId, programId }));
|
|
87
|
+
return { code: readString(result, "code") ?? "", raw: result };
|
|
88
|
+
},
|
|
89
|
+
/** Redeem a referral code on behalf of a reference. */
|
|
90
|
+
redeemCode: async (referenceId, code) => asRecord(await client.referrals.redeemCode({ code, customerId: referenceId }))
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export { createAutumnFeatures };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-iFA55DzU.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* The `autumn-js` SDK surface the adapter uses, as a structural type — a real `Autumn` instance
|
|
4
|
+
* satisfies it without a cast. Resources/methods are `unknown` (the adapter re-types the client as
|
|
5
|
+
* the real `Autumn` internally); this keeps the SDK's full type out of the published declarations.
|
|
6
|
+
*/
|
|
7
|
+
interface AutumnClientLike {
|
|
8
|
+
readonly billing: unknown;
|
|
9
|
+
readonly check: unknown;
|
|
10
|
+
readonly customers: unknown;
|
|
11
|
+
readonly track: unknown;
|
|
12
|
+
}
|
|
13
|
+
interface AutumnAdapterOptions {
|
|
14
|
+
readonly client: AutumnClientLike;
|
|
15
|
+
readonly webhookSecret: string;
|
|
16
|
+
readonly webhookToleranceSeconds?: number;
|
|
17
|
+
}
|
|
18
|
+
declare const createAutumnAdapter: (options: AutumnAdapterOptions) => PaymentAdapter;
|
|
19
|
+
export { type AutumnAdapterOptions, type AutumnClientLike, createAutumnAdapter };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { P as PaymentAdapter } from "../packem_shared/adapter.d-iFA55DzU.js";
|
|
2
|
+
/**
|
|
3
|
+
* The `autumn-js` SDK surface the adapter uses, as a structural type — a real `Autumn` instance
|
|
4
|
+
* satisfies it without a cast. Resources/methods are `unknown` (the adapter re-types the client as
|
|
5
|
+
* the real `Autumn` internally); this keeps the SDK's full type out of the published declarations.
|
|
6
|
+
*/
|
|
7
|
+
interface AutumnClientLike {
|
|
8
|
+
readonly billing: unknown;
|
|
9
|
+
readonly check: unknown;
|
|
10
|
+
readonly customers: unknown;
|
|
11
|
+
readonly track: unknown;
|
|
12
|
+
}
|
|
13
|
+
interface AutumnAdapterOptions {
|
|
14
|
+
readonly client: AutumnClientLike;
|
|
15
|
+
readonly webhookSecret: string;
|
|
16
|
+
readonly webhookToleranceSeconds?: number;
|
|
17
|
+
}
|
|
18
|
+
declare const createAutumnAdapter: (options: AutumnAdapterOptions) => PaymentAdapter;
|
|
19
|
+
export { type AutumnAdapterOptions, type AutumnClientLike, createAutumnAdapter };
|