Klarna clean up
This commit is contained in:
commit
419d5db340
27 files changed
+331
-139
No files matched your search
@@ -16,7 +16,7 @@ export default () => {
|
||||
break
|
||||
case MedusaError.Types.DB_ERROR:
|
||||
statusCode = 500
|
||||
logger.error(err)
|
||||
console.log(err)
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, code } = req.params
|
||||
|
||||
try {
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
|
||||
let cart = await cartService.removeDiscount(id, code)
|
||||
cart = await cartService.decorate(cart, [], ["region"])
|
||||
|
||||
res.status(200).json({ cart })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,11 @@ export default app => {
|
||||
middlewares.wrap(require("./delete-line-item").default)
|
||||
)
|
||||
|
||||
route.delete(
|
||||
"/:id/discounts/:code",
|
||||
middlewares.wrap(require("./delete-discount").default)
|
||||
)
|
||||
|
||||
// Payment sessions
|
||||
route.post(
|
||||
"/:id/payment-sessions",
|
||||
|
||||
@@ -51,7 +51,7 @@ export default async (req, res) => {
|
||||
if (value.discounts && value.discounts.length) {
|
||||
await Promise.all(
|
||||
value.discounts.map(async ({ code }) =>
|
||||
cartService.applyPromoCode(id, code)
|
||||
cartService.applyDiscount(id, code)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ export default async (req, res) => {
|
||||
const { id, line_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
variant_id: Validator.objectId().required(),
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
|
||||
@@ -16,15 +15,23 @@ export default async (req, res) => {
|
||||
try {
|
||||
const lineItemService = req.scope.resolve("lineItemService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
let cart = await cartService.retrieve(id)
|
||||
|
||||
const lineItem = await lineItemService.generate(
|
||||
value.variant_id,
|
||||
value.quantity,
|
||||
cart.region_id
|
||||
)
|
||||
let cart
|
||||
if (value.quantity === 0) {
|
||||
cart = await cartService.removeLineItem(id, line_id)
|
||||
} else {
|
||||
cart = await cartService.retrieve(id)
|
||||
|
||||
const existing = cart.items.find(i => i._id.equals(line_id))
|
||||
const lineItem = await lineItemService.generate(
|
||||
existing.content.variant._id,
|
||||
cart.region_id,
|
||||
value.quantity
|
||||
)
|
||||
|
||||
cart = await cartService.updateLineItem(cart._id, line_id, lineItem)
|
||||
}
|
||||
|
||||
cart = await cartService.updateLineItem(cart._id, line_id, lineItem)
|
||||
cart = await cartService.decorate(cart, [], ["region"])
|
||||
|
||||
res.status(200).json({ cart })
|
||||
|
||||
@@ -5,6 +5,7 @@ export default async (req, res) => {
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
provider_id: Validator.string().required(),
|
||||
data: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
@@ -15,13 +16,7 @@ export default async (req, res) => {
|
||||
try {
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
|
||||
const session = await cartService.retrievePaymentSession(
|
||||
id,
|
||||
value.provider_id
|
||||
)
|
||||
await cartService.setPaymentMethod(id, session)
|
||||
|
||||
let cart = await cartService.retrieve(id)
|
||||
let cart = await cartService.setPaymentMethod(id, value)
|
||||
cart = await cartService.decorate(cart, [], ["region"])
|
||||
|
||||
res.status(200).json({ cart })
|
||||
|
||||
@@ -16,6 +16,8 @@ export default async (req, res) => {
|
||||
|
||||
const cart = await cartService.retrieve(value.cartId)
|
||||
let order = await orderService.createFromCart(cart)
|
||||
|
||||
order = await orderService.retrieveByCartId(value.cartId)
|
||||
order = await orderService.decorate(order, [
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
|
||||
@@ -4,21 +4,25 @@ export default async (req, res) => {
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.retrieve(id)
|
||||
order = await orderService.decorate(order, [
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
"payment_status",
|
||||
"email",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"items",
|
||||
"region",
|
||||
"discounts",
|
||||
"customer_id",
|
||||
"payment_method",
|
||||
"shipping_methods",
|
||||
"metadata",
|
||||
])
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
"payment_status",
|
||||
"email",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"items",
|
||||
"region",
|
||||
"discounts",
|
||||
"customer_id",
|
||||
"payment_method",
|
||||
"shipping_methods",
|
||||
"metadata",
|
||||
],
|
||||
["region"]
|
||||
)
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
}
|
||||
@@ -22,6 +22,7 @@ class OrderModel extends BaseModel {
|
||||
billing_address: { type: AddressSchema, required: true },
|
||||
shipping_address: { type: AddressSchema, required: true },
|
||||
items: { type: [LineItemSchema], required: true },
|
||||
currency_code: { type: String, required: true },
|
||||
region_id: { type: String, required: true },
|
||||
discounts: { type: [DiscountSchema], default: [] },
|
||||
customer_id: { type: String },
|
||||
|
||||
@@ -303,7 +303,7 @@ class CartService extends BaseService {
|
||||
const validatedLineItem = this.lineItemService_.validate(lineItem)
|
||||
const cart = await this.retrieve(cartId)
|
||||
const currentItem = cart.items.find(line =>
|
||||
_.isEqual(line.content, validatedLineItem.content)
|
||||
this.lineItemService_.isEqual(line, validatedLineItem)
|
||||
)
|
||||
|
||||
// If content matches one of the line items currently in the cart we can
|
||||
@@ -393,7 +393,7 @@ class CartService extends BaseService {
|
||||
}
|
||||
|
||||
// Ensure that the line item exists in the cart
|
||||
const lineItemExists = cart.items.find(i => i._id === lineItemId)
|
||||
const lineItemExists = cart.items.find(i => i._id.equals(lineItemId))
|
||||
if (!lineItemExists) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -519,6 +519,8 @@ class CartService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
address.country_code = address.country_code.toUpperCase()
|
||||
|
||||
return this.cartModel_
|
||||
.updateOne(
|
||||
{
|
||||
@@ -551,6 +553,8 @@ class CartService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
address.country_code = address.country_code.toUpperCase()
|
||||
|
||||
const region = await this.regionService_.retrieve(cart.region_id)
|
||||
if (!region.countries.includes(address.country_code.toUpperCase())) {
|
||||
throw new MedusaError(
|
||||
@@ -681,6 +685,16 @@ class CartService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
async removeDiscount(cartId, discountCode) {
|
||||
const cart = await this.retrieve(cartId)
|
||||
return this.cartModel_.updateOne(
|
||||
{ _id: cart._id },
|
||||
{
|
||||
$pull: { discounts: { code: discountCode } },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A payment method represents a way for the customer to pay. The payment
|
||||
* method will typically come from one of the payment sessions.
|
||||
@@ -736,20 +750,6 @@ class CartService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
// The provider service will be able to perform operations on the
|
||||
// session we are trying to set as the payment method.
|
||||
const provider = this.paymentProviderService_.retrieveProvider(
|
||||
paymentMethod.provider_id
|
||||
)
|
||||
|
||||
const status = await provider.getStatus(paymentMethod.data)
|
||||
if (!(status === "authorized" || status === "succeeded")) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`The payment method was not authorized`
|
||||
)
|
||||
}
|
||||
|
||||
// At this point we can register the payment method.
|
||||
return this.cartModel_
|
||||
.updateOne(
|
||||
@@ -883,7 +883,7 @@ class CartService extends BaseService {
|
||||
// that has the same profile as the selected shipping method.
|
||||
let exists = false
|
||||
const newMethods = shipping_methods.map(sm => {
|
||||
if (sm.profile_id === option.profile_id) {
|
||||
if (option.profile_id.equals(sm.profile_id)) {
|
||||
exists = true
|
||||
return option
|
||||
}
|
||||
|
||||
@@ -117,6 +117,28 @@ class LineItemService extends BaseService {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
isEqual(line, match) {
|
||||
if (Array.isArray(line.content)) {
|
||||
if (
|
||||
Array.isArray(match.content) &&
|
||||
match.content.length === line.content.length
|
||||
) {
|
||||
return line.content.every(
|
||||
(c, index) =>
|
||||
c.variant._id.equals(match[index].variant._id) &&
|
||||
c.quantity === match[index].quantity
|
||||
)
|
||||
}
|
||||
} else if (!Array.isArray(match.content)) {
|
||||
return (
|
||||
line.content.variant._id.equals(match.content.variant._id) &&
|
||||
line.content.quantity === match.content.quantity
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export default LineItemService
|
||||
@@ -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
|
||||
}
|
||||
@@ -160,7 +165,7 @@ class OrderService extends BaseService {
|
||||
*/
|
||||
async retrieveByCartId(cartId) {
|
||||
const order = await this.orderModel_
|
||||
.findOne({ metadata: { cart_id: cartId } })
|
||||
.findOne({ cart_id: cartId })
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
@@ -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
|
||||
@@ -231,21 +267,41 @@ class OrderService extends BaseService {
|
||||
|
||||
const { payment_method } = cart
|
||||
|
||||
const paymentProvider = await this.paymentProviderService_.retrieveProvider(
|
||||
payment_method.provider_id
|
||||
let paymentSession = cart.payment_sessions.find(
|
||||
ps => ps.provider_id === payment_method.provider_id
|
||||
)
|
||||
const paymentStatus = await paymentProvider.getStatus(payment_method.data)
|
||||
|
||||
// Throw if payment method does not exist
|
||||
if (!paymentSession) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"Cart does not have an authorized payment session"
|
||||
)
|
||||
}
|
||||
|
||||
const region = await this.regionService_.retrieve(cart.region_id)
|
||||
const paymentProvider = this.paymentProviderService_.retrieveProvider(
|
||||
paymentSession.provider_id
|
||||
)
|
||||
const paymentStatus = await paymentProvider.getStatus(paymentSession.data)
|
||||
|
||||
// If payment status is not authorized, we throw
|
||||
if (paymentStatus !== "authorized") {
|
||||
if (paymentStatus !== "authorized" && paymentStatus !== "succeeded") {
|
||||
throw new MedusaError(
|
||||
MedusaError.types.INVALID_ARGUMENT,
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"Payment method is not authorized"
|
||||
)
|
||||
}
|
||||
|
||||
const paymentData = await paymentProvider.retrievePayment(
|
||||
paymentSession.data
|
||||
)
|
||||
|
||||
const o = {
|
||||
payment_method: cart.payment_method,
|
||||
payment_method: {
|
||||
provider_id: paymentSession.provider_id,
|
||||
data: paymentData,
|
||||
},
|
||||
shipping_methods: cart.shipping_methods,
|
||||
items: cart.items,
|
||||
shipping_address: cart.shipping_address,
|
||||
@@ -254,13 +310,15 @@ class OrderService extends BaseService {
|
||||
email: cart.email,
|
||||
customer_id: cart.customer_id,
|
||||
cart_id: cart._id,
|
||||
currency_code: region.currency_code,
|
||||
}
|
||||
|
||||
const orderDocument = await this.orderModel_.create(o)
|
||||
// Commit transaction
|
||||
await dbSession.commitTransaction()
|
||||
const orderDocument = await this.orderModel_.create([o], {
|
||||
session: dbSession,
|
||||
})
|
||||
|
||||
// Emit and return
|
||||
this.eventBus_emit(OrderService.Events.PLACED, orderDocument)
|
||||
this.eventBus_.emit(OrderService.Events.PLACED, orderDocument)
|
||||
return orderDocument
|
||||
})
|
||||
}
|
||||
@@ -594,9 +652,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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user