const crypto = require('crypto'); const UserError = require('vn-loopback/util/user-error'); const base64url = require('base64url'); module.exports = Self => { Self.remoteMethod('confirm', { description: 'Confirms electronic payment transaction', accessType: 'WRITE', accepts: [ { arg: 'Ds_SignatureVersion', type: 'string', required: false, }, { arg: 'Ds_MerchantParameters', type: 'string', required: true, }, { arg: 'Ds_Signature', type: 'string', 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; const decodedParams = JSON.parse( base64url.decode(merchantParameters, 'utf8')); const params = {}; for (const param in decodedParams) params[param] = decodeURIComponent(decodedParams[param]); console.debug('Payment confirmation received:', { signatureVersion, merchantParameters, signature, params }); const orderId = params['Ds_Order']; const merchantId = parseInt(params['Ds_MerchantCode']); if (!orderId) throw new UserError('Order id not found'); if (!merchantId) throw new UserError('Mechant id not found'); const merchant = await $.TpvMerchant.findById(merchantId, { 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 = cipher.update(zeroPad(orderId, 8), 'utf8', 'base64') + cipher.final(); const res = crypto.createHmac('sha256', Buffer.from(orderKey, 'base64')) .update(merchantParameters) .digest('base64'); const base64Res = base64url.encode(res, 'base64'); // if (res !== signature) // throw new UserError('Invalid signature'); console.debug('Payment signature:', { res, base64Res }); await Self.rawSql( 'CALL hedera.tpvTransaction_confirm(?, ?, ?, ?, ?, ?)', [ params['Ds_Amount'], orderId, merchantId, 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]); } };