# StoreConnect Support

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](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:

1. A snippet (`account/print`) reads the `print` param from the current request. If it matches `invoice` or `receipt`, it finds the matching order and opens a popup window.
2. The popup copies your theme's active stylesheets so the printable content inherits your store's fonts, colors, and layout.
3. A second snippet (`orders/printable`) renders the order content — company header, addresses, order items, payment summary — inside the popup.
4. The popup title is set to `Invoice 00000134` or `Receipt 00000134` so 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](order-liquid-object-reference).
- You have added the following locale keys to your [theme locales](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](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'] -%}

  <style>
    @page { size: A4; margin: 14mm; }

    .SCP-Root {
      max-width: 210mm;
      margin: 0 auto;
      padding: 16mm;
      box-sizing: border-box;
      background: #fff;
    }

    .SCP-Header {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      gap: 16px;
      margin-bottom: 24px;
    }

    .SCP-Brand { display: flex; align-items: center; gap: 12px; }
    .SCP-Logo  { max-height: 48px; display: block; }
    .SCP-DocTitle { margin: 0; }

    .SCP-Meta { display: grid; grid-template-columns: auto 1fr; gap: 4px 16px; margin: 12px 0 8px; }
    .SCP-Meta dt { font-weight: 600; margin: 0; }
    .SCP-Meta dd { margin: 0; }

    .SCP-Columns { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; }
    .SCP-Section { margin-top: 16px; }
    .SCP-Box { border: 1px solid; padding: 12px; border-radius: 8px; }
    .SCP-Muted { opacity: .8; }

    .SCP-Table { width: 100%; border-collapse: collapse; }
    .SCP-Table th,
    .SCP-Table td { padding: 8px; border-top: 1px solid; text-align: left; }
    .SCP-Table thead th { border-top: 0; }

    @media print {
      .SCP-Root { padding: 0; margin: 0; }
    }
  </style>

  <div class="SCP-Root">

    <div class="SCP-Header">
      <div class="SCP-Brand">
        {%- if current_store.logo and current_store.logo.url -%}
          <img class="SCP-Logo" src="{{ current_store.logo.url }}" alt="{{ current_store.name }}">
        {%- endif -%}
        {%- if company_name or company_number -%}
          <div>
            {%- if company_name -%}<strong>{{ company_name }}</strong>{%- endif -%}
            {%- if company_name and company_number -%}&nbsp;&middot;&nbsp;{%- endif -%}
            {%- if company_number -%}<span class="SCP-Muted">{{ company_number }}</span>{%- endif -%}
            {%- if company_address or company_street -%}
              <div class="SCP-Muted">
                {%- if company_address -%}
                  {{ company_address | newline_to_br }}
                {%- else -%}
                  {%- if company_street -%}{{ company_street | newline_to_br }}<br>{%- 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 }}<br>{%- endunless -%}
                  {%- if company_country -%}{{ company_country }}{%- endif -%}
                {%- endif -%}
              </div>
            {%- endif -%}
          </div>
        {%- endif -%}
      </div>

      <h1 class="SCP-DocTitle">{{ heading_key | t: order_number: order.reference }}</h1>
    </div>

    <dl class="SCP-Meta">
      <dt>Reference</dt>   <dd>{{ order.reference }}</dd>
      <dt>Date</dt>        <dd>{{ order.ordered_at | date: "%-d %b %Y" }}</dd>
    </dl>

    {%- comment -%} Payment instructions — only shown when no successful payment yet {%- endcomment -%}
    {%- assign has_success_payment = false -%}
    {%- for p in order.payments -%}
      {%- if p.status == 'success' -%}
        {%- assign has_success_payment = true -%}
        {%- break -%}
      {%- endif -%}
    {%- endfor -%}
    {%- if payment_instructions_raw and has_success_payment == false -%}
      <div class="SCP-Box SCP-Section">
        <strong>Payment instructions</strong>
        <div>{{ payment_instructions_raw | newline_to_br }}</div>
      </div>
    {%- endif -%}

    <div class="SCP-Columns SCP-Section">
      <div class="SCP-Box">
        <strong>Ship to</strong>
        {%- assign ship_contact = order.ship_to_contact -%}
        {%- if ship_contact.name -%}<div>{{ ship_contact.name }}</div>{%- endif -%}
        {%- assign ship_email = ship_contact.email | default: order.checkout_email -%}
        {%- if ship_email -%}<div>{{ ship_email }}</div>{%- endif -%}
        {%- if order.shipping_address -%}
          {%- render "shared/address_multi_line", address: order.shipping_address -%}
        {%- endif -%}
      </div>

      <div class="SCP-Box">
        <strong>Bill to</strong>
        {%- assign bill_contact = order.contact -%}
        {%- if bill_contact.name -%}<div>{{ bill_contact.name }}</div>{%- endif -%}
        {%- assign bill_email = bill_contact.email | default: order.checkout_email -%}
        {%- if bill_email -%}<div>{{ bill_email }}</div>{%- endif -%}
        {%- if order.billing_address -%}
          {%- render "shared/address_multi_line", address: order.billing_address -%}
        {%- endif -%}
      </div>
    </div>

    <div class="SCP-Section">
      {%- render "orders/order_summary", order: order, source: order -%}
    </div>

  </div>
{%- 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

<a href="{{ order.path }}&print=invoice">
  {{ 'accounts.orders.invoice.heading' | t: order_number: order.reference }}
</a>

<a href="{{ order.path }}&print=receipt">
  {{ 'accounts.orders.receipt.heading' | t: order_number: order.reference }}
</a>
```


## Set up store variables

In Salesforce, go to your store record and create the following [store variables](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

1. Log in as a customer who has at least one order.
2. Navigate to **My Account → Orders** and open an order.
3. Append `?&print=invoice` or `?&print=receipt` to the URL.
4. A popup should open and auto-trigger the browser print dialog.
5. 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.

---

## Follow StoreConnect

- [Email Newsletter](https://getstoreconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://getstoreconnect.com/partners)
- [News](https://getstoreconnect.com/articles/news)
- [Events](https://getstoreconnect.com/articles/events)
- [Feature Comparison](https://getstoreconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://getstoreconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

---

StoreConnect Support — https://support.storeconnect.com