Skip to content

Developer notes

Technical reference for developers who customize Riverty for CS-Cart and Multi-Vendor.

Item Value
Add-on id kv_riverty
Function prefix fn_kv_riverty_
Language variables kv_riverty.*
CSS scope .kv-riverty (admin), .kv-riverty-checkout (storefront)
PHP namespace Tygh\Addons\KvRiverty

Riverty API reference used (read 2026-11-08)

Section titled “Riverty API reference used (read 2026-11-08)”

Only the official Riverty BNPL documentation is used: https://docs.riverty.com/bnpl. Base URLs (“Environments & Endpoints”): Partner Test https://api.bnpl-pt.riverty.io, Live https://api.bnpl.riverty.io. Every request carries the header X-Auth-Key (“API Authentication & First API Call”: one key per web shop and per country). Amounts are decimals (185 or 4.79), not minor units.

Purpose Endpoint (reference page) Where in the add-on
Connection test GET /api/v3/version (Get Version) Client\Api::ping(), Client::check()
Available payment methods POST /api/v3/checkout/payment-methods (Available Payment Methods) Client\Api::availablePaymentMethods() (client method; checkout visibility uses the published availability tables instead, see below)
Authorize POST /api/v3/checkout/authorize, header X-Idempotency-Key (Authorize Payment) PaymentService::start()
Order status GET /api/v3/orders/{orderNumber} (Get Order) PaymentService::syncLocked()
Capture (full / partial) POST /api/v3/orders/{orderNumber}/captures, X-Idempotency-Key (Capture Payment) PaymentService::capture() / runOp()
Refund (full / partial) POST /api/v3/orders/{orderNumber}/refunds, X-Idempotency-Key (Refund Payment) PaymentService::refund()
Void POST /api/v3/orders/{orderNumber}/voids, X-Idempotency-Key (Void Authorization) PaymentService::voidReservation()
Customer lookup POST /api/v3/lookup/customer (Customer Lookup) Client\Api::lookupCustomer() (client method, not used by the checkout)
Address check POST /api/v3/validate/address (Validate Address) Client\Api::validateAddress() (client method, not used: the reference does not document the response)
Events Webhooks with Riverty-Signature: t=<unix seconds>, v1=<hex>, v1 = HMAC-SHA256(shared secret, t + raw body), refuse older than 5 minutes (Webhooks Implementation Guide), events AuthorizeAccepted / Rejected / Expired / Cancelled / Failure (Webhook events) Webhook::verify(), frontend controller kv_riverty.webhook?c=<account>

Pages used for rules rather than endpoints: Hosted Checkout (redirect flow, merchantUrl, GetOrder timing, Pending expires after 15 minutes), Idempotency (key: letters, digits, hyphens, underscores, 50 characters, not shared between endpoints), Transaction Reference Numbers (order number a-zA-Z0-9._-, 50 characters, 34 in NL and BE; capture / credit note numbers 19), Required Consumer Information, Payment Method Presentation (availability by country), Compliance Requirements (terms and privacy links and sentences), Response Codes, Test Data, Best Practice: Brand Usage.

  1. Checkout: the buyer chooses the Riverty method and gives B2C / B2B, date of birth (DE, AT, CH, NL, BE, DK), national identity number (SE, NO, FI, DK), mobile phone (mandatory NL, BE), company data for B2B (company name, VAT id, legal form in DE and AT) and accepts Riverty’s terms and privacy sentence. pre_place_order validates the same on the server (Buyer::validate()), the order is not placed otherwise.
  2. PaymentService::start() writes the transaction row (Riverty order number, idempotency key, the authorized lines) before the call, then POST /checkout/authorize with hostedCheckout. Answer outcome: Accepted (reservation), Pending (+ redirectUrl: strong customer authentication, bank check, one-time passcode, manual review), Rejected (riskCheckMessages, customerFacingMessage is shown to the buyer).
  3. Return to merchantUrl (payment_notification.return): the order is read back with GetOrder; Pending is never shown as cancelled (the cart is emptied, the order page shown, the cron / webhook settles it).
  4. The reservation is captured when the order reaches the capture status (queue job capture) or by hand (all lines or selected lines), voided (void job or by hand) or, once captured, refunded.
Riverty (orderDetails.status / displayStatus) Local Order status
Pending pending unchanged (never cancelled)
Accepted / Reserved authorized “Order status when Riverty accepts” (default Open), only on the FIRST acceptance and only from an order that was not taken yet (Incomplete, or the “declines” status); never from a status the merchant chose (Canceled, Processing, Complete …)
Accepted / PartiallyCaptured partial_capture unchanged
Accepted / FullyCaptured paid (“Captured”) unchanged
PartiallyRefunded, FullyRefunded partial_refund, refunded unchanged
Rejected failed “declines” status, only from an incomplete order
Cancelled, Expired canceled, expired only from an incomplete order
anything else pending unchanged

A reservation is never turned back into pending / failed by a late answer, and a refunded invoice never drops back to captured (unless the answer reports more money captured or refunded, which makes it newer). A read-back after a capture, void or refund never moves the order: a refund triggered by the “Canceled” status leaves the order Canceled.

At checkout only a real decline (Riverty’s Rejected, a 4xx refusal, a refused key) sets the “declines” status. No answer, a second submit while the first one is authorizing, or an order that already holds a reservation leave the order as it is: the customer sees the order page, the cart is emptied, and the cron / the event reads the payment back.

  • Keys are kv-<au|cp|vd|rf>-<install salt>-<row id>-<8 random hex>: the install salt is random and renewed on every install (the tables and their row ids start again after a reinstall), and the random part makes every key unique even with the same salt. A retry always reads the key from the row.
  • Authorize: the key is stored in the row before the POST. A lost answer (timeout, connection error, 5xx, 429) repeats the same request with the same key once (Riverty replays the first answer); if that fails too, the order is read back by its number. Still no answer: the attempt stays pending (the cron re-reads it, a new attempt waits up to 16 minutes).
  • Capture, void, refund: a row in kv_riverty_ops (state creating, our invoice number INV<order>-<op> or credit note CN<order>-<op>, key kv-cp|vd|rf-...) is written before the POST. A lost answer sets the row unconfirmed, the order is read back (a capture appears with our invoice number as captureNumber; a refund appears in Get Order refunds[] with a refund number we do not know yet, on the same captureNumber and with the same amount, and its refundNumber is stored, so totals are never compared) and, when it is still not visible, the queue repeats the same key. While an operation of a payment is unconfirmed no new capture, void or refund of that payment starts, so a read-back always identifies one operation. The refund on the order status refunds every captured invoice (one refund per capture). Capturability comes from the amounts (reserved minus captured minus voided), so the rest of a reservation can still be captured after an invoice was refunded. Error 400.120 (capture exists) is adopted by reading back. Captures made in the Riverty merchant portal are adopted as portal captures so they can be refunded here.
  • Locks: GET_LOCK per order (authorize) and per transaction (read-back and operations).

Lines are computed in minor units: gross = net + VAT per line (400.160), the sum equals totalGrossAmount (400.102), order-level discounts, gift certificates and rounding close with one Discount / Surcharge line carrying VAT at the rate of the largest line. A line with quantity above 1 is kept only when gross and VAT divide evenly by the quantity, otherwise it becomes quantity 1 with “x” in the description. Taxes are read for both CS-Cart tax calculation methods (OrderTax).

Open items (not verifiable from the reference, flagged)

Section titled “Open items (not verifiable from the reference, flagged)”
  • Direct debit, instalments, Pay in 3 and the selection page are not verified with Riverty. They are off by default (setting “Enable payment types not verified with Riverty”, default off) and labelled “not verified – needs Riverty confirmation” in the payment method. Where the reference is explicit the add-on follows it: Pay in 3 sends payment.type = PartPayment (Pay in 3 page and the Hosted Checkout request example), direct debit sends payment.directDebit.bankAccount with the IBAN the buyer enters at checkout (validated with the ISO 13616 check digits, wiped from the order after the call), instalments send payment.installment.profileNo (the merchant’s profile number, a payment method setting; without it the method is hidden) plus directDebit.bankAccount in DE and AT. The selection page sends hostedCheckout.paymentSelection: [], which the reference does not show. None of this was run against Riverty’s Partner Test environment: confirm with your Riverty integration manager before switching them on. The SEPA mandate wording of the Compliance Requirements table is shown for direct debit only.
  • Country / currency pairs. The API enumerates the currencies EUR, NOK, SEK, DKK, CHF and the countries; the pairing (DE, AT, NL, BE, FI = EUR; CH = CHF; SE = SEK; NO = NOK; DK = DKK) follows error 400.157 and is not tabulated by Riverty.
  • B2B availability follows the “B2B Invoice” column of the availability table (DE, AT, NL). The text of the request fields mentions B2B for BE and CH as well.
  • CH legal sentence. In the Compliance Requirements table the Swiss German sentence for the invoice is the direct debit text (apparently copied). The add-on shows the English sentence (or French when the storefront is French) for CH until Riverty confirms.
  • CCD2 (from 2026-11-20). Riverty points to “Checkout Best Practices from 20 November 2026” for payment methods in scope of the Consumer Credit Directive 2023/2225; that page was not readable at the time of writing. Instalments and Pay in 3 are credit products: before using them, get Riverty’s confirmation of (a) the method name, tagline and representative example (APR, total cost) your checkout must show, (b) whether the 14 / 30-day invoice stays outside the new duties, (c) consent and advertising rules, (d) whether Riverty’s hosted page covers the creditworthiness and information duties. The invoice is the verified, marketed type.
  • Void after a capture. Response code 400.129 says a partly or fully captured order cannot be cancelled, while the void FAQ says an empty-body void cancels what is left after captures. The add-on follows 400.129 (void only before the first capture; the rest of a reservation can be captured, or it lapses). Confirm in Partner Test.
  • Order numbers after a reinstall. The Riverty order number is the shop order id (plus -<attempt> for a repeated attempt). After a reinstall the attempt counter of an old order starts again, so paying an old, unpaid order again can meet 400.100 (order number already used); use an order number prefix if you reinstall with open orders.
  • Get Refunds / Get Captures / Get Voids are addressed by Riverty’s own numbers; after a lost answer the add-on reconciles through Get Order totals and capture numbers instead.
  • Pending answer shape. Beyond outcome, checkoutId, redirectUrl the reference shows no Pending example; the mock server marks that fixture as built from the field tables.
Table Purpose
?:kv_riverty_accounts Keys per country (one sealed JSON per mode), webhook secret per mode (sealed), merchant id, order prefix, last check, pause (set after a 401)
?:kv_riverty_transactions One row per Riverty order = authorization attempt: order number, country, status, amounts (authorized / captured / voided / refunded, minor units), authorized lines, expiry, customer message
?:kv_riverty_ops Captures, voids, refunds with reference, idempotency key, items and state
?:kv_riverty_log, ?:kv_riverty_jobs Log (masked: keys, birth date, ids, phone, e-mail, IBAN and the whole customer / delivery customer objects of requests and responses are never stored, also with “Debug log” on) and queue
Hook Use
pre_place_order refuses the order while the Riverty buyer data is missing or wrong
prepare_checkout_payment_methods hides a Riverty method while the add-on is off / unlicensed / paused, there is no key for the billing country, or the country / currency pair is not served for that method
change_order_status_post queues capture / void / refund when the order reaches the status set in the add-on settings
init_templater_post upgrade synchronizer (only while an upgrade is pending)

Template: addons/kv_riverty/views/orders/components/payments/kv_riverty.tpl (storefront payment block), hooks/orders/tabs_extra.post.tpl (admin order tab).

The base URL can be overridden for tests in config.local.php (development only):

$config['kv_riverty_base_url'] = 'http://127.0.0.1:11847';

node tests/mock-server/server.js 11847 starts a local stand-in built from the official reference; node tests/e2e/e2e.mjs --root <store dir> runs the HTTP end-to-end test and refuses to run unless the store points at a local mock; php tests/unit.php runs the unit tests (fixtures in tests/fixtures are copied from the official examples). Tests never reach a real Riverty endpoint.

Admin templates live in design/backend/templates/addons/kv_riverty/, the storefront block in var/themes_repository/responsive/templates/addons/kv_riverty/ (copied to the theme on install). Do not edit them in a store; they are replaced on update.