Skip to content

Developer notes

  • Amounts are stored in the store’s primary currency with two decimals; all arithmetic is done in integer cents (Money::cents), so sums and splits are exact.
  • Paying with store credit follows the core gift certificates: the calculate_cart hook (priority 250, after gift certificates at 200) takes the applied amount off $cart['total'] after taxes, shipping and promotions. allow_place_order adds it back to the total used for the minimum order amount. The payment surcharge is calculated by core on the reduced total. A zero total goes through the core “no payment” path.
  • Multi-Vendor split: the parent order records the planned amount; each vendor order (recalculated by core in fn_place_suborders) gets a share proportional to its pre-credit total, never more than its total and never less than what the remaining vendor orders can take; the last one takes the rest. place_suborders lowers the remaining plan (the reward points pattern). Each vendor order is debited separately.
  • ?:kv_cw_ledger is append-only: one row per balance change with amount (signed), balance_after, reason, source, order, admin, notes and expiry. Rows are never updated or deleted (except when the customer is deleted).
  • ?:kv_cw_wallets caches the balance and totals; ?:kv_cw_lots tracks what is left of each credit (for expiry), ?:kv_cw_lot_usage which credits a debit used, so a returned order puts the money back into the same credits.
  • Debits use credits that expire first, then the oldest. A credit first covers a negative balance.
  • Changes of one customer are serialised with a named database lock (GET_LOCK).
  • Reasons: order_payment, order_restore, order_redebit, order_adjust, refund, cashback, cashback_reversal, admin_credit, admin_debit, topup, topup_reversal, expiry, external.
Table Contents
?:kv_cw_wallets user_id, balance, credited_total, debited_total, status (A active, F frozen)
?:kv_cw_ledger the ledger (see above)
?:kv_cw_lots, ?:kv_cw_lot_usage remaining amount and expiry per credit; credits used per debit
?:kv_cw_orders per order: wallet amount and state (A taken, R returned), returned, refunded, cashback amount/state (P pending, C credited, R reversed, X cancelled), top-up amount/state, last status
?:kv_cw_rules cashback rules

calculate_cart (250), allow_place_order, allow_place_order_post, place_order, place_suborders, get_order_info (adds $order_info['kv_wallet']), form_cart, update_cart_by_data_post, change_order_status_post, delete_order, post_delete_user, vendor_payout_details_builder_create_details_post, rma_update_details_create_payout, before_dispatch (schedule fallback). Signatures are identical in 4.17.2 and 4.21.2.

Loyalty, referral or review programs can credit the wallet:

if (function_exists('fn_kv_customer_wallet_credit')) {
// returns the ledger row or false
fn_kv_customer_wallet_credit($user_id, 5.00, 'my_addon', [
'note' => 'Thank you for your referral', // shown to the customer
'ref' => 'referral:123', // your reference
'expires_days' => 90, // optional
'notify' => true, // e-mail (default: setting)
]);
}
fn_kv_customer_wallet_debit($user_id, 2.50, 'my_addon', ['note' => '...']); // refused when the balance is too low
fn_kv_customer_wallet_get_balance($user_id); // float, primary currency

Hooks of the add-on:

  • kv_customer_wallet_post_entry_pre($user_id, $cents, $reason, $params, &$allow): before a balance change; set $allow = false to refuse it.
  • kv_customer_wallet_post_entry_post($row, $params): after a balance change (the ledger row).
  • kv_customer_wallet_cashback_amount($cart, $auth, &$total, $amounts): change the cashback of an order (cents).

Administrators with the View store credit privilege:

  • GET /api/kv_wallets?q=&balance=positive|negative|zero|any&page=&items_per_page=: wallets with balances.
  • GET /api/kv_wallets/<user_id>?reason=&page=&items_per_page=: balance, totals, status, credit expiring within 30 days and the ledger.

POST, PUT and DELETE return 405. In CS-Cart (multi-storefront) a storefront administrator sees the customers of that storefront.

  • Order documents (invoice, order e-mails) get the variable {{ kv_wallet.text }} (“Paid with store credit: $10.00”), {{ kv_wallet.amount }}, {{ kv_wallet.cashback }} and {{ kv_wallet.raw.amount }}. Add it to your invoice template in Design → Documents if you want the line there; the order total of the invoice is the amount charged by the payment method.
  • E-mail templates (Twig, valid in Twig 2 and 3): kv_customer_wallet_changed (variables is_credit, amount_text, balance_text, reason_text, note, order_id, expires_text, customer_name, store_name, account_url) and kv_customer_wallet_expiring (amount_text, expires_text, balance_text, …).

Nothing is printed on cacheable pages: the account link sits in the “My account” block (rendered per visitor by the full page cache of 4.21), the balance only on kv_customer_wallet.view and the checkout pages, which are never cached. Run marks of the schedule go to var/kv_storage while the full page cache is on, so no storage-data write purges the cache.

Fees added in calculate_cart before priority 250 (gift wrap, setup fees) can be paid with store credit; promotions, taxes and VAT exemption are applied by core before. With deposit add-ons, the deposit is a share of the order total after store credit, so store credit + deposit + balance equals the full total. Core gift certificates and reward points work alongside.

app/addons/kv_customer_wallet/
addon.xml, init.php, func.php
controllers/backend/kv_customer_wallet.php wallets, wallet, transactions, rules, refunds, CSV, cron
controllers/backend/orders.post.php data of the order page panel
controllers/backend/order_management.post.php data of the order editor field
controllers/backend/kv_customer_wallet_license.php
controllers/frontend/kv_customer_wallet.php account page, use/remove at checkout, cron URL
controllers/frontend/checkout.post.php checkout block data and cashback estimate
schemas/menu, permissions, settings, documents
Tygh/Addons/KvCustomerWallet/ Config, Money, Ledger, Wallet, CartWallet, Orders, Cashback,
Topups, Scheduler, Notifier, Install, StorageData,
Documents/WalletVariable, License/, Upgrade/Synchronizer
Tygh/Api/Entities/KvWallets.php
design/backend/templates/addons/kv_customer_wallet/ views (manage, view, transactions, rules, rule_update),
hooks (orders, order_management, rma, profiles, index), license/
var/themes_repository/responsive/templates/addons/kv_customer_wallet/ views/kv_customer_wallet/view.tpl,
hooks (checkout, orders, profiles, index)
var/langs/en/addons/kv_customer_wallet.po

The add-on ships the generated License\Client. Gated (switched off without a valid license): paying with store credit (the checkout block and the calculate_cart reduction for new checkouts) and new cashback records. Not gated: everything that concerns existing data or money already moved: returning store credit of cancelled orders, re-taking it, pending cashback of earlier orders, top-ups of paid orders, expiry and reminders, refunds, admin credits and deductions, the account page, reports, CSV and the REST API, so a started customer transaction always completes.