and runs during HTML parse, but hubspotutk is set by js.hs-analytics.net, measured loading at ~834ms. On a first visit the cookie does not exist yet, so a single read returns nothing and the visitor gets no external ID at all — and a first visit is exactly what an ad click is. So poll briefly until HubSpot sets it. The SDK loads lazily (~4.5s) and replays a queue, so an init queued a second or two in still lands before any events are transmitted. */ (function attachExternalId() { var attempts = 0; var MAX_ATTEMPTS = 40; /* 40 x 200ms = 8s, then give up quietly */ var timer = null; function tryAttach() { var raw = getExternalId(); if (!raw) { if (++attempts >= MAX_ATTEMPTS && timer) clearInterval(timer); return; } if (timer) clearInterval(timer); sha256Hex(raw).then(function (hash) { oaiq("init", { user: { external_id_sha256: hash } }); }).catch(function () {}); } tryAttach(); /* returning visitor: immediate */ if (!getExternalId()) timer = setInterval(tryAttach, 200); })(); /* --------------------------------------------------------------------------- 2. Load the SDK off the critical path Whichever comes first: the main thread going idle after load, or the first sign of real user intent. No preconnect on purpose — a TLS handshake during LCP for a script we deliberately deferred is a net loss. Measured on clickpost.ai: load event 3614ms, SDK fetched 4508ms. ------------------------------------------------------------------------ */ var loaded = false; var INTENT = ["pointerdown", "keydown", "scroll"]; function loadSdk() { if (loaded) return; loaded = true; INTENT.forEach(function (t) { removeEventListener(t, loadSdk, { passive: true }); }); var s = document.createElement("script"); s.src = SDK; s.async = true; s.fetchPriority = "low"; document.head.appendChild(s); } INTENT.forEach(function (t) { addEventListener(t, loadSdk, { passive: true, once: true }); }); addEventListener("load", function () { window.requestIdleCallback ? requestIdleCallback(loadSdk, { timeout: 2500 }) : setTimeout(loadSdk, 1500); }, { once: true }); /* --------------------------------------------------------------------------- 3. Conversion id sha256(lowercase(email) + "|" + formId + "|" + ISO-hour), first 20 hex, "lead_" prefix. - email lowercased/trimmed so casing does not fork the id - formId keeps two different forms in the same hour from colliding - the hour bucket is only the server's fallback; the id computed here is sent to the relay verbatim, so the two paths cannot disagree even when a submit straddles an hour boundary Ads Manager's own sample omits event_id. We send it so the pixel event and the relay event collapse into one conversion instead of counting twice. ------------------------------------------------------------------------ */ function buildEventId(email, formId) { var seed = [ String(email || "").trim().toLowerCase(), String(formId || ""), new Date().toISOString().slice(0, 13) ].join("|"); return crypto.subtle.digest("SHA-256", new TextEncoder().encode(seed)).then(function (buf) { var hex = Array.prototype.map.call(new Uint8Array(buf), function (b) { return b.toString(16).padStart(2, "0"); }).join(""); return "lead_" + hex.slice(0, 20); }); } /* Guards a re-fire from the history.pushState that book-a-demo-core.js runs after submit, and from back-navigation onto the same state. */ function firedOnce(id) { try { if (sessionStorage.getItem("oaiq_" + id)) return true; sessionStorage.setItem("oaiq_" + id, "1"); return false; } catch (e) { return false; /* private mode / blocked storage: fire rather than lose it */ } } /* Path 2. keepalive lets the request finish even if the page navigates away. Failures are swallowed deliberately: a reporting call must never surface an error to someone who just submitted a form. The relay hashes the email server-side into user.emails_sha256 and adds the caller's IP and user agent, so this event carries the same match signals the pixel gets for free. */ function sendToRelay(email, formId, eventType, eventId) { try { var payload = { token: RELAY_TOKEN, email: email, form_id: formId || "", event_type: eventType, event_id: eventId, /* verbatim, so both paths always match */ source_url: location.href }; var oppref = getOppref(); if (oppref) payload.oppref = oppref; var externalId = getExternalId(); if (externalId) payload.external_id = externalId; fetch(RELAY_URL, { method: "POST", keepalive: true, headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }).catch(function () {}); } catch (e) {} } /* Capture the email while the form is still on the page. onFormSubmitted does not reliably carry submissionValues, and by then the form may be removed. */ var lastEmail = ""; document.addEventListener("submit", function (e) { var f = e.target; if (!f || !f.querySelector) return; var i = f.querySelector('input[name="email"], input[type="email"]'); if (i && i.value) lastEmail = i.value; }, true); /* Every HubSpot form on the site reports through this one message event, so no module or theme JS needs editing to add coverage. */ window.addEventListener("message", function (e) { var d = e.data; if (!d || d.type !== "hsFormCallback" || d.eventName !== "onFormSubmitted") return; var email = (d.data && d.data.submissionValues && d.data.submissionValues.email) || lastEmail; if (!email) return; /* without an email there is no stable id to dedupe on */ var eventType = EVENT_BY_FORM[d.id] || EVENT_DEFAULT; buildEventId(email, d.id).then(function (id) { if (firedOnce(id)) return; oaiq("measure", eventType, { type: DATA_TYPE }, { event_id: id }); /* path 1 */ sendToRelay(email, d.id, eventType, id); /* path 2 */ }); }); })();

91ÊÓÆµ

See where your logistics stands on automation Start assessment

Home > Blog >

Shopify Refund Policy Free Template

Shopify Refund Policy Free Template

Devaraj Mahantesh
By Devaraj Mahantesh

Ready to ship smarter? Talk to our logistics experts.

Trusted by 600+ brands

In this blog

    What is a Shopify Refund Policy Template?

    What is one of the most challenging aspects of being a Shopify store owner? If you guessed managing refunds and returns, you’re right on the money! Reverse logistics can be costly for both established and new eCommerce stores.

    Hence, it is vital to have a well-planned refund policy to ensure you start things on the right track. 

    This is where you can also use a Shopify refund policy template. You don’t need to put in any extra effort other than simply copying and pasting the entire template on your store.

    You can also customise the template to fit any additional requirements of your Shopify store. We’ll first visit the different factors that make up a good Shopify refund policy, steps on how you can create one and finally, the template itself!

    Essentials of A Good Shopify Return Policy

    Let’s first dive deep into the various essentials that make up a good Shopify refund policy. These points are crucial, especially if you’re looking to build a refund policy from scratch. 

    1. Optimal restriction on time

    Choosing a suitable time frame to allow refunds is difficult for any eCommerce store. On average, many online stores allow their customers to process refunds of their products till 30 days from the date of purchase.

    Another method online retailers use is giving full refunds only for 30 days and not any longer. This time limit works best in either case since it favours you and your customers.

    An extremely short refund time limit can turn off your customers before they even buy. On the other hand, a longer time limit can enable customers to ‘rent’ your products rather than buy them.

    2. Offer benefits with refunds

    Dealing with returns can be hard for your customers too. To ‘reward’ them for facing this trouble, you can provide extra benefits with refunds. For instance, you can give store credits valid for a specific period.

    Even something as simple as a discount code can push your customers to come and buy back from you again.You can also explore apps to surprise customers with small complimentary products when they request a refund, turning a negative experience into brand loyalty. If you make your customers’ returns process memorable, you can ensure they don’t abandon your brand.

    3. Generous T&C

    Over 40% of all customers visit the returns policy page before deciding to purchase. What does this indicate? It tells you that your first impression is your last.

    If your policy has too many complicated conditions, your consumers won’t bother dealing with you in the first place.

    This is true mainly for softline businesses such as fashion. Keep your conditions clear and easy for consumers to accept and avoid any stinginess for refunds. 

    4. Easy to understand language

    How many times have you looked at a refund policy and questioned what half of it even meant? Customers can get confused due to the overwhelming language that brands can use to sound ‘professional’.

    In reality, being simple and using straightforward language helps your customers be confident that they have understood your refund policy.

    Having easy language in your policy will also help reduce any chance of misconceptions that your customers may have while reading. 

    How to Create An Amazing Shopify Refund Policy

    Now that we know what a good refund policy should consist of, it’s time to see how you can create a fantastic Shopify refund policy yourself. 

    1. Ensure your policy is visible easily

    The first order of business is to make sure your refund policy is visible easily. We do not want to confuse customers and make them visit hundreds of other pages before accessing your policy.

    You can choose to do what many modern-day eCommerce brands practice: place the policy at the bottom. This is where all the different call-to-actions, such as ‘contact us, ‘follow us, etc., come in.

    Making your refund policy easy to navigate will help customers save time. 

    2. Be clear with words

    As mentioned earlier, confusing your patrons with heavy-worded conditions can have the opposite effect of pleasing them. If you have something to say, do it the easiest way possible. After all, your customers are humans, too, who prefer to undergo things straightforwardly. 

    3. Avoid too many legal terms

    Another part many Shopify store owners may miss is upsetting visitors with legal terms galore. Not every customer may necessarily know the meaning behind every legal word that you may use in your refund policy. Keep the legal terms that are vital and remove any unnecessary ones. 

    4. Make your customer take action

    So you’ve made the best policy, and your customer goes through it reasonably quickly, but what’s next? You cannot leave them hanging right at the very end.

    Towards the end, you should direct your customer on the next step they should take if they wish to return their product. This will lead to customers needing a return/refund contacting your customer support and adding to unnecessary work pressure. 

    Shopify Refund Policy Template 

    Here’s a Shopify refund policy template you can use in your store. You can customise the ‘[]’ brackets and add the required information. 

    At [Brand’s name], we value our customers more than anything. If you’re unhappy with any product, you can return it to us! For refunds, all items bought at [Store’s website] returned within [Time] days from the day of purchase are eligible for a full refund. All items that are returned must be unused and should contain all of the original packaging and tags [Additional refund requirements]. 

    Some products are not eligible for returns, such as [Any product that is not eligible for return]. To return a product you are dissatisfied with, you can visit our [Returns portal, form, etc.] here. 

    We strive to give you the best Shopify experience! For any further assistance, feel free to contact us at [Customer support email/phone number], and we’ll be glad to help you out!

    How ClickPost Return Plus Helps eCommerce Brands

    If you’re looking to upgrade the way your Shopify store processes returns, then look no further! With the ClickPost Return Plus Shopify app, you can track all of your returns from a single dashboard. You can also create your self-serve return portal for all customers and receive 24/7 support!

    You can check out the ClickPost Return Plus App here. 

    Conclusion

    To wrap up, making a short, crisp and simple refund policy is the best way to show your customers how you process returns and refunds. You can look at the various elements we’ve mentioned and understand how one can make their refund policy stand out from others!

    FAQS

    1. Is having a return/refund policy important on Shopify?

    Yes. Without a return policy, your customers will not be able to know the terms and conditions your store follows for returns. 

    2. Can I make a Shopify refund policy on my own?

    Absolutely! You can also use our template as a reference point to get started. While writing, remember to keep all the essentials of a good Shopify refund policy in mind.

    The Logistics Automation Platform for E-commerce

    Momentum Leader - G2 Users Most Likely To Recommend - G2 Best Results Mid-Market - G2
    . window.disableHubSpotCookieBanner = true; function killNative() { var n = document.getElementById("hs-eu-cookie-confirmation"); if (n && n.parentNode) { n.parentNode.removeChild(n); return true; } return false; } killNative(); if (window.MutationObserver) { var mo = new MutationObserver(function () { killNative(); }); mo.observe(document.documentElement, { childList: true, subtree: true }); setTimeout(function () { mo.disconnect(); }, 15000); } var COOKIE = "cp_cookie_consent"; var EU = ["at","be","bg","hr","cy","cz","dk","ee","fi","fr","de","gr","hu","ie","it","lv","lt","lu","mt","nl","pl","pt","ro","sk","si","es","se","gb","uk","is","li","no","ch"]; function readCookie(nm) { var m = document.cookie.match(new RegExp("(?:^|; )" + nm + "=([^;]*)")); return m ? decodeURIComponent(m[1]) : null; } function writeCookie(nm, val, days) { var d = new Date(); d.setTime(d.getTime() + (days * 864e5)); document.cookie = nm + "=" + encodeURIComponent(val) + ";expires=" + d.toUTCString() + ";path=/;SameSite=Lax" + (location.protocol === "https:" ? ";Secure" : ""); } function stored() { var raw = readCookie(COOKIE); if (!raw) return null; try { var o = JSON.parse(raw); if (typeof o.analytics === "boolean") return o; } catch (e) {} return null; } function apply(state, revoke) { window._hsp = window._hsp || []; window._hsp.push(["setHubSpotConsent", { analytics: !!state.analytics, advertisement: !!state.advertisement, functionality: !!state.functionality }]); if (revoke && !state.analytics && !state.advertisement && !state.functionality) { window._hsp.push(["revokeCookieConsent"]); } window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "cp_cookie_consent", cp_consent_analytics: !!state.analytics, cp_consent_advertisement: !!state.advertisement, cp_consent_functionality: !!state.functionality }); document.documentElement.setAttribute("data-cp-consent", "set"); } function inScope(banner) { if (banner.getAttribute("data-scope") !== "eu_only") return true; var geo = (document.body.getAttribute("data-geo_location") || "").toLowerCase(); if (!geo) return true; return EU.indexOf(geo) !== -1; } function init(banner) { var days = parseInt(banner.getAttribute("data-days"), 10) || 180; var prefs = banner.querySelector("[data-cpcb-prefs]"); var compact = banner.querySelector('[data-cpcb-actions="compact"]'); var expanded = banner.querySelector('[data-cpcb-actions="expanded"]'); var toggles = banner.querySelectorAll("[data-cpcb-toggle]"); function show() { banner.hidden = false; requestAnimationFrame(function () { banner.classList.add("is-visible"); }); } function hide() { banner.classList.remove("is-visible"); setTimeout(function () { banner.hidden = true; }, 300); } function collapse() { if (!prefs) return; prefs.hidden = true; if (expanded) expanded.hidden = true; if (compact) compact.hidden = false; } function expand() { if (!prefs) return; prefs.hidden = false; if (compact) compact.hidden = true; if (expanded) expanded.hidden = false; } function paint(state) { Array.prototype.forEach.call(toggles, function (t) { var key = t.getAttribute("data-cpcb-toggle"); if (key === "necessary") { t.setAttribute("aria-checked", "true"); return; } t.setAttribute("aria-checked", state[key] ? "true" : "false"); }); } function fromToggles() { var state = { analytics: false, advertisement: false, functionality: false }; Array.prototype.forEach.call(toggles, function (t) { var key = t.getAttribute("data-cpcb-toggle"); if (key in state) state[key] = t.getAttribute("aria-checked") === "true"; }); return state; } function commit(state, revoke) { writeCookie(COOKIE, JSON.stringify({ analytics: !!state.analytics, advertisement: !!state.advertisement, functionality: !!state.functionality, v: 1, ts: Date.now() }), days); apply(state, revoke); hide(); } Array.prototype.forEach.call(toggles, function (t) { if (t.disabled) return; t.addEventListener("click", function () { t.setAttribute("aria-checked", t.getAttribute("aria-checked") === "true" ? "false" : "true"); }); }); banner.addEventListener("click", function (e) { if (!e.target || !e.target.closest) return; var btn = e.target.closest("[data-cpcb-action]"); if (!btn) return; var action = btn.getAttribute("data-cpcb-action"); if (action === "customise") { expand(); return; } if (action === "accept") { commit({ analytics: true, advertisement: true, functionality: true }, false); return; } if (action === "reject") { commit({ analytics: false, advertisement: false, functionality: false }, true); return; } if (action === "save") { commit(fromToggles(), true); return; } }); var existing = stored(); if (existing) { apply(existing, false); paint(existing); collapse(); } else if (!inScope(banner)) { // Out of the consent-required region: implied consent, banner never shown. commitSilently(); } else { collapse(); show(); } function commitSilently() { var all = { analytics: true, advertisement: true, functionality: true }; writeCookie(COOKIE, JSON.stringify({ analytics: true, advertisement: true, functionality: true, v: 1, ts: Date.now(), implied: true }), days); apply(all, false); } // Public re-open hook for a footer "Cookie settings" link. window.cpCookieBanner = window.cpCookieBanner || {}; window.cpCookieBanner.open = function () { var s = stored() || { analytics: false, advertisement: false, functionality: false }; paint(s); expand(); show(); }; } function boot() { var banners = document.querySelectorAll("[data-cpcb]"); if (!banners.length) return; init(banners[0]); document.addEventListener("click", function (e) { if (!e.target || !e.target.closest) return; var trigger = e.target.closest("[data-cp-cookie-settings]"); if (!trigger) return; e.preventDefault(); if (window.cpCookieBanner && window.cpCookieBanner.open) window.cpCookieBanner.open(); }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } })();