Skip to content

feat: implement InterswitchGateway (Nigeria - Quickteller/Webpay)#28

Merged
teesofttech merged 5 commits into
masterfrom
feature/issue-11-interswitch-gateway
Jun 4, 2026
Merged

feat: implement InterswitchGateway (Nigeria - Quickteller/Webpay)#28
teesofttech merged 5 commits into
masterfrom
feature/issue-11-interswitch-gateway

Conversation

@teesofttech

@teesofttech teesofttech commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the Interswitch payment gateway (Quickteller/Webpay) for Nigerian payments.

Changes

  • Added Interswitch = 7 to PaymentGatewayType enum
  • Added InterswitchConfig (ClientId, ClientSecret, MerchantCode, PaymentItemCode, IsSandbox) to PaymentGatewayConfig.cs
  • Created InterswitchGateway.cs with full implementation:
    • OAuth2 client credentials token fetch
    • HMAC-SHA512 request signing
    • Live (api.interswitchgroup.com) / Sandbox (sandbox.interswitchng.com) toggle
    • Amounts in kobo (×100)
    • Response code 00 = success, T0 = pending
    • Tx reference prefix: ISW_
  • Registered InterswitchGateway in IServiceCollectionExtensions.cs
  • Added factory switch case in PaymentGatewayFactory.cs
  • Updated PaymentService.cs: NGN routing + ISW_ prefix detection

Closes #11

Summary by CodeRabbit

New Features

  • Added Interswitch payment gateway support, enabling payment processing, verification, and refunds for Nigerian Naira (NGN) transactions with both sandbox and live environment options.

Copilot AI review requested due to automatic review settings June 4, 2026 08:58
@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 15 minutes and 26 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: acd6575d-b564-4d60-b025-e3a0dbbeb040

📥 Commits

Reviewing files that changed from the base of the PR and between 3c51bcc and 388f492.

📒 Files selected for processing (2)
  • PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs
  • PayBridge.SDK/Gateways/InterswitchGateway.cs
📝 Walkthrough

Walkthrough

The PR adds Interswitch (Nigeria's largest payment network) as a new gateway to PayBridge SDK. A new InterswitchGateway class implements OAuth token retrieval and HMAC-SHA512-signed requests for payment creation, verification, and refunds. Configuration, type registration, DI wiring, and service-level routing are updated to support NGN transactions.

Changes

Interswitch Payment Gateway

Layer / File(s) Summary
Configuration and type definitions
PayBridge.SDK/Dtos/PaymentGatewayConfig.cs, PayBridge.SDK/Enums/PaymentGatewayType.cs
InterswitchConfig DTO added with ClientId, ClientSecret, MerchantCode, PaymentItemCode, and IsSandbox properties. PaymentGatewayType enum extended with Interswitch = 10 member.
InterswitchGateway implementation
PayBridge.SDK/Gateways/InterswitchGateway.cs
New gateway class implementing IPaymentGateway with OAuth client_credentials token retrieval, HMAC-SHA512 request signing (Webpay-style), and three core operations: CreatePaymentAsync (returns checkout redirect URL), VerifyPaymentAsync (maps response codes to payment status), and RefundPaymentAsync (posts signed refund requests). Handles sandbox/live URL selection from configuration.
Dependency injection and factory registration
PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs, PayBridge.SDK/Factories/PaymentGatewayFactory.cs
InterswitchGateway added to DI service collection in enable-all mode and when explicitly enabled. PaymentGatewayFactory.TryAddGateway extended to resolve Interswitch type to the gateway from the service provider.
Payment service gateway selection
PayBridge.SDK/Services/PaymentService.cs
SelectBestGateway adds Interswitch to preferred NGN currency gateway list. DetermineGatewayFromReference detects ISW_ transaction reference prefix and routes to Interswitch gateway.

Sequence Diagram(s)

The diagram above illustrates the three main payment operations (create, verify, refund) executed by InterswitchGateway against the Interswitch API, showing signature generation and JSON response parsing paths.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • teesofttech/PayBridge#27: Extends the same gateway-selection and factory-wiring code paths for NGN, adding a different gateway (Squad vs Interswitch).
  • teesofttech/PayBridge#26: Modifies the same DI, factory, and service routing code for a different Nigerian gateway (Monnify via MNF_ prefix vs Interswitch via ISW_).

Poem

🐰 A rabbit hops through payment streams,
Interswitch now joins the team!
With tokens signed in SHA's embrace,
Nigerian payments find their place.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% 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 'feat: implement InterswitchGateway (Nigeria - Quickteller/Webpay)' directly and clearly summarizes the primary change: implementing a new payment gateway for Interswitch with its location and alias names.
Linked Issues check ✅ Passed The pull request fully addresses all coding requirements from issue #11: InterswitchGateway implementation with OAuth2, HMAC-SHA512 signing, PaymentGatewayConfig extension with required fields, PaymentGatewayType enum addition, and NGN routing with ISW_ prefix detection.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the Interswitch gateway; modifications to PaymentGatewayType, PaymentGatewayConfig, IServiceCollectionExtensions, PaymentGatewayFactory, PaymentService, and the new InterswitchGateway are all necessary for the gateway integration.

✏️ 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-11-interswitch-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 support for the Interswitch (Quickteller/Webpay) payment gateway to the PayBridge SDK, enabling Nigeria (NGN) routing and transaction-reference based gateway detection.

Changes:

  • Introduces a new InterswitchGateway implementation with OAuth token retrieval, request signing, create/verify/refund flows, and sandbox/live switching.
  • Extends configuration and enums to support the new gateway (PaymentGatewayType.Interswitch, InterswitchConfig).
  • Wires Interswitch into gateway selection/routing and DI/factory registration.

Reviewed changes

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

Show a summary per file
File Description
PayBridge.SDK/Services/PaymentService.cs Prefer Interswitch for NGN and detect ISW_ references as Interswitch.
PayBridge.SDK/Gateways/InterswitchGateway.cs New gateway implementation: OAuth token fetch, signing headers, create/verify/refund calls.
PayBridge.SDK/Factories/PaymentGatewayFactory.cs Adds Interswitch to factory initialization and resolution switch.
PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs Registers InterswitchGateway in DI for “all gateways” and “enabled gateways” modes.
PayBridge.SDK/Enums/PaymentGatewayType.cs Adds Interswitch = 7 enum member.
PayBridge.SDK/Dtos/PaymentGatewayConfig.cs Adds InterswitchConfig settings section to the SDK config model.

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

Comment thread PayBridge.SDK/Gateways/InterswitchGateway.cs Outdated
Comment thread PayBridge.SDK/Gateways/InterswitchGateway.cs Outdated
Comment thread PayBridge.SDK/Gateways/InterswitchGateway.cs Outdated
Comment on lines +104 to +105
var accessToken = await GetAccessTokenAsync();
var txRef = $"ISW_{Guid.NewGuid():N}";
services.AddScoped<CheckoutGateway>();
services.AddScoped<BenefitPayGateway>();
services.AddScoped<KnetGateway>();
services.AddScoped<InterswitchGateway>();
Comment thread PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs Outdated
@teesofttech
teesofttech force-pushed the feature/issue-11-interswitch-gateway branch from 559103d to 3c51bcc Compare June 4, 2026 10:38

@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/Externsions/IServiceCollectionExtensions.cs`:
- Line 140: InterswitchGateway is only registered as the concrete type
(services.AddScoped<InterswitchGateway>), so PaymentService (which resolves
IEnumerable<IPaymentGateway>) never receives it; change the registration to
register it as IPaymentGateway as well (e.g., register InterswitchGateway with
the IPaymentGateway service) in the same places where InterswitchGateway is
currently added (the default registration at
services.AddScoped<InterswitchGateway>() and the PaymentGatewayType.Interswitch
branch) so PaymentService can discover it via IEnumerable<IPaymentGateway>.

In `@PayBridge.SDK/Gateways/InterswitchGateway.cs`:
- Around line 73-74: The code is logging full Interswitch response bodies (e.g.,
the _logger.LogDebug call that prints "Interswitch auth response: {Body}" and
the similar calls at the other sites), which can expose tokens and PII; update
the InterswitchGateway logging so it no longer emits raw response bodies—instead
parse the JSON/response in the methods where those LogDebug calls occur (in the
InterswitchGateway class), extract and log only safe fields such as status,
correlationId/transactionId, and HTTP status code, and redact or omit sensitive
fields (access_token, customer data, payment details) before any logging; apply
the same change to the other occurrences referenced (the similar
LogDebug/LogInformation calls) and ensure any exception-path logs also avoid
printing full response payloads.
- Around line 116-117: In InterswitchGateway replace the unsafe truncating cast
for request.Amount -> amount (currently "(int)(request.Amount * 100)") with a
rounded, checked conversion: compute the kobo value using rounding (e.g.
Math.Round or equivalent with MidpointRounding.AwayFromZero) and then cast
inside a checked context so overflow raises an exception you can handle; ensure
you catch/handle OverflowException and log/return a clear error rather than
sending a wrong amount to the gateway. Apply the same change to the other
occurrence that sets amount (the second block similar to currencyCode = "566")
so both conversions use rounded+checked conversion and proper error handling.
- Around line 43-44: The constructor for InterswitchGateway currently validates
MerchantCode but not PaymentItemCode, allowing runtime failures in
CreatePaymentAsync; add a guard in the InterswitchGateway constructor to
validate _config.Interswitch.PaymentItemCode (same pattern as the existing
MerchantCode check) and throw an InvalidOperationException with a clear message
like "Interswitch PaymentItemCode is required" so misconfiguration is caught at
startup rather than at CreatePaymentAsync when payableCode is sent.
🪄 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: b5f55c72-1c94-48ac-ae4e-88d8837c0783

📥 Commits

Reviewing files that changed from the base of the PR and between fed2732 and 3c51bcc.

📒 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/InterswitchGateway.cs
  • PayBridge.SDK/Services/PaymentService.cs

services.AddScoped<SquadGateway>();
services.AddScoped<KorapayGateway>();
services.AddScoped<IPaymentGateway>(sp => sp.GetRequiredService<KorapayGateway>());
services.AddScoped<InterswitchGateway>();

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 | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== PaymentService dependency shape =="
rg -n -C2 'IEnumerable<IPaymentGateway>|ToDictionary\(g => g\.GatewayType\)' PayBridge.SDK/Services/PaymentService.cs

echo
echo "== Interswitch registrations =="
rg -n -C2 'InterswitchGateway|PaymentGatewayType\.Interswitch|IPaymentGateway' \
  PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs \
  PayBridge.SDK/Factories/PaymentGatewayFactory.cs

Repository: teesofttech/PayBridge

Length of output: 6282


Fix InterswitchGateway registration so PaymentService can resolve it via IEnumerable<IPaymentGateway>

PaymentService injects IEnumerable<IPaymentGateway> and builds _gateways with gateways.ToDictionary(g => g.GatewayType), but InterswitchGateway is only registered as the concrete type (services.AddScoped<InterswitchGateway>();) in both the default path (line 140) and case PaymentGatewayType.Interswitch (178-180). Unlike other gateways, it is not also registered as IPaymentGateway, so PaymentGatewayType.Interswitch won’t be present in PaymentService’s gateway map.

Suggested fix
 services.AddScoped<InterswitchGateway>();
+services.AddScoped<IPaymentGateway>(sp => sp.GetRequiredService<InterswitchGateway>());

...
 case PaymentGatewayType.Interswitch:
     services.AddScoped<InterswitchGateway>();
+    services.AddScoped<IPaymentGateway>(sp => sp.GetRequiredService<InterswitchGateway>());
     break;
🤖 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/Externsions/IServiceCollectionExtensions.cs` at line 140,
InterswitchGateway is only registered as the concrete type
(services.AddScoped<InterswitchGateway>), so PaymentService (which resolves
IEnumerable<IPaymentGateway>) never receives it; change the registration to
register it as IPaymentGateway as well (e.g., register InterswitchGateway with
the IPaymentGateway service) in the same places where InterswitchGateway is
currently added (the default registration at
services.AddScoped<InterswitchGateway>() and the PaymentGatewayType.Interswitch
branch) so PaymentService can discover it via IEnumerable<IPaymentGateway>.

Comment on lines +43 to +44
if (string.IsNullOrEmpty(_config.Interswitch.MerchantCode))
throw new InvalidOperationException("Interswitch MerchantCode is required");

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

Validate PaymentItemCode in constructor like other required credentials.

CreatePaymentAsync always sends payableCode; allowing empty config here shifts a deterministic misconfiguration into runtime API failures.

Suggested fix
 if (string.IsNullOrEmpty(_config.Interswitch.MerchantCode))
     throw new InvalidOperationException("Interswitch MerchantCode is required");

+if (string.IsNullOrEmpty(_config.Interswitch.PaymentItemCode))
+    throw new InvalidOperationException("Interswitch PaymentItemCode is required");
📝 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
if (string.IsNullOrEmpty(_config.Interswitch.MerchantCode))
throw new InvalidOperationException("Interswitch MerchantCode is required");
if (string.IsNullOrEmpty(_config.Interswitch.MerchantCode))
throw new InvalidOperationException("Interswitch MerchantCode is required");
if (string.IsNullOrEmpty(_config.Interswitch.PaymentItemCode))
throw new InvalidOperationException("Interswitch PaymentItemCode is required");
🤖 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/InterswitchGateway.cs` around lines 43 - 44, The
constructor for InterswitchGateway currently validates MerchantCode but not
PaymentItemCode, allowing runtime failures in CreatePaymentAsync; add a guard in
the InterswitchGateway constructor to validate
_config.Interswitch.PaymentItemCode (same pattern as the existing MerchantCode
check) and throw an InvalidOperationException with a clear message like
"Interswitch PaymentItemCode is required" so misconfiguration is caught at
startup rather than at CreatePaymentAsync when payableCode is sent.

Comment on lines +73 to +74
_logger.LogDebug("Interswitch auth response: {Body}", body);

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

Avoid logging raw Interswitch response bodies.

These logs can expose access tokens, customer data, and transaction metadata. Log status/correlation fields instead.

Suggested hardening
- _logger.LogDebug("Interswitch auth response: {Body}", body);
+ _logger.LogDebug("Interswitch auth response received. StatusCode: {StatusCode}", (int)response.StatusCode);

- _logger.LogDebug("Interswitch create payment response: {Response}", responseBody);
+ _logger.LogDebug("Interswitch create payment response received. StatusCode: {StatusCode}", (int)response.StatusCode);

- _logger.LogDebug("Interswitch verify response: {Response}", responseBody);
+ _logger.LogDebug("Interswitch verify response received. StatusCode: {StatusCode}", (int)response.StatusCode);

- _logger.LogDebug("Interswitch refund response: {Response}", responseBody);
+ _logger.LogDebug("Interswitch refund response received. StatusCode: {StatusCode}", (int)response.StatusCode);

Also applies to: 134-135, 189-190, 277-278

🤖 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/InterswitchGateway.cs` around lines 73 - 74, The code
is logging full Interswitch response bodies (e.g., the _logger.LogDebug call
that prints "Interswitch auth response: {Body}" and the similar calls at the
other sites), which can expose tokens and PII; update the InterswitchGateway
logging so it no longer emits raw response bodies—instead parse the
JSON/response in the methods where those LogDebug calls occur (in the
InterswitchGateway class), extract and log only safe fields such as status,
correlationId/transactionId, and HTTP status code, and redact or omit sensitive
fields (access_token, customer data, payment details) before any logging; apply
the same change to the other occurrences referenced (the similar
LogDebug/LogInformation calls) and ensure any exception-path logs also avoid
printing full response payloads.

Comment on lines +116 to +117
amount = (int)(request.Amount * 100), // in kobo
currencyCode = "566", // ISO 4217 numeric code for NGN

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

Use rounded checked conversion for kobo amounts.

Direct (int)(amount * 100) truncates fractional values and can overflow. That can send wrong amounts to the gateway.

Suggested fix
+ private static long ToKobo(decimal amount) =>
+     checked((long)Math.Round(amount * 100m, MidpointRounding.AwayFromZero));

...
- amount = (int)(request.Amount * 100), // in kobo
+ amount = ToKobo(request.Amount), // in kobo

...
- amount = (int)(request.Amount * 100),
+ amount = ToKobo(request.Amount),

Also applies to: 261-262

🤖 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/InterswitchGateway.cs` around lines 116 - 117, In
InterswitchGateway replace the unsafe truncating cast for request.Amount ->
amount (currently "(int)(request.Amount * 100)") with a rounded, checked
conversion: compute the kobo value using rounding (e.g. Math.Round or equivalent
with MidpointRounding.AwayFromZero) and then cast inside a checked context so
overflow raises an exception you can handle; ensure you catch/handle
OverflowException and log/return a clear error rather than sending a wrong
amount to the gateway. Apply the same change to the other occurrence that sets
amount (the second block similar to currencyCode = "566") so both conversions
use rounded+checked conversion and proper error handling.

teesofttech and others added 4 commits June 4, 2026 11:51
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
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 InterswitchGateway (Nigeria - Quickteller / Webpay)

2 participants