From 0cbc35d0e73c5e6c473c2b746edfdc8dd8a91e20 Mon Sep 17 00:00:00 2001 From: Bishandeep Kaur Manmeet Singh Arora Date: Wed, 21 Oct 2026 11:35:37 +0530 Subject: [PATCH 1/2] Add CreditLinePaymentInstrumentV2 with providerAccountType and bump version to 2.2.4 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 6 +- pom.xml | 2 +- .../CreditLinePaymentInstrumentV2.java | 52 +++++++++++++ .../PaymentInstrumentV2.java | 5 +- src/test/CallbackTest.java | 69 +++++++++++++++++ src/test/OrderStatusTest.java | 74 +++++++++++++++++++ 6 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/CreditLinePaymentInstrumentV2.java diff --git a/README.md b/README.md index 15c18c5..e6b71e7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PhonePe B2B Payment Gateway SDK for Java -[![Maven Central](https://img.shields.io/badge/Maven%20Central-v2.2.1-blue)](https://maven-badges.herokuapp.com/maven-central/com.phonepe/pg-sdk-java) +[![Maven Central](https://img.shields.io/badge/Maven%20Central-v2.2.4-blue)](https://maven-badges.herokuapp.com/maven-central/com.phonepe/pg-sdk-java) ![Java](https://img.shields.io/badge/Java-17%2B-orange) [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](LICENSE) @@ -36,7 +36,7 @@ Add the dependency to your project's POM file: com.phonepe pg-sdk-java - 2.2.3 + 2.2.4 ``` @@ -46,7 +46,7 @@ Add the following to your project's build.gradle file: ```gradle dependencies { - implementation 'com.phonepe:pg-sdk-java:2.2.3' + implementation 'com.phonepe:pg-sdk-java:2.2.4' } ``` diff --git a/pom.xml b/pom.xml index 8863f86..3b552c6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.phonepe pg-sdk-java - 2.2.3 + 2.2.4 B2B JAVA SDK https://github.com/PhonePe/phonepe-pg-sdk-java Phonepe PG JAVA SDK diff --git a/src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/CreditLinePaymentInstrumentV2.java b/src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/CreditLinePaymentInstrumentV2.java new file mode 100644 index 0000000..b3b1562 --- /dev/null +++ b/src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/CreditLinePaymentInstrumentV2.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 Original Author(s), PhonePe India Pvt. Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.phonepe.sdk.pg.common.models.response.paymentinstruments; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties(ignoreUnknown = true) +public class CreditLinePaymentInstrumentV2 extends PaymentInstrumentV2 { + + private String maskedAccountNumber; + private String ifsc; + private String accountHolderName; + private String bankId; + private String providerAccountType; + + public CreditLinePaymentInstrumentV2() { + super(PaymentInstrumentType.CREDIT_LINE); + } + + @Builder + public CreditLinePaymentInstrumentV2( + String maskedAccountNumber, + String ifsc, + String accountHolderName, + String bankId, + String providerAccountType) { + super(PaymentInstrumentType.CREDIT_LINE); + this.maskedAccountNumber = maskedAccountNumber; + this.ifsc = ifsc; + this.accountHolderName = accountHolderName; + this.bankId = bankId; + this.providerAccountType = providerAccountType; + } +} diff --git a/src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/PaymentInstrumentV2.java b/src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/PaymentInstrumentV2.java index 9acf3b3..fd3d6c7 100644 --- a/src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/PaymentInstrumentV2.java +++ b/src/main/java/com/phonepe/sdk/pg/common/models/response/paymentinstruments/PaymentInstrumentV2.java @@ -28,7 +28,8 @@ @JsonSubTypes.Type(name = "NET_BANKING", value = NetBankingPaymentInstrumentV2.class), @JsonSubTypes.Type(name = "ACCOUNT", value = AccountPaymentInstrumentV2.class), @JsonSubTypes.Type(name = "CREDIT_CARD", value = CreditCardPaymentInstrumentV2.class), - @JsonSubTypes.Type(name = "DEBIT_CARD", value = DebitCardPaymentInstrumentV2.class) + @JsonSubTypes.Type(name = "DEBIT_CARD", value = DebitCardPaymentInstrumentV2.class), + @JsonSubTypes.Type(name = "CREDIT_LINE", value = CreditLinePaymentInstrumentV2.class) }) @NoArgsConstructor @Data @@ -41,6 +42,6 @@ public PaymentInstrumentV2(PaymentInstrumentType type) { } public enum PaymentInstrumentType { - WALLET, EGV, ACCOUNT, CREDIT_CARD, DEBIT_CARD, NET_BANKING + WALLET, EGV, ACCOUNT, CREDIT_CARD, DEBIT_CARD, NET_BANKING, CREDIT_LINE } } diff --git a/src/test/CallbackTest.java b/src/test/CallbackTest.java index b9d756d..84d8f89 100644 --- a/src/test/CallbackTest.java +++ b/src/test/CallbackTest.java @@ -21,6 +21,7 @@ import com.phonepe.sdk.pg.common.models.response.PaymentDetail; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.AccountPaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.CreditCardPaymentInstrumentV2; +import com.phonepe.sdk.pg.common.models.response.paymentinstruments.CreditLinePaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.DebitCardPaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.EGVPaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.NetBankingPaymentInstrumentV2; @@ -186,6 +187,74 @@ void testPG_ORDER_FAILED() { Assertions.assertEquals(expected, actual); } + @Test + void testPG_ORDER_COMPLETED_withCreditLineInstrument() { + String jsonString = + "{\"event\":\"pg.order.completed\",\"payload\":" + + "{\"orderId\":\"OMOxx\",\"paymentDetails\":" + + "[{\"paymentMode\":\"UPI_INTENT\",\"splitInstruments\":" + + "[{\"rail\":{\"type\":\"UPI\",\"utr\":\"utr12313\"," + + "\"upiTransactionId\":\"upi12313\"," + + "\"vpa\":\"abcd@ybl\"},\"instrument\":{\"type\":\"CREDIT_LINE\"," + + "\"ifsc\":\"AABE0000000\",\"accountHolderName\":\"accountHolderName\"," + + "\"bankId\":\"bankId\",\"maskedAccountNumber\":\"121212121212\"," + + "\"providerAccountType\":\"providerAccountType\"},\"amount\":10000}]}]}}"; + + CallbackResponse expected = + CallbackResponse.builder() + .event("pg.order.completed") + .payload( + CallbackData.builder() + .orderId("OMOxx") + .paymentDetails( + Arrays.asList( + PaymentDetail.builder() + .paymentMode( + PgV2InstrumentType + .UPI_INTENT) + .splitInstruments( + Arrays.asList( + InstrumentCombo + .builder() + .instrument( + CreditLinePaymentInstrumentV2 + .builder() + .ifsc( + "AABE0000000") + .accountHolderName( + "accountHolderName") + .bankId( + "bankId") + .maskedAccountNumber( + "121212121212") + .providerAccountType( + "providerAccountType") + .build()) + .rail( + UpiPaymentRail + .builder() + .utr( + "utr12313") + .upiTransactionId( + "upi12313") + .vpa( + "abcd@ybl") + .build()) + .amount( + 10000) + .build())) + .build())) + .build()) + .build(); + CallbackResponse actual = + standardCheckoutClient.validateCallback( + "username", + "password", + "bc842c31a9e54efe320d30d948be61291f3ceee4766e36ab25fa65243cd76e0e", + jsonString); + Assertions.assertEquals(expected, actual); + } + @Test void testPG_TRANSACTION_ATTEMPT_FAILED() { String jsonString = diff --git a/src/test/OrderStatusTest.java b/src/test/OrderStatusTest.java index 6e73752..c0d9962 100644 --- a/src/test/OrderStatusTest.java +++ b/src/test/OrderStatusTest.java @@ -25,6 +25,7 @@ import com.phonepe.sdk.pg.common.models.response.PaymentDetail; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.AccountPaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.CreditCardPaymentInstrumentV2; +import com.phonepe.sdk.pg.common.models.response.paymentinstruments.CreditLinePaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.EGVPaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.NetBankingPaymentInstrumentV2; import com.phonepe.sdk.pg.common.models.response.paymentinstruments.WalletPaymentInstrumentV2; @@ -145,6 +146,79 @@ void orderStatusForSplitInstruments() { Assertions.assertEquals(actual, responeToObject); } + @Test + void orderStatusForCreditLineInstrument() { + Map headers = getHeaders(); + String url = String.format(StandardCheckoutConstants.ORDER_STATUS_API, merchantOrderId); + String response = + "{\"orderId\":\"OMO2402281351151884090310\",\"state\":\"COMPLETED\",\"amount\":100," + + "\"expireAt\":1709108893,\"paymentDetails\":[{\"transactionId\":" + + "\"OM2402281351157894090178\",\"paymentMode\":\"UPI_INTENT\",\"timestamp\":" + + "1709108475,\"amount\":100,\"state\":\"COMPLETED\",\"splitInstruments\":" + + "[{\"instrument\":{\"type\":\"CREDIT_LINE\",\"ifsc\":\"AABE0000000\"," + + "\"accountHolderName\":\"accountHolderName\",\"bankId\":\"bankId\"," + + "\"maskedAccountNumber\":\"121212121212\",\"providerAccountType\":" + + "\"providerAccountType\"},\"rail\":{\"type\":\"UPI\",\"utr\":\"utr12313\"," + + "\"upiTransactionId\":\"upi12313\",\"vpa\":" + + "\"abcd@ybl\"},\"amount\":100}]}]}"; + addStubForGetRequest( + url, + ImmutableMap.of(StandardCheckoutConstants.ORDER_DETAILS, "true"), + headers, + HttpStatus.SC_OK, + ImmutableMap.of(), + response); + + OrderStatusResponse responeToObject = + OrderStatusResponse.builder() + .orderId("OMO2402281351151884090310") + .state("COMPLETED") + .amount(100) + .expireAt(1709108893L) + .paymentDetails( + Arrays.asList( + PaymentDetail.builder() + .transactionId("OM2402281351157894090178") + .paymentMode(PgV2InstrumentType.UPI_INTENT) + .timestamp(1709108475L) + .amount(100) + .state("COMPLETED") + .splitInstruments( + Arrays.asList( + InstrumentCombo.builder() + .instrument( + CreditLinePaymentInstrumentV2 + .builder() + .ifsc( + "AABE0000000") + .accountHolderName( + "accountHolderName") + .bankId( + "bankId") + .maskedAccountNumber( + "121212121212") + .providerAccountType( + "providerAccountType") + .build()) + .rail( + UpiPaymentRail + .builder() + .utr( + "utr12313") + .upiTransactionId( + "upi12313") + .vpa( + "abcd@ybl") + .build()) + .amount(100) + .build())) + .build())) + .build(); + + OrderStatusResponse actual = standardCheckoutClient.getOrderStatus(merchantOrderId, true); + Assertions.assertEquals(actual, responeToObject); + } + @Test void testTokenSuccess() { Map headers = getHeaders(); From 218d3438cfcda95859dc5c949f0132606ab38b80 Mon Sep 17 00:00:00 2001 From: Bishandeep Kaur Manmeet Singh Arora Date: Mon, 26 Oct 2026 17:34:24 +0530 Subject: [PATCH 2/2] Add unit tests for CreditLinePaymentInstrumentV2 to meet coverage gate Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CreditLinePaymentInstrumentV2Test.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/test/CreditLinePaymentInstrumentV2Test.java diff --git a/src/test/CreditLinePaymentInstrumentV2Test.java b/src/test/CreditLinePaymentInstrumentV2Test.java new file mode 100644 index 0000000..038b593 --- /dev/null +++ b/src/test/CreditLinePaymentInstrumentV2Test.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Original Author(s), PhonePe India Pvt. Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import com.fasterxml.jackson.databind.ObjectMapper; +import com.phonepe.sdk.pg.common.models.response.paymentinstruments.AccountPaymentInstrumentV2; +import com.phonepe.sdk.pg.common.models.response.paymentinstruments.CreditLinePaymentInstrumentV2; +import com.phonepe.sdk.pg.common.models.response.paymentinstruments.PaymentInstrumentV2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CreditLinePaymentInstrumentV2Test { + + private static CreditLinePaymentInstrumentV2 sample() { + return CreditLinePaymentInstrumentV2.builder() + .maskedAccountNumber("121212121212") + .ifsc("AABE0000000") + .accountHolderName("accountHolderName") + .bankId("bankId") + .providerAccountType("providerAccountType") + .build(); + } + + @Test + void typeIsCreditLine() { + Assertions.assertEquals( + PaymentInstrumentV2.PaymentInstrumentType.CREDIT_LINE, sample().getType()); + Assertions.assertEquals( + PaymentInstrumentV2.PaymentInstrumentType.CREDIT_LINE, + new CreditLinePaymentInstrumentV2().getType()); + } + + @Test + void settersAndGetters() { + CreditLinePaymentInstrumentV2 instrument = new CreditLinePaymentInstrumentV2(); + instrument.setMaskedAccountNumber("121212121212"); + instrument.setIfsc("AABE0000000"); + instrument.setAccountHolderName("accountHolderName"); + instrument.setBankId("bankId"); + instrument.setProviderAccountType("providerAccountType"); + + Assertions.assertEquals("121212121212", instrument.getMaskedAccountNumber()); + Assertions.assertEquals("AABE0000000", instrument.getIfsc()); + Assertions.assertEquals("accountHolderName", instrument.getAccountHolderName()); + Assertions.assertEquals("bankId", instrument.getBankId()); + Assertions.assertEquals("providerAccountType", instrument.getProviderAccountType()); + Assertions.assertEquals(sample(), instrument); + } + + @Test + void equalsAndHashCode() { + CreditLinePaymentInstrumentV2 base = sample(); + CreditLinePaymentInstrumentV2 same = sample(); + + Assertions.assertEquals(base, base); + Assertions.assertEquals(base, same); + Assertions.assertEquals(base.hashCode(), same.hashCode()); + Assertions.assertNotEquals(base, null); + Assertions.assertNotEquals(base, "string"); + Assertions.assertNotEquals(base, AccountPaymentInstrumentV2.builder().build()); + + CreditLinePaymentInstrumentV2 other = sample(); + other.setMaskedAccountNumber("other"); + Assertions.assertNotEquals(base, other); + other = sample(); + other.setIfsc("other"); + Assertions.assertNotEquals(base, other); + other = sample(); + other.setAccountHolderName("other"); + Assertions.assertNotEquals(base, other); + other = sample(); + other.setBankId("other"); + Assertions.assertNotEquals(base, other); + other = sample(); + other.setProviderAccountType("other"); + Assertions.assertNotEquals(base, other); + } + + @Test + void equalsAndHashCodeWithNullFields() { + CreditLinePaymentInstrumentV2 empty = CreditLinePaymentInstrumentV2.builder().build(); + CreditLinePaymentInstrumentV2 anotherEmpty = + CreditLinePaymentInstrumentV2.builder().build(); + + Assertions.assertEquals(empty, anotherEmpty); + Assertions.assertEquals(empty.hashCode(), anotherEmpty.hashCode()); + Assertions.assertNotEquals(empty, sample()); + Assertions.assertNotEquals(sample(), empty); + + CreditLinePaymentInstrumentV2 partial = CreditLinePaymentInstrumentV2.builder().build(); + partial.setMaskedAccountNumber("121212121212"); + Assertions.assertNotEquals(partial, sample()); + partial.setIfsc("AABE0000000"); + Assertions.assertNotEquals(partial, sample()); + partial.setAccountHolderName("accountHolderName"); + Assertions.assertNotEquals(partial, sample()); + partial.setBankId("bankId"); + Assertions.assertNotEquals(partial, sample()); + partial.setProviderAccountType("providerAccountType"); + Assertions.assertEquals(partial, sample()); + } + + @Test + void toStringContainsFields() { + String value = sample().toString(); + Assertions.assertTrue(value.contains("providerAccountType")); + Assertions.assertTrue(value.contains("bankId")); + } + + @Test + void deserializesCreditLineAndIgnoresUnknownProperties() throws Exception { + String json = + "{\"type\":\"CREDIT_LINE\",\"ifsc\":\"AABE0000000\",\"unknown\":\"x\"," + + "\"providerAccountType\":\"providerAccountType\"}"; + PaymentInstrumentV2 actual = new ObjectMapper().readValue(json, PaymentInstrumentV2.class); + + Assertions.assertTrue(actual instanceof CreditLinePaymentInstrumentV2); + CreditLinePaymentInstrumentV2 creditLine = (CreditLinePaymentInstrumentV2) actual; + Assertions.assertEquals("AABE0000000", creditLine.getIfsc()); + Assertions.assertEquals("providerAccountType", creditLine.getProviderAccountType()); + } +}