clear last tickets
This commit is contained in:
@@ -11,12 +11,22 @@ const {
|
||||
} = require("./shared");
|
||||
const { ACTIVE_SUBSCRIPTION_STATUSES } = require("./constants");
|
||||
|
||||
// Toggle to stop monthly grants for annual subscriptions while keeping logic handy.
|
||||
const ENABLE_ANNUAL_GRANT_SCHEDULER = false;
|
||||
|
||||
const processAnnualSubscriptionAllowances = onSchedule(
|
||||
{
|
||||
schedule: "30 3 * * *",
|
||||
timeZone: "Europe/Paris",
|
||||
},
|
||||
async () => {
|
||||
if (!ENABLE_ANNUAL_GRANT_SCHEDULER) {
|
||||
console.log(
|
||||
"[subscription-processAnnualSubscriptionAllowances] skipped (disabled)",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const nowTimestamp = admin.firestore.Timestamp.now();
|
||||
const pageSize = 200;
|
||||
let lastDoc = null;
|
||||
|
||||
@@ -14,15 +14,11 @@ const {
|
||||
computeNextGrantTimestamp,
|
||||
parseCoinsPerMonth,
|
||||
getSubscriptionMetaFromPrice,
|
||||
formatSubscriptionForClient,
|
||||
buildSubscriptionPayload,
|
||||
resolveUserContext,
|
||||
upsertPaymentDocument,
|
||||
} = require("./shared");
|
||||
const {
|
||||
SUBSCRIPTION_LEVEL_ALLOWANCES,
|
||||
PREMIUM_SUBSCRIPTION_STATUSES,
|
||||
} = require("./constants");
|
||||
const { PREMIUM_SUBSCRIPTION_STATUSES } = require("./constants");
|
||||
|
||||
const handleCheckoutSessionCompleted = async (
|
||||
session,
|
||||
@@ -250,7 +246,8 @@ const handleCustomerSubscriptionEvent = async (
|
||||
|
||||
const priceMeta = getSubscriptionMetaFromPrice(subscriptionPayload?.priceId);
|
||||
const metadataLevel = subscription.metadata?.subscriptionLevel || null;
|
||||
const metadataPeriod = subscription.metadata?.subscriptionBillingPeriod || null;
|
||||
const metadataPeriod =
|
||||
subscription.metadata?.subscriptionBillingPeriod || null;
|
||||
const resolvedLevel = metadataLevel || priceMeta?.level || null;
|
||||
const resolvedPeriod = metadataPeriod || priceMeta?.billingPeriod || null;
|
||||
|
||||
@@ -457,6 +454,126 @@ const handleInvoiceEvent = async (invoice, event, { stripe } = {}) => {
|
||||
}
|
||||
|
||||
await userRef.set(userUpdate, { merge: true });
|
||||
|
||||
const subscriptionLine = Array.isArray(invoice?.lines?.data)
|
||||
? invoice.lines.data.find(
|
||||
(line) =>
|
||||
line &&
|
||||
typeof line === "object" &&
|
||||
(line.type === "subscription" || line.price),
|
||||
)
|
||||
: null;
|
||||
|
||||
const priceId =
|
||||
typeof subscriptionLine?.price?.id === "string"
|
||||
? subscriptionLine.price.id
|
||||
: typeof subscriptionLine?.price === "string"
|
||||
? subscriptionLine.price
|
||||
: null;
|
||||
|
||||
const priceMeta = getSubscriptionMetaFromPrice(priceId) || {};
|
||||
const billingPeriod =
|
||||
priceMeta.billingPeriod ||
|
||||
(subscriptionLine?.price?.recurring?.interval === "year"
|
||||
? "annual"
|
||||
: subscriptionLine?.price?.recurring?.interval === "month"
|
||||
? "monthly"
|
||||
: null);
|
||||
|
||||
const coinsPerMonth =
|
||||
priceMeta.coinsPerMonth ??
|
||||
parseCoinsPerMonth(subscriptionLine?.price?.product?.metadata || {}) ??
|
||||
null;
|
||||
|
||||
const coinsToGrant =
|
||||
billingPeriod === "annual" && typeof coinsPerMonth === "number"
|
||||
? coinsPerMonth * 12
|
||||
: null;
|
||||
|
||||
const targetSubscriptionId =
|
||||
resolvedSubscriptionId ||
|
||||
(typeof invoice.subscription === "string" ? invoice.subscription : null) ||
|
||||
(typeof subscriptionLine?.subscription === "string"
|
||||
? subscriptionLine.subscription
|
||||
: null);
|
||||
|
||||
const shouldGrantUpfront =
|
||||
isInvoicePaid &&
|
||||
coinsToGrant &&
|
||||
(billingReason === "subscription_create" ||
|
||||
billingReason === "subscription_cycle");
|
||||
|
||||
if (shouldGrantUpfront && targetSubscriptionId) {
|
||||
let grantSnapshot = null;
|
||||
try {
|
||||
grantSnapshot = await paymentDocRef.get();
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[subscription-handleInvoiceEvent] Unable to read payment doc before grant",
|
||||
invoice?.id || null,
|
||||
error?.message || error,
|
||||
);
|
||||
}
|
||||
|
||||
const alreadyGranted =
|
||||
grantSnapshot?.exists &&
|
||||
Boolean(grantSnapshot.data()?.subscriptionCoinsGrantedAt);
|
||||
|
||||
if (!alreadyGranted) {
|
||||
const targetUserId = uid || firebaseUid || userRef.id;
|
||||
const orderId = `subscription_${targetSubscriptionId}_invoice_${invoice.id}`;
|
||||
|
||||
try {
|
||||
const { orderId: processedOrderId } = await createOrderDocument({
|
||||
userId: targetUserId,
|
||||
type: ORDER_TYPES.SUBSCRIPTION,
|
||||
amount: coinsToGrant,
|
||||
metadata: {
|
||||
source: "STRIPE_INVOICE",
|
||||
billingPeriod,
|
||||
coinsPerMonth,
|
||||
invoiceId: invoice.id || null,
|
||||
subscriptionId: targetSubscriptionId,
|
||||
grantStrategy: "upfront",
|
||||
},
|
||||
orderId,
|
||||
});
|
||||
|
||||
await paymentDocRef.set(
|
||||
{
|
||||
subscriptionCoinsGrantedAt: getServerTimestamp(),
|
||||
subscriptionCoinsGrantAmount: coinsToGrant,
|
||||
subscriptionCoinsGrantOrderId: processedOrderId,
|
||||
subscriptionCoinsGrantSource: "invoice_upfront",
|
||||
},
|
||||
{ merge: true },
|
||||
);
|
||||
|
||||
await userRef.set(
|
||||
{
|
||||
subscriptionLastGrantAt: getServerTimestamp(),
|
||||
subscriptionLastGrantAmount: coinsToGrant,
|
||||
subscriptionLastGrantOrderId: processedOrderId,
|
||||
subscriptionLastGrantSource: "invoice_upfront",
|
||||
subscriptionNextGrantAt: null,
|
||||
subscriptionGrantInterval: null,
|
||||
subscriptionGrantStrategy: "upfront",
|
||||
subscriptionCoinsPerMonth: coinsPerMonth,
|
||||
},
|
||||
{ merge: true },
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[subscription-handleInvoiceEvent] Unable to grant upfront subscription coins",
|
||||
{
|
||||
invoiceId: invoice?.id || null,
|
||||
subscriptionId: targetSubscriptionId,
|
||||
error: error?.message || error,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleStripeWebhookEvent = async ({ event, stripe }) => {
|
||||
@@ -517,7 +634,10 @@ const handleStripeWebhook = onRequest({ region: REGION }, async (req, res) => {
|
||||
try {
|
||||
stripe = getStripeClient();
|
||||
} catch (error) {
|
||||
console.error("[subscription-handleStripeWebhook] Stripe client error", error);
|
||||
console.error(
|
||||
"[subscription-handleStripeWebhook] Stripe client error",
|
||||
error,
|
||||
);
|
||||
res.status(500).send("Client Stripe indisponible");
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user