Answers.Body: <p>The following code allows the customisation of the fields using <a href="https://stripe.com/docs/stripe-js" rel="noreferrer">Stripe Elements</a>. This code will replace the payment form with <code>gateway.getPaymentFormHtml({})</code>.</p>
  
  <p>The thing to note here is this is provided unsupported as it is custom code and does not use the payment form template or javascript file that is in the <a href="https://github.com/craftcms/commerce-stripe" rel="noreferrer">Commerce Stripe plugin</a>. This also means that it will not benefit from any updates to the plugin that change those files.</p>
  
  <pre><code>{% set cart = craft.commerce.carts.cart %}
  {% set gateway = cart.gateway %}
  {% set scenario = 'payment' %}
  {% set billingAddress = cart.billingAddress ?? null %}
  
  &lt;form method="POST" id="payment-form"&gt;
    &lt;input type="hidden" name="action" value="commerce/payments/pay"/&gt;
    {{ redirectInput('/shop/customer/order?number='~cart.number) }}
    &lt;input type="hidden" name="cancelUrl" value="{{ '/shop/checkout/payment'|hash }}"/&gt;
    &lt;input type="hidden" name="orderEmail" value="{{ cart.email }}"/&gt;
    &lt;input type="hidden" name="gatewayId" value="{{ cart.gatewayId }}"/&gt;
    {{ csrfInput() }}
  
    &lt;div class="stripe-payment-intents-form"
      {% if clientSecret is defined %}data-client-secret="{{clientSecret}}" {% endif %}
      {% if scenario is defined %}data-scenario="{{scenario}}" {% endif %}
    &gt;
      &lt;div&gt;
        &lt;input type="text"
               name='firstName'
               maxlength="70"
               placeholder="{{'First Name'|t('commerce')}}"
               autocomplete="false"
               class="card-holder-first-name{{(paymentForm is defined and paymentForm.getErrors('firstName') ? ' error')}}"
               value="{{paymentForm is defined and paymentForm ? paymentForm.firstName : ''}}"
        &gt;
        &lt;input type="text"
               name="lastName"
               maxlength=70",
               placeholder="{{'Last Name'|t('commerce')}}"
               autocomplete="false"
               class="card-holder-last-name{{ (paymentForm is defined and paymentForm.getErrors('lastName') ? ' error') }}"
               value="{{paymentForm is defined and paymentForm ? paymentForm.lastName : ''}}"
        &gt;
      &lt;/div&gt;
  
      {% set errors = paymentForm is defined and paymentForm ? paymentForm.getErrors() : [] %}
      {% if errors|length %}
        &lt;ul class="errors"&gt;
          {% for error in errors %}
            &lt;li&gt;{{ error|join('&lt;br&gt;')|raw }}&lt;/li&gt;
          {% endfor %}
        &lt;/ul&gt;
      {% endif %}
  
  
      &lt;div id="card-number"&gt;&lt;/div&gt;
      &lt;div id="card-expiry"&gt;&lt;/div&gt;
      &lt;div id="card-cvc"&gt;&lt;/div&gt;
  
      &lt;div id="card-errors" role="alert"&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;input type="submit"&gt;
  &lt;/form&gt;
  
  {% do view.registerJsFile('https://js.stripe.com/v3/') %}
  {% js %}
  // Create a Stripe client.
  var stripe = Stripe('{{ gateway.publishableKey ?? '' }}');
  
    // Create an instance of Elements.
    var elements = stripe.elements();
  
    // Custom styling can be passed to options when creating an Element.
    // (Note that this demo uses a wider set of styles than the guide below.)
    var style = {
      base: {
        color: '#000000',
        fontSize: '16px',
        '::placeholder': {
          color: '#CCCCCC'
        }
      },
      invalid: {
        color: '#fa755a',
        iconColor: '#fa755a'
      }
    };
  
    var classes = {
      base: 'StripeElement',
      complete: 'StripeElement--complete',
      empty: 'StripeElement--empty',
      focus: 'StripeElement--focus',
      invalid: 'StripeElement--invalid',
      webkitAutoFill: 'StripeElement--webkit-autofill'
    };
  
    var card = elements.create('cardNumber', {
      style: style,
      classes: classes
    });
    card.mount('#card-number');
  
    var expiry = elements.create('cardExpiry', {
      style: style,
      classes: classes
    });
    expiry.mount('#card-expiry');
  
    var cvc = elements.create('cardCvc', {
      style: style,
      classes: classes
    });
    cvc.mount('#card-cvc');
  
    // Handle real-time validation errors from the card Element.
    function cardErrors(event) {
      var displayError = document.getElementById('card-errors');
      if (event.error) {
        displayError.textContent = event.error.message;
      } else {
        displayError.textContent = '';
      }
    };
  
    card.addEventListener('change', cardErrors);
    expiry.addEventListener('change', cardErrors);
    cvc.addEventListener('change', cardErrors);
  
    // Handle form submission.
    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function(event) {
      event.preventDefault();
      var firstName = document.querySelector('.card-holder-first-name');
      var lastName = document.querySelector('.card-holder-last-name');
  
      var paymentData = {
        billing_details: {
          name: firstName.value + ' ' + lastName.value,
          email: '{{ cart.email }}',
          address: {
            line1: '{{ billingAddress.address1 ?? '' }}',
            city: '{{ billingAddress.city ?? '' }}',
            state: '{{ billingAddress.state ?? '' }}',
            postal_code: '{{ billingAddress.zipCode ?? '' }}',
            country: '{{ billingAddress.country.iso ?? '' }}'
          }
        }
      };
  
      stripe.createPaymentMethod('card', card, paymentData).then(function(result) {
        if (result.error) {
        // Inform the user if there was an error.
        var errorElement = document.getElementById('card-errors');
          errorElement.textContent = result.error.message;
        } else {
            var form = document.getElementById('payment-form');
            var hiddenInput = document.createElement('input');
            hiddenInput.setAttribute('type', 'hidden');
            hiddenInput.setAttribute('name', 'paymentMethodId');
            hiddenInput.setAttribute('value', result.paymentMethod.id);
            form.appendChild(hiddenInput);
  
            // Submit the form
            form.submit();
        }
      });
    });
  
  {% endjs %}
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-12-02T17:07:19.223Z
Answers.LastActivityDate: 2019-12-02T17:07:19.223Z
Answers.UserDisplayName: nfourtythree
Answers.Body: <p>OK so here's how to go about it.</p>
  
  <ol>
  <li>On checkout, you'll need at minimum a form with the following elements:</li>
  </ol>
  
  <pre class="lang-twig prettyprint-override"><code>&lt;form method="post" id="card-info"&gt;
  &lt;input type="hidden" name="action" value="commerce/payments/pay"&gt;
  &lt;input type="hidden" name="paymentMethodId" value="[fill this via JS]"&gt;
  &lt;input type="hidden" name="orderEmail" value="[your customer email address]"&gt;
  &lt;input type="hidden" name="gatewayId" value="[your chosen gateway id - add js to have the user select one if you have multiple gateaways available]"&gt;
  {{ redirectInput('/order?number='~cart.number) }}
  {{ csrfInput() }}
  &lt;input name="firstName" type="text" placeholder="First Name"&gt;
  &lt;input name="lastName" type="text" placeholder="Last Name"&gt;
  &lt;div id="card-data" class="border border-grey-lighter rounded bg-grey-lighter text-grey-darker p-3"&gt;&lt;/div&gt;
  &lt;button type="submit"&gt;Complete Purchase&lt;/button&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p>The real magic happens, of course, via JS.</p>
  
  <p>First, include the stripe library:
  <code>&lt;script src="https://js.stripe.com/v3/"&gt;&lt;/script&gt;</code></p>
  
  <p>Add this to your main JS file to display the stripe credit card input:</p>
  
  <pre class="lang-js prettyprint-override"><code>let stripe = Stripe(`your public sharing key`),
      elements = stripe.elements(),
      card = undefined;
  
  let style = {
      base: {
          // Add your base input styles here. For example:
          fontSize: '16px',
          color: '#32325d',
          // Attn: The number of usable styles is very limited. Refer to https://stripe.com/docs/stripe-js/reference#the-elements-object
      }
  }
  card = elements.create('card', {style: style});
  card.addEventListener('change', updateErrorMessage);
  card.mount(document.querySelector('card-data'));
  
  function updateErrorMessage (event) {
      if (event.error) {
          // Do something with event.error.message
      }
  }
  </code></pre>
  
  <p>Finally, you'll need to add code to make a request to the stripe API before the form is submitted, and fill in the value for the paymentMethodId hidden input. Refer to lines 66 to 94 here to get an idea how it's done: <a href="https://github.com/craftcms/commerce-stripe/blob/develop/src/web/assets/intentsform/js/paymentForm.js#L66" rel="nofollow noreferrer">https://github.com/craftcms/commerce-stripe/blob/develop/src/web/assets/intentsform/js/paymentForm.js#L66</a></p>
  
  <p>In addition, before you submit, make sure the cart has a valid value for "email". If you do a one page checkout like me, you may have to do an ajax post to /actions/commerce/cart/update-cart in order to save it first.</p>
  
  <p>I implemented all of this in vuejs, so the above is untested simplified code. On request I can share my vuejs version but I thought to better keep it framework agnostic in this answer.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-11-26T01:48:58.520Z
Answers.Text: Curious if anything has changed on this since it was posted? I cannot get this to work. Stripe shows no email address coming to it in the POST and I cannot fathom what the issue could be. Yes, I have an email on the cart and am posting it via `orderEmail`.
Answers.UserDisplayName: Mike
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-07-20T06:42:45.990Z
Answers.LastActivityDate: 2019-07-20T06:42:45.990Z
Answers.UserDisplayName: Udo
Body: <p>Where does one start with matching the style of the Stripe payment form to the rest of the site?</p>
  
  <p>Using <a href="https://github.com/craftcms/commerce-stripe" rel="nofollow noreferrer">Stripe for Craft Commerce Plug-In</a></p>
  
  <pre><code> {{ plan.getGateway().getPaymentFormHtml({})|raw }}
  </code></pre>
  
  <p>This enables the form with Card Holder info and credit card details.</p>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-07-17T10:07:55.743Z
Comments.Text: I couldn't find any docs about this at all..from what I gathered so far one has to  manually copy the HTML generated by getPaymentFormHtml()  to the checkout template, and maybe attach some JS to make it all work. Did you find out anything since you posted this?
Comments.UserDisplayName: Udo
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-07-18T21:36:54.207Z
Comments.Text: I have not made any progress on this but still interested in this issue.
Comments.UserDisplayName: SoHo
Tags: <plugin-craftcommerce2><stripe><plugin-commerce-stripe>
Title: Customizing Stripe payment form for Craft Commerce 2
ThreadText: # Customizing Stripe payment form for Craft Commerce 2
  
  (Asked by: username_0 on 2019-06-17)
  
  Where does one start with matching the style of the Stripe payment form to the rest of the site?
  
  Using [Stripe for Craft Commerce Plug-In](https://github.com/craftcms/commerce-stripe)
  
  ```
   {{ plan.getGateway().getPaymentFormHtml({})|raw }}
  
  ```
  
  This enables the form with Card Holder info and credit card details.
  
  **Tags:** plugin-craftcommerce2, stripe, plugin-commerce-stripe
  
  > **Comments:**
  > **username_1**: I couldn't find any docs about this at all..from what I gathered so far one has to  manually copy the HTML generated by getPaymentFormHtml()  to the checkout template, and maybe attach some JS to make it all work. Did you find out anything since you posted this?  
  > **username_0**: I have not made any progress on this but still interested in this issue.  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_2 on 2019-12-02. Score: 5
  
  The following code allows the customisation of the fields using [Stripe Elements](https://stripe.com/docs/stripe-js). This code will replace the payment form with `gateway.getPaymentFormHtml({})`.
  
  The thing to note here is this is provided unsupported as it is custom code and does not use the payment form template or javascript file that is in the [Commerce Stripe plugin](https://github.com/craftcms/commerce-stripe). This also means that it will not benefit from any updates to the plugin that change those files.
  
  ```
  {% set cart = craft.commerce.carts.cart %}
  {% set gateway = cart.gateway %}
  {% set scenario = 'payment' %}
  {% set billingAddress = cart.billingAddress ?? null %}
  
  <form method="POST" id="payment-form">
    <input type="hidden" name="action" value="commerce/payments/pay"/>
    {{ redirectInput('/shop/customer/order?number='~cart.number) }}
    <input type="hidden" name="cancelUrl" value="{{ '/shop/checkout/payment'|hash }}"/>
    <input type="hidden" name="orderEmail" value="{{ cart.email }}"/>
    <input type="hidden" name="gatewayId" value="{{ cart.gatewayId }}"/>
    {{ csrfInput() }}
  
    <div class="stripe-payment-intents-form"
      {% if clientSecret is defined %}data-client-secret="{{clientSecret}}" {% endif %}
      {% if scenario is defined %}data-scenario="{{scenario}}" {% endif %}
    >
      <div>
        <input type="text"
               name='firstName'
               maxlength="70"
               placeholder="{{'First Name'|t('commerce')}}"
               autocomplete="false"
               class="card-holder-first-name{{(paymentForm is defined and paymentForm.getErrors('firstName') ? ' error')}}"
               value="{{paymentForm is defined and paymentForm ? paymentForm.firstName : ''}}"
        >
        <input type="text"
               name="lastName"
               maxlength=70",
               placeholder="{{'Last Name'|t('commerce')}}"
               autocomplete="false"
               class="card-holder-last-name{{ (paymentForm is defined and paymentForm.getErrors('lastName') ? ' error') }}"
               value="{{paymentForm is defined and paymentForm ? paymentForm.lastName : ''}}"
        >
      </div>
  
      {% set errors = paymentForm is defined and paymentForm ? paymentForm.getErrors() : [] %}
      {% if errors|length %}
        <ul class="errors">
          {% for error in errors %}
            <li>{{ error|join('<br>')|raw }}</li>
          {% endfor %}
        </ul>
      {% endif %}
  
      <div id="card-number"></div>
      <div id="card-expiry"></div>
      <div id="card-cvc"></div>
  
      <div id="card-errors" role="alert"></div>
    </div>
    <input type="submit">
  </form>
  
  {% do view.registerJsFile('https://js.stripe.com/v3/') %}
  {% js %}
  // Create a Stripe client.
  var stripe = Stripe('{{ gateway.publishableKey ?? '' }}');
  
    // Create an instance of Elements.
    var elements = stripe.elements();
  
    // Custom styling can be passed to options when creating an Element.
    // (Note that this demo uses a wider set of styles than the guide below.)
    var style = {
      base: {
        color: '#000000',
        fontSize: '16px',
        '::placeholder': {
          color: '#CCCCCC'
        }
      },
      invalid: {
        color: '#fa755a',
        iconColor: '#fa755a'
      }
    };
  
    var classes = {
      base: 'StripeElement',
      complete: 'StripeElement--complete',
      empty: 'StripeElement--empty',
      focus: 'StripeElement--focus',
      invalid: 'StripeElement--invalid',
      webkitAutoFill: 'StripeElement--webkit-autofill'
    };
  
    var card = elements.create('cardNumber', {
      style: style,
      classes: classes
    });
    card.mount('#card-number');
  
    var expiry = elements.create('cardExpiry', {
      style: style,
      classes: classes
    });
    expiry.mount('#card-expiry');
  
    var cvc = elements.create('cardCvc', {
      style: style,
      classes: classes
    });
    cvc.mount('#card-cvc');
  
    // Handle real-time validation errors from the card Element.
    function cardErrors(event) {
      var displayError = document.getElementById('card-errors');
      if (event.error) {
        displayError.textContent = event.error.message;
      } else {
        displayError.textContent = '';
      }
    };
  
    card.addEventListener('change', cardErrors);
    expiry.addEventListener('change', cardErrors);
    cvc.addEventListener('change', cardErrors);
  
    // Handle form submission.
    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function(event) {
      event.preventDefault();
      var firstName = document.querySelector('.card-holder-first-name');
      var lastName = document.querySelector('.card-holder-last-name');
  
      var paymentData = {
        billing_details: {
          name: firstName.value + ' ' + lastName.value,
          email: '{{ cart.email }}',
          address: {
            line1: '{{ billingAddress.address1 ?? '' }}',
            city: '{{ billingAddress.city ?? '' }}',
            state: '{{ billingAddress.state ?? '' }}',
            postal_code: '{{ billingAddress.zipCode ?? '' }}',
            country: '{{ billingAddress.country.iso ?? '' }}'
          }
        }
      };
  
      stripe.createPaymentMethod('card', card, paymentData).then(function(result) {
        if (result.error) {
        // Inform the user if there was an error.
        var errorElement = document.getElementById('card-errors');
          errorElement.textContent = result.error.message;
        } else {
            var form = document.getElementById('payment-form');
            var hiddenInput = document.createElement('input');
            hiddenInput.setAttribute('type', 'hidden');
            hiddenInput.setAttribute('name', 'paymentMethodId');
            hiddenInput.setAttribute('value', result.paymentMethod.id);
            form.appendChild(hiddenInput);
  
            // Submit the form
            form.submit();
        }
      });
    });
  
  {% endjs %}
  
  ```
  
  
  ---
  
  ### Answer by username_1 on 2019-07-20. Score: 2
  
  OK so here's how to go about it.
  
  1. On checkout, you'll need at minimum a form with the following elements:
  
  ```twig
  <form method="post" id="card-info">
  <input type="hidden" name="action" value="commerce/payments/pay">
  <input type="hidden" name="paymentMethodId" value="[fill this via JS]">
  <input type="hidden" name="orderEmail" value="[your customer email address]">
  <input type="hidden" name="gatewayId" value="[your chosen gateway id - add js to have the user select one if you have multiple gateaways available]">
  {{ redirectInput('/order?number='~cart.number) }}
  {{ csrfInput() }}
  <input name="firstName" type="text" placeholder="First Name">
  <input name="lastName" type="text" placeholder="Last Name">
  <div id="card-data" class="border border-grey-lighter rounded bg-grey-lighter text-grey-darker p-3"></div>
  <button type="submit">Complete Purchase</button>
  </form>
  
  ```
  
  The real magic happens, of course, via JS.
  
  First, include the stripe library:
  `<script src="https://js.stripe.com/v3/"></script>`
  
  Add this to your main JS file to display the stripe credit card input:
  
  ```js
  let stripe = Stripe(`your public sharing key`),
      elements = stripe.elements(),
      card = undefined;
  
  let style = {
      base: {
          // Add your base input styles here. For example:
          fontSize: '16px',
          color: '#32325d',
          // Attn: The number of usable styles is very limited. Refer to https://stripe.com/docs/stripe-js/reference#the-elements-object
      }
  }
  card = elements.create('card', {style: style});
  card.addEventListener('change', updateErrorMessage);
  card.mount(document.querySelector('card-data'));
  
  function updateErrorMessage (event) {
      if (event.error) {
          // Do something with event.error.message
      }
  }
  
  ```
  
  Finally, you'll need to add code to make a request to the stripe API before the form is submitted, and fill in the value for the paymentMethodId hidden input. Refer to lines 66 to 94 here to get an idea how it's done: <https://github.com/craftcms/commerce-stripe/blob/develop/src/web/assets/intentsform/js/paymentForm.js#L66>
  
  In addition, before you submit, make sure the cart has a valid value for "email". If you do a one page checkout like me, you may have to do an ajax post to /actions/commerce/cart/update-cart in order to save it first.
  
  I implemented all of this in vuejs, so the above is untested simplified code. On request I can share my vuejs version but I thought to better keep it framework agnostic in this answer.
  
  > **Comments:**
  > **username_3**: Curious if anything has changed on this since it was posted? I cannot get this to work. Stripe shows no email address coming to it in the POST and I cannot fathom what the issue could be. Yes, I have an email on the cart and am posting it via `orderEmail`.  
  
  ---
  
  