Skip to content

Developer notes

Table Contents
cscart_kv_ntcl_accounts One row per customer (user_id): status (A active, H on hold, O on hold overdue, C closed), use_group, credit_limit, terms_days, terms_basis (I invoice date, M end of month), over_limit (‘’ store default, B block, R review), notes, hold reason, dates.
cscart_kv_ntcl_group_rules Usergroup terms: usergroup_id, status, limit, terms.
cscart_kv_ntcl_invoices One row per net-terms leaf order (order_id): parent_order_id, user_id, company_id (vendor in Multi-Vendor), total, paid_amount, state (R review, O open, P paid, X cancelled), terms, issued_at, due_at, paid_at, po_number, the original payment_id, reminder counters, token_hash (sha256) and token_enc (encrypted copy) of the payment link.
cscart_kv_ntcl_payments Payments: order, amount, method (online, manual, status), reference, note, date, staff member.
cscart_kv_ntcl_applications Applications (P pending, A approved, D declined) with the form data and the decision.
cscart_kv_ntcl_events History of accounts and invoices.
cscart_kv_ntcl_throttle Rate limits of the payment page and the application form.

Money is stored with 2 decimals in the primary currency; sums are made in whole cents. Balance = total − paid. Open balance of a customer = sum of the balances of invoices in review or open. Available credit = limit − open balance.

  1. get_payments_post (storefront only): net-terms methods (template addons/kv_net_terms_credit_limit/views/orders/components/payments/net_terms.tpl) are removed for guests and for customers without an active account with a limit, and on the core repay form.
  2. pre_place_order (top-level order, only when an order is really placed; core also calls fn_allow_place_order() while rendering the checkout): licence, account status, PO number, and the order total against the available credit, under a per-customer GET_LOCK that is released in place_order_post. Over the limit: refuse, or set $cart['kv_ntcl']['review'].
  3. place_order: for each leaf order paid with a net-terms method the add-on writes the invoice and sets $order_status to Awaiting payment (net terms) or Credit review (the call_requests pattern). The parent order of a Multi-Vendor checkout keeps its core status; its sub-orders are placed with copies of the cart and get their own invoices. Order editing recalculates the invoice total and never changes the status.
  4. change_order_status_post: a paid status records the rest of the balance; Awaiting payment approves a review; Canceled/Declined/Failed cancels the invoice; leaving those statuses reopens it. The invoice e-mail is sent once, when the invoice is open and has the awaiting status.

The statuses are created at installation with fn_update_status() and marked with the status parameter kv_ntcl_status (awaiting / review), so a reinstall reuses them: inventory D (stock reserved), payment_received N, calculate_for_payouts N, repay N, allow_return N.

The payment page (kv_net_terms_credit_limit.pay?t=<token>) charges the open balance of the same order like core repay: it stores the payment data in ?:order_data (type P, encrypted, the PO number is kept), switches the order’s payment_id to the chosen method and calls fn_start_payment().

  • get_order_info: only when called by fn_start_payment() or from a payment script (*/payments/*.php), the order total of a partly paid invoice is the open balance. Every other reader keeps the order total.
  • finish_payment: a paid status records an online payment for the balance (reference = transaction id); any other status maps the order back to Awaiting payment (net terms) and restores the net-terms payment method, so a failed card never cancels or restocks the order. change_order_status_child_order applies the same mapping to sub-orders paid through a parent.
  • order_placement_routines: after a gateway returns, the customer comes back to the payment page.

Tokens: 24 random bytes (base64url) + an HMAC-SHA256 tag keyed with the store’s crypt key. A wrong tag is rejected before any database lookup. The page is rate-limited per IP (bad tokens) and per invoice (payment attempts), sends Cache-Control: no-store, X-Robots-Tag: noindex and Referrer-Policy: no-referrer, and never stores card data.

Hook Use
get_payments_post Hide net terms for customers who may not use it
pre_place_order Credit check, PO number, lock
place_order, place_order_post Invoice and status; release the lock
get_order_info Open balance for gateways
finish_payment, change_order_status_child_order, change_order_status_post Payment and status mapping
order_placement_routines Back to the payment page
delete_order, post_delete_user Clean-up
template_snippet_render_post Terms, due date, PO number on the bill_to snippet of order documents
before_dispatch Admin fallback of the schedule

All hook lines are identical in CS-Cart 4.17.2 and 4.21.2.

Order documents (Design → Documents) get the payment terms automatically. For a custom layout use the document variable:

{% if kv_net_terms.is_invoice %}
{{ kv_net_terms.terms }} · {{ __("kv_net_terms_credit_limit.due_date") }}: {{ kv_net_terms.due_date }}
{% if kv_net_terms.po_number %}PO {{ kv_net_terms.po_number }}{% endif %}
{% endif %}

Fields: is_invoice, terms, due_date, po_number, balance, paid, status, instructions.

Design → E-mail templates: kv_net_terms_credit_limit_invoice, _reminder (before and after the due date, is_overdue), _receipt, _account (what: approved, hold, released, closed), _application (received, declined) and _admin (store notices). All are valid in Twig 2 and Twig 3.

php admin.php --dispatch=kv_net_terms_credit_limit.run (console), index.php?dispatch=kv_net_terms_credit_limit.cron&key=<key> (URL) or the admin fallback (every 30 minutes, after the page was sent). A lock prevents parallel runs; run marks live in storage_data, or in files under var/kv_storage/ while the full page cache is on.

Nothing is printed on cacheable pages (home, categories, products, pages). The checkout, the account pages, the payment page and the cron URL are not cacheable dispatches and send no-store. The “Invoices & credit” link sits in the My account block, which the full page cache renders per visitor (ESI). Storefront GETs write nothing to storage_data.

use Tygh\Addons\KvNetTermsCreditLimit\Accounts;
use Tygh\Addons\KvNetTermsCreditLimit\Invoices;
$summary = Accounts::summary($user_id); // limit, open, available, overdue, terms_text, status ... or null
Accounts::save($user_id, ['credit_limit' => 5000, 'terms_days' => 30, 'terms_basis' => 'I', 'status' => 'A']);
Invoices::recordPayment($order_id, 150.00, 'manual', 'Bank ref 123', ['note' => 'Wire']);
list($errors, $allocations) = Invoices::allocate($user_id, 900.00, 'Bank ref 124');
app/addons/kv_net_terms_credit_limit/
addon.xml, init.php, func.php
controllers/backend/kv_net_terms_credit_limit.php accounts, invoices, applications, aging, rules, statement, actions, console cron
controllers/backend/kv_net_terms_credit_limit_license.php "Check now" / license cron
controllers/backend/orders.post.php invoice panel data
controllers/frontend/kv_net_terms_credit_limit.php invoices & credit, application, statement, payment page, cron URL
controllers/frontend/checkout.post.php credit summary for the payment form
controllers/frontend/orders.post.php customer order page data
schemas/menu/menu.post.php, schemas/permissions/admin.post.php, vendor_multivendor.post.php
schemas/documents/order.post.php, schemas/settings/variants.functions.post.php, actions.functions.post.php
Tygh/Addons/KvNetTermsCreditLimit/
Config.php Install.php Money.php Accounts.php Invoices.php Checkout.php Gateway.php PayPage.php
Notifier.php Scheduler.php Applications.php Reports.php Documents.php Documents/Order/NetTermsVariable.php
Token.php Throttle.php StorageData.php License/Client.php License/Ed25519.php Upgrade/Synchronizer.php
design/backend/templates/addons/kv_net_terms_credit_limit/
views/kv_net_terms_credit_limit/{manage,account,invoices,applications,application,aging,rules,statement_print}.tpl, components/*
views/orders/components/payments/net_terms.tpl, hooks/orders/details_sidebar.post.tpl, settings/cron_info.tpl, license/*
design/backend/css/addons/kv_net_terms_credit_limit/styles.less, license.less
var/themes_repository/responsive/templates/addons/kv_net_terms_credit_limit/
views/kv_net_terms_credit_limit/{invoices,apply,pay,statement_print}.tpl
views/orders/components/payments/net_terms.tpl, hooks/profiles/my_account_menu.post.tpl, hooks/orders/repay.pre.tpl
var/themes_repository/responsive/css/addons/kv_net_terms_credit_limit/styles.less
js/addons/kv_net_terms_credit_limit/func.js
var/langs/en/addons/kv_net_terms_credit_limit.po

The add-on ships the generated License\Client. Gated (switched off without a valid license): the visibility of the Net terms payment method, new net-terms orders and new applications. Not gated: payment links and online payments of existing invoices, the gateway amount, the payment and status mapping, recorded payments, reminders, overdue detection, the automatic hold, statements, the aging report and the admin pages, so a started customer transaction always completes.