Files
musicland/functions/src/subscription/catalog.js
T
2025-11-18 11:40:39 +01:00

141 lines
3.9 KiB
JavaScript

const { HttpsError, onCall } = require("firebase-functions/https");
const { getStripeClient, mapStripeErrorToHttps } = require("../../helpers/stripe");
const { REGION } = require("./config");
const {
SUBSCRIPTION_PRICE_IDS,
SUBSCRIPTION_PRICE_METADATA,
COIN_PACK_PRODUCTS,
} = require("./constants");
const { parseCoinsPerMonth, formatCoinPack } = require("./shared");
const formatPlan = (price, priceId) => {
if (!price || typeof price !== "object") {
return null;
}
const product =
typeof price.product === "object" && price.product !== null
? price.product
: {};
return {
id: price.id || priceId,
priceId: price.id || priceId,
active: price.active !== false,
currency: price.currency || "eur",
unitAmount: price.unit_amount,
unitAmountDecimal: price.unit_amount_decimal,
transformQuantity: price.transform_quantity || null,
recurring: price.recurring || null,
nickname: price.nickname || null,
billingScheme: price.billing_scheme || null,
coinsPerMonth:
parseCoinsPerMonth(product?.metadata || {}) ||
(SUBSCRIPTION_PRICE_METADATA[price.id || priceId]?.coinsPerMonth ?? null),
metadata: price.metadata || {},
product: {
id: product.id || null,
name: product.name || "",
description: product.description || "",
metadata: product.metadata || {},
},
};
};
const listSubscriptionPlans = onCall({ region: REGION }, async () => {
try {
const stripe = getStripeClient();
const entries = await Promise.all(
Object.entries(SUBSCRIPTION_PRICE_IDS).map(async ([period, priceIds]) => {
const periodPlans = await Promise.all(
priceIds.map(async (priceId) => {
try {
const price = await stripe.prices.retrieve(priceId, {
expand: ["product"],
});
return formatPlan(price, priceId);
} catch (error) {
console.error(
`[subscription-listSubscriptionPlans] Impossible de récupérer ${priceId}`,
error?.message || error,
);
return null;
}
}),
);
return [period, periodPlans.filter(Boolean)];
}),
);
return {
plans: Object.fromEntries(entries),
};
} catch (error) {
console.error("[subscription-listSubscriptionPlans] error", error);
throw mapStripeErrorToHttps(
error,
"Impossible de récupérer les abonnements Stripe.",
);
}
});
const listCoinPacks = onCall({ region: REGION }, async () => {
try {
const stripe = getStripeClient();
const packs = await Promise.all(
COIN_PACK_PRODUCTS.map(async (pack) => {
try {
const product = await stripe.products.retrieve(pack.productId, {
expand: ["default_price"],
});
let resolvedPrice = null;
if (typeof product?.default_price === "string") {
resolvedPrice = await stripe.prices.retrieve(product.default_price);
} else if (
product?.default_price &&
typeof product.default_price === "object"
) {
resolvedPrice = product.default_price;
}
const formatted = formatCoinPack({
product,
price: resolvedPrice,
});
return {
...formatted,
coinPackKey: pack.key || null,
};
} catch (error) {
console.error(
"[subscription-listCoinPacks] Unable to retrieve product",
pack.productId,
error?.message || error,
);
return null;
}
}),
);
return {
packs: packs.filter(Boolean),
};
} catch (error) {
console.error("[subscription-listCoinPacks] error", error);
throw mapStripeErrorToHttps(
error,
"Impossible de récupérer les packs de pièces.",
);
}
});
module.exports = {
listSubscriptionPlans,
listCoinPacks,
};