From 6c909f7577271b8954e8d7a5629cf352565d3327 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Tue, 4 Nov 2025 12:22:50 +0530 Subject: [PATCH 01/12] removed single pattern for client initializations and added pre-commit hook --- .pre-commit-config.yaml | 10 + Formatter.xml | 600 ++++++++++++++++++ formatter_eclipse.xml | 380 +++++++++++ pom.xml | 5 + .../pg/payments/v2/CustomCheckoutClient.java | 7 +- .../payments/v2/StandardCheckoutClient.java | 16 +- .../subscription/v2/SubscriptionClient.java | 7 +- src/test/SingletonCustomCheckoutTest.java | 231 ------- src/test/SingletonStandardCheckoutTest.java | 247 ------- src/test/SingletonSubscriptionClientTest.java | 61 -- src/test/SingletonTest.java | 116 ---- 11 files changed, 1001 insertions(+), 679 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 Formatter.xml create mode 100644 formatter_eclipse.xml delete mode 100644 src/test/SingletonCustomCheckoutTest.java delete mode 100644 src/test/SingletonStandardCheckoutTest.java delete mode 100644 src/test/SingletonSubscriptionClientTest.java delete mode 100644 src/test/SingletonTest.java diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..85d1442 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: + - repo: local + hooks: + - id: spotless-format + name: Spotless Code Formatter + entry: mvn spotless:check + language: system + pass_filenames: false + stages: [pre-commit] + verbose: true \ No newline at end of file diff --git a/Formatter.xml b/Formatter.xml new file mode 100644 index 0000000..594b3d7 --- /dev/null +++ b/Formatter.xml @@ -0,0 +1,600 @@ + + + \ No newline at end of file diff --git a/formatter_eclipse.xml b/formatter_eclipse.xml new file mode 100644 index 0000000..bd267b4 --- /dev/null +++ b/formatter_eclipse.xml @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3c355cf..689733a 100644 --- a/pom.xml +++ b/pom.xml @@ -141,6 +141,11 @@ origin/main + + + ${project.basedir}/formatter_eclipse.xml + 4.26 + src/main/java/**/*.java src/test/java/**/*.java diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java index 1e3ea54..9620581 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java @@ -111,11 +111,8 @@ public static CustomCheckoutClient getInstance( shouldPublishInProd, FlowType.PG); - return cachedInstances.computeIfAbsent( - requestedClientSHA, - key -> - new CustomCheckoutClient( - clientId, clientSecret, clientVersion, env, shouldPublishInProd)); + return new CustomCheckoutClient( + clientId, clientSecret, clientVersion, env, shouldPublishInProd); } /** diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java index 1ec1c38..8745b77 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java @@ -103,20 +103,8 @@ public static StandardCheckoutClient getInstance( boolean shouldPublishEvents) throws PhonePeException { final boolean shouldPublishInProd = shouldPublishEvents && env == Env.PRODUCTION; - final String requestedClientSHA = - CommonUtils.calculateSha256( - clientId, - clientSecret, - clientVersion, - env, - shouldPublishInProd, - FlowType.PG_CHECKOUT); - - return cachedInstances.computeIfAbsent( - requestedClientSHA, - key -> - new StandardCheckoutClient( - clientId, clientSecret, clientVersion, env, shouldPublishInProd)); + return new StandardCheckoutClient( + clientId, clientSecret, clientVersion, env, shouldPublishInProd); } /** diff --git a/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java b/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java index 3640064..98899a3 100644 --- a/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java +++ b/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java @@ -111,11 +111,8 @@ public static SubscriptionClient getInstance( shouldPublishInProd, FlowType.SUBSCRIPTION); - return cachedInstances.computeIfAbsent( - requestedClientSHA, - key -> - new SubscriptionClient( - clientId, clientSecret, clientVersion, env, shouldPublishInProd)); + return new SubscriptionClient( + clientId, clientSecret, clientVersion, env, shouldPublishInProd); } /** diff --git a/src/test/SingletonCustomCheckoutTest.java b/src/test/SingletonCustomCheckoutTest.java deleted file mode 100644 index a2c9198..0000000 --- a/src/test/SingletonCustomCheckoutTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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 static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; - -import com.google.common.collect.Maps; -import com.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.common.models.request.PgPaymentRequest; -import com.phonepe.sdk.pg.common.models.response.PgPaymentResponse; -import com.phonepe.sdk.pg.common.tokenhandler.OAuthResponse; -import com.phonepe.sdk.pg.payments.v2.CustomCheckoutClient; -import com.phonepe.sdk.pg.payments.v2.customcheckout.CustomCheckoutConstants; -import okhttp3.FormBody; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import wiremock.org.apache.http.HttpStatus; - -public class SingletonCustomCheckoutTest extends BaseSetup { - - FormBody formBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - @Test - void testSingletonViaGetInstance() { - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(customCheckoutClient1, customCheckoutClient2); - } - - @Test - void testSingletonWithDiffParameters() { - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = CustomCheckoutClient.getInstance( - "clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotEquals(customCheckoutClient1, customCheckoutClient2); - Assertions.assertNotNull(customCheckoutClient2); - } - - @Test - void testMultipleSameClientSingleAuthCall() { - wireMockServer.resetRequests(); - customCheckoutClient.getTokenService().setOAuthResponse(null); - PgPaymentRequest pgPaymentRequest = - PgPaymentRequest.UpiQrRequestBuilder() - .merchantOrderId("MerchantOrderId") - .amount(100) - .build(); - - PgPaymentResponse pgPaymentResponse = - PgPaymentResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .qrData("qrData") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient3 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient4 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - final String url = CustomCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - pgPaymentRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - pgPaymentResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - formBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - PgPaymentResponse actual = customCheckoutClient1.pay(pgPaymentRequest); - actual = customCheckoutClient2.pay(pgPaymentRequest); - actual = customCheckoutClient3.pay(pgPaymentRequest); - actual = customCheckoutClient4.pay(pgPaymentRequest); - - wireMockServer.verify(1, postRequestedFor(urlPathMatching(authUrl))); - } - - @Test - void testMultipleDifferentClientMultipleAuthCall() { - String clientId = "clientId_for_auth1"; - String clientSecret = "clientSecret_for_auth1"; - int clientVersion = 1; - - String clientId1 = "clientId1"; - String clientSecret1 = "clientSecret1"; - - FormBody mockFormBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - wireMockServer.resetRequests(); - String redirectUrl = "https://redirectUrl.com"; - PgPaymentRequest pgPaymentRequest = - PgPaymentRequest.UpiQrRequestBuilder() - .merchantOrderId("MerchantOrderId") - .amount(100) - .build(); - - PgPaymentResponse pgPaymentResponse = - PgPaymentResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .qrData("qrData") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = - CustomCheckoutClient.getInstance(clientId1, clientSecret1, clientVersion, env); - - final String url = CustomCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - pgPaymentRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - pgPaymentResponse); - - FormBody mockFormBody1 = - new FormBody.Builder() - .add("client_id", clientId1) - .add("client_secret", clientSecret1) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody1, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - - customCheckoutClient1.pay(pgPaymentRequest); - customCheckoutClient2.pay(pgPaymentRequest); - - wireMockServer.verify(2, postRequestedFor(urlPathMatching(authUrl))); - } - - @Test - void testSingletonWithDifferentEnvironments() { - CustomCheckoutClient customCheckoutClientProd = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - CustomCheckoutClient customCheckoutClientSandbox = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - - Assertions.assertNotEquals(customCheckoutClientProd, customCheckoutClientSandbox); - - CustomCheckoutClient customCheckoutClientProd2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - Assertions.assertEquals(customCheckoutClientProd, customCheckoutClientProd2); - - CustomCheckoutClient customCheckoutClientSandbox2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - Assertions.assertEquals(customCheckoutClientSandbox, customCheckoutClientSandbox2); - } -} diff --git a/src/test/SingletonStandardCheckoutTest.java b/src/test/SingletonStandardCheckoutTest.java deleted file mode 100644 index 0ce88af..0000000 --- a/src/test/SingletonStandardCheckoutTest.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * 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 static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.common.constants.Headers; -import com.phonepe.sdk.pg.common.tokenhandler.OAuthResponse; -import com.phonepe.sdk.pg.payments.v2.StandardCheckoutClient; -import com.phonepe.sdk.pg.payments.v2.models.request.StandardCheckoutPayRequest; -import com.phonepe.sdk.pg.payments.v2.models.response.StandardCheckoutPayResponse; -import com.phonepe.sdk.pg.payments.v2.standardcheckout.StandardCheckoutConstants; -import java.util.Map; -import okhttp3.FormBody; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import wiremock.org.apache.http.HttpStatus; - -public class SingletonStandardCheckoutTest extends BaseSetup { - - FormBody formBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - @Test - void testSingletonViaGetInstance() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(standardCheckoutClient1, standardCheckoutClient2); - } - - @Test - void testSingletonWithDiffParameters() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance("clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotEquals(standardCheckoutClient1, standardCheckoutClient2); - Assertions.assertNotNull(standardCheckoutClient1); - Assertions.assertNotNull(standardCheckoutClient2); - } - - @Test - void testMultipleSameClientSingleAuthCall() { - wireMockServer.resetRequests(); - standardCheckoutClient.getTokenService().setOAuthResponse(null); - String redirectUrl = "https://redirectUrl.com"; - StandardCheckoutPayRequest standardCheckoutPayRequest = - StandardCheckoutPayRequest.builder() - .merchantOrderId("merchantOrderId") - .amount(100) - .redirectUrl(redirectUrl) - .build(); - - StandardCheckoutPayResponse standardCheckoutResponse = - StandardCheckoutPayResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .redirectUrl("https://google.com") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient3 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient4 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - final String url = StandardCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - standardCheckoutPayRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - standardCheckoutResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - formBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - StandardCheckoutPayResponse actual = - standardCheckoutClient1.pay(standardCheckoutPayRequest); - actual = standardCheckoutClient2.pay(standardCheckoutPayRequest); - actual = standardCheckoutClient3.pay(standardCheckoutPayRequest); - actual = standardCheckoutClient4.pay(standardCheckoutPayRequest); - - wireMockServer.verify(1, postRequestedFor(urlPathMatching(authUrl))); - } - - @Test - void testMultipleDifferentClientSingleAuthCall() { - String clientId = "clientId_for_auth1"; - String clientSecret = "clientSecret_for_auth1"; - String clientId1 = "clientId_for_auth2"; - String clientSecret1 = "clientSecret_for_auth2"; - int clientVersion = 1; - - FormBody mockFormBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - FormBody mockFormBody1 = - new FormBody.Builder() - .add("client_id", clientId1) - .add("client_secret", clientSecret1) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - wireMockServer.resetRequests(); - String redirectUrl = "https://redirectUrl.com"; - StandardCheckoutPayRequest standardCheckoutPayRequest = - StandardCheckoutPayRequest.builder() - .merchantOrderId("merchantOrderId") - .amount(100) - .redirectUrl(redirectUrl) - .build(); - - StandardCheckoutPayResponse standardCheckoutResponse = - StandardCheckoutPayResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .redirectUrl("https://google.com") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId1, clientSecret1, clientVersion, env); - - final String url = StandardCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - standardCheckoutPayRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - standardCheckoutResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody1, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - standardCheckoutClient1.pay(standardCheckoutPayRequest); - standardCheckoutClient2.pay(standardCheckoutPayRequest); - - wireMockServer.verify(2, postRequestedFor(urlPathMatching(authUrl))); - } - - public Map getHeaders() { - return ImmutableMap.builder() - .put(Headers.CONTENT_TYPE, APPLICATION_JSON) - .put(Headers.SOURCE, Headers.INTEGRATION) - .put(Headers.SOURCE_VERSION, Headers.API_VERSION) - .put(Headers.SOURCE_PLATFORM, Headers.SDK_TYPE) - .put(Headers.SOURCE_PLATFORM_VERSION, Headers.SDK_VERSION) - .put(Headers.OAUTH_AUTHORIZATION, "O-Bearer accessToken") - .build(); - } - - @Test - void testSingletonWithDifferentEnvironments() { - StandardCheckoutClient standardCheckoutClientProd = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - StandardCheckoutClient standardCheckoutClientSandbox = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - - Assertions.assertNotEquals(standardCheckoutClientProd, standardCheckoutClientSandbox); - - StandardCheckoutClient standardCheckoutClientProd2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - Assertions.assertEquals(standardCheckoutClientProd, standardCheckoutClientProd2); - - StandardCheckoutClient standardCheckoutClientSandbox2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - Assertions.assertEquals(standardCheckoutClientSandbox, standardCheckoutClientSandbox2); - } -} diff --git a/src/test/SingletonSubscriptionClientTest.java b/src/test/SingletonSubscriptionClientTest.java deleted file mode 100644 index 76ad08d..0000000 --- a/src/test/SingletonSubscriptionClientTest.java +++ /dev/null @@ -1,61 +0,0 @@ - -/* - * 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.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.subscription.v2.SubscriptionClient; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class SingletonSubscriptionClientTest extends BaseSetup { - - @Test - void testSingletonViaGetInstance() { - SubscriptionClient subscriptionClient1 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, env); - SubscriptionClient subscriptionClient2 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(subscriptionClient1, subscriptionClient2); - } - - @Test - void testSingletonWithDiffParameters() { - SubscriptionClient subscriptionClient1 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, env); - SubscriptionClient subscriptionClient2 = SubscriptionClient.getInstance( - "clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotEquals(subscriptionClient1, subscriptionClient2); - Assertions.assertNotNull(subscriptionClient2); - } - - @Test - void testSingletonWithDifferentEnvironments() { - SubscriptionClient subscriptionClientProd = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - SubscriptionClient subscriptionClientSandbox = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - - Assertions.assertNotEquals(subscriptionClientProd, subscriptionClientSandbox); - - SubscriptionClient subscriptionClientProd2 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - Assertions.assertEquals(subscriptionClientProd, subscriptionClientProd2); - - SubscriptionClient subscriptionClientSandbox2 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - Assertions.assertEquals(subscriptionClientSandbox, subscriptionClientSandbox2); - } -} diff --git a/src/test/SingletonTest.java b/src/test/SingletonTest.java deleted file mode 100644 index 9b94cb6..0000000 --- a/src/test/SingletonTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; - -import com.google.common.collect.Maps; -import com.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.common.tokenhandler.OAuthResponse; -import com.phonepe.sdk.pg.payments.v2.StandardCheckoutClient; -import com.phonepe.sdk.pg.payments.v2.models.request.StandardCheckoutPayRequest; -import com.phonepe.sdk.pg.payments.v2.models.response.StandardCheckoutPayResponse; -import com.phonepe.sdk.pg.payments.v2.standardcheckout.StandardCheckoutConstants; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import wiremock.org.apache.http.HttpStatus; - -public class SingletonTest extends BaseSetupWithOAuth { - - @Test - void testSingletonViaGetInstance() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(standardCheckoutClient1, standardCheckoutClient2); - } - - @Test - void testSingletonWithDiffParameters() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = StandardCheckoutClient.getInstance( - "clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotNull(standardCheckoutClient2); - Assertions.assertNotEquals(standardCheckoutClient1, standardCheckoutClient2); - } - - @Test - void testMultipleClientSingleAuthCall() { - wireMockServer.resetRequests(); - standardCheckoutClient.getTokenService().setOAuthResponse(null); - String redirectUrl = "https://redirectUrl.com"; - StandardCheckoutPayRequest standardCheckoutPayRequest = - StandardCheckoutPayRequest.builder() - .merchantOrderId("merchantOrderId") - .amount(100) - .redirectUrl(redirectUrl) - .build(); - - StandardCheckoutPayResponse standardCheckoutResponse = - StandardCheckoutPayResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .redirectUrl("https://google.com") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient3 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient4 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - final String url = StandardCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - standardCheckoutPayRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - standardCheckoutResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - formBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - standardCheckoutClient1.pay(standardCheckoutPayRequest); - standardCheckoutClient2.pay(standardCheckoutPayRequest); - standardCheckoutClient3.pay(standardCheckoutPayRequest); - standardCheckoutClient4.pay(standardCheckoutPayRequest); - - wireMockServer.verify(1, postRequestedFor(urlPathMatching(authUrl))); - } -} From 78d5e85b61e392819ec02459d9018558d115066d Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Tue, 4 Nov 2025 12:25:21 +0530 Subject: [PATCH 02/12] renamed formater file and removed unwanted file --- Formatter.xml | 600 ------------------------- formatter_eclipse.xml => formatter.xml | 0 pom.xml | 2 +- 3 files changed, 1 insertion(+), 601 deletions(-) delete mode 100644 Formatter.xml rename formatter_eclipse.xml => formatter.xml (100%) diff --git a/Formatter.xml b/Formatter.xml deleted file mode 100644 index 594b3d7..0000000 --- a/Formatter.xml +++ /dev/null @@ -1,600 +0,0 @@ - - - \ No newline at end of file diff --git a/formatter_eclipse.xml b/formatter.xml similarity index 100% rename from formatter_eclipse.xml rename to formatter.xml diff --git a/pom.xml b/pom.xml index 689733a..c730c88 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,7 @@ - ${project.basedir}/formatter_eclipse.xml + ${project.basedir}/formatter.xml 4.26 From d6adbebc9b0c50d6dce5972f2aa10ea58dca05b5 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Tue, 4 Nov 2025 13:10:39 +0530 Subject: [PATCH 03/12] fixed some code smells --- .pre-commit-config.yaml | 8 ++++++++ .../sdk/pg/payments/v2/CustomCheckoutClient.java | 12 ------------ .../sdk/pg/payments/v2/StandardCheckoutClient.java | 3 --- .../sdk/pg/subscription/v2/SubscriptionClient.java | 12 ------------ 4 files changed, 8 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 85d1442..ede76f9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,14 @@ repos: - repo: local hooks: + - id: spotless-format + name: Spotless Code Formatter + entry: mvn spotless:apply + language: system + pass_filenames: false + stages: [pre-commit] + verbose: true + - id: spotless-format name: Spotless Code Formatter entry: mvn spotless:check diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java index 9620581..1cec884 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java @@ -42,13 +42,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import lombok.SneakyThrows; public class CustomCheckoutClient extends BaseClient { - private static final ConcurrentHashMap cachedInstances = - new ConcurrentHashMap<>(); private List headers; private CustomCheckoutClient( @@ -102,15 +99,6 @@ public static CustomCheckoutClient getInstance( boolean shouldPublishEvents) throws PhonePeException { final boolean shouldPublishInProd = shouldPublishEvents && env == Env.PRODUCTION; - final String requestedClientSHA = - CommonUtils.calculateSha256( - clientId, - clientSecret, - clientVersion, - env, - shouldPublishInProd, - FlowType.PG); - return new CustomCheckoutClient( clientId, clientSecret, clientVersion, env, shouldPublishInProd); } diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java index 8745b77..a22405c 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java @@ -42,14 +42,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import lombok.SneakyThrows; /** The StandardCheckout client class provides methods for interacting with the PhonePe APIs. */ public class StandardCheckoutClient extends BaseClient { - private static final ConcurrentHashMap cachedInstances = - new ConcurrentHashMap<>(); private List headers; private StandardCheckoutClient( diff --git a/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java b/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java index 98899a3..165f65c 100644 --- a/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java +++ b/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java @@ -41,13 +41,10 @@ import com.phonepe.sdk.pg.subscription.v2.models.response.SubscriptionStatusResponseV2; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import lombok.SneakyThrows; public class SubscriptionClient extends BaseClient { - private static final ConcurrentHashMap cachedInstances = - new ConcurrentHashMap<>(); private List headers; private SubscriptionClient( @@ -102,15 +99,6 @@ public static SubscriptionClient getInstance( boolean shouldPublishEvents) throws PhonePeException { final boolean shouldPublishInProd = shouldPublishEvents && env == Env.PRODUCTION; - final String requestedClientSHA = - CommonUtils.calculateSha256( - clientId, - clientSecret, - clientVersion, - env, - shouldPublishInProd, - FlowType.SUBSCRIPTION); - return new SubscriptionClient( clientId, clientSecret, clientVersion, env, shouldPublishInProd); } From be057ce61e90e88f127496ebc01ad24875525a00 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Wed, 5 Nov 2025 17:37:28 +0530 Subject: [PATCH 04/12] Version bump and pom changes --- README.md | 6 ++-- pom.xml | 33 ++++++++++++++++++- .../sdk/pg/common/constants/Headers.java | 6 ++-- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c50e5eb..c025da0 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.1.7-blue)](https://maven-badges.herokuapp.com/maven-central/com.phonepe/pg-sdk-java) +[![Maven Central](https://img.shields.io/badge/Maven%20Central-v2.1.8-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.1.7 + 2.1.8 ``` @@ -46,7 +46,7 @@ Add the following to your project's build.gradle file: ```gradle dependencies { - implementation 'com.phonepe:pg-sdk-java:2.1.7' + implementation 'com.phonepe:pg-sdk-java:2.1.8' } ``` diff --git a/pom.xml b/pom.xml index c730c88..91f580c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.phonepe pg-sdk-java - 2.1.7 + 2.1.8 B2B JAVA SDK https://github.com/PhonePe/phonepe-pg-sdk-java Phonepe PG JAVA SDK @@ -97,6 +97,11 @@ Aditi Billore aditi.billore3@gmail.com + + Yogesh-Kamble + Yogesh Kamble + ypk282000@@gmail.com + @@ -134,6 +139,32 @@ + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.0 + + + attach-javadocs + + jar + + + + com.diffplug.spotless spotless-maven-plugin diff --git a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java index b879b75..8a6ce96 100644 --- a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java +++ b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java @@ -21,9 +21,9 @@ public class Headers { public static final String API_VERSION = "V2"; - public static final String SUBSCRIPTION_API_VERSION = "2.1.7"; + public static final String SUBSCRIPTION_API_VERSION = "2.1.8"; public static final String INTEGRATION = "API"; - public static final String SDK_VERSION = "2.1.7"; + public static final String SDK_VERSION = "2.1.8"; public static final String SDK_TYPE = "BACKEND_JAVA_SDK"; public static final String SOURCE = "x-source"; public static final String SOURCE_VERSION = "x-source-version"; @@ -32,4 +32,4 @@ public class Headers { public static final String OAUTH_AUTHORIZATION = "Authorization"; public static final String CONTENT_TYPE = "Content-Type"; public static final String ACCEPT = "Accept"; -} \ No newline at end of file +} From ed916160eb278269ea428d31aed193ed2c5108b0 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Wed, 5 Nov 2025 17:48:32 +0530 Subject: [PATCH 05/12] updated comment to fix source and javaDoc generation --- src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java b/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java index 0dfaf2b..d72f6c6 100644 --- a/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java +++ b/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java @@ -99,7 +99,7 @@ private HttpUrl prepareHttpURL(String url) { /** * Executes the HTTP request and returns the response. * - * @return the response object + * @return the response object */ @SneakyThrows public T execute() { From 2e508e1e0e3d2c879aae540a397dce7452d84243 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Thu, 6 Nov 2025 11:33:42 +0530 Subject: [PATCH 06/12] added sdk.properties for fetching version in headers file --- .pre-commit-config.yaml | 2 ++ pom.xml | 12 +++++++++++ .../sdk/pg/common/constants/Headers.java | 21 +++++++++++++++++-- src/main/resources/sdk.properties | 1 + 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/sdk.properties diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ede76f9..ed0d6e9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,8 @@ repos: stages: [pre-commit] verbose: true + # some complex rules like missing parentheses in a if, loop statements for a single line are not applied by + # mvn spotless:apply so added another check that will block the commit - id: spotless-format name: Spotless Code Formatter entry: mvn spotless:check diff --git a/pom.xml b/pom.xml index 91f580c..b101dfa 100644 --- a/pom.xml +++ b/pom.xml @@ -138,6 +138,12 @@ + + + src/main/resources + true + + org.apache.maven.plugins @@ -352,6 +358,12 @@ + + + src/main/resources + true + + org.sonatype.central diff --git a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java index 8a6ce96..892079a 100644 --- a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java +++ b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java @@ -15,15 +15,32 @@ */ package com.phonepe.sdk.pg.common.constants; +import java.io.InputStream; +import java.util.Properties; import lombok.experimental.UtilityClass; @UtilityClass public class Headers { + private final Properties properties = new Properties(); + private final String propertiesFileName = "/sdk.properties"; + + static { + try (InputStream input = Headers.class.getResourceAsStream(propertiesFileName)) { + if (input == null) { + System.err.println("Could not find " + propertiesFileName); + } else { + properties.load(input); + } + } catch (Exception e) { + System.err.println("Failed to load SDK properties: " + e.getMessage()); + } + } + public static final String API_VERSION = "V2"; - public static final String SUBSCRIPTION_API_VERSION = "2.1.8"; + public static final String SUBSCRIPTION_API_VERSION = properties.getProperty("sdk.version"); public static final String INTEGRATION = "API"; - public static final String SDK_VERSION = "2.1.8"; + public static final String SDK_VERSION = properties.getProperty("sdk.version"); public static final String SDK_TYPE = "BACKEND_JAVA_SDK"; public static final String SOURCE = "x-source"; public static final String SOURCE_VERSION = "x-source-version"; diff --git a/src/main/resources/sdk.properties b/src/main/resources/sdk.properties new file mode 100644 index 0000000..325bf76 --- /dev/null +++ b/src/main/resources/sdk.properties @@ -0,0 +1 @@ +sdk.version=${project.version} \ No newline at end of file From 20db88e7d845796ae7001aebdb9f31c26d7729cf Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Thu, 6 Nov 2025 13:25:16 +0530 Subject: [PATCH 07/12] Added Test case and fixed code smells --- .../sdk/pg/common/constants/Headers.java | 10 +- src/test/HeadersTest.java | 179 ++++++++++++++++++ 2 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 src/test/HeadersTest.java diff --git a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java index 892079a..89ee09d 100644 --- a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java +++ b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java @@ -18,22 +18,24 @@ import java.io.InputStream; import java.util.Properties; import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; @UtilityClass +@Slf4j public class Headers { private final Properties properties = new Properties(); - private final String propertiesFileName = "/sdk.properties"; + private final String PROPERTIES_FILE_NAME = "/sdk.properties"; static { - try (InputStream input = Headers.class.getResourceAsStream(propertiesFileName)) { + try (InputStream input = Headers.class.getResourceAsStream(PROPERTIES_FILE_NAME)) { if (input == null) { - System.err.println("Could not find " + propertiesFileName); + log.error("Could not find {}", PROPERTIES_FILE_NAME); } else { properties.load(input); } } catch (Exception e) { - System.err.println("Failed to load SDK properties: " + e.getMessage()); + log.error("Failed to load SDK properties: {}", e.getMessage()); } } diff --git a/src/test/HeadersTest.java b/src/test/HeadersTest.java new file mode 100644 index 0000000..7737a2f --- /dev/null +++ b/src/test/HeadersTest.java @@ -0,0 +1,179 @@ +/* + * 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 org.junit.jupiter.api.Assertions; + +import com.phonepe.sdk.pg.common.constants.Headers; +import java.io.InputStream; +import java.util.Properties; +import org.junit.jupiter.api.Test; + +public class HeadersTest { + + /** + * Test that SDK_VERSION is loaded from properties file and not null + */ + @Test + public void testSDKVersionIsLoadedFromProperties() { + Assertions.assertNotNull(Headers.SDK_VERSION, "SDK_VERSION should not be null"); + Assertions.assertFalse(Headers.SDK_VERSION.isEmpty(), "SDK_VERSION should not be empty"); + } + + /** + * Test that SDK_VERSION follows semantic versioning format (e.g., 2.1.8) + */ + @Test + public void testSDKVersionFormat() { + Assertions.assertTrue( + Headers.SDK_VERSION.matches("\\d+\\.\\d+\\.\\d+"), + "SDK_VERSION should match semantic version format (X.Y.Z). Got: " + + Headers.SDK_VERSION); + } + + /** + * Test that SUBSCRIPTION_API_VERSION is loaded from properties file and not null + */ + @Test + public void testSubscriptionAPIVersionIsLoadedFromProperties() { + Assertions.assertNotNull( + Headers.SUBSCRIPTION_API_VERSION, "SUBSCRIPTION_API_VERSION should not be null"); + Assertions.assertFalse( + Headers.SUBSCRIPTION_API_VERSION.isEmpty(), + "SUBSCRIPTION_API_VERSION should not be empty"); + } + + /** + * Test that both SDK_VERSION and SUBSCRIPTION_API_VERSION have the same value + * since they both load from the same property + */ + @Test + public void testSDKVersionMatchesSubscriptionAPIVersion() { + Assertions.assertEquals( + Headers.SDK_VERSION, + Headers.SUBSCRIPTION_API_VERSION, + "SDK_VERSION and SUBSCRIPTION_API_VERSION should have the same value"); + } + + /** + * Test that properties file exists and is accessible in classpath + */ + @Test + public void testPropertiesFileExists() { + InputStream input = getClass().getResourceAsStream("/sdk.properties"); + Assertions.assertNotNull(input, "sdk.properties file should exist in classpath"); + } + + /** + * Test that properties file has been filtered by Maven + * (i.e., ${project.version} has been replaced with actual version) + */ + @Test + public void testPropertiesFileIsFiltered() throws Exception { + Properties properties = new Properties(); + try (InputStream input = getClass().getResourceAsStream("/sdk.properties")) { + Assertions.assertNotNull(input, "sdk.properties should be available"); + properties.load(input); + + String version = properties.getProperty("sdk.version"); + Assertions.assertNotNull(version, "sdk.version property should exist"); + Assertions.assertFalse( + version.contains("${"), + "sdk.version should not contain unresolved Maven placeholders like ${project.version}. Got: " + + version); + Assertions.assertTrue( + version.matches("\\d+\\.\\d+\\.\\d+"), + "sdk.version should be in semantic version format. Got: " + version); + } + } + + /** + * Test that all header constants are not null + */ + @Test + public void testAllConstantsAreNotNull() { + Assertions.assertNotNull(Headers.API_VERSION, "API_VERSION should not be null"); + Assertions.assertNotNull( + Headers.SUBSCRIPTION_API_VERSION, "SUBSCRIPTION_API_VERSION should not be null"); + Assertions.assertNotNull(Headers.INTEGRATION, "INTEGRATION should not be null"); + Assertions.assertNotNull(Headers.SDK_VERSION, "SDK_VERSION should not be null"); + Assertions.assertNotNull(Headers.SDK_TYPE, "SDK_TYPE should not be null"); + Assertions.assertNotNull(Headers.SOURCE, "SOURCE should not be null"); + Assertions.assertNotNull(Headers.SOURCE_VERSION, "SOURCE_VERSION should not be null"); + Assertions.assertNotNull(Headers.SOURCE_PLATFORM, "SOURCE_PLATFORM should not be null"); + Assertions.assertNotNull( + Headers.SOURCE_PLATFORM_VERSION, "SOURCE_PLATFORM_VERSION should not be null"); + Assertions.assertNotNull(Headers.OAUTH_AUTHORIZATION, "OAUTH_AUTHORIZATION should not be null"); + Assertions.assertNotNull(Headers.CONTENT_TYPE, "CONTENT_TYPE should not be null"); + Assertions.assertNotNull(Headers.ACCEPT, "ACCEPT should not be null"); + } + + /** + * Test that SDK_VERSION matches the expected format and contains valid version parts + */ + @Test + public void testSDKVersionComponents() { + String[] versionParts = Headers.SDK_VERSION.split("\\."); + Assertions.assertEquals( + 3, + versionParts.length, + "SDK_VERSION should have 3 parts (major.minor.patch). Got: " + + Headers.SDK_VERSION); + + // Each part should be a number + for (int i = 0; i < versionParts.length; i++) { + Assertions.assertTrue( + versionParts[i].matches("\\d+"), + "Version part " + i + " should be numeric. Got: " + versionParts[i]); + } + } + + /** + * Test that Header values don't contain common problematic characters + */ + @Test + public void testHeaderValuesDoNotContainProblematicCharacters() { + // Version values should not contain whitespace or special characters + Assertions.assertFalse( + Headers.SDK_VERSION.contains(" "), + "SDK_VERSION should not contain whitespace"); + Assertions.assertFalse( + Headers.SUBSCRIPTION_API_VERSION.contains(" "), + "SUBSCRIPTION_API_VERSION should not contain whitespace"); + Assertions.assertFalse( + Headers.API_VERSION.contains(" "), "API_VERSION should not contain whitespace"); + } + + /** + * Integration test: Verify that properties loaded in static block + * match what can be read directly from the file + */ + @Test + public void testStaticBlockPropertiesMatchFileContent() throws Exception { + Properties fileProperties = new Properties(); + try (InputStream input = getClass().getResourceAsStream("/sdk.properties")) { + fileProperties.load(input); + } + + String fileVersion = fileProperties.getProperty("sdk.version"); + Assertions.assertEquals( + Headers.SDK_VERSION, + fileVersion, + "SDK_VERSION from Headers should match sdk.version from file"); + Assertions.assertEquals( + Headers.SUBSCRIPTION_API_VERSION, + fileVersion, + "SUBSCRIPTION_API_VERSION from Headers should match sdk.version from file"); + } +} From db94527ee4f3d8eb8e30fb43a4d6655913841705 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Thu, 6 Nov 2025 13:40:38 +0530 Subject: [PATCH 08/12] Fixed code smells --- src/test/{ => headers}/HeadersTest.java | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) rename src/test/{ => headers}/HeadersTest.java (91%) diff --git a/src/test/HeadersTest.java b/src/test/headers/HeadersTest.java similarity index 91% rename from src/test/HeadersTest.java rename to src/test/headers/HeadersTest.java index 7737a2f..97dd23e 100644 --- a/src/test/HeadersTest.java +++ b/src/test/headers/HeadersTest.java @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package headers; + import org.junit.jupiter.api.Assertions; import com.phonepe.sdk.pg.common.constants.Headers; @@ -20,13 +22,13 @@ import java.util.Properties; import org.junit.jupiter.api.Test; -public class HeadersTest { +class HeadersTest { /** * Test that SDK_VERSION is loaded from properties file and not null */ @Test - public void testSDKVersionIsLoadedFromProperties() { + void testSDKVersionIsLoadedFromProperties() { Assertions.assertNotNull(Headers.SDK_VERSION, "SDK_VERSION should not be null"); Assertions.assertFalse(Headers.SDK_VERSION.isEmpty(), "SDK_VERSION should not be empty"); } @@ -35,7 +37,7 @@ public void testSDKVersionIsLoadedFromProperties() { * Test that SDK_VERSION follows semantic versioning format (e.g., 2.1.8) */ @Test - public void testSDKVersionFormat() { + void testSDKVersionFormat() { Assertions.assertTrue( Headers.SDK_VERSION.matches("\\d+\\.\\d+\\.\\d+"), "SDK_VERSION should match semantic version format (X.Y.Z). Got: " @@ -46,7 +48,7 @@ public void testSDKVersionFormat() { * Test that SUBSCRIPTION_API_VERSION is loaded from properties file and not null */ @Test - public void testSubscriptionAPIVersionIsLoadedFromProperties() { + void testSubscriptionAPIVersionIsLoadedFromProperties() { Assertions.assertNotNull( Headers.SUBSCRIPTION_API_VERSION, "SUBSCRIPTION_API_VERSION should not be null"); Assertions.assertFalse( @@ -59,7 +61,7 @@ public void testSubscriptionAPIVersionIsLoadedFromProperties() { * since they both load from the same property */ @Test - public void testSDKVersionMatchesSubscriptionAPIVersion() { + void testSDKVersionMatchesSubscriptionAPIVersion() { Assertions.assertEquals( Headers.SDK_VERSION, Headers.SUBSCRIPTION_API_VERSION, @@ -70,7 +72,7 @@ public void testSDKVersionMatchesSubscriptionAPIVersion() { * Test that properties file exists and is accessible in classpath */ @Test - public void testPropertiesFileExists() { + void testPropertiesFileExists() { InputStream input = getClass().getResourceAsStream("/sdk.properties"); Assertions.assertNotNull(input, "sdk.properties file should exist in classpath"); } @@ -80,7 +82,7 @@ public void testPropertiesFileExists() { * (i.e., ${project.version} has been replaced with actual version) */ @Test - public void testPropertiesFileIsFiltered() throws Exception { + void testPropertiesFileIsFiltered() throws Exception { Properties properties = new Properties(); try (InputStream input = getClass().getResourceAsStream("/sdk.properties")) { Assertions.assertNotNull(input, "sdk.properties should be available"); @@ -102,7 +104,7 @@ public void testPropertiesFileIsFiltered() throws Exception { * Test that all header constants are not null */ @Test - public void testAllConstantsAreNotNull() { + void testAllConstantsAreNotNull() { Assertions.assertNotNull(Headers.API_VERSION, "API_VERSION should not be null"); Assertions.assertNotNull( Headers.SUBSCRIPTION_API_VERSION, "SUBSCRIPTION_API_VERSION should not be null"); @@ -123,7 +125,7 @@ public void testAllConstantsAreNotNull() { * Test that SDK_VERSION matches the expected format and contains valid version parts */ @Test - public void testSDKVersionComponents() { + void testSDKVersionComponents() { String[] versionParts = Headers.SDK_VERSION.split("\\."); Assertions.assertEquals( 3, @@ -143,7 +145,7 @@ public void testSDKVersionComponents() { * Test that Header values don't contain common problematic characters */ @Test - public void testHeaderValuesDoNotContainProblematicCharacters() { + void testHeaderValuesDoNotContainProblematicCharacters() { // Version values should not contain whitespace or special characters Assertions.assertFalse( Headers.SDK_VERSION.contains(" "), @@ -160,7 +162,7 @@ public void testHeaderValuesDoNotContainProblematicCharacters() { * match what can be read directly from the file */ @Test - public void testStaticBlockPropertiesMatchFileContent() throws Exception { + void testStaticBlockPropertiesMatchFileContent() throws Exception { Properties fileProperties = new Properties(); try (InputStream input = getClass().getResourceAsStream("/sdk.properties")) { fileProperties.load(input); From 903d5884cd51cb93975f505d433fea4366ed0e38 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Fri, 7 Nov 2025 18:14:01 +0530 Subject: [PATCH 09/12] Added disablePaymentRetry parameter to createSdkOrder request --- .../v2/models/request/CreateSdkOrderRequest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java index cbbd59b..066236c 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java @@ -35,6 +35,7 @@ public class CreateSdkOrderRequest { private PaymentFlow paymentFlow; private Long expireAfter; private List constraints; + private Boolean disablePaymentRetry; /** * Builds SDK order Request @@ -54,11 +55,13 @@ public CreateSdkOrderRequest( MetaInfo metaInfo, String message, String redirectUrl, - Long expireAfter) { + Long expireAfter, + Boolean disablePaymentRetry) { this.merchantOrderId = merchantOrderId; this.amount = amount; this.metaInfo = metaInfo; this.expireAfter = expireAfter; + this.disablePaymentRetry = disablePaymentRetry; MerchantUrls merchantUrls = MerchantUrls.builder().redirectUrl(redirectUrl).build(); this.setPaymentFlow( PgCheckoutPaymentFlow.builder() @@ -83,12 +86,14 @@ public CreateSdkOrderRequest( long amount, MetaInfo metaInfo, List constraints, - Long expireAfter) { + Long expireAfter, + Boolean disablePaymentRetry) { this.merchantOrderId = merchantOrderId; this.amount = amount; this.metaInfo = metaInfo; this.expireAfter = expireAfter; this.constraints = constraints; + this.disablePaymentRetry = disablePaymentRetry; this.setPaymentFlow(PgPaymentFlow.builder().build()); } } From 5a9918fdc4f7b10035163cfaeadf8ee794761850 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Fri, 7 Nov 2025 18:28:38 +0530 Subject: [PATCH 10/12] added testcases --- src/test/CreateOrderTest.java | 193 ++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/src/test/CreateOrderTest.java b/src/test/CreateOrderTest.java index 92eb03d..de78503 100644 --- a/src/test/CreateOrderTest.java +++ b/src/test/CreateOrderTest.java @@ -123,4 +123,197 @@ void testCreateOrderBadRequest() { Assertions.assertEquals(400, phonePeException.getHttpStatusCode()); Assertions.assertEquals("Bad Request", phonePeException.getCode()); } + + @Test + void testCreateOrderWithDisablePaymentRetryTrue() { + String redirectUrl = "https://google.com"; + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.StandardCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .redirectUrl(redirectUrl) + .disablePaymentRetry(true) + .build(); + final String url = StandardCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = + standardCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertTrue(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderWithDisablePaymentRetryFalse() { + String redirectUrl = "https://google.com"; + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.StandardCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .redirectUrl(redirectUrl) + .disablePaymentRetry(false) + .build(); + final String url = StandardCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = + standardCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertFalse(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderWithDisablePaymentRetryNull() { + String redirectUrl = "https://google.com"; + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.StandardCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .redirectUrl(redirectUrl) + .build(); + final String url = StandardCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = + standardCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertNull(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderCustomCheckoutWithDisablePaymentRetryTrue() { + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.CustomCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .disablePaymentRetry(true) + .build(); + final String url = CustomCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = customCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertTrue(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderCustomCheckoutWithDisablePaymentRetryFalse() { + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.CustomCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .disablePaymentRetry(false) + .build(); + final String url = CustomCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = customCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertFalse(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderCustomCheckoutWithDisablePaymentRetryNull() { + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.CustomCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .build(); + final String url = CustomCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = customCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertNull(createSdkOrderRequest.getDisablePaymentRetry()); + } } From 43c4f73f5cc27c7abaafa26b990f581e1ddcdf60 Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Sun, 9 Nov 2025 09:45:45 +0530 Subject: [PATCH 11/12] Added jacob plugin in pom --- pom.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pom.xml b/pom.xml index b101dfa..92ce084 100644 --- a/pom.xml +++ b/pom.xml @@ -212,6 +212,25 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.11 + + + + prepare-agent + + + + report + test + + report + + + + From a35471d7e5585668a0b1a093e525163e19caf5fc Mon Sep 17 00:00:00 2001 From: 282000ypk Date: Mon, 10 Nov 2025 09:39:51 +0530 Subject: [PATCH 12/12] minor spell checks and renames --- .pre-commit-config.yaml | 10 +++++----- pom.xml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ed0d6e9..7f24d4b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,8 @@ repos: - repo: local hooks: - - id: spotless-format - name: Spotless Code Formatter + - id: spotless-apply + name: Spotless Code Formatter (Apply) entry: mvn spotless:apply language: system pass_filenames: false @@ -11,10 +11,10 @@ repos: # some complex rules like missing parentheses in a if, loop statements for a single line are not applied by # mvn spotless:apply so added another check that will block the commit - - id: spotless-format - name: Spotless Code Formatter + - id: spotless-check + name: Spotless Code Formatter (Check) entry: mvn spotless:check language: system pass_filenames: false stages: [pre-commit] - verbose: true \ No newline at end of file + verbose: true diff --git a/pom.xml b/pom.xml index 92ce084..72ed3ba 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ Yogesh-Kamble Yogesh Kamble - ypk282000@@gmail.com + ypk282000@gmail.com