Skip to content

feat: implement SquadGateway (Nigeria - card, USSD, bank transfer, virtual accounts)#27

Merged
teesofttech merged 1 commit into
masterfrom
feature/issue-10-squad-gateway
Jun 4, 2026
Merged

feat: implement SquadGateway (Nigeria - card, USSD, bank transfer, virtual accounts)#27
teesofttech merged 1 commit into
masterfrom
feature/issue-10-squad-gateway

Conversation

@teesofttech

@teesofttech teesofttech commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the Squad payment gateway by GTCo for Nigeria NGN payments.

Changes

  • PaymentGatewayType.cs — added Squad = 7
  • PaymentGatewayConfig.cs — added SquadConfig with SecretKey, PublicKey, IsSandbox
  • Gateways/SquadGateway.cs — full implementation: create, verify, refund
  • IServiceCollectionExtensions.cs — registered SquadGateway in DI
  • PaymentGatewayFactory.cs — added Squad to factory switch + nullable fix
  • PaymentService.cs — NGN currency now prefers Squad first; added SQ_ prefix detection

How it works

  • Authentication: Bearer token (SecretKey) via Authorization header
  • Supports live and sandbox environments via IsSandbox flag
  • Amounts are in kobo (×100) for requests, converted back on responses
  • Create: POST /transaction/initiate
  • Verify: GET /transaction/verify/{ref}
  • Refund: POST /transaction/refund

Config (appsettings.json)

"PaymentGatewayConfig": {
  "Squad": {
    "SecretKey": "YOUR_SECRET_KEY",
    "PublicKey": "YOUR_PUBLIC_KEY",
    "IsSandbox": false
  }
}

Closes #10

Summary by CodeRabbit

  • New Features
    • Added Squad payment gateway for Nigerian Naira (NGN) transactions.
    • Squad supports payment initiation, verification, and refunds.
    • Squad is included as a preferred option for NGN alongside existing providers.
    • Transaction reference recognition updated to include Squad-prefixed references (e.g., SQ_).

Copilot AI review requested due to automatic review settings June 4, 2026 08:37
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0225503e-4546-4080-b33b-d46d39522102

📥 Commits

Reviewing files that changed from the base of the PR and between 3029442 and 12f4f65.

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

📝 Walkthrough

Walkthrough

Adds Squad (Nigeria) payment gateway: config + SquadConfig type, PaymentGatewayType enum entry, SquadGateway implementation (create/verify/refund), DI and factory registration, and PaymentService routing/selection updates for NGN and SQ_ references.

Changes

Squad Payment Gateway

Layer / File(s) Summary
Squad configuration and gateway type
PayBridge.SDK/Dtos/PaymentGatewayConfig.cs, PayBridge.SDK/Enums/PaymentGatewayType.cs
Adds PaymentGatewayConfig.Squad and new SquadConfig (SecretKey, PublicKey, IsSandbox). Extends PaymentGatewayType with Squad.
Squad gateway implementation
PayBridge.SDK/Gateways/SquadGateway.cs
Adds SquadGateway : IPaymentGateway with HTTP client setup, CreatePaymentAsync (initiates transaction, NGN→kobo, returns checkout_url), VerifyPaymentAsync (GET verify, map statuses, kobo→NGN, parse created_at), and RefundPaymentAsync (POST refund, parse refund_id). Exceptions are wrapped with contextual messages.
Gateway registration and factory
PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs, PayBridge.SDK/Factories/PaymentGatewayFactory.cs
Registers SquadGateway when all gateways are enabled or when explicitly listed; factory resolves SquadGateway from DI (nullable resolution preserved).
Payment service integration
PayBridge.SDK/Services/PaymentService.cs
SelectBestGateway includes Squad among NGN-preferred gateways. DetermineGatewayFromReference recognizes SQ_ references for Squad (and both FW_/FLW_ mapping for Flutterwave).

Sequence Diagram

sequenceDiagram
  participant Client
  participant SquadGateway
  participant SquadAPI as Squad API
  Client->>SquadGateway: CreatePaymentAsync(PaymentRequest)
  SquadGateway->>SquadAPI: POST /transaction/initiate (amount in kobo, metadata)
  SquadAPI-->>SquadGateway: {data.checkout_url}
  SquadGateway-->>Client: PaymentResponse(pending, checkout_url)
  Client->>SquadGateway: VerifyPaymentAsync(transactionReference)
  SquadGateway->>SquadAPI: GET /transaction/verify/{ref}
  SquadAPI-->>SquadGateway: {transaction_status, transaction_amount, transaction_charge, created_at}
  SquadGateway-->>Client: VerificationResponse(status, amounts, payment method)
  Client->>SquadGateway: RefundPaymentAsync(RefundRequest)
  SquadGateway->>SquadAPI: POST /transaction/refund (amount in kobo)
  SquadAPI-->>SquadGateway: {data.refund_id}
  SquadGateway-->>Client: RefundResponse(refunded, refund_id)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

  • teesofttech/PayBridge#26: Also updates PaymentService NGN gateway preferences and reference-prefix mapping; similar registration/factory pattern.

Poem

🐰 A rabbit hops in code tonight,

Squads of payments stitched just right,
From kobo counts to checkout flow,
Refunds and checks all set to go,
Hooray — the gateway's ready! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% 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 pull request title accurately summarizes the main change: implementing SquadGateway for Nigeria with support for multiple payment methods.
Linked Issues check ✅ Passed All coding requirements from issue #10 are implemented: SquadGateway class with create/verify/refund methods, Squad enum value, SquadConfig in PaymentGatewayConfig, DI registration, and NGN preference logic.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing Squad gateway functionality and integrating it into the payment system as specified in issue #10.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-10-squad-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

Implements the Squad (GTCo) payment gateway integration for Nigeria (NGN), wiring it into the SDK’s gateway selection, reference-prefix detection, DI registration, and gateway factory creation flow.

Changes:

  • Added a new SquadGateway implementing create/verify/refund flows against Squad endpoints.
  • Updated gateway selection/detection to prefer Squad for NGN and recognize SQ_ references (plus broadened Flutterwave prefix detection).
  • Extended config, enum, DI registration, and factory initialization to include Squad.

Reviewed changes

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

Show a summary per file
File Description
PayBridge.SDK/Services/PaymentService.cs Prefers Squad for NGN and detects SQ_ references (plus FLW_).
PayBridge.SDK/Gateways/SquadGateway.cs New gateway implementation for initiating, verifying, and refunding Squad transactions.
PayBridge.SDK/Factories/PaymentGatewayFactory.cs Adds Squad to gateway initialization and makes the resolved gateway nullable.
PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs Registers SquadGateway in DI (enabled/all-gateways paths).
PayBridge.SDK/Enums/PaymentGatewayType.cs Adds PaymentGatewayType.Squad = 7.
PayBridge.SDK/Dtos/PaymentGatewayConfig.cs Adds SquadConfig (SecretKey/PublicKey/IsSandbox).

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

Comment on lines +55 to +64
var txRef = $"SQ_{Guid.NewGuid():N}";

var squadRequest = new
{
email = request.CustomerEmail,
amount = (int)(request.Amount * 100), // Squad expects amount in kobo
initiate_type = "inline",
currency = "NGN",
transaction_ref = txRef,
customer_name = request.CustomerName,
Comment on lines +232 to +236
var errorMessage = root.TryGetProperty("message", out var msg)
? msg.GetString() ?? "Refund failed"
: "Refund request failed";

return new RefundResponse { Success = false, Message = errorMessage };
Comment on lines 129 to +135
services.AddScoped<StripeGateway>();
services.AddScoped<PaystackGateway>();
services.AddScoped<FlutterwaveGateway>();
services.AddScoped<CheckoutGateway>();
services.AddScoped<BenefitPayGateway>();
services.AddScoped<KnetGateway>();
services.AddScoped<SquadGateway>();
Comment on lines +162 to +164
case PaymentGatewayType.Squad:
services.AddScoped<SquadGateway>();
break;

@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 135: Register SquadGateway as an implementation of IPaymentGateway so it
is discoverable when PaymentService resolves IEnumerable<IPaymentGateway>;
update the registration in IServiceCollectionExtensions to add SquadGateway
under the IPaymentGateway service type (e.g., replace or add to the existing
services.AddScoped<SquadGateway>() with a registration that binds
IPaymentGateway to SquadGateway), and apply the same change for the other
occurrences referenced around the later block (lines showing similar
registrations) so PaymentService can auto-select Squad correctly.

In `@PayBridge.SDK/Gateways/SquadGateway.cs`:
- Line 60: Replace the unsafe direct cast of request.Amount to int with a safe
kobo conversion: add a helper method (e.g., ToKobo(decimal amount)) that rounds
using MidpointRounding.AwayFromZero and returns a checked long (to catch
overflow), then use ToKobo(request.Amount) wherever you currently cast (the
amount assignment at the line with "amount = (int)(request.Amount * 100)" and
the similar cast around line 202); ensure the payload/property types that
receive the value are updated to long if needed.
- Line 51: Redact sensitive data in SquadGateway logging: replace direct logging
of request.CustomerEmail in the _logger.LogInformation call with a non-PII
identifier (e.g., masked email or customer ID/hash) and stop logging full
provider responses; instead, in the methods that log provider responses (the
LogInformation/LogDebug calls around provider response handling), log only
non-sensitive metadata such as status codes, masked/hashed transaction IDs, and
a truncated/sanitized summary (no full payload or payment details). Locate the
_logger.LogInformation("Creating Squad payment for customer {Email}",
request.CustomerEmail) usage and any Log* calls that output provider responses
and update them to emit only non-PII fields as described.
- Around line 59-63: The Squad gateway is always sending currency "NGN" but
CreatePaymentAsync does not validate the incoming request currency; add a
boundary check in SquadGateway.CreatePaymentAsync to reject or fail fast if
request.Currency (or equivalent property) is not "NGN" (throw an
ArgumentException or return a failed Result/Response), and only proceed to build
the payload (including amount = (int)(request.Amount * 100)) when currency is
NGN; include a clear error message referencing Squad's NGN-only expectation so
callers know why the request was refused.
🪄 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: 02e1c55b-4872-4f5e-8768-8083bdb44c84

📥 Commits

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

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

services.AddScoped<CheckoutGateway>();
services.AddScoped<BenefitPayGateway>();
services.AddScoped<KnetGateway>();
services.AddScoped<SquadGateway>();

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

Register Squad under IPaymentGateway too, not only concrete type.

PaymentService resolves gateways from IEnumerable<IPaymentGateway> (PayBridge.SDK/Services/PaymentService.cs Line 23 and Line 31). With current registration, Squad is unavailable there, so Squad-specific calls fail as not configured and NGN auto-selection cannot actually pick Squad.

💡 Suggested fix
         if (config.EnabledGateways.Count == 0)
         {
             services.AddScoped<StripeGateway>();
             services.AddScoped<PaystackGateway>();
             services.AddScoped<FlutterwaveGateway>();
             services.AddScoped<CheckoutGateway>();
             services.AddScoped<BenefitPayGateway>();
             services.AddScoped<KnetGateway>();
             services.AddScoped<SquadGateway>();
+            services.AddScoped<IPaymentGateway>(sp => sp.GetRequiredService<SquadGateway>());
             return;
         }
@@
                 case PaymentGatewayType.Squad:
                     services.AddScoped<SquadGateway>();
+                    services.AddScoped<IPaymentGateway>(sp => sp.GetRequiredService<SquadGateway>());
                     break;

Also applies to: 162-164

🤖 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 135,
Register SquadGateway as an implementation of IPaymentGateway so it is
discoverable when PaymentService resolves IEnumerable<IPaymentGateway>; update
the registration in IServiceCollectionExtensions to add SquadGateway under the
IPaymentGateway service type (e.g., replace or add to the existing
services.AddScoped<SquadGateway>() with a registration that binds
IPaymentGateway to SquadGateway), and apply the same change for the other
occurrences referenced around the later block (lines showing similar
registrations) so PaymentService can auto-select Squad correctly.


public async Task<PaymentResponse> CreatePaymentAsync(PaymentRequest request)
{
_logger.LogInformation("Creating Squad payment for customer {Email}", request.CustomerEmail);

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

Reduce PII/sensitive payload logging in payment flows.

Line 51 logs customer email, and Lines 82/128/211 log full provider responses. These payloads can contain sensitive customer/payment data and should be redacted.

💡 Suggested fix
-        _logger.LogInformation("Creating Squad payment for customer {Email}", request.CustomerEmail);
+        _logger.LogInformation("Creating Squad payment request");

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

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

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

Also applies to: 82-82, 128-128, 211-211

🤖 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/SquadGateway.cs` at line 51, Redact sensitive data in
SquadGateway logging: replace direct logging of request.CustomerEmail in the
_logger.LogInformation call with a non-PII identifier (e.g., masked email or
customer ID/hash) and stop logging full provider responses; instead, in the
methods that log provider responses (the LogInformation/LogDebug calls around
provider response handling), log only non-sensitive metadata such as status
codes, masked/hashed transaction IDs, and a truncated/sanitized summary (no full
payload or payment details). Locate the _logger.LogInformation("Creating Squad
payment for customer {Email}", request.CustomerEmail) usage and any Log* calls
that output provider responses and update them to emit only non-PII fields as
described.

Comment on lines +59 to +63
email = request.CustomerEmail,
amount = (int)(request.Amount * 100), // Squad expects amount in kobo
initiate_type = "inline",
currency = "NGN",
transaction_ref = txRef,

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

Enforce NGN at the Squad gateway boundary.

CreatePaymentAsync always sends "NGN" to Squad but does not reject non-NGN requests. If Squad is explicitly selected with another currency, this can misstate transaction currency.

💡 Suggested fix
 public async Task<PaymentResponse> CreatePaymentAsync(PaymentRequest request)
 {
-    _logger.LogInformation("Creating Squad payment for customer {Email}", request.CustomerEmail);
+    if (!string.Equals(request.Currency, "NGN", StringComparison.OrdinalIgnoreCase))
+    {
+        return new PaymentResponse
+        {
+            Success = false,
+            Status = PaymentStatus.Failed,
+            Message = $"Squad only supports NGN. Received: {request.Currency}"
+        };
+    }

     try
     {
🤖 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/SquadGateway.cs` around lines 59 - 63, The Squad
gateway is always sending currency "NGN" but CreatePaymentAsync does not
validate the incoming request currency; add a boundary check in
SquadGateway.CreatePaymentAsync to reject or fail fast if request.Currency (or
equivalent property) is not "NGN" (throw an ArgumentException or return a failed
Result/Response), and only proceed to build the payload (including amount =
(int)(request.Amount * 100)) when currency is NGN; include a clear error message
referencing Squad's NGN-only expectation so callers know why the request was
refused.

var squadRequest = new
{
email = request.CustomerEmail,
amount = (int)(request.Amount * 100), // Squad expects amount in kobo

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 safe kobo conversion to avoid truncation/overflow.

Line 60 and Line 202 cast decimal amounts directly to int. That can truncate fractional values and overflow for larger amounts.

💡 Suggested fix
-                amount = (int)(request.Amount * 100), // Squad expects amount in kobo
+                amount = ToKobo(request.Amount), // Squad expects amount in kobo
@@
-                refund_amount = (int)(request.Amount * 100) // in kobo
+                refund_amount = ToKobo(request.Amount) // in kobo
private static long ToKobo(decimal amount)
{
    return checked((long)decimal.Round(amount * 100m, 0, MidpointRounding.AwayFromZero));
}

Also applies to: 202-202

🤖 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/SquadGateway.cs` at line 60, Replace the unsafe direct
cast of request.Amount to int with a safe kobo conversion: add a helper method
(e.g., ToKobo(decimal amount)) that rounds using MidpointRounding.AwayFromZero
and returns a checked long (to catch overflow), then use ToKobo(request.Amount)
wherever you currently cast (the amount assignment at the line with "amount =
(int)(request.Amount * 100)" and the similar cast around line 202); ensure the
payload/property types that receive the value are updated to long if needed.

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 SquadGateway (Nigeria - card, USSD, bank transfer, virtual accounts)

2 participants