UNPKG

pixelpay-module

Version:

Modulo para integrar pagos con PixelPay

658 lines (571 loc) 21.8 kB
export default class PixelPay { constructor () { this.cancel = ''; this.complete = ''; this.order_id = ''; this.amount = ''; this.firstName = ''; this.lastName = ''; this.email = ''; this.baseURL = ''; this.debug = true; this.request = { _key: '', _callback: '', _cancel: '', _complete: '', _order_id: '', _order_date: '', _order_content: [], _order_extras: [], _currency: '', _tax_amount: '', _shipping_amount: '', _amount: '', _first_name: '', _last_name: '', _email: '', _address: '', _address_alt: '', _zip: '', _city: '', _state: '', _country: '', }; } setup(key, endpoint, debug = true) { this.debug = debug; this.key = this.isValidKey(key) var isValid = /^https?:\/\/[\w\-]+(\.[\w\-]+)+[/#?]?.*$/.test(endpoint); if (isValid) { this.baseURL = endpoint; } else { this.logger('La URL del endpoint no es una URL Valida.'); } return true; } sendPayment(){ return new Promise((resolve, reject) => { if (this.key && this.baseURL) { this.request._key = this.key; if (this.request._order_content.length) { this.recalculateAmount(); } if (this.isValid()) { this.request.json = true; this.request.sdk = true; this.createModal(); document.addEventListener('frameclose', function(){ reject(new Error ('Ventana de pago cerrada.')); }); if (this.request._order_extras.length) { this.validExtras() } var counter = 0; var urlRequest = `${this.baseURL}/hosted/payment/sdk`; for (const [key, value] of Object.entries(this.request)) { var val = this.checkInstance(value) if (val) { urlRequest = (counter === 0) ? `${urlRequest}?${key}=${val}` : `${urlRequest}&${key}=${val}`; } counter++; } var self = this; var request = new XMLHttpRequest(); request.open("GET", urlRequest, true); request.addEventListener('load',function(){ var response = JSON.parse(request.responseText); if (response.success) { var url = `${response.url}` self.addIframe(url); window.onmessage = function(event) { if (event && event.data.success) { var resp = event.data; if (self.request._complete) { window.location.replace(resp.data.payment_hash); } else { resp.data.payment_hash = resp.data.payment_hash.split('=')[1]; resolve(resp); } }else { console.log(event); } } } else { if (response.hasOwnProperty('message')) { reject('PIXELPAY: ' + response.message); } if (response.hasOwnProperty('errors')) { reject(response.errors); } self.closeFrame(); reject(); } }) request.send(null); } else { reject(new Error('PixelPay. Uno o más valores requeridos no son validos')); } } else { reject(new Error(`PIXELPAY: para poder enviar los datos de cobro el campo "key", "baseURL" deben estar validados con el método setup().`)); } }) } /** * Metodo publico para validar la llave de un comercio. * @param {*} key - llave del comercio. Valor alfanumerico sin espacios ni caracteres especiales. */ isValidKey(key) { var long = key.length; if (long >= 8 && /^[A-Za-z0-9]*$/.test(key)) { return key } this.logger('El valor de la llave no es un valor válido'); } /** * Metodo publico para validar el order_id de una transaccion. * Debe ser un string sin espacios. * @param {*} string - */ setOrderID(string) { var isValid = /^[A-Za-z0-9]*$/.test(String(string)); if (isValid && string) { this.order_id = string; this.request._order_id = string; return true; } this.logger('El campo "order_id" debe ser un string sin espacios en blanco o caracteres especiales'); } /** * Formato valido 'dd-mm-aa hh:mm' * @param {*} date - fecha y hora de la orden generada */ setDate(date) { var isValid = /\d{4}-\d{2}-\d{2}/.test(date); if (isValid) { this.request._order_date = date; return; } this.logger('Fecha invalida. El formato del campo "order_date" es: "YYYY-MM-DD"'); } /** * Valor de la moneda ej. HNL * @param {*} currency */ setCurrency(currency) { if (currency.length === 3 && typeof currency === 'string' && /[A-Za-z]/.test(currency)) { this.request._currency = currency.toUpperCase(); return true; } this.logger('Formato de moneda invalido. Debe ser un string de 3 caracteres.') } /** * * @param {*} value - Valor de tipo float. * @param {*} key - Llave del objeto @var request */ addAmount(value, key) { if (!isNaN(parseFloat(value))) { var isValid = false; switch(key) { case 'amount': this.amount = value; this.request._amount = this.amount; return true; case 'tax_amount': this.request._tax_amount = value; isValid = true; break; case 'shipping_amount': this.request._shipping_amount = value; isValid = true; break; case 'price': return value; case 'tax': return value; case 'default': this.invalidKeyException(key) } return isValid; } this.logger(`El valor asignado a ${key} no es un valor valido. El método espera recibir un valor de tipo float.`); } setAmount(amount) { return this.addAmount(amount, 'amount'); } setTaxAmount(amount) { return this.addAmount(amount, 'tax_amount'); } setShippingAmount(amount) { return this.addAmount(amount, 'shipping_amount'); } /** * Valores permitidos: (first_name, last_name, city, state, country, zip, address, address_alt) * @param {*} string - Valor asignado a la llave * @param {*} key - Key del objeto @param request * @param min - Minimo de caracteres permitidos * @param max - Maximo de caracteres permitidos */ addStringValue(string, key, min = 3, max = 120){ if (this.validLength(String(string), min, max)) { switch (key) { case 'first_name': this.firstName = string; this.request._first_name = this.firstName; return true; case 'last_name': this.lastName = string; this.request._last_name = this.lastName; return true; case 'city': this.request._city = string; return true; case 'state': this.request._state = string; return true; case 'country': this.request._country = string; return true; case 'zip': this.request._zip = string; return true; case 'address': this.request._address = string; return true; case 'address_alt': this.request._address_alt = string; return true; default: this.invalidKeyException(key) } } else { this.invalidLengthException(key, min, max) } } setFirstName(value) { return this.addStringValue(value, 'first_name'); } setLastName(value){ return this.addStringValue(value, 'last_name') } setCity(value){ return this.addStringValue(value, 'city') } setState(value){ return this.addStringValue(value, 'state') } setCountry(value){ return this.addStringValue(value, 'country') } setZip(value){ return this.addStringValue(value, 'zip') } setAddress(value){ return this.addStringValue(value, 'address') } setAlternateAddress(value){ return this.addStringValue(value, 'address_alt') } /** * * @param {*} url - Valor a evaluar, Debe ser una URL valida * @param {*} key - Llave en @var request */ addKeyUrl(url, key){ var isValid = /^https?:\/\/[\w\-]+(\.[\w\-]+)+[/#?]?.*$/.test(url); if (isValid) { switch (key) { case 'callback': this.request._callback = url break; case 'cancel': this.cancel = url; this.request._cancel = this.cancel; break; case 'complete': this.complete = url; this.request._complete = this.complete; break; default: this.invalidKeyException(key) } return true; } this.invalidUrlException(url); } setCallBack(url){ return this.addKeyUrl(url, 'callback'); } setCancel(url){ return this.addKeyUrl(url, 'cancel'); } setComplete(url){ return this.addKeyUrl(url, 'complete'); } /** * * @param {*} title - Nombre del producto * @param {*} price - Valor del producto * @param qty - Canditadd @default 1 * @param description - Descripcion del producto * @param code - Codigo del producto * @param tax - Impuesto del producto */ setItemContent(title, price, qty = 1, description = "", code = "", tax = ""){ if (this.validLength(title, 3, 60) && !isNaN(parseFloat(price)) && !isNaN(parseInt(qty))) { if (tax) { tax = parseFloat(tax); if (isNaN(tax)) { this.logger('valor de "tax" no es un valor valido.') } } var total = (price * qty); var item = { 'title': title, 'code': code, 'description': description, 'price': parseFloat(price), 'qty': parseInt(qty), 'tax': tax, 'total': total, } this.request._order_content.push(item) return true; } else { this.logger('Uno o más valores no son válidos, revisa la documentación'); } } clearItemContent() { this.request._order_content = []; return true; } /** * * @param {*} key - Llave del objecto que se creara * @param {*} value - Valor de la llave */ setContentExtra(key, value){ if (key && value) { var obj = new Object(); obj[key] = value; this.request._order_extras.push(obj) return true; } this.logger('No se aceptan valores nulos') } setJsonContentExtra(json) { if ( typeof json === 'object') { this.request._order_extras.push(json); return true; } this.logger('El valor debe ser de tipo JSON'); } clearConentExtra() { this.request._order_extras = []; return true; } /** * * @param {*} email - correo del cliente. */ setEmail(email) { var valid = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$/.test(email); if (valid) { this.email = email; this.request._email = this.email; return true; } this.logger(`El valor para el campo de email no es un correo valido`); } /** * * @param {*} string - valor alfanumerico a evaluar * @param {*} min - minimo de catacteres validos * @param {*} max - Maximo de caracteres */ validLength(string, min, max) { if (string.length >= min && string.length <= max) { return true; } return false; } /** * Metodo para validar los requisitos minimos para el envio. */ isValid(){ if (this.setOrderID(this.order_id) && this.setAmount(this.amount, 'amount') && this.setFirstName(this.firstName, 'first_name') && this.setLastName(this.lastName, 'last_name') && this.setEmail(this.email)) { return true; } return false; } /** * * @param {*} value - Metodo para evaluar si un valor es de tipo objeto * si es asi el valor se convierte a string. es llamado en el @method sendPayment() */ checkInstance(value){ if (typeof value == 'object'){ if (value.length) { var str = this.convertToString(value) return str; } return false; } return value; } /** * * @param {*} obj - Valor de tipo objeto que sera convertido a string. * Es llamado en el @method checkInstance() y @method validExtras() */ convertToString(obj){ var json = JSON.stringify(obj); var base64 = window.btoa(json); return encodeURI(base64); } /** * Metodo utilizado para recalcular el precio el array @var request._order_content * es mayor que 0 */ recalculateAmount(){ var totalAmount = 0; var tax_amount = 0; for (const variant of Object.values(this.request._order_content)) { var price = String(variant.price).replace(/[&\/\\#,+()$~%'":*?<>{}]/g, ''); tax_amount = (parseFloat(tax_amount) + parseFloat(variant.tax)); totalAmount = (parseFloat(totalAmount) + (parseFloat(price) * parseInt(variant.qty))) } this.request._tax_amount = tax_amount; this.setAmount(totalAmount); } /** * Metodo para convertir a string los valores extras. * @return void */ validExtras(){ var items = this.request._order_extras.values(); var new_obj = {}; var arr = []; for (var value of items) { Object.keys(value).forEach(function (val) { new_obj[val] = value[val]; }) } arr.push(new_obj); return arr; } createModal(){ document.getElementsByTagName("html")[0].style.overflow = "hidden"; var element = document.createElement('div'); element.setAttribute('id', 'pixelpay-frame') element.style.cssText = 'position: fixed;top: 0;bottom: 0;left: 0;right: 0;background-color: rgba(0,0,0,.8);overflow: hidden;height: 100vh;padding: 50px 0 0 0; opacity: 0; visibility: hidden; transition: opacity .3s;'; document.body.appendChild(element); var loading = document.createElement('div'); loading.style.cssText = 'width:200px; height:4px; background:linear-gradient(to right,#5a86f4,#5a86f4); background-color:#ccc; position:fixed; top:0; bottom:0; left:0; right:0; margin:auto; border-radius:4px; background-size:20%; background-repeat:repeat-y; background-position:-25% 0; animation:scroll 1.2s ease-in-out infinite;'; loading.classList.add('pixelpay--preloader') loading.animate([ { backgroundSize: '100%', }, { backgoundPosition: '125% 0;' } ],4000); element.appendChild(loading); setTimeout(() => { element.style.visibility = 'visible'; element.style.opacity = 1; }, 320); return true; } addIframe(url) { var element = document.getElementById('pixelpay-frame'); var iframe = document.createElement('iframe') iframe.classList.add('frame--loading'); iframe.style.cssText = 'width: 100%;height: 100%;' iframe.setAttribute('frameborder', 0); iframe.setAttribute('frameborder', 0); iframe.setAttribute('scrolling', 'auto'); iframe.setAttribute('src', url); iframe.setAttribute('id', 'pixelpay--iframe'); iframe.classList.add('pixelpay--iframe'); var btnClose = document.createElement('span'); btnClose.setAttribute('id', 'pixelpay--btn--close') var textnode = document.createTextNode("X"); btnClose.style.cssText = 'position: absolute; top: 0; right: 20px; color:white; font-size: 25px; cursor:pointer; line-height: 50px'; btnClose.appendChild(textnode) element.append(btnClose); element.append(iframe); document.body.appendChild(element) this.closeEventListener(); window.setTimeout(this.checkIframeLoaded, 100); } closeFrame(){ var el = document.getElementById("pixelpay--btn--close"); var fr = document.getElementById("pixelpay-frame"); if (el) { el.click(); } else { fr.style.opacity = 0; setTimeout(function(){ fr.style.visibility = 'hidden'; fr.remove(); }, 300); document.getElementsByTagName("html")[0].style.overflow = "auto"; } } closeEventListener(){ var el = document.getElementById("pixelpay--btn--close"); if (el) { el.addEventListener('click', function (){ var iframe = document.getElementById('pixelpay-frame'); iframe.style.opacity = 0; setTimeout(function(){ iframe.style.visibility = 'hidden'; iframe.remove(); }, 300); document.getElementsByTagName("html")[0].style.overflow = "auto"; document.dispatchEvent(new Event('frameclose')); }, false) } } checkIframeLoaded() { var iframe = document.getElementById('pixelpay--iframe'); var iframeDoc = iframe.contentDocument || iframe.contentWindow.document; if ( iframeDoc.readyState == 'complete' ) { var loading = document.querySelector('.pixelpay--preloader'); if (loading) { document.getElementById('pixelpay--iframe').classList.remove('frame--loading') setTimeout(function(){ loading.style.cssText="animation:scroll 1.2s ease-in-out infinite; display: none"; }, 1500) } return; } } logger(msj){ if (this.debug ) { // console.error(`PixelPay: ${msj}`); var error = new Error(msj) error.name = 'Pixelpay'; throw error; } } invalidKeyException(key) { var msj = `El valor de la llave '${key}' no es un valor valido.` this.logger(msj) } invalidLengthException(key, min, max){ var msj = `El mínimo de caracteres especificado para '${key}' es de mínimo ${min} y el máximo ${max}` this.logger(msj) } invalidUrlException(url){ var msj = `'${url}' NO es una URL valida. Ej. 'https://midominio.com'`; this.logger(msj); } }