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
<PackageReference Include="LibertePay.Core.Sdk" Version="1.0.3" />
<PackageVersion Include="LibertePay.Core.Sdk" Version="1.0.3" />
<PackageReference Include="LibertePay.Core.Sdk" />
paket add LibertePay.Core.Sdk --version 1.0.3
#r "nuget: LibertePay.Core.Sdk, 1.0.3"
#addin nuget:?package=LibertePay.Core.Sdk&version=1.0.3
#tool nuget:?package=LibertePay.Core.Sdk&version=1.0.3
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:
- Email: michael.ameyaw@persol.net
- Comprehensive Documentation: https://evat-ng-sync.persolqa.com/
- API Reference: https://evat-ng-sync.persolqa.com/
đ 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 | Versions 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. |
-
net8.0
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- System.Net.Http.Json (>= 8.0.0)
- System.Text.Json (>= 8.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
1.0.3 | 85 | 5/23/2025 | |
1.0.1-alpha | 178 | 5/22/2025 | |
1.0.0 | 98 | 5/23/2025 | |
1.0.0-beta | 144 | 5/23/2025 |