@@ -53,7 +53,8 @@ export function calculateInvoiceAmounts(
5353 productId ?: string
5454) : {
5555 amount : number ;
56- amountAfterDiscount : number ;
56+ amountAfterProcessing : number ;
57+ refundAmount : number ;
5758} {
5859 // Calculate the original amount (product-specific if productId is provided)
5960 let amount = 0 ;
@@ -80,39 +81,84 @@ export function calculateInvoiceAmounts(
8081 // Calculate the discount based on this specific amount
8182 let discountAmount = 0 ;
8283
84+ // Handle both single discount and multiple discounts
85+ const discountsToApply : Stripe . Discount [ ] = [ ] ;
86+
8387 if ( invoice . discount ) {
84- const discountObj = invoice . discount ;
88+ // Single discount case
89+ discountsToApply . push ( invoice . discount ) ;
90+ }
91+
92+ if ( invoice . discounts && invoice . discounts . length > 1 ) {
93+ // Multiple discounts case - can be array of IDs or objects
94+ if ( Array . isArray ( invoice . discounts ) ) {
95+ invoice . discounts . forEach ( ( discount ) => {
96+ // Skip deleted discounts
97+ if ( typeof discount === "object" && "deleted" in discount ) {
98+ return ; // Skip this iteration
99+ }
100+
101+ // If it's a string (discount ID), look it up in the discounts parameter
102+ if ( typeof discount === "string" && discounts && discounts [ discount ] ) {
103+ discountsToApply . push ( discounts [ discount ] ) ;
104+ }
105+ // If it's a Discount object, use it directly
106+ else if ( typeof discount === "object" && "id" in discount ) {
107+ discountsToApply . push ( discount as Stripe . Discount ) ;
108+ }
109+ } ) ;
110+ }
111+ }
112+
113+ // Apply each discount - loop over our collected valid discount objects
114+ discountsToApply . forEach ( ( discountObj ) => {
85115 const { coupon } = discountObj ;
86116
87117 if ( coupon ) {
88- // Check if discount applies to all products or specific ones
89- const hasNoProductRestrictions =
90- ! coupon . applies_to ||
91- ! coupon . applies_to . products ||
92- coupon . applies_to . products . length === 0 ;
118+ // Check if discount applies to specific products
119+ const hasProductRestrictions =
120+ coupon . applies_to &&
121+ coupon . applies_to . products &&
122+ coupon . applies_to . products . length > 0 ;
93123
94124 // If productId is specified, check if discount applies to it
95125 const discountAppliesToProduct =
96- hasNoProductRestrictions ||
126+ ! hasProductRestrictions ||
97127 ( productId && coupon . applies_to ?. products ?. includes ( productId ) ) ;
98128
99129 if ( discountAppliesToProduct ) {
100130 if ( coupon . amount_off ) {
101131 // For amount-based discounts, only apply if product-specific
102- if ( ! hasNoProductRestrictions || amount < 1000 ) {
103- discountAmount = coupon . amount_off / 100 ;
132+ if (
133+ hasProductRestrictions ||
134+ amount < 1000 ||
135+ coupon . amount_off > 1000
136+ ) {
137+ discountAmount += coupon . amount_off / 100 ;
104138 }
105139 } else if ( coupon . percent_off ) {
106140 // Always apply percentage discounts, even at subscription level
107- discountAmount = amount * ( coupon . percent_off / 100 ) ;
141+ discountAmount + = amount * ( coupon . percent_off / 100 ) ;
108142 }
109143 }
110144 }
145+ } ) ;
146+
147+ // Calculate refund amount if available
148+ let refundAmount = 0 ;
149+
150+ // Check if this is a regular invoice with an expanded charge
151+ if ( "charge" in invoice && invoice . charge ) {
152+ // If charge is expanded to an object with refunds
153+ if ( typeof invoice . charge !== "string" && invoice . charge . refunds ) {
154+ refundAmount = ( invoice . charge . amount_refunded || 0 ) / 100 ;
155+ }
111156 }
112157
113- // Return both values
158+ // Return values - calculate final amount after both discounts and refunds
114159 return {
115160 amount,
116- amountAfterDiscount : Math . max ( 0 , amount - discountAmount ) ,
161+ amountAfterProcessing : Math . max ( 0 , amount - discountAmount - refundAmount ) ,
162+ refundAmount,
117163 } ;
118164}
0 commit comments