TailoredApps.Shared.Payments¶
Opis¶
Biblioteka dostarcza zunifikowaną abstrakcję do integracji z bramkami płatności w aplikacjach .NET. Zamiast pisać osobną logikę dla każdego operatora płatności, programujesz do wspólnego interfejsu IPaymentService, który automatycznie kieruje żądania do właściwego providera na podstawie klucza ("Stripe", "PayU", "CashBill" itd.).
Kluczowe elementy architektury:
IPaymentProvider— kontrakt dla każdego providera (kanały, inicjowanie płatności, status, webhook)IWebhookPaymentProvider— rozszerzenie dla providerów obsługujących webhooks z weryfikacją podpisuIPaymentService— fasada agregująca wszystkich zarejestrowanych providerówPaymentOptionsBuilder— fluent API do rejestracji providerów w DI
Biblioteka TailoredApps.Shared.Payments to samo serce — konkretne implementacje providerów są w osobnych pakietach TailoredApps.Shared.Payments.Provider.*.
Instalacja¶
dotnet add package TailoredApps.Shared.Payments
# Dodaj wybrane providery:
dotnet add package TailoredApps.Shared.Payments.Provider.Stripe
dotnet add package TailoredApps.Shared.Payments.Provider.PayU
dotnet add package TailoredApps.Shared.Payments.Provider.CashBill
Rejestracja w DI¶
// Program.cs
using TailoredApps.Shared.Payments;
using TailoredApps.Shared.Payments.Provider.Stripe;
using TailoredApps.Shared.Payments.Provider.PayU;
builder.Services
.AddPayments()
.RegisterPaymentProvider<StripeProvider>()
.RegisterPaymentProvider<PayUProvider>();
Przykład użycia¶
Inicjowanie płatności¶
public class CheckoutService
{
private readonly IPaymentService _payments;
public CheckoutService(IPaymentService payments) => _payments = payments;
public async Task<string> CreatePaymentAsync(CartDto cart, string email)
{
var response = await _payments.RegisterPayment(new PaymentRequest
{
PaymentProvider = "Stripe", // lub "PayU", "CashBill" itp.
PaymentChannel = "card",
PaymentModel = PaymentModel.OneTime,
Title = $"Zamówienie #{cart.OrderId}",
Description = "Zakup w sklepie MyShop",
Currency = "PLN",
Amount = cart.TotalAmount,
Email = email,
FirstName = cart.CustomerFirstName,
Surname = cart.CustomerLastName
});
// Przekieruj użytkownika na stronę płatności
return response.RedirectUrl;
}
public async Task<PaymentStatusEnum> CheckStatusAsync(string providerId, string paymentId)
{
var response = await _payments.GetStatus(providerId, paymentId);
return response.PaymentStatus;
}
}
Obsługa webhooków¶
[ApiController]
[Route("api/webhooks/payments")]
public class PaymentWebhookController : ControllerBase
{
private readonly IPaymentService _payments;
public PaymentWebhookController(IPaymentService payments) => _payments = payments;
[HttpPost("{providerKey}")]
public async Task<IActionResult> HandleWebhook(
string providerKey,
[FromBody] JsonElement body)
{
var request = new PaymentWebhookRequest
{
Body = body.GetRawText(),
Headers = Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()),
QueryParams = Request.Query.ToDictionary(q => q.Key, q => q.Value.ToString())
};
var result = await _payments.HandleWebhookAsync(providerKey, request);
return result switch
{
PaymentWebhookResult.Success => Ok(),
PaymentWebhookResult.Ignored => NoContent(),
_ => BadRequest()
};
}
}
Pobieranie dostępnych kanałów płatności¶
// Wyświetl dostępne metody płatności dla waluty PLN
var providers = await _payments.GetProviders();
foreach (var provider in providers)
{
var channels = await _payments.GetChannels(provider.Key, "PLN");
Console.WriteLine($"{provider.Name}: {string.Join(", ", channels.Select(c => c.Name))}");
}
API Reference¶
Core Interfaces¶
| Typ | Rodzaj | Opis |
|---|---|---|
IPaymentService |
Interfejs | Fasada: GetProviders, GetChannels, RegisterPayment, GetStatus, HandleWebhookAsync |
IPaymentProvider |
Interfejs | Kontrakt providera: Key, Name, GetPaymentChannels, RequestPayment, GetStatus, TransactionStatusChange |
IWebhookPaymentProvider |
Interfejs | Rozszerzenie: HandleWebhookAsync(PaymentWebhookRequest) |
IPaymentOptionsBuilder |
Interfejs | Fluent builder: RegisterPaymentProvider<T>() |
Models¶
| Typ | Rodzaj | Opis |
|---|---|---|
PaymentRequest |
Klasa | Dane płatności: provider, kanał, kwota, waluta, dane płatnika |
PaymentResponse |
Klasa | Wynik: RedirectUrl, PaymentUniqueId, PaymentStatus |
PaymentStatusEnum |
Enum | Created, Pending, Completed, Failed, Cancelled |
PaymentModel |
Enum | OneTime, Subscription |
PaymentChannel |
Klasa | Kanał płatności: Id, Name, Description, PaymentModel |
PaymentProvider |
Klasa | Metadane providera: Key, Name, Description, Url |
PaymentWebhookRequest |
Klasa | Dane HTTP żądania webhook: Body, Headers, QueryParams |
PaymentWebhookResult |
Enum | Success, Ignored, Fail |
DI Registration¶
| Metoda | Opis |
|---|---|
services.AddPayments() |
Rejestruje IPaymentService/PaymentService |
builder.RegisterPaymentProvider<T>() |
Dodaje implementację IPaymentProvider |
🤖 AI Agent Prompt¶
## TailoredApps.Shared.Payments — Instrukcja dla agenta AI
Używasz TailoredApps.Shared.Payments do integracji z bramkami płatności.
### Rejestracja
```csharp
builder.Services.AddPayments()
.RegisterPaymentProvider<StripeProvider>()
.RegisterPaymentProvider<PayUProvider>();
Inicjowanie płatności¶
var response = await _payments.RegisterPayment(new PaymentRequest
{
PaymentProvider = "Stripe", // klucz providera
PaymentChannel = "card",
Currency = "PLN",
Amount = 99.99m,
Email = "user@example.com",
Title = "Zamówienie #123"
});
string redirectUrl = response.RedirectUrl; // → przeglądarka
Sprawdzanie statusu¶
var status = await _payments.GetStatus("Stripe", paymentId);
// status.PaymentStatus: Created/Pending/Completed/Failed/Cancelled
Obsługa webhooków¶
var result = await _payments.HandleWebhookAsync(providerKey, new PaymentWebhookRequest
{
Body = rawBody,
Headers = httpHeaders,
QueryParams = queryParams
});
Zasady¶
- Każdy provider ma unikalny Key (np. "Stripe", "PayU", "CashBill")
- Zawsze sprawdź PaymentWebhookResult — Ignored to OK, Fail to błąd podpisu/konfiguracji
- Kwota Amount w walucie bazowej (nie w groszach — chyba że provider wymaga inaczej)
- Dla webhooków wymagany jest endpoint HTTP POST z raw body ```