feat(medusa-payment-stripe): Stripe PaymentProcessor implementation (#3257)
This commit is contained in:
@@ -9,12 +9,24 @@ export { getRequestedBatchJob } from "./batch-job/get-requested-batch-job"
|
||||
export { doesConditionBelongToDiscount } from "./discount/does-condition-belong-to-discount"
|
||||
export { transformIncludesOptions } from "./transform-includes-options"
|
||||
export { transformBody } from "./transform-body"
|
||||
export { default as authenticate } from "./authenticate"
|
||||
export { default as authenticateCustomer } from "./authenticate-customer"
|
||||
export { default as wrapHandler } from "./await-middleware"
|
||||
export { default as normalizeQuery } from "./normalized-query"
|
||||
export { default as requireCustomerAuthentication } from "./require-customer-authentication"
|
||||
export { transformQuery, transformStoreQuery } from "./transform-query"
|
||||
|
||||
/**
|
||||
* @deprecated you can now import the middlewares directly without passing by the default export
|
||||
* e.g `import { authenticate } from "@medusajs/medusa"
|
||||
*/
|
||||
export default {
|
||||
authenticate,
|
||||
authenticateCustomer,
|
||||
requireCustomerAuthentication,
|
||||
normalizeQuery,
|
||||
/**
|
||||
* @deprecated use `import { wrapHandler } from "@medusajs/medusa"`
|
||||
*/
|
||||
wrap,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./api"
|
||||
export * from "./api/middlewares"
|
||||
export * from "./interfaces"
|
||||
export * from "./models"
|
||||
export * from "./services"
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface ICartCompletionStrategy {
|
||||
}
|
||||
|
||||
export abstract class AbstractCartCompletionStrategy
|
||||
extends TransactionBaseService
|
||||
implements ICartCompletionStrategy
|
||||
{
|
||||
abstract complete(
|
||||
|
||||
@@ -33,11 +33,6 @@ export interface PaymentProcessor {
|
||||
*/
|
||||
getIdentifier(): string
|
||||
|
||||
/**
|
||||
* Used to initialise anything like an SDK or similar
|
||||
*/
|
||||
init(): Promise<void>
|
||||
|
||||
/**
|
||||
* Initiate a payment session with the external provider
|
||||
*/
|
||||
@@ -147,8 +142,6 @@ export abstract class AbstractPaymentProcessor implements PaymentProcessor {
|
||||
return ctr.identifier
|
||||
}
|
||||
|
||||
abstract init(): Promise<void>
|
||||
|
||||
abstract capturePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<
|
||||
|
||||
@@ -205,12 +205,9 @@ async function registerPaymentProcessor({
|
||||
).filter((provider) => provider instanceof AbstractPaymentProcessor)
|
||||
|
||||
const payIds: string[] = []
|
||||
await Promise.all(
|
||||
payProviders.map((paymentProvider) => {
|
||||
payIds.push(paymentProvider.getIdentifier())
|
||||
return paymentProvider.init()
|
||||
})
|
||||
)
|
||||
payProviders.map((paymentProvider) => {
|
||||
payIds.push(paymentProvider.getIdentifier())
|
||||
})
|
||||
|
||||
const pProviderService = container.resolve<PaymentProviderService>(
|
||||
"paymentProviderService"
|
||||
|
||||
@@ -6,7 +6,6 @@ import glob from "glob"
|
||||
import _ from "lodash"
|
||||
import { createRequireFromPath } from "medusa-core-utils"
|
||||
import {
|
||||
BaseService as LegacyBaseService,
|
||||
FileService,
|
||||
FulfillmentService,
|
||||
OauthService,
|
||||
@@ -23,7 +22,6 @@ import {
|
||||
isPriceSelectionStrategy,
|
||||
isSearchService,
|
||||
isTaxCalculationStrategy,
|
||||
TransactionBaseService as BaseService,
|
||||
} from "../interfaces"
|
||||
import { MiddlewareService } from "../services"
|
||||
import {
|
||||
@@ -364,16 +362,6 @@ export async function registerServices(
|
||||
const loaded = require(fn).default
|
||||
const name = formatRegistrationName(fn)
|
||||
|
||||
if (
|
||||
!(loaded.prototype instanceof LegacyBaseService) &&
|
||||
!(loaded.prototype instanceof BaseService)
|
||||
) {
|
||||
const logger = container.resolve<Logger>("logger")
|
||||
const message = `The class must be a valid service implementation, please check ${fn}`
|
||||
logger.error(message)
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const context = { container, pluginDetails, registrationName: name }
|
||||
|
||||
registerPaymentServiceFromClass(loaded, context)
|
||||
@@ -627,8 +615,18 @@ function resolvePlugin(pluginName: string): {
|
||||
)
|
||||
// warnOnIncompatiblePeerDependency(packageJSON.name, packageJSON)
|
||||
|
||||
const computedResolvedPath =
|
||||
resolvedPath + (process.env.DEV_MODE ? "/src" : "")
|
||||
|
||||
// Add support for a plugin to output the build into a dist directory
|
||||
const resolvedPathToDist = resolvedPath + "/dist"
|
||||
const isDistExist =
|
||||
resolvedPathToDist &&
|
||||
!process.env.DEV_MODE &&
|
||||
existsSync(resolvedPath + "/dist")
|
||||
|
||||
return {
|
||||
resolve: resolvedPath + (process.env.DEV_MODE ? "/src" : ""),
|
||||
resolve: isDistExist ? resolvedPathToDist : computedResolvedPath,
|
||||
id: createPluginId(packageJSON.name),
|
||||
name: packageJSON.name,
|
||||
options: {},
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
CreateIdempotencyKeyInput,
|
||||
IdempotencyCallbackResult,
|
||||
} from "../types/idempotency-key"
|
||||
import { Selector } from "../types/common"
|
||||
import { buildQuery, isString } from "../utils"
|
||||
|
||||
const KEY_LOCKED_TIMEOUT = 1000
|
||||
|
||||
@@ -75,14 +77,16 @@ class IdempotencyKeyService extends TransactionBaseService {
|
||||
|
||||
/**
|
||||
* Retrieves an idempotency key
|
||||
* @param idempotencyKey - key to retrieve
|
||||
* @param idempotencyKeyOrSelector - key or selector to retrieve
|
||||
* @return idempotency key
|
||||
*/
|
||||
async retrieve(idempotencyKey: string): Promise<IdempotencyKey | never> {
|
||||
if (!isDefined(idempotencyKey)) {
|
||||
async retrieve(
|
||||
idempotencyKeyOrSelector: string | Selector<IdempotencyKey>
|
||||
): Promise<IdempotencyKey | never> {
|
||||
if (!isDefined(idempotencyKeyOrSelector)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`"idempotencyKey" must be defined`
|
||||
`"idempotencyKeyOrSelector" must be defined`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,15 +94,34 @@ class IdempotencyKeyService extends TransactionBaseService {
|
||||
this.idempotencyKeyRepository_
|
||||
)
|
||||
|
||||
const iKey = await idempotencyKeyRepo.findOne({
|
||||
where: { idempotency_key: idempotencyKey },
|
||||
})
|
||||
const selector = isString(idempotencyKeyOrSelector)
|
||||
? { idempotency_key: idempotencyKeyOrSelector }
|
||||
: idempotencyKeyOrSelector
|
||||
const query = buildQuery(selector)
|
||||
|
||||
const iKeys = await idempotencyKeyRepo.find(query)
|
||||
|
||||
if (iKeys.length > 1) {
|
||||
throw new Error(
|
||||
`Multiple keys were found for constraints: ${JSON.stringify(
|
||||
idempotencyKeyOrSelector
|
||||
)}. There should only be one.`
|
||||
)
|
||||
}
|
||||
|
||||
const iKey = iKeys[0]
|
||||
|
||||
if (!iKey) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Idempotency key ${idempotencyKey} was not found`
|
||||
)
|
||||
let message
|
||||
if (isString(idempotencyKeyOrSelector)) {
|
||||
message = `Idempotency key ${idempotencyKeyOrSelector} was not found`
|
||||
} else {
|
||||
message = `Idempotency key with constraints ${JSON.stringify(
|
||||
idempotencyKeyOrSelector
|
||||
)} was not found`
|
||||
}
|
||||
|
||||
throw new MedusaError(MedusaError.Types.NOT_FOUND, message)
|
||||
}
|
||||
|
||||
return iKey
|
||||
|
||||
@@ -356,17 +356,16 @@ export default class PaymentProviderService extends TransactionBaseService {
|
||||
|
||||
let paymentResponse
|
||||
if (provider instanceof AbstractPaymentProcessor) {
|
||||
paymentResponse =
|
||||
(await provider.updatePayment({
|
||||
amount: context.amount,
|
||||
context: context.context,
|
||||
currency_code: context.currency_code,
|
||||
customer: context.customer,
|
||||
email: context.email,
|
||||
billing_address: context.billing_address,
|
||||
resource_id: context.resource_id,
|
||||
paymentSessionData: paymentSession.data,
|
||||
})) ?? {}
|
||||
paymentResponse = await provider.updatePayment({
|
||||
amount: context.amount,
|
||||
context: context.context,
|
||||
currency_code: context.currency_code,
|
||||
customer: context.customer,
|
||||
email: context.email,
|
||||
billing_address: context.billing_address,
|
||||
resource_id: context.resource_id,
|
||||
paymentSessionData: paymentSession.data,
|
||||
})
|
||||
|
||||
if (paymentResponse && "error" in paymentResponse) {
|
||||
this.throwFromPaymentProcessorError(paymentResponse)
|
||||
@@ -377,7 +376,7 @@ export default class PaymentProviderService extends TransactionBaseService {
|
||||
.updatePayment(paymentSession.data, context)
|
||||
}
|
||||
|
||||
const sessionData = paymentResponse.session_data ?? paymentResponse
|
||||
const sessionData = paymentResponse?.session_data ?? paymentResponse
|
||||
|
||||
// If no update occurs, return the original session
|
||||
if (!sessionData) {
|
||||
|
||||
@@ -30,8 +30,6 @@ type InjectedDependencies = {
|
||||
}
|
||||
|
||||
class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
protected manager_: EntityManager
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
protected readonly productVariantInventoryService_: ProductVariantInventoryService
|
||||
protected readonly paymentProviderService_: PaymentProviderService
|
||||
@@ -47,9 +45,9 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
cartService,
|
||||
orderService,
|
||||
swapService,
|
||||
manager,
|
||||
}: InjectedDependencies) {
|
||||
super()
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(arguments[0])
|
||||
|
||||
this.paymentProviderService_ = paymentProviderService
|
||||
this.productVariantInventoryService_ = productVariantInventoryService
|
||||
@@ -57,7 +55,6 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
this.cartService_ = cartService
|
||||
this.orderService_ = orderService
|
||||
this.swapService_ = swapService
|
||||
this.manager_ = manager
|
||||
}
|
||||
|
||||
async complete(
|
||||
@@ -73,7 +70,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
while (inProgress) {
|
||||
switch (idempotencyKey.recovery_point) {
|
||||
case "started": {
|
||||
await this.manager_
|
||||
await this.activeManager_
|
||||
.transaction("SERIALIZABLE", async (transactionManager) => {
|
||||
idempotencyKey = await this.idempotencyKeyService_
|
||||
.withTransaction(transactionManager)
|
||||
@@ -90,7 +87,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
break
|
||||
}
|
||||
case "tax_lines_created": {
|
||||
await this.manager_
|
||||
await this.activeManager_
|
||||
.transaction("SERIALIZABLE", async (transactionManager) => {
|
||||
idempotencyKey = await this.idempotencyKeyService_
|
||||
.withTransaction(transactionManager)
|
||||
@@ -111,7 +108,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
}
|
||||
|
||||
case "payment_authorized": {
|
||||
await this.manager_
|
||||
await this.activeManager_
|
||||
.transaction("SERIALIZABLE", async (transactionManager) => {
|
||||
idempotencyKey = await this.idempotencyKeyService_
|
||||
.withTransaction(transactionManager)
|
||||
@@ -134,7 +131,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
}
|
||||
|
||||
default:
|
||||
await this.manager_.transaction(async (transactionManager) => {
|
||||
await this.activeManager_.transaction(async (transactionManager) => {
|
||||
idempotencyKey = await this.idempotencyKeyService_
|
||||
.withTransaction(transactionManager)
|
||||
.update(idempotencyKey.idempotency_key, {
|
||||
@@ -149,7 +146,7 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
|
||||
if (err) {
|
||||
if (idempotencyKey.recovery_point !== "started") {
|
||||
await this.manager_.transaction(async (transactionManager) => {
|
||||
await this.activeManager_.transaction(async (transactionManager) => {
|
||||
try {
|
||||
await this.orderService_
|
||||
.withTransaction(transactionManager)
|
||||
@@ -264,16 +261,19 @@ class CartCompletionStrategy extends AbstractCartCompletionStrategy {
|
||||
const cartServiceTx = this.cartService_.withTransaction(manager)
|
||||
|
||||
const cart = await cartServiceTx.retrieveWithTotals(id, {
|
||||
relations: ["region", "payment", "payment_sessions", "items.variant.product",],
|
||||
relations: [
|
||||
"region",
|
||||
"payment",
|
||||
"payment_sessions",
|
||||
"items.variant.product",
|
||||
],
|
||||
})
|
||||
|
||||
let allowBackorder = false
|
||||
let swapId: string
|
||||
|
||||
if (cart.type === "swap") {
|
||||
const swap = await swapServiceTx.retrieveByCartId(id)
|
||||
allowBackorder = swap.allow_backorder
|
||||
swapId = swap.id
|
||||
}
|
||||
|
||||
if (!allowBackorder) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export type CreateIdempotencyKeyInput = {
|
||||
request_method: string
|
||||
request_params: Record<string, unknown>
|
||||
request_path: string
|
||||
request_method?: string
|
||||
request_params?: Record<string, unknown>
|
||||
request_path?: string
|
||||
idempotency_key?: string
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user