Developer notes
Overview · Installation · Configuration · Usage · FAQ
How payments and orders are made
Section titled “How payments and orders are made”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(storefrontorders.php): save the payment data, set the chosenpayment_id, recalculate the payment surcharge (incl. the Multi-Vendor “vendor pays the surcharge” case), increaserepaid, change the status to Open withfn_change_order_status(), thenfn_start_payment()andfn_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_pblkey, 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 (defaultP,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 whenusesreachesmax_uses. - Return routing: the pay request stores the order id in the session (
kv_pbl_flow). For these orders theplacement_routineshook keeps the visitor’s own cart ($clear_cart = false) andorder_placement_routinesredirects 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.
Security
Section titled “Security”- Tokens: 24 random bytes (
random_bytes) base64url +.+ a 12-character HMAC-SHA256 tag keyed with the store’scrypt_key. A token with a wrong tag is rejected before any database query. The database keepssha256(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_idin 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.payis not an FPC-cacheable dispatch; it also sendsCache-Control: no-store, private,X-Robots-Tag: noindex, nofollowandReferrer-Policy: no-referrer. Nothing is written tostorage_data.
Data model
Section titled “Data model”| 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 |
Hooks used
Section titled “Hooks used”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) |
Hook for developers
Section titled “Hook for developers”// 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.}File map
Section titled “File map”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 synchronizerdesign/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.lessvar/themes_repository/responsive/templates/addons/kv_pay_by_link/views/kv_pay_by_link/pay.tplvar/themes_repository/responsive/css/addons/kv_pay_by_link/styles.lessjs/addons/kv_pay_by_link/admin.js, pay.jsvar/langs/en/addons/kv_pay_by_link.po