UNPKG

chilepay-sdk

Version:

Chilepay SDK module for Payments integration

395 lines (361 loc) 15.9 kB
"use strict"; const crypto = require('crypto'); const https = require('https'); const url = require('url'); const qs = require('querystring'); const API_VERSION = 'dev'; /** * @typedef {object} MetaDataElement * * @property {string|undefined} type - El tipo de dato. Por omisión es 'text'. Valores válidos: * - text: para texto. * Codificado como string. * - email: para email. * Codificado como string. * - phone: para número de teléfono. * Codificado como string. * - link: para link. * Codificado como string. * - date: para fechas. * Codificado como string. Usar formato yyyy-mm-dd * Ejemplo: 1989-11-08 * - time: Para tiempo (hora). * Codificado como string. Usar formato hh:mm:ss, formato de 24 horas. * Ejemplo: 09:12:58 * - datetime: para fecha y hora * Codificado como string. Formato yyyy-mm-ddThh:mm:ss (ISO 8601) * Ejemplo: 1989-11-08T09:11:58 * - documentId: para el folio de algún documento, por RUT, número de pasaporte, etc. * Codificado como string * Puede ir junto con .documentType para detallar si es RUT, pasaporte u otro. * Ejemplo: * - number: para un número albitrario. * Codificado como number. * Puede ir en conjunto con .currency o .unit * - list: para lista de elementos. * Codificado como un array. * Ejemplo: [ 'Zapallo', 'Mono' ] * - table: para estructuras de detalle. * Codificado como un array de arrays (una matriz), en donde cada arreglo es una fila. La primera fila siempre * es el encabezado. * Ejemplo: * [ * [ 'Ítem', 'Valor unitario', 'Cantidad', 'Sub Total' ], * [ 'Pilas AAAA4', 1500, 2, 3000 ], * [ 'Shampoo Champú', 2500, 1, 2500 ] * ] * Notar que el máximo de filas y columnas es indeterminado. La cantidad de columnas debe ser igual pora * todas las filas, incluyendo el encabezado. * * @property {string} name - Un rótulo que acompaña al dato, por ejemplo "Nombre" * * @property {*} value - El valor, codificado según type. * * Los siguientes atributos son opcionales según .type * @property {string|undefined} currency - Solo si .type es 'number', opcional. Usar para indicar que el número es un * monto monetario. El valor es el tipo de divisa según la ISO 4217. * Por ejemplo, 'CLP' es 'Peso chileno', 'CLF' es UF (Unidad de Fomento, Chile), 'USD' para doólar estadounidense, etc. * Si la divisa no tiene un código ISO, usar lo más cercano posible, por ejemplo: 'BTC' para Bitcoin. * * @property {string|undefined} unit - Solo si .type es 'nuber', opcional. * Usar para indicar la unidad de lo que sea que exprese el número, por ejemplo: * - u: unidades * - l: litros * - kg: kilográmos * etc. No hay valores predefinidos. * */ class Chilepay { /** * * @param {string} apiKey * @param {string} secretKey */ constructor(apiKey, secretKey) { if(!apiKey || !secretKey) { throw new Error('apiKey and/or secretKey are missing'); } this.apiKey = apiKey; this.secretKey = secretKey; } /** * Retorna las tarifas estándar de Chilepay. * * @return {*} */ getFees() { return this.get('fees', {}); } /** * * - - - - - - Los siguientes son los parámetros OBLIGATORIOS para iniciar la transacción - - - - - - * * @param {string} provider el proveedor de pasarelas de pago que deseas que Chilepay use en la transacción. * Las opciones son: * - webpay: * - Para cobro en tarjetas de débito y crédito bancarias en Chile. Operado por Transbank. * - Divisas soportadas: clp * - khipu: * - Para cobro en transferencias bancarias de banos chilenos. Operado por Khipu. * - Divisas soportadas: clp * - Adicionalmente a paymentUrl, retorna paymentSimplifiedTransferUrl (pasarela simplificada de Khipu) y * paymentTransferUrl (pasarela de transferencia normal de Khipu). * * @param params * * @param {string} params.subject descripción breve sobre el asunto de esta transacción. Texto libre. * Máximo 80 carácteres. * * @param {string} params.buyerEmail el email de quien está pagando. No es necesario que este * email corresponda a una cuenta de Chilepay. Si el pago se realiza de forma exitosa se le enviará el comprobante * a este email. Máximo 100 carácteres. * * @param {string} params.currency divisa usada en la transacción. Todos los montos de la transacción se entenderán * en esta divisa. Valores posibles: * - clp: Para pesos chilenos. Máximo de decimales: 0 * * @param {number} params.amount el monto a pagar. La cantidad mínima y máxima depende del plan contratado. * * @param {string} params.notifyUrl URL en que Chilepay realizará una petición y esperará el acuse de recibo para * confirmar la transacción. Máximo 255 carácteres. * * @param {string} params.returnUrl URL hacia donde Chilepay redirigirá al usuario una vez completada la * transacción (independiente de si la transacción fue exitosa). Máximo 255 carácteres. * * - - - - - - Los siguientes parámetros son OPCIONALES, pero recomendables para mejorar la usabilidad - - - - - - * * @param {...MetaDataElement?} params.metadata Array con información adicional sobre la transacción. * Esta información quedará asociada a esta transacción, y será mostrada en la GUI de Chilepay al consultar los * detalles de esta, tanto por el Comprador como por el Vendedor. * Además, será enviada junto con el comprobante de pago por email al comprador. * No hay máximo definido, pero se recomienda que en total no supere los 140Kb. * * @param {string?} params.selfUrl URL que apunta a los detalles de esta transacción dentro de tu aplicación. Se * mostrará en la GUI de Vendedor Chilepay. Máximo 255 carácteres. * * @param {string?} params.groupId identificador del grupo al que pertenece esta transacción, según la lógica de tu * aplicación. Por ejemplo, si quieres agrupar todas las transacciones sobre la compra de un artículo en tu tienda, * entonces pon el ID del artículo. De esta forma, el usuario podrá buscar todas las transacciones de tal artículo * por su ID. Máximo 40 carácteres. * * @param {string?} params.groupUrl URL que apunta a los detalles del grupo al que pertenece esta transacción, * según la lógica de tu aplicación. Se mostrá en la GUI de Vendedor Chilepay. Máximo 255 carácteres. * * - - - - - - Los siguientes parámetros son obligatorios solo si tu aplicación es reseller - - - - - - * * @param {object?} params.reseller * * @param {string?} params.reseller.sellerAccountId identificador de la cuenta de quién es el vendedor en esta * transacción. Recuerda que debes poseer la autorización del vendedor para que tu aplicación venda en su nombre, * usando el método cp.initAuth({scopes}). Obligatorio si la transacción es reseller. * * @param {number?} params.reseller.feePercent El porcentaje de comisión que se le descontarás al vendedor por esta * transacción (.params.amount es el 100%), adicionalmente a la comisión de Chilepay. * Este dinero irá hacia tu cuenta. El mínimo es 0, el máximo es el que te autorizó el vendedor (si te autorizo 3%, * entonces el máximo es 3). * Obligatorio si la transacción es reseller (si no deseas cobrar esta comisión, indica 0). * Máximo de decimales: 4 * Esta comisión es sumativa a tu favor con params.reseller.feeFixed * * @param {number?} params.reseller.feeFixed El monto fijo (en la divisa de la transacción) que le descontarás al * vendedor para esta transacción, adicional a la comisión de Chilepay. * Este dinero irá hacia tu cuenta. El mínimo es 0, el máximo es el que te autorizó el vendedor (en la divisa de * la transacción). * Obligatorio si la transacción es reseller (si no deseas cobrar esta comisión, indica 0). * Máximo de decimales: Según defina la divisa en .params.currency * Esta comisión es sumativa a tu favor con params.reseller.feePercent * * - - - - - - Los siguientes parámetros son opcionales, úsalos si es que te son útiles - - - - - - * * @param {string?} params.trackerToken token de esta transacción según la lógica de tu aplicación. No sobreescribe al * atributo .transactionId de la transacción (asignado por Chilepay). Lo recuperarás cada vez que llames a * cp.getTransaction(transactionId). Máximo 26 caracteres. * * @param {string?} params.mode determina el modo en que se hará la transacción. Usualmente debes ignorar esto. * Opciones posibles: * - ss: "Server Side" (valor por defecto). Indica que la transacción se realiza por el lado del servidor. * - cs: "Client Side" Usar solo si la transaccion se sigue realizando por el lado del servidor (más que mal, * tu secretKey jamás de los jamáses debe estar en el cliente), pero la transacción es iniciada por el plugin * Javascript SDK de Chilapay. * * @returns {Promise<{ * transactionId, paymentUrl * }>} */ initTransaction(provider, params) { if(typeof provider !== 'string') { return Promise.reject(new Error('provider is missing')); } if(typeof params !== 'object') { return Promise.reject(new Error('initTransactionInput object parameter missing')); } return this.post('transactions/' + provider, params); } /** * * @param {string} transactionId * @returns {*} */ getTransaction(transactionId) { return this.get('transactions/' + transactionId); } /** * * @param params * * @param {string?} params.trackerToken * * @param {string?} params.notifyUrl URL en donde te notificaremos si el usuario aceptó. Max 255 carácteres. * Es obligatorio si .mode es 'ss'. * * @param {string?} params.returnUrl URL hacia adonde dirigir al usuario al terminar. Max 255 carácteres. * Es obligatorio si .mode es 'ss'. * * @param params.scopes un objeto en donde cada clave representa un permiso a solicitar. * * @param {boolean} params.scopes.additionalInfo true para obtener información adicional de la cuenta del usuario. * Por defecto solo se informa el id de la cuenta del usuario. Información adicional: * - name: El nombre de la cuenta del usuario. * - email: El email de la cuenta del usuario. * - activeServices: El nombre de los servicios que el usuario tiene activos ('comprador', 'vendedor', 'desarrollador') * Falso o no definir para caso contrario. Usar solo si es realmente necesario. * * @param {object} params.scopes.reseller definir si deseas venden en nombre del usuario (usando su cuenta Vendedor). * * @param {float} params.scopes.reseller.maxFeePercent porcentaje de comisión máxima. Mínimo 0. * Notar que 0.1 es 0.1%, y 10 es 10%. No puede superar el límite permitido para tu cuenta. * * @param {number} params.scopes.reseller.maxFeeFixedClp monto fijo en pesos chilenos que solicitas como comisión * * @param {{string:boolean}?} params.scopes.reseller.providers un objeto con los proveedores de transacciones * electrónicas que deseas solicitar. Al menos indicar uno. Si se ignora se usará { all: true } * * @param {boolean?} params.scopes.reseller.providers.webpay true para solicitar webpay * @param {boolean?} params.scopes.reseller.providers.khipu true para solicitar khipu * @param {boolean?} params.scopes.reseller.providers.all true para solicitar todos los anteriores * * @param {object} params.scopes.receiveMoney solicitas autorización para realizar transferencias desde tu cuenta * hacia la del usuario. NO DISPONIBLE PARA ESTA VERSIÓN. * */ initAuth(params) { return this.post('auth', params); } /** * * * @param accountId */ getAuthStatus(accountId) { return this.get('auth/' + accountId); } /** * * @param url * @param params * @returns {*} */ get(url, params) { return this._request('get', url, params) } /** * * @param url * @param params * @returns {*} */ post(url, params) { return this._request('post', url, params) } /** * * @param url * @param params * @returns {*} */ put(url, params) { return this._request('put', url, params) } /** * * @param url * @param params * @returns {*} */ delete(url, params) { return this._request('delete', url, params) } _request(method, url, params) { return new Promise((resolve, reject) => { try { method = method.toUpperCase(); let jwt = Chilepay._jwtCreate({ type: 'api' }, 60 * 3 /* 3 minutes*/, this.secretKey); params = params || {}; let queryString, body; if(method === 'GET') { queryString = qs.stringify(params); body = ''; } else { body = JSON.stringify(params); } const req = https.request({ protocol: 'https:', hostname: 'api.chilepay.cl', port: null, method: method.toUpperCase(), path: '/' + API_VERSION + '/' + url + (queryString ? '?' + queryString : ''), headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + jwt, 'x-api-key': this.apiKey } }, (res) => { const chunks = []; res.on('data', (chunk) => chunks.push(chunk)); res.on('end', () => { let responseBody = JSON.parse(Buffer.concat(chunks).toString()); if (res.statusCode < 200 || res.statusCode > 299) { reject(responseBody); } else { resolve(responseBody); } }); }); req.on('error', (err) => reject(err)); req.write(body); req.end(); } catch(error) { reject(error); } }); } static _jwtCreate(payload, expireIn, secretKey) { let header = { alg: 'HS256', typ: 'JWT' }; let iat = Date.now() / 1000; Object.assign(payload, { iat: iat, exp: iat + expireIn }); let tokenHeader = Chilepay._base64UrlEncode(header); let tokenPayload = Chilepay._base64UrlEncode(payload); let signature = crypto.createHmac('sha256', secretKey) .update(tokenHeader + '.' + tokenPayload) .digest('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); return tokenHeader + '.' + tokenPayload + '.' + signature; } static _base64UrlEncode(object) { return (new Buffer(JSON.stringify(object)).toString('base64')) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } } module.exports = Chilepay;