@u11d/medusa-dynamic-pricing
v0.1.2
Published
Fluctum — dynamic pricing plugin for Medusa v2 — real-time precious metals pricing via SSE, checkout price locks, and admin pricing-rule management.
Readme
A Medusa plugin for real-time dynamic pricing in Medusa stores (precious metals and other volatile-price catalogs). Prices update every few seconds via SSE, are computed on the frontend from live spot prices, and are locked at checkout entry. Created and maintained by u11d.
Features
- Live spot prices — scheduled job fetches ask/bid/spot prices from a configurable provider
- Real-time delivery — Server-Sent Events (SSE) push prices to the storefront and admin panel
- Pricing rules — named rules with spread factor, spread fixed, premium percentage, and premium fixed; assigned per product variant
- Per-variant material + weight — each variant carries a material symbol (XAU, XAG, …) and weight via a module link
- Checkout price locks — prices are locked when the customer enters checkout;
force=truecreates fresh locks andforce=falsereuses valid locks, both using the latest spot prices stored in DB - Admin panel — config overview, pricing-rule CRUD, live spot-price dashboard, historical prices, variant/product assignment widgets
- Currency conversion — optional conversion rates stored in DB and applied in the pricing formula
- Built-in providers —
randomProvider(sinusoidal drift, for dev/testing),createGoldApiProvider(goldapi.io),createStaticRatesProviderandexchangeRateHostProviderfor currency conversion
Demo
See it in action: fluctum.medusajs.site — a live storefront with real-time spot prices and price-locked checkout.
Quick Start
For a full walkthrough — installing the plugin, configuring a price provider, and wiring up the storefront — see our blog post: Dynamic Pricing for Medusa: Building Fluctum.
Installation
npm install @u11d/medusa-dynamic-pricingRegister in medusa-config.ts:
import { randomProvider } from "@u11d/medusa-dynamic-pricing";
export default defineConfig({
plugins: [
{
resolve: "@u11d/medusa-dynamic-pricing",
options: {
materials: ["XAU", "XAG"],
fetchIntervalSeconds: 10,
priceLockDurationSeconds: 600,
provider: randomProvider,
},
},
],
});Plugin Options
| Option | Type | Default | Description |
| -------------------------- | --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| materials | string[] | required | Material symbols to track, e.g. ["XAU", "XAG"] |
| fetchIntervalSeconds | number | 10 | How often to fetch/generate spot prices (1–3600) |
| provider | PriceProviderFn | required | Function that returns spot prices for a list of materials |
| pricingCurrency | string | "USD" | ISO-4217 currency code in which provider returns spot prices |
| currencyConversion | CurrencyConversionOptions | null | Optional block: { provider: CurrencyRateProviderFn, refreshIntervalSeconds?: number, targetCurrencies: string[] }. Enables multi-currency conversion |
| priceLockDurationSeconds | number | 600 | How long a price lock is valid during checkout |
Pricing Formula
base = weight_oz × spot_price × spread_factor × currency_conversion
after_premium = base × (1 + premium_percentage / 100)
final = after_premium + spread_fixed + premium_fixedPrices are stored and returned as decimal numbers (not cents). The computeFinalPrice utility is exported from both the main entry point and the ./client subpath for use in storefronts.
Data Models
| Model | Key fields |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| SpotPrice | material, ask, bid, price, timestamp |
| PricingRule | name, spread_factor, spread_fixed, premium_percentage, premium_fixed |
| CartPriceLock | cart_id, variant_id, material, weight_oz, unit_price, spot_price, locked_at, expires_at |
Store API Routes
All routes are under /store/dynamic-pricing/.
| Method | Path | Description |
| ------ | ----------------------- | ------------------------------------------------------------------------------------------------- |
| GET | /spot-prices | Latest spot prices; optional ?material= filter |
| GET | /sse | SSE stream; sends current prices and a currency-rates event on connect, then broadcasts updates |
| POST | /carts/:id/price-lock | Lock prices for a cart; ?force=true always creates fresh locks (from latest DB spot prices) |
| GET | /variant-pricing | Variant pricing details (rule + material + weight) for one or more ?variant_id= values |
| GET | /currency-rates | Latest FX rates relative to pricingCurrency; used as an SSE-fallback polling source |
Admin API Routes
All routes are under /admin/dynamic-pricing/.
| Method | Path | Description |
| --------------- | ---------------------------- | ---------------------------------------------------- |
| GET | /config | Plugin config (read-only) |
| GET/POST | /pricing-rules | List / create pricing rules |
| GET/DELETE | /pricing-rules/:id | Get / delete a pricing rule |
| GET/POST/DELETE | /variants/:id/pricing-rule | Assign / read / remove a pricing rule from a variant |
| POST | /products/:id/pricing-rule | Bulk-assign a rule to all variants in a product |
| GET | /spot-prices | Historical spot prices with pagination |
| GET | /sse | Admin SSE stream |
| POST | /seed | Seed sample products via seedProductsWorkflow |
