Adds a payment provider service that can easily retrieve payment providers
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
export const DefaultProviderMock = {
|
||||
getStatus: jest.fn().mockImplementation(data => {
|
||||
if (data.money_id === "success") {
|
||||
return Promise.resolve("authorized")
|
||||
}
|
||||
|
||||
if (data.money_id === "fail") {
|
||||
return Promise.resolve("fail")
|
||||
}
|
||||
|
||||
return Promise.resolve("initial")
|
||||
}),
|
||||
}
|
||||
|
||||
export const PaymentProviderServiceMock = {
|
||||
retrieveProvider: jest.fn().mockImplementation(providerId => {
|
||||
if (providerId === "default_provider") {
|
||||
return DefaultProviderMock
|
||||
}
|
||||
return undefined
|
||||
}),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return PaymentProviderServiceMock
|
||||
})
|
||||
|
||||
export default mock
|
||||
@@ -6,8 +6,8 @@ export const regions = {
|
||||
name: "Test Region",
|
||||
countries: ["DK", "US", "DE"],
|
||||
tax_rate: 0.25,
|
||||
payment_providers: ["default_provider"],
|
||||
shipping_providers: ["test_shipper"],
|
||||
payment_providers: ["default_provider", "unregistered"],
|
||||
fulfillment_providers: ["test_shipper"],
|
||||
currency_code: "usd",
|
||||
},
|
||||
regionFrance: {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import mongoose from "mongoose"
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import CartService from "../cart"
|
||||
import {
|
||||
PaymentProviderServiceMock,
|
||||
DefaultProviderMock,
|
||||
} from "../__mocks__/payment-provider"
|
||||
import { ProductVariantServiceMock } from "../__mocks__/product-variant"
|
||||
import { RegionServiceMock } from "../__mocks__/region"
|
||||
import { CartModelMock, carts } from "../../models/__mocks__/cart"
|
||||
@@ -588,4 +592,148 @@ describe("CartService", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("setPaymentMethod", () => {
|
||||
const cartService = new CartService({
|
||||
cartModel: CartModelMock,
|
||||
regionService: RegionServiceMock,
|
||||
paymentProviderService: PaymentProviderServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("successfully sets a payment method", async () => {
|
||||
const paymentMethod = {
|
||||
provider_id: "default_provider",
|
||||
data: {
|
||||
money_id: "success",
|
||||
},
|
||||
}
|
||||
|
||||
await cartService.setPaymentMethod(
|
||||
IdMap.getId("cartWithLine"),
|
||||
paymentMethod
|
||||
)
|
||||
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("testRegion")
|
||||
)
|
||||
|
||||
expect(PaymentProviderServiceMock.retrieveProvider).toHaveBeenCalledTimes(
|
||||
1
|
||||
)
|
||||
expect(PaymentProviderServiceMock.retrieveProvider).toHaveBeenCalledWith(
|
||||
"default_provider"
|
||||
)
|
||||
expect(DefaultProviderMock.getStatus).toHaveBeenCalledTimes(1)
|
||||
expect(DefaultProviderMock.getStatus).toHaveBeenCalledWith({
|
||||
money_id: "success",
|
||||
})
|
||||
|
||||
expect(CartModelMock.updateOne).toHaveBeenCalledTimes(1)
|
||||
expect(CartModelMock.updateOne).toHaveBeenCalledWith(
|
||||
{
|
||||
_id: IdMap.getId("cartWithLine"),
|
||||
},
|
||||
{
|
||||
$set: { payment_method: paymentMethod },
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("fails if the region does not contain the provider_id", async () => {
|
||||
const paymentMethod = {
|
||||
provider_id: "unknown_provider",
|
||||
data: {
|
||||
money_id: "success",
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
await cartService.setPaymentMethod(
|
||||
IdMap.getId("cartWithLine"),
|
||||
paymentMethod
|
||||
)
|
||||
} catch (err) {
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("testRegion")
|
||||
)
|
||||
|
||||
expect(err.message).toEqual(
|
||||
`The payment method is not available in this region`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("fails if the payment provider is not registered", async () => {
|
||||
const paymentMethod = {
|
||||
provider_id: "unregistered",
|
||||
data: {
|
||||
money_id: "success",
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
await cartService.setPaymentMethod(
|
||||
IdMap.getId("cartWithLine"),
|
||||
paymentMethod
|
||||
)
|
||||
} catch (err) {
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("testRegion")
|
||||
)
|
||||
|
||||
expect(
|
||||
PaymentProviderServiceMock.retrieveProvider
|
||||
).toHaveBeenCalledTimes(1)
|
||||
expect(
|
||||
PaymentProviderServiceMock.retrieveProvider
|
||||
).toHaveBeenCalledWith("unregistered")
|
||||
|
||||
expect(err.message).toEqual(
|
||||
`The payment provider for the payment method was not found`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("fails if the payment is not authorized", async () => {
|
||||
const paymentMethod = {
|
||||
provider_id: "default_provider",
|
||||
data: {
|
||||
money_id: "fail",
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
await cartService.setPaymentMethod(
|
||||
IdMap.getId("cartWithLine"),
|
||||
paymentMethod
|
||||
)
|
||||
} catch (err) {
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("testRegion")
|
||||
)
|
||||
|
||||
expect(
|
||||
PaymentProviderServiceMock.retrieveProvider
|
||||
).toHaveBeenCalledTimes(1)
|
||||
expect(
|
||||
PaymentProviderServiceMock.retrieveProvider
|
||||
).toHaveBeenCalledWith("default_provider")
|
||||
|
||||
expect(DefaultProviderMock.getStatus).toHaveBeenCalledTimes(1)
|
||||
expect(DefaultProviderMock.getStatus).toHaveBeenCalledWith({
|
||||
money_id: "fail",
|
||||
})
|
||||
|
||||
expect(err.message).toEqual(`The payment method was not authorized`)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,10 +9,11 @@ import { BaseService } from "medusa-interfaces"
|
||||
class CartService extends BaseService {
|
||||
constructor({
|
||||
cartModel,
|
||||
regionService,
|
||||
eventBusService,
|
||||
paymentProviderService,
|
||||
productService,
|
||||
productVariantService,
|
||||
eventBusService,
|
||||
regionService,
|
||||
}) {
|
||||
super()
|
||||
|
||||
@@ -30,6 +31,9 @@ class CartService extends BaseService {
|
||||
|
||||
/** @private @const {RegionService} */
|
||||
this.regionService_ = regionService
|
||||
|
||||
/** @private @const {PaymentProviderService} */
|
||||
this.paymentProviderService_ = paymentProviderService
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,6 +385,79 @@ class CartService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} PaymentMethod
|
||||
* @property {string} provider_id - the identifier of the payment method's
|
||||
* provider
|
||||
* @property {object} data - the data associated with the payment method
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets a payment method for a cart.
|
||||
* @param {string} cartId - the id of the cart to add payment method to
|
||||
* @param {PaymentMethod} paymentMethod - the method to be set to the cart
|
||||
* @returns {Promise} result of update operation
|
||||
*/
|
||||
async setPaymentMethod(cartId, paymentMethod) {
|
||||
const cart = await this.retrieve(cartId)
|
||||
if (!cart) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
"The cart was not found"
|
||||
)
|
||||
}
|
||||
|
||||
const region = await this.regionService_.retrieve(cart.region_id)
|
||||
if (!region) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`The cart does not have a region associated`
|
||||
)
|
||||
}
|
||||
|
||||
// The region must have the provider id in its providers array
|
||||
if (
|
||||
!(
|
||||
region.payment_providers.length &&
|
||||
region.payment_providers.includes(paymentMethod.provider_id)
|
||||
)
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`The payment method is not available in this region`
|
||||
)
|
||||
}
|
||||
|
||||
// Check if the payment method has been authorized.
|
||||
const provider = this.paymentProviderService_.retrieveProvider(
|
||||
paymentMethod.provider_id
|
||||
)
|
||||
if (!provider) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`The payment provider for the payment method was not found`
|
||||
)
|
||||
}
|
||||
|
||||
const status = await provider.getStatus(paymentMethod.data)
|
||||
if (status !== "authorized") {
|
||||
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(
|
||||
{
|
||||
_id: cart._id,
|
||||
},
|
||||
{
|
||||
$set: { payment_method: paymentMethod },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's the region of a cart.
|
||||
* @param {string} cartId - the id of the cart to set region on
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* Helps retrive payment providers
|
||||
*/
|
||||
class PaymentProviderService {
|
||||
constructor(container) {
|
||||
/** @private {logger} */
|
||||
this.container_ = container
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming jobs.
|
||||
* @param job {{ eventName: (string), data: (any) }}
|
||||
* eventName - the name of the event to process
|
||||
* data - data to send to the subscriber
|
||||
*
|
||||
* @returns {PaymentService} the payment provider
|
||||
*/
|
||||
retrieveProvider(provider_id) {
|
||||
try {
|
||||
const provider = this.container_.resolve(`pp_${provider_id}`)
|
||||
return provider
|
||||
} catch (err) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Could not find a payment provider with id: ${provider_id}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PaymentProviderService
|
||||
Reference in New Issue
Block a user