feat(medusa): Introduce Payment Processor API (#2737)
This commit is contained in:
committed by
GitHub
parent
1817b810fc
commit
4a50786fbc
5
.changeset/thirty-parents-design.md
Normal file
5
.changeset/thirty-parents-design.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
feat(medusa): Payment Processor API
|
||||
171
packages/medusa/src/interfaces/payment-processor.ts
Normal file
171
packages/medusa/src/interfaces/payment-processor.ts
Normal file
@@ -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>
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the new PaymentProcessor interface instead
|
||||
*/
|
||||
export interface PaymentService extends TransactionBaseService {
|
||||
getIdentifier(): string
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.retrievePayment instead
|
||||
* @param paymentSession
|
||||
*/
|
||||
getPaymentData(paymentSession: PaymentSession): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.updatePayment instead
|
||||
* @param paymentSessionData
|
||||
* @param data
|
||||
*/
|
||||
updatePaymentData(
|
||||
paymentSessionData: PaymentSessionData,
|
||||
data: Data
|
||||
): 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
|
||||
* 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>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.retrievePayment instead
|
||||
* @param paymentData
|
||||
*/
|
||||
retrievePayment(paymentData: PaymentData): Promise<Data>
|
||||
|
||||
updatePayment(
|
||||
paymentSessionData: PaymentSessionData,
|
||||
context: Cart & PaymentContext
|
||||
): Promise<PaymentSessionData | PaymentSessionResponse>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.updatePayment instead
|
||||
* @param paymentSessionData
|
||||
* @param cart
|
||||
*/
|
||||
updatePayment(
|
||||
paymentSessionData: PaymentSessionData,
|
||||
cart: Cart
|
||||
): Promise<PaymentSessionData>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.authorizePayment instead
|
||||
* @param paymentSession
|
||||
* @param context
|
||||
*/
|
||||
authorizePayment(
|
||||
paymentSession: PaymentSession,
|
||||
context: Data
|
||||
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.capturePayment instead
|
||||
* @param payment
|
||||
*/
|
||||
capturePayment(payment: Payment): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.refundPayment instead
|
||||
* @param payment
|
||||
* @param refundAmount
|
||||
*/
|
||||
refundPayment(payment: Payment, refundAmount: number): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.cancelPayment instead
|
||||
* @param payment
|
||||
*/
|
||||
cancelPayment(payment: Payment): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.cancelPayment instead
|
||||
* @param paymentSession
|
||||
*/
|
||||
deletePayment(paymentSession: PaymentSession): Promise<void>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.getSavedMethods instead
|
||||
* @param customer
|
||||
*/
|
||||
retrieveSavedMethods(customer: Customer): Promise<Data[]>
|
||||
|
||||
/**
|
||||
* @deprecated use PaymentProcessor.getPaymentStatus instead
|
||||
* @param data
|
||||
*/
|
||||
getStatus(data: Data): Promise<PaymentSessionStatus>
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the AbstractPaymentProcessor instead
|
||||
*/
|
||||
export abstract class AbstractPaymentService
|
||||
extends TransactionBaseService
|
||||
implements PaymentService
|
||||
@@ -97,10 +157,16 @@ export abstract class AbstractPaymentService
|
||||
return (this.constructor as typeof AbstractPaymentService).identifier
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract getPaymentData(
|
||||
paymentSession: PaymentSession
|
||||
): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract updatePaymentData(
|
||||
paymentSessionData: PaymentSessionData,
|
||||
data: Data
|
||||
@@ -120,6 +186,9 @@ export abstract class AbstractPaymentService
|
||||
*/
|
||||
public abstract createPayment(cart: Cart): Promise<PaymentSessionData>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract retrievePayment(paymentData: PaymentData): Promise<Data>
|
||||
|
||||
/**
|
||||
@@ -142,30 +211,55 @@ export abstract class AbstractPaymentService
|
||||
cart: Cart
|
||||
): Promise<PaymentSessionData>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract authorizePayment(
|
||||
paymentSession: PaymentSession,
|
||||
context: Data
|
||||
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract capturePayment(payment: Payment): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract refundPayment(
|
||||
payment: Payment,
|
||||
refundAmount: number
|
||||
): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract cancelPayment(payment: Payment): Promise<PaymentData>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public abstract deletePayment(paymentSession: PaymentSession): Promise<void>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public async retrieveSavedMethods(customer: Customer): Promise<Data[]> {
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
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 {
|
||||
return obj instanceof AbstractPaymentService || obj instanceof PaymentService
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ export default class PaymentProviderService extends TransactionBaseService {
|
||||
) as Cart | PaymentSessionInput
|
||||
|
||||
const provider = this.retrieveProvider<AbstractPaymentService>(providerId)
|
||||
const context = this.buildPaymentContext(data)
|
||||
const context = this.buildPaymentProcessorContext(data)
|
||||
|
||||
if (!isDefined(context.currency_code) || !isDefined(context.amount)) {
|
||||
throw new MedusaError(
|
||||
@@ -279,7 +279,7 @@ export default class PaymentProviderService extends TransactionBaseService {
|
||||
return await this.atomicPhase_(async (transactionManager) => {
|
||||
const provider = this.retrieveProvider(paymentSession.provider_id)
|
||||
|
||||
const context = this.buildPaymentContext(sessionInput)
|
||||
const context = this.buildPaymentProcessorContext(sessionInput)
|
||||
|
||||
const sessionData = await provider
|
||||
.withTransaction(transactionManager)
|
||||
@@ -640,7 +640,7 @@ export default class PaymentProviderService extends TransactionBaseService {
|
||||
* @param cartOrData
|
||||
* @protected
|
||||
*/
|
||||
protected buildPaymentContext(
|
||||
protected buildPaymentProcessorContext(
|
||||
cartOrData: Cart | PaymentSessionInput
|
||||
): Cart & PaymentContext {
|
||||
const cart =
|
||||
|
||||
Reference in New Issue
Block a user