init commit
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2021*/
|
||||
/**
|
||||
* Auth Service
|
||||
* @copyright 2021 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
|
||||
const passwordService = require("./PasswordService");
|
||||
const mailService = require("./MailService");
|
||||
const generateCode = require("../utils/generateCode");
|
||||
const db = require("../models");
|
||||
const errors = require("../core/errors");
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Register new user with email and password
|
||||
* @name authService.register
|
||||
* @param {String} email user new email address
|
||||
* @param {String} password user new password
|
||||
* @returns {Promise.<{credential:String, user:String}>} payload to generate jwt access and refresh token
|
||||
* @example
|
||||
* const payload = await authService.register(req.body.email, req.body.password)
|
||||
*/
|
||||
register: async function (email, password, roleId, userDetails = {}) {
|
||||
try {
|
||||
const isEmailAddressExist = await db.credential.getByFields({
|
||||
email,
|
||||
});
|
||||
|
||||
if (isEmailAddressExist) throw new Error(errors.EMAIL_ADDRESS_ALREADY_EXIST);
|
||||
|
||||
const hashedPassword = await passwordService.hash(password);
|
||||
|
||||
var user = await db.user.insert({ ...userDetails, role_id: roleId }, { returnAllFields: true });
|
||||
var credential = await db.credential.insert(
|
||||
{
|
||||
email: email,
|
||||
password: hashedPassword,
|
||||
user_id: user.id,
|
||||
type: 0,
|
||||
verify: 0,
|
||||
status: 1,
|
||||
},
|
||||
{ returnAllFields: true }
|
||||
);
|
||||
|
||||
return { credential: credential.id, user: user.id };
|
||||
} catch (error) {
|
||||
if (credential) {
|
||||
await db.credential.realDelete(credential.id);
|
||||
}
|
||||
if (user) {
|
||||
await db.user.realDelete(user.id);
|
||||
}
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Login user with email and password
|
||||
* @name authService.login
|
||||
* @param {String} email user email address
|
||||
* @param {String} password user password
|
||||
* @returns {Promise.<{credential:String, user:String}>} payload to generate jwt access and refresh token
|
||||
* @example
|
||||
* const payload = await authService.login(req.body.email, req.body.password)
|
||||
*/
|
||||
login: async function (email, password, roldId) {
|
||||
try {
|
||||
const isEmailAddressExist = await db.credential.findOne({
|
||||
where: { email, status: 1, type: 0 },
|
||||
include: [{ model: db.user, required: true, as: "user", where: { role_id: roldId } }],
|
||||
});
|
||||
|
||||
if (!isEmailAddressExist) throw new Error(errors.EMAIL_ADDRESS_NOT_FOUND);
|
||||
|
||||
const { password: hashedPassword, id, user_id } = isEmailAddressExist;
|
||||
|
||||
const user = await db.user.getByPK(user_id);
|
||||
|
||||
if (!user) {
|
||||
throw new Error(errors.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const isPasswordMatch = await passwordService.compareHash(password, hashedPassword);
|
||||
|
||||
if (!isPasswordMatch) throw new Error(errors.INVALID_EMAIL_OR_PASSWORD);
|
||||
|
||||
return { credential: id, user: user.id };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Send email and save to database
|
||||
* @name authService.forgotPassword
|
||||
* @param {String} email user email address
|
||||
* @return {Promise.<Void>}
|
||||
* @example
|
||||
* await authService.forgotPassword(req.body.email)
|
||||
*/
|
||||
forgotPassword: async function (email) {
|
||||
try {
|
||||
const isEmailAddressExist = await db.credential.getByFields({
|
||||
email: email,
|
||||
});
|
||||
|
||||
if (!isEmailAddressExist) throw new Error(errors.EMAIL_ADDRESS_NOT_FOUND);
|
||||
|
||||
const { user_id } = isEmailAddressExist;
|
||||
|
||||
const getUser = await db.user.getByPK(user_id);
|
||||
|
||||
const verificationCode = generateCode(6);
|
||||
|
||||
mailService.initialize({
|
||||
hostname: process.env.EMAIL_SMTP_SMTP_HOST,
|
||||
port: process.env.EMAIL_SMTP_SMTP_PORT,
|
||||
username: process.env.EMAIL_SMTP_SMTP_USER,
|
||||
password: EMAIL_SMTP_SMTP_PASS,
|
||||
from: process.env.MAIL_FROM,
|
||||
to: email,
|
||||
});
|
||||
|
||||
const mailTemplate = await mailService.template("reset-password");
|
||||
|
||||
const injectedMailTemplate = mailService.inject(
|
||||
{
|
||||
body: mailTemplate.body,
|
||||
subject: mailTemplate.subject,
|
||||
},
|
||||
{
|
||||
username: `${getUser.first_name} ${getUser.last_name}`,
|
||||
verification_code: verificationCode,
|
||||
}
|
||||
);
|
||||
|
||||
await mailService.send(injectedMailTemplate);
|
||||
|
||||
await db.token.insert({ token: verificationCode, user_id });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Verify forgot password confirmation code
|
||||
* @name authService.verifyForgotPassword
|
||||
* @param {code} code confirmation code
|
||||
* @returns {Promise.<{credential:String, user:String}>} payload to generate jwt access and refresh token
|
||||
* @example
|
||||
* const payload = await authService.verifyForgotPassword(req.body.code)
|
||||
*/
|
||||
verifyForgotPassword: async function (code) {
|
||||
try {
|
||||
const Token = await db.token.getByFields({
|
||||
token: code,
|
||||
});
|
||||
|
||||
const Credential = await db.credential.getByFields({
|
||||
user_id: Token.user_id,
|
||||
});
|
||||
|
||||
return { credential: Credential.id, user: Credential.user_id };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Reset password
|
||||
* @name authService.resetPassword
|
||||
* @param {String} password user new password
|
||||
* @param {String} credential_id user credential id
|
||||
* @example
|
||||
* await authService.resetPassword(req.body.password, credential_id)
|
||||
*/
|
||||
resetPassword: async function (password, credential_id) {
|
||||
try {
|
||||
const hashedPassword = await passwordService.hash(password);
|
||||
|
||||
await db.credential.edit(
|
||||
{
|
||||
password: hashedPassword,
|
||||
},
|
||||
credential_id
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Email confirmation
|
||||
* @name authService.emailConfirmation
|
||||
* @param {String} email user email address
|
||||
* @example
|
||||
* await authService.emailConfirmation(email)
|
||||
*/
|
||||
emailConfirmation: async function (email) {
|
||||
try {
|
||||
const isEmailAddressExist = await db.credential.getByFields({
|
||||
email: email,
|
||||
});
|
||||
|
||||
if (!isEmailAddressExist) throw new Error(errors.EMAIL_ADDRESS_NOT_FOUND);
|
||||
|
||||
const { user_id } = isEmailAddressExist;
|
||||
|
||||
const user = await db.user.getByPK(user_id);
|
||||
|
||||
const confirmationCode = generateCode(6);
|
||||
|
||||
mailService.initialize({
|
||||
hostname: process.env.EMAIL_SMTP_SMTP_HOST,
|
||||
port: process.env.EMAIL_SMTP_SMTP_PORT,
|
||||
username: process.env.EMAIL_SMTP_SMTP_USER,
|
||||
password: EMAIL_SMTP_SMTP_PASS,
|
||||
from: process.env.MAIL_FROM,
|
||||
to: email,
|
||||
});
|
||||
|
||||
const mailTemplate = await mailService.template("email-confirmation");
|
||||
|
||||
const injectedMailTemplate = mailService.inject(
|
||||
{
|
||||
body: mailTemplate.body,
|
||||
subject: mailTemplate.subject,
|
||||
},
|
||||
{
|
||||
username: `${user.first_name} ${user.last_name}`,
|
||||
confirmation_code: confirmationCode,
|
||||
}
|
||||
);
|
||||
|
||||
await mailService.send(injectedMailTemplate);
|
||||
|
||||
await db.token.insert({ token: confirmationCode, user_id, type: 6 });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Verify Email address
|
||||
* @name authService.emailVerify
|
||||
* @param {String} token email confirmation code
|
||||
* @param {string} user_id user id
|
||||
* @example
|
||||
* await authService.emailVerify(email, user_id)
|
||||
*/
|
||||
emailVerify: async function (token, user_id) {
|
||||
try {
|
||||
const isTokenExist = await db.token.getByFields({
|
||||
user_id,
|
||||
token,
|
||||
type: 6,
|
||||
});
|
||||
|
||||
if (!isTokenExist) throw new Error(errors.INVALID_EMAIL_CONFIRMATION_CODE);
|
||||
|
||||
await db.token.realDelete(isTokenExist.id);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* check if user need to change password before logging in
|
||||
* @name authService.forcePasswordChange
|
||||
* @param {string} user_id user id
|
||||
*/
|
||||
forcePasswordChange: async function (user_id) {
|
||||
try {
|
||||
const { profile_id } = await db.user.getByPK(user_id);
|
||||
const { force_password_change } = await db.profile.getByPK(profile_id);
|
||||
|
||||
if (force_password_change) return true;
|
||||
else return false;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,277 @@
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2021*/
|
||||
/**
|
||||
* Auth Service
|
||||
* @copyright 2021 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
|
||||
const passwordService = require('./PasswordService');
|
||||
const mailService = require('./MailService');
|
||||
const generateCode = require('../utils/generateCode');
|
||||
const db = require('../models');
|
||||
|
||||
const errors = {
|
||||
EMAIL_ADDRESS_NOT_FOUND: 'xyzEMAIL_ADDRESS_NOT_FOUND',
|
||||
EMAIL_ADDRESS_ALREADY_EXIST: 'xyzEMAIL_ADDRESS_ALREADY_EXIST',
|
||||
PASSWORD_NOT_MATCH: 'xyzPASSWORD_NOT_MATCH',
|
||||
INVALID_EMAIL_CONFIRMATION_CODE: 'xyzINVALID_EMAIL_CONFIRMATION_CODE',
|
||||
INVALID_EMAIL_OR_PASSWORD: 'xyzINVALID_EMAIL_OR_PASSWORD',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Register new user with email and password
|
||||
* @name authService.register
|
||||
* @param {String} email user new email address
|
||||
* @param {String} password user new password
|
||||
* @returns {Promise.<{credential:String, user:String}>} payload to generate jwt access and refresh token
|
||||
* @example
|
||||
* const payload = await authService.register(req.body.email, req.body.password)
|
||||
*/
|
||||
register: async function (email, password, role_id, user_details = {}) {
|
||||
try {
|
||||
const isEmailAddressExist = await db.user.getByFields({
|
||||
email,
|
||||
});
|
||||
|
||||
if (isEmailAddressExist) throw new Error(errors.EMAIL_ADDRESS_ALREADY_EXIST);
|
||||
|
||||
const hashedPassword = await passwordService.hash(password);
|
||||
|
||||
var user = await db.user.insert(
|
||||
{
|
||||
...user_details,
|
||||
email: email,
|
||||
password: hashedPassword,
|
||||
role_id,
|
||||
type: 0,
|
||||
verify: 1,
|
||||
status: 1,
|
||||
},
|
||||
{ returnAllFields: true },
|
||||
);
|
||||
|
||||
return { user };
|
||||
} catch (error) {
|
||||
if (user) {
|
||||
await db.user.realDelete(user.id);
|
||||
}
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Login user with email and password
|
||||
* @name authService.login
|
||||
* @param {String} email user email address
|
||||
* @param {String} password user password
|
||||
* @returns {Promise.<{credential:String, user:String}>} payload to generate jwt access and refresh token
|
||||
* @example
|
||||
* const payload = await authService.login(req.body.email, req.body.password)
|
||||
*/
|
||||
login: async function (email, password, role_id) {
|
||||
try {
|
||||
const user = await db.user.getByFields({
|
||||
email,
|
||||
status: 1,
|
||||
role_id,
|
||||
type: 0,
|
||||
});
|
||||
|
||||
if (!user) throw new Error(errors.EMAIL_ADDRESS_NOT_FOUND);
|
||||
|
||||
const { password: hashedPassword } = user;
|
||||
|
||||
const isPasswordMatch = await passwordService.compareHash(password, hashedPassword);
|
||||
|
||||
if (!isPasswordMatch) throw new Error(errors.INVALID_EMAIL_OR_PASSWORD);
|
||||
|
||||
return { user: user };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Send email and save to database
|
||||
* @name authService.forgotPassword
|
||||
* @param {String} email user email address
|
||||
* @return {Promise.<Void>}
|
||||
* @example
|
||||
* await authService.forgotPassword(req.body.email)
|
||||
*/
|
||||
forgotPassword: async function (email) {
|
||||
try {
|
||||
const user = await db.user.getByFields({
|
||||
email: email,
|
||||
});
|
||||
|
||||
if (!user) throw new Error(errors.EMAIL_ADDRESS_NOT_FOUND);
|
||||
|
||||
const verificationCode = generateCode(6);
|
||||
|
||||
mailService.initialize({
|
||||
hostname: process.env.EMAIL_SMTP_SMTP_HOST,
|
||||
port: process.env.EMAIL_SMTP_SMTP_PORT,
|
||||
username: process.env.EMAIL_SMTP_SMTP_USER,
|
||||
password: EMAIL_SMTP_SMTP_PASS,
|
||||
from: process.env.MAIL_FROM,
|
||||
to: email,
|
||||
});
|
||||
|
||||
const mailTemplate = await mailService.template('reset-password');
|
||||
|
||||
const injectedMailTemplate = mailService.inject(
|
||||
{
|
||||
body: mailTemplate.body,
|
||||
subject: mailTemplate.subject,
|
||||
},
|
||||
{
|
||||
username: `${user.first_name} ${user.last_name}`,
|
||||
verification_code: verificationCode,
|
||||
},
|
||||
);
|
||||
|
||||
await mailService.send(injectedMailTemplate);
|
||||
|
||||
await db.token.insert({ token: verificationCode, user_id: user.id });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Verify forgot password confirmation code
|
||||
* @name authService.verifyForgotPassword
|
||||
* @param {code} code confirmation code
|
||||
* @returns {Promise.<{credential:String, user:String}>} payload to generate jwt access and refresh token
|
||||
* @example
|
||||
* const payload = await authService.verifyForgotPassword(req.body.code)
|
||||
*/
|
||||
verifyForgotPassword: async function (code) {
|
||||
try {
|
||||
const Token = await db.token.findByFields({
|
||||
token: code,
|
||||
});
|
||||
const user = await db.user.getLast({ user_id: Token.user_id });
|
||||
return { user };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Reset password
|
||||
* @name authService.resetPassword
|
||||
* @param {String} password user new password
|
||||
* @param {String} credential_id user credential id
|
||||
* @example
|
||||
* await authService.resetPassword(req.body.password, credential_id)
|
||||
*/
|
||||
resetPassword: async function (password, userId) {
|
||||
try {
|
||||
const hashedPassword = await passwordService.hash(password);
|
||||
|
||||
await db.user.edit(
|
||||
{
|
||||
password: hashedPassword,
|
||||
},
|
||||
userId,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Email confirmation
|
||||
* @name authService.emailConfirmation
|
||||
* @param {String} email user email address
|
||||
* @example
|
||||
* await authService.emailConfirmation(email)
|
||||
*/
|
||||
emailConfirmation: async function (email) {
|
||||
try {
|
||||
const user = await db.user.getByFields({
|
||||
email: email,
|
||||
});
|
||||
|
||||
if (!user) throw new Error(errors.EMAIL_ADDRESS_NOT_FOUND);
|
||||
|
||||
const confirmationCode = generateCode(6);
|
||||
|
||||
mailService.initialize({
|
||||
hostname: process.env.EMAIL_SMTP_SMTP_HOST,
|
||||
port: process.env.EMAIL_SMTP_SMTP_PORT,
|
||||
username: process.env.EMAIL_SMTP_SMTP_USER,
|
||||
password: EMAIL_SMTP_SMTP_PASS,
|
||||
from: process.env.MAIL_FROM,
|
||||
to: email,
|
||||
});
|
||||
|
||||
const mailTemplate = await mailService.template('email-confirmation');
|
||||
|
||||
const injectedMailTemplate = mailService.inject(
|
||||
{
|
||||
body: mailTemplate.body,
|
||||
subject: mailTemplate.subject,
|
||||
},
|
||||
{
|
||||
username: `${user.first_name} ${user.last_name}`,
|
||||
confirmation_code: confirmationCode,
|
||||
},
|
||||
);
|
||||
|
||||
await mailService.send(injectedMailTemplate);
|
||||
|
||||
await db.token.insert({ token: confirmationCode, user_id: user.id, type: 6 });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Verify Email address
|
||||
* @name authService.emailVerify
|
||||
* @param {String} token email confirmation code
|
||||
* @param {string} user_id user id
|
||||
* @example
|
||||
* await authService.emailVerify(email, user_id)
|
||||
*/
|
||||
emailVerify: async function (token, user_id) {
|
||||
try {
|
||||
const isTokenExist = await db.token.getByFields({
|
||||
user_id,
|
||||
token,
|
||||
type: 6,
|
||||
});
|
||||
|
||||
if (!isTokenExist) throw new Error(errors.INVALID_EMAIL_CONFIRMATION_CODE);
|
||||
|
||||
await db.token.realDelete(isTokenExist.id);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* check if user need to change password before logging in
|
||||
* @name authService.forcePasswordChange
|
||||
* @param {string} user_id user id
|
||||
*/
|
||||
forcePasswordChange: async function (user_id) {
|
||||
try {
|
||||
const { profile_id } = await db.user.getByPk(user_id);
|
||||
const { force_password_change } = await db.profile.getByPk(profile_id);
|
||||
|
||||
if (force_password_change) return true;
|
||||
else return false;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
const barcode = require('barcode');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Generate barcode
|
||||
* @param {string} string barcode text
|
||||
* @param {{width?: number, height?: number}} param1 width and height for barcode image
|
||||
* @returns {Promise.<string>}
|
||||
*/
|
||||
generateBarcode: async function (string, { width = 400, height = 100 }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const code128 = barcode('Code128', {
|
||||
data: string,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
|
||||
code128.getBase64(function (error, base64String) {
|
||||
if (error) reject(error);
|
||||
else resolve(base64String);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
const multer = require('multer');
|
||||
const converter = require('json-2-csv');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const csv = require('fast-csv');
|
||||
const db = require('./../models');
|
||||
const upload_folder = path.resolve(__dirname, '../uploads');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Import CSV file
|
||||
* @param {request} req
|
||||
* @param {response} res
|
||||
*/
|
||||
csv_preview: function (req, res) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const upload = multer({ dest: path.resolve(upload_folder) }).single('file');
|
||||
upload(req, res, async function (error) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
let data = [];
|
||||
fs.createReadStream(path.resolve(upload_folder, req.file.path))
|
||||
.pipe(csv.parse())
|
||||
.on('error', (error) => reject(error))
|
||||
.on('data', (row) => {
|
||||
data.push(row);
|
||||
})
|
||||
.on('end', (rowCount) => {
|
||||
fs.unlinkSync(path.resolve(upload_folder, req.file.path));
|
||||
console.log(`Parsed ${rowCount} rows`);
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
csv_import: async function (req, res) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const table = req.params.model;
|
||||
const upload = multer({ dest: path.resolve(upload_folder) }).single('file');
|
||||
let data = [];
|
||||
upload(req, res, async function (error) {
|
||||
if (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
fs.createReadStream(path.resolve(upload_folder, req.file.path))
|
||||
.pipe(csv.parse({ headers: true }))
|
||||
.on('error', (error) => reject(error))
|
||||
.on('data', (row) => {
|
||||
data.push(row);
|
||||
})
|
||||
.on('end', async (rowCount) => {
|
||||
fs.unlinkSync(path.resolve(upload_folder, req.file.path));
|
||||
console.log(`Parsed ${rowCount} rows`);
|
||||
await db[table].batchInsert(data).catch((error) => {
|
||||
console.log(error);
|
||||
reject(error);
|
||||
});
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Export CSV file
|
||||
* @param {request} req
|
||||
* @param {response} res
|
||||
*/
|
||||
csv_export: async function (req, res) {
|
||||
try {
|
||||
let fields = await db[req.table].getAll(req.where);
|
||||
fields = JSON.stringify(fields); // do this needed?
|
||||
|
||||
const csv = await converter.json2csvAsync(fields);
|
||||
|
||||
res.header('Content-Type', 'text/csv');
|
||||
res.attachment(req.table + '.csv');
|
||||
|
||||
return res.send(csv);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
module.exports = {
|
||||
iv: crypto.randomBytes(16).toString('hex').slice(0, 16),
|
||||
encrypt: function (message, secret) {
|
||||
const encrypter = crypto.createCipheriv('aes-256-cbc', secret, this.iv);
|
||||
let encryptMessage = encrypter.update(message, 'utf-8', 'hex');
|
||||
return (encryptMessage += encrypter.final('hex'));
|
||||
},
|
||||
decrypt: function (encryptMessage, secret) {
|
||||
const decrypter = crypto.createDecipheriv('aes-256-cbc', secret, this.iv);
|
||||
let decryptedMessage = decrypter.update(encryptMessage, 'hex', 'utf8');
|
||||
return (decryptedMessage += decrypter.final('utf8'));
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
var firebase = require('firebase-admin');
|
||||
var serviceAccount = require('./serviceAccountKey.json');
|
||||
|
||||
/*
|
||||
Examples:
|
||||
Write:
|
||||
firebaseService.write('notification', 1, {
|
||||
a: 1,
|
||||
b: 'd',
|
||||
c:['a','w'],
|
||||
d: [{a:1},{b:2}]
|
||||
})
|
||||
Read Once:
|
||||
firebaseService.read('notification', 1).then(function(notification){
|
||||
console.log(notification);
|
||||
});
|
||||
Update:
|
||||
firebaseService.update('notification', 1, {a:2});
|
||||
usersRef.update({
|
||||
"alanisawesome/nickname": "Alan The Machine",
|
||||
"gracehop/nickname": "Amazing Grace"
|
||||
});
|
||||
Read On Listeners:
|
||||
ref.on("value", function(snapshot) {
|
||||
console.log(snapshot.val());
|
||||
}, function (errorObject) {
|
||||
console.log("The read failed: " + errorObject.code);
|
||||
});
|
||||
Read Previous Listener:
|
||||
ref.on("child_added", function(snapshot, prevChildKey) {
|
||||
var newPost = snapshot.val();
|
||||
console.log("Author: " + newPost.author);
|
||||
console.log("Title: " + newPost.title);
|
||||
console.log("Previous Post ID: " + prevChildKey);
|
||||
});
|
||||
|
||||
Child Changed Listener:
|
||||
// Get the data on a post that has changed
|
||||
ref.on("child_changed", function(snapshot) {
|
||||
var changedPost = snapshot.val();
|
||||
console.log("The updated post title is " + changedPost.title);
|
||||
});
|
||||
|
||||
Child Removed Listener:
|
||||
// Get the data on a post that has been removed
|
||||
ref.on("child_removed", function(snapshot) {
|
||||
var deletedPost = snapshot.val();
|
||||
console.log("The blog post titled '" + deletedPost.title + "' has been deleted");
|
||||
});
|
||||
Order By Listener:
|
||||
ref.orderByChild("height").on("child_added", function(snapshot) {
|
||||
console.log(snapshot.key + " was " + snapshot.val().height + " meters tall");
|
||||
});
|
||||
|
||||
Order By key:
|
||||
var ref = db.ref("dinosaurs");
|
||||
ref.orderByKey().on("child_added", function(snapshot) {
|
||||
console.log(snapshot.key);
|
||||
});
|
||||
|
||||
Limited:
|
||||
var ref = db.ref("dinosaurs");
|
||||
ref.orderByChild("weight").limitToLast(2).on("child_added", function(snapshot) {
|
||||
console.log(snapshot.key);
|
||||
});
|
||||
|
||||
var ref = db.ref("dinosaurs");
|
||||
ref.orderByChild("height").startAt(3).on("child_added", function(snapshot) {
|
||||
console.log(snapshot.key);
|
||||
});
|
||||
|
||||
var ref = db.ref("dinosaurs");
|
||||
ref.orderByKey().endAt("pterodactyl").on("child_added", function(snapshot) {
|
||||
console.log(snapshot.key);
|
||||
});
|
||||
|
||||
var ref = db.ref("dinosaurs");
|
||||
ref.orderByKey().startAt("b").endAt("b\uf8ff").on("child_added", function(snapshot) {
|
||||
console.log(snapshot.key);
|
||||
});
|
||||
Equal To:
|
||||
var ref = db.ref("dinosaurs");
|
||||
ref.orderByChild("height").equalTo(25).on("child_added", function(snapshot) {
|
||||
console.log(snapshot.key);
|
||||
});
|
||||
*/
|
||||
function FirebaseService() {
|
||||
this._transporter = firebase.initializeApp(
|
||||
{
|
||||
// credential: firebase.credential.cert('./serviceAccountKey.json'),
|
||||
credential: firebase.credential.cert(serviceAccount),
|
||||
databaseURL: 'https://konfer-243320.firebaseio.com',
|
||||
},
|
||||
// , 'flashbid-prod'
|
||||
);
|
||||
console.log('Prod Firebase');
|
||||
this._database = this._transporter.database();
|
||||
this._messaging = this._transporter.messaging();
|
||||
|
||||
this.write = function (table, id, payload) {
|
||||
return this._database.ref(table + '/' + id).set(payload);
|
||||
};
|
||||
|
||||
this.read = function (table, id) {
|
||||
return this._database
|
||||
.ref(table + '/' + id)
|
||||
.once('value')
|
||||
.then(function (snapshot) {
|
||||
return snapshot.val();
|
||||
});
|
||||
};
|
||||
|
||||
this.update = function (table, id, payload) {
|
||||
const ref = this._database.ref(table).child(id);
|
||||
ref.update(payload);
|
||||
};
|
||||
|
||||
this.updateSpecific = function (table, id, payload) {
|
||||
return this._database.ref(table + '/' + id).update(payload);
|
||||
};
|
||||
|
||||
this.pushToList = function (table, id, listField, payload) {
|
||||
const ref = this._database.ref(table).child(id).child(listField).push();
|
||||
ref.set(payload);
|
||||
};
|
||||
|
||||
this.sendPushNotification = function (
|
||||
id,
|
||||
title,
|
||||
subs,
|
||||
description,
|
||||
deviceId,
|
||||
) {
|
||||
// This registration token comes from the client FCM SDKs.
|
||||
|
||||
// See the "Defining the message payload" section above for details
|
||||
// on how to define a message payload.
|
||||
var payload = {
|
||||
notification: {
|
||||
title: title,
|
||||
body: description,
|
||||
},
|
||||
data: {
|
||||
title: subs.text,
|
||||
body: subs.desc,
|
||||
action: subs.action,
|
||||
id: id,
|
||||
},
|
||||
};
|
||||
|
||||
// Set the message as high priority and have it expire after 24 hours.
|
||||
var options = {
|
||||
priority: 'high',
|
||||
timeToLive: 60 * 60 * 24,
|
||||
};
|
||||
|
||||
console.log('payload', payload);
|
||||
console.log('device id', deviceId);
|
||||
|
||||
// Send a message to the device corresponding to the provided
|
||||
// registration token with the provided options.
|
||||
return this._messaging.sendToDevice(deviceId, payload, options);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = FirebaseService;
|
||||
@@ -0,0 +1,30 @@
|
||||
const html_to_pdf = require('html-pdf-node');
|
||||
|
||||
module.exports = {
|
||||
html_to_pdf_with_content: async function (content, options) {
|
||||
const file = { content };
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
html_to_pdf
|
||||
.generatePdf(file, options)
|
||||
.then((pdfBuffer) => {
|
||||
resolve(pdfBuffer);
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
},
|
||||
html_to_pdf_with_url: async function (url, options) {
|
||||
const file = { url };
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
html_to_pdf
|
||||
.generatePdf(file, options)
|
||||
.then((pdfBuffer) => {
|
||||
resolve(pdfBuffer);
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// pdf to html https://www.npmjs.com/package/pdf2html
|
||||
@@ -0,0 +1,108 @@
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
|
||||
/**
|
||||
* JWT Service
|
||||
* @copyright 2020 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
var jwt = require('jsonwebtoken');
|
||||
var dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
dotenv.config({ path: './src/.env' });
|
||||
|
||||
module.exports = {
|
||||
createAccessToken: function (
|
||||
payload,
|
||||
expireIn = process.env.DYNAMIC_CONFIG_JWT_EXPIRE_AT,
|
||||
) {
|
||||
const secret = process.env.DYNAMIC_CONFIG_JWT_KEY;
|
||||
|
||||
return jwt.sign(payload, secret, {
|
||||
expiresIn: Number(expireIn),
|
||||
algorithm: 'HS256',
|
||||
});
|
||||
},
|
||||
createRefreshToken: function (payload) {
|
||||
return jwt.sign(payload, process.env.DYNAMIC_CONFIG_JWT_KEY, {
|
||||
expiresIn: Number(process.env.DYNAMIC_CONFIG_JWT_REFRESH_EXPIRE_AT),
|
||||
algorithm: 'HS256',
|
||||
});
|
||||
},
|
||||
|
||||
verifyAccessToken: function (token) {
|
||||
try {
|
||||
return jwt.verify(token, process.env.DYNAMIC_CONFIG_JWT_KEY);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
verifyRefreshToken: function (token) {
|
||||
try {
|
||||
return jwt.verify(token, process.env.DYNAMIC_CONFIG_JWT_KEY);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
generateString: function (length) {
|
||||
let d = new Date().getTime();
|
||||
const time = new Date().getTime();
|
||||
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx-xxxx'.replace(
|
||||
/[xy]/g,
|
||||
function (c) {
|
||||
let r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c == 'x' ? r : (r & 0x7) | 0x8).toString(16);
|
||||
},
|
||||
);
|
||||
|
||||
return (uuid.toUpperCase() + '-' + time.toString()).substring(0, length);
|
||||
},
|
||||
generateUUID: function () {
|
||||
let d = new Date().getTime();
|
||||
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
|
||||
/[xy]/g,
|
||||
function (c) {
|
||||
let r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c == 'x' ? r : (r & 0x7) | 0x8).toString(16);
|
||||
},
|
||||
);
|
||||
return uuid.toUpperCase();
|
||||
},
|
||||
getToken: function (req) {
|
||||
if (
|
||||
req.headers.authorization &&
|
||||
req.headers.authorization.split(' ')[0] === 'Bearer'
|
||||
) {
|
||||
return req.headers.authorization.split(' ')[1];
|
||||
} else if (req.query && req.query.token) {
|
||||
return req.query.token;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
verifyTokenMiddleware: function (role) {
|
||||
const self = this;
|
||||
return function (req, res, next) {
|
||||
const token = self.getToken(req);
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
code: 'UNAUTHORIZED',
|
||||
});
|
||||
} else {
|
||||
const result = self.verifyAccessToken(token);
|
||||
if (!result) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
code: 'TOKEN_EXPIRED',
|
||||
});
|
||||
}
|
||||
req.user_id = result;
|
||||
next();
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
const winston = require("winston");
|
||||
winston.level = process.env.LOG_LEVEL;
|
||||
const tsFormat = () => new Date().toLocaleString();
|
||||
const logger = winston.createLogger({
|
||||
level: "info",
|
||||
format: winston.format.json(),
|
||||
transports: [
|
||||
//
|
||||
// - Write to all logs with level `info` and below to `combined.log`
|
||||
// - Write all logs error (and below) to `error.log`.
|
||||
//
|
||||
new winston.transports.File({
|
||||
filename: "error.log",
|
||||
level: "error",
|
||||
timestamp: tsFormat,
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: "combined.log",
|
||||
timestamp: tsFormat,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
//
|
||||
// If we're not in production then log to the `console` with the format:
|
||||
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
|
||||
if (process.env.NODE_ENV != "production") {
|
||||
logger.add(
|
||||
new winston.transports.Console({
|
||||
format: winston.format.simple(),
|
||||
timestamp: tsFormat,
|
||||
colorize: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = logger;
|
||||
@@ -0,0 +1,99 @@
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
const db = require('../models');
|
||||
|
||||
module.exports = {
|
||||
/** @private */
|
||||
transport: null,
|
||||
/** @private */
|
||||
from: null,
|
||||
/** @private */
|
||||
to: null,
|
||||
/**
|
||||
* Nodemailer initializer
|
||||
* @name mailService.initialize
|
||||
* @param {{hostname: String, port: Number, username: String, password: String, from: String, to: String}} config Nodemailer configuration
|
||||
* @returns {Void}
|
||||
*/
|
||||
initialize: function (config) {
|
||||
this.transport = nodemailer.createTransport({
|
||||
host: config.hostname,
|
||||
port: config.port,
|
||||
auth: {
|
||||
user: config.username,
|
||||
pass: config.password,
|
||||
},
|
||||
});
|
||||
|
||||
this.from = config.from;
|
||||
this.to = config.to;
|
||||
},
|
||||
/**
|
||||
* Get email template from database
|
||||
* @name mailService.template
|
||||
* @param {String} slug email template slug
|
||||
* @reject {Error}
|
||||
* @returns {Promise.<{body: String, subject: String}>} email template
|
||||
*/
|
||||
template: function (slug) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
db.email
|
||||
.findOne({ where: { slug } })
|
||||
.then((response) => {
|
||||
if (!response) {
|
||||
return reject(`TEMPLATE_NOT_FOUND`);
|
||||
} else resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Inject values into email template
|
||||
* @name mailService.inject
|
||||
* @param {{body: String, subject: String}} template email template
|
||||
* @param {Object.<string, string>} payload template values
|
||||
* @returns {{from: String, to: String, subject: String, text: String}} Value injected email template
|
||||
*/
|
||||
inject: function (template, payload) {
|
||||
let mailBody = template.body;
|
||||
let mailSubject = template.subject;
|
||||
|
||||
for (const key in payload) {
|
||||
const value = payload[key];
|
||||
mailBody = mailBody.replace(new RegExp('{{{' + key + '}}}', 'g'), value);
|
||||
}
|
||||
|
||||
for (const key in payload) {
|
||||
const value = payload[key];
|
||||
mailSubject = mailSubject.replace(
|
||||
new RegExp('{{{' + key + '}}}', 'g'),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
from: this.from,
|
||||
to: this.to,
|
||||
subject: mailSubject,
|
||||
html: mailBody,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Send email
|
||||
* @name mailService.send
|
||||
* @param {nodemailer.SendMailOptions} template email template
|
||||
* @reject {Error} send mail error
|
||||
* @returns {Promise.<nodemailer.SentMessageInfo>} send mail info
|
||||
*/
|
||||
send: function (template) {
|
||||
let self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.transport
|
||||
.sendMail(template)
|
||||
.then((response) => resolve(response))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,200 @@
|
||||
const querystring = require('querystring');
|
||||
const axios = require('axios').default;
|
||||
|
||||
const db = require('../models');
|
||||
|
||||
module.exports = {
|
||||
google: {
|
||||
/**
|
||||
* Generates authentication URL
|
||||
* @name oAuth.google.generateAuthURL
|
||||
* @param {{redirect_uri: String, client_id: String}} config
|
||||
* @returns {String} authentication URL
|
||||
*/
|
||||
generateAuthURL(config) {
|
||||
const rootUrl = 'https://accounts.google.com/o/oauth2/v2/auth';
|
||||
|
||||
const options = {
|
||||
redirect_uri: config.redirect_uri,
|
||||
client_id: config.client_id,
|
||||
access_type: 'offline',
|
||||
response_type: 'code',
|
||||
prompt: 'consent',
|
||||
scope: ['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'].join(' '),
|
||||
};
|
||||
|
||||
return `${rootUrl}?${querystring.stringify(options)}`;
|
||||
},
|
||||
/**
|
||||
* Generates authentication Token
|
||||
* @name oAuth.google.generateAuthToken
|
||||
* @param {{auth_code: String, client_id: String, client_secret: String, redirect_uri: String}} config
|
||||
* @returns {Promise.<{{access_token: String, expires_in: String, refresh_token: String, scope: String, token_type: String id_token: String}}>} authentication token
|
||||
*/
|
||||
generateAuthToken(config) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const url = 'https://oauth2.googleapis.com/token';
|
||||
|
||||
const buildQuerystring = querystring.stringify({
|
||||
code: config.auth_code,
|
||||
client_id: config.client_id,
|
||||
client_secret: config.client_secret,
|
||||
redirect_uri: config.redirect_uri,
|
||||
grant_type: 'authorization_code',
|
||||
});
|
||||
|
||||
axios.default
|
||||
.post(url, buildQuerystring, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
})
|
||||
.then((response) => resolve(response.data))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Retrieve user information
|
||||
* @name oAuth.google.getUserInfo
|
||||
* @param {{access_token: String, expires_in: String, refresh_token: String, scope: String, id_token: String}} config
|
||||
* @returns {Promise.<{id: string, email: string, verified_email: true, name: string, given_name: string, family_name: string, picture: string, locale: string}>} user information
|
||||
*/
|
||||
getUserInfo(config, isAPI = false) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
axios
|
||||
.get(
|
||||
`https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${config.access_token}`,
|
||||
isAPI
|
||||
? {}
|
||||
: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.id_token}`,
|
||||
},
|
||||
},
|
||||
)
|
||||
.then((response) => resolve(response.data))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
},
|
||||
},
|
||||
facebook: {
|
||||
/**
|
||||
* Generates authentication URL
|
||||
* @name oAuth.facebook.generateAuthURL
|
||||
* @param {{redirect_uri: String, client_id: String}} config
|
||||
* @returns {String} authentication URL
|
||||
*/
|
||||
generateAuthURL(config) {
|
||||
const stringifiedParams = querystring.stringify({
|
||||
client_id: config.client_id,
|
||||
redirect_uri: process.env.DYNAMIC_CONFIG_FACEBOOK_REDIRECT_URI,
|
||||
scope: ['email', 'user_friends'].join(','),
|
||||
response_type: 'code',
|
||||
auth_type: 'rerequest',
|
||||
display: 'popup',
|
||||
});
|
||||
|
||||
return `https://www.facebook.com/v4.0/dialog/oauth?${stringifiedParams}`;
|
||||
},
|
||||
/**
|
||||
* Generates authentication Token
|
||||
* @name oAuth.facebook.generateAuthToken
|
||||
* @param {{auth_code: String, client_id: String, client_secret: String, redirect_uri: String}} config
|
||||
* @returns {Promise.<{access_token: String, token_type: String, expires_in: String}>} authentication token
|
||||
*/
|
||||
async generateAuthToken(config) {
|
||||
const url = 'https://graph.facebook.com/v4.0/oauth/access_token';
|
||||
|
||||
const { data } = await axios({
|
||||
url,
|
||||
method: 'GET',
|
||||
params: {
|
||||
code: config.auth_code,
|
||||
client_id: config.client_id,
|
||||
client_secret: config.client_secret,
|
||||
redirect_uri: config.redirect_uri,
|
||||
},
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
/**
|
||||
* Retrieve user information
|
||||
* @name oAuth.facebook.getUserInfo
|
||||
* @param {{access_token: String, token_type: String, expires_in: String}} config
|
||||
* @returns {Promise.<{id: string, email: string, first_name: string, last_name: string, image: string}>} user information
|
||||
*/
|
||||
async getUserInfo(config) {
|
||||
const { data } = await axios({
|
||||
url: 'https://graph.facebook.com/me',
|
||||
method: 'GET',
|
||||
params: {
|
||||
fields: ['id', 'email', 'first_name', 'last_name'].join(','),
|
||||
access_token: config.access_token,
|
||||
},
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
},
|
||||
async authenticate(user) {
|
||||
let User;
|
||||
let Credential;
|
||||
try {
|
||||
const isEmailExist = await db.credential.getByField('email', user.email);
|
||||
if (isEmailExist) {
|
||||
// Check status and role
|
||||
if (+isEmailExist.status === 0 || +user.role === +isEmailExist.role) {
|
||||
throw new Error('EMAIL_ADDRESS_NOT_FOUND');
|
||||
}
|
||||
// Check provider type type
|
||||
const type = isEmailExist.type === user.provider;
|
||||
if (!type) {
|
||||
throw new Error('ACCOUNT_IS_REGISTERED_WITH_' + (user.provider === 'n' ? 'EMAIL_AND_PASSWORD' : user.provider === 'g' ? 'GOOGLE' : 'FACEBOOK'));
|
||||
}
|
||||
const userExists = await db.user.getByPK(isEmailExist.user_id);
|
||||
|
||||
if (!userExists) {
|
||||
throw new Error('EMAIL_ADDRESS_NOT_FOUND');
|
||||
}
|
||||
|
||||
return {
|
||||
credential: isEmailExist.id,
|
||||
user: userExists,
|
||||
};
|
||||
}
|
||||
|
||||
User = await db.user.insert(
|
||||
{
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
image: user.image,
|
||||
},
|
||||
{ returnAllFields: true },
|
||||
);
|
||||
|
||||
Credential = await db.credential.insert(
|
||||
{
|
||||
user_id: User.id,
|
||||
email: user.email,
|
||||
type: user.provider,
|
||||
role_id: user.role_id,
|
||||
status: 1,
|
||||
},
|
||||
{ returnAllFields: true },
|
||||
);
|
||||
|
||||
return { credential: Credential.id, user: User };
|
||||
} catch (error) {
|
||||
if (User) {
|
||||
User.destroy();
|
||||
}
|
||||
|
||||
if (Credential) {
|
||||
Credential.destroy();
|
||||
}
|
||||
|
||||
throw new Error(error.message);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
|
||||
/**
|
||||
* Pagination Service
|
||||
* @copyright 2020 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
module.exports = function (page, numItems) {
|
||||
this.page = page ? Number(page) : 1;
|
||||
this.numItems = numItems ? Number(numItems) : 20;
|
||||
this.data = [];
|
||||
this.count = 0;
|
||||
this.numPages = 0;
|
||||
|
||||
this.getItems = function () {
|
||||
return this.data;
|
||||
};
|
||||
|
||||
this.getPage = function () {
|
||||
return this.page;
|
||||
};
|
||||
|
||||
this.getCount = function () {
|
||||
return this.count;
|
||||
};
|
||||
|
||||
this.setCount = function (count) {
|
||||
this.count = count;
|
||||
};
|
||||
|
||||
this.getNumPages = function () {
|
||||
return this.numPages;
|
||||
};
|
||||
|
||||
this.setItems = function (data) {
|
||||
if (data.length == 1 && !data[0].id) {
|
||||
this.count = 0;
|
||||
this.numPages = 1;
|
||||
this.data = [data[0]];
|
||||
return this;
|
||||
}
|
||||
this.data = data;
|
||||
this.numPages = this.count > 1 ? Math.ceil(this.count / this.numItems) : 1;
|
||||
|
||||
this.data = this.data.map(function (transaction) {
|
||||
delete transaction.num;
|
||||
return transaction.toJSON();
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
this.getOffset = function () {
|
||||
return (this.page - 1) * this.numItems;
|
||||
};
|
||||
|
||||
this.getLimit = function () {
|
||||
return this.numItems;
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Hash password
|
||||
* @param {String} password password string to hash
|
||||
* @returns {Promise.<String>} hashed password
|
||||
*/
|
||||
hash: async function (password) {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
return await bcrypt.hash(password, salt);
|
||||
},
|
||||
/**
|
||||
* Compare hashed password with password string
|
||||
* @param {String} password password string
|
||||
* @param {String} hashedPassword hashed password
|
||||
* @returns {Promise.<Boolean>} return true if hashed password match with password string else return false
|
||||
*/
|
||||
compareHash: async function (password, hashedPassword) {
|
||||
return await bcrypt.compare(password, hashedPassword);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,680 @@
|
||||
require('dotenv').config(); //require .env for stripe configurations
|
||||
const moment = require('moment');
|
||||
const stripe = require('./StripeApi'); //this is Payment Service
|
||||
const paypal = require('./PaypalApi');
|
||||
const db = require('../models');
|
||||
const userModel = db.user;
|
||||
const stripeSubscriptionsModel = db.stripe_subscriptions;
|
||||
const stripeSubscriptionsLogModel = db.stripe_subscriptions_log;
|
||||
const paymentPlansModel = db.payment_plans;
|
||||
const stripeProductsModel = db.stripe_products;
|
||||
const paymentServicesModel = db.payment_services;
|
||||
const stripeCardsModel = db.stripe_cards;
|
||||
|
||||
module.exports = new Service();
|
||||
function Service() {
|
||||
this.userId = 0;
|
||||
this.roleId = 0;
|
||||
this.currency = process.env.STRIPE_CURRENCY;
|
||||
this.prorate = process.env.STRIPE_PRORATE;
|
||||
this.forceCancel = process.env.STRIPE_FORCE_CANCEL;
|
||||
|
||||
this.setUserId = function (userId) {
|
||||
this.userId = userId;
|
||||
};
|
||||
this.setRoleId = function (roleId) {
|
||||
this.roleId = roleId;
|
||||
};
|
||||
this.setCurrency = function (currency) {
|
||||
this.currency = currency;
|
||||
};
|
||||
this.setProrationType = function (prorate) {
|
||||
this.prorate = prorate;
|
||||
};
|
||||
this.setCancelType = function (forceCancel) {
|
||||
this.forceCancel = forceCancel;
|
||||
};
|
||||
let convertToCents = function (amount) {
|
||||
return amount * 100;
|
||||
};
|
||||
let handleDBError = function (error) {
|
||||
console.error(error);
|
||||
throw new Error('Internal error: Database error');
|
||||
};
|
||||
let handleRequestError = function (error) {
|
||||
console.error(error);
|
||||
throw new Error('Internal error: Request error');
|
||||
};
|
||||
|
||||
this.subscribe = async function (subscriptionParams, plan) {
|
||||
var subscription = await stripe.createSubscription(subscriptionParams);
|
||||
//create model entry
|
||||
let subscriptionModelEntry = {
|
||||
stripe_id: subscription.id ?? '',
|
||||
cancel_at_period_end: subscription.cancel_at_period_end ?? null,
|
||||
current_period_start: moment(new Date(subscription.current_period_start * 1000)).format('YYYY-MM-DD') ?? null,
|
||||
current_period_end: moment(new Date(subscription.current_period_end * 1000)).format('YYYY-MM-DD') ?? null,
|
||||
user_id: this.userId ?? null,
|
||||
role_id: this.roleId ?? null,
|
||||
plan_id: plan?.id ?? null,
|
||||
coupon_stripe_id: subscription.discount?.coupon?.id ?? '',
|
||||
customer_stripe_id: subscription.customer ?? '',
|
||||
collection_method: subscription.collection_method ?? '',
|
||||
interval: stripeSubscriptionsModel.inverse_interval_mapping(subscription.plan?.interval) ?? 0,
|
||||
interval_count: subscription.plan?.interval_count ?? '',
|
||||
trial_period_days: subscription.plan?.trial_period_days ?? 0,
|
||||
trial_end: subscription.trial_end ? moment(new Date(subscription.trial_end * 1000)).format('YYYY-MM-DD') : null,
|
||||
trial_start: subscription.trial_start ? moment(new Date(subscription.subscriptiontrial_start * 1000)).format('YYYY-MM-DD') : null,
|
||||
status: stripeSubscriptionsModel.inverse_status_mapping(subscription.status) ?? null,
|
||||
};
|
||||
|
||||
let subscriptionCreatedId = await stripeSubscriptionsModel.insert(subscriptionModelEntry);
|
||||
if (!subscriptionCreatedId) {
|
||||
throw new Error('Subscription is not found.');
|
||||
}
|
||||
|
||||
let subscriptionLogUpdated = await this.updateSubscriptionLog(subscriptionCreatedId, subscription.status, plan);
|
||||
if (!subscriptionLogUpdated) {
|
||||
throw new Error('Error while editing subscription log');
|
||||
}
|
||||
return subscriptionCreatedId;
|
||||
};
|
||||
this.updateSubscriptionLog = async function (subscriptionCreatedId, subscriptionCreatedStatus, plan) {
|
||||
let subscriptionLog = await stripeSubscriptionsLogModel.getLast({ user_id: this.userId, role_id: this.roleId });
|
||||
|
||||
if (!subscriptionLog) {
|
||||
let subscriptionLogModelEntry = {
|
||||
user_id: this.userId,
|
||||
role_id: this.roleId,
|
||||
plan_id: plan?.id ?? null,
|
||||
subscription_id: subscriptionCreatedId,
|
||||
type: plan?.type ?? null,
|
||||
status: subscriptionCreatedStatus == 'active' || subscriptionCreatedStatus == 'trialing' ? 1 : 0,
|
||||
};
|
||||
if (!(await stripeSubscriptionsLogModel.insert(subscriptionLogModelEntry))) {
|
||||
throw new Error('Subscription log add not successfull');
|
||||
}
|
||||
} else {
|
||||
let subscriptionLogModelEntry = {
|
||||
plan_id: plan?.id ?? null,
|
||||
subscription_id: subscriptionCreatedId,
|
||||
type: plan?.type ?? null,
|
||||
status: subscriptionCreatedStatus == 'active' || subscriptionCreatedStatus == 'trialing' ? 1 : 0,
|
||||
};
|
||||
|
||||
if (!(await stripeSubscriptionsLogModel.edit(subscriptionLogModelEntry, subscriptionLog.id))) {
|
||||
throw new Error('Subscription log edit not successfull');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
this.createRegularSubscription = async function (user, plan, card, coupon) {
|
||||
let subscriptionParams = {
|
||||
customer: user.stripe_id,
|
||||
items: [{ price: plan.stripe_id }],
|
||||
trial_from_plan: true,
|
||||
default_payment_method: card.stripe_card_id,
|
||||
coupon: coupon?.stripe_id ?? null,
|
||||
};
|
||||
return await this.subscribe(subscriptionParams, plan);
|
||||
};
|
||||
this.createLifetimeSubscription = async function (user, plan, card, coupon) {
|
||||
//create invoice, finalize and pay it.
|
||||
let lifetimePlanStripeObject = await stripe.retrievePrice(plan.stripe_id);
|
||||
let invoiceItemParams = {
|
||||
customer: user.stripe_id,
|
||||
price: plan.stripe_id,
|
||||
discounts: [{ coupon: coupon?.stripe_id }],
|
||||
};
|
||||
let invoiceItemCreated = await stripe.createInvoiceItem(invoiceItemParams);
|
||||
|
||||
let invoiceParams = {
|
||||
customer: user.stripe_id,
|
||||
default_payment_method: card?.stripe_card_id ?? null,
|
||||
};
|
||||
let invoiceCreated = await stripe.createInvoice(invoiceParams);
|
||||
|
||||
let invoicePaid = await stripe.payInvoice(invoiceCreated.id);
|
||||
|
||||
let subscriptionModelEntry = {
|
||||
stripe_id: invoiceItemCreated.id ?? '',
|
||||
cancel_at_period_end: null,
|
||||
current_period_start: moment().format('YYYY-MM-DD') ?? null,
|
||||
current_period_end: null,
|
||||
user_id: this.userId ?? null,
|
||||
role_id: this.roleId ?? null,
|
||||
plan_id: plan?.id ?? null,
|
||||
coupon_stripe_id: invoiceItemCreated.discounts.length > 0 ? coupon?.stripe_id : '',
|
||||
customer_stripe_id: invoicePaid.customer ?? '',
|
||||
collection_method: invoicePaid.collection_method ?? '',
|
||||
interval: 4,
|
||||
interval_count: null,
|
||||
trial_period_days: 0,
|
||||
trial_end: null,
|
||||
trial_start: null,
|
||||
status: 4,
|
||||
};
|
||||
|
||||
let subscriptionCreatedId = await stripeSubscriptionsModel.insert(subscriptionModelEntry);
|
||||
if (!subscriptionCreatedId) {
|
||||
throw new Error('Subscription is not found.');
|
||||
}
|
||||
|
||||
let subscriptionLogUpdated = await this.updateSubscriptionLog(subscriptionCreatedId, 'active', plan);
|
||||
if (!subscriptionLogUpdated) {
|
||||
throw new Error('Error while editing subscription log');
|
||||
}
|
||||
|
||||
return subscriptionCreatedId;
|
||||
};
|
||||
let calculateFullPeriod = function (interval, interval_count = 1) {
|
||||
let totalNumberOfDays = paymentPlansModel.interval_days_mapping()[interval] * interval_count;
|
||||
let cancelAtEpoch = moment().add(totalNumberOfDays, 'days').unix();
|
||||
return cancelAtEpoch;
|
||||
};
|
||||
this.createTrialSubscription = async function (user, plan, card, coupon) {
|
||||
let trialPlanFullPeriod = calculateFullPeriod(plan.interval, plan.interval_count);
|
||||
let subscriptionParams = {
|
||||
customer: user.stripe_id,
|
||||
items: [{ price: plan.stripe_id }],
|
||||
trial_from_plan: true,
|
||||
default_payment_method: card.stripe_card_id,
|
||||
coupon: coupon?.stripe_id ?? null,
|
||||
cancel_at: trialPlanFullPeriod,
|
||||
};
|
||||
return await this.subscribe(subscriptionParams, plan);
|
||||
};
|
||||
this.cancelRegularSubscription = async function (subscription) {
|
||||
if (this.forceCancel === 'true') {
|
||||
var cancellationParams = {
|
||||
subscriptionId: subscription.stripe_id,
|
||||
params: {
|
||||
invoice_now: true,
|
||||
prorate: this.prorate,
|
||||
},
|
||||
};
|
||||
let subscriptionCanceled = await stripe.cancelSubscription(cancellationParams);
|
||||
|
||||
let newStatus = stripeSubscriptionsModel.inverse_status_mapping()[subscriptionCanceled.status];
|
||||
await stripeSubscriptionsModel.edit({ status: newStatus }, subscription.id);
|
||||
} else {
|
||||
let updateParams = {
|
||||
subscriptionId: subscription.stripe_id,
|
||||
params: {
|
||||
cancel_at_period_end: true,
|
||||
},
|
||||
};
|
||||
await stripe.updateSubscription(updateParams);
|
||||
await stripeSubscriptionsModel.edit({ cancel_at_period_end: 1 }, subscription.id);
|
||||
}
|
||||
};
|
||||
this.cancelLifetimeSubscription = async function (subscription) {
|
||||
//TODO check if we refund on this or not
|
||||
//we could make a fixed refund amount
|
||||
//right now we just cancel it locally and refund is never as it is a onetime product thing.
|
||||
let subscriptionEdited = await stripeSubscriptionsModel.edit({ status: 5 }, parseInt(subscription.id));
|
||||
if (!subscriptionEdited) {
|
||||
throw new Error("Couldn't edit subscription");
|
||||
}
|
||||
let subscriptionLog = await stripeSubscriptionsLogModel.getByFields({ subscription_id: parseInt(subscription.id), status: 1 });
|
||||
if (!subscriptionLog) {
|
||||
throw new Error("Couldn't find relative active subscription log");
|
||||
}
|
||||
let subscriptionLogEdited = await stripeSubscriptionsLogModel.edit({ status: 0 }, subscriptionLog.id);
|
||||
if (subscriptionLogEdited) {
|
||||
return true;
|
||||
}
|
||||
throw new Error("Couldn't Edit subscription log");
|
||||
};
|
||||
this.createCustomerWithoutCard = async function (params) {
|
||||
let createdCustomer = await stripe.createCustomer(params);
|
||||
|
||||
let userModelEntry = {
|
||||
stripe_id: createdCustomer.id,
|
||||
};
|
||||
let modelEditedUser = await this.userModel.edit(userModelEntry, this.userId);
|
||||
if (!modelEditedUser) {
|
||||
throw new Error('Internal error: Error editing user stripe id');
|
||||
}
|
||||
return modelEditedUser;
|
||||
};
|
||||
this.createCustomer = async function (params) {
|
||||
let createdCustomer = await stripe.createCustomer(params);
|
||||
let userModelEntry = {
|
||||
stripe_id: createdCustomer.id,
|
||||
};
|
||||
let editedUserModel = await userModel.edit(userModelEntry, this.userId);
|
||||
if (!editedUserModel) {
|
||||
throw new Error('Error editing user stripe id');
|
||||
}
|
||||
|
||||
let customerCardParams = {
|
||||
customerId: createdCustomer.id,
|
||||
cardId: createdCustomer.default_source,
|
||||
};
|
||||
let card = await stripe.retrieveCard(customerCardParams);
|
||||
|
||||
if (card) {
|
||||
cardModelEntry = {
|
||||
card_last: card.last4 ?? '',
|
||||
card_brand: card.brand ?? '',
|
||||
card_exp_month: card.exp_month ?? '',
|
||||
exp_year: card.exp_year ?? '',
|
||||
card_name: createdCustomer.metadata.card_name ?? '',
|
||||
stripe_card_customer: card.customer,
|
||||
stripe_card_id: card.id,
|
||||
is_default: createdCustomer.metadata.is_default,
|
||||
user_id: this.userId ?? null,
|
||||
role_id: this.roleId ?? null,
|
||||
};
|
||||
var modelCreatedCard = await stripeCardsModel.insert(cardModelEntry);
|
||||
if (!modelCreatedCard) {
|
||||
throw new Error('Internal error: Error adding card');
|
||||
}
|
||||
}
|
||||
return modelCreatedCard;
|
||||
};
|
||||
|
||||
this.adminCreateProductPrice = async function (productStripeId, price, productLocalId) {
|
||||
let priceArgs = {
|
||||
unit_amount: convertToCents(price),
|
||||
currency: this.currency,
|
||||
product: productStripeId,
|
||||
};
|
||||
let priceCreated = await stripe.createPrice(priceArgs);
|
||||
|
||||
let productModelEditEntry = {
|
||||
price,
|
||||
price_stripe_id: priceCreated.id,
|
||||
};
|
||||
let modelEditedProduct = await stripeProductsModel.edit(productModelEditEntry, productLocalId);
|
||||
if (!modelEditedProduct) {
|
||||
throw new Error('Internal Error: error setting price');
|
||||
}
|
||||
return priceCreated;
|
||||
};
|
||||
|
||||
this.adminCreateProduct = async function (stripeProductParam, paypalProductParam, localProductType) {
|
||||
if (!stripeProductParam.name) {
|
||||
throw new Error('Must provide a name for your product');
|
||||
}
|
||||
await paypal.setAccessToken();
|
||||
let createdStripeProduct = await stripe.createProduct(stripeProductParam);
|
||||
if (localProductType == 0) {
|
||||
let productModelEntry = {
|
||||
stripe_id: createdStripeProduct.id,
|
||||
name: createdStripeProduct.name,
|
||||
status: createdStripeProduct.active ? 1 : 0,
|
||||
description: createdStripeProduct.description ?? '',
|
||||
images: createdStripeProduct.images.join(';;;'),
|
||||
shippable: createdStripeProduct.shippable ?? 0,
|
||||
unit_label: createdStripeProduct.unit_label ?? '',
|
||||
statement_descriptor: createdStripeProduct.statement_descriptor ?? '',
|
||||
};
|
||||
|
||||
let modelCreatedStripeProduct = await stripeProductsModel.insert(productModelEntry);
|
||||
|
||||
if (!modelCreatedStripeProduct) {
|
||||
throw new Error('Internal error: error adding a product');
|
||||
}
|
||||
return {
|
||||
productStripeId: createdStripeProduct.id,
|
||||
productLocalId: modelCreatedStripeProduct,
|
||||
};
|
||||
} else if (localProductType == 1) {
|
||||
let createdPaypalProduct = await paypal.createProduct(paypalProductParam);
|
||||
|
||||
let paymentServiceModelEntry = {
|
||||
name: createdStripeProduct.name,
|
||||
stripe_id: createdStripeProduct.id,
|
||||
paypal_id: createdPaypalProduct.data.id,
|
||||
status: createdStripeProduct.active ? 1 : 0,
|
||||
image: createdStripeProduct.images.length > 0 ? createdStripeProduct.images[0] : '',
|
||||
url: createdStripeProduct.url ?? '',
|
||||
category: createdStripeProduct.metadata.category,
|
||||
description: createdStripeProduct.description ?? '',
|
||||
};
|
||||
|
||||
let modelCreatedServiceId = await paymentServicesModel.insert(paymentServiceModelEntry);
|
||||
|
||||
let service = paymentServicesModel.getByPK(modelCreatedServiceId);
|
||||
|
||||
return {
|
||||
serviceProductStripeId: service.stripe_id,
|
||||
serviceProductPaypalId: service.paypal_id,
|
||||
serviceProductLocalId: service.id,
|
||||
};
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* [adminCreateSubscriptionPlan allows an admin to create subscription plan through admin portal]
|
||||
* @param {object} planParams [object that contain plan parameters]
|
||||
* @param {integer} planType [integer that represent plan type in relative to stripe plans table] @see /models/stripe_plans.js type_mapping()
|
||||
* @param {integer} localProductId [integer that represent product id in relative to stripe products table]
|
||||
* @return {integer} [the id of the created plan in database]
|
||||
*/
|
||||
this.adminCreateSubscriptionPlan = async function (params, planType, localServiceId) {
|
||||
if (!params.nickname) {
|
||||
throw new Error('Must have a display name');
|
||||
}
|
||||
if (!localServiceId) {
|
||||
throw new Error('Must provide system product id');
|
||||
}
|
||||
if (!planType) {
|
||||
throw new Error('Must provide plan type');
|
||||
}
|
||||
await paypal.setAccessToken();
|
||||
let service = await paymentServicesModel.getByPK(localServiceId);
|
||||
|
||||
let stripeSubscriptionPlanParams = {
|
||||
nickname: params.nickname,
|
||||
currency: process.env.STRIPE_CURRENCY,
|
||||
unit_amount: convertToCents(params.amount),
|
||||
product: service.stripe_id,
|
||||
recurring: {
|
||||
interval: paymentPlansModel.interval_mapping()[params.interval],
|
||||
interval_count: params.interval_count,
|
||||
trial_period_days: params.trial_period_days,
|
||||
},
|
||||
active: parseInt(params.status) === 1 ? true : false,
|
||||
};
|
||||
let paypalSubscriptionPlanParams = {
|
||||
product_id: service.paypal_id,
|
||||
name: params.nickname,
|
||||
billing_cycles: [
|
||||
{
|
||||
frequency: {
|
||||
interval_unit: paymentPlansModel.interval_mapping()[params.interval].toUpperCase(),
|
||||
interval_count: params.interval_count,
|
||||
},
|
||||
tenure_type: 'REGULAR',
|
||||
sequence: 1,
|
||||
total_cycles: 0,
|
||||
pricing_scheme: {
|
||||
fixed_price: {
|
||||
value: params.amount,
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
payment_preferences: {
|
||||
auto_bill_outstanding: true,
|
||||
setup_fee_failure_action: 'CANCEL',
|
||||
payment_failure_threshold: 0,
|
||||
},
|
||||
};
|
||||
if (params.trial_period_days > 0) {
|
||||
paypalSubscriptionPlanParams.billing_cycles[0].sequence = 2;
|
||||
paypalSubscriptionPlanParams.billing_cycles.unshift({
|
||||
frequency: {
|
||||
interval_unit: 'DAY',
|
||||
interval_count: parseInt(params.trial_period_days),
|
||||
},
|
||||
tenure_type: 'TRIAL',
|
||||
sequence: 1,
|
||||
total_cycles: 1,
|
||||
});
|
||||
}
|
||||
|
||||
let stripePlanCreated = await stripe.createPrice(stripeSubscriptionPlanParams);
|
||||
let paypalPlanCreated = await paypal.createSubscriptionPlan(paypalSubscriptionPlanParams);
|
||||
|
||||
let planModelEntry = {
|
||||
stripe_product_id: stripePlanCreated.product,
|
||||
paypal_product_id: paypalPlanCreated.data.product_id,
|
||||
stripe_id: stripePlanCreated.id,
|
||||
paypal_id: paypalPlanCreated.data.id,
|
||||
amount: stripePlanCreated.unit_amount / 100,
|
||||
interval: paymentPlansModel.inverse_interval_mapping(stripePlanCreated.recurring.interval),
|
||||
interval_count: stripePlanCreated.recurring.interval_count,
|
||||
trial_period_days: stripePlanCreated.recurring.trial_period_days ?? 0,
|
||||
nickname: stripePlanCreated.nickname,
|
||||
service_id: localServiceId,
|
||||
status: stripePlanCreated.active ? 1 : 0,
|
||||
type: parseInt(planType),
|
||||
};
|
||||
|
||||
let modelCreatedPlan = await paymentPlansModel.insert(planModelEntry);
|
||||
|
||||
if (!modelCreatedPlan) {
|
||||
throw new Error('Internal error: error creating plan');
|
||||
}
|
||||
return modelCreatedPlan;
|
||||
};
|
||||
|
||||
this._createStripeLifetimePlan = async function (params, service) {
|
||||
//stripe doesn't have lifetime plans
|
||||
//how it works is a price is created with no recurring parameter as a single time product
|
||||
//which acts and dealt with internally within our system as a lifetime plan.
|
||||
let stripeSubscriptionPlanParams = {
|
||||
nickname: params.nickname,
|
||||
currency: this.currency,
|
||||
unit_amount: convertToCents(params.amount),
|
||||
product: service.stripe_id,
|
||||
active: parseInt(params.status) === 1 ? true : false,
|
||||
};
|
||||
return await stripe.createPrice(stripeSubscriptionPlanParams);
|
||||
};
|
||||
this._createPaypalLifetimePlan = async function (params, service) {
|
||||
//paypal doesn't allow lifetime plan
|
||||
//how it works is i create a blan with a trial with 999 years and setup fee for subscription as the main amount for the lifetime plan
|
||||
//paypal needs to have a regular plan so after the 999 years there is a regular plan with 0.01 amount money
|
||||
//this works for both lifetime paid and free plans.
|
||||
await paypal.setAccessToken();
|
||||
|
||||
let paypalSubscriptionPlanParams = {
|
||||
product_id: service.paypal_id,
|
||||
name: params.nickname,
|
||||
billing_cycles: [
|
||||
{
|
||||
frequency: {
|
||||
interval_unit: 'YEAR',
|
||||
interval_count: 1,
|
||||
},
|
||||
tenure_type: 'TRIAL',
|
||||
sequence: 1,
|
||||
total_cycles: 999,
|
||||
},
|
||||
{
|
||||
frequency: {
|
||||
interval_unit: 'YEAR',
|
||||
interval_count: 1,
|
||||
},
|
||||
tenure_type: 'REGULAR',
|
||||
sequence: 2,
|
||||
total_cycles: 0,
|
||||
pricing_scheme: {
|
||||
fixed_price: {
|
||||
value: '0.01',
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
payment_preferences: {
|
||||
auto_bill_outstanding: true,
|
||||
setup_fee: {
|
||||
value: params.amount,
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
setup_fee_failure_action: 'CANCEL',
|
||||
payment_failure_threshold: 0,
|
||||
},
|
||||
};
|
||||
return await paypal.createPlan(paypalSubscriptionPlanParams);
|
||||
};
|
||||
this._destroyStripeLifetimePlan = async function (planStripeId) {
|
||||
//stripe doesn't allow deleting a price. will be set to unactive instead
|
||||
return await stripe.updatePrice({ priceId: planStripeId, params: { active: false } });
|
||||
};
|
||||
this._destroyPaypalLifetimePlan = async function (planPaypalId) {
|
||||
//paypal doesn't allow deleting a price. will be set to unactive instead
|
||||
return await paypal.deactivatePlan(planPaypalId);
|
||||
};
|
||||
|
||||
this.adminCreateLifetimePlan = async function (params, planType, localServiceId) {
|
||||
if (!params.nickname) {
|
||||
throw new Error('Must have a display name');
|
||||
}
|
||||
if (!localServiceId) {
|
||||
throw new Error('Must provide system product id');
|
||||
}
|
||||
if (!planType) {
|
||||
throw new Error('Must provide plan type');
|
||||
}
|
||||
if (parseInt(params.interval) !== 4) {
|
||||
throw new Error('Interval must be "forever".');
|
||||
}
|
||||
|
||||
let service = await paymentServicesModel.getByPK(localServiceId);
|
||||
|
||||
let stripePlanCreated = await this._createStripeLifetimePlan(params, service);
|
||||
let paypalPlanCreated = await this._createPaypalLifetimePlan(params, service);
|
||||
|
||||
if (!stripePlanCreated && paypalPlanCreated) {
|
||||
await this._destroyPaypalLifetimePlan(paypalPlanCreated.data.id);
|
||||
throw new Error('Internal Error: Something happended while creating the plans.');
|
||||
}
|
||||
if (!paypalPlanCreated && stripePlanCreated) {
|
||||
await this._destroyStripeLifetimePlan(stripePlanCreated.id);
|
||||
throw new Error('Internal Error: Something happended while creating the plans.');
|
||||
}
|
||||
|
||||
let planModelEntry = {
|
||||
stripe_product_id: stripePlanCreated?.product ?? '',
|
||||
paypal_product_id: paypalPlanCreated?.data?.product_id ?? '',
|
||||
stripe_id: stripePlanCreated?.id ?? '',
|
||||
paypal_id: paypalPlanCreated?.data?.id ?? '',
|
||||
amount: stripePlanCreated?.unit_amount ? stripePlanCreated.unit_amount / 100 : params.amount,
|
||||
interval: 4,
|
||||
interval_count: 1,
|
||||
trial_period_days: 0,
|
||||
nickname: stripePlanCreated?.nickname ?? paypalPlanCreated.data.name,
|
||||
service_id: localServiceId,
|
||||
status: stripePlanCreated?.active == true ? 1 : params.status,
|
||||
type: parseInt(planType),
|
||||
};
|
||||
|
||||
let modelCreatedPlan = await paymentPlansModel.insert(planModelEntry);
|
||||
|
||||
if (!modelCreatedPlan) {
|
||||
throw new Error('Internal error: error creating plan');
|
||||
}
|
||||
return modelCreatedPlan;
|
||||
};
|
||||
|
||||
this.adminCreateTrialPlan = async function (planParams, planType, localServiceId) {
|
||||
if (Object.keys(planParams.recurring).length === 0 || planParams.recurring.constructor !== Object) {
|
||||
throw new Error('Must have a recurring parameter (subscription interval)');
|
||||
}
|
||||
if (!planParams.nickname) {
|
||||
throw new Error('Must have a display name');
|
||||
}
|
||||
if (!localServiceId) {
|
||||
throw new Error('Must provide system product id');
|
||||
}
|
||||
let priceCreated = await stripe.createPrice(planParams);
|
||||
|
||||
let planModelEntry = {
|
||||
stripe_product_id: priceCreated.product,
|
||||
stripe_id: priceCreated.id,
|
||||
amount: priceCreated.unit_amount / 100,
|
||||
interval: paymentPlansModel.inverse_interval_mapping(priceCreated.recurring.interval),
|
||||
interval_count: priceCreated.recurring.interval_count,
|
||||
trial_period_days: priceCreated.recurring.trial_period_days ?? 0,
|
||||
nickname: priceCreated.nickname,
|
||||
service_id: localServiceId,
|
||||
status: priceCreated.active ? 1 : 0,
|
||||
type: parseInt(planType),
|
||||
};
|
||||
let modelCreatedPlan = await paymentPlansModel.insert(planModelEntry);
|
||||
if (!modelCreatedPlan) {
|
||||
throw new Error('Internal error: error creating plan');
|
||||
}
|
||||
return modelCreatedPlan;
|
||||
};
|
||||
this.adminCancelSubscription = async function (subscriptionId) {
|
||||
let subscriptionToCancel = await stripeSubscriptionsModel.getByPK(subscriptionId);
|
||||
let subscriptionPlan = await paymentPlansModel.getByPK(subscriptionToCancel.plan_id);
|
||||
if (!subscriptionToCancel || !subscriptionPlan) {
|
||||
throw new Error('No subscription or plan of that id');
|
||||
}
|
||||
// let subscirptionType = subscriptionPlan.type;
|
||||
let subscirptionType = 3;
|
||||
//if normal subscription
|
||||
switch (subscirptionType) {
|
||||
case 0:
|
||||
case 3:
|
||||
case 4:
|
||||
let cancelSubscriptionParams = {
|
||||
subscriptionId: subscriptionToCancel.stripe_id,
|
||||
params: {
|
||||
prorate: this.prorate,
|
||||
invoice_now: true,
|
||||
},
|
||||
};
|
||||
let subscriptionCanceled = await stripe.cancelSubscription(cancelSubscriptionParams);
|
||||
let localCancellationParams = {
|
||||
status: stripeSubscriptionsModel.inverse_status_mapping()[subscriptionCanceled.status],
|
||||
};
|
||||
await stripeSubscriptionsModel.edit(localCancellationParams, subscriptionId);
|
||||
return subscriptionCanceled;
|
||||
default:
|
||||
throw new Error('Something wrong');
|
||||
}
|
||||
};
|
||||
this.userAddCard = async function (cardParams) {
|
||||
let createdCard = await stripe.createCard(cardParams);
|
||||
|
||||
if (createdCard) {
|
||||
if (createdCard.metadata.is_default == '1') {
|
||||
let customerUpdateParams = {
|
||||
customerId: createdCard.customer,
|
||||
params: { default_source: createdCard.id },
|
||||
};
|
||||
await stripe.updateCustomer(customerUpdateParams);
|
||||
let defaultCards = await stripeCardsModel.getAll({ is_default: 1 });
|
||||
if (defaultCards.length > 0) {
|
||||
defaultCards.forEach(async (card) => {
|
||||
await stripeCardsModel.edit({ is_default: 0 }, card.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
cardModelEntry = {
|
||||
card_last: createdCard.last4 ?? '',
|
||||
card_brand: createdCard.brand ?? '',
|
||||
card_exp_month: createdCard.exp_month ?? '',
|
||||
exp_year: createdCard.exp_year ?? '',
|
||||
card_name: createdCard.metadata.card_name ?? '',
|
||||
stripe_card_customer: createdCard.customer,
|
||||
stripe_card_id: createdCard.id,
|
||||
is_default: createdCard.metadata.is_default,
|
||||
user_id: this.userId ?? null,
|
||||
role_id: this.roleId ?? null,
|
||||
};
|
||||
var modelCreatedCard = await stripeCardsModel.insert(cardModelEntry);
|
||||
if (!modelCreatedCard) {
|
||||
throw new Error('Internal error: Error adding card');
|
||||
}
|
||||
}
|
||||
return modelCreatedCard;
|
||||
};
|
||||
this.changePlan = async function (currentSubscription, newPlan, user, card, couponId = '') {
|
||||
//if current subscriptions is canceled
|
||||
if (!currentSubscription || currentSubscription.status == 5) {
|
||||
let subscriptionParams = {
|
||||
customer: user.stripe_id,
|
||||
items: { plan: newPlan.id },
|
||||
coupon: couponId,
|
||||
};
|
||||
return await this.subscribe(subscriptionParams, newPlan);
|
||||
}
|
||||
//user has subscription (upgrade/ downgrade)
|
||||
if (currentSubscription && currentSubscription.status != 5) {
|
||||
if (currentSubscription.plan_id == newPlan.id) throw new Error('Same Plan');
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const qs = require('qs'); //parse url form encoded params
|
||||
const paypalBaseUrl = process.env.PAYPAL_BASE_URL;
|
||||
const paypalSandboxBaseUrl = process.env.PAYPAL_SANDBOX_BASE_URL;
|
||||
|
||||
if (process.env.MODE === 'development') {
|
||||
var axios = require('axios').create({
|
||||
baseURL: paypalSandboxBaseUrl,
|
||||
});
|
||||
} else if (process.env.MODE === 'production') {
|
||||
var axios = require('axios').create({
|
||||
baseURL: paypalBaseUrl,
|
||||
});
|
||||
}
|
||||
let accessToken = '';
|
||||
|
||||
module.exports = new Service();
|
||||
function Service() {
|
||||
// this.error = async function () {
|
||||
// Error.call(this);
|
||||
|
||||
// };
|
||||
this.setAccessToken = async function () {
|
||||
accessToken = await this._getPaypalAccessToken().catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
return accessToken;
|
||||
};
|
||||
this._getPaypalAccessToken = async function () {
|
||||
let data = await axios({
|
||||
method: 'post',
|
||||
url: '/v1/oauth2/token',
|
||||
auth: {
|
||||
username: process.env.PAYPAL_CLIENT_ID,
|
||||
password: process.env.PAYPAL_SECRET,
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
data: qs.stringify({
|
||||
grant_type: 'client_credentials',
|
||||
}),
|
||||
}).catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
return data.data.access_token;
|
||||
};
|
||||
this.createProduct = async function (params) {
|
||||
let requiredFields = ['name', 'type'];
|
||||
requiredFields.forEach((field) => {
|
||||
if (!params[field]) {
|
||||
throw new Error(`Must have "${field}" parameter`);
|
||||
}
|
||||
});
|
||||
params = this.filterParams(params);
|
||||
let config = {
|
||||
url: '/v1/catalogs/products',
|
||||
data: params,
|
||||
};
|
||||
try {
|
||||
return await this.axiosPost(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response, { depth: null });
|
||||
throw new Error(error.response?.data?.message ?? 'Internal Error: Error creating product');
|
||||
}
|
||||
};
|
||||
this.getPlans = async function (paginationParams) {
|
||||
let config = {
|
||||
url: '/v1/billing/plans',
|
||||
data: paginationParams,
|
||||
};
|
||||
try {
|
||||
return await this.axiosGet(config);
|
||||
} catch (error) {
|
||||
console.error(error.response);
|
||||
throw new Error('Internal Error: Error getting plans');
|
||||
}
|
||||
};
|
||||
this.getProducts = async function (paginationParams) {
|
||||
let config = {
|
||||
url: '/v1/catalogs/products',
|
||||
data: paginationParams,
|
||||
};
|
||||
try {
|
||||
return await this.axiosGet(config);
|
||||
} catch (error) {
|
||||
console.error(error.response);
|
||||
throw new Error('Internal Error: Error getting products');
|
||||
}
|
||||
};
|
||||
this.getProductDetails = async function (productId) {
|
||||
let config = {
|
||||
url: `/v1/catalogs/products/${productId}`,
|
||||
data: {},
|
||||
};
|
||||
try {
|
||||
return await this.axiosGet(config);
|
||||
} catch (error) {
|
||||
console.error(error.response);
|
||||
throw new Error('Internal Error: Error getting product details');
|
||||
}
|
||||
};
|
||||
|
||||
this.getSubscriptions = async function (paginationParams) {
|
||||
let createdProduct = await axios({
|
||||
method: 'get',
|
||||
url: '/v1/billing/plans',
|
||||
data: qs.stringify(paginationParams),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
}).catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
return createdProduct.data;
|
||||
};
|
||||
this.createSubscription = async function (params) {
|
||||
let config = {
|
||||
url: '/v1/billing/subscriptions',
|
||||
data: params,
|
||||
};
|
||||
return await this.axiosPost(config);
|
||||
};
|
||||
this.createPlan = async function (params) {
|
||||
let config = {
|
||||
url: '/v1/billing/plans',
|
||||
data: params,
|
||||
};
|
||||
try {
|
||||
return await this.axiosPost(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error creating paypal subscription plan');
|
||||
}
|
||||
};
|
||||
this.deactivatePlan = async function (planId) {
|
||||
let config = {
|
||||
url: `/v1/billing/plans/${planId}/deactivate`,
|
||||
data: {},
|
||||
};
|
||||
try {
|
||||
return await this.axiosPost(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error deactivating paypal subscription plan');
|
||||
}
|
||||
};
|
||||
this.getPlanDetails = async function (planId) {
|
||||
let config = {
|
||||
url: `/v1/billing/plans/${planId}`,
|
||||
data: {},
|
||||
};
|
||||
try {
|
||||
return await this.axiosGet(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error getting subscription details');
|
||||
}
|
||||
};
|
||||
this.getSubscriptionDetails = async function (subscriptionId) {
|
||||
let config = {
|
||||
url: `/v1/billing/subscriptions/${subscriptionId}`,
|
||||
data: {},
|
||||
};
|
||||
try {
|
||||
return await this.axiosGet(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error getting subscription details');
|
||||
}
|
||||
};
|
||||
this.updateProduct = async function (params, productId) {
|
||||
let config = {
|
||||
url: `/v1/catalogs/products/${productId}`,
|
||||
data: params,
|
||||
};
|
||||
try {
|
||||
return await this.axiosPatch(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error updating product');
|
||||
}
|
||||
};
|
||||
this.updatePlan = async function (params, planId) {
|
||||
let config = {
|
||||
url: `/v1/billing/plans/${planId}`,
|
||||
data: params,
|
||||
};
|
||||
try {
|
||||
return await this.axiosPatch(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error updating plan');
|
||||
}
|
||||
};
|
||||
this.updatePlanPricing = async function (params, planId) {
|
||||
let config = {
|
||||
url: `/v1/billing/plans/${planId}/update-pricing-schemes`,
|
||||
data: params,
|
||||
};
|
||||
try {
|
||||
return await this.axiosPost(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error updating plan');
|
||||
}
|
||||
};
|
||||
this.activatePlan = async function (planId) {
|
||||
let config = {
|
||||
url: `/v1/billing/plans/${planId}/activate`,
|
||||
data: {},
|
||||
};
|
||||
try {
|
||||
return await this.axiosPost(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error updating plan');
|
||||
}
|
||||
};
|
||||
this.deactivatePlan = async function (planId) {
|
||||
let config = {
|
||||
url: `/v1/billing/plans/${planId}/deactivate`,
|
||||
data: {},
|
||||
};
|
||||
try {
|
||||
return await this.axiosPost(config);
|
||||
} catch (error) {
|
||||
console.dir(error.response.data, { depth: null });
|
||||
throw new Error('Internal Error: Error updating plan');
|
||||
}
|
||||
};
|
||||
this.retrievePlan = async function (params, planId) {
|
||||
let config = {
|
||||
url: `/v1/billing/subscriptions/${planId}`,
|
||||
data: params,
|
||||
};
|
||||
return await this.axiosGet(config);
|
||||
};
|
||||
this.axiosGet = async function (config) {
|
||||
return await axios({
|
||||
method: 'get',
|
||||
url: config.url,
|
||||
data: qs.stringify(config.data ?? {}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
this.axiosPost = async function (config) {
|
||||
return await axios({
|
||||
method: 'post',
|
||||
url: config.url,
|
||||
data: JSON.stringify(config.data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
Prefer: 'return=representation',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
this.axiosPatch = async function (config) {
|
||||
return await axios({
|
||||
method: 'patch',
|
||||
url: config.url,
|
||||
data: JSON.stringify(config.data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* [filterParams filters paramters from null, undefined, empty strings, empty arrays and empty objects as it can cause unwanted changes]
|
||||
* @param {object} params [object that can contain one more object inside]
|
||||
* @return {object} [object with only truth variables]
|
||||
*/
|
||||
this.filterParams = function (params) {
|
||||
Object.keys(params).forEach((param) => {
|
||||
if (this.empty(params[param]) || params[param].length === 0) {
|
||||
console.log(`Parameter empty, null or undefined`);
|
||||
delete params[param];
|
||||
} else if (params[param].constructor === Object && Object.entries(params[param]).length === 0) {
|
||||
console.log(`Parameter object empty`);
|
||||
delete params[param];
|
||||
} else if (params[param].constructor === Object && Object.entries(params[param]).length > 0) {
|
||||
this.filterParams(params[param]);
|
||||
}
|
||||
});
|
||||
return params;
|
||||
};
|
||||
this.empty = (value) => value === null || value === undefined;
|
||||
}
|
||||
@@ -0,0 +1,705 @@
|
||||
require('dotenv').config(); //require .env for stripe configurations
|
||||
// const moment = require('moment'); //this is Payment Service
|
||||
const paypal = require('./PaypalApi');
|
||||
const db = require('../models');
|
||||
const { update } = require('lodash');
|
||||
const paypalPlansModel = db.paypal_plans;
|
||||
const paypalServicesModel = db.paypal_services;
|
||||
const paypalProductsModel = db.paypal_products;
|
||||
|
||||
module.exports = new Service();
|
||||
function Service() {
|
||||
this.userId = 0;
|
||||
this.roleId = 0;
|
||||
this.currency = process.env.PAYPAL_CURRENCY;
|
||||
// this.prorate = process.env.STRIPE_PRORATE;
|
||||
// this.forceCancel = process.env.STRIPE_FORCE_CANCEL;
|
||||
|
||||
this.setUserId = function (userId) {
|
||||
this.userId = userId;
|
||||
};
|
||||
this.setRoleId = function (roleId) {
|
||||
this.roleId = roleId;
|
||||
};
|
||||
this.setCurrency = function (currency) {
|
||||
this.currency = currency;
|
||||
};
|
||||
// this.setProrationType = function (prorate) {
|
||||
// this.prorate = prorate;
|
||||
// };
|
||||
// this.setCancelType = function (forceCancel) {
|
||||
// this.forceCancel = forceCancel;
|
||||
// };
|
||||
|
||||
// this.subscribe = async function (subscriptionParams, plan) {
|
||||
// var subscription = await stripe.createSubscription(subscriptionParams);
|
||||
// //create model entry
|
||||
// let subscriptionModelEntry = {
|
||||
// stripe_id: subscription.id ?? '',
|
||||
// cancel_at_period_end: subscription.cancel_at_period_end ?? null,
|
||||
// current_period_start: moment(new Date(subscription.current_period_start * 1000)).format('YYYY-MM-DD') ?? null,
|
||||
// current_period_end: moment(new Date(subscription.current_period_end * 1000)).format('YYYY-MM-DD') ?? null,
|
||||
// user_id: this.userId ?? null,
|
||||
// role_id: this.roleId ?? null,
|
||||
// plan_id: plan?.id ?? null,
|
||||
// coupon_stripe_id: subscription.discount?.coupon?.id ?? '',
|
||||
// customer_stripe_id: subscription.customer ?? '',
|
||||
// collection_method: subscription.collection_method ?? '',
|
||||
// interval: stripeSubscriptionsModel.inverse_interval_mapping(subscription.plan?.interval) ?? 0,
|
||||
// interval_count: subscription.plan?.interval_count ?? '',
|
||||
// trial_period_days: subscription.plan?.trial_period_days ?? 0,
|
||||
// trial_end: subscription.trial_end ? moment(new Date(subscription.trial_end * 1000)).format('YYYY-MM-DD') : null,
|
||||
// trial_start: subscription.trial_start ? moment(new Date(subscription.subscriptiontrial_start * 1000)).format('YYYY-MM-DD') : null,
|
||||
// status: stripeSubscriptionsModel.inverse_status_mapping(subscription.status) ?? null,
|
||||
// };
|
||||
|
||||
// let subscriptionCreatedId = await stripeSubscriptionsModel.insert(subscriptionModelEntry);
|
||||
// if (!subscriptionCreatedId) {
|
||||
// throw new Error('Subscription is not found.');
|
||||
// }
|
||||
|
||||
// let subscriptionLogUpdated = await this.updateSubscriptionLog(subscriptionCreatedId, subscription.status, plan);
|
||||
// if (!subscriptionLogUpdated) {
|
||||
// throw new Error('Error while editing subscription log');
|
||||
// }
|
||||
// return subscriptionCreatedId;
|
||||
// };
|
||||
// this.updateSubscriptionLog = async function (subscriptionCreatedId, subscriptionCreatedStatus, plan) {
|
||||
// let subscriptionLog = await stripeSubscriptionsLogModel.getLast({ user_id: this.userId, role_id: this.roleId });
|
||||
|
||||
// if (!subscriptionLog) {
|
||||
// let subscriptionLogModelEntry = {
|
||||
// user_id: this.userId,
|
||||
// role_id: this.roleId,
|
||||
// plan_id: plan?.id ?? null,
|
||||
// subscription_id: subscriptionCreatedId,
|
||||
// type: plan?.type ?? null,
|
||||
// status: subscriptionCreatedStatus == 'active' || subscriptionCreatedStatus == 'trialing' ? 1 : 0,
|
||||
// };
|
||||
// if (!(await stripeSubscriptionsLogModel.insert(subscriptionLogModelEntry))) {
|
||||
// throw new Error('Subscription log add not successfull');
|
||||
// }
|
||||
// } else {
|
||||
// let subscriptionLogModelEntry = {
|
||||
// plan_id: plan?.id ?? null,
|
||||
// subscription_id: subscriptionCreatedId,
|
||||
// type: plan?.type ?? null,
|
||||
// status: subscriptionCreatedStatus == 'active' || subscriptionCreatedStatus == 'trialing' ? 1 : 0,
|
||||
// };
|
||||
|
||||
// if (!(await stripeSubscriptionsLogModel.edit(subscriptionLogModelEntry, subscriptionLog.id))) {
|
||||
// throw new Error('Subscription log edit not successfull');
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// };
|
||||
// this.createRegularSubscription = async function (user, plan, card, coupon) {
|
||||
// let subscriptionParams = {
|
||||
// customer: user.stripe_id,
|
||||
// items: [{ price: plan.stripe_id }],
|
||||
// trial_from_plan: true,
|
||||
// default_payment_method: card.stripe_card_id,
|
||||
// coupon: coupon?.stripe_id ?? null,
|
||||
// };
|
||||
// return await this.subscribe(subscriptionParams, plan);
|
||||
// };
|
||||
// this.createLifetimeSubscription = async function (user, plan, card, coupon) {
|
||||
// //create invoice, finalize and pay it.
|
||||
// let lifetimePlanStripeObject = await stripe.retrievePrice(plan.stripe_id);
|
||||
// let invoiceItemParams = {
|
||||
// customer: user.stripe_id,
|
||||
// price: plan.stripe_id,
|
||||
// discounts: [{ coupon: coupon?.stripe_id }],
|
||||
// };
|
||||
// let invoiceItemCreated = await stripe.createInvoiceItem(invoiceItemParams);
|
||||
|
||||
// let invoiceParams = {
|
||||
// customer: user.stripe_id,
|
||||
// default_payment_method: card?.stripe_card_id ?? null,
|
||||
// };
|
||||
// let invoiceCreated = await stripe.createInvoice(invoiceParams);
|
||||
|
||||
// let invoicePaid = await stripe.payInvoice(invoiceCreated.id);
|
||||
|
||||
// let subscriptionModelEntry = {
|
||||
// stripe_id: invoiceItemCreated.id ?? '',
|
||||
// cancel_at_period_end: null,
|
||||
// current_period_start: moment().format('YYYY-MM-DD') ?? null,
|
||||
// current_period_end: null,
|
||||
// user_id: this.userId ?? null,
|
||||
// role_id: this.roleId ?? null,
|
||||
// plan_id: plan?.id ?? null,
|
||||
// coupon_stripe_id: invoiceItemCreated.discounts.length > 0 ? coupon?.stripe_id : '',
|
||||
// customer_stripe_id: invoicePaid.customer ?? '',
|
||||
// collection_method: invoicePaid.collection_method ?? '',
|
||||
// interval: 4,
|
||||
// interval_count: null,
|
||||
// trial_period_days: 0,
|
||||
// trial_end: null,
|
||||
// trial_start: null,
|
||||
// status: 4,
|
||||
// };
|
||||
|
||||
// let subscriptionCreatedId = await stripeSubscriptionsModel.insert(subscriptionModelEntry);
|
||||
// if (!subscriptionCreatedId) {
|
||||
// throw new Error('Subscription is not found.');
|
||||
// }
|
||||
|
||||
// let subscriptionLogUpdated = await this.updateSubscriptionLog(subscriptionCreatedId, 'active', plan);
|
||||
// if (!subscriptionLogUpdated) {
|
||||
// throw new Error('Error while editing subscription log');
|
||||
// }
|
||||
|
||||
// return subscriptionCreatedId;
|
||||
// };
|
||||
// let calculateFullPeriod = function (interval, interval_count = 1) {
|
||||
// let totalNumberOfDays = paypalPlansModel.interval_days_mapping()[interval] * interval_count;
|
||||
// let cancelAtEpoch = moment().add(totalNumberOfDays, 'days').unix();
|
||||
// return cancelAtEpoch;
|
||||
// };
|
||||
// this.createTrialSubscription = async function (user, plan, card, coupon) {
|
||||
// let trialPlanFullPeriod = calculateFullPeriod(plan.interval, plan.interval_count);
|
||||
// let subscriptionParams = {
|
||||
// customer: user.stripe_id,
|
||||
// items: [{ price: plan.stripe_id }],
|
||||
// trial_from_plan: true,
|
||||
// default_payment_method: card.stripe_card_id,
|
||||
// coupon: coupon?.stripe_id ?? null,
|
||||
// cancel_at: trialPlanFullPeriod,
|
||||
// };
|
||||
// return await this.subscribe(subscriptionParams, plan);
|
||||
// };
|
||||
// this.cancelRegularSubscription = async function (subscription) {
|
||||
// if (this.forceCancel === 'true') {
|
||||
// var cancellationParams = {
|
||||
// subscriptionId: subscription.stripe_id,
|
||||
// params: {
|
||||
// invoice_now: true,
|
||||
// prorate: this.prorate,
|
||||
// },
|
||||
// };
|
||||
// let subscriptionCanceled = await stripe.cancelSubscription(cancellationParams);
|
||||
|
||||
// let newStatus = stripeSubscriptionsModel.inverse_status_mapping()[subscriptionCanceled.status];
|
||||
// await stripeSubscriptionsModel.edit({ status: newStatus }, subscription.id);
|
||||
// } else {
|
||||
// let updateParams = {
|
||||
// subscriptionId: subscription.stripe_id,
|
||||
// params: {
|
||||
// cancel_at_period_end: true,
|
||||
// },
|
||||
// };
|
||||
// await stripe.updateSubscription(updateParams);
|
||||
// await stripeSubscriptionsModel.edit({ cancel_at_period_end: 1 }, subscription.id);
|
||||
// }
|
||||
// };
|
||||
// this.cancelLifetimeSubscription = async function (subscription) {
|
||||
// //TODO check if we refund on this or not
|
||||
// //we could make a fixed refund amount
|
||||
// //right now we just cancel it locally and refund is never as it is a onetime product thing.
|
||||
// let subscriptionEdited = await stripeSubscriptionsModel.edit({ status: 5 }, parseInt(subscription.id));
|
||||
// if (!subscriptionEdited) {
|
||||
// throw new Error("Couldn't edit subscription");
|
||||
// }
|
||||
// let subscriptionLog = await stripeSubscriptionsLogModel.getByFields({ subscription_id: parseInt(subscription.id), status: 1 });
|
||||
// if (!subscriptionLog) {
|
||||
// throw new Error("Couldn't find relative active subscription log");
|
||||
// }
|
||||
// let subscriptionLogEdited = await stripeSubscriptionsLogModel.edit({ status: 0 }, subscriptionLog.id);
|
||||
// if (subscriptionLogEdited) {
|
||||
// return true;
|
||||
// }
|
||||
// throw new Error("Couldn't Edit subscription log");
|
||||
// };
|
||||
|
||||
// this.adminCreateProductPrice = async function (productStripeId, price, productLocalId) {
|
||||
// let priceArgs = {
|
||||
// unit_amount: convertToCents(price),
|
||||
// currency: this.currency,
|
||||
// product: productStripeId,
|
||||
// };
|
||||
// let priceCreated = await stripe.createPrice(priceArgs);
|
||||
|
||||
// let productModelEditEntry = {
|
||||
// price,
|
||||
// price_stripe_id: priceCreated.id,
|
||||
// };
|
||||
// let modelEditedProduct = await stripeProductsModel.edit(productModelEditEntry, productLocalId);
|
||||
// if (!modelEditedProduct) {
|
||||
// throw new Error('Internal Error: error setting price');
|
||||
// }
|
||||
// return priceCreated;
|
||||
// };
|
||||
|
||||
this._createPaypalProduct = async function (params) {
|
||||
let productParams = {
|
||||
name: params.name,
|
||||
description: params.description,
|
||||
type: params.type,
|
||||
category: params.category,
|
||||
image_url: params.image,
|
||||
home_url: params.url,
|
||||
};
|
||||
return await paypal.createProduct(productParams);
|
||||
};
|
||||
this.adminUpdateProduct = async function (category, paypalId) {
|
||||
await paypal.setAccessToken();
|
||||
let updateParams = [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/category',
|
||||
value: category,
|
||||
},
|
||||
];
|
||||
await paypal.updateProduct(updateParams, paypalId);
|
||||
return true;
|
||||
};
|
||||
|
||||
this.adminCreateProduct = async function (params, type) {
|
||||
if (!params.name) {
|
||||
throw new Error('Must provide a name for your product');
|
||||
}
|
||||
if (!params.type) {
|
||||
throw new Error('Must provide a type for your product');
|
||||
}
|
||||
|
||||
await paypal.setAccessToken();
|
||||
|
||||
let createdPaypalProduct = await this._createPaypalProduct(params);
|
||||
|
||||
let paypalProductModelEntry = {
|
||||
name: createdPaypalProduct.data.name,
|
||||
paypal_id: createdPaypalProduct.data.id,
|
||||
image: createdPaypalProduct.data.image_url ?? '',
|
||||
type: createdPaypalProduct.data.type ?? '',
|
||||
url: createdPaypalProduct.data.home_url ?? '',
|
||||
category: createdPaypalProduct.data.category ?? '',
|
||||
description: createdPaypalProduct.data.description ?? '',
|
||||
status: params.status,
|
||||
};
|
||||
|
||||
switch (params.type) {
|
||||
case 'SERVICE': {
|
||||
var modelCreatedProductId = await paypalServicesModel.insert(paypalProductModelEntry);
|
||||
var product = await paypalServicesModel.getByPK(modelCreatedProductId);
|
||||
break;
|
||||
}
|
||||
case 'PHYSICAL':
|
||||
case 'DIGITAL': {
|
||||
var modelCreatedProductId = await paypalProductsModel.insert(paypalProductModelEntry);
|
||||
var product = await paypalProductsModel.getByPK(modelCreatedProductId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!modelCreatedProductId) {
|
||||
throw new Error('Internal Error: Error adding product to our system');
|
||||
}
|
||||
|
||||
return {
|
||||
productPaypalId: product.paypal_id,
|
||||
productLocalId: product.id,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* [adminCreateRegularPlan allows an admin to create subscription plan through admin portal]
|
||||
* @param {object} planParams [object that contain plan parameters]
|
||||
* @param {integer} planType [integer that represent plan type in relative to stripe plans table] @see /models/stripe_plans.js type_mapping()
|
||||
* @param {integer} localProductId [integer that represent product id in relative to stripe products table]
|
||||
* @return {integer} [the id of the created plan in database]
|
||||
*/
|
||||
this.adminCreateRegularPlan = async function (params) {
|
||||
if (!params.name) {
|
||||
throw new Error('Must have a display name');
|
||||
}
|
||||
if (!params.type) {
|
||||
throw new Error('Must provide plan type');
|
||||
}
|
||||
if (!params.service_id) {
|
||||
throw new Error('Must attach a service');
|
||||
}
|
||||
await paypal.setAccessToken();
|
||||
let service = await paypalServicesModel.getByPK(params.service_id);
|
||||
|
||||
let paypalSubscriptionPlanParams = {
|
||||
product_id: service.paypal_id,
|
||||
name: params.name,
|
||||
billing_cycles: [
|
||||
{
|
||||
frequency: {
|
||||
interval_unit: paypalPlansModel.interval_unit_mapping()[params.interval_unit].toUpperCase(),
|
||||
interval_count: params.interval_count,
|
||||
},
|
||||
tenure_type: 'REGULAR',
|
||||
sequence: 1,
|
||||
total_cycles: 0,
|
||||
pricing_scheme: {
|
||||
fixed_price: {
|
||||
value: params.pricing,
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
payment_preferences: {
|
||||
auto_bill_outstanding: true,
|
||||
setup_fee_failure_action: 'CANCEL',
|
||||
payment_failure_threshold: 0,
|
||||
},
|
||||
};
|
||||
if (params.trial_period_days > 0) {
|
||||
paypalSubscriptionPlanParams.billing_cycles[0].sequence = 2;
|
||||
paypalSubscriptionPlanParams.billing_cycles.unshift({
|
||||
frequency: {
|
||||
interval_unit: 'DAY',
|
||||
interval_count: parseInt(params.trial_period_days),
|
||||
},
|
||||
tenure_type: 'TRIAL',
|
||||
sequence: 1,
|
||||
total_cycles: 1,
|
||||
});
|
||||
}
|
||||
|
||||
let paypalPlanCreated = await paypal.createPlan(paypalSubscriptionPlanParams);
|
||||
|
||||
let hasTrial = paypalPlanCreated.data.billing_cycles[1] ? true : false;
|
||||
let trialPlanInfo = hasTrial ? paypalPlanCreated.data.billing_cycles[0] : null;
|
||||
let regularPlanInfo = hasTrial ? paypalPlanCreated.data.billing_cycles[1] : paypalPlanCreated.data.billing_cycles[0];
|
||||
let price = regularPlanInfo.pricing_scheme.fixed_price.value;
|
||||
let interval_unit = regularPlanInfo.frequency.interval_unit;
|
||||
let interval_count = regularPlanInfo.frequency.interval_count;
|
||||
|
||||
let planModelEntry = {
|
||||
name: paypalPlanCreated.data.name,
|
||||
type: params.type,
|
||||
paypal_id: paypalPlanCreated.data.id,
|
||||
description: paypalPlanCreated.data.description,
|
||||
paypal_product_id: paypalPlanCreated.data.product_id,
|
||||
pricing: price,
|
||||
interval_unit: this.getMappingKey(paypalPlansModel.interval_unit_mapping, interval_unit.toLowerCase()),
|
||||
interval_count: interval_count,
|
||||
service_id: params.service_id,
|
||||
trial_period_days: trialPlanInfo ? trialPlanInfo.frequency.interval_count : 0,
|
||||
status: this.getMappingKey(paypalPlansModel.status_mapping, paypalPlanCreated.data.status),
|
||||
};
|
||||
|
||||
let modelCreatedPlan = await paypalPlansModel.insert(planModelEntry);
|
||||
|
||||
if (!modelCreatedPlan) {
|
||||
throw new Error('Internal error: error creating plan');
|
||||
}
|
||||
return modelCreatedPlan;
|
||||
};
|
||||
|
||||
this.updateRegularPlan = async function (params, currentSettings) {
|
||||
//if plan has a trial
|
||||
await paypal.setAccessToken();
|
||||
let planPaypalId = currentSettings.paypal_id;
|
||||
|
||||
if (currentSettings.status == 0 && params.status == 1) {
|
||||
await paypal.activatePlan(planPaypalId);
|
||||
} else if (currentSettings.status == 1 && params.status == 0) {
|
||||
await this._destroyPaypalPlan(planPaypalId);
|
||||
return { status: 0 };
|
||||
} else if (currentSettings.status == 0 && params.status == 0) {
|
||||
throw new Error("Can't edit a plan if it is inactive");
|
||||
}
|
||||
|
||||
let regularPlanIndex = currentSettings.trial_period_days ? 2 : 1;
|
||||
|
||||
let updateParams = [
|
||||
{
|
||||
op: 'replace',
|
||||
path: '/name',
|
||||
value: params.name,
|
||||
},
|
||||
];
|
||||
|
||||
await paypal.updatePlan(updateParams, planPaypalId);
|
||||
|
||||
updateParams = {
|
||||
pricing_schemes: [
|
||||
{
|
||||
billing_cycle_sequence: regularPlanIndex,
|
||||
pricing_scheme: {
|
||||
fixed_price: {
|
||||
value: params.pricing,
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await paypal.updatePlanPricing(updateParams, planPaypalId);
|
||||
|
||||
return { name: params.name, pricing: params.pricing, status: params.status };
|
||||
};
|
||||
|
||||
this.adminCreateLifetimePlan = async function (params) {
|
||||
//paypal doesn't allow lifetime plan
|
||||
//how it works is i create a blan with a trial with 999 years and setup fee for subscription as the main amount for the lifetime plan
|
||||
//paypal needs to have a regular plan so after the 999 years there is a regular plan with 0.01 amount money
|
||||
//this works for both lifetime paid and free plans.
|
||||
|
||||
if (!params.name) {
|
||||
throw new Error('Must have a display name');
|
||||
}
|
||||
if (!params.type) {
|
||||
throw new Error('Must provide plan type');
|
||||
}
|
||||
if (!params.service_id) {
|
||||
throw new Error('Must attach a service');
|
||||
}
|
||||
if (parseInt(params.interval_unit) !== 4) {
|
||||
throw new Error('Interval must be "forever".');
|
||||
}
|
||||
|
||||
let service = await paypalServicesModel.getByPK(params.service_id);
|
||||
|
||||
await paypal.setAccessToken();
|
||||
|
||||
let planParams = {
|
||||
product_id: service.paypal_id,
|
||||
name: params.name,
|
||||
billing_cycles: [
|
||||
{
|
||||
frequency: {
|
||||
interval_unit: 'YEAR',
|
||||
interval_count: 1,
|
||||
},
|
||||
tenure_type: 'TRIAL',
|
||||
sequence: 1,
|
||||
total_cycles: 999,
|
||||
},
|
||||
{
|
||||
frequency: {
|
||||
interval_unit: 'YEAR',
|
||||
interval_count: 1,
|
||||
},
|
||||
tenure_type: 'REGULAR',
|
||||
sequence: 2,
|
||||
total_cycles: 0,
|
||||
pricing_scheme: {
|
||||
fixed_price: {
|
||||
value: '0.01',
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
payment_preferences: {
|
||||
auto_bill_outstanding: true,
|
||||
setup_fee: {
|
||||
value: params.pricing,
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
setup_fee_failure_action: 'CANCEL',
|
||||
payment_failure_threshold: 0,
|
||||
},
|
||||
};
|
||||
let paypalPlanCreated = await paypal.createPlan(planParams);
|
||||
|
||||
let price = paypalPlanCreated.data.payment_preferences.setup_fee.value;
|
||||
|
||||
let planModelEntry = {
|
||||
name: paypalPlanCreated.data.name,
|
||||
type: params.type,
|
||||
paypal_id: paypalPlanCreated.data.id,
|
||||
description: paypalPlanCreated.data.description,
|
||||
paypal_product_id: paypalPlanCreated.data.product_id,
|
||||
pricing: price,
|
||||
interval_unit: 4,
|
||||
interval_count: null,
|
||||
service_id: params.service_id,
|
||||
trial_period_days: null,
|
||||
status: this.getMappingKey(paypalPlansModel.status_mapping, paypalPlanCreated.data.status),
|
||||
};
|
||||
|
||||
let modelCreatedPlan = await paypalPlansModel.insert(planModelEntry);
|
||||
|
||||
if (!modelCreatedPlan) {
|
||||
throw new Error('Internal error: error creating plan');
|
||||
}
|
||||
return modelCreatedPlan;
|
||||
};
|
||||
|
||||
this.adminCreateTrialPlan = async function (params) {
|
||||
if (!params.name) {
|
||||
throw new Error('Must have a display name');
|
||||
}
|
||||
if (!params.type) {
|
||||
throw new Error('Must provide plan type');
|
||||
}
|
||||
if (!params.service_id) {
|
||||
throw new Error('Must attach a service');
|
||||
}
|
||||
if (parseInt(params.interval_unit) === 4) {
|
||||
throw new Error('Interval can\'t be "forever".');
|
||||
}
|
||||
let service = await paypalServicesModel.getByPK(params.service_id);
|
||||
|
||||
await paypal.setAccessToken();
|
||||
|
||||
let planParams = {
|
||||
product_id: service.paypal_id,
|
||||
name: params.name,
|
||||
billing_cycles: [
|
||||
// {
|
||||
// frequency: {
|
||||
// interval_unit: 'YEAR',
|
||||
// interval_count: 1,
|
||||
// },
|
||||
// tenure_type: 'TRIAL',
|
||||
// sequence: 1,
|
||||
// total_cycles: 999,
|
||||
// },
|
||||
{
|
||||
frequency: {
|
||||
interval_unit: paypalPlansModel.interval_unit_mapping()[params.interval_unit].toUpperCase(),
|
||||
interval_count: params.interval_count,
|
||||
},
|
||||
tenure_type: 'REGULAR',
|
||||
sequence: 1,
|
||||
total_cycles: 1,
|
||||
pricing_scheme: {
|
||||
fixed_price: {
|
||||
value: params.pricing,
|
||||
currency_code: this.currency.toUpperCase(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
payment_preferences: {
|
||||
auto_bill_outstanding: true,
|
||||
// setup_fee: {
|
||||
// value: params.pricing,
|
||||
// currency_code: this.currency.toUpperCase(),
|
||||
// },
|
||||
setup_fee_failure_action: 'CANCEL',
|
||||
payment_failure_threshold: 0,
|
||||
},
|
||||
};
|
||||
let paypalPlanCreated = await paypal.createPlan(planParams);
|
||||
|
||||
let planInfo = paypalPlanCreated.data.billing_cycles[0];
|
||||
let price = planInfo.pricing_scheme.fixed_price.value;
|
||||
let interval_unit = planInfo.frequency.interval_unit;
|
||||
let interval_count = planInfo.frequency.interval_count;
|
||||
|
||||
let planModelEntry = {
|
||||
name: paypalPlanCreated.data.name,
|
||||
type: params.type,
|
||||
paypal_id: paypalPlanCreated.data.id,
|
||||
description: paypalPlanCreated.data.description,
|
||||
paypal_product_id: paypalPlanCreated.data.product_id,
|
||||
pricing: price,
|
||||
interval_unit: this.getMappingKey(paypalPlansModel.interval_unit_mapping, interval_unit.toLowerCase()),
|
||||
interval_count: interval_count,
|
||||
service_id: params.service_id,
|
||||
trial_period_days: 0,
|
||||
status: this.getMappingKey(paypalPlansModel.status_mapping, paypalPlanCreated.data.status),
|
||||
};
|
||||
|
||||
let modelCreatedPlan = await paypalPlansModel.insert(planModelEntry);
|
||||
|
||||
if (!modelCreatedPlan) {
|
||||
throw new Error('Internal error: error creating plan');
|
||||
}
|
||||
return modelCreatedPlan;
|
||||
};
|
||||
|
||||
this._destroyStripeLifetimePlan = async function (planStripeId) {
|
||||
//stripe doesn't allow deleting a price. will be set to unactive instead
|
||||
return await stripe.updatePrice({ priceId: planStripeId, params: { active: false } });
|
||||
};
|
||||
this._destroyPaypalPlan = async function (planPaypalId) {
|
||||
//paypal doesn't allow deleting a price. will be set to unactive instead
|
||||
return await paypal.deactivatePlan(planPaypalId);
|
||||
};
|
||||
|
||||
// this.adminCancelSubscription = async function (subscriptionId) {
|
||||
// let subscriptionToCancel = await stripeSubscriptionsModel.getByPK(subscriptionId);
|
||||
// let subscriptionPlan = await paypalPlansModel.getByPK(subscriptionToCancel.plan_id);
|
||||
// if (!subscriptionToCancel || !subscriptionPlan) {
|
||||
// throw new Error('No subscription or plan of that id');
|
||||
// }
|
||||
// // let subscirptionType = subscriptionPlan.type;
|
||||
// let subscirptionType = 3;
|
||||
// //if normal subscription
|
||||
// switch (subscirptionType) {
|
||||
// case 0:
|
||||
// case 3:
|
||||
// case 4:
|
||||
// let cancelSubscriptionParams = {
|
||||
// subscriptionId: subscriptionToCancel.stripe_id,
|
||||
// params: {
|
||||
// prorate: this.prorate,
|
||||
// invoice_now: true,
|
||||
// },
|
||||
// };
|
||||
// let subscriptionCanceled = await stripe.cancelSubscription(cancelSubscriptionParams);
|
||||
// let localCancellationParams = {
|
||||
// status: stripeSubscriptionsModel.inverse_status_mapping()[subscriptionCanceled.status],
|
||||
// };
|
||||
// await stripeSubscriptionsModel.edit(localCancellationParams, subscriptionId);
|
||||
// return subscriptionCanceled;
|
||||
// default:
|
||||
// throw new Error('Something wrong');
|
||||
// }
|
||||
// };
|
||||
// this.userAddCard = async function (cardParams) {
|
||||
// let createdCard = await stripe.createCard(cardParams);
|
||||
|
||||
// if (createdCard) {
|
||||
// if (createdCard.metadata.is_default == '1') {
|
||||
// let customerUpdateParams = {
|
||||
// customerId: createdCard.customer,
|
||||
// params: { default_source: createdCard.id },
|
||||
// };
|
||||
// await stripe.updateCustomer(customerUpdateParams);
|
||||
// let defaultCards = await stripeCardsModel.getAll({ is_default: 1 });
|
||||
// if (defaultCards.length > 0) {
|
||||
// defaultCards.forEach(async (card) => {
|
||||
// await stripeCardsModel.edit({ is_default: 0 }, card.id);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// cardModelEntry = {
|
||||
// card_last: createdCard.last4 ?? '',
|
||||
// card_brand: createdCard.brand ?? '',
|
||||
// card_exp_month: createdCard.exp_month ?? '',
|
||||
// exp_year: createdCard.exp_year ?? '',
|
||||
// card_name: createdCard.metadata.card_name ?? '',
|
||||
// stripe_card_customer: createdCard.customer,
|
||||
// stripe_card_id: createdCard.id,
|
||||
// is_default: createdCard.metadata.is_default,
|
||||
// user_id: this.userId ?? null,
|
||||
// role_id: this.roleId ?? null,
|
||||
// };
|
||||
// var modelCreatedCard = await stripeCardsModel.insert(cardModelEntry);
|
||||
// if (!modelCreatedCard) {
|
||||
// throw new Error('Internal error: Error adding card');
|
||||
// }
|
||||
// }
|
||||
// return modelCreatedCard;
|
||||
// };
|
||||
// this.changePlan = async function (currentSubscription, newPlan, user, card, couponId = '') {
|
||||
// //if current subscriptions is canceled
|
||||
// if (!currentSubscription || currentSubscription.status == 5) {
|
||||
// let subscriptionParams = {
|
||||
// customer: user.stripe_id,
|
||||
// items: { plan: newPlan.id },
|
||||
// coupon: couponId,
|
||||
// };
|
||||
// return await this.subscribe(subscriptionParams, newPlan);
|
||||
// }
|
||||
// //user has subscription (upgrade/ downgrade)
|
||||
// if (currentSubscription && currentSubscription.status != 5) {
|
||||
// if (currentSubscription.plan_id == newPlan.id) throw new Error('Same Plan');
|
||||
// }
|
||||
// };
|
||||
this.getMappingKey = function (mappingFunction, value) {
|
||||
return Object.keys(mappingFunction()).find((key) => mappingFunction()[key] === value);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
verifyPermission: function (role, roleName) {
|
||||
return function (req, res, next) {
|
||||
const permissions = req.session.permissions || [];
|
||||
|
||||
const isAllowedPermission = permissions.find(
|
||||
(permission) => `/${roleName}${permission}` === req.originalUrl,
|
||||
);
|
||||
|
||||
if (isAllowedPermission) {
|
||||
return next();
|
||||
} else {
|
||||
return res.redirect(`/${roleName}/dashboard`);
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports = function (req, res, next) {
|
||||
res.setHeader("X-Powered-By", "Manaknightdigital Inc.");
|
||||
next();
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
const db = require('../models');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Get user profile from user and credential table
|
||||
* @name profileService.get_profile
|
||||
* @param {string} user_id user id
|
||||
* @returns {Promise.<{email: string, password: string, first_name: string, last_name: string}>} profile fields
|
||||
*/
|
||||
async get_profile(user_id) {
|
||||
try {
|
||||
const { email, password } = await db.credential.getByField({ user_id });
|
||||
const { first_name, last_name } = await db.user.getByPK(user_id);
|
||||
|
||||
return {
|
||||
email,
|
||||
password,
|
||||
first_name,
|
||||
last_name,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Edit user profile update user and credential tables
|
||||
* @param {string} user_id user id
|
||||
* @param {{email: string, password: string, first_name: string, last_name: string}} profile user profile
|
||||
* @returns {Promise.<{email: string, password: string, first_name: string, last_name: string}>} updated profile fields
|
||||
*/
|
||||
async edit_profile(user_id, profile) {
|
||||
try {
|
||||
await db.credential.editByField(
|
||||
{ email: profile.email, password: profile.password },
|
||||
{ user_id },
|
||||
);
|
||||
await db.user.edit(
|
||||
{ first_name: profile.first_name, last_name: profile.last_name },
|
||||
user_id,
|
||||
);
|
||||
|
||||
return {
|
||||
email,
|
||||
password,
|
||||
first_name,
|
||||
last_name,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
const admin = require('firebase-admin');
|
||||
|
||||
class PushNotification {
|
||||
/**
|
||||
* Initialize firebase admin
|
||||
* {@link https://firebase.google.com/docs/admin/setup#initialize-sdk Initialize the SDK}.
|
||||
*/
|
||||
constructor() {
|
||||
this.admin = admin.initializeApp({
|
||||
credential: admin.credential.applicationDefault(),
|
||||
});
|
||||
|
||||
this.messaging = this.admin.messaging();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate payload
|
||||
* @param {{body: string, title: string, image_url: string}} payload
|
||||
* @param {admin.messaging.MessagingPayload} override override default payload
|
||||
*/
|
||||
generatePayload(payload, override) {
|
||||
return {
|
||||
notification: {
|
||||
body: payload.body,
|
||||
title: payload.title,
|
||||
},
|
||||
data: payload,
|
||||
apns: {
|
||||
payload: {
|
||||
aps: {
|
||||
'mutable-content': 1,
|
||||
},
|
||||
},
|
||||
fcm_options: {
|
||||
image: payload.image_url,
|
||||
},
|
||||
},
|
||||
android: {
|
||||
notification: {
|
||||
image: payload.image_url,
|
||||
},
|
||||
},
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate options
|
||||
* @param {admin.messaging.MessagingOptions} override override default options
|
||||
*/
|
||||
generateOptions(override) {
|
||||
return {
|
||||
contentAvailable: true,
|
||||
priority: 'high',
|
||||
timeToLive: 60 * 60 * 24,
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send push notification
|
||||
* @param {string|string[]} deviceToken
|
||||
* @param {admin.messaging.MessagingPayload} payload
|
||||
* @param {admin.messaging.MessagingOptions} options
|
||||
* @returns {Promise.<admin.messaging.MessagingDevicesResponse>}
|
||||
* @example
|
||||
* const pushNotification = new PushNotification();
|
||||
* const payload = pushNotification.generatePayload({}) // {} = custom configuration
|
||||
* const options = pushNotification.generateOptions({}); // {} = custom configuration
|
||||
*
|
||||
* pushNotification
|
||||
* .sendPushNotification('device_token', payload, options)
|
||||
* .then((response) => {
|
||||
* console.log('message send successfully');
|
||||
* })
|
||||
* .catch((error) => {
|
||||
* console.log('error occurred');
|
||||
* });
|
||||
*/
|
||||
sendPushNotification(deviceToken, payload, options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.messaging
|
||||
.sendToDevice(deviceToken, payload, options)
|
||||
.then((response) => resolve(response))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PushNotification;
|
||||
@@ -0,0 +1,16 @@
|
||||
const QRCode = require('qrcode');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Generate QRCode
|
||||
* @param {string} text QRCode string
|
||||
*/
|
||||
generateQRCode: async function (text) {
|
||||
return new Promise((resolve, reject) => {
|
||||
QRCode.toDataURL(text, (error, url) => {
|
||||
if (error) reject(error);
|
||||
else resolve(url);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,156 @@
|
||||
require('dotenv').config();
|
||||
const AWS = require('aws-sdk');
|
||||
|
||||
process.env.DYNAMIC_CONFIG_AWS_REGION = 'ap-south-1';
|
||||
process.env.DYNAMIC_CONFIG_AWS_KEY = 'AKIA5RTSM74BBOL52JEA';
|
||||
process.env.DYNAMIC_CONFIG_AWS_SECRET =
|
||||
'27WXBUpdHEcTfddkgqbcLjlfcU4c6EVwXsAPWfNR';
|
||||
process.env.DYNAMIC_CONFIG_AWS_BUCKET = 'sentry-test90426-dev';
|
||||
|
||||
class S3Service {
|
||||
/**
|
||||
* Initialize S3 service
|
||||
* @param {bucket} bucket override default aws bucket name
|
||||
*/
|
||||
constructor(bucket) {
|
||||
AWS.config.update({
|
||||
accessKeyId: process.env.DYNAMIC_CONFIG_AWS_KEY,
|
||||
secretAccessKey: process.env.DYNAMIC_CONFIG_AWS_SECRET,
|
||||
region: process.env.DYNAMIC_CONFIG_AWS_REGION,
|
||||
signatureVersion: 'v4',
|
||||
});
|
||||
|
||||
this.S3 = new AWS.S3();
|
||||
this.bucket = bucket || process.env.DYNAMIC_CONFIG_AWS_BUCKET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload an object to bucket
|
||||
* {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property s3.upload}
|
||||
* @param {string} key object key
|
||||
* @param {any} body object body
|
||||
* @param {AWS.S3.PutObjectRequest} options upload object params
|
||||
* @returns {Promise.<AWS.S3.Types.ManagedUpload.SendData>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
upload(key, body, options) {
|
||||
const params = {
|
||||
Bucket: this.bucket,
|
||||
Key: key,
|
||||
Body: body,
|
||||
...options,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.S3.upload(params, function (error, data) {
|
||||
if (error) reject(error);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload objects to bucket
|
||||
* {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property s3.upload}
|
||||
* @param {[{key: string, body: any}]} objects objects key and body
|
||||
* @param {AWS.S3.PutObjectRequest} options upload objects params
|
||||
* @returns {Promise.<[AWS.S3.Types.ManagedUpload.SendData]>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
batchUpload(objects, options) {
|
||||
const uploads = [];
|
||||
|
||||
objects.forEach((object) => {
|
||||
const singleUpload = this.upload(object.key, object.body, options);
|
||||
uploads.push(singleUpload);
|
||||
});
|
||||
|
||||
return Promise.all(uploads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object from bucket
|
||||
* {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getObject-property s3.getObject}
|
||||
* @param {string} key object key
|
||||
* @param {AWS.S3.GetObjectRequest} options get object params
|
||||
* @returns {Promise.<AWS.S3.Types.GetObjectOutput>}
|
||||
* @throws {AWS.S3.Error}
|
||||
*/
|
||||
get(key, options) {
|
||||
const params = {
|
||||
Bucket: this.bucket,
|
||||
Key: key,
|
||||
...options,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.S3.getObject(params, function (error, data) {
|
||||
if (error) reject(error);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get objects from bucket
|
||||
* {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getObject-property s3.getObject}
|
||||
* @param {[key: string]} keys object keys
|
||||
* @param {AWS.S3.GetObjectRequest} options get objects params
|
||||
* @returns {Promise.<[AWS.S3.Types.GetObjectOutput]>}
|
||||
* @throws {AWS.S3.Error}
|
||||
*/
|
||||
batchGet(keys, options) {
|
||||
const gets = [];
|
||||
|
||||
keys.forEach((key) => {
|
||||
const singleGet = this.get(key, options);
|
||||
gets.push(singleGet);
|
||||
});
|
||||
|
||||
return Promise.all(gets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete object from bucket
|
||||
* {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#deleteObject-property s3.getObject}
|
||||
* @param {string} key object key
|
||||
* @param {AWS.S3.DeleteObjectRequest} options delete object params
|
||||
* @returns {Promise.<AWS.S3.Types.DeleteObjectOutput>}
|
||||
* @throws {AWS.S3.Error}
|
||||
*/
|
||||
delete(key, options) {
|
||||
const params = {
|
||||
Bucket: this.bucket,
|
||||
Key: key,
|
||||
...options,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.S3.deleteObject(params, function (error, data) {
|
||||
if (error) reject(error);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete objects from bucket
|
||||
* {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#deleteObjects-property s3.getObjects}
|
||||
* @param {[string]} key objects key
|
||||
* @param {AWS.S3.DeleteObjectRequest} options delete objects params
|
||||
* @returns {Promise.<[AWS.S3.Types.DeleteObjectOutput]>}
|
||||
* @throws {AWS.S3.Error}
|
||||
*/
|
||||
batchDelete(keys, options) {
|
||||
const deletes = [];
|
||||
|
||||
keys.forEach((key) => {
|
||||
const singleDelete = this.delete(key, options);
|
||||
deletes.push(singleDelete);
|
||||
});
|
||||
|
||||
return Promise.all(deletes);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = S3Service;
|
||||
@@ -0,0 +1,61 @@
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
|
||||
/**
|
||||
* Session Service
|
||||
* @copyright 2020 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
verifySessionMiddleware: function (role, roleName = "") {
|
||||
return function (req, res, next) {
|
||||
const two_factor_authentication = req.session.two_factor_authentication;
|
||||
|
||||
const currentRole = req.session.role ? req.session.role : 0;
|
||||
if (currentRole === 0 || currentRole != role) {
|
||||
return res.redirect(`${roleName ? "/" + roleName : ""}/login?redirect_to=${req.originalUrl}`);
|
||||
} else if (two_factor_authentication) {
|
||||
return res.redirect(`/${roleName}/verify-account`);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
preventAuthRoutes: function (role, roleName) {
|
||||
return (req, res, next) => {
|
||||
const sessionRole = req.session.role;
|
||||
|
||||
if (sessionRole === role) {
|
||||
return res.redirect("/" + roleName + "/dashboard");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
prevent2FA: function (role, roleName) {
|
||||
return (req, res, next) => {
|
||||
const currentRole = req.session.role;
|
||||
const twoFA = req.session.two_factor_authentication;
|
||||
|
||||
if (currentRole !== role || !twoFA) {
|
||||
return res.redirect("/" + roleName + "/login");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
randomString: function (len) {
|
||||
const charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_+{}|":?><,./;[]';
|
||||
let randomString = "";
|
||||
for (let i = 0; i < len; i++) {
|
||||
let randomPoz = Math.floor(Math.random() * charSet.length);
|
||||
randomString += charSet.substring(randomPoz, randomPoz + 1);
|
||||
}
|
||||
return randomString;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
|
||||
/**
|
||||
* SMS Service
|
||||
* @copyright 2020 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
|
||||
const db = require('../models');
|
||||
|
||||
const accountSid = process.env.TWILIO_SID;
|
||||
const authToken = process.env.TWILIO_TOKEN;
|
||||
const phoneNumber = process.env.TWILIO_PHONE_NUMBER;
|
||||
const client = require('twilio')(accountSid, authToken);
|
||||
|
||||
module.exports = {
|
||||
from: phoneNumber,
|
||||
|
||||
template: function (slug) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
db.sms
|
||||
.findOne({ where: { slug } })
|
||||
.then((response) => {
|
||||
if (!response) {
|
||||
return reject(`TEMPLATE_NOT_FOUND`);
|
||||
} else resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
inject: function (template, payload) {
|
||||
let body = template.content;
|
||||
|
||||
for (const key in payload) {
|
||||
const element = payload[key];
|
||||
body = body.replace(new RegExp('{{{' + key + '}}}', 'g'), element);
|
||||
}
|
||||
|
||||
return body;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send SMS
|
||||
* @param {string} to
|
||||
* @param {string} body
|
||||
*/
|
||||
send: function (to, body) {
|
||||
let self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
client.messages
|
||||
.create({
|
||||
body,
|
||||
from: self.from,
|
||||
to,
|
||||
})
|
||||
.then((message) => resolve(message.sid))
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,561 @@
|
||||
const Stripe = require('stripe');
|
||||
module.exports = new Service();
|
||||
function Service() {
|
||||
this.apiSecretKey = process.env.STRIPE_SECRET_KEY;
|
||||
this.apiPublishKey = process.env.STRIPE_PUBLISH_KEY;
|
||||
this.stripe = Stripe(this.apiSecretKey);
|
||||
|
||||
// Products Functions
|
||||
this.createProduct = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'product_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.retrieveProduct = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'product_retrieve';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.listAllProducts = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'product_list_all';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.updateProduct = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'product_update';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
|
||||
// Cards Functions
|
||||
this.createCard = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'card_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
// Customers Functions
|
||||
this.createCustomer = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'customer_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.updateCustomer = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'customer_update';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.retrieveCustomer = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'customer_retrieve';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.listAllCustomers = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'customer_list_all';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
// Card Functions
|
||||
this.retrieveCard = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'card_retrieve';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
|
||||
// Prices Functions
|
||||
this.createPrice = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'price_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.updatePrice = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'price_update';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.retrievePrice = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'price_retrieve';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.listAllPrices = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'price_list_all';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
|
||||
// Charges Functions
|
||||
this.createCharge = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'charge_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.retrieveCharge = async function (params) {
|
||||
// args = params;
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'charge_retrieve';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
|
||||
//Invoices Functions
|
||||
this.createInvoice = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'invoice_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.payInvoice = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'invoice_pay';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.retrieveInvoice = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'invoice_retrieve';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.createInvoiceItem = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'invoice_item_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
|
||||
//subscriptions
|
||||
this.createSubscription = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'subscription_create';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.updateSubscription = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'subscription_update';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.retrieveSubscription = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'subscription_retrieve';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.cancelSubscription = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'subscription_cancel';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
this.listAllSubscriptions = async function (params) {
|
||||
args = this.filterParams(params);
|
||||
let stripeType = 'subscription_list_all';
|
||||
var result = await this.stripeType(stripeType, args);
|
||||
return result;
|
||||
};
|
||||
|
||||
this.stripeType = async function (type, args) {
|
||||
try {
|
||||
switch (type) {
|
||||
//Products
|
||||
case 'product_create':
|
||||
// @see https://stripe.com/docs/api/products/create?lang=node
|
||||
var result = await this.stripe.products.create(args);
|
||||
break;
|
||||
case 'product_delete':
|
||||
// @see https://stripe.com/docs/api/products/delete?lang=node
|
||||
var result = await this.stripe.products.del(args);
|
||||
break;
|
||||
case 'product_retrieve':
|
||||
// @see https://stripe.com/docs/api/products/retrieve?lang=node
|
||||
var result = await this.stripe.products.retrieve(args);
|
||||
break;
|
||||
case 'product_update':
|
||||
// @see https://stripe.com/docs/api/products/update?lang=node
|
||||
var result = await this.stripe.products.update(args.productId, args.params);
|
||||
break;
|
||||
case 'product_list_all':
|
||||
// @see https://stripe.com/docs/api/products/list?lang=node
|
||||
var result = await this.stripe.products.list(args);
|
||||
break;
|
||||
|
||||
//Prices
|
||||
case 'price_create':
|
||||
// @see https://stripe.com/docs/api/prices/create?lang=node
|
||||
var result = await this.stripe.prices.create(args);
|
||||
break;
|
||||
case 'price_retrieve':
|
||||
// @see https://stripe.com/docs/api/prices/retrieve?lang=node
|
||||
var result = await this.stripe.prices.retrieve(args);
|
||||
break;
|
||||
case 'price_update':
|
||||
// @see https://stripe.com/docs/api/prices/update?lang=node
|
||||
var result = await this.stripe.prices.update(args.priceId, args.params);
|
||||
break;
|
||||
case 'price_list_all':
|
||||
// @see https://stripe.com/docs/api/prices/list?lang=node
|
||||
var result = await this.stripe.prices.list(args);
|
||||
break;
|
||||
|
||||
//Charges
|
||||
case 'charge_create':
|
||||
// @see https://stripe.com/docs/api/charges/create?lang=node
|
||||
var result = await this.stripe.charges.create(args);
|
||||
break;
|
||||
case 'charge_retrieve':
|
||||
// @see https://stripe.com/docs/api/charges/retrieve?lang=node
|
||||
var result = await this.stripe.charges.retrieve(args);
|
||||
break;
|
||||
case 'charge_update':
|
||||
// @see https://stripe.com/docs/api/charges/update?lang=node
|
||||
var result = await this.stripe.charges.update(args.chargeId, args.params);
|
||||
break;
|
||||
case 'charge_capture':
|
||||
// @see https://stripe.com/docs/api/charges/capture?lang=node
|
||||
var result = await this.stripe.charges.capture(args);
|
||||
break;
|
||||
case 'charge_list_all':
|
||||
// @see https://stripe.com/docs/api/charges/list?lang=node
|
||||
var result = await this.stripe.charges.list(args);
|
||||
break;
|
||||
|
||||
//Customer
|
||||
case 'customer_create':
|
||||
// @see https://stripe.com/docs/api/customers/create?lang=node
|
||||
var result = await this.stripe.customers.create(args);
|
||||
break;
|
||||
case 'customer_retrieve':
|
||||
// @see https://stripe.com/docs/api/customers/retrieve?lang=node
|
||||
var result = await this.stripe.customers.retrieve(args);
|
||||
break;
|
||||
case 'customer_update':
|
||||
// @see https://stripe.com/docs/api/customers/update?lang=node
|
||||
var result = await this.stripe.customers.update(args.customerId, args.params);
|
||||
break;
|
||||
case 'customer_delete':
|
||||
// @see https://stripe.com/docs/api/customers/capture?lang=node
|
||||
var result = await this.stripe.customers.del(args);
|
||||
break;
|
||||
case 'customer_list_all':
|
||||
// @see https://stripe.com/docs/api/customers/list?lang=node
|
||||
var result = await this.stripe.customers.list(args);
|
||||
break;
|
||||
|
||||
//Refunds
|
||||
case 'refund_create':
|
||||
// @see https://stripe.com/docs/api/refunds/create?lang=node
|
||||
var result = await this.stripe.refunds.create(args);
|
||||
break;
|
||||
case 'refund_retrieve':
|
||||
// @see https://stripe.com/docs/api/refunds/retrieve?lang=node
|
||||
var result = await this.stripe.refunds.retrieve(args);
|
||||
break;
|
||||
case 'refund_update':
|
||||
// @see https://stripe.com/docs/api/refunds/update?lang=node
|
||||
var result = await this.stripe.refunds.update(args.refundId, args.params);
|
||||
break;
|
||||
case 'refund_list_all':
|
||||
// @see https://stripe.com/docs/api/refunds/list?lang=node
|
||||
var result = await this.stripe.refunds.list(args);
|
||||
break;
|
||||
|
||||
//Cards (Sources)
|
||||
case 'card_create':
|
||||
// @see https://stripe.com/docs/api/cards/create?lang=node
|
||||
var result = await this.stripe.customers.createSource(args.customerId, args.params);
|
||||
break;
|
||||
case 'card_retrieve':
|
||||
// @see https://stripe.com/docs/api/cards/retrieve?lang=node
|
||||
var result = await this.stripe.customers.retrieveSource(args.customerId, args.cardId);
|
||||
break;
|
||||
case 'card_update':
|
||||
// @see https://stripe.com/docs/api/cards/update?lang=node
|
||||
var result = await this.stripe.customers.updateSource(args.customerId, args.cardId, args.params);
|
||||
break;
|
||||
case 'card_delete':
|
||||
// @see https://stripe.com/docs/api/cards/delete?lang=node
|
||||
var result = await this.stripe.customers.deleteSource(args.customerId, args.cardId);
|
||||
break;
|
||||
case 'card_list_all':
|
||||
// @see https://stripe.com/docs/api/cards/list?lang=node
|
||||
var result = await this.stripe.customers.listSources(args.customerId, args.params);
|
||||
break;
|
||||
|
||||
//Payment Method
|
||||
case 'paymentMethod_create':
|
||||
// @see https://stripe.com/docs/api/payment_methods/create?lang=node
|
||||
var result = await this.stripe.paymentMethods.create(args);
|
||||
break;
|
||||
case 'paymentMethod_retrieve':
|
||||
// @see https://stripe.com/docs/api/payment_methods/retrieve?lang=node
|
||||
var result = await this.stripe.paymentMethods.retrieve(args);
|
||||
break;
|
||||
case 'paymentMethod_update':
|
||||
// @see https://stripe.com/docs/api/payment_methods/update?lang=node
|
||||
var result = await this.stripe.paymentMethods.update(args.paymentMethodId, args.params);
|
||||
break;
|
||||
case 'paymentMethod_attach':
|
||||
// @see https://stripe.com/docs/api/payment_methods/attach?lang=node
|
||||
var result = await this.stripe.paymentMethods.attach(args.paymentMethodId, args.params);
|
||||
break;
|
||||
case 'paymentMethod_detach':
|
||||
// @see https://stripe.com/docs/api/payment_methods/attach?lang=node
|
||||
var result = await this.stripe.paymentMethods.attach(args);
|
||||
break;
|
||||
case 'paymentMethod_list_all':
|
||||
// @see https://stripe.com/docs/api/payment_methods/list?lang=node
|
||||
var result = await this.stripe.paymentMethods.list(args);
|
||||
break;
|
||||
|
||||
//Session
|
||||
case 'session_create':
|
||||
// @see https://stripe.com/docs/api/sessions/create?lang=node
|
||||
var result = await this.stripe.checkout.sessions.create(args);
|
||||
break;
|
||||
case 'session_retrieve':
|
||||
// @see https://stripe.com/docs/api/sessions/retrieve?lang=node
|
||||
var result = await this.stripe.checkout.sessions.retrieve(args);
|
||||
break;
|
||||
case 'session_list_all_lineItems':
|
||||
// @see https://stripe.com/docs/api/sessions/line_items?lang=node
|
||||
var result = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.stripe.checkout.sessions.listLineItems(args.sessionId, args.params, function (err, lineItems) {
|
||||
if (err) return reject(err);
|
||||
return resolve(lineItems);
|
||||
});
|
||||
});
|
||||
};
|
||||
result = await result();
|
||||
break;
|
||||
case 'session_list_all':
|
||||
// @see https://stripe.com/docs/api/session/list?lang=node
|
||||
var result = await this.stripe.checkout.sessions.list(args);
|
||||
break;
|
||||
|
||||
//Coupon
|
||||
case 'coupon_create':
|
||||
// @see https://stripe.com/docs/api/coupons/create?lang=node
|
||||
var result = await this.stripe.coupons.create(args);
|
||||
break;
|
||||
case 'coupon_retrieve':
|
||||
// @see https://stripe.com/docs/api/coupons/retrieve?lang=node
|
||||
var result = await this.stripe.coupons.retrieve(args);
|
||||
break;
|
||||
case 'coupon_update':
|
||||
// @see https://stripe.com/docs/api/coupons/update?lang=node
|
||||
var result = await this.stripe.coupons.update(args.couponId, args.params);
|
||||
break;
|
||||
case 'coupon_delete':
|
||||
// @see https://stripe.com/docs/api/coupons/delete?lang=node
|
||||
var result = await this.stripe.coupons.del(args);
|
||||
break;
|
||||
case 'coupon_list_all':
|
||||
// @see https://stripe.com/docs/api/coupons/list?lang=node
|
||||
var result = await this.stripe.coupons.list(args);
|
||||
break;
|
||||
|
||||
//Invoice items
|
||||
case 'invoice_item_create':
|
||||
// @see https://stripe.com/docs/api/invoiceitems/create?lang=node
|
||||
var result = await this.stripe.invoiceItems.create(args);
|
||||
break;
|
||||
|
||||
//Invoice
|
||||
case 'invoice_create':
|
||||
// @see https://stripe.com/docs/api/invoices/create?lang=node
|
||||
var result = await this.stripe.invoices.create(args);
|
||||
break;
|
||||
case 'invoice_retrieve':
|
||||
// @see https://stripe.com/docs/api/invoices/retrieve?lang=node
|
||||
var result = await this.stripe.invoices.retrieve(args);
|
||||
break;
|
||||
case 'invoice_update':
|
||||
// @see https://stripe.com/docs/api/invoices/update?lang=node
|
||||
var result = await this.stripe.invoices.update(args.invoiceId, args.params);
|
||||
break;
|
||||
case 'invoice_delete':
|
||||
// @see https://stripe.com/docs/api/invoices/delete?lang=node
|
||||
var result = await this.stripe.invoices.del(args);
|
||||
break;
|
||||
case 'invoice_finalize':
|
||||
// @see https://stripe.com/docs/api/invoices/finalize?lang=node
|
||||
var result = await this.stripe.invoices.finalizeInvoice(args);
|
||||
break;
|
||||
case 'invoice_pay':
|
||||
// @see https://stripe.com/docs/api/invoices/pay?lang=node
|
||||
var result = await this.stripe.invoices.pay(args);
|
||||
break;
|
||||
case 'invoice_void':
|
||||
// @see https://stripe.com/docs/api/invoices/void?lang=node
|
||||
var result = await this.stripe.invoices.voidInvoice(args);
|
||||
break;
|
||||
case 'invoice_list_all':
|
||||
// @see https://stripe.com/docs/api/invoices/list?lang=node
|
||||
var result = await this.stripe.invoices.list(args);
|
||||
break;
|
||||
|
||||
//Subscription
|
||||
case 'subscription_create':
|
||||
// @see https://stripe.com/docs/api/subscriptions/create?lang=node
|
||||
var result = await this.stripe.subscriptions.create(args);
|
||||
break;
|
||||
case 'subscription_retrieve':
|
||||
// @see https://stripe.com/docs/api/subscriptions/retrieve?lang=node
|
||||
var result = await this.stripe.subscriptions.retrieve(args);
|
||||
break;
|
||||
case 'subscription_update':
|
||||
// @see https://stripe.com/docs/api/subscriptions/update?lang=node
|
||||
var result = await this.stripe.subscriptions.update(args.subscriptionId, args.params);
|
||||
break;
|
||||
case 'subscription_cancel':
|
||||
// @see https://stripe.com/docs/api/subscriptions/cancel?lang=node
|
||||
var result = await this.stripe.subscriptions.del(args.subscriptionId, args.params);
|
||||
break;
|
||||
case 'subscription_list_all':
|
||||
// @see https://stripe.com/docs/api/subscriptions/list?lang=node
|
||||
var result = await this.stripe.subscriptions.list(args);
|
||||
break;
|
||||
|
||||
//Subscription schedule
|
||||
case 'subscription_schedule_create':
|
||||
// @see https://stripe.com/docs/api/subscription_schedules/create?lang=node
|
||||
var result = await this.stripe.subscriptionSchedules.create(args);
|
||||
break;
|
||||
case 'subscription_schedule_retrieve':
|
||||
// @see https://stripe.com/docs/api/subscription_schedules/retrieve?lang=node
|
||||
var result = await this.stripe.subscriptionSchedules.retrieve(args);
|
||||
break;
|
||||
case 'subscription_schedule_update':
|
||||
// @see https://stripe.com/docs/api/subscription_schedules/update?lang=node
|
||||
var result = await this.stripe.subscriptionSchedules.update(args.subScheduleId, args.params);
|
||||
break;
|
||||
case 'subscription_schedule_cancel':
|
||||
// @see https://stripe.com/docs/api/subscription_schedules/cancel?lang=node
|
||||
var result = await this.stripe.subscriptionSchedules.cancel(args);
|
||||
break;
|
||||
case 'subscription_schedule_release':
|
||||
// @see https://stripe.com/docs/api/subscription_schedules/release?lang=node
|
||||
var result = await this.stripe.subscriptionSchedules.release(args);
|
||||
break;
|
||||
case 'subscription_schedule_list_all':
|
||||
// @see https://stripe.com/docs/api/subscription_schedules/list?lang=node
|
||||
var result = await this.stripe.subscriptionSchedules.list(args);
|
||||
break;
|
||||
|
||||
//Transaction
|
||||
case 'transaction_retrieve':
|
||||
// @see https://stripe.com/docs/api/issuing/transactions/retrieve?lang=node
|
||||
var result = await this.stripe.issuing.transactions.retrieve(args);
|
||||
break;
|
||||
case 'transaction_update':
|
||||
// @see https://stripe.com/docs/api/issuing/transactions/update?lang=node
|
||||
var result = await this.stripe.issuing.transactions.update(args.transactionId, args.params);
|
||||
break;
|
||||
case 'transaction_list_all':
|
||||
// @see https://stripe.com/docs/api/issuing/transactions/list?lang=node
|
||||
var result = await this.stripe.issuing.transactions.list(args);
|
||||
break;
|
||||
|
||||
//Order
|
||||
case 'order_create':
|
||||
// @see https://stripe.com/docs/api/orders/create?lang=node
|
||||
var result = await this.stripe.orders.create(args);
|
||||
break;
|
||||
case 'order_retrieve':
|
||||
// @see https://stripe.com/docs/api/orders/retrieve?lang=node
|
||||
var result = await this.stripe.orders.retrieve(args);
|
||||
break;
|
||||
case 'order_update':
|
||||
// @see https://stripe.com/docs/api/orders/update?lang=node
|
||||
var result = await this.stripe.orders.update(args.orderId, args.params);
|
||||
break;
|
||||
case 'order_pay':
|
||||
// @see https://stripe.com/docs/api/orders/pay?lang=node
|
||||
var result = await this.stripe.orders.pay(args.orderId, args.params);
|
||||
break;
|
||||
case 'order_list_all':
|
||||
// @see https://stripe.com/docs/api/orders/list?lang=node
|
||||
var result = await this.stripe.orders.list(args);
|
||||
break;
|
||||
case 'order_return':
|
||||
// @see https://stripe.com/docs/api/orders/return?lang=node
|
||||
var result = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.stripe.orders.returnOrder(args.orderId, args.params, function (err, order) {
|
||||
if (err) return reject(err);
|
||||
return resolve(order);
|
||||
});
|
||||
});
|
||||
};
|
||||
result = await result();
|
||||
break;
|
||||
|
||||
//Webhooks
|
||||
case 'webhook_create':
|
||||
// @see https://stripe.com/docs/api/webhook_endpoints/create?lang=node
|
||||
var result = await this.stripe.webhookEndpoints.create(args);
|
||||
break;
|
||||
case 'webhook_retrieve':
|
||||
// @see https://stripe.com/docs/api/webhook_endpoints/retrieve?lang=node
|
||||
var result = await this.stripe.webhookEndpoints.retrieve(args);
|
||||
break;
|
||||
case 'webhook_update':
|
||||
// @see https://stripe.com/docs/api/webhook_endpoints/update?lang=node
|
||||
var result = await this.stripe.webhookEndpoints.update(args.webhookId, args.params);
|
||||
break;
|
||||
case 'webhook_delete':
|
||||
// @see https://stripe.com/docs/api/webhook_endpoints/delete?lang=node
|
||||
var result = await this.stripe.webhookEndpoints.del(args);
|
||||
break;
|
||||
case 'webhook_list_all':
|
||||
// @see https://stripe.com/docs/api/webhook_endpoints/list?lang=node
|
||||
var result = await this.stripe.webhookEndpoints.list(args);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
// return { error: true, message: error.message, statusCode: error.statusCode, errorObject: error.raw };
|
||||
console.dir(error, { depth: null });
|
||||
throw new Error('Internal Error: error when doing stripe action');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [filterParams filters paramters from null, undefined, empty strings, empty arrays and empty objects as it can cause unwanted changes]
|
||||
* @param {object} params [object that can contain one more object inside]
|
||||
* @return {object} [object with only truth variables]
|
||||
*/
|
||||
this.filterParams = function (params) {
|
||||
Object.keys(params).forEach((param) => {
|
||||
if (this.empty(params[param]) || (Array.isArray(params[param]) && !params[param].length)) {
|
||||
console.log(`Parameter empty, null or undefined`);
|
||||
delete params[param];
|
||||
} else if (params[param].constructor === Object && Object.entries(params[param]).length === 0) {
|
||||
console.log(`Parameter object empty`);
|
||||
delete params[param];
|
||||
} else if (params[param].constructor === Object && Object.entries(params[param]).length > 0) {
|
||||
this.filterParams(params[param]);
|
||||
}
|
||||
});
|
||||
return params;
|
||||
};
|
||||
// this.empty = (value) => value === null || value === undefined || value === '';
|
||||
this.empty = (value) => value === null || value === undefined;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
const path = require('path');
|
||||
const aws = require('aws-sdk');
|
||||
const multer = require('multer');
|
||||
const multerS3 = require('multer-s3');
|
||||
const { createDirectoriesRecursiveV2 } = require('./../core/helpers');
|
||||
|
||||
module.exports = {
|
||||
upload: function (location) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return this.s3_upload(location);
|
||||
} else {
|
||||
return this.local_upload('');
|
||||
}
|
||||
},
|
||||
s3_upload: function (location) {
|
||||
try {
|
||||
aws.config.update({
|
||||
accessKeyId: process.env.DYNAMIC_CONFIG_AWS_KEY,
|
||||
secretAccessKey: process.env.DYNAMIC_CONFIG_AWS_SECRET,
|
||||
region: process.env.DYNAMIC_CONFIG_AWS_REGION,
|
||||
});
|
||||
|
||||
const s3 = new aws.S3();
|
||||
|
||||
const upload = multer({
|
||||
storage: multerS3({
|
||||
s3: s3,
|
||||
bucket: process.env.DYNAMIC_CONFIG_AWS_BUCKET,
|
||||
acl: 'public-read',
|
||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
||||
key: function (req, file, cb) {
|
||||
cb(null, location + file.originalname);
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
return upload;
|
||||
} catch (error) {
|
||||
console.log('s3_upload => ', error);
|
||||
}
|
||||
},
|
||||
local_upload: function () {
|
||||
try {
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
createDirectoriesRecursiveV2(path.join(__dirname, '..', 'public', 'uploads'));
|
||||
cb(null, path.join(__dirname, '..', 'public', 'uploads'));
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
const fileName = file.filename ?? file.originalname;
|
||||
cb(null, Date.now() + '-' + fileName);
|
||||
},
|
||||
});
|
||||
|
||||
const upload = multer({ storage: storage });
|
||||
|
||||
return upload;
|
||||
} catch (error) {
|
||||
console.log('local_upload => ', error);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
const { Validator, addCustomMessages } = require('node-input-validator');
|
||||
|
||||
// {fieldName:message} eg:{email:"Invalid Email", password:"Password too short"}
|
||||
const formatValidationError = (error) => {
|
||||
const formatted = Object.entries(error)
|
||||
.map(([key, value]) => ({
|
||||
field: key,
|
||||
message: value.message,
|
||||
}))
|
||||
.reduce((accumulator, currentValue) => {
|
||||
if (!accumulator[currentValue]) {
|
||||
accumulator[currentValue.field] = currentValue.message;
|
||||
}
|
||||
return accumulator;
|
||||
}, {});
|
||||
return formatted;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Input Validator middleware for controller
|
||||
* @param {object} validationObject object defining body fields and its validation types eg:{email:required|email}
|
||||
* @param {object} _extendMessages object defining message to throw on validation error eg: {"email.required":"Email is required","email.email":"Invalid email"}
|
||||
*
|
||||
*/
|
||||
validateInput: (validationObject = {}, _extendMessages = {}) => async (
|
||||
req,
|
||||
res,
|
||||
next,
|
||||
) => {
|
||||
const validation = new Validator(req.body, validationObject);
|
||||
addCustomMessages(_extendMessages);
|
||||
|
||||
try {
|
||||
const isValid = await validation.check();
|
||||
if (!isValid) {
|
||||
req.validationError = formatValidationError(validation.errors);
|
||||
}
|
||||
return next();
|
||||
} catch (error) {
|
||||
req.validationError = error.message;
|
||||
return next();
|
||||
}
|
||||
},
|
||||
|
||||
handleValidationErrorForViews: (
|
||||
req,
|
||||
res,
|
||||
viewModel,
|
||||
viewPath = '/',
|
||||
fieldsStoreKey,
|
||||
defaultValue = {},
|
||||
) => {
|
||||
const validationError = req.validationError;
|
||||
|
||||
if (validationError) {
|
||||
// Remembers fields if validation error occurs
|
||||
Object.entries(defaultValue).forEach(([key, value]) => {
|
||||
viewModel[fieldsStoreKey][key] = value;
|
||||
});
|
||||
|
||||
if (typeof validationError === 'string') {
|
||||
viewModel.error = validationError;
|
||||
} else {
|
||||
viewModel.validationError = req.validationError;
|
||||
}
|
||||
return res.render(viewPath, viewModel);
|
||||
}
|
||||
},
|
||||
|
||||
handleValidationErrorForAPI: (req, res, next) => {
|
||||
const validationError = req.validationError;
|
||||
|
||||
if (validationError) {
|
||||
let error;
|
||||
if (typeof validationError === 'string') {
|
||||
error = validationError;
|
||||
} else {
|
||||
error = req.validationError;
|
||||
}
|
||||
return res.json({ success: false, error });
|
||||
}
|
||||
next();
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,449 @@
|
||||
{
|
||||
"categories": {
|
||||
"ACADEMIC_SOFTWARE": "Academic Software",
|
||||
"ACCESSORIES": "Accessories",
|
||||
"ACCOUNTING": "Accounting",
|
||||
"ADULT": "Adult",
|
||||
"ADVERTISING": "Advertising",
|
||||
"AFFILIATED_AUTO_RENTAL": "Affiliated Auto Rental",
|
||||
"AGENCIES": "Agencies",
|
||||
"AGGREGATORS": "Aggregators",
|
||||
"AGRICULTURAL_COOPERATIVE_FOR_MAIL_ORDER": "Agricultural Cooperative for Mail Order",
|
||||
"AIR_CARRIERS_AIRLINES": "Air Carriers, Airlines",
|
||||
"AIRLINES": "Airlines",
|
||||
"AIRPORTS_FLYING_FIELDS": "Airports, Flying Fields",
|
||||
"ALCOHOLIC_BEVERAGES": "Alcoholic Beverages",
|
||||
"AMUSEMENT_PARKS_CARNIVALS": "Amusement Parks/Carnivals",
|
||||
"ANIMATION": "Animation",
|
||||
"ANTIQUES": "Antiques",
|
||||
"APPLIANCES": "Appliances",
|
||||
"AQUARIAMS_SEAQUARIUMS_DOLPHINARIUMS": "Aquariams Seaquariums Dolphinariums",
|
||||
"ARCHITECTURAL_ENGINEERING_AND_SURVEYING_SERVICES": "Architectural,Engineering,And Surveying Services",
|
||||
"ART_AND_CRAFT_SUPPLIES": "Art & Craft Supplies",
|
||||
"ART_DEALERS_AND_GALLERIES": "Art dealers and galleries",
|
||||
"ARTIFACTS_GRAVE_RELATED_AND_NATIVE_AMERICAN_CRAFTS": "Artifacts, Grave related, and Native American Crafts",
|
||||
"ARTS_AND_CRAFTS": "Arts and crafts",
|
||||
"ARTS_CRAFTS_AND_COLLECTIBLES": "Arts, crafts, and collectibles",
|
||||
"AUDIO_BOOKS": "Audio books",
|
||||
"AUTO_ASSOCIATIONS_CLUBS": "Auto Associations/Clubs",
|
||||
"AUTO_DEALER_USED_ONLY": "Auto dealer - used only",
|
||||
"AUTO_RENTALS": "Auto Rentals",
|
||||
"AUTO_SERVICE": "Auto service",
|
||||
"AUTOMATED_FUEL_DISPENSERS": "Automated Fuel Dispensers",
|
||||
"AUTOMOBILE_ASSOCIATIONS": "Automobile Associations",
|
||||
"AUTOMOTIVE": "Automotive",
|
||||
"AUTOMOTIVE_REPAIR_SHOPS_NON_DEALER": "Automotive Repair Shops - Non-Dealer",
|
||||
"AUTOMOTIVE_TOP_AND_BODY_SHOPS": "Automotive Top And Body Shops",
|
||||
"AVIATION": "Aviation",
|
||||
"BABIES_CLOTHING_AND_SUPPLIES": "Babies Clothing & Supplies",
|
||||
"BABY": "Baby",
|
||||
"BANDS_ORCHESTRAS_ENTERTAINERS": "Bands,Orchestras,Entertainers",
|
||||
"BARBIES": "Barbies",
|
||||
"BATH_AND_BODY": "Bath and body",
|
||||
"BATTERIES": "Batteries",
|
||||
"BEAN_BABIES": "Bean Babies",
|
||||
"BEAUTY": "Beauty",
|
||||
"BEAUTY_AND_FRAGRANCES": "Beauty and fragrances",
|
||||
"BED_AND_BATH": "Bed & Bath",
|
||||
"BICYCLE_SHOPS_SALES_AND_SERVICE": "Bicycle Shops-Sales And Service",
|
||||
"BICYCLES_AND_ACCESSORIES": "Bicycles & Accessories",
|
||||
"BILLIARD_POOL_ESTABLISHMENTS": "Billiard/Pool Establishments",
|
||||
"BOAT_DEALERS": "Boat Dealers",
|
||||
"BOAT_RENTALS_AND_LEASING": "Boat Rentals And Leasing",
|
||||
"BOATING_SAILING_AND_ACCESSORIES": "Boating, sailing and accessories",
|
||||
"BOOKS": "Books",
|
||||
"BOOKS_AND_MAGAZINES": "Books and magazines",
|
||||
"BOOKS_MANUSCRIPTS": "Books, Manuscripts",
|
||||
"BOOKS_PERIODICALS_AND_NEWSPAPERS": "Books, Periodicals And Newspapers",
|
||||
"BOWLING_ALLEYS": "Bowling Alleys",
|
||||
"BULLETIN_BOARD": "Bulletin board",
|
||||
"BUS_LINE": "Bus line",
|
||||
"BUS_LINES_CHARTERS_TOUR_BUSES": "Bus Lines,Charters,Tour Buses",
|
||||
"BUSINESS": "Business",
|
||||
"BUSINESS_AND_SECRETARIAL_SCHOOLS": "Business and secretarial schools",
|
||||
"BUYING_AND_SHOPPING_SERVICES_AND_CLUBS": "Buying And Shopping Services And Clubs",
|
||||
"CABLE_SATELLITE_AND_OTHER_PAY_TELEVISION_AND_RADIO_SERVICES": "Cable,Satellite,And Other Pay Television And Radio Services",
|
||||
"CABLE_SATELLITE_AND_OTHER_PAY_TV_AND_RADIO": "Cable, satellite, and other pay TV and radio",
|
||||
"CAMERA_AND_PHOTOGRAPHIC_SUPPLIES": "Camera and photographic supplies",
|
||||
"CAMERAS": "Cameras",
|
||||
"CAMERAS_AND_PHOTOGRAPHY": "Cameras & Photography",
|
||||
"CAMPER_RECREATIONAL_AND_UTILITY_TRAILER_DEALERS": "Camper,Recreational And Utility Trailer Dealers",
|
||||
"CAMPING_AND_OUTDOORS": "Camping and outdoors",
|
||||
"CAMPING_AND_SURVIVAL": "Camping & Survival",
|
||||
"CAR_AND_TRUCK_DEALERS": "Car And Truck Dealers",
|
||||
"CAR_AND_TRUCK_DEALERS_USED_ONLY": "Car And Truck Dealers - Used Only",
|
||||
"CAR_AUDIO_AND_ELECTRONICS": "Car Audio & Electronics",
|
||||
"CAR_RENTAL_AGENCY": "Car rental agency",
|
||||
"CATALOG_MERCHANT": "Catalog Merchant",
|
||||
"CATALOG_RETAIL_MERCHANT": "Catalog/Retail Merchant",
|
||||
"CATERING_SERVICES": "Catering services",
|
||||
"CHARITY": "Charity",
|
||||
"CHECK_CASHIER": "Check Cashier",
|
||||
"CHILD_CARE_SERVICES": "Child Care Services",
|
||||
"CHILDREN_BOOKS": "Children Books",
|
||||
"CHIROPODISTS_PODIATRISTS": "Chiropodists/Podiatrists",
|
||||
"CHIROPRACTORS": "Chiropractors",
|
||||
"CIGAR_STORES_AND_STANDS": "Cigar Stores And Stands",
|
||||
"CIVIC_SOCIAL_FRATERNAL_ASSOCIATIONS": "Civic, Social, Fraternal Associations",
|
||||
"CIVIL_SOCIAL_FRAT_ASSOCIATIONS": "Civil/Social/Frat Associations",
|
||||
"CLOTHING": "Clothing",
|
||||
"CLOTHING_ACCESSORIES_AND_SHOES": "Clothing, accessories, and shoes",
|
||||
"CLOTHING_RENTAL": "Clothing Rental",
|
||||
"COFFEE_AND_TEA": "Coffee and tea",
|
||||
"COIN_OPERATED_BANKS_AND_CASINOS": "Coin Operated Banks & Casinos",
|
||||
"COLLECTIBLES": "Collectibles",
|
||||
"COLLECTION_AGENCY": "Collection agency",
|
||||
"COLLEGES_AND_UNIVERSITIES": "Colleges and universities",
|
||||
"COMMERCIAL_EQUIPMENT": "Commercial Equipment",
|
||||
"COMMERCIAL_FOOTWEAR": "Commercial Footwear",
|
||||
"COMMERCIAL_PHOTOGRAPHY": "Commercial photography",
|
||||
"COMMERCIAL_PHOTOGRAPHY_ART_AND_GRAPHICS": "Commercial photography, art, and graphics",
|
||||
"COMMERCIAL_SPORTS_PROFESSIONA": "Commercial Sports/Professiona",
|
||||
"COMMODITIES_AND_FUTURES_EXCHANGE": "Commodities and futures exchange",
|
||||
"COMPUTER_AND_DATA_PROCESSING_SERVICES": "Computer and data processing services",
|
||||
"COMPUTER_HARDWARE_AND_SOFTWARE": "Computer Hardware & Software",
|
||||
"COMPUTER_MAINTENANCE_REPAIR_AND_SERVICES_NOT_ELSEWHERE_CLAS": "Computer Maintenance, Repair And Services Not Elsewhere Clas",
|
||||
"CONSTRUCTION": "Construction",
|
||||
"CONSTRUCTION_MATERIALS_NOT_ELSEWHERE_CLASSIFIED": "Construction Materials Not Elsewhere Classified",
|
||||
"CONSULTING_SERVICES": "Consulting services",
|
||||
"CONSUMER_CREDIT_REPORTING_AGENCIES": "Consumer Credit Reporting Agencies",
|
||||
"CONVALESCENT_HOMES": "Convalescent Homes",
|
||||
"COSMETIC_STORES": "Cosmetic Stores",
|
||||
"COUNSELING_SERVICES_DEBT_MARRIAGE_PERSONAL": "Counseling Services--Debt,Marriage,Personal",
|
||||
"COUNTERFEIT_CURRENCY_AND_STAMPS": "Counterfeit Currency and Stamps",
|
||||
"COUNTERFEIT_ITEMS": "Counterfeit Items",
|
||||
"COUNTRY_CLUBS": "Country Clubs",
|
||||
"COURIER_SERVICES": "Courier services",
|
||||
"COURIER_SERVICES_AIR_AND_GROUND_AND_FREIGHT_FORWARDERS": "Courier Services-Air And Ground,And Freight Forwarders",
|
||||
"COURT_COSTS_ALIMNY_CHILD_SUPT": "Court Costs/Alimny/Child Supt",
|
||||
"COURT_COSTS_INCLUDING_ALIMONY_AND_CHILD_SUPPORT_COURTS_OF_LAW": "Court Costs, Including Alimony and Child Support - Courts of Law",
|
||||
"CREDIT_CARDS": "Credit Cards",
|
||||
"CREDIT_UNION": "Credit union",
|
||||
"CULTURE_AND_RELIGION": "Culture & Religion",
|
||||
"DAIRY_PRODUCTS_STORES": "Dairy Products Stores",
|
||||
"DANCE_HALLS_STUDIOS_AND_SCHOOLS": "Dance Halls,Studios,And Schools",
|
||||
"DECORATIVE": "Decorative",
|
||||
"DENTAL": "Dental",
|
||||
"DENTISTS_AND_ORTHODONTISTS": "Dentists And Orthodontists",
|
||||
"DEPARTMENT_STORES": "Department Stores",
|
||||
"DESKTOP_PCS": "Desktop PCs",
|
||||
"DEVICES": "Devices",
|
||||
"DIECAST_TOYS_VEHICLES": "Diecast, Toys Vehicles",
|
||||
"DIGITAL_GAMES": "Digital games",
|
||||
"DIGITAL_MEDIA_BOOKS_MOVIES_MUSIC": "Digital media,books,movies,music",
|
||||
"DIRECT_MARKETING": "Direct Marketing",
|
||||
"DIRECT_MARKETING_CATALOG_MERCHANT": "Direct Marketing - Catalog Merchant",
|
||||
"DIRECT_MARKETING_INBOUND_TELE": "Direct Marketing - Inbound Tele",
|
||||
"DIRECT_MARKETING_OUTBOUND_TELE": "Direct Marketing - Outbound Tele",
|
||||
"DIRECT_MARKETING_SUBSCRIPTION": "Direct Marketing - Subscription",
|
||||
"DISCOUNT_STORES": "Discount Stores",
|
||||
"DOOR_TO_DOOR_SALES": "Door-To-Door Sales",
|
||||
"DRAPERY_WINDOW_COVERING_AND_UPHOLSTERY": "Drapery, window covering, and upholstery",
|
||||
"DRINKING_PLACES": "Drinking Places",
|
||||
"DRUGSTORE": "Drugstore",
|
||||
"DURABLE_GOODS": "Durable goods",
|
||||
"ECOMMERCE_DEVELOPMENT": "eCommerce Development",
|
||||
"ECOMMERCE_SERVICES": "eCommerce Services",
|
||||
"EDUCATIONAL_AND_TEXTBOOKS": "Educational and textbooks",
|
||||
"ELECTRIC_RAZOR_STORES": "Electric Razor Stores",
|
||||
"ELECTRICAL_AND_SMALL_APPLIANCE_REPAIR": "Electrical and small appliance repair",
|
||||
"ELECTRICAL_CONTRACTORS": "Electrical Contractors",
|
||||
"ELECTRICAL_PARTS_AND_EQUIPMENT": "Electrical Parts and Equipment",
|
||||
"ELECTRONIC_CASH": "Electronic Cash",
|
||||
"ELEMENTARY_AND_SECONDARY_SCHOOLS": "Elementary and secondary schools",
|
||||
"EMPLOYMENT": "Employment",
|
||||
"ENTERTAINERS": "Entertainers",
|
||||
"ENTERTAINMENT_AND_MEDIA": "Entertainment and media",
|
||||
"EQUIP_TOOL_FURNITURE_AND_APPLIANCE_RENTAL_AND_LEASING": "Equip, Tool, Furniture, And Appliance Rental And Leasing",
|
||||
"ESCROW": "Escrow",
|
||||
"EVENT_AND_WEDDING_PLANNING": "Event & Wedding Planning",
|
||||
"EXERCISE_AND_FITNESS": "Exercise and fitness",
|
||||
"EXERCISE_EQUIPMENT": "Exercise Equipment",
|
||||
"EXTERMINATING_AND_DISINFECTING_SERVICES": "Exterminating and disinfecting services",
|
||||
"FABRICS_AND_SEWING": "Fabrics & Sewing",
|
||||
"FAMILY_CLOTHING_STORES": "Family Clothing Stores",
|
||||
"FASHION_JEWELRY": "Fashion jewelry",
|
||||
"FAST_FOOD_RESTAURANTS": "Fast Food Restaurants",
|
||||
"FICTION_AND_NONFICTION": "Fiction and nonfiction",
|
||||
"FINANCE_COMPANY": "Finance company",
|
||||
"FINANCIAL_AND_INVESTMENT_ADVICE": "Financial and investment advice",
|
||||
"FINANCIAL_INSTITUTIONS_MERCHANDISE_AND_SERVICES": "Financial Institutions - Merchandise And Services",
|
||||
"FIREARM_ACCESSORIES": "Firearm accessories",
|
||||
"FIREARMS_WEAPONS_AND_KNIVES": "Firearms, Weapons and Knives",
|
||||
"FIREPLACE_AND_FIREPLACE_SCREENS": "Fireplace, and fireplace screens",
|
||||
"FIREWORKS": "Fireworks",
|
||||
"FISHING": "Fishing",
|
||||
"FLORISTS": "Florists",
|
||||
"FLOWERS": "Flowers",
|
||||
"FOOD_DRINK_AND_NUTRITION": "Food, Drink & Nutrition",
|
||||
"FOOD_PRODUCTS": "Food Products",
|
||||
"FOOD_RETAIL_AND_SERVICE": "Food retail and service",
|
||||
"FRAGRANCES_AND_PERFUMES": "Fragrances and perfumes",
|
||||
"FREEZER_AND_LOCKER_MEAT_PROVISIONERS": "Freezer and Locker Meat Provisioners",
|
||||
"FUEL_DEALERS_FUEL_OIL_WOOD_AND_COAL": "Fuel Dealers-Fuel Oil, Wood & Coal",
|
||||
"FUEL_DEALERS_NON_AUTOMOTIVE": "Fuel Dealers - Non Automotive",
|
||||
"FUNERAL_SERVICES_AND_CREMATORIES": "Funeral Services & Crematories",
|
||||
"FURNISHING_AND_DECORATING": "Furnishing & Decorating",
|
||||
"FURNITURE": "Furniture",
|
||||
"FURRIERS_AND_FUR_SHOPS": "Furriers and Fur Shops",
|
||||
"GADGETS_AND_OTHER_ELECTRONICS": "Gadgets & other electronics",
|
||||
"GAMBLING": "Gambling",
|
||||
"GAME_SOFTWARE": "Game Software",
|
||||
"GAMES": "Games",
|
||||
"GARDEN_SUPPLIES": "Garden supplies",
|
||||
"GENERAL": "General",
|
||||
"GENERAL_CONTRACTORS": "General contractors",
|
||||
"GENERAL_GOVERNMENT": "General - Government",
|
||||
"GENERAL_SOFTWARE": "General - Software",
|
||||
"GENERAL_TELECOM": "General - Telecom",
|
||||
"GIFTS_AND_FLOWERS": "Gifts and flowers",
|
||||
"GLASS_PAINT_AND_WALLPAPER_STORES": "Glass,Paint,And Wallpaper Stores",
|
||||
"GLASSWARE_CRYSTAL_STORES": "Glassware, Crystal Stores",
|
||||
"GOVERNMENT": "Government",
|
||||
"GOVERNMENT_IDS_AND_LICENSES": "Government IDs and Licenses",
|
||||
"GOVERNMENT_LICENSED_ON_LINE_CASINOS_ON_LINE_GAMBLING": "Government Licensed On-Line Casinos - On-Line Gambling",
|
||||
"GOVERNMENT_OWNED_LOTTERIES": "Government-Owned Lotteries",
|
||||
"GOVERNMENT_SERVICES": "Government services",
|
||||
"GRAPHIC_AND_COMMERCIAL_DESIGN": "Graphic & Commercial Design",
|
||||
"GREETING_CARDS": "Greeting Cards",
|
||||
"GROCERY_STORES_AND_SUPERMARKETS": "Grocery Stores & Supermarkets",
|
||||
"HARDWARE_AND_TOOLS": "Hardware & Tools",
|
||||
"HARDWARE_EQUIPMENT_AND_SUPPLIES": "Hardware, Equipment, and Supplies",
|
||||
"HAZARDOUS_RESTRICTED_AND_PERISHABLE_ITEMS": "Hazardous, Restricted and Perishable Items",
|
||||
"HEALTH_AND_BEAUTY_SPAS": "Health and beauty spas",
|
||||
"HEALTH_AND_NUTRITION": "Health & Nutrition",
|
||||
"HEALTH_AND_PERSONAL_CARE": "Health and personal care",
|
||||
"HEARING_AIDS_SALES_AND_SUPPLIES": "Hearing Aids Sales and Supplies",
|
||||
"HEATING_PLUMBING_AC": "Heating, Plumbing, AC",
|
||||
"HIGH_RISK_MERCHANT": "High Risk Merchant",
|
||||
"HIRING_SERVICES": "Hiring services",
|
||||
"HOBBIES_TOYS_AND_GAMES": "Hobbies, Toys & Games",
|
||||
"HOME_AND_GARDEN": "Home and garden",
|
||||
"HOME_AUDIO": "Home Audio",
|
||||
"HOME_DECOR": "Home decor",
|
||||
"HOME_ELECTRONICS": "Home Electronics",
|
||||
"HOSPITALS": "Hospitals",
|
||||
"HOTELS_MOTELS_INNS_RESORTS": "Hotels/Motels/Inns/Resorts",
|
||||
"HOUSEWARES": "Housewares",
|
||||
"HUMAN_PARTS_AND_REMAINS": "Human Parts and Remains",
|
||||
"HUMOROUS_GIFTS_AND_NOVELTIES": "Humorous Gifts & Novelties",
|
||||
"HUNTING": "Hunting",
|
||||
"IDS_LICENSES_AND_PASSPORTS": "IDs, licenses, and passports",
|
||||
"ILLEGAL_DRUGS_AND_PARAPHERNALIA": "Illegal Drugs & Paraphernalia",
|
||||
"INDUSTRIAL": "Industrial",
|
||||
"INDUSTRIAL_AND_MANUFACTURING_SUPPLIES": "Industrial and manufacturing supplies",
|
||||
"INSURANCE_AUTO_AND_HOME": "Insurance - auto and home",
|
||||
"INSURANCE_DIRECT": "Insurance - Direct",
|
||||
"INSURANCE_LIFE_AND_ANNUITY": "Insurance - life and annuity",
|
||||
"INSURANCE_SALES_UNDERWRITING": "Insurance Sales/Underwriting",
|
||||
"INSURANCE_UNDERWRITING_PREMIUMS": "Insurance Underwriting, Premiums",
|
||||
"INTERNET_AND_NETWORK_SERVICES": "Internet & Network Services",
|
||||
"INTRA_COMPANY_PURCHASES": "Intra-Company Purchases",
|
||||
"LABORATORIES_DENTAL_MEDICAL": "Laboratories-Dental/Medical",
|
||||
"LANDSCAPING": "Landscaping",
|
||||
"LANDSCAPING_AND_HORTICULTURAL_SERVICES": "Landscaping And Horticultural Services",
|
||||
"LAUNDRY_CLEANING_SERVICES": "Laundry, Cleaning Services",
|
||||
"LEGAL": "Legal",
|
||||
"LEGAL_SERVICES_AND_ATTORNEYS": "Legal services and attorneys",
|
||||
"LOCAL_DELIVERY_SERVICE": "Local delivery service",
|
||||
"LOCKSMITH": "Locksmith",
|
||||
"LODGING_AND_ACCOMMODATIONS": "Lodging and accommodations",
|
||||
"LOTTERY_AND_CONTESTS": "Lottery and contests",
|
||||
"LUGGAGE_AND_LEATHER_GOODS": "Luggage and leather goods",
|
||||
"LUMBER_AND_BUILDING_MATERIALS": "Lumber & Building Materials",
|
||||
"MAGAZINES": "Magazines",
|
||||
"MAINTENANCE_AND_REPAIR_SERVICES": "Maintenance and repair services",
|
||||
"MAKEUP_AND_COSMETICS": "Makeup and cosmetics",
|
||||
"MANUAL_CASH_DISBURSEMENTS": "Manual Cash Disbursements",
|
||||
"MASSAGE_PARLORS": "Massage Parlors",
|
||||
"MEDICAL": "Medical",
|
||||
"MEDICAL_AND_PHARMACEUTICAL": "Medical & Pharmaceutical",
|
||||
"MEDICAL_CARE": "Medical care",
|
||||
"MEDICAL_EQUIPMENT_AND_SUPPLIES": "Medical equipment and supplies",
|
||||
"MEDICAL_SERVICES": "Medical Services",
|
||||
"MEETING_PLANNERS": "Meeting Planners",
|
||||
"MEMBERSHIP_CLUBS_AND_ORGANIZATIONS": "Membership clubs and organizations",
|
||||
"MEMBERSHIP_COUNTRY_CLUBS_GOLF": "Membership/Country Clubs/Golf",
|
||||
"MEMORABILIA": "Memorabilia",
|
||||
"MEN_AND_BOY_CLOTHING_AND_ACCESSORY_STORES": "Men's And Boy's Clothing And Accessory Stores",
|
||||
"MEN_CLOTHING": "Men's Clothing",
|
||||
"MERCHANDISE": "Merchandise",
|
||||
"METAPHYSICAL": "Metaphysical",
|
||||
"MILITARIA": "Militaria",
|
||||
"MILITARY_AND_CIVIL_SERVICE_UNIFORMS": "Military and civil service uniforms",
|
||||
"MISC_AUTOMOTIVE_AIRCRAFT_AND_FARM_EQUIPMENT_DEALERS": "Misc,Automotive,Aircraft,And Farm Equipment Dealers",
|
||||
"MISC_GENERAL_MERCHANDISE": "Misc General Merchandise",
|
||||
"MISCELLANEOUS_GENERAL_SERVICES": "Miscellaneous General Services",
|
||||
"MISCELLANEOUS_REPAIR_SHOPS_AND_RELATED_SERVICES": "Miscellaneous Repair Shops And Related Services",
|
||||
"MODEL_KITS": "Model Kits",
|
||||
"MONEY_TRANSFER_MEMBER_FINANCIAL_INSTITUTION": "Money Transfer - Member Financial Institution",
|
||||
"MONEY_TRANSFER_MERCHANT": "Money Transfer--Merchant",
|
||||
"MOTION_PICTURE_THEATERS": "Motion Picture Theaters",
|
||||
"MOTOR_FREIGHT_CARRIERS_AND_TRUCKING": "Motor Freight Carriers & Trucking",
|
||||
"MOTOR_HOME_AND_RECREATIONAL_VEHICLE_RENTAL": "Motor Home And Recreational Vehicle Rental",
|
||||
"MOTOR_HOMES_DEALERS": "Motor Homes Dealers",
|
||||
"MOTOR_VEHICLE_SUPPLIES_AND_NEW_PARTS": "Motor Vehicle Supplies and New Parts",
|
||||
"MOTORCYCLE_DEALERS": "Motorcycle Dealers",
|
||||
"MOTORCYCLES": "Motorcycles",
|
||||
"MOVIE": "Movie",
|
||||
"MOVIE_TICKETS": "Movie tickets",
|
||||
"MOVING_AND_STORAGE": "Moving and storage",
|
||||
"MULTI_LEVEL_MARKETING": "Multi-level marketing",
|
||||
"MUSIC_CDS_CASSETTES_AND_ALBUMS": "Music - CDs, cassettes and albums",
|
||||
"MUSIC_STORE_INSTRUMENTS_AND_SHEET_MUSIC": "Music store - instruments and sheet music",
|
||||
"NETWORKING": "Networking",
|
||||
"NEW_AGE": "New Age",
|
||||
"NEW_PARTS_AND_SUPPLIES_MOTOR_VEHICLE": "New parts and supplies - motor vehicle",
|
||||
"NEWS_DEALERS_AND_NEWSTANDS": "News Dealers and Newstands",
|
||||
"NON_DURABLE_GOODS": "Non-durable goods",
|
||||
"NON_FICTION": "Non-Fiction",
|
||||
"NON_PROFIT_POLITICAL_AND_RELIGION": "Non-Profit, Political & Religion",
|
||||
"NONPROFIT": "Nonprofit",
|
||||
"NOVELTIES": "Novelties",
|
||||
"OEM_SOFTWARE": "Oem Software",
|
||||
"OFFICE_SUPPLIES_AND_EQUIPMENT": "Office Supplies and Equipment",
|
||||
"ONLINE_DATING": "Online Dating",
|
||||
"ONLINE_GAMING": "Online gaming",
|
||||
"ONLINE_GAMING_CURRENCY": "Online gaming currency",
|
||||
"ONLINE_SERVICES": "online services",
|
||||
"OOUTBOUND_TELEMARKETING_MERCH": "Ooutbound Telemarketing Merch",
|
||||
"OPHTHALMOLOGISTS_OPTOMETRIST": "Ophthalmologists/Optometrist",
|
||||
"OPTICIANS_AND_DISPENSING": "Opticians And Dispensing",
|
||||
"ORTHOPEDIC_GOODS_PROSTHETICS": "Orthopedic Goods/Prosthetics",
|
||||
"OSTEOPATHS": "Osteopaths",
|
||||
"OTHER": "Other",
|
||||
"PACKAGE_TOUR_OPERATORS": "Package Tour Operators",
|
||||
"PAINTBALL": "Paintball",
|
||||
"PAINTS_VARNISHES_AND_SUPPLIES": "Paints, Varnishes, and Supplies",
|
||||
"PARKING_LOTS_AND_GARAGES": "Parking Lots & Garages",
|
||||
"PARTS_AND_ACCESSORIES": "Parts and accessories",
|
||||
"PAWN_SHOPS": "Pawn Shops",
|
||||
"PAYCHECK_LENDER_OR_CASH_ADVANCE": "Paycheck lender or cash advance",
|
||||
"PERIPHERALS": "Peripherals",
|
||||
"PERSONALIZED_GIFTS": "Personalized Gifts",
|
||||
"PET_SHOPS_PET_FOOD_AND_SUPPLIES": "Pet shops, pet food, and supplies",
|
||||
"PETROLEUM_AND_PETROLEUM_PRODUCTS": "Petroleum and Petroleum Products",
|
||||
"PETS_AND_ANIMALS": "Pets and animals",
|
||||
"PHOTOFINISHING_LABORATORIES_PHOTO_DEVELOPING": "Photofinishing Laboratories,Photo Developing",
|
||||
"PHOTOGRAPHIC_STUDIOS_PORTRAITS": "Photographic studios - portraits",
|
||||
"PHOTOGRAPHY": "Photography",
|
||||
"PHYSICAL_GOOD": "Physical Good",
|
||||
"PICTURE_VIDEO_PRODUCTION": "Picture/Video Production",
|
||||
"PIECE_GOODS_NOTIONS_AND_OTHER_DRY_GOODS": "Piece Goods Notions and Other Dry Goods",
|
||||
"PLANTS_AND_SEEDS": "Plants and Seeds",
|
||||
"PLUMBING_AND_HEATING_EQUIPMENTS_AND_SUPPLIES": "Plumbing & Heating Equipments & Supplies",
|
||||
"POLICE_RELATED_ITEMS": "Police-Related Items",
|
||||
"POLITICAL_ORGANIZATIONS": "Politcal Organizations",
|
||||
"POSTAL_SERVICES_GOVERNMENT_ONLY": "Postal Services - Government Only",
|
||||
"POSTERS": "Posters",
|
||||
"PREPAID_AND_STORED_VALUE_CARDS": "Prepaid and stored value cards",
|
||||
"PRESCRIPTION_DRUGS": "Prescription Drugs",
|
||||
"PROMOTIONAL_ITEMS": "Promotional Items",
|
||||
"PUBLIC_WAREHOUSING_AND_STORAGE": "Public Warehousing and Storage",
|
||||
"PUBLISHING_AND_PRINTING": "Publishing and printing",
|
||||
"PUBLISHING_SERVICES": "Publishing Services",
|
||||
"RADAR_DECTORS": "Radar Dectors",
|
||||
"RADIO_TELEVISION_AND_STEREO_REPAIR": "Radio, television, and stereo repair",
|
||||
"REAL_ESTATE": "Real Estate",
|
||||
"REAL_ESTATE_AGENT": "Real estate agent",
|
||||
"REAL_ESTATE_AGENTS_AND_MANAGERS_RENTALS": "Real Estate Agents And Managers - Rentals",
|
||||
"RELIGION_AND_SPIRITUALITY_FOR_PROFIT": "Religion and spirituality for profit",
|
||||
"RELIGIOUS": "Religious",
|
||||
"RELIGIOUS_ORGANIZATIONS": "Religious Organizations",
|
||||
"REMITTANCE": "Remittance",
|
||||
"RENTAL_PROPERTY_MANAGEMENT": "Rental property management",
|
||||
"RESIDENTIAL": "Residential",
|
||||
"RETAIL": "Retail",
|
||||
"RETAIL_FINE_JEWELRY_AND_WATCHES": "Retail - fine jewelry and watches",
|
||||
"REUPHOLSTERY_AND_FURNITURE_REPAIR": "Reupholstery and furniture repair",
|
||||
"RINGS": "Rings",
|
||||
"ROOFING_SIDING_SHEET_METAL": "Roofing/Siding, Sheet Metal",
|
||||
"RUGS_AND_CARPETS": "Rugs & Carpets",
|
||||
"SCHOOLS_AND_COLLEGES": "Schools and Colleges",
|
||||
"SCIENCE_FICTION": "Science Fiction",
|
||||
"SCRAPBOOKING": "Scrapbooking",
|
||||
"SCULPTURES": "Sculptures",
|
||||
"SECURITIES_BROKERS_AND_DEALERS": "Securities - Brokers And Dealers",
|
||||
"SECURITY_AND_SURVEILLANCE": "Security and surveillance",
|
||||
"SECURITY_AND_SURVEILLANCE_EQUIPMENT": "Security and surveillance equipment",
|
||||
"SECURITY_BROKERS_AND_DEALERS": "Security brokers and dealers",
|
||||
"SEMINARS": "Seminars",
|
||||
"SERVICE_STATIONS": "Service Stations",
|
||||
"SERVICES": "Services",
|
||||
"SEWING_NEEDLEWORK_FABRIC_AND_PIECE_GOODS_STORES": "Sewing,Needlework,Fabric And Piece Goods Stores",
|
||||
"SHIPPING_AND_PACKING": "Shipping & Packaging",
|
||||
"SHOE_REPAIR_HAT_CLEANING": "Shoe Repair/Hat Cleaning",
|
||||
"SHOE_STORES": "Shoe Stores",
|
||||
"SHOES": "Shoes",
|
||||
"SNOWMOBILE_DEALERS": "Snowmobile Dealers",
|
||||
"SOFTWARE": "Software",
|
||||
"SPECIALTY_AND_MISC_FOOD_STORES": "Specialty and misc food stores",
|
||||
"SPECIALTY_CLEANING_POLISHING_AND_SANITATION_PREPARATIONS": "Specialty Cleaning, Polishing And Sanitation Preparations",
|
||||
"SPECIALTY_OR_RARE_PETS": "Specialty or rare pets",
|
||||
"SPORT_GAMES_AND_TOYS": "Sport games and toys",
|
||||
"SPORTING_AND_RECREATIONAL_CAMPS": "Sporting And Recreational Camps",
|
||||
"SPORTING_GOODS": "Sporting Goods",
|
||||
"SPORTS_AND_OUTDOORS": "Sports and outdoors",
|
||||
"SPORTS_AND_RECREATION": "Sports & Recreation",
|
||||
"STAMP_AND_COIN": "Stamp and coin",
|
||||
"STATIONARY_PRINTING_AND_WRITING_PAPER": "Stationary, printing, and writing paper",
|
||||
"STENOGRAPHIC_AND_SECRETARIAL_SUPPORT_SERVICES": "Stenographic and secretarial support services",
|
||||
"STOCKS_BONDS_SECURITIES_AND_RELATED_CERTIFICATES": "Stocks, Bonds, Securities and Related Certificates",
|
||||
"STORED_VALUE_CARDS": "Stored Value Cards",
|
||||
"SUPPLIES": "Supplies",
|
||||
"SUPPLIES_AND_TOYS": "Supplies & Toys",
|
||||
"SURVEILLANCE_EQUIPMENT": "Surveillance Equipment",
|
||||
"SWIMMING_POOLS_AND_SPAS": "Swimming Pools & Spas",
|
||||
"SWIMMING_POOLS_SALES_SUPPLIES_SERVICES": "Swimming Pools-Sales,Supplies,Services",
|
||||
"TAILORS_AND_ALTERATIONS": "Tailors and alterations",
|
||||
"TAX_PAYMENTS": "Tax Payments",
|
||||
"TAX_PAYMENTS_GOVERNMENT_AGENCIES": "Tax Payments - Government Agencies",
|
||||
"TAXICABS_AND_LIMOUSINES": "Taxicabs and limousines",
|
||||
"TELECOMMUNICATION_SERVICES": "Telecommunication Services",
|
||||
"TELEPHONE_CARDS": "Telephone Cards",
|
||||
"TELEPHONE_EQUIPMENT": "Telephone Equipment",
|
||||
"TELEPHONE_SERVICES": "Telephone Services",
|
||||
"THEATER": "Theater",
|
||||
"TIRE_RETREADING_AND_REPAIR": "Tire Retreading and Repair",
|
||||
"TOLL_OR_BRIDGE_FEES": "Toll or Bridge Fees",
|
||||
"TOOLS_AND_EQUIPMENT": "Tools and equipment",
|
||||
"TOURIST_ATTRACTIONS_AND_EXHIBITS": "Tourist Attractions And Exhibits",
|
||||
"TOWING_SERVICE": "Towing service",
|
||||
"TOYS_AND_GAMES": "Toys and games",
|
||||
"TRADE_AND_VOCATIONAL_SCHOOLS": "Trade And Vocational Schools",
|
||||
"TRADEMARK_INFRINGEMENT": "Trademark Infringement",
|
||||
"TRAILER_PARKS_AND_CAMPGROUNDS": "Trailer Parks And Campgrounds",
|
||||
"TRAINING_SERVICES": "Training services",
|
||||
"TRANSPORTATION_SERVICES": "Transportation Services",
|
||||
"TRAVEL": "Travel",
|
||||
"TRUCK_AND_UTILITY_TRAILER_RENTALS": "Truck And Utility Trailer Rentals",
|
||||
"TRUCK_STOP": "Truck Stop",
|
||||
"TYPESETTING_PLATE_MAKING_AND_RELATED_SERVICES": "Typesetting, Plate Making, and Related Services",
|
||||
"USED_MERCHANDISE_AND_SECONDHAND_STORES": "Used Merchandise And Secondhand Stores",
|
||||
"USED_PARTS_MOTOR_VEHICLE": "Used parts - motor vehicle",
|
||||
"UTILITIES": "Utilities",
|
||||
"UTILITIES_ELECTRIC_GAS_WATER_SANITARY": "Utilities - Electric,Gas,Water,Sanitary",
|
||||
"VARIETY_STORES": "Variety Stores",
|
||||
"VEHICLE_SALES": "Vehicle sales",
|
||||
"VEHICLE_SERVICE_AND_ACCESSORIES": "Vehicle service and accessories",
|
||||
"VIDEO_EQUIPMENT": "Video Equipment",
|
||||
"VIDEO_GAME_ARCADES_ESTABLISH": "Video Game Arcades/Establish",
|
||||
"VIDEO_GAMES_AND_SYSTEMS": "Video Games & Systems",
|
||||
"VIDEO_TAPE_RENTAL_STORES": "Video Tape Rental Stores",
|
||||
"VINTAGE_AND_COLLECTIBLE_VEHICLES": "Vintage and Collectible Vehicles",
|
||||
"VINTAGE_AND_COLLECTIBLES": "Vintage and collectibles",
|
||||
"VITAMINS_AND_SUPPLEMENTS": "Vitamins & Supplements",
|
||||
"VOCATIONAL_AND_TRADE_SCHOOLS": "Vocational and trade schools",
|
||||
"WATCH_CLOCK_AND_JEWELRY_REPAIR": "Watch, clock, and jewelry repair",
|
||||
"WEB_HOSTING_AND_DESIGN": "Web hosting and design",
|
||||
"WELDING_REPAIR": "Welding Repair",
|
||||
"WHOLESALE_CLUBS": "Wholesale Clubs",
|
||||
"WHOLESALE_FLORIST_SUPPLIERS": "Wholesale Florist Suppliers",
|
||||
"WHOLESALE_PRESCRIPTION_DRUGS": "Wholesale Prescription Drugs",
|
||||
"WILDLIFE_PRODUCTS": "Wildlife Products",
|
||||
"WIRE_TRANSFER": "Wire Transfer",
|
||||
"WIRE_TRANSFER_AND_MONEY_ORDER": "Wire transfer and money order",
|
||||
"WOMEN_ACCESSORY_SPECIALITY": "Women's Accessory/Speciality",
|
||||
"WOMEN_CLOTHING": "Women's clothing"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user