Show the quiz discount as a QR code
Goal: the results page shows the discount the quiz just issued as a QR code with the code and its expiry under it, so a shopper who walks into the store shows their phone at the counter and the staff read the code from it.
Uses: the Storefront JS API: discount.issued (discount_id, code, ends_at), fired once per discount when the code reaches the quiz. A QR library on the page (the snippet loads qrcode.js from cdnjs; any library that draws a QR from a string does). Events fire on the Plus and Enterprise plans.
On the page
<div id="octane-discount-qr" hidden> <div data-qr></div> <p>Your code: <strong data-code></strong> <span data-ends></span></p></div><script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script><script> window.octaneai = window.octaneai || []; window.octaneai.push(function (octaneai) { var box = document.getElementById('octane-discount-qr'); var shown = {};
octaneai.on('discount.issued', function (event) { var discount = event.data.object; if (shown[discount.discount_id]) return; // once per discount shown[discount.discount_id] = true;
var target = box.querySelector('[data-qr]'); target.innerHTML = ''; new QRCode(target, { text: discount.code, width: 160, height: 160, correctLevel: QRCode.CorrectLevel.M }); box.querySelector('[data-code]').textContent = discount.code; box.querySelector('[data-ends]').textContent = discount.ends_at ? '(valid until ' + new Date(discount.ends_at).toLocaleDateString() + ')' : '';
event.element.insertAdjacentElement('afterend', box); // right under the quiz box.hidden = false; }); });</script>The QR encodes the code itself as text (SUMMER-4F2K), nothing else: any scanner that reads a QR into a text field, the camera app on the till’s device included, gives the staff the exact code to type into the discount field at checkout.
What the event carries
{ "id": "evt_6f1d2c3b-4a59-4877-8695-a4b3c2d1e0f9", "type": "discount.issued", "created": "2026-09-11T14:03:22.418Z", "api_version": "v1", "data": { "object": { "session_id": "sess_9b2d4a5e6f7a4b8c9d0e1f2a3b4c5d6e", "quiz_id": "quiz_7c9e6679742540de944be07fc1f90ae7", "quiz_name": "Find your routine", "version_id": "ver_15ce0be06b904d0180b1155da02a6c6f", "version_number": 2, "discount_id": "discount-k3x9q", "code": "SUMMER-4F2K", "ends_at": "2026-09-30T23:59:59Z" } }}ends_at is null for a code without an expiry. The same fields ride on quiz.finished under discount_codes[], and discount.copied fires when the shopper copies the code from the quiz’s own coupon component.
Errors to handle
| What | What to do |
|---|---|
The quiz is on a plan without the API: octaneai.enabled is false, on() registers, nothing fires. | The box stays hidden; the quiz’s own coupon component still shows the code. |
| The QR library did not load (a blocked CDN). | new QRCode throws inside the listener; the API logs [OctaneAI] listener for discount.issued threw and the quiz goes on. Host the library yourself if the CDN is not allowed on your store. |
discount.issued fires twice for the same code (two quizzes on one page sharing a discount). | The shown map draws it once. |
Things to know
- When it fires. Once per discount, the moment the code reaches the quiz, which is on the page that issues it, usually the results page. It is not tied to the shopper copying it.
- In-store redemption is the store’s rule. The code is a normal Shopify discount code; whether it can be used at a point of sale depends on how the discount was created in Shopify (sales channels, one use per customer, dates).
ends_atis the expiry the quiz knows. - Keep the box outside the quiz. The quiz renders in its own shadow root; your box lives on the page next to
event.element, so your theme’s CSS styles it and the quiz’s does not. - Two quizzes on one page.
event.elementis the quiz that issued the code; the box moves under whichever fired last. Give each quiz its own box if both issue codes.
