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