Skip to content

Developer notes

Overview · Installation · Configuration · Usage · FAQ

Item Value
Add-on ID kv_order_source_attribution
PHP namespace Tygh\Addons\KvOrderSourceAttribution
Tables cscart_kv_osa_orders, cscart_kv_osa_customers
Cookie kv_osa (first party, path /, SameSite=Lax, Secure on HTTPS)
Language variables kv_order_source_attribution.*
  1. dispatch_before_display (storefront, root template) assigns the script configuration; index:footer prints it as window.kvOsaConfig, index:scripts loads js/addons/kv_order_source_attribution/func.js.
  2. The script collects UTM tags, click IDs, the external referrer (origin + path, never the query) and the landing page, and keeps the first and the last touch in the cookie (with consent) or in sessionStorage.
  3. On checkout.cart and checkout.checkout it posts the state to kv_order_source_attribution.capture (only when it changed); the controller validates it and keeps it in the session.
  4. place_order saves a row for every new order: the session copy or the cookie (the most recent last touch wins), else the referrer core stored when the session started, else direct. Admin orders are manual, API orders api, vendor orders copy the main order. INSERT IGNORE keeps the original source when an order is edited.
  5. The channel is decided on the server (Classifier), so the host lists in the settings apply everywhere; Reclassify recomputes stored rows.

The configuration depends only on the storefront settings and the page type, so cached pages are correct for every guest. Storefront GET requests write nothing (no session, no storage_data); the only write is the AJAX POST from the cart and checkout pages, where a session exists anyway.

With Remember sources across visits = After consent, the script uses window.kvConsent.get(<category>) and follows kvConsent.on('change'): a grant moves the state into the cookie, a withdrawal deletes the cookie. The server stores consent = Y/N with each order from fn_kv_cookie_consent_mode_has_consent(). The inline configuration matches the consent add-on’s kv<Name>Config rule and is never blocked.

Hook File (4.17.2 / 4.21.2) Purpose
dispatch_before_display fn.control.php 623 / 630 storefront script configuration
place_order fn.cart.php 1585 / 1678 saves the source of new orders
get_orders fn.cart.php 7810 / 8044 order list filter (kv_osa_channel, kv_osa_source, kv_osa_medium, kv_osa_campaign, kv_osa_touch, kv_osa_exact)
get_order_info fn.cart.php 2556 / 2657 kv_source_attribution in REST API order responses
delete_order fn.cart.php 2944 / 3071 removes the row
update_profile fn.users.php 2639 / 2685 first source of customers who register on the storefront
delete_user fn.users.php 3403 / 3496 removes the customer row
init_templater_post upgrade synchronizer (only while pending)
before_dispatch (static, License\Client) license checks and admin notices

Template hooks: index:scripts, index:footer (storefront); orders:details_sidebar, orders:manage_header, orders:manage_data, orders:advanced_search, index:styles, addons:tabs_content, addons:manage (admin).

GET /api/orders/<order_id> contains:

"kv_source_attribution": {
"last_touch": {"channel": "paid_search", "source": "google", "medium": "cpc", "campaign": "autumn-sale", "term": "", "content": "", "landing": "/?utm_source=google&utm_medium=cpc&utm_campaign=autumn-sale", "referrer": "", "click_ids": {"gclid": "..."}, "time": 1792000000},
"first_touch": {"channel": "email", "source": "newsletter", "medium": "email", "...": "..."},
"touches": 3, "capture": "cookie", "consent": "Y", "parent_order_id": 0
}

null when the order has no stored source. Orders created through the API are saved with the channel api.

window.kvOsa.get() returns the stored state ({v, n, f, l} with the short keys s source, m medium, c campaign, t term, o content, k click IDs, r referrer, p landing page, ts time) or null.

  • kv_osa_orders: one row per order: capture (cookie, session, server, inherited, admin, api, none), consent, touches, the last touch (channel, source, medium, campaign, term, content, landing, referrer, click_ids JSON, utm, touch_time) and the same fields with the prefix f_ for the first touch.
  • kv_osa_customers: the first known source of a registered customer.

Tygh\Addons\KvOrderSourceAttribution\License\Client verifies the license against the Kavio Labs license server. The client, the “Check now”/cron controller controllers/backend/kv_order_source_attribution_license.php and the license templates are generated from the shared Kavio Labs template; don’t edit them by hand.

  • Admin license UI: design/backend/templates/addons/kv_order_source_attribution/license/card.tpl (License card, every tab of the add-on page), license/badge.tpl (Add-ons > Manage add-ons), license/status_bar.tpl (first line of the Order sources report), license/info.tpl (License tab), design/backend/css/addons/kv_order_source_attribution/license.less. They render nothing for vendors and for administrators without the Manage settings privilege; templates only read $kv_lic_kv_order_source_attribution.
  • Gate: fn_kv_order_source_attribution_is_licensed() (func.php) reads the stored state only. Without a valid license the storefront configuration is not printed (the script does nothing), the capture endpoint stores nothing, and place_order saves no source. The admin panel, the stored data, the report, the order panel and the filters keep working. Checkout is never affected.
app/addons/kv_order_source_attribution/
addon.xml, init.php, func.php
controllers/frontend/kv_order_source_attribution.php capture endpoint (POST)
controllers/backend/kv_order_source_attribution.php report, export, reclassify
controllers/backend/orders.post.php order panel, list column and filter data
controllers/backend/kv_order_source_attribution_license.php license "Check now" / cron (generated)
schemas/menu/menu.post.php, menu_vendor.post.php
schemas/permissions/admin.post.php, vendor_multivendor.post.php
Tygh/Addons/KvOrderSourceAttribution/
Access.php Attribution.php Classifier.php Config.php Report.php Storefront.php Touch.php
License/Client.php License/Ed25519.php Upgrade/Synchronizer.php
design/backend/templates/addons/kv_order_source_attribution/
views/kv_order_source_attribution/report.tpl, components/touch.tpl
hooks/orders/{details_sidebar,manage_header,manage_data,advanced_search}.post.tpl, license/*
design/backend/css/addons/kv_order_source_attribution/{styles,license}.less
var/themes_repository/responsive/templates/addons/kv_order_source_attribution/hooks/index/{scripts,footer}.post.tpl
js/addons/kv_order_source_attribution/func.js
var/langs/en/addons/kv_order_source_attribution.po