Merge pull request 'master' (!1293) from master into test
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #1293
This commit is contained in:
Juan Ferrer 2023-01-31 12:46:42 +00:00
commit 63ff651448
4 changed files with 156 additions and 30 deletions

View File

@ -1,4 +1,3 @@
const crypto = require('crypto');
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
const base64url = require('base64url'); const base64url = require('base64url');
@ -21,19 +20,12 @@ module.exports = Self => {
required: true, required: true,
} }
], ],
returns: {
type: 'Boolean',
root: true
},
http: { http: {
path: `/confirm`, path: `/confirm`,
verb: 'POST' verb: 'POST'
} }
}); });
/*
* Source: https://github.com/santiperez/node-redsys-api
*/
Self.confirm = async(signatureVersion, merchantParameters, signature) => { Self.confirm = async(signatureVersion, merchantParameters, signature) => {
const $ = Self.app.models; const $ = Self.app.models;
@ -56,19 +48,11 @@ module.exports = Self => {
fields: ['id', 'secretKey'] fields: ['id', 'secretKey']
}); });
const secretKey = Buffer.from(merchant.secretKey, 'base64'); const base64hmac = Self.createSignature(
const iv = Buffer.alloc(8, 0); orderId,
merchant.secretKey,
const cipher = crypto.createCipheriv('des-ede3-cbc', secretKey, iv); merchantParameters
cipher.setAutoPadding(false); );
const orderKey = Buffer.concat([
cipher.update(zeroPad(orderId, 8)),
cipher.final()
]);
const base64hmac = crypto.createHmac('sha256', orderKey)
.update(merchantParameters)
.digest('base64');
if (base64hmac !== base64url.toBase64(signature)) if (base64hmac !== base64url.toBase64(signature))
throw new UserError('Invalid signature'); throw new UserError('Invalid signature');
@ -81,14 +65,6 @@ module.exports = Self => {
params['Ds_Currency'], params['Ds_Currency'],
params['Ds_Response'], params['Ds_Response'],
params['Ds_ErrorCode'] 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]);
}
}; };

View File

@ -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
]);
};
};

View File

@ -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
}
};
};
};

View File

@ -1,3 +1,29 @@
const crypto = require('crypto');
module.exports = Self => { module.exports = Self => {
require('../methods/tpv-transaction/confirm')(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]);
}
}; };