@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.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import Stripe from 'stripe';
2
- import { createContext, useContext, useMemo, useState, useCallback } from 'react';
2
+ import { createContext, useContext, useMemo, useRef, useState, useCallback } from 'react';
3
3
  import { loadStripe } from '@stripe/stripe-js';
4
4
  import { Elements, useStripe, useElements, LinkAuthenticationElement, PaymentElement } from '@stripe/react-stripe-js';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -8,12 +8,16 @@ import { jsx, jsxs } from 'react/jsx-runtime';
8
8
  var stripeInstance = null;
9
9
  var currentConfig = null;
10
10
  function initStripe(config) {
11
+ if (stripeInstance) {
12
+ console.warn("[@stripe-sdk/core] Stripe is already initialized. Re-initializing with new config.");
13
+ }
11
14
  currentConfig = config;
12
15
  stripeInstance = new Stripe(config.secretKey, {
13
16
  apiVersion: config.apiVersion ?? "2025-01-27.acacia",
17
+ maxNetworkRetries: config.maxNetworkRetries ?? 2,
14
18
  appInfo: config.appInfo ?? {
15
19
  name: "@stripe-sdk/core",
16
- version: "1.0.0"
20
+ version: "1.0.1"
17
21
  }
18
22
  });
19
23
  return stripeInstance;
@@ -32,17 +36,27 @@ function getConfig() {
32
36
  "[@stripe-sdk/core] Stripe not initialized. Call initStripe({ secretKey, publishableKey }) first."
33
37
  );
34
38
  }
35
- return currentConfig;
39
+ const { secretKey: _sk, ...safeConfig } = currentConfig;
40
+ return safeConfig;
36
41
  }
37
42
 
38
43
  // src/utils/errors.ts
44
+ var SAFE_ERROR_MESSAGES = {
45
+ card_declined: "Your card was declined.",
46
+ expired_card: "Your card has expired.",
47
+ incorrect_cvc: "Incorrect security code.",
48
+ processing_error: "An error occurred while processing your card.",
49
+ incorrect_number: "The card number is incorrect.",
50
+ insufficient_funds: "Insufficient funds."
51
+ };
39
52
  function handleStripeError(error) {
40
53
  if (error?.type) {
41
54
  const stripeError = error;
55
+ const safeMessage = stripeError.code && SAFE_ERROR_MESSAGES[stripeError.code] || getSafeMessage(stripeError.type);
42
56
  return {
43
57
  data: null,
44
58
  error: {
45
- message: stripeError.message,
59
+ message: safeMessage,
46
60
  type: stripeError.type,
47
61
  code: stripeError.code,
48
62
  statusCode: stripeError.statusCode
@@ -52,31 +66,147 @@ function handleStripeError(error) {
52
66
  return {
53
67
  data: null,
54
68
  error: {
55
- message: error instanceof Error ? error.message : "An unknown error occurred",
69
+ message: "An unexpected error occurred",
56
70
  type: "sdk_error"
57
71
  }
58
72
  };
59
73
  }
74
+ function getSafeMessage(type) {
75
+ switch (type) {
76
+ case "card_error":
77
+ return "A card error occurred.";
78
+ case "invalid_request_error":
79
+ return "Invalid request. Please check your input.";
80
+ case "authentication_error":
81
+ return "Authentication failed.";
82
+ case "rate_limit_error":
83
+ return "Too many requests. Please try again later.";
84
+ case "api_error":
85
+ return "A payment processing error occurred. Please try again.";
86
+ default:
87
+ return "An unexpected error occurred.";
88
+ }
89
+ }
60
90
  function success(data) {
61
91
  return { data, error: null };
62
92
  }
63
93
 
94
+ // src/utils/validators.ts
95
+ var STRIPE_ID_PREFIXES = {
96
+ customer: "cus_",
97
+ paymentIntent: "pi_",
98
+ paymentMethod: "pm_",
99
+ subscription: "sub_",
100
+ invoice: "in_",
101
+ invoiceItem: "ii_",
102
+ product: "prod_",
103
+ price: "price_",
104
+ coupon: "",
105
+ // coupons can have custom IDs
106
+ promotionCode: "promo_",
107
+ refund: "re_",
108
+ dispute: "dp_",
109
+ account: "acct_",
110
+ transfer: "tr_",
111
+ payout: "po_",
112
+ setupIntent: "seti_",
113
+ session: "cs_",
114
+ paymentLink: "plink_"
115
+ };
116
+ function validateStripeId(id, type) {
117
+ if (!id || typeof id !== "string") {
118
+ throw new Error(`${type} ID is required and must be a non-empty string`);
119
+ }
120
+ const prefix = STRIPE_ID_PREFIXES[type];
121
+ if (prefix && !id.startsWith(prefix)) {
122
+ throw new Error(`Invalid ${type} ID: expected prefix "${prefix}"`);
123
+ }
124
+ }
125
+ function validateAmount(amount, label = "amount") {
126
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
127
+ throw new Error(`${label} must be a finite number`);
128
+ }
129
+ if (!Number.isInteger(amount)) {
130
+ throw new Error(`${label} must be an integer (amount in smallest currency unit)`);
131
+ }
132
+ if (amount < 0) {
133
+ throw new Error(`${label} must not be negative`);
134
+ }
135
+ if (amount > 99999999) {
136
+ throw new Error(`${label} exceeds maximum allowed value (99999999)`);
137
+ }
138
+ }
139
+ function validateCurrency(currency) {
140
+ if (!currency || typeof currency !== "string") {
141
+ throw new Error("Currency is required");
142
+ }
143
+ const normalized = currency.trim().toLowerCase();
144
+ if (!/^[a-z]{3}$/.test(normalized)) {
145
+ throw new Error("Currency must be a valid 3-letter ISO 4217 code");
146
+ }
147
+ return normalized;
148
+ }
149
+ function validateUrl(url, label = "URL") {
150
+ if (!url || typeof url !== "string") {
151
+ throw new Error(`${label} is required`);
152
+ }
153
+ try {
154
+ const parsed = new URL(url);
155
+ if (!["http:", "https:"].includes(parsed.protocol)) {
156
+ throw new Error(`${label} must use http or https protocol`);
157
+ }
158
+ } catch (e) {
159
+ if (e instanceof Error && e.message.includes("protocol")) throw e;
160
+ throw new Error(`${label} must be a valid URL`);
161
+ }
162
+ }
163
+ function validateMetadata(metadata) {
164
+ const keys = Object.keys(metadata);
165
+ if (keys.length > 50) {
166
+ throw new Error("Metadata cannot have more than 50 keys");
167
+ }
168
+ for (const [key, value] of Object.entries(metadata)) {
169
+ if (key.length > 40) {
170
+ throw new Error(`Metadata key "${key}" exceeds 40 characters`);
171
+ }
172
+ if (typeof value !== "string") {
173
+ throw new Error(`Metadata value for key "${key}" must be a string`);
174
+ }
175
+ if (value.length > 500) {
176
+ throw new Error(`Metadata value for key "${key}" exceeds 500 characters`);
177
+ }
178
+ }
179
+ }
180
+ function sanitizeLimit(limit, defaultLimit = 10) {
181
+ if (limit === void 0) return defaultLimit;
182
+ return Math.min(Math.max(Math.floor(limit), 1), 100);
183
+ }
184
+
64
185
  // src/server/payments/index.ts
65
- async function createPaymentIntent(input) {
186
+ async function createPaymentIntent(input, options) {
66
187
  try {
188
+ validateAmount(input.amount);
189
+ const currency = validateCurrency(input.currency);
190
+ if (input.customerId) validateStripeId(input.customerId, "customer");
191
+ if (input.paymentMethodId) validateStripeId(input.paymentMethodId, "paymentMethod");
192
+ if (input.returnUrl) validateUrl(input.returnUrl, "returnUrl");
193
+ if (input.metadata) validateMetadata(input.metadata);
67
194
  const stripe = getStripe();
68
- const paymentIntent = await stripe.paymentIntents.create({
69
- amount: input.amount,
70
- currency: input.currency,
71
- customer: input.customerId,
72
- payment_method: input.paymentMethodId,
73
- metadata: input.metadata,
74
- description: input.description,
75
- receipt_email: input.receiptEmail,
76
- setup_future_usage: input.setupFutureUsage,
77
- automatic_payment_methods: input.automaticPaymentMethods === false || input.paymentMethodId ? void 0 : { enabled: true },
78
- return_url: input.returnUrl
79
- });
195
+ const paymentIntent = await stripe.paymentIntents.create(
196
+ {
197
+ amount: input.amount,
198
+ currency,
199
+ customer: input.customerId,
200
+ payment_method: input.paymentMethodId,
201
+ metadata: input.metadata,
202
+ description: input.description,
203
+ receipt_email: input.receiptEmail,
204
+ setup_future_usage: input.setupFutureUsage,
205
+ automatic_payment_methods: input.automaticPaymentMethods === false || input.paymentMethodId ? void 0 : { enabled: true },
206
+ return_url: input.returnUrl
207
+ },
208
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
209
+ );
80
210
  return success(paymentIntent);
81
211
  } catch (error) {
82
212
  return handleStripeError(error);
@@ -84,6 +214,7 @@ async function createPaymentIntent(input) {
84
214
  }
85
215
  async function retrievePaymentIntent(paymentIntentId) {
86
216
  try {
217
+ validateStripeId(paymentIntentId, "paymentIntent");
87
218
  const stripe = getStripe();
88
219
  const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
89
220
  return success(paymentIntent);
@@ -93,6 +224,9 @@ async function retrievePaymentIntent(paymentIntentId) {
93
224
  }
94
225
  async function confirmPaymentIntent(input) {
95
226
  try {
227
+ validateStripeId(input.paymentIntentId, "paymentIntent");
228
+ if (input.paymentMethodId) validateStripeId(input.paymentMethodId, "paymentMethod");
229
+ if (input.returnUrl) validateUrl(input.returnUrl, "returnUrl");
96
230
  const stripe = getStripe();
97
231
  const paymentIntent = await stripe.paymentIntents.confirm(input.paymentIntentId, {
98
232
  payment_method: input.paymentMethodId,
@@ -105,6 +239,7 @@ async function confirmPaymentIntent(input) {
105
239
  }
106
240
  async function cancelPaymentIntent(paymentIntentId) {
107
241
  try {
242
+ validateStripeId(paymentIntentId, "paymentIntent");
108
243
  const stripe = getStripe();
109
244
  const paymentIntent = await stripe.paymentIntents.cancel(paymentIntentId);
110
245
  return success(paymentIntent);
@@ -114,9 +249,10 @@ async function cancelPaymentIntent(paymentIntentId) {
114
249
  }
115
250
  async function listPaymentIntents(input) {
116
251
  try {
252
+ if (input?.customerId) validateStripeId(input.customerId, "customer");
117
253
  const stripe = getStripe();
118
254
  const paymentIntents = await stripe.paymentIntents.list({
119
- limit: input?.limit ?? 10,
255
+ limit: sanitizeLimit(input?.limit),
120
256
  starting_after: input?.startingAfter,
121
257
  ending_before: input?.endingBefore,
122
258
  customer: input?.customerId
@@ -126,28 +262,35 @@ async function listPaymentIntents(input) {
126
262
  return handleStripeError(error);
127
263
  }
128
264
  }
129
- async function createCheckoutSession(input) {
265
+ async function createCheckoutSession(input, options) {
130
266
  try {
267
+ validateUrl(input.successUrl, "successUrl");
268
+ validateUrl(input.cancelUrl, "cancelUrl");
269
+ if (input.customerId) validateStripeId(input.customerId, "customer");
270
+ if (input.metadata) validateMetadata(input.metadata);
131
271
  const stripe = getStripe();
132
- const session = await stripe.checkout.sessions.create({
133
- mode: input.mode,
134
- line_items: input.lineItems.map((item) => ({
135
- price: item.priceId,
136
- quantity: item.quantity
137
- })),
138
- success_url: input.successUrl,
139
- cancel_url: input.cancelUrl,
140
- customer: input.customerId,
141
- customer_email: input.customerEmail,
142
- metadata: input.metadata,
143
- allow_promotion_codes: input.allowPromotionCodes,
144
- shipping_address_collection: input.shippingAddressCollection ? { allowed_countries: input.shippingAddressCollection.allowedCountries } : void 0,
145
- billing_address_collection: input.billingAddressCollection,
146
- subscription_data: input.trialPeriodDays ? { trial_period_days: input.trialPeriodDays } : void 0,
147
- tax_id_collection: input.taxIdCollection ? { enabled: true } : void 0,
148
- automatic_tax: input.automaticTax ? { enabled: true } : void 0,
149
- locale: input.locale
150
- });
272
+ const session = await stripe.checkout.sessions.create(
273
+ {
274
+ mode: input.mode,
275
+ line_items: input.lineItems.map((item) => ({
276
+ price: item.priceId,
277
+ quantity: item.quantity
278
+ })),
279
+ success_url: input.successUrl,
280
+ cancel_url: input.cancelUrl,
281
+ customer: input.customerId,
282
+ customer_email: input.customerEmail,
283
+ metadata: input.metadata,
284
+ allow_promotion_codes: input.allowPromotionCodes,
285
+ shipping_address_collection: input.shippingAddressCollection ? { allowed_countries: input.shippingAddressCollection.allowedCountries } : void 0,
286
+ billing_address_collection: input.billingAddressCollection,
287
+ subscription_data: input.trialPeriodDays ? { trial_period_days: input.trialPeriodDays } : void 0,
288
+ tax_id_collection: input.taxIdCollection ? { enabled: true } : void 0,
289
+ automatic_tax: input.automaticTax ? { enabled: true } : void 0,
290
+ locale: input.locale
291
+ },
292
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
293
+ );
151
294
  return success(session);
152
295
  } catch (error) {
153
296
  return handleStripeError(error);
@@ -155,6 +298,7 @@ async function createCheckoutSession(input) {
155
298
  }
156
299
  async function retrieveCheckoutSession(sessionId) {
157
300
  try {
301
+ validateStripeId(sessionId, "session");
158
302
  const stripe = getStripe();
159
303
  const session = await stripe.checkout.sessions.retrieve(sessionId, {
160
304
  expand: ["line_items", "payment_intent", "subscription"]
@@ -168,7 +312,7 @@ async function listCheckoutSessions(input) {
168
312
  try {
169
313
  const stripe = getStripe();
170
314
  const sessions = await stripe.checkout.sessions.list({
171
- limit: input?.limit ?? 10,
315
+ limit: sanitizeLimit(input?.limit),
172
316
  starting_after: input?.startingAfter,
173
317
  ending_before: input?.endingBefore
174
318
  });
@@ -177,29 +321,36 @@ async function listCheckoutSessions(input) {
177
321
  return handleStripeError(error);
178
322
  }
179
323
  }
180
- async function createPaymentLink(input) {
324
+ async function createPaymentLink(input, options) {
181
325
  try {
326
+ if (input.afterCompletion?.redirectUrl) {
327
+ validateUrl(input.afterCompletion.redirectUrl, "afterCompletion.redirectUrl");
328
+ }
329
+ if (input.metadata) validateMetadata(input.metadata);
182
330
  const stripe = getStripe();
183
- const paymentLink = await stripe.paymentLinks.create({
184
- line_items: input.lineItems.map((item) => ({
185
- price: item.priceId,
186
- quantity: item.quantity,
187
- adjustable_quantity: item.adjustableQuantity ? {
188
- enabled: item.adjustableQuantity.enabled,
189
- minimum: item.adjustableQuantity.minimum,
190
- maximum: item.adjustableQuantity.maximum
191
- } : void 0
192
- })),
193
- metadata: input.metadata,
194
- after_completion: input.afterCompletion ? {
195
- type: input.afterCompletion.type,
196
- redirect: input.afterCompletion.redirectUrl ? { url: input.afterCompletion.redirectUrl } : void 0
197
- } : void 0,
198
- allow_promotion_codes: input.allowPromotionCodes,
199
- automatic_tax: input.automaticTax ? { enabled: true } : void 0,
200
- billing_address_collection: input.billingAddressCollection,
201
- shipping_address_collection: input.shippingAddressCollection ? { allowed_countries: input.shippingAddressCollection.allowedCountries } : void 0
202
- });
331
+ const paymentLink = await stripe.paymentLinks.create(
332
+ {
333
+ line_items: input.lineItems.map((item) => ({
334
+ price: item.priceId,
335
+ quantity: item.quantity,
336
+ adjustable_quantity: item.adjustableQuantity ? {
337
+ enabled: item.adjustableQuantity.enabled,
338
+ minimum: item.adjustableQuantity.minimum,
339
+ maximum: item.adjustableQuantity.maximum
340
+ } : void 0
341
+ })),
342
+ metadata: input.metadata,
343
+ after_completion: input.afterCompletion ? {
344
+ type: input.afterCompletion.type,
345
+ redirect: input.afterCompletion.redirectUrl ? { url: input.afterCompletion.redirectUrl } : void 0
346
+ } : void 0,
347
+ allow_promotion_codes: input.allowPromotionCodes,
348
+ automatic_tax: input.automaticTax ? { enabled: true } : void 0,
349
+ billing_address_collection: input.billingAddressCollection,
350
+ shipping_address_collection: input.shippingAddressCollection ? { allowed_countries: input.shippingAddressCollection.allowedCountries } : void 0
351
+ },
352
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
353
+ );
203
354
  return success(paymentLink);
204
355
  } catch (error) {
205
356
  return handleStripeError(error);
@@ -207,6 +358,7 @@ async function createPaymentLink(input) {
207
358
  }
208
359
  async function retrievePaymentLink(paymentLinkId) {
209
360
  try {
361
+ validateStripeId(paymentLinkId, "paymentLink");
210
362
  const stripe = getStripe();
211
363
  const paymentLink = await stripe.paymentLinks.retrieve(paymentLinkId);
212
364
  return success(paymentLink);
@@ -214,15 +366,20 @@ async function retrievePaymentLink(paymentLinkId) {
214
366
  return handleStripeError(error);
215
367
  }
216
368
  }
217
- async function createSetupIntent(input) {
369
+ async function createSetupIntent(input, options) {
218
370
  try {
371
+ if (input.customerId) validateStripeId(input.customerId, "customer");
372
+ if (input.metadata) validateMetadata(input.metadata);
219
373
  const stripe = getStripe();
220
- const setupIntent = await stripe.setupIntents.create({
221
- customer: input.customerId,
222
- payment_method_types: input.paymentMethodTypes,
223
- usage: input.usage,
224
- metadata: input.metadata
225
- });
374
+ const setupIntent = await stripe.setupIntents.create(
375
+ {
376
+ customer: input.customerId,
377
+ payment_method_types: input.paymentMethodTypes,
378
+ usage: input.usage,
379
+ metadata: input.metadata
380
+ },
381
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
382
+ );
226
383
  return success(setupIntent);
227
384
  } catch (error) {
228
385
  return handleStripeError(error);
@@ -230,6 +387,7 @@ async function createSetupIntent(input) {
230
387
  }
231
388
  async function retrieveSetupIntent(setupIntentId) {
232
389
  try {
390
+ validateStripeId(setupIntentId, "setupIntent");
233
391
  const stripe = getStripe();
234
392
  const setupIntent = await stripe.setupIntents.retrieve(setupIntentId);
235
393
  return success(setupIntent);
@@ -239,6 +397,7 @@ async function retrieveSetupIntent(setupIntentId) {
239
397
  }
240
398
  async function listPaymentMethods(customerId, type) {
241
399
  try {
400
+ validateStripeId(customerId, "customer");
242
401
  const stripe = getStripe();
243
402
  const paymentMethods = await stripe.paymentMethods.list({
244
403
  customer: customerId,
@@ -251,6 +410,8 @@ async function listPaymentMethods(customerId, type) {
251
410
  }
252
411
  async function attachPaymentMethod(paymentMethodId, customerId) {
253
412
  try {
413
+ validateStripeId(paymentMethodId, "paymentMethod");
414
+ validateStripeId(customerId, "customer");
254
415
  const stripe = getStripe();
255
416
  const paymentMethod = await stripe.paymentMethods.attach(paymentMethodId, {
256
417
  customer: customerId
@@ -262,6 +423,7 @@ async function attachPaymentMethod(paymentMethodId, customerId) {
262
423
  }
263
424
  async function detachPaymentMethod(paymentMethodId) {
264
425
  try {
426
+ validateStripeId(paymentMethodId, "paymentMethod");
265
427
  const stripe = getStripe();
266
428
  const paymentMethod = await stripe.paymentMethods.detach(paymentMethodId);
267
429
  return success(paymentMethod);
@@ -271,26 +433,31 @@ async function detachPaymentMethod(paymentMethodId) {
271
433
  }
272
434
 
273
435
  // src/server/customers/index.ts
274
- async function createCustomer(input) {
436
+ async function createCustomer(input, options) {
275
437
  try {
438
+ if (input.paymentMethodId) validateStripeId(input.paymentMethodId, "paymentMethod");
439
+ if (input.metadata) validateMetadata(input.metadata);
276
440
  const stripe = getStripe();
277
- const customer = await stripe.customers.create({
278
- email: input.email,
279
- name: input.name,
280
- phone: input.phone,
281
- description: input.description,
282
- metadata: input.metadata,
283
- payment_method: input.paymentMethodId,
284
- invoice_settings: input.paymentMethodId ? { default_payment_method: input.paymentMethodId } : void 0,
285
- address: input.address ? {
286
- line1: input.address.line1,
287
- line2: input.address.line2,
288
- city: input.address.city,
289
- state: input.address.state,
290
- postal_code: input.address.postalCode,
291
- country: input.address.country
292
- } : void 0
293
- });
441
+ const customer = await stripe.customers.create(
442
+ {
443
+ email: input.email,
444
+ name: input.name,
445
+ phone: input.phone,
446
+ description: input.description,
447
+ metadata: input.metadata,
448
+ payment_method: input.paymentMethodId,
449
+ invoice_settings: input.paymentMethodId ? { default_payment_method: input.paymentMethodId } : void 0,
450
+ address: input.address ? {
451
+ line1: input.address.line1,
452
+ line2: input.address.line2,
453
+ city: input.address.city,
454
+ state: input.address.state,
455
+ postal_code: input.address.postalCode,
456
+ country: input.address.country
457
+ } : void 0
458
+ },
459
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
460
+ );
294
461
  return success(customer);
295
462
  } catch (error) {
296
463
  return handleStripeError(error);
@@ -298,6 +465,7 @@ async function createCustomer(input) {
298
465
  }
299
466
  async function retrieveCustomer(customerId) {
300
467
  try {
468
+ validateStripeId(customerId, "customer");
301
469
  const stripe = getStripe();
302
470
  const customer = await stripe.customers.retrieve(customerId, {
303
471
  expand: ["subscriptions", "sources"]
@@ -315,6 +483,9 @@ async function retrieveCustomer(customerId) {
315
483
  }
316
484
  async function updateCustomer(input) {
317
485
  try {
486
+ validateStripeId(input.customerId, "customer");
487
+ if (input.defaultPaymentMethodId) validateStripeId(input.defaultPaymentMethodId, "paymentMethod");
488
+ if (input.metadata) validateMetadata(input.metadata);
318
489
  const stripe = getStripe();
319
490
  const customer = await stripe.customers.update(input.customerId, {
320
491
  email: input.email,
@@ -339,6 +510,7 @@ async function updateCustomer(input) {
339
510
  }
340
511
  async function deleteCustomer(customerId) {
341
512
  try {
513
+ validateStripeId(customerId, "customer");
342
514
  const stripe = getStripe();
343
515
  const deleted = await stripe.customers.del(customerId);
344
516
  return success(deleted);
@@ -350,7 +522,7 @@ async function listCustomers(input) {
350
522
  try {
351
523
  const stripe = getStripe();
352
524
  const customers = await stripe.customers.list({
353
- limit: input?.limit ?? 10,
525
+ limit: sanitizeLimit(input?.limit),
354
526
  starting_after: input?.startingAfter,
355
527
  ending_before: input?.endingBefore,
356
528
  email: input?.email
@@ -360,26 +532,42 @@ async function listCustomers(input) {
360
532
  return handleStripeError(error);
361
533
  }
362
534
  }
363
- async function searchCustomers(query, limit) {
535
+ async function searchCustomers(input) {
364
536
  try {
537
+ const clauses = [];
538
+ if (input.email) clauses.push(`email:"${input.email.replace(/"/g, "")}"`);
539
+ if (input.name) clauses.push(`name:"${input.name.replace(/"/g, "")}"`);
540
+ if (input.phone) clauses.push(`phone:"${input.phone.replace(/"/g, "")}"`);
541
+ if (clauses.length === 0) {
542
+ return {
543
+ data: null,
544
+ error: { message: "At least one search field (email, name, phone) is required", type: "invalid_request_error" }
545
+ };
546
+ }
547
+ const query = clauses.join(" AND ");
365
548
  const stripe = getStripe();
366
549
  const customers = await stripe.customers.search({
367
550
  query,
368
- limit: limit ?? 10
551
+ limit: sanitizeLimit(input.limit)
369
552
  });
370
553
  return success(customers);
371
554
  } catch (error) {
372
555
  return handleStripeError(error);
373
556
  }
374
557
  }
375
- async function createPortalSession(input) {
558
+ async function createPortalSession(input, options) {
376
559
  try {
560
+ validateStripeId(input.customerId, "customer");
561
+ if (input.returnUrl) validateUrl(input.returnUrl, "returnUrl");
377
562
  const stripe = getStripe();
378
- const session = await stripe.billingPortal.sessions.create({
379
- customer: input.customerId,
380
- return_url: input.returnUrl,
381
- configuration: input.configuration
382
- });
563
+ const session = await stripe.billingPortal.sessions.create(
564
+ {
565
+ customer: input.customerId,
566
+ return_url: input.returnUrl,
567
+ configuration: input.configuration
568
+ },
569
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
570
+ );
383
571
  return success(session);
384
572
  } catch (error) {
385
573
  return handleStripeError(error);
@@ -387,23 +575,28 @@ async function createPortalSession(input) {
387
575
  }
388
576
 
389
577
  // src/server/subscriptions/index.ts
390
- async function createSubscription(input) {
578
+ async function createSubscription(input, options) {
391
579
  try {
580
+ validateStripeId(input.customerId, "customer");
581
+ if (input.metadata) validateMetadata(input.metadata);
392
582
  const stripe = getStripe();
393
583
  const items = input.items ? input.items.map((item) => ({ price: item.priceId, quantity: item.quantity })) : [{ price: input.priceId, quantity: input.quantity ?? 1 }];
394
- const subscription = await stripe.subscriptions.create({
395
- customer: input.customerId,
396
- items,
397
- metadata: input.metadata,
398
- trial_period_days: input.trialPeriodDays,
399
- coupon: input.couponId,
400
- promotion_code: input.promotionCodeId,
401
- payment_behavior: input.paymentBehavior ?? "default_incomplete",
402
- cancel_at_period_end: input.cancelAtPeriodEnd,
403
- billing_cycle_anchor: input.billingCycleAnchor,
404
- proration_behavior: input.prorationBehavior,
405
- expand: ["latest_invoice.payment_intent"]
406
- });
584
+ const subscription = await stripe.subscriptions.create(
585
+ {
586
+ customer: input.customerId,
587
+ items,
588
+ metadata: input.metadata,
589
+ trial_period_days: input.trialPeriodDays,
590
+ coupon: input.couponId,
591
+ promotion_code: input.promotionCodeId,
592
+ payment_behavior: input.paymentBehavior ?? "default_incomplete",
593
+ cancel_at_period_end: input.cancelAtPeriodEnd,
594
+ billing_cycle_anchor: input.billingCycleAnchor,
595
+ proration_behavior: input.prorationBehavior,
596
+ expand: ["latest_invoice.payment_intent"]
597
+ },
598
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
599
+ );
407
600
  return success(subscription);
408
601
  } catch (error) {
409
602
  return handleStripeError(error);
@@ -411,6 +604,7 @@ async function createSubscription(input) {
411
604
  }
412
605
  async function retrieveSubscription(subscriptionId) {
413
606
  try {
607
+ validateStripeId(subscriptionId, "subscription");
414
608
  const stripe = getStripe();
415
609
  const subscription = await stripe.subscriptions.retrieve(subscriptionId, {
416
610
  expand: ["latest_invoice.payment_intent", "default_payment_method"]
@@ -422,6 +616,8 @@ async function retrieveSubscription(subscriptionId) {
422
616
  }
423
617
  async function updateSubscription(input) {
424
618
  try {
619
+ validateStripeId(input.subscriptionId, "subscription");
620
+ if (input.metadata) validateMetadata(input.metadata);
425
621
  const stripe = getStripe();
426
622
  const params = {
427
623
  metadata: input.metadata,
@@ -444,6 +640,7 @@ async function updateSubscription(input) {
444
640
  }
445
641
  async function cancelSubscription(input) {
446
642
  try {
643
+ validateStripeId(input.subscriptionId, "subscription");
447
644
  const stripe = getStripe();
448
645
  if (input.cancelAtPeriodEnd) {
449
646
  const subscription2 = await stripe.subscriptions.update(input.subscriptionId, {
@@ -468,6 +665,7 @@ async function cancelSubscription(input) {
468
665
  }
469
666
  async function resumeSubscription(subscriptionId) {
470
667
  try {
668
+ validateStripeId(subscriptionId, "subscription");
471
669
  const stripe = getStripe();
472
670
  const subscription = await stripe.subscriptions.update(subscriptionId, {
473
671
  cancel_at_period_end: false
@@ -479,9 +677,10 @@ async function resumeSubscription(subscriptionId) {
479
677
  }
480
678
  async function listSubscriptions(input) {
481
679
  try {
680
+ if (input?.customerId) validateStripeId(input.customerId, "customer");
482
681
  const stripe = getStripe();
483
682
  const subscriptions = await stripe.subscriptions.list({
484
- limit: input?.limit ?? 10,
683
+ limit: sanitizeLimit(input?.limit),
485
684
  starting_after: input?.startingAfter,
486
685
  ending_before: input?.endingBefore,
487
686
  customer: input?.customerId,
@@ -494,24 +693,32 @@ async function listSubscriptions(input) {
494
693
  }
495
694
 
496
695
  // src/server/products/index.ts
497
- async function createProduct(input) {
696
+ async function createProduct(input, options) {
498
697
  try {
698
+ if (input.metadata) validateMetadata(input.metadata);
699
+ if (input.defaultPriceData) {
700
+ validateAmount(input.defaultPriceData.unitAmount, "unitAmount");
701
+ validateCurrency(input.defaultPriceData.currency);
702
+ }
499
703
  const stripe = getStripe();
500
- const product = await stripe.products.create({
501
- name: input.name,
502
- description: input.description,
503
- images: input.images,
504
- metadata: input.metadata,
505
- active: input.active,
506
- default_price_data: input.defaultPriceData ? {
507
- unit_amount: input.defaultPriceData.unitAmount,
508
- currency: input.defaultPriceData.currency,
509
- recurring: input.defaultPriceData.recurring ? {
510
- interval: input.defaultPriceData.recurring.interval,
511
- interval_count: input.defaultPriceData.recurring.intervalCount
704
+ const product = await stripe.products.create(
705
+ {
706
+ name: input.name,
707
+ description: input.description,
708
+ images: input.images,
709
+ metadata: input.metadata,
710
+ active: input.active,
711
+ default_price_data: input.defaultPriceData ? {
712
+ unit_amount: input.defaultPriceData.unitAmount,
713
+ currency: input.defaultPriceData.currency,
714
+ recurring: input.defaultPriceData.recurring ? {
715
+ interval: input.defaultPriceData.recurring.interval,
716
+ interval_count: input.defaultPriceData.recurring.intervalCount
717
+ } : void 0
512
718
  } : void 0
513
- } : void 0
514
- });
719
+ },
720
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
721
+ );
515
722
  return success(product);
516
723
  } catch (error) {
517
724
  return handleStripeError(error);
@@ -519,6 +726,7 @@ async function createProduct(input) {
519
726
  }
520
727
  async function retrieveProduct(productId) {
521
728
  try {
729
+ validateStripeId(productId, "product");
522
730
  const stripe = getStripe();
523
731
  const product = await stripe.products.retrieve(productId);
524
732
  return success(product);
@@ -528,6 +736,8 @@ async function retrieveProduct(productId) {
528
736
  }
529
737
  async function updateProduct(input) {
530
738
  try {
739
+ validateStripeId(input.productId, "product");
740
+ if (input.metadata) validateMetadata(input.metadata);
531
741
  const stripe = getStripe();
532
742
  const product = await stripe.products.update(input.productId, {
533
743
  name: input.name,
@@ -543,6 +753,7 @@ async function updateProduct(input) {
543
753
  }
544
754
  async function archiveProduct(productId) {
545
755
  try {
756
+ validateStripeId(productId, "product");
546
757
  const stripe = getStripe();
547
758
  const product = await stripe.products.update(productId, { active: false });
548
759
  return success(product);
@@ -554,7 +765,7 @@ async function listProducts(input) {
554
765
  try {
555
766
  const stripe = getStripe();
556
767
  const products = await stripe.products.list({
557
- limit: input?.limit ?? 10,
768
+ limit: sanitizeLimit(input?.limit),
558
769
  starting_after: input?.startingAfter,
559
770
  ending_before: input?.endingBefore,
560
771
  active: input?.active
@@ -564,8 +775,12 @@ async function listProducts(input) {
564
775
  return handleStripeError(error);
565
776
  }
566
777
  }
567
- async function createPrice(input) {
778
+ async function createPrice(input, options) {
568
779
  try {
780
+ validateStripeId(input.productId, "product");
781
+ validateCurrency(input.currency);
782
+ if (input.unitAmount !== void 0) validateAmount(input.unitAmount, "unitAmount");
783
+ if (input.metadata) validateMetadata(input.metadata);
569
784
  const stripe = getStripe();
570
785
  const params = {
571
786
  product: input.productId,
@@ -593,7 +808,10 @@ async function createPrice(input) {
593
808
  } else {
594
809
  params.unit_amount = input.unitAmount;
595
810
  }
596
- const price = await stripe.prices.create(params);
811
+ const price = await stripe.prices.create(
812
+ params,
813
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
814
+ );
597
815
  return success(price);
598
816
  } catch (error) {
599
817
  return handleStripeError(error);
@@ -601,6 +819,7 @@ async function createPrice(input) {
601
819
  }
602
820
  async function retrievePrice(priceId) {
603
821
  try {
822
+ validateStripeId(priceId, "price");
604
823
  const stripe = getStripe();
605
824
  const price = await stripe.prices.retrieve(priceId);
606
825
  return success(price);
@@ -610,9 +829,10 @@ async function retrievePrice(priceId) {
610
829
  }
611
830
  async function listPrices(input) {
612
831
  try {
832
+ if (input?.productId) validateStripeId(input.productId, "product");
613
833
  const stripe = getStripe();
614
834
  const prices = await stripe.prices.list({
615
- limit: input?.limit ?? 10,
835
+ limit: sanitizeLimit(input?.limit),
616
836
  starting_after: input?.startingAfter,
617
837
  ending_before: input?.endingBefore,
618
838
  product: input?.productId,
@@ -626,6 +846,7 @@ async function listPrices(input) {
626
846
  }
627
847
  async function archivePrice(priceId) {
628
848
  try {
849
+ validateStripeId(priceId, "price");
629
850
  const stripe = getStripe();
630
851
  const price = await stripe.prices.update(priceId, { active: false });
631
852
  return success(price);
@@ -635,17 +856,22 @@ async function archivePrice(priceId) {
635
856
  }
636
857
 
637
858
  // src/server/invoices/index.ts
638
- async function createInvoice(input) {
859
+ async function createInvoice(input, options) {
639
860
  try {
861
+ validateStripeId(input.customerId, "customer");
862
+ if (input.metadata) validateMetadata(input.metadata);
640
863
  const stripe = getStripe();
641
- const invoice = await stripe.invoices.create({
642
- customer: input.customerId,
643
- collection_method: input.collectionMethod ?? "charge_automatically",
644
- days_until_due: input.daysUntilDue,
645
- metadata: input.metadata,
646
- description: input.description,
647
- auto_advance: input.autoAdvance
648
- });
864
+ const invoice = await stripe.invoices.create(
865
+ {
866
+ customer: input.customerId,
867
+ collection_method: input.collectionMethod ?? "charge_automatically",
868
+ days_until_due: input.daysUntilDue,
869
+ metadata: input.metadata,
870
+ description: input.description,
871
+ auto_advance: input.autoAdvance
872
+ },
873
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
874
+ );
649
875
  return success(invoice);
650
876
  } catch (error) {
651
877
  return handleStripeError(error);
@@ -653,6 +879,7 @@ async function createInvoice(input) {
653
879
  }
654
880
  async function retrieveInvoice(invoiceId) {
655
881
  try {
882
+ validateStripeId(invoiceId, "invoice");
656
883
  const stripe = getStripe();
657
884
  const invoice = await stripe.invoices.retrieve(invoiceId);
658
885
  return success(invoice);
@@ -662,6 +889,7 @@ async function retrieveInvoice(invoiceId) {
662
889
  }
663
890
  async function finalizeInvoice(invoiceId) {
664
891
  try {
892
+ validateStripeId(invoiceId, "invoice");
665
893
  const stripe = getStripe();
666
894
  const invoice = await stripe.invoices.finalizeInvoice(invoiceId);
667
895
  return success(invoice);
@@ -671,6 +899,7 @@ async function finalizeInvoice(invoiceId) {
671
899
  }
672
900
  async function sendInvoice(invoiceId) {
673
901
  try {
902
+ validateStripeId(invoiceId, "invoice");
674
903
  const stripe = getStripe();
675
904
  const invoice = await stripe.invoices.sendInvoice(invoiceId);
676
905
  return success(invoice);
@@ -680,6 +909,7 @@ async function sendInvoice(invoiceId) {
680
909
  }
681
910
  async function payInvoice(invoiceId) {
682
911
  try {
912
+ validateStripeId(invoiceId, "invoice");
683
913
  const stripe = getStripe();
684
914
  const invoice = await stripe.invoices.pay(invoiceId);
685
915
  return success(invoice);
@@ -689,6 +919,7 @@ async function payInvoice(invoiceId) {
689
919
  }
690
920
  async function voidInvoice(invoiceId) {
691
921
  try {
922
+ validateStripeId(invoiceId, "invoice");
692
923
  const stripe = getStripe();
693
924
  const invoice = await stripe.invoices.voidInvoice(invoiceId);
694
925
  return success(invoice);
@@ -698,9 +929,10 @@ async function voidInvoice(invoiceId) {
698
929
  }
699
930
  async function listInvoices(input) {
700
931
  try {
932
+ if (input?.customerId) validateStripeId(input.customerId, "customer");
701
933
  const stripe = getStripe();
702
934
  const invoices = await stripe.invoices.list({
703
- limit: input?.limit ?? 10,
935
+ limit: sanitizeLimit(input?.limit),
704
936
  starting_after: input?.startingAfter,
705
937
  ending_before: input?.endingBefore,
706
938
  customer: input?.customerId,
@@ -713,6 +945,8 @@ async function listInvoices(input) {
713
945
  }
714
946
  async function getUpcomingInvoice(customerId, subscriptionId) {
715
947
  try {
948
+ validateStripeId(customerId, "customer");
949
+ if (subscriptionId) validateStripeId(subscriptionId, "subscription");
716
950
  const stripe = getStripe();
717
951
  const invoice = await stripe.invoices.retrieveUpcoming({
718
952
  customer: customerId,
@@ -723,19 +957,28 @@ async function getUpcomingInvoice(customerId, subscriptionId) {
723
957
  return handleStripeError(error);
724
958
  }
725
959
  }
726
- async function createInvoiceItem(input) {
960
+ async function createInvoiceItem(input, options) {
727
961
  try {
962
+ validateStripeId(input.customerId, "customer");
963
+ if (input.invoiceId) validateStripeId(input.invoiceId, "invoice");
964
+ if (input.priceId) validateStripeId(input.priceId, "price");
965
+ if (input.amount !== void 0) validateAmount(input.amount, "invoice item amount");
966
+ if (input.currency) validateCurrency(input.currency);
967
+ if (input.metadata) validateMetadata(input.metadata);
728
968
  const stripe = getStripe();
729
- const invoiceItem = await stripe.invoiceItems.create({
730
- customer: input.customerId,
731
- invoice: input.invoiceId,
732
- price: input.priceId,
733
- amount: input.amount,
734
- currency: input.currency,
735
- description: input.description,
736
- quantity: input.quantity,
737
- metadata: input.metadata
738
- });
969
+ const invoiceItem = await stripe.invoiceItems.create(
970
+ {
971
+ customer: input.customerId,
972
+ invoice: input.invoiceId,
973
+ price: input.priceId,
974
+ amount: input.amount,
975
+ currency: input.currency,
976
+ description: input.description,
977
+ quantity: input.quantity,
978
+ metadata: input.metadata
979
+ },
980
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
981
+ );
739
982
  return success(invoiceItem);
740
983
  } catch (error) {
741
984
  return handleStripeError(error);
@@ -743,16 +986,22 @@ async function createInvoiceItem(input) {
743
986
  }
744
987
 
745
988
  // src/server/refunds/index.ts
746
- async function createRefund(input) {
989
+ async function createRefund(input, options) {
747
990
  try {
991
+ if (input.paymentIntentId) validateStripeId(input.paymentIntentId, "paymentIntent");
992
+ if (input.amount !== void 0) validateAmount(input.amount, "refund amount");
993
+ if (input.metadata) validateMetadata(input.metadata);
748
994
  const stripe = getStripe();
749
- const refund = await stripe.refunds.create({
750
- payment_intent: input.paymentIntentId,
751
- charge: input.chargeId,
752
- amount: input.amount,
753
- reason: input.reason,
754
- metadata: input.metadata
755
- });
995
+ const refund = await stripe.refunds.create(
996
+ {
997
+ payment_intent: input.paymentIntentId,
998
+ charge: input.chargeId,
999
+ amount: input.amount,
1000
+ reason: input.reason,
1001
+ metadata: input.metadata
1002
+ },
1003
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
1004
+ );
756
1005
  return success(refund);
757
1006
  } catch (error) {
758
1007
  return handleStripeError(error);
@@ -760,6 +1009,7 @@ async function createRefund(input) {
760
1009
  }
761
1010
  async function retrieveRefund(refundId) {
762
1011
  try {
1012
+ validateStripeId(refundId, "refund");
763
1013
  const stripe = getStripe();
764
1014
  const refund = await stripe.refunds.retrieve(refundId);
765
1015
  return success(refund);
@@ -769,9 +1019,10 @@ async function retrieveRefund(refundId) {
769
1019
  }
770
1020
  async function listRefunds(input) {
771
1021
  try {
1022
+ if (input?.paymentIntentId) validateStripeId(input.paymentIntentId, "paymentIntent");
772
1023
  const stripe = getStripe();
773
1024
  const refunds = await stripe.refunds.list({
774
- limit: input?.limit ?? 10,
1025
+ limit: sanitizeLimit(input?.limit),
775
1026
  starting_after: input?.startingAfter,
776
1027
  ending_before: input?.endingBefore,
777
1028
  payment_intent: input?.paymentIntentId,
@@ -784,6 +1035,7 @@ async function listRefunds(input) {
784
1035
  }
785
1036
  async function retrieveDispute(disputeId) {
786
1037
  try {
1038
+ validateStripeId(disputeId, "dispute");
787
1039
  const stripe = getStripe();
788
1040
  const dispute = await stripe.disputes.retrieve(disputeId);
789
1041
  return success(dispute);
@@ -793,6 +1045,8 @@ async function retrieveDispute(disputeId) {
793
1045
  }
794
1046
  async function updateDispute(input) {
795
1047
  try {
1048
+ validateStripeId(input.disputeId, "dispute");
1049
+ if (input.metadata) validateMetadata(input.metadata);
796
1050
  const stripe = getStripe();
797
1051
  const dispute = await stripe.disputes.update(input.disputeId, {
798
1052
  evidence: input.evidence ? {
@@ -814,6 +1068,7 @@ async function updateDispute(input) {
814
1068
  }
815
1069
  async function closeDispute(disputeId) {
816
1070
  try {
1071
+ validateStripeId(disputeId, "dispute");
817
1072
  const stripe = getStripe();
818
1073
  const dispute = await stripe.disputes.close(disputeId);
819
1074
  return success(dispute);
@@ -825,7 +1080,7 @@ async function listDisputes(input) {
825
1080
  try {
826
1081
  const stripe = getStripe();
827
1082
  const disputes = await stripe.disputes.list({
828
- limit: input?.limit ?? 10,
1083
+ limit: sanitizeLimit(input?.limit),
829
1084
  starting_after: input?.startingAfter,
830
1085
  ending_before: input?.endingBefore
831
1086
  });
@@ -836,17 +1091,21 @@ async function listDisputes(input) {
836
1091
  }
837
1092
 
838
1093
  // src/server/connect/index.ts
839
- async function createConnectAccount(input) {
1094
+ async function createConnectAccount(input, options) {
840
1095
  try {
1096
+ if (input.metadata) validateMetadata(input.metadata);
841
1097
  const stripe = getStripe();
842
- const account = await stripe.accounts.create({
843
- type: input.type,
844
- country: input.country,
845
- email: input.email,
846
- capabilities: input.capabilities,
847
- business_type: input.businessType,
848
- metadata: input.metadata
849
- });
1098
+ const account = await stripe.accounts.create(
1099
+ {
1100
+ type: input.type,
1101
+ country: input.country,
1102
+ email: input.email,
1103
+ capabilities: input.capabilities,
1104
+ business_type: input.businessType,
1105
+ metadata: input.metadata
1106
+ },
1107
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
1108
+ );
850
1109
  return success(account);
851
1110
  } catch (error) {
852
1111
  return handleStripeError(error);
@@ -854,6 +1113,7 @@ async function createConnectAccount(input) {
854
1113
  }
855
1114
  async function retrieveConnectAccount(accountId) {
856
1115
  try {
1116
+ validateStripeId(accountId, "account");
857
1117
  const stripe = getStripe();
858
1118
  const account = await stripe.accounts.retrieve(accountId);
859
1119
  return success(account);
@@ -863,6 +1123,7 @@ async function retrieveConnectAccount(accountId) {
863
1123
  }
864
1124
  async function deleteConnectAccount(accountId) {
865
1125
  try {
1126
+ validateStripeId(accountId, "account");
866
1127
  const stripe = getStripe();
867
1128
  const deleted = await stripe.accounts.del(accountId);
868
1129
  return success(deleted);
@@ -874,7 +1135,7 @@ async function listConnectAccounts(input) {
874
1135
  try {
875
1136
  const stripe = getStripe();
876
1137
  const accounts = await stripe.accounts.list({
877
- limit: input?.limit ?? 10,
1138
+ limit: sanitizeLimit(input?.limit),
878
1139
  starting_after: input?.startingAfter,
879
1140
  ending_before: input?.endingBefore
880
1141
  });
@@ -885,6 +1146,9 @@ async function listConnectAccounts(input) {
885
1146
  }
886
1147
  async function createAccountLink(input) {
887
1148
  try {
1149
+ validateStripeId(input.accountId, "account");
1150
+ validateUrl(input.refreshUrl, "refreshUrl");
1151
+ validateUrl(input.returnUrl, "returnUrl");
888
1152
  const stripe = getStripe();
889
1153
  const accountLink = await stripe.accountLinks.create({
890
1154
  account: input.accountId,
@@ -897,17 +1161,24 @@ async function createAccountLink(input) {
897
1161
  return handleStripeError(error);
898
1162
  }
899
1163
  }
900
- async function createTransfer(input) {
1164
+ async function createTransfer(input, options) {
901
1165
  try {
1166
+ validateAmount(input.amount, "transfer amount");
1167
+ validateCurrency(input.currency);
1168
+ validateStripeId(input.destinationAccountId, "account");
1169
+ if (input.metadata) validateMetadata(input.metadata);
902
1170
  const stripe = getStripe();
903
- const transfer = await stripe.transfers.create({
904
- amount: input.amount,
905
- currency: input.currency,
906
- destination: input.destinationAccountId,
907
- description: input.description,
908
- metadata: input.metadata,
909
- source_transaction: input.sourceTransaction
910
- });
1171
+ const transfer = await stripe.transfers.create(
1172
+ {
1173
+ amount: input.amount,
1174
+ currency: input.currency,
1175
+ destination: input.destinationAccountId,
1176
+ description: input.description,
1177
+ metadata: input.metadata,
1178
+ source_transaction: input.sourceTransaction
1179
+ },
1180
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
1181
+ );
911
1182
  return success(transfer);
912
1183
  } catch (error) {
913
1184
  return handleStripeError(error);
@@ -915,9 +1186,10 @@ async function createTransfer(input) {
915
1186
  }
916
1187
  async function listTransfers(input) {
917
1188
  try {
1189
+ if (input?.destinationAccountId) validateStripeId(input.destinationAccountId, "account");
918
1190
  const stripe = getStripe();
919
1191
  const transfers = await stripe.transfers.list({
920
- limit: input?.limit ?? 10,
1192
+ limit: sanitizeLimit(input?.limit),
921
1193
  starting_after: input?.startingAfter,
922
1194
  ending_before: input?.endingBefore,
923
1195
  destination: input?.destinationAccountId
@@ -927,14 +1199,20 @@ async function listTransfers(input) {
927
1199
  return handleStripeError(error);
928
1200
  }
929
1201
  }
930
- async function createPayout(amount, currency, metadata) {
1202
+ async function createPayout(amount, currency, metadata, options) {
931
1203
  try {
1204
+ validateAmount(amount, "payout amount");
1205
+ const normalizedCurrency = validateCurrency(currency);
1206
+ if (metadata) validateMetadata(metadata);
932
1207
  const stripe = getStripe();
933
- const payout = await stripe.payouts.create({
934
- amount,
935
- currency,
936
- metadata
937
- });
1208
+ const payout = await stripe.payouts.create(
1209
+ {
1210
+ amount,
1211
+ currency: normalizedCurrency,
1212
+ metadata
1213
+ },
1214
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
1215
+ );
938
1216
  return success(payout);
939
1217
  } catch (error) {
940
1218
  return handleStripeError(error);
@@ -944,7 +1222,7 @@ async function listPayouts(input) {
944
1222
  try {
945
1223
  const stripe = getStripe();
946
1224
  const payouts = await stripe.payouts.list({
947
- limit: input?.limit ?? 10,
1225
+ limit: sanitizeLimit(input?.limit),
948
1226
  starting_after: input?.startingAfter,
949
1227
  ending_before: input?.endingBefore,
950
1228
  status: input?.status
@@ -967,7 +1245,7 @@ async function listBalanceTransactions(input) {
967
1245
  try {
968
1246
  const stripe = getStripe();
969
1247
  const transactions = await stripe.balanceTransactions.list({
970
- limit: input?.limit ?? 10,
1248
+ limit: sanitizeLimit(input?.limit),
971
1249
  starting_after: input?.startingAfter,
972
1250
  ending_before: input?.endingBefore,
973
1251
  type: input?.type
@@ -979,20 +1257,26 @@ async function listBalanceTransactions(input) {
979
1257
  }
980
1258
 
981
1259
  // src/server/coupons/index.ts
982
- async function createCoupon(input) {
1260
+ async function createCoupon(input, options) {
983
1261
  try {
1262
+ if (input.amountOff !== void 0) validateAmount(input.amountOff, "amountOff");
1263
+ if (input.currency) validateCurrency(input.currency);
1264
+ if (input.metadata) validateMetadata(input.metadata);
984
1265
  const stripe = getStripe();
985
- const coupon = await stripe.coupons.create({
986
- percent_off: input.percentOff,
987
- amount_off: input.amountOff,
988
- currency: input.currency,
989
- duration: input.duration,
990
- duration_in_months: input.durationInMonths,
991
- max_redemptions: input.maxRedemptions,
992
- redeem_by: input.redeemBy,
993
- name: input.name,
994
- metadata: input.metadata
995
- });
1266
+ const coupon = await stripe.coupons.create(
1267
+ {
1268
+ percent_off: input.percentOff,
1269
+ amount_off: input.amountOff,
1270
+ currency: input.currency,
1271
+ duration: input.duration,
1272
+ duration_in_months: input.durationInMonths,
1273
+ max_redemptions: input.maxRedemptions,
1274
+ redeem_by: input.redeemBy,
1275
+ name: input.name,
1276
+ metadata: input.metadata
1277
+ },
1278
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
1279
+ );
996
1280
  return success(coupon);
997
1281
  } catch (error) {
998
1282
  return handleStripeError(error);
@@ -1000,6 +1284,7 @@ async function createCoupon(input) {
1000
1284
  }
1001
1285
  async function retrieveCoupon(couponId) {
1002
1286
  try {
1287
+ validateStripeId(couponId, "coupon");
1003
1288
  const stripe = getStripe();
1004
1289
  const coupon = await stripe.coupons.retrieve(couponId);
1005
1290
  return success(coupon);
@@ -1009,6 +1294,7 @@ async function retrieveCoupon(couponId) {
1009
1294
  }
1010
1295
  async function deleteCoupon(couponId) {
1011
1296
  try {
1297
+ validateStripeId(couponId, "coupon");
1012
1298
  const stripe = getStripe();
1013
1299
  const deleted = await stripe.coupons.del(couponId);
1014
1300
  return success(deleted);
@@ -1020,7 +1306,7 @@ async function listCoupons(input) {
1020
1306
  try {
1021
1307
  const stripe = getStripe();
1022
1308
  const coupons = await stripe.coupons.list({
1023
- limit: input?.limit ?? 10,
1309
+ limit: sanitizeLimit(input?.limit),
1024
1310
  starting_after: input?.startingAfter,
1025
1311
  ending_before: input?.endingBefore
1026
1312
  });
@@ -1029,22 +1315,32 @@ async function listCoupons(input) {
1029
1315
  return handleStripeError(error);
1030
1316
  }
1031
1317
  }
1032
- async function createPromotionCode(input) {
1318
+ async function createPromotionCode(input, options) {
1033
1319
  try {
1320
+ if (input.metadata) validateMetadata(input.metadata);
1321
+ if (input.restrictions?.minimumAmountCurrency) {
1322
+ validateCurrency(input.restrictions.minimumAmountCurrency);
1323
+ }
1324
+ if (input.restrictions?.minimumAmount !== void 0) {
1325
+ validateAmount(input.restrictions.minimumAmount, "minimumAmount");
1326
+ }
1034
1327
  const stripe = getStripe();
1035
- const promotionCode = await stripe.promotionCodes.create({
1036
- coupon: input.couponId,
1037
- code: input.code,
1038
- active: input.active,
1039
- max_redemptions: input.maxRedemptions,
1040
- expires_at: input.expiresAt,
1041
- metadata: input.metadata,
1042
- restrictions: input.restrictions ? {
1043
- first_time_transaction: input.restrictions.firstTimeTransaction,
1044
- minimum_amount: input.restrictions.minimumAmount,
1045
- minimum_amount_currency: input.restrictions.minimumAmountCurrency
1046
- } : void 0
1047
- });
1328
+ const promotionCode = await stripe.promotionCodes.create(
1329
+ {
1330
+ coupon: input.couponId,
1331
+ code: input.code,
1332
+ active: input.active,
1333
+ max_redemptions: input.maxRedemptions,
1334
+ expires_at: input.expiresAt,
1335
+ metadata: input.metadata,
1336
+ restrictions: input.restrictions ? {
1337
+ first_time_transaction: input.restrictions.firstTimeTransaction,
1338
+ minimum_amount: input.restrictions.minimumAmount,
1339
+ minimum_amount_currency: input.restrictions.minimumAmountCurrency
1340
+ } : void 0
1341
+ },
1342
+ options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : void 0
1343
+ );
1048
1344
  return success(promotionCode);
1049
1345
  } catch (error) {
1050
1346
  return handleStripeError(error);
@@ -1052,6 +1348,7 @@ async function createPromotionCode(input) {
1052
1348
  }
1053
1349
  async function retrievePromotionCode(promotionCodeId) {
1054
1350
  try {
1351
+ validateStripeId(promotionCodeId, "promotionCode");
1055
1352
  const stripe = getStripe();
1056
1353
  const promotionCode = await stripe.promotionCodes.retrieve(promotionCodeId);
1057
1354
  return success(promotionCode);
@@ -1063,7 +1360,7 @@ async function listPromotionCodes(input) {
1063
1360
  try {
1064
1361
  const stripe = getStripe();
1065
1362
  const promotionCodes = await stripe.promotionCodes.list({
1066
- limit: input?.limit ?? 10,
1363
+ limit: sanitizeLimit(input?.limit),
1067
1364
  starting_after: input?.startingAfter,
1068
1365
  ending_before: input?.endingBefore,
1069
1366
  coupon: input?.couponId,
@@ -1117,6 +1414,9 @@ function createWebhookHandler(config) {
1117
1414
  function createNextWebhookHandler(config) {
1118
1415
  const handler = createWebhookHandler(config);
1119
1416
  return async function POST(request) {
1417
+ if (request.method !== "POST") {
1418
+ return new Response(null, { status: 405 });
1419
+ }
1120
1420
  try {
1121
1421
  const contentLength = request.headers.get("content-length");
1122
1422
  if (contentLength && parseInt(contentLength, 10) > MAX_BODY_SIZE) {
@@ -1126,6 +1426,12 @@ function createNextWebhookHandler(config) {
1126
1426
  });
1127
1427
  }
1128
1428
  const body = await request.text();
1429
+ if (body.length > MAX_BODY_SIZE) {
1430
+ return new Response(JSON.stringify({ error: "Webhook body too large" }), {
1431
+ status: 413,
1432
+ headers: { "Content-Type": "application/json" }
1433
+ });
1434
+ }
1129
1435
  const signature = request.headers.get("stripe-signature");
1130
1436
  if (!signature) {
1131
1437
  return new Response(JSON.stringify({ error: "Missing stripe-signature header" }), {
@@ -1139,8 +1445,8 @@ function createNextWebhookHandler(config) {
1139
1445
  headers: { "Content-Type": "application/json" }
1140
1446
  });
1141
1447
  } catch (error) {
1142
- const message = error instanceof Error ? error.message : "Webhook handler failed";
1143
- return new Response(JSON.stringify({ error: message }), {
1448
+ console.error("[@stripe-sdk/core] Webhook error:", error instanceof Error ? error.message : "Unknown error");
1449
+ return new Response(JSON.stringify({ error: "Webhook processing failed" }), {
1144
1450
  status: 400,
1145
1451
  headers: { "Content-Type": "application/json" }
1146
1452
  });
@@ -1164,8 +1470,8 @@ function createPagesWebhookHandler(webhookConfig) {
1164
1470
  const result = await handler(body, signature);
1165
1471
  res.status(200).json(result);
1166
1472
  } catch (error) {
1167
- const message = error instanceof Error ? error.message : "Webhook handler failed";
1168
- res.status(400).json({ error: message });
1473
+ console.error("[@stripe-sdk/core] Webhook error:", error instanceof Error ? error.message : "Unknown error");
1474
+ res.status(400).json({ error: "Webhook processing failed" });
1169
1475
  }
1170
1476
  };
1171
1477
  }
@@ -1193,16 +1499,21 @@ function getRawBody(req) {
1193
1499
  }
1194
1500
  const chunks = [];
1195
1501
  let totalLength = 0;
1502
+ let rejected = false;
1196
1503
  req.on("data", (chunk) => {
1504
+ if (rejected) return;
1197
1505
  const buf = Buffer.from(chunk);
1198
1506
  totalLength += buf.length;
1199
1507
  if (totalLength > MAX_BODY_SIZE) {
1508
+ rejected = true;
1200
1509
  reject(new Error("Webhook body too large"));
1201
1510
  return;
1202
1511
  }
1203
1512
  chunks.push(buf);
1204
1513
  });
1205
- req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
1514
+ req.on("end", () => {
1515
+ if (!rejected) resolve(Buffer.concat(chunks).toString("utf8"));
1516
+ });
1206
1517
  req.on("error", reject);
1207
1518
  });
1208
1519
  }
@@ -1214,12 +1525,26 @@ function useStripeConfig() {
1214
1525
  }
1215
1526
  return context;
1216
1527
  }
1528
+ function validatePublishableKey(key) {
1529
+ if (!key || typeof key !== "string") {
1530
+ throw new Error("StripeProvider requires a publishableKey");
1531
+ }
1532
+ if (key.startsWith("sk_")) {
1533
+ throw new Error(
1534
+ "StripeProvider received a secret key (sk_*). Use a publishable key (pk_*) instead. Secret keys must NEVER be used on the client side."
1535
+ );
1536
+ }
1537
+ if (!key.startsWith("pk_")) {
1538
+ throw new Error('StripeProvider requires a publishable key starting with "pk_"');
1539
+ }
1540
+ }
1217
1541
  function StripeProvider({
1218
1542
  publishableKey,
1219
1543
  children,
1220
1544
  options,
1221
1545
  locale
1222
1546
  }) {
1547
+ validatePublishableKey(publishableKey);
1223
1548
  const stripePromise = useMemo(
1224
1549
  () => loadStripe(publishableKey, locale ? { locale } : void 0),
1225
1550
  [publishableKey, locale]
@@ -1234,6 +1559,10 @@ function StripeElementsProvider({
1234
1559
  locale,
1235
1560
  loader = "auto"
1236
1561
  }) {
1562
+ validatePublishableKey(publishableKey);
1563
+ if (!clientSecret || typeof clientSecret !== "string") {
1564
+ throw new Error("StripeElementsProvider requires a clientSecret");
1565
+ }
1237
1566
  const stripePromise = useMemo(
1238
1567
  () => loadStripe(publishableKey, locale ? { locale } : void 0),
1239
1568
  [publishableKey, locale]
@@ -1248,9 +1577,21 @@ function StripeElementsProvider({
1248
1577
  );
1249
1578
  return /* @__PURE__ */ jsx(StripeContext.Provider, { value: { publishableKey }, children: /* @__PURE__ */ jsx(Elements, { stripe: stripePromise, options, children }) });
1250
1579
  }
1580
+ function validateReturnUrl(url) {
1581
+ try {
1582
+ const parsed = new URL(url);
1583
+ if (!["http:", "https:"].includes(parsed.protocol)) {
1584
+ throw new Error("returnUrl must use http or https protocol");
1585
+ }
1586
+ } catch {
1587
+ throw new Error("returnUrl must be a valid URL");
1588
+ }
1589
+ }
1251
1590
  function usePayment(options) {
1252
1591
  const stripe = useStripe();
1253
1592
  const elements = useElements();
1593
+ const optionsRef = useRef(options);
1594
+ optionsRef.current = options;
1254
1595
  const [state, setState] = useState({
1255
1596
  isProcessing: false,
1256
1597
  isSuccess: false,
@@ -1262,28 +1603,32 @@ function usePayment(options) {
1262
1603
  setState((s) => ({ ...s, error: "Stripe not loaded yet" }));
1263
1604
  return { success: false, error: "Stripe not loaded yet" };
1264
1605
  }
1606
+ const returnUrl = overrides?.returnUrl ?? optionsRef.current?.returnUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1607
+ if (returnUrl && returnUrl !== window.location.href) {
1608
+ validateReturnUrl(returnUrl);
1609
+ }
1265
1610
  setState({ isProcessing: true, isSuccess: false, error: null, paymentIntentId: null });
1266
1611
  const { error, paymentIntent } = await stripe.confirmPayment({
1267
1612
  elements,
1268
1613
  confirmParams: {
1269
- return_url: overrides?.returnUrl ?? options?.returnUrl ?? (typeof window !== "undefined" ? window.location.href : "")
1614
+ return_url: returnUrl
1270
1615
  },
1271
1616
  redirect: "if_required"
1272
1617
  });
1273
1618
  if (error) {
1274
1619
  const message = error.message ?? "Payment failed";
1275
1620
  setState({ isProcessing: false, isSuccess: false, error: message, paymentIntentId: null });
1276
- options?.onError?.(message);
1621
+ optionsRef.current?.onError?.(message);
1277
1622
  return { success: false, error: message };
1278
1623
  }
1279
1624
  if (paymentIntent?.status === "succeeded") {
1280
1625
  setState({ isProcessing: false, isSuccess: true, error: null, paymentIntentId: paymentIntent.id });
1281
- options?.onSuccess?.(paymentIntent.id);
1626
+ optionsRef.current?.onSuccess?.(paymentIntent.id);
1282
1627
  return { success: true, paymentIntentId: paymentIntent.id };
1283
1628
  }
1284
1629
  setState({ isProcessing: false, isSuccess: false, error: null, paymentIntentId: paymentIntent?.id ?? null });
1285
1630
  return { success: false, status: paymentIntent?.status };
1286
- }, [stripe, elements, options]);
1631
+ }, [stripe, elements]);
1287
1632
  const reset = useCallback(() => {
1288
1633
  setState({ isProcessing: false, isSuccess: false, error: null, paymentIntentId: null });
1289
1634
  }, []);
@@ -1294,9 +1639,21 @@ function usePayment(options) {
1294
1639
  isReady: !!stripe && !!elements
1295
1640
  };
1296
1641
  }
1642
+ function validateReturnUrl2(url) {
1643
+ try {
1644
+ const parsed = new URL(url);
1645
+ if (!["http:", "https:"].includes(parsed.protocol)) {
1646
+ throw new Error("returnUrl must use http or https protocol");
1647
+ }
1648
+ } catch {
1649
+ throw new Error("returnUrl must be a valid URL");
1650
+ }
1651
+ }
1297
1652
  function useSetupIntent(options) {
1298
1653
  const stripe = useStripe();
1299
1654
  const elements = useElements();
1655
+ const optionsRef = useRef(options);
1656
+ optionsRef.current = options;
1300
1657
  const [state, setState] = useState({
1301
1658
  isProcessing: false,
1302
1659
  isSuccess: false,
@@ -1309,29 +1666,33 @@ function useSetupIntent(options) {
1309
1666
  setState((s) => ({ ...s, error: "Stripe not loaded yet" }));
1310
1667
  return { success: false, error: "Stripe not loaded yet" };
1311
1668
  }
1669
+ const returnUrl = overrides?.returnUrl ?? optionsRef.current?.returnUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1670
+ if (returnUrl && returnUrl !== window.location.href) {
1671
+ validateReturnUrl2(returnUrl);
1672
+ }
1312
1673
  setState({ isProcessing: true, isSuccess: false, error: null, setupIntentId: null, paymentMethodId: null });
1313
1674
  const { error, setupIntent } = await stripe.confirmSetup({
1314
1675
  elements,
1315
1676
  confirmParams: {
1316
- return_url: overrides?.returnUrl ?? options?.returnUrl ?? (typeof window !== "undefined" ? window.location.href : "")
1677
+ return_url: returnUrl
1317
1678
  },
1318
1679
  redirect: "if_required"
1319
1680
  });
1320
1681
  if (error) {
1321
1682
  const message = error.message ?? "Setup failed";
1322
1683
  setState({ isProcessing: false, isSuccess: false, error: message, setupIntentId: null, paymentMethodId: null });
1323
- options?.onError?.(message);
1684
+ optionsRef.current?.onError?.(message);
1324
1685
  return { success: false, error: message };
1325
1686
  }
1326
1687
  if (setupIntent?.status === "succeeded") {
1327
1688
  const pmId = typeof setupIntent.payment_method === "string" ? setupIntent.payment_method : setupIntent.payment_method?.id ?? null;
1328
1689
  setState({ isProcessing: false, isSuccess: true, error: null, setupIntentId: setupIntent.id, paymentMethodId: pmId });
1329
- if (pmId) options?.onSuccess?.(setupIntent.id, pmId);
1690
+ if (pmId) optionsRef.current?.onSuccess?.(setupIntent.id, pmId);
1330
1691
  return { success: true, setupIntentId: setupIntent.id, paymentMethodId: pmId };
1331
1692
  }
1332
1693
  setState({ isProcessing: false, isSuccess: false, error: null, setupIntentId: setupIntent?.id ?? null, paymentMethodId: null });
1333
1694
  return { success: false, status: setupIntent?.status };
1334
- }, [stripe, elements, options]);
1695
+ }, [stripe, elements]);
1335
1696
  const reset = useCallback(() => {
1336
1697
  setState({ isProcessing: false, isSuccess: false, error: null, setupIntentId: null, paymentMethodId: null });
1337
1698
  }, []);
@@ -1342,6 +1703,14 @@ function useSetupIntent(options) {
1342
1703
  isReady: !!stripe && !!elements
1343
1704
  };
1344
1705
  }
1706
+ function isValidStripeUrl(url) {
1707
+ try {
1708
+ const parsed = new URL(url);
1709
+ return parsed.hostname.endsWith(".stripe.com") && parsed.protocol === "https:";
1710
+ } catch {
1711
+ return false;
1712
+ }
1713
+ }
1345
1714
  function useCheckout(options) {
1346
1715
  const [state, setState] = useState({
1347
1716
  isLoading: false,
@@ -1368,8 +1737,11 @@ function useCheckout(options) {
1368
1737
  options.onError?.(message);
1369
1738
  return { success: false, error: message };
1370
1739
  }
1371
- }, [options]);
1740
+ }, [options.publishableKey, options.onError]);
1372
1741
  const redirectToPortal = useCallback((portalUrl) => {
1742
+ if (!isValidStripeUrl(portalUrl)) {
1743
+ throw new Error("Invalid portal URL: must be a valid stripe.com HTTPS URL");
1744
+ }
1373
1745
  window.location.href = portalUrl;
1374
1746
  }, []);
1375
1747
  return {
@@ -1704,5 +2076,3 @@ function SubscriptionManager({
1704
2076
  }
1705
2077
 
1706
2078
  export { CheckoutForm, PricingTable, SetupForm, StripeElementsProvider, StripeProvider, SubscriptionManager, archivePrice, archiveProduct, attachPaymentMethod, cancelPaymentIntent, cancelSubscription, closeDispute, confirmPaymentIntent, createAccountLink, createCheckoutSession, createConnectAccount, createCoupon, createCustomer, createInvoice, createInvoiceItem, createNextWebhookHandler, createPagesWebhookHandler, createPaymentIntent, createPaymentLink, createPayout, createPortalSession, createPrice, createProduct, createPromotionCode, createRefund, createSetupIntent, createSubscription, createTransfer, createWebhookHandler, deleteConnectAccount, deleteCoupon, deleteCustomer, detachPaymentMethod, finalizeInvoice, getBalance, getConfig, getStripe, getUpcomingInvoice, initStripe, listBalanceTransactions, listCheckoutSessions, listConnectAccounts, listCoupons, listCustomers, listDisputes, listInvoices, listPaymentIntents, listPaymentMethods, listPayouts, listPrices, listProducts, listPromotionCodes, listRefunds, listSubscriptions, listTransfers, payInvoice, resumeSubscription, retrieveCheckoutSession, retrieveConnectAccount, retrieveCoupon, retrieveCustomer, retrieveDispute, retrieveInvoice, retrievePaymentIntent, retrievePaymentLink, retrievePrice, retrieveProduct, retrievePromotionCode, retrieveRefund, retrieveSetupIntent, retrieveSubscription, searchCustomers, sendInvoice, updateCustomer, updateDispute, updateProduct, updateSubscription, useCheckout, usePayment, useSetupIntent, useStripeConfig, voidInvoice };
1707
- //# sourceMappingURL=index.mjs.map
1708
- //# sourceMappingURL=index.mjs.map