feat: Refresh payment collection + delete session (#6594)
### What Add workflow for refreshing a payment collection. The idea is that on all cart updates, we want two things to happen (in the context of payments) 1. the currently active payment sessions should be destroyed, and 2. the payment collection should be updated with the new cart total. We do this to ensure that we always collect the correct payment amount. From a customer perspective, this would mean that every time something on the cart is updated, the customer would need to enter their payment details anew. To me, this is a good tradeoff to avoid inconsistencies with payment collection. Additionally, I updated the Payment Module interface with `upsert` and `updated` following our established convention. ### Note This PR depends on a fix to the `remoteJoiner` that @carlos-r-l-rodrigues is working on. Update: Fix merged in #6602 Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
co-authored by
Adrien de Peretti
parent
a516e7bcba
commit
8c57e61cb8
@@ -0,0 +1,26 @@
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
entry_point: string
|
||||
fields: string[]
|
||||
variables?: Record<string, any>
|
||||
}
|
||||
|
||||
export const useRemoteQueryStepId = "use-remote-query"
|
||||
export const useRemoteQueryStep = createStep(
|
||||
useRemoteQueryStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const query = container.resolve("remoteQuery")
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: data.entry_point,
|
||||
fields: data.fields,
|
||||
variables: data.variables,
|
||||
})
|
||||
|
||||
const result = await query(queryObject)
|
||||
|
||||
return new StepResponse(result)
|
||||
}
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from "./add-to-cart"
|
||||
export * from "./create-carts"
|
||||
export * from "./create-payment-collection-for-cart"
|
||||
export * from "./refresh-payment-collection"
|
||||
export * from "./update-cart"
|
||||
export * from "./update-cart-promotions"
|
||||
export * from "./update-line-item-in-cart"
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
StepResponse,
|
||||
WorkflowData,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||
import {
|
||||
deletePaymentSessionStep,
|
||||
updatePaymentCollectionStep,
|
||||
} from "../../payment-collection"
|
||||
|
||||
type WorklowInput = {
|
||||
cart_id: string
|
||||
}
|
||||
|
||||
interface StepInput {
|
||||
cart_id: string
|
||||
}
|
||||
|
||||
// We export a step running the workflow too, so that we can use it as a subworkflow e.g. in the update cart workflows
|
||||
export const refreshPaymentCollectionForCartStepId =
|
||||
"refresh-payment-collection-for-cart"
|
||||
export const refreshPaymentCollectionForCartStep = createStep(
|
||||
refreshPaymentCollectionForCartStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
await refreshPaymentCollectionForCartWorkflow(container).run({
|
||||
input: {
|
||||
cart_id: data.cart_id,
|
||||
},
|
||||
})
|
||||
|
||||
return new StepResponse(null)
|
||||
}
|
||||
)
|
||||
|
||||
export const refreshPaymentCollectionForCartWorkflowId =
|
||||
"refresh-payment-collection-for-cart"
|
||||
export const refreshPaymentCollectionForCartWorkflow = createWorkflow(
|
||||
refreshPaymentCollectionForCartWorkflowId,
|
||||
(input: WorkflowData<WorklowInput>): WorkflowData<void> => {
|
||||
const carts = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: [
|
||||
"id",
|
||||
"total",
|
||||
"currency_code",
|
||||
"payment_collection.id",
|
||||
"payment_collection.payment_sessions.id",
|
||||
],
|
||||
variables: { id: input.cart_id },
|
||||
})
|
||||
|
||||
deletePaymentSessionStep({
|
||||
payment_session_id: carts[0].payment_collection.payment_sessions?.[0].id,
|
||||
})
|
||||
|
||||
// TODO: Temporary fixed cart total, so we can test the workflow.
|
||||
// This will be removed when the totals utilities are built.
|
||||
const cartTotal = 4242
|
||||
|
||||
updatePaymentCollectionStep({
|
||||
selector: { id: carts[0].payment_collection.id },
|
||||
update: {
|
||||
amount: cartTotal,
|
||||
currency_code: carts[0].currency_code,
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
updateCartsStep,
|
||||
} from "../steps"
|
||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
||||
|
||||
export const updateCartWorkflowId = "update-cart"
|
||||
export const updateCartWorkflow = createWorkflow(
|
||||
@@ -68,6 +69,10 @@ export const updateCartWorkflow = createWorkflow(
|
||||
action: PromotionActions.REPLACE,
|
||||
})
|
||||
|
||||
refreshPaymentCollectionForCartStep({
|
||||
cart_id: input.id,
|
||||
})
|
||||
|
||||
const retrieveCartInput = {
|
||||
id: input.id,
|
||||
config: {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPaymentModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
payment_session_id?: string
|
||||
}
|
||||
|
||||
export const deletePaymentSessionStepId = "delete-payment-session"
|
||||
export const deletePaymentSessionStep = createStep(
|
||||
deletePaymentSessionStepId,
|
||||
async (input: StepInput, { container }) => {
|
||||
const service = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
if (!input.payment_session_id) {
|
||||
return new StepResponse(void 0, null)
|
||||
}
|
||||
|
||||
const [session] = await service.listPaymentSessions({
|
||||
id: input.payment_session_id,
|
||||
})
|
||||
|
||||
await service.deletePaymentSession(input.payment_session_id)
|
||||
|
||||
return new StepResponse(input.payment_session_id, session)
|
||||
},
|
||||
async (input, { container }) => {
|
||||
const service = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
if (!input || !input.payment_collection) {
|
||||
return
|
||||
}
|
||||
|
||||
await service.createPaymentSession(input.payment_collection.id, {
|
||||
provider_id: input.provider_id,
|
||||
currency_code: input.currency_code,
|
||||
amount: input.amount,
|
||||
data: input.data ?? {},
|
||||
context: input.context,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from "./create-payment-session"
|
||||
export * from "./delete-payment-session"
|
||||
export * from "./retrieve-payment-collection"
|
||||
export * from "./update-payment-collection"
|
||||
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
FilterablePaymentCollectionProps,
|
||||
IPaymentModuleService,
|
||||
PaymentCollectionUpdatableFields,
|
||||
} from "@medusajs/types"
|
||||
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
selector: FilterablePaymentCollectionProps
|
||||
update: PaymentCollectionUpdatableFields
|
||||
}
|
||||
|
||||
export const updatePaymentCollectionStepId = "update-payment-collection"
|
||||
export const updatePaymentCollectionStep = createStep(
|
||||
updatePaymentCollectionStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const paymentModuleService = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
data.update,
|
||||
])
|
||||
|
||||
const prevData = await paymentModuleService.listPaymentCollections(
|
||||
data.selector,
|
||||
{
|
||||
select: selects,
|
||||
relations,
|
||||
}
|
||||
)
|
||||
|
||||
const updated = await paymentModuleService.updatePaymentCollections(
|
||||
data.selector,
|
||||
data.update
|
||||
)
|
||||
|
||||
return new StepResponse(updated, prevData)
|
||||
},
|
||||
async (prevData, { container }) => {
|
||||
if (!prevData) {
|
||||
return
|
||||
}
|
||||
const paymentModuleService = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
await paymentModuleService.upsertPaymentCollections(
|
||||
prevData.map((pc) => ({
|
||||
id: pc.id,
|
||||
amount: pc.amount,
|
||||
currency_code: pc.currency_code,
|
||||
metadata: pc.metadata,
|
||||
}))
|
||||
)
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user