master #1293
|
@ -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]);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
};
|
|
@ -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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -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]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue