diff --git a/src/test/java/com/adyen/TerminalCloudAPITest.java b/src/test/java/com/adyen/TerminalCloudAPITest.java index fe86ea561..005a5e3d4 100644 --- a/src/test/java/com/adyen/TerminalCloudAPITest.java +++ b/src/test/java/com/adyen/TerminalCloudAPITest.java @@ -38,11 +38,13 @@ import com.adyen.model.nexo.PaymentReceipt; import com.adyen.model.nexo.PaymentRequest; import com.adyen.model.nexo.PaymentResult; +import com.adyen.model.nexo.PaymentTransaction; import com.adyen.model.nexo.Response; import com.adyen.model.nexo.ResultType; import com.adyen.model.nexo.SaleData; import com.adyen.model.nexo.SaleToPOIRequest; import com.adyen.model.nexo.SaleToPOIResponse; +import com.adyen.model.nexo.TransactionConditions; import com.adyen.model.terminal.SaleToAcquirerData; import com.adyen.model.terminal.TerminalAPIRequest; import com.adyen.model.terminal.TerminalAPIResponse; @@ -299,6 +301,50 @@ public void syncInputRequestSuccess() throws Exception { assertFalse(requestAsJson.contains(": null"), "Found null value"); } + /** + * + *

When {@code setDebitPreferredFlag} is never called, the backing field stays {@code null} and + * Gson must omit it from the serialized JSON entirely. This allows the terminal to choose the + * payment type freely (DEBIT, CREDIT, or VOUCHER) rather than being forced to CREDIT by a + * spurious {@code "DebitPreferredFlag": false}. + */ + @Test + public void debitPreferredFlagOmittedFromJsonWhenNotSet() throws Exception { + Client client = createMockClientFromFile("mocks/terminal-api/payment-async-success"); + TerminalCloudAPI terminalCloudApi = new TerminalCloudAPI(client); + + TerminalAPIRequest terminalAPIRequest = createTerminalAPIPaymentRequest(); + + TransactionConditions transactionConditions = new TransactionConditions(); + // Deliberately do NOT call transactionConditions.setDebitPreferredFlag(...) + + PaymentTransaction paymentTransaction = new PaymentTransaction(); + paymentTransaction.setTransactionConditions(transactionConditions); + + terminalAPIRequest + .getSaleToPOIRequest() + .getPaymentRequest() + .setPaymentTransaction(paymentTransaction); + + terminalCloudApi.async(terminalAPIRequest); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(client.getHttpClient()) + .request( + isNotNull(), + captor.capture(), + any(com.adyen.Config.class), + eq(true), + isNull(), + eq(ApiConstants.HttpMethod.POST), + isNull()); + + String requestAsJson = captor.getValue(); + assertFalse( + requestAsJson.contains("DebitPreferredFlag"), + "DebitPreferredFlag must be absent from JSON when setter is never called"); + } + /** Mocked response for stored value type for POST /sync */ @Test public void syncPaymentRequestStoredValueSuccess() throws Exception { diff --git a/src/test/java/com/adyen/TerminalLocalAPITest.java b/src/test/java/com/adyen/TerminalLocalAPITest.java index f63a85032..a09e5e6fe 100644 --- a/src/test/java/com/adyen/TerminalLocalAPITest.java +++ b/src/test/java/com/adyen/TerminalLocalAPITest.java @@ -32,14 +32,18 @@ import com.adyen.model.nexo.PaymentInstrumentType; import com.adyen.model.nexo.PaymentReceipt; import com.adyen.model.nexo.PaymentResult; +import com.adyen.model.nexo.PaymentTransaction; import com.adyen.model.nexo.Response; import com.adyen.model.nexo.ResultType; import com.adyen.model.nexo.SaleData; import com.adyen.model.nexo.SaleToPOIResponse; +import com.adyen.model.nexo.TransactionConditions; import com.adyen.model.terminal.TerminalAPIRequest; import com.adyen.model.terminal.TerminalAPIResponse; import com.adyen.model.terminal.security.SecurityKey; import com.adyen.service.TerminalLocalAPI; +import com.adyen.terminal.serialization.TerminalAPIGsonBuilder; +import com.google.gson.Gson; import java.math.BigDecimal; import java.util.List; import org.junit.jupiter.api.Test; @@ -135,6 +139,38 @@ public void syncPaymentRequestSuccess() throws Exception { assertEquals(BigDecimal.ONE, paymentResult.getAmountsResp().getAuthorizedAmount()); } + /** + * Regression test for the DebitPreferredFlag workaround (Option A) on the local Terminal API. + * + *

{@link TerminalLocalAPI} serializes the request with {@link TerminalAPIGsonBuilder} before + * encrypting it. When {@code setDebitPreferredFlag} is never called, the backing field stays + * {@code null} and Gson must omit it from the JSON, allowing the terminal to choose the payment + * type freely (DEBIT, CREDIT, or VOUCHER). + */ + @Test + public void debitPreferredFlagOmittedFromJsonWhenNotSet() throws Exception { + Gson gson = TerminalAPIGsonBuilder.create(); + + TerminalAPIRequest terminalAPIRequest = createTerminalAPIPaymentRequest(); + + TransactionConditions transactionConditions = new TransactionConditions(); + // Deliberately do NOT call transactionConditions.setDebitPreferredFlag(...) + + PaymentTransaction paymentTransaction = new PaymentTransaction(); + paymentTransaction.setTransactionConditions(transactionConditions); + + terminalAPIRequest + .getSaleToPOIRequest() + .getPaymentRequest() + .setPaymentTransaction(paymentTransaction); + + String json = gson.toJson(terminalAPIRequest); + + assertFalse( + json.contains("DebitPreferredFlag"), + "DebitPreferredFlag must be absent from JSON when setter is never called"); + } + /** Test success flow for local request that includes unexpected attributes */ @Test public void syncPaymentRequestSuccessWithAdditionalAttributes() throws Exception {