prices table through a polymorphic relationship. Prices are stored in cents following the Stripe/Shopify standard.
Money Storage Standard
All monetary values are stored as integers in cents. $29.99 is stored as2999 cents. For zero-decimal currencies (XAF, JPY, KRW), the value is stored as-is since the currency has no subdivision (15000 FCFA is stored as 15000).
Display
Useshopper_money_format() to convert cents to a human-readable format. The helper divides by 100 for standard currencies and passes zero-decimal currencies through unchanged. It uses Laravel’s Number::currency() with the application locale.
Default Currency
Theshopper_currency() helper returns the store’s default currency code. It reads the default_currency_id setting, looks up the currency code, caches it for one hour, and falls back to 'USD' if no default is configured.
Zero-Decimal Currencies
These helpers let you check whether a currency uses decimal subdivisions:Price Model
The model used isShopper\Core\Models\Price. It implements Shopper\Core\Models\Contracts\Price and is not configurable via config/shopper/models.php.
Database Schema
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint | no | auto | Primary key |
priceable_type | string | no | - | Morph type (product or variant) |
priceable_id | bigint | no | - | Foreign key to the priceable model |
amount | integer (unsigned) | yes | null | Selling price in cents |
compare_amount | integer (unsigned) | yes | null | Original/compare-at price in cents, for strikethrough display |
cost_amount | integer (unsigned) | yes | null | Cost price in cents, for margin and profit tracking |
currency_id | bigint | no | - | Foreign key to currencies table |
created_at | timestamp | yes | null | Creation timestamp |
updated_at | timestamp | yes | null | Last update timestamp |
Price Fields
Each price record has three amount fields that serve distinct business purposes:| Field | What it represents | Shown to customers | Example |
|---|---|---|---|
amount | The current selling price | Yes | 1999 cents = $19.99 |
compare_amount | The original price before a sale or discount. Displayed as a strikethrough next to the selling price (what Shopify calls “compare at price”) | Yes (as strikethrough) | 2999 cents = $29.99 |
cost_amount | What you paid your supplier. Used to calculate profit margins. Never displayed on the storefront | No | 850 cents = $8.50 |
Relationships
Thepriceable relationship connects a price to its parent model (Product or ProductVariant):
currency relationship returns the currency for this price:
Price Helper
The Price model provides three methods that return aShopper\Core\Helpers\Price value object with both the raw amount and a pre-formatted string:
HasPrices Trait
TheHasPrices trait is used by Product and ProductVariant to provide pricing capabilities. Any model implementing the Shopper\Core\Contracts\Priceable interface can use this trait.
To get the price for the store’s default currency:
prices relationship is already loaded to avoid extra queries. When eager-loaded, it filters the collection in memory instead of hitting the database.
To access all prices:
Currency Model
The model used isShopper\Core\Models\Currency. It implements Shopper\Core\Models\Contracts\Currency and has no timestamps.
Currency Schema
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint | no | auto | Primary key |
name | string | no | - | Currency name (e.g., “US Dollar”) |
code | string(10) | no | - | ISO 4217 code (e.g., “USD”), unique |
symbol | string(25) | no | - | Currency symbol (e.g., ”$“) |
format | string(50) | no | - | Display format pattern |
exchange_rate | decimal(10,2) | yes | null | Exchange rate relative to base currency |
is_enabled | boolean | no | true | Whether the currency is active |
Enabled Scope
The Currency model has a globalenabled scope that automatically filters out disabled currencies. All queries return only enabled currencies by default.
Zone Relationship
Each currency can be associated with a zone for region-specific pricing:Creating Prices
To set a price on a product or variant, create a Price record through theprices() relationship:
Multi-Currency Pricing
To set prices in multiple currencies at once, use theSavePricingAction. It accepts an array keyed by currency ID and creates or updates prices in a single pass:
updateOrCreate on each entry, so existing prices for a currency are updated rather than duplicated.
Retrieving Prices
To get a product’s price for the default currency and format it for display:Admin Components
MoneyInput
For Filament forms, use theMoneyInput component instead of TextInput for monetary fields. It handles conversion between human values (form) and cents (database) automatically:
MoneyInput defaults to the store’s configured currency via shopper_currency().
Table Columns
Use the->currency() macro on Filament TextColumn for displaying monetary values:
shopper_money_format() internally.
Publishing Components
To customize the pricing Livewire components:config/shopper/components/product.php: