Fjord for Craft CMS

Offers

Offers are Pro. An offer is a purchasable, a quantity, a discount and some copy. What changes is where it is shown.

KindShownCharged
Order bumpA tick box on the checkoutAs a line item on the cart
UpsellIts own page, after the order completesOn a new order linked to the original
DownsellThe same, after an upsell is declinedThe same

Pricing

DiscountMeans
No discountThe purchasable's own price
Percentage offprice − (price × n%)
Amount offprice − n
Fixed pricen, whatever the product costs

Quantity multiplies everything, and the result is clamped at zero — a 120% discount is a mistake, not a refund. A percentage above 100 is refused at save time.

Eligibility

An offer can carry a Commerce order condition. With no condition it is always eligible; with one it is shown only when the order matches. A condition that throws, or that references something that no longer exists, hides the offer rather than showing it to everybody — the safe direction when the alternative is charging the wrong customer.

Order bumps

Ticking a bump adds a line item stamped with options.fjordOffer, and an order adjuster discounts it. The line item is the truth, not a ledger entry, so the discount survives a recalculation, a control-panel edit, and anything else that touches the cart without going through Fjord.

Two details worth knowing:

  • The adjuster is spliced in ahead of the tax adjuster. Commerce runs shipping → discounts → tax and its registration event appends, so an adjuster that arrived after tax would charge the customer tax on a discount they were given.
  • The offer's own quantity is the discounted quantity. Raising a bumped line to five by hand does not extend the bump price to the extras.

Unticking removes the line and the record of it, so the customer can change their mind twice.

One-click upsells

An accepted post-purchase offer is charged on a new order linked to the original, not added to it. A completed Commerce order is locked, and its totals have already been emailed, invoiced and possibly exported — adding a line to one rewrites a document the customer already has. The linked order is refundable on its own and appears in Commerce's own screens without anything having to know about Fjord.

The charge uses the gateway and payment source captured during checkout. Both must still exist, and the gateway must support payment sources and purchases; if the gateway asks for an interactive step — 3-D Secure, a hosted page, a wallet confirmation — that is not a one-click charge, so the attempt fails and the order it would have been taken on is discarded rather than left behind as a stray cart.

The guards

Four things stand between a button press and a charge, and all four are checked server-side when the endpoint is called, not when the page was rendered:

  1. The parent order must be paid. Configurable, but on by default.
  2. The offer window must still be open — 60 minutes by default. An upsell page left open overnight cannot bill somebody the next morning.
  3. The offer must be eligible for that order.
  4. The offer must not already be resolved for that order.

That last one is enforced by a unique index on (orderId, offerId), not by a check-then-act. A double-submitted form, a retried request and a customer hammering the button all collide on the index instead of charging twice.

Declines are recorded

Turning an offer down is stored, not merely absent — "shown and refused" and "never shown" are different numbers and only one of them is a problem. By default a declined offer is not shown again on the same order; Replay Declined Offers changes that, which is a decision about how hard to push rather than a default.

What a failure leaves behind

A charge that fails leaves a failed record with the gateway's message attached, and no stray order. The customer is moved on to the next step rather than parked on a page that will not work. Failures are visible in Fjord → Reports, under recent offers.