Integrating a payment gateway into an application often means dealing with HTTP requests, authentication, JSON serialization, API response formats, and transaction verification.

For .NET developers integrating with PayChangu, I wanted to make that experience a little easier.

That is why I built PayChangu.NET — an open-source .NET SDK that provides a strongly typed, asynchronous interface for interacting with the PayChangu payment gateway.

Instead of writing HTTP requests and manually handling API responses for every payment operation, you can use familiar C# classes, methods, and dependency injection.

In this article, I'll walk through what PayChangu.NET offers, how to install and configure it, and how to use it to integrate different PayChangu payment flows into a .NET application.

What is PayChangu.NET?

PayChangu.NET is an open-source .NET SDK for the PayChangu payment gateway.

It is designed to simplify communication with PayChangu's APIs by providing:

  • Strongly typed request and response models.
  • Asynchronous API operations.
  • Built-in dependency injection support.
  • HTTP client management through IHttpClientFactory.
  • Bearer token authentication.
  • Standardized API response handling.
  • Transaction verification across supported payment flows.
  • Webhook signature validation and event parsing.
  • Cancellation token support.
  • Access to the original JSON response when needed.

The SDK targets .NET Standard 2.0, making it suitable for integration into a range of .NET applications.

It is released under the MIT License.

GitHub repository: https://github.com/msiskasmith/PayChangu.NET

What can you do with it?

PayChangu.NET covers several payment gateway operations.

Feature Description
Standard Checkout Redirect customers to PayChangu's hosted checkout page.
Mobile Money Direct Charge Send a payment request to a customer's mobile phone.
Bank Transfer Direct Charge Generate bank account details for a customer to make a payment.
Card Direct Charge Initiate card payments, including 3-D Secure authentication.
Transaction Verification Verify payments and retrieve normalized transaction information.
Mobile Money Payout Initiate payouts to mobile money recipients.
Bank Payout Initiate bank payouts and retrieve payout details.
Wallet Balance Retrieve wallet balances.
Reference Data Retrieve supported mobile money operators, banks, and payout providers.
Webhooks Validate webhook signatures and parse incoming PayChangu events.

The SDK currently exposes the following currencies through PayChanguCurrency:

  • MWK — Malawian Kwacha
  • USD — United States Dollar
  • ZMW — Zambian Kwacha

1. Installing PayChangu.NET

The SDK is available as a NuGet package.

You can install it using the .NET CLI:

dotnet add package PayChangu.NET 

Alternatively, search for PayChangu.NET in the NuGet Package Manager in Visual Studio.

Once installed, you're ready to configure the SDK.

2. Configuring the SDK

PayChangu.NET provides an AddPayChangu extension method for registering the client with your application's dependency injection container.

In an ASP.NET Core application, open Program.cs and register the SDK:

using PayChangu.NET.Extensions; builder.Services.AddPayChangu(options => { options.SecretKey = builder.Configuration["PayChangu:SecretKey"]!; }); 

The SDK requires your PayChangu secret key for API authentication.

The default API base URL is:

https://api.paychangu.com/ 

You can override it through the options if required:

builder.Services.AddPayChangu(options => { options.SecretKey = builder.Configuration["PayChangu:SecretKey"]!; options.BaseUrl = "https://api.paychangu.com/"; }); 

If you intend to receive and validate webhooks, configure your webhook secret as well:

builder.Services.AddPayChangu(options => { options.SecretKey = builder.Configuration["PayChangu:SecretKey"]!; options.WebhookSecret = builder.Configuration["PayChangu:WebhookSecret"]; }); 

Keep your credentials secure

Avoid hardcoding your API secret key in source code or committing it to your repository.

For local development, you can use ASP.NET Core User Secrets:

dotnet user-secrets init dotnet user-secrets set "PayChangu:SecretKey" "YOUR_SECRET_KEY" dotnet user-secrets set "PayChangu:WebhookSecret" "YOUR_WEBHOOK_SECRET" 

For production, use environment variables or an appropriate secrets management service.

Dependency injection

Once registered, inject IPayChanguClient wherever you need to interact with PayChangu.

For example:

public class PaymentService { private readonly IPayChanguClient _payChanguClient; public PaymentService(IPayChanguClient payChanguClient) { _payChanguClient = payChanguClient; } } 

The SDK organizes its operations into logical groups:

_payChanguClient.Checkout; _payChanguClient.DirectCharges; _payChanguClient.Transactions; _payChanguClient.Payouts; _payChanguClient.Wallet; _payChanguClient.ReferenceData; 

This makes it easier to discover the available operations without having to work with a large collection of unrelated methods.

3. Understanding API responses

Before initiating a payment, it is worth understanding how PayChangu.NET handles API responses.

The SDK uses PayChanguApiResponse<T> as a common response wrapper.

For example:

var response = await _payChanguClient.Wallet.GetBalanceAsync(); if (!response.Success) { Console.WriteLine(response.ErrorMessage); return; } var balance = response.Result; 

The response wrapper exposes:

  • Success — indicates whether the SDK successfully completed the API request and deserialized the response.
  • StatusCode — the HTTP status code returned by the API.
  • ErrorMessage — information about an unsuccessful API request.
  • Result — the strongly typed response returned by the operation.
  • RawJsonResponse — the original JSON response body.

There is an important distinction here.

PayChanguApiResponse.Success does not mean that a payment transaction succeeded.

It means the API operation completed successfully from the SDK's perspective. A payment can still be pending or failed even when the verification request itself succeeds.

We'll see why this matters when we get to transaction verification.

4. Standard Checkout

Standard Checkout is one of the simplest ways to accept payments through PayChangu.

The customer is redirected to a PayChangu-hosted checkout page, where they can complete the payment using an available payment method.

To initiate a checkout session, create an InitiateCheckoutRequest.

using PayChangu.NET; using PayChangu.NET.Models; var request = new InitiateCheckoutRequest { Amount = 5000m, Currency = PayChanguCurrency.MWK, Email = "customer@example.com", FirstName = "John", LastName = "Banda", TransactionReference = Guid.NewGuid().ToString(), CallbackUrl = "https://example.com/payments/callback", ReturnUrl = "https://example.com/payment/return", Customization = new PaymentCustomization { Title = "Order Payment", Description = "Payment for order #12345" }, Meta = new PaymentMeta { Id = Guid.NewGuid(), Response = "order-12345" } }; var response = await _payChanguClient.Checkout.InitiateAsync( request, cancellationToken); 

If the request is successful, PayChangu returns a checkout URL.

if (!response.Success || response.Result?.Data == null) { // Handle the unsuccessful API response. return; } var checkoutUrl = response.Result.Data.CheckoutUrl; 

You can then redirect the customer to that URL:

return Redirect(checkoutUrl); 

The customer completes the payment on PayChangu's hosted checkout page and is subsequently redirected according to the URLs configured in the request.

Callback URL versus Return URL

These two URLs serve different purposes:

  • CallbackUrl — receives PayChangu's callback for successful transactions.
  • ReturnUrl — brings the customer back to your application when checkout is canceled or fails.

Your webhook endpoint is configured separately in the PayChangu dashboard.

Do not rely solely on a customer redirect or callback to fulfil an order. Verify the transaction before treating it as paid.

5. Mobile Money Direct Charge

What if you don't want to redirect customers to a hosted checkout page?

PayChangu also supports Direct Charge, which allows an application to initiate a payment request directly.

For Mobile Money, the customer receives a payment prompt on their phone and must authorize the transaction.

Step 1: Retrieve supported Mobile Money operators

Before initiating a Mobile Money Direct Charge, retrieve the supported operators.

var operatorsResponse = await _payChanguClient.ReferenceData.MobileMoneyOperators.GetAllAsync( cancellationToken); if (!operatorsResponse.Success || operatorsResponse.Result == null) { // Handle the unsuccessful API response. return; } var operators = operatorsResponse.Result.Data; 

Each operator provides a reference ID that is required when initiating a charge.

For example, you can select an operator:

var operatorRefId = operators .First(x => x.Name == "Airtel Money") .RefId; 

Step 2: Initiate the charge

Create an InitiateMobileMoneyDirectChargeRequest:

var request = new InitiateMobileMoneyDirectChargeRequest { Mobile = "+265991234567", MobileMoneyOperatorRefId = operatorRefId, Amount = 5000m, ChargeId = Guid.NewGuid().ToString(), Email = "customer@example.com", FirstName = "John", LastName = "Banda" }; var response = await _payChanguClient.DirectCharges.MobileMoney.InitiateAsync( request, cancellationToken); 

Handle the response:

if (!response.Success || response.Result?.Data == null) { // Handle the unsuccessful API response. return; } var chargeId = response.Result.Data.ChargeId; 

At this point, the charge request has been accepted by PayChangu.

It does not mean the customer has completed the payment.

The customer still needs to authorize the request on their mobile phone.

Step 3: Verify the transaction

Once the customer has authorized the payment, verify the transaction.

var verificationResponse = await _payChanguClient.Transactions.VerifyAsync( chargeId, PayChanguPaymentFlow.DirectCharge, PayChanguPaymentMethod.MobileMoney, cancellationToken); 

Check the response:

if (!verificationResponse.Success || verificationResponse.Result == null) { // The verification API request was unsuccessful. return; } var isValid = verificationResponse.Result.IsValid( chargeId, "MWK", 5000m); if (!isValid) { // The payment is not successful or its details do not match. return; } // The transaction has been verified successfully. 

The IsValid extension method checks:

  • Whether the transaction status is success.
  • Whether the transaction identifier matches.
  • Whether the currency matches.
  • Whether the amount matches.

This gives you a convenient way to validate the transaction before fulfilling an order.

6. Bank Transfer Direct Charge

PayChangu.NET also supports Bank Transfer Direct Charge.

Instead of redirecting the customer to a hosted checkout page or sending a Mobile Money prompt, this flow generates bank account details that the customer can use to make a payment.

Initiate the charge using InitiateBankTransferDirectChargeRequest:

var chargeId = Guid.NewGuid().ToString(); var request = new InitiateBankTransferDirectChargeRequest { Amount = 5000m, Currency = PayChanguCurrency.MWK, ChargeId = chargeId, Email = "customer@example.com", FirstName = "John", LastName = "Banda", Mobile = "+265991234567", CreatePermanentAccount = false }; var response = await _payChanguClient.DirectCharges.BankTransfer.InitiateAsync( request, cancellationToken); 

If successful, the response contains the generated payment account details:

if (!response.Success || response.Result?.Data == null) { // Handle the unsuccessful API response. return; } var paymentDetails = response.Result.Data.PaymentAccountDetails; var bankName = paymentDetails.BankName; var accountNumber = paymentDetails.AccountNumber; var accountName = paymentDetails.AccountName; 

You can display these details to the customer so they can complete the bank transfer.

As with Mobile Money, generating the account details does not mean the payment has been completed.

You should verify the transaction:

var verificationResponse = await _payChanguClient.Transactions.VerifyAsync( chargeId, PayChanguPaymentFlow.DirectCharge, PayChanguPaymentMethod.BankTransfer, cancellationToken); 

Validate the response against the expected transaction details before fulfilling the order.

7. Card Direct Charge

PayChangu.NET also supports Card Direct Charge, including transactions that require 3-D Secure authentication.

The SDK exposes this through:

_payChanguClient.DirectCharges.Card 

You can initiate a card charge using InitiateCardDirectChargeRequest and InitiateAsync.

Card payments can involve an additional authentication step. When PayChangu indicates that 3-D Secure authentication is required, the application should redirect the customer to the supplied authentication link and allow them to complete the process.

After the authentication process, verify the transaction using:

await _payChanguClient.Transactions.VerifyAsync( chargeId, PayChanguPaymentFlow.DirectCharge, PayChanguPaymentMethod.Card, cancellationToken); 

The SDK also provides support for card transaction refunds.

Security note: Card details should be handled in accordance with your payment provider's requirements and applicable PCI DSS obligations. Avoid logging or storing sensitive card information.

8. Transaction statuses

When working with payment gateways, it is important to distinguish between the result of an API request and the actual status of a transaction.

PayChangu.NET provides the PayChanguTransactionStatus constants:

Constant Value Meaning
Pending pending The transaction is awaiting completion.
Success success The transaction completed successfully.
Failed failed The transaction failed.
Refunded refunded The transaction was refunded.

You can use these constants when handling a verified transaction:

using PayChangu.NET.Constants; if (verificationResponse.Result.Status == PayChanguTransactionStatus.Success) { // Fulfil the order. } else if (verificationResponse.Result.Status == PayChanguTransactionStatus.Pending) { // Wait for the transaction to complete. } else if (verificationResponse.Result.Status == PayChanguTransactionStatus.Failed) { // Handle the failed transaction. } else if (verificationResponse.Result.Status == PayChanguTransactionStatus.Refunded) { // Handle the refunded transaction. } 

Remember:

  • response.Success tells you whether the SDK successfully completed the API operation.
  • response.Result.Status tells you the transaction outcome.

A verification request can therefore return Success = true while the transaction status is failed or pending. That is not contradictory: the API request succeeded in retrieving the transaction result.

9. Webhooks

Polling an API to check transaction status is not always ideal.

Webhooks allow PayChangu to notify your application when transaction events occur.

PayChangu.NET provides IPayChanguWebhookService, which handles webhook signature validation and parsing.

The SDK supports webhook events from:

Event type Payment flow
checkout.payment Standard Checkout
api.charge.payment Direct Charge
api.payout Payout

Configure the webhook secret

When registering the SDK, provide your webhook secret:

builder.Services.AddPayChangu(options => { options.SecretKey = builder.Configuration["PayChangu:SecretKey"]!; options.WebhookSecret = builder.Configuration["PayChangu:WebhookSecret"]; }); 

Create a webhook endpoint

The following example uses an ASP.NET Core Minimal API endpoint:

using System.Text; using System.Text.Json; using PayChangu.NET; app.MapPost("/v1/webhooks/paychangu", async ( HttpRequest request, IPayChanguWebhookService webhookService, CancellationToken cancellationToken) => { if (!request.Headers.TryGetValue("Signature", out var signature)) { return Results.Unauthorized(); } request.EnableBuffering(); using var reader = new StreamReader( request.Body, Encoding.UTF8, leaveOpen: true); var payload = await reader.ReadToEndAsync(cancellationToken); request.Body.Position = 0; if (!webhookService.VerifySignature( payload, signature.ToString())) { return Results.Unauthorized(); } PayChanguWebhookEvent webhookEvent; try { webhookEvent = webhookService.ParseWebhook(payload); } catch (ArgumentException) { return Results.BadRequest(); } catch (JsonException) { return Results.BadRequest(); } // Process the verified webhook event. return Results.Ok(); }); 

The SDK takes care of the signature verification logic, so you don't need to implement the HMAC signature validation yourself.

Working with webhook events

The parsed PayChanguWebhookEvent exposes useful information, including:

  • Event type.
  • Transaction amount and currency.
  • Transaction status.
  • Payment flow.
  • Payment method.
  • Transaction identifier.
  • Customer or recipient information.

You can determine the payment flow using the derived PaymentFlow property:

switch (webhookEvent.PaymentFlow) { case PayChanguPaymentFlow.Checkout: case PayChanguPaymentFlow.DirectCharge: var transactionIdentifier = webhookEvent.TransactionIdentifier; var paymentMethod = webhookEvent.PaymentMethod; // Handle or verify the payment. break; case PayChanguPaymentFlow.Payout: // Handle the payout notification. break; default: // Handle an unrecognized event. break; } 

One useful feature is TransactionIdentifier. It returns the appropriate identifier depending on the payment flow:

  • Standard Checkout uses the merchant transaction reference (tx_ref).
  • Direct Charge uses the PayChangu charge identifier (charge_id).
  • Payout events expose their charge identifier for payout processing.

Webhook signature validation confirms the authenticity of the payload. Your application must still handle transaction statuses correctly and avoid fulfilling orders based on pending notifications.

10. Payouts and wallet operations

Beyond collecting payments, PayChangu.NET also exposes operations for sending payouts and retrieving wallet information.

Mobile Money payouts

You can initiate Mobile Money payouts through:

_payChanguClient.Payouts.MobileMoney 

The SDK provides InitiateAsync for submitting a payout request.

Bank payouts

Bank payouts are available through:

_payChanguClient.Payouts.Bank 

The SDK also supports retrieving individual bank payout details and paginated bank payout transactions.

Wallet balance

To retrieve your wallet balance:

var response = await _payChanguClient.Wallet.GetBalanceAsync( cancellationToken: cancellationToken); if Source:https://dev.to/msiskasmith/integrating-paychangu-payments-into-net-applications-with-paychangunet-3i5a

Related posts