Skip to content

Repository files navigation

PayCore — Payments Processing API

A production-patterned .NET 8 REST API demonstrating core fintech backend engineering concerns. Built as a public portfolio proxy for confidential payments infrastructure work delivered at Access Bank Plc.


What It Demonstrates

Concern Implementation
Idempotent transfers Caller-supplied key, 24hr expiry, DB unique index backstop
Async processing Hosted BackgroundService polls and processes pending transfers
Atomic debit/credit Single SaveChangesAsync under EF Core transaction
Cross-currency FX IExchangeRateService interface — stub in dev, swappable for real provider
Webhook delivery HMAC-SHA256 signed POSTs with exponential backoff retry
Audit trail Separate AuditDbContext — writes survive business transaction rollbacks
Error handling Single middleware, RFC 7807 Problem Details, named exception hierarchy
Crash recovery Worker resets stale Processing transfers on startup

Architecture

┌─────────────────────────────────────────────────────┐
│                    PayCore.Api                      │
│                                                     │
│  Features/                  Infrastructure/         │
│  ├── Accounts/              ├── Data/               │
│  │   ├── AccountsController │   ├── PayCoreDbContext │
│  │   └── AccountService     │   └── AuditDbContext   │
│  ├── Transfers/             ├── Workers/             │
│  │   ├── TransfersController│   ├── TransferProcessor│
│  │   ├── TransferService    │   └── WebhookDispatch  │
│  │   └── ExchangeRates/     ├── Audit/               │
│  └── Webhooks/              ├── Security/            │
│      ├── WebhooksController ├── Middleware/          │
│      └── WebhookService     └── Exceptions/         │
└─────────────────────────────────────────────────────┘

Two DbContexts — one key design decision: PayCoreDbContext owns business data and participates in transactions. AuditDbContext is never enlisted — audit records exist even when business operations fail or roll back.


Tech Stack

  • .NET 8, C#, ASP.NET Core Web API
  • Entity Framework Core 8 + MSSQL
  • FluentValidation
  • Serilog (structured logging)
  • XUnit + FluentAssertions + Moq
  • Swagger / OpenAPI

Getting Started

Prerequisites

  • .NET 8 SDK
  • SQL Server (LocalDB is fine for local dev)

Setup

git clone https://github.com/gravy17/paycore.git
cd paycore

# Restore packages
dotnet restore

# Apply migrations
dotnet ef database update --project src/PayCore.Api --context PayCoreDbContext
dotnet ef database update --project src/PayCore.Api --context AuditDbContext

# Run
dotnet run --project src/PayCore.Api

Swagger UI: https://localhost:{port}/swagger

Run Tests

dotnet test

Key API Endpoints

Method Route Description
POST /api/accounts Create account
GET /api/accounts/{id} Get account + balance
PATCH /api/accounts/{id} Deactivate account
GET /api/accounts/{id}/transfers List transfers (filterable)
POST /api/transfers Initiate transfer (async)
GET /api/transfers/{id} Get transfer status
POST /api/accounts/{id}/webhooks Register webhook
DELETE /api/accounts/{id}/webhooks/{wid} Deactivate webhook

Idempotency

All transfer requests require a caller-supplied idempotencyKey. Duplicate requests with the same key return the original transfer unchanged (200 OK). Keys expire after 24 hours — replays after expiry return 422.

This mirrors the behaviour of production payment processors (Stripe, Paystack).


Webhook Signature Verification

Outbound webhooks include an X-PayCore-Signature: sha256={hex} header. Receivers should verify this using HMAC-SHA256 with the shared secret:

var expectedSig = "sha256=" + 
    Convert.ToHexString(HMACSHA256.HashData(
        Encoding.UTF8.GetBytes(secret),
        Encoding.UTF8.GetBytes(payload)
    )).ToLowerInvariant();

var isValid = CryptographicOperations.FixedTimeEquals(
    Encoding.UTF8.GetBytes(expectedSig),
    Encoding.UTF8.GetBytes(receivedSignature));

Production Notes

This project intentionally omits authentication/authorisation to keep the focus on payments domain patterns. A production deployment would add:

  • JWT bearer auth (Azure AD B2C or similar)
  • Per-tenant account isolation
  • Real FX rate provider (e.g. Open Exchange Rates)
  • Outbox pattern for guaranteed webhook delivery
  • Azure Service Bus for transfer queue (replacing the polling worker)

About

A production-patterned payments API featuring account management, secure fund transfers with idempotency, webhooks, audit logging, and RFC 7807 error handling.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages