Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/three-adults-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@medusajs/core-flows": patch
"@medusajs/types": patch
---

feat(core-flows,types): pass cart in create payment session context
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
createPaymentSessionsWorkflow,
createPaymentSessionsWorkflowId,
} from "@medusajs/core-flows"
import { ICartModuleService, IPaymentModuleService, IRegionModuleService } from "@medusajs/types"
import { Modules } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
ICustomerModuleService,
Expand All @@ -21,20 +23,25 @@ medusaIntegrationTestRunner({
let appContainer
let paymentModule: IPaymentModuleService
let regionModule: IRegionModuleService
let cartModule: ICartModuleService
let remoteLink
let customerModule: ICustomerModuleService
let query

beforeAll(async () => {
appContainer = getContainer()
paymentModule = appContainer.resolve(Modules.PAYMENT)
regionModule = appContainer.resolve(Modules.REGION)
cartModule = appContainer.resolve(Modules.CART)
remoteLink = appContainer.resolve("remoteLink")
customerModule = appContainer.resolve(Modules.CUSTOMER)
query = appContainer.resolve(ContainerRegistrationKeys.QUERY)
})

describe("createPaymentSessionWorkflow", () => {
describe("createPaymentSessionsWorkflow", () => {
let region
let paymentCollection
let cart
let customer

beforeEach(async () => {
Expand All @@ -43,11 +50,31 @@ medusaIntegrationTestRunner({
name: "US",
})

cart = await cartModule.createCarts({
region_id: region.id,
currency_code: "usd",
items: [
{
quantity: 1,
unit_price: 1000,
title: "Test Item",
}
]
})

paymentCollection = await paymentModule.createPaymentCollections({
currency_code: "usd",
amount: 1000,
})

await remoteLink.create({
[Modules.CART]: {
cart_id: cart.id,
},
[Modules.PAYMENT]: {
payment_collection_id: paymentCollection.id,
},

customer = await customerModule.createCustomers({
email: "test@test.com",
first_name: "Test",
Expand Down Expand Up @@ -82,6 +109,28 @@ medusaIntegrationTestRunner({
amount: 1000,
currency_code: "usd",
provider_id: "pp_system_default",
context: expect.objectContaining({
cart: expect.objectContaining({
id: cart.id,
currency_code: "usd",
items: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
tax_lines: expect.arrayContaining([]),
})
]),
total: 1000,
subtotal: 1000,
tax_total: 0,
discount_total: 0,
discount_tax_total: 0,
shipping_total: 0,
shipping_subtotal: 0,
shipping_tax_total: 0,
item_total: 1000,
item_subtotal: 1000,
}),
}),
}),
]),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,28 @@ export const createPaymentSessionsWorkflow = createWorkflow(
): WorkflowResponse<PaymentSessionDTO> => {
const paymentCollection = useRemoteQueryStep({
entry_point: "payment_collection",
fields: ["id", "amount", "currency_code", "payment_sessions.*"],
fields: [
"id",
"amount",
"currency_code",
"payment_sessions.*",
"cart.id",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: If I am not mistaken, this workflow can also be used to create payment sessions for orders. I wonder if we should reconsider the approach here to account for this?

One idea that comes to mind would be to:

  1. Allow passing cart_id and order_id to the workflow
  2. Fetch the data based on which of these was passed
  3. Pass cart_or_order in the context passed to the provider

Not loving it, but this was the first approach that came to mind, so mainly sharing to start the discussion.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that makes sense, but maybe we can have separate cart and order properties in the context? They're both optional, so we don't guarantee they're passed anyway and that would allow us to account for other use cases in the future.

"cart.currency_code",
"cart.items.*",
"cart.items.tax_lines.*",
"cart.total",
"cart.subtotal",
"cart.tax_total",
"cart.discount_total",
"cart.discount_tax_total",
"cart.gift_card_total",
"cart.gift_card_tax_total",
"cart.shipping_total",
"cart.shipping_subtotal",
"cart.shipping_tax_total",
"cart.item_total",
"cart.item_subtotal",
],
variables: { id: input.payment_collection_id },
list: false,
}).config({ name: "get-payment-collection" })
Expand Down Expand Up @@ -175,6 +196,7 @@ export const createPaymentSessionsWorkflow = createWorkflow(
...data.input.context,
customer: data.paymentCustomer,
account_holder: data.accountHolder,
cart: data.paymentCollection.cart,
},
amount: data.paymentCollection.amount,
currency_code: data.paymentCollection.currency_code,
Expand Down
117 changes: 117 additions & 0 deletions packages/core/types/src/payment/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,118 @@ import { ProviderWebhookPayload } from "./mutations"
*/
export type PaymentAddressDTO = Partial<AddressDTO>

export type PaymentCartDTO = {
/**
* The ID of the cart in Medusa.
*/
id: string

/**
* The cart's currency code.
*
* @example
* usd
*/
currency_code: string

/**
* The cart's items.
*/
items: PaymentCartItemDTO[]

/**
* The total of the cart.
*/
total: BigNumberValue

/**
* The subtotal of the cart. (Excluding taxes)
*/
subtotal: BigNumberValue

/**
* The tax total of the cart.
*/
tax_total: BigNumberValue

/**
* The discount total of the cart.
*/
discount_total: BigNumberValue

/**
* The discount tax total of the cart.
*/
discount_tax_total: BigNumberValue

/**
* The gift card total of the cart.
*/
gift_card_total: BigNumberValue

/**
* The gift card tax total of the cart.
*/
gift_card_tax_total: BigNumberValue

/**
* The shipping total of the cart.
*/
shipping_total: BigNumberValue

/**
* The shipping subtotal of the cart.
*/
shipping_subtotal: BigNumberValue

/**
* The shipping tax total of the cart.
*/
shipping_tax_total: BigNumberValue
}

export type PaymentCartItemDTO = {
/**
* The ID of the line item.
*/
id: string

/**
* The title of the line item.
*/
title: string

/**
* The line item's quantity in the cart.
*/
quantity: BigNumberValue

/**
* Whether the line item price is tax inclusive.
*/
is_tax_inclusive: boolean

/**
* The unit price of the item.
*/
unit_price: BigNumberValue

/**
* The tax lines of the line item.
*/
tax_lines?: {
/**
* The tax line's total.
*/
total: BigNumberValue

/**
* The rate of the tax line.
*/
rate: number
}[]
}

/**
* The customer associated with the payment.
*/
Expand Down Expand Up @@ -81,6 +193,11 @@ export type PaymentProviderContext = {
* Idempotency key for the request, if the payment provider supports it. It will be ignored otherwise.
*/
idempotency_key?: string

/**
* The cart information in Medusa if available.
*/
cart?: PaymentCartDTO
}

export type PaymentProviderInput = {
Expand Down
Loading