feat: implement OpayGateway (Nigeria/Africa - mobile money & card)#30
Conversation
📝 WalkthroughWalkthroughThis PR introduces OPay, a Nigeria-focused payment gateway, into the PayBridge SDK. It defines configuration and enum types, implements a full OpayGateway with HMAC-SHA512 request signing and payment operations (create, verify, refund), wires the gateway into DI and factory resolution, and integrates it into service-level payment routing and transaction reference detection. ChangesOPay Payment Gateway Integration
Possibly Related PRs
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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
This PR adds new Nigeria-focused payment gateway support to the SDK by introducing OPay (card, bank transfer, USSD, mobile money) and Remita, wiring them into gateway selection, dependency injection, and factory creation so NGN payments can route across more providers.
Changes:
- Added
RemitaGatewayandOpayGatewayimplementations. - Extended config (
PaymentGatewayConfig) and enum (PaymentGatewayType) to support Remita/OPay. - Updated routing and gateway detection: NGN preference order and
REM_/OP_reference prefix mapping; registered gateways in DI and factory.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| PayBridge.SDK/Services/PaymentService.cs | Updates NGN gateway preference order and adds REM_/OP_ prefix detection for automatic verification routing. |
| PayBridge.SDK/Gateways/RemitaGateway.cs | Adds Remita gateway implementation (init/verify/refund) and reference prefixing. |
| PayBridge.SDK/Gateways/OpayGateway.cs | Adds OPay gateway implementation (init/verify/refund) with HMAC signing and env toggle. |
| PayBridge.SDK/Factories/PaymentGatewayFactory.cs | Adds Remita/OPay to gateway creation and DI resolution switch. |
| PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs | Registers Remita/OPay gateway types in DI. |
| PayBridge.SDK/Enums/PaymentGatewayType.cs | Adds Remita = 7 and Opay = 8. |
| PayBridge.SDK/Dtos/PaymentGatewayConfig.cs | Adds RemitaConfig and OpayConfig sections to configuration model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private string GenerateHash(string orderId, decimal amount) | ||
| { | ||
| var amountKobo = (long)(amount * 100); | ||
| var raw = $"{_config.Remita.MerchantId}{_config.Remita.ServiceTypeId}{orderId}{amountKobo}{_config.Remita.ApiKey}"; | ||
| var bytes = SHA512.HashData(Encoding.UTF8.GetBytes(raw)); | ||
| return Convert.ToHexString(bytes).ToLower(); | ||
| } |
| _httpClient.DefaultRequestHeaders.Clear(); | ||
| _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", | ||
| $"remitaConsumerKey={_config.Remita.MerchantId},remitaConsumerToken={authToken}"); | ||
| _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); | ||
| _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json"); |
| _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; |
| var payload = new | ||
| { | ||
| merchantId = _config.Remita.MerchantId, | ||
| rrr = request.TransactionReference, | ||
| amount = (long)(request.Amount * 100), | ||
| reason = request.Reason ?? "Customer request" | ||
| }; |
| _config = config ?? throw new ArgumentNullException(nameof(config)); | ||
| _httpClient = httpClientFactory.CreateClient(nameof(RemitaGateway)); | ||
| _logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
| _config = config ?? throw new ArgumentNullException(nameof(config)); | ||
| _httpClient = httpClientFactory.CreateClient(nameof(OpayGateway)); | ||
| _logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
| var doc = JsonDocument.Parse(responseBody); | ||
| var root = doc.RootElement; |
| var hash = HMACSHA512.HashData(keyBytes, msgBytes); | ||
| return Convert.ToHexString(hash).ToLower(); |
1c62f59 to
312611c
Compare
312611c to
212e07e
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
PayBridge.SDK/Gateways/OpayGateway.cs (1)
69-69: 💤 Low valueHardcoded payment expiration time lacks documentation.
expireAt = 30is hardcoded without indicating the unit (minutes, seconds, hours?). Consider making this configurable viaOpayConfigor at minimum document the unit and OPay's acceptable range.♻️ Suggested improvement
Add a property to
OpayConfig:public class OpayConfig { public string MerchantId { get; set; } = string.Empty; public string PublicKey { get; set; } = string.Empty; public string SecretKey { get; set; } = string.Empty; public bool IsSandbox { get; set; } = false; + /// <summary> + /// Payment expiration time in minutes (OPay default: 30). + /// </summary> + public int ExpirationMinutes { get; set; } = 30; }Then use
_config.Opay.ExpirationMinutesin the payload.🤖 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/OpayGateway.cs` at line 69, The payload sets expireAt = 30 with no unit or configurability; update the design so the expiration is configurable via OpayConfig (e.g., add an ExpirationMinutes or ExpirationSeconds property under the Opay section) and replace the hardcoded value in OpayGateway (the expireAt assignment) with _config.Opay.ExpirationMinutes (or the chosen property), and ensure the property name and unit are documented in OpayConfig's comments and any public config validation (and keep the payload unit matching OPay's API).
🤖 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/Dtos/PaymentGatewayConfig.cs`:
- Line 116: The change flipped the Remita sandbox default causing deployments to
hit production; revert the IsSandbox property's default back to true on the
PaymentGatewayConfig/RemitaConfig so sandbox remains the default (i.e., set
IsSandbox to true), and if you prefer the alternative mitigation add a runtime
guard in the payment initialization path (check MerchantId, ServiceTypeId,
ApiKey in the remita-related config/initializer) to validate those credentials
before allowing IsSandbox == false.
In `@PayBridge.SDK/Gateways/OpayGateway.cs`:
- Around line 196-245: The refund payload currently hardcodes currency = "NGN"
in RefundPaymentAsync which breaks multi-currency refunds; update the flow so
RefundRequest carries the original currency (add a Currency property to
RefundRequest and ensure callers populate it from the original transaction) and
replace the hardcoded "NGN" with request.Currency (with a sensible fallback such
as _config.Opay.DefaultCurrency or throw if missing) when building the payload;
keep all existing header/signature logic (GenerateSignature, RefundResponse
mapping) unchanged.
- Around line 66-68: The payload currently defaults
returnUrl/callbackUrl/cancelUrl to empty strings which can break OPay; in the
OpayGateway code that builds the payment payload (look for
returnUrl/callbackUrl/cancelUrl assignments and where headers are set) enforce
that request.RedirectUrl is non-null/ non-empty and fail/throw or return an
error before calling OPay (because returnUrl is required), and only include
callbackUrl and cancelUrl keys in the outgoing JSON when request.WebhookUrl or
request.RedirectUrl are present (omit them instead of setting to "") ; also
ensure the Authorization header uses the exact format "Authorization: Bearer
{PublicKey}" when setting headers for the OPay Cashier Create Payment request.
In `@PayBridge.SDK/Gateways/RemitaGateway.cs`:
- Line 14: Breaking change: the RemitaGateway class namespace was changed from
PayBridge.SDK.Gateways to PayBridge.SDK; update the release notes/CHANGELOG,
README and a migration guide showing the exact find-and-replace consumers must
perform (replace PayBridge.SDK.Gateways.RemitaGateway with
PayBridge.SDK.RemitaGateway), include a short code example of old vs new
fully-qualified type usage and any necessary using/import changes, and mark a
major version bump in your release/versioning guidance so downstream consumers
are alerted to the breaking namespace change.
In `@PayBridge.SDK/Services/PaymentService.cs`:
- Line 252: The NGN routing order in the case "NGN" uses ChooseAvailableGateway
with PaymentGatewayType values out of sync with the PR summary; update the
argument order so Paystack is preferred before Opay and Remita/Flutterwave
(e.g., ensure PaymentGatewayType.Paystack appears ahead of
PaymentGatewayType.Opay and PaymentGatewayType.Remita/Flutterwave) when calling
ChooseAvailableGateway in PaymentService.cs to match the intended "Paystack →
OPay → Remita → Flutterwave" priority.
---
Nitpick comments:
In `@PayBridge.SDK/Gateways/OpayGateway.cs`:
- Line 69: The payload sets expireAt = 30 with no unit or configurability;
update the design so the expiration is configurable via OpayConfig (e.g., add an
ExpirationMinutes or ExpirationSeconds property under the Opay section) and
replace the hardcoded value in OpayGateway (the expireAt assignment) with
_config.Opay.ExpirationMinutes (or the chosen property), and ensure the property
name and unit are documented in OpayConfig's comments and any public config
validation (and keep the payload unit matching OPay's API).
🪄 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: 7dfbc11d-7456-4448-bc07-d6f4d2115e46
📒 Files selected for processing (7)
PayBridge.SDK/Dtos/PaymentGatewayConfig.csPayBridge.SDK/Enums/PaymentGatewayType.csPayBridge.SDK/Externsions/IServiceCollectionExtensions.csPayBridge.SDK/Factories/PaymentGatewayFactory.csPayBridge.SDK/Gateways/OpayGateway.csPayBridge.SDK/Gateways/RemitaGateway.csPayBridge.SDK/Services/PaymentService.cs
| public string ServiceTypeId { get; set; } = string.Empty; | ||
| public string ApiKey { get; set; } = string.Empty; | ||
| public bool IsSandbox { get; set; } = true; | ||
| public bool IsSandbox { get; set; } = false; |
There was a problem hiding this comment.
Breaking change: RemitaConfig sandbox default flipped to production.
Changing IsSandbox default from true to false means existing deployments that relied on the sandbox default will now hit Remita's production environment without explicit configuration. This could cause unintended live transactions or API errors if production credentials aren't configured.
⚠️ Mitigation options
- Preferred: Revert to
trueand require explicit opt-in to production via configuration. - Alternative: Document this as a breaking change in release notes and migration guide, and validate that
MerchantId,ServiceTypeId, andApiKeyare configured before allowing production mode.
🤖 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/Dtos/PaymentGatewayConfig.cs` at line 116, The change flipped
the Remita sandbox default causing deployments to hit production; revert the
IsSandbox property's default back to true on the
PaymentGatewayConfig/RemitaConfig so sandbox remains the default (i.e., set
IsSandbox to true), and if you prefer the alternative mitigation add a runtime
guard in the payment initialization path (check MerchantId, ServiceTypeId,
ApiKey in the remita-related config/initializer) to validate those credentials
before allowing IsSandbox == false.
| returnUrl = request.RedirectUrl ?? string.Empty, | ||
| callbackUrl = request.WebhookUrl ?? string.Empty, | ||
| cancelUrl = request.RedirectUrl ?? string.Empty, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
OPay cashier API authentication header format and required fields for create payment
💡 Result:
To authenticate requests for the OPay Cashier create payment API, you must include specific headers in your HTTP request. Authentication Header Format For the cashier creation endpoint, OPay utilizes public key authentication [1][2]. You must provide the following headers [2][3]: Authorization: Bearer {PublicKey} MerchantId: {YourMerchantId} Content-Type: application/json Accept: application/json In this format, {PublicKey} is your OPay merchant account public key, and {YourMerchantId} is your assigned merchant ID [2][3]. Both values can be retrieved from the API Keys & Webhooks section of your OPay merchant dashboard [1]. Required Fields for Create Payment While specific requirements can vary based on regional implementation, the typical JSON payload for a cashier create payment request includes the following fields [2][3]: Amount: The transaction amount. Currency: The currency code (e.g., NGN, EGP) [2][3]. Reference: A unique identifier for the payment order in your system [2][3]. CallbackUrl: The URL where OPay will send status update notifications [2][3]. ReturnUrl: The URL to redirect the customer to after the payment process completes [2][3]. Country: The country code (e.g., NG, EG) [2][3]. ExpireAt: The expiration time for the payment link (often in minutes) [2][3]. Product: An object containing details such as the product name and description [2][3]. UserInfo (Optional but recommended): An object containing details like userId, userMobile, and userName [3]. Note: Ensure you are using the correct endpoint for your region (e.g., sandboxapi.opaycheckout.com for testing) [2]. Always refer to the official OPay documentation portal for the most accurate and region-specific implementation details [1][4][5].
Citations:
- 1: https://documentation.opaycheckout.com/payment-authentication
- 2: https://doc.opaycheckout.com/cashier-create
- 3: https://documentation.opaycheckout.com/cashier-create
- 4: https://documentation.opaycheckout.com/
- 5: https://doc.opaycheckout.com/
🌐 Web query:
OPay cashier create payment CancelUrl field name required or optional
💡 Result:
In the OPay Cashier Create Payment API, the cancelUrl field is optional [1][2]. It is defined as the URL to which the OPay cashier should redirect your client if they choose to cancel the payment process [1][2]. While not mandatory, providing this URL allows you to manage the user experience when a payment is not completed [2][3]. In contrast, the returnUrl field is typically required to handle the completion of the checkout process [2][3].
Citations:
- 1: https://doc.opaycheckout.com/cashier-create
- 2: https://documentation.opaycheckout.com/cashier-create
- 3: https://doc.opaycheckout.com/bind-bank-debit
🌐 Web query:
OPay cashier create payment CallbackUrl ReturnUrl empty string allowed
💡 Result:
According to official OPay technical documentation, the requirements for CallbackUrl and ReturnUrl in the Cashier Create Payment API are as follows: 1. ReturnUrl is mandatory. It is explicitly listed as a "required" parameter in the API specifications for Cashier Create Payment and other payment methods [1][2]. Providing an empty string for this parameter will likely cause the API request to fail, as it is required for redirecting the client after the checkout process [1][2]. 2. CallbackUrl is optional. It is documented as an "optional" parameter [1][2]. If you do not provide a CallbackUrl in your API request, OPay will instead send callback notifications to the webhook URL configured in your merchant dashboard [1][2]. Because ReturnUrl is a required field, you must provide a valid URL string to ensure the payment flow functions correctly. While the API documentation defines it as a required string, using an empty string will not satisfy this requirement and is not recommended [1][2].
Citations:
- 1: https://doc.opaycheckout.com/cashier-create
- 2: https://documentation.opaycheckout.com/cashier-create
Fix OPay URL handling for returnUrl / callbackUrl / cancelUrl
- Auth header format is correct for OPay Cashier Create Payment:
Authorization: Bearer {PublicKey}. returnUrl: OPay documents this as required. Defaultingrequest.RedirectUrl ?? string.Emptycan send an empty string and likely breaks the API request—ensure a non-emptyReturnUrl(or fail/omit the request) whenRedirectUrlis null.callbackUrl: OPay documents this as optional (webhooks can be used if omitted), so avoid sending empty strings whenWebhookUrlis null.cancelUrl: OPay documents this as optional; avoid sendingcancelUrl: ""whenRedirectUrlis null (omit unless you have a real URL).
returnUrl = request.RedirectUrl ?? string.Empty,
callbackUrl = request.WebhookUrl ?? string.Empty,
cancelUrl = request.RedirectUrl ?? 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/OpayGateway.cs` around lines 66 - 68, The payload
currently defaults returnUrl/callbackUrl/cancelUrl to empty strings which can
break OPay; in the OpayGateway code that builds the payment payload (look for
returnUrl/callbackUrl/cancelUrl assignments and where headers are set) enforce
that request.RedirectUrl is non-null/ non-empty and fail/throw or return an
error before calling OPay (because returnUrl is required), and only include
callbackUrl and cancelUrl keys in the outgoing JSON when request.WebhookUrl or
request.RedirectUrl are present (omit them instead of setting to "") ; also
ensure the Authorization header uses the exact format "Authorization: Bearer
{PublicKey}" when setting headers for the OPay Cashier Create Payment request.
| public async Task<RefundResponse> RefundPaymentAsync(RefundRequest request) | ||
| { | ||
| try | ||
| { | ||
| var payload = new | ||
| { | ||
| merchantId = _config.Opay.MerchantId, | ||
| reference = request.TransactionReference, | ||
| amount = new { total = (long)(request.Amount * 100), currency = "NGN" }, | ||
| reason = request.Reason ?? "Customer request" | ||
| }; | ||
|
|
||
| var json = JsonSerializer.Serialize(payload); | ||
| var signature = GenerateSignature(json); | ||
|
|
||
| _httpClient.DefaultRequestHeaders.Clear(); | ||
| _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Bearer {_config.Opay.PublicKey}"); | ||
| _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("MerchantId", _config.Opay.MerchantId); | ||
| _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Signature", signature); | ||
| _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json"); | ||
|
|
||
| var content = new StringContent(json, Encoding.UTF8, "application/json"); | ||
| var response = await _httpClient.PostAsync($"{BaseUrl}/international/cashier/refund", content); | ||
|
|
||
| var responseBody = await response.Content.ReadAsStringAsync(); | ||
| _logger.LogDebug("OPay refund response: {Body}", responseBody); | ||
|
|
||
| var doc = JsonDocument.Parse(responseBody); | ||
| var root = doc.RootElement; | ||
|
|
||
| var code = root.TryGetProperty("code", out var c) ? c.GetString() : null; | ||
| var isSuccess = code == "00000"; | ||
| var msg = root.TryGetProperty("message", out var m) ? m.GetString() : null; | ||
|
|
||
| return new RefundResponse | ||
| { | ||
| Success = isSuccess, | ||
| RefundReference = request.TransactionReference, | ||
| TransactionReference = request.TransactionReference, | ||
| Amount = request.Amount, | ||
| Message = isSuccess ? "Refund initiated successfully" : (msg ?? "Refund failed"), | ||
| Status = isSuccess ? PaymentStatus.Refunded : PaymentStatus.Failed | ||
| }; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Error processing OPay refund: {Ref}", request.TransactionReference); | ||
| throw new PaymentGatewayException("Failed to process OPay refund", ex); | ||
| } | ||
| } |
There was a problem hiding this comment.
Critical: Hardcoded "NGN" currency in refunds breaks multi-currency support.
Line 204 hardcodes currency = "NGN" for all refund requests, regardless of the original transaction's currency. If OPay supports non-NGN currencies (or will in the future), this will cause refunds to fail or process incorrectly. The refund currency must match the original payment currency.
🐛 Proposed fix
Pass the original currency through the RefundRequest or retrieve it from the transaction record:
var payload = new
{
merchantId = _config.Opay.MerchantId,
reference = request.TransactionReference,
- amount = new { total = (long)(request.Amount * 100), currency = "NGN" },
+ amount = new { total = (long)(request.Amount * 100), currency = request.Currency ?? "NGN" },
reason = request.Reason ?? "Customer request"
};Ensure RefundRequest includes a Currency property populated from the original transaction.
🤖 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/OpayGateway.cs` around lines 196 - 245, The refund
payload currently hardcodes currency = "NGN" in RefundPaymentAsync which breaks
multi-currency refunds; update the flow so RefundRequest carries the original
currency (add a Currency property to RefundRequest and ensure callers populate
it from the original transaction) and replace the hardcoded "NGN" with
request.Currency (with a sensible fallback such as _config.Opay.DefaultCurrency
or throw if missing) when building the payload; keep all existing
header/signature logic (GenerateSignature, RefundResponse mapping) unchanged.
| using PayBridge.SDK.Interfaces; | ||
|
|
||
| namespace PayBridge.SDK.Gateways; | ||
| namespace PayBridge.SDK; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check namespace declarations for all gateway implementations.
# Find all gateway files and extract their namespaces
rg -n '^namespace ' --glob '*Gateway.cs' -A 0Repository: teesofttech/PayBridge
Length of output: 1043
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Find any remaining gateway files using the old namespace
rg -n 'namespace\s+PayBridge\.SDK\.Gateways' --glob '*Gateway.cs' .
# 2) Check for any references to the old fully-qualified RemitaGateway name
rg -n 'PayBridge\.SDK\.Gateways\.RemitaGateway' .
# 3) Show where RemitaGateway is declared and what namespace it uses
rg -n 'class\s+RemitaGateway\b|interface\s+IRemitaGateway\b|record\s+RemitaGateway\b' --glob '*RemitaGateway*.cs' .Repository: teesofttech/PayBridge
Length of output: 47
🏁 Script executed:
#!/bin/bash
set +euo pipefail
echo "=== Old namespace declarations (PayBridge.SDK.Gateways) in *Gateway.cs ==="
rg -n 'namespace\s+PayBridge\.SDK\.Gateways' --glob '*Gateway.cs' . || true
echo
echo "=== References to old fully-qualified RemitaGateway (PayBridge.SDK.Gateways.RemitaGateway) ==="
rg -n 'PayBridge\.SDK\.Gateways\.RemitaGateway' . || true
echo
echo "=== RemitaGateway declarations (class/interface/record) ==="
rg -n 'class\s+RemitaGateway\b|interface\s+IRemitaGateway\b|record\s+RemitaGateway\b' --glob '*Remita*' . || true
echo
echo "=== RemitaGateway file content (namespace line + any relevant type signature) ==="
f="PayBridge.SDK/Gateways/RemitaGateway.cs"
if [ -f "$f" ]; then
rg -n 'namespace\s+|class\s+RemitaGateway\b|interface\s+IRemitaGateway\b|record\s+RemitaGateway\b' "$f" || true
else
echo "Missing $f"
fiRepository: teesofttech/PayBridge
Length of output: 546
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== References to old namespace prefix PayBridge.SDK.Gateways. ==="
rg -n 'PayBridge\.SDK\.Gateways\.' .
echo
echo "=== Using directives for PayBridge.SDK.Gateways ==="
rg -n 'using\s+PayBridge\.SDK\.Gateways\b' .Repository: teesofttech/PayBridge
Length of output: 132
Breaking change: RemitaGateway namespace moved to PayBridge.SDK (PayBridge.SDK/Gateways/RemitaGateway.cs:14). Consumers referencing PayBridge.SDK.Gateways.RemitaGateway will break; the repo already uses namespace PayBridge.SDK; consistently across all *Gateway.cs files and contains no remaining PayBridge.SDK.Gateways.* references, so focus on documenting the migration (and version bump/migration guidance) for consumers.
🤖 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` at line 14, Breaking change: the
RemitaGateway class namespace was changed from PayBridge.SDK.Gateways to
PayBridge.SDK; update the release notes/CHANGELOG, README and a migration guide
showing the exact find-and-replace consumers must perform (replace
PayBridge.SDK.Gateways.RemitaGateway with PayBridge.SDK.RemitaGateway), include
a short code example of old vs new fully-qualified type usage and any necessary
using/import changes, and mark a major version bump in your release/versioning
guidance so downstream consumers are alerted to the breaking namespace change.
| { | ||
| case "NGN": | ||
| return ChooseAvailableGateway(PaymentGatewayType.Monnify, PaymentGatewayType.Squad, PaymentGatewayType.Korapay, PaymentGatewayType.Interswitch, PaymentGatewayType.Remita, PaymentGatewayType.Paystack, PaymentGatewayType.Flutterwave); | ||
| return ChooseAvailableGateway(PaymentGatewayType.Monnify, PaymentGatewayType.Squad, PaymentGatewayType.Korapay, PaymentGatewayType.Interswitch, PaymentGatewayType.Remita, PaymentGatewayType.Opay, PaymentGatewayType.Paystack, PaymentGatewayType.Flutterwave); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find all NGN gateway selection logic to verify consistency.
# Search for NGN currency handling
rg -n 'case.*"NGN"' -A 3 -B 1Repository: teesofttech/PayBridge
Length of output: 623
Align NGN gateway preference order with stated routing priority.
In PayBridge.SDK/Services/PaymentService.cs (case "NGN"), the preference list is Monnify → Squad → Korapay → Interswitch → Remita → Opay → Paystack → Flutterwave—so OPay is 6th and Paystack comes after OPay—contradicting the PR summary’s intended “Paystack → OPay → Remita → Flutterwave” priority. Update either the code or the stated business priority so they match.
🤖 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/Services/PaymentService.cs` at line 252, The NGN routing order
in the case "NGN" uses ChooseAvailableGateway with PaymentGatewayType values out
of sync with the PR summary; update the argument order so Paystack is preferred
before Opay and Remita/Flutterwave (e.g., ensure PaymentGatewayType.Paystack
appears ahead of PaymentGatewayType.Opay and
PaymentGatewayType.Remita/Flutterwave) when calling ChooseAvailableGateway in
PaymentService.cs to match the intended "Paystack → OPay → Remita → Flutterwave"
priority.
Summary
Implements the OPay payment gateway for Nigerian and African payments, supporting card, bank transfer, USSD, and mobile money.
Changes
Opay = 8toPaymentGatewayTypeenumRemitaConfigandOpayConfig(MerchantId, PublicKey, SecretKey, IsSandbox) toPaymentGatewayConfig.csRemitaGateway.cs(included as dependency for this branch)OpayGateway.cswith full implementation:cashierapi.opayweb.com) / Sandbox (sandboxapi.opaycheckout.com) toggle00000= success,SUCCESS/PENDING/FAILfor verificationOP_OpayGatewayinIServiceCollectionExtensions.csPaymentGatewayFactory.csPaymentService.cs: NGN routing (Paystack → OPay → Remita → Flutterwave) +OP_prefix detectionCloses #13
Summary by CodeRabbit
New Features
Changes