@payments-ai/mcp 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.http.js +215 -59
- package/package.json +1 -1
package/dist/main.http.js
CHANGED
|
@@ -247,17 +247,23 @@ var SUPPORTED_OAUTH_SCOPES = [
|
|
|
247
247
|
"docs:read",
|
|
248
248
|
"checkout:read",
|
|
249
249
|
"checkout:write",
|
|
250
|
-
"transaction:read"
|
|
250
|
+
"transaction:read",
|
|
251
|
+
"customer:read",
|
|
252
|
+
"invoice:read"
|
|
251
253
|
];
|
|
252
254
|
var OAUTH_CONSENT_READ_SCOPES = [
|
|
253
255
|
"merchant:read",
|
|
254
256
|
"product:read",
|
|
255
257
|
"docs:read",
|
|
256
258
|
"checkout:read",
|
|
257
|
-
"transaction:read"
|
|
259
|
+
"transaction:read",
|
|
260
|
+
"customer:read",
|
|
261
|
+
"invoice:read"
|
|
258
262
|
];
|
|
259
263
|
var EXPLICIT_GRANT_OAUTH_SCOPES = [
|
|
260
|
-
"transaction:read"
|
|
264
|
+
"transaction:read",
|
|
265
|
+
"customer:read",
|
|
266
|
+
"invoice:read"
|
|
261
267
|
];
|
|
262
268
|
var EXPLICIT_GRANT_SCOPE_SET = new Set(EXPLICIT_GRANT_OAUTH_SCOPES);
|
|
263
269
|
function isExplicitGrantScope(scope) {
|
|
@@ -272,6 +278,7 @@ function buildOAuthAuthorizationServerMetadata(issuerUrl, options2) {
|
|
|
272
278
|
issuer: issuerUrl,
|
|
273
279
|
authorization_endpoint: `${issuerUrl}/api/v1/oauth/authorize`,
|
|
274
280
|
token_endpoint: `${issuerUrl}/api/v1/oauth/token`,
|
|
281
|
+
revocation_endpoint: `${issuerUrl}/api/v1/oauth/revoke`,
|
|
275
282
|
registration_endpoint: `${issuerUrl}/api/v1/oauth/register`,
|
|
276
283
|
scopes_supported: SUPPORTED_OAUTH_SCOPES,
|
|
277
284
|
response_types_supported: OAUTH_RESPONSE_TYPES_SUPPORTED,
|
|
@@ -287,6 +294,9 @@ var MCP_OAUTH_AUTHORIZATION_WINDOW_MINUTES = 30;
|
|
|
287
294
|
var MCP_OAUTH_AUTHORIZATION_WINDOW_MS = MCP_OAUTH_AUTHORIZATION_WINDOW_MINUTES * 60 * 1e3;
|
|
288
295
|
|
|
289
296
|
// ../../packages/mcp-contracts/dist/order-limits.js
|
|
297
|
+
var import_currency2 = __toESM(require_currency(), 1);
|
|
298
|
+
|
|
299
|
+
// ../../packages/mcp-contracts/dist/currency-precision.js
|
|
290
300
|
var import_currency = __toESM(require_currency(), 1);
|
|
291
301
|
|
|
292
302
|
// ../../packages/mcp-contracts/dist/plan-amount-limits.js
|
|
@@ -301,7 +311,8 @@ var MIN_PAID_PLAN_AMOUNT_BY_CURRENCY = {
|
|
|
301
311
|
dkk: 2.5,
|
|
302
312
|
nok: 3,
|
|
303
313
|
cad: 1,
|
|
304
|
-
aud: 1
|
|
314
|
+
aud: 1,
|
|
315
|
+
jpy: 50
|
|
305
316
|
};
|
|
306
317
|
var SUPPORTED_CURRENCIES = Object.freeze(Object.keys(MIN_PAID_PLAN_AMOUNT_BY_CURRENCY));
|
|
307
318
|
function isSupportedCurrency(currency5) {
|
|
@@ -320,15 +331,49 @@ function formatMinPaidPlanAmountMessage(currency5, minAmount) {
|
|
|
320
331
|
const code = currency5.trim().toUpperCase() || "USD";
|
|
321
332
|
return `Amount must be at least ${minAmount} ${code} for paid plans`;
|
|
322
333
|
}
|
|
334
|
+
function formatAmountPrecisionMessage(currency5, fractionDigits) {
|
|
335
|
+
const code = currency5.trim().toUpperCase();
|
|
336
|
+
if (fractionDigits === 0) {
|
|
337
|
+
return `Amount must be a whole number of ${code}, which has no minor unit. Recovery: round the amount to whole ${code}, e.g. 500 rather than 500.5.`;
|
|
338
|
+
}
|
|
339
|
+
return `Amount must have at most ${fractionDigits} decimal places in ${code}. Recovery: round the amount to ${fractionDigits} decimal places.`;
|
|
340
|
+
}
|
|
323
341
|
var LOWEST_MIN_PAID_PLAN_AMOUNT = Math.min(...Object.values(MIN_PAID_PLAN_AMOUNT_BY_CURRENCY));
|
|
324
342
|
var PAID_PLAN_NOT_FREE_BY_ZERO_AMOUNT = "A paid plan cannot be made free by setting its amount to 0; create a free-access plan instead.";
|
|
325
343
|
function formatAmountBelowLowestFloorMessage() {
|
|
326
344
|
const higherFloors = Object.entries(MIN_PAID_PLAN_AMOUNT_BY_CURRENCY).filter(([, minAmount]) => minAmount > LOWEST_MIN_PAID_PLAN_AMOUNT).map(([currency5, minAmount]) => `${minAmount} ${currency5.toUpperCase()}`).join(", ");
|
|
327
345
|
return `Amount must be at least ${LOWEST_MIN_PAID_PLAN_AMOUNT} for paid plans, and more in some currencies (${higherFloors}). Recovery: send an amount of at least the floor of the plan's currency, or 0 for a free-access plan. ${PAID_PLAN_NOT_FREE_BY_ZERO_AMOUNT}`;
|
|
328
346
|
}
|
|
329
|
-
var
|
|
330
|
-
|
|
331
|
-
|
|
347
|
+
var DEFAULT_MAX_PLAN_AMOUNT = 1e5;
|
|
348
|
+
var MAX_PLAN_AMOUNT_BY_CURRENCY = {
|
|
349
|
+
usd: DEFAULT_MAX_PLAN_AMOUNT,
|
|
350
|
+
eur: DEFAULT_MAX_PLAN_AMOUNT,
|
|
351
|
+
gbp: DEFAULT_MAX_PLAN_AMOUNT,
|
|
352
|
+
pln: DEFAULT_MAX_PLAN_AMOUNT,
|
|
353
|
+
chf: DEFAULT_MAX_PLAN_AMOUNT,
|
|
354
|
+
sek: DEFAULT_MAX_PLAN_AMOUNT,
|
|
355
|
+
dkk: DEFAULT_MAX_PLAN_AMOUNT,
|
|
356
|
+
nok: DEFAULT_MAX_PLAN_AMOUNT,
|
|
357
|
+
cad: DEFAULT_MAX_PLAN_AMOUNT,
|
|
358
|
+
aud: DEFAULT_MAX_PLAN_AMOUNT,
|
|
359
|
+
// Tech-lead decision (about the USD 100000 ceiling in yen), not a measured Whop limit.
|
|
360
|
+
jpy: 15e6
|
|
361
|
+
};
|
|
362
|
+
function getMaxPlanAmount(currency5) {
|
|
363
|
+
const key = currency5.trim().toLowerCase();
|
|
364
|
+
return MAX_PLAN_AMOUNT_BY_CURRENCY[key] ?? DEFAULT_MAX_PLAN_AMOUNT;
|
|
365
|
+
}
|
|
366
|
+
var HIGHEST_MAX_PLAN_AMOUNT = Math.max(...Object.values(MAX_PLAN_AMOUNT_BY_CURRENCY));
|
|
367
|
+
var MAX_AMOUNT_RECOVERY = "Recovery: lower the plan amount, or contact support if you need a higher per-plan limit.";
|
|
368
|
+
function formatMaxPlanAmountMessage(currency5) {
|
|
369
|
+
const code = currency5.trim().toUpperCase();
|
|
370
|
+
return `Amount must be at most ${getMaxPlanAmount(currency5)} ${code}. ${MAX_AMOUNT_RECOVERY}`;
|
|
371
|
+
}
|
|
372
|
+
function formatHigherMaxAmounts() {
|
|
373
|
+
return Object.entries(MAX_PLAN_AMOUNT_BY_CURRENCY).filter(([, maxAmount]) => maxAmount > DEFAULT_MAX_PLAN_AMOUNT).map(([currency5, maxAmount]) => `${maxAmount} ${currency5.toUpperCase()}`).join(", ");
|
|
374
|
+
}
|
|
375
|
+
function formatAmountAboveCeilingMessage() {
|
|
376
|
+
return `Amount must be at most ${DEFAULT_MAX_PLAN_AMOUNT}, or the higher ceiling of its currency (${formatHigherMaxAmounts()}). ${MAX_AMOUNT_RECOVERY}`;
|
|
332
377
|
}
|
|
333
378
|
var MAX_PERIOD_LENGTH_BY_BILLING_PERIOD = {
|
|
334
379
|
week: 52,
|
|
@@ -343,8 +388,52 @@ function formatPeriodLengthRecovery(maxLength) {
|
|
|
343
388
|
}
|
|
344
389
|
var MAX_FREE_TRIAL_DAYS = 730;
|
|
345
390
|
|
|
391
|
+
// ../../packages/mcp-contracts/dist/currency-precision.js
|
|
392
|
+
function currencyFractionDigits(currencyCode3) {
|
|
393
|
+
const code = currencyCode3.trim().toUpperCase();
|
|
394
|
+
let digits;
|
|
395
|
+
try {
|
|
396
|
+
digits = new Intl.NumberFormat("en-US", {
|
|
397
|
+
style: "currency",
|
|
398
|
+
currency: code
|
|
399
|
+
}).resolvedOptions().maximumFractionDigits;
|
|
400
|
+
} catch {
|
|
401
|
+
throw new Error(`Unsupported currency "${currencyCode3}"`);
|
|
402
|
+
}
|
|
403
|
+
if (typeof digits !== "number") {
|
|
404
|
+
throw new Error(`Unsupported currency "${currencyCode3}"`);
|
|
405
|
+
}
|
|
406
|
+
return digits;
|
|
407
|
+
}
|
|
408
|
+
var MINOR_UNIT_RELATIVE_TOLERANCE = Number.EPSILON * 16;
|
|
409
|
+
function amountExceedsCurrencyPrecision(amount, currencyCode3) {
|
|
410
|
+
const minorUnits = amount * 10 ** currencyFractionDigits(currencyCode3);
|
|
411
|
+
const drift = Math.abs(minorUnits - Math.round(minorUnits));
|
|
412
|
+
return drift > Math.abs(minorUnits) * MINOR_UNIT_RELATIVE_TOLERANCE;
|
|
413
|
+
}
|
|
414
|
+
function describeAmountPrecisionViolation(amount, currencyCode3) {
|
|
415
|
+
const fractionDigits = resolveFractionDigits(currencyCode3);
|
|
416
|
+
if (fractionDigits === void 0 || !amountExceedsCurrencyPrecision(amount, currencyCode3)) {
|
|
417
|
+
return void 0;
|
|
418
|
+
}
|
|
419
|
+
return formatAmountPrecisionMessage(currencyCode3, fractionDigits);
|
|
420
|
+
}
|
|
421
|
+
function describeRequestAmountPrecisionViolation(amount, currencyCode3) {
|
|
422
|
+
return describeAmountPrecisionViolation((0, import_currency.default)(amount).value, currencyCode3);
|
|
423
|
+
}
|
|
424
|
+
function resolveFractionDigits(currencyCode3) {
|
|
425
|
+
try {
|
|
426
|
+
return currencyFractionDigits(currencyCode3);
|
|
427
|
+
} catch {
|
|
428
|
+
return void 0;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
346
432
|
// ../../packages/mcp-contracts/dist/order-limits.js
|
|
347
|
-
|
|
433
|
+
function getMaxOrderAmount(currency5) {
|
|
434
|
+
return getMaxPlanAmount(currency5);
|
|
435
|
+
}
|
|
436
|
+
var HIGHEST_MAX_ORDER_AMOUNT = HIGHEST_MAX_PLAN_AMOUNT;
|
|
348
437
|
var MAX_ORDER_ITEM_QUANTITY = 1e4;
|
|
349
438
|
var MAX_ORDER_PERIOD_LENGTH_BY_BILLING_PERIOD = {
|
|
350
439
|
day: 365,
|
|
@@ -358,7 +447,11 @@ function getMaxOrderPeriodLength(billingPeriod) {
|
|
|
358
447
|
var ORDER_PERIOD_CAPS = Object.entries(MAX_ORDER_PERIOD_LENGTH_BY_BILLING_PERIOD).map(([period, max]) => `${max} ${period}`).join(" / ");
|
|
359
448
|
var ORDER_PERIOD_LENGTH_TOO_LONG_MESSAGE = `periodLength must be at most ${MAX_ORDER_PERIOD_LENGTH}, and at most one year of the chosen billingPeriod (${ORDER_PERIOD_CAPS}). Recovery: lower periodLength to the cap for your billingPeriod; nothing can bill less often than once a year.`;
|
|
360
449
|
var HIGHER_LIMIT_RECOVERY = "or contact support if you need a higher per-order limit.";
|
|
361
|
-
var
|
|
450
|
+
var UNIT_PRICE_RECOVERY = `Recovery: confirm the price with the user (amounts are in major units, so 19.99 means $19.99), ${HIGHER_LIMIT_RECOVERY}`;
|
|
451
|
+
var ORDER_UNIT_PRICE_TOO_HIGH_MESSAGE = `unitPrice must be at most ${DEFAULT_MAX_PLAN_AMOUNT}, or the higher ceiling of the order currency (${formatHigherMaxAmounts()}). ${UNIT_PRICE_RECOVERY}`;
|
|
452
|
+
function formatOrderUnitPriceTooHighMessage(currency5) {
|
|
453
|
+
return `unitPrice must be at most ${formatOrderCeiling(currency5)}. ${UNIT_PRICE_RECOVERY}`;
|
|
454
|
+
}
|
|
362
455
|
var ORDER_QUANTITY_TOO_HIGH_MESSAGE = `quantity must be at most ${MAX_ORDER_ITEM_QUANTITY}. Recovery: lower the quantity, or contact support if you need more units on one order line.`;
|
|
363
456
|
var ORDER_TRIAL_PERIOD_DAYS_TOO_LONG_MESSAGE = `trialPeriodDays must be at most ${MAX_FREE_TRIAL_DAYS} days. Recovery: lower the trial length.`;
|
|
364
457
|
var RECURRING_ORDER_ITEM_CADENCE_MESSAGE = 'billingPeriod and periodLength are required when type is "recurring". Recovery: send both, e.g. billingPeriod "month" with periodLength 1 to bill monthly, or billingPeriod "day" with periodLength 30 to bill every 30 days.';
|
|
@@ -368,48 +461,86 @@ var INTERVAL_DAYS_TOO_SHORT_MESSAGE = `intervalDays must be at least ${MIN_WHOP_
|
|
|
368
461
|
function isBelowMinWhopBillingPeriod(billingPeriod, periodLength) {
|
|
369
462
|
return billingPeriod === "day" && periodLength < MIN_WHOP_BILLING_PERIOD_DAYS;
|
|
370
463
|
}
|
|
371
|
-
function formatOrderLineTotalTooHighMessage(lineTotal2) {
|
|
372
|
-
return `unitPrice x quantity is ${formatOrderAmount(lineTotal2)}; one order line can charge at most ${
|
|
464
|
+
function formatOrderLineTotalTooHighMessage(lineTotal2, currency5) {
|
|
465
|
+
return `unitPrice x quantity is ${formatOrderAmount(lineTotal2, currency5)}; one order line can charge at most ${formatOrderCeiling(currency5)}. Recovery: lower the quantity or the unitPrice, ${HIGHER_LIMIT_RECOVERY}`;
|
|
373
466
|
}
|
|
374
|
-
function formatOrderTotalTooHighMessage(total, moment) {
|
|
375
|
-
return `This order charges ${formatOrderAmount(total)} ${moment}; one order can charge at most ${
|
|
467
|
+
function formatOrderTotalTooHighMessage(total, moment, currency5) {
|
|
468
|
+
return `This order charges ${formatOrderAmount(total, currency5)} ${moment}; one order can charge at most ${formatOrderCeiling(currency5)}. Recovery: remove items or lower quantities, ${HIGHER_LIMIT_RECOVERY}`;
|
|
376
469
|
}
|
|
377
470
|
function formatMaxOrderPeriodLengthMessage(billingPeriod, maxLength) {
|
|
378
471
|
return `periodLength must be at most ${maxLength} when billingPeriod is "${billingPeriod}" (an order renews at least once a year). ${formatPeriodLengthRecovery(maxLength)}`;
|
|
379
472
|
}
|
|
380
|
-
function
|
|
381
|
-
return (
|
|
473
|
+
function formatOrderCeiling(currencyCode3) {
|
|
474
|
+
return `${getMaxOrderAmount(currencyCode3)} ${currencyCode3.trim().toUpperCase()}`;
|
|
475
|
+
}
|
|
476
|
+
function formatOrderAmount(amount, currencyCode3) {
|
|
477
|
+
return (0, import_currency2.default)(amount, { precision: fractionDigitsOrTwo(currencyCode3) }).toString();
|
|
478
|
+
}
|
|
479
|
+
function fractionDigitsOrTwo(currencyCode3) {
|
|
480
|
+
try {
|
|
481
|
+
return currencyFractionDigits(currencyCode3);
|
|
482
|
+
} catch {
|
|
483
|
+
return 2;
|
|
484
|
+
}
|
|
382
485
|
}
|
|
383
486
|
|
|
384
487
|
// ../../packages/mcp-contracts/dist/order-item-limits.js
|
|
385
|
-
var
|
|
488
|
+
var import_currency3 = __toESM(require_currency(), 1);
|
|
386
489
|
var TRIAL_PERIOD_DAYS_NOT_RECURRING_MESSAGE = 'trialPeriodDays only applies to recurring custom items; a one_time line charges immediately. Recovery: remove trialPeriodDays, or use type "recurring" if the buyer should get a trial before the first charge.';
|
|
387
490
|
function checkCustomOrderItemLimits(item, ctx) {
|
|
388
491
|
checkRecurringCadence(item, ctx);
|
|
389
492
|
checkTrialPeriodDaysIsRecurring(item, ctx);
|
|
390
493
|
checkPeriodLengthCap(item, ctx);
|
|
391
494
|
checkDailyCadence(item, ctx);
|
|
392
|
-
checkLineTotal(item, ctx);
|
|
393
495
|
}
|
|
394
|
-
function
|
|
395
|
-
|
|
496
|
+
function checkCustomOrderItemAmounts(body, ctx) {
|
|
497
|
+
body.items.forEach((item, index) => {
|
|
498
|
+
if (!isPricedItem(item))
|
|
499
|
+
return;
|
|
500
|
+
const issue2 = describeLineAmountIssue(item, body.currency);
|
|
501
|
+
if (issue2 === void 0)
|
|
502
|
+
return;
|
|
503
|
+
ctx.addIssue({ code: "custom", message: issue2.message, path: ["items", index, issue2.field] });
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function checkCustomOrderItemsTotal(body, ctx) {
|
|
507
|
+
const items = body.items.filter(isPricedItem);
|
|
508
|
+
if (items.some((item) => hasLineIssue(item, body.currency)))
|
|
509
|
+
return;
|
|
510
|
+
const total = items.reduce((sum, item) => sum.add(lineTotal(item)), (0, import_currency3.default)(0));
|
|
511
|
+
if (total.value <= getMaxOrderAmount(body.currency))
|
|
396
512
|
return;
|
|
397
513
|
ctx.addIssue({
|
|
398
514
|
code: "custom",
|
|
399
|
-
message:
|
|
400
|
-
path: ["
|
|
515
|
+
message: formatOrderTotalTooHighMessage(total.value, "today", body.currency),
|
|
516
|
+
path: ["items"]
|
|
401
517
|
});
|
|
402
518
|
}
|
|
403
|
-
function
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (
|
|
519
|
+
function hasLineIssue(item, currencyCode3) {
|
|
520
|
+
return exceedsFieldCeiling(item) || describeLineAmountIssue(item, currencyCode3) !== void 0;
|
|
521
|
+
}
|
|
522
|
+
function describeLineAmountIssue(item, currencyCode3) {
|
|
523
|
+
if (exceedsFieldCeiling(item))
|
|
524
|
+
return void 0;
|
|
525
|
+
const maxAmount = getMaxOrderAmount(currencyCode3);
|
|
526
|
+
if (item.unitPrice > maxAmount) {
|
|
527
|
+
return { field: "unitPrice", message: formatOrderUnitPriceTooHighMessage(currencyCode3) };
|
|
528
|
+
}
|
|
529
|
+
const precision = describeRequestAmountPrecisionViolation(item.unitPrice, currencyCode3);
|
|
530
|
+
if (precision !== void 0)
|
|
531
|
+
return { field: "unitPrice", message: precision };
|
|
532
|
+
const total = lineTotal(item).value;
|
|
533
|
+
if (total <= maxAmount)
|
|
534
|
+
return void 0;
|
|
535
|
+
return { field: "quantity", message: formatOrderLineTotalTooHighMessage(total, currencyCode3) };
|
|
536
|
+
}
|
|
537
|
+
function checkTrialPeriodDaysIsRecurring(item, ctx) {
|
|
538
|
+
if (item.type === "recurring" || item.trialPeriodDays === void 0)
|
|
408
539
|
return;
|
|
409
540
|
ctx.addIssue({
|
|
410
541
|
code: "custom",
|
|
411
|
-
message:
|
|
412
|
-
path: ["
|
|
542
|
+
message: TRIAL_PERIOD_DAYS_NOT_RECURRING_MESSAGE,
|
|
543
|
+
path: ["trialPeriodDays"]
|
|
413
544
|
});
|
|
414
545
|
}
|
|
415
546
|
function checkRecurringCadence(item, ctx) {
|
|
@@ -447,26 +578,14 @@ function checkDailyCadence(item, ctx) {
|
|
|
447
578
|
path: ["periodLength"]
|
|
448
579
|
});
|
|
449
580
|
}
|
|
450
|
-
function checkLineTotal(item, ctx) {
|
|
451
|
-
if (exceedsFieldCeiling(item))
|
|
452
|
-
return;
|
|
453
|
-
const total = lineTotal(item);
|
|
454
|
-
if (total.value <= MAX_ORDER_AMOUNT)
|
|
455
|
-
return;
|
|
456
|
-
ctx.addIssue({
|
|
457
|
-
code: "custom",
|
|
458
|
-
message: formatOrderLineTotalTooHighMessage(total.value),
|
|
459
|
-
path: ["quantity"]
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
581
|
function lineTotal(item) {
|
|
463
|
-
return (0,
|
|
582
|
+
return (0, import_currency3.default)(item.unitPrice).multiply(item.quantity);
|
|
464
583
|
}
|
|
465
584
|
function exceedsFieldCeiling(item) {
|
|
466
|
-
return item.unitPrice >
|
|
585
|
+
return item.unitPrice > HIGHEST_MAX_ORDER_AMOUNT || item.quantity > MAX_ORDER_ITEM_QUANTITY;
|
|
467
586
|
}
|
|
468
|
-
function
|
|
469
|
-
return
|
|
587
|
+
function isPricedItem(item) {
|
|
588
|
+
return typeof item === "object" && item !== null && "unitPrice" in item && typeof item.unitPrice === "number" && "quantity" in item && typeof item.quantity === "number";
|
|
470
589
|
}
|
|
471
590
|
|
|
472
591
|
// ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/classic/external.js
|
|
@@ -20170,7 +20289,7 @@ var createOrderPlanItemSchema = external_exports.object({
|
|
|
20170
20289
|
}).strict();
|
|
20171
20290
|
var createOrderCustomItemSchema = external_exports.object({
|
|
20172
20291
|
label: external_exports.string().trim().min(1).max(255),
|
|
20173
|
-
unitPrice: external_exports.number().positive().max(
|
|
20292
|
+
unitPrice: external_exports.number().positive().max(HIGHEST_MAX_ORDER_AMOUNT, ORDER_UNIT_PRICE_TOO_HIGH_MESSAGE).describe("Price of one unit in major units of the order currency (decimal dollars, not cents): 10 means $10.00, 1999 means $1,999.00. A currency with no minor unit takes whole numbers: 500 means \xA5500."),
|
|
20174
20293
|
quantity: orderQuantitySchema,
|
|
20175
20294
|
type: external_exports.enum(ORDER_ITEM_TYPES),
|
|
20176
20295
|
billingPeriod: external_exports.enum(ORDER_BILLING_PERIODS).optional(),
|
|
@@ -20186,7 +20305,10 @@ var createOrderInputSchema = external_exports.object({
|
|
|
20186
20305
|
currency: supportedCurrencyCodeSchema,
|
|
20187
20306
|
items: external_exports.array(createOrderItemSchema).min(1).max(MAX_ORDER_ITEMS),
|
|
20188
20307
|
idempotencyKey: external_exports.string().trim().min(1).max(MAX_IDEMPOTENCY_KEY_LENGTH).optional()
|
|
20189
|
-
}).strict().superRefine((input2, ctx) =>
|
|
20308
|
+
}).strict().superRefine((input2, ctx) => {
|
|
20309
|
+
checkCustomOrderItemAmounts(input2, ctx);
|
|
20310
|
+
checkCustomOrderItemsTotal(input2, ctx);
|
|
20311
|
+
});
|
|
20190
20312
|
var createOrderItemResponseSchema = external_exports.object({
|
|
20191
20313
|
label: external_exports.string(),
|
|
20192
20314
|
unitPrice: external_exports.number(),
|
|
@@ -20241,13 +20363,13 @@ function formatPlanNameTooLong(length) {
|
|
|
20241
20363
|
function formatPlanDescriptionTooLong(length) {
|
|
20242
20364
|
return `Plan description is ${length} characters; max ${PLAN_DESCRIPTION_MAX}. Recovery: shorten the description to <=${PLAN_DESCRIPTION_MAX} characters \u2014 it is a one-line caption, so keep the pricing detail that distinguishes this plan \u2014 and move the full copy into this plan's richDescription (up to ${PLAN_RICH_DESCRIPTION_MAX} characters), which the hosted checkout page renders beneath it.`;
|
|
20243
20365
|
}
|
|
20244
|
-
var PLAN_AMOUNT_DESCRIPTION = "Price in major units of `currency` (decimal dollars, not cents): 19.99 means $19.99 and 1999 means $1,999.00. Stored as 0 on a free-access plan.";
|
|
20366
|
+
var PLAN_AMOUNT_DESCRIPTION = "Price in major units of `currency` (decimal dollars, not cents): 19.99 means $19.99 and 1999 means $1,999.00. A currency with no minor unit takes whole numbers: 500 means \xA5500. Stored as 0 on a free-access plan.";
|
|
20245
20367
|
var planShape = {
|
|
20246
20368
|
name: external_exports.string().trim().min(1).max(PLAN_NAME_MAX, tooLongError(formatPlanNameTooLong)),
|
|
20247
20369
|
description: external_exports.string().trim().max(PLAN_DESCRIPTION_MAX, tooLongError(formatPlanDescriptionTooLong)).optional(),
|
|
20248
20370
|
richDescription: external_exports.string().trim().max(PLAN_RICH_DESCRIPTION_MAX).optional(),
|
|
20249
20371
|
currency: supportedCurrencyCodeSchema,
|
|
20250
|
-
amount: external_exports.number().nonnegative().max(
|
|
20372
|
+
amount: external_exports.number().nonnegative().max(HIGHEST_MAX_PLAN_AMOUNT, formatAmountAboveCeilingMessage()).describe(PLAN_AMOUNT_DESCRIPTION),
|
|
20251
20373
|
type: external_exports.enum(PLAN_TYPES2),
|
|
20252
20374
|
billingPeriod: external_exports.enum(BILLING_PERIODS2),
|
|
20253
20375
|
periodLength: external_exports.number().int().positive(),
|
|
@@ -20265,6 +20387,8 @@ var createMcpPlanSchema = external_exports.strictObject(planShape, { error: unre
|
|
|
20265
20387
|
checkPlanPeriodLength(plan, ctx);
|
|
20266
20388
|
checkFreeTrialIsRecurring(plan, ctx);
|
|
20267
20389
|
checkPaidPlanMinimum(plan, ctx);
|
|
20390
|
+
checkPlanCeiling(plan, ctx);
|
|
20391
|
+
checkPaidPlanPrecision(plan, ctx);
|
|
20268
20392
|
});
|
|
20269
20393
|
function checkPlanPeriodLength(plan, ctx) {
|
|
20270
20394
|
const maxPeriodLength = MAX_PERIOD_LENGTH_BY_BILLING_PERIOD[plan.billingPeriod];
|
|
@@ -20297,6 +20421,24 @@ function checkPaidPlanMinimum(plan, ctx) {
|
|
|
20297
20421
|
path: ["amount"]
|
|
20298
20422
|
});
|
|
20299
20423
|
}
|
|
20424
|
+
function checkPlanCeiling(plan, ctx) {
|
|
20425
|
+
if (plan.amount > HIGHEST_MAX_PLAN_AMOUNT || plan.amount <= getMaxPlanAmount(plan.currency)) {
|
|
20426
|
+
return;
|
|
20427
|
+
}
|
|
20428
|
+
ctx.addIssue({
|
|
20429
|
+
code: "custom",
|
|
20430
|
+
message: formatMaxPlanAmountMessage(plan.currency),
|
|
20431
|
+
path: ["amount"]
|
|
20432
|
+
});
|
|
20433
|
+
}
|
|
20434
|
+
function checkPaidPlanPrecision(plan, ctx) {
|
|
20435
|
+
if (plan.type === "free-access")
|
|
20436
|
+
return;
|
|
20437
|
+
const message = describeRequestAmountPrecisionViolation(plan.amount, plan.currency);
|
|
20438
|
+
if (message === void 0)
|
|
20439
|
+
return;
|
|
20440
|
+
ctx.addIssue({ code: "custom", message, path: ["amount"] });
|
|
20441
|
+
}
|
|
20300
20442
|
var productBodyBaseSchema = external_exports.object({
|
|
20301
20443
|
name: external_exports.string().trim().min(1).max(PRODUCT_NAME_MAX, tooLongError(formatProductNameTooLong)).refine((name) => !containsMarkupTag(name), PRODUCT_NAME_MARKUP_MESSAGE),
|
|
20302
20444
|
description: external_exports.string().trim().min(1).max(PRODUCT_DESCRIPTION_MAX, tooLongError(formatProductDescriptionTooLong)).optional(),
|
|
@@ -20385,7 +20527,7 @@ var updatePlanFieldsSchema = external_exports.object({
|
|
|
20385
20527
|
description: external_exports.string().trim().max(PLAN_DESCRIPTION_MAX, tooLongError3(formatPlanDescriptionTooLong2)).optional(),
|
|
20386
20528
|
billingPeriod: external_exports.enum(BILLING_PERIODS3).optional(),
|
|
20387
20529
|
periodLength: external_exports.number().int().positive().optional(),
|
|
20388
|
-
amount: external_exports.number().nonnegative().max(
|
|
20530
|
+
amount: external_exports.number().nonnegative().max(HIGHEST_MAX_PLAN_AMOUNT, formatAmountAboveCeilingMessage()).refine((amount) => amount === 0 || amount >= LOWEST_MIN_PAID_PLAN_AMOUNT, {
|
|
20389
20531
|
error: formatAmountBelowLowestFloorMessage()
|
|
20390
20532
|
}).optional(),
|
|
20391
20533
|
freeTrial: external_exports.number().int().min(0).max(MAX_FREE_TRIAL_DAYS, `freeTrial must be at most ${MAX_FREE_TRIAL_DAYS} days. Recovery: lower the trial length.`).optional()
|
|
@@ -20435,6 +20577,9 @@ var updateMcpPlanBackendBodySchema = external_exports.object({
|
|
|
20435
20577
|
|
|
20436
20578
|
// ../../packages/mcp-contracts/dist/resource-url.js
|
|
20437
20579
|
function canonicalResourceUrl(value) {
|
|
20580
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
20581
|
+
return null;
|
|
20582
|
+
}
|
|
20438
20583
|
if (value.includes("#")) {
|
|
20439
20584
|
return null;
|
|
20440
20585
|
}
|
|
@@ -25282,13 +25427,17 @@ var SANDBOX_TEST_CARD_NOTE = "To pay a sandbox checkout link, use test card 4242
|
|
|
25282
25427
|
|
|
25283
25428
|
// src/tools/tool-descriptions.ts
|
|
25284
25429
|
var CREATE_MERCHANT_CONTACT_GUIDANCE = "Requires only name and email. The email must be the user's own real address, asked from the user or taken from the conversation: example or placeholder addresses (such as you@example.com) and addresses that cannot receive mail are rejected. Phone is optional. If you pass one it must be the user's own mobile number, taken from the conversation or asked from the user; never invent a placeholder or a fictional number (such as a 555 number), because after go_live the identity-verification link is sent to it by SMS and a number that cannot be dialled is rejected. Use E.164 with country code (format only: +14155552671). If the user does not share a phone, omit it and the link goes by email.";
|
|
25285
|
-
var CREATE_PRODUCT_PLAN_BOUNDS = `A plan \`amount\` is at most ${
|
|
25430
|
+
var CREATE_PRODUCT_PLAN_BOUNDS = `A plan \`amount\` is at most ${formatAmountCeilings()} in the plan currency, \`periodLength\` at most one year of the chosen billingPeriod (${formatPeriodCaps(MAX_PERIOD_LENGTH_BY_BILLING_PERIOD)}), and \`freeTrial\` at most ${MAX_FREE_TRIAL_DAYS} days and only on recurring plans. A plan accepts only its documented keys; an unknown key (such as freeTrialDays) is rejected, not ignored.`;
|
|
25431
|
+
function formatAmountCeilings() {
|
|
25432
|
+
const higher = Object.entries(MAX_PLAN_AMOUNT_BY_CURRENCY).filter(([, max]) => max > DEFAULT_MAX_PLAN_AMOUNT).map(([currency5, max]) => `${max} ${currency5.toUpperCase()}`);
|
|
25433
|
+
return higher.length > 0 ? `${DEFAULT_MAX_PLAN_AMOUNT} (${higher.join(", ")})` : `${DEFAULT_MAX_PLAN_AMOUNT}`;
|
|
25434
|
+
}
|
|
25286
25435
|
function formatPeriodCaps(caps) {
|
|
25287
25436
|
return Object.entries(caps).map(([period, max]) => `${max} ${period}`).join(" / ");
|
|
25288
25437
|
}
|
|
25289
|
-
var PRICE_UNITS = `a decimal dollar amount in the plan currency (major units, e.g. 9.99 for $9.99 \u2014 NOT cents, so 1999 means $1,999.00; currency is one of ${SUPPORTED_CURRENCIES.join(", ")})`;
|
|
25438
|
+
var PRICE_UNITS = `a decimal dollar amount in the plan currency (major units, e.g. 9.99 for $9.99 \u2014 NOT cents, so 1999 means $1,999.00; a currency with no minor unit takes whole numbers, so 500 means \xA5500 and 500.5 is rejected; currency is one of ${SUPPORTED_CURRENCIES.join(", ")})`;
|
|
25290
25439
|
var CREATE_PRODUCT_STORED_PLANS = "`plans` repeats what was stored for each plan, in `planIds` order: amount, currency, `price` formatted as the checkout shows it, and for recurring plans billingPeriod, periodLength and freeTrial \u2014 check `price` against what the user asked for. `warnings`, when present, flags an amount that looks like cents; relay it and, if the user meant the smaller price, fix it with update_plan before sharing the link.";
|
|
25291
|
-
var CREATE_ORDER_BOUNDS = `currency is one of ${SUPPORTED_CURRENCIES.join(", ")}. unitPrice is at most ${
|
|
25440
|
+
var CREATE_ORDER_BOUNDS = `currency is one of ${SUPPORTED_CURRENCIES.join(", ")}. unitPrice is at most ${formatAmountCeilings()} in the order currency, and so is each line (unitPrice x quantity) and the order total; quantity is at most ${MAX_ORDER_ITEM_QUANTITY}; periodLength is at most one year of the billingPeriod (${formatPeriodCaps(MAX_ORDER_PERIOD_LENGTH_BY_BILLING_PERIOD)}). When billingPeriod is "day", periodLength must be at least ${MIN_WHOP_BILLING_PERIOD_DAYS} (the payment provider rejects true daily subscriptions). trialPeriodDays is at most ${MAX_FREE_TRIAL_DAYS}, only on recurring custom lines, and must match the trial length of every other recurring line in the order (catalogue plan or custom).`;
|
|
25292
25441
|
var CREATE_PRODUCT_STATUS_NOTE = "Optional `status` ('draft' | 'active' | 'archived') defaults to 'active' \u2014 the product is created live and purchasable immediately via its checkout link, regardless of status. Pass `status: 'draft'` only when the end user explicitly asks for a draft or temporary/not-yet-ready product.";
|
|
25293
25442
|
var CREATE_PRODUCT_COPY_CAPS = `Product \`name\` (<=${PRODUCT_NAME_MAX} chars), product \`description\` (<=${PRODUCT_DESCRIPTION_MAX} chars) and plan \`description\` (<=${PLAN_DESCRIPTION_MAX} chars) are all short one-line copy \u2014 if yours is longer, shorten it before calling and put the full version in that plan's \`richDescription\` (<=${PLAN_RICH_DESCRIPTION_MAX} chars), which the hosted checkout page renders as plain text beneath the short description (newlines preserved, markup not interpreted). \`richDescription\` is per plan, so product-level copy that overflows must still be edited down.`;
|
|
25294
25443
|
var TRANSACTION_STATUS_RULE = 'Status: only "succeeded" means paid. "pending" is not yet confirmed by the payment provider, so never report a pending transaction as paid. "partially_refunded" means some but not all of the amount was refunded; "refunded", "failed" and "disputed" mean what they say.';
|
|
@@ -25650,18 +25799,17 @@ function buildPlanCheckoutUrl(params) {
|
|
|
25650
25799
|
}
|
|
25651
25800
|
|
|
25652
25801
|
// src/tools/product/stored-plan-summary.ts
|
|
25653
|
-
var
|
|
25802
|
+
var import_currency5 = __toESM(require_currency(), 1);
|
|
25654
25803
|
|
|
25655
25804
|
// src/shared/format-price.ts
|
|
25656
|
-
var
|
|
25805
|
+
var import_currency4 = __toESM(require_currency(), 1);
|
|
25657
25806
|
function formatPrice(amount, currencyCode3) {
|
|
25658
|
-
const price = (0,
|
|
25807
|
+
const price = (0, import_currency4.default)(amount);
|
|
25659
25808
|
const code = currencyCode3.toUpperCase();
|
|
25660
25809
|
try {
|
|
25661
25810
|
return new Intl.NumberFormat("en-US", {
|
|
25662
25811
|
style: "currency",
|
|
25663
|
-
currency: code
|
|
25664
|
-
minimumFractionDigits: 2
|
|
25812
|
+
currency: code
|
|
25665
25813
|
}).format(price.value);
|
|
25666
25814
|
} catch {
|
|
25667
25815
|
return `${price.toString()} ${code}`;
|
|
@@ -25702,12 +25850,19 @@ function buildAmountWarnings(plans) {
|
|
|
25702
25850
|
);
|
|
25703
25851
|
}
|
|
25704
25852
|
function looksLikeCents(plan) {
|
|
25705
|
-
return plan.type !== "free-access" && Number.isInteger(plan.amount) && plan.amount >= CENTS_SUSPECT_FLOOR_MULTIPLE * getMinPaidPlanAmount(plan.currency);
|
|
25853
|
+
return plan.type !== "free-access" && hasMinorUnit(plan.currency) && Number.isInteger(plan.amount) && plan.amount >= CENTS_SUSPECT_FLOOR_MULTIPLE * getMinPaidPlanAmount(plan.currency);
|
|
25706
25854
|
}
|
|
25707
25855
|
function formatCentsWarning(plan, index) {
|
|
25708
|
-
const asCents = (0,
|
|
25856
|
+
const asCents = (0, import_currency5.default)(plan.amount).divide(100).value;
|
|
25709
25857
|
return `plans.${index} ("${plan.name}"): amount ${plan.amount} is stored as ${formatPrice(plan.amount, plan.currency)}, because amounts are in major units, not cents. If the user meant ${formatPrice(asCents, plan.currency)}, call update_plan with amount ${asCents} before sharing the checkout link; otherwise no action is needed.`;
|
|
25710
25858
|
}
|
|
25859
|
+
function hasMinorUnit(currencyCode3) {
|
|
25860
|
+
try {
|
|
25861
|
+
return currencyFractionDigits(currencyCode3) > 0;
|
|
25862
|
+
} catch {
|
|
25863
|
+
return true;
|
|
25864
|
+
}
|
|
25865
|
+
}
|
|
25711
25866
|
|
|
25712
25867
|
// src/tools/product/create-product.tool.ts
|
|
25713
25868
|
var backendCreatedProductShapeSchema = external_exports.object({
|
|
@@ -27810,6 +27965,7 @@ var DEFAULT_ALLOWED_MCP_ORIGIN_HOSTNAMES = [
|
|
|
27810
27965
|
"grok.com",
|
|
27811
27966
|
"www.grok.com",
|
|
27812
27967
|
"x.ai",
|
|
27968
|
+
"antigravity.google",
|
|
27813
27969
|
"gemini.google.com",
|
|
27814
27970
|
"aistudio.google.com",
|
|
27815
27971
|
"cursor.com",
|