Paybox.Core 0.1.0

dotnet add package Paybox.Core --version 0.1.0
                    
NuGet\Install-Package Paybox.Core -Version 0.1.0
                    
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="Paybox.Core" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Paybox.Core" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Paybox.Core" />
                    
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 Paybox.Core --version 0.1.0
                    
#r "nuget: Paybox.Core, 0.1.0"
                    
#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.
#:package Paybox.Core@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Paybox.Core&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Paybox.Core&version=0.1.0
                    
Install as a Cake Tool

Paybox

A modular .NET 10 payment gateway library. Install only the providers you need, plug in your own persistence layer, and get webhook endpoints wired up in two lines.

Currently supported: PayMe (payme.uz) · Click (click.uz)
Planned: Stripe · Paymob · Uzum Bank


Packages

Package Description
Paybox.Abstractions Interfaces and models only — no dependencies
Paybox.Core DI builder, PaymentService, signature utilities
Paybox.PayMe PayMe Merchant API + webhook handler
Paybox.Click Click Shop API + Merchant API + webhook handler
Paybox.AspNetCore MapPayboxWebhooks() minimal API routing

Installation

dotnet add package Paybox.Core
dotnet add package Paybox.PayMe
dotnet add package Paybox.Click
dotnet add package Paybox.AspNetCore

Quick start

1. Register services

// Program.cs
builder.Services.AddPaybox()
    .AddPayMe(o =>
    {
        o.MerchantId = builder.Configuration["PayMe:MerchantId"]!;
        o.SecretKey  = builder.Configuration["PayMe:SecretKey"]!;
        o.IsSandbox  = builder.Environment.IsDevelopment();
    })
    .AddClick(o =>
    {
        o.ServiceId      = int.Parse(builder.Configuration["Click:ServiceId"]!);
        o.SecretKey      = builder.Configuration["Click:SecretKey"]!;
        o.MerchantUserId = int.Parse(builder.Configuration["Click:MerchantUserId"]!);
        o.IsSandbox      = builder.Environment.IsDevelopment();
    });

// required — implement with your own DB/ORM (see below)
builder.Services.AddScoped<IPaymentEventStore, MyPaymentEventStore>();

// optional — side effects after payment lifecycle transitions
builder.Services.AddScoped<IPaymentLifecycleHook, MyNotificationHook>();

2. Map webhook endpoints

app.MapPayboxWebhooks(); // uses prefix "/payments" by default

This registers:

Method Route Description
POST /payments/payme/callback PayMe JSON-RPC callback
POST /payments/click/prepare Click prepare phase (action=0)
POST /payments/click/complete Click complete phase (action=1)

Custom prefix:

app.MapPayboxWebhooks(prefix: "/api/v1/payments");

3. Initiate a payment

public class OrderService(IPaymentService payments)
{
    public async Task<string> CreatePaymentAsync(string orderId, long amountTiyin)
    {
        var request = new PaymentRequest(
            OrderId:  orderId,
            Amount:   amountTiyin,   // always in smallest unit (tiyin for UZS)
            Currency: PaymentCurrency.UZS,
            ReturnUrl: "https://yoursite.com/orders/thank-you"
        );

        var result = await payments.InitiateAsync("PayMe", request);
        // result.CheckoutUrl → redirect the user here
        return result.CheckoutUrl!;
    }
}

Implement IPaymentEventStore

Paybox has no opinion on your database. Implement this interface with whatever ORM or driver you use:

public sealed class PaymentEventStore(AppDbContext db) : IPaymentEventStore
{
    public async Task SaveAsync(PaymentEvent @event, CancellationToken ct = default)
    {
        db.PaymentEvents.Add(new PaymentEventEntity
        {
            Id            = @event.Id,
            OrderId       = @event.OrderId,
            TransactionId = @event.TransactionId,
            Provider      = @event.Provider,
            Amount        = @event.Amount,
            Currency      = @event.Currency.ToString(),
            Status        = @event.Status.ToString(),
            CreatedAt     = @event.CreatedAt,
            UpdatedAt     = @event.UpdatedAt
        });
        await db.SaveChangesAsync(ct);
    }

    public async Task<PaymentEvent?> FindByTransactionIdAsync(string transactionId, CancellationToken ct = default)
    {
        var entity = await db.PaymentEvents
            .FirstOrDefaultAsync(e => e.TransactionId == transactionId, ct);
        return entity is null ? null : Map(entity);
    }

    public async Task<PaymentEvent?> FindByOrderIdAsync(string orderId, CancellationToken ct = default)
    {
        var entity = await db.PaymentEvents
            .FirstOrDefaultAsync(e => e.OrderId == orderId, ct);
        return entity is null ? null : Map(entity);
    }

    public async Task UpdateStatusAsync(string transactionId, PaymentStatus status, CancellationToken ct = default)
    {
        await db.PaymentEvents
            .Where(e => e.TransactionId == transactionId)
            .ExecuteUpdateAsync(s => s
                .SetProperty(e => e.Status, status.ToString())
                .SetProperty(e => e.UpdatedAt, DateTimeOffset.UtcNow), ct);
    }

    private static PaymentEvent Map(PaymentEventEntity e) => new()
    {
        Id            = e.Id,
        OrderId       = e.OrderId,
        TransactionId = e.TransactionId,
        Provider      = e.Provider,
        Amount        = e.Amount,
        Currency      = Enum.Parse<PaymentCurrency>(e.Currency),
        Status        = Enum.Parse<PaymentStatus>(e.Status),
        CreatedAt     = e.CreatedAt,
        UpdatedAt     = e.UpdatedAt
    };
}

Implement IPaymentLifecycleHook (optional)

Fires after the event is persisted. Use it for order updates, email/SMS notifications, etc.

public sealed class OrderNotificationHook(IOrderService orders, IEmailService email) : IPaymentLifecycleHook
{
    public async Task OnCompletedAsync(PaymentEvent @event, CancellationToken ct = default)
    {
        await orders.MarkPaidAsync(@event.OrderId, ct);
        await email.SendPaymentConfirmationAsync(@event.OrderId, ct);
    }

    public async Task OnCancelledAsync(PaymentEvent @event, CancellationToken ct = default)
    {
        await orders.MarkCancelledAsync(@event.OrderId, ct);
    }
}

Configuration reference

appsettings.json

{
  "PayMe": {
    "MerchantId": "your_merchant_id",
    "SecretKey":  "your_secret_key",
    "IsSandbox":  true
  },
  "Click": {
    "ServiceId":      12345,
    "SecretKey":      "your_secret_key",
    "MerchantUserId": 99,
    "IsSandbox":      false
  }
}

Bind from config directly:

builder.Services.AddPaybox()
    .AddPayMe(o => builder.Configuration.GetSection("PayMe").Bind(o))
    .AddClick(o => builder.Configuration.GetSection("Click").Bind(o));

Payment flow

PayMe

User → Your server (InitiateAsync) → checkout URL → User redirected to PayMe
PayMe → POST /payments/payme/callback (JSON-RPC: CheckPerform → Create → Perform)
Paybox → IPaymentEventStore.UpdateStatusAsync → IPaymentLifecycleHook.OnCompletedAsync

Amount: tiyin (1 UZS = 100 tiyin). Pass 50000 for 500 UZS.

Click

User → Your server (InitiateAsync) → checkout URL → User redirected to Click
Click → POST /payments/click/prepare  (action=0, validates order)
Click → POST /payments/click/complete (action=1, confirms payment)
Paybox → IPaymentEventStore.UpdateStatusAsync → IPaymentLifecycleHook.OnCompletedAsync

Amount: convert to som internally. Pass 50000 tiyin — Paybox converts to 500.00 som for Click.


Check payment status

var status = await payments.GetStatusAsync("PayMe", transactionId);
// status.Status → PaymentStatus.Completed / Pending / Cancelled / Failed

Refund

var refund = await payments.RefundAsync("Click", transactionId, amount: 50000);

Click allows refunds only for same-month transactions (except the 1st day of the month).


PayMe IP whitelist

PayMe callbacks come from 185.234.113.1–185.234.113.15. The handler rejects requests outside this range by default. Override for testing:

.AddPayMe(o =>
{
    // ...
    o.AllowedIps = ["185.234.113.", "10.0.0."]; // custom ranges
});

Adding a new provider

  1. Create a class library referencing Paybox.Core
  2. Implement IPaymentProvider and IWebhookHandler
  3. Add a AddMyProvider(this PayboxBuilder builder, ...) extension method
  4. Register with builder.Services.TryAddEnumerable

License

MIT

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Paybox.Core:

Package Downloads
Paybox.AspNetCore

ASP.NET Core integration for Paybox. Provides MapPayboxWebhooks() minimal API routing for all registered payment providers.

Paybox.PayMe

PayMe (payme.uz) provider for Paybox. Implements JSON-RPC 2.0 Merchant API, Basic Auth webhook handler with IP whitelist, and fiscalization support.

Paybox.Click

Click (click.uz) provider for Paybox. Implements Shop API (prepare/complete), Merchant API with SHA1 auth, and fiscalization support.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 197 4/18/2026