ForhoppPay Connect — Multi-Currency Support
Feature Reference: FR-28 | Service:
CurrencyConversionService
1. Overview
ForhoppPay Connect supports 7 currencies for charges. When a charge currency differs from the merchant's payout currency, automatic conversion is applied with a 1% fee above the mid-market exchange rate. Both original and converted amounts are stored for full auditability.
2. Supported Currencies
| Currency | ISO Code | Symbol | Minimum Charge |
|---|---|---|---|
| US Dollar | USD |
$ | $0.50 (50 cents) |
| Euro | EUR |
€ | €0.50 (50 cents) |
| British Pound | GBP |
£ | £0.50 (50 pence) |
| Canadian Dollar | CAD |
CA$ | CA$0.50 |
| Australian Dollar | AUD |
A$ | A$0.50 |
| Japanese Yen | JPY |
¥ | ¥50 |
| Swiss Franc | CHF |
CHF | CHF 0.50 |
Currency codes are case-insensitive in the API (normalized to uppercase internally).
3. Conversion Fee
Applied rate = mid-market rate × (1 - 0.01)
Conversion fee = 1% above mid-market rate
The fee is applied by giving the merchant a slightly less favorable exchange rate.
4. Exchange Rates
4.1 Base Currency
All rates are stored relative to USD (base currency).
4.2 Current Rates (Illustrative)
| Currency | Rate to USD |
|---|---|
| USD | 1.000000 |
| EUR | 0.920000 |
| GBP | 0.790000 |
| CAD | 1.360000 |
| AUD | 1.530000 |
| JPY | 149.500000 |
| CHF | 0.880000 |
In production, rates are updated daily from a reliable external source.
4.3 Cross-Currency Conversion
To convert from currency A to currency B:
rate(A → B) = rate(B → USD) / rate(A → USD)
Example: EUR → GBP
rate = 0.79 / 0.92 = 0.858696
5. ConversionResult
public record ConversionResult(
Long originalAmountCents, // Amount in source currency
String originalCurrency, // Source currency code
Long convertedAmountCents, // Amount in target currency
String convertedCurrency, // Target currency code
BigDecimal exchangeRateApplied, // Rate with fee applied
Long conversionFeeCents // Fee amount in source currency
) {
boolean wasConverted(); // true if currencies differ
BigDecimal getEffectiveRate(); // Actual rate applied
}
6. Conversion Examples
6.1 EUR to USD
Original: €50.00 (5000 EUR cents)
Mid-market rate: 1 EUR = 1.087 USD (1/0.92)
Applied rate: 1.087 × 0.99 = 1.076
Converted: 5000 × 1.076 = 5380 USD cents ($53.80)
Fee: ~54 cents
6.2 Same Currency (No Conversion)
Original: $50.00 (5000 USD cents)
Converted: 5000 USD cents
Fee: 0
Rate: 1.000000
6.3 JPY to USD
Original: ¥5000 (5000 JPY)
Mid-market rate: 1 JPY = 0.006689 USD (1/149.5)
Applied rate: 0.006689 × 0.99 = 0.006622
Converted: 5000 × 0.006622 = 33 USD cents ($0.33)
7. Validation
| Rule | Behavior |
|---|---|
| Null currency | IllegalArgumentException |
| Empty currency | IllegalArgumentException |
| Unsupported currency | IllegalArgumentException with supported list |
| Null amount | IllegalArgumentException |
| Negative amount | IllegalArgumentException |
API-level validation returns HTTP 400:
{
"error": {
"type": "invalid_request_error",
"code": "currency_unsupported",
"message": "Currency 'xyz' is not supported. Supported: USD, EUR, GBP, CAD, AUD, JPY, CHF"
}
}
8. Rate Updates
CurrencyConversionService.updateExchangeRates(Map<String, BigDecimal> rates)
In production, a scheduled job fetches rates daily from an external provider and calls this method. Rates are stored in a ConcurrentHashMap for thread-safe access.
9. Correctness Properties
| # | Property | Validates |
|---|---|---|
| 11 | Only supported currencies accepted; 400 for others | FR-5.5, FR-28.1 |
| 37 | Both original and converted amounts stored | FR-28.3, FR-28.4 |
Last Updated: March 2026
