first commit
This commit is contained in:
34
src/utils/mailer.js
Normal file
34
src/utils/mailer.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const nodemailer = require('nodemailer');
|
||||
const ejs = require('ejs');
|
||||
const path = require('path');
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: process.env.EMAIL_USER,
|
||||
pass: process.env.EMAIL_PASS,
|
||||
},
|
||||
});
|
||||
|
||||
const sendEmail = async (to, subject, templateName, templateData) => {
|
||||
// Render EJS template
|
||||
const templatePath = path.join(__dirname, '../views/emails', `${templateName}.ejs`);
|
||||
const html = await ejs.renderFile(templatePath, templateData);
|
||||
|
||||
const mailOptions = {
|
||||
from: `"VC E-Commerce" <${process.env.EMAIL_USER}>`,
|
||||
to,
|
||||
subject,
|
||||
html,
|
||||
};
|
||||
|
||||
try {
|
||||
await transporter.sendMail(mailOptions);
|
||||
console.log('Email sent to', to);
|
||||
} catch (err) {
|
||||
console.error('Error sending email', err);
|
||||
throw new Error('Failed to send email');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = sendEmail;
|
||||
315
src/utils/paytm.js
Normal file
315
src/utils/paytm.js
Normal file
@@ -0,0 +1,315 @@
|
||||
// utils/paytm.js
|
||||
const https = require('https');
|
||||
const crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Paytm Configuration
|
||||
* Add these to your .env file:
|
||||
* PAYTM_MERCHANT_ID=your_merchant_id
|
||||
* PAYTM_MERCHANT_KEY=your_merchant_key
|
||||
* PAYTM_WEBSITE=WEBSTAGING (for staging) or your website name
|
||||
* PAYTM_CHANNEL_ID=WEB
|
||||
* PAYTM_INDUSTRY_TYPE=Retail
|
||||
* PAYTM_HOST=securegw-stage.paytm.in (for staging) or securegw.paytm.in (for production)
|
||||
*/
|
||||
|
||||
const PaytmConfig = {
|
||||
mid: process.env.PAYTM_MERCHANT_ID,
|
||||
key: process.env.PAYTM_MERCHANT_KEY,
|
||||
website: process.env.PAYTM_WEBSITE || 'WEBSTAGING',
|
||||
channelId: process.env.PAYTM_CHANNEL_ID || 'WEB',
|
||||
industryType: process.env.PAYTM_INDUSTRY_TYPE || 'Retail',
|
||||
host: process.env.PAYTM_HOST || 'securegw-stage.paytm.in',
|
||||
callbackUrl: process.env.PAYTM_CALLBACK_URL || 'http://localhost:3000/api/payments/paytm/callback',
|
||||
};
|
||||
console.log(
|
||||
'Merchant Key Length:',
|
||||
process.env.PAYTM_MERCHANT_KEY.length
|
||||
);
|
||||
|
||||
/**
|
||||
* Generate Paytm Checksum
|
||||
*/
|
||||
const generateChecksum = (params, merchantKey) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const data = JSON.stringify(params);
|
||||
|
||||
const salt = crypto.randomBytes(4).toString('hex');
|
||||
const hash = crypto
|
||||
.createHash('sha256')
|
||||
.update(data + salt)
|
||||
.digest('hex');
|
||||
|
||||
const checksum = hash + salt;
|
||||
const encryptedChecksum = encrypt(checksum, merchantKey);
|
||||
|
||||
resolve(encryptedChecksum);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Verify Paytm Checksum
|
||||
*/
|
||||
const verifyChecksum = (params, merchantKey, checksumHash) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const decrypted = decrypt(checksumHash, merchantKey);
|
||||
const salt = decrypted.slice(-8);
|
||||
|
||||
const hash = crypto
|
||||
.createHash('sha256')
|
||||
.update(JSON.stringify(params) + salt)
|
||||
.digest('hex');
|
||||
|
||||
resolve(hash + salt === decrypted);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt data using AES-128-CBC (Paytm standard)
|
||||
*/
|
||||
const encrypt = (data, key) => {
|
||||
if (key.length !== 16) {
|
||||
throw new Error('Paytm Merchant Key must be exactly 16 characters');
|
||||
}
|
||||
|
||||
const iv = Buffer.from('@@@@&&&&####$$$$'); // Paytm fixed IV
|
||||
const cipher = crypto.createCipheriv(
|
||||
'aes-128-cbc',
|
||||
Buffer.from(key, 'utf8'),
|
||||
iv
|
||||
);
|
||||
|
||||
let encrypted = cipher.update(data, 'utf8', 'base64');
|
||||
encrypted += cipher.final('base64');
|
||||
return encrypted;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypt data using AES-128-CBC
|
||||
*/
|
||||
const decrypt = (encryptedData, key) => {
|
||||
const iv = Buffer.from('@@@@&&&&####$$$$');
|
||||
const decipher = crypto.createDecipheriv(
|
||||
'aes-128-cbc',
|
||||
Buffer.from(key, 'utf8'),
|
||||
iv
|
||||
);
|
||||
|
||||
let decrypted = decipher.update(encryptedData, 'base64', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initiate Paytm Transaction
|
||||
*/
|
||||
const initiateTransaction = async (orderId, amount, customerId, email, mobile) => {
|
||||
const paytmParams = {
|
||||
body: {
|
||||
requestType: 'Payment',
|
||||
mid: PaytmConfig.mid,
|
||||
websiteName: PaytmConfig.website,
|
||||
orderId: orderId,
|
||||
callbackUrl: PaytmConfig.callbackUrl,
|
||||
txnAmount: {
|
||||
value: amount.toString(),
|
||||
currency: 'INR',
|
||||
},
|
||||
userInfo: {
|
||||
custId: customerId,
|
||||
email: email,
|
||||
mobile: mobile,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const checksum = await generateChecksum(
|
||||
JSON.stringify(paytmParams.body),
|
||||
PaytmConfig.key
|
||||
);
|
||||
|
||||
paytmParams.head = {
|
||||
signature: checksum,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: PaytmConfig.host,
|
||||
port: 443,
|
||||
path: `/theia/api/v1/initiateTransaction?mid=${PaytmConfig.mid}&orderId=${orderId}`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const response = JSON.parse(data);
|
||||
resolve({
|
||||
success: true,
|
||||
txnToken: response.body.txnToken,
|
||||
orderId: orderId,
|
||||
amount: amount,
|
||||
...response,
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.write(JSON.stringify(paytmParams));
|
||||
req.end();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check Transaction Status
|
||||
*/
|
||||
const checkTransactionStatus = async (orderId) => {
|
||||
const paytmParams = {
|
||||
body: {
|
||||
mid: PaytmConfig.mid,
|
||||
orderId: orderId,
|
||||
},
|
||||
};
|
||||
|
||||
const checksum = await generateChecksum(
|
||||
JSON.stringify(paytmParams.body),
|
||||
PaytmConfig.key
|
||||
);
|
||||
|
||||
paytmParams.head = {
|
||||
signature: checksum,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: PaytmConfig.host,
|
||||
port: 443,
|
||||
path: `/v3/order/status`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const response = JSON.parse(data);
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.write(JSON.stringify(paytmParams));
|
||||
req.end();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Process Refund
|
||||
*/
|
||||
const processRefund = async (orderId, refId, txnId, amount) => {
|
||||
const paytmParams = {
|
||||
body: {
|
||||
mid: PaytmConfig.mid,
|
||||
orderId: orderId,
|
||||
refId: refId,
|
||||
txnId: txnId,
|
||||
txnType: 'REFUND',
|
||||
refundAmount: amount.toString(),
|
||||
},
|
||||
};
|
||||
|
||||
const checksum = await generateChecksum(
|
||||
JSON.stringify(paytmParams.body),
|
||||
PaytmConfig.key
|
||||
);
|
||||
|
||||
paytmParams.head = {
|
||||
signature: checksum,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: PaytmConfig.host,
|
||||
port: 443,
|
||||
path: `/refund/apply`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const response = JSON.parse(data);
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.write(JSON.stringify(paytmParams));
|
||||
req.end();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
PaytmConfig,
|
||||
generateChecksum,
|
||||
verifyChecksum,
|
||||
initiateTransaction,
|
||||
checkTransactionStatus,
|
||||
processRefund,
|
||||
};
|
||||
23
src/utils/uploadToS3.js
Normal file
23
src/utils/uploadToS3.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const { PutObjectCommand } = require("@aws-sdk/client-s3");
|
||||
const s3 = require("../config/s3");
|
||||
const { v4: uuidv4 } = require("uuid");
|
||||
|
||||
const uploadToS3 = async (file, folder = "products") => {
|
||||
const ext = file.originalname.split(".").pop();
|
||||
const key = `${folder}/${uuidv4()}.${ext}`;
|
||||
|
||||
await s3.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: process.env.AWS_S3_BUCKET,
|
||||
Key: key,
|
||||
Body: file.buffer,
|
||||
ContentType: file.mimetype,
|
||||
})
|
||||
);
|
||||
|
||||
// return `${process.env.AWS_ENDPOINT}/${process.env.AWS_S3_BUCKET}/${key}`;
|
||||
return `https://${process.env.AWS_ENDPOINT}/${process.env.AWS_S3_BUCKET}/${key}`;
|
||||
|
||||
};
|
||||
|
||||
module.exports = uploadToS3;
|
||||
Reference in New Issue
Block a user