feat(medusa,medusa-payment-stripe): Move database mutation from plugin to core (#2743)

**what**
The goal of that PR is to first refactor the payment provider and payment plugin to support the new API that removes the data mutation from within the plugin to be done by the core instead. In any case, this pr does not include the steps of the deeper refactoring. The last part will come in later pr.

**How**
- The payment plugin is now capable to handle both the deprecated and new API and the plugin works the same as it use to works.
- The mutation made by the plugin have been moved into the core as well as the subscriber
- The tests have been updated to reflect the changed
- Remove all new methods introduced by the payment collections
  - Mutualise types
  - Update provider and payment collection services
  - cleanup around all those refactoring including cleanup of the payment collection
  - refactor stripe payment plugin

FIXES CORE-887
This commit is contained in:
Adrien de Peretti authored and GitHub committed 2022-12-19 14:37:35 +00:00
1 parent 8dcc805ccf
commit c8724da503
87 files changed
+1138 -1429

No files matched your search

@@ -6,7 +6,7 @@ import { DateComparisonOperator } from "../../../../types/common"
import { IsType } from "../../../../utils/validators/is-type"
import { Request } from "express"
import { pickBy } from "lodash"
import { isDefined } from "../../../../utils"
import { isDefined } from "medusa-core-utils"
/**
* @oas [get] /batch-jobs
@@ -4,7 +4,7 @@ import { GiftCardService } from "../../../../services"
import { Type } from "class-transformer"
import { pickBy } from "lodash"
import { validator } from "../../../../utils/validator"
import { isDefined } from "../../../../utils"
import { isDefined } from "medusa-core-utils"
/**
* @oas [get] /gift-cards
@@ -14,11 +14,10 @@ import {
} from "../../../../services"
import { Type } from "class-transformer"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { Order, Return } from "../../../../models"
import { OrdersReturnItem } from "../../../../types/orders"
import { isDefined } from "../../../../utils"
import { validator } from "../../../../utils/validator"
/**
@@ -17,7 +17,7 @@ import { ProductStatus } from "../../../../models"
import { Request } from "express"
import { Type } from "class-transformer"
import { pickBy } from "lodash"
import { isDefined } from "../../../../utils"
import { isDefined } from "medusa-core-utils"
/**
* @oas [get] /price-lists/{id}/products
@@ -10,7 +10,7 @@ import { OrderService, ReturnService, SwapService } from "../../../../services"
import { EntityManager } from "typeorm"
import { Type } from "class-transformer"
import { validator } from "../../../../utils/validator"
import { isDefined } from "../../../../utils"
import { isDefined } from "medusa-core-utils"
/**
* @oas [post] /returns/{id}/receive
@@ -3,12 +3,11 @@ import { getRetrieveConfig, pickByConfig } from "./utils/get-query-config"
import { EntityManager } from "typeorm"
import { IsType } from "../../../../utils/validators/is-type"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { TaxRate } from "../../../.."
import { TaxRateService } from "../../../../services"
import { omit } from "lodash"
import { validator } from "../../../../utils/validator"
import { isDefined } from "../../../../utils"
/**
* @oas [post] /tax-rates
@@ -7,7 +7,7 @@ import { TaxRate } from "../../../.."
import { TaxRateService } from "../../../../services"
import { omit } from "lodash"
import { validator } from "../../../../utils/validator"
import { isDefined } from "../../../../utils"
import { isDefined } from "medusa-core-utils"
/**
* @oas [post] /tax-rates/{id}
@@ -2,7 +2,7 @@ import { pick } from "lodash"
import { defaultAdminTaxRatesFields, defaultAdminTaxRatesRelations } from "../"
import { TaxRate } from "../../../../.."
import { FindConfig } from "../../../../../types/common"
import { isDefined } from "../../../../../utils"
import { isDefined } from "medusa-core-utils"
export function pickByConfig<T>(
obj: T | T[],
@@ -1,5 +1,5 @@
import { EntityManager } from "typeorm"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import reqIp from "request-ip"
import { Type } from "class-transformer"
import {
@@ -22,7 +22,6 @@ import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators
import { FlagRouter } from "../../../../utils/flag-router"
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
import { CartCreateProps } from "../../../../types/cart"
import { isDefined } from "../../../../utils"
import PublishableAPIKeysFeatureFlag from "../../../../loaders/feature-flags/publishable-api-keys"
/**
@@ -13,14 +13,13 @@ import {
ProductService,
RegionService,
} from "../../../../services"
import { isDefined } from "medusa-core-utils"
import { defaultStoreProductsRelations } from "."
import SalesChannelFeatureFlag from "../../../../loaders/feature-flags/sales-channels"
import { Product } from "../../../../models"
import PricingService from "../../../../services/pricing"
import { DateComparisonOperator } from "../../../../types/common"
import { PriceSelectionParams } from "../../../../types/price-selection"
import { isDefined } from "../../../../utils"
import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators"
import { validator } from "../../../../utils/validator"
import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean"
@@ -5,7 +5,7 @@ import { CustomerService } from "../../services"
import { FindConfig } from "../../types/common"
import { validator } from "../../utils/validator"
import { Customer } from "../../models/customer"
import { isDefined } from "../../utils"
import { isDefined } from "medusa-core-utils"
const listAndCount = async (
scope,
@@ -1,18 +1,38 @@
import { TransactionBaseService } from "./transaction-base-service"
import {
Address,
Cart,
Customer,
Payment,
PaymentSession,
PaymentSessionStatus,
ShippingMethod,
} from "../models"
import { PaymentService } from "medusa-interfaces"
import { PaymentProviderDataInput } from "../types/payment-collection"
export type Data = Record<string, unknown>
export type PaymentData = Data
export type PaymentSessionData = Data
export type PaymentContext = {
cart: {
context: Record<string, unknown>
id: string
email: string
shipping_address: Address | null
shipping_methods: ShippingMethod[]
}
currency_code: string
amount: number
resource_id?: string
customer?: Customer
}
export type PaymentSessionResponse = {
update_requests: { customer_metadata: Record<string, unknown> }
session_data: Record<string, unknown>
}
export interface PaymentService extends TransactionBaseService {
getIdentifier(): string
@@ -23,6 +43,16 @@ export interface PaymentService extends TransactionBaseService {
data: Data
): Promise<PaymentSessionData>
/**
* @param context The type of this argument is meant to be temporary and once the previous method signature
* will be removed, the type will only be PaymentContext instead of Cart & PaymentContext
*/
createPayment(context: Cart & PaymentContext): Promise<PaymentSessionResponse>
/**
* @deprecated use createPayment(context: Cart & PaymentContext): Promise<PaymentSessionResponse> instead
* @param cart
*/
createPayment(cart: Cart): Promise<PaymentSessionData>
retrievePayment(paymentData: PaymentData): Promise<Data>
@@ -76,23 +106,42 @@ export abstract class AbstractPaymentService
data: Data
): Promise<PaymentSessionData>
/**
* @param context The type of this argument is meant to be temporary and once the previous method signature
* will be removed, the type will only be PaymentContext instead of Cart & PaymentContext
*/
public abstract createPayment(
context: Cart & PaymentContext
): Promise<PaymentSessionResponse>
/**
* @deprecated use createPayment(context: Cart & PaymentContext): Promise<PaymentSessionResponse> instead
* @param cart
*/
public abstract createPayment(cart: Cart): Promise<PaymentSessionData>
public abstract createPaymentNew(
paymentInput: PaymentProviderDataInput
): Promise<PaymentSessionData>
public abstract retrievePayment(paymentData: PaymentData): Promise<Data>
/**
* @param paymentSessionData
* @param context The type of this argument is meant to be temporary and once the previous method signature
* will be removed, the type will only be PaymentContext instead of Cart & PaymentContext
*/
public abstract updatePayment(
paymentSessionData: PaymentSessionData,
context: Cart & PaymentContext
): Promise<PaymentSessionResponse>
/**
* @deprecated use updatePayment(paymentSessionData: PaymentSessionData, context: Cart & PaymentContext): Promise<PaymentSessionResponse> instead
* @param paymentSessionData
* @param cart
*/
public abstract updatePayment(
paymentSessionData: PaymentSessionData,
cart: Cart
): Promise<PaymentSessionData>
public abstract updatePaymentNew(
paymentSessionData: PaymentSessionData,
paymentInput: PaymentProviderDataInput
): Promise<PaymentSessionData>
public abstract authorizePayment(
paymentSession: PaymentSession,
context: Data
@@ -4,7 +4,7 @@ import path from "path"
import { trackFeatureFlag } from "medusa-telemetry"
import { FlagSettings } from "../../types/feature-flags"
import { Logger } from "../../types/global"
import { isDefined } from "../../utils"
import { isDefined } from "medusa-core-utils"
import { FlagRouter } from "../../utils/flag-router"
const isTruthy = (val: string | boolean | undefined): boolean => {
+1 -1
View File
@@ -2,7 +2,7 @@ import { asFunction } from "awilix"
import glob from "glob"
import path from "path"
import { ConfigModule, MedusaContainer } from "../types/global"
import { isDefined } from "../utils"
import { isDefined } from "medusa-core-utils"
import formatRegistrationName from "../utils/format-registration-name"
type Options = {
+1 -1
View File
@@ -5,7 +5,7 @@ import { aliasTo, asFunction } from "awilix"
import formatRegistrationName from "../utils/format-registration-name"
import { isBatchJobStrategy } from "../interfaces"
import { MedusaContainer } from "../types/global"
import { isDefined } from "../utils"
import { isDefined } from "medusa-core-utils"
type LoaderOptions = {
container: MedusaContainer
+3 -10
View File
@@ -1,12 +1,4 @@
import {
BeforeInsert,
Column,
Entity,
Index,
JoinColumn,
ManyToOne,
Unique,
} from "typeorm"
import { BeforeInsert, Column, Entity, Index, JoinColumn, ManyToOne, Unique, } from "typeorm"
import { BaseEntity } from "../interfaces"
import { Cart } from "./cart"
@@ -24,12 +16,13 @@ export enum PaymentSessionStatus {
}
@Unique("OneSelected", ["cart_id", "is_selected"])
// TODO: This uniq constraint should be updated once the order edit flag is dropped and should add a where clause on cart_id is not null
@Unique("UniqPaymentSessionCartIdProviderId", ["cart_id", "provider_id"])
@Entity()
export class PaymentSession extends BaseEntity {
@Index()
@Column({ nullable: true })
cart_id: string
cart_id: string | null
@ManyToOne(() => Cart, (cart) => cart.payment_sessions)
@JoinColumn({ name: "cart_id" })
@@ -2,7 +2,6 @@ import { MedusaError } from "medusa-core-utils"
import { PaymentCollection } from "./../models/payment-collection"
import { EntityRepository, Repository } from "typeorm"
import { FindConfig } from "../types/common"
import { PaymentSession } from "../models"
@EntityRepository(PaymentCollection)
// eslint-disable-next-line max-len
@@ -61,12 +60,4 @@ export class PaymentCollectionRepository extends Repository<PaymentCollection> {
return paymentCollection[0]
}
async deleteMultiple(ids: string[]): Promise<void> {
await this.createQueryBuilder()
.delete()
.from(PaymentSession)
.where("id IN (:...ids)", { ids })
.execute()
}
}
+11 -10
View File
@@ -1,22 +1,23 @@
import { unionBy } from "lodash"
import {
In,
Not,
DeleteResult,
SelectQueryBuilder,
EntityRepository,
FindManyOptions,
FindOptionsUtils,
In,
Not,
Repository,
SelectQueryBuilder,
} from "typeorm"
import { TaxRate } from "../models/tax-rate"
import { ProductTaxRate } from "../models/product-tax-rate"
import { ProductTypeTaxRate } from "../models/product-type-tax-rate"
import { ShippingTaxRate } from "../models/shipping-tax-rate"
import { Product } from "../models/product"
import { ShippingMethod } from "../models/shipping-method"
import {
Product,
ProductTaxRate,
ProductTypeTaxRate,
ShippingTaxRate,
TaxRate,
} from "../models"
import { TaxRateListByConfig } from "../types/tax-rate"
import { isDefined } from "../utils"
import { isDefined } from "medusa-core-utils"
const resolveableFields = [
"product_count",
@@ -0,0 +1,18 @@
import { asClass, asValue, createContainer } from "awilix"
import { MockManager, MockRepository } from "medusa-test-utils"
import PaymentProviderService from "../payment-provider";
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider";
import { CustomerServiceMock } from "../__mocks__/customer";
import { FlagRouter } from "../../utils/flag-router";
import Logger from "../../loaders/logger";
export const defaultContainer = createContainer()
defaultContainer.register("paymentProviderService", asClass(PaymentProviderService))
defaultContainer.register("manager", asValue(MockManager))
defaultContainer.register("paymentSessionRepository", asValue(MockRepository()))
defaultContainer.register("paymentProviderRepository", asValue(PaymentProviderServiceMock))
defaultContainer.register("paymentRepository", asValue(MockRepository()))
defaultContainer.register("refundRepository", asValue(MockRepository()))
defaultContainer.register("customerService", asValue(CustomerServiceMock))
defaultContainer.register("featureFlagRouter", asValue(new FlagRouter({})))
defaultContainer.register("logger", asValue(Logger))
@@ -1,3 +1,5 @@
import { isString } from "../../utils";
export const DefaultProviderMock = {
getStatus: jest.fn().mockImplementation((data) => {
if (data.money_id === "success") {
@@ -28,12 +30,6 @@ export const PaymentProviderServiceMock = {
return this
},
updateSession: jest.fn().mockImplementation((session, cart) => {
return Promise.resolve({
...session.data,
id: `${session.data.id}_updated`,
})
}),
updateSessionNew: jest.fn().mockImplementation((session, sessionInput) => {
return Promise.resolve({
...session,
id: `${session.id}_updated`,
@@ -45,16 +41,17 @@ export const PaymentProviderServiceMock = {
registerInstalledProviders: jest.fn().mockImplementation(() => {
return Promise.resolve()
}),
createSession: jest.fn().mockImplementation((providerId, cart) => {
return Promise.resolve({
id: `${providerId}_session`,
cartId: cart._id,
})
}),
createSessionNew: jest.fn().mockImplementation((sessionInput) => {
return Promise.resolve({
id: `${sessionInput.providerId}_session`,
})
createSession: jest.fn().mockImplementation((providerIdOrSessionInput, cart) => {
if (isString(providerIdOrSessionInput)) {
return Promise.resolve({
id: `${providerIdOrSessionInput}_session`,
cartId: cart._id,
})
} else {
return Promise.resolve({
id: `${providerIdOrSessionInput.providerId}_session`,
})
}
}),
retrieveProvider: jest.fn().mockImplementation((providerId) => {
if (providerId === "default_provider") {
@@ -62,9 +59,9 @@ export const PaymentProviderServiceMock = {
}
throw new Error("Provider Not Found")
}),
refreshSessionNew: jest.fn().mockImplementation((session, inputData) => {
refreshSession: jest.fn().mockImplementation((session, inputData) => {
DefaultProviderMock.deletePayment()
PaymentProviderServiceMock.createSessionNew(inputData)
PaymentProviderServiceMock.createSession(inputData)
return Promise.resolve({
...session,
id: `${session.id}_refreshed`,
@@ -73,7 +70,7 @@ export const PaymentProviderServiceMock = {
authorizePayment: jest
.fn()
.mockReturnValue(Promise.resolve({ status: "authorized" })),
createPaymentNew: jest.fn().mockImplementation((session, inputData) => {
createPayment: jest.fn().mockImplementation((session, inputData) => {
Promise.resolve(inputData)
}),
}
+14 -4
View File
@@ -1528,12 +1528,22 @@ describe("CartService", () => {
expect(paymentProviderService.createSession).toHaveBeenCalledTimes(2)
expect(paymentProviderService.createSession).toHaveBeenCalledWith(
"provider_1",
cart1
{
cart: cart1,
customer: cart1.customer,
amount: cart1.total,
currency_code: cart1.region.currency_code,
provider_id: "provider_1",
}
)
expect(paymentProviderService.createSession).toHaveBeenCalledWith(
"provider_2",
cart1
{
cart: cart1,
customer: cart1.customer,
amount: cart1.total,
currency_code: cart1.region.currency_code,
provider_id: "provider_2",
}
)
})
@@ -1,21 +1,8 @@
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
import {
CustomerService,
EventBusService,
PaymentCollectionService,
PaymentProviderService,
PaymentService,
} from "../index"
import {
PaymentCollectionStatus,
PaymentCollectionType,
PaymentCollection,
} from "../../models"
import { CustomerService, EventBusService, PaymentCollectionService, PaymentProviderService, } from "../index"
import { PaymentCollection, PaymentCollectionStatus, PaymentCollectionType, } from "../../models"
import { EventBusServiceMock } from "../__mocks__/event-bus"
import {
DefaultProviderMock,
PaymentProviderServiceMock,
} from "../__mocks__/payment-provider"
import { DefaultProviderMock, PaymentProviderServiceMock, } from "../__mocks__/payment-provider"
import { CustomerServiceMock } from "../__mocks__/customer"
import { PaymentCollectionsSessionsBatchInput } from "../../types/payment-collection"
@@ -395,7 +382,7 @@ describe("PaymentCollectionService", () => {
`Cannot set payment sessions for a payment collection with status ${PaymentCollectionStatus.AUTHORIZED}`
)
)
expect(PaymentProviderServiceMock.createSessionNew).toBeCalledTimes(0)
expect(PaymentProviderServiceMock.createSession).toBeCalledTimes(0)
})
it("should ignore session if provider doesn't belong to the region", async () => {
@@ -408,7 +395,7 @@ describe("PaymentCollectionService", () => {
)
expect(multiRet).rejects.toThrow(`Payment provider not found`)
expect(PaymentProviderServiceMock.createSessionNew).toBeCalledTimes(0)
expect(PaymentProviderServiceMock.createSession).toBeCalledTimes(0)
})
it("should add a new session", async () => {
@@ -420,7 +407,7 @@ describe("PaymentCollectionService", () => {
"lebron"
)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
1
)
expect(CustomerServiceMock.retrieve).toHaveBeenCalledTimes(1)
@@ -436,10 +423,10 @@ describe("PaymentCollectionService", () => {
"lebron"
)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
0
)
expect(PaymentProviderServiceMock.updateSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.updateSession).toHaveBeenCalledTimes(
1
)
expect(CustomerServiceMock.retrieve).toHaveBeenCalledTimes(1)
@@ -459,13 +446,13 @@ describe("PaymentCollectionService", () => {
IdMap.getId("lebron")
)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
1
)
expect(PaymentProviderServiceMock.updateSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.updateSession).toHaveBeenCalledTimes(
0
)
expect(paymentCollectionRepository.deleteMultiple).toHaveBeenCalledTimes(
expect(paymentCollectionRepository.delete).toHaveBeenCalledTimes(
1
)
@@ -497,7 +484,7 @@ describe("PaymentCollectionService", () => {
)
)
expect(PaymentProviderServiceMock.createSessionNew).toBeCalledTimes(0)
expect(PaymentProviderServiceMock.createSession).toBeCalledTimes(0)
})
it("should throw error if amount is different than requested", async () => {
@@ -514,7 +501,7 @@ describe("PaymentCollectionService", () => {
"customer1"
)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
0
)
expect(ret).rejects.toThrow(
@@ -537,7 +524,7 @@ describe("PaymentCollectionService", () => {
"customer1"
)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
0
)
expect(multiRet).rejects.toThrow(
@@ -565,7 +552,7 @@ describe("PaymentCollectionService", () => {
expect(multiRet).rejects.toThrow(
`The sum of sessions is not equal to 100 on Payment Collection`
)
expect(PaymentProviderServiceMock.createSessionNew).toBeCalledTimes(0)
expect(PaymentProviderServiceMock.createSession).toBeCalledTimes(0)
})
it("should add a new session and update existing one", async () => {
@@ -586,10 +573,10 @@ describe("PaymentCollectionService", () => {
"lebron"
)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
1
)
expect(PaymentProviderServiceMock.updateSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.updateSession).toHaveBeenCalledTimes(
1
)
expect(CustomerServiceMock.retrieve).toHaveBeenCalledTimes(1)
@@ -609,13 +596,13 @@ describe("PaymentCollectionService", () => {
IdMap.getId("lebron")
)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
1
)
expect(PaymentProviderServiceMock.updateSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.updateSession).toHaveBeenCalledTimes(
0
)
expect(paymentCollectionRepository.deleteMultiple).toHaveBeenCalledTimes(
expect(paymentCollectionRepository.delete).toHaveBeenCalledTimes(
1
)
@@ -630,10 +617,10 @@ describe("PaymentCollectionService", () => {
)
expect(
PaymentProviderServiceMock.refreshSessionNew
PaymentProviderServiceMock.refreshSession
).toHaveBeenCalledTimes(1)
expect(DefaultProviderMock.deletePayment).toHaveBeenCalledTimes(1)
expect(PaymentProviderServiceMock.createSessionNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createSession).toHaveBeenCalledTimes(
1
)
})
@@ -650,9 +637,9 @@ describe("PaymentCollectionService", () => {
"payCol_session-not-found"
)} was not found`
)
expect(PaymentProviderServiceMock.refreshSessionNew).toBeCalledTimes(0)
expect(PaymentProviderServiceMock.refreshSession).toBeCalledTimes(0)
expect(DefaultProviderMock.deletePayment).toBeCalledTimes(0)
expect(PaymentProviderServiceMock.createSessionNew).toBeCalledTimes(0)
expect(PaymentProviderServiceMock.createSession).toBeCalledTimes(0)
})
})
@@ -695,7 +682,7 @@ describe("PaymentCollectionService", () => {
expect(PaymentProviderServiceMock.authorizePayment).toHaveBeenCalledTimes(
2
)
expect(PaymentProviderServiceMock.createPaymentNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createPayment).toHaveBeenCalledTimes(
2
)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
@@ -710,7 +697,7 @@ describe("PaymentCollectionService", () => {
expect(PaymentProviderServiceMock.authorizePayment).toHaveBeenCalledTimes(
1
)
expect(PaymentProviderServiceMock.createPaymentNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createPayment).toHaveBeenCalledTimes(
1
)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(1)
@@ -725,7 +712,7 @@ describe("PaymentCollectionService", () => {
expect(PaymentProviderServiceMock.authorizePayment).toHaveBeenCalledTimes(
0
)
expect(PaymentProviderServiceMock.createPaymentNew).toHaveBeenCalledTimes(
expect(PaymentProviderServiceMock.createPayment).toHaveBeenCalledTimes(
0
)
expect(EventBusServiceMock.emit).toHaveBeenCalledTimes(0)
@@ -1,17 +1,15 @@
import { MockManager, MockRepository } from "medusa-test-utils"
import { asValue, createContainer } from "awilix"
import { MockRepository } from "medusa-test-utils"
import PaymentProviderService from "../payment-provider"
import { defaultContainer } from "../__fixtures__/payment-provider"
import { testPayServiceMock } from "../__mocks__/test-pay"
import { FlagRouter } from "../../utils/flag-router"
describe("PaymentProviderService", () => {
describe("retrieveProvider", () => {
const container = {
manager: MockManager,
paymentSessionRepository: MockRepository(),
pp_default_provider: "good",
}
const container = createContainer({}, defaultContainer)
container.register("pp_default_provider", asValue("good"))
const providerService = new PaymentProviderService(container)
const providerService = container.resolve("paymentProviderService")
it("successfully retrieves payment provider", () => {
const provider = providerService.retrieveProvider("default_provider")
@@ -30,56 +28,78 @@ describe("PaymentProviderService", () => {
})
describe("createSession", () => {
const createPayment = jest.fn().mockReturnValue(Promise.resolve())
const container = {
manager: MockManager,
paymentSessionRepository: MockRepository(),
pp_default_provider: {
const container = createContainer({}, defaultContainer)
container.register(
"pp_default_provider",
asValue({
withTransaction: function () {
return this
},
createPayment,
},
}
createPayment: jest.fn().mockReturnValue(Promise.resolve({})),
})
)
const providerService = new PaymentProviderService(container)
const providerService = container.resolve("paymentProviderService")
it("successfully creates session", async () => {
await providerService.createSession("default_provider", {
object: "cart",
region: {
currency_code: "usd",
},
total: 100,
})
expect(createPayment).toBeCalledTimes(1)
expect(createPayment).toBeCalledWith({
const defaultProvider = container.resolve("pp_default_provider")
expect(defaultProvider.createPayment).toBeCalledTimes(1)
expect(defaultProvider.createPayment).toBeCalledWith({
amount: 100,
object: "cart",
total: 100,
region: {
currency_code: "usd",
},
cart: {
context: undefined,
email: undefined,
id: undefined,
shipping_address: undefined,
shipping_methods: undefined,
},
currency_code: "usd",
})
})
})
describe("updateSession", () => {
const updatePayment = jest.fn().mockReturnValue(Promise.resolve())
const container = {
manager: MockManager,
paymentSessionRepository: MockRepository({
findOne: () =>
Promise.resolve({
id: "session",
provider_id: "default_provider",
data: {
id: "1234",
},
}),
}),
pp_default_provider: {
const container = createContainer({}, defaultContainer)
container.register(
"paymentSessionRepository",
asValue(
MockRepository({
findOne: () =>
Promise.resolve({
id: "session",
provider_id: "default_provider",
data: {
id: "1234",
},
}),
})
)
)
container.register(
"pp_default_provider",
asValue({
withTransaction: function () {
return this
},
updatePayment,
},
}
updatePayment: jest.fn().mockReturnValue(Promise.resolve()),
})
)
const providerService = new PaymentProviderService(container)
const providerService = container.resolve("paymentProviderService")
it("successfully creates session", async () => {
await providerService.updateSession(
@@ -91,15 +111,28 @@ describe("PaymentProviderService", () => {
},
},
{
object: "cart",
total: 100,
}
)
expect(updatePayment).toBeCalledTimes(1)
expect(updatePayment).toBeCalledWith(
const defaultProvider = container.resolve("pp_default_provider")
expect(defaultProvider.updatePayment).toBeCalledTimes(1)
expect(defaultProvider.updatePayment).toBeCalledWith(
{ id: "1234" },
{
object: "cart",
amount: 100,
total: 100,
cart: {
context: undefined,
email: undefined,
id: undefined,
shipping_address: undefined,
shipping_methods: undefined,
},
currency_code: undefined,
}
)
})
@@ -107,50 +140,53 @@ describe("PaymentProviderService", () => {
})
describe(`PaymentProviderService`, () => {
const featureFlagRouter = new FlagRouter({
order_editing: false,
})
const container = {
manager: MockManager,
paymentSessionRepository: MockRepository({
findOne: () =>
Promise.resolve({
id: "session",
provider_id: "default_provider",
data: {
id: "1234",
},
}),
}),
paymentRepository: MockRepository({
findOne: () =>
Promise.resolve({
id: "pay_jadazdjk",
provider_id: "default_provider",
data: {
id: "1234",
},
}),
find: () =>
Promise.resolve([
{
const container = createContainer({}, defaultContainer)
container.register("pp_default_provider", asValue(testPayServiceMock))
container.register(
"paymentSessionRepository",
asValue(
MockRepository({
findOne: () =>
Promise.resolve({
id: "session",
provider_id: "default_provider",
data: {
id: "1234",
},
}),
})
)
)
container.register(
"paymentRepository",
asValue(
MockRepository({
findOne: () =>
Promise.resolve({
id: "pay_jadazdjk",
provider_id: "default_provider",
data: {
id: "1234",
},
captured_at: new Date(),
amount: 100,
amount_refunded: 0,
},
]),
}),
refundRepository: MockRepository(),
pp_default_provider: testPayServiceMock,
featureFlagRouter,
}
const providerService = new PaymentProviderService(container)
}),
find: () =>
Promise.resolve([
{
id: "pay_jadazdjk",
provider_id: "default_provider",
data: {
id: "1234",
},
captured_at: new Date(),
amount: 100,
amount_refunded: 0,
},
]),
})
)
)
const providerService = container.resolve("paymentProviderService")
afterEach(() => {
jest.clearAllMocks()
@@ -163,12 +199,29 @@ describe(`PaymentProviderService`, () => {
it("successfully creates session", async () => {
await providerService.createSession("default_provider", {
object: "cart",
region: {
currency_code: "usd",
},
total: 100,
})
expect(testPayServiceMock.createPayment).toBeCalledTimes(1)
expect(testPayServiceMock.createPayment).toBeCalledWith({
amount: 100,
object: "cart",
total: 100,
region: {
currency_code: "usd",
},
cart: {
context: undefined,
email: undefined,
id: undefined,
shipping_address: undefined,
shipping_methods: undefined,
},
currency_code: "usd",
})
})
@@ -182,6 +235,7 @@ describe(`PaymentProviderService`, () => {
},
},
{
object: "cart",
total: 100,
}
)
@@ -190,7 +244,16 @@ describe(`PaymentProviderService`, () => {
expect(testPayServiceMock.updatePayment).toBeCalledWith(
{ id: "1234" },
{
amount: 100,
object: "cart",
total: 100,
cart: {
context: undefined,
email: undefined,
id: undefined,
shipping_address: undefined,
shipping_methods: undefined,
},
}
)
})
@@ -205,7 +268,9 @@ describe(`PaymentProviderService`, () => {
},
},
{
total: 100,
provider_id: "default_provider",
amount: 100,
currency_code: "usd",
}
)
+2 -2
View File
@@ -11,8 +11,8 @@ import {
} from "../types/batch-job"
import { FindConfig } from "../types/common"
import { TransactionBaseService } from "../interfaces"
import { buildQuery, isDefined } from "../utils"
import { MedusaError } from "medusa-core-utils"
import { buildQuery } from "../utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EventBusService, StrategyResolverService } from "./index"
import { Request } from "express"
+36 -7
View File
@@ -1,5 +1,5 @@
import { isEmpty, isEqual } from "lodash"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { DeepPartial, EntityManager, In } from "typeorm"
import { IPriceSelectionStrategy, TransactionBaseService } from "../interfaces"
import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels"
@@ -34,7 +34,7 @@ import {
TotalField,
WithRequiredProperty,
} from "../types/common"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import { FlagRouter } from "../utils/flag-router"
import { validateEmail } from "../utils/is-email"
import CustomShippingOptionService from "./custom-shipping-option"
@@ -54,6 +54,7 @@ import ShippingOptionService from "./shipping-option"
import StoreService from "./store"
import TaxProviderService from "./tax-provider"
import TotalsService from "./totals"
import { PaymentSessionInput } from "../types/payment"
type InjectedDependencies = {
manager: EntityManager
@@ -1678,6 +1679,12 @@ class CartService extends TransactionBaseService {
)
const { total, region } = cart
const partialSessionInput: Omit<PaymentSessionInput, "provider_id"> = {
cart: cart as Cart,
customer: cart.customer,
amount: cart.total,
currency_code: cart.region.currency_code,
}
// If there are existing payment sessions ensure that these are up to date
const seen: string[] = []
@@ -1695,9 +1702,15 @@ class CartService extends TransactionBaseService {
.deleteSession(paymentSession)
} else {
seen.push(paymentSession.provider_id)
const paymentSessionInput = {
...partialSessionInput,
provider_id: paymentSession.provider_id,
}
return this.paymentProviderService_
.withTransaction(transactionManager)
.updateSession(paymentSession, cart)
.updateSession(paymentSession, paymentSessionInput)
}
})
)
@@ -1707,9 +1720,14 @@ class CartService extends TransactionBaseService {
// If only one payment session exists, we preselect it
if (region.payment_providers.length === 1 && !cart.payment_session) {
const paymentProvider = region.payment_providers[0]
const paymentSessionInput = {
...partialSessionInput,
provider_id: paymentProvider.id,
}
const paymentSession = await this.paymentProviderService_
.withTransaction(transactionManager)
.createSession(paymentProvider.id, cart)
.createSession(paymentSessionInput)
paymentSession.is_selected = true
@@ -1718,9 +1736,14 @@ class CartService extends TransactionBaseService {
await Promise.all(
region.payment_providers.map(async (paymentProvider) => {
if (!seen.includes(paymentProvider.id)) {
const paymentSessionInput = {
...partialSessionInput,
provider_id: paymentProvider.id,
}
return this.paymentProviderService_
.withTransaction(transactionManager)
.createSession(paymentProvider.id, cart)
.createSession(paymentSessionInput)
}
return
})
@@ -1792,7 +1815,7 @@ class CartService extends TransactionBaseService {
): Promise<Cart> {
return await this.atomicPhase_(
async (transactionManager: EntityManager) => {
const cart = await this.retrieve(cartId, {
const cart = await this.retrieveWithTotals(cartId, {
relations: ["payment_sessions"],
})
@@ -1805,7 +1828,13 @@ class CartService extends TransactionBaseService {
// Delete the session with the provider
await this.paymentProviderService_
.withTransaction(transactionManager)
.refreshSession(paymentSession, cart)
.refreshSession(paymentSession, {
cart: cart as Cart,
customer: cart.customer,
amount: cart.total,
currency_code: cart.region.currency_code,
provider_id: providerId,
})
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import { ClaimImage, ClaimItem, ClaimTag } from "../models"
@@ -7,7 +7,7 @@ import { ClaimItemRepository } from "../repositories/claim-item"
import { ClaimTagRepository } from "../repositories/claim-tag"
import { CreateClaimItemInput } from "../types/claim"
import { FindConfig, Selector } from "../types/common"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import EventBusService from "./event-bus"
import LineItemService from "./line-item"
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { DeepPartial, EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import {
@@ -16,7 +16,7 @@ import { LineItemRepository } from "../repositories/line-item"
import { ShippingMethodRepository } from "../repositories/shipping-method"
import { CreateClaimInput, UpdateClaimInput } from "../types/claim"
import { FindConfig } from "../types/common"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import ClaimItemService from "./claim-item"
import EventBusService from "./event-bus"
import FulfillmentService from "./fulfillment"
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { DeepPartial, EntityManager, ILike } from "typeorm"
import { CustomerService } from "."
import { CustomerGroup } from ".."
@@ -8,13 +8,7 @@ import {
} from "../repositories/customer-group"
import { FindConfig, Selector } from "../types/common"
import { CustomerGroupUpdate } from "../types/customer-groups"
import {
buildQuery,
isDefined,
isString,
PostgresError,
setMetadata,
} from "../utils"
import { buildQuery, isString, PostgresError, setMetadata } from "../utils"
import { TransactionBaseService } from "../interfaces"
type CustomerGroupConstructorProps = {
@@ -35,6 +29,7 @@ class CustomerGroupService extends TransactionBaseService {
customerGroupRepository,
customerService,
}: CustomerGroupConstructorProps) {
// eslint-disable-next-line prefer-rest-params
super(arguments[0])
this.manager_ = manager
+2 -2
View File
@@ -1,5 +1,5 @@
import jwt from "jsonwebtoken"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import Scrypt from "scrypt-kdf"
import { DeepPartial, EntityManager } from "typeorm"
import { EventBusService } from "."
@@ -10,7 +10,7 @@ import { AddressRepository } from "../repositories/address"
import { CustomerRepository } from "../repositories/customer"
import { AddressCreatePayload, FindConfig, Selector } from "../types/common"
import { CreateCustomerInput, UpdateCustomerInput } from "../types/customers"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
type InjectedDependencies = {
manager: EntityManager
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { EventBusService } from "."
import {
@@ -14,7 +14,7 @@ import { DiscountConditionRepository } from "../repositories/discount-condition"
import { FindConfig } from "../types/common"
import { DiscountConditionInput } from "../types/discount"
import { TransactionBaseService } from "../interfaces"
import { buildQuery, isDefined, PostgresError } from "../utils"
import { buildQuery, PostgresError } from "../utils"
type InjectedDependencies = {
manager: EntityManager
+2 -2
View File
@@ -1,6 +1,6 @@
import { parse, toSeconds } from "iso8601-duration"
import { isEmpty, omit } from "lodash"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import {
Brackets,
DeepPartial,
@@ -36,7 +36,7 @@ import {
UpdateDiscountInput,
UpdateDiscountRuleInput,
} from "../types/discount"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import { isFuture, isPast } from "../utils/date-helpers"
import { FlagRouter } from "../utils/flag-router"
import CustomerService from "./customer"
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { Brackets, EntityManager, FindManyOptions, UpdateResult } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import { Cart, CartType, DraftOrder, DraftOrderStatus } from "../models"
@@ -7,7 +7,7 @@ import { OrderRepository } from "../repositories/order"
import { PaymentRepository } from "../repositories/payment"
import { ExtendedFindConfig, FindConfig } from "../types/common"
import { DraftOrderCreateProps } from "../types/draft-orders"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
import CartService from "./cart"
import CustomShippingOptionService from "./custom-shipping-option"
import EventBusService from "./event-bus"
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { ShippingProfileService } from "."
import { TransactionBaseService } from "../interfaces"
@@ -13,7 +13,7 @@ import {
FulfillmentItemPartition,
FulFillmentItemType,
} from "../types/fulfillment"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
import FulfillmentProviderService from "./fulfillment-provider"
import LineItemService from "./line-item"
import TotalsService from "./totals"
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import randomize from "randomatic"
import { EntityManager } from "typeorm"
import { EventBusService } from "."
@@ -17,7 +17,7 @@ import {
CreateGiftCardTransactionInput,
UpdateGiftCardInput,
} from "../types/gift-card"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import RegionService from "./region"
type InjectedDependencies = {
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { v4 } from "uuid"
import { TransactionBaseService } from "../interfaces"
import { DeepPartial, EntityManager } from "typeorm"
@@ -8,7 +8,6 @@ import {
CreateIdempotencyKeyInput,
IdempotencyCallbackResult,
} from "../types/idempotency-key"
import { isDefined } from "../utils"
const KEY_LOCKED_TIMEOUT = 1000
+1 -2
View File
@@ -1,9 +1,8 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { TransactionBaseService } from "../interfaces"
import { EntityManager } from "typeorm"
import ProductVariantService from "./product-variant"
import { ProductVariant } from "../models"
import { isDefined } from "../utils"
type InventoryServiceProps = {
manager: EntityManager
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager, In } from "typeorm"
import { Cart, DiscountRuleType, LineItem, LineItemAdjustment } from "../models"
@@ -7,7 +7,7 @@ import { FindConfig } from "../types/common"
import { FilterableLineItemAdjustmentProps } from "../types/line-item-adjustment"
import DiscountService from "./discount"
import { TransactionBaseService } from "../interfaces"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import { CalculationContextData } from "../types/totals"
type LineItemAdjustmentServiceProps = {
+2 -2
View File
@@ -18,8 +18,8 @@ import { TaxProviderService } from "./index"
import { LineAllocationsMap } from "../types/totals"
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
import { FlagRouter } from "../utils/flag-router"
import { calculatePriceTaxAmount, isDefined } from "../utils"
import { MedusaError } from "medusa-core-utils"
import { calculatePriceTaxAmount } from "../utils"
import { isDefined, MedusaError } from "medusa-core-utils"
type LineItemTotals = {
unit_price: number
+2 -2
View File
@@ -1,11 +1,11 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import { NoteRepository } from "../repositories/note"
import EventBusService from "./event-bus"
import { FindConfig, Selector } from "../types/common"
import { Note } from "../models"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
import { CreateNoteInput } from "../types/note"
type InjectedDependencies = {
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import { Oauth as OAuthModel } from "../models"
@@ -6,7 +6,7 @@ import { OauthRepository } from "../repositories/oauth"
import { Selector } from "../types/common"
import { MedusaContainer } from "../types/global"
import { CreateOauthInput, UpdateOauthInput } from "../types/oauth"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
import EventBusService from "./event-bus"
type InjectedDependencies = MedusaContainer & {
+2 -2
View File
@@ -1,8 +1,8 @@
import { DeepPartial, EntityManager, ILike, IsNull } from "typeorm"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { FindConfig, Selector } from "../types/common"
import { buildQuery, isDefined, isString } from "../utils"
import { buildQuery, isString } from "../utils"
import { OrderEditRepository } from "../repositories/order-edit"
import {
Cart,
+2 -5
View File
@@ -1,5 +1,4 @@
import jwt, { JwtPayload } from "jsonwebtoken"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { Brackets, EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels"
@@ -28,7 +27,7 @@ import {
} from "../types/fulfillment"
import { UpdateOrderInput } from "../types/orders"
import { CreateShippingMethodDto } from "../types/shipping-options"
import { buildQuery, isDefined, isString, setMetadata } from "../utils"
import { buildQuery, isString, setMetadata } from "../utils"
import { FlagRouter } from "../utils/flag-router"
import CartService from "./cart"
import CustomerService from "./customer"
@@ -46,8 +45,6 @@ import ShippingOptionService from "./shipping-option"
import ShippingProfileService from "./shipping-profile"
import TotalsService from "./totals"
import { NewTotalsService, TaxProviderService } from "./index"
import { ConfigModule } from "../types/global"
import logger from "../loaders/logger"
export const ORDER_CART_ALREADY_EXISTS_ERROR = "Order from cart already exists"
@@ -1,8 +1,8 @@
import { DeepPartial, EntityManager } from "typeorm"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { FindConfig } from "../types/common"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import { PaymentCollectionRepository } from "../repositories/payment-collection"
import {
PaymentCollection,
@@ -21,8 +21,8 @@ import {
CreatePaymentCollectionInput,
PaymentCollectionsSessionsBatchInput,
PaymentCollectionsSessionsInput,
PaymentProviderDataInput,
} from "../types/payment-collection"
import { CreatePaymentInput, PaymentSessionInput } from "../types/payment"
type InjectedDependencies = {
manager: EntityManager
@@ -194,10 +194,10 @@ export default class PaymentCollectionService extends TransactionBaseService {
}
if (
[
![
PaymentCollectionStatus.CANCELED,
PaymentCollectionStatus.NOT_PAID,
].includes(paymentCollection.status) === false
].includes(paymentCollection.status)
) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
@@ -251,10 +251,14 @@ export default class PaymentCollectionService extends TransactionBaseService {
)
}
sessionsInput = sessionsInput.filter((session) => {
return !!payCol.region.payment_providers.find(({ id }) => {
return id === session.provider_id
const payColRegionProviderMap = new Map(
payCol.region.payment_providers.map((provider) => {
return [provider.id, provider]
})
)
sessionsInput = sessionsInput.filter((session) => {
return !!payColRegionProviderMap.get(session.provider_id)
})
if (!this.isValidTotalAmount(payCol.amount, sessionsInput)) {
@@ -273,15 +277,30 @@ export default class PaymentCollectionService extends TransactionBaseService {
})
.catch(() => null)
const payColSessionMap = new Map(
(payCol.payment_sessions ?? []).map((session) => {
return [session.id, session]
})
)
const paymentProviderTx =
this.paymentProviderService_.withTransaction(manager)
const selectedSessionIds: string[] = []
const paymentSessions: PaymentSession[] = []
for (const session of sessionsInput) {
const existingSession = payCol.payment_sessions?.find(
(sess) => session.session_id === sess?.id
)
const existingSession =
session.session_id && payColSessionMap.get(session.session_id)
const inputData: PaymentProviderDataInput = {
const inputData: PaymentSessionInput = {
cart: {
email: customer?.email || "",
context: {},
shipping_methods: [],
shipping_address: null,
id: "",
},
resource_id: payCol.id,
currency_code: payCol.currency_code,
amount: session.amount,
@@ -289,21 +308,19 @@ export default class PaymentCollectionService extends TransactionBaseService {
customer,
}
let paymentSession
if (existingSession) {
const paymentSession = await this.paymentProviderService_
.withTransaction(manager)
.updateSessionNew(existingSession, inputData)
selectedSessionIds.push(existingSession.id)
paymentSessions.push(paymentSession)
paymentSession = await paymentProviderTx.updateSession(
existingSession,
inputData
)
} else {
const paymentSession = await this.paymentProviderService_
.withTransaction(manager)
.createSessionNew(inputData)
selectedSessionIds.push(paymentSession.id)
paymentSessions.push(paymentSession)
paymentSession = await paymentProviderTx.createSession(inputData)
}
selectedSessionIds.push(paymentSession.id)
paymentSessions.push(paymentSession)
}
if (payCol.payment_sessions?.length) {
@@ -312,15 +329,16 @@ export default class PaymentCollectionService extends TransactionBaseService {
)
if (removeSessions.length) {
await paymentCollectionRepository.deleteMultiple(
await paymentCollectionRepository.delete(
removeSessions.map((sess) => sess.id)
)
const paymentProviderTx =
this.paymentProviderService_.withTransaction(manager)
Promise.all(
removeSessions.map(async (sess) =>
this.paymentProviderService_
.withTransaction(manager)
.deleteSessionNew(sess)
paymentProviderTx.deleteSession(sess)
)
).catch(() => void 0)
}
@@ -335,7 +353,7 @@ export default class PaymentCollectionService extends TransactionBaseService {
/**
* Manages a single payment sessions of a payment collection.
* @param paymentCollectionId - the id of the payment collection
* @param sessionsInput - object containing payment session info
* @param sessionInput - object containing payment session info
* @param customerId - the id of the customer
* @return the payment collection and its payment session.
*/
@@ -381,7 +399,15 @@ export default class PaymentCollectionService extends TransactionBaseService {
.catch(() => null)
const paymentSessions: PaymentSession[] = []
const inputData: PaymentProviderDataInput = {
const inputData: PaymentSessionInput = {
cart: {
email: customer?.email || "",
context: {},
shipping_methods: [],
shipping_address: null,
id: "",
},
resource_id: payCol.id,
currency_code: payCol.currency_code,
amount: payCol.amount,
@@ -396,13 +422,13 @@ export default class PaymentCollectionService extends TransactionBaseService {
if (existingSession) {
const paymentSession = await this.paymentProviderService_
.withTransaction(manager)
.updateSessionNew(existingSession, inputData)
.updateSession(existingSession, inputData)
paymentSessions.push(paymentSession)
} else {
const paymentSession = await this.paymentProviderService_
.withTransaction(manager)
.createSessionNew(inputData)
.createSession(inputData)
paymentSessions.push(paymentSession)
@@ -411,15 +437,16 @@ export default class PaymentCollectionService extends TransactionBaseService {
)
if (removeSessions.length) {
await paymentCollectionRepository.deleteMultiple(
await paymentCollectionRepository.delete(
removeSessions.map((sess) => sess.id)
)
const paymentProviderTx =
this.paymentProviderService_.withTransaction(manager)
Promise.all(
removeSessions.map(async (sess) =>
this.paymentProviderService_
.withTransaction(manager)
.deleteSessionNew(sess)
paymentProviderTx.deleteSession(sess)
)
).catch(() => void 0)
}
@@ -487,7 +514,14 @@ export default class PaymentCollectionService extends TransactionBaseService {
})
.catch(() => null)
const inputData: PaymentProviderDataInput = {
const inputData: PaymentSessionInput = {
cart: {
email: customer?.email || "",
context: {},
shipping_methods: [],
shipping_address: null,
id: "",
},
resource_id: payCol.id,
currency_code: payCol.currency_code,
amount: session.amount,
@@ -497,7 +531,7 @@ export default class PaymentCollectionService extends TransactionBaseService {
const sessionRefreshed = await this.paymentProviderService_
.withTransaction(manager)
.refreshSessionNew(session, inputData)
.refreshSession(session, inputData)
payCol.payment_sessions = payCol.payment_sessions.map((sess) => {
if (sess.id === sessionId) {
@@ -581,6 +615,9 @@ export default class PaymentCollectionService extends TransactionBaseService {
)
}
const paymentProviderTx =
this.paymentProviderService_.withTransaction(manager)
let authorizedAmount = 0
for (let i = 0; i < payCol.payment_sessions.length; i++) {
const session = payCol.payment_sessions[i]
@@ -594,32 +631,27 @@ export default class PaymentCollectionService extends TransactionBaseService {
continue
}
const auth = await this.paymentProviderService_
.withTransaction(manager)
.authorizePayment(session, context)
const paymentSession = await paymentProviderTx.authorizePayment(
session,
context
)
if (auth) {
payCol.payment_sessions[i] = auth
if (paymentSession) {
payCol.payment_sessions[i] = paymentSession
}
if (auth?.status === PaymentSessionStatus.AUTHORIZED) {
if (paymentSession?.status === PaymentSessionStatus.AUTHORIZED) {
authorizedAmount += session.amount
const inputData: Omit<PaymentProviderDataInput, "customer"> & {
payment_session: PaymentSession
} = {
const inputData: CreatePaymentInput = {
amount: session.amount,
currency_code: payCol.currency_code,
provider_id: session.provider_id,
resource_id: payCol.id,
payment_session: auth,
payment_session: paymentSession,
}
payCol.payments.push(
await this.paymentProviderService_
.withTransaction(manager)
.createPaymentNew(inputData)
)
payCol.payments.push(await paymentProviderTx.createPayment(inputData))
}
}
+216 -159
View File
@@ -1,12 +1,17 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { BasePaymentService } from "medusa-interfaces"
import { AbstractPaymentService, TransactionBaseService } from "../interfaces"
import {
AbstractPaymentService,
PaymentContext,
PaymentSessionResponse,
TransactionBaseService,
} from "../interfaces"
import { EntityManager } from "typeorm"
import { PaymentSessionRepository } from "../repositories/payment-session"
import { PaymentRepository } from "../repositories/payment"
import { RefundRepository } from "../repositories/refund"
import { PaymentProviderRepository } from "../repositories/payment-provider"
import { buildQuery } from "../utils"
import { buildQuery, isString } from "../utils"
import { FindConfig, Selector } from "../types/common"
import {
Cart,
@@ -16,11 +21,12 @@ import {
PaymentSessionStatus,
Refund,
} from "../models"
import { PaymentProviderDataInput } from "../types/payment-collection"
import { FlagRouter } from "../utils/flag-router"
import OrderEditingFeatureFlag from "../loaders/feature-flags/order-editing"
import PaymentService from "./payment"
import { Logger } from "../types/global"
import { CreatePaymentInput, PaymentSessionInput } from "../types/payment"
import { CustomerService } from "./index"
type PaymentProviderKey = `pp_${string}` | "systemPaymentProviderService"
type InjectedDependencies = {
@@ -30,6 +36,7 @@ type InjectedDependencies = {
paymentRepository: typeof PaymentRepository
refundRepository: typeof RefundRepository
paymentService: PaymentService
customerService: CustomerService
featureFlagRouter: FlagRouter
logger: Logger
} & {
@@ -50,6 +57,7 @@ export default class PaymentProviderService extends TransactionBaseService {
protected readonly paymentProviderRepository_: typeof PaymentProviderRepository
protected readonly paymentRepository_: typeof PaymentRepository
protected readonly refundRepository_: typeof RefundRepository
protected readonly customerService_: CustomerService
protected readonly logger_: Logger
protected readonly featureFlagRouter_: FlagRouter
@@ -63,6 +71,7 @@ export default class PaymentProviderService extends TransactionBaseService {
this.paymentProviderRepository_ = container.paymentProviderRepository
this.paymentRepository_ = container.paymentRepository
this.refundRepository_ = container.refundRepository
this.customerService_ = container.customerService
this.featureFlagRouter_ = container.featureFlagRouter
this.logger_ = container.logger
}
@@ -165,55 +174,59 @@ export default class PaymentProviderService extends TransactionBaseService {
/**
* Creates a payment session with the given provider.
* @param providerId - the id of the provider to create payment with
* @param providerIdOrSessionInput - the id of the provider to create payment with or the input data
* @param cart - a cart object used to calculate the amount, etc. from
* @return the payment session
*/
async createSession(providerId: string, cart: Cart): Promise<PaymentSession> {
return await this.atomicPhase_(async (transactionManager) => {
const provider = this.retrieveProvider(providerId)
const sessionData = await provider
.withTransaction(transactionManager)
.createPayment(cart)
const sessionRepo = transactionManager.getCustomRepository(
this.paymentSessionRepository_
)
const toCreate = {
cart_id: cart.id,
provider_id: providerId,
data: sessionData,
status: "pending",
}
const created = sessionRepo.create(toCreate)
return await sessionRepo.save(created)
})
}
async createSessionNew(
sessionInput: PaymentProviderDataInput
async createSession<
TInput extends string | PaymentSessionInput = string | PaymentSessionInput
>(
providerIdOrSessionInput: TInput,
...[cart]: TInput extends string ? [Cart] : [never?]
): Promise<PaymentSession> {
return await this.atomicPhase_(async (transactionManager) => {
const provider = this.retrieveProvider(sessionInput.provider_id)
const sessionData = await provider
.withTransaction(transactionManager)
.createPaymentNew(sessionInput)
const providerId = isString(providerIdOrSessionInput)
? providerIdOrSessionInput
: providerIdOrSessionInput.provider_id
const data = (
isString(providerIdOrSessionInput) ? cart : providerIdOrSessionInput
) as Cart | PaymentSessionInput
const sessionRepo = transactionManager.getCustomRepository(
this.paymentSessionRepository_
const provider = this.retrieveProvider<AbstractPaymentService>(providerId)
const context = this.buildPaymentContext(data)
if (!isDefined(context.currency_code) || !isDefined(context.amount)) {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
"`currency_code` and `amount` are required to create payment session."
)
}
const paymentResponse = await provider
.withTransaction(transactionManager)
.createPayment(context)
const sessionData = paymentResponse.session_data ?? paymentResponse
await this.processUpdateRequestsData(
{
customer: { id: context.customer?.id },
},
paymentResponse
)
const toCreate = {
provider_id: sessionInput.provider_id,
data: sessionData,
status: "pending",
amount: sessionInput.amount,
} as PaymentSession
const amount = this.featureFlagRouter_.isFeatureEnabled(
OrderEditingFeatureFlag.key
)
? context.amount
: undefined
const created = sessionRepo.create(toCreate)
return await sessionRepo.save(created)
return await this.saveSession(providerId, {
cartId: context.id,
sessionData,
status: PaymentSessionStatus.PENDING,
amount,
})
})
}
@@ -222,16 +235,22 @@ export default class PaymentProviderService extends TransactionBaseService {
* This means, that we delete the current one and create a new.
* @param paymentSession - the payment session object to
* update
* @param cart - a cart object used to calculate the amount, etc. from
* @param sessionInput
* @return the payment session
*/
async refreshSession(
paymentSession: PaymentSession,
cart: Cart
paymentSession: {
id: string
data: Record<string, unknown>
provider_id: string
},
sessionInput: PaymentSessionInput
): Promise<PaymentSession> {
return this.atomicPhase_(async (transactionManager) => {
const session = await this.retrieveSession(paymentSession.id)
const provider = this.retrieveProvider(paymentSession.provider_id)
const provider = this.retrieveProvider<AbstractPaymentService>(
paymentSession.provider_id
)
await provider.withTransaction(transactionManager).deletePayment(session)
const sessionRepo = transactionManager.getCustomRepository(
@@ -239,88 +258,44 @@ export default class PaymentProviderService extends TransactionBaseService {
)
await sessionRepo.remove(session)
const sessionData = await provider
.withTransaction(transactionManager)
.createPayment(cart)
const toCreate = {
cart_id: cart.id,
provider_id: session.provider_id,
data: sessionData,
is_selected: true,
status: "pending",
}
const created = sessionRepo.create(toCreate)
return await sessionRepo.save(created)
})
}
async refreshSessionNew(
paymentSession: PaymentSession,
sessionInput: PaymentProviderDataInput
): Promise<PaymentSession> {
return this.atomicPhase_(async (transactionManager) => {
const session = await this.retrieveSession(paymentSession.id)
const provider = this.retrieveProvider(paymentSession.provider_id)
await provider.withTransaction(transactionManager).deletePayment(session)
const sessionRepo = transactionManager.getCustomRepository(
this.paymentSessionRepository_
)
await sessionRepo.remove(session)
return await this.createSessionNew(sessionInput)
return await this.createSession(sessionInput)
})
}
/**
* Updates an existing payment session.
* @param paymentSession - the payment session object to
* update
* @param cart - the cart object to update for
* @return the updated payment session
* Update a payment session with the given provider.
* @param paymentSession - The paymentSession to update
* @param sessionInput
* @return the payment session
*/
async updateSession(
paymentSession: PaymentSession,
cart: Cart
paymentSession: {
id: string
data: Record<string, unknown>
provider_id: string
},
sessionInput: Cart | PaymentSessionInput
): Promise<PaymentSession> {
return await this.atomicPhase_(async (transactionManager) => {
const session = await this.retrieveSession(paymentSession.id)
const provider = this.retrieveProvider(paymentSession.provider_id)
session.data = await provider
.withTransaction(transactionManager)
.updatePayment(paymentSession.data, cart)
const sessionRepo = transactionManager.getCustomRepository(
this.paymentSessionRepository_
)
return await sessionRepo.save(session)
})
}
async updateSessionNew(
paymentSession: PaymentSession,
sessionInput: PaymentProviderDataInput
): Promise<PaymentSession> {
return await this.atomicPhase_(async (transactionManager) => {
const session = await this.retrieveSession(paymentSession.id)
const provider = this.retrieveProvider(paymentSession.provider_id)
session.amount = sessionInput.amount
paymentSession.data.amount = sessionInput.amount
session.data = await provider
const context = this.buildPaymentContext(sessionInput)
const sessionData = await provider
.withTransaction(transactionManager)
.updatePaymentNew(paymentSession.data, sessionInput)
.updatePayment(paymentSession.data, context)
const sessionRepo = transactionManager.getCustomRepository(
this.paymentSessionRepository_
const amount = this.featureFlagRouter_.isFeatureEnabled(
OrderEditingFeatureFlag.key
)
? context.amount
: undefined
return await sessionRepo.save(session)
return await this.saveSession(paymentSession.provider_id, {
payment_session_id: paymentSession.id,
sessionData,
amount,
})
})
}
@@ -349,15 +324,6 @@ export default class PaymentProviderService extends TransactionBaseService {
})
}
async deleteSessionNew(paymentSession: PaymentSession): Promise<void> {
return await this.atomicPhase_(async (transactionManager) => {
const provider = this.retrieveProvider(paymentSession.provider_id)
return await provider
.withTransaction(transactionManager)
.deletePayment(paymentSession)
})
}
/**
* Finds a provider given an id
* @param {string} providerId - the id of the provider to get
@@ -387,26 +353,22 @@ export default class PaymentProviderService extends TransactionBaseService {
}
}
async createPayment(data: {
cart_id: string
amount: number
currency_code: string
payment_session: PaymentSession
}): Promise<Payment> {
async createPayment(data: CreatePaymentInput): Promise<Payment> {
return await this.atomicPhase_(async (transactionManager) => {
const { payment_session: paymentSession, currency_code, amount } = data
const { payment_session, currency_code, amount, provider_id } = data
const providerId = provider_id ?? payment_session.provider_id
const provider = this.retrieveProvider(paymentSession.provider_id)
const provider = this.retrieveProvider<AbstractPaymentService>(providerId)
const paymentData = await provider
.withTransaction(transactionManager)
.getPaymentData(paymentSession)
.getPaymentData(payment_session)
const paymentRepo = transactionManager.getCustomRepository(
this.paymentRepository_
)
const created = paymentRepo.create({
provider_id: paymentSession.provider_id,
provider_id: providerId,
amount,
currency_code,
data: paymentData,
@@ -417,30 +379,6 @@ export default class PaymentProviderService extends TransactionBaseService {
})
}
async createPaymentNew(
paymentInput: Omit<PaymentProviderDataInput, "customer"> & {
payment_session: PaymentSession
}
): Promise<Payment> {
return await this.atomicPhase_(async (transactionManager) => {
const { payment_session, currency_code, amount, provider_id } =
paymentInput
const provider = this.retrieveProvider(provider_id)
const paymentData = await provider
.withTransaction(transactionManager)
.getPaymentData(payment_session)
const paymentService = this.container_.paymentService
return await paymentService.withTransaction(transactionManager).create({
provider_id,
amount,
currency_code,
data: paymentData,
})
})
}
async updatePayment(
paymentId: string,
data: { order_id?: string; swap_id?: string }
@@ -696,4 +634,123 @@ export default class PaymentProviderService extends TransactionBaseService {
return refund
}
/**
* Build the create session context for both legacy and new API
* @param cartOrData
* @protected
*/
protected buildPaymentContext(
cartOrData: Cart | PaymentSessionInput
): Cart & PaymentContext {
const cart =
"object" in cartOrData && cartOrData.object === "cart"
? cartOrData
: ((cartOrData as PaymentSessionInput).cart as Cart)
const context = {} as Cart & PaymentContext
// TODO: only to support legacy API. Once we are ready to break the API, the cartOrData will only support PaymentSessionInput
if ("object" in cartOrData && cartOrData.object === "cart") {
context.cart = {
context: cart.context,
shipping_address: cart.shipping_address,
id: cart.id,
email: cart.email,
shipping_methods: cart.shipping_methods,
}
context.amount = cart.total!
context.currency_code = cart.region?.currency_code
Object.assign(context, cart)
} else {
const data = cartOrData as PaymentSessionInput
context.cart = data.cart
context.amount = data.amount
context.currency_code = data.currency_code
Object.assign(context, cart)
}
return context
}
/**
* Create or update a Payment session data.
* @param providerId
* @param data
* @protected
*/
protected async saveSession(
providerId: string,
data: {
payment_session_id?: string
cartId?: string
amount?: number
sessionData: Record<string, unknown>
isSelected?: boolean
status?: PaymentSessionStatus
}
): Promise<PaymentSession> {
const manager = this.transactionManager_ ?? this.manager_
if (
data.amount != null &&
!this.featureFlagRouter_.isFeatureEnabled(OrderEditingFeatureFlag.key)
) {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
"Amount on payment sessions is only available with the OrderEditing API currently guarded by feature flag `MEDUSA_FF_ORDER_EDITING`. Read more about feature flags here: https://docs.medusajs.com/advanced/backend/feature-flags/toggle/"
)
}
const sessionRepo = manager.getCustomRepository(
this.paymentSessionRepository_
)
if (data.payment_session_id) {
const session = await this.retrieveSession(data.payment_session_id)
session.data = data.sessionData ?? session.data
session.status = data.status ?? session.status
session.amount = data.amount ?? session.amount
return await sessionRepo.save(session)
} else {
const toCreate: Partial<PaymentSession> = {
cart_id: data.cartId || null,
provider_id: providerId,
data: data.sessionData,
is_selected: data.isSelected,
status: data.status,
amount: data.amount,
}
const created = sessionRepo.create(toCreate)
return await sessionRepo.save(created)
}
}
/**
* Process the collected data. Can be used every time we need to process some collected data returned by the provider
* @param data
* @param paymentResponse
* @protected
*/
protected async processUpdateRequestsData(
data: { customer?: { id?: string } } = {},
paymentResponse: PaymentSessionResponse | Record<string, unknown>
): Promise<void> {
const { update_requests } = paymentResponse as PaymentSessionResponse
if (!update_requests) {
return
}
const manager = this.transactionManager_ ?? this.manager_
if (update_requests.customer_metadata && data.customer?.id) {
await this.customerService_
.withTransaction(manager)
.update(data.customer.id, {
metadata: update_requests.customer_metadata,
})
}
}
}
+2 -2
View File
@@ -1,11 +1,11 @@
import { PaymentRepository } from "./../repositories/payment"
import { EntityManager } from "typeorm"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { Payment, Refund } from "../models"
import { TransactionBaseService } from "../interfaces"
import { EventBusService, PaymentProviderService } from "./index"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
import { FindConfig } from "../types/common"
type InjectedDependencies = {
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { DeepPartial, EntityManager, FindOperator } from "typeorm"
import { CustomerGroupService } from "."
import { CustomerGroup, PriceList, Product, ProductVariant } from "../models"
@@ -18,7 +18,7 @@ import {
import ProductService from "./product"
import RegionService from "./region"
import { TransactionBaseService } from "../interfaces"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
import { FilterableProductProps } from "../types/product"
import ProductVariantService from "./product-variant"
import { FilterableProductVariantProps } from "../types/product-variant"
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { Brackets, EntityManager, ILike } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import { ProductCollection } from "../models"
@@ -9,7 +9,7 @@ import {
CreateProductCollection,
UpdateProductCollection,
} from "../types/product-collection"
import { buildQuery, isDefined, isString, setMetadata } from "../utils"
import { buildQuery, isString, setMetadata } from "../utils"
import EventBusService from "./event-bus"
type InjectedDependencies = {
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { Brackets, EntityManager, ILike, SelectQueryBuilder } from "typeorm"
import {
IPriceSelectionStrategy,
@@ -29,7 +29,7 @@ import {
ProductVariantPrice,
UpdateProductVariantInput,
} from "../types/product-variant"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
class ProductVariantService extends TransactionBaseService {
static Events = {
+2 -2
View File
@@ -1,6 +1,6 @@
import { FlagRouter } from "../utils/flag-router"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { ProductVariantService, SearchService } from "."
import { TransactionBaseService } from "../interfaces"
@@ -31,7 +31,7 @@ import {
ProductSelector,
UpdateProductInput,
} from "../types/product"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import EventBusService from "./event-bus"
type InjectedDependencies = {
@@ -1,12 +1,12 @@
import { EntityManager, ILike } from "typeorm"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { PublishableApiKeyRepository } from "../repositories/publishable-api-key"
import { FindConfig, Selector } from "../types/common"
import { PublishableApiKey, SalesChannel } from "../models"
import { TransactionBaseService } from "../interfaces"
import EventBusService from "./event-bus"
import { buildQuery, isDefined, isString } from "../utils"
import { buildQuery, isString } from "../utils"
import {
CreatePublishableApiKeyInput,
UpdatePublishableApiKeyInput,
+2 -2
View File
@@ -1,6 +1,6 @@
import { DeepPartial, EntityManager } from "typeorm"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { TransactionBaseService } from "../interfaces"
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
@@ -13,7 +13,7 @@ import { RegionRepository } from "../repositories/region"
import { TaxProviderRepository } from "../repositories/tax-provider"
import { FindConfig, Selector } from "../types/common"
import { CreateRegionInput, UpdateRegionInput } from "../types/region"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import { countries } from "../utils/countries"
import { FlagRouter } from "../utils/flag-router"
import EventBusService from "./event-bus"
@@ -1,11 +1,11 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import { Return, ReturnReason } from "../models"
import { ReturnReason } from "../models"
import { ReturnReasonRepository } from "../repositories/return-reason"
import { FindConfig, Selector } from "../types/common"
import { CreateReturnReason, UpdateReturnReason } from "../types/return-reason"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
type InjectedDependencies = {
manager: EntityManager
+1 -2
View File
@@ -1,5 +1,4 @@
import { isDefined } from "class-validator"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { DeepPartial, EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import {
@@ -6,12 +6,12 @@ import { FindConfig, QuerySelector, Selector } from "../types/common"
import { EntityManager } from "typeorm"
import EventBusService from "./event-bus"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { SalesChannel } from "../models"
import { SalesChannelRepository } from "../repositories/sales-channel"
import StoreService from "./store"
import { TransactionBaseService } from "../interfaces"
import { buildQuery, isDefined } from "../utils"
import { buildQuery } from "../utils"
type InjectedDependencies = {
salesChannelRepository: typeof SalesChannelRepository
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
@@ -21,7 +21,7 @@ import {
UpdateShippingOptionInput,
ValidatePriceTypeAndAmountInput,
} from "../types/shipping-options"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import { FlagRouter } from "../utils/flag-router"
import FulfillmentProviderService from "./fulfillment-provider"
import RegionService from "./region"
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import {
@@ -15,7 +15,7 @@ import {
CreateShippingProfile,
UpdateShippingProfile,
} from "../types/shipping-profile"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import CustomShippingOptionService from "./custom-shipping-option"
import ProductService from "./product"
import ShippingOptionService from "./shipping-option"
+2 -2
View File
@@ -1,7 +1,7 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { buildQuery, isDefined, setMetadata, validateId } from "../utils"
import { buildQuery, setMetadata, validateId } from "../utils"
import { TransactionBaseService } from "../interfaces"
import LineItemAdjustmentService from "./line-item-adjustment"
+8 -6
View File
@@ -1,9 +1,11 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { ProductTaxRate } from "../models/product-tax-rate"
import { ProductTypeTaxRate } from "../models/product-type-tax-rate"
import { ShippingTaxRate } from "../models/shipping-tax-rate"
import { TaxRate } from "../models/tax-rate"
import {
ProductTaxRate,
ProductTypeTaxRate,
ShippingTaxRate,
TaxRate,
} from "../models"
import { TaxRateRepository } from "../repositories/tax-rate"
import ProductService from "../services/product"
import ProductTypeService from "../services/product-type"
@@ -15,7 +17,7 @@ import {
TaxRateListByConfig,
UpdateTaxRateInput,
} from "../types/tax-rate"
import { buildQuery, isDefined, PostgresError } from "../utils"
import { buildQuery, PostgresError } from "../utils"
import { TransactionBaseService } from "../interfaces"
import { FindConditions } from "typeorm/find-options/FindConditions"
+2 -2
View File
@@ -1,4 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import {
ITaxCalculationStrategy,
TaxCalculationContext,
@@ -28,7 +28,7 @@ import {
import TaxProviderService from "./tax-provider"
import { EntityManager } from "typeorm"
import { calculatePriceTaxAmount, isDefined } from "../utils"
import { calculatePriceTaxAmount } from "../utils"
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
import { FlagRouter } from "../utils/flag-router"
+2 -2
View File
@@ -1,5 +1,5 @@
import jwt from "jsonwebtoken"
import { MedusaError } from "medusa-core-utils"
import { isDefined, MedusaError } from "medusa-core-utils"
import Scrypt from "scrypt-kdf"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
@@ -12,7 +12,7 @@ import {
FilterableUserProps,
UpdateUserInput,
} from "../types/user"
import { buildQuery, isDefined, setMetadata } from "../utils"
import { buildQuery, setMetadata } from "../utils"
import { FlagRouter } from "../utils/flag-router"
import { validateEmail } from "../utils/is-email"
import AnalyticsConfigService from "./analytics-config"
@@ -7,11 +7,11 @@ import {
PriceSelectionResult,
PriceType,
} from "../interfaces"
import { isDefined } from "medusa-core-utils"
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
import { MoneyAmountRepository } from "../repositories/money-amount"
import { TaxServiceRate } from "../types/tax-service"
import { FlagRouter } from "../utils/flag-router"
import { isDefined } from "../utils"
class PriceSelectionStrategy extends AbstractPriceSelectionStrategy {
protected manager_: EntityManager
+70
View File
@@ -0,0 +1,70 @@
import EventBusService from "../services/event-bus"
import { CartService, PaymentProviderService } from "../services"
import { EntityManager } from "typeorm"
type InjectedDependencies = {
eventBusService: EventBusService
cartService: CartService
paymentProviderService: PaymentProviderService
manager: EntityManager
}
class CartSubscriber {
protected readonly manager_: EntityManager
protected readonly cartService_: CartService
protected readonly paymentProviderService_: PaymentProviderService
protected readonly eventBus_: EventBusService
constructor({
manager,
cartService,
paymentProviderService,
eventBusService,
}: InjectedDependencies) {
this.cartService_ = cartService
this.paymentProviderService_ = paymentProviderService
this.eventBus_ = eventBusService
this.manager_ = manager
this.eventBus_.subscribe(
CartService.Events.CUSTOMER_UPDATED,
async (cartId) => {
await this.onCustomerUpdated(cartId)
}
)
}
async onCustomerUpdated(cartId) {
await this.manager_.transaction(
"SERIALIZABLE",
async (transactionManager) => {
const cart = await this.cartService_
.withTransaction(transactionManager)
.retrieveWithTotals(cartId, {
relations: [
"billing_address",
"region",
"region.payment_providers",
"payment_sessions",
"customer",
],
})
if (!cart.payment_sessions?.length) {
return
}
const paymentProviderServiceTx =
this.paymentProviderService_.withTransaction(transactionManager)
return await Promise.all(
cart.payment_sessions.map(async (paymentSession) => {
return paymentProviderServiceTx.updateSession(paymentSession, cart)
})
)
}
)
}
}
export default CartSubscriber
@@ -1,9 +1,4 @@
import {
Cart,
Customer,
PaymentCollection,
PaymentCollectionType,
} from "../models"
import { PaymentCollection, PaymentCollectionType } from "../models"
export type CreatePaymentCollectionInput = {
region_id: string
@@ -25,15 +20,6 @@ export type PaymentCollectionsSessionsInput = {
provider_id: string
}
export type PaymentProviderDataInput = {
resource_id: string
customer: Partial<Customer> | null
currency_code: string
provider_id: string
amount: number
cart_id?: string
cart?: Cart
}
export const defaultPaymentCollectionRelations = [
"region",
"region.payment_providers",
+34
View File
@@ -0,0 +1,34 @@
import {
Address,
Cart,
Customer,
PaymentSession,
ShippingMethod,
} from "../models"
export type PaymentSessionInput = {
provider_id: string
// TODO: Support legacy payment provider API> Once we are ready to break the api then we can remove the Cart type
cart:
| Cart
| {
context: Record<string, unknown>
id: string
email: string
shipping_address: Address | null
shipping_methods: ShippingMethod[]
}
customer?: Customer | null
currency_code: string
amount: number
resource_id?: string
}
export type CreatePaymentInput = {
cart_id?: string
amount: number
currency_code: string
provider_id?: string
payment_session: PaymentSession
resource_id?: string
}
@@ -1,8 +1,7 @@
import { pick } from "lodash"
import { FindConfig, QueryConfig, RequestQueryFields } from "../types/common"
import { MedusaError } from "medusa-core-utils/dist"
import { isDefined, MedusaError } from "medusa-core-utils"
import { BaseEntity } from "../interfaces"
import { isDefined } from "."
export function pickByConfig<TModel extends BaseEntity>(
obj: TModel | TModel[],
-1
View File
@@ -3,7 +3,6 @@ export * from "./set-metadata"
export * from "./validate-id"
export * from "./generate-entity-id"
export * from "./remove-undefined-properties"
export * from "./is-defined"
export * from "./is-string"
export * from "./calculate-price-tax-amount"
export * from "./csv-cell-content-formatter"
-5
View File
@@ -1,5 +0,0 @@
export function isDefined<T = undefined | unknown>(
val: T
): val is T extends undefined ? never : T {
return typeof val !== "undefined"
}
@@ -1,4 +1,4 @@
import { isDefined } from "./is-defined"
import { isDefined } from "medusa-core-utils"
export function removeUndefinedProperties<T extends object>(inputObj: T): T {
const removeProperties = (obj: T) => {