Ga naar inhoud

Betalingen

De webshop verwerkt online betalingen via Pay.nl. De integratie steunt op de officiële SDK paynl/sdk (zie composer.json:58). Niet elke bestelling loopt over Pay.nl: een deel van de B2B-betaalmethodes (op factuur, SEPA, bij levering, cash) wordt buiten de gateway om afgehandeld. Deze pagina beschrijft welke betaalmethodes er zijn, hoe de betaalflow loopt en hoe de bestelstatus wordt bijgewerkt.

Betaalmethodes

Een betaalmethode is een CountryPaymentCost-record (_core/model/classes/atx/orders/CountryPaymentCost.php). Naast eventuele kosten legt dit record vast om wat voor soort betaling het gaat (CountryPaymentCost.php:30-34):

Type Constante Betekenis
0 TYPE_PAYNL Online betaling via de Pay.nl-gateway
1 TYPE_SEPA SEPA-domiciliëring (incasso)
2 TYPE_INVOICE Betaling op factuur
3 TYPE_DELIVERY Betaling bij levering
4 CASH Contant

Alleen methodes die isAcquiredByGateway() zijn én een paynlId != 0 hebben, lopen daadwerkelijk via Pay.nl (CountryPaymentCost.php:482, :575). De koppeling tussen een betaalmethode en de Pay.nl-betaaloptie gebeurt via het veld paynlId. Dit veld wordt beheerd in de backoffice (_admin/controller/classes/atx/orders/AdminPaymentController.php).

De lijst met beschikbare Pay.nl-betaaloptiess wordt live bij Pay.nl opgehaald per land via PaynlService::getPaymentMethods() (Paymentmethods::getList(['country' => $country])). Drie methodes worden manueel van een leesbare naam voorzien: 706Visa and Mastercard, 710Carte Bleue, 138PAYPAL.

Configuratie

De Pay.nl-credentials staan in de setting-tabel onder de namespace atx\paynl (keys paynl_id = Service ID en paynl_token = API-token). De SDK draait automatisch in testmode zolang is_prod() onwaar is ($this->testMode = !is_prod(), _core/model/classes/atx/paynl/PaynlService.php).

Betaalflow

De volledige flow van checkout tot betaalde bestelling:

sequenceDiagram
    participant K as Klant
    participant CC as CartController
    participant PS as PaynlService
    participant PAY as Pay.nl
    participant EX as ExchangeController

    K->>CC: createOrder() (paymentMethod gekozen)
    CC->>CC: Order aanmaken via OrderBuilder (Paymentmethod = 'Q')
    Note over CC: gateway-check: isAcquiredByGateway() && getPaynlId() != 0
    CC->>PS: getPaymentUriForOrder(order, paymentMethod)
    PS->>PAY: Transaction::start([...])
    PAY-->>PS: TransactionId + redirect-URL
    PS->>PS: TransactionId op order (Pay.nl); PaymentId = lokaal DB-id betaalmethode
    CC-->>K: redirect document.location = Pay.nl-URL
    K->>PAY: betaalt op Pay.nl
    PAY->>EX: server-to-server callback (exchangeUrl /exchange)
    EX->>EX: CheckPaymentStatus → orderstatus bijwerken
    PAY-->>K: redirect terug naar returnUrl (pagina 'bestelling')
    K->>CC: orderCallback() → winkelwagen leegmaken + UI tonen

1. Bestelling en transactie starten

De checkout-submit komt binnen in CartController::createOrder() (_core/controller/classes/atx/customers/cart/CartController.php). De gekozen betaalmethode wordt opgezocht (CountryPaymentCostDAO::getById()) en de bestelling wordt opgebouwd via OrderBuilder (src/Atraxion/Order/Builder/OrderBuilder.php). Zolang de betaling nog niet rond is, krijgt de order voorlopig Paymentmethod = 'Q'.

Bij de gateway-check (CartController.php:1388) wordt voor een Pay.nl-methode PaynlService::getPaymentUriForOrder() aangeroepen (CartController.php:1394). Daarin gebeurt de SDK-aanroep Transaction::start([...]) (PaynlService.php:148) met onder meer:

  • amount = $order->getRealTotalPrice();
  • paymentMethod = $paymentMethod->getPaynlId();
  • orderNumber = het ordernummer;
  • returnUrl = de pagina bestelling (waar de klant naar terugkeert, PaynlService.php:162);
  • exchangeUrl = /exchange (de server-to-server callback, PaynlService.php:163);
  • expireDate = +1 week, testmode, taal, en klantgegevens als extra-velden.

De ontvangen TransactionId wordt op de order gezet via $result->getTransactionId(). Het veld PaymentId wordt niet uit de Pay.nl-respons gehaald; het wordt gevuld via $order->setPaymentId($paymentMethod->getId()) — dat is het lokale DB-id van de CountryPaymentCost-rij (PaynlService.php:169). Beide velden worden daarna opgeslagen via OrderDAO::updateObject(). De view voert vervolgens een document.location = url uit om de klant naar Pay.nl te sturen (CartView.php rond regel 1701).

2. Retour-URL (klant keert terug)

Na betalen stuurt Pay.nl de klant terug naar de pagina bestelling, afgehandeld door CartController::orderCallback() (CartController.php rond regel 1821). Hier wordt de transactiestatus rechtstreeks opgevraagd (Transaction::status($transactionId)) en wordt — als de betaling betaald of geautoriseerd is — de winkelwagen leeggemaakt (CartArticleDAO::deleteCart()) en de bevestigings- of annuleringspagina getoond.

Let op: de retour-URL werkt de orderstatus niet zelf bij — dat doet uitsluitend de exchange-callback. De retour-handler dient alleen voor het opruimen van de winkelwagen en de UI.

3. Exchange-callback (statusverwerking)

De feitelijke statusverwerking gebeurt server-to-server via de route GET|POST /exchangeExchangeController::index() (_core/config/router.php:87). De controller:

  1. schrijft TRUE| naar de response-body (vereiste van Pay.nl);
  2. valideert het bron-IP via Validate::isPayServerIp() in productie (in dev wordt het Pay.nl-test-IP toegelaten);
  3. haalt de transactie op (Transaction::status() en Transaction::get());
  4. zoekt de order via het ordernummer en slaat over als de order niet bestaat, geen TransactionId heeft of al betaald is;
  5. delegeert naar CheckPaymentStatus::checkPaymentStatus() (src/Atraxion/Exchange/Service/CheckPaymentStatus.php).

Statusafhandeling

CheckPaymentStatus vertaalt de Pay.nl-status naar de betaal- en orderstatus (CheckPaymentStatus.php):

Pay.nl-status Orderstatus Acties
isBeingVerified() STATUS_PENDING interne notitie + verificatiemail (VerifyOrder) naar office@atraxion.com, in CC naar logs@atraxion.com en ignace.knops@gmail.com (VerifyOrder.php:17–19)
isPaid() STATUS_NEW setIsPaid(), betaalkostenregel toevoegen, orderbevestiging mailen, naar ERP sturen, loyaliteitspunten bevestigen
isAuthorized() STATUS_NEW eerst Transaction::capture(), daarna identiek aan isPaid()
isCancelled() STATUS_CANCELLED order opslaan
overig ongewijzigd status UNKNOWN

De betaalmethodenaam, het betaalde bedrag en de munteenheid worden altijd uit de transactie overgenomen (getPaymentMethodName(), getAmountPaid(), getAmountPaidCurrency()).

Bij een succesvolle betaling worden in dezelfde stap ook de geautomatiseerde vervolgacties uitgevoerd: de orderbevestiging (zie Notificaties & e-mails), het doorsturen naar Odoo (ERP) inclusief eventuele Transsmart-boeking (zie Verzending) en het bevestigen van de loyaliteitspunten via loyaltyPointsService->confirmByOrder() (CheckPaymentStatus.php:117).

De mogelijke betaalstatussen zelf staan in _core/model/classes/atx/orders/OrderPaymentStatus.php: REQUESTED, PENDING, PAID, CANCELED, REFUNDED, AUTHORIZED, BEING_VERIFIED, UNKNOWN.

Inzicht in de backoffice

Bij het openen van een bestelling met een TransactionId haalt de backoffice de actuele Pay.nl-transactiegegevens op via Transaction::get() (src/Atraxion/Admin/Controller/OrderController.php rond regel 108), zodat de betaalstatus naast de order zichtbaar is.

Zie ook