UNPKG

@paykit-sdk/stripe

Version:
998 lines (993 loc) 34.4 kB
import { schema, Schema, validateRequiredKeys, AbstractPayKitProvider, createCheckoutSchema, ValidationError, stringifyMetadataValues, isIdCustomer, isEmailCustomer, updateCheckoutSchema, ResourceNotFoundError, createCustomerSchema, parseCustomerName, createSubscriptionSchema, InvalidTypeError, updateSubscriptionSchema, retrieveSubscriptionSchema, createPaymentSchema, PAYKIT_METADATA_KEY, updatePaymentSchema, retrievePaymentSchema, tryCatchAsync, omitInternalMetadata, deletePaymentSchema, capturePaymentSchema, createRefundSchema, refundReasonMatcher, WebhookError, paykitEvent$InboundSchema, billingModeSchema, invoiceStatusSchema } from '@paykit-sdk/core'; import Stripe from 'stripe'; // src/index.ts var Checkout$inboundSchema = (checkout, lineItems) => { let customer = null; if (typeof checkout.customer === "string") customer = { id: checkout.customer }; else if (checkout.customer?.id) customer = { id: checkout.customer.id }; else if (checkout.customer_email) customer = { email: checkout.customer_email }; return { id: checkout.id, customer, session_type: checkout.mode === "subscription" ? "recurring" : "one_time", payment_url: checkout.url, products: lineItems.map((item) => ({ id: item.id, quantity: item.quantity })), currency: checkout.currency, amount: checkout.amount_total, subscription: null, metadata: omitInternalMetadata(checkout.metadata ?? {}) }; }; var Customer$inboundSchema = (customer) => { return { id: customer.id, email: customer.email ?? "", name: customer.name ?? "", phone: customer.phone ?? null, metadata: omitInternalMetadata(customer.metadata ?? {}), created_at: new Date(customer.created * 1e3), updated_at: null, custom_fields: { default_payment_method: typeof customer.invoice_settings?.default_payment_method === "string" ? customer.invoice_settings.default_payment_method : customer.invoice_settings?.default_payment_method?.id ?? null, balance: customer.balance, currency: customer.currency ?? null, delinquent: customer.delinquent ?? false } }; }; var Subscription$inboundSchema = (subscription) => { const subscriptionStatusMap = { active: "active", trialing: "trialing", incomplete_expired: "expired", incomplete: "past_due", past_due: "past_due", canceled: "canceled", unpaid: "past_due", paused: "past_due" }; const status = subscriptionStatusMap[subscription.status] ?? "pending"; const metadata = omitInternalMetadata(subscription.metadata ?? {}); return { id: subscription.id, status, customer: typeof subscription.customer === "string" ? { id: subscription.customer } : subscription.customer?.id ? { id: subscription.customer.id } : null, amount: subscription.items.data[0].price.unit_amount, currency: subscription.items.data[0].price.currency, item_id: subscription.items.data[0].id, billing_interval: subscription.items.data[0].price.recurring?.interval, current_period_start: new Date(subscription.start_date), current_period_end: new Date(subscription.cancel_at), metadata, custom_fields: null, requires_action: false, payment_url: null }; }; var Invoice$inboundSchema = (invoice) => { const status = (() => { if (invoice.status == "paid") return "paid"; if (["draft", "open", "uncollectible", "void"].includes( invoice.status )) return "open"; throw new Error(`Unknown status: ${invoice.status}`); })(); const customerId = (() => { if (typeof invoice.customer === "string") return invoice.customer; if (invoice.customer?.id) return invoice.customer.id; throw new Error( `Unknown customer ID: ${String(invoice.customer)}` ); })(); return { id: invoice.id, currency: invoice.currency, customer: { id: customerId }, billing_mode: invoice.billingMode, amount_paid: invoice.amount_paid, line_items: invoice.lines.data.map((line) => ({ id: line.id, quantity: line.quantity })), subscription_id: invoice.parent?.subscription_details?.subscription?.toString() ?? null, status, paid_at: new Date(invoice.created * 1e3).toISOString(), metadata: omitInternalMetadata(invoice.metadata ?? {}), custom_fields: invoice.custom_fields?.reduce( (acc, field) => { acc[field.name] = field.value; return acc; }, {} ) ?? null }; }; var Payment$inboundSchema = (intent) => { const itemId = JSON.parse( intent.metadata?.[PAYKIT_METADATA_KEY] ?? "{}" ).itemId; const statusMap = { requires_payment_method: "pending", requires_confirmation: "pending", requires_action: "requires_action", requires_capture: "requires_capture", succeeded: "succeeded", canceled: "canceled", processing: "processing" }; const status = statusMap[intent.status]; const requiresAction = intent.status === "requires_action" || intent.status === "requires_confirmation"; return { id: intent.id, amount: intent.amount, currency: intent.currency, customer: intent.customer ? { id: intent.customer } : null, status, metadata: omitInternalMetadata(intent.metadata ?? {}), item_id: itemId, requires_action: requiresAction, payment_url: intent.next_action?.redirect_to_url?.url ?? null }; }; var Refund$inboundSchema = (refund) => { return { id: refund.id, amount: refund.amount, currency: refund.currency, reason: refund.reason, metadata: omitInternalMetadata(refund.metadata ?? {}) }; }; // src/stripe-provider.ts var StripeOptions$inboundSchema = schema()( Schema.object({ apiKey: Schema.string(), debug: Schema.boolean().optional() }).passthrough() ); var providerName = "stripe"; var StripeProvider = class extends AbstractPayKitProvider { constructor(opts) { super(StripeOptions$inboundSchema, opts, providerName); this.providerName = providerName; this.createCheckout = async (params) => { const { error, data } = createCheckoutSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "createCheckout" ); } const checkoutMetadata = stringifyMetadataValues( data.metadata ?? {} ); const checkoutOptions = { ...data.provider_metadata, mode: data.session_type === "one_time" ? "payment" : "subscription", line_items: [{ price: data.item_id, quantity: data.quantity }], success_url: data.success_url, cancel_url: data.cancel_url }; if (params.session_type == "recurring") { checkoutOptions.subscription_data = { metadata: checkoutMetadata }; } else if (params.session_type == "one_time") { checkoutOptions.metadata = checkoutMetadata; checkoutOptions.payment_intent_data = { setup_future_usage: "off_session" }; } if (isIdCustomer(params.customer)) { checkoutOptions.customer = String(params.customer.id); } else if (isEmailCustomer(params.customer)) { checkoutOptions.customer_email = params.customer.email; } if (params.billing) { checkoutOptions.shipping_address_collection = { allowed_countries: [ params.billing.address.country ] }; checkoutOptions.shipping_options = [ { shipping_rate: params.billing.carrier, shipping_rate_data: { display_name: params.billing.carrier ?? "", fixed_amount: { amount: 0, currency: params.billing.currency } } } ]; } const checkout = await this.stripe.checkout.sessions.create(checkoutOptions); return Checkout$inboundSchema(checkout, [ { id: params.item_id, quantity: params.quantity } ]); }; this.retrieveCheckout = async (id) => { const checkout = await this.stripe.checkout.sessions.retrieve( id, { expand: ["line_items"] } ); return Checkout$inboundSchema( checkout, checkout.line_items?.data.map((item) => ({ id: item.price?.id ?? "", quantity: item.quantity ?? 0 })) ?? [] ); }; this.updateCheckout = async (id, params) => { const { error, data } = updateCheckoutSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "updateCheckout" ); } const originalCheckout = await this.stripe.checkout.sessions.retrieve(id, { expand: ["line_items"] }); if (!originalCheckout) { throw new ResourceNotFoundError( "checkout", id, this.providerName ); } const updatedCheckout = await this.stripe.checkout.sessions.update(id, { ...data, metadata: stringifyMetadataValues(data.metadata ?? {}) }); return Checkout$inboundSchema( updatedCheckout, updatedCheckout.line_items?.data.map((item) => ({ id: item.price?.id ?? "", quantity: item.quantity ?? 0 })) ?? [] ); }; this.deleteCheckout = async (id) => { await this.stripe.checkout.sessions.expire(id); return null; }; /** * Customer management */ this.createCustomer = async (params) => { const { error, data } = createCustomerSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "createCustomer" ); } const { fullName } = parseCustomerName({ name: data.name, email: data.email }); const { billing: _billing, metadata, provider_metadata, name: _name, ...stripeParams } = data; const customer = await this.stripe.customers.create({ ...provider_metadata, ...stripeParams, phone: data.phone ?? void 0, name: fullName, metadata: stringifyMetadataValues(metadata ?? {}) }); return Customer$inboundSchema(customer); }; this.updateCustomer = async (id, params) => { const { provider_metadata, billing: _billing, metadata, ...rest } = params; const customer = await this.stripe.customers.update(id, { ...provider_metadata, ...rest, metadata: stringifyMetadataValues(metadata ?? {}), phone: rest.phone ?? void 0 }); return Customer$inboundSchema(customer); }; this.deleteCustomer = async (id) => { await this.stripe.customers.del(id); return null; }; this.retrieveCustomer = async (id) => { const customer = await this.stripe.customers.retrieve(id); if ("deleted" in customer) return null; return Customer$inboundSchema(customer); }; this.createSubscription = async (params) => { const { error, data } = createSubscriptionSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "createSubscription" ); } if (!isEmailCustomer(data.customer) && !isIdCustomer(data.customer)) { throw new InvalidTypeError( "customer", "object with email or id", "object", { provider: this.providerName, method: "createSubscription" } ); } if (this.opts.debug) { console.info( "Creating subscription with default payment method, this can be overridden by passing `default_payment_method` in the provider_metadata e.g pm_xxx" ); } const defaultPaymentMethod = data.provider_metadata?.default_payment_method; const subscription = await this.stripe.subscriptions.create({ ...data.provider_metadata, ...defaultPaymentMethod && { default_payment_method: defaultPaymentMethod }, customer: isIdCustomer(data.customer) ? String(data.customer.id) : data.customer.email, items: [{ price: data.item_id, quantity: data.quantity }], metadata: stringifyMetadataValues(data.metadata ?? {}), payment_behavior: "default_incomplete" // customer's default payment method will be used if available }); return Subscription$inboundSchema(subscription); }; this.cancelSubscription = async (id) => { const subscription = await this.stripe.subscriptions.cancel(id); return Subscription$inboundSchema(subscription); }; this.updateSubscription = async (id, params) => { const { error, data } = updateSubscriptionSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "updateSubscription" ); } const updatedSubscription = await this.stripe.subscriptions.update(id, { ...data.provider_metadata && { ...data.provider_metadata }, metadata: stringifyMetadataValues(data.metadata ?? {}) }); return Subscription$inboundSchema(updatedSubscription); }; this.retrieveSubscription = async (id) => { const { error, data } = retrieveSubscriptionSchema.safeParse({ id }); if (error) { throw ValidationError.fromZodError( error, this.providerName, "retrieveSubscription" ); } const subscription = await this.stripe.subscriptions.retrieve( data.id ); return Subscription$inboundSchema(subscription); }; this.deleteSubscription = async (id) => { const { error, data } = retrieveSubscriptionSchema.safeParse({ id }); if (error) { throw ValidationError.fromZodError( error, this.providerName, "deleteSubscription" ); } await this.stripe.subscriptions.cancel(data.id); return null; }; this.createPayment = async (params) => { const { error, data } = createPaymentSchema.safeParse(params); if (error) throw new ValidationError(error.message.split("\n").join(" "), { provider: this.providerName, method: "createPayment" }); const { provider_metadata, customer, capture_method, ...rest } = data; const createCheckoutSession = async (customerEmail, customerId2) => { const checkoutMetadata = stringifyMetadataValues({ ...rest.metadata, ...provider_metadata?.metadata ?? {}, [PAYKIT_METADATA_KEY]: JSON.stringify({ itemId: data.item_id ?? null }) }); const { success_url } = validateRequiredKeys( ["success_url"], provider_metadata, "The following fields must be present in the provider_metadata of createPayment: {keys}" ); const checkoutOptions = { mode: "payment", line_items: [ { price_data: { currency: rest.currency, product_data: { name: `Payment for ${data.item_id || "item"}` }, unit_amount: rest.amount }, quantity: 1 } ], metadata: checkoutMetadata, payment_intent_data: { capture_method, setup_future_usage: "off_session" }, success_url }; if (customerId2) checkoutOptions.customer = customerId2; else if (customerEmail) checkoutOptions.customer_email = customerEmail; const checkoutSession = await this.stripe.checkout.sessions.create(checkoutOptions); return { id: checkoutSession.id, amount: rest.amount, currency: rest.currency, customer: customerId2 ? { id: customerId2 } : customerEmail ? { email: customerEmail } : null, status: "pending", metadata: omitInternalMetadata(checkoutMetadata), item_id: data.item_id ?? null, requires_action: true, payment_url: checkoutSession.url }; }; let customerId; if (isEmailCustomer(customer)) { const existingCustomers = await this.stripe.customers.list({ email: customer.email, limit: 1 }); if (existingCustomers.data.length > 0) customerId = existingCustomers.data[0].id; else return createCheckoutSession(customer.email); } else if (isIdCustomer(customer)) { customerId = String(customer.id); } else throw new InvalidTypeError( "customer", "object with email or id", typeof customer, { provider: this.providerName, method: "createPayment" } ); const customerWithDefaultPaymentMethod = await this.stripe.customers.retrieve(customerId, { expand: ["invoice_settings.default_payment_method"] }); if ("deleted" in customerWithDefaultPaymentMethod) { throw new ValidationError("Customer has been deleted", { provider: this.providerName, method: "createPayment" }); } let defaultPaymentMethod = customerWithDefaultPaymentMethod.invoice_settings?.default_payment_method; if (!defaultPaymentMethod) { const paymentMethods = await this.stripe.paymentMethods.list({ customer: customerId }); if (paymentMethods.data.length === 0) { return createCheckoutSession(void 0, customerId); } defaultPaymentMethod = paymentMethods.data[0].id; } const paymentMetadata = stringifyMetadataValues({ ...rest.metadata, ...provider_metadata?.metadata ?? {}, [PAYKIT_METADATA_KEY]: JSON.stringify({ itemId: data.item_id ?? null }) }); const paymentIntentOptions = { currency: rest.currency, amount: rest.amount, metadata: paymentMetadata, customer: customerId, capture_method, confirm: true, payment_method: defaultPaymentMethod, off_session: true }; if (data.billing) { paymentIntentOptions.shipping = { name: data.billing.address.name, phone: data.billing.address.phone, address: { line1: data.billing.address.line1, line2: data.billing.address.line2, city: data.billing.address.city, state: data.billing.address.state, postal_code: data.billing.address.postal_code, country: data.billing.address.country } }; } const payment = await this.stripe.paymentIntents.create( paymentIntentOptions ); return Payment$inboundSchema(payment); }; this.updatePayment = async (id, params) => { const { error, data } = updatePaymentSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "updatePayment" ); } const { provider_metadata, customer, ...rest } = data; const piId = await this._resolvePaymentIntentId(id); const payment = await this.stripe.paymentIntents.retrieve(piId); const paymentOptions = { ...provider_metadata, metadata: stringifyMetadataValues({ ...rest.metadata, ...provider_metadata?.metadata ?? {} }) }; if ([ "requires_payment_method", "requires_confirmation", "requires_action" ].includes(payment.status)) { paymentOptions.amount = rest.amount; paymentOptions.currency = rest.currency; } if (isIdCustomer(customer)) { paymentOptions.customer = String(customer.id); } else if (isEmailCustomer(customer)) { paymentOptions.receipt_email = customer.email; } const updatedPayment = await this.stripe.paymentIntents.update( piId, paymentOptions ); return { ...Payment$inboundSchema(updatedPayment), id }; }; this.retrievePayment = async (id) => { const { error, data } = retrievePaymentSchema.safeParse({ id }); if (error) { throw ValidationError.fromZodError( error, this.providerName, "retrievePayment" ); } if (data.id.startsWith("cs_")) { const [session, sessionError] = await tryCatchAsync( this.stripe.checkout.sessions.retrieve(data.id, { expand: ["payment_intent"] }) ); if (!session || sessionError?.code === "resource_missing") return null; if (session.payment_intent && typeof session.payment_intent !== "string") { return { ...Payment$inboundSchema( session.payment_intent ), id: data.id }; } return { id: data.id, amount: session.amount_total ?? 0, currency: session.currency ?? "", customer: typeof session.customer === "string" ? { id: session.customer } : session.customer?.id ? { id: session.customer.id } : session.customer_email ? { email: session.customer_email } : null, status: "pending", metadata: omitInternalMetadata(session.metadata ?? {}), item_id: null, requires_action: true, payment_url: session.url }; } const [payment, paymentError] = await tryCatchAsync( this.stripe.paymentIntents.retrieve(data.id) ); if (!payment || paymentError?.code === "resource_missing") return null; return Payment$inboundSchema(payment); }; this.deletePayment = async (id) => { const { error, data } = deletePaymentSchema.safeParse({ id }); if (error) { throw ValidationError.fromZodError( error, this.providerName, "deletePayment" ); } if (data.id.startsWith("cs_")) { await this.stripe.checkout.sessions.expire(data.id); return null; } await this.stripe.paymentIntents.cancel(data.id); return null; }; this.capturePayment = async (id, params) => { const { error, data } = capturePaymentSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "capturePayment" ); } const piId = await this._resolvePaymentIntentId(id); const payment = await this.stripe.paymentIntents.capture(piId, { amount_to_capture: data.amount }); return { ...Payment$inboundSchema(payment), id }; }; this.cancelPayment = async (id) => { if (id.startsWith("cs_")) { const session = await this.stripe.checkout.sessions.expire(id); return { id, amount: session.amount_total ?? 0, currency: session.currency ?? "", customer: typeof session.customer === "string" ? { id: session.customer } : session.customer?.id ? { id: session.customer.id } : session.customer_email ? { email: session.customer_email } : null, status: "canceled", metadata: omitInternalMetadata(session.metadata ?? {}), item_id: null, requires_action: false, payment_url: null }; } const canceledPayment = await this.stripe.paymentIntents.cancel(id); return Payment$inboundSchema(canceledPayment); }; /** * Refund management */ this.createRefund = async (params) => { const { error, data } = createRefundSchema.safeParse(params); if (error) { throw ValidationError.fromZodError( error, this.providerName, "createRefund" ); } const { provider_metadata, ...rest } = data; const reason = refundReasonMatcher(rest.reason ?? ""); const reasonMap = { duplicate: "duplicate", fraudulent: "fraudulent", requested_by_customer: "requested_by_customer", other: "requested_by_customer" }; const stripeRefundOptions = { ...provider_metadata, reason: reasonMap[reason], amount: rest.amount, metadata: stringifyMetadataValues(rest.metadata ?? {}) }; if (data.payment_id.startsWith("cs_")) { const session = await this.stripe.checkout.sessions.retrieve( data.payment_id ); if (!session.payment_intent) { throw new ValidationError( "Checkout Session has no associated PaymentIntent to refund", { provider: this.providerName, method: "createRefund" } ); } stripeRefundOptions.payment_intent = typeof session.payment_intent === "string" ? session.payment_intent : session.payment_intent.id; } else if (data.payment_id.startsWith("pi_")) { stripeRefundOptions.payment_intent = data.payment_id; } else { stripeRefundOptions.charge = data.payment_id; } const refund = await this.stripe.refunds.create( stripeRefundOptions ); return Refund$inboundSchema(refund); }; this.handleWebhook = async (params, webhookSecret) => { if (!webhookSecret) { throw new WebhookError( "webhookSecret is required for Stripe webhook verification", { provider: this.providerName } ); } const { body, headersAsObject } = params; const stripeSignature = headersAsObject["stripe-signature"]; if (!stripeSignature) { throw new WebhookError("Missing Stripe signature", { provider: this.providerName }); } const event = this.stripe.webhooks.constructEvent( body, stripeSignature, webhookSecret ); const results = []; results.push({ id: event.id, type: `stripe.${event.type}`, created: event.created, data: event, is_raw: true }); const processStandardMapping = async (ev) => { switch (ev.type) { case "checkout.session.completed": { const data = ev.data.object; if (data.mode !== "payment") return null; const customFields = data.custom_fields.reduce( (acc, field) => { if (field.type === "dropdown") acc[field.key] = field.dropdown?.value; else if (field.type === "text") acc[field.key] = field.text?.value; else if (field.type === "numeric") acc[field.key] = field.numeric?.value; return acc; }, {} ); const invoiceData = { id: data.id, status: invoiceStatusSchema.parse("paid"), paid_at: new Date(ev.created * 1e3).toISOString(), amount_paid: data.amount_total ?? 0, currency: data.currency ?? "", metadata: omitInternalMetadata(data.metadata ?? {}), customer: { id: typeof data.customer === "string" ? data.customer : data.customer?.id ?? "" }, billing_mode: billingModeSchema.parse("one_time"), subscription_id: null, custom_fields: customFields ?? null, line_items: data.line_items?.data.map((item) => ({ id: item.price.id, quantity: item.quantity })) ?? [] }; return [ paykitEvent$InboundSchema({ type: "invoice.generated", created: ev.created, id: ev.id, data: invoiceData }) ]; } case "invoice.paid": { const data = ev.data.object; const relevantBillingReasons = [ "subscription_create", "subscription_cycle" ]; if (data.status !== "paid" || !relevantBillingReasons.includes(data.billing_reason)) { return null; } return [ paykitEvent$InboundSchema({ type: "invoice.generated", created: ev.created, id: ev.id, data: Invoice$inboundSchema({ ...data, billingMode: "recurring" }) }) ]; } case "customer.created": return [ paykitEvent$InboundSchema({ type: "customer.created", created: ev.created, id: ev.id, data: Customer$inboundSchema( ev.data.object ) }) ]; case "customer.updated": return [ paykitEvent$InboundSchema({ type: "customer.updated", created: ev.created, id: ev.id, data: Customer$inboundSchema( ev.data.object ) }) ]; case "customer.deleted": return [ paykitEvent$InboundSchema({ type: "customer.deleted", created: ev.created, id: ev.id, data: null }) ]; case "customer.subscription.created": return [ paykitEvent$InboundSchema({ type: "subscription.created", created: ev.created, id: ev.id, data: Subscription$inboundSchema( ev.data.object ) }) ]; case "customer.subscription.updated": return [ paykitEvent$InboundSchema({ type: "subscription.updated", created: ev.created, id: ev.id, data: Subscription$inboundSchema( ev.data.object ) }) ]; case "customer.subscription.deleted": return [ paykitEvent$InboundSchema({ type: "subscription.canceled", created: ev.created, id: ev.id, data: null }) ]; case "payment_intent.created": return [ paykitEvent$InboundSchema({ type: "payment.created", created: ev.created, id: ev.id, data: Payment$inboundSchema( ev.data.object ) }) ]; case "payment_intent.succeeded": return [ paykitEvent$InboundSchema({ type: "payment.succeeded", created: ev.created, id: ev.id, data: Payment$inboundSchema( ev.data.object ) }) ]; case "payment_intent.canceled": case "payment_intent.payment_failed": return [ paykitEvent$InboundSchema({ type: "payment.failed", created: ev.created, id: ev.id, data: Payment$inboundSchema( ev.data.object ) }) ]; case "payment_intent.processing": case "payment_intent.requires_action": case "payment_intent.amount_capturable_updated": case "payment_intent.partially_funded": return [ paykitEvent$InboundSchema({ type: "payment.updated", created: ev.created, id: ev.id, data: Payment$inboundSchema( ev.data.object ) }) ]; case "refund.created": return [ paykitEvent$InboundSchema({ type: "refund.created", created: ev.created, id: ev.id, data: Refund$inboundSchema( ev.data.object ) }) ]; default: return null; } }; const standardMappedEvents = await processStandardMapping(event); if (standardMappedEvents) { results.push(...standardMappedEvents); } else { if (this.opts.debug) { console.info( `No standard mapping found for Stripe event: ${event.type}. It is still available as a raw event.` ); } } return results; }; const { debug, apiKey, isSandbox, ...stripeConfig } = opts; const resolvedSandbox = isSandbox ?? apiKey.includes("_test_"); this.stripe = new Stripe(apiKey, stripeConfig); this.opts = { ...opts, debug: debug ?? resolvedSandbox }; this.isSandbox = resolvedSandbox; } get _native() { return this.stripe; } async _resolvePaymentIntentId(id) { if (!id.startsWith("cs_")) return id; const session = await this.stripe.checkout.sessions.retrieve(id); if (!session.payment_intent) { throw new ValidationError( "Checkout Session has no associated PaymentIntent yet", { provider: this.providerName, method: "resolvePaymentIntentId" } ); } return typeof session.payment_intent === "string" ? session.payment_intent : session.payment_intent.id; } }; // src/index.ts var createStripe = (config) => { return new StripeProvider(config); }; var stripe = () => { const envVars = validateRequiredKeys( ["STRIPE_API_KEY"], process.env ?? {}, "Missing required environment variables: {keys}" ); return createStripe({ apiKey: envVars.STRIPE_API_KEY, apiVersion: "2025-07-30.basil", isSandbox: envVars.STRIPE_API_KEY.includes("_test_") || process.env.NODE_ENV !== "production" }); }; export { createStripe, stripe };