Sampai sini pembeli udah bisa nemuin produk, milih varian, dan bayar. Bab ini soal hubungan merchant sama pembelinya: login ke akun, ngehubungin merchant, dan setuju nerima email.
Interaksi sama pelanggan itu bagian penting bisnis: persetujuan email marketing buat ngubah pembeli jadi pelanggan tetap, form kontak biar pembeli bisa nanya, dan akun pelanggan biar mereka bisa lihat pesanan dan checkout lebih cepat. Tiga-tiganya punya pola Liquid yang sederhana tapi punya aturan yang harus tepat.
Sign-in dan Redirect
Shopify punya dua generasi akun pelanggan: legacy (login pakai password, halaman akun dirender theme lewat template customers/* dari 24 Template per Halaman) dan customer accounts baru (login tanpa password lewat kode email, halaman akun di-host Shopify, bisa diperluas lewat extension). Object routes dari 13 Objects Global > routes ngasih URL yang bener buat dua-duanya, jadi jangan pernah hardcode /account.
1. Dua Route Utama
routes.account_login_url: ke halaman sign-in, dan setelah berhasil ke halaman daftar pesanan di akun.routes.account_url: langsung ke halaman daftar pesanan (buat yang udah login).
Link akun standar di header, cek dulu shop.customer_accounts_enabled karena merchant bisa matiin akun pelanggan:
{%- if shop.customer_accounts_enabled -%}
{% if customer %}
<a href="{{ routes.account_url }}">Account</a>
{% else %}
<a href="{{ routes.account_login_url }}">Sign in</a>
{%- endif %}
{%- endif -%}Versi Dawn nambahin rel="nofollow" dan ikon:
{%- if shop.customer_accounts_enabled -%}
<a href="{%- if customer -%}{{ routes.account_url }}{%- else -%}{{ routes.account_login_url }}{%- endif -%}"
class="header__icon header__icon--account link focus-inset{% if section.settings.menu != blank %} small-hide{% endif %}"
rel="nofollow">
</a>
{%- endif -%}2. Balik ke Storefront Setelah Login
Bawaannya, setelah login pembeli dibawa ke akun. Kadang itu bukan yang mereka mau: mereka lagi lihat katalog, klik sign-in biar dapat harga member, dan pengen balik ke katalog. Buat itu pakai routes.storefront_login_url: setelah login, pembeli balik ke halaman asal sign-in-nya:
{%- if shop.customer_accounts_enabled -%}
<a href="{%- if customer -%}{{ routes.account_url }}{%- else -%}{{ routes.storefront_login_url }}{%- endif -%}"
class="header__icon header__icon--account link focus-inset{% if section.settings.menu != blank %} small-hide{% endif %}"
rel="nofollow">
</a>
{%- endif -%}Polanya: belum login → storefront_login_url (balik ke storefront), udah login → account_url (ke akun). Jadi klik pertama login dan balik, klik kedua ke akun. Bisa juga dipakai di link sign-in manapun:
<a href="{{ routes.storefront_login_url }}">Sign in</a>Catatan Shopify: kalau kamu ngeganti link sign-in bawaan dengan yang balik ke storefront, sediain link lain yang ngebawa pelanggan ke akun dan pesanannya, biar mereka gak kehilangan akses ke situ.
3. Redirect ke Halaman Tertentu
Buat redirect ke halaman manapun di storefront setelah login, pakai path /customer_authentication/login dengan parameter return_to:
<a href="/customer_authentication/login?return_to={{ "/pages/contact" | url_encode }}">Contact</a>return_to cuma nerima URL relatif, dan nilainya di-url_encode biar / gak ngerusak query string. Berguna buat “Login untuk lihat wishlist” yang balik ke halaman wishlist.
4. Prefill Email: login_hint
Kalau kamu udah tau email pembeli (misalnya dari form yang baru mereka isi), bikin link sign-in yang udah ngisi emailnya lewat parameter login_hint. Pembeli tinggal ngecek dan lanjut:
<a href="{{ routes.storefront_login_url | append: '&login_hint=' | append: customer.email | url_param_escape }}">Sign in</a>url_param_escape di akhir ngamanin karakter @ dan lainnya. Perhatiin & bukan ?, karena storefront_login_url udah punya query parameter (return_to).
5. Link ke Halaman Akun Tertentu
Buat link ke halaman Profile atau full-page extension akun, kamu nempelin path ke routes.account_url. Tapi ada jebakan: routes.account_url kadang mengandung query parameter (misalnya setelah login berhasil). Kalau kamu langsung append: '/profile', hasilnya /account?foo=bar/profile yang rusak.
Solusinya: pisah di ?, tempelin path ke bagian pertama, terus pasang lagi query-nya di akhir:
{% assign url_parts = routes.account_url | split: '?' %}
{% assign base_url = url_parts | first %}
<a href="{{ base_url }}/profile{% if url_parts.size > 1 %}?{{ url_parts.last }}{% endif %}">Account Profile</a>Buat extension (app yang nambahin halaman ke akun pelanggan):
{% assign url_parts = routes.account_url | split: '?' %}
{% assign base_url = url_parts | first %}
<a href="{{ base_url }}/pages/extensionId{% if url_parts.size > 1 %}?{{ url_parts.last }}{% endif %}">Account Extension</a>Pola ini selalu ngasilin link valid, ada query parameter atau gak.
❌ rusak kalau account_url punya query
<a href="{{ routes.account_url }}/profile">Profile</a>✅ dipisah dulu
{% assign url_parts = routes.account_url | split: '?' %}
<a href="{{ url_parts | first }}/profile{% if url_parts.size > 1 %}?{{ url_parts.last }}{% endif %}">Profile</a>Komponen Akun: shopify-account
Cara paling modern buat sign-in di theme: web component <shopify-account> (bagian dari Storefront Web Components). Dia nampilin avatar pelanggan di theme, dan waktu diklik, buka account sheet (panel) berisi link menu. Buat yang belum login, opsi sign-in tampil di atas menu.
Tujuannya ngedorong sign-in biar checkout lebih cepat dan storefront bisa dipersonalisasi. Mendukung passwordless sign-in, sign-in otomatis lewat Shop (kalau pembeli dikenali dari app Shop), dan social sign-in. Pembeli juga bisa opt-in email marketing waktu sign-in lewat komponen ini.
Yang bikin beda dari link biasa: pembeli sign-in dan tetap di storefront, gak dialihin ke akun. Account sheet nampilin link kaya Orders dan Profile di depan, jadi pembeli tau apa yang bisa mereka akses sebelum sign-in.
Avatar-nya tergantung state:
- Belum login: ikon akun bawaan, bisa dikustomisasi lewat slot
signed-out-avatar. - Udah login: inisial pelanggan atau foto profil Shop.
Kalau toko pakai customer accounts baru, klik avatar buka account sheet. Kalau legacy, avatar cuma link ke halaman sign-in. Dan penting: Shopify yang ngontrol komponen ini dan bisa ngupdate-nya independen dari theme. Kamu bisa styling dasar, tapi tampilannya konsisten antar toko.
1. Pasang di Header
Tambahin ke header, pastiin kelihatan di mobile dan desktop:
{% if shop.customer_accounts_enabled %}
<shopify-account menu="customer-account-main-menu">
</shopify-account>
{% endif %}Atribut menu nunjuk ke handle menu yang link-nya ditampilin di sheet.
2. Kustomisasi Avatar Belum Login
Dua cara: styling lewat CSS part, atau ganti markup-nya lewat slot:
<style>
shopify-account::part(signed-out-avatar) {
padding: 12px;
width: 64px;
height: 64px;
}
</style>
<shopify-account menu="customer-account-main-menu">
<div slot="signed-out-avatar">
<img src="{{ 'account-icon.svg' | asset_url }}" alt="Account"/>
</div>
</shopify-account>::part() itu cara resmi nembus shadow DOM buat bagian yang diizinin komponennya, beda sama tombol accelerated checkout di 43 Accelerated Checkout yang shadow DOM-nya tertutup total.
3. Kustomisasi Avatar Login dan Sheet
Avatar login dan account sheet dikontrol komponen. Kustomisasinya lewat CSS variable:
<style>
shopify-account {
--shopify-account-signed-in-avatar-size: 36px;
--shopify-account-signed-in-avatar-color-background: fuchsia;
--shopify-account-signed-in-avatar-color-text: black;
}
</style>
<shopify-account menu="customer-account-main-menu">
</shopify-account>Daftar lengkap variabelnya ada di dokumentasi shopify-account.
4. Menu Lewat Setting
Kasih theme setting biar merchant milih menu yang dipakai:
<shopify-account menu="{{ section.settings.customer_account_menu }}">
</shopify-account>{
"type": "link_list",
"id": "customer_account_menu",
"label": "Customer account menu",
"default": "customer-account-main-menu"
}Hati-hati: link_list dari 30 Settings dan Input Settings > 9. link_list cuma nerima default main-menu dan footer secara resmi, jadi kalau customer-account-main-menu ditolak, hapus default-nya dan biarin merchant milih. Pakai customer-account-main-menu (menu bawaan Shopify buat akun, berisi Orders dan Profile) bikin link konsisten antara komponen dan halaman akun. Merchant ngaturnya di Content > Menus.
Form Kontak
Form biar pembeli bisa ngehubungin merchant, kirimannya masuk ke email merchant dan bisa dilihat di admin. Dibikin pakai {% form 'contact' %} dari 12 Tags Khusus Theme > 3. contact:
{% form 'contact' %}
{{ form.errors | default_errors }}
<div class="first-name">
<label for="first-name">First name</label>
<input type="text" name="contact[first_name]" id="first-name" />
</div>
<div class="last-name">
<label for="last-name">Last name</label>
<input type="text" name="contact[last_name]" id="last-name" />
</div>
<div class="phone">
<label for="phone">Phone</label>
<input type="tel" name="contact[phone]" id="phone" />
</div>
<div class="email">
<label for="email">Email</label>
<input type="email" name="contact[email]" id="email" />
</div>
<div class="order-number">
<label for="order-number">Order number</label>
<input type="text" name="contact[order_number]" id="order-number" />
</div>
<div class="message">
<label for="message">Message</label>
<textarea name="contact[body]" id="message"></textarea>
</div>
<div class="submit">
<input type="submit" value="Create" />
</div>
{% endform %}{{ form.errors | default_errors }} nampilin error validasi (email kosong, format salah) pakai filter dari 19 Filters HTML, URL, Media, Color, dan Font > 1. default_errors. Contoh Dawn di sections/contact-form.liquid.
1. Input Wajib
Cuma satu yang wajib biar form bisa disubmit: Email, type="email", name="contact[email]".
2. Input Opsional
Input opsional bisa tipe HTML apapun, dengan name="contact[information_id]", di mana information_id itu identifier singkat buat info yang dikumpulin. Nama ini muncul di notifikasi email merchant sebagai label, dan harus unik dalam satu form. Jadi contact[order_number] jadi “Order number” di email merchant.
Buat mewajibkan field tertentu ke pembeli, tambahin required="required" di input-nya (validasi browser, bukan Shopify).
Dropdown:
<div class="request-type">
<label for="request-type">What is this regarding?</label>
<select id="request-type" name="contact[request_type]">
<option>Returns</option>
<option>Shipping</option>
<option>Custom order</option>
<option>Other</option>
</select>
</div>Radio:
<div class="contact-method">
<label for="contact-method">How do you want us to contact you?</label>
<input type="radio" name="contact[contact_method]" value="email" id="email" /><label for="email">Email</label><br />
<input type="radio" name="contact[contact_method]" value="phone" id="phone" /><label for="phone">Phone</label><br />
<input type="radio" name="contact[contact_method]" value="text message" id="text" /><label for="text">Text message</label>
</div>Checkbox, dengan jebakan: buat nerima banyak pilihan, tiap checkbox harus punya name unik. Kalau namanya sama, form cuma ngembaliin nilai terakhir yang dipilih:
<div class="contact-time">
<label for="contact-time">When is the best time to reach you?</label>
<input type="checkbox" name="contact[contact_time_1]" value="morning" /><label for="morning">Morning</label><br />
<input type="checkbox" name="contact[contact_time_2]" value="afternoon" /><label for="afternoon">Afternoon</label><br />
<input type="checkbox" name="contact[contact_time_3]" value="evening" /><label for="evening">Evening</label>
</div>❌ cuma “evening” yang sampai ke merchant
<input type="checkbox" name="contact[contact_time]" value="morning">
<input type="checkbox" name="contact[contact_time]" value="afternoon">
<input type="checkbox" name="contact[contact_time]" value="evening">✅ semua sampai
<input type="checkbox" name="contact[contact_time_1]" value="morning">
<input type="checkbox" name="contact[contact_time_2]" value="afternoon">
<input type="checkbox" name="contact[contact_time_3]" value="evening">Form kontak juga otomatis dilindungi CAPTCHA kalau merchant nyalain, dibahas di 50 Trust & Security CAPTCHA dan Security Badges.
Persetujuan Email Marketing
Pembeli bisa setuju nerima email marketing lewat form newsletter di theme (biasanya di footer). Dibikin pakai {% form 'customer' %} dari 12 Tags Khusus Theme > 4. customer, dengan satu input wajib type="email" dan name="contact[email]":
{% form 'customer' %}
<div class="email">
<label for="email">Email</label>
<input type="email" name="contact[email]" />
</div>
<div class="submit">
<input type="submit" value="Sign up" />
</div>
{% endform %}Waktu pembeli daftar lewat form ini, Shopify bikin customer dengan email itu dan nge-set accepts_marketing di object customer (16 Objects Cart, Checkout, Order, dan Customer > customer) jadi true. Kalau emailnya udah ada, customer-nya diupdate. Contoh Dawn ada di sections/footer.liquid.
Yang perlu ditambahin di praktiknya: form.posted_successfully? buat nampilin pesan sukses dan form.errors | default_errors buat error, plus hidden input contact[tags] kalau merchant mau nandain pelanggan dari newsletter:
{% form 'customer' %}
{% if form.posted_successfully? %}
<p>Thanks for subscribing!</p>
{% else %}
{{ form.errors | default_errors }}
<input type="hidden" name="contact[tags]" value="newsletter">
<label for="newsletter-email">Email</label>
<input type="email" id="newsletter-email" name="contact[email]" required>
<button type="submit">Sign up</button>
{% endif %}
{% endform %}Ingat bahasan 24 Template per Halaman: form yang sama dipakai di halaman password buat ngumpulin email sebelum toko buka, dengan tag prospect dan password page.
Ringkasan
- Jangan hardcode URL akun, pakai
routes:account_login_url(login lalu ke pesanan),account_url(langsung ke pesanan),storefront_login_url(login lalu balik ke halaman asal). Selalu cekshop.customer_accounts_enabled. - Redirect ke halaman tertentu:
/customer_authentication/login?return_to=dengan URL relatif yang di-url_encode. Prefill email:&login_hint=denganurl_param_escape. - Link ke halaman akun/extension: pisah
routes.account_urldi?dulu, tempelin/profileatau/pages/extensionId, pasang lagi query-nya. <shopify-account menu="...">nampilin avatar dan account sheet (passwordless, Shop, social sign-in, opt-in email), pembeli tetap di storefront. Kustomisasi avatar belum login lewat::part(signed-out-avatar)atau slot, avatar login dan sheet lewat CSS variable--shopify-account-*. Menu lewat settinglink_list, defaultcustomer-account-main-menu.- Form kontak
{% form 'contact' %}: wajibcontact[email], opsionalcontact[apapun]dengan nama unik yang jadi label di email merchant,requiredbuat validasi browser, dan checkbox harus bedanamebiar semua nilai terkirim. - Form newsletter
{% form 'customer' %}dengancontact[email]: bikin/update customer denganaccepts_marketing = true. Tambahinposted_successfully?,default_errors, dancontact[tags].
Bab selanjutnya kita bahas jualan lintas negara: Shopify Markets, form localization buat pemilih negara dan bahasa, konversi mata uang dan pembulatan, URL per locale, dan object localization.