@lunora/payment 1.0.0-alpha.16 → 1.0.0-alpha.18
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/dist/index.d.mts +17 -456
- package/dist/index.d.ts +17 -456
- package/dist/index.mjs +3 -5
- 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/{constantTimeEqual-BN5Hm31G.mjs → constantTimeEqual-BQjoW85H.mjs} +13 -37
- package/dist/packem_shared/{createPayment-DP_LO7jc.mjs → createPayment-DzX-ji34.mjs} +15 -3
- package/dist/packem_shared/json-DhcPm8EO.mjs +11 -0
- package/dist/packem_shared/{lunoraDatabaseToPaymentDatabase-CQQlnRCJ.mjs → lunoraDatabaseToPaymentDatabase-CL2vdAXq.mjs} +1 -1
- package/dist/packem_shared/not-supported-C0onRyia.mjs +7 -0
- 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-BovYiWCX.mjs → providers/polar.mjs} +47 -46
- package/dist/providers/stripe.d.mts +23 -0
- package/dist/providers/stripe.d.ts +23 -0
- package/dist/{packem_shared/createStripeAdapter-4a7fej3t.mjs → providers/stripe.mjs} +45 -53
- package/package.json +45 -5
- package/dist/packem_shared/json-Db337f36.mjs +0 -6
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { LunoraPaymentError } from '../packem_shared/LunoraPaymentError-BeQlhkZj.mjs';
|
|
2
|
+
import idempotencyKey from '../packem_shared/idempotencyKey-BFzDCA7g.mjs';
|
|
3
|
+
import { r as readString, c as readBoolean, b as readNumber, a as asRecord } from '../packem_shared/json-DhcPm8EO.mjs';
|
|
4
|
+
import { compareMoney, money, zeroMoney } from '../packem_shared/addMoney-jSh_7TfF.mjs';
|
|
5
|
+
import { s as stateToEventType } from '../packem_shared/subscription-event-CwEsWRCK.mjs';
|
|
5
6
|
|
|
6
7
|
const PAYMENT_STATE_BY_STRIPE_STATUS = {
|
|
7
8
|
canceled: "canceled",
|
|
@@ -38,61 +39,49 @@ const firstItem = (object) => {
|
|
|
38
39
|
const firstPriceId = (object) => readString(asRecord(firstItem(object).price), "id");
|
|
39
40
|
const firstQuantity = (object) => readNumber(firstItem(object), "quantity");
|
|
40
41
|
const periodEndMs = (object) => {
|
|
41
|
-
const seconds = readNumber(object, "current_period_end");
|
|
42
|
+
const seconds = readNumber(firstItem(object), "current_period_end") ?? readNumber(object, "current_period_end");
|
|
42
43
|
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
43
44
|
};
|
|
44
45
|
const periodStartMs = (object) => {
|
|
45
|
-
const seconds = readNumber(object, "current_period_start");
|
|
46
|
+
const seconds = readNumber(firstItem(object), "current_period_start") ?? readNumber(object, "current_period_start");
|
|
46
47
|
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
47
48
|
};
|
|
48
|
-
const intentToSession = (
|
|
49
|
-
const
|
|
50
|
-
const
|
|
49
|
+
const intentToSession = (input) => {
|
|
50
|
+
const intent = asRecord(input);
|
|
51
|
+
const currency = readString(intent, "currency") ?? "usd";
|
|
52
|
+
const amountValue = readNumber(intent, "amount") ?? 0;
|
|
53
|
+
const amount = money(BigInt(Math.round(amountValue)), currency);
|
|
54
|
+
const state = PAYMENT_STATE_BY_STRIPE_STATUS[readString(intent, "status") ?? ""] ?? "initiated";
|
|
51
55
|
const now = Date.now();
|
|
52
56
|
return {
|
|
53
57
|
amount,
|
|
54
|
-
capturedAmount: state === "captured" ? money(BigInt(intent
|
|
58
|
+
capturedAmount: state === "captured" ? money(BigInt(Math.round(readNumber(intent, "amount_received") ?? amountValue)), currency) : zeroMoney(currency),
|
|
55
59
|
createdAt: now,
|
|
56
|
-
id: intent
|
|
60
|
+
id: readString(intent, "id") ?? "",
|
|
57
61
|
provider: "stripe",
|
|
58
|
-
referenceId: intent
|
|
59
|
-
refundedAmount: zeroMoney(
|
|
62
|
+
referenceId: readReferenceId(intent) ?? "",
|
|
63
|
+
refundedAmount: zeroMoney(currency),
|
|
60
64
|
state,
|
|
61
65
|
updatedAt: now
|
|
62
66
|
};
|
|
63
67
|
};
|
|
64
|
-
const subscriptionFromStripe = (
|
|
68
|
+
const subscriptionFromStripe = (input) => {
|
|
69
|
+
const subscription = asRecord(input);
|
|
65
70
|
const now = Date.now();
|
|
66
71
|
return {
|
|
67
|
-
cancelAtPeriodEnd: subscription
|
|
72
|
+
cancelAtPeriodEnd: readBoolean(subscription, "cancel_at_period_end") ?? false,
|
|
68
73
|
createdAt: now,
|
|
69
|
-
currentPeriodEnd: subscription
|
|
70
|
-
currentPeriodStart: subscription
|
|
71
|
-
id: subscription
|
|
72
|
-
priceId: subscription
|
|
74
|
+
currentPeriodEnd: periodEndMs(subscription),
|
|
75
|
+
currentPeriodStart: periodStartMs(subscription),
|
|
76
|
+
id: readString(subscription, "id") ?? "",
|
|
77
|
+
priceId: firstPriceId(subscription) ?? "",
|
|
73
78
|
provider: "stripe",
|
|
74
|
-
quantity: subscription
|
|
75
|
-
referenceId: subscription
|
|
76
|
-
state: SUBSCRIPTION_STATE_BY_STRIPE_STATUS[subscription
|
|
79
|
+
quantity: firstQuantity(subscription) ?? 1,
|
|
80
|
+
referenceId: readReferenceId(subscription) ?? "",
|
|
81
|
+
state: SUBSCRIPTION_STATE_BY_STRIPE_STATUS[readString(subscription, "status") ?? ""] ?? "active",
|
|
77
82
|
updatedAt: now
|
|
78
83
|
};
|
|
79
84
|
};
|
|
80
|
-
const subscriptionEventType = (status) => {
|
|
81
|
-
const state = status ? SUBSCRIPTION_STATE_BY_STRIPE_STATUS[status] : void 0;
|
|
82
|
-
if (state === "canceled") {
|
|
83
|
-
return "subscription.canceled";
|
|
84
|
-
}
|
|
85
|
-
if (state === "past_due") {
|
|
86
|
-
return "subscription.past_due";
|
|
87
|
-
}
|
|
88
|
-
if (state === "paused") {
|
|
89
|
-
return "subscription.paused";
|
|
90
|
-
}
|
|
91
|
-
if (state === "active" || state === "trialing") {
|
|
92
|
-
return "subscription.active";
|
|
93
|
-
}
|
|
94
|
-
return "subscription.updated";
|
|
95
|
-
};
|
|
96
85
|
const mapEvent = (eventId, eventType, object) => {
|
|
97
86
|
const base = { eventId, provider: "stripe", raw: { object, type: eventType } };
|
|
98
87
|
const currency = readString(object, "currency") ?? "usd";
|
|
@@ -100,7 +89,7 @@ const mapEvent = (eventId, eventType, object) => {
|
|
|
100
89
|
case "charge.refunded": {
|
|
101
90
|
return {
|
|
102
91
|
...base,
|
|
103
|
-
amount: money(BigInt(readNumber(object, "amount_refunded") ?? 0), currency),
|
|
92
|
+
amount: money(BigInt(Math.round(readNumber(object, "amount_refunded") ?? 0)), currency),
|
|
104
93
|
amountKind: "absolute",
|
|
105
94
|
referenceId: readReferenceId(object),
|
|
106
95
|
sessionId: readString(object, "payment_intent") ?? readString(object, "id"),
|
|
@@ -122,7 +111,7 @@ const mapEvent = (eventId, eventType, object) => {
|
|
|
122
111
|
const amountTotal = readNumber(object, "amount_total");
|
|
123
112
|
return {
|
|
124
113
|
...base,
|
|
125
|
-
amount: amountTotal === void 0 ? void 0 : money(BigInt(amountTotal), currency),
|
|
114
|
+
amount: amountTotal === void 0 ? void 0 : money(BigInt(Math.round(amountTotal)), currency),
|
|
126
115
|
customerId: readString(object, "customer"),
|
|
127
116
|
referenceId: readReferenceId(object),
|
|
128
117
|
sessionId: readString(object, "payment_intent") ?? readString(object, "id"),
|
|
@@ -141,7 +130,7 @@ const mapEvent = (eventId, eventType, object) => {
|
|
|
141
130
|
quantity: firstQuantity(object),
|
|
142
131
|
referenceId: readReferenceId(object),
|
|
143
132
|
subscriptionId: readString(object, "id"),
|
|
144
|
-
type:
|
|
133
|
+
type: stateToEventType(SUBSCRIPTION_STATE_BY_STRIPE_STATUS[readString(object, "status") ?? ""])
|
|
145
134
|
};
|
|
146
135
|
}
|
|
147
136
|
case "customer.subscription.deleted": {
|
|
@@ -150,7 +139,7 @@ const mapEvent = (eventId, eventType, object) => {
|
|
|
150
139
|
case "payment_intent.amount_capturable_updated": {
|
|
151
140
|
return {
|
|
152
141
|
...base,
|
|
153
|
-
amount: money(BigInt(readNumber(object, "amount") ?? 0), currency),
|
|
142
|
+
amount: money(BigInt(Math.round(readNumber(object, "amount") ?? 0)), currency),
|
|
154
143
|
referenceId: readReferenceId(object),
|
|
155
144
|
sessionId: readString(object, "id"),
|
|
156
145
|
type: "payment.authorized"
|
|
@@ -162,7 +151,7 @@ const mapEvent = (eventId, eventType, object) => {
|
|
|
162
151
|
case "payment_intent.succeeded": {
|
|
163
152
|
return {
|
|
164
153
|
...base,
|
|
165
|
-
amount: money(BigInt(readNumber(object, "amount_received") ?? readNumber(object, "amount") ?? 0), currency),
|
|
154
|
+
amount: money(BigInt(Math.round(readNumber(object, "amount_received") ?? readNumber(object, "amount") ?? 0)), currency),
|
|
166
155
|
customerId: readString(object, "customer"),
|
|
167
156
|
referenceId: readReferenceId(object),
|
|
168
157
|
sessionId: readString(object, "id"),
|
|
@@ -175,7 +164,8 @@ const mapEvent = (eventId, eventType, object) => {
|
|
|
175
164
|
}
|
|
176
165
|
};
|
|
177
166
|
const createStripeAdapter = (options) => {
|
|
178
|
-
const {
|
|
167
|
+
const { webhookSecret } = options;
|
|
168
|
+
const client = options.client;
|
|
179
169
|
return {
|
|
180
170
|
cancelPayment: async (sessionId, paymentOptions) => {
|
|
181
171
|
const intent = await client.paymentIntents.cancel(sessionId, void 0, { idempotencyKey: paymentOptions?.idempotencyKey });
|
|
@@ -227,10 +217,14 @@ const createStripeAdapter = (options) => {
|
|
|
227
217
|
identifier: "stripe",
|
|
228
218
|
parseWebhook: async ({ headers, payload }) => {
|
|
229
219
|
const signatureHeader = headers.get("stripe-signature") ?? "";
|
|
230
|
-
|
|
231
|
-
|
|
220
|
+
let event;
|
|
221
|
+
try {
|
|
222
|
+
event = await client.webhooks.constructEventAsync(payload, signatureHeader, webhookSecret, options.webhookToleranceSeconds);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
throw new LunoraPaymentError("WEBHOOK_SIGNATURE_INVALID", error instanceof Error ? error.message : "invalid webhook signature");
|
|
225
|
+
}
|
|
232
226
|
const object = asRecord(asRecord(event.data).object);
|
|
233
|
-
return mapEvent(
|
|
227
|
+
return mapEvent(event.id, event.type, object);
|
|
234
228
|
},
|
|
235
229
|
refundPayment: async (input) => {
|
|
236
230
|
await client.refunds.create(
|
|
@@ -252,7 +246,7 @@ const createStripeAdapter = (options) => {
|
|
|
252
246
|
{
|
|
253
247
|
event_name: input.featureId,
|
|
254
248
|
identifier: input.idempotencyKey,
|
|
255
|
-
payload: { stripe_customer_id: input.customerId, value: String(input.quantity) },
|
|
249
|
+
payload: { stripe_customer_id: input.customerId ?? "", value: String(input.quantity) },
|
|
256
250
|
timestamp: input.timestamp === void 0 ? void 0 : Math.floor(input.timestamp / 1e3)
|
|
257
251
|
},
|
|
258
252
|
{ idempotencyKey: input.idempotencyKey }
|
|
@@ -264,11 +258,9 @@ const createStripeAdapter = (options) => {
|
|
|
264
258
|
},
|
|
265
259
|
updateSubscription: async (subscriptionId, patch) => {
|
|
266
260
|
const parameters = {};
|
|
267
|
-
if (patch.quantity !== void 0) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (patch.priceId) {
|
|
271
|
-
parameters.items = [{ price: patch.priceId }];
|
|
261
|
+
if (patch.priceId !== void 0 || patch.quantity !== void 0) {
|
|
262
|
+
const current = await client.subscriptions.retrieve(subscriptionId);
|
|
263
|
+
parameters.items = [{ id: current.items.data[0]?.id, price: patch.priceId, quantity: patch.quantity }];
|
|
272
264
|
}
|
|
273
265
|
const subscription = await client.subscriptions.update(subscriptionId, parameters);
|
|
274
266
|
return subscriptionFromStripe(subscription);
|
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.18",
|
|
4
4
|
"description": "Provider-agnostic payments for Lunora: Stripe-first adapter, webhook sync, and subscription/payment state machine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"billing",
|
|
@@ -41,21 +41,61 @@
|
|
|
41
41
|
"types": "./dist/index.d.ts",
|
|
42
42
|
"import": "./dist/index.mjs"
|
|
43
43
|
},
|
|
44
|
+
"./stripe": {
|
|
45
|
+
"types": "./dist/providers/stripe.d.ts",
|
|
46
|
+
"import": "./dist/providers/stripe.mjs"
|
|
47
|
+
},
|
|
48
|
+
"./polar": {
|
|
49
|
+
"types": "./dist/providers/polar.d.ts",
|
|
50
|
+
"import": "./dist/providers/polar.mjs"
|
|
51
|
+
},
|
|
52
|
+
"./autumn": {
|
|
53
|
+
"types": "./dist/providers/autumn.d.ts",
|
|
54
|
+
"import": "./dist/providers/autumn.mjs"
|
|
55
|
+
},
|
|
56
|
+
"./autumn-features": {
|
|
57
|
+
"types": "./dist/providers/autumn-features.d.ts",
|
|
58
|
+
"import": "./dist/providers/autumn-features.mjs"
|
|
59
|
+
},
|
|
60
|
+
"./dodopayments": {
|
|
61
|
+
"types": "./dist/providers/dodopayments.d.ts",
|
|
62
|
+
"import": "./dist/providers/dodopayments.mjs"
|
|
63
|
+
},
|
|
64
|
+
"./creem": {
|
|
65
|
+
"types": "./dist/providers/creem.d.ts",
|
|
66
|
+
"import": "./dist/providers/creem.mjs"
|
|
67
|
+
},
|
|
44
68
|
"./package.json": "./package.json"
|
|
45
69
|
},
|
|
46
70
|
"publishConfig": {
|
|
47
71
|
"access": "public"
|
|
48
72
|
},
|
|
49
73
|
"dependencies": {
|
|
50
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
51
|
-
"@lunora/server": "1.0.0-alpha.
|
|
52
|
-
"@lunora/values": "1.0.0-alpha.
|
|
74
|
+
"@lunora/errors": "1.0.0-alpha.2",
|
|
75
|
+
"@lunora/server": "1.0.0-alpha.17",
|
|
76
|
+
"@lunora/values": "1.0.0-alpha.5",
|
|
53
77
|
"dinero.js": "2.0.2"
|
|
54
78
|
},
|
|
55
79
|
"peerDependencies": {
|
|
56
|
-
"
|
|
80
|
+
"@polar-sh/sdk": "^0.48.0",
|
|
81
|
+
"autumn-js": "^1.2.0",
|
|
82
|
+
"creem": "^1.5.0",
|
|
83
|
+
"dodopayments": "^2.41.0",
|
|
84
|
+
"stripe": "^22.0.0"
|
|
57
85
|
},
|
|
58
86
|
"peerDependenciesMeta": {
|
|
87
|
+
"@polar-sh/sdk": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"autumn-js": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"creem": {
|
|
94
|
+
"optional": true
|
|
95
|
+
},
|
|
96
|
+
"dodopayments": {
|
|
97
|
+
"optional": true
|
|
98
|
+
},
|
|
59
99
|
"stripe": {
|
|
60
100
|
"optional": true
|
|
61
101
|
}
|
|
@@ -1,6 +0,0 @@
|
|
|
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 };
|