Leistungen

Backend Engineering, Integration

Branche

E-Commerce-SaaS

Jahr

2022-2024

Drei Zahlungsanbieter. Drei Architekturen. Ein zuverlässiger Checkout.

Eine SaaS-Plattform für digitale Download-Codes musste Kreditkarten, PayPal und europäische Banküberweisungen akzeptieren. Jeder Anbieter hatte ein grundlegend anderes Integrationsmuster: Braintree verarbeitete Karten synchron in einem einzigen API-Aufruf, PayPal erforderte einen Redirect-and-Return-Flow mit einem separaten Ausführungsschritt, und Sofort operierte vollständig über asynchrone XML-Webhooks. Die ursprüngliche Implementierung behandelte jeden als eigenständigen Codepfad, mit duplizierter Finalisierungslogik und ohne Schutz gegen Preismanipulation zwischen Genehmigung und Ausführung.

01.
DIE HERAUSFORDERUNG

Drei Gateways, drei Fehlerszenarien

Jeder Zahlungsanbieter setzte einen anderen Vertrag voraus. Braintree lieferte sofort ein Ergebnisobjekt - Erfolg oder Misserfolg in einem Round-Trip. PayPal erforderte, dass der Benutzer die Seite verließ, auf paypal.com genehmigte und dann zurückkehrte, woraufhin das System einen zweiten API-Aufruf zur Ausführung machen musste. Sofort war am komplexesten: Das System sendete XML, um eine Transaktion zu erstellen, leitete den Benutzer zur Website seiner Bank weiter und wartete dann auf Webhook-Benachrichtigungen, die Minuten bis Stunden später eintrafen. Eine einzelne Bestellung konnte in einem Schwebezustand enden, wenn der PayPal-Redirect ablief, wenn Sofort's Webhook nie ankam, oder wenn der Benutzer sein Code-Paket zwischen Initiierung und Abschluss der Zahlung änderte. Die Finalisierungslogik - Code-Generierung, Rechnungsversand, Empfehlungsprovisionen - war über alle drei Pfade kopiert, jeweils mit leicht unterschiedlicher Fehlerbehandlung.

PayPal leitet dich weiter. Sofort sendet Stunden später einen Webhook. Beide müssen dieselbe Bestellung finalisieren. Der Code war dreifach dupliziert.

02.
DIE LÖSUNG

Einheitliche Finalisierung mit Gateway-spezifischen Adaptern

Ich habe die Zahlungsschicht um eine klare Trennung herum umstrukturiert: Gateway-spezifische Logik behandelt die Mechanik jedes Anbieters, aber jede erfolgreiche Belastung mündet in einen gemeinsamen Finalisierungspfad. Kreditkartenzahlungen rufen Braintrees API auf und finalisieren inline. PayPal-Zahlungen re-validieren den Preis bei der Rückkehr, bevor sie ausgeführt werden, und fangen jede Manipulation während des Redirect-Fensters ab. Sofort-Zahlungen sperren das Code-Paket bei Initiierung als nicht-editierbar und verlassen sich auf Webhook-Benachrichtigungen zur Finalisierung. Die Empfehlungsprovisionsberechnung, Code-Generierung und Rechnungsversand laufen identisch, unabhängig davon, welches Gateway die Belastung abgeschlossen hat.

Der Kreditkarten-Flow - synchron, ein Round-Trip:

Python
@standard_ajax
def create_braintree_payment(request, code_package):
    nonce = request.POST.get('payment_method_nonce')
    amount = str(code_package.calculated_price)

    result = braintree.Transaction.sale({
        'amount': amount,
        'payment_method_nonce': nonce,
        'merchant_account_id': get_merchant_account(
            code_package.currency
        ),
        'options': {'submit_for_settlement': True},
    })

    if result.is_success:
        code_package.paid = True
        code_package.payment_method = 'braintree'
        code_package.transaction_id = result.transaction.id
        code_package.save()

        generate_codes(code_package)
        send_invoice_email(code_package)
        create_referral_commission(code_package)

        return AJAX_RESULT_SUCCESS, 'Payment processed.'

    return AJAX_RESULT_FAILED, result.message

So funktioniert jeder Zahlungsflow

Wähle ein Gateway, um seinen Flow animiert zu sehen. Aktiviere 'Preisänderung während Genehmigung', um zu sehen, wie PayPals Revalidierung eine Abweichung erkennt.

LIVE SIMULATION
Select a payment method to start

PayPal-Return-Handler mit Preis-Revalidierung:

Python
def execute_paypal_payment(request):
    payment_id = request.GET.get('paymentId')
    payer_id = request.GET.get('PayerID')
    package_id = request.GET.get('package_id')

    code_package = CodePackage.objects.get(pk=package_id)
    payment = paypalrestsdk.Payment.find(payment_id)

    # Re-validate: price may have changed during redirect
    current_price = code_package.calculated_price
    stored_price = Decimal(
        payment.transactions[0].amount.total
    )

    if current_price != stored_price:
        send_admin_alert(
            f'Price mismatch: {current_price} vs {stored_price}'
        )
        payment.void()
        return redirect_with_error(
            'payment-cancelled'
        )

    if payment.execute({'payer_id': payer_id}):
        code_package.paid = True
        code_package.payment_method = 'paypal'
        code_package.transaction_id = payment_id
        code_package.save()

        generate_codes(code_package)
        send_invoice_email(code_package)
        create_referral_commission(code_package)

        return redirect('payment-success')

    return redirect_with_error('payment-failed')

Edge Cases in der Produktion

Sofort-Timeout-Recovery

Wenn eine Sofort-Zahlung initiiert wird, wird das Code-Paket nicht-editierbar. Wenn der Benutzer die Bankseite verlässt oder der Webhook nie eintrifft, steuert ein valid_time-Timestamp, wann die Editierbarkeit wiederhergestellt wird. Bei jedem folgenden Zugriff prüft das System datetime.now() > payment.valid_time und aktiviert die Bearbeitung nach Ablauf des Timeouts. Kein Cron-Job nötig.

Dezimal-Präzision für Provisionen

Empfehlungsprovisionen werden als Decimal(float(amount) * REFERRAL_SHARE) berechnet und dann auf Cents gerundet via int(referral_share * 100) / 100.0. Dies vermeidet Gleitkomma-Drift, der Ein-Cent-Abweichungen zwischen Provisionsdatensatz und tatsächlicher Auszahlung erzeugen könnte. Dieselbe Formel läuft identisch über alle drei Gateway-Pfade.

Idempotente Code-Generierung

Sofort sendet sowohl eine 'pending'- als auch eine 'received'-Benachrichtigung für eine einzelne erfolgreiche Zahlung. Die generate_codes()-Funktion ist idempotent - zweimaliger Aufruf erzeugt dasselbe Ergebnis. Der 'received'-Webhook führt die Generierung sicher erneut aus und markiert das Paket als bezahlt, auch wenn 'pending' beides bereits erledigt hat. Keine doppelten Codes, keine doppelten Belastungen.

Sofort-Webhook-Handler mit Zustandsmaschinen-Übergängen:

Python
@csrf_exempt
def sofort_notification(request):
    xml = xmltodict.parse(request.body)
    txn_id = xml['status_notification']['transaction']
    status = xml['status_notification']['status']

    payment = SofortPayment.objects.get(
        transaction_id=txn_id
    )
    code_package = payment.code_package

    if status == 'pending':
        code_package.paid = True
        code_package.payment_method = 'sofort'
        code_package.save()

        generate_codes(code_package)
        send_invoice_email(code_package)
        create_referral_commission(code_package)

    elif status == 'received':
        # Idempotent: safe to re-run
        generate_codes(code_package)
        code_package.paid = True
        code_package.save()

    elif status == 'loss':
        code_package.editable = False
        code_package.lock_reason = 'Payment failed'
        code_package.save()

    elif status == 'refunded':
        code_package.editable = False
        code_package.lock_reason = 'Payment refunded'
        code_package.save()

    return HttpResponse(status=200)
03.
DAS ERGEBNIS

Zuverlässiger Multi-Gateway-Checkout im Betrieb

Die umstrukturierte Zahlungsschicht verarbeitete drei Gateway-Architekturen über eine einheitliche Pipeline. PayPal-Preismanipulationsversuche wurden vor der Ausführung erkannt und geloggt. Sofort's asynchrone Webhooks finalisierten Bestellungen innerhalb von Sekunden nach Bankbestätigung. Der duplizierte Finalisierungscode wurde konsolidiert, wodurch die Drift zwischen den Gateway-Pfaden eliminiert wurde, die intermittierende Fehler bei der Provisionsberechnung verursacht hatte. Der Checkout lief kontinuierlich über alle drei Schienen ohne eine einzige verlorene Transaktion.

WICHTIGE KENNZAHLEN

0Zahlungsanbieter
0NullVerlorene Transaktionen
0%Preis-Validierung
WAS DER KUNDE SAGT

"Wir sind von wöchentlichem Debugging von Zahlungs-Edgecases zu einem System übergegangen, das einfach funktioniert. Die PayPal-Preisvalidierung allein hat zwei Exploit-Versuche im ersten Monat gestoppt."

CTO

E-Commerce-SaaS · Digitale Produkte

FAQ

Warum nicht einen Payment-Aggregator wie Stripe statt drei separater Gateways verwenden?

Wie geht das System mit einem Sofort-Webhook um, der nie eintrifft?

Was verhindert, dass die Empfehlungsprovision doppelt berechnet wird?

TECHNOLOGIE-STACK