Skip to content

Powered by Grav CMS + Helios Theme

Events

KahunaCart announces everything a plugin might want to act on as an ordinary Grav event. This page is the reference for PHP developers writing an add-on: every event name, what it carries, and when it fires. For the events in context, see Writing a KahunaCart Payment Provider.

Before you begin

  • Grav 2.0 or later, with KahunaCart installed and enabled.
  • A plugin of your own to subscribe from.
  • The add-on tables only apply on a store running that add-on.

Subscribe to an event

Subscribe the way every Grav plugin does. The payload arrives as an Event; read and write it by key.

PHP
public static function getSubscribedEvents(): array
{
    return ['onKahunaCartOrderCompleted' => ['onOrderCompleted', 0]];
}

public function onOrderCompleted(Event $event): void
{
    $order = $event['order'];
    // …
}

Three rules apply to every listener:

  1. Append, never assign. A registry or a collection in the payload is shared with every other listener. Replacing it loses their work.
  2. Do not throw. KahunaCart catches, logs and carries on for most events, but a listener that throws loses its own work.
  3. Do slow work in a job. Order events fire inside the checkout request. Queue anything that talks to another server, and register the handler on onKahunaCartRegisterJobHandlers.

Important

onKahunaCartOrderCompleted and onKahunaCartOrderPaid both fire before the email that follows them is queued. The queue runs jobs in id order, so work you queue from either event finishes before the confirmation email renders. A licence key created from the event is included in the first message the customer receives.

Base events

Every event the KahunaCart plugin takes part in. Payload names the keys on the Event. The tag beside each name says what the plugin does with it: fires means it raises the event, listens means it also subscribes to it, and handler method marks a listener method rather than an event.

onKahunaCartAccountPanelsfires

Add a panel to an account page

Payload registry, section, user, customer, account_base

onKahunaCartAccountSectionsfires

Add a tab of your own to the account area

Payload registry

onKahunaCartAdminSectionsfires

Contribute an admin screen (§8)

Payload sections

onKahunaCartBuyButtonFieldsfires

The buy button is rendering. Add a form field of your own to it

Payload product, variants, customer, fields (an ArrayObject to append to)

onKahunaCartBuyReturnLabelfires

Buy a return label from a carrier

Payload provider, request, order, lines (item_id, title, sku, qty, variant_id), address (where the parcel goes); answered with label_pdf or label_url, tracking_number, carrier, or error

onKahunaCartCalculateAdjustmentsfires

Add or inspect a discount, shipping or tax line

Payload calculation

onKahunaCartCartLineKeyfires

Decide what makes two adds the same line

Payload order_id, variant_id, line_data, key

onKahunaCartCartLineNotesfires

The cart and checkout pages are rendering. Add a sentence beside a line

Payload order, items, notes (an ArrayObject keyed by line item id)

onKahunaCartCartLinePricefires

Set a cart line's price and its breakdown

Payload order_id, product, variant, qty, currency, line_data, price

onKahunaCartCartLineValidatefireslistens

Refuse a cart line, or refuse what it is joining

Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors

onKahunaCartCartRecoveredfires

A cart came back from a recovery or share link. Observation only

Payload order (the cart the lines were restored into), cart (the abandoned cart the link belonged to), link (the kahunacart_cart_links row that was redeemed), notices (each as type, attribute, message_key, params, severity), restored, dropped, moved

onKahunaCartCheckoutTaxIdfires

Hand checkout the buyer's VAT number; core checks it through the registered validator and puts it on the order

Payload order, items, fields, post, context, tax_id

onKahunaCartCheckoutValidatefires

Refuse an order before payment starts

Payload order, items, post, context (checkout, api or express), errors

onKahunaCartCollectEncryptedSettingsfireslistens

Have a setting of your own stored encrypted without being a payment provider

Payload keys (a list of plugins.<plugin>.<key> paths; append yours and write the list back)

onKahunaCartCollectErasureBlockersfires

Refuse an erasure because you hold something on this customer's behalf

Payload customer_id, customer (the row), blockers (a Customers\ErasureBlockers to call block() on)

onKahunaCartCounterReturnfires

A return was taken at the counter

Payload order_id, order, request_id, request, return_number, refund_to (original, cash or store_credit), refunds (transaction_id, provider, amount_minor, status), refunded_minor, restocked, staff_user

onKahunaCartCreditPostedfires

Store credit moved — a gift card issued or redeemed, an order paid with credit, a refund sent to credit, a merchant's adjustment

Payload account_id, transaction_id, amount_minor (signed), currency, kind, reference, reference_id, order_id, balance_minor

onKahunaCartCustomerErasedfires

A customer has been anonymised. Observation only

Payload customer_id, orders

onKahunaCartCustomerGroupsGrantedfires

Put a customer in a group while something of yours is live, without storing it

Payload customer_id, grants

onKahunaCartCustomerPanelsfireslistens

Add a panel to a customer record, in the admin and in the account area

Payload registry, customer, channel, site_url

onKahunaCartCustomersMergedfires

Move what you keep by customer id to the survivor, and say what you would move

Payload from_customer_id (the merged-away customer), to_customer_id (the survivor), dry_run (true for a preview: write nothing), self_service (true when the customer asked for it themselves), report (an array of counts; read it, add yours, write it back), blockers (a list of strings; read it, append yours, write it back), customer_notes (a list of plain sentences written for the customer, starting empty; read it, append yours, write it back; never an email address or an id)

onKahunaCartDeclareFeatureCompatibilityfires

Declare which core features your add-on understands

Payload features, codes

onKahunaCartDisputeClosedfires

A dispute was settled

Payload order_id, provider, dispute_id, outcome, amount_minor, currency, reason, manual

onKahunaCartDisputeOpenedfires

A dispute was opened

Payload order_id, provider, dispute_id, amount_minor, currency, reason, respond_by

onKahunaCartDownloadDeliverfires

Send different bytes for a download — a licence stamped into the zip on the way out

Payload ticket, grant_id, file_id, product_id, order_id, customer_id, customer_email, filename, method, path (yours to replace), cleanup, force_php

onKahunaCartExchangeRatesRefreshedfireslistens

Exchange rates changed, so a listener can react to a currency that moved

Payload source (the rate source's code), base, changes (a list of quote, old (null for a new pair), new, moved_percent)

onKahunaCartExtendCatalogIndexfires

Declare a column of your own on the catalog index, created by the same migrate command, then fill it once per index row

Payload stage (columns or row); at columns: table, engine, existing (the base columns), columns (yours to write); at row: row (the index row being assembled, yours to write back), product

onKahunaCartExtendStorefrontSchemafires

Add keys of your own to a storefront JSON API response

Payload resource (cart, line_item, shipping, payment_methods, checkout, countries), payload (an ArrayObject; add keys to it, documented keys cannot be overwritten), context (the rows the payload was built from)

onKahunaCartGroupOffersfires

Say what sells a customer group, so the subscribe panel on a paywalled article lists it with its price

Payload groups (the customer group codes the page names that the store has, lower case, never empty), offers (rows of label, price_html already escaped, url a same-site path or absolute URL to the product page, group the code the row grants, and optionally product_id and plan_id; append yours, never remove another listener's, and write the array back)

onKahunaCartImportRowWrittenfires

One row of an import was written

Payload entity, run_id, action (created, updated, skipped), id, values

onKahunaCartImportRunFinishedfireslistens

An import run reached the end of its file

Payload run_id, run (the finished row with its counts)

onKahunaCartOrderBalanceOverduefires

Decide what a late balance costs the customer, and ask to be asked again after a grace period

Payload order_id, order, balance_minor, balance_due_at, currency, days_over, look, again

onKahunaCartOrderChangeConfirmedfireslistens

A change was applied to a new version

Payload order_id, change_id, change, version_from, version_to, order, restocked

onKahunaCartOrderChangeStagedfires

A change was opened, or an action was added to one. Nothing has been written to the order

Payload order_id, change_id, change, action

onKahunaCartOrderCompletedfireslistens

An order was placed — placed, not necessarily paid, because a deposit completes an order at Part paid. A listener that spends money or gives something away asks countsAsPaid($order['payment_status']) first

Payload order (the full row)

onKahunaCartOrderHistoryRecordedfires

Something was recorded against an order

Payload order_id, entry

onKahunaCartOrderNoticefires

A notice was put on an order

Payload order_id, order, notice, audience

onKahunaCartOrderPaidfireslistens

Money landed after the fact

Payload order_id

onKahunaCartOrderPanelsfireslistens

Add a panel to a customer's receipt page and to their order emails

Payload registry, order, channel, site_url

onKahunaCartOrderPartPaidfireslistens

Act on a deposit before the order counts as paid

Payload order_id, order, provider, paid_minor, captured_minor, balance_minor, balance_due_at, currency

onKahunaCartOrderPaymentSchedulefires

Take a deposit at checkout and the balance later

Payload order_id, order, items, currency, total_minor, schedule

onKahunaCartOrderReversedfireslistens

A sale came undone: the order was cancelled, or refunded in full

Payload order_id, order (the row as it now stands)

onKahunaCartOrdersReassignedfires

Orders moved from one customer to another. Observation only

Payload from_customer_id, to_customer_id, orders

onKahunaCartPaymentAuthorizedfires

Observe an authorisation. Observation only

Payload order_id, order, transaction (the authorisation row), provider, amount_minor, currency

onKahunaCartPaymentCapturedfireslistens

Observe a capture and the money it moved, which the order event does not carry. Observation only

Payload order_id, order, transaction (the capture row), authorization, provider, amount_minor, authorized_minor, currency, full

onKahunaCartPaymentCollectionChangedfires

Watch a part-paid order's money move. Observation only

Payload order_id, collection_id, session_id, kind (deposit, balance, refund), amount_minor, status

onKahunaCartPaymentFailedfires

A payment attempt failed

Payload order_id, provider, transaction_id, amount_minor, currency, reason

onKahunaCartPaymentMethodAvailabilityfires

Refuse a fee-bearing payment method by setting an existing MethodAvailability reason; subscription drivers must be able to collect every agreed payment

Payload order, items, method, fee_minor, reason

onKahunaCartPaymentMethodFeeLinesfires

Leave lines whose price includes the fee out of a method fee

Payload calculation, excluded_item_ids

onKahunaCartPaymentStartingfires

Start the payment yourself instead

Payload order, items, provider, context, result, handled

onKahunaCartPaymentVoidedfires

Observe a void. Observation only

Payload order_id, order (as it now stands), transaction (the voided row), provider, amount_minor, currency, reason

onKahunaCartProductDeletedfireslistens

A product is gone, with its variants and its option axes

Payload id

onKahunaCartProductRelationsChangedfires

A product's curated related, cross-sell or up-sell lists changed

Payload product_id, relations (the lists as they now stand)

onKahunaCartProductSavedfireslistens

A product was written — created, edited, published, unpublished, hidden, or one of its variants changed

Payload id, action (created, updated or variant)

onKahunaCartProductsBulkUpdatedfireslistens

A bulk edit was applied

Payload product_ids, operations, result

onKahunaCartProviderSuitabilityfireslistens

Narrow the payment methods offered for one cart

Payload order, items, providers, suitability

onKahunaCartReaderPaymentStartedfires

A card payment is waiting on a reader

Payload order_id, transaction_id, transaction_hash, provider, reader, reference (the platform's id for the reader action), amount_minor, currency

onKahunaCartRefundAppliedfireslistens

A refund the provider confirmed has been applied to the goods

Payload order_id, order, transaction, items, shipping_minor, full

onKahunaCartRefundFailedfires

A refund the provider rejected after it was submitted

Payload order_id, order, transaction_id, provider, provider_name, amount_minor, currency, reference, reason

onKahunaCartRefundRequestedfires

A customer asked for a refund — the asking, not the refunding

Payload order_id, order, request_id, reason_code, message, amount_minor

onKahunaCartRegisterCarrierRateProvidersfires

Plug a live carrier into shipping

Payload registry (a Shipping\Carrier\CarrierRegistry; call add() on it)

onKahunaCartRegisterCheckoutFieldsfires

Add a field to the checkout form, with the conditions that decide when it is required, when it is hidden and what a valid answer is

Payload registry, fields (the same object)

onKahunaCartRegisterCustomFieldsfires

Declare a typed custom field on a core entity, with its visibility, so it persists, renders in the admin and reaches the storefront or does not

Payload registry (a Extension\CustomFieldRegistry to call declare() and registerEntity() on)

onKahunaCartRegisterDashboardAlertsfires

Tell a merchant about something in your add-on that needs their attention, on the dashboard rather than in an email

Payload registry (an Admin\Alerts\AlertRegistry to call register() on)

onKahunaCartRegisterExportsfires

Register your add-on's data as an export set, usually a Transfer\TableExportSet naming your tables, so it is in a store bundle and comes back in a restore

Payload registry (a Transfer\ExportRegistry to call add() on)

onKahunaCartRegisterExpressButtonsfires

Draw a wallet button into the express region on the cart or checkout page

Payload registry (call register($providerSlug, $id, $render, $weight, $zones) on it), order, items, style (the ExpressButtonStyle every button honours), providers (the ones offered to this cart that declare express_pay)

onKahunaCartRegisterHooksfires

Run a side effect inside an order flow with an undo that runs automatically, in reverse, when a later step fails

Payload registry (an Extension\Hooks\HookRegistry to call on() on)

onKahunaCartRegisterImportFieldsfires

Add mappable fields of your own to the CSV importer's mapping screen

Payload entity, fields (an array to add to)

onKahunaCartRegisterJobHandlersfires

Register a background job type of your own

Payload registry

onKahunaCartRegisterMigrationsfires

Migrations are being collected. Append your own migrations directory

Payload paths

onKahunaCartRegisterNotificationsfires

Register the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker

Payload registry

onKahunaCartRegisterOperationsfires

Register a discount operation of your own — a code, a blueprint of its arguments, and one method that returns what should come off

Payload registry

onKahunaCartRegisterOrderStatusesfireslistens

Contribute a code to the order-status registry; KahunaCart itself registers quote through it

Payload registry

onKahunaCartRegisterPaymentMethodsfires

Contribute a payment method from an add-on

Payload methods

onKahunaCartRegisterProductTypesfireslistens

Register a kind of thing this store can sell

Payload registry

onKahunaCartRegisterProvidersfireslistens

Register yourself as a payment provider

Payload registry

onKahunaCartRegisterRateSourcesfires

Add a source of exchange rates, such as a keyed service

Payload registry (a Currency\Rates\RateSources; call add() on it with a Currency\Rates\RateSource)

onKahunaCartRegisterResetTablesfires

Declare your add-on's transactional tables so a pre-launch reset takes them too, and leave your configuration tables out of it

Payload registry (an Ops\ResetTableRegistry to call add() or addAll() on)

onKahunaCartRegisterRuleAttributesfires

Register something a condition can be written against

Payload registry

onKahunaCartRegisterSchemaExtensionsfires

Add your own data to a storefront, admin API or Twig fragment response under your own namespace, with a declared schema

Payload registry (a Extension\Schema\SchemaExtensionRegistry to call register() on)

onKahunaCartRegisterSettingsNamespacesfires

Declare a namespace of scoped settings for your add-on, stored in kahunacart_settings with no columns of your own

Payload registry (a Extension\SettingsNamespaceRegistry to call register() on)

onKahunaCartRegisterTaxEnginesfires

Quote tax for a region through your own service instead of the store's rate table, and replace the zone and rate editors while you do

Payload registry

onKahunaCartRegisterTaxIdValidatorsfires

Check a VAT number against the authority that issued it. Nothing in core implements this — the implementation is the tax/VAT add-on's

Payload registry

onKahunaCartRegisterZonesfires

Declare a zone on a screen of your own so a fourth party can extend it, or mount a widget into a catalogued zone

Payload registry (a Extension\Zone\ZoneRegistry to call declare() and mount() on)

onKahunaCartReleaseEntitlementfires

A customer asked for a release. Decide which versions their purchase entitles them to

Payload product_id, customer (email, customer_id, order_id), releases (the candidates), entitled (the ids, yours to narrow)

onKahunaCartResolvePricefires

Observe which price a variant resolved to and why

Payload variant_id, product_id, quantity, currency, candidates (the price lists that applied and priced this quantity), price_list_id (the one that won, or null), price_minor, list_price_minor (the strikethrough reference, or null), reason, decision (the whole PriceDecision)

onKahunaCartRetentionSweptfires

The retention schedule ran. Observation only

Payload dry_run, results (rule => {examined, affected, cutoff, dry_run, error, details})

onKahunaCartReturnApprovedfires

A return was authorised

Payload order_id, order, request_id, request, return_number, lines, method, note, change_id

onKahunaCartReturnCancelledfires

A return was cancelled

Payload order_id, order, request_id, request, return_number, by (merchant or customer), note

onKahunaCartReturnLabelProvidersfires

Offer to buy return labels

Payload providers (Refund\ReturnLabelProviders, call add(id, label)), request (the return, when one is being approved), order

onKahunaCartReturnReceivedfires

The goods of a return arrived

Payload order_id, order, request_id, request, return_number, lines (item_id, qty, received_qty, damaged_qty), restocked, change_id

onKahunaCartReturnRefundedfires

A return was refunded

Payload order_id, order, request_id, request, return_number, transaction_id

onKahunaCartReturnRequestedfires

A customer asked to return goods

Payload order_id, order, request_id, request (the row), return_number, lines (item_id, title, qty), reason_code, message, method

onKahunaCartReviewModeratedfires

A review's state changed, however it changed. Observation only

Payload review_id, product_id, state, previous_state, moderated_by

onKahunaCartReviewSubmittedfires

A review was written, before anybody moderated it. Observation only

Payload review_id, product_id, customer_id, rating, state, verified

onKahunaCartShipmentCreatedfireslistens

A parcel went out

Payload order_id, shipment, items, notify

onKahunaCartShipmentDeliveredfires

A parcel arrived

Payload order_id, shipment

onKahunaCartShippingMethodExcludedfires

Reinstate a refused shipping method, reword why it was refused, or attach carrier metadata

Payload method, calculation, package, active, reason, reasonKey, reasonParams, metadata, reinstate

onKahunaCartStockAdjustedfireslistens

An administrative stock correction — a bulk edit or an import, never a sale or a refund

Payload variant_id, product_id, from, to, delta, reason, context

onKahunaCartStoreStatusChangedfires

The store opened, paused or went to coming soon; switch your own storefront pieces on or off with it

Payload from, to (each open, coming_soon or paused), message (what shoppers are now told), by (the admin's username, cli or empty)

onKahunaCartSubscriptionWebhookEventsfires

Act on the recurring events a webhook delivery carried

Payload provider, events, result

onKahunaCartTenderRecordedfires

A tender was taken at a till

Payload order_id, order, tender (type, transaction_id, provider, amount_minor, tendered_minor, change_minor, rounding_minor, reference, taken_by, register_ref), outstanding_minor, payment_status

onKahunaCartWebhookRejectedfires

A webhook was rejected

Payload provider, provider_name, status, reason

onKahunaCartWishlistItemAddedfires

Something was saved to a wishlist. Observation only

Payload wishlist_id, customer_id (null on an anonymous list), product_id, variant_id (0 when no particular variant was named), quantity

onKahunaCartWishlistMergedfires

An anonymous list was folded into an account's on login. Observation only

Payload customer_id, from_wishlist_id, into_wishlist_id, moved (the rows that actually landed, which is the overlap subtracted)

onKahunaCartWorkerTickfireslistens

Put recurring work of your own on the store's own worker clock

Payload queue, now

Licenses add-on events

The Licenses add-on raises the onKahunaCartLicense* events and subscribes to the base and subscription events beside them. Entries tagged listens show what the add-on already acts on.

onKahunaCartAccountPanelslistens

Add a panel to an account page

Payload registry, section, user, customer, account_base

onKahunaCartAccountSectionslistens

Add a tab of your own to the account area

Payload registry

onKahunaCartAdminSectionslistens

Contribute an admin screen (§8)

Payload sections

onKahunaCartBuyButtonFieldslistens

The buy button is rendering. Add a form field of your own to it

Payload product, variants, customer, fields (an ArrayObject to append to)

onKahunaCartCalculateAdjustmentslistens

Add or inspect a discount, shipping or tax line

Payload calculation

onKahunaCartCartLineNoteslistens

The cart and checkout pages are rendering. Add a sentence beside a line

Payload order, items, notes (an ArrayObject keyed by line item id)

onKahunaCartCartLineValidatelistens

Refuse a cart line, or refuse what it is joining

Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors

onKahunaCartCheckoutValidatelistens

Refuse an order before payment starts

Payload order, items, post, context (checkout, api or express), errors

onKahunaCartCollectErasureBlockerslistens

Refuse an erasure because you hold something on this customer's behalf

Payload customer_id, customer (the row), blockers (a Customers\ErasureBlockers to call block() on)

onKahunaCartCustomersMergedlistens

Move what this plugin keeps for the merged-away customer to the one they were merged into. Fired by the base plugin after its own transaction commits, and again if the same merge is repeated

Payload from_customer_id, to_customer_id, dry_run, self_service, report, blockers

onKahunaCartDeclareFeatureCompatibilitylistens

Declare which core features your add-on understands

Payload features, codes

onKahunaCartDownloadDeliverlistens

Send different bytes for a customer's download. Fired on {base}/download/{token} after the grant is authorized and the path resolved, before any header

Payload ticket, grant_id, file_id, product_id, order_id, customer_id, customer_email, filename, method, path, cleanup, force_php

onKahunaCartLicenseActivatedfires

A machine took a seat on a license key

Payload license, activation

onKahunaCartLicenseExtendedfires

A renewal moved a key's expiry or its updates window forward

Payload license, previous

onKahunaCartLicenseIssuedfireslistens

A license key was minted

Payload license

onKahunaCartLicenseRevokedfires

A license key was killed permanently

Payload license, reason, reversible

onKahunaCartLicenseWidenedfires

An existing key now covers a product it did not before. Nothing was minted

Payload license, product_id

onKahunaCartOrderCompletedlistens

An order was placed — placed, not necessarily paid, because a deposit completes an order at Part paid. A listener that spends money or gives something away asks countsAsPaid($order['payment_status']) first

Payload order (the full row)

onKahunaCartOrderPaidlistens

Money landed after the fact

Payload order_id

onKahunaCartOrderPanelslistens

Add a panel to a customer's receipt page and to their order emails

Payload registry, order, channel, site_url

onKahunaCartOrderReversedlistens

A sale came undone: the order was cancelled, or refunded in full

Payload order_id, order (the row as it now stands)

onKahunaCartRegisterJobHandlerslistens

Register a background job type of your own

Payload registry

onKahunaCartRegisterMigrationslistens

Migrations are being collected. Append your own migrations directory

Payload paths

onKahunaCartRegisterNotificationslistens

Register the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker

Payload registry

onKahunaCartRegisterResetTableslistens

Declare your add-on's transactional tables so a pre-launch reset takes them too, and leave your configuration tables out of it

Payload registry (an Ops\ResetTableRegistry to call add() or addAll() on)

onKahunaCartRegisterZoneslistens

Declare a zone on a screen of your own so a fourth party can extend it, or mount a widget into a catalogued zone

Payload registry (a Extension\Zone\ZoneRegistry to call declare() and mount() on)

onKahunaCartReleaseEntitlementlistens

A customer asked for a release. Decide which versions their purchase entitles them to

Payload product_id, customer, releases, entitled

onKahunaCartSubscriptionActivatedlistens

A subscription became active, out of incomplete or out of a trial

Payload order, order_id (both null on a trial), period, trial (bool), actor

onKahunaCartSubscriptionCancelledlistens

The subscription ended. Check at_period_end before revoking anything

Payload at_period_end (bool), period, ends_at (int, when it actually ended), reason, actor

onKahunaCartSubscriptionExpiredlistens

The subscription ran out and was not renewed

Payload period, reason, actor

onKahunaCartSubscriptionRenewalOrderBuildinglistens

A renewal order is being assembled. Modify lines to carry something into it

Payload subscription, plan, period, sequence (int), lines

onKahunaCartWorkerTicklistens

Put recurring work of your own on the store's own worker clock

Payload queue, now

Subscriptions add-on events

The Subscriptions add-on raises one event per lifecycle change. Every one of them carries subscription, subscription_id, hash, plan_id and status, plus the keys specific to what changed.

onKahunaCartAccountSectionslistens

Add a tab of your own to the account area

Payload registry

onKahunaCartAdminSectionslistens

Contribute an admin screen (§8)

Payload sections

onKahunaCartBuyButtonFieldslistens

The buy button is rendering. Add a form field of your own to it

Payload product, variants, customer, fields (an ArrayObject to append to)

onKahunaCartCalculateAdjustmentslistens

Add or inspect a discount, shipping or tax line

Payload calculation

onKahunaCartCartLineNoteslistens

The cart and checkout pages are rendering. Add a sentence beside a line

Payload order, items, notes (an ArrayObject keyed by line item id)

onKahunaCartCartLinePricelistens

Set a cart line's price and its breakdown

Payload order_id, product, variant, qty, currency, line_data, price

onKahunaCartCartLineValidatelistens

Refuse a cart line, or refuse what it is joining

Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors

onKahunaCartCollectErasureBlockerslistens

Refuse an erasure because you hold something on this customer's behalf

Payload customer_id, customer (the row), blockers (a Customers\ErasureBlockers to call block() on)

onKahunaCartCustomerGroupsGrantedlistens

Put a customer in a group while something of yours is live, without storing it

Payload customer_id, grants

onKahunaCartCustomerPanelslistens

Add a panel to a customer record, in the admin and in the account area

Payload registry, customer, channel, site_url

onKahunaCartDeclareFeatureCompatibilitylistens

Declare which core features your add-on understands

Payload features, codes

onKahunaCartGroupOfferslistens

Say what sells a customer group, so the subscribe panel on a paywalled article lists it with its price

Payload groups (the customer group codes the page names that the store has, lower case, never empty), offers (rows of label, price_html already escaped, url a same-site path or absolute URL to the product page, group the code the row grants, and optionally product_id and plan_id; append yours, never remove another listener's, and write the array back)

onKahunaCartInvoiceOrderCompletedhandler method

Not an event: the handler this add-on binds to onKahunaCartOrderCompleted for a renewal paid through a pay link

Payload order

onKahunaCartInvoiceOrderPaidhandler method

Not an event: the handler this add-on binds to onKahunaCartOrderPaid for a renewal paid through a pay link

Payload order_id

onKahunaCartNewslettersRegisterSegmentFieldslistens

The segment vocabulary is being collected. Declare a field of your own on the registry

Payload —

onKahunaCartOrderCompletedlistens

An order was placed — placed, not necessarily paid, because a deposit completes an order at Part paid. A listener that spends money or gives something away asks countsAsPaid($order['payment_status']) first

Payload order (the full row)

onKahunaCartOrderPaidlistens

Money landed after the fact

Payload order_id

onKahunaCartOrderPanelslistens

Add a panel to a customer's receipt page and to their order emails

Payload registry, order, channel, site_url

onKahunaCartOrderReversedlistens

A sale came undone: the order was cancelled, or refunded in full

Payload order_id, order (the row as it now stands)

onKahunaCartPaymentMethodAvailabilitylistens

Refuse a fee-bearing payment method by setting an existing MethodAvailability reason; subscription drivers must be able to collect every agreed payment

Payload order, items, method, fee_minor, reason

onKahunaCartPaymentMethodFeeLineslistens

Leave lines whose price includes the fee out of a method fee

Payload calculation, excluded_item_ids

onKahunaCartPaymentStartinglistens

Start the payment yourself instead

Payload order, items, provider, context, result, handled

onKahunaCartProviderSuitabilitylistens

Narrow the payment methods offered for one cart

Payload order, items, providers, suitability

onKahunaCartRegisterDashboardAlertslistens

Tell a merchant about something in your add-on that needs their attention, on the dashboard rather than in an email

Payload registry (an Admin\Alerts\AlertRegistry to call register() on)

onKahunaCartRegisterJobHandlerslistens

Register a background job type of your own

Payload registry

onKahunaCartRegisterMigrationslistens

Migrations are being collected. Append your own migrations directory

Payload paths

onKahunaCartRegisterNotificationslistens

Register the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker

Payload registry

onKahunaCartRegisterResetTableslistens

Declare your add-on's transactional tables so a pre-launch reset takes them too, and leave your configuration tables out of it

Payload registry (an Ops\ResetTableRegistry to call add() or addAll() on)

onKahunaCartSubscriptionActivatedfires

A subscription became active, out of incomplete or out of a trial

Payload order, order_id (both null on a trial), period, trial (bool), actor

onKahunaCartSubscriptionCancelScheduledfires

A cancellation was booked for the end of the current period. Access continues until then

Payload at_period_end (always true), period, ends_at (int, the period end), reason, actor

onKahunaCartSubscriptionCancelledfires

The subscription ended. Check at_period_end before revoking anything

Payload at_period_end (bool), period, ends_at (int, when it actually ended), reason, actor

onKahunaCartSubscriptionCreatedfires

A subscription row was opened, before the platform has confirmed anything

Payload plan (the plan row), order (the checkout order), order_id, period, actor (always customer)

onKahunaCartSubscriptionExpiredfires

The subscription ran out and was not renewed

Payload period, reason, actor

onKahunaCartSubscriptionPastDuefires

The platform stopped retrying. The grace clock starts here

Payload period, order_id, reason, actor

onKahunaCartSubscriptionPausedfires

Billing stopped without ending the subscription

Payload period, reason, actor

onKahunaCartSubscriptionPaymentFailedfires

A charge was declined. The platform is still retrying

Payload attempt (int, the running failure count), reason, order_id, actor

onKahunaCartSubscriptionPaymentMethodUpdatedfires

The customer changed the card the platform bills

Payload actor, plus whatever the caller passed — the webhook path sends provider_ref

onKahunaCartSubscriptionPeriodExtendedfires

A merchant added days to the current period by hand

Payload days (int), period (after the move), previous_period, note, actor

onKahunaCartSubscriptionPeriodRolledBackfires

A period was undone, because the renewal it was built on was reversed

Payload order, order_id (the refunded renewal), period (the period restored), previous_period (the one undone), sequence (int, the renewal's position), actor

onKahunaCartSubscriptionRenewalOrderBuildingfires

A renewal order is being assembled. Modify lines to carry something into it

Payload subscription, plan, period, sequence (int), lines

onKahunaCartSubscriptionRenewedfires

A period was paid for and the calendar moved on. A renewal order exists

Payload order, order_id, period (the new one), previous_period, provider, provider_invoice_ref, amount_minor, actor

onKahunaCartSubscriptionResumedfires

Billing restarted after a pause

Payload period, cancel_at_period_end (bool), actor

onKahunaCartSubscriptionWebhookEventsfireslistens

Act on the recurring events a webhook delivery carried

Payload provider, events, result

onKahunaCartSubscriptionsSweptfires

The scheduled sweep finished. Carries what it moved

Payload summary

Newsletters add-on events

The Newsletters add-on raises two events when somebody joins or confirms a list, one after every segment recount sweep, and two registry events an add-on contributes to. See Developers for the registry contracts.

onKahunaCartAccountPanelslistens

Add a panel to an account page

Payload registry, section, user, customer, account_base

onKahunaCartAccountSectionslistens

Add a tab of your own to the account area

Payload registry

onKahunaCartAdminSectionslistens

Contribute an admin screen (§8)

Payload sections

onKahunaCartCartRecoveredlistens

A cart came back from a recovery or share link. Observation only

Payload order (the cart the lines were restored into), cart (the abandoned cart the link belonged to), link (the kahunacart_cart_links row that was redeemed), notices (each as type, attribute, message_key, params, severity), restored, dropped, moved

onKahunaCartCollectErasureBlockerslistens

Refuse an erasure because you hold something on this customer's behalf

Payload customer_id, customer (the row), blockers (a Customers\ErasureBlockers to call block() on)

onKahunaCartCustomerErasedlistens

A customer has been anonymised. Observation only

Payload customer_id, orders

onKahunaCartCustomersMergedlistens

Move what you keep by customer id to the survivor, and say what you would move

Payload from_customer_id (the merged-away customer), to_customer_id (the survivor), dry_run (true for a preview: write nothing), self_service (true when the customer asked for it themselves), report (an array of counts; read it, add yours, write it back), blockers (a list of strings; read it, append yours, write it back), customer_notes (a list of plain sentences written for the customer, starting empty; read it, append yours, write it back; never an email address or an id)

onKahunaCartDeclareFeatureCompatibilitylistens

Declare which core features your add-on understands

Payload features, codes

onKahunaCartNewslettersConfirmedfireslistens

Somebody clicked the link in their confirmation email. Carries subscriber_id, list_id and source

Payload subscriber_id, list_id, source

onKahunaCartNewslettersRegisterDeliverabilityChecksfires

The Health checks are being collected. Add a check of your own to the registry

Payload —

onKahunaCartNewslettersRegisterSegmentFieldsfires

The segment vocabulary is being collected. Declare a field of your own on the registry

Payload —

onKahunaCartNewslettersSegmentsCountedfires

The worker finished a segment recount sweep. Carries counted and failed

Payload —

onKahunaCartNewslettersSubscribedfireslistens

Somebody joined a list and is mailable straight away. Carries subscriber_id, list_id and source

Payload subscriber_id, list_id, source

onKahunaCartOrderCompletedlistens

An order was placed — placed, not necessarily paid, because a deposit completes an order at Part paid. A listener that spends money or gives something away asks countsAsPaid($order['payment_status']) first

Payload order (the full row)

onKahunaCartOrderPaidlistens

Money landed after the fact

Payload order_id

onKahunaCartRegisterCheckoutFieldslistens

Add a field to the checkout form, with the conditions that decide when it is required, when it is hidden and what a valid answer is

Payload registry, fields (the same object)

onKahunaCartRegisterDashboardAlertslistens

Tell a merchant about something in your add-on that needs their attention, on the dashboard rather than in an email

Payload registry (an Admin\Alerts\AlertRegistry to call register() on)

onKahunaCartRegisterJobHandlerslistens

Register a background job type of your own

Payload registry

onKahunaCartRegisterMigrationslistens

Migrations are being collected. Append your own migrations directory

Payload paths

onKahunaCartRegisterNotificationslistens

Register the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker

Payload registry

onKahunaCartRegisterResetTableslistens

Declare your add-on's transactional tables so a pre-launch reset takes them too, and leave your configuration tables out of it

Payload registry (an Ops\ResetTableRegistry to call add() or addAll() on)

onKahunaCartRegisterZoneslistens

Declare a zone on a screen of your own so a fourth party can extend it, or mount a widget into a catalogued zone

Payload registry (a Extension\Zone\ZoneRegistry to call declare() and mount() on)

onKahunaCartSubscriptionActivatedlistens

A subscription became active, out of incomplete or out of a trial

Payload order, order_id (both null on a trial), period, trial (bool), actor

onKahunaCartSubscriptionCancelledlistens

The subscription ended. Check at_period_end before revoking anything

Payload at_period_end (bool), period, ends_at (int, when it actually ended), reason, actor

onKahunaCartSubscriptionRenewedlistens

A period was paid for and the calendar moved on. A renewal order exists

Payload order, order_id, period (the new one), previous_period, provider, provider_invoice_ref, amount_minor, actor

Note

onKahunaCartSubscriptionCancelScheduled and onKahunaCartSubscriptionCancelled are separate events. A listener that revokes access on cancellation must not fire for a customer who still has three weeks left. Both payloads carry at_period_end; subscribe to both if you want both.

Shipping add-on events

The Shipping add-on fires no event of its own. It listens to the base events below to buy labels, run its jobs and draw its panels. See Developers.

onKahunaCartAccountPanelslistens

Add a panel to an account page

Payload registry, section, user, customer, account_base

onKahunaCartAdminSectionslistens

Contribute an admin screen (§8)

Payload sections

onKahunaCartCollectEncryptedSettingslistens

Have a setting of your own stored encrypted without being a payment provider

Payload keys (a list of plugins.<plugin>.<key> paths; append yours and write the list back)

onKahunaCartDeclareFeatureCompatibilitylistens

Declare which core features your add-on understands

Payload features, codes

onKahunaCartOrderPaidlistens

Money landed after the fact

Payload order_id

onKahunaCartOrderPanelslistens

Add a panel to a customer's receipt page and to their order emails

Payload registry, order, channel, site_url

onKahunaCartRegisterCarrierRateProviderslistens

Plug a live carrier into shipping

Payload registry (a Shipping\Carrier\CarrierRegistry; call add() on it)

onKahunaCartRegisterHookslistens

Run a side effect inside an order flow with an undo that runs automatically, in reverse, when a later step fails

Payload registry (an Extension\Hooks\HookRegistry to call on() on)

onKahunaCartRegisterJobHandlerslistens

Register a background job type of your own

Payload registry

onKahunaCartRegisterMigrationslistens

Migrations are being collected. Append your own migrations directory

Payload paths

onKahunaCartRegisterNotificationslistens

Register the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker

Payload registry

onKahunaCartRegisterResetTableslistens

Declare your add-on's transactional tables so a pre-launch reset takes them too, and leave your configuration tables out of it

Payload registry (an Ops\ResetTableRegistry to call add() or addAll() on)

onKahunaCartRegisterZoneslistens

Declare a zone on a screen of your own so a fourth party can extend it, or mount a widget into a catalogued zone

Payload registry (a Extension\Zone\ZoneRegistry to call declare() and mount() on)

onKahunaCartShippingMethodExcludedlistens

Reinstate a refused shipping method, reword why it was refused, or attach carrier metadata

Payload method, calculation, package, active, reason, reasonKey, reasonParams, metadata, reinstate

onKahunaCartWorkerTicklistens

Put recurring work of your own on the store's own worker clock

Payload queue, now

Bookings add-on events

The Bookings add-on raises an event at each step of a booking's life and listens to the base events it prices, confirms and cancels on. See Developers.

onKahunaCartAccountPanelslistens

Add a panel to an account page

Payload registry, section, user, customer, account_base

onKahunaCartAccountSectionslistens

Add a tab of your own to the account area

Payload registry

onKahunaCartAdminSectionslistens

Contribute an admin screen (§8)

Payload sections

onKahunaCartBookingsRegisterAdminSlotsfires

Once per request, the first time the section asks what is in it

Payload registry, an Admin\AdminSlots to mount a screen into

onKahunaCartBookingsRegisterModesfires

Once per request, the first time anything asks what modes exist

Payload registry, a Mode\ModeRegistry to append a mode to, and services, a Mode\ModeServices the mode reads Bookings through

onKahunaCartBookingsRegisterPickerFacesfires

Once per request, wherever Bookings decides about its own picker assets

Payload registry, a Frontend\FaceAssets to append a script, a stylesheet and the mode's own translated words to, and modes, the mode codes that really are registered

onKahunaCartBuyButtonFieldslistens

The buy button is rendering. Add a form field of your own to it

Payload product, variants, customer, fields (an ArrayObject to append to)

onKahunaCartCartLineNoteslistens

The cart and checkout pages are rendering. Add a sentence beside a line

Payload order, items, notes (an ArrayObject keyed by line item id)

onKahunaCartCartLinePricelistens

Set a cart line's price and its breakdown

Payload order_id, product, variant, qty, currency, line_data, price

onKahunaCartCartLineValidatelistens

Refuse a cart line, or refuse what it is joining

Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors

onKahunaCartCheckoutValidatelistens

Refuse an order before payment starts

Payload order, items, post, context (checkout, api or express), errors

onKahunaCartCollectErasureBlockerslistens

Refuse an erasure because you hold something on this customer's behalf

Payload customer_id, customer (the row), blockers (a Customers\ErasureBlockers to call block() on)

onKahunaCartCustomerErasedlistens

A customer has been anonymised. Observation only

Payload customer_id, orders

onKahunaCartDeclareFeatureCompatibilitylistens

Declare which core features your add-on understands

Payload features, codes

onKahunaCartOrderBalanceOverduelistens

Decide what a late balance costs the customer, and ask to be asked again after a grace period

Payload order_id, order, balance_minor, balance_due_at, currency, days_over, look, again

onKahunaCartOrderPaidlistens

Money landed after the fact

Payload order_id

onKahunaCartOrderPanelslistens

Add a panel to a customer's receipt page and to their order emails

Payload registry, order, channel, site_url

onKahunaCartOrderPartPaidlistens

Act on a deposit before the order counts as paid

Payload order_id, order, provider, paid_minor, captured_minor, balance_minor, balance_due_at, currency

onKahunaCartOrderPaymentSchedulelistens

Take a deposit at checkout and the balance later

Payload order_id, order, items, currency, total_minor, schedule

onKahunaCartPaymentStartinglistens

Start the payment yourself instead

Payload order, items, provider, context, result, handled

onKahunaCartRegisterDashboardAlertslistens

Tell a merchant about something in your add-on that needs their attention, on the dashboard rather than in an email

Payload registry (an Admin\Alerts\AlertRegistry to call register() on)

onKahunaCartRegisterHookslistens

Run a side effect inside an order flow with an undo that runs automatically, in reverse, when a later step fails

Payload registry (an Extension\Hooks\HookRegistry to call on() on)

onKahunaCartRegisterJobHandlerslistens

Register a background job type of your own

Payload registry

onKahunaCartRegisterMigrationslistens

Migrations are being collected. Append your own migrations directory

Payload paths

onKahunaCartRegisterNotificationslistens

Register the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker

Payload registry

onKahunaCartRegisterProductTypeslistens

Register a kind of thing this store can sell

Payload registry

onKahunaCartRegisterResetTableslistens

Declare your add-on's transactional tables so a pre-launch reset takes them too, and leave your configuration tables out of it

Payload registry (an Ops\ResetTableRegistry to call add() or addAll() on)

onKahunaCartRegisterZoneslistens

Declare a zone on a screen of your own so a fourth party can extend it, or mount a widget into a catalogued zone

Payload registry (a Extension\Zone\ZoneRegistry to call declare() and mount() on)

onKahunaCartWorkerTicklistens

Put recurring work of your own on the store's own worker clock

Payload queue, now

Rentals add-on events

The Rentals add-on raises none of its own. It answers the Bookings and base events a stay's price, deposit and cancellation travel through. See Developers.

onKahunaCartBookingsRegisterAdminSlotslistens

Once per request, the first time the section asks what is in it

Payload registry, an Admin\AdminSlots to mount a screen into

onKahunaCartBookingsRegisterModeslistens

Once per request, the first time anything asks what modes exist

Payload registry, a Mode\ModeRegistry to append a mode to, and services, a Mode\ModeServices the mode reads Bookings through

onKahunaCartBookingsRegisterPickerFaceslistens

Once per request, wherever Bookings decides about its own picker assets

Payload registry, a Frontend\FaceAssets to append a script, a stylesheet and the mode's own translated words to, and modes, the mode codes that really are registered

onKahunaCartRegisterJobHandlerslistens

Register a background job type of your own

Payload registry

onKahunaCartRegisterMigrationslistens

Migrations are being collected. Append your own migrations directory

Payload paths

onKahunaCartRegisterNotificationslistens

Register the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker

Payload registry

onKahunaCartWorkerTicklistens

Put recurring work of your own on the store's own worker clock

Payload queue, now

Order history

onKahunaCartOrderHistoryRecorded fires after every entry appended to an order's record, whoever made it. The payload is order_id and entry; entry carries the dotted type, the actor, an optional message and a data object.

To write your own entry, call the recorder, not the table:

PHP
$kahunacart = KahunaCart::instance();
$kahunacart->orderHistory()->record($orderId, 'license.issued', [
    'message' => 'Issued 3 keys',
    'data'    => ['count' => 3],
]);

record() returns the new entry id. forOrder() reads the whole record, notes() reads the notes thread, and recordStatus() is the shortcut for a status column change. actingAs() sets an actor once and stamps every entry that follows in the same request.

Order emails

OrderEmails queues an order's messages. Reach it through the facade and queue by kind; do not render a message yourself:

PHP
$kahunacart->orderEmails()->queue($orderId, OrderEmails::KIND_SHIPPED);

The kinds are confirmation, payment-received, shipped, refund, pay-link and customer-note. resend() re-renders the last message of a kind against the order as it stands now, and lastSeen() reports which kinds were last queued and sent. Nothing is sent inline; the queue owns delivery, so a mail server refusing connections is a failed job, not a hung checkout.

Shipments

onKahunaCartShipmentCreated fires when a parcel is recorded, carrying order_id, the shipment row, the items it covered and whether the customer was notified (notify). It also fires for the one-click fulfill route, which is a shipment covering everything still owed.

Refunds

onKahunaCartRefundApplied fires for every refund the provider has confirmed, not only the refund that brings the order to a fully refunded state. It carries order_id, order, the transaction, the items the money was for, shipping_minor, and full. Each item carries a line_full flag, so an add-on that issued something per line can decide whether to revoke it.

onKahunaCartOrderReversed fires when an order is cancelled or refunded in full. When full is true on a refund event, the reversal has already fired.

Cart line identity

A cart line's identity is the variant it names plus a canonical hash of its line_data. onKahunaCartCartLineKey fires before that key is used, on every add, carrying order_id, variant_id, line_data and the key you may replace. Two adds that produce the same key merge into one line and bump the quantity; two that do not are two lines.

Use it when your add-on makes two otherwise identical adds different purchases, such as a booking for a different date or a licence renewal for a different key. To refuse a line outright, use onKahunaCartCartLineValidate instead, which fires before the line is in the cart.