190 lines
5.8 KiB
JavaScript
190 lines
5.8 KiB
JavaScript
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,
|
|
}
|