From eac11b1e2e69e19c2afb98d75df96b4648ef206c Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 29 Apr 2026 14:31:19 +0530 Subject: [PATCH 1/8] Refactor product ID handling in Stripe webhook integrations --- .../webhook/checkout-session-completed.ts | 9 +++-- .../integration/webhook/invoice-paid.ts | 8 +++- .../utils/get-checkout-session-product-id.ts | 40 +++++++++++++------ apps/web/lib/zod/schemas/rewards.ts | 3 +- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts index 1627cd24778..21a74caf66f 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts @@ -27,7 +27,7 @@ import { Customer, Project } from "@dub/prisma/client"; import { COUNTRIES_TO_CONTINENTS, nanoid, pick } from "@dub/utils"; import { waitUntil } from "@vercel/functions"; import type Stripe from "stripe"; -import { getCheckoutSessionProductId } from "./utils/get-checkout-session-product-id"; +import { getCheckoutSessionProductIds } from "./utils/get-checkout-session-product-id"; import { getConnectedCustomer } from "./utils/get-connected-customer"; import { getPromotionCode } from "./utils/get-promotion-code"; import { updateCustomerWithStripeCustomerId } from "./utils/update-customer-with-stripe-customer-id"; @@ -358,9 +358,10 @@ export async function checkoutSessionCompleted( if (!ok) { console.info( - "[Stripe Webhook] Skipping already processed invoice.", + "[checkout.session.completed] Skipping already processed invoice.", invoiceId, ); + return { response: `Invoice with ID ${invoiceId} already processed, skipping...`, workspaceId: workspace.id, @@ -483,7 +484,7 @@ export async function checkoutSessionCompleted( | undefined = undefined; if (link && link.programId && link.partnerId) { - const productId = await getCheckoutSessionProductId({ + const productIds = await getCheckoutSessionProductIds({ checkoutSessionId: charge.id, stripeAccountId, mode, @@ -506,7 +507,7 @@ export async function checkoutSessionCompleted( signupDate: customer.createdAt, }, sale: { - productId, + productIds, amount: saleData.amount, }, }, diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts index 64bf076b016..321594c4729 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts @@ -105,7 +105,7 @@ export async function invoicePaid( if (!ok) { console.info( - "[Stripe Webhook] Skipping already processed invoice.", + "[invoice.paid] Skipping already processed invoice.", invoiceId, ); return { @@ -244,6 +244,10 @@ export async function invoicePaid( | undefined = undefined; if (link.programId && link.partnerId) { + const productIds = invoice.lines.data + .map(({ pricing }) => pricing?.price_details?.product) + .filter((productId) => productId != null); + createdCommission = await createPartnerCommission({ event: "sale", programId: link.programId, @@ -261,7 +265,7 @@ export async function invoicePaid( signupDate: customer.createdAt, }, sale: { - productId: invoice.lines.data[0]?.pricing?.price_details?.product, + productIds, amount: saleData.amount, }, }, diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts index 2191ecfee1e..3fcfa2ce458 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts @@ -2,7 +2,7 @@ import { stripeAppClient } from "@/lib/stripe"; import { StripeMode } from "@/lib/types"; import type Stripe from "stripe"; -function productIdFromLineItemPrice( +export function productIdFromLineItemPrice( price: Stripe.Price | string | null | undefined, ): string | null { if (!price || typeof price === "string") { @@ -26,7 +26,7 @@ function productIdFromLineItemPrice( return product.id; } -export async function getCheckoutSessionProductId({ +export async function getCheckoutSessionProductIds({ checkoutSessionId, stripeAccountId, mode, @@ -34,15 +34,17 @@ export async function getCheckoutSessionProductId({ checkoutSessionId: string; stripeAccountId?: string | null; mode: StripeMode; -}): Promise { +}): Promise { if (!stripeAccountId) { return null; } try { - const lineItems = await stripeAppClient({ + const stripeApp = stripeAppClient({ mode, - }).checkout.sessions.listLineItems( + }); + + const lineItems = await stripeApp.checkout.sessions.listLineItems( checkoutSessionId, { expand: ["data.price.product"], @@ -53,16 +55,30 @@ export async function getCheckoutSessionProductId({ }, ); - for (const item of lineItems.data) { - const productId = productIdFromLineItemPrice(item.price); - if (productId) { - return productId; - } + if (lineItems.data.length === 0) { + console.log( + `[getCheckoutSessionProductIds] No line items found for checkout session ${checkoutSessionId}.`, + ); + return null; } - return null; + const productIds = lineItems.data + .map((item) => productIdFromLineItemPrice(item.price)) + .filter((productId) => productId !== null); + + if (productIds.length === 0) { + console.log( + `[getCheckoutSessionProductIds] No valid product IDs found for checkout session ${checkoutSessionId}.`, + ); + return null; + } + + return productIds; } catch (error) { - console.log("Failed to get checkout session product ID:", error); + console.log( + "[getCheckoutSessionProductIds] Failed to get checkout session product ID:", + error, + ); return null; } } diff --git a/apps/web/lib/zod/schemas/rewards.ts b/apps/web/lib/zod/schemas/rewards.ts index c4040ea37ce..e2a90d4210c 100644 --- a/apps/web/lib/zod/schemas/rewards.ts +++ b/apps/web/lib/zod/schemas/rewards.ts @@ -377,7 +377,8 @@ export const rewardContextSchema = z.object({ sale: z .object({ - productId: z.string().nullish(), + // productId: z.string().nullish(), + productIds: z.array(z.string()).nullish(), amount: z.number().nullish(), }) .optional(), From 2ad8449892954531dcf3cbedda444a39111cbc7a Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 29 Apr 2026 18:52:27 +0530 Subject: [PATCH 2/8] Refactor product handling in Stripe webhook integrations to use structured product objects --- .../webhook/checkout-session-completed.ts | 6 +- .../integration/webhook/invoice-paid.ts | 21 +++- .../utils/get-checkout-session-product-id.ts | 41 ++++--- .../partners/evaluate-reward-conditions.ts | 107 +++++++++++------- apps/web/lib/zod/schemas/rewards.ts | 13 ++- 5 files changed, 124 insertions(+), 64 deletions(-) diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts index 21a74caf66f..4fec28a00a0 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts @@ -27,7 +27,7 @@ import { Customer, Project } from "@dub/prisma/client"; import { COUNTRIES_TO_CONTINENTS, nanoid, pick } from "@dub/utils"; import { waitUntil } from "@vercel/functions"; import type Stripe from "stripe"; -import { getCheckoutSessionProductIds } from "./utils/get-checkout-session-product-id"; +import { getCheckoutSessionProducts } from "./utils/get-checkout-session-product-id"; import { getConnectedCustomer } from "./utils/get-connected-customer"; import { getPromotionCode } from "./utils/get-promotion-code"; import { updateCustomerWithStripeCustomerId } from "./utils/update-customer-with-stripe-customer-id"; @@ -484,7 +484,7 @@ export async function checkoutSessionCompleted( | undefined = undefined; if (link && link.programId && link.partnerId) { - const productIds = await getCheckoutSessionProductIds({ + const products = await getCheckoutSessionProducts({ checkoutSessionId: charge.id, stripeAccountId, mode, @@ -507,7 +507,7 @@ export async function checkoutSessionCompleted( signupDate: customer.createdAt, }, sale: { - productIds, + products, amount: saleData.amount, }, }, diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts index 321594c4729..b6b2a1471b4 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts @@ -244,9 +244,22 @@ export async function invoicePaid( | undefined = undefined; if (link.programId && link.partnerId) { - const productIds = invoice.lines.data - .map(({ pricing }) => pricing?.price_details?.product) - .filter((productId) => productId != null); + const products = invoice.lines.data + .map((line) => { + const productId = line.pricing?.price_details?.product; + + if (!productId) return null; + + return { + id: productId, + amount: line.amount, + quantity: line.quantity ?? 1, + }; + }) + .filter( + (p): p is { id: string; amount: number; quantity: number } => + p !== null, + ); createdCommission = await createPartnerCommission({ event: "sale", @@ -265,7 +278,7 @@ export async function invoicePaid( signupDate: customer.createdAt, }, sale: { - productIds, + products, amount: saleData.amount, }, }, diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts index 3fcfa2ce458..70be52f9719 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts @@ -26,7 +26,7 @@ export function productIdFromLineItemPrice( return product.id; } -export async function getCheckoutSessionProductIds({ +export async function getCheckoutSessionProducts({ checkoutSessionId, stripeAccountId, mode, @@ -34,9 +34,9 @@ export async function getCheckoutSessionProductIds({ checkoutSessionId: string; stripeAccountId?: string | null; mode: StripeMode; -}): Promise { +}) { if (!stripeAccountId) { - return null; + return []; } try { @@ -57,28 +57,41 @@ export async function getCheckoutSessionProductIds({ if (lineItems.data.length === 0) { console.log( - `[getCheckoutSessionProductIds] No line items found for checkout session ${checkoutSessionId}.`, + `[getCheckoutSessionProducts] No line items found for checkout session ${checkoutSessionId}.`, ); - return null; + return []; } - const productIds = lineItems.data - .map((item) => productIdFromLineItemPrice(item.price)) - .filter((productId) => productId !== null); + const products = lineItems.data + .map((line) => { + const productId = productIdFromLineItemPrice(line.price); + + if (!productId) return null; - if (productIds.length === 0) { + return { + id: productId, + amount: line.amount_total, + quantity: line.quantity ?? 1, + }; + }) + .filter( + (p): p is { id: string; amount: number; quantity: number } => + p !== null, + ); + + if (products.length === 0) { console.log( - `[getCheckoutSessionProductIds] No valid product IDs found for checkout session ${checkoutSessionId}.`, + `[getCheckoutSessionProducts] No valid products found for checkout session ${checkoutSessionId}.`, ); - return null; + return []; } - return productIds; + return products; } catch (error) { console.log( - "[getCheckoutSessionProductIds] Failed to get checkout session product ID:", + "[getCheckoutSessionProducts] Failed to get checkout session products:", error, ); - return null; + return []; } } diff --git a/apps/web/lib/partners/evaluate-reward-conditions.ts b/apps/web/lib/partners/evaluate-reward-conditions.ts index 6822736917a..83fea66b1cb 100644 --- a/apps/web/lib/partners/evaluate-reward-conditions.ts +++ b/apps/web/lib/partners/evaluate-reward-conditions.ts @@ -82,50 +82,75 @@ const evaluateCondition = ({ condition: RewardCondition; fieldValue: string | number | string[] | number[]; }) => { - switch (condition.operator) { - case "equals_to": - return fieldValue === condition.value; - case "not_equals": - return fieldValue !== condition.value; - case "starts_with": - if ( - typeof fieldValue === "string" && - typeof condition.value === "string" - ) { - return fieldValue.startsWith(condition.value); - } + // Equals + if (condition.operator === "equals_to") { + return fieldValue === condition.value; + } + + // Not equals + if (condition.operator === "not_equals") { + return fieldValue !== condition.value; + } + + // Starts with + if (condition.operator === "starts_with") { + if (typeof fieldValue !== "string" || typeof condition.value !== "string") { return false; - case "ends_with": - if ( - typeof fieldValue === "string" && - typeof condition.value === "string" - ) { - return fieldValue.endsWith(condition.value); - } + } + + return fieldValue.startsWith(condition.value); + } + + // Ends with + if (condition.operator === "ends_with") { + if (typeof fieldValue !== "string" || typeof condition.value !== "string") { return false; - case "in": - if (Array.isArray(condition.value)) { - return (condition.value as (string | number)[]).includes( - fieldValue as string | number, - ); - } + } + + return fieldValue.endsWith(condition.value); + } + + // In + if (condition.operator === "in") { + if (!Array.isArray(condition.value)) { return false; - case "not_in": - if (Array.isArray(condition.value)) { - return !(condition.value as (string | number)[]).includes( - fieldValue as string | number, - ); - } - return true; - case "greater_than": - return Number(fieldValue) > Number(condition.value); - case "greater_than_or_equal": - return Number(fieldValue) >= Number(condition.value); - case "less_than": - return Number(fieldValue) < Number(condition.value); - case "less_than_or_equal": - return Number(fieldValue) <= Number(condition.value); - default: + } + + return (condition.value as (string | number)[]).includes( + fieldValue as string | number, + ); + } + + // Not in + if (condition.operator === "not_in") { + if (!Array.isArray(condition.value)) { return false; + } + + return !(condition.value as (string | number)[]).includes( + fieldValue as string | number, + ); + } + + // Greater than + if (condition.operator === "greater_than") { + return Number(fieldValue) > Number(condition.value); } + + // Greater than or equal + if (condition.operator === "greater_than_or_equal") { + return Number(fieldValue) >= Number(condition.value); + } + + // Less than + if (condition.operator === "less_than") { + return Number(fieldValue) < Number(condition.value); + } + + // Less than or equal + if (condition.operator === "less_than_or_equal") { + return Number(fieldValue) <= Number(condition.value); + } + + return false; }; diff --git a/apps/web/lib/zod/schemas/rewards.ts b/apps/web/lib/zod/schemas/rewards.ts index e2a90d4210c..3cb407aa4b3 100644 --- a/apps/web/lib/zod/schemas/rewards.ts +++ b/apps/web/lib/zod/schemas/rewards.ts @@ -377,9 +377,18 @@ export const rewardContextSchema = z.object({ sale: z .object({ - // productId: z.string().nullish(), - productIds: z.array(z.string()).nullish(), + productId: z.string().nullish(), amount: z.number().nullish(), + products: z + .array( + z.object({ + id: z.string(), + amount: z.number(), + quantity: z.number(), + }), + ) + .nullish() + .describe("Only used in Stripe integration."), }) .optional(), From 263c6d2bf1191b69cb901c144577c16fb11796f2 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 29 Apr 2026 19:25:32 +0530 Subject: [PATCH 3/8] Update create-partner-commission.ts --- .../lib/partners/create-partner-commission.ts | 84 ++++++++++++++++--- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/apps/web/lib/partners/create-partner-commission.ts b/apps/web/lib/partners/create-partner-commission.ts index 5d191217d11..2c713fa4381 100644 --- a/apps/web/lib/partners/create-partner-commission.ts +++ b/apps/web/lib/partners/create-partner-commission.ts @@ -45,6 +45,11 @@ export type CreatePartnerCommissionProps = { skipWorkflow?: boolean; }; +type RewardWithProduct = { + reward: RewardProps; + sale: { amount: number; quantity: number }; +}; + const constructWebhookPartner = ( programEnrollment: ProgramEnrollment & { partner: Partner; links: Link[] }, { @@ -79,8 +84,9 @@ export const createPartnerCommission = async ({ skipWorkflow = false, }: CreatePartnerCommissionProps) => { let earnings = 0; - let reward: RewardProps | null = null; let status: CommissionStatus = "pending"; + let reward: RewardProps | null = null; + let rewards: RewardWithProduct[] = []; // TODO: Find a better name const programEnrollment = await getProgramEnrollmentOrThrow({ partnerId, @@ -141,17 +147,64 @@ export const createPartnerCommission = async ({ }; } - reward = determinePartnerReward({ - event, - programEnrollment, - context, - }); + // Sale event products + const products = context?.sale?.products ?? []; + + if (products.length > 0) { + for (const product of products) { + const reward = determinePartnerReward({ + event, + programEnrollment, + context: { + ...context, + sale: { + ...context?.sale, + productId: product.id, + amount: product.amount, + }, + }, + }); + + if (reward) { + rewards.push({ + reward, + sale: { + amount: product.amount, + quantity: product.quantity, + }, + }); + } + } + + if (rewards.length > 0) { + reward = rewards[0].reward; + } + } else { + reward = determinePartnerReward({ + event, + programEnrollment, + context, + }); + + if (reward) { + rewards.push({ + reward, + sale: { + quantity, + amount, + }, + }); + } + } + + console.log("rewards calculated", rewards); // if there is no reward, skip commission creation if (!reward) { console.log( `Partner ${partnerId} has no reward for ${event} event, skipping commission creation...`, ); + return { commission: null, programEnrollment, @@ -198,6 +251,7 @@ export const createPartnerCommission = async ({ } else { if ( firstCommission.rewardId && + reward && firstCommission.rewardId !== reward.id ) { const originalReward = await prisma.reward.findUnique({ @@ -267,12 +321,18 @@ export const createPartnerCommission = async ({ // for lead events, we just multiply the reward amount by the quantity if (event === "lead") { earnings = getRewardAmount(reward) * quantity; - // for sale events, we need to calculate the earnings based on the sale amount - } else { - earnings = calculateSaleEarnings({ - reward, - sale: { quantity, amount }, - }); + } + // for sale events, we need to calculate the earnings based on the sale amount + else { + earnings = rewards.reduce( + (acc, { reward, sale }) => + acc + + calculateSaleEarnings({ + reward, + sale, + }), + 0, + ); } } } From 30588a62bdd1a63b0fdf6abb20287e2842586df6 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 29 Apr 2026 22:29:36 +0530 Subject: [PATCH 4/8] Update create-partner-commission.ts --- .../lib/partners/create-partner-commission.ts | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/web/lib/partners/create-partner-commission.ts b/apps/web/lib/partners/create-partner-commission.ts index 2c713fa4381..040f2ec87cd 100644 --- a/apps/web/lib/partners/create-partner-commission.ts +++ b/apps/web/lib/partners/create-partner-commission.ts @@ -23,6 +23,7 @@ import { RewardContext, RewardProps } from "../types"; import { sendWorkspaceWebhook } from "../webhook/publish"; import { CommissionWebhookSchema } from "../zod/schemas/commissions"; import { DEFAULT_PARTNER_GROUP } from "../zod/schemas/groups"; +import { rewardConditionsArraySchema } from "../zod/schemas/rewards"; import { aggregatePartnerLinksStats } from "./aggregate-partner-links-stats"; import { determinePartnerReward } from "./determine-partner-reward"; import { getRewardAmount } from "./get-reward-amount"; @@ -147,10 +148,23 @@ export const createPartnerCommission = async ({ }; } - // Sale event products const products = context?.sale?.products ?? []; - if (products.length > 0) { + const modifiers = rewardConditionsArraySchema.safeParse( + programEnrollment.saleReward?.modifiers, + ); + + const hasProductIdModifier = modifiers.success + ? modifiers.data.some((m) => + m.conditions.some( + (c) => c.entity === "sale" && c.attribute === "productId", + ), + ) + : false; + + // If there are products and a productId modifier, + // we need to calculate the reward for each product (for Stripe integration only) + if (products.length > 0 && hasProductIdModifier) { for (const product of products) { const reward = determinePartnerReward({ event, @@ -179,7 +193,20 @@ export const createPartnerCommission = async ({ if (rewards.length > 0) { reward = rewards[0].reward; } - } else { + } + // If there are no products or no productId modifier, + // we can calculate the reward for the entire sale + else { + context = { + ...context, + sale: { + ...context?.sale, + ...(event === "sale" && { + productId: context?.sale?.productId ?? products[0]?.id, + }), + }, + }; + reward = determinePartnerReward({ event, programEnrollment, @@ -197,8 +224,6 @@ export const createPartnerCommission = async ({ } } - console.log("rewards calculated", rewards); - // if there is no reward, skip commission creation if (!reward) { console.log( @@ -251,7 +276,6 @@ export const createPartnerCommission = async ({ } else { if ( firstCommission.rewardId && - reward && firstCommission.rewardId !== reward.id ) { const originalReward = await prisma.reward.findUnique({ From 168a09f62c05640411f2628c89c19fdf891b85c7 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 29 Apr 2026 22:31:26 +0530 Subject: [PATCH 5/8] Update create-partner-commission.ts --- apps/web/lib/partners/create-partner-commission.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/web/lib/partners/create-partner-commission.ts b/apps/web/lib/partners/create-partner-commission.ts index 040f2ec87cd..76e08b25831 100644 --- a/apps/web/lib/partners/create-partner-commission.ts +++ b/apps/web/lib/partners/create-partner-commission.ts @@ -201,6 +201,8 @@ export const createPartnerCommission = async ({ ...context, sale: { ...context?.sale, + // Callers that pass it explicitly keep their value, and + // Stripe webhooks (which send `products[]`) still surface a productId via the first line item ...(event === "sale" && { productId: context?.sale?.productId ?? products[0]?.id, }), From a26261326285872e15a71ebd6c4a27d45ad4e1f7 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 30 Apr 2026 09:32:17 +0530 Subject: [PATCH 6/8] Refactor checkout session product retrieval and update related imports --- .../stripe/integration/webhook/checkout-session-completed.ts | 2 +- .../web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts | 2 +- ...t-session-product-id.ts => get-checkout-session-products.ts} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename apps/web/app/(ee)/api/stripe/integration/webhook/utils/{get-checkout-session-product-id.ts => get-checkout-session-products.ts} (98%) diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts index 4fec28a00a0..fdf4e60ab48 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/checkout-session-completed.ts @@ -27,7 +27,7 @@ import { Customer, Project } from "@dub/prisma/client"; import { COUNTRIES_TO_CONTINENTS, nanoid, pick } from "@dub/utils"; import { waitUntil } from "@vercel/functions"; import type Stripe from "stripe"; -import { getCheckoutSessionProducts } from "./utils/get-checkout-session-product-id"; +import { getCheckoutSessionProducts } from "./utils/get-checkout-session-products"; import { getConnectedCustomer } from "./utils/get-connected-customer"; import { getPromotionCode } from "./utils/get-promotion-code"; import { updateCustomerWithStripeCustomerId } from "./utils/update-customer-with-stripe-customer-id"; diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts index b6b2a1471b4..c698551acff 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/invoice-paid.ts @@ -253,7 +253,7 @@ export async function invoicePaid( return { id: productId, amount: line.amount, - quantity: line.quantity ?? 1, + quantity: line.quantity, }; }) .filter( diff --git a/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts b/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-products.ts similarity index 98% rename from apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts rename to apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-products.ts index 70be52f9719..28c0c8a2f86 100644 --- a/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-product-id.ts +++ b/apps/web/app/(ee)/api/stripe/integration/webhook/utils/get-checkout-session-products.ts @@ -71,7 +71,7 @@ export async function getCheckoutSessionProducts({ return { id: productId, amount: line.amount_total, - quantity: line.quantity ?? 1, + quantity: line.quantity, }; }) .filter( From 65d611927202793ff6f286e2c3e5567fcebdc2b2 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 30 Apr 2026 10:11:08 +0530 Subject: [PATCH 7/8] Update create-partner-commission.ts --- apps/web/lib/partners/create-partner-commission.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/lib/partners/create-partner-commission.ts b/apps/web/lib/partners/create-partner-commission.ts index 76e08b25831..fcfde7ce761 100644 --- a/apps/web/lib/partners/create-partner-commission.ts +++ b/apps/web/lib/partners/create-partner-commission.ts @@ -219,8 +219,8 @@ export const createPartnerCommission = async ({ rewards.push({ reward, sale: { - quantity, amount, + quantity, }, }); } From 6a888ad67248b715e8baf4f00b81d1b1a484e746 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 30 Apr 2026 10:11:44 +0530 Subject: [PATCH 8/8] Update create-partner-commission.ts --- apps/web/lib/partners/create-partner-commission.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/web/lib/partners/create-partner-commission.ts b/apps/web/lib/partners/create-partner-commission.ts index fcfde7ce761..a7bd7410a55 100644 --- a/apps/web/lib/partners/create-partner-commission.ts +++ b/apps/web/lib/partners/create-partner-commission.ts @@ -193,10 +193,7 @@ export const createPartnerCommission = async ({ if (rewards.length > 0) { reward = rewards[0].reward; } - } - // If there are no products or no productId modifier, - // we can calculate the reward for the entire sale - else { + } else { context = { ...context, sale: {