Skip to content

Developer notes

Overview · Installation · Configuration · Usage · FAQ

The add-on has no payment gateway of its own. It uses the store’s payment methods through core functions:

  • Order links run the core repay logic of orders.repay (storefront orders.php): save the payment data, set the chosen payment_id, recalculate the payment surcharge (incl. the Multi-Vendor “vendor pays the surcharge” case), increase repaid, change the status to Open with fn_change_order_status(), then fn_start_payment() and fn_order_placement_routines('repay', ...).
  • Custom amount and cart links build a cart array on the server (never the session cart), like the core call requests add-on: fn_add_product_to_cart(), fn_calculate_cart_content() (no cart promotions, shipping calculation skipped), fn_place_order(), fn_start_payment(), fn_order_placement_routines('route', ...). Stock, taxes, order statuses, invoices, inventory and core notifications are therefore exactly as at checkout, and other add-ons see normal orders.
  • Fixed prices (the amount of a custom amount link, fixed unit prices of cart lines) come only from the link record: the hooks act only on a cart that carries the add-on’s own kv_pbl key, which only the add-on’s controller sets. The price is a stored price (stored_price = Y), so options modifiers and catalog promotions do not change it.
  • Custom amounts use a hidden service product per company (KV-PBL-PAYMENT, status Hidden, price 0 with “do not allow adding to cart”, no inventory tracking, free shipping, no taxes) in a hidden category. Its product page answers 404 and the normal cart refuses it.
  • Payment detection: change_order_status_post. When an order enters a paid status of the settings (default P, C) from a non-paid status, every link of the order (link orders not yet paid, and order links of that order) records the payment once: uses + 1, paid_total, paid_at, status Paid when uses reaches max_uses.
  • Return routing: the pay request stores the order id in the session (kv_pbl_flow). For these orders the placement_routines hook keeps the visitor’s own cart ($clear_cart = false) and order_placement_routines redirects back to the pay page with the result; core leftovers (processed_order_id, failed_order_id) of that order are removed from the session cart.
  • Tokens: 24 random bytes (random_bytes) base64url + . + a 12-character HMAC-SHA256 tag keyed with the store’s crypt_key. A token with a wrong tag is rejected before any database query. The database keeps sha256(token) for the lookup and an encrypted copy (fn_encrypt_text) to show the URL to staff again; the plain token is not stored.
  • Rate limits (own table, fixed 10-minute windows, IP hashed): 30 unknown tokens per IP (then HTTP 429 for every token), 12 pay attempts per link, page views per link and IP counted up to 120.
  • No IDOR: everything on the pay page is resolved from the token; an order_id in the request is only used to show the result when the order belongs to the link. Staff pages check the company scope (Ultimate storefront, Multi-Vendor vendor) for every link and order.
  • CSRF: core form protection on all POST requests (admin and storefront).
  • Caching: kv_pay_by_link.pay is not an FPC-cacheable dispatch; it also sends Cache-Control: no-store, private, X-Robots-Tag: noindex, nofollow and Referrer-Policy: no-referrer. Nothing is written to storage_data.
Table Contents
?:kv_pbl_links link_id, company_id, storefront_id, type (O order, A amount, C cart), order_id, amount, description, message, note, token_hash (unique), token_enc, status (C created, O opened, P paid, X cancelled; expired is derived from expires_at), max_uses (0 = unlimited), uses, paid_total, expires_at, user_id (binding), email, customer_name, phone, payment_ids, shipping_cost, created_by, auto, created/updated/opened/emailed/paid/cancelled timestamps, open_count, attempt_order_id
?:kv_pbl_link_items product lines of cart links: product_id, amount, price, fixed_price
?:kv_pbl_orders orders of a link: link_id, order_id, amount, paid, created_at, paid_at
?:kv_pbl_events history: created, updated, opened, emailed, attempt, paid, cancelled, auto
?:kv_pbl_throttle rate-limit buckets
?:kv_pbl_service_products the hidden service product and category per company

All hooks exist with the same arguments in 4.17.2 and 4.21.2.

Hook Why
get_cart_product_data_pre, get_cart_product_data, add_product_to_cart_get_price Fixed link prices in the add-on’s own cart
allow_place_order Skip core shipping for link orders
calculate_cart_post Fixed shipping charge of cart links
placement_routines, order_placement_routines Keep the visitor’s cart, return to the pay page
change_order_status_post Payment detection, automatic links
delete_order Cancel links of deleted orders, forget link orders
Template hooks orders:details_tools, orders:details_sidebar Order page action and links
Template hooks product_picker:table_header, product_picker:table_column_options Fixed unit price column (only when picker_for=kv_pay_by_link)
// init.php: fn_register_hooks('kv_pay_by_link_paid');
function fn_my_addon_kv_pay_by_link_paid($link, $order_id, $order_info)
{
// A payment link was paid: $link is the updated link record.
}
app/addons/kv_pay_by_link/
addon.xml settings, tables, e-mail templates
init.php, func.php hooks, settings info, licence helpers
controllers/backend/kv_pay_by_link.php list, form, link page, send, cancel, delete, QR, CSV
controllers/backend/orders.post.php order page action + sidebar
controllers/backend/kv_pay_by_link_license.php "Check now" / cron licence check
controllers/frontend/kv_pay_by_link.php pay page
controllers/frontend/products.pre.php 404 for the hidden service product
schemas/menu/menu.post.php, menu_vendor.post.php Orders > Payment links
schemas/permissions/admin.post.php, vendor_multivendor.post.php
schemas/settings/variants.functions.post.php order status variants
Tygh/Addons/KvPayByLink/
Config.php settings, licence state
Token.php token generation, signature, hash, encryption
Throttle.php rate limits
Repository.php links, lines, link orders, history, list and counts
Links.php validation, create/update/cancel, scope, presentation
Payments.php allowed payment methods and fees
ServiceProduct.php hidden "Payment" product
Checkout.php pay page data, repay, server-side cart, order placement
Notifier.php e-mails
Hooks.php payment detection, automatic links, deleted orders
Qr.php QR code encoder (SVG)
License/ domain-bound licence client
Upgrade/ self-healing upgrade synchronizer
design/backend/templates/addons/kv_pay_by_link/
views/kv_pay_by_link/manage.tpl, update.tpl, view.tpl, components/status.tpl
hooks/orders/*, hooks/product_picker/*, hooks/index/*, license/*
design/backend/css/addons/kv_pay_by_link/styles.less, license.less
var/themes_repository/responsive/templates/addons/kv_pay_by_link/views/kv_pay_by_link/pay.tpl
var/themes_repository/responsive/css/addons/kv_pay_by_link/styles.less
js/addons/kv_pay_by_link/admin.js, pay.js
var/langs/en/addons/kv_pay_by_link.po