@@ -61,6 +61,7 @@ import {
6161 TokenizationAlreadyExistingDetailsNotificationRequest ,
6262} from '@adyen/api-library/lib/src/typings/tokenizationWebhooks/models' ;
6363import { RecurringApi } from '@adyen/api-library/lib/src/services/checkout/recurringApi' ;
64+ import { OrdersApi } from '@adyen/api-library/lib/src/services/checkout/ordersApi' ;
6465
6566import * as FastifyContext from '../../src/libs/fastify/context/context' ;
6667import { StoredPaymentMethod } from '../../src/dtos/stored-payment-methods.dto' ;
@@ -128,7 +129,7 @@ describe('adyen-payment.service', () => {
128129
129130 test ( 'getSupportedPaymentComponents' , async ( ) => {
130131 const result : SupportedPaymentComponentsSchemaDTO = await paymentService . getSupportedPaymentComponents ( ) ;
131- expect ( result ?. components ) . toHaveLength ( 24 ) ;
132+ expect ( result ?. components ) . toHaveLength ( 27 ) ;
132133 expect ( result ?. components [ 0 ] ?. type ) . toStrictEqual ( 'afterpay' ) ;
133134 expect ( result ?. components [ 1 ] ?. type ) . toStrictEqual ( 'applepay' ) ;
134135 expect ( result ?. components [ 2 ] ?. type ) . toStrictEqual ( 'bancontactcard' ) ;
@@ -153,6 +154,9 @@ describe('adyen-payment.service', () => {
153154 expect ( result ?. components [ 21 ] ?. type ) . toStrictEqual ( 'clearpay' ) ;
154155 expect ( result ?. components [ 22 ] ?. type ) . toStrictEqual ( 'mbway' ) ;
155156 expect ( result ?. components [ 23 ] ?. type ) . toStrictEqual ( 'trustly' ) ;
157+ expect ( result ?. components [ 24 ] ?. type ) . toStrictEqual ( 'zip' ) ;
158+ expect ( result ?. components [ 25 ] ?. type ) . toStrictEqual ( 'wechatpay' ) ;
159+ expect ( result ?. components [ 26 ] ?. type ) . toStrictEqual ( 'alipay' ) ;
156160 } ) ;
157161
158162 test ( 'getStatus' , async ( ) => {
@@ -2215,5 +2219,116 @@ describe('adyen-payment.service', () => {
22152219 const result = service . calculateRemainingAmount ( cartWithPayments ( [ p1 , p2 ] ) ) ;
22162220 expect ( result . centAmount ) . toBe ( 10000 - 1000 - 3000 ) ;
22172221 } ) ;
2222+
2223+ test ( 'throws ErrorInvalidOperation when remaining amount is exactly zero' , ( ) => {
2224+ const payment = approvedPayment ( {
2225+ amountPlanned : { type : 'centPrecision' , centAmount : 10000 , currencyCode : 'USD' , fractionDigits : 2 } ,
2226+ transactions : [
2227+ {
2228+ id : 'tx-1' ,
2229+ type : 'Authorization' ,
2230+ state : 'Success' ,
2231+ amount : { type : 'centPrecision' , centAmount : 10000 , currencyCode : 'USD' , fractionDigits : 2 } ,
2232+ timestamp : '2024-01-01T00:00:00Z' ,
2233+ } ,
2234+ ] ,
2235+ } ) ;
2236+ expect ( ( ) => service . calculateRemainingAmount ( cartWithPayments ( [ payment ] ) ) ) . toThrow ( ErrorInvalidOperation ) ;
2237+ } ) ;
2238+
2239+ test ( 'throws ErrorInvalidOperation when remaining amount would be negative' , ( ) => {
2240+ jest . spyOn ( FastifyContext , 'getGiftCardPlannedCentAmountFromContext' ) . mockReturnValue ( 10001 ) ;
2241+ expect ( ( ) => service . calculateRemainingAmount ( baseCart ( ) ) ) . toThrow ( ErrorInvalidOperation ) ;
2242+ } ) ;
2243+ } ) ;
2244+
2245+ describe ( 'createPayment - gift card split payment (getAmountToPay)' , ( ) => {
2246+ const giftCardPaymentOpts : { data : CreatePaymentRequestDTO } = {
2247+ data : {
2248+ paymentMethod : { type : 'giftcard' , brand : 'givex' } as Record < string , string > ,
2249+ order : { orderData : 'some-order-data' , pspReference : 'ORDER-PSP-1' } ,
2250+ } ,
2251+ } ;
2252+
2253+ beforeEach ( ( ) => {
2254+ jest . spyOn ( DefaultCartService . prototype , 'getCart' ) . mockResolvedValue ( mockGetCartResultShippingModeSimple ( ) ) ;
2255+ jest . spyOn ( DefaultCartService . prototype , 'getPaymentAmount' ) . mockResolvedValue ( mockGetPaymentAmount ) ;
2256+ jest . spyOn ( DefaultPaymentService . prototype , 'createPayment' ) . mockResolvedValue ( mockGetPaymentResult ) ;
2257+ jest . spyOn ( DefaultCartService . prototype , 'addPayment' ) . mockResolvedValue ( mockGetCartResultShippingModeSimple ( ) ) ;
2258+ jest . spyOn ( FastifyContext , 'getProcessorUrlFromContext' ) . mockReturnValue ( 'http://127.0.0.1' ) ;
2259+ jest . spyOn ( FastifyContext , 'getMerchantReturnUrlFromContext' ) . mockReturnValue ( 'http://127.0.0.1/checkout/result' ) ;
2260+ jest . spyOn ( PaymentsApi . prototype , 'payments' ) . mockResolvedValue ( mockAdyenCreatePaymentResponse ) ;
2261+ jest . spyOn ( DefaultPaymentService . prototype , 'updatePayment' ) . mockResolvedValue ( mockGetPaymentResult ) ;
2262+ } ) ;
2263+
2264+ test ( 'uses gift card balance as amountPlanned when balance is less than cart amount' , async ( ) => {
2265+ // cartAmount = 150000 (mockGetPaymentAmount), balance = 5000 → use 5000
2266+ jest . spyOn ( OrdersApi . prototype , 'getBalanceOfGiftCard' ) . mockResolvedValue ( {
2267+ balance : { value : 5000 , currency : 'USD' } ,
2268+ pspReference : 'BALANCE-PSP-1' ,
2269+ resultCode : 'Success' ,
2270+ } ) ;
2271+
2272+ const adyenPaymentService = new AdyenPaymentService ( opts ) ;
2273+ await adyenPaymentService . createPayment ( giftCardPaymentOpts ) ;
2274+
2275+ expect ( DefaultPaymentService . prototype . createPayment ) . toHaveBeenCalledWith (
2276+ expect . objectContaining ( {
2277+ amountPlanned : expect . objectContaining ( { centAmount : 5000 , currencyCode : 'USD' } ) ,
2278+ } ) ,
2279+ ) ;
2280+ } ) ;
2281+
2282+ test ( 'caps amountPlanned at cart amount when gift card balance exceeds remaining cart amount' , async ( ) => {
2283+ // cartAmount = 150000 (mockGetPaymentAmount), balance = 200000 → cap at 150000
2284+ jest . spyOn ( OrdersApi . prototype , 'getBalanceOfGiftCard' ) . mockResolvedValue ( {
2285+ balance : { value : 200000 , currency : 'USD' } ,
2286+ pspReference : 'BALANCE-PSP-2' ,
2287+ resultCode : 'Success' ,
2288+ } ) ;
2289+
2290+ const adyenPaymentService = new AdyenPaymentService ( opts ) ;
2291+ await adyenPaymentService . createPayment ( giftCardPaymentOpts ) ;
2292+
2293+ expect ( DefaultPaymentService . prototype . createPayment ) . toHaveBeenCalledWith (
2294+ expect . objectContaining ( {
2295+ amountPlanned : expect . objectContaining ( { centAmount : 150000 , currencyCode : 'USD' } ) ,
2296+ } ) ,
2297+ ) ;
2298+ } ) ;
2299+
2300+ test ( 'falls back to cart amount when balance check returns no balance' , async ( ) => {
2301+ jest . spyOn ( OrdersApi . prototype , 'getBalanceOfGiftCard' ) . mockResolvedValue ( {
2302+ pspReference : 'BALANCE-PSP-3' ,
2303+ resultCode : 'NotEnoughBalance' ,
2304+ } ) ;
2305+
2306+ const adyenPaymentService = new AdyenPaymentService ( opts ) ;
2307+ await adyenPaymentService . createPayment ( giftCardPaymentOpts ) ;
2308+
2309+ expect ( DefaultPaymentService . prototype . createPayment ) . toHaveBeenCalledWith (
2310+ expect . objectContaining ( {
2311+ amountPlanned : expect . objectContaining ( { centAmount : 150000 , currencyCode : 'USD' } ) ,
2312+ } ) ,
2313+ ) ;
2314+ } ) ;
2315+
2316+ test ( 'uses cart amount directly when payment is not a gift card split payment' , async ( ) => {
2317+ const cardPaymentOpts : { data : CreatePaymentRequestDTO } = {
2318+ data : { paymentMethod : { type : 'scheme' } as Record < string , string > } ,
2319+ } ;
2320+
2321+ const balanceSpy = jest . spyOn ( OrdersApi . prototype , 'getBalanceOfGiftCard' ) ;
2322+
2323+ const adyenPaymentService = new AdyenPaymentService ( opts ) ;
2324+ await adyenPaymentService . createPayment ( cardPaymentOpts ) ;
2325+
2326+ expect ( balanceSpy ) . not . toHaveBeenCalled ( ) ;
2327+ expect ( DefaultPaymentService . prototype . createPayment ) . toHaveBeenCalledWith (
2328+ expect . objectContaining ( {
2329+ amountPlanned : expect . objectContaining ( { centAmount : 150000 } ) ,
2330+ } ) ,
2331+ ) ;
2332+ } ) ;
22182333 } ) ;
22192334} ) ;
0 commit comments