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 a28d4ec568..1486602a48 100644 --- a/packages/medusa-payment-klarna/src/api/routes/hooks/push.js +++ b/packages/medusa-payment-klarna/src/api/routes/hooks/push.js @@ -12,7 +12,10 @@ export default async (req, res) => { const cartId = klarnaOrder.merchant_data const order = await orderService.list({ cart_id: cartId })[0] - await klarnaProviderService.acknowledgeOrder(klarnaOrder.id, order._id) + await klarnaProviderService.acknowledgeOrder( + klarnaOrder.order_id, + order._id + ) res.sendStatus(200) } catch (error) { throw error diff --git a/packages/medusa-payment-klarna/src/services/klarna-provider.js b/packages/medusa-payment-klarna/src/services/klarna-provider.js index 24ac70eb99..4fcb0652d7 100644 --- a/packages/medusa-payment-klarna/src/services/klarna-provider.js +++ b/packages/medusa-payment-klarna/src/services/klarna-provider.js @@ -181,8 +181,10 @@ class KlarnaProviderService extends PaymentService { */ async getStatus(paymentData) { try { - const { id } = paymentData - const order = await this.klarna_.get(`${this.klarnaOrderUrl_}/${id}`) + const { order_id } = paymentData + const order = await this.klarna_.get( + `${this.klarnaOrderUrl_}/${order_id}` + ) // TODO: Klarna docs does not provide a list of statues, so we need to // play around our selves to figure it out let status = "initial" @@ -215,7 +217,7 @@ class KlarnaProviderService extends PaymentService { async retrievePayment(cart) { try { const { data } = cart.payment_method - return this.klarna_.get(`${this.klarnaOrderUrl_}/${data.id}`) + return this.klarna_.get(`${this.klarnaOrderUrl_}/${data.order_id}`) } catch (error) { throw error } @@ -283,7 +285,10 @@ class KlarnaProviderService extends PaymentService { async updatePayment(order, update) { try { const { data } = order.payment_method - return this.klarna_.post(`${this.klarnaOrderUrl_}/${data.id}`, update) + return this.klarna_.post( + `${this.klarnaOrderUrl_}/${data.order_id}`, + update + ) } catch (error) { throw error } @@ -296,17 +301,19 @@ class KlarnaProviderService extends PaymentService { */ async capturePayment(paymentData) { try { - const { id } = paymentData - const orderData = await this.klarna_.get(`${this.klarnaOrderUrl_}/${id}`) + const { order_id } = paymentData + const orderData = await this.klarna_.get( + `${this.klarnaOrderUrl_}/${order_id}` + ) const { order_amount } = orderData.order await this.klarna_.post( - `${this.klarnaOrderManagementUrl_}/${id}/captures`, + `${this.klarnaOrderManagementUrl_}/${order_id}/captures`, { captured_amount: order_amount, } ) - return id + return order_id } catch (error) { throw error } @@ -319,14 +326,14 @@ class KlarnaProviderService extends PaymentService { */ async refundPayment(paymentData, amount) { try { - const { id } = paymentData + const { order_id } = paymentData await this.klarna_.post( - `${this.klarnaOrderManagementUrl_}/${id}/refunds`, + `${this.klarnaOrderManagementUrl_}/${order_id}/refunds`, { refunded_amount: amount, } ) - return id + return order_id } catch (error) { throw error } @@ -339,9 +346,9 @@ class KlarnaProviderService extends PaymentService { */ async cancelPayment(paymentData) { try { - const { id } = paymentData - await this.klarna_.post(`${this.klarnaOrderUrl_}/${id}/cancel`) - return id + const { order_id } = paymentData + await this.klarna_.post(`${this.klarnaOrderUrl_}/${order_id}/cancel`) + return order_id } catch (error) { throw error } diff --git a/packages/medusa-plugin-sendgrid/src/services/sendgrid.js b/packages/medusa-plugin-sendgrid/src/services/sendgrid.js index 4d150ba5c1..4241597917 100644 --- a/packages/medusa-plugin-sendgrid/src/services/sendgrid.js +++ b/packages/medusa-plugin-sendgrid/src/services/sendgrid.js @@ -10,7 +10,7 @@ class SendGridService extends BaseService { * from: Medusa , * order_placed_template: 01234, * order_updated_template: 56789, - * order_updated_cancelled_template: 4242, + * order_cancelled_template: 4242, * user_password_reset_template: 0000, * customer_password_reset_template: 1111, * } @@ -42,6 +42,9 @@ class SendGridService extends BaseService { case "order.cancelled": templateId = this.options_.order_cancelled_template break + case "order.completed": + templateId = this.options_.order_completed_template + break case "user.password_reset": templateId = this.options_.user_password_reset_template break @@ -55,7 +58,7 @@ class SendGridService extends BaseService { try { return SendGrid.send({ template_id: templateId, - from: options.from, + from: this.options_.from, to: order.email, dynamic_template_data: order, }) diff --git a/packages/medusa-plugin-sendgrid/src/subscribers/order.js b/packages/medusa-plugin-sendgrid/src/subscribers/order.js index e6226cc787..af82482b43 100644 --- a/packages/medusa-plugin-sendgrid/src/subscribers/order.js +++ b/packages/medusa-plugin-sendgrid/src/subscribers/order.js @@ -12,6 +12,10 @@ class OrderSubscriber { await this.sendgridService_.transactionalEmail("order.cancelled", order) }) + this.eventBus_.subscribe("order.completed", async (order) => { + await this.sendgridService_.transactionalEmail("order.completed", order) + }) + this.eventBus_.subscribe("order.updated", async (order) => { await this.sendgridService_.transactionalEmail("order.updated", order) }) diff --git a/packages/medusa/src/api/routes/admin/orders/complete-order.js b/packages/medusa/src/api/routes/admin/orders/complete-order.js new file mode 100644 index 0000000000..c2ca4282a2 --- /dev/null +++ b/packages/medusa/src/api/routes/admin/orders/complete-order.js @@ -0,0 +1,12 @@ +export default async (req, res) => { + const { id } = req.params + + try { + const orderService = req.scope.resolve("orderService") + const order = await orderService.completeOrder(id) + res.json({ order }) + } catch (error) { + console.log(error) + throw error + } +} diff --git a/packages/medusa/src/api/routes/admin/orders/get-order.js b/packages/medusa/src/api/routes/admin/orders/get-order.js index 0eba81a217..d3912b871e 100644 --- a/packages/medusa/src/api/routes/admin/orders/get-order.js +++ b/packages/medusa/src/api/routes/admin/orders/get-order.js @@ -3,7 +3,9 @@ export default async (req, res) => { try { const orderService = req.scope.resolve("orderService") - const order = await orderService.retrieve(id) + + let order = await orderService.retrieve(id) + order = await orderService.decorate(order, [], ["region"]) res.json({ order }) } catch (error) { diff --git a/packages/medusa/src/api/routes/admin/orders/index.js b/packages/medusa/src/api/routes/admin/orders/index.js index 195674feb3..7f71c698ab 100644 --- a/packages/medusa/src/api/routes/admin/orders/index.js +++ b/packages/medusa/src/api/routes/admin/orders/index.js @@ -11,6 +11,10 @@ export default app => { route.post("/", middlewares.wrap(require("./create-order").default)) route.post("/:id", middlewares.wrap(require("./update-order").default)) + route.post( + "/:id/complete", + middlewares.wrap(require("./complete-order").default) + ) route.post( "/:id/capture", diff --git a/packages/medusa/src/loaders/index.js b/packages/medusa/src/loaders/index.js index e00ffbdce1..a39d783abd 100644 --- a/packages/medusa/src/loaders/index.js +++ b/packages/medusa/src/loaders/index.js @@ -4,6 +4,7 @@ import mongooseLoader from "./mongoose" import apiLoader from "./api" import modelsLoader from "./models" import servicesLoader from "./services" +import subscribersLoader from "./subscribers" import passportLoader from "./passport" import pluginsLoader from "./plugins" import defaultsLoader from "./defaults" @@ -37,6 +38,9 @@ export default async ({ directory: rootDirectory, expressApp }) => { await servicesLoader({ container }) Logger.info("Services initialized") + await subscribersLoader({ container }) + Logger.info("Subscribers initialized") + const dbConnection = await mongooseLoader({ container }) Logger.info("MongoDB Intialized") diff --git a/packages/medusa/src/loaders/subscribers.js b/packages/medusa/src/loaders/subscribers.js new file mode 100644 index 0000000000..2340c03ca3 --- /dev/null +++ b/packages/medusa/src/loaders/subscribers.js @@ -0,0 +1,22 @@ +import glob from "glob" +import path from "path" +import { asFunction } from "awilix" + +/** + * Registers all subscribers in the subscribers directory + */ +export default ({ container }) => { + const isTest = process.env.NODE_ENV === "test" + + const corePath = isTest + ? "../subscribers/__mocks__/*.js" + : "../subscribers/*.js" + const coreFull = path.join(__dirname, corePath) + + const core = glob.sync(coreFull, { cwd: __dirname }) + core.forEach(fn => { + const loaded = require(fn).default + + container.build(asFunction(cradle => new loaded(cradle)).singleton()) + }) +} diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index 87f398302f..b157d5c880 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -7,6 +7,7 @@ class OrderService extends BaseService { PLACED: "order.placed", UPDATED: "order.updated", CANCELLED: "order.cancelled", + COMPLETED: "order.completed", } constructor({ @@ -16,6 +17,7 @@ class OrderService extends BaseService { fulfillmentProviderService, lineItemService, totalsService, + regionService, eventBusService, }) { super() @@ -38,6 +40,9 @@ class OrderService extends BaseService { /** @private @const {TotalsService} */ this.totalsService_ = totalsService + /** @private @const {RegionService} */ + this.regionService_ = regionService + /** @private @const {EventBus} */ this.eventBus_ = eventBusService } @@ -200,6 +205,37 @@ class OrderService extends BaseService { return this.orderModel_.find(selector) } + /** + * @param {string} orderId - id of the order to complete + * @return {Promise} the result of the find operation + */ + async completeOrder(orderId) { + const order = await this.retrieve(orderId) + this.orderModel_ + .updateOne( + { _id: order._id }, + { + $set: { status: "completed" }, + } + ) + .then(async result => { + const completeOrderJob = await this.eventBus_.emit( + OrderService.Events.COMPLETED, + result + ) + + return completeOrderJob + .finished() + .then(async () => { + return this.retrieve(order._id) + }) + .catch(error => { + console.log(error) + throw error + }) + }) + } + /** * Creates an order from a cart * @param {object} order - the order to create @@ -603,9 +639,17 @@ class OrderService extends BaseService { * @return {Order} return the decorated order. */ async decorate(order, fields, expandFields = []) { - const requiredFields = ["_id", "metadata"] - const decorated = _.pick(order, fields.concat(requiredFields)) - return decorated + const o = order.toObject() + o.shipping_total = await this.totalsService_.getShippingTotal(order) + o.discount_total = await this.totalsService_.getDiscountTotal(order) + o.tax_total = await this.totalsService_.getTaxTotal(order) + o.subtotal = await this.totalsService_.getSubtotal(order) + o.total = await this.totalsService_.getTotal(order) + o.created = order._id.getTimestamp() + if (expandFields.includes("region")) { + o.region = await this.regionService_.retrieve(order.region_id) + } + return o } /** diff --git a/packages/medusa/src/subscribers/order.js b/packages/medusa/src/subscribers/order.js new file mode 100644 index 0000000000..b88c2e410f --- /dev/null +++ b/packages/medusa/src/subscribers/order.js @@ -0,0 +1,17 @@ +class OrderSubscriber { + constructor({ paymentProviderService, eventBusService }) { + this.paymentProviderService_ = paymentProviderService + + this.eventBus_ = eventBusService + + this.eventBus_.subscribe("order.completed", async order => { + const paymentProvider = this.paymentProviderService_.retrieveProvider( + order.payment_method.provider_id + ) + + await paymentProvider.capturePayment(order._id) + }) + } +} + +export default OrderSubscriber