Skip to main content
Joinery Core fires joinery_core_init once it’s ready. Register your rule types there with joinery_register_condition() and joinery_register_action().

A condition

A condition answers one question: does this rule apply? Its scope() says whether it looks at the whole cart (Scope::CART) or at one line (Scope::ITEM).
my-plugin/src/DifferentProducts.php
Conditions must be pure: decide from $settings and $context only, with no database or WordPress calls, because rules are checked many times per request. If you need more about the customer, look at what Context already holds (such as returning, whether they’ve ordered before) before adding your own lookups.

An action

Item actions implement ItemPriceAction and return a new unit price. Cart actions implement CartDiscountAction and return an amount to take off; it’s spread over the cart lines by value. Offers that take money off particular lines implement LineDiscountAction: line_discounts() returns the amount off each line (keyed by its key in $context->lines) and discount_label() names the cart totals line, such as “Buy 2, get 1 free”. The core takes VAT off with each discount at the rates of the lines it applies to.
my-plugin/src/RoundDown.php

An action that runs after the order

Event actions do something once an order is placed, such as giving store credit or adding a customer to a list. Implement EventAction: events() lists what it responds to, and handle() does the work. Gift Cards’ “Give store credit” is one.
my-plugin/src/ThankYouTag.php
  • Events: EventRunner::ORDER_PAID (order.paid, the first time the order reaches Processing or Completed) and EventRunner::ORDER_COMPLETED (order.completed).
  • When it runs: in the background via Action Scheduler (group joinery), once per order and event. Conditions are checked against the order: its customer, the time it was placed, its subtotal and quantity, and a product condition matches when any line meets it.
  • Payload: order_id, customer_id (0 for guests), rule_id and rule_name.
  • Make handle() safe to run twice for the same order and rule, for example by recording what you did in order meta. Action Scheduler retries failed jobs.
  • Rules that only have event actions never take part in pricing, so they can’t stop a “Don’t combine” pricing rule.
If your action’s price changes at set quantities, also implement Joinery\Core\Rules\QuantityBreaks. Headless storefronts then get those quantities in quantity_breaks.

Register them

my-plugin/my-plugin.php

Fields

fields() describes the settings form in the rules editor. Each field is an array with key, type and label. Supported types include number, money, percent, select (with options), role-select, category-select, datetime and tiers. Values arrive in $settings under each field’s key. Add help to show a line of guidance under a number or select field.
Keep matches() and adjust_price() fast and free of side effects. They run for every cart line on every cart calculation. Side effects belong in an event action.