From 312e4055883315d25615d393d0583860f94ec1f9 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Tue, 14 Jul 2020 17:23:43 +0200 Subject: [PATCH] Robustness --- packages/medusa-core-utils/src/validator.js | 2 +- .../src/api/routes/hooks/address.js | 3 +- .../src/api/routes/hooks/index.js | 2 + .../src/api/routes/hooks/push.js | 5 +- .../src/services/klarna-provider.js | 18 +++-- .../src/api/routes/hooks/index.js | 4 +- .../src/api/routes/hooks/stripe.js | 24 +++++-- .../src/services/stripe-provider.js | 9 +++ .../src/subscribers/cart.js | 2 +- .../src/services/sendgrid.js | 7 +- .../store/carts/delete-payment-session.js | 14 ++++ .../src/api/routes/store/carts/index.js | 5 ++ .../src/api/routes/store/customers/index.js | 4 -- .../routes/store/customers/reset-password.js | 9 +-- .../routes/store/customers/update-customer.js | 10 +-- .../routes/store/customers/update-password.js | 26 -------- packages/medusa/src/loaders/api.js | 4 ++ packages/medusa/src/loaders/express.js | 2 - packages/medusa/src/services/cart.js | 65 ++++++++++++++++--- packages/medusa/src/services/customer.js | 6 +- .../medusa/src/services/payment-provider.js | 5 ++ 21 files changed, 152 insertions(+), 74 deletions(-) create mode 100644 packages/medusa/src/api/routes/store/carts/delete-payment-session.js delete mode 100644 packages/medusa/src/api/routes/store/customers/update-password.js diff --git a/packages/medusa-core-utils/src/validator.js b/packages/medusa-core-utils/src/validator.js index 18f8619bd2..cad2195b18 100644 --- a/packages/medusa-core-utils/src/validator.js +++ b/packages/medusa-core-utils/src/validator.js @@ -12,7 +12,7 @@ Joi.address = () => { country_code: Joi.string().required(), province: Joi.string().allow(""), postal_code: Joi.string().required(), - phone: Joi.string().required(), + phone: Joi.string(), metadata: Joi.object(), }) } diff --git a/packages/medusa-payment-klarna/src/api/routes/hooks/address.js b/packages/medusa-payment-klarna/src/api/routes/hooks/address.js index 4b9fc24762..302d1bc795 100644 --- a/packages/medusa-payment-klarna/src/api/routes/hooks/address.js +++ b/packages/medusa-payment-klarna/src/api/routes/hooks/address.js @@ -16,8 +16,9 @@ export default async (req, res) => { address_1: shipping_address.street_address, address_2: shipping_address.street_address2, city: shipping_address.city, - country_code: shipping_address.country, + country_code: shipping_address.country.toUpperCase(), postal_code: shipping_address.postal_code, + phone: shipping_address.phone } await cartService.updateShippingAddress(cart._id, updatedAddress) diff --git a/packages/medusa-payment-klarna/src/api/routes/hooks/index.js b/packages/medusa-payment-klarna/src/api/routes/hooks/index.js index 16edcd40fb..909dbe2fd8 100644 --- a/packages/medusa-payment-klarna/src/api/routes/hooks/index.js +++ b/packages/medusa-payment-klarna/src/api/routes/hooks/index.js @@ -1,4 +1,5 @@ import { Router } from "express" +import bodyParser from "body-parser" import middlewares from "../../middlewares" const route = Router() @@ -6,6 +7,7 @@ const route = Router() export default (app) => { app.use("/klarna", route) + route.use(bodyParser.json()) route.post("/address", middlewares.wrap(require("./address").default)) route.post("/shipping", middlewares.wrap(require("./shipping").default)) route.post("/push", middlewares.wrap(require("./push").default)) diff --git a/packages/medusa-payment-klarna/src/api/routes/hooks/push.js b/packages/medusa-payment-klarna/src/api/routes/hooks/push.js index 99cfd64da1..2e9a6a9796 100644 --- a/packages/medusa-payment-klarna/src/api/routes/hooks/push.js +++ b/packages/medusa-payment-klarna/src/api/routes/hooks/push.js @@ -10,15 +10,14 @@ export default async (req, res) => { const klarnaOrder = await klarnaProviderService.retrieveCompletedOrder( klarna_order_id - ) + ).then(({ data }) => data) const cartId = klarnaOrder.merchant_data - try { const order = await orderService.retrieveByCartId(cartId) await klarnaProviderService.acknowledgeOrder(klarnaOrder.order_id, order._id) } catch (err) { - if (err.type === MeudsaError.Types.NOT_FOUND) { + if (err.type === MedusaError.Types.NOT_FOUND) { const cart = await cartService.retrieve(cartId) const order = await orderService.createFromCart(cart) await klarnaProviderService.acknowledgeOrder(klarnaOrder.order_id, order._id) diff --git a/packages/medusa-payment-klarna/src/services/klarna-provider.js b/packages/medusa-payment-klarna/src/services/klarna-provider.js index 752692337a..2c1dc34765 100644 --- a/packages/medusa-payment-klarna/src/services/klarna-provider.js +++ b/packages/medusa-payment-klarna/src/services/klarna-provider.js @@ -26,7 +26,7 @@ class KlarnaProviderService extends PaymentService { this.klarnaOrderManagementUrl_ = "/ordermanagement/v1/orders" this.backendUrl_ = - process.env.BACKEND_URL || "https://2fe4e28015f5.ngrok.io" + process.env.BACKEND_URL || "https://7e9a5bc2a2eb.ngrok.io" this.totalsService_ = totalsService @@ -155,7 +155,7 @@ class KlarnaProviderService extends PaymentService { terms: this.options_.merchant_urls.terms, checkout: this.options_.merchant_urls.checkout, confirmation: this.options_.merchant_urls.confirmation, - push: `${this.backendUrl_}/klarna/push?klarna_order_id={checkout.order_id}`, + push: `${this.backendUrl_}/klarna/push?klarna_order_id={checkout.order.id}`, shipping_option_update: `${this.backendUrl_}/klarna/shipping`, address_update: `${this.backendUrl_}/klarna/address`, } @@ -266,12 +266,20 @@ class KlarnaProviderService extends PaymentService { * @param {string} klarnaOrderId - id of the order to acknowledge * @returns {string} id of acknowledged order */ - async acknowledgeOrder(klarnaOrderId) { + async acknowledgeOrder(klarnaOrderId, orderId) { try { await this.klarna_.post( - `${this.klarnaOrderManagementUrl_}/${klarnaOrderId}/acknowledge`, - {} + `${this.klarnaOrderManagementUrl_}/${klarnaOrderId}/acknowledge` ) + + await this.klarna_.patch( + `${this.klarnaOrderManagementUrl_}/${klarnaOrderId}/merchant-references`, + { + merchant_reference1: orderId + } + ) + + return klarnaOrderId } catch (error) { diff --git a/packages/medusa-payment-stripe/src/api/routes/hooks/index.js b/packages/medusa-payment-stripe/src/api/routes/hooks/index.js index 9a0e535ae5..68f1ccdb86 100644 --- a/packages/medusa-payment-stripe/src/api/routes/hooks/index.js +++ b/packages/medusa-payment-stripe/src/api/routes/hooks/index.js @@ -5,10 +5,10 @@ import middlewares from "../../middlewares" const route = Router() export default (app) => { - app.use("/hooks", route) + app.use("/stripe", route) route.post( - "/stripe", + "/hooks", // stripe constructEvent fails without body-parser bodyParser.raw({ type: "application/json" }), middlewares.wrap(require("./stripe").default) diff --git a/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js b/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js index 407d1b5b3d..231f303e88 100644 --- a/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js +++ b/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js @@ -12,27 +12,37 @@ export default async (req, res) => { const paymentIntent = event.data.object + const cartService = req.scope.resolve("cartService") const orderService = req.scope.resolve("orderService") + const cartId = paymentIntent.metadata.cart_id const order = await orderService.retrieveByCartId(cartId) + .catch(() => undefined) // handle payment intent events switch (event.type) { case "payment_intent.succeeded": - await orderService.update(order._id, { - payment_status: "captured", - }) + if (order) { + await orderService.update(order._id, { + payment_status: "captured", + }) + } break case "payment_intent.cancelled": - await orderService.update(order._id, { - status: "cancelled", - }) + if (order) { + await orderService.update(order._id, { + status: "cancelled", + }) + } break case "payment_intent.payment_failed": // TODO: Not implemented yet break case "payment_intent.amount_capturable_updated": - // TODO: Not implemented yet + if (!order) { + const cart = await cartService.retrieve(cartId) + await orderService.createFromCart(cart) + } break default: res.status(400) diff --git a/packages/medusa-payment-stripe/src/services/stripe-provider.js b/packages/medusa-payment-stripe/src/services/stripe-provider.js index c4e575c1a1..183b34c8af 100644 --- a/packages/medusa-payment-stripe/src/services/stripe-provider.js +++ b/packages/medusa-payment-stripe/src/services/stripe-provider.js @@ -158,6 +158,15 @@ class StripeProviderService extends PaymentService { } } + async deletePayment(data) { + try { + const { id } = data + return this.stripe_.paymentIntents.cancel(id) + } catch (error) { + throw error + } + } + /** * Updates customer of Stripe PaymentIntent. * @param {string} cart - the cart to update payment intent for diff --git a/packages/medusa-payment-stripe/src/subscribers/cart.js b/packages/medusa-payment-stripe/src/subscribers/cart.js index 4cca3bdfcb..b1c694ad82 100644 --- a/packages/medusa-payment-stripe/src/subscribers/cart.js +++ b/packages/medusa-payment-stripe/src/subscribers/cart.js @@ -60,7 +60,7 @@ class CartSubscriber { } if (stripeCustomer.id !== paymentIntent.customer) { - await this.stripeProviderService_.cancelPayment(paymentIntent.id) + await this.stripeProviderService_.cancelPayment(paymentIntent) const newPaymentIntent = await this.stripeProviderService_.createPayment( cart ) diff --git a/packages/medusa-plugin-sendgrid/src/services/sendgrid.js b/packages/medusa-plugin-sendgrid/src/services/sendgrid.js index 4241597917..d50d4a4397 100644 --- a/packages/medusa-plugin-sendgrid/src/services/sendgrid.js +++ b/packages/medusa-plugin-sendgrid/src/services/sendgrid.js @@ -30,7 +30,7 @@ class SendGridService extends BaseService { * correlate with the structure specificed in the dynamic template * @returns {Promise} result of the send operation */ - async transactionalEmail(event, order) { + async transactionalEmail(event, data) { let templateId switch (event) { case "order.placed": @@ -54,13 +54,12 @@ class SendGridService extends BaseService { default: return } - try { return SendGrid.send({ template_id: templateId, from: this.options_.from, - to: order.email, - dynamic_template_data: order, + to: data.email, + dynamic_template_data: data, }) } catch (error) { throw error diff --git a/packages/medusa/src/api/routes/store/carts/delete-payment-session.js b/packages/medusa/src/api/routes/store/carts/delete-payment-session.js new file mode 100644 index 0000000000..55104d4bd5 --- /dev/null +++ b/packages/medusa/src/api/routes/store/carts/delete-payment-session.js @@ -0,0 +1,14 @@ +export default async (req, res) => { + const { id, provider_id } = req.params + + try { + const cartService = req.scope.resolve("cartService") + + let cart = await cartService.deletePaymentSession(id, provider_id) + cart = await cartService.decorate(cart, [], ["region"]) + + res.status(200).json({ cart }) + } catch (err) { + throw err + } +} diff --git a/packages/medusa/src/api/routes/store/carts/index.js b/packages/medusa/src/api/routes/store/carts/index.js index a561399b58..b90151ca48 100644 --- a/packages/medusa/src/api/routes/store/carts/index.js +++ b/packages/medusa/src/api/routes/store/carts/index.js @@ -35,6 +35,11 @@ export default app => { "/:id/payment-sessions", middlewares.wrap(require("./create-payment-sessions").default) ) + + route.delete( + "/:id/payment-sessions/:provider_id", + middlewares.wrap(require("./delete-payment-session").default) + ) route.post( "/:id/payment-method", middlewares.wrap(require("./update-payment-method").default) diff --git a/packages/medusa/src/api/routes/store/customers/index.js b/packages/medusa/src/api/routes/store/customers/index.js index e1a78e2681..fa58e6e848 100644 --- a/packages/medusa/src/api/routes/store/customers/index.js +++ b/packages/medusa/src/api/routes/store/customers/index.js @@ -25,10 +25,6 @@ export default app => { route.get("/:id", middlewares.wrap(require("./get-customer").default)) route.post("/:id", middlewares.wrap(require("./update-customer").default)) - route.post( - "/:id/password", - middlewares.wrap(require("./update-password").default) - ) route.post( "/:id/addresses", diff --git a/packages/medusa/src/api/routes/store/customers/reset-password.js b/packages/medusa/src/api/routes/store/customers/reset-password.js index 036c39783e..9ca9ca51d5 100644 --- a/packages/medusa/src/api/routes/store/customers/reset-password.js +++ b/packages/medusa/src/api/routes/store/customers/reset-password.js @@ -20,13 +20,14 @@ export default async (req, res) => { const customer = await customerService.retrieveByEmail(value.email) const decodedToken = await jwt.verify(value.token, customer.password_hash) - if (!decodedToken || decodedToken.customer_id !== customer._id) { + if (!decodedToken || !customer._id.equals(decodedToken.customer_id)) { res.status(401).send("Invalid or expired password reset token") + return } - await customerService.update(customer._id, { password: value.password }) - - const updated = await customerService.retrieve(customer._id) + const updated = await customerService.update(customer._id, { + password: value.password, + }) const data = await customerService.decorate(customer) res.status(200).json({ customer: data }) } catch (error) { diff --git a/packages/medusa/src/api/routes/store/customers/update-customer.js b/packages/medusa/src/api/routes/store/customers/update-customer.js index bde5a7c3ce..e39b411893 100644 --- a/packages/medusa/src/api/routes/store/customers/update-customer.js +++ b/packages/medusa/src/api/routes/store/customers/update-customer.js @@ -16,10 +16,12 @@ export default async (req, res) => { try { const customerService = req.scope.resolve("customerService") - await customerService.update(id, value) - - const customer = await customerService.retrieve(id) - const data = await customerService.decorate(customer) + const customer = await customerService.update(id, value) + const data = await customerService.decorate( + customer, + ["email", "first_name", "last_name", "shipping_addresses"], + ["orders"] + ) res.status(200).json({ customer: data }) } catch (err) { throw err diff --git a/packages/medusa/src/api/routes/store/customers/update-password.js b/packages/medusa/src/api/routes/store/customers/update-password.js deleted file mode 100644 index 4def54a68b..0000000000 --- a/packages/medusa/src/api/routes/store/customers/update-password.js +++ /dev/null @@ -1,26 +0,0 @@ -import { Validator, MedusaError } from "medusa-core-utils" - -export default async (req, res) => { - const { id } = req.params - - const schema = Validator.object().keys({ - password: Validator.string().required(), - }) - - const { value, error } = schema.validate(req.body) - if (error) { - throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details) - } - - try { - const customerService = req.scope.resolve("customerService") - - await customerService.update(id, value) - - const customer = await customerService.retrieve(id) - const data = await customerService.decorate(customer) - res.status(200).json({ customer: data }) - } catch (err) { - throw err - } -} diff --git a/packages/medusa/src/loaders/api.js b/packages/medusa/src/loaders/api.js index 10c05bcee6..65d2a5fbeb 100644 --- a/packages/medusa/src/loaders/api.js +++ b/packages/medusa/src/loaders/api.js @@ -1,3 +1,4 @@ +import bodyParser from "body-parser" import { getConfigFile } from "medusa-core-utils" import routes from "../api" @@ -5,6 +6,9 @@ import routes from "../api" export default async ({ app, rootDirectory, container }) => { const { configModule } = getConfigFile(rootDirectory, `medusa-config`) const config = (configModule && configModule.projectConfig) || {} + + app.use(bodyParser.json()) app.use("/", routes(container, config)) + return app } diff --git a/packages/medusa/src/loaders/express.js b/packages/medusa/src/loaders/express.js index 59917d0c19..7dd22f674a 100644 --- a/packages/medusa/src/loaders/express.js +++ b/packages/medusa/src/loaders/express.js @@ -1,5 +1,4 @@ import express from "express" -import bodyParser from "body-parser" import session from "client-sessions" import cookieParser from "cookie-parser" import morgan from "morgan" @@ -14,7 +13,6 @@ export default async ({ app }) => { }) ) app.use(cookieParser()) - app.use(bodyParser.json()) app.use( session({ cookieName: "session", diff --git a/packages/medusa/src/services/cart.js b/packages/medusa/src/services/cart.js index c4917ec8c1..61b91ab65f 100644 --- a/packages/medusa/src/services/cart.js +++ b/packages/medusa/src/services/cart.js @@ -539,10 +539,7 @@ class CartService extends BaseService { const cart = await this.retrieve(cartId) const { value, error } = Validator.address().validate(address) if (error) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "The address is not valid" - ) + throw new MedusaError(MedusaError.Types.INVALID_DATA, error.message) } address.country_code = address.country_code.toUpperCase() @@ -573,10 +570,7 @@ class CartService extends BaseService { const cart = await this.retrieve(cartId) const { value, error } = Validator.address().validate(address) if (error) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "The address is not valid" - ) + throw new MedusaError(MedusaError.Types.INVALID_DATA, error.message) } address.country_code = address.country_code.toUpperCase() @@ -867,6 +861,61 @@ class CartService extends BaseService { }) } + async deletePaymentSession(cartId, providerId) { + const cart = await this.retrieve(cartId) + if (cart.payment_sessions) { + const session = cart.payment_sessions.find( + s => s.provider_id === providerId + ) + + if (session) { + // Delete the session with the provider + await this.paymentProviderService_.deleteSession(session) + + const selector = { + $pull: { payment_sessions: { provider_id: providerId } }, + } + + if ( + cart.payment_method && + cart.payment_method.provider_id === providerId + ) { + selector["$set"] = { payment_method: null } + } + + return this.cartModel_ + .updateOne({ _id: cart._id }, selector) + .then(result => { + // Notify subscribers + this.eventBus_.emit(CartService.Events.UPDATED, result) + return result + }) + } + } + + return cart + } + + async updatePaymentSession(cartId, providerId, session) { + const cart = await this.retrieve(cartId) + + return this.cartModel_ + .updateOne( + { + _id: cart._id, + "payment_sessions.provider_id": providerId, + }, + { + $set: { "payment_sessions.$": session }, + } + ) + .then(result => { + // Notify subscribers + this.eventBus_.emit(CartService.Events.UPDATED, result) + return result + }) + } + /** * Adds the shipping method to the list of shipping methods associated with * the cart. Shipping Methods are the ways that an order is shipped, whereas a diff --git a/packages/medusa/src/services/customer.js b/packages/medusa/src/services/customer.js index 8ed4bb70f3..0787b6d842 100644 --- a/packages/medusa/src/services/customer.js +++ b/packages/medusa/src/services/customer.js @@ -1,4 +1,4 @@ -import mongoose from "mongoose" +import jwt from "jsonwebtoken" import bcrypt from "bcrypt" import _ from "lodash" import { Validator, MedusaError } from "medusa-core-utils" @@ -102,6 +102,8 @@ class CustomerService extends BaseService { // Notify subscribers this.eventBus_.emit(CustomerService.Events.PASSWORD_RESET, { email: customer.email, + first_name: customer.first_name, + last_name: customer.last_name, token, }) return token @@ -328,7 +330,7 @@ class CustomerService extends BaseService { * @param {string[]} expandFields - fields to expand. * @return {Customer} return the decorated customer. */ - async decorate(customer, fields, expandFields = []) { + async decorate(customer, fields = [], expandFields = []) { const requiredFields = ["_id", "metadata"] const decorated = _.pick(customer, fields.concat(requiredFields)) diff --git a/packages/medusa/src/services/payment-provider.js b/packages/medusa/src/services/payment-provider.js index d23fea7798..9a65508de1 100644 --- a/packages/medusa/src/services/payment-provider.js +++ b/packages/medusa/src/services/payment-provider.js @@ -32,6 +32,11 @@ class PaymentProviderService { return provider.updatePayment(paymentSession.data, cart) } + deleteSession(paymentSession) { + const provider = this.retrieveProvider(paymentSession.provider_id) + return provider.deletePayment(paymentSession.data) + } + /** * Finds a provider given an id * @param {string} providerId - the id of the provider to get