@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.
Files changed (33) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +3 -2
  3. package/dist/index.d.mts +17 -456
  4. package/dist/index.d.ts +17 -456
  5. package/dist/index.mjs +3 -5
  6. package/dist/packem_shared/adapter.d-iFA55DzU.d.mts +312 -0
  7. package/dist/packem_shared/adapter.d-iFA55DzU.d.ts +312 -0
  8. package/dist/packem_shared/{constantTimeEqual-BN5Hm31G.mjs → constantTimeEqual-BQjoW85H.mjs} +13 -37
  9. package/dist/packem_shared/{createPayment-DP_LO7jc.mjs → createPayment-DzX-ji34.mjs} +15 -3
  10. package/dist/packem_shared/json-DhcPm8EO.mjs +11 -0
  11. package/dist/packem_shared/{lunoraDatabaseToPaymentDatabase-CQQlnRCJ.mjs → lunoraDatabaseToPaymentDatabase-CL2vdAXq.mjs} +1 -1
  12. package/dist/packem_shared/not-supported-C0onRyia.mjs +7 -0
  13. package/dist/packem_shared/subscription-event-CwEsWRCK.mjs +17 -0
  14. package/dist/providers/autumn-features.d.mts +94 -0
  15. package/dist/providers/autumn-features.d.ts +94 -0
  16. package/dist/providers/autumn-features.mjs +95 -0
  17. package/dist/providers/autumn.d.mts +19 -0
  18. package/dist/providers/autumn.d.ts +19 -0
  19. package/dist/providers/autumn.mjs +294 -0
  20. package/dist/providers/creem.d.mts +17 -0
  21. package/dist/providers/creem.d.ts +17 -0
  22. package/dist/providers/creem.mjs +205 -0
  23. package/dist/providers/dodopayments.d.mts +21 -0
  24. package/dist/providers/dodopayments.d.ts +21 -0
  25. package/dist/providers/dodopayments.mjs +259 -0
  26. package/dist/providers/polar.d.mts +22 -0
  27. package/dist/providers/polar.d.ts +22 -0
  28. package/dist/{packem_shared/createPolarAdapter-BovYiWCX.mjs → providers/polar.mjs} +47 -46
  29. package/dist/providers/stripe.d.mts +23 -0
  30. package/dist/providers/stripe.d.ts +23 -0
  31. package/dist/{packem_shared/createStripeAdapter-4a7fej3t.mjs → providers/stripe.mjs} +45 -53
  32. package/package.json +45 -5
  33. package/dist/packem_shared/json-Db337f36.mjs +0 -6
@@ -1,7 +1,8 @@
1
- import idempotencyKey from './idempotencyKey-BFzDCA7g.mjs';
2
- import { a as asRecord, r as readString, c as readNumber, b as readBoolean } from './json-Db337f36.mjs';
3
- import { compareMoney, money, zeroMoney } from './addMoney-jSh_7TfF.mjs';
4
- import { verifyStripeSignature } from './constantTimeEqual-BN5Hm31G.mjs';
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 = (intent) => {
49
- const amount = money(BigInt(intent.amount), intent.currency);
50
- const state = PAYMENT_STATE_BY_STRIPE_STATUS[intent.status] ?? "initiated";
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.amount_received ?? intent.amount), intent.currency) : zeroMoney(intent.currency),
58
+ capturedAmount: state === "captured" ? money(BigInt(Math.round(readNumber(intent, "amount_received") ?? amountValue)), currency) : zeroMoney(currency),
55
59
  createdAt: now,
56
- id: intent.id,
60
+ id: readString(intent, "id") ?? "",
57
61
  provider: "stripe",
58
- referenceId: intent.metadata?.referenceId ?? "",
59
- refundedAmount: zeroMoney(intent.currency),
62
+ referenceId: readReferenceId(intent) ?? "",
63
+ refundedAmount: zeroMoney(currency),
60
64
  state,
61
65
  updatedAt: now
62
66
  };
63
67
  };
64
- const subscriptionFromStripe = (subscription) => {
68
+ const subscriptionFromStripe = (input) => {
69
+ const subscription = asRecord(input);
65
70
  const now = Date.now();
66
71
  return {
67
- cancelAtPeriodEnd: subscription.cancel_at_period_end ?? false,
72
+ cancelAtPeriodEnd: readBoolean(subscription, "cancel_at_period_end") ?? false,
68
73
  createdAt: now,
69
- currentPeriodEnd: subscription.current_period_end ? subscription.current_period_end * 1e3 : void 0,
70
- currentPeriodStart: subscription.current_period_start ? subscription.current_period_start * 1e3 : void 0,
71
- id: subscription.id,
72
- priceId: subscription.items?.data[0]?.price?.id ?? "",
74
+ currentPeriodEnd: periodEndMs(subscription),
75
+ currentPeriodStart: periodStartMs(subscription),
76
+ id: readString(subscription, "id") ?? "",
77
+ priceId: firstPriceId(subscription) ?? "",
73
78
  provider: "stripe",
74
- quantity: subscription.items?.data[0]?.quantity ?? 1,
75
- referenceId: subscription.metadata?.referenceId ?? "",
76
- state: SUBSCRIPTION_STATE_BY_STRIPE_STATUS[subscription.status] ?? "active",
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: subscriptionEventType(readString(object, "status"))
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 { client, webhookSecret } = options;
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
- await verifyStripeSignature({ payload, secret: webhookSecret, signatureHeader, toleranceSeconds: options.webhookToleranceSeconds });
231
- const event = asRecord(JSON.parse(payload));
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(readString(event, "id") ?? "", readString(event, "type") ?? "", object);
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
- parameters.quantity = patch.quantity;
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.16",
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.1",
51
- "@lunora/server": "1.0.0-alpha.16",
52
- "@lunora/values": "1.0.0-alpha.4",
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
- "stripe": "^19.0.0"
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 };