@lunora/payment 0.0.0 → 1.0.0-alpha.1
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 +105 -0
- package/README.md +150 -9
- package/__assets__/package-og.svg +14 -0
- package/dist/index.d.mts +778 -0
- package/dist/index.d.ts +778 -0
- package/dist/index.mjs +16 -0
- package/dist/packem_shared/LunoraPaymentError-B3hEzXSs.mjs +22 -0
- package/dist/packem_shared/MemoryPaymentStore-DvgdWa3C.mjs +72 -0
- package/dist/packem_shared/applyWebhookAction-6NObmIh4.mjs +177 -0
- package/dist/packem_shared/canTransitionPayment-DrxV0clv.mjs +26 -0
- package/dist/packem_shared/constantTimeEqual-CfY0jYcL.mjs +95 -0
- package/dist/packem_shared/createAdapterRegistry-BuDHFCBc.mjs +24 -0
- package/dist/packem_shared/createDatabasePaymentStore-C0hXSKmj.mjs +172 -0
- package/dist/packem_shared/createPayment-DG97pe-f.mjs +190 -0
- package/dist/packem_shared/createPolarAdapter-fRZ9Yke0.mjs +217 -0
- package/dist/packem_shared/createStripeAdapter-C7xpOpsD.mjs +270 -0
- package/dist/packem_shared/idempotencyKey-BFzDCA7g.mjs +3 -0
- package/dist/packem_shared/isZeroDecimalCurrency-bCcs1nyw.mjs +60 -0
- package/dist/packem_shared/json-Db337f36.mjs +6 -0
- package/dist/packem_shared/lunoraDatabaseToPaymentDatabase-D_ViFvZV.mjs +29 -0
- package/dist/packem_shared/observability-CvhJ205g.mjs +11 -0
- package/dist/packem_shared/paymentTables-DccHwWr_.mjs +127 -0
- package/dist/packem_shared/reconcile-BhJCfqcT.mjs +73 -0
- package/dist/packem_shared/usagePeriodStart-CzZGXPoZ.mjs +51 -0
- package/package.json +51 -18
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { toSnapshot } from 'dinero.js';
|
|
2
|
+
import { add, allocate, compare, subtract, dinero } from 'dinero.js/bigint';
|
|
3
|
+
import { LunoraPaymentError } from './LunoraPaymentError-B3hEzXSs.mjs';
|
|
4
|
+
|
|
5
|
+
const ZERO_DECIMAL = /* @__PURE__ */ new Set(["BIF", "CLP", "DJF", "GNF", "JPY", "KMF", "KRW", "MGA", "PYG", "RWF", "UGX", "VND", "VUV", "XAF", "XOF", "XPF"]);
|
|
6
|
+
const THREE_DECIMAL = /* @__PURE__ */ new Set(["BHD", "IQD", "JOD", "KWD", "LYD", "OMR", "TND"]);
|
|
7
|
+
const exponentFor = (code) => {
|
|
8
|
+
if (ZERO_DECIMAL.has(code)) {
|
|
9
|
+
return 0n;
|
|
10
|
+
}
|
|
11
|
+
return THREE_DECIMAL.has(code) ? 3n : 2n;
|
|
12
|
+
};
|
|
13
|
+
const currencyFor = (code) => {
|
|
14
|
+
return { base: 10n, code: code.toUpperCase(), exponent: exponentFor(code.toUpperCase()) };
|
|
15
|
+
};
|
|
16
|
+
const toDinero = (value) => dinero({ amount: value.minorUnits, currency: currencyFor(value.currency) });
|
|
17
|
+
const fromDinero = (value) => {
|
|
18
|
+
const snapshot = toSnapshot(value);
|
|
19
|
+
return { currency: snapshot.currency.code, minorUnits: snapshot.amount };
|
|
20
|
+
};
|
|
21
|
+
const assertSameCurrency = (a, b) => {
|
|
22
|
+
if (a.currency !== b.currency) {
|
|
23
|
+
throw new LunoraPaymentError("CURRENCY_MISMATCH", `cannot combine ${a.currency} with ${b.currency}`);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const isZeroDecimalCurrency = (currency) => exponentFor(currency.toUpperCase()) === 0n;
|
|
27
|
+
const money = (minorUnits, currency) => {
|
|
28
|
+
const units = typeof minorUnits === "bigint" ? minorUnits : BigInt(Math.trunc(minorUnits));
|
|
29
|
+
return { currency: currency.toUpperCase(), minorUnits: units };
|
|
30
|
+
};
|
|
31
|
+
const zeroMoney = (currency) => money(0n, currency);
|
|
32
|
+
const formatMoney = (value, locale = "en-US") => {
|
|
33
|
+
const exponent = Number(exponentFor(value.currency.toUpperCase()));
|
|
34
|
+
const amount = Number(value.minorUnits) / 10 ** exponent;
|
|
35
|
+
try {
|
|
36
|
+
return new Intl.NumberFormat(locale, { currency: value.currency, style: "currency" }).format(amount);
|
|
37
|
+
} catch {
|
|
38
|
+
return `${amount.toFixed(exponent)} ${value.currency}`;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const addMoney = (a, b) => {
|
|
42
|
+
assertSameCurrency(a, b);
|
|
43
|
+
return fromDinero(add(toDinero(a), toDinero(b)));
|
|
44
|
+
};
|
|
45
|
+
const subtractMoney = (a, b) => {
|
|
46
|
+
assertSameCurrency(a, b);
|
|
47
|
+
return fromDinero(subtract(toDinero(a), toDinero(b)));
|
|
48
|
+
};
|
|
49
|
+
const compareMoney = (a, b) => {
|
|
50
|
+
assertSameCurrency(a, b);
|
|
51
|
+
return compare(toDinero(a), toDinero(b));
|
|
52
|
+
};
|
|
53
|
+
const allocateMoney = (amount, ratios) => allocate(toDinero(amount), [...ratios]).map((part) => fromDinero(part));
|
|
54
|
+
const isZeroMoney = (a) => a.minorUnits === 0n;
|
|
55
|
+
const toMoneyJSON = (m) => {
|
|
56
|
+
return { currency: m.currency, minorUnits: m.minorUnits.toString() };
|
|
57
|
+
};
|
|
58
|
+
const fromMoneyJSON = (json) => money(BigInt(json.minorUnits), json.currency);
|
|
59
|
+
|
|
60
|
+
export { addMoney, allocateMoney, compareMoney, formatMoney, fromMoneyJSON, isZeroDecimalCurrency, isZeroMoney, money, subtractMoney, toMoneyJSON, zeroMoney };
|
|
@@ -0,0 +1,6 @@
|
|
|
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
|
+
|
|
6
|
+
export { asRecord as a, readBoolean as b, readNumber as c, readString as r };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createPayment } from './createPayment-DG97pe-f.mjs';
|
|
2
|
+
import { createDatabasePaymentStore } from './createDatabasePaymentStore-C0hXSKmj.mjs';
|
|
3
|
+
|
|
4
|
+
const lunoraDatabaseToPaymentDatabase = (database) => {
|
|
5
|
+
return {
|
|
6
|
+
delete: async (id) => database.delete(id),
|
|
7
|
+
findFirst: async (table, where) => await database.findFirst(table, { where }),
|
|
8
|
+
findMany: async (table, where) => {
|
|
9
|
+
const result = await database.findMany(table, { where });
|
|
10
|
+
return result.page;
|
|
11
|
+
},
|
|
12
|
+
insert: async (table, document) => database.insert(table, document),
|
|
13
|
+
patch: async (id, patch) => database.patch(id, patch)
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
const paymentsFromContext = (context, options) => {
|
|
17
|
+
const userId = context.auth?.userId ?? void 0;
|
|
18
|
+
return createPayment({
|
|
19
|
+
adapter: options.adapter,
|
|
20
|
+
// The default authorizer fails closed on an empty/whitespace reference: a missing identity or a
|
|
21
|
+
// blank reference (e.g. webhook-orphaned rows with `referenceId: ""`) is never authorized.
|
|
22
|
+
authorize: options.authorize ?? ((referenceId) => referenceId.trim() !== "" && userId !== void 0 && referenceId === userId),
|
|
23
|
+
entitlements: options.entitlements,
|
|
24
|
+
observability: options.observability,
|
|
25
|
+
store: createDatabasePaymentStore(lunoraDatabaseToPaymentDatabase(context.db))
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export { lunoraDatabaseToPaymentDatabase, paymentsFromContext };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { defineTable } from '@lunora/server';
|
|
2
|
+
import { v } from '@lunora/values';
|
|
3
|
+
|
|
4
|
+
const products = defineTable({
|
|
5
|
+
description: v.optional(v.string()),
|
|
6
|
+
name: v.string(),
|
|
7
|
+
provider: v.string(),
|
|
8
|
+
providerProductId: v.string()
|
|
9
|
+
}).index("by_provider_product", ["provider", "providerProductId"], { unique: true });
|
|
10
|
+
const prices = defineTable({
|
|
11
|
+
active: v.boolean(),
|
|
12
|
+
amountMinor: v.bigint(),
|
|
13
|
+
currency: v.string(),
|
|
14
|
+
interval: v.optional(v.string()),
|
|
15
|
+
provider: v.string(),
|
|
16
|
+
providerPriceId: v.string(),
|
|
17
|
+
providerProductId: v.string()
|
|
18
|
+
}).index("by_provider_price", ["provider", "providerPriceId"], { unique: true });
|
|
19
|
+
const customers = defineTable({
|
|
20
|
+
createdAt: v.number(),
|
|
21
|
+
email: v.optional(v.string()),
|
|
22
|
+
provider: v.string(),
|
|
23
|
+
providerCustomerId: v.string(),
|
|
24
|
+
referenceId: v.string()
|
|
25
|
+
}).index("by_provider_customer", ["provider", "providerCustomerId"], { unique: true }).index("by_reference", ["referenceId"]);
|
|
26
|
+
const subscriptions = defineTable({
|
|
27
|
+
cancelAtPeriodEnd: v.boolean(),
|
|
28
|
+
createdAt: v.number(),
|
|
29
|
+
currentPeriodEnd: v.optional(v.number()),
|
|
30
|
+
currentPeriodStart: v.optional(v.number()),
|
|
31
|
+
priceId: v.string(),
|
|
32
|
+
provider: v.string(),
|
|
33
|
+
providerSubscriptionId: v.string(),
|
|
34
|
+
quantity: v.number(),
|
|
35
|
+
referenceId: v.string(),
|
|
36
|
+
state: v.string(),
|
|
37
|
+
updatedAt: v.number()
|
|
38
|
+
}).index("by_provider_subscription", ["provider", "providerSubscriptionId"], { unique: true }).index("by_reference", ["referenceId"]);
|
|
39
|
+
const checkouts = defineTable({
|
|
40
|
+
createdAt: v.number(),
|
|
41
|
+
mode: v.string(),
|
|
42
|
+
priceId: v.string(),
|
|
43
|
+
provider: v.string(),
|
|
44
|
+
providerCheckoutId: v.string(),
|
|
45
|
+
referenceId: v.string(),
|
|
46
|
+
url: v.string()
|
|
47
|
+
}).index("by_provider_checkout", ["provider", "providerCheckoutId"], { unique: true });
|
|
48
|
+
const paymentSessions = defineTable({
|
|
49
|
+
amountMinor: v.bigint(),
|
|
50
|
+
capturedMinor: v.bigint(),
|
|
51
|
+
createdAt: v.number(),
|
|
52
|
+
currency: v.string(),
|
|
53
|
+
provider: v.string(),
|
|
54
|
+
providerSessionId: v.string(),
|
|
55
|
+
referenceId: v.string(),
|
|
56
|
+
refundedMinor: v.bigint(),
|
|
57
|
+
state: v.string(),
|
|
58
|
+
updatedAt: v.number()
|
|
59
|
+
}).index("by_provider_session", ["provider", "providerSessionId"], { unique: true }).index("by_reference", ["referenceId"]);
|
|
60
|
+
const payments = defineTable({
|
|
61
|
+
amountMinor: v.bigint(),
|
|
62
|
+
createdAt: v.number(),
|
|
63
|
+
currency: v.string(),
|
|
64
|
+
provider: v.string(),
|
|
65
|
+
providerPaymentId: v.string(),
|
|
66
|
+
referenceId: v.string(),
|
|
67
|
+
sessionId: v.string(),
|
|
68
|
+
status: v.string()
|
|
69
|
+
}).index("by_provider_payment", ["provider", "providerPaymentId"], { unique: true });
|
|
70
|
+
const captures = defineTable({
|
|
71
|
+
amountMinor: v.bigint(),
|
|
72
|
+
createdAt: v.number(),
|
|
73
|
+
currency: v.string(),
|
|
74
|
+
provider: v.string(),
|
|
75
|
+
providerCaptureId: v.string(),
|
|
76
|
+
sessionId: v.string()
|
|
77
|
+
}).index("by_session", ["sessionId"]);
|
|
78
|
+
const refunds = defineTable({
|
|
79
|
+
amountMinor: v.bigint(),
|
|
80
|
+
createdAt: v.number(),
|
|
81
|
+
currency: v.string(),
|
|
82
|
+
provider: v.string(),
|
|
83
|
+
providerRefundId: v.string(),
|
|
84
|
+
reason: v.optional(v.string()),
|
|
85
|
+
sessionId: v.string()
|
|
86
|
+
}).index("by_session", ["sessionId"]);
|
|
87
|
+
const invoices = defineTable({
|
|
88
|
+
amountMinor: v.bigint(),
|
|
89
|
+
createdAt: v.number(),
|
|
90
|
+
currency: v.string(),
|
|
91
|
+
provider: v.string(),
|
|
92
|
+
providerInvoiceId: v.string(),
|
|
93
|
+
referenceId: v.string(),
|
|
94
|
+
status: v.string(),
|
|
95
|
+
subscriptionId: v.optional(v.string())
|
|
96
|
+
}).index("by_provider_invoice", ["provider", "providerInvoiceId"], { unique: true });
|
|
97
|
+
const events = defineTable({
|
|
98
|
+
processedAt: v.number(),
|
|
99
|
+
provider: v.string(),
|
|
100
|
+
providerEventId: v.string(),
|
|
101
|
+
type: v.string()
|
|
102
|
+
}).index("by_provider_event", ["provider", "providerEventId"], { unique: true });
|
|
103
|
+
const usageEvents = defineTable({
|
|
104
|
+
createdAt: v.number(),
|
|
105
|
+
featureId: v.string(),
|
|
106
|
+
idempotencyKey: v.string(),
|
|
107
|
+
provider: v.string(),
|
|
108
|
+
quantity: v.number(),
|
|
109
|
+
referenceId: v.string(),
|
|
110
|
+
reportedToProvider: v.boolean()
|
|
111
|
+
}).index("by_idempotency", ["provider", "idempotencyKey"], { unique: true }).index("by_reference_feature", ["referenceId", "featureId"]);
|
|
112
|
+
const paymentTables = {
|
|
113
|
+
captures,
|
|
114
|
+
checkouts,
|
|
115
|
+
customers,
|
|
116
|
+
events,
|
|
117
|
+
invoices,
|
|
118
|
+
paymentSessions,
|
|
119
|
+
payments,
|
|
120
|
+
prices,
|
|
121
|
+
products,
|
|
122
|
+
refunds,
|
|
123
|
+
subscriptions,
|
|
124
|
+
usageEvents
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export { paymentTables as default };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { compareMoney } from './isZeroDecimalCurrency-bCcs1nyw.mjs';
|
|
2
|
+
import { n as notifyObserver } from './observability-CvhJ205g.mjs';
|
|
3
|
+
|
|
4
|
+
const sameCurrencyAmount = (a, b) => a.currency === b.currency && compareMoney(a, b) === 0;
|
|
5
|
+
const subscriptionDrifted = (existing, current) => existing?.state !== current.state || existing.cancelAtPeriodEnd !== current.cancelAtPeriodEnd || existing.currentPeriodEnd !== current.currentPeriodEnd || existing.priceId !== current.priceId || existing.quantity !== current.quantity;
|
|
6
|
+
const paymentDrifted = (existing, current) => existing?.state !== current.state || !sameCurrencyAmount(existing.capturedAmount, current.capturedAmount) || !sameCurrencyAmount(existing.refundedAmount, current.refundedAmount);
|
|
7
|
+
const reconcileSubscription = async (adapter, store, id, observer) => {
|
|
8
|
+
const current = await adapter.getSubscriptionStatus(id);
|
|
9
|
+
const existing = await store.getSubscription(adapter.identifier, id);
|
|
10
|
+
if (!subscriptionDrifted(existing, current)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
await store.upsertSubscription({ ...current, createdAt: existing?.createdAt ?? current.createdAt });
|
|
14
|
+
notifyObserver(observer, { id, kind: "subscription", provider: adapter.identifier, type: "reconcile.drift" });
|
|
15
|
+
return true;
|
|
16
|
+
};
|
|
17
|
+
const reconcilePayment = async (adapter, store, id, observer) => {
|
|
18
|
+
const current = await adapter.getPaymentStatus(id);
|
|
19
|
+
const existing = await store.getPaymentSession(adapter.identifier, id);
|
|
20
|
+
if (!paymentDrifted(existing, current)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
await store.upsertPaymentSession({ ...current, createdAt: existing?.createdAt ?? current.createdAt });
|
|
24
|
+
notifyObserver(observer, { id, kind: "payment", provider: adapter.identifier, type: "reconcile.drift" });
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
27
|
+
const sweep = async (ids, kind, reconcileOne, adapter, observer) => {
|
|
28
|
+
const settled = await Promise.allSettled(ids.map((id) => reconcileOne(id)));
|
|
29
|
+
let updated = 0;
|
|
30
|
+
let failed = 0;
|
|
31
|
+
for (const [index, result] of settled.entries()) {
|
|
32
|
+
if (result.status === "fulfilled") {
|
|
33
|
+
if (result.value) {
|
|
34
|
+
updated += 1;
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
failed += 1;
|
|
38
|
+
notifyObserver(observer, { error: result.reason, id: ids[index] ?? "", kind, provider: adapter.identifier, type: "reconcile.error" });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { failed, updated };
|
|
42
|
+
};
|
|
43
|
+
const reconcile = async (input) => {
|
|
44
|
+
const { adapter, observability, store } = input;
|
|
45
|
+
const subscriptionIds = input.subscriptionIds ?? [];
|
|
46
|
+
const paymentSessionIds = input.paymentSessionIds ?? [];
|
|
47
|
+
const subscriptionCounts = await sweep(
|
|
48
|
+
subscriptionIds,
|
|
49
|
+
"subscription",
|
|
50
|
+
(id) => reconcileSubscription(adapter, store, id, observability),
|
|
51
|
+
adapter,
|
|
52
|
+
observability
|
|
53
|
+
);
|
|
54
|
+
const paymentCounts = await sweep(paymentSessionIds, "payment", (id) => reconcilePayment(adapter, store, id, observability), adapter, observability);
|
|
55
|
+
notifyObserver(observability, {
|
|
56
|
+
failedPayments: paymentCounts.failed,
|
|
57
|
+
failedSubscriptions: subscriptionCounts.failed,
|
|
58
|
+
provider: adapter.identifier,
|
|
59
|
+
type: "reconcile.completed",
|
|
60
|
+
updatedPayments: paymentCounts.updated,
|
|
61
|
+
updatedSubscriptions: subscriptionCounts.updated
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
checkedPayments: paymentSessionIds.length,
|
|
65
|
+
checkedSubscriptions: subscriptionIds.length,
|
|
66
|
+
failedPayments: paymentCounts.failed,
|
|
67
|
+
failedSubscriptions: subscriptionCounts.failed,
|
|
68
|
+
updatedPayments: paymentCounts.updated,
|
|
69
|
+
updatedSubscriptions: subscriptionCounts.updated
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export { reconcile };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const ACTIVE_STATES = /* @__PURE__ */ new Set(["active", "trialing"]);
|
|
2
|
+
const usagePeriodStart = (subscriptions) => {
|
|
3
|
+
let start = 0;
|
|
4
|
+
for (const subscription of subscriptions) {
|
|
5
|
+
if (ACTIVE_STATES.has(subscription.state) && subscription.currentPeriodStart !== void 0) {
|
|
6
|
+
start = Math.max(start, subscription.currentPeriodStart);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
return start;
|
|
10
|
+
};
|
|
11
|
+
const featureNames = (config) => {
|
|
12
|
+
const names = /* @__PURE__ */ new Set();
|
|
13
|
+
for (const plan of Object.values(config.plans)) {
|
|
14
|
+
for (const feature of plan.features ?? []) {
|
|
15
|
+
names.add(feature);
|
|
16
|
+
}
|
|
17
|
+
for (const key of Object.keys(plan.limits ?? {})) {
|
|
18
|
+
names.add(key);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return [...names].toSorted((a, b) => a.localeCompare(b));
|
|
22
|
+
};
|
|
23
|
+
const hasActivePrice = (subscriptions, priceId) => subscriptions.some((subscription) => subscription.priceId === priceId && ACTIVE_STATES.has(subscription.state));
|
|
24
|
+
const resolveEntitlements = (config, subscriptions) => {
|
|
25
|
+
const activePriceIds = new Set(subscriptions.filter((subscription) => ACTIVE_STATES.has(subscription.state)).map((subscription) => subscription.priceId));
|
|
26
|
+
const plans = [];
|
|
27
|
+
const features = /* @__PURE__ */ new Set();
|
|
28
|
+
const limits = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const [name, plan] of Object.entries(config.plans)) {
|
|
30
|
+
if (!plan.priceIds.some((id) => activePriceIds.has(id))) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
plans.push(name);
|
|
34
|
+
for (const feature of plan.features ?? []) {
|
|
35
|
+
features.add(feature);
|
|
36
|
+
}
|
|
37
|
+
for (const [key, value] of Object.entries(plan.limits ?? {})) {
|
|
38
|
+
const current = limits.get(key);
|
|
39
|
+
limits.set(key, current === void 0 ? value : Math.max(current, value));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
features,
|
|
44
|
+
has: (feature) => features.has(feature),
|
|
45
|
+
limit: (key) => limits.get(key),
|
|
46
|
+
plans
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const entitlementsForReference = async (store, config, referenceId) => resolveEntitlements(config, await store.listSubscriptionsByReference(referenceId));
|
|
50
|
+
|
|
51
|
+
export { entitlementsForReference, featureNames, hasActivePrice, resolveEntitlements, usagePeriodStart };
|
package/package.json
CHANGED
|
@@ -1,32 +1,65 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/payment",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
4
|
"description": "Provider-agnostic payments for Lunora: Stripe-first adapter, webhook sync, and subscription/payment state machine",
|
|
5
|
-
"license": "FSL-1.1-Apache-2.0",
|
|
6
|
-
"homepage": "https://lunora.sh",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/anolilab/lunora.git",
|
|
10
|
-
"directory": "packages/payment"
|
|
11
|
-
},
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/anolilab/lunora/issues"
|
|
14
|
-
},
|
|
15
5
|
"keywords": [
|
|
16
|
-
"
|
|
6
|
+
"billing",
|
|
17
7
|
"cloudflare",
|
|
18
|
-
"workers",
|
|
19
8
|
"durable-objects",
|
|
9
|
+
"lunora",
|
|
20
10
|
"payment",
|
|
21
|
-
"billing",
|
|
22
11
|
"stripe",
|
|
23
12
|
"subscriptions",
|
|
24
|
-
"webhooks"
|
|
13
|
+
"webhooks",
|
|
14
|
+
"workers"
|
|
25
15
|
],
|
|
16
|
+
"homepage": "https://lunora.sh",
|
|
17
|
+
"bugs": "https://github.com/anolilab/lunora/issues",
|
|
18
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
19
|
+
"author": {
|
|
20
|
+
"name": "Daniel Bannert",
|
|
21
|
+
"email": "d.bannert@anolilab.de"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/anolilab/lunora.git",
|
|
26
|
+
"directory": "packages/payment"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"__assets__",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE.md"
|
|
33
|
+
],
|
|
34
|
+
"type": "module",
|
|
35
|
+
"sideEffects": false,
|
|
36
|
+
"main": "./dist/index.mjs",
|
|
37
|
+
"module": "./dist/index.mjs",
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"import": "./dist/index.mjs"
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
26
46
|
"publishConfig": {
|
|
27
47
|
"access": "public"
|
|
28
48
|
},
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@lunora/server": "1.0.0-alpha.1",
|
|
51
|
+
"@lunora/values": "1.0.0-alpha.1",
|
|
52
|
+
"dinero.js": "2.0.2"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"stripe": "^19.0.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"stripe": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": "^22.15.0 || >=24.11.0"
|
|
64
|
+
}
|
|
32
65
|
}
|