├── .jshintrc ├── .gitignore ├── public └── images │ ├── files-1547928133070.jpg │ ├── files-1548073484784.jpg │ ├── files-1548073590601.jpg │ ├── files-1548073665175.jpg │ ├── files-1557703155720.jpg │ ├── files-1557786833876.jpg │ ├── files-1557793443690.jpg │ ├── files-1557794073568.jpg │ ├── files-1557797653525.jpg │ ├── files-1557797661063.jpg │ ├── files-1557957851248.jpg │ ├── files-1557958626284.jpg │ ├── files-1558290751169.jpg │ ├── files-1558290765795.jpg │ ├── files-1558290786531.jpg │ ├── files-1558298790187.jpg │ ├── files-1558298980728.jpg │ ├── files-1558299238190.jpg │ ├── files-1558299405892.jpg │ ├── files-1558299430294.jpg │ ├── files-1558389858408.jpg │ ├── files-1558396951051.jpg │ ├── files-1560536850453.jpg │ ├── files-1560536877174.jpg │ ├── files-1560536916513.jpg │ ├── files-1560536984895.jpg │ ├── files-1561127311052.jpg │ ├── files-1561127316747.jpg │ ├── files-1561127323818.jpg │ ├── files-1561127336221.jpg │ ├── files-1561127355498.jpg │ └── files-1561127363760.jpg ├── config ├── database.json ├── email.js ├── correios.js ├── index.js ├── multer.js └── pagseguro.js ├── routes ├── index.js ├── api │ └── v1 │ │ ├── index.js │ │ ├── entregas.js │ │ ├── lojas.js │ │ ├── avaliacoes.js │ │ ├── pagamentos.js │ │ ├── variacoes.js │ │ ├── usuarios.js │ │ ├── categorias.js │ │ ├── pedidos.js │ │ ├── produtos.js │ │ └── clientes.js └── auth.js ├── views ├── partials │ └── header.ejs ├── recovery │ ├── index.ejs │ └── store.ejs └── pagseguro │ └── index.ejs ├── models ├── index.js ├── registroPedido.js ├── categoria.js ├── avaliacao.js ├── loja.js ├── produto.js ├── pedido.js ├── variacao.js ├── entrega.js ├── cliente.js ├── pagamento.js └── usuario.js ├── controllers ├── validacoes │ ├── usuarioValidation.js │ ├── avaliacaoValidation.js │ ├── categoriaValidation.js │ ├── carrinhoValidation.js │ ├── quantidadeValidation.js │ ├── entregaValidation.js │ ├── lojaValidation.js │ ├── produtoValidation.js │ ├── pagamentoValidation.js │ ├── variacaoValidation.js │ ├── pedidoValidation.js │ └── clienteValidation.js ├── LojaController.js ├── integracoes │ ├── correios.js │ └── pagseguro.js ├── AvaliacaoController.js ├── EntregaController.js ├── EmailController.js ├── CategoriaController.js ├── VariacaoController.js ├── UsuarioController.js ├── PagamentoController.js ├── ProdutoController.js ├── ClienteController.js └── PedidoController.js ├── package.json ├── helpers ├── email-recovery.js ├── calcBox.js └── pagseguro.js └── server.js /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 6 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json -------------------------------------------------------------------------------- /public/images/files-1547928133070.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1547928133070.jpg -------------------------------------------------------------------------------- /public/images/files-1548073484784.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1548073484784.jpg -------------------------------------------------------------------------------- /public/images/files-1548073590601.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1548073590601.jpg -------------------------------------------------------------------------------- /public/images/files-1548073665175.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1548073665175.jpg -------------------------------------------------------------------------------- /public/images/files-1557703155720.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557703155720.jpg -------------------------------------------------------------------------------- /public/images/files-1557786833876.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557786833876.jpg -------------------------------------------------------------------------------- /public/images/files-1557793443690.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557793443690.jpg -------------------------------------------------------------------------------- /public/images/files-1557794073568.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557794073568.jpg -------------------------------------------------------------------------------- /public/images/files-1557797653525.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557797653525.jpg -------------------------------------------------------------------------------- /public/images/files-1557797661063.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557797661063.jpg -------------------------------------------------------------------------------- /public/images/files-1557957851248.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557957851248.jpg -------------------------------------------------------------------------------- /public/images/files-1557958626284.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1557958626284.jpg -------------------------------------------------------------------------------- /public/images/files-1558290751169.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558290751169.jpg -------------------------------------------------------------------------------- /public/images/files-1558290765795.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558290765795.jpg -------------------------------------------------------------------------------- /public/images/files-1558290786531.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558290786531.jpg -------------------------------------------------------------------------------- /public/images/files-1558298790187.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558298790187.jpg -------------------------------------------------------------------------------- /public/images/files-1558298980728.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558298980728.jpg -------------------------------------------------------------------------------- /public/images/files-1558299238190.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558299238190.jpg -------------------------------------------------------------------------------- /public/images/files-1558299405892.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558299405892.jpg -------------------------------------------------------------------------------- /public/images/files-1558299430294.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558299430294.jpg -------------------------------------------------------------------------------- /public/images/files-1558389858408.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558389858408.jpg -------------------------------------------------------------------------------- /public/images/files-1558396951051.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1558396951051.jpg -------------------------------------------------------------------------------- /public/images/files-1560536850453.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1560536850453.jpg -------------------------------------------------------------------------------- /public/images/files-1560536877174.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1560536877174.jpg -------------------------------------------------------------------------------- /public/images/files-1560536916513.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1560536916513.jpg -------------------------------------------------------------------------------- /public/images/files-1560536984895.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1560536984895.jpg -------------------------------------------------------------------------------- /public/images/files-1561127311052.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1561127311052.jpg -------------------------------------------------------------------------------- /public/images/files-1561127316747.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1561127316747.jpg -------------------------------------------------------------------------------- /public/images/files-1561127323818.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1561127323818.jpg -------------------------------------------------------------------------------- /public/images/files-1561127336221.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1561127336221.jpg -------------------------------------------------------------------------------- /public/images/files-1561127355498.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1561127355498.jpg -------------------------------------------------------------------------------- /public/images/files-1561127363760.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mateusfmachado/edz-api/HEAD/public/images/files-1561127363760.jpg -------------------------------------------------------------------------------- /config/database.json: -------------------------------------------------------------------------------- 1 | { 2 | "dbTest": "mongodb://localhost:27017/ecommerce-api-teste", 3 | "dbProduction": "mongodb://localhost:27017/ecommerce-api" 4 | } -------------------------------------------------------------------------------- /config/email.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | host: "smtp.gmail.com", 3 | port: 465, 4 | auth: { 5 | user: "email", 6 | pass: "senha" 7 | } 8 | }; -------------------------------------------------------------------------------- /config/correios.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // nCdEmpresa: "", 3 | // sDsSenha :"", 4 | nCdServico: "40010,41106", // Sedex Varejo e PAC Varejo, 5 | sCepOrigem: "04852-410" 6 | }; -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | router.use('/v1/api', require('./api/v1/')); 4 | router.get('/', (req, res, next) => res.send({ ok: true })); 5 | 6 | module.exports = router; -------------------------------------------------------------------------------- /views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | Loja TI 3 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /models/index.js: -------------------------------------------------------------------------------- 1 | require("./loja"); 2 | 3 | require("./usuario"); 4 | require("./cliente"); 5 | 6 | require("./categoria"); 7 | require("./produto"); 8 | require("./avaliacao"); 9 | require("./variacao"); 10 | 11 | require("./pedido"); 12 | require("./pagamento"); 13 | require("./entrega"); 14 | 15 | require("./registroPedido"); -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | secret: process.env.NODE_ENV === "production" ? process.env.SECRET : "secret", 3 | api: process.env.NODE_ENV === "production" ? "https://api.loja-teste.ampliee.com" : "http://localhost:3000", 4 | loja: process.env.NODE_ENV === "production" ? "https://loja-teste.ampliee.com" : "http://localhost:8000" 5 | }; -------------------------------------------------------------------------------- /config/multer.js: -------------------------------------------------------------------------------- 1 | const multer = require("multer"); 2 | 3 | const storage = multer.diskStorage({ 4 | destination: (req, file, callback) => callback(null, __dirname + '/../public/images'), 5 | filename: (req, file, callback) => callback(null, file.fieldname + '-' + Date.now() + '.jpg' ) 6 | }); 7 | 8 | const upload = multer({ storage }); 9 | 10 | module.exports = upload; -------------------------------------------------------------------------------- /config/pagseguro.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: process.env.NODE_ENV === 'production' ? "live" : 'sandbox', 3 | sandbox: process.env.NODE_ENV === 'production' ? false : true, 4 | sandbox_email: process.env.NODE_ENV === 'production' ? null : 'email@sandbox.pagseguro.com.br', 5 | email: 'email', 6 | token: 'token', 7 | notificationURL: "https://api.loja-teste.ampliee.com/v1/api/pagamentos/notificacao", 8 | } -------------------------------------------------------------------------------- /models/registroPedido.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"), 2 | Schema = mongoose.Schema; 3 | 4 | const RegistroPedidoSchema = Schema({ 5 | pedido: { type: Schema.Types.ObjectId, ref: "Pedido", required: true }, 6 | tipo: { type: String, required: true }, 7 | situacao: { type: String, required: true }, 8 | data: { type: Date, default: Date.now }, 9 | payload: { type: Object } 10 | }, { timestamps: true }); 11 | 12 | module.export = mongoose.model("RegistroPedido", RegistroPedidoSchema); -------------------------------------------------------------------------------- /models/categoria.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | const CategoriaSchema = Schema({ 5 | nome: { type: String, required: true }, 6 | codigo: { type: String, required: true }, 7 | disponibilidade: { type: Boolean, default: true }, 8 | produtos: { type: [{ type: Schema.Types.ObjectId, ref: "Produto" }] }, 9 | loja: { type: Schema.Types.ObjectId, ref: "Loja" } 10 | },{ timestamps: true }); 11 | 12 | module.exports = mongoose.model("Categoria", CategoriaSchema); -------------------------------------------------------------------------------- /models/avaliacao.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | const AvaliacaoSchema = Schema({ 5 | nome: { type: String, required: true }, 6 | texto: { type: String, required: true }, 7 | pontuacao: { type: Number, default: 1 }, 8 | produto: { type: Schema.Types.ObjectId, ref: "Produto", required: true }, 9 | loja: { type: Schema.Types.ObjectId, ref: "Loja", required: true } 10 | },{ timestamps: true }); 11 | 12 | module.exports = mongoose.model("Avaliacao", AvaliacaoSchema); -------------------------------------------------------------------------------- /routes/api/v1/index.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | router.use("/usuarios", require("./usuarios")); 4 | router.use("/clientes", require("./clientes")); 5 | router.use("/lojas", require("./lojas")); 6 | 7 | router.use("/categorias", require("./categorias")); 8 | router.use("/produtos", require("./produtos")); 9 | router.use("/avaliacoes", require("./avaliacoes")); 10 | router.use("/variacoes", require("./variacoes")); 11 | 12 | router.use("/pedidos", require("./pedidos")); 13 | router.use("/entregas", require("./entregas")); 14 | router.use("/pagamentos", require("./pagamentos")); 15 | 16 | module.exports = router; -------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require("express-jwt"); 2 | const secret = require("../config").secret; 3 | 4 | function getTokenFromHeader(req){ 5 | if(!req.headers.authorization) return null; 6 | const token = req.headers.authorization.split(" "); 7 | if(token[0] !== "Ecommerce") return null; 8 | return token[1]; 9 | } 10 | 11 | const auth = { 12 | required: jwt({ 13 | secret, 14 | userProperty: 'payload', 15 | getToken: getTokenFromHeader 16 | }), 17 | optional: jwt({ 18 | secret, 19 | userProperty: 'payload', 20 | credentialsRequired: false, 21 | getToken: getTokenFromHeader 22 | }) 23 | }; 24 | 25 | module.exports = auth; -------------------------------------------------------------------------------- /routes/api/v1/entregas.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const EntregaController = require("../../../controllers/EntregaController"); 4 | 5 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 6 | const Validation = require("express-validation"); 7 | const { EntregaValidation } = require("../../../controllers/validacoes/entregaValidation"); 8 | const auth = require("../../auth"); 9 | 10 | const entregaController = new EntregaController(); 11 | 12 | router.get("/:id", auth.required, Validation(EntregaValidation.show), entregaController.show); 13 | router.put("/:id", auth.required, LojaValidation.admin, Validation(EntregaValidation.update), entregaController.update); 14 | router.post("/calcular", Validation(EntregaValidation.calcular), entregaController.calcular); 15 | 16 | module.exports = router; -------------------------------------------------------------------------------- /routes/api/v1/lojas.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const auth = require("../../auth"); 4 | const Validation = require("express-validation"); 5 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 6 | 7 | const LojaController = require("../../../controllers/LojaController"); 8 | const lojaController = new LojaController(); 9 | 10 | router.get("/", lojaController.index); // testado 11 | router.get("/:id", Validation(LojaValidation.show), lojaController.show); // testado 12 | 13 | router.post("/", auth.required, Validation(LojaValidation.store), lojaController.store); // testado 14 | router.put("/:id", auth.required, LojaValidation.admin, Validation(LojaValidation.update), lojaController.update); // testado 15 | router.delete("/:id", auth.required, LojaValidation.admin, lojaController.remove); // testado 16 | 17 | module.exports = router; -------------------------------------------------------------------------------- /models/loja.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const uniqueValidator = require('mongoose-unique-validator'); 3 | 4 | const LojaSchema = mongoose.Schema({ 5 | nome: { type: String, required: true }, 6 | cnpj: { type: String, required: true, unique: true }, 7 | email: { type: String }, 8 | telefones: { 9 | type: [{ type: String }] 10 | }, 11 | endereco: { 12 | type: { 13 | local: { type: String, required: true }, 14 | numero: { type: String, required: true }, 15 | complemento: { type: String }, 16 | bairro: { type: String, required: true }, 17 | cidade: { type: String, required: true }, 18 | CEP: { type: String, required: true } 19 | }, 20 | required: true 21 | } 22 | },{ timestamps: true }); 23 | 24 | LojaSchema.plugin(uniqueValidator, { message: "já está sendo utilizado" }); 25 | 26 | module.exports = mongoose.model("Loja", LojaSchema); -------------------------------------------------------------------------------- /routes/api/v1/avaliacoes.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const AvaliacaoController = require("../../../controllers/AvaliacaoController"); 4 | 5 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 6 | const Validation = require("express-validation"); 7 | const { AvaliacaoValidation } = require("../../../controllers/validacoes/avaliacaoValidation"); 8 | const auth = require("../../auth"); 9 | 10 | const avaliacaoController = new AvaliacaoController(); 11 | 12 | // CLIENTES/VISITANTES 13 | router.get("/", Validation(AvaliacaoValidation.index), avaliacaoController.index); 14 | router.get("/:id", Validation(AvaliacaoValidation.show), avaliacaoController.show); 15 | router.post("/", auth.required, Validation(AvaliacaoValidation.store), avaliacaoController.store); 16 | 17 | // ADMIN 18 | router.delete("/:id", auth.required, LojaValidation.admin, Validation(AvaliacaoValidation.remove), avaliacaoController.remove); 19 | 20 | module.exports = router; -------------------------------------------------------------------------------- /models/produto.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const mongoosePaginate = require("mongoose-paginate"); 3 | const Schema = mongoose.Schema; 4 | 5 | const ProdutoSchema = Schema({ 6 | titulo: { type: String, required: true }, 7 | disponibilidade: { type: Boolean, default: true }, 8 | descricao: { type: String, required: true }, 9 | fotos: { type: Array, default: [] }, 10 | preco: { type: Number, required: true }, 11 | promocao: { type: Number }, 12 | sku: { type: String, required: true }, 13 | categoria: { type: Schema.Types.ObjectId, ref: "Categoria" }, 14 | loja: { type: Schema.Types.ObjectId, ref: "Loja" }, 15 | avaliacoes: { type: [{ type: Schema.Types.ObjectId, ref: "Avaliacoes"}] }, 16 | variacoes: { type: [{ type: Schema.Types.ObjectId, ref: "Variacoes"}] } 17 | }, { timestamps: true }); 18 | 19 | ProdutoSchema.plugin(mongoosePaginate); 20 | 21 | ProdutoSchema.index({ titulo: "text", descricao: "text", sku: "text" }); 22 | 23 | module.exports = mongoose.model("Produto", ProdutoSchema); -------------------------------------------------------------------------------- /controllers/validacoes/usuarioValidation.js: -------------------------------------------------------------------------------- 1 | const BaseJoi = require("joi"); 2 | const Extension = require("joi-date-extensions"); 3 | const Joi = BaseJoi.extend(Extension); 4 | 5 | const UsuarioValidation = { 6 | show: { 7 | params: { 8 | id: Joi.string().alphanum().length(24).required() 9 | } 10 | }, 11 | store:{ 12 | body: { 13 | nome: Joi.string().required(), 14 | email: Joi.string().email().required(), 15 | password: Joi.string().required(), 16 | loja: Joi.string().alphanum().length(24).required() 17 | } 18 | }, 19 | update:{ 20 | body: { 21 | nome: Joi.string().optional(), 22 | email: Joi.string().email().optional(), 23 | password: Joi.string().optional() 24 | } 25 | }, 26 | login: { 27 | body: { 28 | email: Joi.string().email().required(), 29 | password: Joi.string().required() 30 | } 31 | } 32 | }; 33 | 34 | module.exports = { 35 | UsuarioValidation 36 | }; -------------------------------------------------------------------------------- /models/pedido.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const mongoosePaginate = require("mongoose-paginate"); 3 | const Schema = mongoose.Schema; 4 | 5 | const PedidoSchema = Schema({ 6 | cliente: { type: Schema.Types.ObjectId, ref: "Cliente", required: true }, 7 | carrinho: { 8 | type: [{ 9 | produto: { type: Schema.Types.ObjectId, ref: "Produto", required: true }, 10 | variacao: { type: Schema.Types.ObjectId, ref: "Variacao", required: true }, 11 | produtoEstatico: { type: String }, 12 | quantidade: { type: Number, default: 1 }, 13 | precoUnitario: { type: Number, required: true } 14 | }] 15 | }, 16 | pagamento: { type: Schema.Types.ObjectId, ref: "Pagamento", required: true }, 17 | entrega: { type: Schema.Types.ObjectId, ref: "Entrega", required: true }, 18 | cancelado: { type: Boolean, default: false }, 19 | loja: { type: Schema.Types.ObjectId, ref: "Loja", required: true } 20 | }, { timestamps: true }); 21 | 22 | PedidoSchema.plugin(mongoosePaginate); 23 | 24 | module.exports = mongoose.model("Pedido", PedidoSchema); -------------------------------------------------------------------------------- /controllers/validacoes/avaliacaoValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const AvaliacaoValidation = { 4 | index: { 5 | query: { 6 | produto: Joi.string().alphanum().length(24).required(), 7 | loja: Joi.string().alphanum().length(24).required() 8 | } 9 | }, 10 | show: { 11 | query: { 12 | produto: Joi.string().alphanum().length(24).required(), 13 | loja: Joi.string().alphanum().length(24).required() 14 | }, 15 | params: { 16 | id: Joi.string().alphanum().length(24).required() 17 | } 18 | }, 19 | store: { 20 | query: { 21 | produto: Joi.string().alphanum().length(24).required(), 22 | loja: Joi.string().alphanum().length(24).required() 23 | }, 24 | body: { 25 | nome: Joi.string().required(), 26 | pontuacao: Joi.number().min(1).max(5).required(), 27 | texto: Joi.string().required() 28 | } 29 | }, 30 | remove: { 31 | params: { 32 | id: Joi.string().alphanum().length(24).required() 33 | } 34 | } 35 | }; 36 | 37 | module.exports = { AvaliacaoValidation }; -------------------------------------------------------------------------------- /models/variacao.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | const VariacaoSchema = Schema({ 5 | codigo: { type: String, required: true, unique: true }, 6 | nome: { type: String, required: true }, 7 | preco: { type: Number, required: true }, 8 | promocao: { type: Number }, 9 | fotos: { type: Array, default: [] }, 10 | entrega: { 11 | type: { 12 | dimensoes: { 13 | type: { 14 | alturaCm: { type: Number }, 15 | larguraCm: { type: Number }, 16 | profundidadeCm: { type: Number } 17 | }, 18 | required: true 19 | }, 20 | pesoKg: { type: Number, required: true }, 21 | freteGratis: { type: Boolean, default: false } 22 | } 23 | }, 24 | quantidade: { type: Number, default: 0 }, 25 | quantidadeBloqueada: { type: Number, default: 0 }, 26 | produto: { type: Schema.Types.ObjectId, ref: "Produto", required: true }, 27 | loja: { type: Schema.Types.ObjectId, ref: "Loja", required: true } 28 | },{ timestamps: true }); 29 | 30 | module.exports = mongoose.model("Variacao", VariacaoSchema); -------------------------------------------------------------------------------- /routes/api/v1/pagamentos.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const PagamentoController = require("../../../controllers/PagamentoController"); 4 | 5 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 6 | const { PagamentoValidation } = require("../../../controllers/validacoes/pagamentoValidation"); 7 | const Validation = require("express-validation"); 8 | const auth = require("../../auth"); 9 | 10 | const pagamentoController = new PagamentoController(); 11 | 12 | // TESTE 13 | if(process.env.NODE_ENV !== "production"){ 14 | router.get("/tokens", (req,res) => res.render("pagseguro/index")); 15 | } 16 | 17 | // PAGSEGURO 18 | router.post("/notificacao", pagamentoController.verNotificacao); 19 | router.get("/session", pagamentoController.getSessionId); 20 | 21 | // CLIENTE 22 | router.get("/:id", auth.required, Validation(PagamentoValidation.show), pagamentoController.show); 23 | router.post("/pagar/:id", auth.required, Validation(PagamentoValidation.pagar), pagamentoController.pagar); 24 | 25 | // ADMIN 26 | router.put("/:id", auth.required, LojaValidation.admin, Validation(PagamentoValidation.update), pagamentoController.update); 27 | 28 | module.exports = router; -------------------------------------------------------------------------------- /models/entrega.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"), 2 | mongoosePaginate = require("mongoose-paginate"), 3 | Schema = mongoose.Schema; 4 | 5 | const EntregaSchema = Schema({ 6 | status: { type: String, required: true }, 7 | codigoRastreamento: { type: String }, 8 | tipo: { type: String, required: true }, 9 | custo: { type: Number, required: true }, 10 | prazo: { type: Number, required: true }, 11 | endereco: { 12 | type: { 13 | local: { type: String, required: true }, 14 | numero: { type: String, required: true }, 15 | complemento: { type: String }, 16 | bairro: { type: String, required: true }, 17 | cidade: { type: String, required: true }, 18 | estado: { type: String, required: true }, 19 | CEP: { type: String, required: true } 20 | }, 21 | required: true 22 | }, 23 | pedido: { type: Schema.Types.ObjectId, ref: "Pedido", required: true }, 24 | loja: { type: Schema.Types.ObjectId, ref: "Loja", required: true }, 25 | payload: { type: Object } 26 | }, { timestamps: true }); 27 | 28 | EntregaSchema.plugin(mongoosePaginate); 29 | 30 | module.exports = mongoose.model("Entrega", EntregaSchema); -------------------------------------------------------------------------------- /models/cliente.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const mongoosePaginate = require("mongoose-paginate"); 3 | const Schema = mongoose.Schema; 4 | 5 | const ClienteSchema = Schema({ 6 | usuario: { type: Schema.Types.ObjectId, ref: "Usuario", required: true }, 7 | nome: { type: String, required: true }, 8 | dataDeNascimento: { type: Date, required: true }, 9 | cpf: { type: String, required: true }, 10 | telefones: { type: [{ type: String }] }, 11 | deletado: { type: Boolean, default: false }, 12 | loja: { type: Schema.Types.ObjectId, ref: "Loja", required: true }, 13 | endereco: { 14 | type: { 15 | local: { type: String, required: true }, 16 | numero: { type: String, required: true }, 17 | complemento: { type: String }, 18 | bairro: { type: String, required: true }, 19 | cidade: { type: String, required: true }, 20 | estado: { type: String, required: true }, 21 | CEP: { type: String, required: true } 22 | }, 23 | required: true 24 | } 25 | }, { timestamps: true }); 26 | 27 | ClienteSchema.plugin(mongoosePaginate); 28 | 29 | ClienteSchema.index({ nome: "text" }); 30 | 31 | module.exports = mongoose.model("Cliente", ClienteSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce-api", 3 | "version": "0.0.1", 4 | "description": "API para Curso Ecommerce do Zero", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "mocha --timeout 2000 --exit", 8 | "dev": "NODE_ENV=development nodemon server.js", 9 | "build": "NODE_ENV=production pm2 start server.js --name=\"ecommerce-api\"" 10 | }, 11 | "author": "Mateus Machado", 12 | "license": "ISC", 13 | "dependencies": { 14 | "body-parser": "^1.18.3", 15 | "compression": "^1.7.3", 16 | "cors": "^2.8.5", 17 | "crypto": "^1.0.1", 18 | "ejs": "^2.6.1", 19 | "express": "^4.16.4", 20 | "express-jwt": "^5.3.1", 21 | "express-validation": "^1.0.2", 22 | "faker": "^4.1.0", 23 | "joi": "^14.3.1", 24 | "joi-date-extensions": "^1.2.0", 25 | "jsonwebtoken": "^8.4.0", 26 | "moment": "^2.23.0", 27 | "mongoose": "^5.4.0", 28 | "mongoose-paginate": "^5.0.3", 29 | "mongoose-unique-validator": "^2.0.2", 30 | "morgan": "^1.9.1", 31 | "multer": "^1.4.1", 32 | "node-correios": "^2.2.0", 33 | "node-pagseguro": "^0.1.9", 34 | "nodemailer": "^4.7.0", 35 | "nodemon": "^1.18.9", 36 | "request": "^2.88.0", 37 | "xml2js": "^0.4.19", 38 | "xml2json": "^0.11.2" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /routes/api/v1/variacoes.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const VariacaoController = require("../../../controllers/VariacaoController"); 4 | 5 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 6 | const auth = require("../../auth"); 7 | const upload = require("../../../config/multer"); 8 | 9 | const { VariacaoValidation } = require("../../../controllers/validacoes/variacaoValidation"); 10 | const Validation = require("express-validation"); 11 | 12 | const variacaoController = new VariacaoController(); 13 | 14 | router.get("/", Validation(VariacaoValidation.index), variacaoController.index); 15 | router.get("/:id", Validation(VariacaoValidation.show), variacaoController.show); 16 | 17 | router.post("/", auth.required, LojaValidation.admin, Validation(VariacaoValidation.store), variacaoController.store); 18 | router.put("/:id", auth.required, LojaValidation.admin, Validation(VariacaoValidation.update), variacaoController.update); 19 | router.put("/images/:id", auth.required, LojaValidation.admin, Validation(VariacaoValidation.updateImages), upload.array("files", 4), variacaoController.updateImages); 20 | router.delete("/:id", auth.required, LojaValidation.admin, Validation(VariacaoValidation.remove), variacaoController.remove); 21 | 22 | module.exports = router; -------------------------------------------------------------------------------- /routes/api/v1/usuarios.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const auth = require("../../auth"); 3 | const UsuarioController = require("../../../controllers/UsuarioController"); 4 | 5 | const Validation = require("express-validation"); 6 | const { UsuarioValidation } = require("../../../controllers/validacoes/usuarioValidation"); 7 | 8 | const usuarioController = new UsuarioController(); 9 | 10 | router.post("/login", Validation(UsuarioValidation.login), usuarioController.login); // testado 11 | router.post("/registrar", Validation(UsuarioValidation.store), usuarioController.store); // testado 12 | router.put("/", auth.required, Validation(UsuarioValidation.update), usuarioController.update); // testado 13 | router.delete("/", auth.required, usuarioController.remove); // testado 14 | 15 | router.get("/recuperar-senha", usuarioController.showRecovery); // testado 16 | router.post("/recuperar-senha", usuarioController.createRecovery); // testado 17 | router.get("/senha-recuperada", usuarioController.showCompleteRecovery); // testado 18 | router.post("/senha-recuperada", usuarioController.completeRecovery); // testado 19 | 20 | router.get("/", auth.required, usuarioController.index); // testado 21 | router.get("/:id", auth.required, Validation(UsuarioValidation.show), usuarioController.show);// testado 22 | 23 | module.exports = router; -------------------------------------------------------------------------------- /controllers/validacoes/categoriaValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const CategoriaValidation = { 4 | index: { 5 | query: { 6 | loja: Joi.string().alphanum().length(24).required() 7 | } 8 | }, 9 | indexDisponiveis: { 10 | query: { 11 | loja: Joi.string().alphanum().length(24).required() 12 | } 13 | }, 14 | show: { 15 | query: { 16 | loja: Joi.string().alphanum().length(24).required() 17 | }, 18 | params: { 19 | id: Joi.string().alphanum().length(24).required() 20 | } 21 | }, 22 | store: { 23 | body: { 24 | nome: Joi.string().required(), 25 | codigo: Joi.string().required() 26 | } 27 | }, 28 | update: { 29 | params: { 30 | id: Joi.string().alphanum().length(24).required() 31 | }, 32 | body: { 33 | nome: Joi.string().optional(), 34 | codigo: Joi.string().optional(), 35 | disponibilidade: Joi.boolean().optional(), 36 | produtos: Joi.array().items(Joi.string().alphanum().length(24)).optional() 37 | } 38 | }, 39 | remove: { 40 | params: { 41 | id: Joi.string().alphanum().length(24).required() 42 | } 43 | } 44 | }; 45 | 46 | module.exports = { CategoriaValidation }; -------------------------------------------------------------------------------- /routes/api/v1/categorias.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const CategoriaController = require("../../../controllers/CategoriaController"); 4 | 5 | const auth = require("../../auth"); 6 | const Validation = require("express-validation"); 7 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 8 | const { CategoriaValidation } = require("../../../controllers/validacoes/categoriaValidation"); 9 | 10 | const categoriaController = new CategoriaController(); 11 | 12 | router.get("/", Validation(CategoriaValidation.index), categoriaController.index); 13 | router.get("/disponiveis", Validation(CategoriaValidation.indexDisponiveis), categoriaController.indexDisponiveis); 14 | router.get("/:id", Validation(CategoriaValidation.show), categoriaController.show); 15 | 16 | router.post("/", auth.required, LojaValidation.admin, Validation(CategoriaValidation.store), categoriaController.store); 17 | router.put("/:id", auth.required, LojaValidation.admin, Validation(CategoriaValidation.update), categoriaController.update); 18 | router.delete("/:id", auth.required, LojaValidation.admin, Validation(CategoriaValidation.remove), categoriaController.remove); 19 | 20 | // ROTAS AO PRODUTO 21 | router.get("/:id/produtos", categoriaController.showProdutos); 22 | router.put("/:id/produtos", auth.required, LojaValidation.admin, categoriaController.updateProdutos ); 23 | 24 | module.exports = router; -------------------------------------------------------------------------------- /helpers/email-recovery.js: -------------------------------------------------------------------------------- 1 | const transporter = require("nodemailer").createTransport(require("../config/email")); 2 | const { api: link } = require("../config/index"); 3 | 4 | module.exports = ({ usuario, recovery }, cb) => { 5 | const message = ` 6 |

Recuperacao de Senha

7 |
8 |

9 | Aqui está o link para redefinir a sua senha. Acesse ele e digite sua nova senha: 10 |

11 | 12 | ${link}/v1/api/usuarios/senha-recuperada?token=${recovery.token} 13 | 14 |


15 |

16 | Obs.: Se você não solicitou a redefinicao, apenas ignore esse email. 17 |

18 |
19 |

Atenciosamente, Loja TI

20 | `; 21 | 22 | const opcoesEmail = { 23 | from: "naoresponder@lojati.com", 24 | to: usuario.email, 25 | subject: "Redefinicao de Senha - Loja TI", 26 | html: message 27 | }; 28 | 29 | if( process.env.NODE_ENV === "production" ){ 30 | transporter.sendMail(opcoesEmail, (error, info) => { 31 | if(error){ 32 | console.log(error); 33 | return cb("Aconteceu um erro no envio do email, tente novamente."); 34 | } else { 35 | return cb(null, "Link para redefinicao de senha foi enviado com sucesso para seu email."); 36 | } 37 | }); 38 | } else { 39 | console.log(opcoesEmail); 40 | return cb(null, "Link para redefinicao de senha foi enviado com sucesso para seu email."); 41 | } 42 | }; -------------------------------------------------------------------------------- /routes/api/v1/pedidos.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const PedidoController = require("../../../controllers/PedidoController"); 4 | 5 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 6 | const Validation = require("express-validation"); 7 | const { PedidoValidation } = require("../../../controllers/validacoes/pedidoValidation"); 8 | const auth = require("../../auth"); 9 | 10 | const pedidoController = new PedidoController(); 11 | 12 | // ADMIN 13 | router.get("/admin", auth.required, LojaValidation.admin, Validation(PedidoValidation.indexAdmin), pedidoController.indexAdmin); 14 | router.get("/admin/:id", auth.required, LojaValidation.admin, Validation(PedidoValidation.showAdmin), pedidoController.showAdmin); 15 | 16 | router.delete("/admin/:id", auth.required, LojaValidation.admin, Validation(PedidoValidation.removeAdmin), pedidoController.removeAdmin); 17 | 18 | // -- carrinho 19 | router.get("/admin/:id/carrinho", auth.required, LojaValidation.admin, Validation(PedidoValidation.showCarrinhoPedidoAdmin), pedidoController.showCarrinhoPedidoAdmin); 20 | 21 | // CLIENTE 22 | router.get("/", auth.required, Validation(PedidoValidation.index), pedidoController.index); 23 | router.get("/:id", auth.required, Validation(PedidoValidation.show), pedidoController.show); 24 | 25 | router.post("/", auth.required, Validation(PedidoValidation.store), pedidoController.store); 26 | router.delete("/:id", auth.required, Validation(PedidoValidation.remove), pedidoController.remove); 27 | 28 | // -- carrinho 29 | router.get("/:id/carrinho", auth.required, Validation(PedidoValidation.showCarrinhoPedido), pedidoController.showCarrinhoPedido); 30 | 31 | module.exports = router; -------------------------------------------------------------------------------- /models/pagamento.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"), 2 | mongoosePaginate = require("mongoose-paginate"), 3 | Schema = mongoose.Schema; 4 | 5 | const PagamentoSchema = Schema({ 6 | valor: { type: Number, required: true }, 7 | forma: { type: String, required: true }, 8 | parcelas: { type: Number, default: 1 }, 9 | status: { type: String, required: true }, 10 | endereco: { 11 | type: { 12 | local: { type: String, required: true }, 13 | numero: { type: String, required: true }, 14 | complemento: { type: String }, 15 | bairro: { type: String, required: true }, 16 | cidade: { type: String, required: true }, 17 | estado: { type: String, required: true }, 18 | CEP: { type: String, required: true } 19 | }, 20 | required: true 21 | }, 22 | cartao: { 23 | type: { 24 | nomeCompleto: { type: String, required: true }, 25 | codigoArea: { type: String, required: true }, 26 | telefone: { type: String, required: true }, 27 | dataDeNascimento: { type: String, required: true }, 28 | credit_card_token: { type: String, required: true }, 29 | cpf: { type: String, required: true } 30 | } 31 | }, 32 | enderecoEntregaIgualCobranca: { type: Boolean, default: true }, 33 | pedido: { type: Schema.Types.ObjectId, ref: "Pedido", required: true }, 34 | loja: { type: Schema.Types.ObjectId, ref: "Loja", required: true }, 35 | payload: { type: Array }, 36 | pagSeguroCode: { type: String } 37 | }, { timestamps: true }); 38 | 39 | PagamentoSchema.plugin(mongoosePaginate); 40 | 41 | module.exports = mongoose.model("Pagamento", PagamentoSchema); -------------------------------------------------------------------------------- /routes/api/v1/produtos.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const ProdutoController = require("../../../controllers/ProdutoController"); 4 | 5 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 6 | const Validation = require("express-validation"); 7 | const { ProdutoValidation } = require("../../../controllers/validacoes/produtoValidation"); 8 | const auth = require("../../auth"); 9 | const upload = require("../../../config/multer"); 10 | 11 | const produtoController = new ProdutoController(); 12 | 13 | // ADMIN 14 | router.post("/", auth.required, LojaValidation.admin, Validation(ProdutoValidation.store), produtoController.store); 15 | router.put("/:id", auth.required, LojaValidation.admin, Validation(ProdutoValidation.update), produtoController.update); 16 | router.put("/images/:id", auth.required, LojaValidation.admin, Validation(ProdutoValidation.updateImages), upload.array("files", 4), produtoController.updateImages); 17 | router.delete("/:id", auth.required, LojaValidation.admin, Validation(ProdutoValidation.remove), produtoController.remove); 18 | 19 | // CLIENTES/VISITANTES 20 | router.get("/", Validation(ProdutoValidation.index), produtoController.index); 21 | router.get("/disponiveis", Validation(ProdutoValidation.indexDisponiveis), produtoController.indexDisponiveis); 22 | router.get("/search/:search", Validation(ProdutoValidation.search), produtoController.search); 23 | router.get("/:id", Validation(ProdutoValidation.show), produtoController.show); 24 | 25 | // VARIACOES 26 | router.get("/:id/variacoes", Validation(ProdutoValidation.showVariacoes), produtoController.showVariacoes); 27 | 28 | // AVALIACOES 29 | router.get("/:id/avaliacoes", Validation(ProdutoValidation.showAvaliacoes), produtoController.showAvaliacoes); 30 | 31 | module.exports = router; -------------------------------------------------------------------------------- /views/recovery/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include('../partials/header') %> 5 | 6 | 7 | 8 |
9 |
10 |
11 |

Recuperar Senha

12 |

13 | 14 | <% if(success){ %> 15 |
16 | <%= success %> 17 |
18 | <% } %> 19 | 20 | <% if(error){ %> 21 |
22 | <%= error %> 23 |
24 | <% } %> 25 | 26 |

27 | Digite seu email para receber as instrucoes de como reiniciar a sua senha 28 |

29 | 30 |
31 |
32 | 38 |
39 | 42 |
43 |
44 |
45 |
46 | 47 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | // PACOTES 2 | const compression = require("compression"); 3 | const express = require("express"); 4 | const ejs = require("ejs"); 5 | const bodyParser = require("body-parser"); 6 | const mongoose = require("mongoose"); 7 | const morgan = require("morgan"); 8 | const cors = require("cors"); 9 | 10 | // START 11 | const app = express(); 12 | 13 | // AMBIENTE 14 | const isProduction = process.env.NODE_ENV === "production"; 15 | const PORT = process.env.PORT || 3000; 16 | 17 | // ARQUIVOS ESTATICOS 18 | app.use("/public", express.static(__dirname + "/public")); 19 | app.use("/public/images", express.static(__dirname + "/public/images")); 20 | 21 | // SETUP MONGODB 22 | const dbs = require("./config/database"); 23 | const dbURI = isProduction ? dbs.dbProduction : dbs.dbTest; 24 | mongoose.connect(dbURI, { useNewUrlParser: true }); 25 | 26 | // SETUP EJS 27 | app.set("view engine", "ejs"); 28 | 29 | // CONFIGURACOES 30 | if(!isProduction) app.use(morgan("dev")); 31 | app.use(cors()); 32 | app.disable('x-powered-by'); 33 | app.use(compression()); 34 | 35 | // SETUP BODY PARSER 36 | app.use(bodyParser.urlencoded({ extended: false, limit: 1.5*1024*1024 })); 37 | app.use(bodyParser.json({ limit: 1.5*1024*1024 })); 38 | 39 | // MODELS 40 | require("./models"); 41 | // ROTAS 42 | app.use("/", require("./routes")); 43 | 44 | // 404 - ROTA 45 | app.use((req, res, next) => { 46 | const err = new Error("Not Found"); 47 | err.status = 404; 48 | next(err); 49 | }); 50 | 51 | // ROTA - 422, 500, 401 52 | app.use((err, req, res, next) => { 53 | res.status(err.status || 500); 54 | if(err.status !== 404) console.warn("Error: ", err.message, new Date()); 55 | res.json(err); 56 | }); 57 | 58 | // ESCUTAR 59 | app.listen(PORT, (err) => { 60 | if(err) throw err; 61 | console.log(`Rodando na //localhost:${PORT}`); 62 | }); -------------------------------------------------------------------------------- /routes/api/v1/clientes.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | 3 | const ClienteController = require("../../../controllers/ClienteController"); 4 | const { LojaValidation } = require("../../../controllers/validacoes/lojaValidation"); 5 | const { ClienteValidation } = require("../../../controllers/validacoes/clienteValidation"); 6 | const Validation = require("express-validation"); 7 | const auth = require("../../auth"); 8 | 9 | const clienteController = new ClienteController(); 10 | 11 | // ADMIN 12 | router.get("/", auth.required, LojaValidation.admin, Validation(ClienteValidation.index), clienteController.index); 13 | router.get("/search/:search/pedidos", auth.required, LojaValidation.admin, Validation(ClienteValidation.searchPedidos), clienteController.searchPedidos); 14 | router.get("/search/:search", auth.required, LojaValidation.admin, Validation(ClienteValidation.search), clienteController.search); 15 | router.get("/admin/:id", auth.required, LojaValidation.admin, Validation(ClienteValidation.showAdmin), clienteController.showAdmin); 16 | router.get("/admin/:id/pedidos", auth.required, LojaValidation.admin, Validation(ClienteValidation.showPedidosCliente), clienteController.showPedidosCliente); 17 | 18 | router.delete("/admin/:id", auth.required, LojaValidation.admin, clienteController.removeAdmin); 19 | 20 | router.put("/admin/:id", auth.required, LojaValidation.admin, Validation(ClienteValidation.updateAdmin), clienteController.updateAdmin); 21 | 22 | // CLIENTE 23 | router.get("/:id", auth.required, Validation(ClienteValidation.show), clienteController.show); 24 | 25 | router.post("/", Validation(ClienteValidation.store), clienteController.store); 26 | router.put("/:id", auth.required, Validation(ClienteValidation.update), clienteController.update); 27 | router.delete("/:id", auth.required, clienteController.remove); 28 | 29 | 30 | module.exports = router; -------------------------------------------------------------------------------- /controllers/validacoes/carrinhoValidation.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Produto = mongoose.model("Produto"); 4 | const Variacao = mongoose.model("Variacao"); 5 | 6 | const getCarrinhoValue = (carrinho) => { 7 | let precoTotal = 0; 8 | let quantidade = 0; 9 | carrinho.forEach(item => { 10 | precoTotal += item.precoUnitario * item.quantidade; 11 | quantidade += item.quantidade; 12 | }); 13 | return { precoTotal, quantidade }; 14 | }; 15 | 16 | const getLojaValue = async (carrinho) => { 17 | const results = await Promise.all(carrinho.map(async (item) => { 18 | const produto = await Produto.findById(item.produto); 19 | const variacao = await Variacao.findById(item.variacao); 20 | let preco = 0; 21 | let qtd = 0; 22 | if( produto && variacao && produto.variacoes.map(item => item.toString()).includes(variacao._id.toString()) ){ 23 | let _preco = variacao.promocao || variacao.preco; 24 | preco = _preco * item.quantidade; 25 | qtd = item.quantidade; 26 | } 27 | return { preco, qtd }; 28 | })); 29 | let precoTotal = results.reduce((all, item) => all + item.preco, 0); 30 | let quantidade = results.reduce((all, item) => all + item.qtd, 0); 31 | return { precoTotal, quantidade }; 32 | } 33 | 34 | const CarrinhoValidation = async (carrinho) => { 35 | const { precoTotal: precoTotalCarrinho, quantidade: quantidadeTotalCarrinho } = getCarrinhoValue(carrinho); 36 | const { precoTotal: precoTotalLoja, quantidade: quantidadeTotalLoja } = await getLojaValue(carrinho); 37 | console.log(precoTotalCarrinho, quantidadeTotalCarrinho, precoTotalLoja, quantidadeTotalLoja) 38 | return precoTotalCarrinho === precoTotalLoja && quantidadeTotalCarrinho === quantidadeTotalLoja; 39 | } 40 | 41 | module.exports = CarrinhoValidation; -------------------------------------------------------------------------------- /controllers/LojaController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Loja = mongoose.model("Loja"); 3 | 4 | class LojaController { 5 | 6 | // GET / 7 | index(req,res,next){ 8 | Loja.find({ }).select("_id nome cnpj email telefones endereco") 9 | .then(lojas => res.send({ lojas })) 10 | .catch(next); 11 | } 12 | 13 | // GET /:id 14 | show(req,res,next){ 15 | Loja.findById(req.params.id).select("_id nome cnpj email telefones endereco") 16 | .then(loja => res.send({ loja })) 17 | .catch(next); 18 | } 19 | 20 | // POST / 21 | store(req,res,next){ 22 | const { nome, cnpj, email, telefones, endereco } = req.body; 23 | const loja = new Loja ({ nome, cnpj, email, telefones, endereco }); 24 | loja.save().then(() => res.send({ loja })).catch(next); 25 | } 26 | 27 | // PUT /:id 28 | update(req,res,next){ 29 | const { nome, cnpj, email, telefones, endereco } = req.body; 30 | Loja.findById(req.query.loja).then(loja => { 31 | if(!loja) return res.status(422).send({ error: "Loja não existe." }); 32 | 33 | if( nome ) loja.nome = nome; 34 | if( cnpj ) loja.cnpj = cnpj; 35 | if( email ) loja.email = email; 36 | if( telefones ) loja.telefones = telefones; 37 | if( endereco ) loja.endereco = endereco; 38 | 39 | loja.save().then(() => res.send({ loja })).catch(next); 40 | 41 | }) 42 | .catch(next); 43 | } 44 | 45 | // DELETE /:id 46 | remove(req,res,next){ 47 | Loja.findById(req.query.loja).then(loja => { 48 | if(!loja) return res.status(422).send({ error: "Loja não existe." }); 49 | loja.remove().then(() => res.send({ deleted: true })).catch(next); 50 | }) 51 | .catch(next); 52 | } 53 | 54 | } 55 | 56 | module.exports = LojaController; -------------------------------------------------------------------------------- /controllers/integracoes/correios.js: -------------------------------------------------------------------------------- 1 | const Correios = require("node-correios"), 2 | correios = new Correios(), 3 | config = require("../../config/correios"), 4 | { calcBox } = require("../../helpers/calcBox"); 5 | 6 | const calcularFrete = async ({ cep, produtos }) => { 7 | 8 | const _produtos = produtos.map(item => ({ 9 | pesoKg: item.variacao.entrega.pesoKg, 10 | profundidadeCm: item.variacao.entrega.dimensoes.profundidadeCm, 11 | alturaCm: item.variacao.entrega.dimensoes.alturaCm, 12 | larguraCm: item.variacao.entrega.dimensoes.larguraCm, 13 | quantidade: item.quantidade, 14 | preco: item.precoUnitario 15 | })); 16 | 17 | const caixa = calcBox(_produtos); 18 | const pesoTotal = _produtos.reduce((all, item) => all + ( item.pesoKg * item.quantidade ) , 0); 19 | const valorFinal = _produtos.reduce((all, item) => all + ( item.preco * item.quantidade ) , 0); 20 | 21 | try { 22 | const resultados = await Promise.all( 23 | config.nCdServico.split(',').map(async(servico)=>{ 24 | const resultado = await correios.calcPrecoPrazo({ 25 | nCdServico: servico, 26 | sCepOrigem: config.sCepOrigem, 27 | sCepDestino: cep, 28 | nVlPeso: pesoTotal, 29 | nCdFormato: 1, 30 | nVlComprimento: caixa.comprimento, 31 | nVlAltura: caixa.altura, 32 | nVlLargura: caixa.largura, 33 | nVlDiamentro: 0, 34 | nVlValorDeclarado: valorFinal < 19.5 ? 19.5 : valorFinal 35 | }); 36 | return { ...resultado[0] }; 37 | }) 38 | ); 39 | return resultados; 40 | } catch(e){ 41 | console.log(e); 42 | } 43 | } 44 | 45 | module.exports = { calcularFrete }; -------------------------------------------------------------------------------- /controllers/validacoes/quantidadeValidation.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Variacao = mongoose.model("Variacao"); 4 | 5 | const validarQuantidadeDisponivel = async (_carrinho) => { 6 | let todosTemQuantidadeDisponivel = true; 7 | try { 8 | 9 | const carrinho = await Promise.all(_carrinho.map(async item => { 10 | item.variacao = await Variacao.findById(item.variacao._id || item.variacao ); 11 | return item; 12 | })); 13 | 14 | carrinho.forEach(item => { 15 | if( !item.variacao.quantidade || item.variacao.quantidade < item.quantidade ){ 16 | todosTemQuantidadeDisponivel = false; 17 | } 18 | }); 19 | 20 | return todosTemQuantidadeDisponivel; 21 | } catch(e){ 22 | console.warn(e); 23 | return false; 24 | } 25 | }; 26 | 27 | const atualizarQuantidade = async (tipo, pedido) => { 28 | try { 29 | const carrinho = await Promise.all(pedido.carrinho.map(async item => { 30 | 31 | item.variacao = await Variacao.findById(item.variacao._id || item.variacao ); 32 | // ALTERACOES 33 | 34 | if( tipo === "salvar_pedido" ){ 35 | item.variacao.quantidade -= item.quantidade; 36 | item.variacao.quantidadeBloqueada += item.quantidade; 37 | } else if( tipo === "confirmar_pedido" ){ 38 | item.variacao.quantidadeBloqueada -= item.quantidade; 39 | } else if( tipo === "cancelar_pedido" ){ 40 | item.variacao.quantidadeBloqueada -= item.quantidade; 41 | item.variacao.quantidade += item.quantidade; 42 | } 43 | 44 | await item.variacao.save(); 45 | return item; 46 | })); 47 | return true; 48 | }catch(e){ 49 | console.warn(e); 50 | return e; 51 | } 52 | } 53 | 54 | module.exports = { 55 | validarQuantidadeDisponivel, 56 | atualizarQuantidade 57 | }; -------------------------------------------------------------------------------- /views/recovery/store.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include('../partials/header') %> 5 | 6 | 7 | 8 |
9 |
10 |
11 |

Recuperar Senha

12 |

13 | 14 | <% if(success){ %> 15 |
16 | <%= success %> 17 |
18 |
19 |

Agora pode fazer seu login novamente :)

20 |
21 | <% } %> 22 | 23 | <% if(error){ %> 24 |
25 | <%= error %> 26 |
27 | <% } %> 28 | 29 | <% if(!success) { %> 30 | 31 |

32 | Digite aqui sua nova senha 33 |

34 | 35 |
36 | 37 |
38 | 44 |
45 | 48 |
49 | 50 | <% } %> 51 |
52 |
53 |
54 | 55 | -------------------------------------------------------------------------------- /controllers/AvaliacaoController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Avaliacao = mongoose.model("Avaliacao"); 4 | const Produto = mongoose.model("Produto"); 5 | 6 | class AvaliacaoController { 7 | 8 | // GET / 9 | async index(req,res,next){ 10 | const { loja, produto } = req.query; 11 | try { 12 | const avaliacoes = await Avaliacao.find({ loja, produto }); 13 | return res.send({ avaliacoes }); 14 | } catch(e){ 15 | next(e); 16 | } 17 | } 18 | 19 | // GET /:id 20 | async show(req,res,next){ 21 | const { loja, produto } = req.query; 22 | const { id: _id } = req.params; 23 | try { 24 | const avaliacao = await Avaliacao.findOne({ _id, loja, produto }); 25 | return res.send({ avaliacao }); 26 | }catch(e){ 27 | next(e); 28 | } 29 | } 30 | 31 | // POST / - store 32 | async store(req,res,next){ 33 | const { nome, texto, pontuacao } = req.body; 34 | const { loja, produto } = req.query; 35 | try { 36 | const avaliacao = new Avaliacao({ nome, texto, pontuacao, loja, produto }); 37 | 38 | const _produto = await Produto.findById(produto); 39 | if(!_produto) return res.status(422).send({ error: "Produto não existe" }) 40 | _produto.avaliacoes.push(avaliacao._id); 41 | 42 | await _produto.save(); 43 | await avaliacao.save(); 44 | return res.send({ avaliacao }); 45 | }catch(e){ 46 | next(e); 47 | } 48 | } 49 | 50 | // DELETE /:id - remove 51 | async remove(req,res,next){ 52 | try { 53 | const avaliacao = await Avaliacao.findById(req.params.id); 54 | 55 | const produto = await Produto.findById(avaliacao.produto); 56 | produto.avaliacoes = produto.avaliacoes.filter(item => item.toString() !== avaliacao._id.toString()); 57 | await produto.save(); 58 | 59 | await avaliacao.remove(); 60 | return res.send({ deletado: true }); 61 | 62 | }catch(e){ 63 | next(e); 64 | } 65 | } 66 | 67 | } 68 | 69 | module.exports = AvaliacaoController; -------------------------------------------------------------------------------- /views/pagseguro/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | PagSeguro - Token 3 | 4 | 5 | 6 | 7 | 10 | 59 | -------------------------------------------------------------------------------- /controllers/validacoes/entregaValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const mongoose = require("mongoose"); 4 | 5 | const Produto = mongoose.model("Produto"); 6 | const Variacao = mongoose.model("Variacao"); 7 | 8 | const { calcularFrete } = require("../integracoes/correios"); 9 | 10 | const EntregaValidation = { 11 | show: { 12 | params: { 13 | id: Joi.string().alphanum().length(24).required() 14 | }, 15 | query: { 16 | loja: Joi.string().alphanum().length(24).required() 17 | } 18 | }, 19 | update: { 20 | params: { 21 | id: Joi.string().alphanum().length(24).required() 22 | }, 23 | query: { 24 | loja: Joi.string().alphanum().length(24).required() 25 | }, 26 | body: { 27 | status: Joi.string().optional(), 28 | codigoRastreamento: Joi.string().optional() 29 | } 30 | }, 31 | calcular: { 32 | body: { 33 | cep: Joi.string().required(), 34 | carrinho: Joi.array().items(Joi.object({ 35 | produto: Joi.string().alphanum().length(24).required(), 36 | variacao: Joi.string().alphanum().length(24).required(), 37 | precoUnitario: Joi.number().required(), 38 | quantidade: Joi.number().required() 39 | })).required() 40 | } 41 | } 42 | }; 43 | 44 | const checarValorPrazo = async (cep, carrinho, entrega) => { 45 | try { 46 | const _carrinho = await Promise.all(carrinho.map(async (item) => { 47 | item.produto = await Produto.findById(item.produto); 48 | item.variacao = await Variacao.findById(item.variacao); 49 | return item; 50 | })); 51 | const resultados = await calcularFrete({ cep, produtos: _carrinho }); 52 | let found = false; 53 | resultados.forEach(resultado => { 54 | if( 55 | resultado.Codigo.toString() === entrega.tipo && 56 | Number(resultado.Valor.replace(/,/g, ".")) === entrega.custo && 57 | resultado.PrazoEntrega === entrega.prazo.toString() 58 | ) found = true; 59 | }); 60 | return found; 61 | }catch(e){ 62 | console.log(e); 63 | return false; 64 | } 65 | }; 66 | 67 | module.exports = { EntregaValidation, checarValorPrazo }; -------------------------------------------------------------------------------- /controllers/validacoes/lojaValidation.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Usuario = mongoose.model("Usuario"); 3 | const Loja = mongoose.model("Loja"); 4 | 5 | const BaseJoi = require("joi"); 6 | const Extension = require("joi-date-extensions"); 7 | const Joi = BaseJoi.extend(Extension); 8 | 9 | const LojaValidation = { 10 | admin: (req,res,next) => { 11 | if(!req.payload.id) return res.sendStatus(401); 12 | const { loja } = req.query; 13 | if(!loja) return res.sendStatus(401); 14 | Usuario.findById(req.payload.id).then(usuario => { 15 | if(!usuario) return res.sendStatus(401); 16 | if(!usuario.loja) return res.sendStatus(401); 17 | if(!usuario.permissao.includes("admin")) return res.sendStatus(401); 18 | if(usuario.loja.toString() !== loja) return res.sendStatus(401); 19 | next(); 20 | }).catch(next); 21 | }, 22 | show: { 23 | params: { 24 | id: Joi.string().alphanum().length(24).required() 25 | } 26 | }, 27 | store: { 28 | body: { 29 | nome: Joi.string().required(), 30 | cnpj: Joi.string().length(18).required(), 31 | email: Joi.string().email().required(), 32 | telefones: Joi.array().items(Joi.string()).required(), 33 | endereco: Joi.object({ 34 | local: Joi.string().required(), 35 | numero: Joi.string().required(), 36 | complemento: Joi.string().optional(), 37 | bairro: Joi.string().required(), 38 | cidade: Joi.string().required(), 39 | CEP: Joi.string().required() 40 | }).required() 41 | } 42 | }, 43 | update: { 44 | body: { 45 | nome: Joi.string().optional(), 46 | cnpj: Joi.string().length(18).optional(), 47 | email: Joi.string().email().optional(), 48 | telefones: Joi.array().items(Joi.string()).optional(), 49 | endereco: Joi.object({ 50 | local: Joi.string().required(), 51 | numero: Joi.string().required(), 52 | complemento: Joi.string().optional(), 53 | bairro: Joi.string().required(), 54 | cidade: Joi.string().required(), 55 | CEP: Joi.string().required() 56 | }).optional() 57 | } 58 | } 59 | }; 60 | 61 | module.exports = { LojaValidation }; -------------------------------------------------------------------------------- /controllers/validacoes/produtoValidation.js: -------------------------------------------------------------------------------- 1 | const BaseJoi = require("joi"); 2 | const Extension = require("joi-date-extensions"); 3 | const Joi = BaseJoi.extend(Extension); 4 | 5 | const ProdutoValidation = { 6 | store: { 7 | body: { 8 | titulo: Joi.string().required(), 9 | descricao: Joi.string().required(), 10 | categoria: Joi.string().alphanum().length(24).required(), 11 | preco: Joi.number().required(), 12 | promocao: Joi.number(), 13 | sku: Joi.string().required() 14 | } 15 | }, 16 | update: { 17 | params: { 18 | id: Joi.string().alphanum().length(24).required() 19 | }, 20 | body: { 21 | titulo: Joi.string().optional(), 22 | descricao: Joi.string().optional(), 23 | categoria: Joi.string().alphanum().length(24).optional(), 24 | fotos: Joi.array().items(Joi.string()).optional(), 25 | preco: Joi.number().optional(), 26 | promocao: Joi.number(), 27 | sku: Joi.string().optional(), 28 | disponibilidade: Joi.boolean().optional() 29 | } 30 | }, 31 | updateImages: { 32 | params: { 33 | id: Joi.string().alphanum().length(24).required() 34 | } 35 | }, 36 | remove: { 37 | params: { 38 | id: Joi.string().alphanum().length(24).required() 39 | } 40 | }, 41 | index: { 42 | query: { 43 | loja: Joi.string().alphanum().length(24).required(), 44 | limit: Joi.number(), 45 | offset: Joi.number(), 46 | sortType: Joi.string() 47 | } 48 | }, 49 | indexDisponiveis: { 50 | query: { 51 | loja: Joi.string().alphanum().length(24).required(), 52 | limit: Joi.number(), 53 | offset: Joi.number(), 54 | sortType: Joi.string() 55 | } 56 | }, 57 | search: { 58 | query: { 59 | loja: Joi.string().alphanum().length(24).required(), 60 | limit: Joi.number(), 61 | offset: Joi.number(), 62 | sortType: Joi.string() 63 | }, 64 | params: { 65 | search: Joi.string().required() 66 | } 67 | }, 68 | show: { 69 | params: { 70 | id: Joi.string().alphanum().length(24).required() 71 | } 72 | }, 73 | showAvaliacoes: { 74 | params: { 75 | id: Joi.string().alphanum().length(24).required() 76 | } 77 | }, 78 | showVariacoes: { 79 | params: { 80 | id: Joi.string().alphanum().length(24).required() 81 | } 82 | } 83 | }; 84 | 85 | module.exports = { ProdutoValidation }; -------------------------------------------------------------------------------- /models/usuario.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"), 2 | Schema= mongoose.Schema; 3 | const uniqueValidator = require('mongoose-unique-validator'); 4 | const crypto = require("crypto"); 5 | const jwt = require("jsonwebtoken"); 6 | const secret = require("../config").secret; 7 | 8 | const UsuarioSchema = new mongoose.Schema({ 9 | nome: { 10 | type: String, 11 | required: [true,"não pode ficar vazio."] 12 | }, 13 | email: { 14 | type: String, 15 | lowercase: true, 16 | unique: true, 17 | required: [true,"não pode ficar vazio."], 18 | index: true, 19 | match: [/\S+@\S+\.\S+/, 'é inválido.'] 20 | }, 21 | loja: { 22 | type: Schema.Types.ObjectId, 23 | ref: "Loja", 24 | required: [true,"não pode ficar vazia."] 25 | }, 26 | permissao: { 27 | type: Array, 28 | default: ["cliente"] 29 | }, 30 | hash: { type: String }, 31 | salt: { type: String }, 32 | recovery: { 33 | type: { 34 | token: String, 35 | date: Date 36 | }, 37 | default: {} 38 | } 39 | },{ timestamps: true }); 40 | 41 | UsuarioSchema.plugin(uniqueValidator, { message: "já está sendo utilizado" }); 42 | 43 | UsuarioSchema.methods.setSenha = function(password){ 44 | this.salt = crypto.randomBytes(16).toString("hex"); 45 | this.hash = crypto.pbkdf2Sync(password, this.salt, 10000,512, "sha512").toString("hex"); 46 | }; 47 | 48 | UsuarioSchema.methods.validarSenha = function(password){ 49 | const hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, "sha512").toString("hex"); 50 | return hash === this.hash; 51 | }; 52 | 53 | UsuarioSchema.methods.gerarToken = function(){ 54 | const hoje = new Date(); 55 | const exp = new Date(hoje); 56 | exp.setDate(hoje.getDate() + 15); 57 | 58 | return jwt.sign({ 59 | id: this._id, 60 | email: this.email, 61 | nome: this.nome, 62 | exp: parseFloat(exp.getTime() / 1000, 10) 63 | }, secret); 64 | }; 65 | 66 | UsuarioSchema.methods.enviarAuthJSON = function(){ 67 | return { 68 | _id: this._id, 69 | nome: this.nome, 70 | email: this.email, 71 | loja: this.loja, 72 | role: this.permissao, 73 | token: this.gerarToken() 74 | }; 75 | }; 76 | 77 | // RECUPERACAO 78 | UsuarioSchema.methods.criarTokenRecuperacaoSenha = function(){ 79 | this.recovery = {}; 80 | this.recovery.token = crypto.randomBytes(16).toString("hex"); 81 | this.recovery.date = new Date( new Date().getTime() + 24*60*60*1000 ); 82 | return this.recovery; 83 | }; 84 | 85 | UsuarioSchema.methods.finalizarTokenRecuperacaoSenha = function(){ 86 | this.recovery = { token: null, date: null }; 87 | return this.recovery; 88 | }; 89 | 90 | module.exports = mongoose.model("Usuario", UsuarioSchema); -------------------------------------------------------------------------------- /controllers/EntregaController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { calcularFrete } = require("./integracoes/correios"); 3 | 4 | const Entrega = mongoose.model("Entrega"); 5 | const Pedido = mongoose.model("Pedido"); 6 | const Produto = mongoose.model("Produto"); 7 | const Variacao = mongoose.model("Variacao"); 8 | const RegistroPedido = mongoose.model("RegistroPedido"); 9 | 10 | const EmailController = require("./EmailController"); 11 | 12 | class EntregaController { 13 | 14 | // GET /:id show 15 | async show (req,res,next){ 16 | try { 17 | const entrega = await Entrega.findOne({ _id: req.params.id, loja: req.query.loja }); 18 | const registros = await RegistroPedido.find({ pedido: entrega.pedido, tipo: "entrega" }); 19 | return res.send({ entrega,registros }); 20 | }catch(e){ 21 | next(e); 22 | } 23 | } 24 | 25 | // PUT /:id 26 | async update(req,res,next){ 27 | const { status, codigoRastreamento } = req.body; 28 | const { loja } = req.query; 29 | try { 30 | const entrega = await Entrega.findOne({ loja, _id: req.params.id }); 31 | 32 | if(status) entrega.status = status; 33 | if(codigoRastreamento) entrega.codigoRastreamento = codigoRastreamento; 34 | 35 | const registroPedido = new RegistroPedido({ 36 | pedido: entrega.pedido, 37 | tipo: "entrega", 38 | situacao: status, 39 | payload: req.body 40 | }); 41 | await registroPedido.save(); 42 | // Enviar email de aviso para o cliente - aviso de atualizacao na entrega 43 | const pedido = await Pedido.findById(entrega.pedido).populate({ path:"cliente", populate: { path: "usuario" } }); 44 | EmailController.atualizarPedido({ 45 | usuario: pedido.cliente.usuario, 46 | pedido, 47 | tipo: "entrega", 48 | status: status, 49 | data: new Date() 50 | }); 51 | 52 | await entrega.save(); 53 | return res.send({ entrega }); 54 | }catch(e){ 55 | next(e); 56 | } 57 | } 58 | 59 | // POST /calcular 60 | async calcular(req,res,next){ 61 | const { cep, carrinho } = req.body; 62 | try { 63 | const _carrinho = await Promise.all(carrinho.map(async (item) => { 64 | item.produto = await Produto.findById(item.produto); 65 | item.variacao = await Variacao.findById(item.variacao); 66 | return item; 67 | })); 68 | const resultados = await calcularFrete({ cep, produtos: _carrinho }); 69 | return res.send({ resultados }); 70 | } catch(e){ 71 | next(e); 72 | } 73 | } 74 | 75 | } 76 | 77 | module.exports = EntregaController; -------------------------------------------------------------------------------- /controllers/validacoes/pagamentoValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const mongoose = require("mongoose"); 4 | const Produto = mongoose.model("Produto"); 5 | const Variacao = mongoose.model("Variacao"); 6 | 7 | const PagamentoValidation = { 8 | show: { 9 | params: { 10 | id: Joi.string().alphanum().length(24).required() 11 | }, 12 | query: { 13 | loja: Joi.string().alphanum().length(24).required() 14 | } 15 | }, 16 | pagar: { 17 | params: { 18 | id: Joi.string().alphanum().length(24).required() 19 | }, 20 | query: { 21 | loja: Joi.string().alphanum().length(24).required() 22 | }, 23 | body: { 24 | senderHash: Joi.string().required() 25 | } 26 | }, 27 | update: { 28 | params: { 29 | id: Joi.string().alphanum().length(24).required() 30 | }, 31 | query: { 32 | loja: Joi.string().alphanum().length(24).required() 33 | }, 34 | body: { 35 | status: Joi.string().optional() 36 | } 37 | } 38 | }; 39 | 40 | const checarValorTotal = async ({ carrinho, entrega, pagamento }) => { 41 | try { 42 | const _carrinho = await Promise.all(carrinho.map(async (item) => { 43 | item.produto = await Produto.findById(item.produto); 44 | item.variacao = await Variacao.findById(item.variacao); 45 | return item; 46 | })); 47 | let valorTotal = entrega.custo; 48 | valorTotal += _carrinho.reduce((all, item) => all + (item.quantidade * item.precoUnitario) , 0); 49 | console.log(valorTotal.toFixed(2), pagamento.valor.toFixed(2), pagamento.parcelas); 50 | return ( 51 | valorTotal.toFixed(2) === pagamento.valor.toFixed(2) && 52 | ( !pagamento.parcelas || pagamento.parcelas <= 6 ) 53 | ); 54 | } catch(e){ 55 | console.log(e); 56 | return false; 57 | } 58 | } 59 | 60 | const checarCartao = (pagamento) => { 61 | if( pagamento.forma === "creditCard" ){ 62 | return ( 63 | pagamento.cartao.nomeCompleto && typeof pagamento.cartao.nomeCompleto === "string" && 64 | pagamento.cartao.codigoArea && typeof pagamento.cartao.codigoArea === "string" && 65 | pagamento.cartao.telefone && typeof pagamento.cartao.telefone === "string" && 66 | pagamento.cartao.dataDeNascimento && typeof pagamento.cartao.dataDeNascimento === "string" && 67 | pagamento.cartao.credit_card_token && typeof pagamento.cartao.credit_card_token === "string" && 68 | pagamento.cartao.cpf && typeof pagamento.cartao.cpf === "string" 69 | ); 70 | } else if( pagamento.forma === "boleto" ) return true; 71 | else return false; 72 | } 73 | 74 | module.exports = { 75 | PagamentoValidation, 76 | checarValorTotal, 77 | checarCartao 78 | }; -------------------------------------------------------------------------------- /controllers/EmailController.js: -------------------------------------------------------------------------------- 1 | const transporter = require("nodemailer").createTransport(require("../config/email")); 2 | const { loja } = require("../config/index"); 3 | const moment = require("moment"); 4 | 5 | const _send = ({ subject, emails, message }, cb = null) => { 6 | const mailOptions = { 7 | from: "no-response@lojati.com", 8 | to: emails, 9 | subject, 10 | html: message 11 | }; 12 | if( process.env.NODE_ENV === "production" ){ 13 | transporter.sendMail(mailOptions, function(error, info){ 14 | if(error){ 15 | console.warn(error); 16 | if(cb) return cb(error); 17 | } else { 18 | if(cb) return cb(null, true); 19 | } 20 | }); 21 | } else { 22 | console.log(mailOptions); 23 | if(cb) return cb(null, true); 24 | } 25 | }; 26 | 27 | // NOVO PEDIDO 28 | const enviarNovoPedido = ({ usuario, pedido }) => { 29 | const message = ` 30 |

Pedido Recebido

31 |
32 |

O pedido realizado hoje, no dia ${moment(pedido.createdAt).format("DD/MM/YYYY")}, foi recebido com sucesso.

33 |
34 | Acesse a loja para saber mais. 35 |

36 |

Atenciosamente,

37 |

Equipe - Loja TI

38 | `; 39 | _send({ 40 | subject: "Pedido Recebido - Loja TI", 41 | emails: usuario.email, 42 | message 43 | }); 44 | } 45 | 46 | // PEDIDO CANCELADO 47 | const cancelarPedido = ({ usuario, pedido }) => { 48 | const message = ` 49 |

Pedido Cancelado

50 |
51 |

O pedido feito no dia ${moment(pedido.createdAt).format("DD/MM/YYYY")} foi cancelado.

52 |
53 | Acesse a loja para saber mais. 54 |

55 |

Atenciosamente,

56 |

Equipe - Loja TI

57 | `; 58 | _send({ 59 | subject: "Pedido Cancelado - Loja TI", 60 | emails: usuario.email, 61 | message 62 | }); 63 | } 64 | 65 | // ATUALIZACAO DE PAGAMENTO E ENTREGA 66 | const atualizarPedido = ({ usuario, pedido, status, data, tipo }) => { 67 | const message = ` 68 |

Pedido Atualizado

69 |
70 |

O pedido feito no dia ${moment(pedido.createdAt).format("DD/MM/YYYY")} teve uma atualização.

71 |
72 |

Nova Atualização: ${status} - realizado em ${moment(data).format("DD/MM/YYYY HH:mm")}

73 |
74 | Acesse a loja para saber mais. 75 |

76 |

Atenciosamente,

77 |

Equipe - Loja TI

78 | `; 79 | _send({ 80 | subject: "Pedido Atualizado - Loja TI", 81 | emails: usuario.email, 82 | message 83 | }); 84 | } 85 | 86 | module.exports = { 87 | enviarNovoPedido, 88 | cancelarPedido, 89 | atualizarPedido 90 | }; -------------------------------------------------------------------------------- /controllers/validacoes/variacaoValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const VariacaoValidation = { 4 | index: { 5 | query: { 6 | loja: Joi.string().alphanum().length(24).required(), 7 | produto: Joi.string().alphanum().length(24).required() 8 | } 9 | }, 10 | show: { 11 | query: { 12 | loja: Joi.string().alphanum().length(24).required(), 13 | produto: Joi.string().alphanum().length(24).required() 14 | }, 15 | params: { 16 | id: Joi.string().alphanum().length(24).required() 17 | } 18 | }, 19 | store: { 20 | query: { 21 | loja: Joi.string().alphanum().length(24).required(), 22 | produto: Joi.string().alphanum().length(24).required() 23 | }, 24 | body: { 25 | codigo: Joi.string().required(), 26 | nome: Joi.string().required(), 27 | preco: Joi.number().required(), 28 | promocao: Joi.number().optional(), 29 | entrega: Joi.object({ 30 | dimensoes: Joi.object({ 31 | alturaCm: Joi.number().required(), 32 | larguraCm: Joi.number().required(), 33 | profundidadeCm: Joi.number().required() 34 | }).required(), 35 | pesoKg: Joi.number().required(), 36 | freteGratis: Joi.boolean().optional() 37 | }).required(), 38 | quantidade: Joi.number().optional(), 39 | } 40 | }, 41 | update: { 42 | query: { 43 | loja: Joi.string().alphanum().length(24).required(), 44 | produto: Joi.string().alphanum().length(24).required() 45 | }, 46 | params: { 47 | id: Joi.string().alphanum().length(24).required() 48 | }, 49 | body: { 50 | codigo: Joi.string().optional(), 51 | nome: Joi.string().optional(), 52 | preco: Joi.number().optional(), 53 | promocao: Joi.number().optional(), 54 | entrega: Joi.object({ 55 | dimensoes: Joi.object({ 56 | alturaCm: Joi.number().required(), 57 | larguraCm: Joi.number().required(), 58 | profundidadeCm: Joi.number().required() 59 | }).required(), 60 | pesoKg: Joi.number().required(), 61 | freteGratis: Joi.boolean().optional() 62 | }).optional(), 63 | quantidade: Joi.number().optional(), 64 | fotos: Joi.array().items(Joi.string()).optional() 65 | } 66 | }, 67 | updateImages: { 68 | query: { 69 | loja: Joi.string().alphanum().length(24).required(), 70 | produto: Joi.string().alphanum().length(24).required() 71 | }, 72 | params: { 73 | id: Joi.string().alphanum().length(24).required() 74 | } 75 | }, 76 | remove: { 77 | query: { 78 | loja: Joi.string().alphanum().length(24).required(), 79 | produto: Joi.string().alphanum().length(24).required() 80 | }, 81 | params: { 82 | id: Joi.string().alphanum().length(24).required() 83 | } 84 | } 85 | }; 86 | 87 | module.exports = { VariacaoValidation }; -------------------------------------------------------------------------------- /controllers/validacoes/pedidoValidation.js: -------------------------------------------------------------------------------- 1 | const BaseJoi = require("joi"); 2 | const Extension = require("joi-date-extensions"); 3 | const Joi = BaseJoi.extend(Extension); 4 | 5 | const PedidoValidation = { 6 | indexAdmin: { 7 | query: { 8 | offset: Joi.number().required(), 9 | limit: Joi.number().required() 10 | } 11 | }, 12 | showAdmin: { 13 | params: { 14 | id: Joi.string().alphanum().length(24).required() 15 | } 16 | }, 17 | removeAdmin: { 18 | params: { 19 | id: Joi.string().alphanum().length(24).required() 20 | } 21 | }, 22 | showCarrinhoPedidoAdmin: { 23 | params: { 24 | id: Joi.string().alphanum().length(24).required() 25 | } 26 | }, 27 | index: { 28 | query: { 29 | offset: Joi.number().required(), 30 | limit: Joi.number().required(), 31 | loja: Joi.string().alphanum().length(24).required() 32 | } 33 | }, 34 | show: { 35 | params: { 36 | id: Joi.string().alphanum().length(24).required() 37 | } 38 | }, 39 | remove: { 40 | params: { 41 | id: Joi.string().alphanum().length(24).required() 42 | } 43 | }, 44 | showCarrinhoPedido: { 45 | params: { 46 | id: Joi.string().alphanum().length(24).required() 47 | } 48 | }, 49 | store: { 50 | query: { 51 | loja: Joi.string().alphanum().length(24).required() 52 | }, 53 | body: { 54 | carrinho: Joi.array().items(Joi.object({ 55 | produto: Joi.string().alphanum().length(24).required(), 56 | variacao: Joi.string().alphanum().length(24).required(), 57 | precoUnitario: Joi.number().required(), 58 | quantidade: Joi.number().required() 59 | })).required(), 60 | pagamento: Joi.object({ 61 | valor: Joi.number().required(), 62 | forma: Joi.string().required(), 63 | parcelas: Joi.number().optional(), 64 | enderecoEntregaIgualCobranca: Joi.boolean().required(), 65 | endereco: Joi.object({ 66 | local: Joi.string().required(), 67 | numero: Joi.string().required(), 68 | complemento: Joi.string().optional(), 69 | bairro: Joi.string().required(), 70 | cidade: Joi.string().required(), 71 | estado: Joi.string().required(), 72 | CEP: Joi.string().required() 73 | }).required(), 74 | cartao: Joi.object({ 75 | nomeCompleto: Joi.string().required(), 76 | codigoArea: Joi.string().required(), 77 | telefone: Joi.string().required(), 78 | dataDeNascimento: Joi.date().format("DD/MM/YYYY").raw().required(), 79 | credit_card_token: Joi.string().required(), 80 | cpf: Joi.string().required() 81 | }).optional() 82 | }).required(), 83 | entrega: Joi.object({ 84 | custo: Joi.number().required(), 85 | tipo: Joi.string().required(), 86 | prazo: Joi.number().required(), 87 | endereco: Joi.object({ 88 | local: Joi.string().required(), 89 | numero: Joi.string().required(), 90 | complemento: Joi.string().optional(), 91 | bairro: Joi.string().required(), 92 | cidade: Joi.string().required(), 93 | estado: Joi.string().required(), 94 | CEP: Joi.string().required() 95 | }).required() 96 | }).required(), 97 | } 98 | } 99 | }; 100 | 101 | module.exports = { PedidoValidation }; -------------------------------------------------------------------------------- /controllers/CategoriaController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Categoria = mongoose.model("Categoria"); 4 | const Produto = mongoose.model("Produto"); 5 | 6 | class CategoriaController { 7 | 8 | // GET / index 9 | index(req,res,next){ 10 | Categoria.find({ loja: req.query.loja }) 11 | .select("_id produtos nome codigo disponibilidade loja") 12 | .then((categorias) => res.send({ categorias })) 13 | .catch(next); 14 | } 15 | 16 | // GET /disponiveis 17 | indexDisponiveis(req,res,next){ 18 | Categoria 19 | .find({ loja: req.query.loja, disponibilidade: true }) 20 | .select("_id produtos nome codigo disponibilidade loja") 21 | .then((categorias) => res.send({ categorias })) 22 | .catch(next); 23 | } 24 | 25 | // GET /:id show 26 | show(req,res,next){ 27 | Categoria.findOne({ loja: req.query.loja, _id: req.params.id }) 28 | .select("_id produtos nome codigo disponibilidade loja") 29 | .populate(["produtos"]) 30 | .then(categoria => res.send({ categoria })) 31 | .catch(next); 32 | } 33 | 34 | // POST / store 35 | store(req,res,next){ 36 | const { nome, codigo } = req.body; 37 | const { loja } = req.query; 38 | 39 | const categoria = new Categoria({ nome, codigo, loja, disponibilidade: true }); 40 | categoria.save() 41 | .then(() => res.send({ categoria })) 42 | .catch(next); 43 | } 44 | 45 | // PUT /:id update 46 | async update(req,res,next){ 47 | const { nome, codigo, disponibilidade, produtos } = req.body; 48 | try { 49 | const categoria = await Categoria.findById(req.params.id); 50 | 51 | if(nome) categoria.nome = nome; 52 | if(disponibilidade !== undefined) categoria.disponibilidade = disponibilidade; 53 | if(codigo) categoria.codigo = codigo; 54 | if(produtos) categoria.produtos = produtos; 55 | 56 | await categoria.save(); 57 | return res.send({ categoria }); 58 | } catch(e){ 59 | next(e); 60 | } 61 | } 62 | 63 | // DELETE /:id remove 64 | async remove(req,res,next){ 65 | try { 66 | const categoria = await Categoria.findById(req.params.id); 67 | await categoria.remove(); 68 | return res.send({ deletado: true }); 69 | }catch(e){ 70 | next(e); 71 | } 72 | } 73 | 74 | /** 75 | * PRODUTOS 76 | */ 77 | // GET /:id/produtos - showProdutos 78 | async showProdutos(req,res,next){ 79 | const { offset, limit } = req.query; 80 | try { 81 | const produtos = await Produto.paginate( 82 | { categoria: req.params.id }, 83 | { offset: Number(offset) || 0, limit: Number(limit) || 30 } 84 | ); 85 | return res.send({ produtos }); 86 | } catch(e){ 87 | next(e); 88 | } 89 | } 90 | 91 | // PUT /:id/produtos - updateProdutos 92 | async updateProdutos(req,res,next){ 93 | try { 94 | const categoria = await Categoria.findById(req.params.id); 95 | const { produtos } = req.body; 96 | if(produtos) categoria.produtos = produtos; 97 | await categoria.save(); 98 | 99 | let _produtos = await Produto.find({ 100 | $or: [ 101 | { categoria: req.params.id }, 102 | { _id: { $in: produtos } } 103 | ] 104 | }); 105 | _produtos = await Promise.all(_produtos.map(async (produto) => { 106 | if(!produtos.includes(produto._id.toString())){ 107 | produto.categoria = null; 108 | } else { 109 | produto.categoria = req.params.id; 110 | } 111 | await produto.save(); 112 | return produto; 113 | })); 114 | 115 | const resultado = await Produto.paginate({ categoria: req.params.id }, { offset: 0, limit: 30 }); 116 | return res.send({ produtos: resultado }); 117 | }catch(e){ 118 | next(e); 119 | } 120 | } 121 | 122 | } 123 | 124 | module.exports = CategoriaController; -------------------------------------------------------------------------------- /controllers/validacoes/clienteValidation.js: -------------------------------------------------------------------------------- 1 | const BaseJoi = require("joi"); 2 | const Extension = require("joi-date-extensions"); 3 | const Joi = BaseJoi.extend(Extension); 4 | 5 | const ClienteValidation = { 6 | index: { 7 | query: { 8 | offset: Joi.number(), 9 | limit: Joi.number() 10 | } 11 | }, 12 | searchPedidos: { 13 | query: { 14 | offset: Joi.number(), 15 | limit: Joi.number() 16 | }, 17 | params: { 18 | search: Joi.string().required() 19 | } 20 | }, 21 | search: { 22 | query: { 23 | offset: Joi.number(), 24 | limit: Joi.number() 25 | }, 26 | params: { 27 | search: Joi.string().required() 28 | } 29 | }, 30 | showAdmin: { 31 | params: { 32 | id: Joi.string().alphanum().length(24).required() 33 | } 34 | }, 35 | showPedidosCliente: { 36 | query: { 37 | offset: Joi.number(), 38 | limit: Joi.number() 39 | }, 40 | params: { 41 | id: Joi.string().alphanum().length(24).required() 42 | } 43 | }, 44 | updateAdmin:{ 45 | params: { 46 | id: Joi.string().alphanum().length(24).required() 47 | }, 48 | body: { 49 | nome: Joi.string().optional(), 50 | cpf: Joi.string().length(14).optional(), 51 | email: Joi.string().email().optional(), 52 | telefones: Joi.array().items(Joi.string()).optional(), 53 | endereco: Joi.object({ 54 | local: Joi.string().required(), 55 | numero: Joi.string().required(), 56 | complemento: Joi.string(), 57 | bairro: Joi.string().required(), 58 | cidade: Joi.string().required(), 59 | estado: Joi.string().required(), 60 | CEP: Joi.string().required() 61 | }).optional(), 62 | dataDeNascimento: Joi.date().format("YYYY-MM-DD").raw().optional() 63 | } 64 | }, 65 | show: { 66 | query: { 67 | loja: Joi.string().alphanum().length(24).required() 68 | } 69 | }, 70 | store: { 71 | query: { 72 | loja: Joi.string().alphanum().length(24).required() 73 | }, 74 | body: { 75 | nome: Joi.string().required(), 76 | password: Joi.string().required(), 77 | cpf: Joi.string().length(14).required(), 78 | email: Joi.string().email().required(), 79 | telefones: Joi.array().items(Joi.string()).required(), 80 | endereco: Joi.object({ 81 | local: Joi.string().required(), 82 | numero: Joi.string().required(), 83 | complemento: Joi.string(), 84 | bairro: Joi.string().required(), 85 | cidade: Joi.string().required(), 86 | estado: Joi.string().required(), 87 | CEP: Joi.string().required() 88 | }).required(), 89 | dataDeNascimento: Joi.date().format("YYYY-MM-DD").raw().required() 90 | } 91 | }, 92 | update: { 93 | query: { 94 | loja: Joi.string().alphanum().length(24).required() 95 | }, 96 | params: { 97 | id: Joi.string().alphanum().length(24).required() 98 | }, 99 | body: { 100 | nome: Joi.string().optional(), 101 | password: Joi.string().optional(), 102 | cpf: Joi.string().length(14).optional(), 103 | email: Joi.string().email().optional(), 104 | telefones: Joi.array().items(Joi.string()).optional(), 105 | endereco: Joi.object({ 106 | local: Joi.string().required(), 107 | numero: Joi.string().required(), 108 | complemento: Joi.string(), 109 | bairro: Joi.string().required(), 110 | cidade: Joi.string().required(), 111 | estado: Joi.string().required(), 112 | CEP: Joi.string().required() 113 | }).optional(), 114 | dataDeNascimento: Joi.date().format("YYYY-MM-DD").raw().optional() 115 | } 116 | } 117 | }; 118 | 119 | module.exports = {ClienteValidation}; -------------------------------------------------------------------------------- /controllers/VariacaoController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Variacao = mongoose.model("Variacao"); 4 | const Produto = mongoose.model("Produto"); 5 | 6 | class VariacaoController { 7 | 8 | // GET / - index 9 | async index(req,res,next){ 10 | const { loja, produto } = req.query; 11 | console.log(loja, produto) 12 | try { 13 | const variacoes = await Variacao.find({ loja, produto }); 14 | return res.send({ variacoes }); 15 | }catch(e){ 16 | next(e); 17 | } 18 | } 19 | 20 | // GET /:id - show 21 | async show(req,res,next){ 22 | const { loja, produto } = req.query; 23 | const { id: _id } = req.params; 24 | try { 25 | const variacao = await Variacao.findOne({ loja, produto, _id }); 26 | return res.send({ variacao }); 27 | }catch(e){ 28 | next(e); 29 | } 30 | } 31 | 32 | // POST / - store 33 | async store(req,res,next){ 34 | const { codigo, nome, preco, promocao, entrega, quantidade } = req.body; 35 | const { loja, produto } = req.query; 36 | try { 37 | const variacao = new Variacao({ 38 | codigo, nome, preco, promocao, entrega, quantidade, loja, produto 39 | }); 40 | 41 | const _produto = await Produto.findById(produto); 42 | if(!_produto) return res.status(400).send({ error: "Produto não encontrado." }); 43 | _produto.variacoes.push(variacao._id); 44 | 45 | await _produto.save(); 46 | await variacao.save(); 47 | return res.send({ variacao }); 48 | } catch(e){ 49 | next(e); 50 | } 51 | } 52 | 53 | // PUT /:id - update 54 | async update(req,res,next){ 55 | const { codigo, fotos, nome, preco, promocao, entrega, quantidade } = req.body; 56 | 57 | const { loja, produto } = req.query; 58 | const { id: _id } = req.params; 59 | try { 60 | 61 | const variacao = await Variacao.findOne({ loja, produto, _id }); 62 | if(!variacao) return res.status(400).send({ error: "Variação não encontrada" }); 63 | 64 | if( codigo ) variacao.codigo = codigo; 65 | if( nome ) variacao.nome = nome; 66 | if( preco ) variacao.preco = preco; 67 | if( promocao ) variacao.promocao = promocao; 68 | if( entrega ) variacao.entrega = entrega; 69 | if( quantidade ) variacao.quantidade = quantidade; 70 | if( fotos ) variacao.fotos = fotos; 71 | 72 | await variacao.save(); 73 | return res.send({ variacao }); 74 | } catch(e){ 75 | next(e); 76 | } 77 | } 78 | 79 | // PUT /images/:id - updateImages 80 | async updateImages(req,res,next){ 81 | const { loja, produto } = req.query; 82 | const { id: _id } = req.params; 83 | try { 84 | const variacao = await Variacao.findOne({ loja, produto, _id }); 85 | if(!variacao) return res.status(400).send({ error: "Variação não encontrada" }); 86 | 87 | const novasImagens = req.files.map(item => item.filename); 88 | variacao.fotos = variacao.fotos.filter(item => item).concat(novasImagens); 89 | 90 | await variacao.save(); 91 | return res.send({ variacao }); 92 | 93 | }catch(e){ 94 | next(e); 95 | } 96 | } 97 | 98 | // DELETE /:id - remove 99 | async remove(req,res,next){ 100 | const { loja, produto } = req.query; 101 | const { id: _id } = req.params; 102 | try { 103 | const variacao = await Variacao.findOne({ loja, produto, _id }); 104 | if(!variacao) return res.status(400).send({ error: "Variação não encontrada" }); 105 | 106 | const _produto = await Produto.findById(variacao.produto); 107 | _produto.variacoes = _produto.variacoes.filter(item => item.toString() !== variacao._id.toString()); 108 | await _produto.save(); 109 | 110 | await variacao.remove(); 111 | 112 | return res.send({ deletado: true }); 113 | 114 | }catch(e){ 115 | next(e); 116 | } 117 | } 118 | 119 | } 120 | 121 | module.exports = VariacaoController; -------------------------------------------------------------------------------- /helpers/calcBox.js: -------------------------------------------------------------------------------- 1 | const MIN_LARGURA = 11; 2 | const MAX_LARGURA = 105; 3 | 4 | const MIN_ALTURA = 2; 5 | const MAX_ALTURA = 105; 6 | 7 | const MIN_COMPRIMENTO = 16; 8 | const MAX_COMPRIMENTO = 105; 9 | 10 | const MIN_SOMA_CLA = 29; 11 | const MAX_SOMA_CLA = 200; 12 | 13 | const orderCart = (cart = null) => { 14 | if(!Array.isArray(cart)) return cart; 15 | 16 | let _cart = cart.map(item => { 17 | let novaAltura = Math.min( item.alturaCm, item.profundidadeCm, item.larguraCm ); 18 | let novoComprimento = Math.max( item.alturaCm, item.profundidadeCm, item.larguraCm ); 19 | let _temp = [item.alturaCm, item.profundidadeCm, item.larguraCm].sort((a,b) => a < b); 20 | item.larguraCm = _temp[1]; 21 | item.profundidadeCm = novoComprimento; 22 | item.alturaCm = novaAltura; 23 | item.areaCm = item.larguraCm * item.profundidadeCm; 24 | return item; 25 | }); 26 | return _cart.sort((a,b) => a.areaCm < b.areaCm); 27 | }; 28 | 29 | const calcBox = (_carrinho = null) => { 30 | if(!Array.isArray(_carrinho)) return _carrinho; 31 | 32 | let carrinho = orderCart(_carrinho); 33 | 34 | const box = { 35 | 'altura': 0, /* altura final da caixa */ 36 | 'largura': 0, /* largura */ 37 | 'comprimento': 0, /* ... */ 38 | 'qtd_itens': 0, /* qtd de itens dentro da caixa */ 39 | 'message': null, /* caso erro guarda mensagem */ 40 | 'volume': 0, /* capacidade total de armazenamento da caixa */ 41 | 'volume_itens': 0, /* volume armazenado */ 42 | 'volume_vazio': 0, /* volume livre */ 43 | 'comprimento_remanescente': 0, 44 | 'largura_remanescente': 0, 45 | 'altura_remanescente': 0 46 | }; 47 | 48 | if(carrinho.length === 0 ) return "Erro: Carrinho encontra-se vazio."; 49 | 50 | carrinho.forEach(item => { 51 | 52 | box.qtd_itens+=1; 53 | 54 | box.volume_itens += item.alturaCm * item.profundidadeCm * item.larguraCm; 55 | 56 | if( box.comprimento_remanescente >= item.profundidadeCm && box.largura_remanescente >= item.larguraCm ){ 57 | 58 | if(item.alturaCm > box.altura_remanescente){ 59 | box.altura += item.alturaCm - box.altura_remanescente; 60 | } 61 | 62 | if(item.profundidadeCm > box.comprimento){ 63 | box.comprimento = item.profundidadeCm; 64 | } 65 | 66 | box.comprimento_remanescente = box.comprimento - item.profundidadeCm; 67 | 68 | box.largura_remanescente = box.largura_remanescente - item.larguraCm; 69 | 70 | box.altura_remanescente = item.alturaCm > box.altura_remanescente ? item.alturaCm : box.altura_remanescente; 71 | 72 | return; 73 | } 74 | 75 | // passo (N-1) - altura e' a variavel que sempre incrementa independente de condicao ... 76 | box.altura += item.alturaCm; 77 | 78 | // passo N - verificando se item tem dimensoes maiores que a caixa... 79 | if ( item.larguraCm > box.largura ) box.largura = item.larguraCm; 80 | 81 | if ( item.profundidadeCm > box.comprimento ) box.comprimento = item.profundidadeCm; 82 | 83 | // calculando volume remanescente... 84 | box.comprimento_remanescente = box.comprimento; 85 | box.largura_remanescente = box.largura - item.larguraCm; 86 | box.altura_remanescente = item.alturaCm; 87 | }); 88 | 89 | // @opcional - calculando volume da caixa ... 90 | box.volume = ( box.altura * box.largura * box.comprimento ); 91 | 92 | // @opcional - calculando volume vazio! Ar dentro da caixa! 93 | box.volume_vazio = box.volume - box.volume_itens; 94 | 95 | // checa se temos produtos e se conseguimos alcancar a dimensao minima ... 96 | if( !carrinho.length === 0 ){ 97 | // verificando se dimensoes minimas sao alcancadas ... 98 | if( box.altura > 0 && box.altura < MIN_ALTURA ) box.altura = MIN_ALTURA ; 99 | if( box.largura > 0 && box.largura < MIN_LARGURA ) box.largura = MIN_LARGURA ; 100 | if( box.comprimento > 0 && box.comprimento < MIN_COMPRIMENTO ) box.comprimento = MIN_COMPRIMENTO ; 101 | } 102 | 103 | // verifica se as dimensoes nao ultrapassam valor maximo 104 | if( box.altura > MAX_ALTURA ) box.message = "Erro: Altura maior que o permitido."; 105 | if( box.largura > MAX_LARGURA ) box.message = "Erro: Largura maior que o permitido."; 106 | if( box.comprimento > MAX_COMPRIMENTO ) box.message = "Erro: Comprimento maior que o permitido."; 107 | 108 | // @nota - nao sei se e' uma regra, mas por via das duvidas esta ai 109 | // Soma (C+L+A) MIN 29 cm e MAX 200 cm 110 | if( (box.comprimento+box.altura+box.largura) < MIN_SOMA_CLA ) box.message = "Erro: Soma dos valores C+L+A menor que o permitido."; 111 | 112 | if( (box.comprimento+box.altura+box.largura) > MAX_SOMA_CLA ) box.message = "Erro: Soma dos valores C+L+A maior que o permitido."; 113 | 114 | return box; 115 | } 116 | 117 | // carrinho = [ 118 | // { title: 'Livro - A Arte da Guerra', alturaCm: 5, larguraCm: 30, profundidadeCm: 20 }, 119 | // { title: 'Livro - A Arte da Guerra', alturaCm: 5, larguraCm: 30, profundidadeCm: 20 }, 120 | // { title: 'Livro - Use a Cabeça Estatistica', alturaCm: 5, larguraCm: 8, profundidadeCm: 22 }, 121 | // { title: 'Livro - Use a Cabeça Web Design', alturaCm: 28, larguraCm: 15, profundidadeCm: 15 } 122 | // ]; 123 | 124 | // const box = calcBox( carrinho ); 125 | 126 | // console.log(box); 127 | 128 | module.exports = { calcBox }; 129 | -------------------------------------------------------------------------------- /controllers/UsuarioController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Usuario = mongoose.model("Usuario"); 3 | const enviarEmailRecovery = require("../helpers/email-recovery"); 4 | 5 | class UsuarioController { 6 | 7 | // GET / 8 | index(req, res, next){ 9 | Usuario.findById(req.payload.id).then(usuario => { 10 | if(!usuario) return res.status(401).json({ errors: "Usuario não registrado" }); 11 | return res.json({ usuario: usuario.enviarAuthJSON() }); 12 | }).catch(next); 13 | } 14 | 15 | // GET /:id 16 | show(req, res, next){ 17 | Usuario.findById(req.params.id).populate({ path: "loja" }) 18 | .then(usuario => { 19 | if(!usuario) return res.status(401).json({ errors: "Usuario não registrado" }); 20 | return res.json({ 21 | usuario: { 22 | nome: usuario.nome, 23 | email: usuario.email, 24 | permissao: usuario.permissao, 25 | loja: usuario.loja 26 | } 27 | }); 28 | }).catch(next); 29 | } 30 | 31 | // POST /registrar 32 | store(req, res, next){ 33 | const { nome, email, password, loja } = req.body; 34 | 35 | const usuario = new Usuario({ nome, email, loja }); 36 | usuario.setSenha(password); 37 | 38 | usuario.save() 39 | .then(() => res.json({ usuario: usuario.enviarAuthJSON() })) 40 | .catch((err) => { 41 | console.log(err); 42 | next(err); 43 | }); 44 | } 45 | 46 | // PUT / 47 | update(req, res, next){ 48 | const { nome, email, password } = req.body; 49 | Usuario.findById(req.payload.id).then((usuario) => { 50 | if(!usuario) return res.status(401).json({ errors: "Usuario não registrado" }); 51 | if(typeof nome !== "undefined") usuario.nome = nome; 52 | if(typeof email !== "undefined") usuario.email = email; 53 | if(typeof password !== "undefined") usuario.setSenha(password); 54 | 55 | return usuario.save().then(() => { 56 | return res.json({ usuario: usuario.enviarAuthJSON() }); 57 | }).catch(next); 58 | }).catch(next); 59 | } 60 | 61 | // DELETE / 62 | remove(req, res, next){ 63 | Usuario.findById(req.payload.id).then(usuario => { 64 | if(!usuario) return res.status(401).json({ errors: "Usuario não registrado" }); 65 | return usuario.remove().then(() => { 66 | return res.json({ deletado: true }); 67 | }).catch(next); 68 | }).catch(next); 69 | } 70 | 71 | // POST /login 72 | login(req, res, next){ 73 | const { email, password } = req.body; 74 | Usuario.findOne({ email }).then((usuario) => { 75 | if(!usuario) return res.status(401).json({ errors: "Usuario não registrado" }); 76 | if(!usuario.validarSenha(password)) return res.status(401).json({ errors: "Senha inválida" }); 77 | return res.json({ usuario: usuario.enviarAuthJSON() }); 78 | }).catch(next); 79 | } 80 | 81 | // RECOVERY 82 | 83 | // GET /recuperar-senha 84 | showRecovery(req, res, next){ 85 | return res.render('recovery', { error: null, success: null }); 86 | } 87 | 88 | // POST /recuperar-senha 89 | createRecovery(req, res, next){ 90 | const { email } = req.body; 91 | if(!email) return res.render('recovery', { error: "Preencha com o seu email", success: null }); 92 | 93 | Usuario.findOne({ email }).then((usuario) => { 94 | if(!usuario) return res.render("recovery", { error: "Não existe usuário com este email", success: null }); 95 | const recoveryData = usuario.criarTokenRecuperacaoSenha(); 96 | return usuario.save().then(() => { 97 | enviarEmailRecovery({ usuario, recovery: recoveryData }, (error = null, success = null) => { 98 | return res.render("recovery", { error, success }); 99 | }); 100 | }).catch(next); 101 | }).catch(next); 102 | } 103 | 104 | // GET /senha-recuperada 105 | showCompleteRecovery(req, res, next){ 106 | if(!req.query.token) return res.render("recovery", { error: "Token não identificado", success: null }); 107 | Usuario.findOne({ "recovery.token": req.query.token }).then(usuario => { 108 | if(!usuario) return res.render("recovery", { error: "Não existe usuário com este token", success: null }); 109 | if( new Date(usuario.recovery.date) < new Date() ) return res.render("recovery", { error: "Token expirado. Tente novamente.", success: null }); 110 | return res.render("recovery/store", { error: null, success: null, token: req.query.token }); 111 | }).catch(next); 112 | } 113 | 114 | // POST /senha-recuperada 115 | completeRecovery(req, res, next){ 116 | const { token, password } = req.body; 117 | if(!token || !password) return res.render("recovery/store", { error: "Preencha novamente com sua nova senha", success: null, token: token }); 118 | Usuario.findOne({ "recovery.token": token }).then(usuario => { 119 | if(!usuario) return res.render("recovery", { error: "Usuario nao identificado", success: null }); 120 | 121 | usuario.finalizarTokenRecuperacaoSenha(); 122 | usuario.setSenha(password); 123 | return usuario.save().then(() => { 124 | return res.render("recovery/store", { 125 | error: null, 126 | success: "Senha alterada com sucesso. Tente novamente fazer login.", 127 | token: null 128 | }); 129 | }).catch(next); 130 | }); 131 | } 132 | 133 | } 134 | 135 | module.exports = UsuarioController; -------------------------------------------------------------------------------- /controllers/integracoes/pagseguro.js: -------------------------------------------------------------------------------- 1 | const pagSeguroConfig = require("../../config/pagseguro"); 2 | const PagSeguro = require("../../helpers/pagseguro"); 3 | 4 | const _criarPagamentoComBoleto = (senderHash, { cliente, carrinho, entrega, pagamento }) => { 5 | return new Promise((resolver, rejeitar) => { 6 | 7 | const pag = new PagSeguro(pagSeguroConfig); 8 | 9 | pag.setSender({ 10 | name: cliente.nome, 11 | email: cliente.usuario.email, 12 | cpf_cnpj: cliente.cpf.replace(/[-\.]/g, ""), 13 | area_code: cliente.telefones[0].slice(0,2), 14 | phone: cliente.telefones[0].slice(2).trim().split(" ").join(""), 15 | birth_date: cliente.dataDeNascimento // formato DD/MM/YYYY 16 | }); 17 | 18 | pag.setShipping({ 19 | street: entrega.endereco.local, 20 | number: entrega.endereco.numero, 21 | district: entrega.endereco.bairro, 22 | city: entrega.endereco.cidade, 23 | state: entrega.endereco.estado, 24 | postal_code: entrega.endereco.CEP.replace(/-/g,""), 25 | same_for_billing: pagamento.enderecoEntregaIgualCobranca // true ou false 26 | }); 27 | 28 | pag.setBilling({ 29 | street: pagamento.endereco.local, 30 | number: pagamento.endereco.numero, 31 | district: pagamento.endereco.bairro, 32 | city: pagamento.endereco.cidade, 33 | state: pagamento.endereco.estado, 34 | postal_code: pagamento.endereco.CEP.replace(/-/g,"") 35 | }); 36 | 37 | carrinho.forEach(item => { 38 | pag.addItem({ 39 | qtde: item.quantidade, 40 | value: item.precoUnitario, 41 | description: `${item.produto.titulo} - ${item.variacao.nome}` 42 | }); 43 | }); 44 | pag.addItem({ 45 | qtde: 1, 46 | value: entrega.custo, 47 | description: `Custo de Entrega - Correios` 48 | }); 49 | 50 | pag.sendTransaction({ 51 | method: "boleto", 52 | value: pagamento.valor, 53 | installments: 1, 54 | hash: senderHash 55 | }, (err, data) => (err) ? rejeitar(err) : resolver(data) ); 56 | 57 | }); 58 | } 59 | 60 | const _criarPagamentoComCartao = (senderHash, { cliente, carrinho, entrega, pagamento }) => { 61 | return new Promise((resolver, rejeitar) => { 62 | 63 | const pag = new PagSeguro(pagSeguroConfig); 64 | 65 | pag.setSender({ 66 | name: cliente.nome, 67 | email: cliente.usuario.email, 68 | cpf_cnpj: cliente.cpf.replace(/[-\.]/g, ""), 69 | area_code: cliente.telefones[0].slice(0,2), 70 | phone: cliente.telefones[0].slice(2).trim().split(" ").join(""), 71 | birth_date: cliente.dataDeNascimento // formato DD/MM/YYYY 72 | }); 73 | 74 | pag.setShipping({ 75 | street: entrega.endereco.local, 76 | number: entrega.endereco.numero, 77 | district: entrega.endereco.bairro, 78 | city: entrega.endereco.cidade, 79 | state: entrega.endereco.estado, 80 | postal_code: entrega.endereco.CEP.replace(/-/g,""), 81 | same_for_billing: pagamento.enderecoEntregaIgualCobranca // true ou false 82 | }); 83 | 84 | pag.setBilling({ 85 | street: pagamento.endereco.local, 86 | number: pagamento.endereco.numero, 87 | district: pagamento.endereco.bairro, 88 | city: pagamento.endereco.cidade, 89 | state: pagamento.endereco.estado, 90 | postal_code: pagamento.endereco.CEP.replace(/-/g,"") 91 | }); 92 | 93 | carrinho.forEach(item => { 94 | pag.addItem({ 95 | qtde: item.quantidade, 96 | value: item.precoUnitario, 97 | description: `${item.produto.titulo} - ${item.variacao.nome}` 98 | }); 99 | }); 100 | pag.addItem({ 101 | qtde: 1, 102 | value: entrega.custo, 103 | description: `Custo de Entrega - Correios` 104 | }); 105 | 106 | pag.setCreditCardHolder({ 107 | name: pagamento.cartao.nomeCompleto || cliente.nome, 108 | area_code: pagamento.cartao.codigoArea.trim() || cliente.telefones[0].slice(0,2), 109 | phone: (pagamento.cartao.telefone.trim() || cliente.telefones[0].slice(2)).split(" ").join(""), 110 | birth_date: pagamento.cartao.dataDeNascimento || cliente.dataDeNascimento, 111 | cpf_cnpj: ( pagamento.cartao.cpf || cliente.cpf ).replace(/[-\.]/g, "") 112 | }); 113 | 114 | pag.sendTransaction({ 115 | method: "creditCard", 116 | value: pagamento.valor % 2 !== 0 && pagamento.parcelas !== 1 ? pagamento.valor + 0.01 : pagamento.valor, 117 | installments: pagamento.parcelas, 118 | hash: senderHash, 119 | credit_card_token: pagamento.cartao.credit_card_token 120 | }, (err, data) => (err) ? rejeitar(err) : resolver(data) ); 121 | 122 | }); 123 | } 124 | 125 | const criarPagamento = async (senderHash, data) => { 126 | try { 127 | if( data.pagamento.forma === "boleto" ) return await _criarPagamentoComBoleto(senderHash, data); 128 | else if ( data.pagamento.forma === "creditCard" ) return await _criarPagamentoComCartao(senderHash, data); 129 | else return { errorMessage: "Forma de pagamento não encontrada." }; 130 | } catch(e){ 131 | console.log(e); 132 | return { errorMessage: "Ocorreu um erro", errors: e }; 133 | } 134 | } 135 | 136 | 137 | const getSessionId = () => { 138 | return new Promise((resolver, rejeitar) => { 139 | const pag = new PagSeguro(pagSeguroConfig); 140 | pag.sessionId((err, session_id) => (err) ? rejeitar(err) : resolver(session_id)); 141 | }) 142 | } 143 | 144 | 145 | const getTransactionStatus = (codigo) => { 146 | return new Promise((resolver, rejeitar) => { 147 | const pag = new PagSeguro(pagSeguroConfig); 148 | pag.transactionStatus(codigo, (err, result) => (err) ? rejeitar(err) : resolver(result)); 149 | }); 150 | } 151 | 152 | 153 | const getNotification = (codigo) => { 154 | return new Promise((resolver, rejeitar) => { 155 | const pag = new PagSeguro(pagSeguroConfig); 156 | pag.getNotification(codigo, (err, result) => (err) ? rejeitar(err) : resolver(result)); 157 | }); 158 | } 159 | 160 | module.exports = { 161 | criarPagamento, 162 | getSessionId, 163 | getTransactionStatus, 164 | getNotification 165 | }; -------------------------------------------------------------------------------- /controllers/PagamentoController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { criarPagamento, getSessionId, getTransactionStatus, getNotification } = require("./integracoes/pagseguro"); 3 | 4 | const Pagamento = mongoose.model("Pagamento"); 5 | 6 | const Pedido = mongoose.model("Pedido"); 7 | const Produto = mongoose.model("Produto"); 8 | const Variacao = mongoose.model("Variacao"); 9 | const RegistroPedido = mongoose.model("RegistroPedido"); 10 | const QuantidadeValidation = require("./validacoes/quantidadeValidation"); 11 | 12 | const EmailController = require("./EmailController"); 13 | 14 | class PagamentoController { 15 | 16 | // CLIENTES 17 | async show (req,res,next){ 18 | try { 19 | const pagamento = await Pagamento.findOne({ _id: req.params.id, loja: req.query.loja }); 20 | if(!pagamento) return res.status(400).send({ error: "Pagamento nao existe" }); 21 | 22 | const registros = await RegistroPedido.find({ pedido: pagamento.pedido, tipo: "pagamento" }); 23 | 24 | const situacao = (pagamento.pagSeguroCode) ? await getTransactionStatus(pagamento.pagSeguroCode) : null; 25 | 26 | if( 27 | situacao && 28 | ( 29 | registros.length === 0 || 30 | !registros[registros.length-1].payload || 31 | !registros[registros.length-1].payload.code || 32 | registros[registros.length-1].payload.code !== situacao.code 33 | ) 34 | ){ 35 | const registroPedido = new RegistroPedido({ 36 | pedido: pagamento.pedido, 37 | tipo: "pagamento", 38 | situacao: situacao.status || "Situacao", 39 | payload: situacao 40 | }); 41 | pagamento.status = situacao.status; 42 | await pagamento.save(); 43 | await registroPedido.save(); 44 | registros.push(registroPedido); 45 | } 46 | // return res.send({ pagamento: { ...pagamento._doc, payload: null }, registros, situacao }); 47 | return res.send({ pagamento, registros, situacao }); 48 | }catch(e){ 49 | next(e); 50 | } 51 | } 52 | 53 | async pagar(req,res,next){ 54 | const { senderHash } = req.body; 55 | 56 | try { 57 | 58 | const pagamento = await Pagamento.findOne({ _id: req.params.id, loja: req.query.loja }); 59 | if(!pagamento) return res.status(400).send({ error: "Pagamento nao existe" }); 60 | const pedido = await Pedido.findById(pagamento.pedido).populate([ 61 | { path: "cliente", populate: "usuario" }, 62 | { path: "entrega" }, 63 | { path: "pagamento" } 64 | ]); 65 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 66 | item.produto = await Produto.findById(item.produto); 67 | item.variacao = await Variacao.findById(item.variacao); 68 | return item; 69 | })); 70 | 71 | const payload = await criarPagamento(senderHash, pedido); 72 | pagamento.payload = (pagamento.payload) ? pagamento.payload.concat([payload]) : [ payload ]; 73 | if(payload.code) pagamento.pagSeguroCode = payload.code; 74 | await pagamento.save(); 75 | 76 | // return res.send({ pagamento: { ...pagamento._doc, payload: null } }); 77 | return res.send({ pagamento }); 78 | }catch(e){ 79 | next(e); 80 | } 81 | } 82 | 83 | // ADMIN 84 | async update(req,res,next){ 85 | const { status } = req.body; 86 | const { loja } = req.query; 87 | try { 88 | const pagamento = await Pagamento.findOne({ _id: req.params.id, loja }); 89 | if(!pagamento) return res.status(400).send({ error: "Pagamento nao existe" }); 90 | 91 | if(status) pagamento.status = status; 92 | 93 | const registroPedido = new RegistroPedido({ 94 | pedido: pagamento.pedido, 95 | tipo: "pagamento", 96 | situacao: status 97 | }); 98 | await registroPedido.save(); 99 | // Enviar email de aviso para o cliente - aviso de atualizacao de pagamento 100 | const pedido = await Pedido.findById(pagamento.pedido).populate({ path:"cliente", populate: { path: "usuario" } }); 101 | EmailController.atualizarPedido({ 102 | usuario: pedido.cliente.usuario, 103 | pedido, 104 | tipo: "pagamento", 105 | status, 106 | data: new Date() 107 | }); 108 | 109 | await pagamento.save(); 110 | 111 | if( status.toLowerCase().includes("pago") ) await QuantidadeValidation.atualizarQuantidade("confirmar_pedido", pedido); 112 | else if( status.toLowerCase().includes("cancelado") ) await QuantidadeValidation.atualizarQuantidade("cancelar_pedido", pedido); 113 | 114 | return res.send({ pagamento }); 115 | }catch(e){ 116 | next(e); 117 | } 118 | } 119 | 120 | // PAGSEGURO 121 | async getSessionId(req,res,next){ 122 | try { 123 | const sessionId = await getSessionId(); 124 | return res.send({ sessionId }); 125 | } catch(e){ 126 | console.log(e); 127 | next(e); 128 | } 129 | } 130 | 131 | async verNotificacao(req,res,next){ 132 | try { 133 | const { notificationCode, notificationType } = req.body; 134 | if( notificationType !== "transaction" ) return res.send({ success: true }); 135 | 136 | const result = await getNotification(notificationCode); 137 | 138 | const pagamento = await Pagamento.findOne({ pagSeguroCode: result.code }); 139 | if(!pagamento) return res.status(400).send({ error: "Pagamento nao existe" }); 140 | 141 | const registros = await RegistroPedido.find({ pedido: pagamento.pedido, tipo: "pagamento" }); 142 | 143 | const situacao = (pagamento.pagSeguroCode) ? await getTransactionStatus(pagamento.pagSeguroCode) : null; 144 | 145 | if( 146 | situacao && 147 | ( 148 | registros.length === 0 || 149 | registros[registros.length-1].payload.code !== situacao.code 150 | ) 151 | ){ 152 | const registroPedido = new RegistroPedido({ 153 | pedido: pagamento.pedido, 154 | tipo: "pagamento", 155 | situacao: situacao.status || "Situacao", 156 | payload: situacao 157 | }); 158 | pagamento.status = situacao.status; 159 | await pagamento.save(); 160 | 161 | await registroPedido.save(); 162 | // Enviar email de aviso para o cliente - aviso de atualizacao de pagamento 163 | const pedido = await Pedido.findById(pagamento.pedido).populate({ path:"cliente", populate: { path: "usuario" } }); 164 | EmailController.atualizarPedido({ 165 | usuario: pedido.cliente.usuario, 166 | pedido, 167 | tipo: "pagamento", 168 | status: situacao.status, 169 | data: new Date() 170 | }); 171 | 172 | if( situacao.status === "Paga" ) await QuantidadeValidation.atualizarQuantidade("confirmar_pedido", pedido); 173 | else if( situacao.status === "Cancelada" ) await QuantidadeValidation.atualizarQuantidade("cancelar_pedido", pedido); 174 | } 175 | return res.send({ success: true }); 176 | 177 | }catch(e){ 178 | next(e); 179 | } 180 | } 181 | 182 | } 183 | 184 | module.exports = PagamentoController; -------------------------------------------------------------------------------- /controllers/ProdutoController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Produto = mongoose.model("Produto"); 4 | const Categoria = mongoose.model("Categoria"); 5 | 6 | const Avaliacao = mongoose.model("Avaliacao"); 7 | const Variacao = mongoose.model("Variacao"); 8 | 9 | const getSort = (sortType) => { 10 | switch(sortType){ 11 | case "alfabetica_a-z": 12 | return { titulo: 1 }; 13 | case "alfabetica_z-a": 14 | return { titulo: -1 }; 15 | case "preco-crescente": 16 | return { preco: 1 }; 17 | case "preco-decrescente": 18 | return { preco: -1 }; 19 | default: 20 | return {}; 21 | } 22 | }; 23 | 24 | class ProdutoController { 25 | // ADMIN 26 | 27 | // POST / - store 28 | async store(req,res,next){ 29 | const { titulo, descricao, categoria: categoriaId, preco, promocao, sku } = req.body; 30 | const { loja } = req.query; 31 | 32 | try { 33 | 34 | const produto = new Produto({ 35 | titulo, 36 | disponibilidade: true, 37 | descricao, 38 | categoria: categoriaId, 39 | preco, 40 | promocao, 41 | sku, 42 | loja 43 | }); 44 | 45 | const categoria = await Categoria.findById(categoriaId); 46 | categoria.produtos.push(produto._id); 47 | 48 | await produto.save(); 49 | await categoria.save(); 50 | 51 | return res.send({ produto }); 52 | 53 | }catch(e){ 54 | next(e); 55 | } 56 | } 57 | 58 | // PUT /:id 59 | async update(req,res,next){ 60 | const { titulo, descricao, disponibilidade, fotos, categoria, preco, promocao, sku } = req.body; 61 | const { loja } = req.query; 62 | 63 | try { 64 | 65 | const produto = await Produto.findById(req.params.id); 66 | if(!produto) return res.status(400).send({ error: "Produto não encontrado." }); 67 | 68 | if( titulo ) produto.titulo = titulo; 69 | if( descricao ) produto.descricao = descricao; 70 | if( disponibilidade !== undefined ) produto.disponibilidade = disponibilidade; 71 | if( fotos ) produto.fotos = fotos; 72 | if( preco ) produto.preco = preco; 73 | if( promocao ) produto.promocao = promocao; 74 | if( sku ) produto.sku = sku; 75 | 76 | if( categoria && categoria.toString() !== produto.categoria.toString() ){ 77 | const oldCategoria = await Categoria.findById(produto.categoria); 78 | const newCategoria = await Categoria.findById(categoria); 79 | 80 | if(oldCategoria && newCategoria){ 81 | oldCategoria.produtos = oldCategoria.produtos.filter(item => item.toString() !== produto._id.toString()); 82 | newCategoria.produtos.push(produto._id); 83 | produto.categoria = categoria; 84 | await oldCategoria.save(); 85 | await newCategoria.save(); 86 | } else if(newCategoria){ 87 | newCategoria.produtos.push(produto._id); 88 | produto.categoria = categoria; 89 | await newCategoria.save(); 90 | } 91 | } 92 | 93 | await produto.save(); 94 | 95 | return res.send({ produto }); 96 | 97 | }catch(e){ 98 | next(e); 99 | } 100 | } 101 | 102 | // PUT /images/:id 103 | async updateImages(req,res,next){ 104 | try { 105 | const { loja } = req.query; 106 | const produto = await Produto.findOne({ _id: req.params.id, loja }); 107 | if(!produto) return res.status(400).send({ error: "Produto não encontrado." }); 108 | 109 | const novasImagens = req.files.map(item => item.filename); 110 | produto.fotos = produto.fotos.filter(item => item).concat(novasImagens); 111 | 112 | await produto.save(); 113 | 114 | return res.send({ produto }); 115 | } catch (e){ 116 | next(e); 117 | } 118 | } 119 | 120 | // DELETE :/id - remove 121 | async remove(req,res,next){ 122 | const { loja } = req.query; 123 | 124 | try { 125 | 126 | const produto = await Produto.findOne({ _id: req.params.id, loja }); 127 | if(!produto) return res.status(400).send({ error: "Produto não encontrado." }); 128 | 129 | const categoria = await Categoria.findById(produto.categoria); 130 | if(categoria){ 131 | categoria.produtos = categoria.produtos.filter(item => item !== produto._id); 132 | await categoria.save(); 133 | } 134 | 135 | await produto.remove(); 136 | return res.send({ deleted: true }); 137 | 138 | }catch(e){ 139 | next(e); 140 | } 141 | } 142 | 143 | // CLIENTE 144 | // GET / - index 145 | async index(req,res,next){ 146 | const offset = Number(req.query.offset) || 0; 147 | const limit = Number(req.query.limit) || 30; 148 | try { 149 | const produtos = await Produto.paginate( 150 | { loja: req.query.loja }, 151 | { offset, limit, sort: getSort(req.query.sortType), populate: ["categoria"] } 152 | ); 153 | return res.send({ produtos }); 154 | } catch(e){ 155 | next(e); 156 | } 157 | } 158 | 159 | // GET /disponiveis - indexDisponiveis 160 | async indexDisponiveis(req,res,next){ 161 | const offset = Number(req.query.offset) || 0; 162 | const limit = Number(req.query.limit) || 30; 163 | try { 164 | const produtos = await Produto.paginate( 165 | { loja: req.query.loja, disponibilidade: true }, 166 | { offset, limit, sort: getSort(req.query.sortType), populate: ["categoria"] } 167 | ); 168 | return res.send({ produtos }); 169 | } catch(e){ 170 | next(e); 171 | } 172 | } 173 | 174 | // get /search/:search - search 175 | async search(req,res,next){ 176 | const offset = Number(req.query.offset) || 0; 177 | const limit = Number(req.query.limit) || 30; 178 | const search = new RegExp(req.params.search, "i"); 179 | try { 180 | const produtos = await Produto.paginate( 181 | { 182 | loja: req.query.loja, 183 | $text: { $search: search, $diacriticSensitive: false } 184 | }, 185 | { offset, limit, sort: getSort(req.query.sortType), populate: ["categoria"] } 186 | ); 187 | return res.send({ produtos }); 188 | }catch(e){ 189 | next(e); 190 | } 191 | } 192 | 193 | // GET /:id 194 | async show(req,res,next){ 195 | try { 196 | const produto = await Produto 197 | .findById(req.params.id) 198 | .populate([ 199 | "loja", 200 | "categoria" 201 | ]); 202 | return res.send({ produto }); 203 | } catch(e){ 204 | next(e); 205 | } 206 | } 207 | 208 | 209 | // AVALIACOES 210 | // GET /:id/avaliacoes - showAvaliacoes 211 | async showAvaliacoes(req,res,next){ 212 | try { 213 | const avaliacoes = await Avaliacao.find({ produto: req.params.id }); 214 | return res.send({ avaliacoes }); 215 | }catch(e){ 216 | next(e); 217 | } 218 | } 219 | 220 | // VARIACOES 221 | // GET /:id/variacoes - showVariacoes 222 | async showVariacoes(req,res,next){ 223 | try { 224 | const variacoes = await Variacao.find({ produto: req.params.id }); 225 | return res.send({ variacoes }); 226 | }catch(e){ 227 | next(e); 228 | } 229 | } 230 | 231 | } 232 | 233 | module.exports = ProdutoController; -------------------------------------------------------------------------------- /controllers/ClienteController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Pedido = mongoose.model("Pedido"); 4 | const Produto = mongoose.model("Produto"); 5 | const Variacao = mongoose.model("Variacao"); 6 | 7 | const Cliente = mongoose.model("Cliente"); 8 | const Usuario = mongoose.model("Usuario"); 9 | 10 | class ClienteController { 11 | 12 | /** 13 | * 14 | * ADMIN 15 | */ 16 | 17 | // GET / index 18 | async index(req,res,next){ 19 | try { 20 | const offset = Number(req.query.offset) || 0; 21 | const limit = Number(req.query.limit) || 30; 22 | const clientes = await Cliente.paginate( 23 | { loja: req.query.loja }, 24 | { offset, limit, populate: { path:"usuario", select: "-salt -hash" } } 25 | ); 26 | return res.send({ clientes }); 27 | } catch(e){ 28 | next(e); 29 | } 30 | } 31 | 32 | // GET /search/:search/pedidos 33 | async searchPedidos(req,res,next){ 34 | const { offset, limit, loja } = req.query; 35 | try { 36 | const search = new RegExp(req.params.search, "i"); 37 | const clientes = await Cliente.find({ loja, $text: { $search: search, $diacriticSensitive: false } }); 38 | const pedidos = await Pedido.paginate( 39 | { loja, cliente: { $in: clientes.map(item => item._id) } }, 40 | { offset, limit, populate: ["cliente","pagamento","entrega"] } 41 | ); 42 | pedidos.docs = await Promise.all(pedidos.docs.map(async (pedido) => { 43 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 44 | item.produto = await Produto.findById(item.produto); 45 | item.variacao = await Variacao.findById(item.variacao); 46 | return item; 47 | })); 48 | return pedido; 49 | })); 50 | return res.send({ pedidos }); 51 | } catch(e){ 52 | next(e); 53 | } 54 | } 55 | 56 | // GET /search/:search 57 | async search(req,res,next){ 58 | const offset = Number(req.query.offset) || 0; 59 | const limit = Number(req.query.limit) || 30; 60 | const search = new RegExp(req.params.search, "i"); 61 | try { 62 | const clientes = await Cliente.paginate( 63 | { 64 | loja: req.query.loja, 65 | $or: [ 66 | { $text: { $search: search, $diacriticSensitive: false } }, 67 | { telefones: { $regex: search } } 68 | ] 69 | }, 70 | { offset, limit, populate: { path:"usuario", select: "-salt -hash" } } 71 | ); 72 | return res.send({ clientes }); 73 | } catch(e){ 74 | next(e); 75 | } 76 | } 77 | 78 | // GET /admin/:id 79 | async showAdmin(req,res,next){ 80 | try { 81 | const cliente = await Cliente.findOne({ _id: req.params.id, loja: req.query.loja }).populate({ path:"usuario", select: "-salt -hash" }); 82 | return res.send({ cliente }); 83 | } catch(e){ 84 | next(e); 85 | } 86 | } 87 | 88 | // GET /admin/:id/pedidos 89 | async showPedidosCliente(req,res,next){ 90 | const { offset, limit, loja } = req.query; 91 | try { 92 | const pedidos = await Pedido.paginate( 93 | { loja, cliente: req.params.id }, 94 | { 95 | offset: Number(offset || 0), 96 | limit: Number(limit || 30), 97 | populate: ["cliente", "pagamento","entrega"] 98 | } 99 | ); 100 | pedidos.docs = await Promise.all(pedidos.docs.map(async (pedido) => { 101 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 102 | item.produto = await Produto.findById(item.produto); 103 | item.variacao = await Variacao.findById(item.variacao); 104 | return item; 105 | })); 106 | return pedido; 107 | })); 108 | return res.send({ pedidos }); 109 | } catch(e){ 110 | next(e); 111 | } 112 | } 113 | 114 | // PUT /admin/:id 115 | async updateAdmin(req,res,next){ 116 | const { nome, cpf, email, telefones, endereco, dataDeNascimento } = req.body; 117 | try { 118 | const cliente = await Cliente.findById(req.params.id).populate({ path:"usuario", select: "-salt -hash" }); 119 | if(nome){ 120 | cliente.usuario.nome = nome; 121 | cliente.nome = nome; 122 | } 123 | if(email) cliente.usuario.email = email; 124 | if(cpf) cliente.cpf = cpf; 125 | if(telefones) cliente.telefones = telefones; 126 | if(endereco) cliente.endereco = endereco; 127 | if(dataDeNascimento) cliente.dataDeNascimento = dataDeNascimento; 128 | await cliente.usuario.save(); 129 | await cliente.save(); 130 | return res.send({ cliente }); 131 | } catch(e){ 132 | next(e); 133 | } 134 | } 135 | 136 | async removeAdmin(req,res,next){ 137 | try { 138 | const cliente = await Cliente.findById(req.params.id).populate("usuario"); 139 | if(!cliente) return res.status(400).send({ error: "Cliente nao encontrado." }) 140 | await cliente.usuario.remove(); 141 | await cliente.remove(); 142 | return res.send({ deletado: true }); 143 | }catch(e){ 144 | next(e); 145 | } 146 | } 147 | 148 | /** 149 | * 150 | * CLIENTE 151 | */ 152 | async show (req,res,next){ 153 | try { 154 | const cliente = await Cliente.findOne({ usuario: req.payload.id, loja: req.query.loja }).populate({ path:"usuario", select: "-salt -hash" }); 155 | return res.send({ cliente }); 156 | }catch(e){ 157 | next(e); 158 | } 159 | } 160 | 161 | async store(req,res,next){ 162 | const { nome, email, cpf, telefones,endereco, dataDeNascimento, password } = req.body; 163 | const { loja } = req.query; 164 | 165 | const usuario = new Usuario({ nome, email, loja }); 166 | usuario.setSenha(password); 167 | const cliente = new Cliente({ nome, cpf, telefones, endereco, loja, dataDeNascimento, usuario: usuario._id }); 168 | 169 | try { 170 | await usuario.save(); 171 | await cliente.save(); 172 | return res.send({ cliente: Object.assign({}, cliente._doc, { email: usuario.email }) }); 173 | }catch(e){ 174 | next(e); 175 | } 176 | } 177 | 178 | async update(req,res,next){ 179 | const { nome, email, cpf, telefones,endereco, dataDeNascimento, password } = req.body; 180 | try { 181 | const cliente = await Cliente.findOne({usuario: req.payload.id}).populate("usuario"); 182 | if(!cliente) return res.send({ error: "Cliente não existe." }) 183 | if(nome){ 184 | cliente.usuario.nome = nome; 185 | cliente.nome = nome; 186 | } 187 | if(email) cliente.usuario.email = email; 188 | if(password) cliente.usuario.setSenha(password); 189 | if(cpf) cliente.cpf = cpf; 190 | if(telefones) cliente.telefones = telefones; 191 | if(endereco) cliente.endereco = endereco; 192 | if(dataDeNascimento) cliente.dataDeNascimento = dataDeNascimento; 193 | await cliente.usuario.save(); 194 | await cliente.save(); 195 | cliente.usuario = { 196 | email: cliente.usuario.email, 197 | _id: cliente.usuario._id, 198 | permissao: cliente.usuario.permissao 199 | }; 200 | return res.send({ cliente }); 201 | }catch(e){ 202 | next(e); 203 | } 204 | } 205 | 206 | async remove(req,res,next){ 207 | try { 208 | const cliente = await Cliente.findOne({ usuario: req.payload.id }).populate("usuario"); 209 | await cliente.usuario.remove(); 210 | cliente.deletado = true; 211 | await cliente.save(); 212 | return res.send({ deletado: true }); 213 | }catch(e){ 214 | next(e); 215 | } 216 | } 217 | 218 | } 219 | 220 | module.exports = ClienteController; -------------------------------------------------------------------------------- /helpers/pagseguro.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const xmlParser = require('xml2json'); 3 | 4 | const pagseguro = function(params) { 5 | this.email = params.email; 6 | this.token = params.token; 7 | this.notificationURL = params.notificationURL; 8 | this.mode = params.sandbox == true ? 'sandbox' : 'prod'; 9 | this.currency = params.currency || 'BRL'; 10 | this.sandbox_email = params.sandbox_email; 11 | 12 | switch (this.mode) { 13 | case 'prod': this.url = 'https://ws.pagseguro.uol.com.br/v2'; break; 14 | case 'sandbox': this.url = 'https://ws.sandbox.pagseguro.uol.com.br/v2'; break; 15 | } 16 | 17 | this.checkoutData = { 18 | email: this.email, 19 | token: this.token, 20 | mode: this.mode, 21 | currency: this.currency, 22 | url: this.url 23 | } 24 | 25 | this.items = []; 26 | } 27 | 28 | pagseguro.prototype.setSender = function(sender) { 29 | this.checkoutData.senderName = sender.name; 30 | this.checkoutData.senderAreaCode = sender.area_code; 31 | this.checkoutData.senderPhone = sender.phone; 32 | this.checkoutData.senderEmail = this.mode == 'sandbox' ? this.sandbox_email : sender.email; 33 | 34 | if (sender.cpf_cnpj.length == 11) { 35 | this.checkoutData.senderCPF = sender.cpf_cnpj; 36 | } else { 37 | this.checkoutData.senderCNPJ = sender.cpf_cnpj; 38 | } 39 | 40 | this.sender = sender; 41 | } 42 | 43 | pagseguro.prototype.setShipping = function(shipping) { 44 | this.checkoutData.shippingAddressStreet = shipping.street; 45 | this.checkoutData.shippingAddressNumber = shipping.number; 46 | this.checkoutData.shippingAddressDistrict = shipping.district; 47 | this.checkoutData.shippingAddressCity = shipping.city; 48 | this.checkoutData.shippingAddressState = shipping.state; 49 | this.checkoutData.shippingAddressPostalCode = shipping.postal_code; 50 | this.checkoutData.shippingAddressCountry = shipping.country || 'BRA'; 51 | 52 | if (shipping.same_for_billing) { 53 | this.setBilling(shipping); 54 | } 55 | 56 | this.shipping = shipping; 57 | } 58 | 59 | pagseguro.prototype.setBilling = function(billing) { 60 | this.checkoutData.billingAddressStreet = billing.street; 61 | this.checkoutData.billingAddressNumber = billing.number; 62 | this.checkoutData.billingAddressDistrict = billing.district; 63 | this.checkoutData.billingAddressCity = billing.city; 64 | this.checkoutData.billingAddressState = billing.state; 65 | this.checkoutData.billingAddressPostalCode = billing.postal_code; 66 | this.checkoutData.billingAddressCountry = billing.country || 'BRA'; 67 | 68 | this.billing = billing; 69 | } 70 | 71 | pagseguro.prototype.setCreditCardHolder = function(holder) { 72 | this.holder = holder; 73 | } 74 | 75 | pagseguro.prototype.addItem = function(item) { 76 | this.items.push({ 77 | qtde: item.qtde, 78 | value: item.value, 79 | description: item.description 80 | }) 81 | 82 | this.checkoutData['itemQuantity' + (this.items.length)] = item.qtde; 83 | this.checkoutData['itemAmount' + (this.items.length)] = item.value.toFixed(2); 84 | this.checkoutData['itemId' + (this.items.length)] = this.items.length; 85 | this.checkoutData['itemDescription' + (this.items.length)] = item.description; 86 | } 87 | 88 | pagseguro.prototype.sendTransaction = function(transaction, cb) { 89 | this.checkoutData.paymentMethod = transaction.method; 90 | this.checkoutData.installmentQuantity = transaction.installments || 1; 91 | this.checkoutData.installmentValue = (transaction.value / transaction.installments).toFixed(2); 92 | this.checkoutData.senderHash = transaction.hash; 93 | 94 | if (transaction.installments && transaction.installments > 1) { 95 | this.checkoutData.noInterestInstallmentQuantity = transaction.installments; 96 | } 97 | 98 | if (this.checkoutData.paymentMethod == 'creditCard') { 99 | this.checkoutData.creditCardToken = transaction.credit_card_token; 100 | this.checkoutData.creditCardHolderName = this.holder ? this.holder.name : this.sender.name; 101 | this.checkoutData.creditCardHolderAreaCode = this.holder ? this.holder.area_code : this.sender.area_code; 102 | this.checkoutData.creditCardHolderPhone = this.holder ? this.holder.phone : this.sender.phone; 103 | this.checkoutData.creditCardHolderBirthDate = this.holder ? this.holder.birth_date : this.sender.birth_date; 104 | 105 | let cpf_cnpj = this.holder ? this.holder.cpf_cnpj : this.sender.cpf_cnpj 106 | if (cpf_cnpj.length == 11) { 107 | this.checkoutData.creditCardHolderCPF = cpf_cnpj; 108 | } else { 109 | this.checkoutData.creditCardHolderCNPJ = cpf_cnpj; 110 | } 111 | } 112 | 113 | const params = { 114 | url: this.url + '/transactions?token=' + this.token + '&email=' + this.email, 115 | form: this.checkoutData 116 | } 117 | if( this.notificationURL ){ 118 | params.form.notificationURL = this.notificationURL; 119 | } 120 | 121 | request.post(params, function(err, response, body) { 122 | if (err) { 123 | return cb(err, false); 124 | } else if (response.statusCode == 200) { 125 | const json = JSON.parse(xmlParser.toJson(body)); 126 | return cb(false, json.transaction); 127 | } else { 128 | const json = JSON.parse(xmlParser.toJson(body)); 129 | if (json.errors && json.errors.error) { 130 | return cb(json.errors.error, false); 131 | } 132 | 133 | return cb(body, false); 134 | } 135 | }) 136 | } 137 | 138 | pagseguro.prototype.getNotification = function(notificationCode, cb){ 139 | const params = { 140 | url: this.url + '/transactions/notifications/'+ notificationCode + "?token=" + this.token + "&email=" + this.email 141 | } 142 | 143 | request.get(params, function(err, response, body){ 144 | if(err){ 145 | return cb(err, false); 146 | } else if(response.statusCode === 200){ 147 | const json = JSON.parse(xmlParser.toJson(body)); 148 | return cb(false, json.transaction); 149 | } else { 150 | const json = JSON.parse(xmlParser.toJson(body)); 151 | if (json.errors && json.errors.error){ 152 | return cb(json.errors.error, false); 153 | } 154 | return cb(body, false); 155 | } 156 | }); 157 | } 158 | 159 | pagseguro.prototype.sessionId = function(cb) { 160 | const url = this.url + '/sessions?token=' + this.token + '&email=' + this.email; 161 | 162 | request.post({ url: url }, function(err, response, body) { 163 | if (err) { 164 | return cb(err, false); 165 | } else if (response.statusCode == 200) { 166 | const json = JSON.parse(xmlParser.toJson(body)); 167 | return cb(false, json.session.id); 168 | } else { 169 | const json = JSON.parse(xmlParser.toJson(body)); 170 | if (json.errors && json.errors.error) { 171 | return cb(json.errors.error, false); 172 | } 173 | 174 | return cb(body, false); 175 | } 176 | }) 177 | } 178 | 179 | pagseguro.prototype.transactionStatus = function(code, cb) { 180 | request.get({ url: this.url + '/transactions/' + code + '?token=' + this.token + '&email=' + this.email }, function(err, response, body) { 181 | if (err) { 182 | return cb(err, false); 183 | } else if (response.statusCode == 200) { 184 | const json = JSON.parse(xmlParser.toJson(body)); 185 | 186 | let status = ''; 187 | switch (json.transaction.status) { 188 | case '1': status = 'Aguardando Pagamento'; break; 189 | case '2': status = 'Em Análise'; break; 190 | case '3': status = 'Paga'; break; 191 | case '4': status = 'Disponível'; break; 192 | case '5': status = 'Em Disputa'; break; 193 | case '6': status = 'Devolvida'; break; 194 | case '7': status = 'Cancelada'; break; 195 | } 196 | 197 | return cb(false, { 198 | code: json.transaction.status, 199 | status: status, 200 | date: json.transaction.date 201 | }); 202 | } else { 203 | const json = JSON.parse(xmlParser.toJson(body)); 204 | if (json.errors && json.errors.error) { 205 | return cb(json.errors.error, false); 206 | } 207 | 208 | return cb(body, false); 209 | } 210 | }) 211 | } 212 | 213 | module.exports = pagseguro; -------------------------------------------------------------------------------- /controllers/PedidoController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Pedido = mongoose.model("Pedido"); 4 | const Usuario = mongoose.model("Usuario"); 5 | const Produto = mongoose.model("Produto"); 6 | const Variacao = mongoose.model("Variacao"); 7 | const Pagamento = mongoose.model("Pagamento"); 8 | const Entrega = mongoose.model("Entrega"); 9 | const Cliente = mongoose.model("Cliente"); 10 | const RegistroPedido = mongoose.model("RegistroPedido"); 11 | 12 | const { calcularFrete } = require("./integracoes/correios"); 13 | const PagamentoValidation = require("./validacoes/pagamentoValidation"); 14 | const EntregaValidation = require("./validacoes/entregaValidation"); 15 | const QuantidadeValidation = require("./validacoes/quantidadeValidation"); 16 | 17 | const EmailController = require("./EmailController"); 18 | 19 | const CarrinhoValidation = require("./validacoes/carrinhoValidation"); 20 | 21 | class PedidoController { 22 | 23 | // ADMIN 24 | // get /admin indexAdmin 25 | async indexAdmin (req, res, next){ 26 | const { offset, limit, loja } = req.query; 27 | try { 28 | const pedidos = await Pedido.paginate( 29 | { loja }, 30 | { 31 | offset: Number(offset || 0), 32 | limit: Number(limit || 30), 33 | populate: ["cliente", "pagamento","entrega"] 34 | } 35 | ); 36 | pedidos.docs = await Promise.all(pedidos.docs.map(async (pedido) => { 37 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 38 | item.produto = await Produto.findById(item.produto); 39 | item.variacao = await Variacao.findById(item.variacao); 40 | return item; 41 | })); 42 | return pedido; 43 | })); 44 | return res.send({ pedidos }); 45 | } catch(e){ 46 | next(e); 47 | } 48 | } 49 | 50 | // get /admin/:id showAdmin 51 | async showAdmin(req,res,next){ 52 | try { 53 | const pedido = await Pedido 54 | .findOne({ loja: req.query.loja, _id: req.params.id }) 55 | .populate(["cliente","pagamento","entrega","loja"]); 56 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 57 | item.produto = await Produto.findById(item.produto); 58 | item.variacao = await Variacao.findById(item.variacao); 59 | return item; 60 | })); 61 | const registros = await RegistroPedido.find({ pedido: pedido._id }); 62 | return res.send({ pedido, registros }); 63 | }catch(e){ 64 | next(e); 65 | } 66 | } 67 | 68 | // delete /admin/:id removeAdmin 69 | async removeAdmin(req,res,next){ 70 | try { 71 | const pedido = await Pedido.findOne({ 72 | loja: req.query.loja, 73 | _id: req.params.id 74 | }) 75 | .populate({ path: "cliente", populate: { path: "usuario" } }); 76 | 77 | if(!pedido) return res.status(400).send({ error: "Pedido não encontrado" }); 78 | pedido.cancelado = true; 79 | 80 | const registroPedido = new RegistroPedido({ 81 | pedido: pedido._id, 82 | tipo: "pedido", 83 | situacao: "pedido_cancelado" 84 | }); 85 | await registroPedido.save(); 86 | 87 | EmailController.cancelarPedido({ usuario: pedido.cliente.usuario, pedido }); 88 | 89 | await pedido.save(); 90 | 91 | await QuantidadeValidation.atualizarQuantidade("cancelar_pedido", pedido); 92 | 93 | return res.send({ cancelado: true }); 94 | }catch(e){ 95 | next(e); 96 | } 97 | } 98 | 99 | // get /admin/:id/carrinho showCarrinhoPedidoAdmin 100 | async showCarrinhoPedidoAdmin(req,res,next){ 101 | try { 102 | const pedido = await Pedido.findOne({ loja: req.query.loja, _id: req.params.id }); 103 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 104 | item.produto = await Produto.findById(item.produto); 105 | item.variacao = await Variacao.findById(item.variacao); 106 | return item; 107 | })); 108 | return res.send({ carrinho: pedido.carrinho }); 109 | }catch(e){ 110 | next(e); 111 | } 112 | } 113 | 114 | // CLIENTE 115 | // get / index 116 | async index (req, res, next){ 117 | const { offset, limit, loja } = req.query; 118 | try { 119 | const cliente = await Cliente.findOne({ usuario: req.payload.id }); 120 | const pedidos = await Pedido.paginate( 121 | { loja, cliente: cliente._id }, 122 | { 123 | offset: Number(offset || 0), 124 | limit: Number(limit || 30), 125 | populate: ["cliente", "pagamento","entrega"] 126 | } 127 | ); 128 | pedidos.docs = await Promise.all(pedidos.docs.map(async (pedido) => { 129 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 130 | item.produto = await Produto.findById(item.produto); 131 | item.variacao = await Variacao.findById(item.variacao); 132 | return item; 133 | })); 134 | return pedido; 135 | })); 136 | return res.send({ pedidos }); 137 | } catch(e){ 138 | next(e); 139 | } 140 | } 141 | 142 | // get /:id show 143 | async show(req,res,next){ 144 | try { 145 | const cliente = await Cliente.findOne({ usuario: req.payload.id }); 146 | const pedido = await Pedido 147 | .findOne({ cliente: cliente._id, _id: req.params.id }) 148 | .populate(["cliente","pagamento","entrega","loja"]); 149 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 150 | item.produto = await Produto.findById(item.produto); 151 | item.variacao = await Variacao.findById(item.variacao); 152 | return item; 153 | })); 154 | const registros = await RegistroPedido.find({ pedido: pedido._id }); 155 | return res.send({ pedido, registros }); 156 | }catch(e){ 157 | next(e); 158 | } 159 | } 160 | 161 | // post / store 162 | async store(req,res,next){ 163 | const { carrinho, pagamento, entrega } = req.body; 164 | const { loja } = req.query; 165 | const _carrinho = carrinho.slice(); 166 | try { 167 | // CHECAR DADOS DO CARRINHO 168 | if(!await CarrinhoValidation(carrinho)) return res.status(422).send({ error: "Carrinho Inválido" }); 169 | 170 | const cliente = await Cliente.findOne({ usuario: req.payload.id }).populate({path:"usuario", select:"_id nome email"}); 171 | 172 | if(!await QuantidadeValidation.validarQuantidadeDisponivel(carrinho)) return res.status(400).send({ error: "Produtos não tem quantidade disponivel" }); 173 | 174 | // CHECAR DADOS DE ENTREGA 175 | if(!await EntregaValidation.checarValorPrazo(cliente.endereco.CEP, carrinho, entrega)) return res.status(422).send({ error: "Dados de Entrega Inválidos" }); 176 | 177 | // CHECAR DADOS DO PAGAMENTO 178 | if(!await PagamentoValidation.checarValorTotal({carrinho, entrega, pagamento})) return res.status(422).send({ error: "Dados de Pagamento Inválidos" }); 179 | if(!PagamentoValidation.checarCartao(pagamento)) return res.status(422).send({ error: "Dados de Pagamento com Cartao Inválidos" }); 180 | 181 | const novoPagamento = new Pagamento({ 182 | valor: pagamento.valor, 183 | parcelas: pagamento.parcelas || 1, 184 | forma: pagamento.forma, 185 | status: "iniciando", 186 | endereco: pagamento.endereco, 187 | cartao: pagamento.cartao, 188 | enderecoEntregaIgualCobranca: pagamento.enderecoEntregaIgualCobranca, 189 | loja 190 | }); 191 | 192 | const novaEntrega = new Entrega ({ 193 | status: "nao_iniciado", 194 | custo: entrega.custo, 195 | prazo: entrega.prazo, 196 | tipo: entrega.tipo, 197 | endereco: entrega.endereco, 198 | loja 199 | }); 200 | 201 | const pedido = new Pedido({ 202 | cliente: cliente._id, 203 | carrinho: _carrinho, 204 | pagamento: novoPagamento._id, 205 | entrega: novaEntrega._id, 206 | loja 207 | }); 208 | novoPagamento.pedido = pedido._id; 209 | novaEntrega.pedido = pedido._id; 210 | 211 | await pedido.save(); 212 | await novoPagamento.save(); 213 | await novaEntrega.save(); 214 | 215 | await QuantidadeValidation.atualizarQuantidade("salvar_pedido", pedido); 216 | 217 | const registroPedido = new RegistroPedido({ 218 | pedido: pedido._id, 219 | tipo: "pedido", 220 | situacao: "pedido_criado" 221 | }); 222 | await registroPedido.save(); 223 | 224 | EmailController.enviarNovoPedido({ pedido, usuario: cliente.usuario }); 225 | const administradores = await Usuario.find({ permissao: "admin", loja }); 226 | administradores.forEach((usuario) => { 227 | EmailController.enviarNovoPedido({ pedido, usuario }); 228 | }); 229 | 230 | return res.send({ pedido: Object.assign({}, pedido._doc, { entrega: novaEntrega, pagamento: novoPagamento, cliente }) }); 231 | }catch(e){ 232 | next(e); 233 | } 234 | } 235 | 236 | // delete /:id remove 237 | async remove(req,res,next){ 238 | try { 239 | const cliente = await Cliente.findOne({ usuario: req.payload.id }); 240 | if(!cliente) return res.status(400).send({ error: "Cliente não encontrado" }); 241 | const pedido = await Pedido.findOne({ cliente: cliente._id, _id: req.params.id }); 242 | if(!pedido) return res.status(400).send({ error: "Pedido não encontrado" }); 243 | pedido.cancelado = true; 244 | 245 | const registroPedido = new RegistroPedido({ 246 | pedido: pedido._id, 247 | tipo: "pedido", 248 | situacao: "pedido_cancelado" 249 | }); 250 | await registroPedido.save(); 251 | 252 | const administradores = await Usuario.find({ permissao: "admin", loja: pedido.loja }); 253 | administradores.forEach((usuario) => { 254 | EmailController.cancelarPedido({ pedido, usuario }); 255 | }); 256 | 257 | await pedido.save(); 258 | 259 | await QuantidadeValidation.atualizarQuantidade("cancelar_pedido", pedido); 260 | 261 | return res.send({ cancelado: true }); 262 | }catch(e){ 263 | next(e); 264 | } 265 | } 266 | 267 | // get /:id/carrinho showCarrinhoPedido 268 | async showCarrinhoPedido(req,res,next){ 269 | try { 270 | const cliente = await Cliente.findOne({ usuario: req.payload.id }); 271 | const pedido = await Pedido.findOne({ cliente: cliente._id, _id: req.params.id }); 272 | pedido.carrinho = await Promise.all(pedido.carrinho.map(async (item) => { 273 | item.produto = await Produto.findById(item.produto); 274 | item.variacao = await Variacao.findById(item.variacao); 275 | return item; 276 | })); 277 | return res.send({ carrinho: pedido.carrinho }); 278 | }catch(e){ 279 | next(e); 280 | } 281 | } 282 | 283 | 284 | } 285 | 286 | module.exports = PedidoController; --------------------------------------------------------------------------------