@@ -2120,6 +2120,7 @@ describe('CartService', () => {
21202120 } ) ;
21212121
21222122 describe ( 'getCart' , ( ) => {
2123+ const mockPaymentIntentInvoiceId = 'in_mock_from_intent' ;
21232124 const mockCustomer = StripeResponseFactory ( StripeCustomerFactory ( ) ) ;
21242125 const mockCustomerSession = StripeResponseFactory (
21252126 StripeCustomerSessionFactory ( )
@@ -2323,6 +2324,13 @@ describe('CartService', () => {
23232324 jest
23242325 . spyOn ( paymentMethodManager , 'retrieve' )
23252326 . mockResolvedValue ( mockPaymentMethod ) ;
2327+ jest
2328+ . spyOn ( paymentIntentManager , 'retrieve' )
2329+ . mockResolvedValue (
2330+ StripeResponseFactory (
2331+ StripePaymentIntentFactory ( { invoice : mockPaymentIntentInvoiceId } )
2332+ )
2333+ ) ;
23262334
23272335 const result = await cartService . getCart ( mockCart . id ) ;
23282336 expect ( result ) . toEqual ( {
@@ -2358,8 +2366,11 @@ describe('CartService', () => {
23582366 customer : mockCustomer ,
23592367 taxAddress : mockCart . taxAddress ,
23602368 } ) ;
2369+ expect ( paymentIntentManager . retrieve ) . toHaveBeenCalledWith (
2370+ mockCart . stripeIntentId
2371+ ) ;
23612372 expect ( invoiceManager . preview ) . toHaveBeenCalledWith (
2362- mockSubscription . latest_invoice
2373+ mockPaymentIntentInvoiceId
23632374 ) ;
23642375 } ) ;
23652376
@@ -2860,12 +2871,125 @@ describe('CartService', () => {
28602871 } ) ;
28612872
28622873 describe ( 'CartState.SUCCESS' , ( ) => {
2874+ const mockInvoiceId = 'in_mock_invoice_id' ;
28632875 const mockCart = ResultCartFactory ( {
28642876 state : CartState . SUCCESS ,
28652877 } ) ;
28662878
28672879 beforeEach ( ( ) => {
28682880 jest . spyOn ( cartManager , 'fetchCartById' ) . mockResolvedValue ( mockCart ) ;
2881+ jest
2882+ . spyOn ( paymentIntentManager , 'retrieve' )
2883+ . mockResolvedValue (
2884+ StripeResponseFactory (
2885+ StripePaymentIntentFactory ( { invoice : mockInvoiceId } )
2886+ )
2887+ ) ;
2888+ } ) ;
2889+
2890+ it ( 'uses invoice from PaymentIntent for Stripe carts' , async ( ) => {
2891+ await cartService . getCart ( mockCart . id ) ;
2892+
2893+ expect ( paymentIntentManager . retrieve ) . toHaveBeenCalledWith (
2894+ mockCart . stripeIntentId
2895+ ) ;
2896+ expect ( invoiceManager . preview ) . toHaveBeenCalledWith ( mockInvoiceId ) ;
2897+ } ) ;
2898+
2899+ it ( 'falls back to timestamp lookup when PaymentIntent invoice is null' , async ( ) => {
2900+ const fallbackInvoiceId = 'in_timestamp_fallback' ;
2901+ jest
2902+ . spyOn ( paymentIntentManager , 'retrieve' )
2903+ . mockResolvedValue (
2904+ StripeResponseFactory (
2905+ StripePaymentIntentFactory ( { invoice : null } )
2906+ )
2907+ ) ;
2908+ jest
2909+ . spyOn ( invoiceManager , 'retrieveBySubscriptionBeforeTimestamp' )
2910+ . mockResolvedValue ( fallbackInvoiceId ) ;
2911+
2912+ await cartService . getCart ( mockCart . id ) ;
2913+
2914+ expect ( paymentIntentManager . retrieve ) . toHaveBeenCalledWith (
2915+ mockCart . stripeIntentId
2916+ ) ;
2917+ expect (
2918+ invoiceManager . retrieveBySubscriptionBeforeTimestamp
2919+ ) . toHaveBeenCalledWith (
2920+ mockCart . stripeSubscriptionId ,
2921+ mockCart . updatedAt
2922+ ) ;
2923+ expect ( invoiceManager . preview ) . toHaveBeenCalledWith ( fallbackInvoiceId ) ;
2924+ } ) ;
2925+
2926+ it ( 'falls back to subscription invoice list for PayPal carts' , async ( ) => {
2927+ const paypalCart = ResultCartFactory ( {
2928+ state : CartState . SUCCESS ,
2929+ stripeIntentId : null ,
2930+ } ) ;
2931+ const fallbackInvoiceId = 'in_paypal_invoice' ;
2932+ jest
2933+ . spyOn ( cartManager , 'fetchCartById' )
2934+ . mockResolvedValue ( paypalCart ) ;
2935+ jest
2936+ . spyOn ( invoiceManager , 'retrieveBySubscriptionBeforeTimestamp' )
2937+ . mockResolvedValue ( fallbackInvoiceId ) ;
2938+
2939+ await cartService . getCart ( paypalCart . id ) ;
2940+
2941+ expect ( paymentIntentManager . retrieve ) . not . toHaveBeenCalled ( ) ;
2942+ expect (
2943+ invoiceManager . retrieveBySubscriptionBeforeTimestamp
2944+ ) . toHaveBeenCalledWith (
2945+ paypalCart . stripeSubscriptionId ,
2946+ paypalCart . updatedAt
2947+ ) ;
2948+ expect ( invoiceManager . preview ) . toHaveBeenCalledWith ( fallbackInvoiceId ) ;
2949+ } ) ;
2950+
2951+ it ( 'falls back to timestamp lookup for SetupIntent carts' , async ( ) => {
2952+ const setupIntentCart = ResultCartFactory ( {
2953+ state : CartState . SUCCESS ,
2954+ stripeIntentId : `seti_${ faker . string . alphanumeric ( 14 ) } ` ,
2955+ } ) ;
2956+ const fallbackInvoiceId = 'in_setup_intent_invoice' ;
2957+ jest
2958+ . spyOn ( cartManager , 'fetchCartById' )
2959+ . mockResolvedValue ( setupIntentCart ) ;
2960+ jest
2961+ . spyOn ( invoiceManager , 'retrieveBySubscriptionBeforeTimestamp' )
2962+ . mockResolvedValue ( fallbackInvoiceId ) ;
2963+
2964+ await cartService . getCart ( setupIntentCart . id ) ;
2965+
2966+ expect ( paymentIntentManager . retrieve ) . not . toHaveBeenCalled ( ) ;
2967+ expect (
2968+ invoiceManager . retrieveBySubscriptionBeforeTimestamp
2969+ ) . toHaveBeenCalledWith (
2970+ setupIntentCart . stripeSubscriptionId ,
2971+ setupIntentCart . updatedAt
2972+ ) ;
2973+ expect ( invoiceManager . preview ) . toHaveBeenCalledWith ( fallbackInvoiceId ) ;
2974+ } ) ;
2975+
2976+ it ( 'does not fall back to subscription.latest_invoice for SUCCESS carts' , async ( ) => {
2977+ const cartWithSetupIntent = ResultCartFactory ( {
2978+ state : CartState . SUCCESS ,
2979+ stripeIntentId : `seti_${ faker . string . alphanumeric ( 14 ) } ` ,
2980+ } ) ;
2981+ jest
2982+ . spyOn ( cartManager , 'fetchCartById' )
2983+ . mockResolvedValue ( cartWithSetupIntent ) ;
2984+ jest
2985+ . spyOn ( invoiceManager , 'retrieveBySubscriptionBeforeTimestamp' )
2986+ . mockResolvedValue ( undefined ) ;
2987+
2988+ await expect (
2989+ cartService . getCart ( cartWithSetupIntent . id )
2990+ ) . rejects . toThrow ( / G e t C a r t L a t e s t I n v o i c e P r e v i e w M i s s i n g E r r o r / ) ;
2991+
2992+ expect ( subscriptionManager . retrieve ) . not . toHaveBeenCalled ( ) ;
28692993 } ) ;
28702994
28712995 it ( 'throws assertion error on missing latestInvoicePreview' , async ( ) => {
@@ -3041,6 +3165,13 @@ describe('CartService', () => {
30413165 . mockResolvedValue ( mockUpcoming ) ;
30423166 jest . spyOn ( invoiceManager , 'preview' ) . mockResolvedValue ( mockLatest ) ;
30433167 jest . spyOn ( paymentMethodManager , 'retrieve' ) . mockResolvedValue ( mockPM ) ;
3168+ jest
3169+ . spyOn ( paymentIntentManager , 'retrieve' )
3170+ . mockResolvedValue (
3171+ StripeResponseFactory (
3172+ StripePaymentIntentFactory ( { invoice : mockPaymentIntentInvoiceId } )
3173+ )
3174+ ) ;
30443175
30453176 const result = await cartService . getCart ( mockCart . id ) ;
30463177 expect ( result . freeTrialOffer ) . toBeNull ( ) ;
@@ -3092,6 +3223,13 @@ describe('CartService', () => {
30923223 jest
30933224 . spyOn ( subscriptionManager , 'listForCustomer' )
30943225 . mockResolvedValue ( [ mockTrialSubscription ] ) ;
3226+ jest
3227+ . spyOn ( paymentIntentManager , 'retrieve' )
3228+ . mockResolvedValue (
3229+ StripeResponseFactory (
3230+ StripePaymentIntentFactory ( { invoice : mockPaymentIntentInvoiceId } )
3231+ )
3232+ ) ;
30953233
30963234 const result = await cartService . getCart ( mockCart . id ) ;
30973235 expect ( result . trialStartDate ) . toBe ( mockTrialSubscription . trial_start ) ;
0 commit comments