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.
public static function getSubscribedEvents(): array
{
return ['onKahunaCartOrderCompleted' => ['onOrderCompleted', 0]];
}
public function onOrderCompleted(Event $event): void
{
$order = $event['order'];
// …
}
Three rules apply to every listener:
- Append, never assign. A registry or a collection in the payload is shared with every other listener. Replacing it loses their work.
- Do not throw. KahunaCart catches, logs and carries on for most events, but a listener that throws loses its own work.
- 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.
onKahunaCartAccountPanelsfiresAdd a panel to an account page
Payload registry, section, user, customer, account_base
onKahunaCartAccountSectionsfiresAdd a tab of your own to the account area
Payload registry
onKahunaCartAdminSectionsfiresContribute an admin screen (§8)
Payload sections
onKahunaCartBuyButtonFieldsfiresThe buy button is rendering. Add a form field of your own to it
Payload product, variants, customer, fields (an ArrayObject to append to)
onKahunaCartBuyReturnLabelfiresBuy 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
onKahunaCartCalculateAdjustmentsfiresAdd or inspect a discount, shipping or tax line
Payload calculation
onKahunaCartCartLineKeyfiresDecide what makes two adds the same line
Payload order_id, variant_id, line_data, key
onKahunaCartCartLineNotesfiresThe cart and checkout pages are rendering. Add a sentence beside a line
Payload order, items, notes (an ArrayObject keyed by line item id)
onKahunaCartCartLinePricefiresSet a cart line's price and its breakdown
Payload order_id, product, variant, qty, currency, line_data, price
onKahunaCartCartLineValidatefireslistensRefuse a cart line, or refuse what it is joining
Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors
onKahunaCartCartRecoveredfiresA 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
onKahunaCartCheckoutTaxIdfiresHand 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
onKahunaCartCheckoutValidatefiresRefuse an order before payment starts
Payload order, items, post, context (checkout, api or express), errors
onKahunaCartCollectEncryptedSettingsfireslistensHave 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)
onKahunaCartCollectErasureBlockersfiresRefuse 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)
onKahunaCartCounterReturnfiresA 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
onKahunaCartCreditPostedfiresStore 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
onKahunaCartCustomerErasedfiresA customer has been anonymised. Observation only
Payload customer_id, orders
onKahunaCartCustomerGroupsGrantedfiresPut a customer in a group while something of yours is live, without storing it
Payload customer_id, grants
onKahunaCartCustomerPanelsfireslistensAdd a panel to a customer record, in the admin and in the account area
Payload registry, customer, channel, site_url
onKahunaCartCustomersMergedfiresMove 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)
onKahunaCartDeclareFeatureCompatibilityfiresDeclare which core features your add-on understands
Payload features, codes
onKahunaCartDisputeClosedfiresA dispute was settled
Payload order_id, provider, dispute_id, outcome, amount_minor, currency, reason, manual
onKahunaCartDisputeOpenedfiresA dispute was opened
Payload order_id, provider, dispute_id, amount_minor, currency, reason, respond_by
onKahunaCartDownloadDeliverfiresSend 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
onKahunaCartExchangeRatesRefreshedfireslistensExchange 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)
onKahunaCartExtendCatalogIndexfiresDeclare 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
onKahunaCartExtendStorefrontSchemafiresAdd 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)
onKahunaCartGroupOffersfiresSay 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)
onKahunaCartImportRowWrittenfiresOne row of an import was written
Payload entity, run_id, action (created, updated, skipped), id, values
onKahunaCartImportRunFinishedfireslistensAn import run reached the end of its file
Payload run_id, run (the finished row with its counts)
onKahunaCartOrderBalanceOverduefiresDecide 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
onKahunaCartOrderChangeConfirmedfireslistensA change was applied to a new version
Payload order_id, change_id, change, version_from, version_to, order, restocked
onKahunaCartOrderChangeStagedfiresA change was opened, or an action was added to one. Nothing has been written to the order
Payload order_id, change_id, change, action
onKahunaCartOrderCompletedfireslistensAn 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)
onKahunaCartOrderHistoryRecordedfiresSomething was recorded against an order
Payload order_id, entry
onKahunaCartOrderNoticefiresA notice was put on an order
Payload order_id, order, notice, audience
onKahunaCartOrderPaidfireslistensMoney landed after the fact
Payload order_id
onKahunaCartOrderPanelsfireslistensAdd a panel to a customer's receipt page and to their order emails
Payload registry, order, channel, site_url
onKahunaCartOrderPartPaidfireslistensAct on a deposit before the order counts as paid
Payload order_id, order, provider, paid_minor, captured_minor, balance_minor, balance_due_at, currency
onKahunaCartOrderPaymentSchedulefiresTake a deposit at checkout and the balance later
Payload order_id, order, items, currency, total_minor, schedule
onKahunaCartOrderReversedfireslistensA sale came undone: the order was cancelled, or refunded in full
Payload order_id, order (the row as it now stands)
onKahunaCartOrdersReassignedfiresOrders moved from one customer to another. Observation only
Payload from_customer_id, to_customer_id, orders
onKahunaCartPaymentAuthorizedfiresObserve an authorisation. Observation only
Payload order_id, order, transaction (the authorisation row), provider, amount_minor, currency
onKahunaCartPaymentCapturedfireslistensObserve 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
onKahunaCartPaymentCollectionChangedfiresWatch a part-paid order's money move. Observation only
Payload order_id, collection_id, session_id, kind (deposit, balance, refund), amount_minor, status
onKahunaCartPaymentFailedfiresA payment attempt failed
Payload order_id, provider, transaction_id, amount_minor, currency, reason
onKahunaCartPaymentMethodAvailabilityfiresRefuse 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
onKahunaCartPaymentMethodFeeLinesfiresLeave lines whose price includes the fee out of a method fee
Payload calculation, excluded_item_ids
onKahunaCartPaymentStartingfiresStart the payment yourself instead
Payload order, items, provider, context, result, handled
onKahunaCartPaymentVoidedfiresObserve a void. Observation only
Payload order_id, order (as it now stands), transaction (the voided row), provider, amount_minor, currency, reason
onKahunaCartProductDeletedfireslistensA product is gone, with its variants and its option axes
Payload id
onKahunaCartProductRelationsChangedfiresA product's curated related, cross-sell or up-sell lists changed
Payload product_id, relations (the lists as they now stand)
onKahunaCartProductSavedfireslistensA product was written — created, edited, published, unpublished, hidden, or one of its variants changed
Payload id, action (created, updated or variant)
onKahunaCartProductsBulkUpdatedfireslistensA bulk edit was applied
Payload product_ids, operations, result
onKahunaCartProviderSuitabilityfireslistensNarrow the payment methods offered for one cart
Payload order, items, providers, suitability
onKahunaCartReaderPaymentStartedfiresA 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
onKahunaCartRefundAppliedfireslistensA refund the provider confirmed has been applied to the goods
Payload order_id, order, transaction, items, shipping_minor, full
onKahunaCartRefundFailedfiresA refund the provider rejected after it was submitted
Payload order_id, order, transaction_id, provider, provider_name, amount_minor, currency, reference, reason
onKahunaCartRefundRequestedfiresA customer asked for a refund — the asking, not the refunding
Payload order_id, order, request_id, reason_code, message, amount_minor
onKahunaCartRegisterCarrierRateProvidersfiresPlug a live carrier into shipping
Payload registry (a Shipping\Carrier\CarrierRegistry; call add() on it)
onKahunaCartRegisterCheckoutFieldsfiresAdd 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)
onKahunaCartRegisterCustomFieldsfiresDeclare 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)
onKahunaCartRegisterDashboardAlertsfiresTell 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)
onKahunaCartRegisterExportsfiresRegister 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)
onKahunaCartRegisterExpressButtonsfiresDraw 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)
onKahunaCartRegisterHooksfiresRun 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)
onKahunaCartRegisterImportFieldsfiresAdd mappable fields of your own to the CSV importer's mapping screen
Payload entity, fields (an array to add to)
onKahunaCartRegisterJobHandlersfiresRegister a background job type of your own
Payload registry
onKahunaCartRegisterMigrationsfiresMigrations are being collected. Append your own migrations directory
Payload paths
onKahunaCartRegisterNotificationsfiresRegister the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker
Payload registry
onKahunaCartRegisterOperationsfiresRegister a discount operation of your own — a code, a blueprint of its arguments, and one method that returns what should come off
Payload registry
onKahunaCartRegisterOrderStatusesfireslistensContribute a code to the order-status registry; KahunaCart itself registers quote through it
Payload registry
onKahunaCartRegisterPaymentMethodsfiresContribute a payment method from an add-on
Payload methods
onKahunaCartRegisterProductTypesfireslistensRegister a kind of thing this store can sell
Payload registry
onKahunaCartRegisterProvidersfireslistensRegister yourself as a payment provider
Payload registry
onKahunaCartRegisterRateSourcesfiresAdd 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)
onKahunaCartRegisterResetTablesfiresDeclare 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)
onKahunaCartRegisterRuleAttributesfiresRegister something a condition can be written against
Payload registry
onKahunaCartRegisterSchemaExtensionsfiresAdd 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)
onKahunaCartRegisterSettingsNamespacesfiresDeclare 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)
onKahunaCartRegisterTaxEnginesfiresQuote 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
onKahunaCartRegisterTaxIdValidatorsfiresCheck 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
onKahunaCartRegisterZonesfiresDeclare 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)
onKahunaCartReleaseEntitlementfiresA 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)
onKahunaCartResolvePricefiresObserve 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)
onKahunaCartRetentionSweptfiresThe retention schedule ran. Observation only
Payload dry_run, results (rule => {examined, affected, cutoff, dry_run, error, details})
onKahunaCartReturnApprovedfiresA return was authorised
Payload order_id, order, request_id, request, return_number, lines, method, note, change_id
onKahunaCartReturnCancelledfiresA return was cancelled
Payload order_id, order, request_id, request, return_number, by (merchant or customer), note
onKahunaCartReturnLabelProvidersfiresOffer to buy return labels
Payload providers (Refund\ReturnLabelProviders, call add(id, label)), request (the return, when one is being approved), order
onKahunaCartReturnReceivedfiresThe 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
onKahunaCartReturnRefundedfiresA return was refunded
Payload order_id, order, request_id, request, return_number, transaction_id
onKahunaCartReturnRequestedfiresA 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
onKahunaCartReviewModeratedfiresA review's state changed, however it changed. Observation only
Payload review_id, product_id, state, previous_state, moderated_by
onKahunaCartReviewSubmittedfiresA review was written, before anybody moderated it. Observation only
Payload review_id, product_id, customer_id, rating, state, verified
onKahunaCartShipmentCreatedfireslistensA parcel went out
Payload order_id, shipment, items, notify
onKahunaCartShipmentDeliveredfiresA parcel arrived
Payload order_id, shipment
onKahunaCartShippingMethodExcludedfiresReinstate a refused shipping method, reword why it was refused, or attach carrier metadata
Payload method, calculation, package, active, reason, reasonKey, reasonParams, metadata, reinstate
onKahunaCartStockAdjustedfireslistensAn 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
onKahunaCartStoreStatusChangedfiresThe 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)
onKahunaCartSubscriptionWebhookEventsfiresAct on the recurring events a webhook delivery carried
Payload provider, events, result
onKahunaCartTenderRecordedfiresA 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
onKahunaCartWebhookRejectedfiresA webhook was rejected
Payload provider, provider_name, status, reason
onKahunaCartWishlistItemAddedfiresSomething 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
onKahunaCartWishlistMergedfiresAn 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)
onKahunaCartWorkerTickfireslistensPut 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.
onKahunaCartAccountPanelslistensAdd a panel to an account page
Payload registry, section, user, customer, account_base
onKahunaCartAccountSectionslistensAdd a tab of your own to the account area
Payload registry
onKahunaCartAdminSectionslistensContribute an admin screen (§8)
Payload sections
onKahunaCartBuyButtonFieldslistensThe buy button is rendering. Add a form field of your own to it
Payload product, variants, customer, fields (an ArrayObject to append to)
onKahunaCartCalculateAdjustmentslistensAdd or inspect a discount, shipping or tax line
Payload calculation
onKahunaCartCartLineNoteslistensThe cart and checkout pages are rendering. Add a sentence beside a line
Payload order, items, notes (an ArrayObject keyed by line item id)
onKahunaCartCartLineValidatelistensRefuse a cart line, or refuse what it is joining
Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors
onKahunaCartCheckoutValidatelistensRefuse an order before payment starts
Payload order, items, post, context (checkout, api or express), errors
onKahunaCartCollectErasureBlockerslistensRefuse 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)
onKahunaCartCustomersMergedlistensMove 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
onKahunaCartDeclareFeatureCompatibilitylistensDeclare which core features your add-on understands
Payload features, codes
onKahunaCartDownloadDeliverlistensSend 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
onKahunaCartLicenseActivatedfiresA machine took a seat on a license key
Payload license, activation
onKahunaCartLicenseExtendedfiresA renewal moved a key's expiry or its updates window forward
Payload license, previous
onKahunaCartLicenseIssuedfireslistensA license key was minted
Payload license
onKahunaCartLicenseRevokedfiresA license key was killed permanently
Payload license, reason, reversible
onKahunaCartLicenseWidenedfiresAn existing key now covers a product it did not before. Nothing was minted
Payload license, product_id
onKahunaCartOrderCompletedlistensAn 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)
onKahunaCartOrderPaidlistensMoney landed after the fact
Payload order_id
onKahunaCartOrderPanelslistensAdd a panel to a customer's receipt page and to their order emails
Payload registry, order, channel, site_url
onKahunaCartOrderReversedlistensA sale came undone: the order was cancelled, or refunded in full
Payload order_id, order (the row as it now stands)
onKahunaCartRegisterJobHandlerslistensRegister a background job type of your own
Payload registry
onKahunaCartRegisterMigrationslistensMigrations are being collected. Append your own migrations directory
Payload paths
onKahunaCartRegisterNotificationslistensRegister the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker
Payload registry
onKahunaCartRegisterResetTableslistensDeclare 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)
onKahunaCartRegisterZoneslistensDeclare 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)
onKahunaCartReleaseEntitlementlistensA customer asked for a release. Decide which versions their purchase entitles them to
Payload product_id, customer, releases, entitled
onKahunaCartSubscriptionActivatedlistensA subscription became active, out of incomplete or out of a trial
Payload order, order_id (both null on a trial), period, trial (bool), actor
onKahunaCartSubscriptionCancelledlistensThe subscription ended. Check at_period_end before revoking anything
Payload at_period_end (bool), period, ends_at (int, when it actually ended), reason, actor
onKahunaCartSubscriptionExpiredlistensThe subscription ran out and was not renewed
Payload period, reason, actor
onKahunaCartSubscriptionRenewalOrderBuildinglistensA renewal order is being assembled. Modify lines to carry something into it
Payload subscription, plan, period, sequence (int), lines
onKahunaCartWorkerTicklistensPut 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.
onKahunaCartAccountSectionslistensAdd a tab of your own to the account area
Payload registry
onKahunaCartAdminSectionslistensContribute an admin screen (§8)
Payload sections
onKahunaCartBuyButtonFieldslistensThe buy button is rendering. Add a form field of your own to it
Payload product, variants, customer, fields (an ArrayObject to append to)
onKahunaCartCalculateAdjustmentslistensAdd or inspect a discount, shipping or tax line
Payload calculation
onKahunaCartCartLineNoteslistensThe cart and checkout pages are rendering. Add a sentence beside a line
Payload order, items, notes (an ArrayObject keyed by line item id)
onKahunaCartCartLinePricelistensSet a cart line's price and its breakdown
Payload order_id, product, variant, qty, currency, line_data, price
onKahunaCartCartLineValidatelistensRefuse a cart line, or refuse what it is joining
Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors
onKahunaCartCollectErasureBlockerslistensRefuse 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)
onKahunaCartCustomerGroupsGrantedlistensPut a customer in a group while something of yours is live, without storing it
Payload customer_id, grants
onKahunaCartCustomerPanelslistensAdd a panel to a customer record, in the admin and in the account area
Payload registry, customer, channel, site_url
onKahunaCartDeclareFeatureCompatibilitylistensDeclare which core features your add-on understands
Payload features, codes
onKahunaCartGroupOfferslistensSay 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 methodNot an event: the handler this add-on binds to onKahunaCartOrderCompleted for a renewal paid through a pay link
Payload order
onKahunaCartInvoiceOrderPaidhandler methodNot an event: the handler this add-on binds to onKahunaCartOrderPaid for a renewal paid through a pay link
Payload order_id
onKahunaCartNewslettersRegisterSegmentFieldslistensThe segment vocabulary is being collected. Declare a field of your own on the registry
Payload —
onKahunaCartOrderCompletedlistensAn 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)
onKahunaCartOrderPaidlistensMoney landed after the fact
Payload order_id
onKahunaCartOrderPanelslistensAdd a panel to a customer's receipt page and to their order emails
Payload registry, order, channel, site_url
onKahunaCartOrderReversedlistensA sale came undone: the order was cancelled, or refunded in full
Payload order_id, order (the row as it now stands)
onKahunaCartPaymentMethodAvailabilitylistensRefuse 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
onKahunaCartPaymentMethodFeeLineslistensLeave lines whose price includes the fee out of a method fee
Payload calculation, excluded_item_ids
onKahunaCartPaymentStartinglistensStart the payment yourself instead
Payload order, items, provider, context, result, handled
onKahunaCartProviderSuitabilitylistensNarrow the payment methods offered for one cart
Payload order, items, providers, suitability
onKahunaCartRegisterDashboardAlertslistensTell 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)
onKahunaCartRegisterJobHandlerslistensRegister a background job type of your own
Payload registry
onKahunaCartRegisterMigrationslistensMigrations are being collected. Append your own migrations directory
Payload paths
onKahunaCartRegisterNotificationslistensRegister the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker
Payload registry
onKahunaCartRegisterResetTableslistensDeclare 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)
onKahunaCartSubscriptionActivatedfiresA subscription became active, out of incomplete or out of a trial
Payload order, order_id (both null on a trial), period, trial (bool), actor
onKahunaCartSubscriptionCancelScheduledfiresA 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
onKahunaCartSubscriptionCancelledfiresThe subscription ended. Check at_period_end before revoking anything
Payload at_period_end (bool), period, ends_at (int, when it actually ended), reason, actor
onKahunaCartSubscriptionCreatedfiresA 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)
onKahunaCartSubscriptionExpiredfiresThe subscription ran out and was not renewed
Payload period, reason, actor
onKahunaCartSubscriptionPastDuefiresThe platform stopped retrying. The grace clock starts here
Payload period, order_id, reason, actor
onKahunaCartSubscriptionPausedfiresBilling stopped without ending the subscription
Payload period, reason, actor
onKahunaCartSubscriptionPaymentFailedfiresA charge was declined. The platform is still retrying
Payload attempt (int, the running failure count), reason, order_id, actor
onKahunaCartSubscriptionPaymentMethodUpdatedfiresThe customer changed the card the platform bills
Payload actor, plus whatever the caller passed — the webhook path sends provider_ref
onKahunaCartSubscriptionPeriodExtendedfiresA merchant added days to the current period by hand
Payload days (int), period (after the move), previous_period, note, actor
onKahunaCartSubscriptionPeriodRolledBackfiresA 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
onKahunaCartSubscriptionRenewalOrderBuildingfiresA renewal order is being assembled. Modify lines to carry something into it
Payload subscription, plan, period, sequence (int), lines
onKahunaCartSubscriptionRenewedfiresA 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
onKahunaCartSubscriptionResumedfiresBilling restarted after a pause
Payload period, cancel_at_period_end (bool), actor
onKahunaCartSubscriptionWebhookEventsfireslistensAct on the recurring events a webhook delivery carried
Payload provider, events, result
onKahunaCartSubscriptionsSweptfiresThe 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.
onKahunaCartAccountPanelslistensAdd a panel to an account page
Payload registry, section, user, customer, account_base
onKahunaCartAccountSectionslistensAdd a tab of your own to the account area
Payload registry
onKahunaCartAdminSectionslistensContribute an admin screen (§8)
Payload sections
onKahunaCartCartRecoveredlistensA 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
onKahunaCartCollectErasureBlockerslistensRefuse 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)
onKahunaCartCustomerErasedlistensA customer has been anonymised. Observation only
Payload customer_id, orders
onKahunaCartCustomersMergedlistensMove 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)
onKahunaCartDeclareFeatureCompatibilitylistensDeclare which core features your add-on understands
Payload features, codes
onKahunaCartNewslettersConfirmedfireslistensSomebody clicked the link in their confirmation email. Carries subscriber_id, list_id and source
Payload subscriber_id, list_id, source
onKahunaCartNewslettersRegisterDeliverabilityChecksfiresThe Health checks are being collected. Add a check of your own to the registry
Payload —
onKahunaCartNewslettersRegisterSegmentFieldsfiresThe segment vocabulary is being collected. Declare a field of your own on the registry
Payload —
onKahunaCartNewslettersSegmentsCountedfiresThe worker finished a segment recount sweep. Carries counted and failed
Payload —
onKahunaCartNewslettersSubscribedfireslistensSomebody joined a list and is mailable straight away. Carries subscriber_id, list_id and source
Payload subscriber_id, list_id, source
onKahunaCartOrderCompletedlistensAn 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)
onKahunaCartOrderPaidlistensMoney landed after the fact
Payload order_id
onKahunaCartRegisterCheckoutFieldslistensAdd 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)
onKahunaCartRegisterDashboardAlertslistensTell 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)
onKahunaCartRegisterJobHandlerslistensRegister a background job type of your own
Payload registry
onKahunaCartRegisterMigrationslistensMigrations are being collected. Append your own migrations directory
Payload paths
onKahunaCartRegisterNotificationslistensRegister the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker
Payload registry
onKahunaCartRegisterResetTableslistensDeclare 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)
onKahunaCartRegisterZoneslistensDeclare 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)
onKahunaCartSubscriptionActivatedlistensA subscription became active, out of incomplete or out of a trial
Payload order, order_id (both null on a trial), period, trial (bool), actor
onKahunaCartSubscriptionCancelledlistensThe subscription ended. Check at_period_end before revoking anything
Payload at_period_end (bool), period, ends_at (int, when it actually ended), reason, actor
onKahunaCartSubscriptionRenewedlistensA 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.
onKahunaCartAccountPanelslistensAdd a panel to an account page
Payload registry, section, user, customer, account_base
onKahunaCartAdminSectionslistensContribute an admin screen (§8)
Payload sections
onKahunaCartCollectEncryptedSettingslistensHave 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)
onKahunaCartDeclareFeatureCompatibilitylistensDeclare which core features your add-on understands
Payload features, codes
onKahunaCartOrderPaidlistensMoney landed after the fact
Payload order_id
onKahunaCartOrderPanelslistensAdd a panel to a customer's receipt page and to their order emails
Payload registry, order, channel, site_url
onKahunaCartRegisterCarrierRateProviderslistensPlug a live carrier into shipping
Payload registry (a Shipping\Carrier\CarrierRegistry; call add() on it)
onKahunaCartRegisterHookslistensRun 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)
onKahunaCartRegisterJobHandlerslistensRegister a background job type of your own
Payload registry
onKahunaCartRegisterMigrationslistensMigrations are being collected. Append your own migrations directory
Payload paths
onKahunaCartRegisterNotificationslistensRegister the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker
Payload registry
onKahunaCartRegisterResetTableslistensDeclare 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)
onKahunaCartRegisterZoneslistensDeclare 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)
onKahunaCartShippingMethodExcludedlistensReinstate a refused shipping method, reword why it was refused, or attach carrier metadata
Payload method, calculation, package, active, reason, reasonKey, reasonParams, metadata, reinstate
onKahunaCartWorkerTicklistensPut 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.
onKahunaCartAccountPanelslistensAdd a panel to an account page
Payload registry, section, user, customer, account_base
onKahunaCartAccountSectionslistensAdd a tab of your own to the account area
Payload registry
onKahunaCartAdminSectionslistensContribute an admin screen (§8)
Payload sections
onKahunaCartBookingsRegisterAdminSlotsfiresOnce per request, the first time the section asks what is in it
Payload registry, an Admin\AdminSlots to mount a screen into
onKahunaCartBookingsRegisterModesfiresOnce 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
onKahunaCartBookingsRegisterPickerFacesfiresOnce 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
onKahunaCartBuyButtonFieldslistensThe buy button is rendering. Add a form field of your own to it
Payload product, variants, customer, fields (an ArrayObject to append to)
onKahunaCartCartLineNoteslistensThe cart and checkout pages are rendering. Add a sentence beside a line
Payload order, items, notes (an ArrayObject keyed by line item id)
onKahunaCartCartLinePricelistensSet a cart line's price and its breakdown
Payload order_id, product, variant, qty, currency, line_data, price
onKahunaCartCartLineValidatelistensRefuse a cart line, or refuse what it is joining
Payload order_id, product, variant, qty, line_data, item_id, cart_items, errors
onKahunaCartCheckoutValidatelistensRefuse an order before payment starts
Payload order, items, post, context (checkout, api or express), errors
onKahunaCartCollectErasureBlockerslistensRefuse 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)
onKahunaCartCustomerErasedlistensA customer has been anonymised. Observation only
Payload customer_id, orders
onKahunaCartDeclareFeatureCompatibilitylistensDeclare which core features your add-on understands
Payload features, codes
onKahunaCartOrderBalanceOverduelistensDecide 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
onKahunaCartOrderPaidlistensMoney landed after the fact
Payload order_id
onKahunaCartOrderPanelslistensAdd a panel to a customer's receipt page and to their order emails
Payload registry, order, channel, site_url
onKahunaCartOrderPartPaidlistensAct on a deposit before the order counts as paid
Payload order_id, order, provider, paid_minor, captured_minor, balance_minor, balance_due_at, currency
onKahunaCartOrderPaymentSchedulelistensTake a deposit at checkout and the balance later
Payload order_id, order, items, currency, total_minor, schedule
onKahunaCartPaymentStartinglistensStart the payment yourself instead
Payload order, items, provider, context, result, handled
onKahunaCartRegisterDashboardAlertslistensTell 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)
onKahunaCartRegisterHookslistensRun 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)
onKahunaCartRegisterJobHandlerslistensRegister a background job type of your own
Payload registry
onKahunaCartRegisterMigrationslistensMigrations are being collected. Append your own migrations directory
Payload paths
onKahunaCartRegisterNotificationslistensRegister the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker
Payload registry
onKahunaCartRegisterProductTypeslistensRegister a kind of thing this store can sell
Payload registry
onKahunaCartRegisterResetTableslistensDeclare 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)
onKahunaCartRegisterZoneslistensDeclare 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)
onKahunaCartWorkerTicklistensPut 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.
onKahunaCartBookingsRegisterAdminSlotslistensOnce per request, the first time the section asks what is in it
Payload registry, an Admin\AdminSlots to mount a screen into
onKahunaCartBookingsRegisterModeslistensOnce 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
onKahunaCartBookingsRegisterPickerFaceslistensOnce 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
onKahunaCartRegisterJobHandlerslistensRegister a background job type of your own
Payload registry
onKahunaCartRegisterMigrationslistensMigrations are being collected. Append your own migrations directory
Payload paths
onKahunaCartRegisterNotificationslistensRegister the emails your plugin sends so they appear on the Notifications screen with a switch and a template picker
Payload registry
onKahunaCartWorkerTicklistensPut 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:
$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:
$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.