medusajs / medusa

The world's most flexible commerce platform.
https://medusajs.com
MIT License
25.93k stars 2.6k forks source link

Bug: Amount sent to stripe vs displayed in summary page #8733

Closed 420coupe closed 2 months ago

420coupe commented 2 months ago

Bug report

Describe the bug

The discrepancy occurs with the Total amount displayed on the review page vs what is sent to stripe payment.

Ex.

If actual total is 2.675 the amount sent to stripe is truncated 2.67 and the Total displayed is 2.68

System information

Medusa version (including plugins): @medusajs/medusa@npm:1.20.6-preview-20240819150500 (via preview) Node.js version: v21.7.1 Database: Supabase (postgres) Operating system: Ubuntu 22.04.4 Browser (if relevant):

Steps to reproduce the behavior

  1. Setup V2 backend and configs
  2. Configure stripe payment provider
  3. Attempt to checkout with a tax rate applied
  4. Amount discrepancy between what is displayed and what is sent to stripe.

Expected behavior

The totals should match and it should round up to nearest hundreth place for usd currency.

Screenshots

image

{
  "id": ["evt_3PqlXrK6QjZA3FWk1MmBx45R"](https://dashboard.stripe.com/test/events/evt_3PqlXrK6QjZA3FWk1MmBx45R),
  "object": "event",
  "api_version": "2024-06-20",
  "created": 1724373622,
  "data": {
    "object": {
      "id": ["pi_3PqlXrK6QjZA3FWk1pRKmGuT"](https://dashboard.stripe.com/test/payments/pi_3PqlXrK6QjZA3FWk1pRKmGuT),
      "object": "payment_intent",
      "amount": 267,
      "amount_capturable": 0,
      "amount_details": {
        "tip": {
        }
      },
      "amount_received": 267,
      "application": null,
      "application_fee_amount": null,
      "automatic_payment_methods": {
        "allow_redirects": "always",
        "enabled": true
      },
      "canceled_at": null,
      "cancellation_reason": null,
      "capture_method": "automatic",
      "client_secret": "pi_3PqlXrK6QjZA3FWk1pRKmGuT_secret_gxaSNTvWzKH61XGRYNiyoB8Qd",
      "confirmation_method": "automatic",
      "created": 1724373603,
      "currency": "usd",
      "customer": ["cus_QiBvPxJ6dYqozI"](https://dashboard.stripe.com/test/customers/cus_QiBvPxJ6dYqozI),
      "description": null,
      "invoice": null,
      "last_payment_error": null,
      "latest_charge": "ch_3PqlXrK6QjZA3FWk1939Mciu",
      "livemode": false,
      "metadata": {
        "session_id": "payses_01J5YAT4T7KVNKWBEMJQBADZV2"
      },
      "next_action": null,
      "on_behalf_of": null,
      "payment_method": "pm_1PqlYAK6QjZA3FWko7IOU08I",
      "payment_method_configuration_details": {
        "id": "pmc_1PoE5nK6QjZA3FWk3b502P3b",
        "parent": null
      },
      "payment_method_options": {
        "amazon_pay": {
          "express_checkout_element_session_id": null
        },
        "card": {
          "installments": null,
          "mandate_options": null,
          "network": null,
          "request_three_d_secure": "automatic"
        },
        "cashapp": {
        },
        "klarna": {
          "preferred_locale": null
        },
        "link": {
          "persistent_token": null
        }
      },
      "payment_method_types": [
        "card",
        "klarna",
        "link",
        "cashapp",
        "amazon_pay"
      ],
      "processing": null,
      "receipt_email": null,
      "review": null,
      "setup_future_usage": null,
      "shipping": null,
      "source": null,
      "statement_descriptor": null,
      "statement_descriptor_suffix": null,
      "status": "succeeded",
      "transfer_data": null,
      "transfer_group": null
    }
  },
  "livemode": false,
  "pending_webhooks": 1,
  "request": {
    "id": "req_EjRbwD31oOmSrD",
    "idempotency_key": "c4f7ca70-d35b-4c68-9136-70d6464f368b"
  },
  "type": "payment_intent.succeeded"
}
420coupe commented 2 months ago

So this is the piece of code causing the issue. it's just truncating instead of rounding. should something like a Math.round() be implemented to resolve this?


/**
 * Converts an amount to the format required by Stripe based on currency.
 * https://docs.stripe.com/currencies
 * @param {BigNumberInput} amount - The amount to be converted.
 * @param {string} currency - The currency code (e.g., 'USD', 'JOD').
 * @returns {number} - The converted amount in the smallest currency unit.
 */
export function getSmallestUnit(
  amount: BigNumberInput,
  currency: string
): number {
  const multiplier = getCurrencyMultiplier(currency)
  const smallestAmount = new BigNumber(MathBN.mult(amount, multiplier))

  let numeric = smallestAmount.numeric

  // Check if the currency requires rounding to the nearest ten
  if (multiplier === 1e3) {
    numeric = Math.ceil(numeric / 10) * 10
  }

  return parseInt(numeric.toString().split(".").shift()!, 10)
}```