Siren

OrderDiscountsDetected

The event an integration fires when an order that used discounts completes, so Siren can take any store credit it spent from the collaborator's balance.

Requires Siren Essentials

WordPress plugin

Last updated: October 26, 2026

OrderDiscountsDetected is the event an integration fires to report store credit spent at checkout. It fires when an order completes and lists the order’s discounts. Siren picks out the ones paid with store credit and takes that credit from the shopper’s balance, which fires CreditRedeemed. Every integration that declares Features::CreditRedemption fires it.

The event ID is order_discounts_detected, and its fully qualified class is Siren\Commerce\Events\OrderDiscountsDetected.

What does this event carry?

  • getUserId() is the shopper’s user ID on the platform. Siren finds the collaborator linked to that user.
  • getUserType() is the mapping type the user ID is linked under. It defaults to user, the platform’s own user.
  • getPlatformReference() names the order, such as WooCommerce order #1042. Siren builds the ledger entry’s idempotency key from it, so the same order is never debited twice. Pass the same reference to OrderDiscountsReversed, which finds the debit by it.
  • getDiscounts() lists the order’s discounts, each an array with a currency and a dollarAmount. For store credit, currency is the credit coupon’s code, which is the custom currency’s code in capitals, and dollarAmount is what the coupon took off the order in the store’s money. Siren ignores any discount that isn’t in a store credit currency, so you can pass every coupon on the order.
  • getHoldToken() is the checkout session’s hold token, the one your locator stamped on the order. Null means the order carries no hold, and Siren settles no hold for it.
use Siren\Commerce\Events\OrderDiscountsDetected;

$event = new OrderDiscountsDetected(
    $customerId,                            // int: the shopper's user ID
    'WooCommerce order #' . $orderId,       // string: names the order and keys the ledger entry
    [
        ['currency' => 'STORE_CREDIT', 'dollarAmount' => 12.50],
    ],                                      // array: one entry per discount on the order
    $locator->getTokenForOrder($orderId)    // ?string: the hold token stamped on the order
);

Where does the hold token come from?

When the cart or checkout loads, the integration fires CreditAutoApplyRequested. Siren applies the shopper’s credit and holds it under a token from the integration’s checkout session locator, so another checkout can’t spend the same credit. When the order is created, the integration reads that token with the locator’s peekToken() and stamps it on the order with stampOrder(). When the order completes, it reads the token back with getTokenForOrder() and passes it here.

Once Siren takes the credit, it marks that checkout’s holds settled. Without the token, Siren still takes the credit, but the holds stay active until they expire, up to 30 minutes later. Until then, the shopper’s available credit counts the order’s credit twice, once as spent and once as held.

How does the pipeline react?

ProcessOrderDiscountsForCredits in the credits domain picks it up. It keeps the discounts in a store credit currency with an amount above zero and finds the collaborator linked to the shopper. Then it queues a settlement job instead of debiting right away, because settlement runs one collaborator at a time so two checkouts can’t both spend the same balance.

The job converts each dollarAmount to credit at the currency’s exchange rate, takes no more than the collaborator holds, and writes a redemption entry to the ledger. When a discount was larger than the balance, Siren takes what was there and logs the shortfall.

Which integrations fire it?

The WooCommerce, Easy Digital Downloads, and LifterLMS store credit add-ons fire it. WooCommerce binds it to two order statuses:

OrderDiscountsDetected::class => [
    ['action' => 'woocommerce_order_status_completed', 'transformer' => $discountCallback],
    ['action' => 'woocommerce_order_status_processing', 'transformer' => $discountCallback],
],

An order that reaches both statuses fires it twice, and the idempotency key makes the second one change nothing. Easy Digital Downloads fires it on edd_complete_purchase, and LifterLMS on lifterlms_order_status_completed and lifterlms_order_status_active.

The Commerce Events guide shows the full store credit wiring for an integration. For the ledger events this one leads to, see Store Credit Events.

Your privacy choices

Account, checkout and chat you request work without optional tracking. Choose whether to allow these other uses on this website.

You can change or withdraw your choices at any time using “Privacy choices.” We remember your choice for 180 days. Read our privacy policy.