From 4e12168dbd2ce2654acbac27bc85819ea1cb668c Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Mon, 6 May 2024 15:14:40 +0200 Subject: [PATCH] feat: Consolidate payment functions and handle sessions for storefront (#7231) --- .../cart/store/cart.workflows.spec.ts | 9 +- .../__tests__/cart/store/carts.spec.ts | 101 +++++++++++++++--- .../steps/validate-cart-shipping-options.ts | 8 +- .../workflows/add-shipping-method-to-cart.ts | 1 + .../list-shipping-options-for-cart.ts | 4 + packages/medusa/src/api-v2/middlewares.ts | 6 ++ .../carts/[id]/payment-collections/route.ts | 39 ------- .../src/api-v2/store/carts/middlewares.ts | 11 -- .../src/api-v2/store/carts/query-config.ts | 8 +- .../[id]/payment-sessions/middlewares.ts | 11 -- .../[id]/payment-sessions/query-config.ts | 20 ---- .../[id]/payment-sessions/route.ts | 35 +++--- .../[id]/payment-sessions/validators.ts | 7 -- .../store/payment-collections/helpers.ts | 10 ++ .../store/payment-collections/middlewares.ts | 44 ++++++++ .../store/payment-collections/query-config.ts | 14 +++ .../api-v2/store/payment-collections/route.ts | 55 ++++++++++ .../store/payment-collections/validators.ts | 30 ++++++ .../store/payment-providers/middlewares.ts | 17 +++ .../store/payment-providers/query-config.ts | 6 ++ .../api-v2/store/payment-providers/route.ts | 50 +++++++++ .../store/payment-providers/validators.ts | 18 ++++ .../store/shipping-options/middlewares.ts | 12 ++- .../shipping-options/{[cart_id] => }/route.ts | 11 +- .../store/shipping-options/validators.ts | 16 +++ 25 files changed, 408 insertions(+), 135 deletions(-) delete mode 100644 packages/medusa/src/api-v2/store/carts/[id]/payment-collections/route.ts delete mode 100644 packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/middlewares.ts delete mode 100644 packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/query-config.ts delete mode 100644 packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/validators.ts create mode 100644 packages/medusa/src/api-v2/store/payment-collections/helpers.ts create mode 100644 packages/medusa/src/api-v2/store/payment-collections/middlewares.ts create mode 100644 packages/medusa/src/api-v2/store/payment-collections/query-config.ts create mode 100644 packages/medusa/src/api-v2/store/payment-collections/route.ts create mode 100644 packages/medusa/src/api-v2/store/payment-collections/validators.ts create mode 100644 packages/medusa/src/api-v2/store/payment-providers/middlewares.ts create mode 100644 packages/medusa/src/api-v2/store/payment-providers/query-config.ts create mode 100644 packages/medusa/src/api-v2/store/payment-providers/route.ts create mode 100644 packages/medusa/src/api-v2/store/payment-providers/validators.ts rename packages/medusa/src/api-v2/store/shipping-options/{[cart_id] => }/route.ts (78%) create mode 100644 packages/medusa/src/api-v2/store/shipping-options/validators.ts diff --git a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts index 597eda54e9..4f387ff73c 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -1455,8 +1455,13 @@ medusaIntegrationTestRunner({ rules: [ { operator: RuleOperator.EQ, - attribute: "shipping_address.province", - value: "ny", + attribute: "is_return", + value: "false", + }, + { + operator: RuleOperator.EQ, + attribute: "enabled_in_store", + value: "true", }, ], }) diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index a889d3ca43..e2f79607a4 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -1197,7 +1197,7 @@ medusaIntegrationTestRunner({ }) }) - describe("POST /store/carts/:id/payment-collections", () => { + describe("POST /store/payment-collections", () => { it("should create a payment collection for the cart", async () => { const region = await regionModule.create({ name: "US", @@ -1209,22 +1209,92 @@ medusaIntegrationTestRunner({ region_id: region.id, }) - const response = await api.post( - `/store/carts/${cart.id}/payment-collections` - ) + const response = await api.post(`/store/payment-collections`, { + region_id: region.id, + cart_id: cart.id, + amount: 0, + currency_code: cart.currency_code, + }) expect(response.status).toEqual(200) - expect(response.data.cart).toEqual( + expect(response.data.payment_collection).toEqual( expect.objectContaining({ - id: cart.id, - currency_code: "usd", - payment_collection: expect.objectContaining({ - id: expect.any(String), - amount: 0, - }), + id: expect.any(String), + amount: 0, }) ) }) + + it("should return an existing payment collection for the cart", async () => { + const region = await regionModule.create({ + name: "US", + currency_code: "usd", + }) + + const cart = await cartModule.create({ + currency_code: "usd", + region_id: region.id, + }) + + const firstCollection = ( + await api.post(`/store/payment-collections`, { + region_id: region.id, + cart_id: cart.id, + amount: 0, + currency_code: cart.currency_code, + }) + ).data.payment_collection + + const response = await api.post(`/store/payment-collections`, { + region_id: region.id, + cart_id: cart.id, + amount: 0, + currency_code: cart.currency_code, + }) + + expect(response.status).toEqual(200) + expect(response.data.payment_collection.id).toEqual( + firstCollection.id + ) + }) + + it("should create a new payment collection for a new cart", async () => { + const region = await regionModule.create({ + name: "US", + currency_code: "usd", + }) + + const firstCart = await cartModule.create({ + currency_code: "usd", + region_id: region.id, + }) + + const secondCart = await cartModule.create({ + currency_code: "usd", + region_id: region.id, + }) + + const firstCollection = ( + await api.post(`/store/payment-collections`, { + region_id: region.id, + cart_id: firstCart.id, + amount: 0, + currency_code: firstCart.currency_code, + }) + ).data.payment_collection + + const secondCollection = ( + await api.post(`/store/payment-collections`, { + region_id: region.id, + cart_id: secondCart.id, + amount: 0, + currency_code: secondCart.currency_code, + }) + ).data.payment_collection + + expect(firstCollection.id).toBeTruthy() + expect(firstCollection.id).not.toEqual(secondCollection.id) + }) }) describe("POST /store/carts/:id/taxes", () => { @@ -1363,8 +1433,13 @@ medusaIntegrationTestRunner({ rules: [ { operator: RuleOperator.EQ, - attribute: "shipping_address.country_code", - value: "us", + attribute: "is_return", + value: "false", + }, + { + operator: RuleOperator.EQ, + attribute: "enabled_in_store", + value: "true", }, ], }) diff --git a/packages/core/core-flows/src/definition/cart/steps/validate-cart-shipping-options.ts b/packages/core/core-flows/src/definition/cart/steps/validate-cart-shipping-options.ts index 6468ae503a..ff430926bc 100644 --- a/packages/core/core-flows/src/definition/cart/steps/validate-cart-shipping-options.ts +++ b/packages/core/core-flows/src/definition/cart/steps/validate-cart-shipping-options.ts @@ -5,6 +5,10 @@ import { createStep, StepResponse } from "@medusajs/workflows-sdk" interface StepInput { cart: CartDTO + shippingOptionsContext: { + enabled_in_store?: "true" | "false" + is_return?: "true" | "false" + } option_ids: string[] } @@ -13,7 +17,7 @@ export const validateCartShippingOptionsStepId = export const validateCartShippingOptionsStep = createStep( validateCartShippingOptionsStepId, async (data: StepInput, { container }) => { - const { option_ids: optionIds = [], cart } = data + const { option_ids: optionIds = [], cart, shippingOptionsContext } = data if (!optionIds.length) { return new StepResponse(void 0) @@ -27,7 +31,7 @@ export const validateCartShippingOptionsStep = createStep( await fulfillmentModule.listShippingOptionsForContext( { id: optionIds, - context: { ...cart }, + context: shippingOptionsContext, address: { country_code: cart.shipping_address?.country_code, province_code: cart.shipping_address?.province, diff --git a/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts index 192d22ac99..b5ef6dd0f1 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts @@ -40,6 +40,7 @@ export const addShippingMethodToWorkflow = createWorkflow( validateCartShippingOptionsStep({ option_ids: optionIds, cart, + shippingOptionsContext: { is_return: "false", enabled_in_store: "true" }, }) const shippingOptions = useRemoteQueryStep({ diff --git a/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts b/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts index 377039d72f..8b4d8eb267 100644 --- a/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts +++ b/packages/core/core-flows/src/definition/cart/workflows/list-shipping-options-for-cart.ts @@ -46,6 +46,10 @@ export const listShippingOptionsForCartWorkflow = createWorkflow( variables: { id: input.sales_channel_id, "stock_locations.fulfillment_sets.service_zones.shipping_options": { + context: { + is_return: "false", + enabled_in_store: "true", + }, filters: { address: { city: input.shipping_address?.city, diff --git a/packages/medusa/src/api-v2/middlewares.ts b/packages/medusa/src/api-v2/middlewares.ts index a0484015db..c53676fdba 100644 --- a/packages/medusa/src/api-v2/middlewares.ts +++ b/packages/medusa/src/api-v2/middlewares.ts @@ -40,6 +40,9 @@ import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares" import { storeProductRoutesMiddlewares } from "./store/products/middlewares" import { storeProductCategoryRoutesMiddlewares } from "./store/product-categories/middlewares" import { storeRegionRoutesMiddlewares } from "./store/regions/middlewares" +import { storePaymentProvidersMiddlewares } from "./store/payment-providers/middlewares" +import { storePaymentCollectionsMiddlewares } from "./store/payment-collections/middlewares" +import { storeShippingOptionRoutesMiddlewares } from "./store/shipping-options/middlewares" export const config: MiddlewaresConfig = { routes: [ @@ -52,6 +55,9 @@ export const config: MiddlewaresConfig = { ...storeCartRoutesMiddlewares, ...storeCollectionRoutesMiddlewares, ...storeProductCategoryRoutesMiddlewares, + ...storePaymentProvidersMiddlewares, + ...storeShippingOptionRoutesMiddlewares, + ...storePaymentCollectionsMiddlewares, ...authRoutesMiddlewares, ...adminWorkflowsExecutionsMiddlewares, ...storeRegionRoutesMiddlewares, diff --git a/packages/medusa/src/api-v2/store/carts/[id]/payment-collections/route.ts b/packages/medusa/src/api-v2/store/carts/[id]/payment-collections/route.ts deleted file mode 100644 index fd5ad696e4..0000000000 --- a/packages/medusa/src/api-v2/store/carts/[id]/payment-collections/route.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { createPaymentCollectionForCartWorkflow } from "@medusajs/core-flows" -import { MedusaRequest, MedusaResponse } from "../../../../../types/routing" -import { refetchCart } from "../../helpers" -import { StoreUpdateCartType } from "../../validators" - -export const POST = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - const workflow = createPaymentCollectionForCartWorkflow(req.scope) - let cart = await refetchCart(req.params.id, req.scope, [ - "id", - "currency_code", - "region_id", - "total", - ]) - - const { errors } = await workflow.run({ - input: { - cart_id: req.params.id, - region_id: cart.region_id, - currency_code: cart.currency_code, - amount: cart.total ?? 0, // TODO: This should be calculated from the cart when totals decoration is introduced - }, - throwOnError: false, - }) - - if (Array.isArray(errors) && errors[0]) { - throw errors[0].error - } - - cart = await refetchCart( - req.params.id, - req.scope, - req.remoteQueryConfig.fields - ) - - res.status(200).json({ cart }) -} diff --git a/packages/medusa/src/api-v2/store/carts/middlewares.ts b/packages/medusa/src/api-v2/store/carts/middlewares.ts index 1dc2e89724..f4fa16a1a1 100644 --- a/packages/medusa/src/api-v2/store/carts/middlewares.ts +++ b/packages/medusa/src/api-v2/store/carts/middlewares.ts @@ -114,17 +114,6 @@ export const storeCartRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, - { - method: ["POST"], - matcher: "/store/carts/:id/payment-collections", - middlewares: [ - validateAndTransformBody(StoreUpdateCart), - validateAndTransformQuery( - StoreGetCartsCart, - QueryConfig.retrieveTransformQueryConfig - ), - ], - }, { method: ["POST"], matcher: "/store/carts/:id/shipping-methods", diff --git a/packages/medusa/src/api-v2/store/carts/query-config.ts b/packages/medusa/src/api-v2/store/carts/query-config.ts index ccd66c5e5e..5ae34de5c7 100644 --- a/packages/medusa/src/api-v2/store/carts/query-config.ts +++ b/packages/medusa/src/api-v2/store/carts/query-config.ts @@ -56,11 +56,11 @@ export const defaultStoreCartFields = [ "shipping_methods.tax_lines.code", "shipping_methods.tax_lines.rate", "shipping_methods.tax_lines.provider_id", - "shipping_methods.shipping_option_id", "shipping_methods.amount", "shipping_methods.adjustments.id", "shipping_methods.adjustments.code", "shipping_methods.adjustments.amount", + "shipping_methods.shipping_option_id", "shipping_address.id", "shipping_address.first_name", "shipping_address.last_name", @@ -86,18 +86,16 @@ export const defaultStoreCartFields = [ "region.name", "region.currency_code", "region.automatic_taxes", + "*region.countries", "sales_channel_id", // TODO: To be updated when payment sessions are introduces in the Rest API "payment_collection.id", "payment_collection.amount", - "payment_collection.payment_sessions", + "*payment_collection.payment_sessions", ] -const allowedFields = [...defaultStoreCartFields] - export const retrieveTransformQueryConfig = { defaults: defaultStoreCartFields, - allowed: allowedFields, isList: false, } diff --git a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/middlewares.ts b/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/middlewares.ts deleted file mode 100644 index 3b8aace995..0000000000 --- a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/middlewares.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { transformBody } from "../../../../../api/middlewares" -import { MiddlewareRoute } from "../../../../../loaders/helpers/routing/types" -import { StorePostPaymentCollectionsPaymentSessionReq } from "./validators" - -export const storeCartRoutesMiddlewares: MiddlewareRoute[] = [ - { - method: ["POST"], - matcher: "/store/payment-collections/:id/payment-sessions", - middlewares: [transformBody(StorePostPaymentCollectionsPaymentSessionReq)], - }, -] diff --git a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/query-config.ts b/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/query-config.ts deleted file mode 100644 index 96ad0d6a49..0000000000 --- a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/query-config.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const defaultStorePaymentCollectionFields = [ - "id", - "currency_code", - "amount", - "payment_sessions", - "payment_sessions.id", - "payment_sessions.amount", - "payment_sessions.provider_id", -] - -export const defaultStorePaymentCollectionRelations = ["payment_sessions"] - -export const allowedStorePaymentCollectionRelations = ["payment_sessions"] - -export const retrieveTransformQueryConfig = { - defaultFields: defaultStorePaymentCollectionFields, - defaultRelations: defaultStorePaymentCollectionRelations, - allowedRelations: allowedStorePaymentCollectionRelations, - isList: false, -} diff --git a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/route.ts b/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/route.ts index 066c8a3e92..c8df61dab9 100644 --- a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/route.ts +++ b/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/route.ts @@ -1,36 +1,33 @@ import { createPaymentSessionsWorkflow } from "@medusajs/core-flows" -import { remoteQueryObjectFromString } from "@medusajs/utils" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../../types/routing" -import { defaultStorePaymentCollectionFields } from "./query-config" -import { StorePostPaymentCollectionsPaymentSessionReq } from "./validators" +import { StoreCreatePaymentSessionType } from "../../validators" +import { refetchPaymentCollection } from "../../helpers" export const POST = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const { id } = req.params - const { context = {}, provider_id, data } = req.body + const collectionId = req.params.id + const { context = {}, data, provider_id } = req.body // If the customer is logged in, we auto-assign them to the payment collection if (req.auth?.actor_id) { - context.customer = { - ...context.customer, + ;(context as any).customer = { id: req.auth.actor_id, } } - const workflowInput = { - payment_collection_id: id, + payment_collection_id: collectionId, provider_id: provider_id, data, context, } const { errors } = await createPaymentSessionsWorkflow(req.scope).run({ - input: workflowInput as any, + input: workflowInput, throwOnError: false, }) @@ -38,15 +35,11 @@ export const POST = async ( throw errors[0].error } - const remoteQuery = req.scope.resolve("remoteQuery") + const paymentCollection = await refetchPaymentCollection( + collectionId, + req.scope, + req.remoteQueryConfig.fields + ) - const query = remoteQueryObjectFromString({ - entryPoint: "payment_collection", - variables: { cart: { id } }, - fields: defaultStorePaymentCollectionFields, - }) - - const [result] = await remoteQuery(query) - - res.status(200).json({ payment_collection: result }) + res.status(200).json({ payment_collection: paymentCollection }) } diff --git a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/validators.ts b/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/validators.ts deleted file mode 100644 index 0a7c21c873..0000000000 --- a/packages/medusa/src/api-v2/store/payment-collections/[id]/payment-sessions/validators.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PaymentProviderContext } from "@medusajs/types" - -export class StorePostPaymentCollectionsPaymentSessionReq { - provider_id: string - context?: PaymentProviderContext - data?: Record -} diff --git a/packages/medusa/src/api-v2/store/payment-collections/helpers.ts b/packages/medusa/src/api-v2/store/payment-collections/helpers.ts new file mode 100644 index 0000000000..fd537a8b89 --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-collections/helpers.ts @@ -0,0 +1,10 @@ +import { MedusaContainer, PaymentCollectionDTO } from "@medusajs/types" +import { refetchEntity } from "../../utils/refetch-entity" + +export const refetchPaymentCollection = async ( + id: string, + scope: MedusaContainer, + fields: string[] +): Promise => { + return refetchEntity("payment_collection", id, scope, fields) +} diff --git a/packages/medusa/src/api-v2/store/payment-collections/middlewares.ts b/packages/medusa/src/api-v2/store/payment-collections/middlewares.ts new file mode 100644 index 0000000000..5d438072ee --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-collections/middlewares.ts @@ -0,0 +1,44 @@ +import { MiddlewareRoute } from "../../../types/middlewares" +import { authenticate } from "../../../utils/authenticate-middleware" +import { validateAndTransformBody } from "../../utils/validate-body" +import { validateAndTransformQuery } from "../../utils/validate-query" +import * as queryConfig from "./query-config" +import { + StoreGetPaymentCollectionParams, + StoreCreatePaymentCollection, + StoreCreatePaymentSession, +} from "./validators" + +export const storePaymentCollectionsMiddlewares: MiddlewareRoute[] = [ + { + method: "ALL", + matcher: "/store/payment-collections*", + middlewares: [ + authenticate("store", ["session", "bearer"], { + allowUnauthenticated: true, + }), + ], + }, + { + method: ["POST"], + matcher: "/store/payment-collections", + middlewares: [ + validateAndTransformBody(StoreCreatePaymentCollection), + validateAndTransformQuery( + StoreGetPaymentCollectionParams, + queryConfig.retrievePaymentCollectionTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/store/payment-collections/:id/payment-sessions", + middlewares: [ + validateAndTransformBody(StoreCreatePaymentSession), + validateAndTransformQuery( + StoreGetPaymentCollectionParams, + queryConfig.retrievePaymentCollectionTransformQueryConfig + ), + ], + }, +] diff --git a/packages/medusa/src/api-v2/store/payment-collections/query-config.ts b/packages/medusa/src/api-v2/store/payment-collections/query-config.ts new file mode 100644 index 0000000000..d709e3f1fe --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-collections/query-config.ts @@ -0,0 +1,14 @@ +export const defaultPaymentCollectionFields = [ + "id", + "currency_code", + "amount", + "payment_sessions", + "payment_sessions.id", + "payment_sessions.amount", + "payment_sessions.provider_id", +] + +export const retrievePaymentCollectionTransformQueryConfig = { + defaults: defaultPaymentCollectionFields, + isList: false, +} diff --git a/packages/medusa/src/api-v2/store/payment-collections/route.ts b/packages/medusa/src/api-v2/store/payment-collections/route.ts new file mode 100644 index 0000000000..a1cacff58b --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-collections/route.ts @@ -0,0 +1,55 @@ +import { createPaymentCollectionForCartWorkflow } from "@medusajs/core-flows" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "../../../types/routing" +import { StoreCreatePaymentCollectionType } from "./validators" + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const { cart_id } = req.body + + // We can potentially refactor the workflow to behave more like an upsert and return an existing collection if there is one. + const [cartCollectionRelation] = await remoteQuery( + remoteQueryObjectFromString({ + entryPoint: "cart_payment_collection", + variables: { filters: { cart_id } }, + fields: req.remoteQueryConfig.fields.map( + (f) => `payment_collection.${f}` + ), + }) + ) + let paymentCollection = cartCollectionRelation?.payment_collection + + if (!paymentCollection) { + const { errors } = await createPaymentCollectionForCartWorkflow( + req.scope + ).run({ + input: req.body, + }) + + if (Array.isArray(errors) && errors[0]) { + throw errors[0].error + } + + const [cartCollectionRelation] = await remoteQuery( + remoteQueryObjectFromString({ + entryPoint: "cart_payment_collection", + variables: { filters: { cart_id } }, + fields: req.remoteQueryConfig.fields.map( + (f) => `payment_collection.${f}` + ), + }) + ) + paymentCollection = cartCollectionRelation?.payment_collection + } + + res.status(200).json({ payment_collection: paymentCollection }) +} diff --git a/packages/medusa/src/api-v2/store/payment-collections/validators.ts b/packages/medusa/src/api-v2/store/payment-collections/validators.ts new file mode 100644 index 0000000000..d7b2514bc1 --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-collections/validators.ts @@ -0,0 +1,30 @@ +import { z } from "zod" +import { createSelectParams } from "../../utils/validators" + +export type StoreGetPaymentCollectionParamsType = z.infer< + typeof StoreGetPaymentCollectionParams +> +export const StoreGetPaymentCollectionParams = createSelectParams() + +export type StoreCreatePaymentSessionType = z.infer< + typeof StoreCreatePaymentSession +> +export const StoreCreatePaymentSession = z + .object({ + provider_id: z.string(), + context: z.record(z.unknown()).optional(), + data: z.record(z.unknown()).optional(), + }) + .strict() + +export type StoreCreatePaymentCollectionType = z.infer< + typeof StoreCreatePaymentCollection +> +export const StoreCreatePaymentCollection = z + .object({ + cart_id: z.string(), + region_id: z.string(), + currency_code: z.string(), + amount: z.number(), + }) + .strict() diff --git a/packages/medusa/src/api-v2/store/payment-providers/middlewares.ts b/packages/medusa/src/api-v2/store/payment-providers/middlewares.ts new file mode 100644 index 0000000000..382758ef12 --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-providers/middlewares.ts @@ -0,0 +1,17 @@ +import { MiddlewareRoute } from "../../../types/middlewares" +import { validateAndTransformQuery } from "../../utils/validate-query" +import * as queryConfig from "./query-config" +import { StoreGetPaymentProvidersParams } from "./validators" + +export const storePaymentProvidersMiddlewares: MiddlewareRoute[] = [ + { + method: ["GET"], + matcher: "/store/payment-providers", + middlewares: [ + validateAndTransformQuery( + StoreGetPaymentProvidersParams, + queryConfig.listTransformPaymentProvidersQueryConfig + ), + ], + }, +] diff --git a/packages/medusa/src/api-v2/store/payment-providers/query-config.ts b/packages/medusa/src/api-v2/store/payment-providers/query-config.ts new file mode 100644 index 0000000000..f35cb421c2 --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-providers/query-config.ts @@ -0,0 +1,6 @@ +export const defaultAdminPaymentProviderFields = ["id", "is_enabled"] + +export const listTransformPaymentProvidersQueryConfig = { + defaults: defaultAdminPaymentProviderFields, + isList: true, +} diff --git a/packages/medusa/src/api-v2/store/payment-providers/route.ts b/packages/medusa/src/api-v2/store/payment-providers/route.ts new file mode 100644 index 0000000000..a9897aef31 --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-providers/route.ts @@ -0,0 +1,50 @@ +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "../../../types/routing" +import { + ContainerRegistrationKeys, + MedusaError, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { StoreGetPaymentProvidersParamsType } from "./validators" + +// TODO: Add more fields to provider, such as default name and maybe logo. +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + if (!req.filterableFields.region_id) { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + "You must provide the region_id to list payment providers" + ) + } + + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const queryObject = remoteQueryObjectFromString({ + entryPoint: "region_payment_provider", + variables: { + filters: { + region_id: req.filterableFields.region_id, + }, + ...req.remoteQueryConfig.pagination, + }, + fields: req.remoteQueryConfig.fields.map((f) => `payment_provider.${f}`), + }) + + const { rows: regionPaymentProvidersRelation, metadata } = await remoteQuery( + queryObject + ) + + const paymentProviders = regionPaymentProvidersRelation.map( + (relation) => relation.payment_provider + ) + + res.json({ + payment_providers: paymentProviders, + count: metadata.count, + offset: metadata.skip, + limit: metadata.take, + }) +} diff --git a/packages/medusa/src/api-v2/store/payment-providers/validators.ts b/packages/medusa/src/api-v2/store/payment-providers/validators.ts new file mode 100644 index 0000000000..72b9e395ef --- /dev/null +++ b/packages/medusa/src/api-v2/store/payment-providers/validators.ts @@ -0,0 +1,18 @@ +import { z } from "zod" +import { createFindParams } from "../../utils/validators" + +export type StoreGetPaymentProvidersParamsType = z.infer< + typeof StoreGetPaymentProvidersParams +> +export const StoreGetPaymentProvidersParams = createFindParams({ + limit: 20, + offset: 0, +}).merge( + z.object({ + region_id: z.string(), + id: z.union([z.string(), z.array(z.string())]).optional(), + is_enabled: z.boolean().optional(), + $and: z.lazy(() => StoreGetPaymentProvidersParams.array()).optional(), + $or: z.lazy(() => StoreGetPaymentProvidersParams.array()).optional(), + }) +) diff --git a/packages/medusa/src/api-v2/store/shipping-options/middlewares.ts b/packages/medusa/src/api-v2/store/shipping-options/middlewares.ts index 58deccddbb..e042f97bb7 100644 --- a/packages/medusa/src/api-v2/store/shipping-options/middlewares.ts +++ b/packages/medusa/src/api-v2/store/shipping-options/middlewares.ts @@ -1,9 +1,17 @@ import { MiddlewareRoute } from "../../../loaders/helpers/routing/types" +import { validateAndTransformQuery } from "../../utils/validate-query" +import { listTransformQueryConfig } from "./query-config" +import { StoreGetShippingOptions } from "./validators" export const storeShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [ { method: ["GET"], - matcher: "/store/shipping-options/:cart_id", - middlewares: [], + matcher: "/store/shipping-options", + middlewares: [ + validateAndTransformQuery( + StoreGetShippingOptions, + listTransformQueryConfig + ), + ], }, ] diff --git a/packages/medusa/src/api-v2/store/shipping-options/[cart_id]/route.ts b/packages/medusa/src/api-v2/store/shipping-options/route.ts similarity index 78% rename from packages/medusa/src/api-v2/store/shipping-options/[cart_id]/route.ts rename to packages/medusa/src/api-v2/store/shipping-options/route.ts index f2cdf2bd18..4f2e98136d 100644 --- a/packages/medusa/src/api-v2/store/shipping-options/[cart_id]/route.ts +++ b/packages/medusa/src/api-v2/store/shipping-options/route.ts @@ -1,10 +1,17 @@ import { listShippingOptionsForCartWorkflow } from "@medusajs/core-flows" import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { ICartModuleService } from "@medusajs/types" -import { MedusaRequest, MedusaResponse } from "../../../../types/routing" +import { MedusaRequest, MedusaResponse } from "../../../types/routing" +import { MedusaError } from "@medusajs/utils" export const GET = async (req: MedusaRequest, res: MedusaResponse) => { - const { cart_id } = req.params + const { cart_id } = req.filterableFields as { cart_id: string } + if (!cart_id) { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + "You must provide the cart_id to list shipping options" + ) + } const cartService = req.scope.resolve( ModuleRegistrationName.CART diff --git a/packages/medusa/src/api-v2/store/shipping-options/validators.ts b/packages/medusa/src/api-v2/store/shipping-options/validators.ts new file mode 100644 index 0000000000..464c81dada --- /dev/null +++ b/packages/medusa/src/api-v2/store/shipping-options/validators.ts @@ -0,0 +1,16 @@ +import { z } from "zod" +import { createFindParams } from "../../utils/validators" + +export type StoreGetShippingOptionsType = z.infer< + typeof StoreGetShippingOptions +> +export const StoreGetShippingOptions = createFindParams({ + limit: 20, + offset: 0, +}).merge( + z.object({ + cart_id: z.string(), + $and: z.lazy(() => StoreGetShippingOptions.array()).optional(), + $or: z.lazy(() => StoreGetShippingOptions.array()).optional(), + }) +)