{"title":"Collect customer signature at checkout","slug":"collect-an-e-signature-at-checkout","url":"https://support.storeconnect.com/articles/collect-an-e-signature-at-checkout","url_markdown":"https://support.storeconnect.com/articles/collect-an-e-signature-at-checkout.md","subtitle":null,"summary":"Add a digital signature capture field to your checkout form using a custom theme snippet and JavaScript. The signature is stored as a Base64 PNG in a Salesforce custom form answer.","type":"Help_Documentation","video_url":"","keywords":"e-signature, digital signature, checkout form, custom form, signature capture, terms acceptance, base64, theme snippet, javascript, form question, salesforce flow, checkout customization","last_modified":"2026-08-21T07:12:35+0000","body_markdown":"To collect an e-signature during the checkout process, you'll need to integrate a custom form that allows customers to digitally sign an agreement. This feature is useful for terms acceptance, donation authorization, or any other legal acknowledgment required before payment.\n\nBelow are the procedure steps to successfully configure and render this feature using StoreConnect and Salesforce.\n\n## Before you begin\n\n-   Check that you are using StoreConnect **version 19 or later**.\n\n\n## Set up a checkout form with an e-signature\n\n### Add a checkout form\n\n1.  In Salesforce, go to **App Launcher** and search **Forms**.\n2.  Select the **Form Type** as **Checkout Form**.\n3.  Give the form a **Name** and select **Save**. \n\nSee the [Checkout forms](https://support.getstoreconnect.com/article/Checkout-Form \"Checkout forms\") topic for more information.\n\n### Add the e-signature form question\n\n1.  In the **Related** tab of the new form, select **New** in the **Form Questions** section.\n2.  Set the **Data Type** as **Text.** (This is required because the signature is saved as a Base64 PNG string.)\n3.  Set the **Required** field as **True**.\n4.  Set the **Hidden** field as **True**.\n5.  Copy this markdown code and paste it in the **Question (Markdown)** field.\n    \n6.  Select **Save**.\n\nThis adds the signature field to the form in the checkout flow.\n\n### Add the e-signature snippet to your theme\n\nYou also need to update your theme, so that the signature block appears correctly on the checkout page.\n\n1.  Open the theme that you want to add the signature block to.\n2.  In the **Theme Template** section, select **New**.\n3.  Enter snippets/shared/e\\_signature as the **Key.**\n4.  Copy and paste the code below into the **Content** area of the template record, then select **Save**. Make sure to include the full code snippet.\n\n```javascript\n\n  E-Signature\n\n\n\n\n\n\n\n\n\n\n        Signature is required.\n\n\n        Signed!\n\n\n\n\n\n        Clear\n\n\n        Save\n\n\n\n\n\n\n\n    // Shared selector for submit buttons\n    const SC_SUBMIT_BUTTON_SELECTORS = [\n      'button[data-payment-info-submit]',\n      'button[data-disable-with]',\n      'button[type=\"submit\"]',\n      'input[type=\"submit\"]',\n      'button.SC-Button-primary'\n    ].join(', ');\n\n    // Initialise one signature block\n    function initSignatureBlock(signatureEl) {\n      if (!signatureEl || signatureEl.dataset.initialized === 'true') return;\n      signatureEl.dataset.initialized = 'true';\n\n      const signatureCanvas = signatureEl.querySelector('[data-signature-canvas]');\n      if (!signatureCanvas) return;\n\n      const signaturePad = new SignaturePad(signatureCanvas, {\n        backgroundColor: 'rgb(255,255,255)',\n        penColor: 'rgb(0,0,0)'\n      });\n\n      const signaturePreview = signatureEl.querySelector('[data-signature-preview]');\n      const signatureClear   = signatureEl.querySelector('[data-signature-clear]');\n      const signatureSave    = signatureEl.querySelector('[data-signature-save]');\n      const signatureSubmit  = signatureEl.querySelector('[data-signature-submit]');\n      const alertRequired    = signatureEl.querySelector('[data-signature-alert-required]');\n      const alertSigned      = signatureEl.querySelector('[data-signature-alert-signed]');\n\n      // Hide the backing answer input for THIS question\n      const container = signatureEl.closest('label, section');\n      const signatureAnswerInput = container\n        ? container.querySelector('input[name^=\"answers[\"][name$=\"][answer]\"]')\n        : null;\n      if (signatureAnswerInput) signatureAnswerInput.classList.add('sc-hide');\n\n      function showAlert(el) {\n        if (!el) return;\n        el.classList.remove('sc-hide');\n        setTimeout(() =\u003e el.classList.add('sc-hide'), 5000);\n      }\n\n      function hideAlerts() {\n        if (alertRequired) alertRequired.classList.add('sc-hide');\n        if (alertSigned)   alertSigned.classList.add('sc-hide');\n      }\n\n      // Clear button\n      signatureClear?.addEventListener('mousedown', () =\u003e {\n        signaturePad.clear();\n        if (signaturePreview) {\n          signaturePreview.classList.add('sc-hide');\n          signaturePreview.removeAttribute('src');\n        }\n        signatureCanvas.classList.remove('sc-hide');\n        signatureSave?.classList.remove('sc-hide');\n        signatureSubmit?.classList.add('sc-hide');\n        hideAlerts();\n        if (signatureAnswerInput) signatureAnswerInput.value = '';\n      });\n\n      // Save button\n      signatureSave?.addEventListener('click', () =\u003e {\n        hideAlerts();\n\n        if (signaturePad.isEmpty()) {\n          showAlert(alertRequired);\n          return;\n        }\n\n        const dataURL = signaturePad.toDataURL('image/png');\n\n        if (signaturePreview) {\n          signaturePreview.src = dataURL;\n          signaturePreview.classList.remove('sc-hide');\n        }\n        signatureCanvas.classList.add('sc-hide');\n\n        signatureSave.classList.add('sc-hide');\n        signatureSubmit?.classList.remove('sc-hide');\n\n        if (signatureAnswerInput) {\n          signatureAnswerInput.value = dataURL;\n        }\n\n        showAlert(alertSigned);\n      });\n\n      // Require a signature on submit for THIS form\n      const form = signatureEl.closest('form');\n      if (form) {\n        form.addEventListener('submit', (e) =\u003e {\n          if (signaturePad.isEmpty() \u0026\u0026 !(signatureAnswerInput \u0026\u0026 signatureAnswerInput.value)) {\n            e.preventDefault();\n            showAlert(alertRequired);\n          }\n        });\n\n        // Move this signature block above this form's submit button\n        const submitBtn = form.querySelector(SC_SUBMIT_BUTTON_SELECTORS);\n        if (submitBtn \u0026\u0026 !signatureEl.dataset.moved) {\n          submitBtn.parentNode.insertBefore(signatureEl, submitBtn);\n          signatureEl.dataset.moved = 'true';\n        }\n      }\n    }\n\n    // Initialise all signature blocks currently in the DOM\n    function initAllSignatures() {\n      document.querySelectorAll('.SC-Signature').forEach(initSignatureBlock);\n    }\n\n    if (document.readyState === 'loading') {\n      document.addEventListener('DOMContentLoaded', initAllSignatures);\n    } else {\n      initAllSignatures();\n    }\n\n    // Watch for payment provider DOM changes (tabs switching, etc.)\n    const observer = new MutationObserver(() =\u003e {\n      setTimeout(initAllSignatures, 50);\n    });\n    observer.observe(document.body, { childList: true, subtree: true });\n\n    window.addEventListener('beforeunload', () =\u003e observer.disconnect());\n\n    // Auto-click \"Save\" when any submit-like button is clicked\n    document.addEventListener('click', (e) =\u003e {\n      if (!e.target.closest(SC_SUBMIT_BUTTON_SELECTORS)) return;\n\n      document.querySelectorAll('.SC-Signature [data-signature-save]').forEach((btn) =\u003e {\n        if (!(btn instanceof HTMLButtonElement)) return;\n        btn.click();\n      });\n    });\n\n    if (window.location.pathname.includes('/account/orders')) {\n      function hideSignaturePanel() {\n        document.querySelectorAll('[data-e-signature]').forEach((field) =\u003e {\n          const panel = field.closest('.Panel');\n          if (panel) panel.classList.add('sc-hide');\n        });\n      }\n\n      if (document.readyState === 'loading') {\n        document.addEventListener('DOMContentLoaded', hideSignaturePanel);\n      } else {\n        hideSignaturePanel();\n      }\n\n      const observer = new MutationObserver(() =\u003e {\n        setTimeout(hideSignaturePanel, 100);\n      });\n      observer.observe(document.body, { childList: true, subtree: true });\n    }\n```\n\n## Preview the change in your store\n\nWe recommend checking the template element in your own store's checkout, to verify that it looks correct and works. Here's an example of what the signature block might look.\n\n![Store checkout showing e-signature field](https://res.cloudinary.com/hzkr6fi81/image/upload/v1763686945/knowledge/themes/Theme_AddEsignature_xxwmwt.png)\n\n## Manage e-signatures\n\n### The user experience\n\nThe user draws their signature in the E-Signature block area.\n\n-   To re-draw the signature, the user can click **Clear.**\n-   The form **cannot be submitted** unless a signature is saved.\n-   To save the signature, the user clicks **Save**. The signature image is stored as a Base64 PNG in Salesforce.\n-   If the user tries to submit their order without a signature, an error will show.\n-   If the user enter their signature and tries to submit the form, a background script automatically attempts to save the signature first.\n\n### Image storage and visibility\n\nWhen saved, the signature is stored in a hidden field in Salesforce and Base64 encoded.\n\n-   The underlying form’s hidden answer input is automatically filled.\n-   The signature UI is moved above the checkout submit button.\n\nIf you want to be able to view a customer's signature as an image in Salesforce, you need to create a Salesforce Flow to:\n\n-   Display the PNG image\n\n-   Store the signature elsewhere.\n\nIf you decide to set this up, the signature is saved as standard **Custom Form Answer** text data."}