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