Initial commit
diff --git a/assets/js/index.html b/assets/js/index.html
new file mode 100644
index 0000000..5634921
--- /dev/null
+++ b/assets/js/index.html
@@ -0,0 +1,14 @@
+<html><head><title> - Revision 2844313: /woocommerce-gateway-stripe/trunk/assets/js</title></head>
+<body>
+ <h2> - Revision 2844313: /woocommerce-gateway-stripe/trunk/assets/js</h2>
+ <ul>
+  <li><a href="../">..</a></li>
+  <li><a href="jquery.mask.js">jquery.mask.js</a></li>
+  <li><a href="jquery.mask.min.js">jquery.mask.min.js</a></li>
+  <li><a href="stripe-payment-request.js">stripe-payment-request.js</a></li>
+  <li><a href="stripe-payment-request.min.js">stripe-payment-request.min.js</a></li>
+  <li><a href="stripe.js">stripe.js</a></li>
+  <li><a href="stripe.min.js">stripe.min.js</a></li>
+ </ul>
+ <hr noshade><em>Powered by <a href="http://subversion.apache.org/">Apache Subversion</a> version 1.9.5 (r1770682).</em>
+</body></html>
\ No newline at end of file
diff --git a/assets/js/jquery.mask.js b/assets/js/jquery.mask.js
new file mode 100644
index 0000000..6f2417d
--- /dev/null
+++ b/assets/js/jquery.mask.js
@@ -0,0 +1,604 @@
+/**
+ * jquery.mask.js
+ * @version: v1.14.16
+ * @author: Igor Escobar
+ *
+ * Created by Igor Escobar on 2012-03-10. Please report any bug at github.com/igorescobar/jQuery-Mask-Plugin
+ *
+ * Copyright (c) 2012 Igor Escobar http://igorescobar.com
+ *
+ * The MIT License (http://www.opensource.org/licenses/mit-license.php)
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* jshint laxbreak: true */
+/* jshint maxcomplexity:17 */
+/* global define */
+
+// UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere.
+// https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js
+(function (factory, jQuery, Zepto) {
+
+	if (typeof define === 'function' && define.amd) {
+		define(['jquery'], factory);
+	} else if (typeof exports === 'object' && typeof Meteor === 'undefined') {
+		module.exports = factory(require('jquery'));
+	} else {
+		factory(jQuery || Zepto);
+	}
+
+}(function ($) {
+	'use strict';
+
+	var Mask = function (el, mask, options) {
+
+		var p = {
+			invalid: [],
+			getCaret: function () {
+				try {
+					var sel,
+						pos = 0,
+						ctrl = el.get(0),
+						dSel = document.selection,
+						cSelStart = ctrl.selectionStart;
+
+					// IE Support
+					if (dSel && navigator.appVersion.indexOf('MSIE 10') === -1) {
+						sel = dSel.createRange();
+						sel.moveStart('character', -p.val().length);
+						pos = sel.text.length;
+					}
+					// Firefox support
+					else if (cSelStart || cSelStart === '0') {
+						pos = cSelStart;
+					}
+
+					return pos;
+				} catch (e) {}
+			},
+			setCaret: function(pos) {
+				try {
+					if (el.is(':focus')) {
+						var range, ctrl = el.get(0);
+
+						// Firefox, WebKit, etc..
+						if (ctrl.setSelectionRange) {
+							ctrl.setSelectionRange(pos, pos);
+						} else { // IE
+							range = ctrl.createTextRange();
+							range.collapse(true);
+							range.moveEnd('character', pos);
+							range.moveStart('character', pos);
+							range.select();
+						}
+					}
+				} catch (e) {}
+			},
+			events: function() {
+				el
+					.on('keydown.mask', function(e) {
+						el.data('mask-keycode', e.keyCode || e.which);
+						el.data('mask-previus-value', el.val());
+						el.data('mask-previus-caret-pos', p.getCaret());
+						p.maskDigitPosMapOld = p.maskDigitPosMap;
+					})
+					.on($.jMaskGlobals.useInput ? 'input.mask' : 'keyup.mask', p.behaviour)
+					.on('paste.mask drop.mask', function() {
+						setTimeout(function() {
+							el.keydown().keyup();
+						}, 100);
+					})
+					.on('change.mask', function(){
+						el.data('changed', true);
+					})
+					.on('blur.mask', function(){
+						if (oldValue !== p.val() && !el.data('changed')) {
+							el.trigger('change');
+						}
+						el.data('changed', false);
+					})
+					// it's very important that this callback remains in this position
+					// otherwhise oldValue it's going to work buggy
+					.on('blur.mask', function() {
+						oldValue = p.val();
+					})
+					// select all text on focus
+					.on('focus.mask', function (e) {
+						if (options.selectOnFocus === true) {
+							$(e.target).select();
+						}
+					})
+					// clear the value if it not complete the mask
+					.on('focusout.mask', function() {
+						if (options.clearIfNotMatch && !regexMask.test(p.val())) {
+							p.val('');
+						}
+					});
+			},
+			getRegexMask: function() {
+				var maskChunks = [], translation, pattern, optional, recursive, oRecursive, r;
+
+				for (var i = 0; i < mask.length; i++) {
+					translation = jMask.translation[mask.charAt(i)];
+
+					if (translation) {
+
+						pattern = translation.pattern.toString().replace(/.{1}$|^.{1}/g, '');
+						optional = translation.optional;
+						recursive = translation.recursive;
+
+						if (recursive) {
+							maskChunks.push(mask.charAt(i));
+							oRecursive = {digit: mask.charAt(i), pattern: pattern};
+						} else {
+							maskChunks.push(!optional && !recursive ? pattern : (pattern + '?'));
+						}
+
+					} else {
+						maskChunks.push(mask.charAt(i).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'));
+					}
+				}
+
+				r = maskChunks.join('');
+
+				if (oRecursive) {
+					r = r.replace(new RegExp('(' + oRecursive.digit + '(.*' + oRecursive.digit + ')?)'), '($1)?')
+						.replace(new RegExp(oRecursive.digit, 'g'), oRecursive.pattern);
+				}
+
+				return new RegExp(r);
+			},
+			destroyEvents: function() {
+				el.off(['input', 'keydown', 'keyup', 'paste', 'drop', 'blur', 'focusout', ''].join('.mask '));
+			},
+			val: function(v) {
+				var isInput = el.is('input'),
+					method = isInput ? 'val' : 'text',
+					r;
+
+				if (arguments.length > 0) {
+					if (el[method]() !== v) {
+						el[method](v);
+					}
+					r = el;
+				} else {
+					r = el[method]();
+				}
+
+				return r;
+			},
+			calculateCaretPosition: function(oldVal) {
+				var newVal = p.getMasked(),
+					caretPosNew = p.getCaret();
+				if (oldVal !== newVal) {
+					var caretPosOld = el.data('mask-previus-caret-pos') || 0,
+						newValL = newVal.length,
+						oldValL = oldVal.length,
+						maskDigitsBeforeCaret = 0,
+						maskDigitsAfterCaret = 0,
+						maskDigitsBeforeCaretAll = 0,
+						maskDigitsBeforeCaretAllOld = 0,
+						i = 0;
+
+					for (i = caretPosNew; i < newValL; i++) {
+						if (!p.maskDigitPosMap[i]) {
+							break;
+						}
+						maskDigitsAfterCaret++;
+					}
+
+					for (i = caretPosNew - 1; i >= 0; i--) {
+						if (!p.maskDigitPosMap[i]) {
+							break;
+						}
+						maskDigitsBeforeCaret++;
+					}
+
+					for (i = caretPosNew - 1; i >= 0; i--) {
+						if (p.maskDigitPosMap[i]) {
+							maskDigitsBeforeCaretAll++;
+						}
+					}
+
+					for (i = caretPosOld - 1; i >= 0; i--) {
+						if (p.maskDigitPosMapOld[i]) {
+							maskDigitsBeforeCaretAllOld++;
+						}
+					}
+
+					// if the cursor is at the end keep it there
+					if (caretPosNew > oldValL) {
+						caretPosNew = newValL * 10;
+					} else if (caretPosOld >= caretPosNew && caretPosOld !== oldValL) {
+						if (!p.maskDigitPosMapOld[caretPosNew])  {
+							var caretPos = caretPosNew;
+							caretPosNew -= maskDigitsBeforeCaretAllOld - maskDigitsBeforeCaretAll;
+							caretPosNew -= maskDigitsBeforeCaret;
+							if (p.maskDigitPosMap[caretPosNew])  {
+								caretPosNew = caretPos;
+							}
+						}
+					}
+					else if (caretPosNew > caretPosOld) {
+						caretPosNew += maskDigitsBeforeCaretAll - maskDigitsBeforeCaretAllOld;
+						caretPosNew += maskDigitsAfterCaret;
+					}
+				}
+				return caretPosNew;
+			},
+			behaviour: function(e) {
+				e = e || window.event;
+				p.invalid = [];
+
+				var keyCode = el.data('mask-keycode');
+
+				if ($.inArray(keyCode, jMask.byPassKeys) === -1) {
+					var newVal = p.getMasked(),
+						caretPos = p.getCaret(),
+						oldVal = el.data('mask-previus-value') || '';
+
+					// this is a compensation to devices/browsers that don't compensate
+					// caret positioning the right way
+					setTimeout(function() {
+						p.setCaret(p.calculateCaretPosition(oldVal));
+					}, $.jMaskGlobals.keyStrokeCompensation);
+
+					p.val(newVal);
+					p.setCaret(caretPos);
+					return p.callbacks(e);
+				}
+			},
+			getMasked: function(skipMaskChars, val) {
+				var buf = [],
+					value = val === undefined ? p.val() : val + '',
+					m = 0, maskLen = mask.length,
+					v = 0, valLen = value.length,
+					offset = 1, addMethod = 'push',
+					resetPos = -1,
+					maskDigitCount = 0,
+					maskDigitPosArr = [],
+					lastMaskChar,
+					check;
+
+				if (options.reverse) {
+					addMethod = 'unshift';
+					offset = -1;
+					lastMaskChar = 0;
+					m = maskLen - 1;
+					v = valLen - 1;
+					check = function () {
+						return m > -1 && v > -1;
+					};
+				} else {
+					lastMaskChar = maskLen - 1;
+					check = function () {
+						return m < maskLen && v < valLen;
+					};
+				}
+
+				var lastUntranslatedMaskChar;
+				while (check()) {
+					var maskDigit = mask.charAt(m),
+						valDigit = value.charAt(v),
+						translation = jMask.translation[maskDigit];
+
+					if (translation) {
+						if (valDigit.match(translation.pattern)) {
+							buf[addMethod](valDigit);
+							if (translation.recursive) {
+								if (resetPos === -1) {
+									resetPos = m;
+								} else if (m === lastMaskChar && m !== resetPos) {
+									m = resetPos - offset;
+								}
+
+								if (lastMaskChar === resetPos) {
+									m -= offset;
+								}
+							}
+							m += offset;
+						} else if (valDigit === lastUntranslatedMaskChar) {
+							// matched the last untranslated (raw) mask character that we encountered
+							// likely an insert offset the mask character from the last entry; fall
+							// through and only increment v
+							maskDigitCount--;
+							lastUntranslatedMaskChar = undefined;
+						} else if (translation.optional) {
+							m += offset;
+							v -= offset;
+						} else if (translation.fallback) {
+							buf[addMethod](translation.fallback);
+							m += offset;
+							v -= offset;
+						} else {
+							p.invalid.push({p: v, v: valDigit, e: translation.pattern});
+						}
+						v += offset;
+					} else {
+						if (!skipMaskChars) {
+							buf[addMethod](maskDigit);
+						}
+
+						if (valDigit === maskDigit) {
+							maskDigitPosArr.push(v);
+							v += offset;
+						} else {
+							lastUntranslatedMaskChar = maskDigit;
+							maskDigitPosArr.push(v + maskDigitCount);
+							maskDigitCount++;
+						}
+
+						m += offset;
+					}
+				}
+
+				var lastMaskCharDigit = mask.charAt(lastMaskChar);
+				if (maskLen === valLen + 1 && !jMask.translation[lastMaskCharDigit]) {
+					buf.push(lastMaskCharDigit);
+				}
+
+				var newVal = buf.join('');
+				p.mapMaskdigitPositions(newVal, maskDigitPosArr, valLen);
+				return newVal;
+			},
+			mapMaskdigitPositions: function(newVal, maskDigitPosArr, valLen) {
+				var maskDiff = options.reverse ? newVal.length - valLen : 0;
+				p.maskDigitPosMap = {};
+				for (var i = 0; i < maskDigitPosArr.length; i++) {
+					p.maskDigitPosMap[maskDigitPosArr[i] + maskDiff] = 1;
+				}
+			},
+			callbacks: function (e) {
+				var val = p.val(),
+					changed = val !== oldValue,
+					defaultArgs = [val, e, el, options],
+					callback = function(name, criteria, args) {
+						if (typeof options[name] === 'function' && criteria) {
+							options[name].apply(this, args);
+						}
+					};
+
+				callback('onChange', changed === true, defaultArgs);
+				callback('onKeyPress', changed === true, defaultArgs);
+				callback('onComplete', val.length === mask.length, defaultArgs);
+				callback('onInvalid', p.invalid.length > 0, [val, e, el, p.invalid, options]);
+			}
+		};
+
+		el = $(el);
+		var jMask = this, oldValue = p.val(), regexMask;
+
+		mask = typeof mask === 'function' ? mask(p.val(), undefined, el,  options) : mask;
+
+		// public methods
+		jMask.mask = mask;
+		jMask.options = options;
+		jMask.remove = function() {
+			var caret = p.getCaret();
+			if (jMask.options.placeholder) {
+				el.removeAttr('placeholder');
+			}
+			if (el.data('mask-maxlength')) {
+				el.removeAttr('maxlength');
+			}
+			p.destroyEvents();
+			p.val(jMask.getCleanVal());
+			p.setCaret(caret);
+			return el;
+		};
+
+		// get value without mask
+		jMask.getCleanVal = function() {
+			return p.getMasked(true);
+		};
+
+		// get masked value without the value being in the input or element
+		jMask.getMaskedVal = function(val) {
+			return p.getMasked(false, val);
+		};
+
+		jMask.init = function(onlyMask) {
+			onlyMask = onlyMask || false;
+			options = options || {};
+
+			jMask.clearIfNotMatch  = $.jMaskGlobals.clearIfNotMatch;
+			jMask.byPassKeys       = $.jMaskGlobals.byPassKeys;
+			jMask.translation      = $.extend({}, $.jMaskGlobals.translation, options.translation);
+
+			jMask = $.extend(true, {}, jMask, options);
+
+			regexMask = p.getRegexMask();
+
+			if (onlyMask) {
+				p.events();
+				p.val(p.getMasked());
+			} else {
+				if (options.placeholder) {
+					el.attr('placeholder' , options.placeholder);
+				}
+
+				// this is necessary, otherwise if the user submit the form
+				// and then press the "back" button, the autocomplete will erase
+				// the data. Works fine on IE9+, FF, Opera, Safari.
+				if (el.data('mask')) {
+					el.attr('autocomplete', 'off');
+				}
+
+				// detect if is necessary let the user type freely.
+				// for is a lot faster than forEach.
+				for (var i = 0, maxlength = true; i < mask.length; i++) {
+					var translation = jMask.translation[mask.charAt(i)];
+					if (translation && translation.recursive) {
+						maxlength = false;
+						break;
+					}
+				}
+
+				if (maxlength) {
+					el.attr('maxlength', mask.length).data('mask-maxlength', true);
+				}
+
+				p.destroyEvents();
+				p.events();
+
+				var caret = p.getCaret();
+				p.val(p.getMasked());
+				p.setCaret(caret);
+			}
+		};
+
+		jMask.init(!el.is('input'));
+	};
+
+	$.maskWatchers = {};
+	var HTMLAttributes = function () {
+			var input = $(this),
+				options = {},
+				prefix = 'data-mask-',
+				mask = input.attr('data-mask');
+
+			if (input.attr(prefix + 'reverse')) {
+				options.reverse = true;
+			}
+
+			if (input.attr(prefix + 'clearifnotmatch')) {
+				options.clearIfNotMatch = true;
+			}
+
+			if (input.attr(prefix + 'selectonfocus') === 'true') {
+				options.selectOnFocus = true;
+			}
+
+			if (notSameMaskObject(input, mask, options)) {
+				return input.data('mask', new Mask(this, mask, options));
+			}
+		},
+		notSameMaskObject = function(field, mask, options) {
+			options = options || {};
+			var maskObject = $(field).data('mask'),
+				stringify = JSON.stringify,
+				value = $(field).val() || $(field).text();
+			try {
+				if (typeof mask === 'function') {
+					mask = mask(value);
+				}
+				return typeof maskObject !== 'object' || stringify(maskObject.options) !== stringify(options) || maskObject.mask !== mask;
+			} catch (e) {}
+		},
+		eventSupported = function(eventName) {
+			var el = document.createElement('div'), isSupported;
+
+			eventName = 'on' + eventName;
+			isSupported = (eventName in el);
+
+			if ( !isSupported ) {
+				el.setAttribute(eventName, 'return;');
+				isSupported = typeof el[eventName] === 'function';
+			}
+			el = null;
+
+			return isSupported;
+		};
+
+	$.fn.mask = function(mask, options) {
+		options = options || {};
+		var selector = this.selector,
+			globals = $.jMaskGlobals,
+			interval = globals.watchInterval,
+			watchInputs = options.watchInputs || globals.watchInputs,
+			maskFunction = function() {
+				if (notSameMaskObject(this, mask, options)) {
+					return $(this).data('mask', new Mask(this, mask, options));
+				}
+			};
+
+		$(this).each(maskFunction);
+
+		if (selector && selector !== '' && watchInputs) {
+			clearInterval($.maskWatchers[selector]);
+			$.maskWatchers[selector] = setInterval(function(){
+				$(document).find(selector).each(maskFunction);
+			}, interval);
+		}
+		return this;
+	};
+
+	$.fn.masked = function(val) {
+		return this.data('mask').getMaskedVal(val);
+	};
+
+	$.fn.unmask = function() {
+		clearInterval($.maskWatchers[this.selector]);
+		delete $.maskWatchers[this.selector];
+		return this.each(function() {
+			var dataMask = $(this).data('mask');
+			if (dataMask) {
+				dataMask.remove().removeData('mask');
+			}
+		});
+	};
+
+	$.fn.cleanVal = function() {
+		return this.data('mask').getCleanVal();
+	};
+
+	$.applyDataMask = function(selector) {
+		selector = selector || $.jMaskGlobals.maskElements;
+		var $selector = (selector instanceof $) ? selector : $(selector);
+		$selector.filter($.jMaskGlobals.dataMaskAttr).each(HTMLAttributes);
+	};
+
+	var globals = {
+		maskElements: 'input,td,span,div',
+		dataMaskAttr: '*[data-mask]',
+		dataMask: true,
+		watchInterval: 300,
+		watchInputs: true,
+		keyStrokeCompensation: 10,
+		// old versions of chrome dont work great with input event
+		useInput: !/Chrome\/[2-4][0-9]|SamsungBrowser/.test(window.navigator.userAgent) && eventSupported('input'),
+		watchDataMask: false,
+		byPassKeys: [9, 16, 17, 18, 36, 37, 38, 39, 40, 91],
+		translation: {
+			'0': {pattern: /\d/},
+			'9': {pattern: /\d/, optional: true},
+			'#': {pattern: /\d/, recursive: true},
+			'A': {pattern: /[a-zA-Z0-9]/},
+			'S': {pattern: /[a-zA-Z]/}
+		}
+	};
+
+	$.jMaskGlobals = $.jMaskGlobals || {};
+	globals = $.jMaskGlobals = $.extend(true, {}, globals, $.jMaskGlobals);
+
+	// looking for inputs with data-mask attribute
+	if (globals.dataMask) {
+		$.applyDataMask();
+	}
+
+	setInterval(function() {
+		if ($.jMaskGlobals.watchDataMask) {
+			$.applyDataMask();
+		}
+	}, globals.watchInterval);
+}, window.jQuery, window.Zepto));
diff --git a/assets/js/jquery.mask.min.js b/assets/js/jquery.mask.min.js
new file mode 100644
index 0000000..77ef3e4
--- /dev/null
+++ b/assets/js/jquery.mask.min.js
@@ -0,0 +1 @@
+!function(t,a,e){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof exports&&"undefined"==typeof Meteor?module.exports=t(require("jquery")):t(a||e)}(function(o){"use strict";function i(u,M,y){var b={invalid:[],getCaret:function(){try{var t,a=0,e=u.get(0),n=document.selection,s=e.selectionStart;return n&&-1===navigator.appVersion.indexOf("MSIE 10")?((t=n.createRange()).moveStart("character",-b.val().length),a=t.text.length):!s&&"0"!==s||(a=s),a}catch(t){}},setCaret:function(t){try{var a,e;u.is(":focus")&&((e=u.get(0)).setSelectionRange?e.setSelectionRange(t,t):((a=e.createTextRange()).collapse(!0),a.moveEnd("character",t),a.moveStart("character",t),a.select()))}catch(t){}},events:function(){u.on("keydown.mask",function(t){u.data("mask-keycode",t.keyCode||t.which),u.data("mask-previus-value",u.val()),u.data("mask-previus-caret-pos",b.getCaret()),b.maskDigitPosMapOld=b.maskDigitPosMap}).on(o.jMaskGlobals.useInput?"input.mask":"keyup.mask",b.behaviour).on("paste.mask drop.mask",function(){setTimeout(function(){u.keydown().keyup()},100)}).on("change.mask",function(){u.data("changed",!0)}).on("blur.mask",function(){r===b.val()||u.data("changed")||u.trigger("change"),u.data("changed",!1)}).on("blur.mask",function(){r=b.val()}).on("focus.mask",function(t){!0===y.selectOnFocus&&o(t.target).select()}).on("focusout.mask",function(){y.clearIfNotMatch&&!s.test(b.val())&&b.val("")})},getRegexMask:function(){for(var t,a,e,n,s,r=[],o=0;o<M.length;o++)(e=w.translation[M.charAt(o)])?(t=e.pattern.toString().replace(/.{1}$|^.{1}/g,""),a=e.optional,(e=e.recursive)?(r.push(M.charAt(o)),n={digit:M.charAt(o),pattern:t}):r.push(a||e?t+"?":t)):r.push(M.charAt(o).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&"));return s=r.join(""),n&&(s=s.replace(new RegExp("("+n.digit+"(.*"+n.digit+")?)"),"($1)?").replace(new RegExp(n.digit,"g"),n.pattern)),new RegExp(s)},destroyEvents:function(){u.off(["input","keydown","keyup","paste","drop","blur","focusout",""].join(".mask "))},val:function(t){var a=u.is("input")?"val":"text",a=0<arguments.length?(u[a]()!==t&&u[a](t),u):u[a]();return a},calculateCaretPosition:function(t){var a=b.getMasked(),e=b.getCaret();if(t!==a){for(var n=u.data("mask-previus-caret-pos")||0,s=a.length,t=t.length,r=0,o=0,i=0,l=0,c=0,c=e;c<s&&b.maskDigitPosMap[c];c++)o++;for(c=e-1;0<=c&&b.maskDigitPosMap[c];c--)r++;for(c=e-1;0<=c;c--)b.maskDigitPosMap[c]&&i++;for(c=n-1;0<=c;c--)b.maskDigitPosMapOld[c]&&l++;t<e?e=10*s:e<=n&&n!==t?b.maskDigitPosMapOld[e]||(t=e,e-=l-i,b.maskDigitPosMap[e-=r]&&(e=t)):n<e&&(e+=i-l,e+=o)}return e},behaviour:function(t){t=t||window.event,b.invalid=[];var a=u.data("mask-keycode");if(-1===o.inArray(a,w.byPassKeys)){var e=b.getMasked(),a=b.getCaret(),n=u.data("mask-previus-value")||"";return setTimeout(function(){b.setCaret(b.calculateCaretPosition(n))},o.jMaskGlobals.keyStrokeCompensation),b.val(e),b.setCaret(a),b.callbacks(t)}},getMasked:function(t,a){for(var e,n,s=[],r=void 0===a?b.val():a+"",o=0,i=M.length,l=0,c=r.length,u=1,k="push",p=-1,d=0,f=[],h=y.reverse?(k="unshift",u=-1,e=0,o=i-1,l=c-1,function(){return-1<o&&-1<l}):(e=i-1,function(){return o<i&&l<c});h();){var v=M.charAt(o),g=r.charAt(l),m=w.translation[v];m?(g.match(m.pattern)?(s[k](g),m.recursive&&(-1===p?p=o:o===e&&o!==p&&(o=p-u),e===p&&(o-=u)),o+=u):g===n?(d--,n=void 0):m.optional?(o+=u,l-=u):m.fallback?(s[k](m.fallback),o+=u,l-=u):b.invalid.push({p:l,v:g,e:m.pattern}),l+=u):(t||s[k](v),g===v?(f.push(l),l+=u):(n=v,f.push(l+d),d++),o+=u)}a=M.charAt(e);i!==c+1||w.translation[a]||s.push(a);a=s.join("");return b.mapMaskdigitPositions(a,f,c),a},mapMaskdigitPositions:function(t,a,e){var n=y.reverse?t.length-e:0;b.maskDigitPosMap={};for(var s=0;s<a.length;s++)b.maskDigitPosMap[a[s]+n]=1},callbacks:function(t){function a(t,a,e){"function"==typeof y[t]&&a&&y[t].apply(this,e)}var e=b.val(),n=e!==r,s=[e,t,u,y];a("onChange",!0==n,s),a("onKeyPress",!0==n,s),a("onComplete",e.length===M.length,s),a("onInvalid",0<b.invalid.length,[e,t,u,b.invalid,y])}};u=o(u);var s,w=this,r=b.val();M="function"==typeof M?M(b.val(),void 0,u,y):M,w.mask=M,w.options=y,w.remove=function(){var t=b.getCaret();return w.options.placeholder&&u.removeAttr("placeholder"),u.data("mask-maxlength")&&u.removeAttr("maxlength"),b.destroyEvents(),b.val(w.getCleanVal()),b.setCaret(t),u},w.getCleanVal=function(){return b.getMasked(!0)},w.getMaskedVal=function(t){return b.getMasked(!1,t)},w.init=function(t){if(t=t||!1,y=y||{},w.clearIfNotMatch=o.jMaskGlobals.clearIfNotMatch,w.byPassKeys=o.jMaskGlobals.byPassKeys,w.translation=o.extend({},o.jMaskGlobals.translation,y.translation),w=o.extend(!0,{},w,y),s=b.getRegexMask(),t)b.events(),b.val(b.getMasked());else{y.placeholder&&u.attr("placeholder",y.placeholder),u.data("mask")&&u.attr("autocomplete","off");for(var a=0,e=!0;a<M.length;a++){var n=w.translation[M.charAt(a)];if(n&&n.recursive){e=!1;break}}e&&u.attr("maxlength",M.length).data("mask-maxlength",!0),b.destroyEvents(),b.events();t=b.getCaret();b.val(b.getMasked()),b.setCaret(t)}},w.init(!u.is("input"))}o.maskWatchers={};function a(){var t=o(this),a={},e="data-mask-",n=t.attr("data-mask");if(t.attr(e+"reverse")&&(a.reverse=!0),t.attr(e+"clearifnotmatch")&&(a.clearIfNotMatch=!0),"true"===t.attr(e+"selectonfocus")&&(a.selectOnFocus=!0),l(t,n,a))return t.data("mask",new i(this,n,a))}function l(t,a,e){e=e||{};var n=o(t).data("mask"),s=JSON.stringify,t=o(t).val()||o(t).text();try{return"function"==typeof a&&(a=a(t)),"object"!=typeof n||s(n.options)!==s(e)||n.mask!==a}catch(t){}}o.fn.mask=function(t,a){a=a||{};function e(){if(l(this,t,a))return o(this).data("mask",new i(this,t,a))}var n=this.selector,s=o.jMaskGlobals,r=s.watchInterval,s=a.watchInputs||s.watchInputs;return o(this).each(e),n&&""!==n&&s&&(clearInterval(o.maskWatchers[n]),o.maskWatchers[n]=setInterval(function(){o(document).find(n).each(e)},r)),this},o.fn.masked=function(t){return this.data("mask").getMaskedVal(t)},o.fn.unmask=function(){return clearInterval(o.maskWatchers[this.selector]),delete o.maskWatchers[this.selector],this.each(function(){var t=o(this).data("mask");t&&t.remove().removeData("mask")})},o.fn.cleanVal=function(){return this.data("mask").getCleanVal()},o.applyDataMask=function(t){((t=t||o.jMaskGlobals.maskElements)instanceof o?t:o(t)).filter(o.jMaskGlobals.dataMaskAttr).each(a)};var t,e,n={maskElements:"input,td,span,div",dataMaskAttr:"*[data-mask]",dataMask:!0,watchInterval:300,watchInputs:!0,keyStrokeCompensation:10,useInput:!/Chrome\/[2-4][0-9]|SamsungBrowser/.test(window.navigator.userAgent)&&(t="input",e=document.createElement("div"),(n=(t="on"+t)in e)||(e.setAttribute(t,"return;"),n="function"==typeof e[t]),e=null,n),watchDataMask:!1,byPassKeys:[9,16,17,18,36,37,38,39,40,91],translation:{0:{pattern:/\d/},9:{pattern:/\d/,optional:!0},"#":{pattern:/\d/,recursive:!0},A:{pattern:/[a-zA-Z0-9]/},S:{pattern:/[a-zA-Z]/}}};o.jMaskGlobals=o.jMaskGlobals||{},(n=o.jMaskGlobals=o.extend(!0,{},n,o.jMaskGlobals)).dataMask&&o.applyDataMask(),setInterval(function(){o.jMaskGlobals.watchDataMask&&o.applyDataMask()},n.watchInterval)},window.jQuery,window.Zepto);
diff --git a/assets/js/stripe-payment-request.js b/assets/js/stripe-payment-request.js
new file mode 100644
index 0000000..3b4fc54
--- /dev/null
+++ b/assets/js/stripe-payment-request.js
@@ -0,0 +1,825 @@
+/* global wc_stripe_payment_request_params, Stripe */
+jQuery( function( $ ) {
+	'use strict';
+
+	var stripe = Stripe( wc_stripe_payment_request_params.stripe.key, {
+		locale: wc_stripe_payment_request_params.stripe.locale
+	} ),
+		paymentRequestType;
+
+	/**
+	 * Object to handle Stripe payment forms.
+	 */
+	var wc_stripe_payment_request = {
+		/**
+		 * Get WC AJAX endpoint URL.
+		 *
+		 * @param  {String} endpoint Endpoint.
+		 * @return {String}
+		 */
+		getAjaxURL: function( endpoint ) {
+			return wc_stripe_payment_request_params.ajax_url
+				.toString()
+				.replace( '%%endpoint%%', 'wc_stripe_' + endpoint );
+		},
+
+		getCartDetails: function() {
+			var data = {
+				security: wc_stripe_payment_request_params.nonce.payment
+			};
+
+			$.ajax( {
+				type:    'POST',
+				data:    data,
+				url:     wc_stripe_payment_request.getAjaxURL( 'get_cart_details' ),
+				success: function( response ) {
+					wc_stripe_payment_request.startPaymentRequest( response );
+				}
+			} );
+		},
+
+		getAttributes: function() {
+			var select = $( '.variations_form' ).find( '.variations select' ),
+				data   = {},
+				count  = 0,
+				chosen = 0;
+
+			select.each( function() {
+				var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
+				var value          = $( this ).val() || '';
+
+				if ( value.length > 0 ) {
+					chosen ++;
+				}
+
+				count ++;
+				data[ attribute_name ] = value;
+			});
+
+			return {
+				'count'      : count,
+				'chosenCount': chosen,
+				'data'       : data
+			};
+		},
+
+		processSource: function( source, paymentRequestType ) {
+			var data = wc_stripe_payment_request.getOrderData( source, paymentRequestType );
+
+			return $.ajax( {
+				type:    'POST',
+				data:    data,
+				dataType: 'json',
+				url:     wc_stripe_payment_request.getAjaxURL( 'create_order' )
+			} );
+		},
+
+		/**
+		 * Get order data.
+		 *
+		 * @since 3.1.0
+		 * @version 4.0.0
+		 * @param {PaymentResponse} source Payment Response instance.
+		 *
+		 * @return {Object}
+		 */
+		getOrderData: function( evt, paymentRequestType ) {
+			var source   = evt.source;
+			var email    = source.owner.email;
+			var phone    = source.owner.phone;
+			var billing  = source.owner.address;
+			var name     = source.owner.name;
+			var shipping = evt.shippingAddress;
+			var data     = {
+				_wpnonce:                  wc_stripe_payment_request_params.nonce.checkout,
+				billing_first_name:        null !== name ? name.split( ' ' ).slice( 0, 1 ).join( ' ' ) : '',
+				billing_last_name:         null !== name ? name.split( ' ' ).slice( 1 ).join( ' ' ) : '',
+				billing_company:           '',
+				billing_email:             null !== email   ? email : evt.payerEmail,
+				billing_phone:             null !== phone   ? phone : evt.payerPhone && evt.payerPhone.replace( '/[() -]/g', '' ),
+				billing_country:           null !== billing ? billing.country : '',
+				billing_address_1:         null !== billing ? billing.line1 : '',
+				billing_address_2:         null !== billing ? billing.line2 : '',
+				billing_city:              null !== billing ? billing.city : '',
+				billing_state:             null !== billing ? billing.state : '',
+				billing_postcode:          null !== billing ? billing.postal_code : '',
+				shipping_first_name:       '',
+				shipping_last_name:        '',
+				shipping_company:          '',
+				shipping_country:          '',
+				shipping_address_1:        '',
+				shipping_address_2:        '',
+				shipping_city:             '',
+				shipping_state:            '',
+				shipping_postcode:         '',
+				shipping_method:           [ null === evt.shippingOption ? null : evt.shippingOption.id ],
+				order_comments:            '',
+				payment_method:            'stripe',
+				ship_to_different_address: 1,
+				terms:                     1,
+				stripe_source:             source.id,
+				payment_request_type:      paymentRequestType
+			};
+
+			if ( shipping ) {
+				data.shipping_first_name = shipping.recipient.split( ' ' ).slice( 0, 1 ).join( ' ' );
+				data.shipping_last_name  = shipping.recipient.split( ' ' ).slice( 1 ).join( ' ' );
+				data.shipping_company    = shipping.organization;
+				data.shipping_country    = shipping.country;
+				data.shipping_address_1  = typeof shipping.addressLine[0] === 'undefined' ? '' : shipping.addressLine[0];
+				data.shipping_address_2  = typeof shipping.addressLine[1] === 'undefined' ? '' : shipping.addressLine[1];
+				data.shipping_city       = shipping.city;
+				data.shipping_state      = shipping.region;
+				data.shipping_postcode   = shipping.postalCode;
+			}
+
+			return data;
+		},
+
+		/**
+		 * Generate error message HTML.
+		 *
+		 * @since 3.1.0
+		 * @version 4.0.0
+		 * @param  {String} message Error message.
+		 * @return {Object}
+		 */
+		getErrorMessageHTML: function( message ) {
+			return $( '<div class="woocommerce-error" />' ).text( message );
+		},
+
+		/**
+		 * Display error messages.
+		 *
+		 * @since 4.8.0
+		 * @param {Object} message DOM object with error message to display.
+		 */
+		displayErrorMessage: function( message ) {
+			$( '.woocommerce-error' ).remove();
+
+			if ( wc_stripe_payment_request_params.is_product_page ) {
+				var element = $( '.product' ).first();
+				element.before( message );
+
+				$( 'html, body' ).animate({
+					scrollTop: element.prev( '.woocommerce-error' ).offset().top
+				}, 600 );
+			} else {
+				var $form = $( '.shop_table.cart' ).closest( 'form' );
+				$form.before( message );
+				$( 'html, body' ).animate({
+					scrollTop: $form.prev( '.woocommerce-error' ).offset().top
+				}, 600 );
+			}
+		},
+
+		/**
+		 * Abort payment and display error messages.
+		 *
+		 * @since 3.1.0
+		 * @version 4.8.0
+		 * @param {PaymentResponse} payment Payment response instance.
+		 * @param {Object}          message DOM object with error message to display.
+		 */
+		abortPayment: function( payment, message ) {
+			payment.complete( 'fail' );
+			wc_stripe_payment_request.displayErrorMessage( message );
+		},
+
+		/**
+		 * Complete payment.
+		 *
+		 * @since 3.1.0
+		 * @version 4.0.0
+		 * @param {PaymentResponse} payment Payment response instance.
+		 * @param {String}          url     Order thank you page URL.
+		 */
+		completePayment: function( payment, url ) {
+			wc_stripe_payment_request.block();
+
+			payment.complete( 'success' );
+
+			// Success, then redirect to the Thank You page.
+			window.location = url;
+		},
+
+		block: function() {
+			$.blockUI( {
+				message: null,
+				overlayCSS: {
+					background: '#fff',
+					opacity: 0.6
+				}
+			} );
+		},
+
+		/**
+		 * Update shipping options.
+		 *
+		 * @param {Object}         details Payment details.
+		 * @param {PaymentAddress} address Shipping address.
+		 */
+		updateShippingOptions: function( details, address ) {
+			var data = {
+				security:  wc_stripe_payment_request_params.nonce.shipping,
+				country:   address.country,
+				state:     address.region,
+				postcode:  address.postalCode,
+				city:      address.city,
+				address:   typeof address.addressLine[0] === 'undefined' ? '' : address.addressLine[0],
+				address_2: typeof address.addressLine[1] === 'undefined' ? '' : address.addressLine[1],
+				payment_request_type: paymentRequestType,
+				is_product_page: wc_stripe_payment_request_params.is_product_page,
+			};
+
+			return $.ajax( {
+				type:    'POST',
+				data:    data,
+				url:     wc_stripe_payment_request.getAjaxURL( 'get_shipping_options' )
+			} );
+		},
+
+		/**
+		 * Updates the shipping price and the total based on the shipping option.
+		 *
+		 * @param {Object}   details        The line items and shipping options.
+		 * @param {String}   shippingOption User's preferred shipping option to use for shipping price calculations.
+		 */
+		updateShippingDetails: function( details, shippingOption ) {
+			var data = {
+				security: wc_stripe_payment_request_params.nonce.update_shipping,
+				shipping_method: [ shippingOption.id ],
+				payment_request_type: paymentRequestType,
+				is_product_page: wc_stripe_payment_request_params.is_product_page,
+			};
+
+			return $.ajax( {
+				type: 'POST',
+				data: data,
+				url:  wc_stripe_payment_request.getAjaxURL( 'update_shipping_method' )
+			} );
+		},
+
+		/**
+		 * Adds the item to the cart and return cart details.
+		 *
+		 */
+		addToCart: function() {
+			var product_id = $( '.single_add_to_cart_button' ).val();
+
+			// Check if product is a variable product.
+			if ( $( '.single_variation_wrap' ).length ) {
+				product_id = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
+			}
+
+			var data = {
+				security: wc_stripe_payment_request_params.nonce.add_to_cart,
+				product_id: product_id,
+				qty: $( '.quantity .qty' ).val(),
+				attributes: $( '.variations_form' ).length ? wc_stripe_payment_request.getAttributes().data : []
+			};
+
+			// add addons data to the POST body
+			var formData = $( 'form.cart' ).serializeArray();
+			$.each( formData, function( i, field ) {
+				if ( /^addon-/.test( field.name ) ) {
+					if ( /\[\]$/.test( field.name ) ) {
+						var fieldName = field.name.substring( 0, field.name.length - 2);
+						if ( data[ fieldName ] ) {
+							data[ fieldName ].push( field.value );
+						} else {
+							data[ fieldName ] = [ field.value ];
+						}
+					} else {
+						data[ field.name ] = field.value;
+					}
+				}
+			} );
+
+			return $.ajax( {
+				type: 'POST',
+				data: data,
+				url:  wc_stripe_payment_request.getAjaxURL( 'add_to_cart' )
+			} );
+		},
+
+		clearCart: function() {
+			var data = {
+					'security': wc_stripe_payment_request_params.nonce.clear_cart
+				};
+
+			return $.ajax( {
+				type:    'POST',
+				data:    data,
+				url:     wc_stripe_payment_request.getAjaxURL( 'clear_cart' ),
+				success: function( response ) {}
+			} );
+		},
+
+		getRequestOptionsFromLocal: function() {
+			return {
+				total: wc_stripe_payment_request_params.product.total,
+				currency: wc_stripe_payment_request_params.checkout.currency_code,
+				country: wc_stripe_payment_request_params.checkout.country_code,
+				requestPayerName: true,
+				requestPayerEmail: true,
+				requestPayerPhone: wc_stripe_payment_request_params.checkout.needs_payer_phone,
+				requestShipping: wc_stripe_payment_request_params.product.requestShipping,
+				displayItems: wc_stripe_payment_request_params.product.displayItems
+			};
+		},
+
+		/**
+		 * Starts the payment request
+		 *
+		 * @since 4.0.0
+		 * @version 4.8.0
+		 */
+		startPaymentRequest: function( cart ) {
+			var paymentDetails,
+				options;
+
+			if ( wc_stripe_payment_request_params.is_product_page ) {
+				options = wc_stripe_payment_request.getRequestOptionsFromLocal();
+
+				paymentDetails = options;
+			} else {
+				options = {
+					total: cart.order_data.total,
+					currency: cart.order_data.currency,
+					country: cart.order_data.country_code,
+					requestPayerName: true,
+					requestPayerEmail: true,
+					requestPayerPhone: wc_stripe_payment_request_params.checkout.needs_payer_phone,
+					requestShipping: cart.shipping_required ? true : false,
+					displayItems: cart.order_data.displayItems
+				};
+
+				paymentDetails = cart.order_data;
+			}
+
+			// Puerto Rico (PR) is the only US territory/possession that's supported by Stripe.
+			// Since it's considered a US state by Stripe, we need to do some special mapping.
+			if ( 'PR' === options.country ) {
+				options.country = 'US';
+			}
+
+			// Handle errors thrown by Stripe, so we don't break the product page
+			try {
+				var paymentRequest = stripe.paymentRequest( options );
+
+				var elements = stripe.elements( { locale: wc_stripe_payment_request_params.button.locale } );
+				var prButton = wc_stripe_payment_request.createPaymentRequestButton( elements, paymentRequest );
+
+				// Check the availability of the Payment Request API first.
+				paymentRequest.canMakePayment().then( function( result ) {
+					if ( ! result ) {
+						return;
+					}
+					if ( result.applePay ) {
+						paymentRequestType = 'apple_pay';
+					} else if ( result.googlePay ) {
+						paymentRequestType = 'google_pay';
+					} else {
+						paymentRequestType = 'payment_request_api';
+					}
+
+					wc_stripe_payment_request.attachPaymentRequestButtonEventListeners( prButton, paymentRequest );
+					wc_stripe_payment_request.showPaymentRequestButton( prButton );
+				} );
+
+				// Possible statuses success, fail, invalid_payer_name, invalid_payer_email, invalid_payer_phone, invalid_shipping_address.
+				paymentRequest.on( 'shippingaddresschange', function( evt ) {
+					$.when( wc_stripe_payment_request.updateShippingOptions( paymentDetails, evt.shippingAddress ) ).then( function( response ) {
+						evt.updateWith( { status: response.result, shippingOptions: response.shipping_options, total: response.total, displayItems: response.displayItems } );
+					} );
+				} );
+
+				paymentRequest.on( 'shippingoptionchange', function( evt ) {
+					$.when( wc_stripe_payment_request.updateShippingDetails( paymentDetails, evt.shippingOption ) ).then( function( response ) {
+						if ( 'success' === response.result ) {
+							evt.updateWith( { status: 'success', total: response.total, displayItems: response.displayItems } );
+						}
+
+						if ( 'fail' === response.result ) {
+							evt.updateWith( { status: 'fail' } );
+						}
+					} );
+				} );
+
+				paymentRequest.on( 'source', function( evt ) {
+					// Check if we allow prepaid cards.
+					if ( 'no' === wc_stripe_payment_request_params.stripe.allow_prepaid_card && 'prepaid' === evt.source.card.funding ) {
+						wc_stripe_payment_request.abortPayment( evt, wc_stripe_payment_request.getErrorMessageHTML( wc_stripe_payment_request_params.i18n.no_prepaid_card ) );
+					} else {
+						$.when( wc_stripe_payment_request.processSource( evt, paymentRequestType ) ).then( function( response ) {
+							if ( 'success' === response.result ) {
+								wc_stripe_payment_request.completePayment( evt, response.redirect );
+							} else {
+								wc_stripe_payment_request.abortPayment( evt, response.messages );
+							}
+						} );
+					}
+				} );
+			} catch( e ) {
+				// Leave for troubleshooting
+				console.error( e );
+			}
+		},
+
+		getSelectedProductData: function() {
+			var product_id = $( '.single_add_to_cart_button' ).val();
+
+			// Check if product is a variable product.
+			if ( $( '.single_variation_wrap' ).length ) {
+				product_id = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
+			}
+
+			var addons = $( '#product-addons-total' ).data('price_data') || [];
+			var addon_value = addons.reduce( function ( sum, addon ) { return sum + addon.cost; }, 0 );
+
+			var data = {
+				security: wc_stripe_payment_request_params.nonce.get_selected_product_data,
+				product_id: product_id,
+				qty: $( '.quantity .qty' ).val(),
+				attributes: $( '.variations_form' ).length ? wc_stripe_payment_request.getAttributes().data : [],
+				addon_value: addon_value,
+			};
+
+			return $.ajax( {
+				type: 'POST',
+				data: data,
+				url:  wc_stripe_payment_request.getAjaxURL( 'get_selected_product_data' )
+			} );
+		},
+
+		/**
+		 * Creates a wrapper around a function that ensures a function can not
+		 * called in rappid succesion. The function can only be executed once and then agin after
+		 * the wait time has expired.  Even if the wrapper is called multiple times, the wrapped
+		 * function only excecutes once and then blocks until the wait time expires.
+		 *
+		 * @param {int} wait       Milliseconds wait for the next time a function can be executed.
+		 * @param {function} func       The function to be wrapped.
+		 * @param {bool} immediate Overriding the wait time, will force the function to fire everytime.
+		 *
+		 * @return {function} A wrapped function with execution limited by the wait time.
+		 */
+		debounce: function( wait, func, immediate ) {
+			var timeout;
+			return function() {
+				var context = this, args = arguments;
+				var later = function() {
+					timeout = null;
+					if (!immediate) func.apply(context, args);
+				};
+				var callNow = immediate && !timeout;
+				clearTimeout(timeout);
+				timeout = setTimeout(later, wait);
+				if (callNow) func.apply(context, args);
+			};
+		},
+
+		/**
+		 * Creates stripe paymentRequest element or connects to custom button
+		 *
+		 * @param {object} elements       Stripe elements instance.
+		 * @param {object} paymentRequest Stripe paymentRequest object.
+		 *
+		 * @return {object} Stripe paymentRequest element or custom button jQuery element.
+		 */
+		createPaymentRequestButton: function( elements, paymentRequest ) {
+			var button;
+			if ( wc_stripe_payment_request_params.button.is_custom ) {
+				button = $( wc_stripe_payment_request_params.button.css_selector );
+				if ( button.length ) {
+					// We fallback to default paymentRequest button if no custom button is found in the UI.
+					// Add flag to be sure that created button is custom button rather than fallback element.
+					button.data( 'isCustom', true );
+					return button;
+				}
+			}
+
+			if ( wc_stripe_payment_request_params.button.is_branded ) {
+				if ( wc_stripe_payment_request.shouldUseGooglePayBrand() ) {
+					button = wc_stripe_payment_request.createGooglePayButton();
+					// Add flag to be sure that created button is branded rather than fallback element.
+					button.data( 'isBranded', true );
+					return button;
+				} else {
+					// Not implemented branded buttons default to Stripe's button
+					// Apple Pay buttons can also fall back to Stripe's button, as it's already branded
+					// Set button type to default or buy, depending on branded type, to avoid issues with Stripe
+					wc_stripe_payment_request_params.button.type = 'long' === wc_stripe_payment_request_params.button.branded_type ? 'buy' : 'default';
+				}
+			}
+
+			return elements.create( 'paymentRequestButton', {
+				paymentRequest: paymentRequest,
+				style: {
+					paymentRequestButton: {
+						type: wc_stripe_payment_request_params.button.type,
+						theme: wc_stripe_payment_request_params.button.theme,
+						height: wc_stripe_payment_request_params.button.height + 'px',
+					},
+				},
+			} );
+		},
+
+		/**
+		 * Checks if button is custom payment request button.
+		 *
+		 * @param {object} prButton Stripe paymentRequest element or custom jQuery element.
+		 *
+		 * @return {boolean} True when prButton is custom button jQuery element.
+		 */
+		isCustomPaymentRequestButton: function ( prButton ) {
+			return prButton && 'function' === typeof prButton.data && prButton.data( 'isCustom' );
+		},
+
+		isBrandedPaymentRequestButton: function ( prButton ) {
+			return prButton && 'function' === typeof prButton.data && prButton.data( 'isBranded' );
+		},
+
+		shouldUseGooglePayBrand: function () {
+			var ua = window.navigator.userAgent.toLowerCase();
+			var isChrome = /chrome/.test( ua ) && ! /edge|edg|opr|brave\//.test( ua ) && 'Google Inc.' === window.navigator.vendor;
+			// newer versions of Brave do not have the userAgent string
+			var isBrave = isChrome && window.navigator.brave;
+			return isChrome && ! isBrave;
+		},
+
+		createGooglePayButton: function () {
+			var allowedThemes = [ 'dark', 'light', 'light-outline' ];
+			var allowedTypes = [ 'short', 'long' ];
+
+			var theme  = wc_stripe_payment_request_params.button.theme;
+			var type   = wc_stripe_payment_request_params.button.branded_type;
+			var locale = wc_stripe_payment_request_params.button.locale;
+			var height = wc_stripe_payment_request_params.button.height;
+			theme = allowedThemes.includes( theme ) ? theme : 'light';
+			var gpaySvgTheme = 'dark' === theme ? 'dark' : 'light';
+			type = allowedTypes.includes( type ) ? type : 'long';
+
+			var button = $( '<button type="button" id="wc-stripe-branded-button" aria-label="Google Pay" class="gpay-button"></button>' );
+			button.css( 'height', height + 'px' );
+			button.addClass( theme + ' ' + type );
+			if ( 'long' === type ) {
+				var url = 'https://www.gstatic.com/instantbuy/svg/' + gpaySvgTheme + '/' + locale + '.svg';
+				var fallbackUrl = 'https://www.gstatic.com/instantbuy/svg/' + gpaySvgTheme + '/en.svg';
+				// Check if locale GPay button exists, default to en if not
+				setBackgroundImageWithFallback( button, url, fallbackUrl );
+			}
+
+			return button;
+		},
+
+		attachPaymentRequestButtonEventListeners: function( prButton, paymentRequest ) {
+			// First, mark the body so we know a payment request button was used.
+			// This way error handling can any display errors in the most appropriate place.
+			prButton.on( 'click', function ( evt ) {
+				$( 'body' ).addClass( 'woocommerce-stripe-prb-clicked' );
+			});
+
+			// Then, attach specific handling for selected pages and button types
+			if ( wc_stripe_payment_request_params.is_product_page ) {
+				wc_stripe_payment_request.attachProductPageEventListeners( prButton, paymentRequest );
+			} else {
+				wc_stripe_payment_request.attachCartPageEventListeners( prButton, paymentRequest );
+			}
+		},
+
+		attachProductPageEventListeners: function( prButton, paymentRequest ) {
+			var paymentRequestError = [];
+			var addToCartButton = $( '.single_add_to_cart_button' );
+
+			prButton.on( 'click', function ( evt ) {
+				// If login is required for checkout, display redirect confirmation dialog.
+				if ( wc_stripe_payment_request_params.login_confirmation ) {
+					evt.preventDefault();
+					displayLoginConfirmation( paymentRequestType );
+					return;
+				}
+
+				// First check if product can be added to cart.
+				if ( addToCartButton.is( '.disabled' ) ) {
+					evt.preventDefault(); // Prevent showing payment request modal.
+					if ( addToCartButton.is( '.wc-variation-is-unavailable' ) ) {
+						window.alert( wc_add_to_cart_variation_params.i18n_unavailable_text );
+					} else if ( addToCartButton.is( '.wc-variation-selection-needed' ) ) {
+						window.alert( wc_add_to_cart_variation_params.i18n_make_a_selection_text );
+					}
+					return;
+				}
+
+				if ( 0 < paymentRequestError.length ) {
+					evt.preventDefault();
+					window.alert( paymentRequestError );
+					return;
+				}
+
+				wc_stripe_payment_request.addToCart();
+
+				if ( wc_stripe_payment_request.isCustomPaymentRequestButton( prButton ) || wc_stripe_payment_request.isBrandedPaymentRequestButton( prButton ) ) {
+					evt.preventDefault();
+					paymentRequest.show();
+				}
+			});
+
+			$( document.body ).on( 'wc_stripe_unblock_payment_request_button wc_stripe_enable_payment_request_button', function () {
+				wc_stripe_payment_request.unblockPaymentRequestButton();
+			} );
+
+			$( document.body ).on( 'wc_stripe_block_payment_request_button', function () {
+				wc_stripe_payment_request.blockPaymentRequestButton( 'wc_request_button_is_blocked' );
+			} );
+
+			$( document.body ).on( 'wc_stripe_disable_payment_request_button', function () {
+				wc_stripe_payment_request.blockPaymentRequestButton( 'wc_request_button_is_disabled' );
+			} );
+
+			$( document.body ).on( 'woocommerce_variation_has_changed', function () {
+				$( document.body ).trigger( 'wc_stripe_block_payment_request_button' );
+
+				$.when( wc_stripe_payment_request.getSelectedProductData() ).then( function ( response ) {
+					$.when(
+						paymentRequest.update( {
+							total: response.total,
+							displayItems: response.displayItems,
+						} )
+					).then( function () {
+						$( document.body ).trigger( 'wc_stripe_unblock_payment_request_button' );
+					} );
+				});
+			} );
+
+			// Block the payment request button as soon as an "input" event is fired, to avoid sync issues
+			// when the customer clicks on the button before the debounced event is processed.
+			$( '.quantity' ).on( 'input', '.qty', function() {
+				$( document.body ).trigger( 'wc_stripe_block_payment_request_button' );
+			} );
+
+			$( '.quantity' ).on( 'input', '.qty', wc_stripe_payment_request.debounce( 250, function() {
+				$( document.body ).trigger( 'wc_stripe_block_payment_request_button' );
+				paymentRequestError = [];
+
+				$.when( wc_stripe_payment_request.getSelectedProductData() ).then( function ( response ) {
+					if ( response.error ) {
+						paymentRequestError = [ response.error ];
+						$( document.body ).trigger( 'wc_stripe_unblock_payment_request_button' );
+					} else {
+						$.when(
+							paymentRequest.update( {
+								total: response.total,
+								displayItems: response.displayItems,
+							} )
+						).then( function () {
+							$( document.body ).trigger( 'wc_stripe_unblock_payment_request_button' );
+						});
+					}
+				} );
+			}));
+
+			if ( $('.variations_form').length ) {
+				$( '.variations_form' ).on( 'found_variation.wc-variation-form', function ( evt, variation ) {
+					if ( variation.is_in_stock ) {
+						wc_stripe_payment_request.unhidePaymentRequestButton();
+					} else {
+						wc_stripe_payment_request.hidePaymentRequestButton();
+					}
+				} );
+			}
+		},
+
+		attachCartPageEventListeners: function ( prButton, paymentRequest ) {
+			prButton.on( 'click', function ( evt ) {
+				// If login is required for checkout, display redirect confirmation dialog.
+				if ( wc_stripe_payment_request_params.login_confirmation ) {
+					evt.preventDefault();
+					displayLoginConfirmation( paymentRequestType );
+					return;
+				}
+
+				if (
+					wc_stripe_payment_request.isCustomPaymentRequestButton(
+						prButton
+					) ||
+					wc_stripe_payment_request.isBrandedPaymentRequestButton(
+						prButton
+					)
+				) {
+					evt.preventDefault();
+					paymentRequest.show();
+				}
+			} );
+		},
+
+		showPaymentRequestButton: function( prButton ) {
+			if ( wc_stripe_payment_request.isCustomPaymentRequestButton( prButton ) ) {
+				prButton.addClass( 'is-active' );
+				$( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).show();
+			} else if ( wc_stripe_payment_request.isBrandedPaymentRequestButton( prButton ) ) {
+				$( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).show();
+				$( '#wc-stripe-payment-request-button' ).html( prButton );
+			} else if ( $( '#wc-stripe-payment-request-button' ).length ) {
+				$( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).show();
+				prButton.mount( '#wc-stripe-payment-request-button' );
+			}
+		},
+
+		hidePaymentRequestButton: function () {
+			$( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).hide();
+		},
+
+		unhidePaymentRequestButton: function () {
+			const stripe_wrapper = $( '#wc-stripe-payment-request-wrapper' );
+			const stripe_separator = $( '#wc-stripe-payment-request-button-separator' );
+			// If either element is hidden, ensure both show.
+			if ( stripe_wrapper.is(':hidden') || stripe_separator.is(':hidden') ) {
+				stripe_wrapper.show();
+				stripe_separator.show();
+			}
+		},
+
+		blockPaymentRequestButton: function( cssClassname ) {
+			// check if element isn't already blocked before calling block() to avoid blinking overlay issues
+			// blockUI.isBlocked is either undefined or 0 when element is not blocked
+			if ( $( '#wc-stripe-payment-request-button' ).data( 'blockUI.isBlocked' ) ) {
+				return;
+			}
+
+			$( '#wc-stripe-payment-request-button' )
+				.addClass( cssClassname )
+				.block( { message: null } );
+		},
+
+		unblockPaymentRequestButton: function() {
+			$( '#wc-stripe-payment-request-button' )
+				.removeClass( ['wc_request_button_is_blocked', 'wc_request_button_is_disabled'] )
+				.unblock();
+		},
+
+		/**
+		 * Initialize event handlers and UI state
+		 *
+		 * @since 4.0.0
+		 * @version 4.0.0
+		 */
+		init: function() {
+			if ( wc_stripe_payment_request_params.is_product_page ) {
+				wc_stripe_payment_request.startPaymentRequest( '' );
+			} else {
+				wc_stripe_payment_request.getCartDetails();
+			}
+
+		},
+	};
+
+	wc_stripe_payment_request.init();
+
+	// We need to refresh payment request data when total is updated.
+	$( document.body ).on( 'updated_cart_totals', function() {
+		wc_stripe_payment_request.init();
+	} );
+
+	// We need to refresh payment request data when total is updated.
+	$( document.body ).on( 'updated_checkout', function() {
+		wc_stripe_payment_request.init();
+	} );
+
+	function setBackgroundImageWithFallback( element, background, fallback ) {
+		element.css( 'background-image', 'url(' + background + ')' );
+		// Need to use an img element to avoid CORS issues
+		var testImg = document.createElement("img");
+		testImg.onerror = function () {
+			element.css( 'background-image', 'url(' + fallback + ')' );
+		}
+		testImg.src = background;
+	}
+
+	// TODO: Replace this by `client/blocks/payment-request/login-confirmation.js`
+	// when we start using webpack to build this file.
+	function displayLoginConfirmation( paymentRequestType ) {
+		if ( ! wc_stripe_payment_request_params.login_confirmation ) {
+			return;
+		}
+
+		var message = wc_stripe_payment_request_params.login_confirmation.message;
+
+		// Replace dialog text with specific payment request type "Apple Pay" or "Google Pay".
+		if ( 'payment_request_api' !== paymentRequestType ) {
+			message = message.replace(
+				/\*\*.*?\*\*/,
+				'apple_pay' === paymentRequestType ? 'Apple Pay' : 'Google Pay'
+			);
+		}
+
+		// Remove asterisks from string.
+		message = message.replace( /\*\*/g, '' );
+
+		if ( confirm( message ) ) {
+			// Redirect to my account page.
+			window.location.href = wc_stripe_payment_request_params.login_confirmation.redirect_url;
+		}
+	}
+} );
diff --git a/assets/js/stripe-payment-request.min.js b/assets/js/stripe-payment-request.min.js
new file mode 100644
index 0000000..f11a3d8
--- /dev/null
+++ b/assets/js/stripe-payment-request.min.js
@@ -0,0 +1 @@
+jQuery(function(i){"use strict";var o,p=Stripe(wc_stripe_payment_request_params.stripe.key,{locale:wc_stripe_payment_request_params.stripe.locale}),u={getAjaxURL:function(t){return wc_stripe_payment_request_params.ajax_url.toString().replace("%%endpoint%%","wc_stripe_"+t)},getCartDetails:function(){var t={security:wc_stripe_payment_request_params.nonce.payment};i.ajax({type:"POST",data:t,url:u.getAjaxURL("get_cart_details"),success:function(t){u.startPaymentRequest(t)}})},getAttributes:function(){var t=i(".variations_form").find(".variations select"),n={},a=0,r=0;return t.each(function(){var t=i(this).data("attribute_name")||i(this).attr("name"),e=i(this).val()||"";0<e.length&&r++,a++,n[t]=e}),{count:a,chosenCount:r,data:n}},processSource:function(t,e){e=u.getOrderData(t,e);return i.ajax({type:"POST",data:e,dataType:"json",url:u.getAjaxURL("create_order")})},getOrderData:function(t,e){var n=t.source,a=n.owner.email,r=n.owner.phone,s=n.owner.address,i=n.owner.name,o=t.shippingAddress,e={_wpnonce:wc_stripe_payment_request_params.nonce.checkout,billing_first_name:null!==i?i.split(" ").slice(0,1).join(" "):"",billing_last_name:null!==i?i.split(" ").slice(1).join(" "):"",billing_company:"",billing_email:null!==a?a:t.payerEmail,billing_phone:null!==r?r:t.payerPhone&&t.payerPhone.replace("/[() -]/g",""),billing_country:null!==s?s.country:"",billing_address_1:null!==s?s.line1:"",billing_address_2:null!==s?s.line2:"",billing_city:null!==s?s.city:"",billing_state:null!==s?s.state:"",billing_postcode:null!==s?s.postal_code:"",shipping_first_name:"",shipping_last_name:"",shipping_company:"",shipping_country:"",shipping_address_1:"",shipping_address_2:"",shipping_city:"",shipping_state:"",shipping_postcode:"",shipping_method:[null===t.shippingOption?null:t.shippingOption.id],order_comments:"",payment_method:"stripe",ship_to_different_address:1,terms:1,stripe_source:n.id,payment_request_type:e};return o&&(e.shipping_first_name=o.recipient.split(" ").slice(0,1).join(" "),e.shipping_last_name=o.recipient.split(" ").slice(1).join(" "),e.shipping_company=o.organization,e.shipping_country=o.country,e.shipping_address_1=void 0===o.addressLine[0]?"":o.addressLine[0],e.shipping_address_2=void 0===o.addressLine[1]?"":o.addressLine[1],e.shipping_city=o.city,e.shipping_state=o.region,e.shipping_postcode=o.postalCode),e},getErrorMessageHTML:function(t){return i('<div class="woocommerce-error" />').text(t)},displayErrorMessage:function(t){var e;i(".woocommerce-error").remove(),wc_stripe_payment_request_params.is_product_page?((e=i(".product").first()).before(t),i("html, body").animate({scrollTop:e.prev(".woocommerce-error").offset().top},600)):((e=i(".shop_table.cart").closest("form")).before(t),i("html, body").animate({scrollTop:e.prev(".woocommerce-error").offset().top},600))},abortPayment:function(t,e){t.complete("fail"),u.displayErrorMessage(e)},completePayment:function(t,e){u.block(),t.complete("success"),window.location=e},block:function(){i.blockUI({message:null,overlayCSS:{background:"#fff",opacity:.6}})},updateShippingOptions:function(t,e){e={security:wc_stripe_payment_request_params.nonce.shipping,country:e.country,state:e.region,postcode:e.postalCode,city:e.city,address:void 0===e.addressLine[0]?"":e.addressLine[0],address_2:void 0===e.addressLine[1]?"":e.addressLine[1],payment_request_type:o,is_product_page:wc_stripe_payment_request_params.is_product_page};return i.ajax({type:"POST",data:e,url:u.getAjaxURL("get_shipping_options")})},updateShippingDetails:function(t,e){e={security:wc_stripe_payment_request_params.nonce.update_shipping,shipping_method:[e.id],payment_request_type:o,is_product_page:wc_stripe_payment_request_params.is_product_page};return i.ajax({type:"POST",data:e,url:u.getAjaxURL("update_shipping_method")})},addToCart:function(){var t=i(".single_add_to_cart_button").val();i(".single_variation_wrap").length&&(t=i(".single_variation_wrap").find('input[name="product_id"]').val());var a={security:wc_stripe_payment_request_params.nonce.add_to_cart,product_id:t,qty:i(".quantity .qty").val(),attributes:i(".variations_form").length?u.getAttributes().data:[]},t=i("form.cart").serializeArray();return i.each(t,function(t,e){var n;/^addon-/.test(e.name)&&(/\[\]$/.test(e.name)?(n=e.name.substring(0,e.name.length-2),a[n]?a[n].push(e.value):a[n]=[e.value]):a[e.name]=e.value)}),i.ajax({type:"POST",data:a,url:u.getAjaxURL("add_to_cart")})},clearCart:function(){var t={security:wc_stripe_payment_request_params.nonce.clear_cart};return i.ajax({type:"POST",data:t,url:u.getAjaxURL("clear_cart"),success:function(t){}})},getRequestOptionsFromLocal:function(){return{total:wc_stripe_payment_request_params.product.total,currency:wc_stripe_payment_request_params.checkout.currency_code,country:wc_stripe_payment_request_params.checkout.country_code,requestPayerName:!0,requestPayerEmail:!0,requestPayerPhone:wc_stripe_payment_request_params.checkout.needs_payer_phone,requestShipping:wc_stripe_payment_request_params.product.requestShipping,displayItems:wc_stripe_payment_request_params.product.displayItems}},startPaymentRequest:function(t){var e,n=wc_stripe_payment_request_params.is_product_page?e=u.getRequestOptionsFromLocal():(e={total:t.order_data.total,currency:t.order_data.currency,country:t.order_data.country_code,requestPayerName:!0,requestPayerEmail:!0,requestPayerPhone:wc_stripe_payment_request_params.checkout.needs_payer_phone,requestShipping:!!t.shipping_required,displayItems:t.order_data.displayItems},t.order_data);"PR"===e.country&&(e.country="US");try{var a=p.paymentRequest(e),r=p.elements({locale:wc_stripe_payment_request_params.button.locale}),s=u.createPaymentRequestButton(r,a);a.canMakePayment().then(function(t){t&&(o=t.applePay?"apple_pay":t.googlePay?"google_pay":"payment_request_api",u.attachPaymentRequestButtonEventListeners(s,a),u.showPaymentRequestButton(s))}),a.on("shippingaddresschange",function(e){i.when(u.updateShippingOptions(n,e.shippingAddress)).then(function(t){e.updateWith({status:t.result,shippingOptions:t.shipping_options,total:t.total,displayItems:t.displayItems})})}),a.on("shippingoptionchange",function(e){i.when(u.updateShippingDetails(n,e.shippingOption)).then(function(t){"success"===t.result&&e.updateWith({status:"success",total:t.total,displayItems:t.displayItems}),"fail"===t.result&&e.updateWith({status:"fail"})})}),a.on("source",function(e){"no"===wc_stripe_payment_request_params.stripe.allow_prepaid_card&&"prepaid"===e.source.card.funding?u.abortPayment(e,u.getErrorMessageHTML(wc_stripe_payment_request_params.i18n.no_prepaid_card)):i.when(u.processSource(e,o)).then(function(t){"success"===t.result?u.completePayment(e,t.redirect):u.abortPayment(e,t.messages)})})}catch(t){console.error(t)}},getSelectedProductData:function(){var t=i(".single_add_to_cart_button").val();i(".single_variation_wrap").length&&(t=i(".single_variation_wrap").find('input[name="product_id"]').val());var e=(i("#product-addons-total").data("price_data")||[]).reduce(function(t,e){return t+e.cost},0),e={security:wc_stripe_payment_request_params.nonce.get_selected_product_data,product_id:t,qty:i(".quantity .qty").val(),attributes:i(".variations_form").length?u.getAttributes().data:[],addon_value:e};return i.ajax({type:"POST",data:e,url:u.getAjaxURL("get_selected_product_data")})},debounce:function(a,r,s){var i;return function(){var t=this,e=arguments,n=s&&!i;clearTimeout(i),i=setTimeout(function(){i=null,s||r.apply(t,e)},a),n&&r.apply(t,e)}},createPaymentRequestButton:function(t,e){var n;if(wc_stripe_payment_request_params.button.is_custom&&(n=i(wc_stripe_payment_request_params.button.css_selector)).length)return n.data("isCustom",!0),n;if(wc_stripe_payment_request_params.button.is_branded){if(u.shouldUseGooglePayBrand())return(n=u.createGooglePayButton()).data("isBranded",!0),n;wc_stripe_payment_request_params.button.type="long"===wc_stripe_payment_request_params.button.branded_type?"buy":"default"}return t.create("paymentRequestButton",{paymentRequest:e,style:{paymentRequestButton:{type:wc_stripe_payment_request_params.button.type,theme:wc_stripe_payment_request_params.button.theme,height:wc_stripe_payment_request_params.button.height+"px"}}})},isCustomPaymentRequestButton:function(t){return t&&"function"==typeof t.data&&t.data("isCustom")},isBrandedPaymentRequestButton:function(t){return t&&"function"==typeof t.data&&t.data("isBranded")},shouldUseGooglePayBrand:function(){var t=window.navigator.userAgent.toLowerCase(),e=/chrome/.test(t)&&!/edge|edg|opr|brave\//.test(t)&&"Google Inc."===window.navigator.vendor,t=e&&window.navigator.brave;return e&&!t},createGooglePayButton:function(){var t=wc_stripe_payment_request_params.button.theme,e=wc_stripe_payment_request_params.button.branded_type,n=wc_stripe_payment_request_params.button.locale,a=wc_stripe_payment_request_params.button.height,r="dark"===(t=["dark","light","light-outline"].includes(t)?t:"light")?"dark":"light",e=["short","long"].includes(e)?e:"long",s=i('<button type="button" id="wc-stripe-branded-button" aria-label="Google Pay" class="gpay-button"></button>');return s.css("height",a+"px"),s.addClass(t+" "+e),"long"===e&&function(t,e,n){t.css("background-image","url("+e+")");var a=document.createElement("img");a.onerror=function(){t.css("background-image","url("+n+")")},a.src=e}(s,"https://www.gstatic.com/instantbuy/svg/"+r+"/"+n+".svg","https://www.gstatic.com/instantbuy/svg/"+r+"/en.svg"),s},attachPaymentRequestButtonEventListeners:function(t,e){t.on("click",function(t){i("body").addClass("woocommerce-stripe-prb-clicked")}),wc_stripe_payment_request_params.is_product_page?u.attachProductPageEventListeners(t,e):u.attachCartPageEventListeners(t,e)},attachProductPageEventListeners:function(e,n){var a=[],r=i(".single_add_to_cart_button");e.on("click",function(t){return wc_stripe_payment_request_params.login_confirmation?(t.preventDefault(),void s(o)):r.is(".disabled")?(t.preventDefault(),void(r.is(".wc-variation-is-unavailable")?window.alert(wc_add_to_cart_variation_params.i18n_unavailable_text):r.is(".wc-variation-selection-needed")&&window.alert(wc_add_to_cart_variation_params.i18n_make_a_selection_text))):0<a.length?(t.preventDefault(),void window.alert(a)):(u.addToCart(),void((u.isCustomPaymentRequestButton(e)||u.isBrandedPaymentRequestButton(e))&&(t.preventDefault(),n.show())))}),i(document.body).on("wc_stripe_unblock_payment_request_button wc_stripe_enable_payment_request_button",function(){u.unblockPaymentRequestButton()}),i(document.body).on("wc_stripe_block_payment_request_button",function(){u.blockPaymentRequestButton("wc_request_button_is_blocked")}),i(document.body).on("wc_stripe_disable_payment_request_button",function(){u.blockPaymentRequestButton("wc_request_button_is_disabled")}),i(document.body).on("woocommerce_variation_has_changed",function(){i(document.body).trigger("wc_stripe_block_payment_request_button"),i.when(u.getSelectedProductData()).then(function(t){i.when(n.update({total:t.total,displayItems:t.displayItems})).then(function(){i(document.body).trigger("wc_stripe_unblock_payment_request_button")})})}),i(".quantity").on("input",".qty",function(){i(document.body).trigger("wc_stripe_block_payment_request_button")}),i(".quantity").on("input",".qty",u.debounce(250,function(){i(document.body).trigger("wc_stripe_block_payment_request_button"),a=[],i.when(u.getSelectedProductData()).then(function(t){t.error?(a=[t.error],i(document.body).trigger("wc_stripe_unblock_payment_request_button")):i.when(n.update({total:t.total,displayItems:t.displayItems})).then(function(){i(document.body).trigger("wc_stripe_unblock_payment_request_button")})})})),i(".variations_form").length&&i(".variations_form").on("found_variation.wc-variation-form",function(t,e){e.is_in_stock?u.unhidePaymentRequestButton():u.hidePaymentRequestButton()})},attachCartPageEventListeners:function(e,n){e.on("click",function(t){return wc_stripe_payment_request_params.login_confirmation?(t.preventDefault(),void s(o)):void((u.isCustomPaymentRequestButton(e)||u.isBrandedPaymentRequestButton(e))&&(t.preventDefault(),n.show()))})},showPaymentRequestButton:function(t){u.isCustomPaymentRequestButton(t)?(t.addClass("is-active"),i("#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator").show()):u.isBrandedPaymentRequestButton(t)?(i("#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator").show(),i("#wc-stripe-payment-request-button").html(t)):i("#wc-stripe-payment-request-button").length&&(i("#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator").show(),t.mount("#wc-stripe-payment-request-button"))},hidePaymentRequestButton:function(){i("#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator").hide()},unhidePaymentRequestButton:function(){const t=i("#wc-stripe-payment-request-wrapper"),e=i("#wc-stripe-payment-request-button-separator");(t.is(":hidden")||e.is(":hidden"))&&(t.show(),e.show())},blockPaymentRequestButton:function(t){i("#wc-stripe-payment-request-button").data("blockUI.isBlocked")||i("#wc-stripe-payment-request-button").addClass(t).block({message:null})},unblockPaymentRequestButton:function(){i("#wc-stripe-payment-request-button").removeClass(["wc_request_button_is_blocked","wc_request_button_is_disabled"]).unblock()},init:function(){wc_stripe_payment_request_params.is_product_page?u.startPaymentRequest(""):u.getCartDetails()}};function s(t){var e;wc_stripe_payment_request_params.login_confirmation&&(e=wc_stripe_payment_request_params.login_confirmation.message,e=(e="payment_request_api"!==t?e.replace(/\*\*.*?\*\*/,"apple_pay"===t?"Apple Pay":"Google Pay"):e).replace(/\*\*/g,""),confirm(e)&&(window.location.href=wc_stripe_payment_request_params.login_confirmation.redirect_url))}u.init(),i(document.body).on("updated_cart_totals",function(){u.init()}),i(document.body).on("updated_checkout",function(){u.init()})});
diff --git a/assets/js/stripe.js b/assets/js/stripe.js
new file mode 100644
index 0000000..f055320
--- /dev/null
+++ b/assets/js/stripe.js
@@ -0,0 +1,1041 @@
+/* global wc_stripe_params */
+
+jQuery( function( $ ) {
+	'use strict';
+
+	try {
+		var stripe = Stripe( wc_stripe_params.key, {
+			locale: wc_stripe_params.stripe_locale || 'auto',
+		} );
+	} catch( error ) {
+		console.log( error );
+		return;
+	}
+
+	var stripe_elements_options = Object.keys( wc_stripe_params.elements_options ).length ? wc_stripe_params.elements_options : {},
+		sepa_elements_options   = Object.keys( wc_stripe_params.sepa_elements_options ).length ? wc_stripe_params.sepa_elements_options : {},
+		elements                = stripe.elements( stripe_elements_options ),
+		iban                    = elements.create( 'iban', sepa_elements_options ),
+		stripe_card,
+		stripe_exp,
+		stripe_cvc;
+
+	/**
+	 * Object to handle Stripe elements payment form.
+	 */
+	var wc_stripe_form = {
+		/**
+		 * Get WC AJAX endpoint URL.
+		 *
+		 * @param  {String} endpoint Endpoint.
+		 * @return {String}
+		 */
+		getAjaxURL: function( endpoint ) {
+			return wc_stripe_params.ajaxurl
+				.toString()
+				.replace( '%%endpoint%%', 'wc_stripe_' + endpoint );
+		},
+
+		/**
+		 * Unmounts all Stripe elements when the checkout page is being updated.
+		 */
+		unmountElements: function() {
+			if ( 'yes' === wc_stripe_params.inline_cc_form ) {
+				stripe_card.unmount( '#stripe-card-element' );
+			} else {
+				stripe_card.unmount( '#stripe-card-element' );
+				stripe_exp.unmount( '#stripe-exp-element' );
+				stripe_cvc.unmount( '#stripe-cvc-element' );
+			}
+		},
+
+		/**
+		 * Mounts all elements to their DOM nodes on initial loads and updates.
+		 */
+		mountElements: function() {
+			if ( ! $( '#stripe-card-element' ).length ) {
+				return;
+			}
+
+			if ( 'yes' === wc_stripe_params.inline_cc_form ) {
+				stripe_card.mount( '#stripe-card-element' );
+				return;
+			}
+
+			stripe_card.mount( '#stripe-card-element' );
+			stripe_exp.mount( '#stripe-exp-element' );
+			stripe_cvc.mount( '#stripe-cvc-element' );
+		},
+
+		/**
+		 * Creates all Stripe elements that will be used to enter cards or IBANs.
+		 */
+		createElements: function() {
+			var elementStyles = {
+				base: {
+					iconColor: '#666EE8',
+					color: '#31325F',
+					fontSize: '15px',
+					'::placeholder': {
+				  		color: '#CFD7E0',
+					}
+				}
+			};
+
+			var elementClasses = {
+				focus: 'focused',
+				empty: 'empty',
+				invalid: 'invalid',
+			};
+
+			elementStyles  = wc_stripe_params.elements_styling ? wc_stripe_params.elements_styling : elementStyles;
+			elementClasses = wc_stripe_params.elements_classes ? wc_stripe_params.elements_classes : elementClasses;
+
+			if ( 'yes' === wc_stripe_params.inline_cc_form ) {
+				stripe_card = elements.create( 'card', { style: elementStyles, hidePostalCode: true } );
+
+				stripe_card.addEventListener( 'change', function( event ) {
+					wc_stripe_form.onCCFormChange();
+
+					if ( event.error ) {
+						$( document.body ).trigger( 'stripeError', event );
+					}
+				} );
+			} else {
+				stripe_card = elements.create( 'cardNumber', { style: elementStyles, classes: elementClasses } );
+				stripe_exp  = elements.create( 'cardExpiry', { style: elementStyles, classes: elementClasses } );
+				stripe_cvc  = elements.create( 'cardCvc', { style: elementStyles, classes: elementClasses } );
+
+				stripe_card.addEventListener( 'change', function( event ) {
+					wc_stripe_form.onCCFormChange();
+
+					wc_stripe_form.updateCardBrand( event.brand );
+
+					if ( event.error ) {
+						$( document.body ).trigger( 'stripeError', event );
+					}
+				} );
+
+				stripe_exp.addEventListener( 'change', function( event ) {
+					wc_stripe_form.onCCFormChange();
+
+					if ( event.error ) {
+						$( document.body ).trigger( 'stripeError', event );
+					}
+				} );
+
+				stripe_cvc.addEventListener( 'change', function( event ) {
+					wc_stripe_form.onCCFormChange();
+
+					if ( event.error ) {
+						$( document.body ).trigger( 'stripeError', event );
+					}
+				} );
+			}
+
+			/**
+			 * Only in checkout page we need to delay the mounting of the
+			 * card as some AJAX process needs to happen before we do.
+			 */
+			if ( 'yes' === wc_stripe_params.is_checkout ) {
+				$( document.body ).on( 'updated_checkout', function() {
+					// Don't re-mount if already mounted in DOM.
+					if ( $( '#stripe-card-element' ).children().length ) {
+						return;
+					}
+
+					// Unmount prior to re-mounting.
+					if ( stripe_card ) {
+						wc_stripe_form.unmountElements();
+					}
+
+					wc_stripe_form.mountElements();
+
+					if ( $( '#stripe-iban-element' ).length ) {
+						iban.mount( '#stripe-iban-element' );
+					}
+				} );
+			} else if ( $( 'form#add_payment_method' ).length || $( 'form#order_review' ).length ) {
+				wc_stripe_form.mountElements();
+
+				if ( $( '#stripe-iban-element' ).length ) {
+					iban.mount( '#stripe-iban-element' );
+				}
+			}
+		},
+
+		/**
+		 * Updates the card brand logo with non-inline CC forms.
+		 *
+		 * @param {string} brand The identifier of the chosen brand.
+		 */
+		updateCardBrand: function( brand ) {
+			var brandClass = {
+				'visa': 'stripe-visa-brand',
+				'mastercard': 'stripe-mastercard-brand',
+				'amex': 'stripe-amex-brand',
+				'discover': 'stripe-discover-brand',
+				'diners': 'stripe-diners-brand',
+				'jcb': 'stripe-jcb-brand',
+				'unknown': 'stripe-credit-card-brand'
+			};
+
+			var imageElement = $( '.stripe-card-brand' ),
+				imageClass = 'stripe-credit-card-brand';
+
+			if ( brand in brandClass ) {
+				imageClass = brandClass[ brand ];
+			}
+
+			// Remove existing card brand class.
+			$.each( brandClass, function( index, el ) {
+				imageElement.removeClass( el );
+			} );
+
+			imageElement.addClass( imageClass );
+		},
+
+		/**
+		 * Initialize event handlers and UI state.
+		 */
+		init: function() {
+			// Initialize tokenization script if on change payment method page and pay for order page.
+			if ( 'yes' === wc_stripe_params.is_change_payment_page || 'yes' === wc_stripe_params.is_pay_for_order_page ) {
+				$( document.body ).trigger( 'wc-credit-card-form-init' );
+			}
+
+			// checkout page
+			if ( $( 'form.woocommerce-checkout' ).length ) {
+				this.form = $( 'form.woocommerce-checkout' );
+			}
+
+			$( 'form.woocommerce-checkout' )
+				.on(
+					'checkout_place_order_stripe checkout_place_order_stripe_bancontact checkout_place_order_stripe_sofort checkout_place_order_stripe_giropay checkout_place_order_stripe_ideal checkout_place_order_stripe_alipay checkout_place_order_stripe_sepa checkout_place_order_stripe_boleto checkout_place_order_stripe_oxxo',
+					this.onSubmit
+				);
+
+			// pay order page
+			if ( $( 'form#order_review' ).length ) {
+				this.form = $( 'form#order_review' );
+			}
+
+			$( 'form#order_review, form#add_payment_method' )
+				.on(
+					'submit',
+					this.onSubmit
+				);
+
+			// add payment method page
+			if ( $( 'form#add_payment_method' ).length ) {
+				this.form = $( 'form#add_payment_method' );
+			}
+
+			$( 'form.woocommerce-checkout' )
+				.on(
+					'change',
+					this.reset
+				);
+
+			$( document )
+				.on(
+					'stripeError',
+					this.onError
+				)
+				.on(
+					'checkout_error',
+					this.reset
+				);
+
+			// SEPA IBAN.
+			iban.on( 'change',
+				this.onSepaError
+			);
+
+			// Subscription early renewals modal.
+			if ($('#early_renewal_modal_submit[data-payment-method]').length) {
+				$('#early_renewal_modal_submit[data-payment-method=stripe]').on('click', this.onEarlyRenewalSubmit);
+			} else {
+				$('#early_renewal_modal_submit').on('click', this.onEarlyRenewalSubmit);
+			}
+
+			wc_stripe_form.createElements();
+
+			// Listen for hash changes in order to handle payment intents
+			window.addEventListener( 'hashchange', wc_stripe_form.onHashChange );
+			wc_stripe_form.maybeConfirmIntent();
+
+			//Mask CPF/CNPJ field when using Boleto
+			$( document ).on( 'change', '.wc_payment_methods', function () {
+				if ( ! $( '#stripe_boleto_tax_id' ).length ) {
+					return;
+				}
+
+				var TaxIdMaskBehavior = function ( val ) {
+						return val.replace( /\D/g, '' ).length >= 12 ? '00.000.000/0000-00' : '000.000.000-009999';
+					},
+					spOptions = {
+						onKeyPress: function ( val, e, field, options ) {
+							field.mask( TaxIdMaskBehavior.apply( {}, arguments ), options );
+						}
+					};
+
+				$( '#stripe_boleto_tax_id' ).mask( TaxIdMaskBehavior, spOptions );
+			});
+		},
+
+		/**
+		 * Check to see if Stripe in general is being used for checkout.
+		 *
+		 * @return {boolean}
+		 */
+		isStripeChosen: function() {
+			return $( '#payment_method_stripe, #payment_method_stripe_bancontact, #payment_method_stripe_sofort, #payment_method_stripe_giropay, #payment_method_stripe_ideal, #payment_method_stripe_alipay, #payment_method_stripe_sepa, #payment_method_stripe_eps, #payment_method_stripe_multibanco, #payment_method_stripe_boleto, #payment_method_stripe_oxxo' ).is( ':checked' ) || ( $( '#payment_method_stripe' ).is( ':checked' ) && 'new' === $( 'input[name="wc-stripe-payment-token"]:checked' ).val() ) || ( $( '#payment_method_stripe_sepa' ).is( ':checked' ) && 'new' === $( 'input[name="wc-stripe-payment-token"]:checked' ).val() );
+		},
+
+		/**
+		 * Currently only support saved cards via credit cards and SEPA. No other payment method.
+		 *
+		 * @return {boolean}
+		 */
+		isStripeSaveCardChosen: function() {
+			return (
+				$( '#payment_method_stripe' ).is( ':checked' ) &&
+				$( 'input[name="wc-stripe-payment-token"]' ).is( ':checked' ) &&
+				'new' !== $( 'input[name="wc-stripe-payment-token"]:checked' ).val()
+			) || (
+				$( '#payment_method_stripe_sepa' ).is( ':checked' ) &&
+				$( 'input[name="wc-stripe_sepa-payment-token"]' ).is( ':checked' ) &&
+				'new' !== $( 'input[name="wc-stripe_sepa-payment-token"]:checked' ).val()
+			);
+		},
+
+		/**
+		 * Check if Stripe credit card is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isStripeCardChosen: function() {
+			return $( '#payment_method_stripe' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe Bancontact is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isBancontactChosen: function() {
+			return $( '#payment_method_stripe_bancontact' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe giropay is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isGiropayChosen: function() {
+			return $( '#payment_method_stripe_giropay' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe iDEAL is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isIdealChosen: function() {
+			return $( '#payment_method_stripe_ideal' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe Sofort is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isSofortChosen: function() {
+			return $( '#payment_method_stripe_sofort' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe Alipay is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isAlipayChosen: function() {
+			return $( '#payment_method_stripe_alipay' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe SEPA Direct Debit is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isSepaChosen: function() {
+			return $( '#payment_method_stripe_sepa' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe P24 is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isP24Chosen: function() {
+			return $( '#payment_method_stripe_p24' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe EPS is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isEpsChosen: function() {
+			return $( '#payment_method_stripe_eps' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe Multibanco is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isMultibancoChosen: function() {
+			return $( '#payment_method_stripe_multibanco' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe Boleto is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isBoletoChosen: function() {
+			return $( '#payment_method_stripe_boleto' ).is( ':checked' );
+		},
+
+		/**
+		 * Check if Stripe OXXO is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isOxxoChosen: function() {
+			return $( '#payment_method_stripe_oxxo' ).is( ':checked' );
+		},
+
+		/**
+		 * Checks if a source ID is present as a hidden input.
+		 * Only used when SEPA Direct Debit is chosen.
+		 *
+		 * @return {boolean}
+		 */
+		hasSource: function() {
+			return 0 < $( 'input.stripe-source' ).length;
+		},
+
+		/**
+		 * Check whether a mobile device is being used.
+		 *
+		 * @return {boolean}
+		 */
+		isMobile: function() {
+			if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ) ) {
+				return true;
+			}
+
+			return false;
+		},
+
+		/**
+		 * Blocks payment forms with an overlay while being submitted.
+		 */
+		block: function() {
+			if ( ! wc_stripe_form.isMobile() ) {
+				wc_stripe_form.form.block( {
+					message: null,
+					overlayCSS: {
+						background: '#fff',
+						opacity: 0.6
+					}
+				} );
+			}
+		},
+
+		/**
+		 * Removes overlays from payment forms.
+		 */
+		unblock: function() {
+			wc_stripe_form.form && wc_stripe_form.form.unblock();
+		},
+
+		/**
+		 * Returns the selected payment method HTML element.
+		 *
+		 * @return {HTMLElement}
+		 */
+		getSelectedPaymentElement: function() {
+			return $( '.payment_methods input[name="payment_method"]:checked' );
+		},
+
+		/**
+		 * Retrieves "owner" data from either the billing fields in a form or preset settings.
+		 *
+		 * @return {Object}
+		 */
+		getOwnerDetails: function() {
+			var first_name = $( '#billing_first_name' ).length ? $( '#billing_first_name' ).val() : wc_stripe_params.billing_first_name,
+				last_name  = $( '#billing_last_name' ).length ? $( '#billing_last_name' ).val() : wc_stripe_params.billing_last_name,
+				owner      = { name: '', address: {}, email: '', phone: '' };
+
+			owner.name = first_name;
+
+			if ( first_name && last_name ) {
+				owner.name = first_name + ' ' + last_name;
+			} else {
+				owner.name = $( '#stripe-payment-data' ).data( 'full-name' );
+			}
+
+			owner.email = $( '#billing_email' ).val();
+			owner.phone = $( '#billing_phone' ).val();
+
+			/* Stripe does not like empty string values so
+			 * we need to remove the parameter if we're not
+			 * passing any value.
+			 */
+			if ( typeof owner.phone === 'undefined' || 0 >= owner.phone.length ) {
+				delete owner.phone;
+			}
+
+			if ( typeof owner.email === 'undefined' || 0 >= owner.email.length ) {
+				if ( $( '#stripe-payment-data' ).data( 'email' ).length ) {
+					owner.email = $( '#stripe-payment-data' ).data( 'email' );
+				} else {
+					delete owner.email;
+				}
+			}
+
+			if ( typeof owner.name === 'undefined' || 0 >= owner.name.length ) {
+				delete owner.name;
+			}
+
+			owner.address.line1       = $( '#billing_address_1' ).val() || wc_stripe_params.billing_address_1;
+			owner.address.line2       = $( '#billing_address_2' ).val() || wc_stripe_params.billing_address_2;
+			owner.address.state       = $( '#billing_state' ).val()     || wc_stripe_params.billing_state;
+			owner.address.city        = $( '#billing_city' ).val()      || wc_stripe_params.billing_city;
+			owner.address.postal_code = $( '#billing_postcode' ).val()  || wc_stripe_params.billing_postcode;
+			owner.address.country     = $( '#billing_country' ).val()   || wc_stripe_params.billing_country;
+
+			return {
+				owner: owner,
+			};
+		},
+
+		/**
+		 * Initiates the creation of a Source object.
+		 *
+		 * Currently this is only used for credit cards and SEPA Direct Debit,
+		 * all other payment methods work with redirects to create sources.
+		 */
+		createSource: function() {
+			var extra_details = wc_stripe_form.getOwnerDetails();
+
+			// Handle SEPA Direct Debit payments.
+			if ( wc_stripe_form.isSepaChosen() ) {
+				extra_details.currency = $( '#stripe-sepa_debit-payment-data' ).data( 'currency' );
+				extra_details.mandate  = { notification_method: wc_stripe_params.sepa_mandate_notification };
+				extra_details.type     = 'sepa_debit';
+
+				return stripe.createSource( iban, extra_details ).then( wc_stripe_form.sourceResponse );
+			}
+
+			// Handle card payments.
+			return stripe.createSource( stripe_card, extra_details )
+				.then( wc_stripe_form.sourceResponse );
+		},
+
+		/**
+		 * Handles responses, based on source object.
+		 *
+		 * @param {Object} response The `stripe.createSource` response.
+		 */
+		sourceResponse: function( response ) {
+			if ( response.error ) {
+				$( document.body ).trigger( 'stripeError', response );
+				return;
+			}
+
+			wc_stripe_form.reset();
+
+			wc_stripe_form.form.append(
+				$( '<input type="hidden" />' )
+					.addClass( 'stripe-source' )
+					.attr( 'name', 'stripe_source' )
+					.val( response.source.id )
+			);
+
+			if ( $( 'form#add_payment_method' ).length || $( '#wc-stripe-change-payment-method' ).length ) {
+				wc_stripe_form.sourceSetup( response );
+				return;
+			}
+
+			wc_stripe_form.form.trigger( 'submit' );
+		},
+
+		/**
+		 * Authenticate Source if necessary by creating and confirming a SetupIntent.
+		 *
+		 * @param {Object} response The `stripe.createSource` response.
+		 */
+		sourceSetup: function( response ) {
+			var apiError = {
+				error: {
+					type: 'api_connection_error'
+				}
+			};
+
+			$.post( {
+				url: wc_stripe_form.getAjaxURL( 'create_setup_intent'),
+				dataType: 'json',
+				data: {
+					stripe_source_id: response.source.id,
+					nonce: wc_stripe_params.add_card_nonce,
+				},
+				error: function() {
+					$( document.body ).trigger( 'stripeError', apiError );
+				}
+			} ).done( function( serverResponse ) {
+				if ( 'success' === serverResponse.status ) {
+					if ( $( 'form#add_payment_method' ).length ) {
+						$( wc_stripe_form.form ).off( 'submit', wc_stripe_form.form.onSubmit );
+					}
+					wc_stripe_form.form.trigger( 'submit' );
+					return;
+				} else if ( 'requires_action' !== serverResponse.status ) {
+					$( document.body ).trigger( 'stripeError', serverResponse );
+					return;
+				}
+
+				stripe.confirmCardSetup( serverResponse.client_secret, { payment_method: response.source.id } )
+					.then( function( result ) {
+						if ( result.error ) {
+							$( document.body ).trigger( 'stripeError', result );
+							return;
+						}
+
+						if ( $( 'form#add_payment_method' ).length ) {
+							$( wc_stripe_form.form ).off( 'submit', wc_stripe_form.form.onSubmit );
+						}
+						wc_stripe_form.form.trigger( 'submit' );
+					} )
+					.catch( function( err ) {
+						console.log( err );
+						$( document.body ).trigger( 'stripeError', { error: err } );
+					} );
+			} );
+		},
+
+		/**
+		 * Performs payment-related actions when a checkout/payment form is being submitted.
+		 *
+		 * @return {boolean} An indicator whether the submission should proceed.
+		 *                   WooCommerce's checkout.js stops only on `false`, so this needs to be explicit.
+		 */
+		onSubmit: function() {
+			if ( ! wc_stripe_form.isStripeChosen() ) {
+				return true;
+			}
+
+			// If a source is already in place, submit the form as usual.
+			if ( wc_stripe_form.isStripeSaveCardChosen() || wc_stripe_form.hasSource() ) {
+				return true;
+			}
+
+			// For methods that needs redirect, we will create the source server side so we can obtain the order ID.
+			if (
+				wc_stripe_form.isBancontactChosen() ||
+				wc_stripe_form.isGiropayChosen() ||
+				wc_stripe_form.isIdealChosen() ||
+				wc_stripe_form.isAlipayChosen() ||
+				wc_stripe_form.isSofortChosen() ||
+				wc_stripe_form.isP24Chosen() ||
+				wc_stripe_form.isEpsChosen() ||
+				wc_stripe_form.isMultibancoChosen()
+			) {
+				return true;
+			}
+
+			wc_stripe_form.block();
+
+			if( wc_stripe_form.isBoletoChosen() ) {
+				if( ! $( '#stripe_boleto_tax_id' ).val() ) {
+					wc_stripe_form.submitError( wc_stripe_params.cpf_cnpj_required_msg );
+					wc_stripe_form.unblock();
+					return false;
+				}
+
+				wc_stripe_form.handleBoleto();
+			} else if ( wc_stripe_form.isOxxoChosen() ) {
+				wc_stripe_form.handleOxxo();
+			} else {
+				wc_stripe_form.createSource();
+			}
+
+			return false;
+		},
+
+		/**
+		 * Will show a modal for printing the Boleto.
+		 * After the customer closes the modal proceeds with checkout normally
+		 */
+		handleBoleto: function () {
+			wc_stripe_form.executeCheckout( 'boleto', function ( checkout_response ) {
+				stripe.confirmBoletoPayment(
+					checkout_response.client_secret,
+					checkout_response.confirm_payment_data
+				)
+					.then(function ( response ) {
+						wc_stripe_form.handleConfirmResponse( checkout_response, response );
+					});
+			} );
+		},
+
+		/**
+		 * Executes the checkout and then execute the callback instead of redirect to success page
+		 * @param callback
+		 */
+		executeCheckout: function ( payment_method, callback ) {
+			const formFields = wc_stripe_form.form.serializeArray().reduce( ( obj, field ) => {
+				obj[ field.name ] = field.value;
+				return obj;
+			}, {} );
+
+			if( wc_stripe_form.form.attr('id') === 'order_review' ) {
+				formFields._ajax_nonce = wc_stripe_params.updatePaymentIntentNonce;
+				formFields.order_id = wc_stripe_params.orderId;
+
+				$.ajax( {
+					url: wc_stripe_form.getAjaxURL( payment_method + '_update_payment_intent' ),
+					type: 'POST',
+					data: formFields,
+					success: function ( response ) {
+
+						if( 'success' !== response.result ) {
+							wc_stripe_form.submitError( response.messages );
+							wc_stripe_form.unblock();
+							return;
+						}
+
+						callback( response );
+					}
+				} );
+
+			} else {
+				$.ajax( {
+					url: wc_stripe_params.checkout_url,
+					type: 'POST',
+					data: formFields,
+					success: function ( checkout_response ) {
+
+						if( 'success' !== checkout_response.result ) {
+							wc_stripe_form.submitError( checkout_response.messages, true );
+							wc_stripe_form.unblock();
+							return;
+						}
+
+						callback( checkout_response );
+					}
+				} );
+			}
+		},
+
+		/**
+		 * Handles response of the Confirm<payment_method>Payment like confirmBoletoPayment and confirmOxxoPayment
+		 * @param checkout_response
+		 * @param response
+		 */
+		handleConfirmResponse: function ( checkout_response, response ) {
+			if ( response.error ) {
+				$( document.body ).trigger( 'stripeError', response );
+				return;
+			}
+
+			if ( -1 === checkout_response.redirect.indexOf( 'https://' ) || -1 === checkout_response.redirect.indexOf( 'http://' ) ) {
+				window.location = checkout_response.redirect;
+			} else {
+				window.location = decodeURI( checkout_response.redirect );
+			}
+		},
+
+		/**
+		 * Will show a modal for printing the OXXO Voucher.
+		 * After the customer closes the modal proceeds with checkout normally
+		 */
+		handleOxxo: function () {
+			wc_stripe_form.executeCheckout( 'oxxo', function ( checkout_response ) {
+				stripe.confirmOxxoPayment(
+					checkout_response.client_secret,
+					checkout_response.confirm_payment_data
+				)
+					.then(function (response) {
+						wc_stripe_form.handleConfirmResponse( checkout_response, response );
+					} );
+			} );
+		},
+
+		/**
+		 * If a new credit card is entered, reset sources.
+		 */
+		onCCFormChange: function() {
+			wc_stripe_form.reset();
+		},
+
+		/**
+		 * Removes all Stripe errors and hidden fields with IDs from the form.
+		 */
+		reset: function() {
+			$( '.wc-stripe-error, .stripe-source' ).remove();
+		},
+
+		/**
+		 * Displays a SEPA-specific error message.
+		 *
+		 * @param {Event} e The event with the error.
+		 */
+		onSepaError: function( e ) {
+			var errorContainer = wc_stripe_form.getSelectedPaymentElement().parents( 'li' ).eq( 0 ).find( '.stripe-source-errors' );
+
+			if ( ! e.error ) {
+				$( errorContainer ).html( '' );
+				return;
+			}
+
+			console.log( e.error.message ); // Leave for troubleshooting.
+			$( errorContainer ).html( '<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>' );
+			$( errorContainer ).find( 'li' ).text( e.error.message ); // Prevent XSS
+		},
+
+		/**
+		 * Displays stripe-related errors.
+		 *
+		 * @param {Event}  e      The jQuery event.
+		 * @param {Object} result The result of Stripe call.
+		 */
+		onError: function( e, result ) {
+			var message = result.error.message;
+			var selectedMethodElement = wc_stripe_form.getSelectedPaymentElement().closest( 'li' );
+			var savedTokens = selectedMethodElement.find( '.woocommerce-SavedPaymentMethods-tokenInput' );
+			var errorContainer;
+
+			var prButtonClicked = $( 'body' ).hasClass( 'woocommerce-stripe-prb-clicked' );
+			if ( prButtonClicked ) {
+				// If payment was initiated with a payment request button, display errors in the notices div.
+				$( 'body' ).removeClass( 'woocommerce-stripe-prb-clicked' );
+				errorContainer = $( 'div.woocommerce-notices-wrapper' ).first();
+			} else if ( savedTokens.length ) {
+				// In case there are saved cards too, display the message next to the correct one.
+				var selectedToken = savedTokens.filter( ':checked' );
+
+				if ( selectedToken.closest( '.woocommerce-SavedPaymentMethods-new' ).length ) {
+					// Display the error next to the CC fields if a new card is being entered.
+					errorContainer = $( '#wc-stripe-cc-form .stripe-source-errors' );
+				} else {
+					// Display the error next to the chosen saved card.
+					errorContainer = selectedToken.closest( 'li' ).find( '.stripe-source-errors' );
+				}
+			} else {
+				// When no saved cards are available, display the error next to CC fields.
+				errorContainer = selectedMethodElement.find( '.stripe-source-errors' );
+			}
+
+			/*
+			 * If payment method is SEPA and owner name is not completed,
+			 * source cannot be created. So we need to show the normal
+			 * Billing name is required error message on top of form instead
+			 * of inline.
+			 */
+			if ( wc_stripe_form.isSepaChosen() ) {
+				if ( 'invalid_owner_name' === result.error.code && wc_stripe_params.hasOwnProperty( result.error.code ) ) {
+					wc_stripe_form.submitError( wc_stripe_params[ result.error.code ] );
+					return;
+				}
+			}
+
+			// Notify users that the email is invalid.
+			if ( 'email_invalid' === result.error.code ) {
+				message = wc_stripe_params.email_invalid;
+			} else if (
+				/*
+				 * Customers do not need to know the specifics of the below type of errors
+				 * therefore return a generic localizable error message.
+				 */
+				'invalid_request_error' === result.error.type ||
+				'api_connection_error'  === result.error.type ||
+				'api_error'             === result.error.type ||
+				'authentication_error'  === result.error.type ||
+				'rate_limit_error'      === result.error.type
+			) {
+				message = wc_stripe_params.invalid_request_error;
+			}
+
+			if ( wc_stripe_params.hasOwnProperty(result.error.code) ) {
+				message = wc_stripe_params[ result.error.code ];
+			}
+
+			wc_stripe_form.reset();
+			$( '.woocommerce-NoticeGroup-checkout' ).remove();
+			console.log( result.error.message ); // Leave for troubleshooting.
+			$( errorContainer ).html( '<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>' );
+			$( errorContainer ).find( 'li' ).text( message ); // Prevent XSS
+
+			if ( $( '.wc-stripe-error' ).length ) {
+				$( 'html, body' ).animate({
+					scrollTop: ( $( '.wc-stripe-error' ).offset().top - 200 )
+				}, 200 );
+			}
+			wc_stripe_form.unblock();
+			$.unblockUI(); // If arriving via Payment Request Button.
+		},
+
+		/**
+		 * Displays an error message in the beginning of the form and scrolls to it.
+		 *
+		 * @param {Object} error_message An error message jQuery object.
+		 */
+		submitError: function( error_message, is_html = false ) {
+			if( ! is_html ) {
+				var error = $( '<div><ul class="woocommerce-error"><li /></ul></div>' );
+				error.find( 'li' ).text( error_message ); // Prevent XSS
+				error_message = error.html();
+			}
+
+			$( '.woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message' ).remove();
+			wc_stripe_form.form.prepend( '<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">' + error_message + '</div>' );
+			wc_stripe_form.form.removeClass( 'processing' ).unblock();
+			wc_stripe_form.form.find( '.input-text, select, input:checkbox' ).trigger( 'blur' );
+
+			var selector = '';
+
+			if ( $( '#add_payment_method' ).length ) {
+				selector = $( '#add_payment_method' );
+			}
+
+			if ( $( '#order_review' ).length ) {
+				selector = $( '#order_review' );
+			}
+
+			if ( $( 'form.checkout' ).length ) {
+				selector = $( 'form.checkout' );
+			}
+
+			if ( selector.length ) {
+				$( 'html, body' ).animate({
+					scrollTop: ( selector.offset().top - 100 )
+				}, 500 );
+			}
+
+			$( document.body ).trigger( 'checkout_error' );
+			wc_stripe_form.unblock();
+		},
+
+		/**
+		 * Handles changes in the hash in order to show a modal for PaymentIntent/SetupIntent confirmations.
+		 *
+		 * Listens for `hashchange` events and checks for a hash in the following format:
+		 * #confirm-pi-<intentClientSecret>:<successRedirectURL>
+		 *
+		 * If such a hash appears, the partials will be used to call `stripe.handleCardPayment`
+		 * in order to allow customers to confirm an 3DS/SCA authorization, or stripe.handleCardSetup if
+		 * what needs to be confirmed is a SetupIntent.
+		 *
+		 * Those redirects/hashes are generated in `WC_Gateway_Stripe::process_payment`.
+		 */
+		onHashChange: function() {
+			var partials = window.location.hash.match( /^#?confirm-(pi|si)-([^:]+):(.+)$/ );
+
+			if ( ! partials || 4 > partials.length ) {
+				return;
+			}
+
+			var type               = partials[1];
+			var intentClientSecret = partials[2];
+			var redirectURL        = decodeURIComponent( partials[3] );
+
+			// Cleanup the URL
+			window.location.hash = '';
+
+			wc_stripe_form.openIntentModal( intentClientSecret, redirectURL, false, 'si' === type );
+		},
+
+		maybeConfirmIntent: function() {
+			if ( ! $( '#stripe-intent-id' ).length || ! $( '#stripe-intent-return' ).length ) {
+				return;
+			}
+
+			var intentSecret = $( '#stripe-intent-id' ).val();
+			var returnURL    = $( '#stripe-intent-return' ).val();
+
+			wc_stripe_form.openIntentModal( intentSecret, returnURL, true, false );
+		},
+
+		/**
+		 * Opens the modal for PaymentIntent authorizations.
+		 *
+		 * @param {string}  intentClientSecret The client secret of the intent.
+		 * @param {string}  redirectURL        The URL to ping on fail or redirect to on success.
+		 * @param {boolean} alwaysRedirect     If set to true, an immediate redirect will happen no matter the result.
+		 *                                     If not, an error will be displayed on failure.
+		 * @param {boolean} isSetupIntent      If set to true, ameans that the flow is handling a Setup Intent.
+		 *                                     If false, it's a Payment Intent.
+		 */
+		openIntentModal: function( intentClientSecret, redirectURL, alwaysRedirect, isSetupIntent ) {
+			stripe[ isSetupIntent ? 'handleCardSetup' : 'handleCardPayment' ]( intentClientSecret )
+				.then( function( response ) {
+					if ( response.error ) {
+						throw response.error;
+					}
+
+					var intent = response[ isSetupIntent ? 'setupIntent' : 'paymentIntent' ];
+					if ( 'requires_capture' !== intent.status && 'succeeded' !== intent.status ) {
+						return;
+					}
+
+					window.location = redirectURL;
+				} )
+				.catch( function( error ) {
+					if ( alwaysRedirect ) {
+						window.location = redirectURL;
+						return;
+					}
+
+					$( document.body ).trigger( 'stripeError', { error: error } );
+					wc_stripe_form.form && wc_stripe_form.form.removeClass( 'processing' );
+
+					// Report back to the server.
+					$.get( redirectURL + '&is_ajax' );
+				} );
+		},
+
+		/**
+		 * Prevents the standard behavior of the "Renew Now" button in the
+		 * early renewals modal by using AJAX instead of a simple redirect.
+		 *
+		 * @param {Event} e The event that occured.
+		 */
+		onEarlyRenewalSubmit: function( e ) {
+			e.preventDefault();
+
+			$.ajax( {
+				url: $( '#early_renewal_modal_submit' ).attr( 'href' ),
+				method: 'get',
+				success: function( html ) {
+					var response = JSON.parse( html );
+
+					if ( response.stripe_sca_required ) {
+						wc_stripe_form.openIntentModal( response.intent_secret, response.redirect_url, true, false );
+					} else {
+						window.location = response.redirect_url;
+					}
+				},
+			} );
+
+			return false;
+		},
+	};
+
+	wc_stripe_form.init();
+} );
diff --git a/assets/js/stripe.min.js b/assets/js/stripe.min.js
new file mode 100644
index 0000000..06c3497
--- /dev/null
+++ b/assets/js/stripe.min.js
@@ -0,0 +1 @@
+jQuery(function(i){"use strict";try{var n=Stripe(wc_stripe_params.key,{locale:wc_stripe_params.stripe_locale||"auto"})}catch(e){return void console.log(e)}var t,o,s,e=Object.keys(wc_stripe_params.elements_options).length?wc_stripe_params.elements_options:{},r=Object.keys(wc_stripe_params.sepa_elements_options).length?wc_stripe_params.sepa_elements_options:{},a=n.elements(e),c=a.create("iban",r),m={getAjaxURL:function(e){return wc_stripe_params.ajaxurl.toString().replace("%%endpoint%%","wc_stripe_"+e)},unmountElements:function(){"yes"===wc_stripe_params.inline_cc_form?t.unmount("#stripe-card-element"):(t.unmount("#stripe-card-element"),o.unmount("#stripe-exp-element"),s.unmount("#stripe-cvc-element"))},mountElements:function(){i("#stripe-card-element").length&&("yes"!==wc_stripe_params.inline_cc_form?(t.mount("#stripe-card-element"),o.mount("#stripe-exp-element"),s.mount("#stripe-cvc-element")):t.mount("#stripe-card-element"))},createElements:function(){var e={base:{iconColor:"#666EE8",color:"#31325F",fontSize:"15px","::placeholder":{color:"#CFD7E0"}}},r={focus:"focused",empty:"empty",invalid:"invalid"},e=wc_stripe_params.elements_styling||e,r=wc_stripe_params.elements_classes||r;"yes"===wc_stripe_params.inline_cc_form?(t=a.create("card",{style:e,hidePostalCode:!0})).addEventListener("change",function(e){m.onCCFormChange(),e.error&&i(document.body).trigger("stripeError",e)}):(t=a.create("cardNumber",{style:e,classes:r}),o=a.create("cardExpiry",{style:e,classes:r}),s=a.create("cardCvc",{style:e,classes:r}),t.addEventListener("change",function(e){m.onCCFormChange(),m.updateCardBrand(e.brand),e.error&&i(document.body).trigger("stripeError",e)}),o.addEventListener("change",function(e){m.onCCFormChange(),e.error&&i(document.body).trigger("stripeError",e)}),s.addEventListener("change",function(e){m.onCCFormChange(),e.error&&i(document.body).trigger("stripeError",e)})),"yes"===wc_stripe_params.is_checkout?i(document.body).on("updated_checkout",function(){i("#stripe-card-element").children().length||(t&&m.unmountElements(),m.mountElements(),i("#stripe-iban-element").length&&c.mount("#stripe-iban-element"))}):(i("form#add_payment_method").length||i("form#order_review").length)&&(m.mountElements(),i("#stripe-iban-element").length&&c.mount("#stripe-iban-element"))},updateCardBrand:function(e){var r={visa:"stripe-visa-brand",mastercard:"stripe-mastercard-brand",amex:"stripe-amex-brand",discover:"stripe-discover-brand",diners:"stripe-diners-brand",jcb:"stripe-jcb-brand",unknown:"stripe-credit-card-brand"},t=i(".stripe-card-brand"),e=e in r?r[e]:"stripe-credit-card-brand";i.each(r,function(e,r){t.removeClass(r)}),t.addClass(e)},init:function(){"yes"!==wc_stripe_params.is_change_payment_page&&"yes"!==wc_stripe_params.is_pay_for_order_page||i(document.body).trigger("wc-credit-card-form-init"),i("form.woocommerce-checkout").length&&(this.form=i("form.woocommerce-checkout")),i("form.woocommerce-checkout").on("checkout_place_order_stripe checkout_place_order_stripe_bancontact checkout_place_order_stripe_sofort checkout_place_order_stripe_giropay checkout_place_order_stripe_ideal checkout_place_order_stripe_alipay checkout_place_order_stripe_sepa checkout_place_order_stripe_boleto checkout_place_order_stripe_oxxo",this.onSubmit),i("form#order_review").length&&(this.form=i("form#order_review")),i("form#order_review, form#add_payment_method").on("submit",this.onSubmit),i("form#add_payment_method").length&&(this.form=i("form#add_payment_method")),i("form.woocommerce-checkout").on("change",this.reset),i(document).on("stripeError",this.onError).on("checkout_error",this.reset),c.on("change",this.onSepaError),(i("#early_renewal_modal_submit[data-payment-method]").length?i("#early_renewal_modal_submit[data-payment-method=stripe]"):i("#early_renewal_modal_submit")).on("click",this.onEarlyRenewalSubmit),m.createElements(),window.addEventListener("hashchange",m.onHashChange),m.maybeConfirmIntent(),i(document).on("change",".wc_payment_methods",function(){var n,e;i("#stripe_boleto_tax_id").length&&(n=function(e){return 12<=e.replace(/\D/g,"").length?"00.000.000/0000-00":"000.000.000-009999"},e={onKeyPress:function(e,r,t,o){t.mask(n.apply({},arguments),o)}},i("#stripe_boleto_tax_id").mask(n,e))})},isStripeChosen:function(){return i("#payment_method_stripe, #payment_method_stripe_bancontact, #payment_method_stripe_sofort, #payment_method_stripe_giropay, #payment_method_stripe_ideal, #payment_method_stripe_alipay, #payment_method_stripe_sepa, #payment_method_stripe_eps, #payment_method_stripe_multibanco, #payment_method_stripe_boleto, #payment_method_stripe_oxxo").is(":checked")||i("#payment_method_stripe").is(":checked")&&"new"===i('input[name="wc-stripe-payment-token"]:checked').val()||i("#payment_method_stripe_sepa").is(":checked")&&"new"===i('input[name="wc-stripe-payment-token"]:checked').val()},isStripeSaveCardChosen:function(){return i("#payment_method_stripe").is(":checked")&&i('input[name="wc-stripe-payment-token"]').is(":checked")&&"new"!==i('input[name="wc-stripe-payment-token"]:checked').val()||i("#payment_method_stripe_sepa").is(":checked")&&i('input[name="wc-stripe_sepa-payment-token"]').is(":checked")&&"new"!==i('input[name="wc-stripe_sepa-payment-token"]:checked').val()},isStripeCardChosen:function(){return i("#payment_method_stripe").is(":checked")},isBancontactChosen:function(){return i("#payment_method_stripe_bancontact").is(":checked")},isGiropayChosen:function(){return i("#payment_method_stripe_giropay").is(":checked")},isIdealChosen:function(){return i("#payment_method_stripe_ideal").is(":checked")},isSofortChosen:function(){return i("#payment_method_stripe_sofort").is(":checked")},isAlipayChosen:function(){return i("#payment_method_stripe_alipay").is(":checked")},isSepaChosen:function(){return i("#payment_method_stripe_sepa").is(":checked")},isP24Chosen:function(){return i("#payment_method_stripe_p24").is(":checked")},isEpsChosen:function(){return i("#payment_method_stripe_eps").is(":checked")},isMultibancoChosen:function(){return i("#payment_method_stripe_multibanco").is(":checked")},isBoletoChosen:function(){return i("#payment_method_stripe_boleto").is(":checked")},isOxxoChosen:function(){return i("#payment_method_stripe_oxxo").is(":checked")},hasSource:function(){return 0<i("input.stripe-source").length},isMobile:function(){return!!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},block:function(){m.isMobile()||m.form.block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},unblock:function(){m.form&&m.form.unblock()},getSelectedPaymentElement:function(){return i('.payment_methods input[name="payment_method"]:checked')},getOwnerDetails:function(){var e=i("#billing_first_name").length?i("#billing_first_name").val():wc_stripe_params.billing_first_name,r=i("#billing_last_name").length?i("#billing_last_name").val():wc_stripe_params.billing_last_name,t={name:"",address:{},email:"",phone:""};return t.name=e,t.name=e&&r?e+" "+r:i("#stripe-payment-data").data("full-name"),t.email=i("#billing_email").val(),t.phone=i("#billing_phone").val(),(void 0===t.phone||t.phone.length<=0)&&delete t.phone,(void 0===t.email||t.email.length<=0)&&(i("#stripe-payment-data").data("email").length?t.email=i("#stripe-payment-data").data("email"):delete t.email),(void 0===t.name||t.name.length<=0)&&delete t.name,t.address.line1=i("#billing_address_1").val()||wc_stripe_params.billing_address_1,t.address.line2=i("#billing_address_2").val()||wc_stripe_params.billing_address_2,t.address.state=i("#billing_state").val()||wc_stripe_params.billing_state,t.address.city=i("#billing_city").val()||wc_stripe_params.billing_city,t.address.postal_code=i("#billing_postcode").val()||wc_stripe_params.billing_postcode,t.address.country=i("#billing_country").val()||wc_stripe_params.billing_country,{owner:t}},createSource:function(){var e=m.getOwnerDetails();return m.isSepaChosen()?(e.currency=i("#stripe-sepa_debit-payment-data").data("currency"),e.mandate={notification_method:wc_stripe_params.sepa_mandate_notification},e.type="sepa_debit",n.createSource(c,e).then(m.sourceResponse)):n.createSource(t,e).then(m.sourceResponse)},sourceResponse:function(e){e.error?i(document.body).trigger("stripeError",e):(m.reset(),m.form.append(i('<input type="hidden" />').addClass("stripe-source").attr("name","stripe_source").val(e.source.id)),i("form#add_payment_method").length||i("#wc-stripe-change-payment-method").length?m.sourceSetup(e):m.form.trigger("submit"))},sourceSetup:function(r){var e={error:{type:"api_connection_error"}};i.post({url:m.getAjaxURL("create_setup_intent"),dataType:"json",data:{stripe_source_id:r.source.id,nonce:wc_stripe_params.add_card_nonce},error:function(){i(document.body).trigger("stripeError",e)}}).done(function(e){return"success"===e.status?(i("form#add_payment_method").length&&i(m.form).off("submit",m.form.onSubmit),void m.form.trigger("submit")):void("requires_action"===e.status?n.confirmCardSetup(e.client_secret,{payment_method:r.source.id}).then(function(e){e.error?i(document.body).trigger("stripeError",e):(i("form#add_payment_method").length&&i(m.form).off("submit",m.form.onSubmit),m.form.trigger("submit"))}).catch(function(e){console.log(e),i(document.body).trigger("stripeError",{error:e})}):i(document.body).trigger("stripeError",e))})},onSubmit:function(){if(!m.isStripeChosen())return!0;if(m.isStripeSaveCardChosen()||m.hasSource())return!0;if(m.isBancontactChosen()||m.isGiropayChosen()||m.isIdealChosen()||m.isAlipayChosen()||m.isSofortChosen()||m.isP24Chosen()||m.isEpsChosen()||m.isMultibancoChosen())return!0;if(m.block(),m.isBoletoChosen()){if(!i("#stripe_boleto_tax_id").val())return m.submitError(wc_stripe_params.cpf_cnpj_required_msg),m.unblock(),!1;m.handleBoleto()}else m.isOxxoChosen()?m.handleOxxo():m.createSource();return!1},handleBoleto:function(){m.executeCheckout("boleto",function(r){n.confirmBoletoPayment(r.client_secret,r.confirm_payment_data).then(function(e){m.handleConfirmResponse(r,e)})})},executeCheckout:function(e,r){const t=m.form.serializeArray().reduce((e,r)=>(e[r.name]=r.value,e),{});"order_review"===m.form.attr("id")?(t._ajax_nonce=wc_stripe_params.updatePaymentIntentNonce,t.order_id=wc_stripe_params.orderId,i.ajax({url:m.getAjaxURL(e+"_update_payment_intent"),type:"POST",data:t,success:function(e){if("success"!==e.result)return m.submitError(e.messages),void m.unblock();r(e)}})):i.ajax({url:wc_stripe_params.checkout_url,type:"POST",data:t,success:function(e){if("success"!==e.result)return m.submitError(e.messages,!0),void m.unblock();r(e)}})},handleConfirmResponse:function(e,r){r.error?i(document.body).trigger("stripeError",r):-1===e.redirect.indexOf("https://")||-1===e.redirect.indexOf("http://")?window.location=e.redirect:window.location=decodeURI(e.redirect)},handleOxxo:function(){m.executeCheckout("oxxo",function(r){n.confirmOxxoPayment(r.client_secret,r.confirm_payment_data).then(function(e){m.handleConfirmResponse(r,e)})})},onCCFormChange:function(){m.reset()},reset:function(){i(".wc-stripe-error, .stripe-source").remove()},onSepaError:function(e){var r=m.getSelectedPaymentElement().parents("li").eq(0).find(".stripe-source-errors");e.error?(console.log(e.error.message),i(r).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>'),i(r).find("li").text(e.error.message)):i(r).html("")},onError:function(e,r){var t=r.error.message,o=m.getSelectedPaymentElement().closest("li"),n=o.find(".woocommerce-SavedPaymentMethods-tokenInput");o=i("body").hasClass("woocommerce-stripe-prb-clicked")?(i("body").removeClass("woocommerce-stripe-prb-clicked"),i("div.woocommerce-notices-wrapper").first()):n.length?(n=n.filter(":checked")).closest(".woocommerce-SavedPaymentMethods-new").length?i("#wc-stripe-cc-form .stripe-source-errors"):n.closest("li").find(".stripe-source-errors"):o.find(".stripe-source-errors"),m.isSepaChosen()&&"invalid_owner_name"===r.error.code&&wc_stripe_params.hasOwnProperty(r.error.code)?m.submitError(wc_stripe_params[r.error.code]):("email_invalid"===r.error.code?t=wc_stripe_params.email_invalid:"invalid_request_error"!==r.error.type&&"api_connection_error"!==r.error.type&&"api_error"!==r.error.type&&"authentication_error"!==r.error.type&&"rate_limit_error"!==r.error.type||(t=wc_stripe_params.invalid_request_error),wc_stripe_params.hasOwnProperty(r.error.code)&&(t=wc_stripe_params[r.error.code]),m.reset(),i(".woocommerce-NoticeGroup-checkout").remove(),console.log(r.error.message),i(o).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>'),i(o).find("li").text(t),i(".wc-stripe-error").length&&i("html, body").animate({scrollTop:i(".wc-stripe-error").offset().top-200},200),m.unblock(),i.unblockUI())},submitError:function(e,r=!1){r||((r=i('<div><ul class="woocommerce-error"><li /></ul></div>')).find("li").text(e),e=r.html()),i(".woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message").remove(),m.form.prepend('<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">'+e+"</div>"),m.form.removeClass("processing").unblock(),m.form.find(".input-text, select, input:checkbox").trigger("blur");e="";i("#add_payment_method").length&&(e=i("#add_payment_method")),i("#order_review").length&&(e=i("#order_review")),(e=i("form.checkout").length?i("form.checkout"):e).length&&i("html, body").animate({scrollTop:e.offset().top-100},500),i(document.body).trigger("checkout_error"),m.unblock()},onHashChange:function(){var e,r,t=window.location.hash.match(/^#?confirm-(pi|si)-([^:]+):(.+)$/);!t||t.length<4||(e=t[1],r=t[2],t=decodeURIComponent(t[3]),window.location.hash="",m.openIntentModal(r,t,!1,"si"===e))},maybeConfirmIntent:function(){var e,r;i("#stripe-intent-id").length&&i("#stripe-intent-return").length&&(e=i("#stripe-intent-id").val(),r=i("#stripe-intent-return").val(),m.openIntentModal(e,r,!0,!1))},openIntentModal:function(e,r,t,o){n[o?"handleCardSetup":"handleCardPayment"](e).then(function(e){if(e.error)throw e.error;e=e[o?"setupIntent":"paymentIntent"];"requires_capture"!==e.status&&"succeeded"!==e.status||(window.location=r)}).catch(function(e){t?window.location=r:(i(document.body).trigger("stripeError",{error:e}),m.form&&m.form.removeClass("processing"),i.get(r+"&is_ajax"))})},onEarlyRenewalSubmit:function(e){return e.preventDefault(),i.ajax({url:i("#early_renewal_modal_submit").attr("href"),method:"get",success:function(e){e=JSON.parse(e);e.stripe_sca_required?m.openIntentModal(e.intent_secret,e.redirect_url,!0,!1):window.location=e.redirect_url}}),!1}};m.init()});