From 08cf7c7f2b63ac534153d822aa5422a03435ccd2 Mon Sep 17 00:00:00 2001 From: olivermrbl Date: Thu, 9 Jul 2020 18:16:45 +0200 Subject: [PATCH] Adds createFromCart and order.completed subscribers --- .../src/api/routes/hooks/stripe.js | 22 ++++++--- .../src/services/stripe-provider.js | 2 + .../src/subscribers/cart.js | 9 ++-- .../src/subscribers/order.js | 4 ++ .../api/routes/store/orders/create-order.js | 2 +- packages/medusa/src/models/__mocks__/order.js | 6 +++ .../medusa/src/services/__tests__/order.js | 45 +++++++++++++++++++ packages/medusa/src/services/order.js | 39 ++++++++++++++++ 8 files changed, 119 insertions(+), 10 deletions(-) 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 9584b26a78..03e9e6b0d4 100644 --- a/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js +++ b/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js @@ -12,19 +12,31 @@ export default async (req, res) => { const paymentIntent = event.data.object + const orderService = req.scope.resolve("orderService") + // handle payment intent events switch (event.type) { case "payment_intent.succeeded": + const cartId = paymentIntent.metadata.cart_id + const order = await orderService.retrieveByCartId(cartId) + + await orderService.update(order._id, { + payment_status: "captured", + }) break - case "payment_intent.canceled": - break - case "payment_intent.created": + case "payment_intent.cancelled": + const cartId = paymentIntent.metadata.cart_id + const order = await orderService.retrieveByCartId(cartId) + + await orderService.update(order._id, { + status: "cancelled", + }) break case "payment_intent.payment_failed": + // TODO: Not implemented yet break case "payment_intent.amount_capturable_updated": - break - case "payment_intent.processing": + // TODO: Not implemented yet 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 87001ddccc..412c6bcc38 100644 --- a/packages/medusa-payment-stripe/src/services/stripe-provider.js +++ b/packages/medusa-payment-stripe/src/services/stripe-provider.js @@ -104,6 +104,8 @@ class StripeProviderService extends PaymentService { customer: stripeCustomerId, amount: amount * 100, // Stripe amount is in cents currency: currency_code, + capture_method: "manual", + metadata: { cart_id: cart._id }, }) return paymentIntent diff --git a/packages/medusa-payment-stripe/src/subscribers/cart.js b/packages/medusa-payment-stripe/src/subscribers/cart.js index e5a0af6c1e..ef3b65fa5d 100644 --- a/packages/medusa-payment-stripe/src/subscribers/cart.js +++ b/packages/medusa-payment-stripe/src/subscribers/cart.js @@ -10,13 +10,14 @@ class CartSubscriber { this.stripeProviderService_ = stripeProviderService this.eventBus_ = eventBusService - this.eventBus_.subscribe("cart.created", (data) => { - console.log(data) - }) - this.eventBus_.subscribe("cart.customer_updated", async (cart) => { await this.onCustomerUpdated(cart) }) + + this.eventBus_.subscribe("order.completed", async (order) => { + const paymentData = order.payment_method.data + await this.stripeProviderService_.capturePayment(paymentData) + }) } async onCustomerUpdated(cart) { diff --git a/packages/medusa-plugin-economic/src/subscribers/order.js b/packages/medusa-plugin-economic/src/subscribers/order.js index 038254f0dc..40c81f41a8 100644 --- a/packages/medusa-plugin-economic/src/subscribers/order.js +++ b/packages/medusa-plugin-economic/src/subscribers/order.js @@ -7,6 +7,10 @@ class OrderSubscriber { this.eventBus_.subscribe("order.placed", async (order) => { await this.economicService_.draftEconomicInvoice(order) }) + + this.eventBus_.subscribe("order.completed", async (order) => { + await this.economicService_.bookEconomicInvoice(order._id) + }) } } diff --git a/packages/medusa/src/api/routes/store/orders/create-order.js b/packages/medusa/src/api/routes/store/orders/create-order.js index bbba9da8fa..b4d3a5ea7e 100644 --- a/packages/medusa/src/api/routes/store/orders/create-order.js +++ b/packages/medusa/src/api/routes/store/orders/create-order.js @@ -15,7 +15,7 @@ export default async (req, res) => { const orderService = req.scope.resolve("orderService") const cart = await cartService.retrieve(value.cartId) - let order = await orderService.create(cart) + let order = await orderService.createFromCart(cart) order = await orderService.decorate(order, [ "status", "fulfillment_status", diff --git a/packages/medusa/src/models/__mocks__/order.js b/packages/medusa/src/models/__mocks__/order.js index e3c198ed5f..67530bf4e4 100644 --- a/packages/medusa/src/models/__mocks__/order.js +++ b/packages/medusa/src/models/__mocks__/order.js @@ -63,6 +63,9 @@ export const orders = { fulfillment_status: "not_fulfilled", payment_status: "awaiting", status: "pending", + metadata: { + cart_id: IdMap.getId("test-cart"), + }, }, processedOrder: { _id: IdMap.getId("processed-order"), @@ -237,6 +240,9 @@ export const OrderModelMock = { orders.orderToRefund.payment_status = "captured" return Promise.resolve(orders.orderToRefund) } + if (query.metadata.cart_id === IdMap.getId("test-cart")) { + return Promise.resolve(orders.testOrder) + } return Promise.resolve(undefined) }), } diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index c760eefe58..8bee6106ab 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -1,5 +1,6 @@ import { IdMap } from "medusa-test-utils" import { OrderModelMock, orders } from "../../models/__mocks__/order" +import { carts } from "../../models/__mocks__/cart" import OrderService from "../order" import { PaymentProviderServiceMock } from "../__mocks__/payment-provider" import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider" @@ -30,6 +31,27 @@ describe("OrderService", () => { }) }) + describe("createFromCart", () => { + const orderService = new OrderService({ + orderModel: OrderModelMock, + eventBusService: EventBusServiceMock, + }) + + beforeEach(async () => { + jest.clearAllMocks() + }) + + it("calls order model functions", async () => { + await orderService.createFromCart(carts.completeCart) + + expect(OrderModelMock.create).toHaveBeenCalledTimes(1) + expect(OrderModelMock.create).toHaveBeenCalledWith({ + ...carts.completeCart, + metadata: { cart_id: carts.completeCart._id }, + }) + }) + }) + describe("retrieve", () => { let result const orderService = new OrderService({ @@ -53,6 +75,29 @@ describe("OrderService", () => { }) }) + describe("retrieveByCartId", () => { + let result + const orderService = new OrderService({ + orderModel: OrderModelMock, + }) + + beforeAll(async () => { + jest.clearAllMocks() + result = await orderService.retrieveByCartId(IdMap.getId("test-cart")) + }) + + it("calls order model functions", async () => { + expect(OrderModelMock.findOne).toHaveBeenCalledTimes(1) + expect(OrderModelMock.findOne).toHaveBeenCalledWith({ + metadata: { cart_id: IdMap.getId("test-cart") }, + }) + }) + + it("returns correct order", async () => { + expect(result._id).toEqual(IdMap.getId("test-order")) + }) + }) + describe("update", () => { const orderService = new OrderService({ orderModel: OrderModelMock, diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index 603ad97ff8..f58da00647 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -153,6 +153,27 @@ class OrderService extends BaseService { return order } + /** + * Gets an order by cart id. + * @param {string} cartId - cart id to find order + * @return {Promise} the order document + */ + async retrieveByCartId(cartId) { + const order = await this.orderModel_ + .findOne({ metadata: { cart_id: cartId } }) + .catch(err => { + throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) + }) + + if (!order) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Order with cart id ${cartId} was not found` + ) + } + return order + } + /** * @param {Object} selector - the query object for find * @return {Promise} the result of the find operation @@ -161,6 +182,24 @@ class OrderService extends BaseService { return this.orderModel_.find(selector) } + /** + * Creates an order from a cart + * @param {object} order - the order to create + * @return {Promise} resolves to the creation result. + */ + async createFromCart(cart) { + return this.orderModel_ + .create({ ...cart, metadata: { cart_id: cart._id } }) + .then(result => { + // Notify subscribers + this.eventBus_.emit(OrderService.Events.PLACED, result) + return result + }) + .catch(err => { + throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) + }) + } + /** * Creates an order * @param {object} order - the order to create