LibertePay.Core.Sdk 1.0.3

dotnet add package LibertePay.Core.Sdk --version 1.0.3
                    
NuGet\Install-Package LibertePay.Core.Sdk -Version 1.0.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LibertePay.Core.Sdk" Version="1.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LibertePay.Core.Sdk" Version="1.0.3" />
                    
Directory.Packages.props
<PackageReference Include="LibertePay.Core.Sdk" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LibertePay.Core.Sdk --version 1.0.3
                    
#r "nuget: LibertePay.Core.Sdk, 1.0.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=LibertePay.Core.Sdk&version=1.0.3
                    
Install LibertePay.Core.Sdk as a Cake Addin
#tool nuget:?package=LibertePay.Core.Sdk&version=1.0.3
                    
Install LibertePay.Core.Sdk as a Cake Tool

LibertePay .NET SDK

LibertePay .NET SDK provides a comprehensive and seamless payment integration solution for your .NET applications. This SDK simplifies interaction with LibertePay's robust payment processing services, offering a clean, intuitive API for all your payment needs.

✨ Features

Our SDK is designed to be a complete payment toolkit, offering a wide range of functionalities:

đŸ’ŗ Payment Processing

  • Mobile Money Collections: Easily collect payments directly from mobile money wallets.
  • Bank Account Transfers: Facilitate transfers to and from bank accounts.
  • Card Payments: (Coming soon) Prepare for upcoming support for card-based transactions.

💸 Disbursements

  • Mobile Money Wallets: Disburse funds directly to mobile money accounts.
  • Bank Accounts: Send payments to various bank accounts.
  • Bulk Payments: Efficiently handle large-scale disbursements for both Mobile Money and Bank Accounts.

🔄 Transaction Management

  • Status Tracking: Monitor the real-time status of your transactions.
  • Reversals: (Coming soon) Gain the ability to reverse transactions.
  • Reconciliation: (Coming soon) Streamline your financial reconciliation processes.

👤 Account Services

  • Name Enquiry: Verify account names for both bank accounts and mobile money numbers, reducing errors.
  • Balance Checking: Programmatically check your LibertePay account balance.
  • KYC Verification: (Coming soon) Integrate Know Your Customer verification directly into your application.

🚀 Advanced Features

  • Built-in Telemetry: Gain insights into SDK performance and usage.
  • High Performance: Optimized for speed and efficiency.
  • Thread-safe Token Management: Secure and reliable handling of authentication tokens.
  • Dependency Injection Support: Seamlessly integrate with modern .NET application architectures.
  • Type-safe Institution Codes: Ensure accuracy and reduce errors with strongly typed institution codes.
  • Automatic Callback Handling: Simplify the processing of asynchronous payment notifications.

đŸ’ģ Installation

Get started quickly by installing the LibertePay SDK via NuGet

âš™ī¸ Configuration

Basic Configuration

Add the LibertePay configuration details to your appsettings.json file. Replace the placeholder values with your actual credentials provided during your LibertePay setup.

{
  "LibertePay": {
    "BaseUrl": "https://app-base-url/", /* Provided during LibertePay setup */
    "Username": "your-username", /* Provided during LibertePay setup */
    "Password": "your-password", /* Provided during LibertePay setup */
    "HttpClientTimeoutSeconds": 30,
    "CallbackUrl": "https://your-domain.com/webhook", /* Provided during LibertePay setup */
    "TelemetryApiUrl": "" /* Provided during LibertePay setup */
  }
}

Service Registration

Register the LibertePay services in your Program.cs (for .NET 6+) or Startup.cs file using the extension method:

using LibertePay.Core.DependencyInjection;
using Microsoft.Extensions.DependencyInjection; // Required for AddLibertePay

// ... (other using statements)

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// ... (other service registrations)

builder.Services.AddLibertePay(builder.Configuration);

// ... (rest of your Program.cs/Startup.cs)

Error Handling

The SDK provides a consistent response format through the LibertePayResponse<T> class, simplifying error detection and handling:

  • IsSuccess: A boolean indicating if the operation was successful.
  • StatusCode: The API's status code for the operation.
  • StatusDesc: A human-readable description of the status.
  • Data: The actual response data when the operation is successful.
  • Message: An error message if the operation was unsuccessful.

Example Error Handling

using LibertePay.Core.Exceptions;

public async Task<SomeDataType> ProcessPayment(DebitMoneyRequest request)
{
    try
    {
        var response = await _client.Collections.DebitMoneyAsync(request);
        if (!response.IsSuccess)
        {
            _logger.LogError("Collection failed: {Message} - StatusCode: {StatusCode}", response.Message, response.StatusCode);
            throw new PaymentException(response.Message); // Throw a specific exception
        }

        return response.Data;
    }
    catch (PaymentException pEx) // Catch specific payment exceptions
    {
        _logger.LogError(pEx, "Payment operation failed: {Message}", pEx.Message);
        throw; // Re-throw or handle as appropriate for your application
    }
    catch (Exception ex) // Catch any other unexpected errors
    {
        _logger.LogError(ex, "An unexpected error occurred during collection");
        throw; // Re-throw or handle as appropriate
    }
}

🚀 Usage Guide

This section demonstrates common use cases of the LibertePay SDK.

1. Name Enquiry Services

Verify account holder names for both bank accounts and mobile money numbers.

Bank Account Name Enquiry (Single & Bulk)
using LibertePay.Core.Clients; // For ILibertePayClient
using LibertePay.Core.Requests; // For NameEnquiryRequest, BulkNameEnquiryRequest etc.
using LibertePay.Core.Constants; // For Channels, InstitutionCodes
using Microsoft.AspNetCore.Mvc; // For IActionResult, HttpGet
using Microsoft.Extensions.Logging; // For ILogger
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq; // For .Select()

[ApiController]
[Route("api/[controller]")]
public class PaymentServiceController : ControllerBase // Renamed for clarity as an API controller
{
    private readonly ILibertePayClient _client;
    private readonly ILogger<PaymentServiceController> _logger;

    public PaymentServiceController(ILibertePayClient client, ILogger<PaymentServiceController> logger)
    {
        _client = client;
        _logger = logger;
    }

    /// <summary>
    /// Verifies a single bank account name.
    /// </summary>
    /// <returns>The account name or an error message.</returns>
    [HttpGet("VerifyBankAccount")]
    public async Task<IActionResult> VerifyBankAccountAsync()
    {
        var request = new NameEnquiryRequest
        {
            AccountNumber = "1020820171412", // Example: Use a test account number provided by LibertePay
            PaymentChannel = Channels.INTERBANK,
            Institution = InstitutionCodes.TestGhanaLimited, /* This is a test institution code */
            TransactionId = Guid.NewGuid().ToString("N") // Unique transaction ID for tracking
        };

        var response = await _client.NameEnquiry.GetNameEnquiryAsync(request);
        if (response.IsSuccess)
        {
            _logger.LogInformation("Bank account name enquiry successful for {AccountNumber}: {AccountName}", request.AccountNumber, response.Data?.AccountName);
            return Ok(response.Data?.AccountName);
        }
        _logger.LogError("Bank account name enquiry failed for {AccountNumber}: {StatusDescription} - {Message}", request.AccountNumber, response.StatusDescription, response.Message);
        return BadRequest($"Name Enquiry Failed: {response.StatusDescription}. Details: {response.Message}");
    }

    /// <summary>
    /// Verifies multiple bank account names in a single bulk request.
    /// </summary>
    /// <returns>The batch transaction ID or an error message.</returns>
    [HttpGet("VerifyBulkBankAccount")] // Removed 'Async' suffix from route name for REST best practices
    public async Task<IActionResult> VerifyBulkBankAccountAsync()
    {
        var requestBulkNameCheck = new BulkNameEnquiryRequest
        {
            BatchTransactionId = Guid.NewGuid().ToString("N"), // Unique batch ID
            BulkNameEnquiryData =
            [
                new BulkNameEnquiryItem
                {
                    AccountNumber = "1020820171412",
                    Institution = InstitutionCodes.TestGhanaLimited
                },
                new BulkNameEnquiryItem
                {
                    AccountNumber = "1020820171413", // Another example account
                    Institution = InstitutionCodes.TestGhanaLimited
                }
            ]
        };

        var response = await _client.NameEnquiry.GetBulkNameEnquiryAsync(requestBulkNameCheck);

        if (response.IsSuccess)
        {
            _logger.LogInformation("Bulk bank account name enquiry successful. Batch ID: {BatchTransactionId}", response.Data?.BatchTransactionId);
            return Ok($"Bulk Name Enquiry Batch ID: {response.Data?.BatchTransactionId}");
        }
        _logger.LogError("Bulk bank account name enquiry failed: {StatusDescription} - {Message}", response.StatusDescription, response.Message);
        return BadRequest($"Bulk Name Enquiry Failed: {response.StatusDescription}. Details: {response.Message}");
    }
}
Mobile Money Name Enquiry (Single & Bulk)
// Inside PaymentServiceController class

/// <summary>
/// Verifies a single mobile money account name.
/// </summary>
/// <returns>The account name or an error message.</returns>
[HttpGet("VerifyMobileMoney")] // Removed 'Async' suffix from route name
public async Task<IActionResult> VerifyMobileMoneyAsync()
{
    var request = new NameEnquiryRequest
    {
        AccountNumber = "233246089019", // Example: Use a test phone number
        PaymentChannel = Channels.MNO, // Correct channel for mobile money
        Institution = InstitutionCodes.Mtn,
        TransactionId = Guid.NewGuid().ToString("N")
    };

    var response = await _client.NameEnquiry.GetNameEnquiryAsync(request);

    if (response.IsSuccess)
    {
        _logger.LogInformation("Mobile money name enquiry successful for {AccountNumber}: {AccountName}", request.AccountNumber, response.Data?.AccountName);
        return Ok(response.Data?.AccountName);
    }
    _logger.LogError("Mobile money name enquiry failed for {AccountNumber}: {StatusDescription} - {Message}", request.AccountNumber, response.StatusDescription, response.Message);
    return BadRequest($"Name Enquiry Failed: {response.StatusDescription}. Details: {response.Message}");
}

/// <summary>
/// Verifies multiple mobile money account names in a single bulk request.
/// </summary>
/// <returns>The batch transaction ID or an error message.</returns>
[HttpGet("VerifyBulkMobileMoney")] // Removed 'Async' suffix from route name
public async Task<IActionResult> VerifyBulkMobileMoneyAsync()
{
    // Assuming TestPhoneNumber is a defined constant or variable accessible here
    const string TestPhoneNumber = "233246089019"; // Define if not already
    const string AnotherTestPhoneNumber = "233551234567"; // Example

    var requestBulkNameCheck = new BulkNameEnquiryRequest
    {
        BatchTransactionId = Guid.NewGuid().ToString("N"),
        BulkNameEnquiryData =
        [
            new BulkNameEnquiryItem
            {
                AccountNumber = TestPhoneNumber,
                Institution = InstitutionCodes.Mtn
            },
            new BulkNameEnquiryItem
            {
                AccountNumber = AnotherTestPhoneNumber,
                Institution = InstitutionCodes.Vodafone // Example with another institution
            }
        ]
    };

    var response = await _client.NameEnquiry.GetBulkNameEnquiryAsync(requestBulkNameCheck);

    if (response.IsSuccess)
    {
        _logger.LogInformation("Bulk mobile money name enquiry successful. Batch ID: {BatchTransactionId}", response.Data?.BatchTransactionId);
        return Ok($"Bulk Mobile Money Name Enquiry Batch ID: {response.Data?.BatchTransactionId}");
    }
    _logger.LogError("Bulk mobile money name enquiry failed: {StatusDescription} - {Message}", response.StatusDescription, response.Message);
    return BadRequest($"Bulk Name Enquiry Failed: {response.StatusDescription}. Details: {response.Message}");
}

2. Payment Collections

Initiate payments from various channels.

Mobile Money Collection
// Inside PaymentServiceController class

/// <summary>
/// Initiates a mobile money collection (debit) from a customer's account.
/// </summary>
/// <param name="amount">The amount to debit.</param>
/// <param name="description">A narration for the debit.</param>
/// <returns>The transaction ID or an error message.</returns>
[HttpGet("MobileMoneyCollection")]
public async Task<IActionResult> MobileMoneyCollection(decimal amount, string description)
{
    // Ensure TestPhoneNumber is defined or replaced with a dynamic value
    const string TestPhoneNumber = "233246089019";

    var collectionRequest = new DebitMoneyRequest
    {
        AccountNumber = TestPhoneNumber,
        AccountName = "Customer Name From Enquiry", /* IMPORTANT: Obtain this from a prior name enquiry */
        Amount = amount,
        PaymentChannel = Channels.MNO, // Correct channel for mobile money
        Institution = InstitutionCodes.Mtn,
        TransactionId = Guid.NewGuid().ToString("N"), // Unique transaction ID
        DebitNarration = description,
        Currency = "GHS" // Specify currency, e.g., Ghanaian Cedi
    };
    var response = await _client.Collections.DebitMoneyAsync(collectionRequest);

    if (response.IsSuccess)
    {
        _logger.LogInformation("Mobile Money collection initiated successfully. Transaction ID: {TransactionId}", response.Data?.TransactionId);
        return Ok($"Transaction ID: {response.Data?.TransactionId}");
    }
    _logger.LogError("Mobile Money collection failed: {StatusDescription} - {Message}", response.StatusDescription, response.Message);
    return BadRequest($"Collection Failed: {response.StatusDescription}. Details: {response.Message}");
}

Bank Collection (Not yet available)

  • Bank collection functionality is not yet implemented in the SDK.
  • This section will be updated when it becomes available.

3. Disbursements

Send funds to bank accounts and mobile money wallets.

Bank Disbursement
// Inside PaymentServiceController class

/// <summary>
/// Handles a single bank disbursement (credit) to a recipient's bank account.
/// </summary>
/// <param name="amount">The amount to disburse.</param>
/// <param name="description">A narration for the credit.</param>
/// <returns>The transaction ID or an error message.</returns>
public async Task<IActionResult> DisburseToBankAsync(decimal amount, string description)
{
    var request = new DisbursementRequest
    {
        AccountNumber = "TEST_ACCOUNT_NUMBER", // Example: Replace with actual recipient account number
        AccountName = "Account Name From Enquiry", /* IMPORTANT: Obtain from a prior name enquiry */
        Amount = amount,
        PaymentChannel = Channels.INTERBANK,
        Institution = InstitutionCodes.StandardCharteredBank, // Example institution
        TransactionId = Guid.NewGuid().ToString("N"),
        CreditNarration = description,
        Currency = "GHS"
    };

    var response = await _client.Disbursements.DisburseAsync(request);

    if (response.IsSuccess)
    {
        _logger.LogInformation("Bank disbursement initiated successfully. Transaction ID: {TransactionId}", response.Data?.TransactionId);
        return Ok($"Transaction ID: {response.Data?.TransactionId}");
    }
    _logger.LogError("Bank disbursement failed: {StatusDescription} - {Message}", response.StatusDescription, response.Message);
    return BadRequest($"Disbursement Failed: {response.StatusDescription}. Details: {response.Message}");
}

/// <summary>
/// Processes a bulk bank disbursement to multiple recipients.
/// </summary>
/// <param name="amount">The amount to disburse to each recipient.</param>
/// <returns>The bulk transaction response or an error message.</returns>
[HttpGet("DisburseBulkToBank")]
public async Task<IActionResult> ProcessBulkDisbursementAsync(decimal amount)
{
    /* You would typically retrieve your recipient data from a database or other persistent storage */
    var recipients = new List<Recipient>()
    {
        new Recipient
        {
            AccountNumber = "BANK_ACCOUNT_1", // Replace with actual account number
            AccountName = "Recipient One Name",
            BankInstitutionCode = InstitutionCodes.AbsaBankGhanaLimited.ToString()
        },
        new Recipient
        {
            AccountNumber = "BANK_ACCOUNT_2", // Replace with actual account number
            AccountName = "Recipient Two Name",
            BankInstitutionCode = InstitutionCodes.EcobankGhanaLtd.ToString()
        }
        // Add more recipients as needed
    };

    var request = new BulkPaymentRequest
    {
        BatchTransactionId = Guid.NewGuid().ToString("N"), // Unique batch ID
        BulkPaymentData = recipients.Select(r => new BulkPaymentItem
        {
            AccountNumber = r.AccountNumber,
            AccountName = r.AccountName,
            Amount = amount, // Same amount for all in this example, or vary per recipient
            Institution = (InstitutionCodes)int.Parse(r.BankInstitutionCode), // Cast string code to enum
            Narration = "Bulk Payment for employee salaries"
        }).ToList()! // Ensure the list is not null
    };

    var response = await _client.BulkTransactions.BulkDisbursementAsync(request);

    if (response.IsSuccess)
    {
        _logger.LogInformation("Bulk bank disbursement initiated successfully. Batch ID: {BatchTransactionId}", response.Data?.BatchTransactionId);
        return Ok(response.Data);
    }
    _logger.LogError("Bulk bank disbursement failed: {StatusDescription} - {Message}", response.StatusDescription, response.Message);
    return BadRequest($"Bulk Disbursement Failed: {response.StatusDescription}. Details: {response.Message}");
}
Mobile Money Disbursement
// Inside PaymentServiceController class

/// <summary>
/// Handles a single mobile money disbursement (credit) to a recipient's mobile money wallet.
/// </summary>
/// <param name="amount">The amount to disburse.</param>
/// <param name="description">A narration for the credit.</param>
/// <returns>The transaction ID or an error message.</returns>
public async Task<IActionResult> DisburseToMobileMoneyAsync(decimal amount, string description)
{
    // Assuming SCAccountNumber was a placeholder for a test mobile number
    const string TestMobileNumber = "233246089019";

    var request = new DisbursementRequest
    {
        AccountNumber = TestMobileNumber,
        AccountName = "Mobile Money Recipient Name", /* IMPORTANT: Obtain from a prior name enquiry */
        Amount = amount,
        PaymentChannel = Channels.MNO, // Correct channel for mobile money
        Institution = InstitutionCodes.Mtn, // Example mobile money operator
        TransactionId = Guid.NewGuid().ToString("N"),
        CreditNarration = description,
        Currency = "GHS"
    };

    var response = await _client.Disbursements.DisburseAsync(request);

    if (response.IsSuccess)
    {
        _logger.LogInformation("Mobile Money disbursement initiated successfully. Transaction ID: {TransactionId}", response.Data?.TransactionId);
        return Ok($"Transaction ID: {response.Data?.TransactionId}");
    }
    _logger.LogError("Mobile Money disbursement failed: {StatusDescription} - {Message}", response.StatusDescription, response.Message);
    return BadRequest($"Disbursement Failed: {response.StatusDescription}. Details: {response.Message}");
}

/// <summary>
/// Processes a bulk mobile money disbursement to multiple recipients.
/// </summary>
/// <param name="amount">The amount to disburse to each recipient.</param>
/// <returns>The bulk transaction response or an error message.</returns>
public async Task<IActionResult> ProcessBulkMobileMoneyDisbursementAsync(decimal amount)
{
    /* You would typically retrieve your recipient data from a database or other persistent storage */
    var recipients = new List<Recipient>()
    {
        new Recipient
        {
            AccountNumber = "233246089019", // Replace with actual mobile number
            AccountName = "Momo Recipient One Name",
            MomoInstitutionCode = InstitutionCodes.Mtn.ToString()
        },
        new Recipient
        {
            AccountNumber = "233551234567", // Replace with actual mobile number
            AccountName = "Momo Recipient Two Name",
            MomoInstitutionCode = InstitutionCodes.Vodafone.ToString()
        }
        // Add more recipients as needed
    };

    var request = new BulkPaymentRequest
    {
        BatchTransactionId = Guid.NewGuid().ToString("N"),
        BulkPaymentData = recipients.Select(r => new BulkPaymentItem
        {
            AccountNumber = r.AccountNumber,
            AccountName = r.AccountName,
            Amount = amount,
            Institution = (InstitutionCodes)int.Parse(r.MomoInstitutionCode), // Cast string code to enum
            Narration = "Bulk Payment for commissions"
        }).ToList()!
    };

    var response = await _client.BulkTransactions.BulkDisbursementAsync(request);

    if (response.IsSuccess)
    {
        _logger.LogInformation("Bulk Mobile Money disbursement initiated successfully. Batch ID: {BatchTransactionId}", response.Data?.BatchTransactionId);
        return Ok(response.Data!);
    }
    _logger.LogError("Bulk Mobile Money disbursement failed: {StatusDescription} - {Message}", response.StatusDescription, response.Message);
    return BadRequest($"Bulk Disbursement Failed: {response.StatusDescription}. Details: {response.Message}");
}

4. Transaction Management

Monitor and manage the lifecycle of your transactions.

Check Transaction Status
using LibertePay.Core.Responses; // For TransactionStatusResponse
using LibertePay.Core.Requests; // For TransactionStatusRequest
using LibertePay.Core.Constants; // For ErrorCodes (if applicable)

// Inside PaymentServiceController or a dedicated TransactionService

/// <summary>
/// Checks the status of a single transaction.
/// </summary>
/// <param name="transactionId">The ID of the transaction to check.</param>
/// <param name="transactionType">The type of transaction ("DEBIT" or "CREDIT").</param>
/// <returns>The transaction status response.</returns>
/// <exception cref="ApplicationException">Thrown if the status check fails.</exception>
public async Task<TransactionStatusResponse> CheckTransactionStatusAsync(
    string transactionId,
    string transactionType) // "DEBIT" or "CREDIT"
{
    var request = new TransactionStatusRequest
    {
        TransactionId = transactionId,
        TransactionType = transactionType
    };

    var response = await _client.Transactions.GetTransactionStatusAsync(request);
    if (!response.IsSuccess)
    {
        _logger.LogError("Failed to get transaction status for {TransactionId}: {Message} - {StatusDescription}", transactionId, response.Message, response.StatusDescription);
        throw new ApplicationException($"Failed to retrieve transaction status for {transactionId}: {response.Message}");
    }
    _logger.LogInformation("Transaction {TransactionId} status: {StatusDescription}", transactionId, response.Data?.StatusDescription);
    return response.Data!;
}

/* You can also implement transaction status checking as a background job for asynchronous updates */

/// <summary>
/// Monitors transaction status in a background process with retries until a final status is reached or timeout.
/// </summary>
/// <param name="transactionId">The ID of the transaction to monitor.</param>
/// <param name="transactionType">The type of transaction ("DEBIT" or "CREDIT").</param>
/// <param name="cancellationToken">Cancellation token for graceful shutdown.</param>
private async Task MonitorTransactionStatusAsync(
    string transactionId, string transactionType,
    CancellationToken cancellationToken)
{
    const int maxAttempts = 10;
    const int delayMilliseconds = 2000; // 2 seconds delay between attempts

    for (int attempt = 0; attempt < maxAttempts; attempt++)
    {
        var response = await _client.Transactions.GetTransactionStatusAsync(
            new TransactionStatusRequest
            {
                TransactionId = transactionId,
                TransactionType = transactionType
            },
            cancellationToken);

        // Assuming ErrorCodes.IsPending is a helper method provided by LibertePay.Core.Constants
        if (response.IsSuccess && !ErrorCodes.IsPending(response.StatusCode))
        {
            _logger.LogInformation("Transaction {TransactionId} completed successfully with status: {StatusDescription}", transactionId, response.StatusDescription);
            return; // Transaction completed
        }
        else if (!response.IsSuccess)
        {
            _logger.LogError("Transaction {TransactionId} failed: {StatusDescription} - {Message}",
                transactionId, response.StatusDescription, response.Message);
            return; // Transaction failed or has a final non-pending status
        }

        _logger.LogInformation("Transaction {TransactionId} is still pending. Retrying in {Delay}ms (Attempt {Attempt}/{MaxAttempts})",
            transactionId, delayMilliseconds, attempt + 1, maxAttempts);
        await Task.Delay(delayMilliseconds, cancellationToken);
    }

    _logger.LogWarning("Transaction {TransactionId} status check timed out after {MaxAttempts} attempts. Last known status: {StatusDescription}",
        transactionId, maxAttempts, (await _client.Transactions.GetTransactionStatusAsync(new TransactionStatusRequest { TransactionId = transactionId, TransactionType = transactionType })).StatusDescription);
}

Reverse Transaction (Coming Soon)

  • Transaction reversal functionality will be added in a future update.
  • This section will be updated with code examples when available.

5. Account Services

Retrieve information about your LibertePay account.

Check Balance
// Inside PaymentServiceController or a dedicated AccountService

/// <summary>
/// Retrieves the available and current balance of your LibertePay account.
/// </summary>
/// <returns>A tuple containing available and current balance.</returns>
/// <exception cref="ApplicationException">Thrown if balance retrieval fails.</exception>
public async Task<(decimal Available, decimal Current)> GetBalanceAsync()
{
    var response = await _client.Balance.GetBalanceAsync();
    if (!response.IsSuccess)
    {
        _logger.LogError("Failed to retrieve balance: {Message} - {StatusDescription}", response.Message, response.StatusDescription);
        throw new ApplicationException($"Could not retrieve account balance: {response.Message}");
    }
    _logger.LogInformation("Account balance retrieved. Available: {AvailableBalance}, Current: {CurrentBalance}", response.Data.AvailableBalance, response.Data.CurrentBalance);
    return (response.Data.AvailableBalance, response.Data.CurrentBalance);
}

KYC Verification (Coming Soon)

  • KYC verification functionality is not yet implemented.
  • This section will be updated with code examples when available.
using LibertePay.Core.Responses; // For KycResponse
using LibertePay.Core.Requests; // For KycRequest

public async Task<KycResponse> VerifyCustomerAsync(
    string customerId,
    string documentType)
{
    var request = new KycRequest
    {
        CustomerId = customerId,
        DocumentType = documentType
    };

    var response = await _client.Kyc.VerifyCustomerAsync(request);
    if (!response.IsSuccess)
    {
        _logger.LogError("KYC verification failed for customer {CustomerId}: {Message} - {StatusDescription}", customerId, response.Message, response.StatusDescription);
        throw new ApplicationException($"KYC verification failed: {response.Message}");
    }
    _logger.LogInformation("KYC verification successful for customer {CustomerId}", customerId);
    return response.Data;
}

â†Šī¸ Callback Implementation

LibertePay leverages callbacks to asynchronously notify your application about transaction and mandate status updates. Proper implementation of callbacks is crucial for robust payment processing.

1. Configure Callback URL

Ensure your callback URL is correctly configured in your appsettings.json. This is the endpoint LibertePay will send notifications to.

{
  "LibertePay": {
    "CallbackUrl": "https://your-domain.com/api/callback" /* This URL must be provided during LibertePay setup */
  }
}

2. Callback Types and Scenarios

The LibertePay gateway sends real-time status updates via configured callback URLs. Your application is expected to respond with a specific success response to acknowledge receipt and close the transaction's end-to-end lifecycle.

Transaction Callbacks

Transaction callbacks are sent for various payment scenarios, including:

  • Mobile Money Collection: Updates on initiation, status changes (pending, success, failed), and reversals.
  • Bank Transfer: Updates on initiation, status changes, and reversals.
  • Bulk Payment: Individual updates for each payment within a bulk transaction, as well as overall batch status changes.
Mandate Callbacks

Mandate callbacks are specifically for recurring payment scenarios:

  • Mandate Creation: Notifications for new mandate creation, failures, and cancellations.
  • Recurring Payment: Updates for initiated recurring payments, status changes, and failures.

3. Callback Payload Structure

Transaction Callback

(For CreditMoney, DebitMoney, DirectDebit operations)

{
  "status": "string",
  "statusCode": "string", 
  "message": "string",         // Additional details or error message
  "transactionId": "string",   // Your original transaction ID
  "institutionApprovalCode": "string", // Approval code from the institution
  "externalTransactionId": "string"    // LibertePay's internal transaction ID
}
Mandate Callback
{
  "statusCode": "string",
  "status": "string", 
  "message": "string",       // Additional details or error message
  "expiryDate": "string",    // Expiry date of the mandate (if applicable)
  "transactionId": "string",
  "mandateReference": "string", // Unique reference for the mandate
  "mandateId": "string",     // LibertePay's ID for the mandate
  "msisdnToken": null        // MSISDN token (if applicable for mobile money mandates)
}

4. Create Callback Controller

Implement an API controller in your application to receive and process these callbacks. The SDK provides helper methods to simplify handling.

using LibertePay.Core.Callbacks; // Assuming these types are defined in your SDK
using LibertePay.Core.Clients;
using LibertePay.Core.Constants; // For ErrorCodes
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class CallbackController : ControllerBase
{
    private readonly ILibertePayClient _libertePayClient;
    private readonly ILogger<CallbackController> _logger;

    public CallbackController(
        ILibertePayClient libertePayClient,
        ILogger<CallbackController> logger)
    {
        _libertePayClient = libertePayClient;
        _logger = logger;
    }

    /// <summary>
    /// Handles incoming transaction callbacks from LibertePay.
    /// </summary>
    /// <param name="request">The transaction callback payload.</param>
    /// <param name="cancellationToken">Cancellation token.</param>
    /// <returns>An acknowledgement response to LibertePay.</returns>
    [HttpPost("transaction")]
    public async Task<IActionResult> HandleTransactionCallback(
        [FromBody] TransactionCallbackRequest request, // Use the specific request type for transactions
        CancellationToken cancellationToken)
    {
        try
        {
            // Use the SDK's HandleTransactionCallbackAsync for proper callback processing.
            // The SDK handles signature verification and other security aspects.
            var response = await _libertePayClient.Callbacks.HandleTransactionCallbackAsync(
                async (callbackData, ct) =>
                {
                    // Log callback details for debugging and auditing purposes
                    _logger.LogInformation(
                        "Received transaction callback for TransactionId: {TransactionId}, Status: {Status}, StatusCode: {StatusCode}, Message: {Message}",
                        callbackData.TransactionId,
                        callbackData.Status,
                        callbackData.StatusCode,
                        callbackData.Message);

                    // Implement your specific business logic based on the transaction status.
                    // This is where you would update your internal database, notify users, etc.
                    switch (callbackData.StatusCode)
                    {
                        case "00": // Success
                            _logger.LogInformation("Transaction {TransactionId} - SUCCESS. External ID: {ExternalTransactionId}", callbackData.TransactionId, callbackData.ExternalTransactionId);
                            // Example: Update order status to 'completed'
                            break;
                        case "01": // Pending
                            _logger.LogInformation("Transaction {TransactionId} - PENDING. Will await final status.", callbackData.TransactionId);
                            // Example: Keep order status as 'pending'
                            break;
                        case "02": // Failed
                            _logger.LogError("Transaction {TransactionId} - FAILED: {Message}", callbackData.TransactionId, callbackData.Message);
                            // Example: Mark order as 'failed', initiate refund process if applicable
                            break;
                        case "03": // Reversed
                            _logger.LogWarning("Transaction {TransactionId} - REVERSED: {Message}", callbackData.TransactionId, callbackData.Message);
                            // Example: Reverse the transaction in your system
                            break;
                        default:
                            _logger.LogWarning("Transaction {TransactionId} - UNKNOWN STATUS CODE: {StatusCode}", callbackData.TransactionId, callbackData.StatusCode);
                            break;
                    }

                    // Return a success response to LibertePay to acknowledge successful receipt and processing.
                    // This is crucial for LibertePay to consider the callback delivered.
                    return await Task.FromResult(new CallbackResponse
                    {
                        Status = "OK",
                        Message = $"Transaction {callbackData.TransactionId} processed successfully by callback handler."
                    });
                },
                request, // Pass the received callback request object
                cancellationToken);

            return Ok(response); // Return the response generated by the SDK's handler
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing transaction callback for TransactionId: {TransactionId}", request?.TransactionId);
            // In case of an error in your handler, return an ERROR status to LibertePay.
            // This might prompt LibertePay to retry the callback.
            return StatusCode(500, new CallbackResponse
            {
                Status = "ERROR",
                Message = "Internal server error during transaction callback processing."
            });
        }
    }

    /// <summary>
    /// Handles incoming mandate callbacks from LibertePay.
    /// </summary>
    /// <param name="request">The mandate callback payload.</param>
    /// <param name="cancellationToken">Cancellation token.</param>
    /// <returns>An acknowledgement response to LibertePay.</returns>
    [HttpPost("mandate")]
    public async Task<IActionResult> HandleMandateCallback(
        [FromBody] MandateCallbackRequest request, // Use the specific request type for mandates
        CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation(
                "Received mandate callback for TransactionId: {TransactionId}, MandateId: {MandateId}, Status: {Status}, Message: {Message}",
                request.TransactionId,
                request.MandateId,
                request.Status,
                request.Message);

            var response = await _libertePayClient.Callbacks.HandleMandateCallbackAsync(
                async (callbackData, ct) =>
                {
                    // Implement your specific business logic for mandate status updates.
                    switch (callbackData.Status)
                    {
                        case "ACTIVE":
                            _logger.LogInformation("Mandate {MandateId} - ACTIVE. Reference: {MandateReference}", callbackData.MandateId, callbackData.MandateReference);
                            // Example: Mark mandate as active in your subscription system
                            break;
                        case "CANCELLED":
                            _logger.LogWarning("Mandate {MandateId} - CANCELLED.", callbackData.MandateId);
                            // Example: Update mandate status to cancelled, stop recurring charges
                            break;
                        case "EXPIRED":
                            _logger.LogWarning("Mandate {MandateId} - EXPIRED.", callbackData.MandateId);
                            // Example: Handle expired mandate, prompt user to renew
                            break;
                        case "FAILED":
                            _logger.LogError("Mandate {MandateId} - FAILED: {Message}", callbackData.MandateId, callbackData.Message);
                            // Example: Address mandate creation/payment failure, notify user
                            break;
                        default:
                            _logger.LogWarning("Mandate {MandateId} - UNKNOWN STATUS: {Status}", callbackData.MandateId, callbackData.Status);
                            break;
                    }

                    // Return a success response to LibertePay.
                    return await Task.FromResult(new CallbackResponse
                    {
                        Status = "OK",
                        Message = $"Mandate {callbackData.MandateId} processed successfully by callback handler."
                    });
                },
                request,
                cancellationToken);

            return Ok(response);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing mandate callback for MandateId: {MandateId}", request?.MandateId);
            return StatusCode(500, new CallbackResponse
            {
                Status = "ERROR",
                Message = "Internal server error during mandate callback processing."
            });
        }
    }
}

đŸĻ Institution Codes

The SDK provides a type-safe way to interact with various financial institutions through the InstitutionCodes enum. This offers several benefits:

  • Compile-time checking: Catch errors related to invalid institution codes before runtime.
  • IntelliSense support: Easily discover available institutions directly in your IDE.
  • Automatic conversion: The SDK handles the conversion from enum to the correct string format for API calls.

Available Institution Codes

  • Refer to the InstitutionCodes enum in the SDK for a complete and up-to-date list

Using Institution Codes

// Direct enum usage for type safety and IntelliSense
var request = new NameEnquiryRequest
{
    AccountNumber = "1234567890",
    Institution = InstitutionCodes.Mtn // Recommended approach
};

// Getting the string value of an institution code (e.g., for logging or specific API needs)
string mtnCodeString = InstitutionCodes.Mtn.ToString(); // Returns "300591"

// Parsing a string back to an enum (useful if codes come from configuration or external sources)
// Ensure robust error handling if parsing user input
if (Enum.TryParse("300304", out InstitutionCodes parsedBankCode))
{
    Console.WriteLine($"Parsed bank code: {parsedBankCode}"); // Output: GcbBankLimited
}

🌐 Channel Types

When specifying payment channels, use the Channels constants for clarity and consistency:

  • Channels.INTERBANK: Used for bank-to-bank transactions.
  • Channels.MNO: Used for Mobile Network Operator (Mobile Money) transactions.
  • Channels.CARD: Used for card payments (coming soon).

❓ Support

For any questions, issues, or assistance with the LibertePay SDK, please reach out to our support team:

📜 License

This SDK is open-sourced and licensed under the MIT License. For more details, please refer to the LICENSE file in the repository.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.