@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/README.md +88 -261
- package/dist/client/index.d.mts +0 -4
- package/dist/client/index.d.ts +0 -4
- package/dist/client/index.js +70 -11
- package/dist/client/index.mjs +71 -12
- package/dist/{index-D8rM_YD4.d.mts → index-BKDJf1Hz.d.mts} +1 -27
- package/dist/{index-D8rM_YD4.d.ts → index-BKDJf1Hz.d.ts} +1 -27
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +601 -231
- package/dist/index.mjs +602 -232
- package/dist/next/index.d.mts +55 -32
- package/dist/next/index.d.ts +55 -32
- package/dist/next/index.js +362 -143
- package/dist/next/index.mjs +362 -143
- package/dist/server/index.d.mts +61 -21
- package/dist/server/index.d.ts +61 -21
- package/dist/server/index.js +546 -237
- package/dist/server/index.mjs +546 -237
- package/dist/server/webhooks/index.d.mts +1 -1
- package/dist/server/webhooks/index.d.ts +1 -1
- package/dist/server/webhooks/index.js +19 -7
- package/dist/server/webhooks/index.mjs +19 -7
- package/package.json +8 -5
- package/dist/client/index.js.map +0 -1
- package/dist/client/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/next/index.js.map +0 -1
- package/dist/next/index.mjs.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/server/index.mjs.map +0 -1
- package/dist/server/webhooks/index.js.map +0 -1
- package/dist/server/webhooks/index.mjs.map +0 -1
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.
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
-
}
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
|
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
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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
|
|
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
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
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
|
|
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
|
-
|
|
941
|
-
|
|
942
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
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
|
|
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
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
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
|
|
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
|
-
|
|
1149
|
-
return new Response(JSON.stringify({ error:
|
|
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
|
-
|
|
1174
|
-
res.status(400).json({ error:
|
|
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", () =>
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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)
|
|
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
|
|
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
|