run402-mcp
Version:
MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.
166 lines • 7.27 kB
JavaScript
/**
* `projects.stripe` namespace — the organization's Stripe account for a
* project's app, and Checkout / customer-portal sessions on it.
*
* The organization that owns the project holds its own Stripe account,
* connected to Run402's platform; Run402 charges nothing on it (zero platform
* fee) and never holds the money. Every call here is a thin, typed request to
* the project-scoped `/projects/v1/:project_id/stripe/*` routes: the gateway
* owns account state, body validation, the return-URL allowlist, and
* idempotency, and its `next_actions` and error codes surface unchanged.
*
* Two methods hand out a one-time Stripe-hosted onboarding link meant for the
* account holder, not a payer: `account.connect` (while onboarding is
* incomplete) and `onboardingLinks.create`. They are secret-returning, so the
* MCP `run` sandbox refuses them and names the CLI command. Checkout and
* portal URLs are meant to be handed to a payer and run on every surface.
* Nothing here caches a URL.
*/
import { gateSecret } from "../secret-gate.js";
function stripePath(projectId, suffix) {
return `/projects/v1/${encodeURIComponent(projectId)}/stripe/${suffix}`;
}
function authMeta(method, projectId) {
return { method, target: { project_id: projectId } };
}
function defined(entries) {
const out = {};
for (const [key, value] of Object.entries(entries))
if (value !== undefined)
out[key] = value;
return out;
}
/** `r.projects.stripe.account` — connect, read, and detach the organization's account. */
export class ProjectStripeAccount {
client;
constructor(client) {
this.client = client;
}
/**
* Connect the organization's Stripe account, or return the one it already
* has (idempotent per organization). While onboarding is incomplete the
* result carries a one-time `onboarding` link for the account holder.
*/
async connect(projectId, input = {}) {
gateSecret(this.client, "projects.stripe.account.connect", projectId, input);
return this.client.request(stripePath(projectId, "account"), {
method: "POST",
body: defined({
country: input.country,
business_type: input.businessType,
business_name: input.businessName,
support_email: input.supportEmail,
statement_descriptor: input.statementDescriptor,
email: input.email,
}),
authMeta: authMeta("projects.stripe.account.connect", projectId),
context: "connecting Stripe account",
});
}
/** The organization's account, read fresh from Stripe. Never carries a link. */
async get(projectId) {
return this.client.request(stripePath(projectId, "account"), {
method: "GET",
authMeta: authMeta("projects.stripe.account.get", projectId),
context: "reading Stripe account",
});
}
/** Detach the account from the platform. The Stripe account itself is unaffected. */
async disconnect(projectId) {
return this.client.request(stripePath(projectId, "account"), {
method: "DELETE",
authMeta: authMeta("projects.stripe.account.disconnect", projectId),
context: "disconnecting Stripe account",
});
}
}
/** `r.projects.stripe.onboardingLinks` — one-time Stripe-hosted onboarding links. */
export class ProjectStripeOnboardingLinks {
client;
constructor(client) {
this.client = client;
}
/** Mint a one-time onboarding (or update) link for the account holder. */
async create(projectId, input = {}) {
gateSecret(this.client, "projects.stripe.onboardingLinks.create", projectId, input);
return this.client.request(stripePath(projectId, "onboarding-links"), {
method: "POST",
body: defined({ return_url: input.returnUrl, refresh_url: input.refreshUrl }),
authMeta: authMeta("projects.stripe.onboardingLinks.create", projectId),
context: "minting Stripe onboarding link",
});
}
}
/** `r.projects.stripe.checkoutSessions` — Checkout Sessions on the organization's account. */
export class ProjectStripeCheckoutSessions {
client;
constructor(client) {
this.client = client;
}
/**
* Create a Checkout Session and get the URL to send the payer to. Pass
* `idempotencyKey` so a retry returns the original session (`deduplicated`).
*/
async create(projectId, input) {
const { idempotencyKey, ...rest } = input;
return this.client.request(stripePath(projectId, "checkout-sessions"), {
method: "POST",
body: defined({
mode: rest.mode,
line_items: rest.lineItems,
success_url: rest.successUrl,
cancel_url: rest.cancelUrl,
customer: rest.customer,
customer_email: rest.customerEmail,
client_reference_id: rest.clientReferenceId,
metadata: rest.metadata,
automatic_tax: rest.automaticTax,
subscription: rest.subscription,
locale: rest.locale,
allow_promotion_codes: rest.allowPromotionCodes,
expires_at: rest.expiresAt,
}),
...(idempotencyKey ? { headers: { "Idempotency-Key": idempotencyKey } } : {}),
authMeta: authMeta("projects.stripe.checkoutSessions.create", projectId),
context: "creating Stripe checkout session",
});
}
/** A session's outcome (`status`, `payment_status`, ids), read from Stripe. */
async get(projectId, checkoutSessionId) {
return this.client.request(stripePath(projectId, `checkout-sessions/${encodeURIComponent(checkoutSessionId)}`), {
method: "GET",
authMeta: authMeta("projects.stripe.checkoutSessions.get", projectId),
context: "reading Stripe checkout session",
});
}
}
/** `r.projects.stripe.portalSessions` — the organization's Stripe customer portal. */
export class ProjectStripePortalSessions {
client;
constructor(client) {
this.client = client;
}
/** Open the customer portal for an existing customer; send them to `url`. */
async create(projectId, input) {
return this.client.request(stripePath(projectId, "portal-sessions"), {
method: "POST",
body: { customer: input.customer, return_url: input.returnUrl },
authMeta: authMeta("projects.stripe.portalSessions.create", projectId),
context: "creating Stripe portal session",
});
}
}
/** `r.projects.stripe` — the organization's Stripe account and sessions for a project. */
export class ProjectStripe {
account;
onboardingLinks;
checkoutSessions;
portalSessions;
constructor(client) {
this.account = new ProjectStripeAccount(client);
this.onboardingLinks = new ProjectStripeOnboardingLinks(client);
this.checkoutSessions = new ProjectStripeCheckoutSessions(client);
this.portalSessions = new ProjectStripePortalSessions(client);
}
}
//# sourceMappingURL=stripe.js.map