| Element | Source |
| Products, prices, images, stock state | Real — the live /brand/hayati/ archive |
| Multibuy offers | Real — each product’s own hv-deal-tag, read off the live archive. Offers vary by format as well as range |
| What is in the basket | Illustrative, and the same basket the cart template uses so the two can be read against each other. The products, prices and offers are real; the choice of what is in it is ours. Use the review tool at the top to move between states |
| The line item and the subtotal | The live checkout renders a line at £5.00 against a £5.49 subtotal for one item, where the cart a click earlier showed £5.49/£5.49. Cart and checkout here share one priceBasket() (build/pricing.py), so they cannot disagree. The cause of the live discrepancy is still an open question — the checkout HTML is not in the crawl, so it could not be diagnosed |
| Delivery services and prices | Real — the live shipping table (Tracked 48 £2.95, Tracked 24 £3.95, DPD Express £7.95, Special Next Day £8.95) |
| Delivery dates | Derived, not quoted: the dispatch rule (same day before 4pm Mon–Fri, after 4pm Friday goes Monday, none on Bank Holidays) plus each service’s own working-day window, counted off the browser’s clock. Bank Holidays come from GOV.UK’s England and Wales list and are skipped in both the dispatch and the working-day maths. That list runs to 26 December 2028; past it the page adds “Bank Holidays excepted” rather than assuming there are none |
| Free delivery threshold | Real — “Free delivery on UK orders £30 and over”, and the live shipping page names the service: “All orders of £30 and above are sent via Free Royal Mail Tracked 48”. Calculated on the subtotal after multibuy and before any coupon, which is what the cart does. Whether the live site agrees is an open question, and one benchmarked competitor gets it wrong |
| “Offers drop it under £30” | A real edge the cart brief warns about — one benchmarked competitor reads the threshold before discounts, so qualifying baskets lose free delivery at checkout. That state’s basket is £30.42 before the offers and £25.00 after them. Both templates read it after, and the gate loads both pages and compares them on this basket |
| The pence | Every figure is derived by summing the pence that are actually printed, not by apportioning a float and rounding at the end. Both templates import one priceBasket() (build/pricing.py) that works in integer pence and hands out a chunk’s saving by largest remainder, so the line prices a customer reads always add up to the subtotal underneath them. The earlier build drifted a penny on real baskets — the same defect as the live site’s £5.00 line under a £5.49 subtotal. build/arith.js checks 391,782 baskets against the real catalogue on every gate run |
| Coupons | FIRST10 is real — the live promo bar. One implementation for both pages. A code that is not recognised no longer removes the one already applied, the discount is rounded to pence, and the threshold is read before it |
| FIRST10 | Real — the live promo bar, “Get 10% off your first order - use code FIRST10”. Applied here to the post-multibuy subtotal. Whether the live site stacks it with multibuy is an open question |
| Worldpay | Real, and the only payment method Hayati has. Hayati have confirmed they use Worldpay’s own hosted payment page: the customer leaves hayativape.co.uk to enter their card, exactly as the live checkout does today (Pay with Card (Worldpay), then “on the next page, enter your card details”). This build keeps that flow |
| The redirect, and what the design does about it | The checkout brief calls the off-domain redirect the single largest conversion leak on the site, and that stands — but it is Hayati’s gateway arrangement, not something a template can change. What a template can change is whether the domain switch is a surprise. The live page fires it off a button reading PLACE ORDER. Here the button reads Continue to payment, a three-step strip above it says where the card is entered and that the customer comes back, and a line under the button repeats it. A redirect nobody expected reads as a hijack; one you were told about reads as a bank. Deliberate copy change from the live PLACE ORDER, because on a redirect the order is not placed at that click |
| Express wallets — deliberately not here | Worldpay card payment is the only method Hayati has today, so it is the only method on this page. An earlier version of this template designed in Apple Pay, Google Pay and PayPal; Hayati have confirmed there is no express checkout at present and that it is a potential future upgrade, so it has been taken out rather than shown as though it existed. It remains the brief’s highest-value recommendation — guest checkout, PayPal and Klarna are table stakes at all six benchmarked competitors, and on an 83%-mobile site a wallet collapses this whole form into a thumbprint. Picking it up needs Worldpay to confirm which wallets the account supports, and whether Klarna needs its own agreement — a commercial question, not a build one |
| Card details | Not on this page at all, and correctly so — Worldpay collects them on its own page. That is also why there is no card validation here: what this form must get right before the customer leaves is the address and the consent, because coming back to fix a typo means going through Worldpay twice |
| Acceptance criterion 1 | The brief’s first criterion is “a returning mobile customer with Apple Pay can complete an order without typing an address”. It is not met, on purpose. Hayati have confirmed there is no express checkout today. Criteria 2 to 7 are met. The form is cut to six required fields with postcode lookup, full autocomplete and the right keyboard per field, which is the most that can be done for a typed checkout — but it is not a thumbprint, and the gap is the wallet row above |
| VAT | Derived: UK vaping products are standard rated, and a UK shelf price is VAT inclusive, so the line is a breakdown of the total rather than an addition to it — the total divided by six. The live checkout does not show it |
| Delivery dates on the basket | The basket offered four bare radios and this page four dated cards, for the same four services, so the customer chose delivery twice and had less to go on the first time. Both now use one shared table and one shared date rule (build/shipping.py, build/dates.py) |
| Crossing the 4pm cut-off mid-session | Every quoted date moves. It used to move silently — the numbers simply changed while the customer was reading them, and on the basket they did not change at all while the page said they had. Both pages now say so, in a notice under the dispatch line |
| Continue to payment | It validates the form and stops there. There was no <form> element at all before, so every required on the page was inert, the error styling was dead CSS and the button did nothing whatever on a blank form. It now names each problem beside the field, counts them above the button, and lands the keyboard on the first one in document order. The rules are the narrowest that are actually true — a postcode is checked for shape, not against a database, because a checkout that rejects a real address is worse than one that accepts a typo. Nothing is submitted, stored or sent anywhere, and the page says so on success rather than pretending an order was placed |
| The 18 or over confirmation | New, and not on the live checkout. Nicotine is an age-restricted product and the order form is where the customer states it. Unticked by default, as is the terms box — a pre-ticked consent is a dark pattern the CMA and the ICO both name, and it is not consent the client could rely on |
| The trust strip’s dispatch line | The live strip reads “Ordered by 4pm, sent today” with no weekday on it. On a Saturday that sits directly above a live dispatch line reading “dispatched Monday”. Mon–Fri has been added — the qualifier is on Hayati’s own shipping page, so this completes the client’s copy rather than replacing it. The only change to a verified brand string in this build |
| Age verification | The brief says there is no entry age gate; the live site serves one (“Are you over 18?”) on every page in the crawl. This build makes no claim about what happens after the order, because we have not verified it. What it adds is an 18 or over confirmation at the point of sale, which the live checkout does not have. Worth settling before build — what runs after the order, and by whom |
| Returns, dispatch, support hours | Real — the live returns policy (14 days, authorised by support first) and the live shipping page and FAQ (4pm cut-off, Monday rule, Bank Holidays) |
| Nav, footer, logo, palette, type | Real — verbatim from the live site |
| What the demo does not do | Find my address goes nowhere — it needs a paid lookup provider. Place order validates the form and stops there; nothing is submitted. Quantities are set by the review tool rather than edited here — editing belongs in the basket. Delivery choice, dates, the coupon, the progress bar and every total are live |