UNPKG

cosmic-payments

Version:

A payments library for cosmic.new. Designed to be used and deployed on cosmic.new

277 lines (276 loc) 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createCosmicPaymentsHandler = createCosmicPaymentsHandler; const server_1 = require("next/server"); /** * Creates a Next.js API route handler for Cosmic Payments * * @example * ```ts * // app/api/cosmic-payments/route.ts * import { createCosmicPaymentsHandler } from 'cosmic-payments/server'; * import { getServerSession } from '@/lib/auth'; * * const handler = createCosmicPaymentsHandler({ * getServerSession * }); * * export const POST = handler; * ``` */ function createCosmicPaymentsHandler(options) { const { getServerSession } = options; // Validate required environment variables at initialization const COSMIC_PAYMENTS_SECRET = process.env.COSMIC_PAYMENTS_SECRET; if (!COSMIC_PAYMENTS_SECRET) { throw new Error('❌ COSMIC PAYMENTS ERROR: COSMIC_PAYMENTS_SECRET environment variable is required'); } // TypeScript now knows this is defined const SECRET = COSMIC_PAYMENTS_SECRET; async function getEnvironmentVariables() { const projectId = process.env.NEXT_PUBLIC_CLIENT_ID; const stripeConnectId = process.env.STRIPE_CONNECT_ID; const userId = process.env.USER_ID; if (!projectId) { throw new Error('❌ COSMIC PAYMENTS ERROR: NEXT_PUBLIC_CLIENT_ID environment variable is required'); } if (!userId) { throw new Error('❌ COSMIC PAYMENTS ERROR: USER_ID environment variable is required'); } return { projectId, stripeConnectId, userId }; } async function getBillingPortal(request) { // User authentication mandatory const session = await getServerSession(); if (!session?.uid) { return server_1.NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } // Get environment variables, which are guaranteed to be set in the environment const { projectId, stripeConnectId } = await getEnvironmentVariables(); // Get extract return URL from the origin of the request const returnUrl = new URL(request.headers.get('referer') || ''); if (!returnUrl) { return server_1.NextResponse.json({ error: 'Return URL is required' }, { status: 400 }); } const payload = { customer_id: session.uid, return_url: returnUrl, user_id: session.uid, project_id: projectId, is_live: process.env.NODE_ENV !== 'development', }; // Only include stripe_connect_id if NODE_ENV is not 'development' if (process.env.NODE_ENV !== 'development') { payload.stripe_connect_id = stripeConnectId; } // Call the Cosmic API to create billing portal session const cosmicResponse = await fetch('https://api.cosmic.new/cosmic-payments/create-billing-portal-session', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Cosmic-Payments-Secret': SECRET, }, body: JSON.stringify(payload), }); if (!cosmicResponse.ok) { return server_1.NextResponse.json({ error: 'Failed to create billing portal session' }, { status: 500 }); } const cosmicData = await cosmicResponse.json(); const billingPortalLink = cosmicData.billing_portal_link; if (!billingPortalLink) { return server_1.NextResponse.json({ error: 'No billing portal link returned from API' }, { status: 500 }); } return server_1.NextResponse.json({ url: billingPortalLink }); } async function createCheckoutLink(request, body) { const session = await getServerSession(); // Get allowGuestCheckout from the request body, default to false const { allowGuestCheckout = false } = body; // User authentication optional depending on allowGuestCheckout if (!allowGuestCheckout) { if (!session?.uid) { return server_1.NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } } // Get environment variables, which are guaranteed to be set in the environment const { projectId, stripeConnectId, userId } = await getEnvironmentVariables(); // Get the origin from the request const origin = new URL(request.headers.get('referer') || '').origin; // Get custom paths from the request body, or use defaults const { successPath = '/payments/success', cancelPath = '/payments/cancel' } = body; // Ensure paths start with '/' const normalizedSuccessPath = successPath.startsWith('/') ? successPath : `/${successPath}`; const normalizedCancelPath = cancelPath.startsWith('/') ? cancelPath : `/${cancelPath}`; // Create full URLs const successUrl = origin + normalizedSuccessPath; const cancelUrl = origin + normalizedCancelPath; // Get action specific parameters from the request body const { priceId, productId, quantity = 1 } = body; if (!priceId) { return server_1.NextResponse.json({ error: 'Price ID is required' }, { status: 400 }); } if (!productId) { return server_1.NextResponse.json({ error: 'Product ID is required' }, { status: 400 }); } if (!quantity) { return server_1.NextResponse.json({ error: 'Quantity is required' }, { status: 400 }); } const payload = { project_id: projectId, price_id: priceId, product_id: productId, quantity: quantity, success_url: successUrl, cancel_url: cancelUrl, parent_client_id: userId, is_live: process.env.NODE_ENV !== 'development', }; // Only include stripe_connect_id if NODE_ENV is not 'development' if (process.env.NODE_ENV !== 'development') { payload.stripe_connect_id = stripeConnectId; } if (session?.uid) { payload.user_id = session.uid; } // Call the Cosmic API to create checkout link const cosmicResponse = await fetch('https://api.cosmic.new/cosmic-payments/create-checkout-link', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Cosmic-Payments-Secret': SECRET, }, body: JSON.stringify(payload), }); if (!cosmicResponse.ok) { return server_1.NextResponse.json({ error: 'Failed to create checkout link' }, { status: 500 }); } const cosmicData = await cosmicResponse.json(); const checkoutLink = cosmicData.checkout_link; if (!checkoutLink) { return server_1.NextResponse.json({ error: 'No checkout link returned from API' }, { status: 500 }); } return server_1.NextResponse.json({ url: checkoutLink }); } async function getAllSubscriptionProducts() { // User authentication not required const { projectId, userId } = await getEnvironmentVariables(); // Call the Cosmic API with a GET request with query parameters const cosmicResponse = await fetch(`https://api.cosmic.new/cosmic-payments/get-all-subscription-products?project_id=${projectId}&parent_client_id=${userId}`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Cosmic-Payments-Secret': SECRET, }, }); if (!cosmicResponse.ok) { return server_1.NextResponse.json({ error: 'Failed to get all subscription products' }, { status: 500 }); } const cosmicData = await cosmicResponse.json(); const products = cosmicData.subscription_products; return server_1.NextResponse.json({ products }); } async function getAllNonSubscriptionProducts() { // User authentication not required const { projectId, userId } = await getEnvironmentVariables(); // Call the Cosmic API with a GET request with query parameters const cosmicResponse = await fetch(`https://api.cosmic.new/cosmic-payments/get-all-non-subscription-products?project_id=${projectId}&parent_client_id=${userId}`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Cosmic-Payments-Secret': SECRET, }, }); if (!cosmicResponse.ok) { return server_1.NextResponse.json({ error: 'Failed to get all non-subscription products' }, { status: 500 }); } const cosmicData = await cosmicResponse.json(); const products = cosmicData.non_subscription_products; return server_1.NextResponse.json({ products }); } async function getPurchaseHistory() { // User authentication required const session = await getServerSession(); if (!session?.uid) { return server_1.NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } // Get environment variables, which are guaranteed to be set in the environment const { projectId } = await getEnvironmentVariables(); // Call the Cosmic API with a GET request with request args const cosmicResponse = await fetch(`https://api.cosmic.new/cosmic-payments/get-purchase-history?project_id=${projectId}&user_id=${session.uid}`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Cosmic-Payments-Secret': SECRET, }, }); if (!cosmicResponse.ok) { return server_1.NextResponse.json({ error: 'Failed to get purchase history' }, { status: 500 }); } const cosmicData = await cosmicResponse.json(); const purchaseHistory = cosmicData.purchase_history; return server_1.NextResponse.json({ purchaseHistory }); } async function getActiveSubscriptions() { // User authentication required const session = await getServerSession(); if (!session?.uid) { return server_1.NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } // Get environment variables, which are guaranteed to be set in the environment const { projectId } = await getEnvironmentVariables(); // Call the Cosmic API with a GET request with request args const cosmicResponse = await fetch(`https://api.cosmic.new/cosmic-payments/get-active-subscriptions?project_id=${projectId}&user_id=${session.uid}`, { method: 'GET', headers: { 'X-Cosmic-Payments-Secret': SECRET, 'Content-Type': 'application/json', }, }); if (!cosmicResponse.ok) { return server_1.NextResponse.json({ error: 'Failed to get active subscriptions' }, { status: 500 }); } const cosmicData = await cosmicResponse.json(); const activeSubscriptions = cosmicData.active_subscriptions; return server_1.NextResponse.json({ activeSubscriptions }); } // Return the actual handler function return async function handler(request) { try { // Parse the entire request body once const body = await request.json(); const { actionType } = body; if (!actionType) { return server_1.NextResponse.json({ error: 'Action type is required' }, { status: 400 }); } if (actionType === 'get-billing-portal') { return await getBillingPortal(request); } else if (actionType === 'create-checkout-link') { return await createCheckoutLink(request, body); } else if (actionType === 'get-all-subscription-products') { return await getAllSubscriptionProducts(); } else if (actionType === 'get-all-non-subscription-products') { return await getAllNonSubscriptionProducts(); } else if (actionType === 'get-purchase-history') { return await getPurchaseHistory(); } else if (actionType === 'get-active-subscriptions') { return await getActiveSubscriptions(); } else { return server_1.NextResponse.json({ error: 'Invalid action type' }, { status: 400 }); } } catch (error) { console.error('Error in cosmic-payments API:', error); return server_1.NextResponse.json({ error: 'Internal server error' }, { status: 500 }); } }; }