import { toDateTime } from '@/utils/helpers'; // import { stripe } from '@/utils/stripe/config'; import { createClient } from '@supabase/supabase-js'; import Stripe from 'stripe'; import type { Database, Tables, TablesInsert } from 'types_db'; // type Product = Tables<'products'>; // type Price = Tables<'prices'>; // Change to control trial period length const TRIAL_PERIOD_DAYS = 0; // Note: supabaseAdmin uses the SERVICE_ROLE_KEY which you must only use in a secure server-side context // as it has admin privileges and overwrites RLS policies! const supabaseAdmin = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL || '', process.env.SUPABASE_SERVICE_ROLE_KEY || '' ); const upsertProductRecord = async (product: Stripe.Product) => { // const productData: Product = { // id: product.id, // active: product.active, // name: product.name, // description: product.description ?? null, // image: product.images?.[0] ?? null, // metadata: product.metadata // }; // const { error: upsertError } = await supabaseAdmin // .from('products') // .upsert([productData]); // if (upsertError) // throw new Error(`Product insert/update failed: ${upsertError.message}`); // console.log(`Product inserted/updated: ${product.id}`); }; const upsertPriceRecord = async ( price: Stripe.Price, retryCount = 0, maxRetries = 3 ) => { // const priceData: Price = { // id: price.id, // product_id: typeof price.product === 'string' ? price.product : '', // active: price.active, // currency: price.currency, // type: price.type, // unit_amount: price.unit_amount ?? null, // interval: price.recurring?.interval ?? null, // interval_count: price.recurring?.interval_count ?? null, // trial_period_days: price.recurring?.trial_period_days ?? TRIAL_PERIOD_DAYS // }; // const { error: upsertError } = await supabaseAdmin // .from('prices') // .upsert([priceData]); // if (upsertError?.message.includes('foreign key constraint')) { // if (retryCount < maxRetries) { // console.log(`Retry attempt ${retryCount + 1} for price ID: ${price.id}`); // await new Promise((resolve) => setTimeout(resolve, 2000)); // await upsertPriceRecord(price, retryCount + 1, maxRetries); // } else { // throw new Error( // `Price insert/update failed after ${maxRetries} retries: ${upsertError.message}` // ); // } // } else if (upsertError) { // throw new Error(`Price insert/update failed: ${upsertError.message}`); // } else { // console.log(`Price inserted/updated: ${price.id}`); // } }; const deleteProductRecord = async (product: Stripe.Product) => { // const { error: deletionError } = await supabaseAdmin // .from('products') // .delete() // .eq('id', product.id); // if (deletionError) // throw new Error(`Product deletion failed: ${deletionError.message}`); // console.log(`Product deleted: ${product.id}`); }; const deletePriceRecord = async (price: Stripe.Price) => { // const { error: deletionError } = await supabaseAdmin // .from('prices') // .delete() // .eq('id', price.id); // if (deletionError) throw new Error(`Price deletion failed: ${deletionError.message}`); // console.log(`Price deleted: ${price.id}`); }; const upsertCustomerToSupabase = async (uuid: string, customerId: string) => { // const { error: upsertError } = await supabaseAdmin // .from('customers') // .upsert([{ id: uuid, stripe_customer_id: customerId }]); // if (upsertError) // throw new Error(`Supabase customer record creation failed: ${upsertError.message}`); // return customerId; }; const createCustomerInStripe = async (uuid: string, email: string) => { // const customerData = { metadata: { supabaseUUID: uuid }, email: email }; // const newCustomer = await stripe.customers.create(customerData); // if (!newCustomer) throw new Error('Stripe customer creation failed.'); // return newCustomer.id; }; const createOrRetrieveCustomer = async ({ email, uuid }: { email: string; uuid: string; }) => { // // Check if the customer already exists in Supabase // const { data: existingSupabaseCustomer, error: queryError } = // await supabaseAdmin // .from('customers') // .select('*') // .eq('id', uuid) // .maybeSingle(); // if (queryError) { // throw new Error(`Supabase customer lookup failed: ${queryError.message}`); // } // // Retrieve the Stripe customer ID using the Supabase customer ID, with email fallback // let stripeCustomerId: string | undefined; // if (existingSupabaseCustomer?.stripe_customer_id) { // const existingStripeCustomer = await stripe.customers.retrieve( // existingSupabaseCustomer.stripe_customer_id // ); // stripeCustomerId = existingStripeCustomer.id; // } else { // // If Stripe ID is missing from Supabase, try to retrieve Stripe customer ID by email // const stripeCustomers = await stripe.customers.list({ email: email }); // stripeCustomerId = // stripeCustomers.data.length > 0 ? stripeCustomers.data[0].id : undefined; // } // // If still no stripeCustomerId, create a new customer in Stripe // const stripeIdToInsert = stripeCustomerId // ? stripeCustomerId // : await createCustomerInStripe(uuid, email); // if (!stripeIdToInsert) throw new Error('Stripe customer creation failed.'); // if (existingSupabaseCustomer && stripeCustomerId) { // // If Supabase has a record but doesn't match Stripe, update Supabase record // if (existingSupabaseCustomer.stripe_customer_id !== stripeCustomerId) { // const { error: updateError } = await supabaseAdmin // .from('customers') // .update({ stripe_customer_id: stripeCustomerId }) // .eq('id', uuid); // if (updateError) // throw new Error( // `Supabase customer record update failed: ${updateError.message}` // ); // console.warn( // `Supabase customer record mismatched Stripe ID. Supabase record updated.` // ); // } // // If Supabase has a record and matches Stripe, return Stripe customer ID // return stripeCustomerId; // } else { // console.warn( // `Supabase customer record was missing. A new record was created.` // ); // // If Supabase has no record, create a new record and return Stripe customer ID // const upsertedStripeCustomer = await upsertCustomerToSupabase( // uuid, // stripeIdToInsert // ); // if (!upsertedStripeCustomer) // throw new Error('Supabase customer record creation failed.'); // return upsertedStripeCustomer; // } return ''; }; /** * Copies the billing details from the payment method to the customer object. */ const copyBillingDetailsToCustomer = async ( uuid: string, payment_method: Stripe.PaymentMethod ) => { // //Todo: check this assertion // const customer = payment_method.customer as string; // const { name, phone, address } = payment_method.billing_details; // if (!name || !phone || !address) return; // //@ts-ignore // await stripe.customers.update(customer, { name, phone, address }); // const { error: updateError } = await supabaseAdmin // .from('users') // .update({ // billing_address: { ...address }, // payment_method: { ...payment_method[payment_method.type] } // }) // .eq('id', uuid); // if (updateError) throw new Error(`Customer update failed: ${updateError.message}`); }; const manageSubscriptionStatusChange = async ( subscriptionId: string, customerId: string, createAction = false ) => { // Get customer's UUID from mapping table. // const { data: customerData, error: noCustomerError } = await supabaseAdmin // .from('customers') // .select('id') // .eq('stripe_customer_id', customerId) // .single(); // if (noCustomerError) // throw new Error(`Customer lookup failed: ${noCustomerError.message}`); // const { id: uuid } = customerData!; // const subscription = await stripe.subscriptions.retrieve(subscriptionId, { // expand: ['default_payment_method'] // }); // // Upsert the latest status of the subscription object. // const subscriptionData: TablesInsert<'subscriptions'> = { // id: subscription.id, // user_id: uuid, // metadata: subscription.metadata, // status: subscription.status, // price_id: subscription.items.data[0].price.id, // //TODO check quantity on subscription // // @ts-ignore // quantity: subscription.quantity, // cancel_at_period_end: subscription.cancel_at_period_end, // cancel_at: subscription.cancel_at // ? toDateTime(subscription.cancel_at).toISOString() // : null, // canceled_at: subscription.canceled_at // ? toDateTime(subscription.canceled_at).toISOString() // : null, // current_period_start: toDateTime( // subscription.current_period_start // ).toISOString(), // current_period_end: toDateTime( // subscription.current_period_end // ).toISOString(), // created: toDateTime(subscription.created).toISOString(), // ended_at: subscription.ended_at // ? toDateTime(subscription.ended_at).toISOString() // : null, // trial_start: subscription.trial_start // ? toDateTime(subscription.trial_start).toISOString() // : null, // trial_end: subscription.trial_end // ? toDateTime(subscription.trial_end).toISOString() // : null // }; // const { error: upsertError } = await supabaseAdmin // .from('subscriptions') // .upsert([subscriptionData]); // if (upsertError) // throw new Error(`Subscription insert/update failed: ${upsertError.message}`); // console.log( // `Inserted/updated subscription [${subscription.id}] for user [${uuid}]` // ); // // For a new subscription copy the billing details to the customer object. // // NOTE: This is a costly operation and should happen at the very end. // if (createAction && subscription.default_payment_method && uuid) // //@ts-ignore // await copyBillingDetailsToCustomer( // uuid, // subscription.default_payment_method as Stripe.PaymentMethod // ); }; export { upsertProductRecord, upsertPriceRecord, deleteProductRecord, deletePriceRecord, createOrRetrieveCustomer, manageSubscriptionStatusChange };