refactor: restructure Stripe pricing config to support monthly and annual tiers
- Nest price IDs under `monthly` and `annual` keys for each tier (basic, plus, pro)
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# EIA Brent Price Source — Primary with FRED Fallback
|
||||
|
||||
**Date:** 2026-04-14
|
||||
**Status:** Approved
|
||||
|
||||
## Problem
|
||||
|
||||
FRED (`DCOILBRENTEU`) publishes Brent crude prices with a lag on top of the EIA's own publication delay. As of 2026-04-14, FRED's most recent data is 2026-04-02 — 12 days stale. EIA is the original data source; FRED mirrors it. Using EIA directly removes the mirroring lag.
|
||||
|
||||
## Goal
|
||||
|
||||
Replace FRED as the primary source for `fetchBrentPrices()` with the EIA Open Data API, keeping FRED as a fallback so the system degrades gracefully if EIA is unavailable.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
All changes are confined to `app/Services/OilPriceService.php` and supporting config/env files.
|
||||
|
||||
### `fetchBrentPrices()` — updated flow
|
||||
|
||||
```
|
||||
1. $rows = fetchFromEia()
|
||||
2. if ($rows === null):
|
||||
Log::warning('OilPriceService: EIA fetch failed, falling back to FRED')
|
||||
$rows = fetchFromFred()
|
||||
3. if ($rows === null):
|
||||
Log::error('OilPriceService: both EIA and FRED fetch failed')
|
||||
return
|
||||
4. BrentPrice::upsert($rows, ['date'], ['price_usd'])
|
||||
```
|
||||
|
||||
### `fetchFromEia(): ?array` (new private method)
|
||||
|
||||
- Endpoint: `GET https://api.eia.gov/v2/petroleum/pri/spt/data/`
|
||||
- Params: `api_key`, `frequency=daily`, `data[0]=value`, `facets[series][]=RBRTE`, `sort[0][column]=period`, `sort[0][direction]=desc`, `length=30`
|
||||
- Returns `null` on: HTTP error, exception, empty/missing `response.data` array
|
||||
- Maps each row: `['date' => $row['period'], 'price_usd' => (float) $row['value']]`
|
||||
- Filters rows where `value` is `'.'` (EIA uses same placeholder as FRED)
|
||||
|
||||
### `fetchFromFred(): ?array` (extracted private method)
|
||||
|
||||
- Existing FRED logic moved verbatim from the current `fetchBrentPrices()` body
|
||||
- Returns `null` on: HTTP error, exception, empty observations
|
||||
- Maps each row: `['date' => $obs['date'], 'price_usd' => (float) $obs['value']]`
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### `config/services.php`
|
||||
|
||||
Add under existing entries:
|
||||
```php
|
||||
'eia' => [
|
||||
'api_key' => env('EIA_API_KEY'),
|
||||
],
|
||||
```
|
||||
|
||||
### `.env.example`
|
||||
|
||||
Add:
|
||||
```
|
||||
EIA_API_KEY= # US EIA Open Data API key — register free at eia.gov/opendata
|
||||
```
|
||||
|
||||
FRED key stays in `.env.example` (still used as fallback).
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
| Event | Level | Message |
|
||||
|---|---|---|
|
||||
| EIA fetch succeeded | — | (no log — normal path) |
|
||||
| EIA fetch failed, FRED used | `warning` | `OilPriceService: EIA fetch failed, falling back to FRED` |
|
||||
| Both failed | `error` | `OilPriceService: both EIA and FRED fetch failed` |
|
||||
| No rows after filter | `warning` | `OilPriceService: no valid EIA observations returned` (existing pattern) |
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
Existing `OilPriceService` fetch tests are updated (not replaced):
|
||||
|
||||
1. **EIA succeeds** — `Http::fake()` returns valid EIA response; assert upsert called with correct rows; assert FRED endpoint never called.
|
||||
2. **EIA fails, FRED succeeds** — EIA returns 500; assert FRED endpoint called; assert upsert called with FRED rows; assert warning logged.
|
||||
3. **Both fail** — both return 500; assert upsert never called; assert error logged.
|
||||
4. **EIA returns empty data** — `response.data` is `[]`; assert falls back to FRED.
|
||||
|
||||
---
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- No changes to `generatePrediction()`, `PredictOilPrices` command, or scheduler
|
||||
- No new service classes or interfaces
|
||||
- No changes to `brent_prices` schema
|
||||
Reference in New Issue
Block a user