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
-[](https://maven-badges.herokuapp.com/maven-central/com.phonepe/pg-sdk-java)
+[](https://maven-badges.herokuapp.com/maven-central/com.phonepe/pg-sdk-java)

[](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/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());
+ }
+}
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();