From 3e1f2c7a0bae8febc43aa106bdafc1fb9c568ebd Mon Sep 17 00:00:00 2001 From: Adrien Dieudonne Date: Wed, 15 May 2019 07:19:18 +0000 Subject: [PATCH] [FIX] payment: update jquery.payment from 1.0.2 to 3.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After deep comparison between majors versions, it seems that there is no breaking changes and this fix a lot of issues in mobile. e.g.: Before this update, the validity date wasn't correctly formated and the end user had to enter the right format himself. Some users were blocked at the payment process. The root cause for this issue is the inconsistent list of native events triggered by mobile devices. Note that this library is only used for acquirer forms (S2S Form Template) when "Redirection to the acquirer website" is checked (payment_flow == 's2s') in the acquirer configuration. We are aware that this library is deprecated and should be replaced in the future. See https://github.com/stripe/jquery.payment opw-1986545 closes #33356 closes odoo/odoo#33376 Signed-off-by: Adrien Dieudonné (adr) --- .../lib/jquery.payment/jquery.payment.js | 329 +++++++++++++----- 1 file changed, 242 insertions(+), 87 deletions(-) diff --git a/addons/payment/static/lib/jquery.payment/jquery.payment.js b/addons/payment/static/lib/jquery.payment/jquery.payment.js index c81c6e00d81..dcf829fbc8c 100644 --- a/addons/payment/static/lib/jquery.payment/jquery.payment.js +++ b/addons/payment/static/lib/jquery.payment/jquery.payment.js @@ -1,11 +1,10 @@ -// Generated by CoffeeScript 1.4.0 +// Generated by CoffeeScript 1.7.1 (function() { - var $, cardFromNumber, cardFromType, cards, defaultFormat, formatBackCardNumber, formatBackExpiry, formatCardNumber, formatExpiry, formatForwardExpiry, formatForwardSlash, hasTextSelected, luhnCheck, reFormatCardNumber, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric, setCardType, + var $, cardFromNumber, cardFromType, cards, defaultFormat, formatBackCardNumber, formatBackExpiry, formatCardNumber, formatExpiry, formatForwardExpiry, formatForwardSlashAndSpace, hasTextSelected, luhnCheck, reFormatCVC, reFormatCardNumber, reFormatExpiry, reFormatNumeric, replaceFullWidthChars, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric, safeVal, setCardType, __slice = [].slice, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, - _this = this; + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; - $ = jQuery; + $ = window.jQuery || window.Zepto || window.$; $.payment = {}; @@ -19,80 +18,92 @@ defaultFormat = /(\d{1,4})/g; - cards = [ + $.payment.cards = cards = [ { type: 'maestro', - pattern: /^(5018|5020|5038|6304|6759|676[1-3])/, + patterns: [5018, 502, 503, 506, 56, 58, 639, 6220, 67], format: defaultFormat, length: [12, 13, 14, 15, 16, 17, 18, 19], cvcLength: [3], luhn: true }, { - type: 'dinersclub', - pattern: /^(36|38|30[0-5])/, - format: defaultFormat, - length: [14], - cvcLength: [3], - luhn: true - }, { - type: 'laser', - pattern: /^(6706|6771|6709)/, - format: defaultFormat, - length: [16, 17, 18, 19], - cvcLength: [3], - luhn: true - }, { - type: 'jcb', - pattern: /^35/, + type: 'forbrugsforeningen', + patterns: [600], format: defaultFormat, length: [16], cvcLength: [3], luhn: true }, { - type: 'unionpay', - pattern: /^62/, - format: defaultFormat, - length: [16, 17, 18, 19], - cvcLength: [3], - luhn: false - }, { - type: 'discover', - pattern: /^(6011|65|64[4-9]|622)/, + type: 'dankort', + patterns: [5019], format: defaultFormat, length: [16], cvcLength: [3], luhn: true + }, { + type: 'visa', + patterns: [4], + format: defaultFormat, + length: [13, 16], + cvcLength: [3], + luhn: true }, { type: 'mastercard', - pattern: /^5[1-5]/, + patterns: [51, 52, 53, 54, 55, 22, 23, 24, 25, 26, 27], format: defaultFormat, length: [16], cvcLength: [3], luhn: true }, { type: 'amex', - pattern: /^3[47]/, + patterns: [34, 37], format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/, length: [15], cvcLength: [3, 4], luhn: true }, { - type: 'visa', - pattern: /^4/, + type: 'dinersclub', + patterns: [30, 36, 38, 39], + format: /(\d{1,4})(\d{1,6})?(\d{1,4})?/, + length: [14], + cvcLength: [3], + luhn: true + }, { + type: 'discover', + patterns: [60, 64, 65, 622], format: defaultFormat, - length: [13, 14, 15, 16], + length: [16], + cvcLength: [3], + luhn: true + }, { + type: 'unionpay', + patterns: [62, 88], + format: defaultFormat, + length: [16, 17, 18, 19], + cvcLength: [3], + luhn: false + }, { + type: 'jcb', + patterns: [35], + format: defaultFormat, + length: [16], cvcLength: [3], luhn: true } ]; cardFromNumber = function(num) { - var card, _i, _len; + var card, p, pattern, _i, _j, _len, _len1, _ref; num = (num + '').replace(/\D/g, ''); for (_i = 0, _len = cards.length; _i < _len; _i++) { card = cards[_i]; - if (card.pattern.test(num)) { - return card; + _ref = card.patterns; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + pattern = _ref[_j]; + p = pattern + ''; + if (num.substr(0, p.length) === p) { + return card; + } } } }; @@ -131,20 +142,82 @@ if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== $target.prop('selectionEnd')) { return true; } - if (typeof document !== "undefined" && document !== null ? (_ref = document.selection) != null ? typeof _ref.createRange === "function" ? _ref.createRange().text : void 0 : void 0 : void 0) { - return true; + if ((typeof document !== "undefined" && document !== null ? (_ref = document.selection) != null ? _ref.createRange : void 0 : void 0) != null) { + if (document.selection.createRange().text) { + return true; + } } return false; }; - reFormatCardNumber = function(e) { - var _this = this; + safeVal = function(value, $target) { + var currPair, cursor, digit, error, last, prevPair; + try { + cursor = $target.prop('selectionStart'); + } catch (_error) { + error = _error; + cursor = null; + } + last = $target.val(); + $target.val(value); + if (cursor !== null && $target.is(":focus")) { + if (cursor === last.length) { + cursor = value.length; + } + if (last !== value) { + prevPair = last.slice(cursor - 1, +cursor + 1 || 9e9); + currPair = value.slice(cursor - 1, +cursor + 1 || 9e9); + digit = value[cursor]; + if (/\d/.test(digit) && prevPair === ("" + digit + " ") && currPair === (" " + digit)) { + cursor = cursor + 1; + } + } + $target.prop('selectionStart', cursor); + return $target.prop('selectionEnd', cursor); + } + }; + + replaceFullWidthChars = function(str) { + var chars, chr, fullWidth, halfWidth, idx, value, _i, _len; + if (str == null) { + str = ''; + } + fullWidth = '\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19'; + halfWidth = '0123456789'; + value = ''; + chars = str.split(''); + for (_i = 0, _len = chars.length; _i < _len; _i++) { + chr = chars[_i]; + idx = fullWidth.indexOf(chr); + if (idx > -1) { + chr = halfWidth[idx]; + } + value += chr; + } + return value; + }; + + reFormatNumeric = function(e) { + var $target; + $target = $(e.currentTarget); return setTimeout(function() { - var $target, value; - $target = $(e.currentTarget); + var value; value = $target.val(); + value = replaceFullWidthChars(value); + value = value.replace(/\D/g, ''); + return safeVal(value, $target); + }); + }; + + reFormatCardNumber = function(e) { + var $target; + $target = $(e.currentTarget); + return setTimeout(function() { + var value; + value = $target.val(); + value = replaceFullWidthChars(value); value = $.payment.formatCardNumber(value); - return $target.val(value); + return safeVal(value, $target); }); }; @@ -175,10 +248,14 @@ } if (re.test(value)) { e.preventDefault(); - return $target.val(value + ' ' + digit); + return setTimeout(function() { + return $target.val(value + ' ' + digit); + }); } else if (re.test(value + digit)) { e.preventDefault(); - return $target.val(value + digit + ' '); + return setTimeout(function() { + return $target.val(value + digit + ' '); + }); } }; @@ -186,9 +263,6 @@ var $target, value; $target = $(e.currentTarget); value = $target.val(); - if (e.meta) { - return; - } if (e.which !== 8) { return; } @@ -197,13 +271,29 @@ } if (/\d\s$/.test(value)) { e.preventDefault(); - return $target.val(value.replace(/\d\s$/, '')); + return setTimeout(function() { + return $target.val(value.replace(/\d\s$/, '')); + }); } else if (/\s\d?$/.test(value)) { e.preventDefault(); - return $target.val(value.replace(/\s\d?$/, '')); + return setTimeout(function() { + return $target.val(value.replace(/\d$/, '')); + }); } }; + reFormatExpiry = function(e) { + var $target; + $target = $(e.currentTarget); + return setTimeout(function() { + var value; + value = $target.val(); + value = replaceFullWidthChars(value); + value = $.payment.formatExpiry(value); + return safeVal(value, $target); + }); + }; + formatExpiry = function(e) { var $target, digit, val; digit = String.fromCharCode(e.which); @@ -214,10 +304,21 @@ val = $target.val() + digit; if (/^\d$/.test(val) && (val !== '0' && val !== '1')) { e.preventDefault(); - return $target.val("0" + val + " / "); + return setTimeout(function() { + return $target.val("0" + val + " / "); + }); } else if (/^\d\d$/.test(val)) { e.preventDefault(); - return $target.val("" + val + " / "); + return setTimeout(function() { + var m1, m2; + m1 = parseInt(val[0], 10); + m2 = parseInt(val[1], 10); + if (m2 > 2 && m1 !== 0) { + return $target.val("0" + m1 + " / " + m2); + } else { + return $target.val("" + val + " / "); + } + }); } }; @@ -234,10 +335,10 @@ } }; - formatForwardSlash = function(e) { - var $target, slash, val; - slash = String.fromCharCode(e.which); - if (slash !== '/') { + formatForwardSlashAndSpace = function(e) { + var $target, val, which; + which = String.fromCharCode(e.which); + if (!(which === '/' || which === ' ')) { return; } $target = $(e.currentTarget); @@ -249,9 +350,6 @@ formatBackExpiry = function(e) { var $target, value; - if (e.meta) { - return; - } $target = $(e.currentTarget); value = $target.val(); if (e.which !== 8) { @@ -260,15 +358,26 @@ if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) { return; } - if (/\d(\s|\/)+$/.test(value)) { + if (/\d\s\/\s$/.test(value)) { e.preventDefault(); - return $target.val(value.replace(/\d(\s|\/)*$/, '')); - } else if (/\s\/\s?\d?$/.test(value)) { - e.preventDefault(); - return $target.val(value.replace(/\s\/\s?\d?$/, '')); + return setTimeout(function() { + return $target.val(value.replace(/\d\s\/\s$/, '')); + }); } }; + reFormatCVC = function(e) { + var $target; + $target = $(e.currentTarget); + return setTimeout(function() { + var value; + value = $target.val(); + value = replaceFullWidthChars(value); + value = value.replace(/\D/g, '').slice(0, 4); + return safeVal(value, $target); + }); + }; + restrictNumeric = function(e) { var input; if (e.metaKey || e.ctrlKey) { @@ -330,6 +439,9 @@ if (!/^\d+$/.test(digit)) { return; } + if (hasTextSelected($target)) { + return; + } val = $target.val() + digit; return val.length <= 4; }; @@ -358,33 +470,44 @@ }; $.payment.fn.formatCardCVC = function() { - this.payment('restrictNumeric'); + this.on('keypress', restrictNumeric); this.on('keypress', restrictCVC); + this.on('paste', reFormatCVC); + this.on('change', reFormatCVC); + this.on('input', reFormatCVC); return this; }; $.payment.fn.formatCardExpiry = function() { - this.payment('restrictNumeric'); + this.on('keypress', restrictNumeric); this.on('keypress', restrictExpiry); this.on('keypress', formatExpiry); - this.on('keypress', formatForwardSlash); + this.on('keypress', formatForwardSlashAndSpace); this.on('keypress', formatForwardExpiry); this.on('keydown', formatBackExpiry); + this.on('change', reFormatExpiry); + this.on('input', reFormatExpiry); return this; }; $.payment.fn.formatCardNumber = function() { - this.payment('restrictNumeric'); + this.on('keypress', restrictNumeric); this.on('keypress', restrictCardNumber); this.on('keypress', formatCardNumber); this.on('keydown', formatBackCardNumber); this.on('keyup', setCardType); this.on('paste', reFormatCardNumber); + this.on('change', reFormatCardNumber); + this.on('input', reFormatCardNumber); + this.on('input', setCardType); return this; }; $.payment.fn.restrictNumeric = function() { this.on('keypress', restrictNumeric); + this.on('paste', reFormatNumeric); + this.on('change', reFormatNumeric); + this.on('input', reFormatNumeric); return this; }; @@ -394,8 +517,7 @@ $.payment.cardExpiryVal = function(value) { var month, prefix, year, _ref; - value = value.replace(/\s/g, ''); - _ref = value.split('/', 2), month = _ref[0], year = _ref[1]; + _ref = value.split(/[\s\/]+/, 2), month = _ref[0], year = _ref[1]; if ((year != null ? year.length : void 0) === 2 && /^\d+$/.test(year)) { prefix = (new Date).getFullYear(); prefix = prefix.toString().slice(0, 2); @@ -423,7 +545,7 @@ }; $.payment.validateCardExpiry = function(month, year) { - var currentTime, expiry, prefix, _ref; + var currentTime, expiry, _ref; if (typeof month === 'object' && 'month' in month) { _ref = month, month = _ref.month, year = _ref.year; } @@ -438,13 +560,18 @@ if (!/^\d+$/.test(year)) { return false; } - if (!(parseInt(month, 10) <= 12)) { + if (!((1 <= month && month <= 12))) { return false; } if (year.length === 2) { - prefix = (new Date).getFullYear(); - prefix = prefix.toString().slice(0, 2); - year = prefix + year; + if (year < 70) { + year = "20" + year; + } else { + year = "19" + year; + } + } + if (year.length !== 4) { + return false; } expiry = new Date(year, month); currentTime = new Date; @@ -454,13 +581,14 @@ }; $.payment.validateCardCVC = function(cvc, type) { - var _ref, _ref1; + var card, _ref; cvc = $.trim(cvc); if (!/^\d+$/.test(cvc)) { return false; } - if (type) { - return _ref = cvc.length, __indexOf.call((_ref1 = cardFromType(type)) != null ? _ref1.cvcLength : void 0, _ref) >= 0; + card = cardFromType(type); + if (card != null) { + return _ref = cvc.length, __indexOf.call(card.cvcLength, _ref) >= 0; } else { return cvc.length >= 3 && cvc.length <= 4; } @@ -476,22 +604,49 @@ $.payment.formatCardNumber = function(num) { var card, groups, upperLength, _ref; + num = num.replace(/\D/g, ''); card = cardFromNumber(num); if (!card) { return num; } upperLength = card.length[card.length.length - 1]; - num = num.replace(/\D/g, ''); - num = num.slice(0, +upperLength + 1 || 9e9); + num = num.slice(0, upperLength); if (card.format.global) { return (_ref = num.match(card.format)) != null ? _ref.join(' ') : void 0; } else { groups = card.format.exec(num); - if (groups != null) { - groups.shift(); + if (groups == null) { + return; } - return groups != null ? groups.join(' ') : void 0; + groups.shift(); + groups = $.grep(groups, function(n) { + return n; + }); + return groups.join(' '); } }; + $.payment.formatExpiry = function(expiry) { + var mon, parts, sep, year; + parts = expiry.match(/^\D*(\d{1,2})(\D+)?(\d{1,4})?/); + if (!parts) { + return ''; + } + mon = parts[1] || ''; + sep = parts[2] || ''; + year = parts[3] || ''; + if (year.length > 0) { + sep = ' / '; + } else if (sep === ' /') { + mon = mon.substring(0, 1); + sep = ''; + } else if (mon.length === 2 || sep.length > 0) { + sep = ' / '; + } else if (mon.length === 1 && (mon !== '0' && mon !== '1')) { + mon = "0" + mon; + sep = ' / '; + } + return mon + sep + year; + }; + }).call(this);