Skip to content

feat: implement RemitaGateway (Nigeria - government & corporate payments)#29

Merged
teesofttech merged 1 commit into
masterfrom
feature/issue-12-remita-gateway
Jun 6, 2026
Merged

feat: implement RemitaGateway (Nigeria - government & corporate payments)#29
teesofttech merged 1 commit into
masterfrom
feature/issue-12-remita-gateway

Conversation

@teesofttech

@teesofttech teesofttech commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the Remita payment gateway for Nigerian government, corporate, and retail payments.

Changes

  • Added Remita = 7 to PaymentGatewayType enum
  • Added RemitaConfig (MerchantId, ServiceTypeId, ApiKey, IsSandbox) to PaymentGatewayConfig.cs
  • Created RemitaGateway.cs with full implementation:
    • SHA512 HMAC hash generation per request
    • Live (login.remita.net) / Sandbox (remitademo.net) toggle
    • Amounts in kobo (×100)
    • Status code 025 = success, 01 = verified, 02 = pending
    • Tx reference prefix: REM_
  • Registered RemitaGateway in IServiceCollectionExtensions.cs
  • Added factory switch case in PaymentGatewayFactory.cs
  • Updated PaymentService.cs: NGN routing includes Remita + REM_ prefix detection

Closes #12

Summary by CodeRabbit

New Features

  • Added support for Remita payment gateway, enabling Nigerian Naira (NGN) payment processing
  • Integrated full payment lifecycle support including payment creation, verification, and refunds
  • Configured sandbox and live environment options for Remita transactions

Copilot AI review requested due to automatic review settings June 4, 2026 09:05
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@teesofttech, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 20 minutes and 28 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7abac3f5-ae03-4ae3-8ff9-51cacbf25b7e

📥 Commits

Reviewing files that changed from the base of the PR and between 16d331e and ebcb26e.

📒 Files selected for processing (6)
  • PayBridge.SDK/Dtos/PaymentGatewayConfig.cs
  • PayBridge.SDK/Enums/PaymentGatewayType.cs
  • PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs
  • PayBridge.SDK/Factories/PaymentGatewayFactory.cs
  • PayBridge.SDK/Gateways/RemitaGateway.cs
  • PayBridge.SDK/Services/PaymentService.cs
📝 Walkthrough

Walkthrough

This PR implements the Remita payment gateway for Nigeria (NGN). It adds configuration and type definitions, introduces RemitaGateway implementing the IPaymentGateway contract with payment creation, verification, and refund flows, wires the gateway into dependency injection and the factory, and updates the payment service to route NGN payments through Remita and detect Remita transaction references.

Changes

Remita Gateway Integration

Layer / File(s) Summary
Remita Configuration & Type Definitions
PayBridge.SDK/Enums/PaymentGatewayType.cs, PayBridge.SDK/Dtos/PaymentGatewayConfig.cs
PaymentGatewayType.Remita enum value added; RemitaConfig DTO introduced with merchant credentials (MerchantId, ServiceTypeId, ApiKey) and IsSandbox flag; new Remita property initialized on PaymentGatewayConfig.
RemitaGateway Core Implementation
PayBridge.SDK/Gateways/RemitaGateway.cs
RemitaGateway class implements IPaymentGateway with injected config, HttpClient, and logger. GenerateHash computes SHA-512 hashes from merchant/service/order/amount/apiKey. CreatePaymentAsync generates transaction reference (REM_ prefix), builds Authorization header, posts payment initiation, parses response (statuscode/status/RRR), and returns checkout URL. VerifyPaymentAsync queries order status and maps Remita status codes to PaymentStatus. RefundPaymentAsync sends reversal requests and validates statuscode. All operations include exception handling and logging.
Dependency Injection & Factory Wiring
PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs, PayBridge.SDK/Factories/PaymentGatewayFactory.cs
RegisterGateways adds RemitaGateway to default gateway registration and extends enabled-gateways switch with Remita case. PaymentGatewayFactory updates auto-enable logic and TryAddGateway to resolve RemitaGateway via DI with nullable typing support.
Payment Service Routing & Reference Detection
PayBridge.SDK/Services/PaymentService.cs
SelectBestGateway extends NGN currency routing to include Remita alongside Paystack/Flutterwave. DetermineGatewayFromReference recognizes REM_ transaction reference prefix and maps to Remita gateway.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #13: Parallel payment gateway implementation pattern with config/type updates and gateway class addition.
  • #10: Shared implementation pattern extending PaymentGatewayConfig, PaymentGatewayType, IServiceCollectionExtensions, and PaymentGatewayFactory.
  • #8: Similar gateway integration across configuration, DI registration, and factory resolution points.

Poem

🐰 Remita hops into Nigeria's payment parade,
SHA-512 hashes dancing through the cryptic shade,
NGN coins flowing, transaction refs marked with REM_,
From creation to refund, the gateway guards them,
DI and factories in harmony ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: implementing the RemitaGateway for Nigeria payments.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #12: RemitaGateway class, PaymentGatewayType enum update, PaymentGatewayConfig extension, DI registration, factory integration, and NGN routing logic.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing RemitaGateway support. No unrelated modifications detected in the affected files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-12-remita-gateway

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Remita as a new payment gateway option for NGN flows, wiring it into gateway selection/routing and providing a new RemitaGateway implementation that can initiate, verify, and refund payments against Remita endpoints.

Changes:

  • Introduces RemitaGateway with request hashing, sandbox/live base URLs, and kobo (×100) amount handling.
  • Extends gateway configuration and enum (PaymentGatewayConfig.Remita, PaymentGatewayType.Remita = 7).
  • Wires Remita into selection and construction paths (NGN routing + REM_ prefix detection, factory switch case, DI registration).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
PayBridge.SDK/Services/PaymentService.cs Adds Remita to NGN gateway preference and detects REM_ references.
PayBridge.SDK/Gateways/RemitaGateway.cs New Remita gateway implementation (initiate/verify/refund + hashing + URLs).
PayBridge.SDK/Factories/PaymentGatewayFactory.cs Adds Remita to factory initialization/switch mapping.
PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs Registers Remita in DI gateway registration flow.
PayBridge.SDK/Enums/PaymentGatewayType.cs Adds Remita = 7 enum member.
PayBridge.SDK/Dtos/PaymentGatewayConfig.cs Adds RemitaConfig to gateway configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs
Comment thread PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs
using PayBridge.SDK.Exceptions;
using PayBridge.SDK.Interfaces;

namespace PayBridge.SDK.Gateways;
Comment thread PayBridge.SDK/Gateways/RemitaGateway.cs
Comment thread PayBridge.SDK/Gateways/RemitaGateway.cs
Comment thread PayBridge.SDK/Gateways/RemitaGateway.cs
Comment thread PayBridge.SDK/Gateways/RemitaGateway.cs
Comment on lines +186 to +191
var payload = new
{
merchantId = _config.Remita.MerchantId,
rrr = request.TransactionReference,
amount = (long)(request.Amount * 100),
reason = request.Reason ?? "Customer request"
Comment on lines +179 to +184
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation(
"Authorization",
$"remitaConsumerKey={_config.Remita.MerchantId},remitaConsumerToken={hash}");
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json");
Comment on lines +201 to +202
var doc = JsonDocument.Parse(responseBody);
var root = doc.RootElement;

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@PayBridge.SDK/Gateways/RemitaGateway.cs`:
- Around line 104-117: The code currently treats statuscode "025" as success
even if RRR is missing: check the parsed rrr (from root.TryGetProperty("RRR",
out var rrrProp) ? rrrProp.GetString() : string.Empty) and if it is null or
empty, do not return a successful PaymentResponse; instead return a
failed/invalid PaymentResponse (e.g., Success = false, Status =
PaymentStatus.Failed or appropriate enum, and a clear Message indicating missing
RRR) and avoid constructing the checkoutUrl; adjust the RemitaGateway method
that builds the PaymentResponse to validate rrr before setting Success = true
and CheckoutUrl.
- Around line 30-38: The constructor of RemitaGateway allows a
PaymentGatewayConfig with empty MerchantId, ServiceTypeId, or ApiKey, so
validate these required credentials in the RemitaGateway constructor: check
config.MerchantId, config.ServiceTypeId and config.ApiKey for null/empty and
throw ArgumentException (or ArgumentNullException) with clear parameter names if
any are missing; keep existing null check for config and logger and use the same
constructor (RemitaGateway) and type names (PaymentGatewayConfig) so the gateway
is rejected at initialization rather than later during calls.
- Around line 186-191: The refund/reversal payload in RemitaGateway currently
sets rrr = request.TransactionReference but Remita requires the actual Remita
Retrieval Reference (RRR); replace the mapping to use the persisted RRR value
(e.g., request.RemitaRetrievalReference or the field where you store the
provider's RRR from the payment flow) when building the payload variable (the
rrr property), ensure callers populate that persisted RRR, and add a
guard/validation in the same method to throw or return an error if the RRR is
missing before sending the refund request.

In `@PayBridge.SDK/Services/PaymentService.cs`:
- Line 251: The NGN branch is calling ChooseAvailableGateway with Paystack
before Remita, so Remita won't be chosen when both are enabled; update the call
in PaymentService (the NGN routing branch) to pass PaymentGatewayType.Remita
first, e.g. ChooseAvailableGateway(PaymentGatewayType.Remita,
PaymentGatewayType.Paystack, PaymentGatewayType.Flutterwave), so Remita is
preferred but falls back to Paystack/Flutterwave.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9070e5bc-0a04-490f-9493-1ff8051133dd

📥 Commits

Reviewing files that changed from the base of the PR and between dbefb11 and 16d331e.

📒 Files selected for processing (6)
  • PayBridge.SDK/Dtos/PaymentGatewayConfig.cs
  • PayBridge.SDK/Enums/PaymentGatewayType.cs
  • PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs
  • PayBridge.SDK/Factories/PaymentGatewayFactory.cs
  • PayBridge.SDK/Gateways/RemitaGateway.cs
  • PayBridge.SDK/Services/PaymentService.cs

Comment thread PayBridge.SDK/Gateways/RemitaGateway.cs
Comment on lines +104 to +117
var rrr = root.TryGetProperty("RRR", out var rrrProp) ? rrrProp.GetString() : string.Empty;
var checkoutUrl = _config.Remita.IsSandbox
? $"https://remitademo.net/remita/ecomm/finalize.reg?merchantId={_config.Remita.MerchantId}&hash={hash}&RRR={rrr}"
: $"https://login.remita.net/remita/ecomm/finalize.reg?merchantId={_config.Remita.MerchantId}&hash={hash}&RRR={rrr}";

return new PaymentResponse
{
Success = true,
TransactionReference = txRef,
CheckoutUrl = checkoutUrl,
Message = "Payment initiated successfully",
Status = PaymentStatus.Pending,
GatewayResponse = new Dictionary<string, string> { ["RRR"] = rrr ?? string.Empty }
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not return success when RRR is missing.

statuscode == "025" with an empty/missing RRR currently returns Success = true and builds an invalid checkout URL. That creates a broken transaction state.

Proposed fix
 var rrr = root.TryGetProperty("RRR", out var rrrProp) ? rrrProp.GetString() : string.Empty;
+if (string.IsNullOrWhiteSpace(rrr))
+{
+    return new PaymentResponse
+    {
+        Success = false,
+        Message = "Remita payment initiation failed: missing RRR",
+        Status = PaymentStatus.Failed
+    };
+}
 var checkoutUrl = _config.Remita.IsSandbox
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var rrr = root.TryGetProperty("RRR", out var rrrProp) ? rrrProp.GetString() : string.Empty;
var checkoutUrl = _config.Remita.IsSandbox
? $"https://remitademo.net/remita/ecomm/finalize.reg?merchantId={_config.Remita.MerchantId}&hash={hash}&RRR={rrr}"
: $"https://login.remita.net/remita/ecomm/finalize.reg?merchantId={_config.Remita.MerchantId}&hash={hash}&RRR={rrr}";
return new PaymentResponse
{
Success = true,
TransactionReference = txRef,
CheckoutUrl = checkoutUrl,
Message = "Payment initiated successfully",
Status = PaymentStatus.Pending,
GatewayResponse = new Dictionary<string, string> { ["RRR"] = rrr ?? string.Empty }
};
var rrr = root.TryGetProperty("RRR", out var rrrProp) ? rrrProp.GetString() : string.Empty;
if (string.IsNullOrWhiteSpace(rrr))
{
return new PaymentResponse
{
Success = false,
Message = "Remita payment initiation failed: missing RRR",
Status = PaymentStatus.Failed
};
}
var checkoutUrl = _config.Remita.IsSandbox
? $"https://remitademo.net/remita/ecomm/finalize.reg?merchantId={_config.Remita.MerchantId}&hash={hash}&RRR={rrr}"
: $"https://login.remita.net/remita/ecomm/finalize.reg?merchantId={_config.Remita.MerchantId}&hash={hash}&RRR={rrr}";
return new PaymentResponse
{
Success = true,
TransactionReference = txRef,
CheckoutUrl = checkoutUrl,
Message = "Payment initiated successfully",
Status = PaymentStatus.Pending,
GatewayResponse = new Dictionary<string, string> { ["RRR"] = rrr ?? string.Empty }
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PayBridge.SDK/Gateways/RemitaGateway.cs` around lines 104 - 117, The code
currently treats statuscode "025" as success even if RRR is missing: check the
parsed rrr (from root.TryGetProperty("RRR", out var rrrProp) ?
rrrProp.GetString() : string.Empty) and if it is null or empty, do not return a
successful PaymentResponse; instead return a failed/invalid PaymentResponse
(e.g., Success = false, Status = PaymentStatus.Failed or appropriate enum, and a
clear Message indicating missing RRR) and avoid constructing the checkoutUrl;
adjust the RemitaGateway method that builds the PaymentResponse to validate rrr
before setting Success = true and CheckoutUrl.

Comment on lines +186 to +191
var payload = new
{
merchantId = _config.Remita.MerchantId,
rrr = request.TransactionReference,
amount = (long)(request.Amount * 100),
reason = request.Reason ?? "Customer request"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

In the official Remita reversal API contract, does the rrrfield strictly require the Remita RRR value, or can it accept a merchant order/transaction reference (e.g., internalREM_*)?

💡 Result:

The official Remita reversal/chargeback API contract (the “rrr” field in the Remita Retrieval Reference payment/reversal flow) expects the field value to be the Remita Retrieval Reference itself; it is not a merchant order/transaction reference such as your internal “REM_*”. The contract defines rrr as “Required” and explicitly states it is “This is the Remita Retrieval Reference”. [1] So: you should send the actual Remita Retrieval Reference (RRR) generated/associated with that payer’s transaction, not your internal orderId/transaction reference. [1]

Citations:


Fix Remita refund rrr mapping to send the actual Remita Retrieval Reference (RRR)

In PayBridge.SDK/Gateways/RemitaGateway.cs (lines 186-191), the reversal/refund payload sets rrr = request.TransactionReference. Remita’s contract defines rrr as the Remita Retrieval Reference itself and marks it Required, so this value must be the provider’s RRR for that payer transaction—not an internal merchant/order/transaction reference. Persist the RRR from the payment flow and use that stored RRR when building the rrr field for refunds/reversals.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PayBridge.SDK/Gateways/RemitaGateway.cs` around lines 186 - 191, The
refund/reversal payload in RemitaGateway currently sets rrr =
request.TransactionReference but Remita requires the actual Remita Retrieval
Reference (RRR); replace the mapping to use the persisted RRR value (e.g.,
request.RemitaRetrievalReference or the field where you store the provider's RRR
from the payment flow) when building the payload variable (the rrr property),
ensure callers populate that persisted RRR, and add a guard/validation in the
same method to throw or return an error if the RRR is missing before sending the
refund request.

Comment thread PayBridge.SDK/Services/PaymentService.cs Outdated
@teesofttech
teesofttech force-pushed the feature/issue-12-remita-gateway branch from 68edaf3 to ebcb26e Compare June 6, 2026 16:00
@teesofttech
teesofttech merged commit 2f193f4 into master Jun 6, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: implement RemitaGateway (Nigeria - government, school fees, utility payments)

2 participants