Shopify Checkout MFPP code
zip-checkout-mfpp.liquid
{% comment %}<!-- Zip MFPP -->{% endcomment %}
{% assign zip_mfpp_url = "https://gateway.quadpay.com/orders/calculate-merchant-fees" -%}
{% if zip_test -%}
{% assign zip_mfpp_url = "https://sandbox.gateway.quadpay.com/orders/calculate-merchant-fees" -%}
{% endif -%}
<script type="application/javascript">
// Zip Checkout MFPP - v1.2.0
{% assign zip_default_money_format = '${{amount}}' -%}
window.QuadPayCheckoutOptions = {
merchantId: "{{zip_merchant_id}}",
state: "{{checkout.shipping_address.province_code | default: checkout.billing_address.province_code}}",
country: "{{checkout.shipping_address.country_code | default: checkout.billing_address.country_code}}",
currency: "{{shop.currency}}",
moneyFormat: "{{zip_money_format | default: zip_default_money_format}}",
quadpayFeeApi: "{{ zip_mfpp_url }}",
quadpayRegion: "{{ zip_region | default: "US" }}"
};
{% if zip_selector %}
window.QuadPayCheckoutOptions.quadpayGatewaySelector = "{{zip_selector}}";
{% endif %}
{% if order.attributes.zip-mfpp-amount -%}
{% comment %}/* Thank You page and paid with Zip. */{% endcomment %}
window.QuadPayCheckoutOptions.thankYouPageFee = {{ order.attributes.zip-mfpp-amount }};
{% endif -%}
/* global document, window */
window.QuadPayCheckout = {
selectors: {
orderSummaryTotalLines: '.total-line-table__tbody',
paymentMethodSection: '.section--payment-method .section__content',
total: '.total-line-table__footer .total-line__price .payment-due__price',
totalRecap: '.total-recap__final-price',
mainForm: "form[data-payment-form]",
paymentMethodTransactions: ".payment-method-list__item__amount",
orderAttributesInputName: "checkout[attributes][zip-mfpp-amount]",
},
quadpayFeeTotalLineClass: '.total-line--quadpay-fee',
init: function(options, step) {
this.options = options;
if (step === 'payment_method') {
this.initPaymentMethodStep();
} else {
this.initThankYouPage();
}
},
initPaymentMethodStep: function() {
const quadpayGatewaySelector = this.options.quadpayGatewaySelector || this.findGatewaySelector();
if (!quadpayGatewaySelector) {
const style = "background-color:white;font-weight:bold;";
console.log(
"%c[ZIP] Cannot find Zip gateway selector, please refer to documentation at: %chttps://docs.us.zip.co/docs/mfpp-shopify-plus",
"color:#411361;" + style,
"color:#6542be;" + style
);
return;
}
this.shopifyTotal = this.getShopifyTotal();
this.monitorGatewaySelection(quadpayGatewaySelector);
this.monitorCurrencySelection();
this.refresh(true);
},
initThankYouPage: function() {
let fee = this.options.thankYouPageFee;
if (fee) {
this.shopifyTotal = this.getShopifyTotal();
this.setTotalLinePriceIncrease(fee, this.options.currency);
this.setQuadPayLinePrice(fee);
}
},
findGatewaySelector: function() {
const identifyingElement = document.querySelector('[data-payment-icon="zip"]') ||
Array.prototype.find.call(document.querySelectorAll('.radio__label__primary'), function (el) {
return el.innerText.match(/(Zip|Quadpay|QuadPay)/);
});
if (!identifyingElement) {
return null;
}
const wrapperElement = this.findClosestParentElement(identifyingElement, ".radio-wrapper");
if (!wrapperElement || !wrapperElement.dataset.selectGateway) {
return null;
}
return "#checkout_payment_gateway_" + wrapperElement.dataset.selectGateway;
},
findClosestParentElement: function(element, selector) {
if (element.closest && typeof element.closest === "function") {
return element.closest(selector);
}
const findClosest = function (element, selector) {
if (element.matches(selector)) {
return element;
} else if (!element.parentNode) {
return null;
}
return findClosest(element.parentNode, selector);
};
return findClosest(element, selector);
},
refresh: function(reloadFee) {
if (reloadFee === true) {
this.fee = null;
}
this.getQuadPayFee().then((function (fee) {
this.fee = fee;
if (this.quadpayIsSelected === true) {
this.repaintFee(this.fee);
this.generateOrderAttributesInput(this.fee);
} else {
this.hideFee();
this.removeOrderAttributesInput();
}
}).bind(this));
},
repaintFee: function(fee) {
this.setTotalLinePriceIncrease(fee);
this.setQuadPayLinePrice(fee);
},
hideFee: function() {
this.setTotalLinePriceIncrease(0);
this.setQuadPayLinePrice(null);
},
setTotalLinePriceIncrease: function(fee, currency) {
const money = this.formatInCurrency(this.shopifyTotal + fee, currency);
const selector = this.selectors.total + ',' + this.selectors.totalRecap + ',' + this.selectors.paymentMethodTransactions;
document.querySelectorAll(selector).forEach(function(el) {
el.innerHTML = money;
});
},
setQuadPayLinePrice: function(fee) {
let quadPayLine = document.querySelector(this.quadpayFeeTotalLineClass);
if (quadPayLine) {
quadPayLine.parentNode.removeChild(quadPayLine);
}
if (fee === null || fee < 0.01) {
return;
}
this.generateQuadPayFeeLine(fee);
},
monitorGatewaySelection: function(quadpayGatewaySelector) {
this.quadpayIsSelected = null;
const gatewaySelected = (function() {
const radio = document.querySelector(quadpayGatewaySelector);
const isSelected = radio && radio.checked;
const shouldRefresh = this.quadpayIsSelected !== null && this.quadpayIsSelected !== isSelected;
this.quadpayIsSelected = isSelected;
if (shouldRefresh) {
this.refresh(false);
}
}).bind(this);
const paymentMethodSection = document.querySelector(this.selectors.paymentMethodSection);
if (!paymentMethodSection) {
return;
}
paymentMethodSection.addEventListener('click', gatewaySelected);
gatewaySelected();
},
monitorCurrencySelection: function() {
const currencies = document.querySelector("#currencies");
if (!currencies) {
return;
}
currencies.addEventListener('change', (function () {
this.refresh(false);
}).bind(this));
},
generateQuadPayFeeLine: function(fee) {
let subtotalsElement = document.querySelector(this.selectors.orderSummaryTotalLines);
if (subtotalsElement) {
subtotalsElement.insertAdjacentHTML('beforeend', this.generateLine(fee));
}
},
removeOrderAttributesInput: function() {
document.getElementsByName(this.selectors.orderAttributesInputName).forEach(function (input) {
try {
input.parentNode.removeChild(input);
} catch (_) {}
});
},
generateOrderAttributesInput: function(fee) {
const orderAttributeInputs = document.getElementsByName(this.selectors.orderAttributesInputName);
let orderAttributeInput = orderAttributeInputs.length > 0 ? orderAttributeInputs[0] : null;
if (!orderAttributeInput) {
const form = document.querySelector(this.selectors.mainForm);
if (!form) {
return;
}
orderAttributeInput = document.createElement("input");
orderAttributeInput.setAttribute("type", "hidden");
orderAttributeInput.setAttribute("name", this.selectors.orderAttributesInputName);
form.appendChild(orderAttributeInput);
}
orderAttributeInput.setAttribute("value", fee);
},
generateLine: function(fee) {
const money = this.formatInCurrency(fee);
let className = this.quadpayFeeTotalLineClass.replace(/^\./, '');
return '<tr class="total-line ' + className + '">' +
'<th class="total-line__name" scope="row">Merchant Fee for Payment Plan</th>' +
'<td class="total-line__price"><b>' + money + '</b></td>' +
'</tr>';
},
getQuadPayFee: function() {
if (this.fee !== null) {
return Promise.resolve(this.fee);
}
return this.fetchFee().then(function(data) {
if (!('merchantFeeForPaymentPlan' in data)) {
return Promise.resolve(null);
}
let fee = parseFloat(data.merchantFeeForPaymentPlan) * 100;
return Promise.resolve(fee);
});
},
fetchFee: function() {
let totalForQuadPay = this.shopifyTotal / 100;
let dataForQuadPay = {
merchantId: this.options.merchantId,
currency: this.options.currency,
customerCountry: this.options.country,
amount: totalForQuadPay
};
if (this.options.state) {
dataForQuadPay.customerState = this.options.state;
}
return window.fetch(this.options.quadpayFeeApi, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'QP-Territory': this.options.quadpayRegion
},
body: JSON.stringify(dataForQuadPay)
}).then(function (response) {
return response.json();
});
},
getShopifyTotal: function() {
let totalElement = document.querySelector(this.selectors.total);
if (!totalElement) {
return;
}
return parseInt(totalElement.getAttribute('data-checkout-payment-due-target'), 10);
},
formatInCurrency: function (amount, currency) {
if (window.Currency) {
const currentCurrency = currency || window.Currency.cookie.read();
const total = window.Currency.convert(amount, window.Shopify.Checkout.currency || this.options.currency, currentCurrency);
const moneyFormat = window.Currency.moneyFormats[currentCurrency][window.Currency.format];
return window.Currency.formatMoney(total, moneyFormat);
}
return this.shopifyFormatMoney(amount);
},
shopifyFormatMoney: function(cents) {
if (typeof cents == 'string') {
cents = cents.replace('.','');
}
let value = '';
let placeholderRegex = /\{\{\s*(\w+)\s*\}\}/;
let formatString = this.options.moneyFormat;
function defaultOption(opt, def) {
return (typeof opt == 'undefined' ? def : opt);
}
function formatWithDelimiters(number, precision, thousands, decimal) {
precision = defaultOption(precision, 2);
thousands = defaultOption(thousands, ',');
decimal = defaultOption(decimal, '.');
if (isNaN(number) || number == null) { return 0; }
number = (number/100.0).toFixed(precision);
let parts = number.split('.'),
dollars = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + thousands),
cents = parts[1] ? (decimal + parts[1]) : '';
return dollars + cents;
}
switch(formatString.match(placeholderRegex)[1]) {
case 'amount':
value = formatWithDelimiters(cents, 2);
break;
case 'amount_no_decimals':
value = formatWithDelimiters(cents, 0);
break;
case 'amount_with_comma_separator':
value = formatWithDelimiters(cents, 2, '.', ',');
break;
case 'amount_no_decimals_with_comma_separator':
value = formatWithDelimiters(cents, 0, '.', ',');
break;
}
return formatString.replace(placeholderRegex, value);
}
};
(function() {
let step = window.Shopify.Checkout.step;
if (!step && window.QuadPayCheckoutOptions.thankYouPageFee) {
step = 'thank_you';
}
if (['payment_method', 'thank_you'].indexOf(step) !== -1) {
window.QuadPayCheckout.init(window.QuadPayCheckoutOptions, step);
}
// re-init on apply gift card and calculating taxes
if (step === 'payment_method') {
document.addEventListener('page:change', function () {
window.setTimeout(function () {
window.QuadPayCheckout.init(window.QuadPayCheckoutOptions, step);
}, 500);
});
}
})();
</script>
Updated over 1 year ago