From 0d9af6ae3aa707959a52ff166e4b4828d2c95efa Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 09:59:30 +0300 Subject: [PATCH 01/10] Enhance SMSBridge configuration handling - Updated `setSmsBridgeConfig` method to clear existing configurations and add new ones if provided. - Ensured that each new configuration is associated with the current SMSBridge instance. --- .../fineract/messagegateway/sms/domain/SMSBridge.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/domain/SMSBridge.java b/src/main/java/org/fineract/messagegateway/sms/domain/SMSBridge.java index 18ac328..1eb90a8 100644 --- a/src/main/java/org/fineract/messagegateway/sms/domain/SMSBridge.java +++ b/src/main/java/org/fineract/messagegateway/sms/domain/SMSBridge.java @@ -167,7 +167,14 @@ public void setSMSBridgeToBridgeConfigs() { public void setSmsBridgeConfig(final Collection bridgeConfigurations) { this.bridgeConfigurations.clear(); - this.bridgeConfigurations = bridgeConfigurations; + + // Add new configurations + if (bridgeConfigurations != null) { + for (SMSBridgeConfig config : bridgeConfigurations) { + config.setSMSBridge(this); + this.bridgeConfigurations.add(config); + } + } } public String generateApiKey() { From 1af2dd10617106b09a94c728f0f6af123f88029e Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 10:05:00 +0300 Subject: [PATCH 02/10] Refactor AfricastalkingMessageProvider to separate sandbox and live SMS sending logic - Introduced `sendMessageSandbox` and `sendMessageLive` methods to handle SMS sending for sandbox and live environments respectively. - Enhanced logging for request details and responses. - Improved error handling for empty responses from the Africa's Talking API. --- .../AfricastalkingMessageProvider.java | 149 +++++++++++------- 1 file changed, 88 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java index d5f7a38..61348b9 100644 --- a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java +++ b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java @@ -1,5 +1,6 @@ package org.fineract.messagegateway.sms.providers.impl.africastalking; +import okhttp3.*; import org.fineract.messagegateway.exception.MessageGatewayException; import org.fineract.messagegateway.sms.domain.SMSBridge; import org.fineract.messagegateway.sms.domain.SMSMessage; @@ -11,81 +12,25 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import okhttp3.FormBody; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - import java.io.IOException; +import java.util.Collections; @Service(value = "Africastalking") public class AfricastalkingMessageProvider extends SMSProvider { private static final Logger logger = LoggerFactory.getLogger(AfricastalkingMessageProvider.class); + private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); @Override public void sendMessage(SMSBridge smsBridgeConfig, SMSMessage message) throws MessageGatewayException { try { OkHttpClient client = new OkHttpClient(); - String from = smsBridgeConfig.getPhoneNo(); - logger.info("Sending SMS from: {}", from); - if (from.equals("AFRICASTKNG")) { - from = null; - } String username = smsBridgeConfig.getConfigValue("username"); - String url = "https://api.africastalking.com/version1/messaging"; - if (username.equals("sandbox")) { - url = "https://api.sandbox.africastalking.com/version1/messaging"; - } String mobile = smsBridgeConfig.getCountryCode() + message.getMobileNumber(); - FormBody.Builder requestBodyBuilder = new FormBody.Builder() - .add("username", username) - .add("to", mobile) - .add("message", message.getMessage()) - .add("bulkSMSMode", "1") - .add("enqueue", "0"); - - if (from != null) { - requestBodyBuilder.add("from", from); - } - FormBody requestBody = requestBodyBuilder.build(); - - Request request = new Request.Builder() - .url(url) - .addHeader("apiKey", smsBridgeConfig.getConfigValue("apiKey")) - .addHeader("Content-Type", "application/x-www-form-urlencoded") - .addHeader("Accept", "application/json") - .post(requestBody) - .build(); - Response response = client.newCall(request).execute(); - if (response.isSuccessful()) { - String responseBody = response.body().string(); - logger.info("SMS sent successfully. Response: {}", responseBody); - - // Parse the response body - JSONObject responseJson = new JSONObject(responseBody); - JSONObject smsMessageData = responseJson.getJSONObject("SMSMessageData"); - JSONArray recipients = smsMessageData.getJSONArray("Recipients"); - - if (recipients.length() > 0) { - JSONObject recipient = recipients.getJSONObject(0); - - // Update the message with external ID and delivery status - String messageId = recipient.getString("messageId"); - int statusCode = recipient.getInt("statusCode"); - SmsMessageStatusType deliveryStatus = AfricastalkingStatus.smsStatus(statusCode); - - message.setExternalId(messageId); - message.setDeliveryStatus(deliveryStatus.getValue()); - } else { - String errorMessage = smsMessageData.getString("Message"); - logger.error("Failed to send SMS. Error message: {}", errorMessage); - throw new MessageGatewayException("Failed to send SMS. Error message: " + errorMessage); - } + if (username.equals("sandbox")) { + sendMessageSandbox(client, smsBridgeConfig, message, mobile); } else { - logger.error("Failed to send SMS. Response code: {}, Response body: {}", response.code(), - response.body().string()); - throw new MessageGatewayException("Failed to send SMS. Please check the logs for more details."); + sendMessageLive(client, smsBridgeConfig, message, mobile); } } catch (IOException e) { logger.error("An error occurred while sending the SMS: {}", e.getMessage(), e); @@ -93,6 +38,88 @@ public void sendMessage(SMSBridge smsBridgeConfig, SMSMessage message) throws Me } } + private void sendMessageSandbox(OkHttpClient client, SMSBridge smsBridgeConfig, SMSMessage message, String mobile) throws IOException, MessageGatewayException { + String from = smsBridgeConfig.getPhoneNo(); + logger.info("Sending SMS via sandbox from: {}", from); + if (from.equals("AFRICASTKNG")) { + from = null; + } + + String url = "https://api.sandbox.africastalking.com/version1/messaging"; + + FormBody.Builder requestBodyBuilder = new FormBody.Builder().add("username", smsBridgeConfig.getConfigValue("username")).add("to", mobile).add("message", message.getMessage()).add("bulkSMSMode", "1").add("enqueue", "0"); + + if (from != null) { + requestBodyBuilder.add("from", from); + } + + FormBody requestBody = requestBodyBuilder.build(); + + Request request = new Request.Builder().url(url).addHeader("apiKey", smsBridgeConfig.getConfigValue("apiKey")).addHeader("Content-Type", "application/x-www-form-urlencoded").addHeader("Accept", "application/json").post(requestBody).build(); + + logger.info("Request URL: {}", url); + logger.info("Request Headers: apiKey: {}, Content-Type: application/x-www-form-urlencoded, Accept: application/json", smsBridgeConfig.getConfigValue("apiKey")); + logger.info("Request Payload: username={}, to={}, message={}, bulkSMSMode=1, enqueue=0{}", smsBridgeConfig.getConfigValue("username"), mobile, message.getMessage(), from != null ? ", from=" + from : ""); + + processResponse(client.newCall(request).execute(), message); + } + + private void sendMessageLive(OkHttpClient client, SMSBridge smsBridgeConfig, SMSMessage message, String mobile) throws IOException, MessageGatewayException { + String senderId = smsBridgeConfig.getConfigValue("senderId"); + if (senderId == null || senderId.trim().isEmpty()) { + throw new MessageGatewayException("senderId configuration is required for live environment"); + } + + logger.info("Sending SMS via live API with senderId: {}", senderId); + + String url = "https://api.africastalking.com/version1/messaging/bulk"; + + JSONObject requestBody = new JSONObject().put("username", smsBridgeConfig.getConfigValue("username")).put("phoneNumbers", Collections.singletonList(mobile)).put("message", message.getMessage()).put("senderId", senderId); + + RequestBody body = RequestBody.create(JSON, requestBody.toString()); + + Request request = new Request.Builder().url(url).addHeader("apiKey", smsBridgeConfig.getConfigValue("apiKey")).addHeader("Content-Type", "application/json").addHeader("Accept", "application/json").post(body).build(); + processResponse(client.newCall(request).execute(), message); + } + + private void processResponse(Response response, SMSMessage message) throws IOException, MessageGatewayException { + ResponseBody responseBody = response.body(); + if (responseBody == null) { + throw new MessageGatewayException("Empty response received from Africa's Talking API"); + } + + String responseString = responseBody.string(); + if (response.isSuccessful()) { + logger.info("SMS sent successfully. Response: {}", responseString); + + // Parse the response body + JSONObject responseJson = new JSONObject(responseString); + JSONObject smsMessageData = responseJson.getJSONObject("SMSMessageData"); + JSONArray recipients = smsMessageData.getJSONArray("Recipients"); + + if (!recipients.isEmpty()) { + JSONObject recipient = recipients.getJSONObject(0); + + // Update the message with external ID and delivery status + String messageId = recipient.getString("messageId"); + int statusCode = recipient.getInt("statusCode"); + logger.info("Africa's Talking API response - MessageId: {}, StatusCode: {}", messageId, statusCode); + SmsMessageStatusType deliveryStatus = AfricastalkingStatus.smsStatus(statusCode); + logger.info("Mapped delivery status: {}", deliveryStatus); + + message.setExternalId(messageId); + message.setDeliveryStatus(deliveryStatus.getValue()); + } else { + String errorMessage = smsMessageData.getString("Message"); + logger.error("Failed to send SMS. Error message: {}", errorMessage); + throw new MessageGatewayException("Failed to send SMS. Error message: " + errorMessage); + } + } else { + logger.error("Failed to send SMS. Response code: {}, Response body: {}", response.code(), responseString); + throw new MessageGatewayException("Failed to send SMS. Response code: " + response.code() + ", Response body: " + responseString); + } + } + @Override public void updateStatusByMessageId(SMSBridge smsBridgeConfig, String messageId) throws MessageGatewayException { } From 77accdefe0432af34cb72e275b462691a2e14c2d Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 10:05:26 +0300 Subject: [PATCH 03/10] Update AfricastalkingStatus to reflect correct SMS delivery status - Changed the status mapping for code 100 from PENDING to SENT to accurately represent the message delivery state. --- .../sms/providers/impl/africastalking/AfricastalkingStatus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingStatus.java b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingStatus.java index d39d98f..f3f09ab 100644 --- a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingStatus.java +++ b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingStatus.java @@ -7,7 +7,7 @@ public static SmsMessageStatusType smsStatus(final Integer AfricastalkingStatus) SmsMessageStatusType smsStatus = SmsMessageStatusType.PENDING; switch (AfricastalkingStatus) { case 100: - smsStatus = SmsMessageStatusType.PENDING; + smsStatus = SmsMessageStatusType.SENT; break; case 101: smsStatus = SmsMessageStatusType.SENT; From a506a206e83fe14ea485d1fed7980603fc7aae43 Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 10:23:52 +0300 Subject: [PATCH 04/10] Update SMSBridgeSerializer to associate SMSBridge with configurations - Added a call to `setSMSBridge` on the `SMSBridgeConfig` instance to ensure each configuration is linked to the correct SMSBridge instance. --- .../messagegateway/sms/serialization/SmsBridgeSerializer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/fineract/messagegateway/sms/serialization/SmsBridgeSerializer.java b/src/main/java/org/fineract/messagegateway/sms/serialization/SmsBridgeSerializer.java index 06f6d68..41a7e78 100644 --- a/src/main/java/org/fineract/messagegateway/sms/serialization/SmsBridgeSerializer.java +++ b/src/main/java/org/fineract/messagegateway/sms/serialization/SmsBridgeSerializer.java @@ -153,6 +153,7 @@ private void assembleBridgeConfigParams(final SMSBridge bridge, final JsonArray baseDataValidator.reset().parameter(SmsConstants.configname_paramname).value(configName).notBlank(); baseDataValidator.reset().parameter(SmsConstants.configvalue_paramname).value(configValue).notBlank(); final SMSBridgeConfig config = new SMSBridgeConfig(configName, configValue) ; + config.setSMSBridge(bridge); configs.add(config) ; } bridge.setSmsBridgeConfig(configs) ; From 0380ab729947c7813a7071004061c37d8139fb33 Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 10:55:55 +0300 Subject: [PATCH 05/10] Refactor: Do not log API Keys --- .../impl/africastalking/AfricastalkingMessageProvider.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java index 61348b9..6d28b8a 100644 --- a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java +++ b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java @@ -58,8 +58,10 @@ private void sendMessageSandbox(OkHttpClient client, SMSBridge smsBridgeConfig, Request request = new Request.Builder().url(url).addHeader("apiKey", smsBridgeConfig.getConfigValue("apiKey")).addHeader("Content-Type", "application/x-www-form-urlencoded").addHeader("Accept", "application/json").post(requestBody).build(); logger.info("Request URL: {}", url); - logger.info("Request Headers: apiKey: {}, Content-Type: application/x-www-form-urlencoded, Accept: application/json", smsBridgeConfig.getConfigValue("apiKey")); - logger.info("Request Payload: username={}, to={}, message={}, bulkSMSMode=1, enqueue=0{}", smsBridgeConfig.getConfigValue("username"), mobile, message.getMessage(), from != null ? ", from=" + from : ""); + logger.info("Request Headers: Content-Type: application/x-www-form-urlencoded, Accept: application/json"); + logger.info("Request Payload: username={}, to={}, message={}, bulkSMSMode=1, enqueue=0{}", + smsBridgeConfig.getConfigValue("username"), mobile, message.getMessage(), + from != null ? ", from=" + from : ""); processResponse(client.newCall(request).execute(), message); } From d53d623d0dcf7a52e67bbdcc478b27ce6592a530 Mon Sep 17 00:00:00 2001 From: William Mwai Date: Fri, 9 May 2025 10:58:47 +0300 Subject: [PATCH 06/10] Create qodo-review-comment.yml --- .github/workflows/qodo-review-comment.yml | 74 +++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/qodo-review-comment.yml diff --git a/.github/workflows/qodo-review-comment.yml b/.github/workflows/qodo-review-comment.yml new file mode 100644 index 0000000..e2b8bd0 --- /dev/null +++ b/.github/workflows/qodo-review-comment.yml @@ -0,0 +1,74 @@ +name: Comment on PR + +on: + pull_request: + branches: + - dev + - main + types: [opened, synchronize] + +jobs: + trigger-bot-review: + if: github.event.pull_request.draft == false + name: Trigger Bot Review + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + GITHUB_TOKEN: ${{ secrets.BOT_REVIEW_COMMENT_ACCESS_TOKEN }} + MAIN_BRANCH: main + steps: + - uses: actions/checkout@v2 + + - name: Add comment on PR creation on dev and main + if: false + uses: actions/github-script@v7 + continue-on-error: true + timeout-minutes: 5 + with: + github-token: ${{ env.GITHUB_TOKEN }} + script: | + const comments = ['/describe']; + for (const comment of comments) { + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: comment + }); + } + + - name: Add comment on PR creation to trigger bot review + if: github.event.action == 'opened' && github.base_ref == ${{ env.MAIN_BRANCH }} + uses: actions/github-script@v7 + continue-on-error: true + timeout-minutes: 5 + with: + github-token: ${{ env.GITHUB_TOKEN }} + script: | + const comments = ['/describe', '/review', '/improve']; + for (const comment of comments) { + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: comment + }); + } + + - name: Add comment on PR update + if: github.event.action == 'synchronize' && github.base_ref == ${{ env.MAIN_BRANCH }} + uses: actions/github-script@v7 + continue-on-error: true + timeout-minutes: 5 + with: + github-token: ${{ env.GITHUB_TOKEN }} + script: | + const comments = ['/review', '/improve']; + for (const comment of comments) { + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: comment + }); + } From b2e26b8d66039ef322eb4cb24671a0b238e799e8 Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 11:08:31 +0300 Subject: [PATCH 07/10] Refactor AfricastalkingMessageProvider to improve recipient check --- .../impl/africastalking/AfricastalkingMessageProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java index 6d28b8a..1b22a0c 100644 --- a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java +++ b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java @@ -98,8 +98,8 @@ private void processResponse(Response response, SMSMessage message) throws IOExc JSONObject responseJson = new JSONObject(responseString); JSONObject smsMessageData = responseJson.getJSONObject("SMSMessageData"); JSONArray recipients = smsMessageData.getJSONArray("Recipients"); - - if (!recipients.isEmpty()) { + + if (recipients.length() > 0) { JSONObject recipient = recipients.getJSONObject(0); // Update the message with external ID and delivery status From da2088a887cf38fd475e0e07557be25b20d68e32 Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 11:37:45 +0300 Subject: [PATCH 08/10] Refactor: Handling edge cases where the recipients array is empty --- .../AfricastalkingMessageProvider.java | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java index 1b22a0c..3ce1c30 100644 --- a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java +++ b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java @@ -59,9 +59,9 @@ private void sendMessageSandbox(OkHttpClient client, SMSBridge smsBridgeConfig, logger.info("Request URL: {}", url); logger.info("Request Headers: Content-Type: application/x-www-form-urlencoded, Accept: application/json"); - logger.info("Request Payload: username={}, to={}, message={}, bulkSMSMode=1, enqueue=0{}", - smsBridgeConfig.getConfigValue("username"), mobile, message.getMessage(), - from != null ? ", from=" + from : ""); + logger.info("Request Payload: username={}, to={}, message={}, bulkSMSMode=1, enqueue=0{}", + smsBridgeConfig.getConfigValue("username"), mobile, message.getMessage(), + from != null ? ", from=" + from : ""); processResponse(client.newCall(request).execute(), message); } @@ -94,27 +94,32 @@ private void processResponse(Response response, SMSMessage message) throws IOExc if (response.isSuccessful()) { logger.info("SMS sent successfully. Response: {}", responseString); - // Parse the response body JSONObject responseJson = new JSONObject(responseString); JSONObject smsMessageData = responseJson.getJSONObject("SMSMessageData"); - JSONArray recipients = smsMessageData.getJSONArray("Recipients"); - - if (recipients.length() > 0) { - JSONObject recipient = recipients.getJSONObject(0); - - // Update the message with external ID and delivery status - String messageId = recipient.getString("messageId"); - int statusCode = recipient.getInt("statusCode"); - logger.info("Africa's Talking API response - MessageId: {}, StatusCode: {}", messageId, statusCode); - SmsMessageStatusType deliveryStatus = AfricastalkingStatus.smsStatus(statusCode); - logger.info("Mapped delivery status: {}", deliveryStatus); - - message.setExternalId(messageId); - message.setDeliveryStatus(deliveryStatus.getValue()); + + if (smsMessageData.has("Recipients") && !smsMessageData.isNull("Recipients")) { + JSONArray recipients = smsMessageData.getJSONArray("Recipients"); + + if (!recipients.isEmpty()) { + JSONObject recipient = recipients.getJSONObject(0); + + String messageId = recipient.getString("messageId"); + int statusCode = recipient.getInt("statusCode"); + logger.info("Africa's Talking API response - MessageId: {}, StatusCode: {}", messageId, statusCode); + SmsMessageStatusType deliveryStatus = AfricastalkingStatus.smsStatus(statusCode); + logger.info("Mapped delivery status: {}", deliveryStatus); + + message.setExternalId(messageId); + message.setDeliveryStatus(deliveryStatus.getValue()); + } else { + String errorMessage = smsMessageData.getString("Message"); + logger.error("Failed to send SMS. Empty recipients array. Error message: {}", errorMessage); + throw new MessageGatewayException("Failed to send SMS. Empty recipients array. Error message: " + errorMessage); + } } else { - String errorMessage = smsMessageData.getString("Message"); - logger.error("Failed to send SMS. Error message: {}", errorMessage); - throw new MessageGatewayException("Failed to send SMS. Error message: " + errorMessage); + String errorMessage = smsMessageData.has("Message") ? smsMessageData.getString("Message") : "No Recipients array in response"; + logger.error("Failed to send SMS. Missing Recipients data. Error message: {}", errorMessage); + throw new MessageGatewayException("Failed to send SMS. Missing Recipients data. Error message: " + errorMessage); } } else { logger.error("Failed to send SMS. Response code: {}, Response body: {}", response.code(), responseString); From 8dab49a7c05762f397b9afdf1d8f716bf1ebccb6 Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 14:37:38 +0300 Subject: [PATCH 09/10] Refactor: modularising code for reusability and maintainability --- .../AfricastalkingMessageProvider.java | 84 ++++++++++++++----- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java index 3ce1c30..27f5f19 100644 --- a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java +++ b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java @@ -19,6 +19,8 @@ public class AfricastalkingMessageProvider extends SMSProvider { private static final Logger logger = LoggerFactory.getLogger(AfricastalkingMessageProvider.class); private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static final String SANDBOX_API_URL = "https://api.sandbox.africastalking.com/version1/messaging"; + private static final String LIVE_API_URL = "https://api.africastalking.com/version1/messaging/bulk"; @Override public void sendMessage(SMSBridge smsBridgeConfig, SMSMessage message) throws MessageGatewayException { @@ -26,11 +28,12 @@ public void sendMessage(SMSBridge smsBridgeConfig, SMSMessage message) throws Me OkHttpClient client = new OkHttpClient(); String username = smsBridgeConfig.getConfigValue("username"); String mobile = smsBridgeConfig.getCountryCode() + message.getMobileNumber(); + String apiKey = smsBridgeConfig.getConfigValue("apiKey"); if (username.equals("sandbox")) { - sendMessageSandbox(client, smsBridgeConfig, message, mobile); + sendMessageSandbox(client, smsBridgeConfig, message, mobile, apiKey); } else { - sendMessageLive(client, smsBridgeConfig, message, mobile); + sendMessageLive(client, smsBridgeConfig, message, mobile, apiKey); } } catch (IOException e) { logger.error("An error occurred while sending the SMS: {}", e.getMessage(), e); @@ -38,16 +41,19 @@ public void sendMessage(SMSBridge smsBridgeConfig, SMSMessage message) throws Me } } - private void sendMessageSandbox(OkHttpClient client, SMSBridge smsBridgeConfig, SMSMessage message, String mobile) throws IOException, MessageGatewayException { + private void sendMessageSandbox(OkHttpClient client, SMSBridge smsBridgeConfig, SMSMessage message, String mobile, String apiKey) throws IOException, MessageGatewayException { String from = smsBridgeConfig.getPhoneNo(); logger.info("Sending SMS via sandbox from: {}", from); if (from.equals("AFRICASTKNG")) { from = null; } - String url = "https://api.sandbox.africastalking.com/version1/messaging"; - - FormBody.Builder requestBodyBuilder = new FormBody.Builder().add("username", smsBridgeConfig.getConfigValue("username")).add("to", mobile).add("message", message.getMessage()).add("bulkSMSMode", "1").add("enqueue", "0"); + FormBody.Builder requestBodyBuilder = new FormBody.Builder() + .add("username", smsBridgeConfig.getConfigValue("username")) + .add("to", mobile) + .add("message", message.getMessage()) + .add("bulkSMSMode", "1") + .add("enqueue", "0"); if (from != null) { requestBodyBuilder.add("from", from); @@ -55,18 +61,18 @@ private void sendMessageSandbox(OkHttpClient client, SMSBridge smsBridgeConfig, FormBody requestBody = requestBodyBuilder.build(); - Request request = new Request.Builder().url(url).addHeader("apiKey", smsBridgeConfig.getConfigValue("apiKey")).addHeader("Content-Type", "application/x-www-form-urlencoded").addHeader("Accept", "application/json").post(requestBody).build(); - - logger.info("Request URL: {}", url); - logger.info("Request Headers: Content-Type: application/x-www-form-urlencoded, Accept: application/json"); - logger.info("Request Payload: username={}, to={}, message={}, bulkSMSMode=1, enqueue=0{}", - smsBridgeConfig.getConfigValue("username"), mobile, message.getMessage(), - from != null ? ", from=" + from : ""); + // Log request details + logRequestDetails(SANDBOX_API_URL, "application/x-www-form-urlencoded", + String.format("username=%s, to=%s, message=%s, bulkSMSMode=1, enqueue=0%s", + smsBridgeConfig.getConfigValue("username"), mobile, message.getMessage(), + from != null ? ", from=" + from : "")); - processResponse(client.newCall(request).execute(), message); + // Build and execute request + Request request = buildRequest(SANDBOX_API_URL, apiKey, "application/x-www-form-urlencoded", requestBody); + executeRequest(client, request, message); } - private void sendMessageLive(OkHttpClient client, SMSBridge smsBridgeConfig, SMSMessage message, String mobile) throws IOException, MessageGatewayException { + private void sendMessageLive(OkHttpClient client, SMSBridge smsBridgeConfig, SMSMessage message, String mobile, String apiKey) throws IOException, MessageGatewayException { String senderId = smsBridgeConfig.getConfigValue("senderId"); if (senderId == null || senderId.trim().isEmpty()) { throw new MessageGatewayException("senderId configuration is required for live environment"); @@ -74,14 +80,50 @@ private void sendMessageLive(OkHttpClient client, SMSBridge smsBridgeConfig, SMS logger.info("Sending SMS via live API with senderId: {}", senderId); - String url = "https://api.africastalking.com/version1/messaging/bulk"; + JSONObject jsonBody = new JSONObject() + .put("username", smsBridgeConfig.getConfigValue("username")) + .put("phoneNumbers", Collections.singletonList(mobile)) + .put("message", message.getMessage()) + .put("senderId", senderId); + + RequestBody requestBody = RequestBody.create(JSON, jsonBody.toString()); + + // Log request details + logRequestDetails(LIVE_API_URL, "application/json", jsonBody.toString()); - JSONObject requestBody = new JSONObject().put("username", smsBridgeConfig.getConfigValue("username")).put("phoneNumbers", Collections.singletonList(mobile)).put("message", message.getMessage()).put("senderId", senderId); + // Build and execute request + Request request = buildRequest(LIVE_API_URL, apiKey, "application/json", requestBody); + executeRequest(client, request, message); + } + + /** + * Builds the HTTP request with appropriate headers + */ + private Request buildRequest(String url, String apiKey, String contentType, RequestBody requestBody) { + return new Request.Builder() + .url(url) + .addHeader("apiKey", apiKey) + .addHeader("Content-Type", contentType) + .addHeader("Accept", "application/json") + .post(requestBody) + .build(); + } - RequestBody body = RequestBody.create(JSON, requestBody.toString()); + /** + * Logs request details for debugging + */ + private void logRequestDetails(String url, String contentType, String payload) { + logger.info("Request URL: {}", url); + logger.info("Request Headers: Content-Type: {}, Accept: application/json", contentType); + logger.info("Request Payload: {}", payload); + } - Request request = new Request.Builder().url(url).addHeader("apiKey", smsBridgeConfig.getConfigValue("apiKey")).addHeader("Content-Type", "application/json").addHeader("Accept", "application/json").post(body).build(); - processResponse(client.newCall(request).execute(), message); + /** + * Executes the request and processes the response + */ + private void executeRequest(OkHttpClient client, Request request, SMSMessage message) throws IOException, MessageGatewayException { + Response response = client.newCall(request).execute(); + processResponse(response, message); } private void processResponse(Response response, SMSMessage message) throws IOException, MessageGatewayException { @@ -130,4 +172,4 @@ private void processResponse(Response response, SMSMessage message) throws IOExc @Override public void updateStatusByMessageId(SMSBridge smsBridgeConfig, String messageId) throws MessageGatewayException { } -} +} \ No newline at end of file From 5c4672a76bee487265002b773b33f9b46ac16a9a Mon Sep 17 00:00:00 2001 From: Pinchez25 Date: Fri, 9 May 2025 14:46:47 +0300 Subject: [PATCH 10/10] refactor: formatting --- .../impl/africastalking/AfricastalkingMessageProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java index 27f5f19..a9c95c5 100644 --- a/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java +++ b/src/main/java/org/fineract/messagegateway/sms/providers/impl/africastalking/AfricastalkingMessageProvider.java @@ -172,4 +172,4 @@ private void processResponse(Response response, SMSMessage message) throws IOExc @Override public void updateStatusByMessageId(SMSBridge smsBridgeConfig, String messageId) throws MessageGatewayException { } -} \ No newline at end of file +}