diff --git a/packages/medusa-interfaces/src/base-model.js b/packages/medusa-interfaces/src/base-model.js index b7df4afda6..6cada78213 100644 --- a/packages/medusa-interfaces/src/base-model.js +++ b/packages/medusa-interfaces/src/base-model.js @@ -44,6 +44,13 @@ class BaseModel { return mongoose.model(this.getModelName(), this.getSchema()) } + /** + * @private + */ + startSession() { + return this.mongooseModel_.startSession() + } + /** * Queries the mongoose model via the mongoose's findOne. * @param query {object} a mongoose selector query diff --git a/packages/medusa-payment-stripe/src/services/stripe-provider.js b/packages/medusa-payment-stripe/src/services/stripe-provider.js index 412c6bcc38..c3be378d4c 100644 --- a/packages/medusa-payment-stripe/src/services/stripe-provider.js +++ b/packages/medusa-payment-stripe/src/services/stripe-provider.js @@ -35,7 +35,7 @@ class StripeProviderService extends PaymentService { return status } - if (paymentIntent.status === "requires_action") { + if (paymentIntent.status === "requires_capture") { status = "authorized" } diff --git a/packages/medusa/src/models/order.js b/packages/medusa/src/models/order.js index 3389a01ea3..f6e93eed1d 100644 --- a/packages/medusa/src/models/order.js +++ b/packages/medusa/src/models/order.js @@ -18,7 +18,7 @@ class OrderModel extends BaseModel { // awaiting, captured, refunded payment_status: { type: String, default: "awaiting" }, email: { type: String, required: true }, - cart_id: { type: String }, + cart_id: { type: String, unique: true, sparse: true }, billing_address: { type: AddressSchema, required: true }, shipping_address: { type: AddressSchema, required: true }, items: { type: [LineItemSchema], required: true }, diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index c9a1c62876..87f398302f 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -174,6 +174,24 @@ class OrderService extends BaseService { return order } + /** + * Checks the existence of an order by cart id. + * @param {string} cartId - cart id to find order + * @return {Promise} the order document + */ + async existsByCartId(cartId) { + const order = await this.orderModel_ + .findOne({ metadata: { cart_id: cartId } }) + .catch(err => { + throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) + }) + + if (!order) { + return false + } + return true + } + /** * @param {Object} selector - the query object for find * @return {Promise} the result of the find operation @@ -188,28 +206,72 @@ class OrderService extends BaseService { * @return {Promise} resolves to the creation result. */ async createFromCart(cart) { - const o = { - payment_method: cart.payment_method, - shipping_methods: cart.shipping_methods, - items: cart.items, - shipping_address: cart.shipping_address, - billing_address: cart.shipping_address, - region_id: cart.region_id, - email: cart.email, - customer_id: cart.customer_id, - cart_id: cart._id, - } + // Create DB session for transaction + const dbSession = await this.orderModel_.startSession() - return this.orderModel_ - .create(o) - .then(result => { - // Notify subscribers - this.eventBus_.emit(OrderService.Events.PLACED, result) - return result - }) - .catch(err => { - throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) + try { + // Initialize DB transaction + await dbSession.withTransaction(async () => { + // Check if order from cart already exists + // If so, this function throws + const exists = await this.existsByCartId(cart._id) + if (exists) { + throw new MedusaError( + MedusaError.types.INVALID_ARGUMENT, + "Order from cart already exists" + ) + } + + // Throw if payment method does not exist + if (!cart.payment_method) { + throw new MedusaError( + MedusaError.types.INVALID_ARGUMENT, + "Cart does not contain a payment method" + ) + } + + const { payment_method } = cart + + const paymentProvider = await this.paymentProviderService_.retrieveProvider( + payment_method.provider_id + ) + const paymentStatus = await paymentProvider.getStatus( + payment_method.data + ) + + // If payment status is not authorized, we throw + if (paymentStatus !== "authorized") { + throw new MedusaError( + MedusaError.types.INVALID_ARGUMENT, + "Payment method is not authorized" + ) + } + + const o = { + payment_method: cart.payment_method, + shipping_methods: cart.shipping_methods, + items: cart.items, + shipping_address: cart.shipping_address, + billing_address: cart.shipping_address, + region_id: cart.region_id, + email: cart.email, + customer_id: cart.customer_id, + cart_id: cart._id, + } + + const orderDocument = await this.orderModel_.create(o) + // Commit transaction + await dbSession.commitTransaction() + // Emit and return + this.eventBus_emit(OrderService.Events.PLACED, orderDocument) + return orderDocument }) + } catch (error) { + console.log(error) + await dbSession.abortTransaction() + } finally { + await dbSession.endSession() + } } /**