feat(medusa): Introduce Payment Processor API (#2737)
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa): Payment Processor API
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import { Address, Customer, PaymentSessionStatus } from "../models"
|
||||||
|
import { MedusaContainer } from "../types/global"
|
||||||
|
|
||||||
|
export type PaymentProcessorContext = {
|
||||||
|
billing_address?: Address | null
|
||||||
|
email: string
|
||||||
|
currency_code: string
|
||||||
|
amount: number
|
||||||
|
resource_id?: string
|
||||||
|
customer?: Customer
|
||||||
|
context: Record<string, unknown>
|
||||||
|
paymentSessionData: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PaymentProcessorSessionResponse = {
|
||||||
|
update_requests: { customer_metadata: Record<string, unknown> }
|
||||||
|
session_data: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaymentProcessorError {
|
||||||
|
error: string
|
||||||
|
code: number
|
||||||
|
details: any
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The new payment service plugin interface
|
||||||
|
* This work is still experimental and can be changed until it becomes stable
|
||||||
|
*/
|
||||||
|
export interface PaymentProcessor {
|
||||||
|
/**
|
||||||
|
* Return a unique identifier to retrieve the payment plugin provider
|
||||||
|
*/
|
||||||
|
getIdentifier(): string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to initialise anything like an SDK or similar
|
||||||
|
*/
|
||||||
|
init(): Promise<void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a payment session with the external provider
|
||||||
|
*/
|
||||||
|
initiatePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | PaymentProcessorSessionResponse>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing payment session
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
updatePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refund an existing session
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
refundPayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize an existing session if it is not already authorized
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
authorizePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capture an existing session
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
capturePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an existing session
|
||||||
|
*/
|
||||||
|
deletePayment(paymentId: string): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an existing session
|
||||||
|
*/
|
||||||
|
retrievePayment(
|
||||||
|
paymentId: string
|
||||||
|
): Promise<
|
||||||
|
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
|
||||||
|
>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel an existing session
|
||||||
|
*/
|
||||||
|
cancelPayment(paymentId: string): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the status of the session
|
||||||
|
*/
|
||||||
|
getPaymentStatus(paymentId: string): Promise<PaymentSessionStatus>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payment processor in charge of creating , managing and processing a payment
|
||||||
|
*/
|
||||||
|
export abstract class AbstractPaymentProcessor implements PaymentProcessor {
|
||||||
|
protected constructor(
|
||||||
|
protected readonly container: MedusaContainer,
|
||||||
|
protected readonly config?: Record<string, unknown> // eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
) {}
|
||||||
|
|
||||||
|
protected static identifier: string
|
||||||
|
|
||||||
|
public getIdentifier(): string {
|
||||||
|
const ctr = this.constructor as typeof AbstractPaymentProcessor
|
||||||
|
|
||||||
|
if (!ctr.identifier) {
|
||||||
|
throw new Error(`Missing static property "identifier".`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctr.identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract init(): Promise<void>
|
||||||
|
|
||||||
|
abstract capturePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
abstract authorizePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
abstract cancelPayment(
|
||||||
|
paymentId: string
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
abstract initiatePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | PaymentProcessorSessionResponse>
|
||||||
|
|
||||||
|
abstract deletePayment(
|
||||||
|
paymentId: string
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
abstract getPaymentStatus(paymentId: string): Promise<PaymentSessionStatus>
|
||||||
|
|
||||||
|
abstract refundPayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
|
||||||
|
abstract retrievePayment(
|
||||||
|
paymentId: string
|
||||||
|
): Promise<
|
||||||
|
PaymentProcessorError | PaymentProcessorSessionResponse["session_data"]
|
||||||
|
>
|
||||||
|
|
||||||
|
abstract updatePayment(
|
||||||
|
context: PaymentProcessorContext
|
||||||
|
): Promise<PaymentProcessorError | void>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the input object is AbstractPaymentProcessor
|
||||||
|
* @param obj
|
||||||
|
*/
|
||||||
|
export function isPaymentProcessor(obj: unknown): boolean {
|
||||||
|
return obj instanceof AbstractPaymentProcessor
|
||||||
|
}
|
||||||
@@ -33,17 +33,30 @@ export type PaymentSessionResponse = {
|
|||||||
session_data: Record<string, unknown>
|
session_data: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use the new PaymentProcessor interface instead
|
||||||
|
*/
|
||||||
export interface PaymentService extends TransactionBaseService {
|
export interface PaymentService extends TransactionBaseService {
|
||||||
getIdentifier(): string
|
getIdentifier(): string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.retrievePayment instead
|
||||||
|
* @param paymentSession
|
||||||
|
*/
|
||||||
getPaymentData(paymentSession: PaymentSession): Promise<PaymentData>
|
getPaymentData(paymentSession: PaymentSession): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.updatePayment instead
|
||||||
|
* @param paymentSessionData
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
updatePaymentData(
|
updatePaymentData(
|
||||||
paymentSessionData: PaymentSessionData,
|
paymentSessionData: PaymentSessionData,
|
||||||
data: Data
|
data: Data
|
||||||
): Promise<PaymentSessionData>
|
): Promise<PaymentSessionData>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.initiatePayment instead
|
||||||
* @param context The type of this argument is meant to be temporary and once the previous method signature
|
* @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
|
* will be removed, the type will only be PaymentContext instead of Cart & PaymentContext
|
||||||
*/
|
*/
|
||||||
@@ -55,31 +68,78 @@ export interface PaymentService extends TransactionBaseService {
|
|||||||
*/
|
*/
|
||||||
createPayment(cart: Cart): Promise<PaymentSessionData>
|
createPayment(cart: Cart): Promise<PaymentSessionData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.retrievePayment instead
|
||||||
|
* @param paymentData
|
||||||
|
*/
|
||||||
retrievePayment(paymentData: PaymentData): Promise<Data>
|
retrievePayment(paymentData: PaymentData): Promise<Data>
|
||||||
|
|
||||||
|
updatePayment(
|
||||||
|
paymentSessionData: PaymentSessionData,
|
||||||
|
context: Cart & PaymentContext
|
||||||
|
): Promise<PaymentSessionData | PaymentSessionResponse>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.updatePayment instead
|
||||||
|
* @param paymentSessionData
|
||||||
|
* @param cart
|
||||||
|
*/
|
||||||
updatePayment(
|
updatePayment(
|
||||||
paymentSessionData: PaymentSessionData,
|
paymentSessionData: PaymentSessionData,
|
||||||
cart: Cart
|
cart: Cart
|
||||||
): Promise<PaymentSessionData>
|
): Promise<PaymentSessionData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.authorizePayment instead
|
||||||
|
* @param paymentSession
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
authorizePayment(
|
authorizePayment(
|
||||||
paymentSession: PaymentSession,
|
paymentSession: PaymentSession,
|
||||||
context: Data
|
context: Data
|
||||||
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }>
|
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.capturePayment instead
|
||||||
|
* @param payment
|
||||||
|
*/
|
||||||
capturePayment(payment: Payment): Promise<PaymentData>
|
capturePayment(payment: Payment): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.refundPayment instead
|
||||||
|
* @param payment
|
||||||
|
* @param refundAmount
|
||||||
|
*/
|
||||||
refundPayment(payment: Payment, refundAmount: number): Promise<PaymentData>
|
refundPayment(payment: Payment, refundAmount: number): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.cancelPayment instead
|
||||||
|
* @param payment
|
||||||
|
*/
|
||||||
cancelPayment(payment: Payment): Promise<PaymentData>
|
cancelPayment(payment: Payment): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.cancelPayment instead
|
||||||
|
* @param paymentSession
|
||||||
|
*/
|
||||||
deletePayment(paymentSession: PaymentSession): Promise<void>
|
deletePayment(paymentSession: PaymentSession): Promise<void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.getSavedMethods instead
|
||||||
|
* @param customer
|
||||||
|
*/
|
||||||
retrieveSavedMethods(customer: Customer): Promise<Data[]>
|
retrieveSavedMethods(customer: Customer): Promise<Data[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use PaymentProcessor.getPaymentStatus instead
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
getStatus(data: Data): Promise<PaymentSessionStatus>
|
getStatus(data: Data): Promise<PaymentSessionStatus>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use the AbstractPaymentProcessor instead
|
||||||
|
*/
|
||||||
export abstract class AbstractPaymentService
|
export abstract class AbstractPaymentService
|
||||||
extends TransactionBaseService
|
extends TransactionBaseService
|
||||||
implements PaymentService
|
implements PaymentService
|
||||||
@@ -97,10 +157,16 @@ export abstract class AbstractPaymentService
|
|||||||
return (this.constructor as typeof AbstractPaymentService).identifier
|
return (this.constructor as typeof AbstractPaymentService).identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract getPaymentData(
|
public abstract getPaymentData(
|
||||||
paymentSession: PaymentSession
|
paymentSession: PaymentSession
|
||||||
): Promise<PaymentData>
|
): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract updatePaymentData(
|
public abstract updatePaymentData(
|
||||||
paymentSessionData: PaymentSessionData,
|
paymentSessionData: PaymentSessionData,
|
||||||
data: Data
|
data: Data
|
||||||
@@ -120,6 +186,9 @@ export abstract class AbstractPaymentService
|
|||||||
*/
|
*/
|
||||||
public abstract createPayment(cart: Cart): Promise<PaymentSessionData>
|
public abstract createPayment(cart: Cart): Promise<PaymentSessionData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract retrievePayment(paymentData: PaymentData): Promise<Data>
|
public abstract retrievePayment(paymentData: PaymentData): Promise<Data>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,30 +211,55 @@ export abstract class AbstractPaymentService
|
|||||||
cart: Cart
|
cart: Cart
|
||||||
): Promise<PaymentSessionData>
|
): Promise<PaymentSessionData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract authorizePayment(
|
public abstract authorizePayment(
|
||||||
paymentSession: PaymentSession,
|
paymentSession: PaymentSession,
|
||||||
context: Data
|
context: Data
|
||||||
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }>
|
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract capturePayment(payment: Payment): Promise<PaymentData>
|
public abstract capturePayment(payment: Payment): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract refundPayment(
|
public abstract refundPayment(
|
||||||
payment: Payment,
|
payment: Payment,
|
||||||
refundAmount: number
|
refundAmount: number
|
||||||
): Promise<PaymentData>
|
): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract cancelPayment(payment: Payment): Promise<PaymentData>
|
public abstract cancelPayment(payment: Payment): Promise<PaymentData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract deletePayment(paymentSession: PaymentSession): Promise<void>
|
public abstract deletePayment(paymentSession: PaymentSession): Promise<void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
public async retrieveSavedMethods(customer: Customer): Promise<Data[]> {
|
public async retrieveSavedMethods(customer: Customer): Promise<Data[]> {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public abstract getStatus(data: Data): Promise<PaymentSessionStatus>
|
public abstract getStatus(data: Data): Promise<PaymentSessionStatus>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the input object is one of AbstractPaymentService or PaymentService or AbstractPaymentPluginService
|
||||||
|
* @param obj
|
||||||
|
*/
|
||||||
export function isPaymentService(obj: unknown): boolean {
|
export function isPaymentService(obj: unknown): boolean {
|
||||||
return obj instanceof AbstractPaymentService || obj instanceof PaymentService
|
return obj instanceof AbstractPaymentService || obj instanceof PaymentService
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ export default class PaymentProviderService extends TransactionBaseService {
|
|||||||
) as Cart | PaymentSessionInput
|
) as Cart | PaymentSessionInput
|
||||||
|
|
||||||
const provider = this.retrieveProvider<AbstractPaymentService>(providerId)
|
const provider = this.retrieveProvider<AbstractPaymentService>(providerId)
|
||||||
const context = this.buildPaymentContext(data)
|
const context = this.buildPaymentProcessorContext(data)
|
||||||
|
|
||||||
if (!isDefined(context.currency_code) || !isDefined(context.amount)) {
|
if (!isDefined(context.currency_code) || !isDefined(context.amount)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
@@ -279,7 +279,7 @@ export default class PaymentProviderService extends TransactionBaseService {
|
|||||||
return await this.atomicPhase_(async (transactionManager) => {
|
return await this.atomicPhase_(async (transactionManager) => {
|
||||||
const provider = this.retrieveProvider(paymentSession.provider_id)
|
const provider = this.retrieveProvider(paymentSession.provider_id)
|
||||||
|
|
||||||
const context = this.buildPaymentContext(sessionInput)
|
const context = this.buildPaymentProcessorContext(sessionInput)
|
||||||
|
|
||||||
const sessionData = await provider
|
const sessionData = await provider
|
||||||
.withTransaction(transactionManager)
|
.withTransaction(transactionManager)
|
||||||
@@ -640,7 +640,7 @@ export default class PaymentProviderService extends TransactionBaseService {
|
|||||||
* @param cartOrData
|
* @param cartOrData
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected buildPaymentContext(
|
protected buildPaymentProcessorContext(
|
||||||
cartOrData: Cart | PaymentSessionInput
|
cartOrData: Cart | PaymentSessionInput
|
||||||
): Cart & PaymentContext {
|
): Cart & PaymentContext {
|
||||||
const cart =
|
const cart =
|
||||||
|
|||||||
Reference in New Issue
Block a user