Sie zeigen eine alte Version dieser Seite an. Zeigen Sie die aktuelle Version an.

Unterschiede anzeigen Seitenhistorie anzeigen

« Vorherige Version anzeigen Version 2 Aktuelle »

Payrexx ist ein Schweizer Payment-Aggregator: Ein einziger Vertrag und eine einzige API decken mehrere PSP (Payment Service Provider, z. B. Kartenacquirer, Twint, Postfinance) ab. Dieser Artikel beschreibt den generischen Payrexx-Zahlungsablauf, dessen konkrete Umsetzung im Payment-Modul von JAXForms und die dabei relevanten Eigenheiten. Für die vollstaendige Konfigurationsreferenz (Schluessel, Defaults, Statusmodell-Tabelle) siehe die separate Feature-Doku zum Payrexx-Provider.

Zahlungsprozess Payrexx generell

Payrexx kennt zwei zentrale Objekte: das Gateway (der Bezahlvorgang als Ganzes, inkl. Checkout-Seite) und die Transaction (die konkrete Belastung, existiert erst ab Zahlungsabschluss). Ein Gateway kann theoretisch mehrere Transactions durchlaufen (z. B. bei einem Zahlungsabbruch mit erneutem Versuch).

Reservation vs. Pre-Authorization

Payrexx bietet zwei Formen der Vorautorisierung, die sich fachlich deutlich unterscheiden:

ModusBedeutungBesonderheit
reservationBetrag wird 7 Tage blockiert, danach Capture (Belastung) oder AufhebungKeine Kartenspeicherung noetig
preAuthorizationKarte wird tokenisiert fuer spaeter vom Haendler ausgeloeste Belastungen (Charge)Datenschutz- und PCI-relevant, da Kartendaten bei Payrexx gespeichert bleiben

Anmerkung: Nicht jeder PSP unterstuetzt Reservationen. Der Modus ist deshalb pro Mandant konfigurierbar.

Abbildung 1: Generischer Payrexx-Zahlungsablauf

sequenceDiagram
    autonumber
    actor Zahler as Zahler
    participant Merchant as Merchant-System
    participant Payrexx as Payrexx API
    participant PSP as PSP / Kartennetz

    Merchant->>Payrexx: POST Gateway/ (amount, currency, reservation/preAuthorization, Redirect-URLs)
    Payrexx-->>Merchant: Gateway { id, hash, link, status=waiting }
    Merchant->>Zahler: Weiterleitung auf Gateway-Link (Payrexx-Checkout-Seite)
    Zahler->>Payrexx: Zahlungsmittel waehlen, Daten eingeben
    Payrexx->>PSP: Autorisierung anfragen
    PSP-->>Payrexx: Autorisiert (reserved/authorized) oder abgelehnt (declined)
    Payrexx-->>Zahler: Weiterleitung auf successRedirectUrl / failedRedirectUrl
    Merchant->>Payrexx: GET Gateway/{id}/ (Status pruefen)
    Payrexx-->>Merchant: Gateway-Status + Transaction-Referenz
    Merchant->>Payrexx: GET Transaction/{id}/ (Details lesen)
    Payrexx-->>Merchant: Transaction { status, payment.brand, invoice.totalAmount }
    alt Reservation oder Pre-Authorization
        Merchant->>Payrexx: POST Transaction/{id}/capture oder /charge
        Payrexx->>PSP: Belastung ausloesen
        PSP-->>Payrexx: Bestaetigt
        Payrexx-->>Merchant: Transaction { status=confirmed }
    else Zahlung fehlgeschlagen oder abgebrochen
        Merchant->>Payrexx: DELETE Gateway/{id}/ oder DELETE Transaction/{id}/
        Payrexx-->>Merchant: Gateway/Transaction { status=cancelled }
    end

Zahlungsprozess Payrexx mit JAXForms

Das Payment-Modul jaxforms-payment kapselt den Ablauf in PayrexxService (HTTP-Kommunikation) und SaveFormPayrexxAction (Workflow-Anbindung). Zwei Integrationsmodi stehen zur Wahl, gesteuert ueber die Workflow-Property integrationMode.

FORWARD (Redirect)

Der Nutzer wird auf die Payrexx-Checkout-Seite umgeleitet, analog zum bestehenden Datatrans-FORWARD-Muster. Die Rueckkehr laeuft ueber einen signierten serverAccessToken direkt im Formular-Workflow.

Abbildung 2: JAXForms FORWARD-Ablauf

sequenceDiagram
    autonumber
    actor Nutzer as Formular-Nutzer
    participant Browser as Browser
    participant Action as SaveFormPayrexxAction
    participant Service as PayrexxService
    participant Payrexx as Payrexx API
    participant DB as JAX_PAYMENT

    Nutzer->>Browser: Zahlungs-Button klicken
    Browser->>Action: commitWorkflow (kein serverAccessToken)
    Action->>Service: initializePayment(user, formDescriptor, paymentItem)
    Service->>Payrexx: POST Gateway/ (Redirect-URLs inkl. serverAccessToken)
    Payrexx-->>Service: Gateway { id, link, status=waiting }
    Service->>DB: persist INITIALIZED (TRANSACTION_ID = Gateway-ID)
    Action->>Service: initializePaymentPage(gatewayId, FORWARD)
    Service->>Payrexx: GET Gateway/{id}/ (Link lesen)
    Payrexx-->>Service: Gateway { link }
    Action->>Browser: redirect(gateway.link)
    Browser->>Payrexx: Checkout-Seite oeffnen
    Nutzer->>Payrexx: Zahlung durchfuehren
    Payrexx-->>Browser: Redirect auf clientCallbackUrl + serverAccessToken
    Browser->>Action: commitWorkflow (mit serverAccessToken)
    Action->>Service: verifyTransactionAuthorized(dom, gatewayId)
    loop Polling, 1s Intervall, 30s Timeout
        Service->>Payrexx: GET Gateway/{id}/
        Payrexx-->>Service: Status (waiting/reserved/authorized/confirmed)
    end
    Service->>Payrexx: GET Transaction/{id}/
    Payrexx-->>Service: Transaction-Details
    Service->>DB: persist Status, SUB_TRANSACTION_ID, PAYMENT_METHOD
    alt captureAfterAuthorizing = true
        Action->>Service: captureTransaction(user, gatewayId)
        Service->>Payrexx: POST Transaction/{id}/capture oder /charge
        Payrexx-->>Service: Transaction { status=confirmed }
        Service->>DB: persist CONFIRMED
    end
    Action->>Browser: Workflow committet, Formular abgeschlossen

MODAL

Payrexx laedt den Checkout in einem eigenen Modal-Fenster (modal.min.js), das den Gateway-Link in einem iFrame anzeigt. Die Rueckkehr laeuft deshalb nicht direkt im Formular-Workflow, sondern ueber den oeffentlichen REST-Callback /pay/callback/payrexx/{mandant} - das gleiche Muster wie beim bestehenden Saferpay-IFRAME-Modus.

Abbildung 3: JAXForms MODAL-Ablauf

sequenceDiagram
    autonumber
    actor Nutzer as Formular-Nutzer
    participant Browser as Browser (JS)
    participant Action as SaveFormPayrexxAction
    participant Service as PayrexxService
    participant Payrexx as Payrexx API
    participant Callback as RESTPaymentCallbackHandler

    Nutzer->>Browser: Zahlungs-Button klicken
    Browser->>Action: commitWorkflow (kein serverAccessToken)
    Action->>Service: initializePayment(...) + initializePaymentPage(gatewayId, MODAL)
    Service->>Payrexx: POST Gateway/ + GET Gateway/{id}/
    Payrexx-->>Service: Gateway { link }
    Service-->>Action: PaymentConnection { url=link, scriptUrl=modal.min.js }
    Action->>Browser: initPayrexxModal(jsUrl, link, gatewayId, domId, actionId)
    Browser->>Browser: modal.min.js laden, window.jQuery Fallback pruefen
    Browser->>Payrexx: Payrexx-Modal oeffnen (iFrame mit Gateway-Link)
    Nutzer->>Payrexx: Zahlung im Modal durchfuehren
    Payrexx->>Callback: Redirect im iFrame auf /pay/callback/payrexx/{mandant}?serverAccessToken=...
    Callback->>Service: verifyTransactionAuthorized(dom, gatewayId)
    Service->>Payrexx: GET Gateway/{id}/ + GET Transaction/{id}/ (Polling)
    Payrexx-->>Service: Status + Transaction-Details
    Callback-->>Browser: payrexxSuccess.html / payrexxFailed.html (im iFrame)
    Browser->>Browser: postMessage ans Elternfenster (paymentOutcomeReady)
    Browser->>Browser: shown/hidden-Callback: context=disableButton/enableButton
    Browser->>Action: sendActionRequestHRefTargetWithDomID (SaveFormPayrexxAction)
    alt captureAfterAuthorizing = true
        Action->>Service: captureTransaction(user, gatewayId)
        Service->>Payrexx: POST Transaction/{id}/capture oder /charge
    end
    Action->>Browser: Modal schliesst, Formular abgeschlossen

Eigenheiten

Payrexx-API

EigenheitBedeutung fuer die Umsetzung
Envelope-FormatJede Antwort ist {"status":"success"|"error","data":[...]} - mit HTTP-Status 200 auch im Fehlerfall. Ein reiner HTTP-Status-Check reicht nicht; der Envelope muss zusaetzlich ausgewertet werden.
Betrag in Rappenamount ist ein Integer in Rappen/Cents, nicht Franken - Umrechnung via Math.round(betrag * 100).
Rate-Limit600 Requests / 5 Minuten pro Instanz. Beim Status-Polling relevant (siehe unten).
Gateway vs. TransactionDie Transaction-ID ist erst ab Zahlungsabschluss bekannt; bis dahin existiert nur die Gateway-ID.
Charge-Endpunkt undokumentiertDer Endpunkt fuer PRE_AUTHORIZATION-Belastungen (POST Transaction/{id}/charge) ist auf der offiziellen Referenzseite nicht aufgefuehrt - verifiziert ueber die Action-Mapping-Logik des offiziellen PHP-SDK (payrexx/payrexx-php).
Modal-Widgetmodal.min.js braucht ein globales window.jQuery - unabhaengig vom Framework-eigenen $jax.

JAXForms-Implementierung

EigenheitBegruendung
TRANSACTION_ID = Payrexx-Gateway-IDStabil ab Initialisierung, deshalb Schluessel fuer alle Lookups. Die Payrexx-Transaction-ID landet separat in der neuen Spalte SUB_TRANSACTION_ID (die bestehende REQUEST_ID-Spalte wird von keinem Provider tatsaechlich beschrieben und blieb deshalb unangetastet).
API-Key als ClientRequestFilterNicht als withHeader(...) gesetzt, da der generische HTTP-Client bei aktivem Debug-Logging alle per withHeader gesetzten Header inkl. Wert protokolliert. Analog zu BasicAuthenticator bei Datatrans/Saferpay.
MODAL folgt dem Saferpay-IFRAME-Muster, nicht Datatrans-LIGHTBOXPayrexx laedt die Redirect-URLs innerhalb des Modal-iFrames; die Rueckkehr braucht deshalb einen oeffentlichen REST-Callback statt eines direkten Lightbox-Callbacks.
Tenant-Aufloesung im REST-CallbackDie Payrexx-API-Konfiguration (Secret/Instanz) wird ueber den durch den signierten serverAccessToken authentifizierten dom.getUser() aufgeloest, nicht ueber den frei waehlbaren URL-Pfadparameter mandantName - Fund aus dem Security-Review, im bestehenden Saferpay-Zweig nicht behoben (siehe Offene Punkte).
Polling statt WebhookTicket 1 verifiziert synchron per Polling (1s-Intervall, 30s-Timeout) auf GET Gateway/{id}/. Ein Webhook-Endpunkt fuer asynchrone Statusaenderungen ist nicht Teil von Ticket 1.

Offene Punkte

  • Payrexx-Testinstanz und Test-PSP beschaffen, um den Ablauf end-to-end zu verifizieren
  • Mit Payrexx-Support klaeren, ob der PSP der Ziel-Instanz Reservationen unterstuetzt
  • Folge-Ticket: identische Tenant-Aufloesungsschwaeche im Saferpay-Zweig des REST-Callback-Handlers beheben
  • Folge-Ticket: Replay-Verhalten im Callback-Handler (Status-Downgrade auf ERROR bei wiederholtem Aufruf) - betrifft alle drei Provider
  • Keine Stichwörter