Skip to content

Developer notes

Step Hook Class
Payment list at checkout prepare_checkout_payment_methods_before_get_payments / _after_get_payments, checkout_select_default_payment_method Checkout::preparePaymentParams(), filterPaymentList(), selectDefault()
Refusal (block, forged COD) allow_place_order_post Checkout::refusal()
Risk record, hold / confirmation status place_order (before the core sets the status) Checkout::placeOrder()
Confirmation e-mail change_order_status_post (once per checkout) Verification::sendPending()
Order list column and filter get_orders (back end) Orders::listQuery()
Cleanup delete_order Orders::deleted()

All hooks exist with the same arguments in 4.17.2 and 4.21.2. The add-on runs after the core Anti fraud add-on (priority 2200 > 2100): when an earlier handler already set $action = 'save' (Anti fraud hold, zero total), Order Risk Guard only records its score.

Payment hiding adds its own parameter kv_org_hide to the fn_get_payments() parameters so the core static cache of the payment list keeps a separate entry; other add-ons that hide payments (for example Shipping & Payment Restrictions) work at the same time.

Holds only change the status of orders whose payment method has no processor script; online payments are never interrupted. On Multi-Vendor the parent order keeps its “parent” status and the child orders get the hold / confirmation status. One evaluation per checkout is shared by the parent and its children.

Scorer::contextFromCart($cart, $auth) / Scorer::contextFromOrder($order_info) build a context (e-mail, domain, phone keys, IP, IP country, address fingerprints, countries, total, user ID). Scorer::evaluate($ctx) returns:

[
'score' => 0..100, 'level' => 'L'|'M'|'H',
'signals' => [['code' => 'problem_orders', 'points' => 50, 'n' => 2], ...],
'allow' => bool, 'block' => bool, 'prepay' => bool, 'review' => bool,
'entry_ids' => [...], 'history' => ['orders' => 3, 'settled' => 1, 'problem' => 2],
]

Scorer::mustBlock(), mustPrepay(), mustReview(), mustVerify() apply the settings. Normalisation is in Normalizer (phones: last 10 digits; addresses: CC|ZIP|street in lower-case letters and digits).

// Add own signals or change the score (keep 'score' 0-100).
fn_set_hook('kv_order_risk_guard_evaluate_post', $ctx, $result);
// Send the confirmation code by SMS, WhatsApp or another channel. Add the channel name to $channels.
fn_set_hook('kv_order_risk_guard_send_code', $group_id, $order, $code, $link, $channels);
// Change the confirmation e-mail data (group_id, token, code).
fn_set_hook('kv_order_risk_guard_notify_pre', $data);

Example: an SMS add-on

function fn_my_sms_kv_order_risk_guard_send_code($group_id, $order, $code, $link, &$channels)
{
$phone = $order['phone'] ?: $order['s_phone'];
if ($phone && my_sms_send($phone, "Your order #{$group_id} code: {$code}")) {
$channels[] = 'sms';
}
}

The add-on itself sends e-mail only; it does not include a paid SMS provider.

Table Contents
?:kv_org_entries Block list (list_type B) and allow list (A): type, normalised value, original display, action, points, note, expires_at, hits, last_hit_at. Unique per list, type and value.
?:kv_org_orders One row per scored order (Multi-Vendor: parent and children, same group_id): identifiers, score, level, signals (JSON), action (hold, verify, flag, allow), review decision.
?:kv_org_verifications One row per checkout group: SHA-256 hash of the link token, hash of the code, attempts, sends, status (P pending, V confirmed, C cancelled, X expired), expiry. Plain tokens and codes are never stored.
?:kv_org_attempts The last 500 refused checkouts.

Order statuses: the codes of “Risk review” and “Awaiting confirmation” are in the hidden settings review_status and verify_status.

Dispatch Area Purpose
kv_order_risk_guard.manage, update, delete, m_delete, import, export admin Lists
kv_order_risk_guard.order_action (do = approve, cancel, resend, score, block) admin Order panel buttons
kv_order_risk_guard.cron admin console Expiry (php admin.php --dispatch=kv_order_risk_guard.cron)
kv_order_risk_guard.confirm storefront Confirmation page (GET), confirm / cancel / resend (POST, CSRF-checked)
kv_order_risk_guard.cron&cron_key=... storefront Expiry for external cron services

Nothing is printed on cacheable storefront pages: the payment note, the confirmation box and the confirmation page are session pages. No storage_data writes from storefront GET requests (the pseudo-cron run mark is written from admin pages or cron, through a file store while the full page cache is on). Scoring runs a handful of indexed queries once per checkout step and once at order placement.

With the core GDPR add-on, the risk records of a customer are part of the data export; anonymisation wipes e-mail, phone, IP and address from the risk records (scores stay). Block-list entries are kept (fraud prevention); delete them on the list page if needed.

app/addons/kv_order_risk_guard/
addon.xml, init.php, func.php
controllers/backend/kv_order_risk_guard.php lists, order actions, cron
controllers/backend/orders.post.php order panel data, list filter values
controllers/frontend/kv_order_risk_guard.php confirmation page, cron URL
controllers/frontend/checkout.post.php box on the order-placed page
controllers/frontend/orders.post.php box on the customer order page
schemas/ gdpr, menu, notifications, permissions, security
Tygh/Addons/KvOrderRiskGuard/
Config, Normalizer, Disposable, Lists, Scorer, Checkout, Verification, Orders, Installer,
Notifier, ConfirmDataProvider, StorageData, License/*, Upgrade/Synchronizer
design/backend/templates/addons/kv_order_risk_guard/ views/manage.tpl, components/panel.tpl, hooks/orders, license
design/backend/css/addons/kv_order_risk_guard/styles.less
var/themes_repository/responsive/templates/addons/kv_order_risk_guard/ views/confirm.tpl, components/box.tpl, hooks
var/themes_repository/responsive/css/addons/kv_order_risk_guard/styles.less
var/langs/en/addons/kv_order_risk_guard.po