feat(medusa-payment-stripe): Stripe PaymentProcessor implementation (#3257)

This commit is contained in:
Adrien de Peretti
2023-02-28 18:44:24 +01:00
committed by GitHub
parent d61d6c7b7f
commit 589d1c09b0
55 changed files with 2325 additions and 1278 deletions
@@ -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
View File
@@ -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<
+3 -6
View File
@@ -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"
+11 -13
View File
@@ -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: {},
+34 -11
View File
@@ -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) {
+3 -3
View File
@@ -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
}