Offers
Offers are Pro. An offer is a purchasable, a quantity, a discount and some copy. What changes is where it is shown.
| Kind | Shown | Charged |
|---|---|---|
| Order bump | A tick box on the checkout | As a line item on the cart |
| Upsell | Its own page, after the order completes | On a new order linked to the original |
| Downsell | The same, after an upsell is declined | The same |
Pricing
| Discount | Means |
|---|---|
| No discount | The purchasable's own price |
| Percentage off | price − (price × n%) |
| Amount off | price − n |
| Fixed price | n, 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:
- The parent order must be paid. Configurable, but on by default.
- The offer window must still be open — 60 minutes by default. An upsell page left open overnight cannot bill somebody the next morning.
- The offer must be eligible for that order.
- 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.