diff --git a/.changeset/dull-sheep-raise.md b/.changeset/dull-sheep-raise.md new file mode 100644 index 0000000000..e5dbb0cc7c --- /dev/null +++ b/.changeset/dull-sheep-raise.md @@ -0,0 +1,7 @@ +--- +"medusa-payment-klarna": patch +"medusa-payment-paypal": patch +"medusa-payment-stripe": patch +--- + +Rely on cart totals in payment providers diff --git a/packages/medusa-payment-klarna/src/__mocks__/cart.js b/packages/medusa-payment-klarna/src/__mocks__/cart.js index b3eb771fe5..25fb145e28 100644 --- a/packages/medusa-payment-klarna/src/__mocks__/cart.js +++ b/packages/medusa-payment-klarna/src/__mocks__/cart.js @@ -16,6 +16,7 @@ export const carts = { title: "merge line", description: "This is a new line", thumbnail: "test-img-yeah.com/thumb", + tax_lines: [], content: [ { unit_price: 8, @@ -45,6 +46,7 @@ export const carts = { title: "merge line", description: "This is a new line", thumbnail: "test-img-yeah.com/thumb", + tax_lines: [], content: { unit_price: 10, variant: { @@ -62,6 +64,7 @@ export const carts = { { id: IdMap.getId("freeShipping"), name: "Free shipping", + tax_lines: [], data: { name: "test", }, diff --git a/packages/medusa-payment-klarna/src/api/routes/hooks/address.js b/packages/medusa-payment-klarna/src/api/routes/hooks/address.js index 1081b4fee3..09c2e58d63 100644 --- a/packages/medusa-payment-klarna/src/api/routes/hooks/address.js +++ b/packages/medusa-payment-klarna/src/api/routes/hooks/address.js @@ -2,107 +2,78 @@ export default async (req, res) => { // In Medusa, we store the cart id in merchant_data const { shipping_address, merchant_data } = req.body - try { - const manager = req.scope.resolve("manager") - const cartService = req.scope.resolve("cartService") - const klarnaProviderService = req.scope.resolve("pp_klarna") - const shippingProfileService = req.scope.resolve("shippingProfileService") + const cartService = req.scope.resolve("cartService") + const klarnaProviderService = req.scope.resolve("pp_klarna") + const shippingProfileService = req.scope.resolve("shippingProfileService") - const result = await manager.transaction("SERIALIZABLE", async (m) => { - const cart = await cartService.retrieve(merchant_data, { - select: ["subtotal"], - relations: [ - "shipping_address", - "billing_address", - "region", - "items", - "shipping_methods", - "shipping_methods.shipping_option", - "items.variant", - "items.variant.product", - ], - }) + if (shipping_address) { + const shippingAddress = { + first_name: shipping_address.given_name, + last_name: shipping_address.family_name, + address_1: shipping_address.street_address, + address_2: shipping_address.street_address2, + city: shipping_address.city, + country_code: shipping_address.country, + postal_code: shipping_address.postal_code, + phone: shipping_address.phone, + } - if (shipping_address) { - const shippingAddress = { - first_name: shipping_address.given_name, - last_name: shipping_address.family_name, - address_1: shipping_address.street_address, - address_2: shipping_address.street_address2, - city: shipping_address.city, - country_code: shipping_address.country, - postal_code: shipping_address.postal_code, - phone: shipping_address.phone, - } + let billingAddress = { + first_name: shipping_address.given_name, + last_name: shipping_address.family_name, + address_1: shipping_address.street_address, + address_2: shipping_address.street_address2, + city: shipping_address.city, + country_code: shipping_address.country, + postal_code: shipping_address.postal_code, + phone: shipping_address.phone, + } - let billingAddress = { - first_name: shipping_address.given_name, - last_name: shipping_address.family_name, - address_1: shipping_address.street_address, - address_2: shipping_address.street_address2, - city: shipping_address.city, - country_code: shipping_address.country, - postal_code: shipping_address.postal_code, - phone: shipping_address.phone, - } - - await cartService.update(cart.id, { - shipping_address: shippingAddress, - billing_address: billingAddress, - email: shipping_address.email, - }) - - const shippingOptions = await shippingProfileService.fetchCartOptions( - cart - ) - - if (shippingOptions?.length) { - const option = shippingOptions.find( - (o) => o.data && !o.data.require_drop_point - ) - await cartService - .withTransaction(m) - .addShippingMethod(cart.id, option.id, option.data) - } - - // Fetch and return updated Klarna order - const updatedCart = await cartService - .withTransaction(m) - .retrieve(cart.id, { - select: [ - "gift_card_total", - "subtotal", - "total", - "shipping_total", - "tax_total", - "discount_total", - "subtotal", - ], - relations: [ - "shipping_address", - "billing_address", - "region", - "shipping_methods", - "shipping_methods.shipping_option", - "items", - "items.variant", - "items.variant.product", - ], - }) - return klarnaProviderService.cartToKlarnaOrder(updatedCart) - } else { - return null - } + await cartService.update(merchant_data, { + shipping_address: shippingAddress, + billing_address: billingAddress, + email: shipping_address.email, }) - if (result) { - res.json(result) - return - } else { - res.sendStatus(400) - return + let cart = await cartService.retrieveWithTotals(merchant_data, { + relations: [ + "shipping_address", + "billing_address", + "region", + "shipping_methods", + "shipping_methods.shipping_option", + "items", + "items.variant", + "items.variant.product", + ], + }) + const shippingOptions = await shippingProfileService.fetchCartOptions(cart) + + if (shippingOptions?.length) { + const option = shippingOptions.find( + (o) => o.data && !o.data.require_drop_point + ) + if (option) { + await cartService.addShippingMethod(cart.id, option.id, option.data) + cart = await cartService.retrieveWithTotals(cart.id, { + relations: [ + "shipping_address", + "billing_address", + "region", + "shipping_methods", + "shipping_methods.shipping_option", + "items", + "items.variant", + "items.variant.product", + ], + }) + } } - } catch (error) { - throw error + + const order = await klarnaProviderService.cartToKlarnaOrder(cart) + res.json(order) + } else { + res.sendStatus(400) + return } } diff --git a/packages/medusa-payment-klarna/src/api/routes/hooks/shipping.js b/packages/medusa-payment-klarna/src/api/routes/hooks/shipping.js index b7bb449da5..523cf52c12 100644 --- a/packages/medusa-payment-klarna/src/api/routes/hooks/shipping.js +++ b/packages/medusa-payment-klarna/src/api/routes/hooks/shipping.js @@ -7,20 +7,23 @@ export default async (req, res) => { const klarnaProviderService = req.scope.resolve("pp_klarna") const shippingProfileService = req.scope.resolve("shippingProfileService") - const cart = await cartService.retrieve(merchant_data, { - select: ["subtotal"], - relations: [ - "shipping_address", - "billing_address", - "region", - "shipping_methods", - "shipping_methods.shipping_option", - "items", - "items.adjustments", - "items.variant", - "items.variant.product", - ], - }) + const cart = await cartService.retrieveWithTotals( + merchant_data, + { + relations: [ + "shipping_address", + "billing_address", + "region", + "shipping_methods", + "shipping_methods.shipping_option", + "items", + "items.adjustments", + "items.variant", + "items.variant.product", + ], + }, + { force_taxes: true } + ) let shippingOptions = await shippingProfileService.fetchCartOptions(cart) shippingOptions = shippingOptions.filter( @@ -35,28 +38,23 @@ export default async (req, res) => { } } - const newCart = await cartService.retrieve(cart.id, { - select: [ - "gift_card_total", - "subtotal", - "total", - "shipping_total", - "tax_total", - "discount_total", - "subtotal", - ], - relations: [ - "shipping_address", - "billing_address", - "shipping_methods", - "shipping_methods.shipping_option", - "region", - "items", - "items.adjustments", - "items.variant", - "items.variant.product", - ], - }) + const newCart = await cartService.retrieveWithTotals( + cart.id, + { + relations: [ + "shipping_address", + "billing_address", + "shipping_methods", + "shipping_methods.shipping_option", + "region", + "items", + "items.adjustments", + "items.variant", + "items.variant.product", + ], + }, + { force_taxes: true } + ) const order = await klarnaProviderService.cartToKlarnaOrder(newCart) diff --git a/packages/medusa-payment-klarna/src/services/klarna-provider.js b/packages/medusa-payment-klarna/src/services/klarna-provider.js index f9614dbd9b..5907c1da00 100644 --- a/packages/medusa-payment-klarna/src/services/klarna-provider.js +++ b/packages/medusa-payment-klarna/src/services/klarna-provider.js @@ -5,7 +5,7 @@ import { PaymentService } from "medusa-interfaces" class KlarnaProviderService extends PaymentService { static identifier = "klarna" - constructor({ shippingProfileService, totalsService }, options) { + constructor({ logger, shippingProfileService, totalsService }, options) { super() /** @@ -23,6 +23,7 @@ class KlarnaProviderService extends PaymentService { * } */ this.options_ = options + this.logger_ = logger /** @private @const {Klarna} */ this.klarna_ = axios.create({ @@ -53,22 +54,16 @@ class KlarnaProviderService extends PaymentService { // Withdraw discount from the total item amount const quantity = item.quantity - const totals = await this.totalsService_.getLineItemTotals(item, cart, { - include_tax: true, - }) - - const tax = - totals.tax_lines.reduce((acc, next) => acc + next.rate, 0) / 100 + const tax = item.tax_lines.reduce((acc, next) => acc + next.rate, 0) / 100 order_lines.push({ name: item.title, tax_rate: tax * 10000, quantity, - unit_price: Math.round(totals.original_total / item.quantity), - total_amount: totals.total - totals.gift_card_total, - total_tax_amount: totals.tax_total, - total_discount_amount: - totals.original_total - totals.total + totals.gift_card_total, + unit_price: Math.round(item.original_total / item.quantity), + total_amount: item.total, + total_tax_amount: item.tax_total, + total_discount_amount: item.original_total - item.total, }) } @@ -79,23 +74,15 @@ class KlarnaProviderService extends PaymentService { let taxRate = 0 if (cart.shipping_total > 0) { - for (const next of cart.shipping_methods) { - const totals = await this.totalsService_.getShippingMethodTotals( - next, - cart, - { - include_tax: true, - } - ) - + for (const method of cart.shipping_methods) { const methodTaxRate = - totals.tax_lines.reduce((acc, next) => acc + next.rate, 0) / 100 + method.tax_lines.reduce((acc, next) => acc + next.rate, 0) / 100 - name.push(next?.shipping_option.name) + name.push(method.shipping_option.name) - total += totals.total - taxRate += (totals.price / cart.shipping_total) * methodTaxRate - tax += totals.tax_total + total += method.total + taxRate += (method.price / cart.shipping_total) * methodTaxRate + tax += method.tax_total } } @@ -124,16 +111,17 @@ class KlarnaProviderService extends PaymentService { order.order_lines = await this.lineItemsToOrderLines_(cart) - if (gift_card_total && !region.gift_cards_taxable) { + if (gift_card_total) { + const taxRate = cart.gift_card_tax_total / cart.gift_card_total + order.order_lines.push({ - name: `Gift Card`, + name: "Gift Card", quantity: 1, type: "gift_card", - unit_price: 0, - total_discount_amount: gift_card_total, - tax_rate: 0, - total_amount: -gift_card_total, - total_tax_amount: 0, + unit_price: -1 * (cart.gift_card_total + cart.gift_card_tax_total), + tax_rate: Math.round(taxRate * 10000), + total_amount: -1 * (cart.gift_card_total + cart.gift_card_tax_total), + total_tax_amount: -1 * cart.gift_card_tax_total, }) } @@ -159,7 +147,7 @@ class KlarnaProviderService extends PaymentService { } order.order_amount = total - order.order_tax_amount = tax_total + order.order_tax_amount = tax_total - cart.gift_card_tax_total order.purchase_currency = region.currency_code.toUpperCase() order.merchant_urls = { @@ -183,21 +171,13 @@ class KlarnaProviderService extends PaymentService { // If the cart does not have shipping methods yet, preselect one from // shipping_options and set the selected shipping method if (cart.shipping_methods.length) { - const shipping_method = cart.shipping_methods[0] - const totals = await this.totalsService_.getShippingMethodTotals( - shipping_method, - cart, - { - include_tax: true, - } - ) - - const taxRate = totals.tax_total / (totals.total - totals.tax_total) + const method = cart.shipping_methods[0] + const taxRate = method.tax_total / (method.total - method.tax_total) order.selected_shipping_option = { - id: shipping_method.shipping_option.id, - name: shipping_method.shipping_option.name, - price: totals.total, - tax_amount: total.tax_total, + id: method.shipping_option.id, + name: method.shipping_option.name, + price: method.total, + tax_amount: method.tax_total, tax_rate: taxRate * 10000, } } @@ -286,6 +266,7 @@ class KlarnaProviderService extends PaymentService { return klarnaPayment } catch (error) { + this.logger_.error(error) throw error } } diff --git a/packages/medusa-payment-paypal/src/services/__tests__/paypal-provider.js b/packages/medusa-payment-paypal/src/services/__tests__/paypal-provider.js index 7dd4afd470..9c0de9f29f 100644 --- a/packages/medusa-payment-paypal/src/services/__tests__/paypal-provider.js +++ b/packages/medusa-payment-paypal/src/services/__tests__/paypal-provider.js @@ -72,7 +72,6 @@ describe("PaypalProviderService", () => { const paypalProviderService = new PayPalProviderService( { regionService: RegionServiceMock, - totalsService: TotalsServiceMock, }, { api_key: "test", @@ -98,7 +97,6 @@ describe("PaypalProviderService", () => { const paypalProviderService = new PayPalProviderService( { regionService: RegionServiceMock, - totalsService: TotalsServiceMock, }, { api_key: "test", @@ -149,7 +147,6 @@ describe("PaypalProviderService", () => { const paypalProviderService = new PayPalProviderService( { regionService: RegionServiceMock, - totalsService: TotalsServiceMock, }, { api_key: "test", @@ -196,7 +193,6 @@ describe("PaypalProviderService", () => { const paypalProviderService = new PayPalProviderService( { regionService: RegionServiceMock, - totalsService: TotalsServiceMock, }, { api_key: "test", @@ -275,7 +271,6 @@ describe("PaypalProviderService", () => { const paypalProviderService = new PayPalProviderService( { regionService: RegionServiceMock, - totalsService: TotalsServiceMock, }, { api_key: "test", @@ -349,9 +344,13 @@ describe("PaypalProviderService", () => { data: { id: "test-voided" }, }) - expect(PayPalMock.payments.AuthorizationsVoidRequest).not.toHaveBeenCalled() + expect( + PayPalMock.payments.AuthorizationsVoidRequest + ).not.toHaveBeenCalled() expect(PayPalMock.payments.CapturesRefundRequest).not.toHaveBeenCalled() - expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith("test-voided") + expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith( + "test-voided" + ) expect(PayPalClientMock.execute).toHaveBeenCalledTimes(1) expect(result.id).toEqual("test-voided") @@ -366,9 +365,13 @@ describe("PaypalProviderService", () => { }, }) - expect(PayPalMock.payments.AuthorizationsVoidRequest).not.toHaveBeenCalled() + expect( + PayPalMock.payments.AuthorizationsVoidRequest + ).not.toHaveBeenCalled() expect(PayPalMock.payments.CapturesRefundRequest).not.toHaveBeenCalled() - expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith("test-refund") + expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith( + "test-refund" + ) expect(PayPalClientMock.execute).toHaveBeenCalledTimes(1) expect(result.id).toEqual("test-refund") diff --git a/packages/medusa-payment-paypal/src/services/paypal-provider.js b/packages/medusa-payment-paypal/src/services/paypal-provider.js index 690d0e1ed8..de0aa5c0c1 100644 --- a/packages/medusa-payment-paypal/src/services/paypal-provider.js +++ b/packages/medusa-payment-paypal/src/services/paypal-provider.js @@ -12,7 +12,7 @@ function roundToTwo(num, currency) { class PayPalProviderService extends PaymentService { static identifier = "paypal" - constructor({ totalsService, regionService }, options) { + constructor({ regionService }, options) { super() /** @@ -44,9 +44,6 @@ class PayPalProviderService extends PaymentService { /** @private @const {RegionService} */ this.regionService_ = regionService - - /** @private @const {TotalsService} */ - this.totalsService_ = totalsService } /** @@ -94,7 +91,7 @@ class PayPalProviderService extends PaymentService { const { region_id } = cart const { currency_code } = await this.regionService_.retrieve(region_id) - const amount = await this.totalsService_.getTotal(cart) + const amount = cart.total const request = new PayPal.orders.OrdersCreateRequest() request.requestBody({ @@ -289,7 +286,8 @@ class PayPalProviderService extends PaymentService { async cancelPayment(payment) { const order = await this.retrievePayment(payment.data) const isAlreadyCanceled = order.status === "VOIDED" - const isCanceledAndFullyRefund = order.status === "COMPLETED" && !!order.invoice_id + const isCanceledAndFullyRefund = + order.status === "COMPLETED" && !!order.invoice_id if (isAlreadyCanceled || isCanceledAndFullyRefund) { return order } diff --git a/packages/medusa-payment-stripe/src/services/stripe-provider.js b/packages/medusa-payment-stripe/src/services/stripe-provider.js index fde19d86eb..f9a38b23ab 100644 --- a/packages/medusa-payment-stripe/src/services/stripe-provider.js +++ b/packages/medusa-payment-stripe/src/services/stripe-provider.js @@ -135,9 +135,7 @@ class StripeProviderService extends AbstractPaymentService { .withTransaction(this.manager_) .retrieve(region_id) - const amount = await this.totalsService_ - .withTransaction(this.manager_) - .getTotal(cart) + const amount = cart.total const intentRequest = { description: