Skip to content

Commit

Permalink
feat(medusa): Load PaymentProcessors + integrate in PaymentProviderSe…
Browse files Browse the repository at this point in the history
…rvice (#2978)

* feat: Add payment process support into the loader and payment provider

* WIP

* feat: continue payment provider alignment

* fix tests and defer payment service resolution

* continue to add support to payment provider

* continue to add support to payment provider

* fix fixtures

* chore: add updateSessionData unsupported error

* chore: Adress feedback

* chore: Adress feedback

* chore: fix default loader

* cleanup

* cleanup

* fix unit tests

* Create purple-sloths-confess.md

* address feedback

* minor changes

* fix unit test

---------

Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
  • Loading branch information
adrien2p and olivermrbl authored Feb 21, 2023
1 parent 1c40346 commit f43e9f0
Show file tree
Hide file tree
Showing 13 changed files with 1,716 additions and 535 deletions.
8 changes: 8 additions & 0 deletions .changeset/purple-sloths-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@medusajs/medusa": patch
---

feat(medusa): Load PaymentProcessors
- Add loading of PaymentProcessors
- Add PaymentProcessor support in the payment-provider
- Add backward compatibility for the PaymentService
1 change: 1 addition & 0 deletions packages/medusa/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export * from "./models/base-entity"
export * from "./models/soft-deletable-entity"
export * from "./search-service"
export * from "./payment-service"
export * from "./payment-processor"
export * from "./services"
114 changes: 83 additions & 31 deletions packages/medusa/src/interfaces/payment-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ export type PaymentProcessorContext = {
email: string
currency_code: string
amount: number
resource_id?: string
resource_id: string
customer?: Customer
context: Record<string, unknown>
paymentSessionData: Record<string, unknown>
}

export type PaymentProcessorSessionResponse = {
update_requests: { customer_metadata: Record<string, unknown> }
update_requests?: { customer_metadata?: Record<string, unknown> }
session_data: Record<string, unknown>
}

export interface PaymentProcessorError {
error: string
code: number
details: any
code?: string
detail?: any
}

/**
Expand Down Expand Up @@ -51,55 +51,79 @@ export interface PaymentProcessor {
*/
updatePayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
): Promise<PaymentProcessorError | PaymentProcessorSessionResponse | void>

/**
* Refund an existing session
* @param context
* @param paymentSessionData
* @param refundAmount
*/
refundPayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>,
refundAmount: number
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

/**
* Authorize an existing session if it is not already authorized
* @param paymentSessionData
* @param context
*/
authorizePayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>,
context: Record<string, unknown>
): Promise<
| PaymentProcessorError
| {
status: PaymentSessionStatus
data: PaymentProcessorSessionResponse["session_data"]
}
>

/**
* Capture an existing session
* @param context
* @param paymentSessionData
*/
capturePayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

/**
* Delete an existing session
*/
deletePayment(paymentId: string): Promise<PaymentProcessorError | void>
deletePayment(
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

/**
* Retrieve an existing session
*/
retrievePayment(
paymentId: string
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

/**
* Cancel an existing session
*/
cancelPayment(paymentId: string): Promise<PaymentProcessorError | void>
cancelPayment(
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

/**
* Return the status of the session
*/
getPaymentStatus(paymentId: string): Promise<PaymentSessionStatus>
getPaymentStatus(
paymentSessionData: Record<string, unknown>
): Promise<PaymentSessionStatus>
}

/**
Expand All @@ -111,7 +135,7 @@ export abstract class AbstractPaymentProcessor implements PaymentProcessor {
protected readonly config?: Record<string, unknown> // eslint-disable-next-line @typescript-eslint/no-empty-function
) {}

protected static identifier: string
public static identifier: string

public getIdentifier(): string {
const ctr = this.constructor as typeof AbstractPaymentProcessor
Expand All @@ -126,40 +150,58 @@ export abstract class AbstractPaymentProcessor implements PaymentProcessor {
abstract init(): Promise<void>

abstract capturePayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

abstract authorizePayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>,
context: Record<string, unknown>
): Promise<
| PaymentProcessorError
| {
status: PaymentSessionStatus
data: PaymentProcessorSessionResponse["session_data"]
}
>

abstract cancelPayment(
paymentId: string
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

abstract initiatePayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | PaymentProcessorSessionResponse>

abstract deletePayment(
paymentId: string
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

abstract getPaymentStatus(paymentId: string): Promise<PaymentSessionStatus>
abstract getPaymentStatus(
paymentSessionData: Record<string, unknown>
): Promise<PaymentSessionStatus>

abstract refundPayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
paymentSessionData: Record<string, unknown>,
refundAmount: number
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

abstract retrievePayment(
paymentId: string
paymentSessionData: Record<string, unknown>
): Promise<
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
>

abstract updatePayment(
context: PaymentProcessorContext
): Promise<PaymentProcessorError | void>
): Promise<PaymentProcessorError | PaymentProcessorSessionResponse | void>
}

/**
Expand All @@ -169,3 +211,13 @@ export abstract class AbstractPaymentProcessor implements PaymentProcessor {
export function isPaymentProcessor(obj: unknown): boolean {
return obj instanceof AbstractPaymentProcessor
}

/**
* Utility function to determine if an object is a processor error
* @param obj
*/
export function isPaymentProcessorError(
obj: any
): obj is PaymentProcessorError {
return obj && typeof obj === "object" && (obj.error || obj.code || obj.detail)
}
6 changes: 4 additions & 2 deletions packages/medusa/src/interfaces/payment-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ export type PaymentContext = {
email: string
shipping_address: Address | null
shipping_methods: ShippingMethod[]
billing_address?: Address | null
}
currency_code: string
amount: number
resource_id?: string
resource_id: string
customer?: Customer
paymentSessionData: Record<string, unknown>
}

export type PaymentSessionResponse = {
Expand Down Expand Up @@ -148,7 +150,7 @@ export abstract class AbstractPaymentService
super(container, config)
}

protected static identifier: string
public static identifier: string

public getIdentifier(): string {
if (!(this.constructor as typeof AbstractPaymentService).identifier) {
Expand Down
17 changes: 15 additions & 2 deletions packages/medusa/src/loaders/__tests__/default.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { asValue, createContainer } from "awilix"
import { MockManager, MockRepository } from "medusa-test-utils"
import { StoreServiceMock } from "../../services/__mocks__/store"
import { ShippingProfileServiceMock } from "../../services/__mocks__/shipping-profile"
import {
ShippingProfileServiceMock
} from "../../services/__mocks__/shipping-profile"
import Logger from "../logger"
import featureFlagsLoader from "../feature-flags"
import { default as defaultLoader } from "../defaults"
import { SalesChannelServiceMock } from "../../services/__mocks__/sales-channel"
import { PaymentProviderServiceMock } from "../../services/__mocks__/payment-provider"
import {
PaymentProviderServiceMock
} from "../../services/__mocks__/payment-provider"

describe("default", () => {
describe("sales channel default", () => {
Expand Down Expand Up @@ -40,14 +44,23 @@ describe("default", () => {
paymentProviderService: asValue(PaymentProviderServiceMock),
notificationProviders: asValue([]),
notificationService: asValue({
withTransaction: function () {
return this
},
registerInstalledProviders: jest.fn(),
}),
fulfillmentProviders: asValue([]),
fulfillmentProviderService: asValue({
withTransaction: function () {
return this
},
registerInstalledProviders: jest.fn(),
}),
taxProviders: asValue([]),
taxProviderService: asValue({
withTransaction: function () {
return this
},
registerInstalledProviders: jest.fn(),
}),
})
Expand Down
Loading

0 comments on commit f43e9f0

Please sign in to comment.