feat: implement PeachPaymentsGateway (South Africa, Kenya, Nigeria)#35
Conversation
- Add PeachPayments = 15 to PaymentGatewayType enum - Add PeachPaymentsConfig (EntityId, AccessToken, IsSandbox) - Implement PeachPaymentsGateway: checkout initiation, payment verification, refund - Register gateway in IServiceCollectionExtensions and PaymentGatewayFactory - Add ZAR/KES/NGN/BWP routing to PeachPayments in PaymentService - Add PEACH_ prefix detection for gateway resolution
📝 WalkthroughWalkthroughThis PR adds Peach Payments as a new payment gateway. It introduces configuration, enum entry, DI/factory registration, a complete gateway implementation with payment creation/verification/refund flows, and service-level routing for supported currencies (ZAR, KES, NGN, BWP) with transaction reference prefix recognition. ChangesPeach Payments Gateway Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
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 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
Implements a new Peach Payments payment gateway in the SDK and wires it into the existing gateway factory/DI registration and automatic routing logic.
Changes:
- Added
PeachPaymentstoPaymentGatewayTypeand introducedPeachPaymentsConfiginPaymentGatewayConfig. - Implemented
PeachPaymentsGateway(checkout creation, verification, refund) and registered it in DI +PaymentGatewayFactory. - Updated currency-based routing and reference-prefix detection to select Peach Payments.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| PayBridge.SDK/Services/PaymentService.cs | Updates currency routing and reference-prefix detection to include Peach Payments. |
| PayBridge.SDK/Gateways/PeachPaymentsGateway.cs | New gateway implementation for checkout, verification, and refunds. |
| PayBridge.SDK/Factories/PaymentGatewayFactory.cs | Adds Peach Payments to factory initialization and service resolution. |
| PayBridge.SDK/Externsions/IServiceCollectionExtensions.cs | Registers PeachPaymentsGateway with DI for enabled/all gateways paths. |
| PayBridge.SDK/Enums/PaymentGatewayType.cs | Adds PeachPayments = 15 enum member. |
| PayBridge.SDK/Dtos/PaymentGatewayConfig.cs | Adds PeachPaymentsConfig with EntityId, AccessToken, and IsSandbox. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case "XOF": | ||
| case "XAF": | ||
| case "MWK": | ||
| return ChooseAvailableGateway(PaymentGatewayType.PawaPay, PaymentGatewayType.DpoGroup, PaymentGatewayType.Flutterwave, PaymentGatewayType.Paystack); | ||
| return ChooseAvailableGateway(PaymentGatewayType.PeachPayments, PaymentGatewayType.PawaPay, PaymentGatewayType.DpoGroup, PaymentGatewayType.Flutterwave, PaymentGatewayType.Paystack); | ||
|
|
| var json = JsonDocument.Parse(body).RootElement; | ||
| var payment = json.ValueKind == JsonValueKind.Array ? json[0] : json; | ||
|
|
| ["entityId"] = _config.PeachPayments.EntityId, | ||
| ["amount"] = request.Amount.ToString("F2", System.Globalization.CultureInfo.InvariantCulture), | ||
| ["currency"] = "ZAR", | ||
| ["paymentType"] = "RF", |
| var response = await _httpClient.PostAsync($"{BaseUrl}/payments/{request.TransactionReference}", content); | ||
| var body = await response.Content.ReadAsStringAsync(); | ||
| _logger.LogDebug("PeachPayments refund response: {Body}", body); | ||
|
|
||
| var json = JsonDocument.Parse(body).RootElement; |
| /// <summary> | ||
| /// Peach Payments gateway (South Africa, Kenya, Nigeria) | ||
| /// </summary> |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
PayBridge.SDK/Gateways/PeachPaymentsGateway.cs (2)
30-49: ⚡ Quick winConsider more targeted header management in
SetAuthHeader.Line 46 clears all default request headers before setting the Authorization header. If any other headers (e.g.,
Accept,User-Agent) were previously set on this HttpClient instance, they will be removed. Since the HttpClient is created per gateway instance viaIHttpClientFactory, this is likely safe, but consider only clearing or overwriting theAuthorizationheader specifically to avoid unintended side effects.♻️ Proposed refinement
private void SetAuthHeader() { - _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _config.PeachPayments.AccessToken); }🤖 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/PeachPaymentsGateway.cs` around lines 30 - 49, The SetAuthHeader method currently calls _httpClient.DefaultRequestHeaders.Clear(), which removes all pre-set headers; instead, only remove or overwrite the Authorization header to preserve other headers like Accept/User-Agent: use _httpClient.DefaultRequestHeaders.Remove("Authorization") (or check for existing Authorization values) and then set _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(...). Update the SetAuthHeader method to stop calling Clear() and only manipulate the Authorization header while keeping other DefaultRequestHeaders intact.
232-239: 💤 Low valueConsider explicit USD mapping or validation.
The class documentation (line 16) mentions USD as a supported currency, but
CurrencyToCountrydoesn't include an explicit mapping for it—USD falls through to the default"ZA". Verify whether Peach Payments acceptsbilling.country = "ZA"for USD transactions, or if USD should map to a different country code or omit the field entirely.🤖 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/PeachPaymentsGateway.cs` around lines 232 - 239, The CurrencyToCountry method currently maps currencies and defaults to "ZA", causing USD to incorrectly fall through; update CurrencyToCountry (and any callers that use its result) to explicitly handle "USD"—either map "USD" => "US" if Peach Payments expects a US country code, or return a sentinel (e.g., null/empty) so the caller can omit billing.country for USD and perform validation/validation failure handling. Ensure the change is made in the CurrencyToCountry method and adjust any call sites that consume its return value to handle the new case (omit field or throw a clear validation error) rather than silently using "ZA".
🤖 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/PeachPaymentsGateway.cs`:
- Around line 188-230: PeachPaymentsGateway.RefundPaymentAsync currently
hardcodes "ZAR" for currency which breaks multi-currency refunds; add a Currency
property to RefundRequest, update PaymentService.RefundPaymentAsync to load the
transaction currency and set request.Currency before calling the gateway, and
then change PeachPaymentsGateway.RefundPaymentAsync to use request.Currency
(falling back to a sensible default or throwing if null) when building formData
instead of the hardcoded "ZAR".
In `@PayBridge.SDK/Services/PaymentService.cs`:
- Line 265: PaymentService is currently routing XOF/XAF/MWK to
PaymentGatewayType.PeachPayments even though PeachPaymentsGateway only supports
ZAR/KES/NGN/BWP/USD; update the routing logic in PaymentService (the branch that
returns ChooseAvailableGateway(...)) to exclude PaymentGatewayType.PeachPayments
for currencies XOF, XAF, and MWK by removing it from the gateway list for those
currencies, and simultaneously verify the NGN branch near the existing NGN
routing logic still includes PaymentGatewayType.PeachPayments in its
ChooseAvailableGateway call so Nigeria uses PeachPayments; reference
PeachPaymentsGateway.CurrencyToCountry, PaymentGatewayType.PeachPayments, and
the ChooseAvailableGateway call when making these adjustments.
---
Nitpick comments:
In `@PayBridge.SDK/Gateways/PeachPaymentsGateway.cs`:
- Around line 30-49: The SetAuthHeader method currently calls
_httpClient.DefaultRequestHeaders.Clear(), which removes all pre-set headers;
instead, only remove or overwrite the Authorization header to preserve other
headers like Accept/User-Agent: use
_httpClient.DefaultRequestHeaders.Remove("Authorization") (or check for existing
Authorization values) and then set
_httpClient.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue(...). Update the SetAuthHeader method to stop calling
Clear() and only manipulate the Authorization header while keeping other
DefaultRequestHeaders intact.
- Around line 232-239: The CurrencyToCountry method currently maps currencies
and defaults to "ZA", causing USD to incorrectly fall through; update
CurrencyToCountry (and any callers that use its result) to explicitly handle
"USD"—either map "USD" => "US" if Peach Payments expects a US country code, or
return a sentinel (e.g., null/empty) so the caller can omit billing.country for
USD and perform validation/validation failure handling. Ensure the change is
made in the CurrencyToCountry method and adjust any call sites that consume its
return value to handle the new case (omit field or throw a clear validation
error) rather than silently using "ZA".
🪄 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: 21626191-6fa4-43e1-b01e-4a3dd04f7bb3
📒 Files selected for processing (6)
PayBridge.SDK/Dtos/PaymentGatewayConfig.csPayBridge.SDK/Enums/PaymentGatewayType.csPayBridge.SDK/Externsions/IServiceCollectionExtensions.csPayBridge.SDK/Factories/PaymentGatewayFactory.csPayBridge.SDK/Gateways/PeachPaymentsGateway.csPayBridge.SDK/Services/PaymentService.cs
| public async Task<RefundResponse> RefundPaymentAsync(RefundRequest request) | ||
| { | ||
| try | ||
| { | ||
| var formData = new Dictionary<string, string> | ||
| { | ||
| ["entityId"] = _config.PeachPayments.EntityId, | ||
| ["amount"] = request.Amount.ToString("F2", System.Globalization.CultureInfo.InvariantCulture), | ||
| ["currency"] = "ZAR", | ||
| ["paymentType"] = "RF", | ||
| }; | ||
|
|
||
| SetAuthHeader(); | ||
| var content = new FormUrlEncodedContent(formData); | ||
| var response = await _httpClient.PostAsync($"{BaseUrl}/payments/{request.TransactionReference}", content); | ||
| var body = await response.Content.ReadAsStringAsync(); | ||
| _logger.LogDebug("PeachPayments refund response: {Body}", body); | ||
|
|
||
| var json = JsonDocument.Parse(body).RootElement; | ||
| var resultCode = json.TryGetProperty("result", out var resultEl) && | ||
| resultEl.TryGetProperty("code", out var codeEl) | ||
| ? codeEl.GetString() : null; | ||
|
|
||
| bool success = resultCode != null && resultCode.StartsWith("000"); | ||
| var refundId = json.TryGetProperty("id", out var idEl) ? idEl.GetString() ?? string.Empty : string.Empty; | ||
|
|
||
| return new RefundResponse | ||
| { | ||
| Success = success, | ||
| RefundReference = refundId, | ||
| TransactionReference = request.TransactionReference, | ||
| Amount = request.Amount, | ||
| Status = success ? PaymentStatus.Refunded : PaymentStatus.Failed, | ||
| Message = resultEl.TryGetProperty("description", out var descEl) | ||
| ? descEl.GetString() ?? "Unknown" : "Unknown" | ||
| }; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Error processing PeachPayments refund: {Ref}", request.TransactionReference); | ||
| throw new PaymentGatewayException("Failed to process PeachPayments refund", ex); | ||
| } | ||
| } |
There was a problem hiding this comment.
Hardcoded refund currency breaks multi-currency support.
Line 196 hardcodes "ZAR" as the refund currency, but Peach Payments supports ZAR, KES, NGN, BWP, and USD according to the PR objectives and gateway documentation. Refunds for transactions in other currencies will fail or be rejected by the API.
The RefundRequest doesn't include a currency field, so you'll need to retrieve the original transaction's currency. The calling code in PaymentService.RefundPaymentAsync already loads the transaction from the repository (line 173 in PaymentService.cs), so the currency should be available there.
🐛 Recommended fix: pass currency through RefundRequest
Option 1: Add Currency property to RefundRequest and populate it in PaymentService:
In RefundRequest:
public class RefundRequest
{
public string TransactionReference { get; set; }
public decimal Amount { get; set; }
+ public string Currency { get; set; }
public string Reason { get; set; }
}In PaymentService.RefundPaymentAsync (around line 173):
+request.Currency = transaction.Currency;
var response = await _gateways[selectedGateway].RefundPaymentAsync(request);In PeachPaymentsGateway.RefundPaymentAsync:
var formData = new Dictionary<string, string>
{
["entityId"] = _config.PeachPayments.EntityId,
["amount"] = request.Amount.ToString("F2", System.Globalization.CultureInfo.InvariantCulture),
- ["currency"] = "ZAR",
+ ["currency"] = request.Currency ?? "ZAR",
["paymentType"] = "RF",
};🤖 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/PeachPaymentsGateway.cs` around lines 188 - 230,
PeachPaymentsGateway.RefundPaymentAsync currently hardcodes "ZAR" for currency
which breaks multi-currency refunds; add a Currency property to RefundRequest,
update PaymentService.RefundPaymentAsync to load the transaction currency and
set request.Currency before calling the gateway, and then change
PeachPaymentsGateway.RefundPaymentAsync to use request.Currency (falling back to
a sensible default or throwing if null) when building formData instead of the
hardcoded "ZAR".
| case "XAF": | ||
| case "MWK": | ||
| return ChooseAvailableGateway(PaymentGatewayType.PawaPay, PaymentGatewayType.DpoGroup, PaymentGatewayType.Flutterwave, PaymentGatewayType.Paystack); | ||
| return ChooseAvailableGateway(PaymentGatewayType.PeachPayments, PaymentGatewayType.PawaPay, PaymentGatewayType.DpoGroup, PaymentGatewayType.Flutterwave, PaymentGatewayType.Paystack); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for currency references in PeachPaymentsGateway and related docs
rg -n -C3 'ZAR|KES|NGN|BWP|XOF|XAF|MWK' --glob '*PeachPayments*'Repository: teesofttech/PayBridge
Length of output: 3517
Fix Peach Payments first-priority routing for unsupported currencies (XOF/XAF/MWK) + check NGN routing
PayBridge.SDK/Gateways/PeachPaymentsGateway.cs explicitly documents supported currencies as ZAR, KES, NGN, BWP, USD and maps only those currencies to countries (CurrencyToCountry has ZAR/KES/NGN/BWP, defaulting to ZA). It does not mention XOF/XAF/MWK, so routing those currencies to PaymentGatewayType.PeachPayments (first priority) appears inconsistent with the gateway’s own documentation.
Also re-check PayBridge.SDK/Services/PaymentService.cs around the NGN routing (line ~252) to ensure it includes PeachPayments as intended, since the existing logic may route NGN without PeachPayments despite Nigeria being in the gateway summary.
🤖 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 265, PaymentService is
currently routing XOF/XAF/MWK to PaymentGatewayType.PeachPayments even though
PeachPaymentsGateway only supports ZAR/KES/NGN/BWP/USD; update the routing logic
in PaymentService (the branch that returns ChooseAvailableGateway(...)) to
exclude PaymentGatewayType.PeachPayments for currencies XOF, XAF, and MWK by
removing it from the gateway list for those currencies, and simultaneously
verify the NGN branch near the existing NGN routing logic still includes
PaymentGatewayType.PeachPayments in its ChooseAvailableGateway call so Nigeria
uses PeachPayments; reference PeachPaymentsGateway.CurrencyToCountry,
PaymentGatewayType.PeachPayments, and the ChooseAvailableGateway call when
making these adjustments.
Closes #16
Summary
Implements Peach Payments gateway integration.
Gateway Details
PEACH_PeachPayments = 15Changes
PeachPayments = 15added toPaymentGatewayTypeenumPeachPaymentsConfigadded toPaymentGatewayConfigPeachPaymentsGatewayimplemented (checkout initiation, verification, refund)IServiceCollectionExtensionsandPaymentGatewayFactoryPEACH_→PaymentGatewayType.PeachPaymentsSummary by CodeRabbit
Release Notes