const { HttpsError, onCall } = require('firebase-functions/https') const { getStripeClient, mapStripeErrorToHttps } = require('../../helpers/stripe') const { STRIPE_SECRET_KEY } = require('../../config/secrets') const { REGION } = require('./config') const { CANCELABLE_SUBSCRIPTION_STATUSES, ACTIVE_SUBSCRIPTION_STATUSES } = require('./constants') const { refsList, formatSubscriptionForClient, resolveUserContext } = require('./shared') const STRIPE_FUNCTION_OPTIONS = { region: REGION, cors: true, invoker: 'public', secrets: [STRIPE_SECRET_KEY], } const cancelActiveSubscription = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => { try { const uid = request?.auth?.uid if (!uid) { throw new HttpsError('unauthenticated', 'Connecte-toi pour gérer ton abonnement.') } const stripe = getStripeClient() const userRef = refsList?.users?.doc(uid) || null const snapshot = userRef ? await userRef.get() : null const userData = snapshot?.exists ? snapshot.data() || {} : {} const inputSubscriptionId = typeof request?.data?.subscriptionId === 'string' ? request.data.subscriptionId.trim() : '' let subscriptionId = inputSubscriptionId || userData?.stripeSubscription?.id || userData?.stripeSubscription?.subscriptionId || null const customerId = userData?.stripeCustomerId || null if (!subscriptionId && customerId) { try { const response = await stripe.subscriptions.list({ customer: customerId, status: 'all', limit: 5, }) const { data: subscriptionList = [] } = response || {} const activeSubscription = subscriptionList.find( (candidate) => candidate?.status && CANCELABLE_SUBSCRIPTION_STATUSES.has(candidate.status) ) if (activeSubscription?.id) { subscriptionId = activeSubscription.id } } catch (error) { console.warn( '[subscription-cancelActiveSubscription] Unable to list subscriptions', customerId, error?.message || error ) } } if (!subscriptionId) { throw new HttpsError('failed-precondition', 'Aucun abonnement actif à annuler.') } const subscription = await stripe.subscriptions.retrieve(subscriptionId) if (!subscription) { throw new HttpsError('not-found', 'Abonnement introuvable côté Stripe.') } if (subscription.status === 'canceled') { return { subscriptionId: subscription.id, status: subscription.status, cancelAtPeriodEnd: subscription.cancel_at_period_end === true, currentPeriodEnd: subscription.current_period_end || null, alreadyCanceled: true, } } if (subscription.cancel_at_period_end === true) { return { subscriptionId: subscription.id, status: subscription.status, cancelAtPeriodEnd: true, currentPeriodEnd: subscription.current_period_end || null, alreadyCanceled: false, } } const updatedSubscription = await stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: true, }) return { subscriptionId: updatedSubscription.id, status: updatedSubscription.status, cancelAtPeriodEnd: updatedSubscription.cancel_at_period_end === true, currentPeriodEnd: updatedSubscription.current_period_end || null, alreadyCanceled: false, } } catch (error) { console.error('[subscription-cancelActiveSubscription] error', error) if (error instanceof HttpsError) { throw error } throw mapStripeErrorToHttps(error, "Impossible d'annuler l'abonnement Stripe.") } }) const getActiveSubscription = onCall(STRIPE_FUNCTION_OPTIONS, async (request) => { try { const uid = request?.auth?.uid if (!uid) { throw new HttpsError('unauthenticated', 'Connecte-toi pour récupérer ton abonnement.') } const stripe = getStripeClient() const { uid: resolvedUid, userRef, userData, } = await resolveUserContext({ metadata: request?.data?.metadata || {}, customerId: null, }) const lookupUid = resolvedUid || uid const lookupRef = userRef || refsList?.users?.doc(lookupUid) || null const snapshot = lookupRef ? await lookupRef.get() : null const data = snapshot?.exists ? snapshot.data() || {} : userData || {} const subscriptionId = data?.stripeSubscription?.id || data?.stripeSubscription?.subscriptionId || null const customerId = data?.stripeCustomerId || null if (!subscriptionId && !customerId) { return { subscription: null, customerId: null, } } if (subscriptionId) { const subscription = await stripe.subscriptions.retrieve(subscriptionId, { expand: ['items.data.price'], }) if (subscription) { return { subscription: formatSubscriptionForClient(subscription), customerId: subscription.customer || customerId || null, } } } if (customerId) { const response = await stripe.subscriptions.list({ customer: customerId, status: 'all', limit: 5, expand: ['data.items.data.price'], }) const [subscription] = response?.data || [] if (subscription && ACTIVE_SUBSCRIPTION_STATUSES.has(subscription.status)) { return { subscription: formatSubscriptionForClient(subscription), customerId, } } } return { subscription: null, customerId, } } catch (error) { console.error('[subscription-getActiveSubscription] error', error) if (error instanceof HttpsError) { throw error } throw mapStripeErrorToHttps(error, "Impossible de récupérer l'abonnement Stripe.") } }) module.exports = { cancelActiveSubscription, getActiveSubscription, }