MercanPay Blog

Python ile Kripto Ödeme Entegrasyonu: Flask ve Django Örneği

Python ile kripto ödeme entegrasyonu: pip install mercanpay ile USDT/TRX faturası oluşturma, webhook doğrulama ve sipariş onayını Flask ve Django ile öğrenin.

Yayınlanma: 30 Eylül 20265 dk okumaRead in English
Python ile Kripto Ödeme Entegrasyonu: Flask ve Django Örneği

Python ile kripto ödeme entegrasyonu, resmi mercanpay paketi sayesinde birkaç satırlık bir iştir. Siparişte bir fatura açar, müşteriyi ödeme sayfasına yönlendirir, imzalı webhook ile de siparişi onaylarsınız. Bu rehberde USDT ve TRX (TRC20) ödemelerini Flask ve Django uygulamalarına adım adım bağlıyoruz.

Gereksinimler

  • Python 3 ve pip
  • Onaylı bir MercanPay satıcı hesabı (başvuru)
  • Panelden oluşturulmuş, invoices yetkili bir API anahtarı
  • Ayarlar sayfasındaki webhook imza sırrı (whsec_...)

1. Paketi kurun

pip install mercanpay

Paket yalnızca requests kütüphanesine dayanır. Gizli bilgileri ortam değişkenlerinde tutun:

export MERCANPAY_API_KEY="mp_..."
export MERCANPAY_WEBHOOK_SECRET="whsec_..."

2. İstemciyi oluşturun

import os
from mercanpay import MercanPay, MercanPayError, verify_webhook

zp = MercanPay("https://mercanpay.com", os.environ["MERCANPAY_API_KEY"])

İstemci varsayılan olarak 15 saniyelik zaman aşımı kullanır. Gerekirse MercanPay(base_url, api_key, timeout=10) ile değiştirebilirsiniz.

3. Python ile kripto ödeme faturası oluşturun

create_invoice metodu iki kullanım biçimini destekler:

# USD fiyatlı: müşteri ödeme sayfasında TRX veya USDT seçer, kur kilitlenir
inv = zp.create_invoice(
    price_usd="49.90",
    order_id="order-1001",
    description="Pro lisans (1 yıl)",
    success_url="https://magazam.com/siparis/1001/tesekkurler",
    return_url="https://magazam.com/sepet",
    metadata={"customer_id": 42},
)

# Sabit coin: tam olarak 49.90 USDT
inv = zp.create_invoice("USDT", "49.90", order_id="order-1002")

print(inv["status"], inv["address"], inv["payment_url"])

Coin seçeneklerini sınırlamak için tokens=["USDT"], faturanın süresini belirlemek için expires_in_minutes (5–1440) kullanabilirsiniz.

Unutmamanız gereken kurallar:

  • Tutarlar metin olarak gönderilir: "49.90". Float kullanmayın.
  • order_id tekrar denemeye karşı güvenlidir: Aynı değerle ikinci çağrı yeni fatura açmaz, mevcut faturayı döndürür.
  • Minimum fatura tutarı 10 $'dır. USDT faturalarına, müşterinin ödediği 1 USDT ağ ücreti eklenir.
  • Faturalar en fazla 24 saat açık kalır.

4. Flask ile uçtan uca örnek

import json
import os
from flask import Flask, abort, redirect, request
from mercanpay import MercanPay, MercanPayError, verify_webhook

app = Flask(__name__)
zp = MercanPay("https://mercanpay.com", os.environ["MERCANPAY_API_KEY"])
SECRET = os.environ["MERCANPAY_WEBHOOK_SECRET"]


@app.post("/checkout/<order_id>")
def checkout(order_id):
    order = load_order(order_id)  # kendi veritabanı fonksiyonunuz
    try:
        inv = zp.create_invoice(
            price_usd=order.total_usd,          # ör. "49.90"
            order_id=order.id,
            callback_url="https://magazam.com/mercanpay/webhook",
            success_url=f"https://magazam.com/siparis/{order.id}/tesekkurler",
        )
    except MercanPayError as e:
        app.logger.error("MercanPay %s %s", e.code, e.message)
        abort(502)
    return redirect(inv["payment_url"])


@app.post("/mercanpay/webhook")
def webhook():
    raw = request.get_data()  # ham gövde, imza bunun üzerinden hesaplanır
    header = request.headers.get("X-MercanPay-Signature", "")
    if not verify_webhook(SECRET, raw, header):
        abort(401)

    event = json.loads(raw)
    invoice = event["data"].get("invoice") or {}

    if event["type"] in ("invoice.paid", "invoice.overpaid"):
        fulfil_once(event["id"], invoice["order_id"], invoice["token"], invoice["paid_amount"])
    elif event["type"] == "invoice.partial":
        notify_customer_remaining(invoice["order_id"])
    elif event["type"] in ("invoice.expired", "invoice.cancelled"):
        release_stock(invoice["order_id"])

    return "", 200

verify_webhook, X-MercanPay-Signature başlığındaki t=<unix>,v1=<hex> değerini ayrıştırır. Zaman damgası 5 dakikadan eskiyse isteği reddeder, imzayı hmac.compare_digest ile sabit zamanlı karşılaştırır. Mekanizmanın ayrıntıları webhook imzası doğrulama yazısında.

İmza ham gövde üzerinden hesaplanır. request.get_json() ile ayrıştırıp yeniden serileştirdiğiniz veriyle doğrulama yapmayın, baytlar değişebilir.

Tekrarlanan olaylara dikkat

Sunucunuz 10 saniye içinde 2xx dönmezse webhook artan aralıklarla tekrar gönderilir. Bu yüzden aynı olay size birden fazla kez ulaşabilir. fulfil_once fonksiyonunuz olay kimliğini (event["id"]) benzersiz bir sütuna kaydetmeli ve daha önce işlenmiş olayları atlamalıdır. Ağır işleri (e-posta, fatura PDF'i vb.) bir iş kuyruğuna bırakıp hızlıca 200 dönün.

5. Django ile webhook

import json
import os
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from mercanpay import verify_webhook


@csrf_exempt
@require_POST
def mercanpay_webhook(request):
    header = request.headers.get("X-MercanPay-Signature", "")
    if not verify_webhook(os.environ["MERCANPAY_WEBHOOK_SECRET"], request.body, header):
        return HttpResponse(status=401)

    event = json.loads(request.body)
    if event["type"] in ("invoice.paid", "invoice.overpaid"):
        fulfil_once(event["id"], event["data"]["invoice"]["order_id"])
    return HttpResponse(status=200)

Webhook istekleri tarayıcıdan gelmediği için CSRF korumasını bu görünümde kapatmanız gerekir. Güvenliği imza doğrulaması sağlar.

6. Sorgulama ve mutabakat

Webhook'a ek olarak API'den de fatura durumunu okuyabilirsiniz. Başarılı sayfasında veya gece çalışan bir mutabakat görevinde işe yarar:

inv = zp.get_invoice_by_order("order-1001")
if inv["status"] in ("paid", "overpaid"):
    ...

page = zp.list_invoices(status="paid", limit=20)
for inv in page["data"]:
    reconcile(inv)
next_page = zp.list_invoices(status="paid", limit=20, before=page["next_before"])

Güncel kurları görmek için zp.rates() kullanabilirsiniz. Örneğin {"TRX": "0.3342", "USDT": "1"} biçiminde bir sözlük döner.

USD fiyatı mı, sabit coin mi?

Entegrasyona başlamadan önce vermeniz gereken ilk karar, faturalarınızı nasıl fiyatlandıracağınızdır:

price_usd (USD fiyatlı) token + amount (sabit coin)
Müşteri deneyimi Ödeme sayfasında TRX veya USDT seçer Tek coin ile öder
Kur Müşteri coin seçtiğinde kilitlenir Kur hesabı yok, tutar sabit
Uygun olduğu durum Mağaza fiyatları dolar cinsindense Fiyatlarınız zaten USDT ise
Geç ödeme USD fiyatlı TRX faturasında güncel kurdan yeniden fiyatlanır Faturadaki tutar geçerlidir

Dolar fiyatlı bir mağazada çoğu zaman price_usd en az sürtünmeli seçenektir. Müşteri elindeki coin ile ödeyebilir, siz de kur hesabıyla uğraşmazsınız. Kur riskini ve kur kilidinin nasıl çalıştığını kripto ödemede kur riski yazısında ayrıntılı ele aldık.

Canlıya geçmeden önce kontrol listesi

  • API anahtarı yalnızca sunucuda, ortam değişkeninde duruyor. Kaynak koda veya ön yüze hiç girmiyor.
  • Anahtara yalnızca ihtiyaç duyduğu yetkiler verilmiş. Fatura oluşturan bir web uygulamasının withdrawals yetkisine ihtiyacı yoktur.
  • Webhook URL HTTPS ve verify_webhook her istekte çalışıyor.
  • Her siparişin benzersiz bir order_id değeri var, tekrar denemeler çift fatura açmıyor.
  • invoice.partial, invoice.overpaid ve invoice.expired olayları ele alınıyor.
  • Sipariş, success_url yönlendirmesine değil webhook'a göre onaylanıyor.
  • Akış, küçük tutarlı gerçek bir ödemeyle uçtan uca denendi.

API anahtarlarının ve çekim adreslerinin güvenliği için kripto hesap güvenliği yazımıza da göz atabilirsiniz.

Hata yönetimi

API 2xx dışında bir yanıt verdiğinde MercanPayError fırlatılır:

Özellik Açıklama
e.status HTTP kodu
e.code Makine okunur kod, ör. validation_error, unauthorized, insufficient_funds
e.message Açıklama

429 ve 5xx hatalarında kısa bir beklemenin ardından aynı çağrıyı tekrarlayın. Hız sınırı varsayılan olarak IP başına dakikada 120 istektir.

Sık sorulan sorular

Asenkron (asyncio) bir uygulamada kullanabilir miyim?

İstemci senkron requests kullanır. Asenkron uygulamalarda çağrıyı bir thread havuzunda çalıştırabilir ya da REST API'yi kendi asenkron HTTP istemcinizle doğrudan çağırabilirsiniz.

Çekim yapabilir miyim?

Evet. withdrawals yetkili bir anahtarla zp.create_withdrawal("USDT", "50", "T...", idempotency_key="payout-001") çağrısını kullanabilirsiniz. Aynı idempotency_key ile tekrar deneme ikinci bir çekim oluşturmaz. API çekimleri yalnızca panelde güvenilir olarak işaretlenmiş adreslere yapılabilir.

Test için nasıl bir yol izlemeliyim?

Panelde Webhooklar sayfasındaki "Test gönder" ile bir test.ping olayı alabilirsiniz. Canlıya geçmeden önce akışı küçük tutarlı gerçek bir ödemeyle uçtan uca deneyin.

MercanPay ile başlayın

Python ile kripto ödeme entegrasyonu için tek bir pip install yeterli. PHP kullanıyorsanız PHP entegrasyon rehberimize göz atın. Satıcı hesabınızı oluşturun ve ilk USDT ödemenizi bugün alın.

#Python#Flask#Django#API#Entegrasyon

İlgili yazılar

Kripto ödemeleri dakikalar içinde almaya başlayın

USDT ve TRX (TRC20) ile ödeme alın. %0,4'ten başlayan komisyon, hazır ödeme sayfası, API ve webhook.