home, subscription and other fixes

This commit is contained in:
Thomas Demirdjian
2025-11-06 17:33:07 +01:00
parent 5f777f3b6d
commit 4365f1e46d
31 changed files with 1579 additions and 348 deletions
+82 -5
View File
@@ -13,6 +13,33 @@ const { sendNotification } = require("./notifications");
const DISTRIBUTION_REVENUE_BASELINE = 1000;
const DISTRIBUTION_RATIO = 0.3;
const ACTIVE_SUBSCRIPTION_STATUSES = new Set(["active"]);
const hasActiveSubscription = (userData) => {
if (!userData || typeof userData !== "object") {
return false;
}
const isPremium = userData.isPremium === true;
if (!isPremium) {
return false;
}
const status =
typeof userData.stripeSubscriptionStatus === "string"
? userData.stripeSubscriptionStatus.trim().toLowerCase()
: null;
if (status && ACTIVE_SUBSCRIPTION_STATUSES.has(status)) {
return true;
}
const billingPeriod =
typeof userData.premiumBillingPeriod === "string"
? userData.premiumBillingPeriod.trim().toLowerCase()
: null;
if (billingPeriod === "monthly" || billingPeriod === "annual") {
// Fallback: billing period is set only for active subscribers.
return true;
}
return false;
};
exports.distributeMonthlyPayouts = onSchedule(
{
@@ -51,6 +78,51 @@ exports.distributeMonthlyPayouts = onSchedule(
.orderBy(["streams"], ["desc"])
.value();
const userEligibilityMap = {};
const userIds = _.uniq(
entries.map((entry) => entry.userId).filter((userId) => !!userId),
);
if (userIds.length) {
const chunkSize = 300;
for (let index = 0; index < userIds.length; index += chunkSize) {
const chunk = userIds.slice(index, index + chunkSize);
const snapshots = await Promise.all(
chunk.map(async (userId) => {
try {
return await refList.users.doc(userId).get();
} catch (error) {
console.warn(
"[distributeMonthlyPayouts] Unable to load user profile",
{
userId,
error: error?.message || String(error),
},
);
return null;
}
}),
);
snapshots.forEach((snapshot, snapshotIndex) => {
const userId = chunk[snapshotIndex];
if (snapshot?.exists) {
userEligibilityMap[userId] = hasActiveSubscription(
snapshot.data(),
);
} else {
userEligibilityMap[userId] = false;
}
});
}
}
const eligibleEntries = entries.filter(
(entry) =>
!!entry.userId && userEligibilityMap[entry.userId] === true,
);
const eligibleTotalStreams = _.sumBy(eligibleEntries, "streams");
const payoutsTotalStreamsFromDocs = _.sumBy(entries, "streams");
const totalsData = totalsSnapshot.exists ? totalsSnapshot.data() : null;
let totalStreams = _.toFinite(_.get(totalsData, "totalStreams", 0));
@@ -63,9 +135,9 @@ exports.distributeMonthlyPayouts = onSchedule(
2,
);
let allocations = _.map(entries, (entry) => {
if (!totalStreams) return 0;
const rawAmount = (payoutPool * entry.streams) / totalStreams;
let allocations = _.map(eligibleEntries, (entry) => {
if (!eligibleTotalStreams) return 0;
const rawAmount = (payoutPool * entry.streams) / eligibleTotalStreams;
return _.round(rawAmount, 2);
});
@@ -77,12 +149,14 @@ exports.distributeMonthlyPayouts = onSchedule(
}
}
const payouts = entries.map((entry, idx) => ({
const payouts = eligibleEntries.map((entry, idx) => ({
rank: idx + 1,
projectId: entry.projectId,
userId: entry.userId,
streams: entry.streams,
share: totalStreams ? _.round(entry.streams / totalStreams, 6) : 0,
share: eligibleTotalStreams
? _.round(entry.streams / eligibleTotalStreams, 6)
: 0,
amount: allocations[idx],
statsDocPath: entry.statsDocPath,
}));
@@ -192,7 +266,10 @@ exports.distributeMonthlyPayouts = onSchedule(
payoutRatio: DISTRIBUTION_RATIO,
payoutPool,
totalStreams,
eligibleTotalStreams,
totalRecipients: payouts.length,
totalEntries: entries.length,
eligibleEntries: eligibleEntries.length,
totalAllocated: _.round(_.sumBy(payouts, "amount"), 2),
payouts,
status: payouts.length ? "computed" : "no-data",