diff --git a/modules/client/back/methods/tpv-transaction/confirm.js b/modules/client/back/methods/tpv-transaction/confirm.js index bec818bc9..b7d0c736f 100644 --- a/modules/client/back/methods/tpv-transaction/confirm.js +++ b/modules/client/back/methods/tpv-transaction/confirm.js @@ -1,4 +1,3 @@ -const crypto = require('crypto'); const UserError = require('vn-loopback/util/user-error'); const base64url = require('base64url'); @@ -21,19 +20,12 @@ module.exports = Self => { required: true, } ], - returns: { - type: 'Boolean', - root: true - }, http: { path: `/confirm`, verb: 'POST' } }); - /* - * Source: https://github.com/santiperez/node-redsys-api - */ Self.confirm = async(signatureVersion, merchantParameters, signature) => { const $ = Self.app.models; @@ -56,19 +48,11 @@ module.exports = Self => { fields: ['id', 'secretKey'] }); - const secretKey = Buffer.from(merchant.secretKey, 'base64'); - const iv = Buffer.alloc(8, 0); - - const cipher = crypto.createCipheriv('des-ede3-cbc', secretKey, iv); - cipher.setAutoPadding(false); - const orderKey = Buffer.concat([ - cipher.update(zeroPad(orderId, 8)), - cipher.final() - ]); - - const base64hmac = crypto.createHmac('sha256', orderKey) - .update(merchantParameters) - .digest('base64'); + const base64hmac = Self.createSignature( + orderId, + merchant.secretKey, + merchantParameters + ); if (base64hmac !== base64url.toBase64(signature)) throw new UserError('Invalid signature'); @@ -81,14 +65,6 @@ module.exports = Self => { params['Ds_Currency'], params['Ds_Response'], params['Ds_ErrorCode'] - ] - ); - return true; + ]); }; - - function zeroPad(buf, blocksize) { - const buffer = typeof buf === 'string' ? Buffer.from(buf, 'utf8') : buf; - const pad = Buffer.alloc((blocksize - (buffer.length % blocksize)) % blocksize, 0); - return Buffer.concat([buffer, pad]); - } }; diff --git a/modules/client/back/methods/tpv-transaction/end.js b/modules/client/back/methods/tpv-transaction/end.js new file mode 100644 index 000000000..5a757aa48 --- /dev/null +++ b/modules/client/back/methods/tpv-transaction/end.js @@ -0,0 +1,39 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethodCtx('end', { + description: 'Ends electronic payment transaction', + accessType: 'WRITE', + accepts: [ + { + arg: 'orderId', + type: 'string', + required: true, + }, { + arg: 'status', + type: 'string', + required: true, + } + ], + http: { + path: `/end`, + verb: 'POST' + } + }); + + Self.end = async(ctx, orderId, status) => { + const userId = ctx.req.accessToken.userId; + const transaction = await Self.findById(orderId, { + fields: ['id', 'clientFk'] + }); + + if (transaction?.clientFk != userId) + throw new UserError('Transaction not owned by user'); + + await Self.rawSql( + 'CALL hedera.tpvTransaction_end(?, ?)', [ + orderId, + status + ]); + }; +}; diff --git a/modules/client/back/methods/tpv-transaction/start.js b/modules/client/back/methods/tpv-transaction/start.js new file mode 100644 index 000000000..ea6ed05eb --- /dev/null +++ b/modules/client/back/methods/tpv-transaction/start.js @@ -0,0 +1,85 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethodCtx('start', { + description: 'Starts electronic payment transaction', + accessType: 'WRITE', + accepts: [ + { + arg: 'amount', + type: 'Number', + required: true, + }, { + arg: 'companyId', + type: 'Number', + required: false, + }, { + arg: 'urlOk', + type: 'String', + required: false, + }, { + arg: 'urlKo', + type: 'String', + required: false, + } + ], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/start`, + verb: 'POST' + } + }); + + Self.start = async(ctx, amount, companyId, urlOk, urlKo) => { + const userId = ctx.req.accessToken.userId; + const [[row]] = await Self.rawSql( + 'CALL hedera.tpvTransaction_start(?, ?, ?)', [ + amount, + companyId, + userId + ]); + + if (!row) + throw new UserError('Transaction error'); + + const orderId = row.transactionId.padStart(12, '0'); + const merchantUrl = row.merchantUrl ? row.merchantUrl : ''; + urlOk = urlOk ? urlOk.replace('_transactionId_', orderId) : ''; + urlKo = urlKo ? urlKo.replace('_transactionId_', orderId) : ''; + + const params = { + 'Ds_Merchant_Amount': amount, + 'Ds_Merchant_Order': orderId, + 'Ds_Merchant_MerchantCode': row.merchant, + 'Ds_Merchant_Currency': row.currency, + 'Ds_Merchant_TransactionType': row.transactionType, + 'Ds_Merchant_Terminal': row.terminal, + 'Ds_Merchant_MerchantURL': merchantUrl, + 'Ds_Merchant_UrlOK': urlOk, + 'Ds_Merchant_UrlKO': urlKo + }; + for (const param in params) + params[param] = encodeURIComponent(params[param]); + + const json = JSON.stringify(params); + const merchantParameters = Buffer.from(json).toString('base64'); + + const signature = Self.createSignature( + orderId, + row.secretKey, + merchantParameters + ); + + return { + url: row.url, + postValues: { + 'Ds_SignatureVersion': 'HMAC_SHA256_V1', + 'Ds_MerchantParameters': merchantParameters, + 'Ds_Signature': signature + } + }; + }; +}; diff --git a/modules/client/back/models/tpv-transaction.js b/modules/client/back/models/tpv-transaction.js index b511ae52e..eb9dd7d6c 100644 --- a/modules/client/back/models/tpv-transaction.js +++ b/modules/client/back/models/tpv-transaction.js @@ -1,3 +1,29 @@ +const crypto = require('crypto'); + module.exports = Self => { require('../methods/tpv-transaction/confirm')(Self); + require('../methods/tpv-transaction/start')(Self); + require('../methods/tpv-transaction/end')(Self); + + Self.createSignature = function(orderId, secretKey, merchantParameters) { + secretKey = Buffer.from(secretKey, 'base64'); + const iv = Buffer.alloc(8, 0); + + const cipher = crypto.createCipheriv('des-ede3-cbc', secretKey, iv); + cipher.setAutoPadding(false); + const orderKey = Buffer.concat([ + cipher.update(zeroPad(orderId, 8)), + cipher.final() + ]); + + return crypto.createHmac('sha256', orderKey) + .update(merchantParameters) + .digest('base64'); + }; + + function zeroPad(buf, blocksize) { + const buffer = typeof buf === 'string' ? Buffer.from(buf, 'utf8') : buf; + const pad = Buffer.alloc((blocksize - (buffer.length % blocksize)) % blocksize, 0); + return Buffer.concat([buffer, pad]); + } };