Currency Configuration
Currency Setup
<!-- app/code/Vendor/Currency/etc/config.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<currency>
<options>
<base>USD</base>
<default>USD</default>
<allow>USD,EUR,GBP,JPY</allow>
</options>
</currency>
</default>
</config>
Currency Manager
namespace Vendor\Currency\Manager;
class CurrencyManager
{
private CurrencyInterface $currency;
private StoreManagerInterface $storeManager;
public function getStoreCurrencies(int $storeId): array
{
$store = $this->storeManager->getStore($storeId);
$allowed = explode(',', $store->getConfig('currency/options/allow'));
return array_map(function ($code) {
return [
'code' => $code,
'symbol' => $this->currency->getSymbol($code),
'name' => $this->currency->getName($code),
];
}, $allowed);
}
public function getBaseCurrency(int $storeId): string
{
return $this->storeManager->getStore($storeId)
->getConfig('currency/options/base');
}
public function getDefaultCurrency(int $storeId): string
{
return $this->storeManager->getStore($storeId)
->getConfig('currency/options/default');
}
}
Exchange Rates
Exchange Rate Service
namespace Vendor\Currency\ExchangeRate;
class ExchangeRateService
{
private RateRepositoryInterface $rateRepo;
public function convert(
float $amount,
string $fromCurrency,
string $toCurrency,
int $storeId = null
): float {
if ($fromCurrency === $toCurrency) {
return $amount;
}
$rate = $this->getRate($fromCurrency, $toCurrency, $storeId);
return round($amount * $rate, 4);
}
public function getRate(
string $fromCurrency,
string $toCurrency,
int $storeId = null
): float {
$rate = $this->rateRepo->getByCurrencies(
$fromCurrency,
$toCurrency,
$storeId
);
if ($rate === null) {
// Try inverse rate
$inverseRate = $this->rateRepo->getByCurrencies(
$toCurrency,
$fromCurrency,
$storeId
);
if ($inverseRate !== null) {
return 1 / $inverseRate;
}
throw new \Magento\Framework\Exception\LocalizedException(
__('Exchange rate not found for %1 to %2', $fromCurrency, $toCurrency)
);
}
return $rate;
}
}
Rate Import
namespace Vendor\Currency\ExchangeRate\Import;
class RateImporter
{
private RateRepositoryInterface $rateRepo;
private RateConverterFactory $converterFactory;
public function importFromSource(
string $source,
array $currencyPairs
): ImportResult {
$converter = $this->converterFactory->create($source);
$rates = $converter->fetch($currencyPairs);
$imported = 0;
foreach ($rates as $pair => $rate) {
[$from, $to] = explode('_', $pair);
$rateEntity = new RateData([
'currency_from' => $from,
'currency_to' => $to,
'rate' => $rate,
'import_source' => $source,
'imported_at' => new \DateTime(),
]);
$this->rateRepo->save($rateEntity);
$imported++;
}
return new ImportResult($imported);
}
}
Rate Sources
namespace Vendor\Currency\ExchangeRate\Source;
interface RateSourceInterface
{
public function fetch(array $pairs): array;
public function getName(): string;
}
class ECBRateSource implements RateSourceInterface
{
private HttpClientInterface $httpClient;
public function fetch(array $pairs): array
{
$response = $this->httpClient->get(
'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
);
return $this->parseXml($response->getBody(), $pairs);
}
}
Currency Display
Price Formatter
namespace Vendor\Currency\Display;
class PriceFormatter
{
private CurrencyInterface $currency;
private PriceCurrencyInterface $priceCurrency;
public function format(
float $amount,
string $currencyCode,
bool $includeContainer = true
): string {
$symbol = $this->currency->getSymbol($currencyCode);
$formatted = number_format($amount, 2);
if ($includeContainer) {
return sprintf(
'<span class="price" data-currency="%s">%s%s</span>',
$currencyCode,
$symbol,
$formatted
);
}
return $symbol . $formatted;
}
public function formatForStore(
float $amount,
int $storeId
): string {
$currency = $this->priceCurrency->getCurrency($storeId);
return $this->currency->format(
$amount,
['container' => '<span class="price">%s</span>']
);
}
}
Currency Switcher
namespace Vendor\Currency\Switcher;
class CurrencySwitcher
{
private StoreManagerInterface $storeManager;
public function getAvailableCurrencies(int $storeId): array
{
$store = $this->storeManager->getStore($storeId);
$allowed = explode(',', $store->getConfig('currency/options/allow'));
return array_map(function ($code) use ($storeId) {
return [
'code' => $code,
'symbol' => $this->currency->getSymbol($code),
'label' => $this->getCurrencyLabel($code),
'is_current' => $this->isCurrentCurrency($code, $storeId),
];
}, $allowed);
}
public function switchCurrency(
string $currencyCode,
int $storeId
): void {
$session = $this->customerSession;
$session->setCurrencyCode($currencyCode);
}
}
Currency Conversion in Pricing
Price Converter
namespace Vendor\Currency\Pricing;
class PriceConverter
{
private ExchangeRateServiceInterface $rateService;
private StoreManagerInterface $storeManager;
public function convertPrice(
float $price,
string $fromCurrency,
string $toCurrency,
int $storeId = null
): float {
return $this->rateService->convert(
$price,
$fromCurrency,
$toCurrency,
$storeId
);
}
public function convertCatalogPrices(
array $products, string $targetCurrency, int $storeId): array
{
$baseCurrency = $this->getBaseCurrency($storeId);
foreach ($products as $product) {
$product->setPrice(
$this->convertPrice(
$product->getPrice(),
$baseCurrency,
$targetCurrency,
$storeId
)
);
}
return $products;
}
}
Cart Currency Handling
namespace Vendor\Currency\Cart;
class CartCurrencyHandler
{
public function recalculateCart(
CartInterface $cart,
string $newCurrency
): void {
$baseCurrency = $cart->getQuote()->getCurrency();
foreach ($cart->getItems() as $item) {
$item->setPrice(
$this->converter->convert(
$item->getBasePrice(),
$baseCurrency,
$newCurrency
)
);
}
$cart->recalculate();
}
}
Quiz
1. What is the base currency?
2. How do exchange rates work?
3. What is the currency switcher?
Flashcards
Question
What is base currency?
Click to reveal answer
Answer
Currency used for internal price calculations
Question
What is default currency?
Click to reveal answer
Answer
Default display currency for the store view
Question
How to import exchange rates?
Click to reveal answer
Answer
Use RateImporter with external source (ECB, etc.)
Question
What is currency conversion?
Click to reveal answer
Answer
Transforming prices between currencies using exchange rates
Revision Notes
Key Takeaways
- 1. Base currency is for internal calculations
- 2. Default currency is for display
- 3. Exchange rates convert between currencies
- 4. Currency switcher changes display currency
- 5. Catalog prices stored in base currency, converted on display
Interview Tips
- • Explain base vs default vs allowed currencies
- • Discuss exchange rate import strategies
- • Describe currency conversion in cart/checkout
- • Talk about rounding and precision in conversions
Cheat Sheet
Currency Types:
Base → internal calculations
Default → primary display
Allowed → customer switchable
Exchange Rate:
from_currency → to_currency ratio
Inverse: 1 / rate
Display:
Symbol + formatted amount
Currency switcher UI
Conversion:
price × rate = converted_price
Store in base, display in default