feat: implement RemitaGateway (Nigeria - government & corporate payments)#29
Conversation
|
Warning Review limit reached
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 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis 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. ChangesRemita Gateway Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
RemitaGatewaywith 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.
| using PayBridge.SDK.Exceptions; | ||
| using PayBridge.SDK.Interfaces; | ||
|
|
||
| namespace PayBridge.SDK.Gateways; |
| var payload = new | ||
| { | ||
| merchantId = _config.Remita.MerchantId, | ||
| rrr = request.TransactionReference, | ||
| amount = (long)(request.Amount * 100), | ||
| reason = request.Reason ?? "Customer request" |
| _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"); |
| var doc = JsonDocument.Parse(responseBody); | ||
| var root = doc.RootElement; |
There was a problem hiding this comment.
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
📒 Files selected for processing (6)
PayBridge.SDK/Dtos/PaymentGatewayConfig.csPayBridge.SDK/Enums/PaymentGatewayType.csPayBridge.SDK/Externsions/IServiceCollectionExtensions.csPayBridge.SDK/Factories/PaymentGatewayFactory.csPayBridge.SDK/Gateways/RemitaGateway.csPayBridge.SDK/Services/PaymentService.cs
| 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 } | ||
| }; |
There was a problem hiding this comment.
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.
| 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.
| var payload = new | ||
| { | ||
| merchantId = _config.Remita.MerchantId, | ||
| rrr = request.TransactionReference, | ||
| amount = (long)(request.Amount * 100), | ||
| reason = request.Reason ?? "Customer request" |
There was a problem hiding this comment.
🧩 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.
68edaf3 to
ebcb26e
Compare
Summary
Implements the Remita payment gateway for Nigerian government, corporate, and retail payments.
Changes
Remita = 7toPaymentGatewayTypeenumRemitaConfig(MerchantId, ServiceTypeId, ApiKey, IsSandbox) toPaymentGatewayConfig.csRemitaGateway.cswith full implementation:login.remita.net) / Sandbox (remitademo.net) toggle025= success,01= verified,02= pendingREM_RemitaGatewayinIServiceCollectionExtensions.csPaymentGatewayFactory.csPaymentService.cs: NGN routing includes Remita +REM_prefix detectionCloses #12
Summary by CodeRabbit
New Features