Developer notes
Money model
Section titled “Money model”- 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_carthook (priority 250, after gift certificates at 200) takes the applied amount off$cart['total']after taxes, shipping and promotions.allow_place_orderadds 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_suborderslowers the remaining plan (the reward points pattern). Each vendor order is debited separately.
Ledger
Section titled “Ledger”?:kv_cw_ledgeris append-only: one row per balance change withamount(signed),balance_after,reason,source, order, admin, notes and expiry. Rows are never updated or deleted (except when the customer is deleted).?:kv_cw_walletscaches the balance and totals;?:kv_cw_lotstracks what is left of each credit (for expiry),?:kv_cw_lot_usagewhich 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.
Data model
Section titled “Data model”| 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 |
Hooks used
Section titled “Hooks used”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.
PHP API for other add-ons
Section titled “PHP API for other add-ons”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 lowfn_kv_customer_wallet_get_balance($user_id); // float, primary currencyHooks of the add-on:
kv_customer_wallet_post_entry_pre($user_id, $cents, $reason, $params, &$allow): before a balance change; set$allow = falseto 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).
REST API (read only)
Section titled “REST API (read only)”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.
Documents and e-mails
Section titled “Documents and e-mails”- 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(variablesis_credit,amount_text,balance_text,reason_text,note,order_id,expires_text,customer_name,store_name,account_url) andkv_customer_wallet_expiring(amount_text,expires_text,balance_text, …).
Caching and the full page cache
Section titled “Caching and the full page cache”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.
Compatibility with other add-ons
Section titled “Compatibility with other add-ons”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.
File map
Section titled “File map”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.phpdesign/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.poLicense
Section titled “License”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.