@stripe-sdk/core 1.0.0 → 1.0.2

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.
@@ -59,6 +59,9 @@ function createWebhookHandler(config) {
59
59
  function createNextWebhookHandler(config) {
60
60
  const handler = createWebhookHandler(config);
61
61
  return async function POST(request) {
62
+ if (request.method !== "POST") {
63
+ return new Response(null, { status: 405 });
64
+ }
62
65
  try {
63
66
  const contentLength = request.headers.get("content-length");
64
67
  if (contentLength && parseInt(contentLength, 10) > MAX_BODY_SIZE) {
@@ -68,6 +71,12 @@ function createNextWebhookHandler(config) {
68
71
  });
69
72
  }
70
73
  const body = await request.text();
74
+ if (body.length > MAX_BODY_SIZE) {
75
+ return new Response(JSON.stringify({ error: "Webhook body too large" }), {
76
+ status: 413,
77
+ headers: { "Content-Type": "application/json" }
78
+ });
79
+ }
71
80
  const signature = request.headers.get("stripe-signature");
72
81
  if (!signature) {
73
82
  return new Response(JSON.stringify({ error: "Missing stripe-signature header" }), {
@@ -81,8 +90,8 @@ function createNextWebhookHandler(config) {
81
90
  headers: { "Content-Type": "application/json" }
82
91
  });
83
92
  } catch (error) {
84
- const message = error instanceof Error ? error.message : "Webhook handler failed";
85
- return new Response(JSON.stringify({ error: message }), {
93
+ console.error("[@stripe-sdk/core] Webhook error:", error instanceof Error ? error.message : "Unknown error");
94
+ return new Response(JSON.stringify({ error: "Webhook processing failed" }), {
86
95
  status: 400,
87
96
  headers: { "Content-Type": "application/json" }
88
97
  });
@@ -106,8 +115,8 @@ function createPagesWebhookHandler(webhookConfig) {
106
115
  const result = await handler(body, signature);
107
116
  res.status(200).json(result);
108
117
  } catch (error) {
109
- const message = error instanceof Error ? error.message : "Webhook handler failed";
110
- res.status(400).json({ error: message });
118
+ console.error("[@stripe-sdk/core] Webhook error:", error instanceof Error ? error.message : "Unknown error");
119
+ res.status(400).json({ error: "Webhook processing failed" });
111
120
  }
112
121
  };
113
122
  }
@@ -135,28 +144,42 @@ function getRawBody(req) {
135
144
  }
136
145
  const chunks = [];
137
146
  let totalLength = 0;
147
+ let rejected = false;
138
148
  req.on("data", (chunk) => {
149
+ if (rejected) return;
139
150
  const buf = Buffer.from(chunk);
140
151
  totalLength += buf.length;
141
152
  if (totalLength > MAX_BODY_SIZE) {
153
+ rejected = true;
142
154
  reject(new Error("Webhook body too large"));
143
155
  return;
144
156
  }
145
157
  chunks.push(buf);
146
158
  });
147
- req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
159
+ req.on("end", () => {
160
+ if (!rejected) resolve(Buffer.concat(chunks).toString("utf8"));
161
+ });
148
162
  req.on("error", reject);
149
163
  });
150
164
  }
151
165
 
152
166
  // src/utils/errors.ts
167
+ var SAFE_ERROR_MESSAGES = {
168
+ card_declined: "Your card was declined.",
169
+ expired_card: "Your card has expired.",
170
+ incorrect_cvc: "Incorrect security code.",
171
+ processing_error: "An error occurred while processing your card.",
172
+ incorrect_number: "The card number is incorrect.",
173
+ insufficient_funds: "Insufficient funds."
174
+ };
153
175
  function handleStripeError(error) {
154
176
  if (error?.type) {
155
177
  const stripeError = error;
178
+ const safeMessage = stripeError.code && SAFE_ERROR_MESSAGES[stripeError.code] || getSafeMessage(stripeError.type);
156
179
  return {
157
180
  data: null,
158
181
  error: {
159
- message: stripeError.message,
182
+ message: safeMessage,
160
183
  type: stripeError.type,
161
184
  code: stripeError.code,
162
185
  statusCode: stripeError.statusCode
@@ -166,72 +189,196 @@ function handleStripeError(error) {
166
189
  return {
167
190
  data: null,
168
191
  error: {
169
- message: error instanceof Error ? error.message : "An unknown error occurred",
192
+ message: "An unexpected error occurred",
170
193
  type: "sdk_error"
171
194
  }
172
195
  };
173
196
  }
197
+ function getSafeMessage(type) {
198
+ switch (type) {
199
+ case "card_error":
200
+ return "A card error occurred.";
201
+ case "invalid_request_error":
202
+ return "Invalid request. Please check your input.";
203
+ case "authentication_error":
204
+ return "Authentication failed.";
205
+ case "rate_limit_error":
206
+ return "Too many requests. Please try again later.";
207
+ case "api_error":
208
+ return "A payment processing error occurred. Please try again.";
209
+ default:
210
+ return "An unexpected error occurred.";
211
+ }
212
+ }
174
213
  function success(data) {
175
214
  return { data, error: null };
176
215
  }
177
216
 
217
+ // src/utils/validators.ts
218
+ var STRIPE_ID_PREFIXES = {
219
+ customer: "cus_",
220
+ paymentIntent: "pi_",
221
+ paymentMethod: "pm_",
222
+ subscription: "sub_",
223
+ invoice: "in_",
224
+ invoiceItem: "ii_",
225
+ product: "prod_",
226
+ price: "price_",
227
+ coupon: "",
228
+ // coupons can have custom IDs
229
+ promotionCode: "promo_",
230
+ refund: "re_",
231
+ dispute: "dp_",
232
+ account: "acct_",
233
+ transfer: "tr_",
234
+ payout: "po_",
235
+ setupIntent: "seti_",
236
+ session: "cs_",
237
+ paymentLink: "plink_"
238
+ };
239
+ function validateStripeId(id, type) {
240
+ if (!id || typeof id !== "string") {
241
+ throw new Error(`${type} ID is required and must be a non-empty string`);
242
+ }
243
+ const prefix = STRIPE_ID_PREFIXES[type];
244
+ if (prefix && !id.startsWith(prefix)) {
245
+ throw new Error(`Invalid ${type} ID: expected prefix "${prefix}"`);
246
+ }
247
+ }
248
+ function validateAmount(amount, label = "amount") {
249
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
250
+ throw new Error(`${label} must be a finite number`);
251
+ }
252
+ if (!Number.isInteger(amount)) {
253
+ throw new Error(`${label} must be an integer (amount in smallest currency unit)`);
254
+ }
255
+ if (amount < 0) {
256
+ throw new Error(`${label} must not be negative`);
257
+ }
258
+ if (amount > 99999999) {
259
+ throw new Error(`${label} exceeds maximum allowed value (99999999)`);
260
+ }
261
+ }
262
+ function validateCurrency(currency) {
263
+ if (!currency || typeof currency !== "string") {
264
+ throw new Error("Currency is required");
265
+ }
266
+ const normalized = currency.trim().toLowerCase();
267
+ if (!/^[a-z]{3}$/.test(normalized)) {
268
+ throw new Error("Currency must be a valid 3-letter ISO 4217 code");
269
+ }
270
+ return normalized;
271
+ }
272
+ function validateUrl(url, label = "URL") {
273
+ if (!url || typeof url !== "string") {
274
+ throw new Error(`${label} is required`);
275
+ }
276
+ try {
277
+ const parsed = new URL(url);
278
+ if (!["http:", "https:"].includes(parsed.protocol)) {
279
+ throw new Error(`${label} must use http or https protocol`);
280
+ }
281
+ } catch (e) {
282
+ if (e instanceof Error && e.message.includes("protocol")) throw e;
283
+ throw new Error(`${label} must be a valid URL`);
284
+ }
285
+ }
286
+ function validateMetadata(metadata) {
287
+ const keys = Object.keys(metadata);
288
+ if (keys.length > 50) {
289
+ throw new Error("Metadata cannot have more than 50 keys");
290
+ }
291
+ for (const [key, value] of Object.entries(metadata)) {
292
+ if (key.length > 40) {
293
+ throw new Error(`Metadata key "${key}" exceeds 40 characters`);
294
+ }
295
+ if (typeof value !== "string") {
296
+ throw new Error(`Metadata value for key "${key}" must be a string`);
297
+ }
298
+ if (value.length > 500) {
299
+ throw new Error(`Metadata value for key "${key}" exceeds 500 characters`);
300
+ }
301
+ }
302
+ }
303
+
178
304
  // src/server/payments/index.ts
179
- async function createPaymentIntent(input) {
305
+ async function createPaymentIntent(input, options) {
180
306
  try {
307
+ validateAmount(input.amount);
308
+ const currency = validateCurrency(input.currency);
309
+ if (input.customerId) validateStripeId(input.customerId, "customer");
310
+ if (input.paymentMethodId) validateStripeId(input.paymentMethodId, "paymentMethod");
311
+ if (input.returnUrl) validateUrl(input.returnUrl, "returnUrl");
312
+ if (input.metadata) validateMetadata(input.metadata);
181
313
  const stripe = getStripe();
182
- const paymentIntent = await stripe.paymentIntents.create({
183
- amount: input.amount,
184
- currency: input.currency,
185
- customer: input.customerId,
186
- payment_method: input.paymentMethodId,
187
- metadata: input.metadata,
188
- description: input.description,
189
- receipt_email: input.receiptEmail,
190
- setup_future_usage: input.setupFutureUsage,
191
- automatic_payment_methods: input.automaticPaymentMethods === false || input.paymentMethodId ? void 0 : { enabled: true },
192
- return_url: input.returnUrl
193
- });
314
+ const paymentIntent = await stripe.paymentIntents.create(
315
+ {
316
+ amount: input.amount,
317
+ currency,
318
+ customer: input.customerId,
319
+ payment_method: input.paymentMethodId,
320
+ metadata: input.metadata,
321
+ description: input.description,
322
+ receipt_email: input.receiptEmail,
323
+ setup_future_usage: input.setupFutureUsage,
324
+ automatic_payment_methods: input.automaticPaymentMethods === false || input.paymentMethodId ? void 0 : { enabled: true },
325
+ return_url: input.returnUrl
326
+ },
327
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
328
+ );
194
329
  return success(paymentIntent);
195
330
  } catch (error) {
196
331
  return handleStripeError(error);
197
332
  }
198
333
  }
199
- async function createCheckoutSession(input) {
334
+ async function createCheckoutSession(input, options) {
200
335
  try {
336
+ validateUrl(input.successUrl, "successUrl");
337
+ validateUrl(input.cancelUrl, "cancelUrl");
338
+ if (input.customerId) validateStripeId(input.customerId, "customer");
339
+ if (input.metadata) validateMetadata(input.metadata);
201
340
  const stripe = getStripe();
202
- const session = await stripe.checkout.sessions.create({
203
- mode: input.mode,
204
- line_items: input.lineItems.map((item) => ({
205
- price: item.priceId,
206
- quantity: item.quantity
207
- })),
208
- success_url: input.successUrl,
209
- cancel_url: input.cancelUrl,
210
- customer: input.customerId,
211
- customer_email: input.customerEmail,
212
- metadata: input.metadata,
213
- allow_promotion_codes: input.allowPromotionCodes,
214
- shipping_address_collection: input.shippingAddressCollection ? { allowed_countries: input.shippingAddressCollection.allowedCountries } : void 0,
215
- billing_address_collection: input.billingAddressCollection,
216
- subscription_data: input.trialPeriodDays ? { trial_period_days: input.trialPeriodDays } : void 0,
217
- tax_id_collection: input.taxIdCollection ? { enabled: true } : void 0,
218
- automatic_tax: input.automaticTax ? { enabled: true } : void 0,
219
- locale: input.locale
220
- });
341
+ const session = await stripe.checkout.sessions.create(
342
+ {
343
+ mode: input.mode,
344
+ line_items: input.lineItems.map((item) => ({
345
+ price: item.priceId,
346
+ quantity: item.quantity
347
+ })),
348
+ success_url: input.successUrl,
349
+ cancel_url: input.cancelUrl,
350
+ customer: input.customerId,
351
+ customer_email: input.customerEmail,
352
+ metadata: input.metadata,
353
+ allow_promotion_codes: input.allowPromotionCodes,
354
+ shipping_address_collection: input.shippingAddressCollection ? { allowed_countries: input.shippingAddressCollection.allowedCountries } : void 0,
355
+ billing_address_collection: input.billingAddressCollection,
356
+ subscription_data: input.trialPeriodDays ? { trial_period_days: input.trialPeriodDays } : void 0,
357
+ tax_id_collection: input.taxIdCollection ? { enabled: true } : void 0,
358
+ automatic_tax: input.automaticTax ? { enabled: true } : void 0,
359
+ locale: input.locale
360
+ },
361
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
362
+ );
221
363
  return success(session);
222
364
  } catch (error) {
223
365
  return handleStripeError(error);
224
366
  }
225
367
  }
226
- async function createSetupIntent(input) {
368
+ async function createSetupIntent(input, options) {
227
369
  try {
370
+ if (input.customerId) validateStripeId(input.customerId, "customer");
371
+ if (input.metadata) validateMetadata(input.metadata);
228
372
  const stripe = getStripe();
229
- const setupIntent = await stripe.setupIntents.create({
230
- customer: input.customerId,
231
- payment_method_types: input.paymentMethodTypes,
232
- usage: input.usage,
233
- metadata: input.metadata
234
- });
373
+ const setupIntent = await stripe.setupIntents.create(
374
+ {
375
+ customer: input.customerId,
376
+ payment_method_types: input.paymentMethodTypes,
377
+ usage: input.usage,
378
+ metadata: input.metadata
379
+ },
380
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
381
+ );
235
382
  return success(setupIntent);
236
383
  } catch (error) {
237
384
  return handleStripeError(error);
@@ -239,39 +386,49 @@ async function createSetupIntent(input) {
239
386
  }
240
387
 
241
388
  // src/server/customers/index.ts
242
- async function createCustomer(input) {
389
+ async function createCustomer(input, options) {
243
390
  try {
391
+ if (input.paymentMethodId) validateStripeId(input.paymentMethodId, "paymentMethod");
392
+ if (input.metadata) validateMetadata(input.metadata);
244
393
  const stripe = getStripe();
245
- const customer = await stripe.customers.create({
246
- email: input.email,
247
- name: input.name,
248
- phone: input.phone,
249
- description: input.description,
250
- metadata: input.metadata,
251
- payment_method: input.paymentMethodId,
252
- invoice_settings: input.paymentMethodId ? { default_payment_method: input.paymentMethodId } : void 0,
253
- address: input.address ? {
254
- line1: input.address.line1,
255
- line2: input.address.line2,
256
- city: input.address.city,
257
- state: input.address.state,
258
- postal_code: input.address.postalCode,
259
- country: input.address.country
260
- } : void 0
261
- });
394
+ const customer = await stripe.customers.create(
395
+ {
396
+ email: input.email,
397
+ name: input.name,
398
+ phone: input.phone,
399
+ description: input.description,
400
+ metadata: input.metadata,
401
+ payment_method: input.paymentMethodId,
402
+ invoice_settings: input.paymentMethodId ? { default_payment_method: input.paymentMethodId } : void 0,
403
+ address: input.address ? {
404
+ line1: input.address.line1,
405
+ line2: input.address.line2,
406
+ city: input.address.city,
407
+ state: input.address.state,
408
+ postal_code: input.address.postalCode,
409
+ country: input.address.country
410
+ } : void 0
411
+ },
412
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
413
+ );
262
414
  return success(customer);
263
415
  } catch (error) {
264
416
  return handleStripeError(error);
265
417
  }
266
418
  }
267
- async function createPortalSession(input) {
419
+ async function createPortalSession(input, options) {
268
420
  try {
421
+ validateStripeId(input.customerId, "customer");
422
+ if (input.returnUrl) validateUrl(input.returnUrl, "returnUrl");
269
423
  const stripe = getStripe();
270
- const session = await stripe.billingPortal.sessions.create({
271
- customer: input.customerId,
272
- return_url: input.returnUrl,
273
- configuration: input.configuration
274
- });
424
+ const session = await stripe.billingPortal.sessions.create(
425
+ {
426
+ customer: input.customerId,
427
+ return_url: input.returnUrl,
428
+ configuration: input.configuration
429
+ },
430
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
431
+ );
275
432
  return success(session);
276
433
  } catch (error) {
277
434
  return handleStripeError(error);
@@ -279,23 +436,28 @@ async function createPortalSession(input) {
279
436
  }
280
437
 
281
438
  // src/server/subscriptions/index.ts
282
- async function createSubscription(input) {
439
+ async function createSubscription(input, options) {
283
440
  try {
441
+ validateStripeId(input.customerId, "customer");
442
+ if (input.metadata) validateMetadata(input.metadata);
284
443
  const stripe = getStripe();
285
444
  const items = input.items ? input.items.map((item) => ({ price: item.priceId, quantity: item.quantity })) : [{ price: input.priceId, quantity: input.quantity ?? 1 }];
286
- const subscription = await stripe.subscriptions.create({
287
- customer: input.customerId,
288
- items,
289
- metadata: input.metadata,
290
- trial_period_days: input.trialPeriodDays,
291
- coupon: input.couponId,
292
- promotion_code: input.promotionCodeId,
293
- payment_behavior: input.paymentBehavior ?? "default_incomplete",
294
- cancel_at_period_end: input.cancelAtPeriodEnd,
295
- billing_cycle_anchor: input.billingCycleAnchor,
296
- proration_behavior: input.prorationBehavior,
297
- expand: ["latest_invoice.payment_intent"]
298
- });
445
+ const subscription = await stripe.subscriptions.create(
446
+ {
447
+ customer: input.customerId,
448
+ items,
449
+ metadata: input.metadata,
450
+ trial_period_days: input.trialPeriodDays,
451
+ coupon: input.couponId,
452
+ promotion_code: input.promotionCodeId,
453
+ payment_behavior: input.paymentBehavior ?? "default_incomplete",
454
+ cancel_at_period_end: input.cancelAtPeriodEnd,
455
+ billing_cycle_anchor: input.billingCycleAnchor,
456
+ proration_behavior: input.prorationBehavior,
457
+ expand: ["latest_invoice.payment_intent"]
458
+ },
459
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
460
+ );
299
461
  return success(subscription);
300
462
  } catch (error) {
301
463
  return handleStripeError(error);
@@ -303,6 +465,7 @@ async function createSubscription(input) {
303
465
  }
304
466
  async function cancelSubscription(input) {
305
467
  try {
468
+ validateStripeId(input.subscriptionId, "subscription");
306
469
  const stripe = getStripe();
307
470
  if (input.cancelAtPeriodEnd) {
308
471
  const subscription2 = await stripe.subscriptions.update(input.subscriptionId, {
@@ -327,6 +490,7 @@ async function cancelSubscription(input) {
327
490
  }
328
491
  async function resumeSubscription(subscriptionId) {
329
492
  try {
493
+ validateStripeId(subscriptionId, "subscription");
330
494
  const stripe = getStripe();
331
495
  const subscription = await stripe.subscriptions.update(subscriptionId, {
332
496
  cancel_at_period_end: false
@@ -338,113 +502,168 @@ async function resumeSubscription(subscriptionId) {
338
502
  }
339
503
 
340
504
  // src/next/index.ts
341
- var actions = {
342
- createPaymentIntent: async (input) => {
343
- return createPaymentIntent(input);
344
- },
345
- createCheckoutSession: async (input) => {
346
- return createCheckoutSession(input);
347
- },
348
- createSetupIntent: async (input) => {
349
- return createSetupIntent(input);
350
- },
351
- createCustomer: async (input) => {
352
- return createCustomer(input);
353
- },
354
- createPortalSession: async (input) => {
355
- return createPortalSession(input);
356
- },
357
- createSubscription: async (input) => {
358
- return createSubscription(input);
359
- },
360
- cancelSubscription: async (input) => {
361
- return cancelSubscription(input);
362
- },
363
- resumeSubscription: async (subscriptionId) => {
364
- return resumeSubscription(subscriptionId);
505
+ function createActions(config) {
506
+ if (typeof config?.authorize !== "function") {
507
+ throw new Error("[@stripe-sdk/core] createActions requires an authorize callback");
365
508
  }
366
- };
509
+ async function checkAuth() {
510
+ const result = await config.authorize();
511
+ if (result === false) {
512
+ throw new Error("Unauthorized");
513
+ }
514
+ }
515
+ return {
516
+ createPaymentIntent: async (input) => {
517
+ await checkAuth();
518
+ return createPaymentIntent(input);
519
+ },
520
+ createCheckoutSession: async (input) => {
521
+ await checkAuth();
522
+ return createCheckoutSession(input);
523
+ },
524
+ createSetupIntent: async (input) => {
525
+ await checkAuth();
526
+ return createSetupIntent(input);
527
+ },
528
+ createCustomer: async (input) => {
529
+ await checkAuth();
530
+ return createCustomer(input);
531
+ },
532
+ createPortalSession: async (input) => {
533
+ await checkAuth();
534
+ return createPortalSession(input);
535
+ },
536
+ createSubscription: async (input) => {
537
+ await checkAuth();
538
+ return createSubscription(input);
539
+ },
540
+ cancelSubscription: async (input) => {
541
+ await checkAuth();
542
+ return cancelSubscription(input);
543
+ },
544
+ resumeSubscription: async (subscriptionId) => {
545
+ await checkAuth();
546
+ return resumeSubscription(subscriptionId);
547
+ }
548
+ };
549
+ }
550
+ function errorResponse(message, status) {
551
+ return new Response(JSON.stringify({ error: message }), {
552
+ status,
553
+ headers: { "Content-Type": "application/json" }
554
+ });
555
+ }
556
+ function validateRouteOptions(options) {
557
+ if (typeof options?.authorize !== "function") {
558
+ throw new Error("[@stripe-sdk/core] Route helpers require an authorize callback");
559
+ }
560
+ }
561
+ function ensureJsonContentType(request) {
562
+ const contentType = request.headers.get("content-type");
563
+ if (!contentType?.includes("application/json")) {
564
+ return errorResponse("Content-Type must be application/json", 415);
565
+ }
566
+ return null;
567
+ }
568
+ async function parseJsonBody(request) {
569
+ try {
570
+ const data = await request.json();
571
+ return { data };
572
+ } catch {
573
+ return { error: errorResponse("Invalid JSON in request body", 400) };
574
+ }
575
+ }
367
576
  function createPaymentIntentRoute(options) {
577
+ validateRouteOptions(options);
368
578
  return async function POST(request) {
369
579
  try {
370
- let input = await request.json();
371
- if (options?.beforeCreate) {
580
+ const ctError = ensureJsonContentType(request);
581
+ if (ctError) return ctError;
582
+ const authResult = await options.authorize(request);
583
+ if (authResult === false) return errorResponse("Unauthorized", 401);
584
+ const parsed = await parseJsonBody(request);
585
+ if ("error" in parsed) return parsed.error;
586
+ let input = parsed.data;
587
+ if (options.beforeCreate) {
372
588
  input = await options.beforeCreate(input, request);
373
589
  }
374
590
  const result = await createPaymentIntent(input);
375
591
  if (result.error) {
376
- return new Response(JSON.stringify(result.error), {
377
- status: 400,
378
- headers: { "Content-Type": "application/json" }
379
- });
592
+ return errorResponse(result.error.message, 400);
380
593
  }
381
594
  return new Response(
382
595
  JSON.stringify({ clientSecret: result.data.client_secret, id: result.data.id }),
383
596
  { status: 200, headers: { "Content-Type": "application/json" } }
384
597
  );
385
598
  } catch (error) {
386
- return new Response(
387
- JSON.stringify({ error: error instanceof Error ? error.message : "Internal error" }),
388
- { status: 500, headers: { "Content-Type": "application/json" } }
389
- );
599
+ const message = error instanceof Error && error.message === "Unauthorized" ? "Unauthorized" : "Internal error";
600
+ const status = message === "Unauthorized" ? 401 : 500;
601
+ return errorResponse(message, status);
390
602
  }
391
603
  };
392
604
  }
393
605
  function createCheckoutSessionRoute(options) {
606
+ validateRouteOptions(options);
394
607
  return async function POST(request) {
395
608
  try {
396
- let input = await request.json();
397
- if (options?.beforeCreate) {
609
+ const ctError = ensureJsonContentType(request);
610
+ if (ctError) return ctError;
611
+ const authResult = await options.authorize(request);
612
+ if (authResult === false) return errorResponse("Unauthorized", 401);
613
+ const parsed = await parseJsonBody(request);
614
+ if ("error" in parsed) return parsed.error;
615
+ let input = parsed.data;
616
+ if (options.beforeCreate) {
398
617
  input = await options.beforeCreate(input, request);
399
618
  }
400
619
  const result = await createCheckoutSession(input);
401
620
  if (result.error) {
402
- return new Response(JSON.stringify(result.error), {
403
- status: 400,
404
- headers: { "Content-Type": "application/json" }
405
- });
621
+ return errorResponse(result.error.message, 400);
406
622
  }
407
623
  return new Response(
408
624
  JSON.stringify({ sessionId: result.data.id, url: result.data.url }),
409
625
  { status: 200, headers: { "Content-Type": "application/json" } }
410
626
  );
411
627
  } catch (error) {
412
- return new Response(
413
- JSON.stringify({ error: error instanceof Error ? error.message : "Internal error" }),
414
- { status: 500, headers: { "Content-Type": "application/json" } }
415
- );
628
+ const message = error instanceof Error && error.message === "Unauthorized" ? "Unauthorized" : "Internal error";
629
+ const status = message === "Unauthorized" ? 401 : 500;
630
+ return errorResponse(message, status);
416
631
  }
417
632
  };
418
633
  }
419
- function createPortalSessionRoute() {
634
+ function createPortalSessionRoute(options) {
635
+ validateRouteOptions(options);
420
636
  return async function POST(request) {
421
637
  try {
422
- const input = await request.json();
638
+ const ctError = ensureJsonContentType(request);
639
+ if (ctError) return ctError;
640
+ const authResult = await options.authorize(request);
641
+ if (authResult === false) return errorResponse("Unauthorized", 401);
642
+ const parsed = await parseJsonBody(request);
643
+ if ("error" in parsed) return parsed.error;
644
+ let input = parsed.data;
645
+ if (options.beforeCreate) {
646
+ input = await options.beforeCreate(input, request);
647
+ }
423
648
  const result = await createPortalSession(input);
424
649
  if (result.error) {
425
- return new Response(JSON.stringify(result.error), {
426
- status: 400,
427
- headers: { "Content-Type": "application/json" }
428
- });
650
+ return errorResponse(result.error.message, 400);
429
651
  }
430
652
  return new Response(
431
653
  JSON.stringify({ url: result.data.url }),
432
654
  { status: 200, headers: { "Content-Type": "application/json" } }
433
655
  );
434
656
  } catch (error) {
435
- return new Response(
436
- JSON.stringify({ error: error instanceof Error ? error.message : "Internal error" }),
437
- { status: 500, headers: { "Content-Type": "application/json" } }
438
- );
657
+ const message = error instanceof Error && error.message === "Unauthorized" ? "Unauthorized" : "Internal error";
658
+ const status = message === "Unauthorized" ? 401 : 500;
659
+ return errorResponse(message, status);
439
660
  }
440
661
  };
441
662
  }
442
663
 
443
- exports.actions = actions;
664
+ exports.createActions = createActions;
444
665
  exports.createCheckoutSessionRoute = createCheckoutSessionRoute;
445
666
  exports.createNextWebhookHandler = createNextWebhookHandler;
446
667
  exports.createPagesWebhookHandler = createPagesWebhookHandler;
447
668
  exports.createPaymentIntentRoute = createPaymentIntentRoute;
448
669
  exports.createPortalSessionRoute = createPortalSessionRoute;
449
- //# sourceMappingURL=index.js.map
450
- //# sourceMappingURL=index.js.map