From 34448b6352e54f10f25e2e771b0adc5e3b6adc7b Mon Sep 17 00:00:00 2001 From: Xhunter Date: Thu, 13 Jun 2024 14:46:00 +0530 Subject: [PATCH] remove(coupon): remove coupon support --- coupons/coupons.csv | 2 - coupons/projects.csv | 2 - package.json | 4 +- src/coupon.js | 285 --------------------------------------- src/server.js | 314 ++++++++++++++++++++----------------------- 5 files changed, 145 insertions(+), 462 deletions(-) delete mode 100644 coupons/coupons.csv delete mode 100644 coupons/projects.csv delete mode 100644 src/coupon.js diff --git a/coupons/coupons.csv b/coupons/coupons.csv deleted file mode 100644 index b54e733..0000000 --- a/coupons/coupons.csv +++ /dev/null @@ -1,2 +0,0 @@ -type,validity,description,couponCode,value,projectTag,couponQty,minAmount,id -FLAT,2022-12-26,,kode500,20,api,10,90,1 diff --git a/coupons/projects.csv b/coupons/projects.csv deleted file mode 100644 index df27a57..0000000 --- a/coupons/projects.csv +++ /dev/null @@ -1,2 +0,0 @@ -pid,isApplied,appliedAt,couponCode,amountAfterDiscount,budget - diff --git a/package.json b/package.json index 2bed5a2..15f931b 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "license": "Koders", "scripts": { "dev": "nodemon ./src/server.js", - "test": "jest --coverage", - "prepare": "husky install" + "test": "jest --coverage" }, "dependencies": { "axios": "^0.27.2", @@ -45,7 +44,6 @@ "eslint-plugin-n": "^15.3.0", "eslint-plugin-promise": "^6.0.1", "eslint-plugin-react": "^7.31.10", - "husky": "^8.0.3", "jest-sonar-reporter": "^2.0.0", "nodemon": "^2.0.19", "typescript": "^4.8.4" diff --git a/src/coupon.js b/src/coupon.js deleted file mode 100644 index 8a0e7b5..0000000 --- a/src/coupon.js +++ /dev/null @@ -1,285 +0,0 @@ -const fs = require('fs') -const csvjson = require('csvjson') -const path = require('path') -const moment = require('moment') -const { stringify } = require('csv-stringify') -const { getTagsFromIssues } = require('./helper') - -const couponHeader = { - type: 'type', - validity: 'validity', - description: 'description', - couponCode: 'couponCode', - value: 'value', - projectTag: 'projectTag', - couponQty: 'couponQty', - minAmount: 'minAmount', - id: 'id' -} - -const projectHeader = { - pid: 'pid', - isApplied: 'isApplied', - appliedAt: 'appliedAt', - couponCode: 'couponCode', - amountAfterDiscount: 'amountAfterDiscount', - budget: 'budget' -} - -class CouponManager { - constructor () { - this.availableCoupon = [] - this.projects = [] - this.couponFileName = 'coupons.csv' - this.projectFileName = 'projects.csv' - this.isCouponFileRead = false - this.isProjectFileRead = false - this.cacheCouponDetails = {} - this.readCoupons() - } - - createPath (fileName) { - return path.join(__dirname, 'coupons', fileName) - } - - readCsvFile (fileName) { - return fs.readFileSync(this.createPath(fileName), { - encoding: 'utf8' - }) - } - - isFileExist (fileName) { - return fs.existsSync(this.createPath(fileName)) - } - - readCoupons () { - if (this.isFileExist(this.couponFileName)) { - if (!this.isCouponFileRead) { - const fileData = this.readCsvFile(this.couponFileName) - const options = { - delimiter: ',' - } - this.availableCoupon = csvjson.toObject(fileData, options) - console.log('Coupons read from csv file') - this.isCouponFileRead = true - } - } else console.log('coupons.csv file does not exist') - } - - writeCsvFile (data, header, fileName) { - stringify(data, { header: true, columns: header }, (e, output) => { - if (e) return console.log(`Error while stringify data:->${e}`) - fs.writeFile(fileName, output, (er) => { - if (er) return console.log(`Error while writing data:->${er}`) - console.log(`${fileName} updated successfully🎉🎉`) - }) - }) - } - - updateCsvFile (pid) { - if (pid === undefined) return - if (this.isFileExist(this.couponFileName)) { - console.log('Getting cache details for ' + pid) - const details = this.getCacheCouponDetails(pid) - if (details !== undefined) { - const tempCoupons = this.availableCoupon.map((coupon) => { - if (coupon?.id === details.coupon?.id) { - return details.coupon - } - return coupon - }) - console.log('Updating... coupon file') - this.availableCoupon = tempCoupons - this.writeCsvFile(tempCoupons, couponHeader, this.couponFileName) - this.updateProjectFile(details.project) - } - } - } - - updateProjectFile (project) { - if (this.isFileExist(this.projectFileName)) { - const projects = this.readProjects() - let data = [] - if (projects !== undefined) { - data = [...projects, project] - } else { - data = [project] - } - console.log('Updating... project file') - this.writeCsvFile(data, projectHeader, this.projectFileName) - } - } - - readProjects () { - if (this.isFileExist(this.projectFileName)) { - if (!this.isProjectFileRead) { - const fileData = this.readCsvFile(this.projectFileName) - const options = { - delimiter: ',' - } - const projects = csvjson.toObject(fileData, options) - if (projects.length > 0) { - return projects - } else console.log('No, projects exist in csv file') - } - } else console.log('projects.csv file does not exist') - } - - parseCouponCode (code = '') { - return code.replace(/\s/g, '').toLowerCase() - } - - isValidCode (code) { - return (code.length !== Number(0)) - } - - isValidAmount (amount) { - if (Number.isInteger(amount)) { - return { - result: true, - amount: parseInt(amount) - } - } - const parseAmount = parseFloat(amount) - if (!isNaN(parseAmount)) { - return { - amount: parseAmount, - result: true - } - } - return { - result: false, - amount: 0 - } - } - - formatDate () { - return moment(moment.now()).format('YYYY-MM-DD') - } - - isCouponExpired (couponDetail, couponCode) { - if (couponDetail.couponCode.toLowerCase() === couponCode) { - return !moment().isBefore(moment(couponDetail.validity)) - } - - return true - } - - async isValidTag (tag, apiKey, issues) { - return getTagsFromIssues(apiKey, issues, tag) - } - - getProject (pid) { - const response = { data: null, isValid: false } - const projects = this.readProjects() - if (projects !== undefined) { - for (const project of projects) { - if (parseInt(project.pid) === pid) { - response.data = project - response.isValid = false - return response - } - } - } - response.isValid = true - return response - } - - isCouponExistInList (couponCode) { - for (const coupon of this.availableCoupon) { - if (coupon.couponCode.toLowerCase() === couponCode) { - return { - exist: true, - couponDetail: coupon - } - } - } - return { - exist: false, - couponDetail: null - } - } - - createResponse (msg, isValid, data) { - return { msg, isValid, data } - } - - isCouponLeft (couponDetail) { - return Number(couponDetail.couponQty) > 0 - } - - applyCouponAmount (amount, budget, pid, couponDetail) { - if (amount > 0) { - this.cacheCouponDetails[pid] = { - couponDetail, - pid, - budget, - amount - } - return this.createResponse('COUPON_APPLIED', true, { - originalBudget: budget, - budgetAfterAppliedCoupon: amount - }) - } - return this.createResponse("CAN'T_APPLIED_COUPON", false, null) - } - - getCacheCouponDetails (pid) { - if (pid in this.cacheCouponDetails) { - const couponDetails = { ...this.cacheCouponDetails[pid].couponDetail } - return { - coupon: { - ...couponDetails, - couponQty: Number(couponDetails.couponQty) - 1 - }, - project: { - pid, - isApplied: true, - appliedAt: this.formatDate(), - couponCode: couponDetails.couponCode, - amountAfterDiscount: this.cacheCouponDetails[pid].amount, - budget: this.cacheCouponDetails[pid].budget - } - } - } - } - - calculate (budget, code, pid, apiKey, issues = []) { - const coupon = this.parseCouponCode(code) - if (!this.isValidCode(coupon)) { - return this.createResponse('INVALID_COUPON_CODE', false, null) - } - const { couponDetail, exist } = this.isCouponExistInList(coupon) - if (!exist) return this.createResponse('COUPON_NOT_EXIST', false, null) - const { isValid, data } = this.getProject(Number(pid)) - if (!isValid) { - return this.createResponse('ALREADY_APPLIED_ON_THIS_PID', false, data) - } - if (this.isCouponExpired(couponDetail, coupon)) { - return this.createResponse('EXPIRED_COUPON', false, null) - } - if (!this.isValidTag(couponDetail.projectTag, apiKey, issues)) { - return this.createResponse('TAG_NOT_MATCHED', false, null) - } - if (!this.isCouponLeft(couponDetail)) { - return this.createResponse('NO_COUPON_LEFT', false, null) - } - const { amount, result } = this.isValidAmount(budget) - if (result) { - if ((amount <= couponDetail.minAmount)) { - // eslint-disable-next-line quotes - return this.createResponse("BUDGET_IS_TOO_SMALL", false, null) - } - // eslint-disable-next-line eqeqeq - if (couponDetail.type == 'FLAT') { - const discount = amount - couponDetail.value - return this.applyCouponAmount(discount, amount, pid, couponDetail) - } else { - const discount = amount - amount * (couponDetail.value / 100) - return this.applyCouponAmount(discount, amount, pid, couponDetail) - } - } - } -} - -module.exports = new CouponManager() diff --git a/src/server.js b/src/server.js index 20d3607..b344108 100644 --- a/src/server.js +++ b/src/server.js @@ -1,139 +1,113 @@ -require("dotenv").config(); -const fs = require("fs"); -const express = require("express"); -const cors = require("cors"); -const axios = require("axios"); -const app = express(); -const stripe = require("stripe")(process.env.STRIPE_SK); +require('dotenv').config() +const fs = require('fs') +const express = require('express') +const cors = require('cors') +const axios = require('axios') +const app = express() +const stripe = require('stripe')(process.env.STRIPE_SK) const { fetchProject, getProjectData, getBudget, getInvoiceDetails, getFilteredProjectStatus, - getInvoiceType, -} = require("./helper"); -const sendEmail = require("./mail"); -const generatePDF = require("./invoice/helper"); -const getWebhookPayload = require("./constant"); + getInvoiceType +} = require('./helper') +const sendEmail = require('./mail') +const generatePDF = require('./invoice/helper') +const getWebhookPayload = require('./constant') -const appUrl = process.env.APP_URL; -const port = 9442; -const serverHost = `http://localhost:${port}`; -const allowedUrls = [appUrl, "https://raagwaas.com"]; -app.disable("x-powered-by"); +const appUrl = process.env.APP_URL +const port = 9442 +const serverHost = `http://localhost:${port}` +const allowedUrls = [appUrl, 'https://raagwaas.com'] +app.disable('x-powered-by') // enable cors with allowed urls app.use( cors({ origin: allowedUrls, - optionsSuccessStatus: 200, + optionsSuccessStatus: 200 }) -); +) app.use((req, res, next) => { - if (req.originalUrl === "/stripe") { - next(); + if (req.originalUrl === '/stripe') { + next() } else { - express.json()(req, res, next); + express.json()(req, res, next) } -}); +}) -app.get("/", (_, res) => { - res.send("Payment API is working perfectly"); -}); +app.get('/', (_, res) => { + res.send('Payment API is working perfectly') +}) -app.get("/status", async (_, res) => { - const apiKey = process.env.REDMINE_API_KEY; +app.get('/status', async (_, res) => { + const apiKey = process.env.REDMINE_API_KEY if (apiKey) { const blackListedProjects = [ - "public-relations", - "kore", - "x12-mirror", - "graphana", - "test-project-budget-check", - "wait-list", - "getting-started-with-koders", - ]; - const data = await getFilteredProjectStatus(apiKey, blackListedProjects); - res.status(200).json({ msg: "Project Status", data }); + 'public-relations', + 'kore', + 'x12-mirror', + 'graphana', + 'test-project-budget-check', + 'wait-list', + 'getting-started-with-koders' + ] + const data = await getFilteredProjectStatus(apiKey, blackListedProjects) + res.status(200).json({ msg: 'Project Status', data }) } else { - res.status(400).json({ msg: "Bad request" }); + res.status(400).json({ msg: 'Bad request' }) } -}); +}) -app.post("/get-project", async (req, res) => { - const { apiKey, projectIdentifier } = req.body; +app.post('/get-project', async (req, res) => { + const { apiKey, projectIdentifier } = req.body if (apiKey && projectIdentifier) { - const data = await fetchProject(apiKey, projectIdentifier); - const type = await getInvoiceType(apiKey, projectIdentifier); - if (data !== null && String(data) !== "") { - res.status(200).json({ msg: "Project Details", ...{ data, ...type } }); - } else res.status(400).json({ msg: "Bad request" }); - } else res.status(404).json({ msg: "Some keys are missing", data: null }); -}); + const data = await fetchProject(apiKey, projectIdentifier) + const type = await getInvoiceType(apiKey, projectIdentifier) + if (data !== null && String(data) !== '') { + res.status(200).json({ msg: 'Project Details', ...{ data, ...type } }) + } else res.status(400).json({ msg: 'Bad request' }) + } else res.status(404).json({ msg: 'Some keys are missing', data: null }) +}) -app.post("/get-budget", async (req, res) => { - const { apiKey, issues } = req.body; +app.post('/get-budget', async (req, res) => { + const { apiKey, issues } = req.body if (apiKey && issues) { - const amount = await getBudget(apiKey, issues); - if (amount === null && String(amount) === "") { - res.status(400).json({ msg: "Bad request" }); - } else res.status(200).json({ msg: "Budget amount", data: amount }); - } else res.status(404).json({ msg: "Some keys are missing", data: null }); -}); + const amount = await getBudget(apiKey, issues) + if (amount === null && String(amount) === '') { + res.status(400).json({ msg: 'Bad request' }) + } else res.status(200).json({ msg: 'Budget amount', data: amount }) + } else res.status(404).json({ msg: 'Some keys are missing', data: null }) +}) -// FEAT: Add coupon functionality to hasura -// app.post('/coupon', async (req, res) => { -// const { apiKey, issues, coupon, pid } = req.body -// if (apiKey && issues && coupon) { -// const amount = await getBudget(apiKey, issues) -// if (!(amount === null && String(amount) === '')) { -// const result = couponManager.calculate( -// amount, -// coupon, -// pid, -// apiKey, -// issues -// ) -// let code = 200 -// if (result !== undefined) { -// if (!result.isValid) { -// code = 400 -// } -// } else code = 400 -// res.status(code).json(result) -// } else res.status(400).json({ msg: 'Bad request' }) -// } else res.status(404).json({ msg: 'Some keys are missing', data: null }) -// }) - -app.get("/stripe-redirect/:id", (req, res) => { - const paramValue = req.params.id; - let url = ""; +app.get('/stripe-redirect/:id', (req, res) => { + const paramValue = req.params.id + let url = '' if (paramValue !== undefined) { - if (paramValue.toLowerCase() === "cancel") { - url = `${appUrl}/`; + if (paramValue.toLowerCase() === 'cancel') { + url = `${appUrl}/` } else { - // const pid = req.query.pid - url = `${appUrl}/#/success`; - // couponManager.updateCsvFile(pid) + url = `${appUrl}/#/success` } } - res.status(302).redirect(url); -}); + res.status(302).redirect(url) +}) -app.post("/checkout", async (req, res) => { +app.post('/checkout', async (req, res) => { try { const { milestoneTitle, milestoneUnitAmount: amount, apiKey, projectIdentifier, - type, - } = req.body; + type + } = req.body const { projectName, projectIcon } = await getProjectData( apiKey, projectIdentifier - ); + ) if ((milestoneTitle && amount) || projectIcon) { const session = await stripe.checkout.sessions.create({ line_items: [ @@ -143,149 +117,149 @@ app.post("/checkout", async (req, res) => { product_data: { name: projectName, description: milestoneTitle, - images: [projectIcon], + images: [projectIcon] }, - unit_amount: Math.round(Number(amount)) * 100, + unit_amount: Math.round(Number(amount)) * 100 }, - quantity: 1, - }, + quantity: 1 + } ], - mode: "payment", + mode: 'payment', success_url: `${serverHost}/stripe-redirect/success?pid=${projectIdentifier}`, - cancel_url: `${serverHost}/stripe-redirect/cancel`, - }); - res.status(200).json({ msg: "Checkout URL", data: session.url }); - } else res.status(404).json({ msg: "Some keys are missing", data: null }); + cancel_url: `${serverHost}/stripe-redirect/cancel` + }) + res.status(200).json({ msg: 'Checkout URL', data: session.url }) + } else res.status(404).json({ msg: 'Some keys are missing', data: null }) } catch (error) { - res.status(500).json({ msg: error?.message, data: null }); + res.status(500).json({ msg: error?.message, data: null }) } -}); +}) -app.post("/invoice", async (req, res) => { +app.post('/invoice', async (req, res) => { try { const { - data: { project, apiKey }, - } = req.body; + data: { project, apiKey } + } = req.body if (!project && !apiKey) { res .status(400) - .json({ message: "Bad request! All parameter's are required." }); - return; + .json({ message: "Bad request! All parameter's are required." }) + return } - const response = await getInvoiceDetails(project, apiKey); + const response = await getInvoiceDetails(project, apiKey) if (response === null) { res.status(500).json({ - message: "Internal Server Error:Unable to fetch data from redmine", - }); + message: 'Internal Server Error:Unable to fetch data from redmine' + }) } else { - const path = await generatePDF(response); + const path = await generatePDF(response) if (path?.filename) { const base64PDF = fs.readFileSync(path.filename, { - encoding: "base64", - }); + encoding: 'base64' + }) res.json({ data: base64PDF, name: - response?.invoiceData?.project?.name || new Date().toLocaleString(), - }); + response?.invoiceData?.project?.name || new Date().toLocaleString() + }) fs.unlink(path.filename, (err) => { - if (err) console.log(err); - }); - } else throw Error("Unable to generate PDF"); + if (err) console.log(err) + }) + } else throw Error('Unable to generate PDF') } } catch (error) { - console.log(error); + console.log(error) res .status(500) - .json({ message: "Internal Server Error:" + error?.message }); + .json({ message: 'Internal Server Error:' + error?.message }) } -}); +}) -app.post("/send-email", async (req, res) => { +app.post('/send-email', async (req, res) => { // This endpoint is not the part of KODERS, This is used for raagwaas website. - const { data } = req.body; - let response = null; - if (data?.type === "contact") { + const { data } = req.body + let response = null + if (data?.type === 'contact') { if (data?.name && data?.phone && data?.message && data?.email) { - response = await sendEmail(data); + response = await sendEmail(data) } else { - res.status(400).json({ message: "All parameters are required." }); + res.status(400).json({ message: 'All parameters are required.' }) } - } else if (data?.type === "subscription" && data?.email) { - response = await sendEmail(data); - } else res.status(400).json({ message: "Bad request." }); + } else if (data?.type === 'subscription' && data?.email) { + response = await sendEmail(data) + } else res.status(400).json({ message: 'Bad request.' }) if (response?.data) { res .status(200) - .json({ message: "Mail sent Successfully!", data: response?.data }); + .json({ message: 'Mail sent Successfully!', data: response?.data }) } else if (response === null) { - res.status(500).json({ message: "Internal server error." }); + res.status(500).json({ message: 'Internal server error.' }) } else { - res.status(400).json({ message: "Unable to send email. Try later." }); + res.status(400).json({ message: 'Unable to send email. Try later.' }) } -}); +}) -let id = null; -const endpointSecret = process.env.WH_STRIPE_SECRET; +let id = null +const endpointSecret = process.env.WH_STRIPE_SECRET app.post( - "/stripe", - express.raw({ type: "application/json" }), + '/stripe', + express.raw({ type: 'application/json' }), async (request, response) => { try { - const sig = request.headers["stripe-signature"]; - let event = {}; + const sig = request.headers['stripe-signature'] + let event = {} try { event = stripe.webhooks.constructEvent( request.body, sig, endpointSecret - ); + ) } catch (err) { - console.log(err?.message); - response.status(400).send(`Webhook Error: ${err.message}`); - return; + console.log(err?.message) + response.status(400).send(`Webhook Error: ${err.message}`) + return } if (id !== event?.data?.object?.id) { - id = event?.data?.object?.id; + id = event?.data?.object?.id switch (event.type) { - case "charge.succeeded": + case 'charge.succeeded': { - const payload = getWebhookPayload(event?.data?.object); + const payload = getWebhookPayload(event?.data?.object) axios(process.env.WH_DISCORD_URL, { - method: "POST", - headers: { "Content-Type": "application/json" }, - data: JSON.stringify(payload), + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: JSON.stringify(payload) }) .then((res) => { - console.log("Send successfully!"); + console.log('Send successfully!') }) .catch((e) => { - console.log("Error:", e?.message); - }); + console.log('Error:', e?.message) + }) } - break; + break default: { - console.log(`Unhandled event type---> ${event.type}`); + console.log(`Unhandled event type---> ${event.type}`) } } } - response.send(); + response.send() } catch (error) { - console.log(error?.message); - response.status(400).send(error.message); + console.log(error?.message) + response.status(400).send(error.message) } } -); +) -app.get("*", function (req, res) { - const msg = `

No possible ${req.path} endpoint for ${req.method} method

`; - res.status(404).send(msg); -}); +app.get('*', function (req, res) { + const msg = `

No possible ${req.path} endpoint for ${req.method} method

` + res.status(404).send(msg) +}) app.listen(port, () => { - console.log(`Koders payment app listening at ${serverHost}`); -}); + console.log(`Koders payment app listening at ${serverHost}`) +}) -module.exports = app; +module.exports = app