Enable printable invoices and receipts
On this page
StoreConnect does not generate invoices or receipts as built-in documents, but you can add printable invoice and receipt links to the customer account page using Liquid theme snippets. When a customer clicks the link, a popup opens containing only the printable content — using your theme’s existing CSS — and the browser’s print dialog opens automatically so they can save to PDF or print directly.
:::note This applies to web orders only, and requires access to your theme’s snippet and template files via Theme Builder or a local theme repo. POS receipts and invoices use a separate print template system — see POS print templates. :::
How it works
The solution uses a URL parameter (?print=invoice or ?print=receipt) on the order detail page to trigger a popup:
- A snippet (
account/print) reads theprintparam from the current request. If it matchesinvoiceorreceipt, it finds the matching order and opens a popup window. - The popup copies your theme’s active stylesheets so the printable content inherits your store’s fonts, colors, and layout.
- A second snippet (
orders/printable) renders the order content — company header, addresses, order items, payment summary — inside the popup. - The popup title is set to
Invoice 00000134orReceipt 00000134so the suggested PDF filename is correct when customers save.
Before you start
- You have theme access (Theme Builder or a local theme repo) and can add or edit snippets and page templates.
- You understand the StoreConnect Liquid object model.
- You have added the following locale keys to your theme locales:
| Key | Value |
|---|---|
accounts.orders.invoice.heading |
Invoice %{order_number} |
accounts.orders.receipt.heading |
Receipt %{order_number} |
Step 1 — Create the print controller snippet
Create snippets/account/print. This snippet runs on every account page load but only acts when ?print=invoice or ?print=receipt is present and the section is orders.
```liquid
{%- comment -%} Snippet: account/print Inputs: section, identifier, print (‘invoice’|’receipt’) Opens a clean popup, copies page styles, injects printable HTML, sets the title to ‘{Type} {OrderRef}’ and triggers window.print(). {%- endcomment -%}
{%- assign _section = section | downcase -%} {%- assign _print = print | downcase -%} {%- assign _idref = identifier -%}
{%- if _section == ‘orders’ and _idref != blank and (_print == ‘invoice’ or _print == ‘receipt’) -%} {%- assign order = nil -%} {%- for o in current_customer.orders -%} {%- if o.reference == _idref -%} {%- assign order = o -%} {%- break -%} {%- endif -%} {%- endfor -%}
{%- if order -%} {%- capture __printable -%} {% render “orders/printable”, order: order, type: _print %} {%- endcapture -%} {%- assign __doc_title = _print | capitalize | append: ‘ ‘ | append: order.reference -%}
<script>
(function () {
var w = window.open('', '_blank');
if (!w) return;
var title = {{ __doc_title | json }};
var headHTML = ''
+ '<meta charset="utf-8">'
+ '<meta name="viewport" content="width=device-width,initial-scale=1">'
+ '<meta name="robots" content="noindex,nofollow,noarchive">'
+ '<title>' + title + '</title>';
w.document.open();
w.document.write('<!doctype html><html><head>' + headHTML + '</head><body></body></html>');
w.document.close();
try { w.document.title = title; } catch (e) {}
// Copy active stylesheets so the popup inherits the store's theme
var head = w.document.head;
document.querySelectorAll('link[rel="stylesheet"], style').forEach(function (node) {
try { head.appendChild(node.cloneNode(true)); } catch (e) {}
});
var content = {{ __printable | json }};
w.document.body.innerHTML = content;
setTimeout(function () { try { w.print(); } catch (e) {} }, 100);
})();
</script> {%- else -%}
{{ "accounts.orders.show.not_found" | t }} {%- endif -%} {%- endif -%} ```
This uses a popup rather than a full-page redirect so your site header, footer, and chat widgets stay out of the printed output. The popup inherits your theme CSS automatically.
Step 2 — Create the printable layout snippet
Create snippets/orders/printable. This snippet renders the actual invoice or receipt content inside the popup. Keep structural CSS (spacing, borders, grids) here and let your theme handle fonts and colors.
The snippet reads company details from store variables you define on your store record. Create the following store variables and mark each one as Available in Liquid:
| Variable key | Example value |
|---|---|
company_name |
ACME Pty Ltd |
company_number |
ABN 12 345 678 901 |
company_address |
123 Main St, Sydney NSW 2000 (multiline) |
invoice_payment_instructions |
Please pay to BSB 123-456, Account 12345678, quoting (multiline) |
Alternatively, split the address into individual fields (company_street, company_city, company_state, company_postal_code, company_country) — the snippet handles both formats.
```liquid
{%- comment -%} Snippet: orders/printable Required: order Optional: type = ‘invoice’ | ‘receipt’ Store variables expected: company_name, company_number, invoice_payment_instructions company_address OR company_street, company_city, company_state, company_postal_code, company_country {%- endcomment -%}
{%- default order: nil, type: ‘invoice’ -%} {%- unless order == blank -%} {%- assign heading_key = ‘accounts.orders.’ | append: type | append: ‘.heading’ -%} {%- assign company_name = store_variables[‘company_name’] -%} {%- assign company_number = store_variables[‘company_number’] -%} {%- assign payment_instructions_raw = store_variables[‘invoice_payment_instructions’] -%} {%- assign company_address = store_variables[‘company_address’] -%} {%- assign company_street = store_variables[‘company_street’] -%} {%- assign company_city = store_variables[‘company_city’] -%} {%- assign company_state = store_variables[‘company_state’] -%} {%- assign company_postal_code = store_variables[‘company_postal_code’] -%} {%- assign company_country = store_variables[‘company_country’] -%}
{%- endif -%} {%- assign _line = '' -%} {%- if company_city -%}{%- assign _line = company_city -%}{%- endif -%} {%- if company_state -%}{%- unless _line == '' -%}{%- assign _line = _line | append: ', ' -%}{%- endunless -%}{%- assign _line = _line | append: company_state -%}{%- endif -%} {%- if company_postal_code -%}{%- unless _line == '' -%}{%- assign _line = _line | append: ' ' -%}{%- endunless -%}{%- assign _line = _line | append: company_postal_code -%}{%- endif -%} {%- unless _line == '' -%}{{ _line }}
{%- endunless -%} {%- if company_country -%}{{ company_country }}{%- endif -%} {%- endif -%}
{{ heading_key | t: order_number: order.reference }}
- Reference
- {{ order.reference }}
- Date
- {{ order.ordered_at | date: "%-d %b %Y" }}
{%- endunless -%} ```
Step 3 — Update the account page template
Open templates/pages/account and add the print snippet render at the top of the file, passing the section, identifier, and print params. Your existing section routing continues to work as before.
```liquid
{%- layout “account” %}
{%- assign print = current_request.params.print | downcase -%} {%- assign section = current_request.params.section -%} {%- assign identifier = current_request.params.identifier -%}
{% render “account/print”, section: section, identifier: identifier, print: print %}
{%- case section -%} {%- when “orders” -%} {% render “account/orders”, identifier: identifier %} {%- when “fulfillments” -%} {% render “account/fulfillments”, identifier: identifier %} {%- when “subscriptions” -%} {% render “account/subscriptions”, identifier: identifier %} {%- when “account_credits” -%} {%- if current_customer.can_use_account_credit? -%} {% render “account/account_credits”, identifier: identifier %} {%- else -%} {% render “account/not_authorised” %} {%- endif -%} {%- when “account_points” -%} {%- if current_customer.can_use_account_points? -%} {% render “account/account_points”, identifier: identifier %} {%- else -%} {% render “account/not_authorised” %} {%- endif -%} {%- when “product_approvals” -%} {% render “account/product_approvals”, identifier: identifier %} {%- when “credentials” -%} {% render “account/credentials” %} {%- when “contact” -%} {% render “account/contact” %} {%- when “shipping” -%} {% render “account/shipping” %} {%- when “billing” -%} {% render “account/billing” %} {%- else -%} {% render “account/profile” %} {%- endcase -%} ```
Step 4 — Add invoice and receipt links to the order page
To give customers a way to trigger printing, add links to your snippets/orders/show template (or wherever you display individual order details). Use order.path as the base URL:
```liquid
{{ ‘accounts.orders.invoice.heading’ | t: order_number: order.reference }}
{{ ‘accounts.orders.receipt.heading’ | t: order_number: order.reference }} ```
Set up store variables
In Salesforce, go to your store record and create the following store variables. Set Available in Liquid to true for each one.
| Key | Description |
|---|---|
company_name |
Your business name, shown in the invoice header |
company_number |
Business registration number (e.g., ABN, GST number) |
company_address |
Full address as a single multiline field |
invoice_payment_instructions |
Bank transfer details or other payment instructions, shown only on unpaid invoices |
If you prefer separate address fields, use company_street, company_city, company_state, company_postal_code, and company_country instead of company_address.
Testing
- Log in as a customer who has at least one order.
- Navigate to My Account → Orders and open an order.
- Append
?&print=invoiceor?&print=receiptto the URL. - A popup should open and auto-trigger the browser print dialog.
- Save as PDF — the browser suggests a filename based on the page title (e.g.,
Invoice 00000134.pdf).
:::tip
If the popup is blocked, your browser’s popup blocker is preventing window.open(). Allow popups for your store domain and try again.
:::
Security
The print snippet only accesses orders belonging to the currently logged-in customer. It loops through current_customer.orders and matches on reference, so customers cannot access each other’s order data.
Was this article helpful?
Thanks for your feedback! It helps us improve our docs.