Skip to content

Instantly share code, notes, and snippets.

@mdunbavan
Created August 20, 2018 11:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdunbavan/ff9ca9483aad636713d1565236c86681 to your computer and use it in GitHub Desktop.
Save mdunbavan/ff9ca9483aad636713d1565236c86681 to your computer and use it in GitHub Desktop.
Stripe element card payment page Craft commerce
{% extends '_layout' %}
{% block content %}
{% include 'shop/_includes/meter' %}
<section class="container center-ns ph6-ns ph4">
<div class="flex-ns flex-wrap items-start justify-left">
<div class="w-100 tc mb4">
<h2 class="pa0 ma0 f2">Payment</h2>
</div>
<div class="w-50-ns w-100" style="flex: 2;">
<div class="review-card background-gray pa4 mt4 black body">
<h3 class="pa0 mb3 mt0">Order Review</h3>
<div class="flex flex-wrap items-center justify-center" style="border-bottom: 1px solid;">
<div class="w-75 tl mv1 fw6">Item name</div><div class="w-25 tr mv1 fw6">Item price</div>
</div>
<div class="flex flex-wrap items-center justify-center mb4">
{% for item in cart.lineItems %}
<div class="w-75 tl mv2">
{% set image = item.purchasable.productImages.first() ?? null %}
<figure class="dib w-20 v-top mv0 mh0">
<img src="{{ item.purchasable.product.productImages[0].url ?? null }}" alt="{{ item.sku }} image">
</figure>
<p class="dib v-top w-70 mv0 mh0 pl2">
{{ item.description }}<br>
<small>
{% set variantSizeAttribute = item.purchasable.variantSize %}
{% set variantMaterialAttribute = item.purchasable.variantMaterials %}
{% for size in variantSizeAttribute %}
<span class="size dib v-top tc">{{ size.title }}</span>
{% endfor %}
{% for material in variantMaterialAttribute.parent().level(3) %}
<span id="{{ material.slug }}" class="dib v-top bg">({{ material.title }})</span>
{% endfor %}
</small>
</p>
</div>
<div class="w-25 tr mv2">
{% set settings = craft.commerce.settings %}
{% set rates = item.taxCategory.getTaxRates %}
{% set rateAmount = [] %}
{% set hasDiscount = item.purchasable.product.productBrand.first().hasDiscount ?? null %}
{% for rate in rates %}
{% set rateAmount = rate.rate| number_format(2) | trim("0", "left") | trim(".") %}
{% endfor %}
{% if rateAmount|length %}
{% set percent = ((item.price)/100)*rateAmount %}
{% set incVat = item.purchasable.price + percent|round %}
{% if hasDiscount == true %}
<span class="product-price strike" data-tax-price="{{ percent }}" data-variant-price-full>{{ incVat|commerceCurrency(cart.currency, stripZeros = true) }}</span>
{% set discountPercent = item.purchasable.product.productBrand.first().discountPercent ?? null %}
{% set salePercent = ((incVat)/100)*discountPercent %}
{% set salePrice = incVat - salePercent %}
<span class="product-price" data-tax-price="{{ salePercent }}" data-variant-price-full>
{{ salePrice|round|commerceCurrency(cart.currency, stripZeros = true) }}</span>
<span class="db gray f7">Sale applied</span>
{% else %}
<span class="product-price" data-tax-price="{{ percent }}" data-variant-price-full>{{ incVat|commerceCurrency(cart.currency, stripZeros = true) }}</span>
{% endif %}
{% endif %}
</div>
{% endfor %}
</div>
<ul class="list ma0 pa0 lh-copy">
<h3 class="pa0 mb3 mt0 f4 underline">Order adjustments</h3>
{% for adjustment in cart.adjustments %}
<p class="mb2">
{% if adjustment.type == 'Brand' %}
{% else %}
<span>{{ adjustment.type }}</span>
<span><strong>{{ adjustment.name }}</strong> applied<br>({{ adjustment.description }}) - {{ adjustment.amount|commerceCurrency(cart.currency) }}</span>
{% endif %}
</p>
{% endfor %}
<div class="tl w-100-ns">
<h3 class="pa0 mb3 mt4 f4 underline">Order totals</h3>
Order Subtotal: {{ cart.totalPrice|round(0,'floor')|commerceCurrency(cart.currency, stripZeros = true) }}<br>
{% if cart.totalDiscount %}
Total Discount Applied: -{{ cart.totalDiscount|round|commerceCurrency(cart.currency, stripZeros = true) }}<br>
{% endif %}
Tax VAT: {{ cart.totalTax|commerceCurrency(cart.currency, stripZeros = true) }}<br>
<div class="mv3"><h4>Total Price: {{ cart.totalPrice|round(0,'floor')|commerceCurrency(cart.currency, stripZeros = true) }}</h4></div>
</div>
{% if cart.currency != cart.paymentCurrency %}
<li>
<strong>Payment {{ cart.paymentCurrency }}:</strong> <span class="right">{{ cart.totalPrice|commerceCurrency(cart.paymentCurrency,convert=true) }}</span>
</li>
{% endif %}
</ul>
</div>
</div>
<div class="w-50-ns w-100 ml5" style="flex: 2;">
{#
Using the update cart action you could allow the user to select the payment method here.
We recommend using ajax to set this, so no page refresh is needed. This would also allow you to dynamically show or hide the credit card form
on method change.
#}
{% if not craft.commerce.paymentMethods|length %}
<p>No payment methods available.</p>
{% endif %}
{% if craft.commerce.paymentMethods|length %}
<form method="POST" id="paymentMethod" class="form-inline">
<input type="hidden" name="action" value="commerce/cart/updateCart">
<input type="hidden" name="redirect"
value="shop/checkout/payment">
{{ getCsrfInput() }}
{% if craft.commerce.paymentMethods|length < 2 %}
{% else %}
<label for="paymentMethodId" class="f3 db mb3">Select a payment method:</label>
<div class="ba b--light-gray mv3 ph3 mw6 body">
<select id="paymentMethodId" name="paymentMethodId"
class="input-reset custom-select black w-100">
{% for id,name in craft.commerce.paymentMethods %}
<option value="{{ id }}"
{% if id == cart.paymentMethodId %}selected{% endif %}>{{ name }}</option>
{% endfor %}
</select>
</div>
{% endif %}
{% set currencies = craft.commerce.paymentCurrencies %}
{% if currencies|length > 1 %}
<div class="flex relative w-100 mt2 mb3 select-style">
<select class="pl3 pr4 lh-copy v-top pv3 graphik br0 w-100 mb0-ns mb3 ba b--light-gray input-reset address-country"
id="paymentCurrency" name="paymentCurrency">
{% for currency in currencies %}
<option value="{{ currency.iso }}" {% if currency.iso == cart.paymentCurrency %}selected{% endif %}>{{ currency.name }} {{ currency.iso }} - {{ cart.totalPrice|round(0,'floor')|commerceCurrency(currency.iso,convert=true,stripZeros = true) }}</option>
{% endfor %}
</select>
</div>
{% endif %}
</form>
{% endif %}
{% if cart.paymentMethodId %}
<form method="POST" class="form background-gray ph4 pb4 pt6 black body relative mt4" {% if cart.paymentMethod.class == 'Stripe' %}id="payment-form"{% endif %}>
<div class="w-100 secure-header bb absolute top-0 left-0 pv4 ph4 tr"><i style="width: 85px;" class="pf card-icon pf-stripe text-gray-sub dib fl"></i><h3 class="ma0 pa0">Secure payment form</h3></div>
<div class="flex flex-wrap items-center mb3">
<div class="w-75 pr4 no-margin body black">
<p>We accept Visa, Visa Debit and Mastercard. This payment is bank-level secure using SSL and Stripe.</p>
</div>
<div class="w-25 flex flex-wrap">
<i class="pf card-icon-small pf-visa text-gray-sub mb2"></i>
<i class="pf card-icon-small pf-mastercard text-gray-sub mb2"></i>
<i class="pf card-icon-small pf-visa-debit text-gray-sub mb2"></i>
</div>
</div>
<input type="hidden" name="action" value="commerce/payments/pay"/>
<input type="hidden" name="redirect" value="/shop/customer/order?number={number}"/>
<input type="hidden" name="cancelUrl" value="/checkout/cancelled"/>
{{ getCsrfInput() }}
{% if errors is defined %}
<ul class="errors">
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{% if cart.paymentMethod.class == 'Stripe' %}
{% set currentYear = date()|date_modify("+1 year").format('Y') %}
{% set formValues = {
firstName: paymentForm is defined ? paymentForm.firstName : (cart.billingAddress ? cart.billingAddress.firstName : ''),
lastName: paymentForm is defined ? paymentForm.lastName : (cart.billingAddress ? cart.billingAddress.lastName : ''),
number: paymentForm is defined ? paymentForm.number : '',
cvv: paymentForm is defined ? paymentForm.cvv : '',
month: paymentForm is defined ? paymentForm.month : 1,
year: paymentForm is defined ? paymentForm.year : currentYear,
} %}
<!-- Card Holder Name -->
<fieldset class="checkout__card-details body mb3">
<div class="form__row w-100">
<div class="form__row w-100">
<div class="db w-40 form__field form__field--half mr4-ns mr0">
<label class="form__label form__label--calculator" for="firstName">First name</label>
<div class="form__control">
<input type="text" name="firstName" id="firstName" maxlength="70" placeholder="John" class="form__field form__field--text bg-transparent" value="{{ formValues.firstName }}">
</div>
{% if paymentForm is defined %}<span class="form-error">{{ paymentForm.getError('firstName') }}</span>{% endif %}
</div>
<div class="db w-40 form__field form__field--half fr">
<label class="form__label form__label--calculator" for="lastName">Last name</label>
<div class="form__control">
<input type="text" name="lastName" id="lastName" maxlength="70" placeholder="Smith" class="form__field form__field--text bg-transparent" value="{{ formValues.lastName }}">
</div>
{% if paymentForm is defined %}<span class="form-error">{{ paymentForm.getError('lastName') }}</span>{% endif %}
</div>
</div>
</div>
</fieldset>
<fieldset class="checkout__card-form body {% if paymentForm is defined and paymentForm.hasErrors('number') %}has-error{% endif %}">
<div class="form__row w-100">
<div class="form__field w-100">
<div class="w-100 db mb3">
<label for="cardNumber" class="form__label--stripe">Card Number</label>
<span class="brand"><i class="pf pf-credit-card" id="brand-icon"></i></span>
<div id="cardNumber"></div>
</div>
<div class="w-40 dib mb3">
<label for="expiryMonth" class="form__label--stripe">Card Expiry Month</label>
<div id="cardExpiry"></div>
</div>
<div class="w-40 dib fr mb3">
<label for="cvv" class="form__label--stripe">CVV/CVV2</label>
<div id="cardCvc"></div>
</div>
</div>
</div>
</fieldset>
<fieldset class="checkout__card-form body">
<div class="form__row">
<!-- Submit -->
<div class="form__field form__field--half">
<button class="button-style-green ttu h-100 no-underline near-white inline-flex items-center center bg-animate bg-green tc ph3 ph4-ns pv2 pv2-ns graphik fw5 input-reset bw0 br1" id="stripeSubmit" type="submit">Pay {{ cart.totalPrice|round(0,'floor')|commerceCurrency(cart.currency, stripZeros = true) }} Now</button>
</div>
</div>
<div class="outcome">
<div class="error"></div>
<div class="success">
</div>
</div>
<input type="hidden" name="token">
</fieldset>
{% endif %}
</form>
{% endif %}
{% if cart.paymentMethod.class == 'Stripe' %}
<!-- custom stripe checkout -->
<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
// set stripe key
var stripe = Stripe("{{ cart.paymentMethod.settings.publishableKey ?? '' }}");
// create elements
var elements = stripe.elements();
var $style = {
base: {
background: '#ffffff',
iconColor: '#666EE8',
color: '#58595',
lineHeight: '1.15',
fontWeight: 300,
fontFamily: 'Open Sans,sans-serif',
fontSize: '16px',
width: '100%',
'::placeholder': {
color: '#CCCCCC',
fontFamily: 'Open Sans,sans-serif',
fontSize: '16px'
},
},
};
// Create an instance of the card Element.
var cardBrandToPfClass = {
'visa': 'pf-visa',
'mastercard': 'pf-mastercard',
'amex': 'pf-american-express',
'discover': 'pf-discover',
'diners': 'pf-diners',
'jcb': 'pf-jcb',
'unknown': 'pf-credit-card',
}
function setBrandIcon(brand) {
var brandIconElement = document.getElementById('brand-icon');
var pfClass = 'pf-credit-card';
if (brand in cardBrandToPfClass) {
pfClass = cardBrandToPfClass[brand];
}
for (var i = brandIconElement.classList.length - 1; i >= 0; i--) {
brandIconElement.classList.remove(brandIconElement.classList[i]);
}
brandIconElement.classList.add('pf');
brandIconElement.classList.add(pfClass);
}
// Add an instance of the card Element into the `card-element` <div>.
var cardNumberElement = elements.create('cardNumber', {
style: $style,
classes: {
base: 'form__field form__field--text'
}
});
cardNumberElement.mount('#cardNumber');
var cardExpiryElement = elements.create('cardExpiry', {
style: $style,
classes: {
base: 'form__field form__field--text'
}
});
cardExpiryElement.mount('#cardExpiry');
var cardCvcElement = elements.create('cardCvc', {
style: $style,
classes: {
base: 'form__field form__field--text'
}
});
cardCvcElement.mount('#cardCvc');
function setOutcome(result) {
var successElement = document.querySelector('.success');
var errorElement = document.querySelector('.error');
successElement.classList.remove('visible');
errorElement.classList.remove('visible');
if (result.token) {
//successElement.classList.add('visible');
document.querySelector('#stripeSubmit').textContent = "Processing, please wait a moment";
// In a real integration, you'd submit the form with the token to your backend server
var form = document.querySelector('#payment-form');
form.querySelector('input[name="token"]').setAttribute('value', result.token.id);
form.submit();
} else if (result.error) {
errorElement.textContent = result.error.message;
errorElement.classList.add('visible');
}
}
cardNumberElement.on('change', function(event) {
if (event.brand) {
setBrandIcon(event.brand);
}
setOutcome(event);
});
document.querySelector('#payment-form').addEventListener('submit', function(e) {
e.preventDefault();
var form = document.querySelector('#payment-form');
var name = form.querySelector('#firstName').value + ' ' + form.querySelector('#lastName').value;
var extraData = {
name: name
};
stripe.createToken(cardNumberElement,extraData).then(setOutcome);
});
</script>
{% endif %}
</div>
</section>
{% includejs %}
$('#paymentMethodId , #paymentCurrency').change(function(){
$('form#paymentMethod').submit();
});
{% endincludejs %}
{% endblock %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment