252 lines
7.0 KiB
Markdown
252 lines
7.0 KiB
Markdown
# Phase 8: Order Processing (BullMQ + X-API)
|
|
|
|
**Status:** ⏳ Todo
|
|
**Progress:** 0/15 tasks (0%)
|
|
**Started:** -
|
|
**Completed:** -
|
|
**Assigned to:** -
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
Implement asynchronous order processing: BullMQ queue for order submission to X-API, worker for processing orders, retry logic, and BullBoard dashboard for monitoring.
|
|
|
|
**Goal:** Orders are reliably submitted to X-API (NAV ERP) after successful payment.
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
- ✅ Phase 2: Database (orders table needed)
|
|
- ✅ Phase 7: Payment (orders created after payment)
|
|
- ✅ Docker Redis running (from docker-compose.dev.yml)
|
|
- ⚠️ **Required:** X-API credentials (USERNAME, PASSWORD, BASE_URL)
|
|
|
|
---
|
|
|
|
## Tasks
|
|
|
|
### BullMQ Setup
|
|
|
|
- [ ] Install BullMQ + ioredis
|
|
|
|
```bash
|
|
pnpm add bullmq ioredis
|
|
pnpm add -D @bull-board/api @bull-board/nuxt
|
|
```
|
|
|
|
- [ ] Configure Redis connection
|
|
- File: `server/utils/redis.ts`
|
|
|
|
```typescript
|
|
import { Redis } from 'ioredis'
|
|
|
|
const config = useRuntimeConfig()
|
|
export const redis = new Redis({
|
|
host: config.redisHost,
|
|
port: config.redisPort,
|
|
password: config.redisPassword,
|
|
maxRetriesPerRequest: null,
|
|
})
|
|
```
|
|
|
|
- [ ] Create order queue
|
|
- File: `server/queues/orderQueue.ts`
|
|
|
|
```typescript
|
|
import { Queue } from 'bullmq'
|
|
import { redis } from '../utils/redis'
|
|
|
|
export const orderQueue = new Queue('x-api-orders', {
|
|
connection: redis,
|
|
defaultJobOptions: {
|
|
attempts: 5,
|
|
backoff: { type: 'exponential', delay: 2000 },
|
|
removeOnComplete: 1000,
|
|
removeOnFail: false,
|
|
},
|
|
})
|
|
```
|
|
|
|
- [ ] Create order worker
|
|
- File: `server/workers/orderWorker.ts`
|
|
|
|
```typescript
|
|
import { Worker } from 'bullmq'
|
|
import { redis } from '../utils/redis'
|
|
|
|
export const orderWorker = new Worker(
|
|
'x-api-orders',
|
|
async (job) => {
|
|
const { orderId } = job.data
|
|
|
|
// 1. Fetch order from DB with items and user
|
|
const order = await db.query.orders.findFirst({
|
|
where: eq(orders.id, orderId),
|
|
with: { items: true, user: true },
|
|
})
|
|
|
|
// 2. Transform to X-API format
|
|
const payload = transformOrderToXAPI(order, order.user)
|
|
|
|
// 3. Submit to X-API
|
|
const result = await submitOrderToXAPI(payload)
|
|
|
|
// 4. Update order status
|
|
await db
|
|
.update(orders)
|
|
.set({ status: 'completed', xapiResponse: result })
|
|
.where(eq(orders.id, orderId))
|
|
|
|
return result
|
|
},
|
|
{
|
|
connection: redis,
|
|
concurrency: 5,
|
|
limiter: { max: 10, duration: 1000 },
|
|
}
|
|
)
|
|
```
|
|
|
|
### X-API Client
|
|
|
|
- [ ] Create X-API client utility
|
|
- File: `server/utils/xapi/client.ts`
|
|
- Functions:
|
|
- `submitOrderToXAPI(payload)` - Submit order with retry logic & Basic Auth
|
|
- See: [CLAUDE.md: X-API Order Transformation](../CLAUDE.md#x-api-order-transformation-pattern)
|
|
|
|
- [ ] Implement transformOrderToXAPI function
|
|
- File: `server/utils/xapi/transformer.ts`
|
|
- Transform order from DB schema to X-API schema
|
|
- Critical transformations:
|
|
- Prices: EUR (Decimal) → Cents (Integer): `Math.round(price * 100)`
|
|
- Dates: JavaScript Date → ISO 8601 UTC: `.toISOString()`
|
|
- Line numbers: 10000, 20000, 30000... (multiples of 10000)
|
|
- Salutation: 'male' → 'HERR', 'female' → 'FRAU', other → 'K_ANGABE'
|
|
- See: [docs/ARCHITECTURE.md: X-API Format](../docs/ARCHITECTURE.md#34-x-api-order-transformation)
|
|
|
|
- [ ] Implement submitOrderToXAPI with retry
|
|
- Exponential backoff: 1s, 3s, 9s
|
|
- Max 3 retries
|
|
- HTTP Basic Auth header
|
|
- Timeout: 30 seconds
|
|
- Log all attempts
|
|
- See: [CLAUDE.md: X-API Pattern](../CLAUDE.md#x-api-order-transformation-pattern)
|
|
|
|
### API Endpoints
|
|
|
|
- [ ] Create /api/orders/index.post.ts endpoint
|
|
- Protected (requires auth)
|
|
- Body: { billingAddress, paymentId }
|
|
- Create order record in DB
|
|
- Create order_items from cart
|
|
- Queue order for X-API submission
|
|
- Return: { orderId, orderNumber }
|
|
|
|
- [ ] Create /api/orders/[id].get.ts endpoint
|
|
- Protected (requires auth)
|
|
- Fetch order by ID (only user's own orders)
|
|
- Include order items with product details
|
|
- Return: Order object
|
|
|
|
### Testing
|
|
|
|
- [ ] Test queue processing
|
|
- Add job to queue manually: `orderQueue.add('submit-order', { orderId: '...' })`
|
|
- Verify worker picks up job
|
|
- Verify job completes successfully
|
|
- Check BullBoard dashboard
|
|
|
|
- [ ] Test X-API submission (mock)
|
|
- Create mock X-API endpoint for testing
|
|
- Submit order via queue
|
|
- Verify transformation is correct
|
|
- Verify Basic Auth header is present
|
|
|
|
- [ ] Add error handling & logging
|
|
- Log all queue events (active, completed, failed, stalled)
|
|
- Log X-API requests/responses
|
|
- Handle X-API errors gracefully
|
|
- Update order status on failure
|
|
|
|
### Monitoring
|
|
|
|
- [ ] Setup BullBoard dashboard
|
|
- File: `server/api/admin/queues.ts`
|
|
|
|
```typescript
|
|
import { createBullBoard } from '@bull-board/api'
|
|
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'
|
|
import { NuxtAdapter } from '@bull-board/nuxt'
|
|
|
|
const serverAdapter = new NuxtAdapter()
|
|
serverAdapter.setBasePath('/admin/queues')
|
|
|
|
createBullBoard({
|
|
queues: [new BullMQAdapter(orderQueue)],
|
|
serverAdapter,
|
|
})
|
|
|
|
export default fromNodeMiddleware(serverAdapter.registerPlugin())
|
|
```
|
|
|
|
- Access at: http://localhost:3000/admin/queues
|
|
|
|
- [ ] Test retry logic
|
|
- Simulate X-API failure (wrong credentials or mock 500 error)
|
|
- Verify job retries with exponential backoff
|
|
- Verify job moves to failed after 5 attempts
|
|
- Check retry logs
|
|
|
|
- [ ] Document order processing
|
|
- Document queue flow: payment → queue → worker → X-API
|
|
- Document retry strategy
|
|
- Document error handling and recovery
|
|
- Document how to monitor queues
|
|
|
|
---
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] BullMQ is installed and configured
|
|
- [x] Redis connection is working
|
|
- [x] Order queue is created
|
|
- [x] Order worker processes jobs correctly
|
|
- [x] transformOrderToXAPI transforms orders correctly
|
|
- [x] submitOrderToXAPI submits with Basic Auth and retry logic
|
|
- [x] /api/orders endpoints create orders and queue jobs
|
|
- [x] Queue processing works end-to-end
|
|
- [x] X-API submissions succeed (or fail gracefully)
|
|
- [x] Error handling and logging are comprehensive
|
|
- [x] BullBoard dashboard is accessible and functional
|
|
- [x] Retry logic works as expected
|
|
- [x] Order processing is documented
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- **Async Processing:** Orders are queued immediately, processed in background
|
|
- **Job Timeout:** 60 seconds per job (X-API timeout + overhead)
|
|
- **Concurrency:** 5 jobs processed simultaneously
|
|
- **Rate Limit:** 10 requests/second to X-API
|
|
- **Failed Jobs:** Kept in Redis for manual inspection (not auto-deleted)
|
|
|
|
---
|
|
|
|
## Blockers
|
|
|
|
- ⚠️ **X-API Credentials:** Cannot test real submission without credentials
|
|
- **Workaround:** Use mock X-API endpoint for testing, document real integration
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [docs/ARCHITECTURE.md: Queue Architecture](../docs/ARCHITECTURE.md#35-queue-architektur-bullmq--redis)
|
|
- [docs/ARCHITECTURE.md: X-API Format](../docs/ARCHITECTURE.md#34-x-api-order-transformation)
|
|
- [docs/TECH_STACK.md: BullMQ](../docs/TECH_STACK.md#52-queue-system-bullmq)
|
|
- [CLAUDE.md: X-API Pattern](../CLAUDE.md#x-api-order-transformation-pattern)
|