Solentik.Hubtel
1.0.2
dotnet add package Solentik.Hubtel --version 1.0.2
NuGet\Install-Package Solentik.Hubtel -Version 1.0.2
<PackageReference Include="Solentik.Hubtel" Version="1.0.2" />
<PackageVersion Include="Solentik.Hubtel" Version="1.0.2" />
<PackageReference Include="Solentik.Hubtel" />
paket add Solentik.Hubtel --version 1.0.2
#r "nuget: Solentik.Hubtel, 1.0.2"
#:package Solentik.Hubtel@1.0.2
#addin nuget:?package=Solentik.Hubtel&version=1.0.2
#tool nuget:?package=Solentik.Hubtel&version=1.0.2
Solentik.Hubtel
Strongly typed Hubtel hosted payments and invoicing for .NET 8, 9, and 10.
Documentation
Full installation, configuration, and API documentation is available at:
https://hubtel.solentik.com/dotnet/introduction.html
Install
dotnet add package Solentik.Hubtel --version 1.0.0
Register only what you use
using Solentik.Hubtel.DependencyInjection;
builder.Services.AddHubtelPayments(builder.Configuration);
builder.Services.AddHubtelInvoicing(builder.Configuration);
AddHubtelPayments and AddHubtelInvoicing are independent. Payment-only applications do not need invoicing credentials.
Configuration
Both registration methods bind configuration from the standard .NET configuration pipeline, including appsettings.json, environment-specific settings, user secrets, and environment variables.
{
"Hubtel": {
"Payments": {
"ApiKey": "your-application-key",
"ApiSecret": "your-application-secret",
"MerchantAccountNumber": "your-merchant-account-number",
"CallbackUrl": "https://api.example.com/hubtel/payments/callback",
"ReturnUrl": "https://example.com/payment-complete",
"CancellationUrl": "https://example.com/payment-cancelled",
"Timeout": "00:00:45"
},
"Invoicing": {
"ApiId": "your-invoicing-api-id",
"ApiKey": "your-invoicing-api-key",
"CollectionAccountNumber": "11684",
"CallbackUrl": "https://api.example.com/hubtel/invoices/callback",
"BaseAddress": "https://invoicing.hubtel.com/",
"Timeout": "00:00:45"
}
}
}
The default API endpoints are built into the package. They can be overridden through InitiateEndpoint, TransactionStatusEndpointTemplate, or BaseAddress when required.
Environment variables
.NET maps double underscores to nested configuration sections:
Hubtel__Payments__ApiKey=your-application-key
Hubtel__Payments__ApiSecret=your-application-secret
Hubtel__Payments__MerchantAccountNumber=your-merchant-account-number
Hubtel__Payments__CallbackUrl=https://api.example.com/hubtel/payments/callback
Hubtel__Payments__ReturnUrl=https://example.com/payment-complete
Hubtel__Payments__CancellationUrl=https://example.com/payment-cancelled
Hubtel__Invoicing__ApiId=your-invoicing-api-id
Hubtel__Invoicing__ApiKey=your-invoicing-api-key
Hubtel__Invoicing__CollectionAccountNumber=11684
Hubtel__Invoicing__CallbackUrl=https://api.example.com/hubtel/invoices/callback
User secrets
For local development:
dotnet user-secrets set "Hubtel:Payments:ApiKey" "your-application-key"
dotnet user-secrets set "Hubtel:Payments:ApiSecret" "your-application-secret"
dotnet user-secrets set "Hubtel:Payments:MerchantAccountNumber" "your-merchant-account-number"
dotnet user-secrets set "Hubtel:Invoicing:ApiId" "your-invoicing-api-id"
dotnet user-secrets set "Hubtel:Invoicing:ApiKey" "your-invoicing-api-key"
dotnet user-secrets set "Hubtel:Invoicing:CollectionAccountNumber" "11684"
Do not commit production credentials to appsettings.json. Use environment variables, user secrets, or your deployment platform's secret store.
Hosted payment
var response = await hubtel.Payments.InitiateAsync(
new CreatePaymentRequest
{
TotalAmount = 100m,
Description = "Order ORD-123",
ClientReference = "ORD-123-12434"
},
cancellationToken);
var checkoutUrl = response.Data?.CheckoutUrl;
Invoice
var response = await invoices.CreateAsync(
new CreateInvoiceRequest
{
InvoiceNumber = "INV-123",
DueDate = DateTimeOffset.UtcNow.AddDays(7),
CreatedBy = "Billing",
CustomerName = "John Doe",
CustomerPhoneNumber = "233555654321",
Items =
[
new InvoiceItem { Description = "Service", Quantity = 1, UnitPrice = 100m }
]
},
cancellationToken);
The invoicing client supports one-time invoices through CreateAsync and repeat invoices through CreateRepeatAsync. Both hosted payments and invoices provide transaction-status checks for callback recovery and reconciliation. Callback parsers are registered as IHubtelCallbackParser and IInvoiceCallbackParser.
Securing your callback endpoint
IHubtelCallbackParser/IInvoiceCallbackParser only parse and validate the shape of a callback body — they do not verify that a request actually came from Hubtel. Anyone who can reach your callback URL can POST a fabricated "payment succeeded" body. Before crediting an order or invoice from a callback:
- Restrict the endpoint (a random/secret path segment, an IP allowlist for Hubtel's published ranges, or mutual TLS if your deployment supports it).
- Treat the callback as a hint, not a source of truth: call
Transactions.GetStatusAsync(orInvoices.GetStatusAsync) to re-confirm the transaction status directly with Hubtel before fulfilling an order.
Resilience and observability
Both AddHubtelPayments and AddHubtelInvoicing wire their HttpClient through AddStandardResilienceHandler, which adds retry, circuit-breaker, and timeout handling for transient failures (network errors, timeouts, and 408/429/5xx responses). Retries are deliberately disabled for POST requests — payment initiation and invoice creation are not safe to replay automatically, since a retried request after a timeout could double-charge or double-invoice a customer if the original request actually reached Hubtel. GET requests (status checks) are naturally idempotent and are retried.
If an ILoggerFactory is registered in your service collection, each client logs a Debug entry for successful requests and a Warning entry (method, URI, status code, Hubtel response code, and message — never credentials) for failed ones.
See the hubtel-docs site in the repository for installation, configuration, payment, invoicing, status-check, callback, and error-handling guides.
| 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 is compatible. 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 is compatible. 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. |
-
net10.0
- Microsoft.Extensions.Http (>= 10.0.11)
- Microsoft.Extensions.Http.Resilience (>= 10.9.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.11)
-
net8.0
- Microsoft.Extensions.Http (>= 10.0.11)
- Microsoft.Extensions.Http.Resilience (>= 10.9.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.11)
-
net9.0
- Microsoft.Extensions.Http (>= 10.0.11)
- Microsoft.Extensions.Http.Resilience (>= 10.9.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.11)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.