feat: Consolidate payment functions and handle sessions for storefront (#7231)

This commit is contained in:
Stevche Radevski
2024-05-06 13:14:40 +00:00
committed by GitHub
parent 16528c57b0
commit 4e12168dbd
25 changed files with 408 additions and 135 deletions
@@ -1455,8 +1455,13 @@ medusaIntegrationTestRunner({
rules: [ rules: [
{ {
operator: RuleOperator.EQ, operator: RuleOperator.EQ,
attribute: "shipping_address.province", attribute: "is_return",
value: "ny", value: "false",
},
{
operator: RuleOperator.EQ,
attribute: "enabled_in_store",
value: "true",
}, },
], ],
}) })
@@ -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 () => { it("should create a payment collection for the cart", async () => {
const region = await regionModule.create({ const region = await regionModule.create({
name: "US", name: "US",
@@ -1209,22 +1209,92 @@ medusaIntegrationTestRunner({
region_id: region.id, region_id: region.id,
}) })
const response = await api.post( const response = await api.post(`/store/payment-collections`, {
`/store/carts/${cart.id}/payment-collections` region_id: region.id,
) cart_id: cart.id,
amount: 0,
currency_code: cart.currency_code,
})
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.payment_collection).toEqual(
expect.objectContaining({ expect.objectContaining({
id: cart.id, id: expect.any(String),
currency_code: "usd", amount: 0,
payment_collection: expect.objectContaining({
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", () => { describe("POST /store/carts/:id/taxes", () => {
@@ -1363,8 +1433,13 @@ medusaIntegrationTestRunner({
rules: [ rules: [
{ {
operator: RuleOperator.EQ, operator: RuleOperator.EQ,
attribute: "shipping_address.country_code", attribute: "is_return",
value: "us", value: "false",
},
{
operator: RuleOperator.EQ,
attribute: "enabled_in_store",
value: "true",
}, },
], ],
}) })
@@ -5,6 +5,10 @@ import { createStep, StepResponse } from "@medusajs/workflows-sdk"
interface StepInput { interface StepInput {
cart: CartDTO cart: CartDTO
shippingOptionsContext: {
enabled_in_store?: "true" | "false"
is_return?: "true" | "false"
}
option_ids: string[] option_ids: string[]
} }
@@ -13,7 +17,7 @@ export const validateCartShippingOptionsStepId =
export const validateCartShippingOptionsStep = createStep( export const validateCartShippingOptionsStep = createStep(
validateCartShippingOptionsStepId, validateCartShippingOptionsStepId,
async (data: StepInput, { container }) => { async (data: StepInput, { container }) => {
const { option_ids: optionIds = [], cart } = data const { option_ids: optionIds = [], cart, shippingOptionsContext } = data
if (!optionIds.length) { if (!optionIds.length) {
return new StepResponse(void 0) return new StepResponse(void 0)
@@ -27,7 +31,7 @@ export const validateCartShippingOptionsStep = createStep(
await fulfillmentModule.listShippingOptionsForContext( await fulfillmentModule.listShippingOptionsForContext(
{ {
id: optionIds, id: optionIds,
context: { ...cart }, context: shippingOptionsContext,
address: { address: {
country_code: cart.shipping_address?.country_code, country_code: cart.shipping_address?.country_code,
province_code: cart.shipping_address?.province, province_code: cart.shipping_address?.province,
@@ -40,6 +40,7 @@ export const addShippingMethodToWorkflow = createWorkflow(
validateCartShippingOptionsStep({ validateCartShippingOptionsStep({
option_ids: optionIds, option_ids: optionIds,
cart, cart,
shippingOptionsContext: { is_return: "false", enabled_in_store: "true" },
}) })
const shippingOptions = useRemoteQueryStep({ const shippingOptions = useRemoteQueryStep({
@@ -46,6 +46,10 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
variables: { variables: {
id: input.sales_channel_id, id: input.sales_channel_id,
"stock_locations.fulfillment_sets.service_zones.shipping_options": { "stock_locations.fulfillment_sets.service_zones.shipping_options": {
context: {
is_return: "false",
enabled_in_store: "true",
},
filters: { filters: {
address: { address: {
city: input.shipping_address?.city, city: input.shipping_address?.city,
@@ -40,6 +40,9 @@ import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares"
import { storeProductRoutesMiddlewares } from "./store/products/middlewares" import { storeProductRoutesMiddlewares } from "./store/products/middlewares"
import { storeProductCategoryRoutesMiddlewares } from "./store/product-categories/middlewares" import { storeProductCategoryRoutesMiddlewares } from "./store/product-categories/middlewares"
import { storeRegionRoutesMiddlewares } from "./store/regions/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 = { export const config: MiddlewaresConfig = {
routes: [ routes: [
@@ -52,6 +55,9 @@ export const config: MiddlewaresConfig = {
...storeCartRoutesMiddlewares, ...storeCartRoutesMiddlewares,
...storeCollectionRoutesMiddlewares, ...storeCollectionRoutesMiddlewares,
...storeProductCategoryRoutesMiddlewares, ...storeProductCategoryRoutesMiddlewares,
...storePaymentProvidersMiddlewares,
...storeShippingOptionRoutesMiddlewares,
...storePaymentCollectionsMiddlewares,
...authRoutesMiddlewares, ...authRoutesMiddlewares,
...adminWorkflowsExecutionsMiddlewares, ...adminWorkflowsExecutionsMiddlewares,
...storeRegionRoutesMiddlewares, ...storeRegionRoutesMiddlewares,
@@ -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<StoreUpdateCartType>,
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 })
}
@@ -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"], method: ["POST"],
matcher: "/store/carts/:id/shipping-methods", matcher: "/store/carts/:id/shipping-methods",
@@ -56,11 +56,11 @@ export const defaultStoreCartFields = [
"shipping_methods.tax_lines.code", "shipping_methods.tax_lines.code",
"shipping_methods.tax_lines.rate", "shipping_methods.tax_lines.rate",
"shipping_methods.tax_lines.provider_id", "shipping_methods.tax_lines.provider_id",
"shipping_methods.shipping_option_id",
"shipping_methods.amount", "shipping_methods.amount",
"shipping_methods.adjustments.id", "shipping_methods.adjustments.id",
"shipping_methods.adjustments.code", "shipping_methods.adjustments.code",
"shipping_methods.adjustments.amount", "shipping_methods.adjustments.amount",
"shipping_methods.shipping_option_id",
"shipping_address.id", "shipping_address.id",
"shipping_address.first_name", "shipping_address.first_name",
"shipping_address.last_name", "shipping_address.last_name",
@@ -86,18 +86,16 @@ export const defaultStoreCartFields = [
"region.name", "region.name",
"region.currency_code", "region.currency_code",
"region.automatic_taxes", "region.automatic_taxes",
"*region.countries",
"sales_channel_id", "sales_channel_id",
// TODO: To be updated when payment sessions are introduces in the Rest API // TODO: To be updated when payment sessions are introduces in the Rest API
"payment_collection.id", "payment_collection.id",
"payment_collection.amount", "payment_collection.amount",
"payment_collection.payment_sessions", "*payment_collection.payment_sessions",
] ]
const allowedFields = [...defaultStoreCartFields]
export const retrieveTransformQueryConfig = { export const retrieveTransformQueryConfig = {
defaults: defaultStoreCartFields, defaults: defaultStoreCartFields,
allowed: allowedFields,
isList: false, isList: false,
} }
@@ -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)],
},
]
@@ -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,
}
@@ -1,36 +1,33 @@
import { createPaymentSessionsWorkflow } from "@medusajs/core-flows" import { createPaymentSessionsWorkflow } from "@medusajs/core-flows"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "../../../../../types/routing" } from "../../../../../types/routing"
import { defaultStorePaymentCollectionFields } from "./query-config" import { StoreCreatePaymentSessionType } from "../../validators"
import { StorePostPaymentCollectionsPaymentSessionReq } from "./validators" import { refetchPaymentCollection } from "../../helpers"
export const POST = async ( export const POST = async (
req: AuthenticatedMedusaRequest<StorePostPaymentCollectionsPaymentSessionReq>, req: AuthenticatedMedusaRequest<StoreCreatePaymentSessionType>,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const { id } = req.params const collectionId = req.params.id
const { context = {}, provider_id, data } = req.body const { context = {}, data, provider_id } = req.body
// If the customer is logged in, we auto-assign them to the payment collection // If the customer is logged in, we auto-assign them to the payment collection
if (req.auth?.actor_id) { if (req.auth?.actor_id) {
context.customer = { ;(context as any).customer = {
...context.customer,
id: req.auth.actor_id, id: req.auth.actor_id,
} }
} }
const workflowInput = { const workflowInput = {
payment_collection_id: id, payment_collection_id: collectionId,
provider_id: provider_id, provider_id: provider_id,
data, data,
context, context,
} }
const { errors } = await createPaymentSessionsWorkflow(req.scope).run({ const { errors } = await createPaymentSessionsWorkflow(req.scope).run({
input: workflowInput as any, input: workflowInput,
throwOnError: false, throwOnError: false,
}) })
@@ -38,15 +35,11 @@ export const POST = async (
throw errors[0].error throw errors[0].error
} }
const remoteQuery = req.scope.resolve("remoteQuery") const paymentCollection = await refetchPaymentCollection(
collectionId,
req.scope,
req.remoteQueryConfig.fields
)
const query = remoteQueryObjectFromString({ res.status(200).json({ payment_collection: paymentCollection })
entryPoint: "payment_collection",
variables: { cart: { id } },
fields: defaultStorePaymentCollectionFields,
})
const [result] = await remoteQuery(query)
res.status(200).json({ payment_collection: result })
} }
@@ -1,7 +0,0 @@
import { PaymentProviderContext } from "@medusajs/types"
export class StorePostPaymentCollectionsPaymentSessionReq {
provider_id: string
context?: PaymentProviderContext
data?: Record<string, unknown>
}
@@ -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<PaymentCollectionDTO> => {
return refetchEntity("payment_collection", id, scope, fields)
}
@@ -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
),
],
},
]
@@ -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,
}
@@ -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<StoreCreatePaymentCollectionType>,
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 })
}
@@ -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()
@@ -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
),
],
},
]
@@ -0,0 +1,6 @@
export const defaultAdminPaymentProviderFields = ["id", "is_enabled"]
export const listTransformPaymentProvidersQueryConfig = {
defaults: defaultAdminPaymentProviderFields,
isList: true,
}
@@ -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<StoreGetPaymentProvidersParamsType>,
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,
})
}
@@ -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(),
})
)
@@ -1,9 +1,17 @@
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types" 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[] = [ export const storeShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
{ {
method: ["GET"], method: ["GET"],
matcher: "/store/shipping-options/:cart_id", matcher: "/store/shipping-options",
middlewares: [], middlewares: [
validateAndTransformQuery(
StoreGetShippingOptions,
listTransformQueryConfig
),
],
}, },
] ]
@@ -1,10 +1,17 @@
import { listShippingOptionsForCartWorkflow } from "@medusajs/core-flows" import { listShippingOptionsForCartWorkflow } from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { ICartModuleService } from "@medusajs/types" 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) => { 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<ICartModuleService>( const cartService = req.scope.resolve<ICartModuleService>(
ModuleRegistrationName.CART ModuleRegistrationName.CART
@@ -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(),
})
)