Skip to content

Support multi-currency pricing for experiences #271

Description

@masch

Summary

Currently, every experience has a single price (integer) implicitly denominated in ARS. The purchases.currency column always defaults to "ARS", and MercadoPago checkouts are hardcoded to ARS as well. This works for Argentina-only, but blocks expansion to other markets (Mexico, Brazil, etc.) where MercadoPago operates with different currencies.

Proposal

Replace experiences.price with a separate experience_prices table, so each experience can have multiple prices — one per currency — without changing the experience row itself.

New schema

CREATE TABLE experience_prices (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  experience_id UUID NOT NULL REFERENCES experiences(id) ON DELETE CASCADE,
  currency    TEXT NOT NULL DEFAULT 'ARS',
  amount      INTEGER NOT NULL,              -- price in minor units (cents)
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(experience_id, currency)
);

API changes

  • GET /experiences/:id — include a prices map: { "ARS": 1500, "MXN": 45000, "BRL": 900 } (client picks the right currency)
  • POST /payments/create — accept an optional currency param (defaults to "ARS"); look up the price from experience_prices
  • Admin CRUD for prices (future, not MVP)

Mobile changes

  • PaymentPrompt already takes currency as a prop — it just needs to receive the real value instead of "ARS"
  • BaseExperience interface would include prices: Record<string, number> instead of price: number | null

MercadoPago consideration

MercadoPago access tokens are per-country (ARG token ≠ MEX token). Multi-currency support would require:

  • A map of currency → MP token in environment config (e.g. MERCADO_PAGO_ACCESS_TOKEN_ARS, MERCADO_PAGO_ACCESS_TOKEN_MXN)
  • The provider to select the correct token when creating checkout based on the currency parameter
  • Webhook URLs remain the same (one endpoint for all currencies)

Migration steps

  1. Create experience_prices table via migration
  2. Write a script to migrate existing experiences.price values into experience_prices as ARS rows
  3. Drop experiences.price column (after verifying no code references it)
  4. Update BaseExperience in packages/shared/src/experiences.ts
  5. Update API serializers to include prices map
  6. Update POST /payments/create to accept and use currency
  7. Update mobile detail views to pass the correct currency to PaymentPrompt
  8. Deploy backend first, then mobile update

Tradeoffs / Alternatives considered

Approach Pros Cons
Option 2: Base currency + conversion (USD cents, convert on the fly) Single price point, simpler schema Currency conversion dependency, conversion lag, MP still needs local currency
Option 3: experience_prices table (chosen) Full flexibility, auditable, no conversion dependency, historical price_at_access in access log already supports whatever currency was used More schema, more API work, client needs to know which currency to request
Option 4: JSONB prices column No new table, easy schema No referential integrity, harder to query, no FK

Additional context

  • The experience_accesses.price_at_access column (added in #TODO) already stores a snapshot of the price at access time — multi-currency fits naturally since the currency context per access is now implicit in the price used.
  • Access tokens are NOT the right place to detect currency — the currency param from the client should drive which token to use.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions