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
- Create
experience_prices table via migration
- Write a script to migrate existing
experiences.price values into experience_prices as ARS rows
- Drop
experiences.price column (after verifying no code references it)
- Update
BaseExperience in packages/shared/src/experiences.ts
- Update API serializers to include
prices map
- Update
POST /payments/create to accept and use currency
- Update mobile detail views to pass the correct currency to
PaymentPrompt
- 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.
Summary
Currently, every experience has a single
price(integer) implicitly denominated in ARS. Thepurchases.currencycolumn 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.pricewith a separateexperience_pricestable, so each experience can have multiple prices — one per currency — without changing the experience row itself.New schema
API changes
GET /experiences/:id— include apricesmap:{ "ARS": 1500, "MXN": 45000, "BRL": 900 }(client picks the right currency)POST /payments/create— accept an optionalcurrencyparam (defaults to"ARS"); look up the price fromexperience_pricesMobile changes
currencyas a prop — it just needs to receive the real value instead of"ARS"BaseExperienceinterface would includeprices: Record<string, number>instead ofprice: number | nullMercadoPago consideration
MercadoPago access tokens are per-country (ARG token ≠ MEX token). Multi-currency support would require:
MERCADO_PAGO_ACCESS_TOKEN_ARS,MERCADO_PAGO_ACCESS_TOKEN_MXN)currencyparameterMigration steps
experience_pricestable via migrationexperiences.pricevalues intoexperience_pricesas ARS rowsexperiences.pricecolumn (after verifying no code references it)BaseExperienceinpackages/shared/src/experiences.tspricesmapPOST /payments/createto accept and usecurrencyPaymentPromptTradeoffs / Alternatives considered
price_at_accessin access log already supports whatever currency was usedAdditional context
experience_accesses.price_at_accesscolumn (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.currencyparam from the client should drive which token to use.