From c5aea42e126c6bed7b4a973dd3d41712fdac8622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 8 Nov 2013 16:50:54 +0100 Subject: [PATCH] [IMP] website_payment: wip work to add credit card front-end management + jquery.payment library use bzr revid: tde@openerp.com-20131108155054-t4wsnvfu5eb0o3hf --- addons/website_payment/__openerp__.py | 2 +- .../lib/jquery.payment/jquery.payment.js | 497 ++++++++++++++++++ .../website_payment/static/src/css/Makefile | 2 + .../static/src/css/website_payment.css | 24 + .../static/src/css/website_payment.sass | 29 + .../website_payment/static/src/img/amex.png | Bin 0 -> 1349 bytes .../website_payment/static/src/img/diners.png | Bin 0 -> 1258 bytes .../static/src/img/discover.png | Bin 0 -> 1114 bytes .../static/src/img/grey-dots.png | Bin 0 -> 2916 bytes addons/website_payment/static/src/img/jcb.png | Bin 0 -> 1080 bytes .../static/src/img/mastercard.png | Bin 0 -> 1425 bytes .../static/src/img/placeholder.png | Bin 0 -> 3082 bytes .../website_payment/static/src/img/visa.png | Bin 0 -> 1088 bytes .../static/src/js/payment_acquirer.js | 12 + .../views/website_payment_templates.xml | 76 ++- 15 files changed, 633 insertions(+), 9 deletions(-) create mode 100644 addons/website_payment/static/lib/jquery.payment/jquery.payment.js create mode 100644 addons/website_payment/static/src/css/Makefile create mode 100644 addons/website_payment/static/src/css/website_payment.css create mode 100644 addons/website_payment/static/src/css/website_payment.sass create mode 100644 addons/website_payment/static/src/img/amex.png create mode 100644 addons/website_payment/static/src/img/diners.png create mode 100644 addons/website_payment/static/src/img/discover.png create mode 100644 addons/website_payment/static/src/img/grey-dots.png create mode 100644 addons/website_payment/static/src/img/jcb.png create mode 100644 addons/website_payment/static/src/img/mastercard.png create mode 100644 addons/website_payment/static/src/img/placeholder.png create mode 100644 addons/website_payment/static/src/img/visa.png create mode 100644 addons/website_payment/static/src/js/payment_acquirer.js diff --git a/addons/website_payment/__openerp__.py b/addons/website_payment/__openerp__.py index 9a4886c0d66..2a01d2f1aec 100644 --- a/addons/website_payment/__openerp__.py +++ b/addons/website_payment/__openerp__.py @@ -31,7 +31,7 @@ 'website', 'payment_acquirer', 'payment_acquirer_ogone', - 'payment_acquirer_paypal' + 'payment_acquirer_paypal', 'payment_acquirer_transfer', ], 'data': [ diff --git a/addons/website_payment/static/lib/jquery.payment/jquery.payment.js b/addons/website_payment/static/lib/jquery.payment/jquery.payment.js new file mode 100644 index 00000000000..c81c6e00d81 --- /dev/null +++ b/addons/website_payment/static/lib/jquery.payment/jquery.payment.js @@ -0,0 +1,497 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var $, cardFromNumber, cardFromType, cards, defaultFormat, formatBackCardNumber, formatBackExpiry, formatCardNumber, formatExpiry, formatForwardExpiry, formatForwardSlash, hasTextSelected, luhnCheck, reFormatCardNumber, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric, 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; + + $ = jQuery; + + $.payment = {}; + + $.payment.fn = {}; + + $.fn.payment = function() { + var args, method; + method = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + return $.payment.fn[method].apply(this, args); + }; + + defaultFormat = /(\d{1,4})/g; + + cards = [ + { + type: 'maestro', + pattern: /^(5018|5020|5038|6304|6759|676[1-3])/, + 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/, + 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)/, + format: defaultFormat, + length: [16], + cvcLength: [3], + luhn: true + }, { + type: 'mastercard', + pattern: /^5[1-5]/, + format: defaultFormat, + length: [16], + cvcLength: [3], + luhn: true + }, { + type: 'amex', + pattern: /^3[47]/, + format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/, + length: [15], + cvcLength: [3, 4], + luhn: true + }, { + type: 'visa', + pattern: /^4/, + format: defaultFormat, + length: [13, 14, 15, 16], + cvcLength: [3], + luhn: true + } + ]; + + cardFromNumber = function(num) { + var card, _i, _len; + num = (num + '').replace(/\D/g, ''); + for (_i = 0, _len = cards.length; _i < _len; _i++) { + card = cards[_i]; + if (card.pattern.test(num)) { + return card; + } + } + }; + + cardFromType = function(type) { + var card, _i, _len; + for (_i = 0, _len = cards.length; _i < _len; _i++) { + card = cards[_i]; + if (card.type === type) { + return card; + } + } + }; + + luhnCheck = function(num) { + var digit, digits, odd, sum, _i, _len; + odd = true; + sum = 0; + digits = (num + '').split('').reverse(); + for (_i = 0, _len = digits.length; _i < _len; _i++) { + digit = digits[_i]; + digit = parseInt(digit, 10); + if ((odd = !odd)) { + digit *= 2; + } + if (digit > 9) { + digit -= 9; + } + sum += digit; + } + return sum % 10 === 0; + }; + + hasTextSelected = function($target) { + var _ref; + 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; + } + return false; + }; + + reFormatCardNumber = function(e) { + var _this = this; + return setTimeout(function() { + var $target, value; + $target = $(e.currentTarget); + value = $target.val(); + value = $.payment.formatCardNumber(value); + return $target.val(value); + }); + }; + + formatCardNumber = function(e) { + var $target, card, digit, length, re, upperLength, value; + digit = String.fromCharCode(e.which); + if (!/^\d+$/.test(digit)) { + return; + } + $target = $(e.currentTarget); + value = $target.val(); + card = cardFromNumber(value + digit); + length = (value.replace(/\D/g, '') + digit).length; + upperLength = 16; + if (card) { + upperLength = card.length[card.length.length - 1]; + } + if (length >= upperLength) { + return; + } + if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) { + return; + } + if (card && card.type === 'amex') { + re = /^(\d{4}|\d{4}\s\d{6})$/; + } else { + re = /(?:^|\s)(\d{4})$/; + } + if (re.test(value)) { + e.preventDefault(); + return $target.val(value + ' ' + digit); + } else if (re.test(value + digit)) { + e.preventDefault(); + return $target.val(value + digit + ' '); + } + }; + + formatBackCardNumber = function(e) { + var $target, value; + $target = $(e.currentTarget); + value = $target.val(); + if (e.meta) { + return; + } + if (e.which !== 8) { + return; + } + if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) { + return; + } + if (/\d\s$/.test(value)) { + e.preventDefault(); + return $target.val(value.replace(/\d\s$/, '')); + } else if (/\s\d?$/.test(value)) { + e.preventDefault(); + return $target.val(value.replace(/\s\d?$/, '')); + } + }; + + formatExpiry = function(e) { + var $target, digit, val; + digit = String.fromCharCode(e.which); + if (!/^\d+$/.test(digit)) { + return; + } + $target = $(e.currentTarget); + val = $target.val() + digit; + if (/^\d$/.test(val) && (val !== '0' && val !== '1')) { + e.preventDefault(); + return $target.val("0" + val + " / "); + } else if (/^\d\d$/.test(val)) { + e.preventDefault(); + return $target.val("" + val + " / "); + } + }; + + formatForwardExpiry = function(e) { + var $target, digit, val; + digit = String.fromCharCode(e.which); + if (!/^\d+$/.test(digit)) { + return; + } + $target = $(e.currentTarget); + val = $target.val(); + if (/^\d\d$/.test(val)) { + return $target.val("" + val + " / "); + } + }; + + formatForwardSlash = function(e) { + var $target, slash, val; + slash = String.fromCharCode(e.which); + if (slash !== '/') { + return; + } + $target = $(e.currentTarget); + val = $target.val(); + if (/^\d$/.test(val) && val !== '0') { + return $target.val("0" + val + " / "); + } + }; + + formatBackExpiry = function(e) { + var $target, value; + if (e.meta) { + return; + } + $target = $(e.currentTarget); + value = $target.val(); + if (e.which !== 8) { + return; + } + if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) { + return; + } + if (/\d(\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?$/, '')); + } + }; + + restrictNumeric = function(e) { + var input; + if (e.metaKey || e.ctrlKey) { + return true; + } + if (e.which === 32) { + return false; + } + if (e.which === 0) { + return true; + } + if (e.which < 33) { + return true; + } + input = String.fromCharCode(e.which); + return !!/[\d\s]/.test(input); + }; + + restrictCardNumber = function(e) { + var $target, card, digit, value; + $target = $(e.currentTarget); + digit = String.fromCharCode(e.which); + if (!/^\d+$/.test(digit)) { + return; + } + if (hasTextSelected($target)) { + return; + } + value = ($target.val() + digit).replace(/\D/g, ''); + card = cardFromNumber(value); + if (card) { + return value.length <= card.length[card.length.length - 1]; + } else { + return value.length <= 16; + } + }; + + restrictExpiry = function(e) { + var $target, digit, value; + $target = $(e.currentTarget); + digit = String.fromCharCode(e.which); + if (!/^\d+$/.test(digit)) { + return; + } + if (hasTextSelected($target)) { + return; + } + value = $target.val() + digit; + value = value.replace(/\D/g, ''); + if (value.length > 6) { + return false; + } + }; + + restrictCVC = function(e) { + var $target, digit, val; + $target = $(e.currentTarget); + digit = String.fromCharCode(e.which); + if (!/^\d+$/.test(digit)) { + return; + } + val = $target.val() + digit; + return val.length <= 4; + }; + + setCardType = function(e) { + var $target, allTypes, card, cardType, val; + $target = $(e.currentTarget); + val = $target.val(); + cardType = $.payment.cardType(val) || 'unknown'; + if (!$target.hasClass(cardType)) { + allTypes = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = cards.length; _i < _len; _i++) { + card = cards[_i]; + _results.push(card.type); + } + return _results; + })(); + $target.removeClass('unknown'); + $target.removeClass(allTypes.join(' ')); + $target.addClass(cardType); + $target.toggleClass('identified', cardType !== 'unknown'); + return $target.trigger('payment.cardType', cardType); + } + }; + + $.payment.fn.formatCardCVC = function() { + this.payment('restrictNumeric'); + this.on('keypress', restrictCVC); + return this; + }; + + $.payment.fn.formatCardExpiry = function() { + this.payment('restrictNumeric'); + this.on('keypress', restrictExpiry); + this.on('keypress', formatExpiry); + this.on('keypress', formatForwardSlash); + this.on('keypress', formatForwardExpiry); + this.on('keydown', formatBackExpiry); + return this; + }; + + $.payment.fn.formatCardNumber = function() { + this.payment('restrictNumeric'); + this.on('keypress', restrictCardNumber); + this.on('keypress', formatCardNumber); + this.on('keydown', formatBackCardNumber); + this.on('keyup', setCardType); + this.on('paste', reFormatCardNumber); + return this; + }; + + $.payment.fn.restrictNumeric = function() { + this.on('keypress', restrictNumeric); + return this; + }; + + $.payment.fn.cardExpiryVal = function() { + return $.payment.cardExpiryVal($(this).val()); + }; + + $.payment.cardExpiryVal = function(value) { + var month, prefix, year, _ref; + value = value.replace(/\s/g, ''); + _ref = value.split('/', 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); + year = prefix + year; + } + month = parseInt(month, 10); + year = parseInt(year, 10); + return { + month: month, + year: year + }; + }; + + $.payment.validateCardNumber = function(num) { + var card, _ref; + num = (num + '').replace(/\s+|-/g, ''); + if (!/^\d+$/.test(num)) { + return false; + } + card = cardFromNumber(num); + if (!card) { + return false; + } + return (_ref = num.length, __indexOf.call(card.length, _ref) >= 0) && (card.luhn === false || luhnCheck(num)); + }; + + $.payment.validateCardExpiry = function(month, year) { + var currentTime, expiry, prefix, _ref; + if (typeof month === 'object' && 'month' in month) { + _ref = month, month = _ref.month, year = _ref.year; + } + if (!(month && year)) { + return false; + } + month = $.trim(month); + year = $.trim(year); + if (!/^\d+$/.test(month)) { + return false; + } + if (!/^\d+$/.test(year)) { + return false; + } + if (!(parseInt(month, 10) <= 12)) { + return false; + } + if (year.length === 2) { + prefix = (new Date).getFullYear(); + prefix = prefix.toString().slice(0, 2); + year = prefix + year; + } + expiry = new Date(year, month); + currentTime = new Date; + expiry.setMonth(expiry.getMonth() - 1); + expiry.setMonth(expiry.getMonth() + 1, 1); + return expiry > currentTime; + }; + + $.payment.validateCardCVC = function(cvc, type) { + var _ref, _ref1; + 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; + } else { + return cvc.length >= 3 && cvc.length <= 4; + } + }; + + $.payment.cardType = function(num) { + var _ref; + if (!num) { + return null; + } + return ((_ref = cardFromNumber(num)) != null ? _ref.type : void 0) || null; + }; + + $.payment.formatCardNumber = function(num) { + var card, groups, upperLength, _ref; + 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); + 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(); + } + return groups != null ? groups.join(' ') : void 0; + } + }; + +}).call(this); diff --git a/addons/website_payment/static/src/css/Makefile b/addons/website_payment/static/src/css/Makefile new file mode 100644 index 00000000000..7f7f16d3616 --- /dev/null +++ b/addons/website_payment/static/src/css/Makefile @@ -0,0 +1,2 @@ +sass: + sass --trace -t expanded website_payment.sass:website_payment.css diff --git a/addons/website_payment/static/src/css/website_payment.css b/addons/website_payment/static/src/css/website_payment.css new file mode 100644 index 00000000000..e61696fcd30 --- /dev/null +++ b/addons/website_payment/static/src/css/website_payment.css @@ -0,0 +1,24 @@ +@charset "UTF-8"; + +input#cc_number { + background-image: url('/website_payment/static/src/img/grey-dots.png'); + background-repeat: no-repeat; + background-position: 14px 14px; +} + +div.card_placeholder { + background-image: url('/website_payment/static/src/img/placeholder.png'); + background-repeat: no-repeat; + width: 32px; + height: 20px; +} + +/*div.visa { + background-image: url('/website_payment/static/src/img/placeholder.png'); + background-repeat: no-repeat; + background-position: 14px 14px; +}*/ + +input#cc_number:active, input#cc_number:focus { + background-image: none; +} diff --git a/addons/website_payment/static/src/css/website_payment.sass b/addons/website_payment/static/src/css/website_payment.sass new file mode 100644 index 00000000000..d467df2de47 --- /dev/null +++ b/addons/website_payment/static/src/css/website_payment.sass @@ -0,0 +1,29 @@ +@charset "utf-8" + +@import "compass/css3" + +.css_website_mail + .has-error + border-color: red + .css_nav_month + display: none + &:first-of-type + display: block + +.blog_content + a.oe_mail_expand:after + content: " →" + a.oe_mail_expand + font-weight: bold + +p.post-meta + position: relative + top: -5px + +.js_website_blog + div#right_column + section + +opacity(0.6) + section:hover + +opacity(1) + @include transition(all 0.2s ease-out) diff --git a/addons/website_payment/static/src/img/amex.png b/addons/website_payment/static/src/img/amex.png new file mode 100644 index 0000000000000000000000000000000000000000..7de472cdd9b267b059a5e6a2c637dad2a077c1a8 GIT binary patch literal 1349 zcmV-L1-kl)P)V!Z9CSrkbW?9;ba!ELWdK2BZ(?O2Mrm?ocW-iQb09-gHt4*vi~s-xpGibP zR7i=nl}(6bR~5&9=f01su8;1io}S5MIx}&j8N!&*Ac&|aj^jcX3gRMU(JmBpr7J<* zx(t}TAh-}gaS=p=M3hKyWk`A^W*nj!Gu<;2`m4IS>Q&c!_nv!Pyz1(1rdQHFxV+1` z_ucb*_x#T}VrBq*`?cXY;ALQy|L^k);QK$j+1&$353M2S%k8FHUA~ynOa%vsoQ+2_ zh`=Nm4>tX3F^K@*JOQ9yS3A9gy?swqBRMPYhv%E&5}`9E><@MIF@WF$gCXRJkUD|0 z^-naYDx*>(_%O520K5;Bqd=*OnBfFO1f$l79176cGN3V{YTVn{=W4IP!w*ON;q6Vn z{QO1AamD@Z1MWQ-5-P2Qrjg6@IV&HT=U0DS=d;iBxHO;hli%D$HJ$?C6<-FbD$D>> zjSJld&pthmsu81c5Y3^%A&C%y@BaKYT83%WlcNSDwMa3;rLGoOG%&-= z>eNjg8Y1EZBB&35B-7ru^Bhds&1aVSpqSBet!8ok4$f5ED^$|>uElhP)O>Jse zG)y7JK+I-tPjtgN-c^_Z{BI4(PBqm4_3gxC8j$RhKTNybZlkmO(O)Kw)>ktGZ2EU+ z)e&u4F9bY>mg84;@BQP&qA1F=*Xt!Agx22r-IwMTE}bXseCCYYp3V}6Zw$BBUutA| zE6?+g4h93-?Y0dEgRRMU{5>FBSXjV&kE)`oV!Z9CSrkbW?9;ba!ELWdK2BZ(?O2Mrm?ocW-iQb09-gHt4*vi~s-xL`g(J zR7i=nl}~6~RTRd5_syF(lQ?}dDb{A1q)n+J3U*>^X`6ziR9vXkxN%bHqDgd(x6>13K@X5OE3T)a1vc_eicdf~$5 zz4N~F-Sd6tJfo@r{Pc6V0E_`6Jlu5_7~S<;sRCd(pQ`FuS0O*LZQB+s%X%o8MT8qS zrbq5o<^c{T35bsL_7?H|5LF*A02s5jq;Y`(#Tbj;-Xe2zm5~($B#u-Sz^plOb2V{P zO^m&qD?vb2X!^mJh`DKFp{h*Xsd0Q_hHt0-WufjvDg_w_G8yun4hJ81IksmbTikSl zgtjiH@s`%Rme?xV7LfcvMEGLzA6^>&h3y+Uxcu%e^yG_N9o@}`gWVJss?1jw_;m6P zyUza3g+D6~#8OpMR;hrf5{hD3k^1cl{BdWI56(~V_0heQin-SFQZYxV_yohHB10ct zrXrnG{S-$o{Kb_wpJub`B&rZyMiUZPi2xBKj2JdIs*Fuc^Y&mLrDBfq>;n5wmjT#+ zx=eX?fl@KY=ukf_E}+U2Q~q4_{(cd zT)zvz#Pz!jpN+^24{U;39fAOYfb!o}KoeN1VSHZAgraefg@|H|0tQvXuQ%^e>TvvO2KYu`Le!UL@ehzcUhE^Rl^jvkltt$6?jHg-n0 z1PB89W5<_o%}1>hSK#?AZbJJeX5@j@x*(Py4CA1-{Y;T$U=Itaj}(LqW*F6EgD zV;8SQ1pI)!4M+F&A(F&ud0@3RR8_)IV%_6*G|_^`zj5;XBnwMESO#nx>=gN&&8s_lIrhp^bg#4VJRcEB_&_A;h}B22C_xaS3L=VS zS#6_O2t%Q-;Be-hJ=pfX6@N?-AqYZz-=|h<#MMmtEAm+QU#%js2 zmet<>^?HNZ+1pgB)eH5y_eMIMuGy+;yRMsCTwFZ0v{c(LH&@xawv_+BJg@PCZQG|( zsZ{R%{rkS%)zw811Zr8>DHIBLo<~PVr#C-8U(M(9>NpPn0T~$@ UR`2sxrvLx|07*qoM6N<$f;JpL?f?J) literal 0 HcmV?d00001 diff --git a/addons/website_payment/static/src/img/discover.png b/addons/website_payment/static/src/img/discover.png new file mode 100644 index 0000000000000000000000000000000000000000..692bd76d40384418c90c3df2ad7af81de10263a9 GIT binary patch literal 1114 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 literal 0 HcmV?d00001 diff --git a/addons/website_payment/static/src/img/placeholder.png b/addons/website_payment/static/src/img/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..665f42efba8f06de5ec7785cbccfba20cbd28aa2 GIT binary patch literal 3082 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 Y0gt{88CB!5Z2$lO07*qoM6N<$g6G8hF#rGn literal 0 HcmV?d00001 diff --git a/addons/website_payment/static/src/img/visa.png b/addons/website_payment/static/src/img/visa.png new file mode 100644 index 0000000000000000000000000000000000000000..0358fbf00b6ed119d6d01d12fa26e2761132c4fe GIT binary patch literal 1088 zcmV-G1i$-V!Z9CSrkbW?9;ba!ELWdK2BZ(?O2Mrm?ocW-iQb09-gHt4*vi~s-wnn^@K zR7i=vmQ83}RTze!bMM@lJ3pBjgNdy%O+->tG}2Vig}P{|_yZ~k3NB0`Zu|qIxKnT^ zT10fwjoL*~?5@>9bt430l~SyUCT+AyYTBl0XA(0rcmD3r+;d!Hawn6fE~L{JE_~lP z_k8dBz2}_E6-p@pZhPy8H1G+qkJjQ4Fp~LrZyq4DHtN~xQ^Sl5wQt*sZWt|L_PrV_ z7Z>+=wj-!F8VGQ0ITSZ-Dhq$NgnSKQplqw5YXo?Z(Lg|Fo1mvNdQ$`}T@^tb2tY}cg0)-GDhMG!$spH)3FQL{DJ4=$d^?Xj z{t>0G?#BN9SFDl%h}H<{xi?9U$3A!Wkw#|%7si3%_);M1wWo$ zW@+oU+bMAG;<0;E95_D1ok>Y=XPl|IEK>Fa^`w;7aX`wt z1G4FPxIfL2nI6uZTja$@y6}CU$=M7$Z;w!`)tH!F=HRQl=uHVGXENl94#$6)V_{*5 z6Ti(fdG1P6J*5<^YjP6@8tMRc+>ziG!v)}pU0aaSr>9Ik&r6v4L9JrCt!eNBdXdu`E>)_6dKzB>o8@QBw zX<{3!bD*3nb+`Aop&Q!um)!vPo@C|EVt2BhhaTT9^hhLPeDGxYaJ5){A#$sM2#NnJ z@_(Uf3TN5FvHioZ+!r-VCEE*Wnx>nk89j6|zyGN|Bc90ebW7OlupD*l$XRE9p-_v) zV!9O1-JcBSa#k!Bi=-^eiH1U}+v0g(7+{*Ke@B&yhm>HN0;Lp&Vc<9(>2xyqz__kU zx$M!=k)l?sQLR= - + Paypal (Test) /payment/paypal/test - 50 + 90 - - + Ogone (Test) /payment/ogone/test - 50 + 91 + + + + Transfer (Test) + /payment/transfer/test + + 92 - + - + + +