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
JavaScript
"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 });
}
};
}