feat(medusa): Introduce Payment Processor API (#2737)

This commit is contained in:
Adrien de Peretti
2023-01-10 12:15:42 +01:00
committed by GitHub
parent 1817b810fc
commit 4a50786fbc
4 changed files with 273 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
feat(medusa): Payment Processor API

View 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
}

View File

@@ -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
}

View File

@@ -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 =