tahsilat-dotnet 1.1.1

dotnet add package tahsilat-dotnet --version 1.1.1
                    
NuGet\Install-Package tahsilat-dotnet -Version 1.1.1
                    
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="tahsilat-dotnet" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="tahsilat-dotnet" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="tahsilat-dotnet" />
                    
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 tahsilat-dotnet --version 1.1.1
                    
#r "nuget: tahsilat-dotnet, 1.1.1"
                    
#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 tahsilat-dotnet@1.1.1
                    
#: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=tahsilat-dotnet&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=tahsilat-dotnet&version=1.1.1
                    
Install as a Cake Tool

Tahsilat .NET Library

Tahsilat Payment Gateway için resmi .NET Library.

NuGet

Gereksinimler

Platform Versiyon
.NET Framework 4.5.2, 4.6.2, 4.7.2, 4.8
.NET Standard 2.0, 2.1
.NET 8.0, 9.0

Not: .NET Standard 2.0/2.1 desteği sayesinde .NET 5, 6, 7, 10 ve sonraki tüm .NET sürümleri uyumludur.

Kurulum

NuGet ile

dotnet add package tahsilat-dotnet

Package Manager Console

Install-Package tahsilat-dotnet

Hızlı Başlangıç

Client Başlatma

//Doğrudan bir şekilde Controller ya da servis içinde kullanabilirsiniz.

using Tahsilat.NET;

// Sandbox (test) ortamı
var tahsilat = new TahsilatClient("sk_test_YOUR_SECRET_KEY");

// Production (canlı) ortamı
var tahsilat = new TahsilatClient("sk_live_YOUR_SECRET_KEY");

Dependency Injection (.NET 6+)

// Program.cs
using Tahsilat.NET.Extensions;

builder.Services.AddTahsilat(options =>
{
    options.ApiKey = "sk_test_YOUR_SECRET_KEY";
    options.TimeoutSeconds = 30; // Varsayılan: 30 saniye
});
// Controller veya Service içinde
public class PaymentController : Controller
{
    private readonly ITahsilatClient _tahsilat;

    public PaymentController(ITahsilatClient tahsilat)
    {
        _tahsilat = tahsilat;
    }
}

Önemli: Sadece secret key'ler (sk_test_* veya sk_live_*) kabul edilir. Public key'ler (pk_*) server-side API çağrıları için kullanılamaz.

Kullanım Örnekleri

Müşteri Oluşturma

var request = new CustomerCreateRequest
{
    Name = "Test",
    LastName = "User",
    Email = "testuser@mail.com",
    Phone = "+901234567890",
    Country = "TR",
    City = "İstanbul",
    District = "Sarıyer",
    Address = "Sarıyer, İstanbul",
    ZipCode = "34000",
    Metadata = new()
    {
        new Dictionary<string, object>
        {
            ["customer_name"] = "testuser",
            ["customer_type"] = "premium"
        },
        new Dictionary<string, object>
        {
            ["customer_created"] = "Today",
            ["source"] = "tahsilat-dotnet"
        }
    }
};

var response = await tahsilat.Customers.CreateAsync(request);

Müşteri servisinin diğer metotları:

var customer = await tahsilat.Customers.GetAsync(20585467989184);
var results  = await tahsilat.Customers.SearchAsync("testuser");   // ada/e-postaya göre arama
var updated  = await tahsilat.Customers.UpdateAsync(20585467989184, new CustomerUpdateRequest { City = "Ankara" });
var deleted  = await tahsilat.Customers.DeleteAsync(20585467989184); // bool döner

Ürün Oluşturma

var request = new ProductCreateRequest
{
    ProductName = "Test Product",
    Price = 75900, // Kuruş cinsinden: 759,00 TL
    Description = "Integration Test Product",
    Metadata = new()
    {
        new Dictionary<string, object>
        {
            ["product_name"] = "Test Product",
            ["product_type"] = "phone"
        },
        new Dictionary<string, object>
        {
            ["product_created"] = "Today",
            ["source"] = "tahsilat-dotnet"
        }
    }
};

var response = await tahsilat.Products.CreateAsync(request);

Ürün servisinin diğer metotları:

var product = await tahsilat.Products.GetAsync(55437751141488);
var results = await tahsilat.Products.SearchAsync("Test Product");
var updated = await tahsilat.Products.UpdateAsync(55437751141488, new ProductUpdateRequest { Price = 89900 });
var deleted = await tahsilat.Products.DeleteAsync(55437751141488); // bool döner

Ödeme Oluşturma

Ürün Bilgileri ile
var request = new PaymentCreateRequest
{
    Currency = "TRY",
    Amount = 70000,
    RedirectUrl = "https://example.com/payment/callback",
    Products = new List<ProductItem>
    {
        new ProductItem
        {
            ProductName = "Product1",
            Price = 50000,
            Description = "Test Product"
        },
        new ProductItem
        {
            ProductName = "Product2",
            Price = 10000,
            Description = "Test Product"
        },
        new ProductItem
        {
            ProductName = "Product3",
            Price = 10000,
            Description = "Test Product"
        }
    },
    Metadata = new()
    {
        new Dictionary<string, object>
        {
            ["order_id"] = 123456,
            ["customer_type"] = "premium"
        },
        new Dictionary<string, object>
        {
            ["created"] = "Subat2026",
            ["source"] = "Tahsilat-dotnet-test"
        }
    },
    Description = "Integration Test Product"
};

var response = await tahsilat.Payments.CreateAsync(request);
Kayıtlı Ürün ID'leri ile
var request = new PaymentCreateRequest
{
    Amount = 50000,
    Currency = "TRY",
    RedirectUrl = "https://example.com/payment/callback",
    ProductIds = new List<long>
    {
        55437751141488,
        84920468860151
    },
    CustomerId = 20585467989184,
    Metadata = new()
    {
        new Dictionary<string, object>
        {
            ["order_id"] = 123456,
            ["customer_type"] = "premium"
        },
        new Dictionary<string, object>
        {
            ["created"] = "Subat2026",
            ["source"] = "Tahsilat-dotnet-test"
        }
    },
};

var response = await tahsilat.Payments.CreateAsync(request);
İstek Alanları
Alan Zorunlu Açıklama
Amount ✅ Kuruş cinsinden toplam tutar (ör. 70000 = 700,00 TL)
Currency ✅ ISO 4217 para birimi kodu (ör. TRY)
Products ⚠️ Ürün listesi. ProductIds göndermiyorsanız zorunlu
ProductIds ⚠️ Kayıtlı ürün ID'leri. Products göndermiyorsanız zorunlu
RedirectUrl ❌ Ödeme sonrası dönülecek adres. Boş bırakılırsa Tahsilat'ın sonuç sayfası gösterilir
CustomerId ❌ İşlemi kayıtlı bir müşteriyle ilişkilendirir
PreAuth ❌ true ise tutar çekilmez, yalnızca bloke edilir (varsayılan false)
Description ❌ İşlem açıklaması
Metadata ❌ Raporlama için ek veri (en fazla 25 nesne)
Ödeme Yanıtının Kullanımı

CreateAsync bir PaymentResponse döner. Müşteriyi PaymentPageUrl adresine yönlendirmeniz gerekir:

var response = await tahsilat.Payments.CreateAsync(request);

Console.WriteLine(response.TransactionId);   // İşlem numarası — kendi tarafınızda saklayın
Console.WriteLine(response.PaymentPageUrl);  // Müşterinin yönlendirileceği ödeme sayfası
Console.WriteLine(response.ExpiresAt);       // Ödeme sayfasının geçerlilik süresi

// ASP.NET Core örneği
return Redirect(response.PaymentPageUrl);
Ödeme Sonrası Yönlendirme (RedirectUrl)

RedirectUrl opsiyoneldir ve davranışı gönderilip gönderilmemesine göre değişir:

Durum Ödeme sonrası ne olur
RedirectUrl verilir Müşteri sizin belirttiğiniz adrese döner
RedirectUrl boş bırakılır / gönderilmez Müşteri Tahsilat'ın kendi ödeme sonuç sayfasına yönlendirilir
// Müşteri kendi sitenize döner
var request = new PaymentCreateRequest
{
    Amount = 70000,
    Currency = "TRY",
    RedirectUrl = "https://example.com/payment/callback"
};

// RedirectUrl verilmezse müşteri Tahsilat'ın sonuç sayfasında kalır
var request = new PaymentCreateRequest
{
    Amount = 70000,
    Currency = "TRY"
};

Dikkat: Müşteriyi ödeme sonrasında kendi sitenize geri almak istiyorsanız RedirectUrl göndermek zorundasınız. Boş bırakırsanız akış Tahsilat'ta biter ve müşteri sitenize dönmez.

RedirectUrl adresi query parametresi olarak yalnızca transaction_id içermelidir. null bıraktığınızda alan istek gövdesine hiç yazılmaz.

Ön Provizyon (Pre-Auth)

PreAuth = true gönderirseniz tutar karttan çekilmez, yalnızca bloke edilir:

var request = new PaymentCreateRequest
{
    Amount = 50000,
    Currency = "TRY",
    RedirectUrl = "https://example.com/payment/callback",
    PreAuth = true
};

var response = await tahsilat.Payments.CreateAsync(request);

Bloke edilen tutarı sonradan kapatmanız (capture) veya iptal etmeniz (void) gerekir:

// Provizyonu kapat — tutar tahsil edilir
var approve = await tahsilat.Transactions.ResolvePreAuthAsync(new PreAuthResolveRequest
{
    TransactionId = 78810412652494,
    Status = true
});

// Provizyonu iptal et — bloke çözülür, tahsilat yapılmaz
var cancel = await tahsilat.Transactions.ResolvePreAuthAsync(new PreAuthResolveRequest
{
    TransactionId = 78810412652494,
    Status = false
});

if (approve.Status)
{
    Console.WriteLine(approve.Message);
}

ResolvePreAuthAsync bir ApiResponse<PreAuthResolveResponse> döner; sonucu Status ve Message alanlarından okuyun.

İşlem Sorgulama

var transaction = await tahsilat.Transactions.RetrieveAsync(78810412652494);

Console.WriteLine(transaction.TransactionId);
Console.WriteLine(transaction.PaymentStatusText); // success, fail, incomplete
Console.WriteLine(transaction.TransactionStatusText); // completed, pending, cancelled
Console.WriteLine(transaction.Amount);

// Başarı kontrolü
if (transaction.PaymentStatus == 1) { //Success
    Console.WriteLine("Ödeme Başarılı");
}

if (transaction.PaymentStatus == 2) {
    Console.WriteLine("Ödeme Başarısız.");
}

if (transaction.PaymentStatus == 3) {
    Console.WriteLine("Ödeme henüz tamamlanmadı.");
}

İade İşlemi

İade işlemlerinin tek giriş noktası tahsilat.Transactions.RefundAsync metodudur.

Tam İade

Amount alanı opsiyoneldir. Boş (null) bırakırsanız işlem tutarının tamamı iade edilir:

var request = new RefundCreateRequest
{
    TransactionId = 78810412652494,
    Description = "Müşteri talebi ile iade"
};

var response = await tahsilat.Transactions.RefundAsync(request);
Kısmi İade

Amount alanına değer verirseniz kısmi iade yapılır. Tutar kuruş cinsindendir, en az 100 (1,00 TL) olmalı ve işlem tutarını aşmamalıdır:

var request = new RefundCreateRequest
{
    TransactionId = 78810412652494,
    Amount = 1000, // Kısmi iade (10.00 TL)
    Description = "Müşteri talebi ile iade"
};

var response = await tahsilat.Transactions.RefundAsync(request);
Alan Zorunlu Açıklama
TransactionId ✅ İade edilecek işlemin ID'si
Amount ❌ Kuruş cinsinden iade tutarı (min 100). Boş bırakılırsa tam iade yapılır.
Description ✅ İade açıklaması (en fazla 255 karakter)
İade Yanıtının Okunması

RefundAsync, diğer metotlardan farklı olarak ApiResponse<RefundResponse> döner. Sonucu Status ve Message alanlarından okursunuz:

var response = await tahsilat.Transactions.RefundAsync(request);

if (response.Status)
{
    // "İade işlemi başarıyla gerçekleştirildi ve tutar bakiyenizden düşüldü."
    Console.WriteLine(response.Message);
}
else
{
    // Banka reddetti — iade beklemede kalır, tekrar denenebilir
    Console.WriteLine($"İade reddedildi: {response.Message}");
}

Önemli: İade endpoint'i Data alanını doldurmaz, her zaman null döner. İade kaydının detaylarına (tutar, banka referans kodu, durum) bu yanıttan erişemezsiniz. İşlemin güncel durumunu görmek için Transactions.RetrieveAsync(transactionId) ile işlemi yeniden sorgulayın ya da webhook'u dinleyin.

Önemli: Banka iadeyi reddederse HTTP hatası oluşmaz; Status alanı false gelir ve sebep Message içinde yer alır. Bu yüzden Status kontrolünü atlamayın — exception beklemek yeterli değildir.

Not: Bir işlem üzerinde önceki iade tamamlanmadan yeni iade başlatılamaz.

Not: Senkron kullanım için tahsilat.Transactions.Refund(request) metodunu kullanabilirsiniz.

BIN Sorgulama

var response = await tahsilat.BinLookup.DetailAsync(48945540);

Console.WriteLine(response.BankName);
Console.WriteLine(response.CardType);
Console.WriteLine(response.CardBrand);

Komisyon Sorgulama

var commissions = await tahsilat.Commissions.SearchAsync();

// BIN numarasına göre filtrele
var filtered = await tahsilat.Commissions.SearchAsync(new CommissionSearchRequest 
{ 
    BinNumber = 48945540 
});

Yanıt Satırındaki Kart Boyutu Alanları

Aynı komisyon oranı listede birden fazla kez görünebilir. Bunun sebebi, her satırın farklı bir kart senaryosuna ait olmasıdır. Bir satır şu kombinasyonla tekilleşir: Installment + CardType + IsOnUs + IsForeign.

Alan Tip Anlam
CompanyPosCredentialId long? Oranın ait olduğu POS kredensiyalinin kimliği
PosId long? Oranın ait olduğu POS entegrasyonunun kimliği
PosName string Oranın ait olduğu POS'un adı (ör. Ziraat Pay Pos)
InstallmentText string Taksit açıklaması (ör. Tek çekim)
CardType string Oranın geçerli olduğu kart türü: credit / debit / prepaid. null = tüm kart türleri
IsOnUs bool? true = kartı çıkaran bankanın kendi POS'una (on-us) ait oran, false = on-us değil, null = her ikisi için geçerli
IsForeign bool true = yabancı (yurt dışı) kart oranı, false = yerli kart

Dikkat: CardType ve IsOnUs alanlarında null bir eksiklik değil, anlamlı bir değerdir. CardType == null "her kart türü için geçerli", IsOnUs == null ise "hem on-us hem not-on-us için geçerli" demektir.

CardType karşılaştırması için CardTypes sabitlerini kullanabilirsiniz:

using Tahsilat.NET.Models.Common;

// Yerli kredi kartı, tek çekim oranları
var credit = commissions
    .Where(c => c.Installment == 1)
    .Where(c => !c.IsForeign)
    .Where(c => c.CardType == CardTypes.Credit || c.CardType == null)
    .ToList();

foreach (var c in credit)
{
    Console.WriteLine($"{c.PosName} · {c.InstallmentText} · %{c.CommissionRate}");
}

CardTypes sabitleri (Credit, Debit, Prepaid) yalnızca bilinen değerleri içerir. API ileride yeni bir kart türü ekleyebileceği için CardType alanını string olarak karşılaştırın, tüm olasılıkları kapsayan bir switch yazmayın.

Çoklu POS Davranışı

BinNumber göndermediğiniz istekte liste, sadece birincil POS'un değil, üye işyerinin tüm aktif POS'larının oranlarını döner. Bu yüzden aynı taksit sayısı birden fazla satırda görünebilir:

var all = await tahsilat.Commissions.SearchAsync();

// POS bazında grupla
foreach (var group in all.GroupBy(c => c.PosName))
{
    Console.WriteLine($"--- {group.Key} ---");
    foreach (var c in group.OrderBy(c => c.Installment))
        Console.WriteLine($"{c.InstallmentText}: %{c.CommissionRate}");
}

BinNumber gönderdiğiniz istekte ise her satır, o taksit için kazanan POS'u gösterir.

Taksit sayısına göre tek bir oran seçen mevcut kodunuz (commissions.First(c => c.Installment == 3) gibi) artık rastgele bir POS'un oranını dönebilir. Böyle bir yerde PosName / PosId ile filtreleme yapın.

Hata Yönetimi


using Tahsilat.NET.Exceptions;

try
{
    var payment = await tahsilat.Payments.CreateAsync(new PaymentCreateRequest
    {
        Amount = 10000,
        Currency = "TRY",
        RedirectUrl = "https://example.com/payment/callback"
    });
}
catch (TahsilatAuthenticationException ex)
{
    // Geçersiz API key (401)
    Console.WriteLine($"Kimlik doğrulama hatası: {ex.Message}");
    Console.WriteLine($"HTTP Durum Kodu: {ex.StatusCode}");
}
catch (TahsilatValidationException ex)
{
    // Geçersiz istek parametreleri (422)
    Console.WriteLine($"Validasyon hatası: {ex.Message}");
    Console.WriteLine($"Hata Kodu: {ex.ErrorCode}");
}
catch (TahsilatNotFoundException ex)
{
    // Kaynak bulunamadı (404)
    Console.WriteLine($"Bulunamadı: {ex.Message}");
}
catch (TahsilatPaymentException ex)
{
    // Ödeme işlemi hatası
    Console.WriteLine($"Ödeme hatası: {ex.Message}");
    Console.WriteLine($"Hata Kodu: {ex.ErrorCode}");
}
catch (TahsilatRateLimitException ex)
{
    // Rate limit aşıldı (429)
    Console.WriteLine($"Rate limit: {ex.Message}");
    Console.WriteLine($"Tekrar deneme süresi: {ex.RetryAfterSeconds} saniye");
}
catch (TahsilatNetworkException ex)
{
    // API'den 424 yanıtı döndü (banka/sağlayıcı tarafında bağımlılık hatası)
    // DİKKAT: gerçek bağlantı sorunları ve timeout bu bloğa DÜŞMEZ,
    // aşağıdaki "Timeout ve Bağlantı Hataları" bölümüne bakın
    Console.WriteLine($"Bağımlılık hatası: {ex.Message}");
}
catch (TahsilatApiException ex)
{
    // Diğer API hataları (5xx vb.)
    Console.WriteLine($"API Hatası: {ex.Message}");
    Console.WriteLine($"HTTP Durum Kodu: {ex.StatusCode}");
    Console.WriteLine($"Hata Kodu: {ex.ErrorCode}");
}
catch (TahsilatException ex)
{
    // Genel SDK hatası (tüm Tahsilat exception'larının base sınıfı)
    Console.WriteLine($"Hata: {ex.Message}");
    Console.WriteLine($"Hata Kodu: {ex.ErrorCode}");
}

Exception Hiyerarşisi

Exception Açıklama Özel Property'ler
TahsilatException Tüm SDK hatalarının base sınıfı ErrorCode
├─ TahsilatAuthenticationException Geçersiz API key (401) StatusCode
├─ TahsilatValidationException Geçersiz istek parametreleri (422) —
├─ TahsilatNotFoundException Kaynak bulunamadı (404) —
├─ TahsilatPaymentException Ödeme işlemi hatası —
├─ TahsilatRateLimitException İstek limiti aşıldı (429) RetryAfterSeconds
├─ TahsilatNetworkException API'den 424 yanıtı (bağımlılık hatası) —
├─ TahsilatApiException Diğer API hataları StatusCode
└─ TahsilatWebhookException Webhook doğrulama hatası —

Not: İade endpoint'i pratikte 200, 400, 403, 404, 422, 429, 500 durum kodlarını döndürür; 402 ve 424 döndürmez. SDK'daki TahsilatPaymentException (402) ve TahsilatNetworkException (424) eşlemeleri savunma amaçlı genel eşlemelerdir ve diğer endpoint'ler için korunmaktadır.

Timeout ve Bağlantı Hataları

Yukarıdaki exception'ların tamamı, API'den bir yanıt döndüğü durumları temsil eder. Yanıtın hiç dönmediği iki durum Tahsilat exception hiyerarşisinin dışında kalır ve ham .NET exception'ları olarak fırlar:

Durum Fırlayan exception
TimeoutSeconds süresi aşıldı TaskCanceledException
DNS çözülemedi, bağlantı reddedildi, SSL hatası HttpRequestException

Bu ikisi catch (TahsilatException) ile yakalanmaz; ayrıca ele almanız gerekir.

SDK otomatik tekrar deneme yapmaz. Bu bilinçli bir tercihtir: timeout, isteğin başarısız olduğu anlamına gelmez — istek sunucuya ulaşmış ve işlenmiş, yanıt size dönerken süre aşılmış olabilir.

Uyarı: Timeout alan bir mutasyon çağrısını (ödeme oluşturma, iade, ön provizyon kapatma) körlemesine tekrarlamayın. İşlem sunucuda gerçekleşmiş olabilir; tekrar etmek ikinci bir iade ya da ikinci bir ödeme oluşturabilir.

Doğru yaklaşım — tekrar denemeden önce işlemin güncel durumunu sorgulayın:

using Tahsilat.NET.Extensions;

try
{
    var response = await tahsilat.Transactions.RefundAsync(request);
}
catch (TaskCanceledException)
{
    // Timeout: iade sunucuda gerçekleşmiş OLABİLİR — doğrulamadan tekrar etme
    var transaction = await tahsilat.Transactions.RetrieveAsync(request.TransactionId);

    if (transaction.HasRefund())
        return; // İade zaten işlenmiş, tekrar gönderme

    // İade işlenmemiş, güvenle tekrar denenebilir
}
catch (HttpRequestException)
{
    // İstek sunucuya hiç ulaşmadı — tekrar denemek güvenlidir
}

Bağlantının hiç kurulamadığı durumlarda istek sunucuya ulaşmadığı için tekrar denemek güvenlidir. Belirsiz olan tek durum timeout'tur; orada mutlaka önce sorgulayın.

TimeoutSeconds varsayılan olarak 30 saniyedir ve TahsilatClientOptions üzerinden değiştirilebilir.

API Key Türleri

Key Türü Format Kullanım
Secret Test sk_test_* Test ortamı - tam erişim
Secret Live sk_live_* Canlı ortam - tam erişim

Not: Public key'ler (pk_test_*, pk_live_*) bu SDK ile kullanılamaz. Client-side işlemler için JavaScript SDK kullanın.

Webhook Doğrulama

Uyarı: Webhook endpoint'iniz harici bir POST isteği aldığı için CSRF korumasından muaf tutulmalıdır.

Her webhook isteği X-Tahsilat-Signature başlığı ile HMAC-SHA256 imzası içerir. İmza formatı: t=timestamp,v1=signature.

using Tahsilat.NET.Exceptions;
using Tahsilat.NET.Webhooks;

[HttpPost("webhook")]
public async Task<IActionResult> Webhook()
{
    // 1. Request body'yi oku
    using var ms = new MemoryStream();
    await Request.Body.CopyToAsync(ms);
    var payloadBytes = ms.ToArray();

    // 2. Signature header'ını al
    var signature = Request.Headers["X-Tahsilat-Signature"].FirstOrDefault() ?? string.Empty;

    try
    {
        // 3. Webhook event'i doğrula ve parse et
        var webhookEvent = WebhookHandler.ConstructEvent(payloadBytes, signature, "whsec_YOUR_WEBHOOK_SECRET");

        // 4. Ödeme durumuna göre işlem yap
        if (webhookEvent.IsSuccess())
        {
            // Ödeme başarılı
            Console.WriteLine($"Ödeme başarılı! Transaction ID: {webhookEvent.TransactionId}");
            Console.WriteLine($"Tutar: {webhookEvent.Amount} {webhookEvent.CurrencyCode}");
        }
        else if (webhookEvent.IsFailed())
        {
            // Ödeme başarısız
            Console.WriteLine($"Ödeme başarısız. Transaction ID: {webhookEvent.TransactionId}");
        }

        return Ok();
    }
    catch (TahsilatWebhookException ex)
    {
        // İmza doğrulaması başarısız
        return BadRequest(new { error = "Invalid signature" });
    }
}

Not: IsSuccess(), IsFailed(), IsPending(), IsRefunded() gibi extension metotları ile ödeme ve işlem durumunu kolayca kontrol edebilirsiniz.

Senkron Kullanım

Tüm servisler hem asenkron hem de senkron metotları destekler. Eski .NET Framework projelerinde async/await kullanamıyorsanız:

// Senkron kullanım
var response = tahsilat.Payments.Create(request);
var transaction = tahsilat.Transactions.Retrieve(transactionId);
var customer = tahsilat.Customers.Create(customerRequest);

Güvenlik

  • 🔒 Tüm API iletişimi HTTPS üzerinden zorunludur
  • 🔑 API anahtarları sk_test_ / sk_live_ prefix kontrolü ile doğrulanır
  • 🛡️ Webhook imzaları HMAC-SHA256 ile doğrulanır
  • ⏱️ Webhook replay koruması (timestamp toleransı)
  • 🔐 Constant-time karşılaştırma ile timing attack koruması

Ortam Ayrımı

SDK, API anahtarınızın prefix'ine göre ortamı otomatik belirler:

Prefix Ortam API URL
sk_test_ Sandbox https://api.sandbox.tahsilat.com/v1/
sk_live_ Production https://api.tahsilat.com/v1/

Lisans

MIT License - detaylar için LICENSE dosyasına bakın.

Destek

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  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 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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net452 is compatible.  net46 was computed.  net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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.

Version Downloads Last Updated
1.1.1 94 9/13/2026
1.1.0 124 8/16/2026
1.0.1 134 6/21/2026
1.0.0 125 3/5/2026

Updated v1.1.1 =>
- Documentation only, no code changes. The v1.1.0 README incorrectly stated that TahsilatNetworkException covers connection failures and timeouts. It does not: it maps to an HTTP 424 response. A timeout surfaces as TaskCanceledException and a connection failure as HttpRequestException, and neither is caught by catch (TahsilatException)
- Added a "Timeout ve Baglanti Hatalari" section explaining that the SDK never retries automatically, that a timed out mutating call (payment, refund, pre-auth resolve) may still have been processed by the server, and that you must re-query the transaction before retrying instead of resending blindly

Updated v1.1.0 =>
- BREAKING: removed the unused RefundService and the TahsilatClient.Refunds property. Refunds now have a single entry point: Transactions.RefundAsync(request) / Transactions.Refund(request)
- RefundCreateRequest.Amount is now nullable (int?). Leave it null to refund the full transaction amount; provide a value (in kurus, min 100) for a partial refund
- Documented that the refund endpoint never populates the response Data field (it is always null). Read the outcome from Status and Message; a bank rejection returns Status false instead of an HTTP error
- PaymentCreateRequest.RedirectUrl is now string? to match its optional semantics. If omitted, the customer is redirected to Tahsilat's own payment result page instead of your site
- BREAKING: CommissionResponse.CompanyPosCredentialId is now nullable (long?). Use ?? 0 or .Value where you previously assigned it to a long
- CommissionResponse: added the card dimension fields that make each rate unique - PosId, PosName, CardType, IsOnUs, IsForeign - plus the previously missing InstallmentText. A row is now identified by Installment + CardType + IsOnUs + IsForeign
- CardType is null when the rate applies to all card types; IsOnUs is null when it applies both to on-us and not-on-us transactions. Compare CardType against the new Tahsilat.NET.Models.Common.CardTypes constants (Credit/Debit/Prepaid)
- BEHAVIOUR: Commissions.SearchAsync() without a BinNumber now returns the rates of every active POS of the merchant instead of the primary POS only, so the same Installment can appear on multiple rows. Use PosName/PosId to tell them apart

Updated v1.0.1 =>
- TransactionResult: added ChargedAmount, FormattedChargedAmount fields and GetChargedAmountDecimal() helper
- RefundResponse: rewritten to expose full refund record (id, refund_amount, status, bank_response_*, currency_code, etc.)
- CommissionResponse: added TransactionFee field
- BinLookupResponse: added CardFamilyLogoPath field
- CustomerResponse: added Ip, FormattedUpdatedAt fields
- ProductResponse: added CurrencyId, ProductImageUrl fields