Metadata-Version: 2.4
Name: moka-python
Version: 1.0.1
Summary: Moka United sanal POS API'si icin Python istemcisi. Bu kütüphane bağımsız olarak geliştirilmiştir; Moka United'ın resmi ürünü değildir, Moka United tarafından geliştirilmemiş, onaylanmamış ve desteklenmemektedir.
Author-email: Hasan Cagri Gungor <hasancagrigungor@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/hasancagrigungor/moka-python
Project-URL: Documentation, https://github.com/hasancagrigungor/moka-python#readme
Project-URL: Source, https://github.com/hasancagrigungor/moka-python
Project-URL: Issues, https://github.com/hasancagrigungor/moka-python/issues
Project-URL: Moka United API, https://developer.mokaunited.com
Keywords: moka,moka united,sanal pos,odeme,payment,virtual pos,turkey
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: Turkish
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Point-Of-Sale
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# moka-python

> **Bu kütüphane bağımsız olarak geliştirilmiştir; Moka United'ın resmi ürünü değildir, Moka United tarafından geliştirilmemiş, onaylanmamış ve desteklenmemektedir.**

Moka United sanal POS API'si icin Python istemcisi.

Bu kutuphane, [Moka United API](https://developer.mokaunited.com) servislerine Python uygulamalarindan erisim saglar. Resmi PHP istemcisindeki (moka-php) tum servisleri ve istek modellerini birebir kapsar. Harici hicbir bagimliligi yoktur; yalnizca Python standart kutuphanesini kullanir.

## Ozellikler

- 3D Secure olmadan odeme (Non-3D)
- 3D Secure ile odeme
- 3D Secure ile mobil odeme
- On provizyon ve capture islemi
- Havuz odemesi onaylama ve onay iptali
- Odeme iptali (void) ve iade talebi
- Odeme guncelleme
- Odeme linki olusturma (odeme istegi gonderme)
- Odeme listesi, transaction listesi ve odeme detay sorgulama
- Bin sorgulama, taksit tablosu ve karttan cekilecek tutar hesaplama
- Kart saklama servisleri (kart ekleme, guncelleme, silme, listeleme)
- Musteri yonetimi servisleri
- Tekrarlayan odeme servisleri (satis, takvim, odeme plani, urun)
- Bayi bilgisi, muhasebe ve ekstre raporlari
- CheckKey uretimi ve 3D Secure hash dogrulamasi
- Moka United test kartlari ve banka hata kodlari sozlugu

## Gereksinimler

- Python 3.8 ve uzeri

## Kurulum

```bash
pip install moka-python
```

Kaynak koddan kurulum:

```bash
cd moka-python
pip install .
```

## Ortam Adresleri

| Ortam | Adres |
| --- | --- |
| Test ortami | https://service.refmokaunited.com |
| Canli ortam | https://service.mokaunited.com |

Istemci varsayilan olarak canli ortama baglanir. Test ortami icin `base_url` parametresi verilmelidir. Eski `service.moka.com` ve `service.refmoka.com` adresleri icin `LEGACY_API_BASE` ve `LEGACY_TEST_API_BASE` sabitleri de mevcuttur.

Moka United servisleri PCI-DSS kurallari geregi yalnizca TLS 1.2 ve ustu protokollere izin verir. Bu kutuphanenin HTTP katmani TLS 1.2 zorunlulugunu otomatik olarak uygular.

## Hizli Baslangic

```python
from moka import MokaClient, TEST_API_BASE, models

client = MokaClient(
    dealer_code="bayi kodunuz",
    username="api kullanici adiniz",
    password="api sifreniz",
    base_url=TEST_API_BASE,  # canli ortam icin bu satiri kaldirin
)
```

Kimlik dogrulamada kullanilan CheckKey degeri (DealerCode + "MK" + Username + "PD" + Password bilgisinin SHA-256 ozeti) her istekte otomatik olarak uretilir ve eklenir.

### Alan adlari

Istek modelleri, Moka United dokumantasyonundaki alan adlariyla (PascalCase) birebir ayni alanlari tasir. Alanlar hem API'deki adiyla hem de Python uslubundaki snake_case adiyla kullanilabilir:

```python
istek = models.CreatePaymentRequest(CardNumber="...", Amount=10)
istek.card_holder_full_name = "Ali Yilmaz"   # snake_case
istek.ClientIP = "192.168.1.116"             # PascalCase
```

### Yanit nesnesi

Tum servisler `ApiResponse` nesnesi dondurur:

| Alan | Aciklama |
| --- | --- |
| `data` | Istek basarili ise servis verisi (dict), aksi halde None |
| `result_code` | Basarili istekte "Success", hatada Moka hata kodu |
| `result_message` | Hataya iliskin varsa aciklama |
| `exception` | Beklenmeyen hata olustugunda (EX) aciklama |
| `is_success` | Istek Moka United tarafinda islendiyse True |
| `is_payment_successful` | Istek ve banka islemi birlikte basariliysa True |

Onemli: `is_success` yalnizca istegin Moka United tarafinda islendigini gosterir. Odeme islemlerinde bankanin islemi onaylayip onaylamadigini gormek icin `is_payment_successful` ozelligi veya `data["IsSuccessful"]` alani kontrol edilmelidir.

## Odeme Islemleri

### 3D Secure Olmadan Odeme (Non-3D)

```python
from moka import models

istek = models.CreatePaymentRequest(
    CardHolderFullName="Ali Yilmaz",
    CardNumber="5127541122223332",
    ExpMonth="12",
    ExpYear="2030",
    CvcNumber="000",
    Amount=0.01,
    Currency="TL",
    InstallmentNumber=1,
    ClientIP="192.168.1.116",
    OtherTrxCode="SIPARIS-2026-0001",
    IsPoolPayment=0,
    IsTokenized=0,
    Software="yazilim adiniz",
    IsPreAuth=0,
    BuyerInformation=models.Buyer(
        BuyerFullName="Ali Yilmaz",
        BuyerGsmNumber="5551110022",
        BuyerEmail="ali@ornek.com",
        BuyerAddress="Tasdelen / Cekmekoy",
    ),
)

yanit = client.payments().create(istek)

if yanit.is_payment_successful:
    # Iptal, iade ve havuz onayi islemleri icin bu deger saklanmalidir
    siparis_no = yanit.data["VirtualPosOrderId"]
elif yanit.is_success:
    # Banka islemi reddetti
    print(yanit.data["ResultCode"], yanit.data["ResultMessage"])
else:
    # Istek Moka United tarafinda islenemedi
    print(yanit.result_code, yanit.result_message)
```

### 3D Secure ile Odeme

3D odemede `ReturnHash=1` ve `RedirectUrl` zorunludur. Yanittaki `Url` degerine kullanici yonlendirilir; `CodeForHash` degeri veritabaninda saklanir.

```python
istek = models.CreatePaymentRequest(
    CardHolderFullName="Ali Yilmaz",
    CardNumber="5127541122223332",
    ExpMonth="12",
    ExpYear="2030",
    CvcNumber="000",
    Amount=100.50,
    Currency="TL",
    InstallmentNumber=1,
    ClientIP="192.168.1.116",
    OtherTrxCode="SIPARIS-2026-0002",
    Software="yazilim adiniz",
    ReturnHash=1,
    RedirectUrl="https://www.siteniz.com/odeme-sonucu?islem=SIPARIS-2026-0002",
    RedirectType=0,
)

yanit = client.payments().create_threeds(istek)

if yanit.is_success:
    yonlendirme_adresi = yanit.data["Url"]
    code_for_hash = yanit.data["CodeForHash"]  # saklayin
```

Kart dogrulamasi tamamlandiginda Moka United, `RedirectUrl` adresinize `hashValue`, `resultCode`, `resultMessage`, `trxCode` ve `OtherTrxCode` alanlarini POST eder. Sonuc su sekilde dogrulanir:

```python
from moka import verify_threeds_result

sonuc = verify_threeds_result(code_for_hash, gelen_hash_value)

if sonuc is True:
    # SHA256(CodeForHash + "T") eslesti: odeme basarili
    ...
elif sonuc is False:
    # SHA256(CodeForHash + "F") eslesti: odeme basarisiz
    ...
else:
    # Hash eslesmedi: istek gecersiz veya kurcalanmis
    ...
```

Basarili islemde `trxCode` alaninda donen OrderId degeri saklanmalidir; iptal, iade ve havuz onayi islemleri bu degerle yapilir.

### 3D Secure ile Mobil Odeme

```python
istek = models.CreateMobilePaymentRequest(
    PaymentType=1,
    Amount=100,
    Currency="TL",
    InstallmentNumber=1,
    ClientIP="192.168.1.116",
    RedirectURL="https://www.siteniz.com/odeme-sonucu",
    OtherTrxCode="SIPARIS-2026-0003",
    Software="yazilim adiniz",
)

yanit = client.payments().create_threeds_mobile(istek)
```

### On Provizyon ve Capture

On provizyon icin odeme istegi `IsPreAuth=1` ile gonderilir; daha sonra capture ile odemeye cevrilir:

```python
istek = models.CaptureRequest(
    VirtualPosOrderId="ORDER-...",
    OtherTrxCode="",
    Amount=100.50,
    ClientIP="192.168.1.116",
)

yanit = client.payments().capture(istek)
```

### Havuzdaki Odemeyi Onaylama ve Onay Iptali

Havuz odemesi icin odeme istegi `IsPoolPayment=1` ile gonderilir. Urun veya hizmet teslim edildikten sonra odeme onaylanir:

```python
yanit = client.payments().approval(
    models.ApprovalRequest(VirtualPosOrderId="ORDER-...")
)

yanit = client.payments().cancel_approval(
    models.CancelApprovalRequest(VirtualPosOrderId="ORDER-...")
)
```

### Odeme Iptali ve Iade

```python
# Iptal (void)
yanit = client.payments().cancel(
    models.CancelPaymentRequest(
        VirtualPosOrderId="ORDER-...",
        ClientIP="192.168.1.116",
        VoidRefundReason=2,
    )
)

# Iade talebi (tutar verilerek kismi iade de yapilabilir)
yanit = client.refunds().create(
    models.CreateRefundRequest(
        VirtualPosOrderId="ORDER-...",
        Amount=50.25,
    )
)
```

### Odeme Guncelleme

```python
yanit = client.payments().update(
    models.UpdatePaymentRequest(
        DealerPaymentId=12345,
        Description="Guncellenmis aciklama",
    )
)
```

### Odeme Linki Olusturma

```python
istek = models.CreatePaymentLinkRequest(
    OtherTrxCode="SIPARIS-2026-0004",
    FullName="Ali Yilmaz",
    GsmNumber="5551110022",
    Email="ali@ornek.com",
    Amount=250,
    Currency="TL",
    IsThreeD=1,
)

yanit = client.payment_links().create(istek)
```

## Bilgi Alma Islemleri

```python
# Odeme listesi
yanit = client.payments().all(
    models.RetrievePaymentListRequest(
        PaymentStartDate="2026-01-01",
        PaymentEndDate="2026-01-31",
    )
)

# Transaction listesi
yanit = client.transactions().all(
    models.RetrieveTransactionListRequest(
        TrxStartDate="2026-01-01",
        TrxEndDate="2026-01-31",
    )
)

# Odeme detayi (kendi islem kodunuzla sorgulayabilirsiniz)
yanit = client.payments().retrieve(
    models.RetrievePaymentDetailRequest(OtherTrxCode="SIPARIS-2026-0001")
)

# Bin sorgulama
yanit = client.bin_number().retrieve(
    models.RetrieveBinNumberRequest(BinNumber="512754")
)

# Taksit tablosu hesaplama
yanit = client.payments().retrieve_installment_info(
    models.RetrieveInstallmentInfoRequest(
        BinNumber="512754",
        Currency="TL",
        OrderAmount=1000,
        IsThreeD=1,
    )
)

# Karttan cekilecek tutar hesaplama
yanit = client.payments().retrieve_amount(
    models.RetrievePaymentAmountRequest(
        BinNumber="512754",
        Currency="TL",
        OrderAmount=1000,
        InstallmentNumber=3,
        IsThreeD=1,
    )
)
```

## Kart Saklama Servisleri

Kart saklama servislerini kullanabilmek icin bayinin Moka United tarafinda kart saklama hizmetinin aktive edilmis olmasi gerekir.

```python
# Musteri ekleme
yanit = client.customers().create(
    models.CreateCustomerRequest(
        CustomerCode="MUSTERI-1",
        FirstName="Ali",
        LastName="Yilmaz",
        Email="ali@ornek.com",
    )
)

# Kartiyla birlikte musteri ekleme
yanit = client.customers().create_with_card(
    models.CreateCustomerWithCardRequest(
        CustomerCode="MUSTERI-2",
        FirstName="Veli",
        LastName="Yilmaz",
        CardHolderFullName="Veli Yilmaz",
        CardNumber="5127541122223332",
        ExpMonth="12",
        ExpYear="2030",
        CardName="Is kartim",
    )
)

# Musteriye kart ekleme
yanit = client.cards().create(
    models.CreateCardRequest(
        CustomerCode="MUSTERI-1",
        CardHolderFullName="Ali Yilmaz",
        CardNumber="5127541122223332",
        ExpMonth="12",
        ExpYear="2030",
        CardName="Maximum kartim",
    )
)

# Kart listesi, bilgi alma, guncelleme, silme
yanit = client.cards().all(models.RetrieveCardListRequest(CustomerCode="MUSTERI-1"))
yanit = client.cards().retrieve(models.RetrieveCardRequest(CardToken="..."))
yanit = client.cards().update(models.UpdateCardRequest(CardToken="...", CardName="Yeni ad"))
yanit = client.cards().delete(models.DeleteCardRequest(CardToken="..."))

# Musteri listesi, bilgi alma, guncelleme, silme
yanit = client.customers().all()
yanit = client.customers().retrieve(models.RetrieveCustomerRequest(CustomerCode="MUSTERI-1"))
yanit = client.customers().update(models.UpdateCustomerRequest(CustomerCode="MUSTERI-1", Email="yeni@ornek.com"))
yanit = client.customers().delete(models.DeleteCustomerRequest(CustomerCode="MUSTERI-1"))
```

Sakli kartla odeme yapmak icin odeme isteginde kart bilgileri yerine `CardToken` gonderilir.

## Tekrarlayan Odeme Servisleri

```python
# Urun tanimlama
yanit = client.products().create(
    models.CreateProductRequest(ProductName="Aylik Uyelik", ProductCode="UYELIK-AY")
)

# Takvim tanimlama (ornek: her ayin 1'i)
yanit = client.schedules().create(
    models.CreateScheduleRequest(
        ScheduleName="Aylik",
        DailyWeeklyMonthly=3,
        EveryX=1,
        DaysOfMonth="1",
    )
)

# Satis olusturma
yanit = client.sales().create(
    models.CreateSaleRequest(
        CustomerCode="MUSTERI-1",
        ProductCode="UYELIK-AY",
        SaleCode="SATIS-1",
        Amount=99.90,
        Currency="TL",
    )
)

# Odeme plani islemleri
yanit = client.payment_plans().all(models.RetrievePaymentPlanListRequest(SaleCode="SATIS-1"))
yanit = client.payment_plans().create(models.CreatePaymentPlanRequest(SaleCode="SATIS-1", PaymentDate="2026-08-01", Amount=99.90))
yanit = client.payment_plans().retrieve_history(models.RetrievePaymentPlanHistoryListRequest(DealerPaymentPlanId=1))
```

## Muhasebe ve Raporlama

```python
# Bayi muhasebesi
yanit = client.reporting().accounting(
    models.ReportingAccountingListRequest(
        TransferStartDate="2026-01-01",
        TransferEndDate="2026-01-31",
    )
)

# Bayi ekstresi
yanit = client.reporting().statement(
    models.ReportingStatementListRequest(
        StatementStartDate="2026-01-01",
        StatementEndDate="2026-01-31",
    )
)

# Bayi bilgisi
yanit = client.dealers().retrieve(models.RetrieveDealerRequest())
```

## Hata Kodlari

Bankadan donen islem hatalarinin Turkce karsiliklari `moka.error_codes` modulunde yer alir:

```python
from moka import get_bank_error_message

mesaj = get_bank_error_message("002")  # "Limit Yetersiz"
```

Moka United tarafindan donen servis hata kodlari (ornegin `PaymentDealer.CheckPaymentDealerAuthentication.InvalidAccount`) `ApiResponse.result_code` alaninda bulunur; tam liste icin [Moka United dokumantasyonuna](https://developer.mokaunited.com) bakiniz.

## Test Kartlari

Test kartlariyla yapilan odemeler bankaya gonderilmez; cevap Moka United sisteminden doner. Guncel test karti listesi icin resmi dokumantasyona bakiniz (liste zaman icinde degisebilir):

https://developer.mokaunited.com/home.php?page=test-kartlari

Gelistirme kolayligi icin kartlara kod icinden `moka.test_cards.TEST_CARDS` listesiyle veya `get_test_card` fonksiyonuyla da erisilebilir:

```python
from moka import get_test_card

kart = get_test_card(bank="Garanti Bankasi")
kart = get_test_card(card_type="Troy")
```

## Testler

Birim testler ag baglantisi gerektirmez:

```bash
python -m unittest discover -s tests -v
```

Moka United test ortamina karsi gercek istek atan canli testler, asagidaki ortam degiskenleri tanimlandiginda otomatik olarak calisir:

```bash
export MOKA_DEALER_CODE="bayi kodunuz"
export MOKA_USERNAME="api kullanici adiniz"
export MOKA_PASSWORD="api sifreniz"
python -m unittest tests.test_live -v
```

## PyPI Yayinlama (twine)

```bash
pip install build twine
python -m build
twine upload dist/*
```

## Ornek Kodlar

`samples/` klasorunde calistirilabilir ornekler yer alir:

- `create_payment.py`: Non-3D odeme
- `create_threeds_payment.py`: 3D Secure ile odeme
- `retrieve_bin.py`: Bin sorgulama
- `retrieve_installment_info.py`: Taksit tablosu hesaplama

## Lisans

MIT lisansi ile dagitilmaktadir. Ayrintilar icin LICENSE dosyasina bakiniz.
