Developer guide
| Path | Purpose |
|---|---|
app/addons/kv_abandoned_cart_recovery/addon.xml |
Settings, tables, e-mail templates |
app/addons/kv_abandoned_cart_recovery/func.php |
Hook handlers, install/uninstall, settings info handlers |
app/addons/kv_abandoned_cart_recovery/Tygh/Addons/KvAbandonedCartRecovery/ |
CartCapture, Sender, MessageBuilder, Coupons, Restorer, Attribution, Consent, Links, Stats, Repository, Config, StorageData, Logger, Upgrade\Synchronizer |
controllers/backend/kv_abandoned_cart_recovery.php |
Dashboard, cart list, cart view, preview, run now, console cron |
controllers/backend/kv_abandoned_cart_recovery_license.php |
“Check now” / cron license check |
app/addons/kv_abandoned_cart_recovery/Tygh/Addons/KvAbandonedCartRecovery/License/Client.php |
License check client (generated from the shared Kavio Labs template) |
design/backend/templates/addons/kv_abandoned_cart_recovery/license/info.tpl |
License tab (points to the card) |
design/backend/templates/addons/kv_abandoned_cart_recovery/license/card.tpl |
License card (add-on page) / banner |
design/backend/templates/addons/kv_abandoned_cart_recovery/license/status_bar.tpl |
Status chip/banner for own admin pages |
design/backend/templates/addons/kv_abandoned_cart_recovery/license/badge.tpl |
Badge in Add-ons > Manage add-ons |
design/backend/templates/addons/kv_abandoned_cart_recovery/hooks/addons/ |
Tabs_content.pre.tpl (card), manage.post.tpl (badge) |
design/backend/templates/addons/kv_abandoned_cart_recovery/hooks/index/styles.post.tpl |
Loads license.less |
design/backend/css/addons/kv_abandoned_cart_recovery/license.less |
Card, chip and badge styles (admin LESS variables) |
controllers/frontend/kv_abandoned_cart_recovery.php |
Restore, unsubscribe/resubscribe, capture endpoint, cron URL |
controllers/frontend/checkout.post.php |
Checkbox state for the checkout page |
schemas/ |
Admin menu, permissions (admin, vendor), settings variants, GDPR |
var/themes_repository/responsive/templates/addons/kv_abandoned_cart_recovery/ |
hooks/index/scripts.post.tpl (checkout only), unsubscribe page |
js/addons/kv_abandoned_cart_recovery/func.js |
Checkout checkbox and e-mail capture |
Hooks used
Section titled “Hooks used”| Hook | Since | Use |
|---|---|---|
save_cart_content_post |
4.x | Capture storefront carts (AREA === 'C', type C). No write when the cart hash and e-mail are unchanged. |
place_order_post |
4.x | Attribution (parent orders only), coupon single use |
delete_user |
4.x | Delete the customer’s captured carts |
before_dispatch |
4.x | Optional admin fallback runner (shutdown function, every 10 minutes at most) |
before_dispatch (admin area and console only; static handler License\Client::onBeforeDispatch) |
4.x | Runs the due license check and shows license notices (not on the add-on’s own pages, which show the status bar) |
init_templater_post |
4.x | Upgrade synchronizer (only while an upgrade is pending) |
Template hooks: index:scripts (storefront, only when the checkout controller assigned $kv_acr_checkout); admin addons:tabs_content (pre, license card at the top of the add-on page), addons:manage (post, license badge, moved into the add-on’s row) and index:styles (post, license.less).
Tables
Section titled “Tables”| Table | Content |
|---|---|
?:kv_acr_carts |
One row per captured cart: identity (user_type R/U, user_id = customer id or guest cart id), storefront, company (Ultimate), e-mail, name, language, currency, subtotal, cart_hash, status (P, R, C, O, U, X), steps_sent, last_sent_at, clicks, order_id, order_total, recovered_step, token, timestamps |
?:kv_acr_items |
Cart lines: product, vendor (company_id), amount, unit price, product_options (JSON), name |
?:kv_acr_messages |
One row per reminder: step, status (S sent, F failed), error, sent/clicked time, coupon code, promotion, coupon status (A active, U used, E expired/withdrawn), expiry |
?:kv_acr_contacts |
Per e-mail consent: Y (ticked the box or resubscribed), N (unticked or unsubscribed) |
All tables are InnoDB, created with CREATE TABLE IF NOT EXISTS and dropped on uninstall.
Coupons
Section titled “Coupons”Codes are added to the core Coupon code (auto-generated) condition (auto_coupons) of the chosen cart promotion: the same mechanism the core Give coupon bonus uses. Coupons::issue() appends a code under a MySQL named lock, rewrites conditions and conditions_hash with core fn_promotion_serialize(). markUsed() (order placed), expire() (run) and withdrawForCarts() (delete/uninstall) remove codes again; resync() re-adds active codes that are missing (e.g. after the promotion was saved from an admin page opened before the code was added). The code list stays small because only valid codes are in it; a promotion whose conditions grow beyond 50 KB gets no new codes.
Full page cache (CS-Cart 4.21)
Section titled “Full page cache (CS-Cart 4.21)”- No per-visitor output on cacheable pages: the checkout script and its configuration are only rendered on the checkout (never cached). The restore, unsubscribe, capture and cron dispatches are not cacheable.
- Capture writes only to the add-on tables, and only when the cart changed.
- Run information (last run, last result, error log) uses
StorageData: files invar/kv_storage/kv_abandoned_cart_recovery/while thefull_page_cacheadd-on is active,?:storage_dataotherwise. Writing?:storage_datawould purge the whole page cache.
Console and URL
Section titled “Console and URL”php admin.php --dispatch=kv_abandoned_cart_recovery.cronGET index.php?dispatch=kv_abandoned_cart_recovery.cron&key=<cron key>Both print the JSON summary of the run: sent, failed, skipped, ordered, closed, expired, purged, locked, disabled, unlicensed. A MySQL named lock prevents parallel runs.
Signed links
Section titled “Signed links”index.php?dispatch=kv_abandoned_cart_recovery.restore&t=<token>&s=<step>&h=<signature> and ...unsubscribe&t=<token>&h=<signature>. The token is a random 128-bit value per cart; the signature is an HMAC-SHA256 (truncated) of the action, token and step with the add-on’s link secret (hidden setting generated on install).
License check
Section titled “License check”Tygh\Addons\KvAbandonedCartRecovery\License\Client verifies the license against the Kavio Labs license server.
fn_kv_abandoned_cart_recovery_is_licensed()is the gate. It reads the stored state only (one storage read per request) and never contacts the server. In the storefront it uses the current storefront’s host; in the admin panel, console and cron it means “licensed for at least one storefront”. When it returnsfalse:fn_kv_abandoned_cart_recovery_save_cart_content_post()captures no carts;controllers/frontend/checkout.post.phpassigns no$kv_acr_checkout, so the checkout checkbox and its script are not rendered;- the capture endpoint (
kv_abandoned_cart_recovery.capture) answers{"ok":false}; Sender::run()(console cron, cron URL, admin fallback, Run now) still expires and re-syncs the codes of e-mails already sent, closes stale carts and purges old records, but sends no reminders and issues no coupons; its summary has"unlicensed": true;Sender::sendNext()(Send reminder N now on the cart page) returnsunlicensed.
fn_kv_abandoned_cart_recovery_is_licensed_for_storefront($storefront_id)checks the host of a cart’s storefront (?:storefronts.url): on CS-Cart (Ultimate) with storefronts on several domains,Sender::run()skips the carts of storefronts the license does not cover (Repository::getDue()excludes them).- Not gated: restore and unsubscribe/resubscribe links, order attribution of restored carts, the e-mail preview and Send test (to an admin address), the dashboard, cart list and cart pages, GDPR export/anonymization,
delete_usercleanup, uninstall. - No output inside core-cached blocks, so no
callable_handlersentry is needed. Full page cache (4.21): the gate only reads stored state. - Request:
POST https://cs-cart.kaviolabs.com/index.php?dispatch=kv_licenses.checkwith JSON{"v":1, "license", "addon", "version", "hosts":[…], "platform":{"version", "edition"}, "nonce"}, sent throughTygh\Http(3 s connect, 5 s total; kept out of the CS-Cart request log). The answer is accepted only when its Ed25519 signature matches a public key embedded in the client and it echoes the nonce. - State: storage key
kv_license_kv_abandoned_cart_recovery(?:storage_data), orvar/kv_storage/kv_abandoned_cart_recovery/license.jsonwhile the full page cache is active, protected by an HMAC with the store’s crypt key. - Cron:
php admin.php --dispatch=kv_abandoned_cart_recovery_license.check. - Admin UI:
License\Clientassigns the view data of the license card to$kv_lic_kv_abandoned_cart_recoveryin itsbefore_dispatchhandler, only for administrators with the Manage settings privilege (update_settings); vendors never get it. The templates only read this variable, so they render nothing (and no error) while an older version of the PHP code is loaded, for example in the request that uploads a new zip over the installed add-on. PHP code can callfn_kv_abandoned_cart_recovery_license_ui($context). Every full page of the add-on’s own admin controllers (dashboard, cart list, cart page) includeslicense/status_bar.tplas the first line of its mainbox: a green chip when licensed, the card as a compact banner otherwise. The add-on’s license notice is not shown on these pages (the bar shows the status); it appears on the next other admin page. - Development stores only (
http_hostandhttps_hostare development hosts): the constantsKV_LICENSE_SERVER_URL,KV_LICENSE_TEST_HOST(act as if the development storefronts ran on that host) andKV_LICENSE_TEST_TIME_OFFSET(seconds). They are ignored on production hosts. - The license check endpoint (
kv_abandoned_cart_recovery_license.check) accepts only CSRF-protected POST requests of administrators, and console requests. Vendors cannot use it.
Kavio Labs consent contract
Section titled “Kavio Labs consent contract”With kv_cookie_consent_mode active and the setting on, the as-you-type capture runs only when window.kvConsent.has('marketing') is true in the browser, and the capture endpoint re-checks fn_kv_cookie_consent_mode_has_consent('marketing'). An explicit tick of the reminder checkbox is always accepted. The add-on sets no cookies of its own.
Extending
Section titled “Extending”- Edit the e-mails in Design → E-mail templates; all variables are listed in Configuration.
- The add-on uses core hooks only; it does not override core templates or controllers.
- Kill switch for the upgrade synchronizer:
define('KV_UPGRADE_SYNC_DISABLED', true);inconfig.local.php.
Third-party code
Section titled “Third-party code”Tygh/Addons/KvAbandonedCartRecovery/License/Ed25519.php (checks the signature of the license server’s answers on servers whose PHP has no sodium extension) is derived from paragonie/sodium_compat, ISC License, Copyright (c) 2016-2023 Paragon Initiative Enterprises and Copyright (c) 2013-2019 Frank Denis. The full license notice is in the file header.