Store Credit Event Reference
Store Credit is designed to be extended through events rather than through class rewrites. It dispatches five Magento events of its own around reading and writing balances, observes nine Magento events to keep the ledger in step with orders, and emits two Magewire events on the storefront. This page is the reference for all of them.
Store Credit uses its own events internally too: the balance change emails are an observer on hyva_store_credit_balance_changed, not a hardcoded call. Anything the module can do to itself, your module can do the same way.
Events Store Credit Dispatches
These are the extension points for your own code. All five are dispatched by Hyva\StoreCredit\Model\Ledger\LedgerWriter and Hyva\StoreCredit\Model\Ledger\BalanceReader.
hyva_store_credit_append_before
Fired inside LedgerWriter::append() after the entry has been validated and after the idempotency check, but before the database transaction opens. Nothing has been written yet.
| Key | Type | Contents |
|---|---|---|
ledger_entry |
LedgerEntryInterface |
The entry about to be written. balance_after is not set yet. |
This is the only veto point in the write path. Throwing an exception from an observer here aborts the write before any database work happens, which makes it the right place for a policy check such as refusing credit to a customer group or capping how much credit a single adjustment may add.
hyva_store_credit_append_after
Fired inside LedgerWriter::append() after the transaction has been committed. The entry now has its ID, its balance_after, and its resolved currency.
| Key | Type | Contents |
|---|---|---|
ledger_entry |
LedgerEntryInterface |
The entry as written, with entry_id and balance_after populated. |
balance_before |
float |
The balance before this entry was applied. |
Use this when you need the written entry itself, for example to push a row into a reporting table or an external accounting system.
hyva_store_credit_balance_changed
Fired immediately after hyva_store_credit_append_after, with the change flattened into scalars so an observer does not have to unpack the entry. This is the event most integrations want.
| Key | Type | Contents |
|---|---|---|
customer_id |
int |
Whose balance changed. |
website_id |
int |
Which website's balance changed. |
event_type |
int |
One of the EventType constants. |
delta |
float |
The signed change, negative for spends and debits. |
balance_before |
float |
The balance before the change. |
balance_after |
float or null |
The balance after the change. |
base_currency_code |
string or null |
The currency the balance is held in. |
reason |
string or null |
The internal reason. The customer-facing reason is on the entry as reason_external. |
source |
string or null |
One of the Source constants, such as checkout or creditmemo. |
ledger_entry |
LedgerEntryInterface |
The full entry, if you need the references or attribution. |
This event fires after the balance change has been committed, so it cannot veto anything. That is deliberate: a failing notification must never roll back a customer's credit.
hyva_store_credit_get_balance_before
Fired at the start of BalanceReader::getBalance(), before the projection is read.
| Key | Type | Contents |
|---|---|---|
customer_id |
int |
The customer whose balance is being read. |
website_id |
int |
The website the balance is read for. |
hyva_store_credit_get_balance_after
Fired at the end of BalanceReader::getBalance(), with the resolved balance.
| Key | Type | Contents |
|---|---|---|
customer_id |
int |
The customer whose balance was read. |
website_id |
int |
The website the balance was read for. |
balance |
BalanceInterface |
The balance object, which observers can modify. |
The balance object is mutable, so an observer can rewrite the amount before it reaches the caller. That makes it possible to layer promotional credit on top of the stored balance without writing it to the ledger, but it also means an incorrect observer here makes every balance in the system wrong. Read carefully before reaching for it.
Duplicate Writes Fire No Events
When LedgerWriter::append() is called with an idempotency key that already exists, the existing entry is returned and none of append_before, append_after, or balance_changed fires. Anything you hang off these events must therefore be safe to not run on a retry, and must not be the only place a required side effect happens.
Observing a Store Credit Event
Observers are registered the ordinary Magento way. This example logs every balance change to your own audit table.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="hyva_store_credit_balance_changed">
<observer name="vendor_module_record_credit_change"
instance="Vendor\Module\Observer\RecordCreditChange"/>
</event>
</config>
The observer reads the flattened payload straight off the event:
<?php
declare(strict_types=1);
namespace Vendor\Module\Observer;
use Hyva\StoreCredit\Model\Ledger\EventType;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
class RecordCreditChange implements ObserverInterface
{
public function __construct(private readonly \Vendor\Module\Model\AuditWriter $auditWriter)
{
}
public function execute(Observer $observer): void
{
$event = $observer->getEvent();
$eventType = (int) $event->getData('event_type');
// EventType classifies the change, so no need to test the sign of the delta.
$this->auditWriter->record(
(int) $event->getData('customer_id'),
(int) $event->getData('website_id'),
(float) $event->getData('delta'),
EventType::getLabel($eventType),
EventType::isCredit($eventType)
);
}
}
Magento Events Store Credit Observes
Store Credit already hooks these events. Check this table before adding an observer of your own, so you know what has run before yours.
| Magento event | Observer | What it does |
|---|---|---|
sales_model_service_quote_submit_before |
SpendStoreCredit |
Copies the applied amount from the quote to the order and writes the SPEND entry, keyed on the order increment ID. Retries with a raised attempt counter when a spend key was already compensated. |
sales_model_service_quote_submit_success |
LinkSpendOrder |
Back-fills order_id on the spend entry once the order has an ID. |
sales_model_service_quote_submit_failure |
RevertStoreCredit |
Writes a REVERT entry when submission fails. Skipped when the increment ID is already used, so a late duplicate failure cannot refund twice. |
order_cancel_after |
RevertStoreCredit |
Writes a REVERT entry for the credit applied minus the credit already refunded. |
payment_method_is_active |
TogglePaymentMethods |
Disables every payment method except free when store credit brings the base grand total to zero. |
sales_order_invoice_save_after |
IncreaseInvoicedStoreCredit |
Accumulates base_store_credit_invoiced on the order. Writes no ledger entry. |
adminhtml_sales_order_creditmemo_register_before |
PrepareCreditmemoRefund |
Reads the requested refund amount, runs the ACL and ceiling checks, and re-collects the credit memo total. |
sales_order_creditmemo_save_after |
RefundStoreCredit |
Writes the REFUND entry keyed on the credit memo ID, and accumulates base_store_credit_refunded on the order only when a new entry was created. |
hyva_store_credit_balance_changed |
SendBalanceChangeEmail |
Sends the balance change email when the entry is marked notifiable. Catches and logs any failure. |
hyva_config_generate_before |
RegisterModuleForHyvaConfig |
Registers the module in hyva-themes.json so its Tailwind config is merged. Adminhtml area only. |
Magewire and Browser Events
On the storefront, the checkout component talks to the rest of checkout through Magewire events rather than direct calls.
Hyva\StoreCredit\Magewire\Checkout\StoreCredit emits:
store_credit_appliedwhen the customer applies their credit.store_credit_revokedwhen the customer removes it.
Hyva\StoreCredit\Plugin\Checkout\PriceSummaryListeners adds both of those to Hyvä Checkout's PriceSummary component as refresh listeners, which is how the order summary updates itself when credit goes in or out. If you build a component that needs to react to store credit, listen for the same two events.
The store credit component listens to everything that could change the order total, all mapped to refresh: shipping_method_selected, payment_method_selected, coupon_code_applied, coupon_code_revoked, shipping_address_saved, shipping_address_activated, billing_address_saved, and billing_address_activated.
In the browser, two more events matter:
checkout:step:loadedwithdetail.subsequentre-refreshes the store credit component, which is what keeps the panel alive when the customer navigates forward to payment and back again.private-content-loadeddrives the cart and minicart panels, which dispatchreload-customer-section-dataafter applying or removing credit to refresh thehyva-store-creditsection.
Related Topics
- Store Credit Service API - writing to the ledger from your own code, rather than reacting to it.
- Store Credit Architecture - idempotency keys, the projection, and the write guards these events sit around.
- Theming Store Credit - the components that emit and listen to the Magewire events.