> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fusioncore.us/llms.txt
> Use this file to discover all available pages before exploring further.

# Event Framework

> The fusionCore in-memory event framework: how packages define hooks and register handlers that run synchronously when an event fires.

<Note>
  **This is not Salesforce Platform Events.** The fusionCore event framework is an
  in-memory hook system. Handlers run **synchronously, in the same transaction,
  immediately** when the event fires — there is no queue and no delivery delay.

  Its purpose is to let one package define extension points ("hooks") and let any
  other package react to them **without a compile-time dependency**. The package
  that fires an event does not need to know which handlers exist; handlers are
  bound to events at runtime through metadata.
</Note>

This page covers how to fire an event, how to write and register a handler, the
events fired across fusionCore today, and the execution characteristics you need
to keep in mind. For the related metadata-driven trigger system, see
[Trigger Framework](/developer-guide/trigger-framework). For the logging and
service utilities referenced below, see [Logging](/developer-guide/logging)
and [Services](/developer-guide/services).

## Firing an Event

Fire an event with `FCORE_BASE.ServiceLayer.fireEvent(String name, FCORE_BASE.EventContext context)`.
You pass data into the handlers through an `FCORE_BASE.EventContext`, which is a
simple key/value bag:

* `setData(String key, Object value)` — put a value into the context before firing.
* `getData(String key)` — read a value back out (handlers call this).

Event names are plain strings. Each subscriber package keeps its names in a
constants class named `ServiceLayerEventConstants` that it owns — Commerce
(`FCORE_PAY`) and Subscriptions (`FCORE_MEM`) each have their own copy. There is
no Platform-owned copy.

The example below fires the wallet-item-removed hook from a Commerce trigger unit:

```java theme={null}
FCORE_BASE.EventContext eventContext = new FCORE_BASE.EventContext();
eventContext.setData('ids', removedWalletItemIds);
FCORE_BASE.ServiceLayer.fireEvent(ServiceLayerEventConstants.EVENT_WALLET_ITEM_REMOVED, eventContext);
```

## Handling an Event

A handler extends `FCORE_BASE.BaseEventHandler`, uses the `EH` suffix by
convention, and overrides `execute()`. Read the payload through
`getContext().getData(key)`.

Wrap the body in a try/catch: log the exception via `FCORE_BASE.Logger` and then
rethrow it so the caller can react. Do **not** swallow it.

```java theme={null}
global without sharing class WalletItemRemovedEH extends FCORE_BASE.BaseEventHandler {
    public override void execute() {
        List<Id> walletItemIds = (List<Id>) getContext().getData('ids');
        try {
            // react to the hook
        } catch (Exception e) {
            FCORE_BASE.Logger.setApexExecutionContext(WalletItemRemovedEH.class, 'execute');
            FCORE_BASE.Logger.logAndCommit(e, FCORE_BASE.Logger.LogLevel.ERROR, walletItemIds, false);
            throw e;
        }
    }
}
```

<Note>
  The `WalletItemRemovedEH` above is illustrative of the handler **shape** only.
  See the [WalletItemRemoved](#a-note-on-walletitemremoved) note for how that hook
  is actually wired in current source.
</Note>

There is no `EventHandlerException` class — rethrow the caught exception directly
(`throw e;`) after logging it.

## Registering a Handler

Bind an event name to a handler class with an `FCORE_BASE__Event_Handler__mdt`
custom metadata record. The relevant fields are:

* `FCORE_BASE__Event_Name__c` — the event string this handler responds to.
* `FCORE_BASE__Apex_Class__c` — the handler class (the one extending `FCORE_BASE.BaseEventHandler`).
* `FCORE_BASE__Order__c` — ascending execution order when several handlers respond to the same event.
* `FCORE_BASE__Is_Enabled__c` — whether the handler runs.

A minimal metadata record looks like this:

```xml theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata">
    <label>Wallet Item Replaced Handler</label>
    <values>
        <field>FCORE_BASE__Event_Name__c</field>
        <value xsi:type="xsd:string">WalletItemReplaced</value>
    </values>
    <values>
        <field>FCORE_BASE__Apex_Class__c</field>
        <value xsi:type="xsd:string">FCORE_PAY.WalletItemReplacedEH</value>
    </values>
    <values>
        <field>FCORE_BASE__Order__c</field>
        <value xsi:type="xsd:double">10</value>
    </values>
    <values>
        <field>FCORE_BASE__Is_Enabled__c</field>
        <value xsi:type="xsd:boolean">true</value>
    </values>
</CustomMetadata>
```

When metadata is not appropriate — for example, registering a handler inside a
test — register it programmatically instead (see
[Execution Characteristics](#execution-characteristics)).

## Events Fired Across fusionCore

The following events are fired across the fusionCore packages today.

| Event Name              | Handler                                                                   | What It Does                                                                                                                                                                                          |
| ----------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CalculatePrice`        | `FCORE_PAY.CalculatePriceEH`                                              | Requests price recalculation for orders whose items changed; runs the Pricing Engine (synchronously, or via batch for large orders).                                                                  |
| `CalculateTax`          | `FCORE_PAY.CalculateTaxEH`                                                | Requests tax calculation; fired by `CalculatePriceEH` after pricing so tax is computed on fresh prices; runs the Tax Engine.                                                                          |
| `Payment`               | `FCORE_PAY.PaymentEH`                                                     | Fired when a payment is applied to one or more orders; validates the financial details in the context and creates the payment Financial Events.                                                       |
| `GenerateInvoice`       | `FCORE_PAY.GenerateInvoiceEH`                                             | Fired during order activation; validates the invoice configurations and creates the invoice Financial Events.                                                                                         |
| `WalletItemReplaced`    | `FCORE_PAY.WalletItemReplacedEH` (Subscriptions also subscribes)          | Fired when a stored payment method is replaced; Subscriptions reassigns auto-renewing Purchase Activities from the old wallet item to its replacement.                                                |
| `GenerateRenewalOrder`  | `FCORE_MEM.GenerateRenewalOrderEH`                                        | Fired when subscriptions are due for renewal; runs the Subscription Renewal Engine, then fires `RenewalOrderGenerated` and triggers installment generation.                                           |
| `RenewalOrderGenerated` | `FCORE_MEM.RenewalOrderGeneratedEH`                                       | Fired after the renewal orders are created; moves them to the status configured in the `FC_Renewal_Order_Status` `Constant__mdt` record.                                                              |
| `CancelSubscription`    | `FCORE_MEM.CancelSubscriptionEH`                                          | Requests cancellation of one or more subscriptions described by the DTOs in the context.                                                                                                              |
| `GenerateInstallments`  | `FCORE_PAY.GenerateInstallmentsEH` and `FCORE_MEM.GenerateInstallmentsEH` | Requests installment generation for payment plans; runs the Installment Generator, switching to batch for large volumes. Both packages register a handler for this event — disambiguate by namespace. |

### A Note on WalletItemRemoved

`WalletItemRemoved` is fired when a stored payment method
(`FCORE_PAY__Wallet_Item__c`) is flagged as removed — both from the trigger and
from the expiration batch. In current source it is processed by the
`WalletItemRemovedTU` trigger unit in Commerce, and the Stripe add-on subscribes
to this hook to detach the payment method in Stripe asynchronously. There is no
`WalletItemRemovedEH` class in the Platform, Commerce, or Subscriptions repos,
which is why it does not appear in the table above.

## Execution Characteristics

Keep these behaviors in mind when you fire events or write handlers:

* Handlers for an event run **synchronously**, in ascending `FCORE_BASE__Order__c`.
* An exception in a handler is logged (`FCORE_BASE.Logger.logAndCommit`) and
  rethrown. Remaining handlers do **not** run, and the caller must handle the
  exception.
* `FCORE_BASE.ServiceLayer.stopPropagation(String eventName)` — a handler can stop
  the remaining handlers for that event.
* `FCORE_BASE.ServiceLayer.disableEvent(String eventName)` /
  `FCORE_BASE.ServiceLayer.enableEvent(String eventName)` — temporarily mute an
  event. Useful in tests and as a recursion guard.
* `FCORE_BASE.ServiceLayer.registerEventHandler(String eventName, String apexClass, Integer order)` —
  register a handler programmatically (three arguments) when metadata is not
  appropriate, such as in tests.
* Avoid firing an event from its own handler. There is no recursion guard, so it
  loops.
