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