feat: add Letzshop bidirectional order integration
Add complete Letzshop marketplace integration with: - GraphQL client for order import and fulfillment operations - Encrypted credential storage per vendor (Fernet encryption) - Admin and vendor API endpoints for credentials management - Order import, confirmation, rejection, and tracking - Fulfillment queue and sync logging - Comprehensive documentation and test coverage New files: - app/services/letzshop/ - GraphQL client and services - app/utils/encryption.py - Fernet encryption utility - models/database/letzshop.py - Database models - models/schema/letzshop.py - Pydantic schemas - app/api/v1/admin/letzshop.py - Admin API endpoints - app/api/v1/vendor/letzshop.py - Vendor API endpoints - docs/guides/letzshop-order-integration.md - Documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
542
docs/guides/letzshop-order-integration.md
Normal file
542
docs/guides/letzshop-order-integration.md
Normal file
@@ -0,0 +1,542 @@
|
||||
# Letzshop Order Integration Guide
|
||||
|
||||
Complete guide for bidirectional order management with Letzshop marketplace via GraphQL API.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Architecture](#architecture)
|
||||
- [Setup and Configuration](#setup-and-configuration)
|
||||
- [Order Import](#order-import)
|
||||
- [Fulfillment Operations](#fulfillment-operations)
|
||||
- [API Reference](#api-reference)
|
||||
- [Database Models](#database-models)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Letzshop Order Integration provides bidirectional synchronization with Letzshop marketplace:
|
||||
|
||||
- **Order Import**: Fetch unconfirmed orders from Letzshop via GraphQL
|
||||
- **Order Confirmation**: Confirm or reject inventory units
|
||||
- **Tracking Updates**: Set shipment tracking information
|
||||
- **Audit Trail**: Complete logging of all sync operations
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Encrypted Credentials**: API keys stored with Fernet encryption
|
||||
- **Per-Vendor Configuration**: Each vendor manages their own Letzshop connection
|
||||
- **Admin Oversight**: Platform admins can manage any vendor's integration
|
||||
- **Queue-Based Fulfillment**: Retry logic for failed operations
|
||||
- **Multi-Channel Support**: Orders tracked with channel attribution
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### System Components
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Frontend Interfaces │
|
||||
├─────────────────────────────────────────┤
|
||||
│ Vendor Portal Admin Portal │
|
||||
│ /vendor/letzshop /admin/letzshop │
|
||||
└─────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────┐
|
||||
│ API Layer │
|
||||
├─────────────────────────────────────────┤
|
||||
│ /api/v1/vendor/letzshop/* │
|
||||
│ /api/v1/admin/letzshop/* │
|
||||
└─────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Service Layer │
|
||||
├─────────────────────────────────────────┤
|
||||
│ LetzshopClient CredentialsService│
|
||||
│ (GraphQL) (Encryption) │
|
||||
└─────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Data Layer │
|
||||
├─────────────────────────────────────────┤
|
||||
│ VendorLetzshopCredentials │
|
||||
│ LetzshopOrder │
|
||||
│ LetzshopFulfillmentQueue │
|
||||
│ LetzshopSyncLog │
|
||||
└─────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Letzshop GraphQL API │
|
||||
│ https://letzshop.lu/graphql │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. **Credentials Setup**: Vendor/Admin stores encrypted API key
|
||||
2. **Order Import**: System fetches unconfirmed shipments from Letzshop
|
||||
3. **Order Processing**: Orders stored locally with Letzshop IDs
|
||||
4. **Fulfillment**: Vendor confirms/rejects orders, sets tracking
|
||||
5. **Sync Back**: Operations sent to Letzshop via GraphQL mutations
|
||||
|
||||
---
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Letzshop API key (obtained from Letzshop merchant portal)
|
||||
- Active vendor account on the platform
|
||||
|
||||
### Step 1: Configure API Credentials
|
||||
|
||||
#### Via Vendor Portal
|
||||
|
||||
1. Navigate to **Settings > Letzshop Integration**
|
||||
2. Enter your Letzshop API key
|
||||
3. Click **Test Connection** to verify
|
||||
4. Enable **Auto-Sync** if desired (optional)
|
||||
5. Click **Save**
|
||||
|
||||
#### Via Admin Portal
|
||||
|
||||
1. Navigate to **Marketplace > Letzshop**
|
||||
2. Select the vendor from the list
|
||||
3. Click **Configure Credentials**
|
||||
4. Enter the API key
|
||||
5. Click **Save & Test**
|
||||
|
||||
### Step 2: Test Connection
|
||||
|
||||
```bash
|
||||
# Test connection via API
|
||||
curl -X POST /api/v1/vendor/letzshop/test \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Connection successful",
|
||||
"response_time_ms": 245.5
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `api_endpoint` | `https://letzshop.lu/graphql` | GraphQL endpoint URL |
|
||||
| `auto_sync_enabled` | `false` | Enable automatic order sync |
|
||||
| `sync_interval_minutes` | `15` | Auto-sync interval (5-1440 minutes) |
|
||||
|
||||
---
|
||||
|
||||
## Order Import
|
||||
|
||||
### Manual Import
|
||||
|
||||
Import orders on-demand via the vendor portal or API:
|
||||
|
||||
```bash
|
||||
# Trigger order import
|
||||
curl -X POST /api/v1/vendor/letzshop/orders/import \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"operation": "order_import"}'
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Import completed: 5 imported, 2 updated",
|
||||
"orders_imported": 5,
|
||||
"orders_updated": 2,
|
||||
"errors": []
|
||||
}
|
||||
```
|
||||
|
||||
### What Gets Imported
|
||||
|
||||
The import fetches **unconfirmed shipments** from Letzshop containing:
|
||||
|
||||
- Order ID and number
|
||||
- Customer email and name
|
||||
- Order total and currency
|
||||
- Inventory units (products to fulfill)
|
||||
- Shipping/billing addresses
|
||||
- Current order state
|
||||
|
||||
### Order States
|
||||
|
||||
| Letzshop State | Description |
|
||||
|----------------|-------------|
|
||||
| `unconfirmed` | Awaiting vendor confirmation |
|
||||
| `confirmed` | Vendor confirmed, ready to ship |
|
||||
| `shipped` | Tracking number set |
|
||||
| `delivered` | Delivery confirmed |
|
||||
| `returned` | Items returned |
|
||||
|
||||
### Sync Status
|
||||
|
||||
Local orders track their sync status:
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| `pending` | Imported, awaiting action |
|
||||
| `confirmed` | Confirmed with Letzshop |
|
||||
| `rejected` | Rejected with Letzshop |
|
||||
| `shipped` | Tracking set with Letzshop |
|
||||
|
||||
---
|
||||
|
||||
## Fulfillment Operations
|
||||
|
||||
### Confirm Order
|
||||
|
||||
Confirm that you can fulfill the order:
|
||||
|
||||
```bash
|
||||
# Confirm all inventory units in an order
|
||||
curl -X POST /api/v1/vendor/letzshop/orders/{order_id}/confirm \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# Or confirm specific units
|
||||
curl -X POST /api/v1/vendor/letzshop/orders/{order_id}/confirm \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"inventory_unit_ids": ["unit_abc123", "unit_def456"]}'
|
||||
```
|
||||
|
||||
### Reject Order
|
||||
|
||||
Reject order if you cannot fulfill:
|
||||
|
||||
```bash
|
||||
curl -X POST /api/v1/vendor/letzshop/orders/{order_id}/reject \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"reason": "Out of stock"}'
|
||||
```
|
||||
|
||||
### Set Tracking
|
||||
|
||||
Add tracking information for shipment:
|
||||
|
||||
```bash
|
||||
curl -X POST /api/v1/vendor/letzshop/orders/{order_id}/tracking \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"tracking_number": "1Z999AA10123456784",
|
||||
"tracking_carrier": "ups"
|
||||
}'
|
||||
```
|
||||
|
||||
Supported carriers: `dhl`, `ups`, `fedex`, `post_lu`, etc.
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### Vendor Endpoints
|
||||
|
||||
Base path: `/api/v1/vendor/letzshop`
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/status` | Get integration status |
|
||||
| GET | `/credentials` | Get credentials (API key masked) |
|
||||
| POST | `/credentials` | Create/update credentials |
|
||||
| PATCH | `/credentials` | Partial update credentials |
|
||||
| DELETE | `/credentials` | Remove credentials |
|
||||
| POST | `/test` | Test stored credentials |
|
||||
| POST | `/test-key` | Test API key without saving |
|
||||
| GET | `/orders` | List Letzshop orders |
|
||||
| GET | `/orders/{id}` | Get order details |
|
||||
| POST | `/orders/import` | Import orders from Letzshop |
|
||||
| POST | `/orders/{id}/confirm` | Confirm order |
|
||||
| POST | `/orders/{id}/reject` | Reject order |
|
||||
| POST | `/orders/{id}/tracking` | Set tracking info |
|
||||
| GET | `/logs` | List sync logs |
|
||||
| GET | `/queue` | List fulfillment queue |
|
||||
|
||||
### Admin Endpoints
|
||||
|
||||
Base path: `/api/v1/admin/letzshop`
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/vendors` | List vendors with Letzshop status |
|
||||
| GET | `/vendors/{id}/credentials` | Get vendor credentials |
|
||||
| POST | `/vendors/{id}/credentials` | Set vendor credentials |
|
||||
| PATCH | `/vendors/{id}/credentials` | Update vendor credentials |
|
||||
| DELETE | `/vendors/{id}/credentials` | Delete vendor credentials |
|
||||
| POST | `/vendors/{id}/test` | Test vendor connection |
|
||||
| POST | `/test` | Test any API key |
|
||||
| GET | `/vendors/{id}/orders` | List vendor's Letzshop orders |
|
||||
| POST | `/vendors/{id}/sync` | Trigger sync for vendor |
|
||||
|
||||
### Response Schemas
|
||||
|
||||
#### Credentials Response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"vendor_id": 5,
|
||||
"api_key_masked": "letz****",
|
||||
"api_endpoint": "https://letzshop.lu/graphql",
|
||||
"auto_sync_enabled": false,
|
||||
"sync_interval_minutes": 15,
|
||||
"last_sync_at": "2025-01-15T10:30:00Z",
|
||||
"last_sync_status": "success",
|
||||
"last_sync_error": null,
|
||||
"created_at": "2025-01-01T00:00:00Z",
|
||||
"updated_at": "2025-01-15T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Order Response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"vendor_id": 5,
|
||||
"letzshop_order_id": "gid://letzshop/Order/12345",
|
||||
"letzshop_shipment_id": "gid://letzshop/Shipment/67890",
|
||||
"letzshop_order_number": "LS-2025-001234",
|
||||
"letzshop_state": "unconfirmed",
|
||||
"customer_email": "customer@example.com",
|
||||
"customer_name": "John Doe",
|
||||
"total_amount": "99.99",
|
||||
"currency": "EUR",
|
||||
"sync_status": "pending",
|
||||
"inventory_units": [
|
||||
{"id": "gid://letzshop/InventoryUnit/111", "state": "unconfirmed"}
|
||||
],
|
||||
"created_at": "2025-01-15T10:00:00Z",
|
||||
"updated_at": "2025-01-15T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Models
|
||||
|
||||
### VendorLetzshopCredentials
|
||||
|
||||
Stores encrypted API credentials per vendor.
|
||||
|
||||
```python
|
||||
class VendorLetzshopCredentials(Base):
|
||||
__tablename__ = "vendor_letzshop_credentials"
|
||||
|
||||
id: int # Primary key
|
||||
vendor_id: int # FK to vendors (unique)
|
||||
api_key_encrypted: str # Fernet encrypted API key
|
||||
api_endpoint: str # GraphQL endpoint URL
|
||||
auto_sync_enabled: bool # Enable auto-sync
|
||||
sync_interval_minutes: int # Sync interval
|
||||
last_sync_at: datetime # Last sync timestamp
|
||||
last_sync_status: str # success, failed, partial
|
||||
last_sync_error: str # Error message if failed
|
||||
```
|
||||
|
||||
### LetzshopOrder
|
||||
|
||||
Tracks imported orders from Letzshop.
|
||||
|
||||
```python
|
||||
class LetzshopOrder(Base):
|
||||
__tablename__ = "letzshop_orders"
|
||||
|
||||
id: int # Primary key
|
||||
vendor_id: int # FK to vendors
|
||||
letzshop_order_id: str # Letzshop order GID
|
||||
letzshop_shipment_id: str # Letzshop shipment GID
|
||||
letzshop_order_number: str # Human-readable order number
|
||||
local_order_id: int # FK to orders (if imported locally)
|
||||
letzshop_state: str # Current Letzshop state
|
||||
customer_email: str # Customer email
|
||||
customer_name: str # Customer name
|
||||
total_amount: str # Order total
|
||||
currency: str # Currency code
|
||||
raw_order_data: JSON # Full order data from Letzshop
|
||||
inventory_units: JSON # List of inventory units
|
||||
sync_status: str # pending, confirmed, rejected, shipped
|
||||
tracking_number: str # Tracking number (if set)
|
||||
tracking_carrier: str # Carrier code
|
||||
```
|
||||
|
||||
### LetzshopFulfillmentQueue
|
||||
|
||||
Queue for outbound operations with retry logic.
|
||||
|
||||
```python
|
||||
class LetzshopFulfillmentQueue(Base):
|
||||
__tablename__ = "letzshop_fulfillment_queue"
|
||||
|
||||
id: int # Primary key
|
||||
vendor_id: int # FK to vendors
|
||||
letzshop_order_id: int # FK to letzshop_orders
|
||||
operation: str # confirm, reject, set_tracking
|
||||
payload: JSON # Operation data
|
||||
status: str # pending, processing, completed, failed
|
||||
attempts: int # Retry count
|
||||
max_attempts: int # Max retries (default 3)
|
||||
error_message: str # Last error if failed
|
||||
response_data: JSON # Response from Letzshop
|
||||
```
|
||||
|
||||
### LetzshopSyncLog
|
||||
|
||||
Audit trail for all sync operations.
|
||||
|
||||
```python
|
||||
class LetzshopSyncLog(Base):
|
||||
__tablename__ = "letzshop_sync_logs"
|
||||
|
||||
id: int # Primary key
|
||||
vendor_id: int # FK to vendors
|
||||
operation_type: str # order_import, confirm, etc.
|
||||
direction: str # inbound, outbound
|
||||
status: str # success, failed, partial
|
||||
records_processed: int # Total records
|
||||
records_succeeded: int # Successful records
|
||||
records_failed: int # Failed records
|
||||
error_details: JSON # Detailed error info
|
||||
started_at: datetime # Operation start time
|
||||
completed_at: datetime # Operation end time
|
||||
duration_seconds: int # Total duration
|
||||
triggered_by: str # user_id, scheduler, webhook
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### API Key Encryption
|
||||
|
||||
API keys are encrypted using Fernet symmetric encryption:
|
||||
|
||||
```python
|
||||
from app.utils.encryption import encrypt_value, decrypt_value
|
||||
|
||||
# Encrypt before storing
|
||||
encrypted_key = encrypt_value(api_key)
|
||||
|
||||
# Decrypt when needed
|
||||
api_key = decrypt_value(encrypted_key)
|
||||
```
|
||||
|
||||
The encryption key is derived from the application's `jwt_secret_key` using PBKDF2.
|
||||
|
||||
### Access Control
|
||||
|
||||
- **Vendors**: Can only manage their own Letzshop integration
|
||||
- **Admins**: Can manage any vendor's integration
|
||||
- **API Keys**: Never returned in plain text (always masked)
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Failed
|
||||
|
||||
**Symptoms**: "Connection failed" error when testing
|
||||
|
||||
**Possible Causes**:
|
||||
- Invalid API key
|
||||
- API key expired
|
||||
- Network issues
|
||||
- Letzshop service unavailable
|
||||
|
||||
**Solutions**:
|
||||
1. Verify API key in Letzshop merchant portal
|
||||
2. Regenerate API key if expired
|
||||
3. Check network connectivity
|
||||
4. Check Letzshop status page
|
||||
|
||||
### Orders Not Importing
|
||||
|
||||
**Symptoms**: Import runs but no orders appear
|
||||
|
||||
**Possible Causes**:
|
||||
- No unconfirmed orders in Letzshop
|
||||
- API key doesn't have required permissions
|
||||
- Orders already imported
|
||||
|
||||
**Solutions**:
|
||||
1. Check Letzshop dashboard for unconfirmed orders
|
||||
2. Verify API key has order read permissions
|
||||
3. Check existing orders with `sync_status: pending`
|
||||
|
||||
### Fulfillment Failed
|
||||
|
||||
**Symptoms**: Confirm/reject/tracking operations fail
|
||||
|
||||
**Possible Causes**:
|
||||
- Order already processed
|
||||
- Invalid inventory unit IDs
|
||||
- API permission issues
|
||||
|
||||
**Solutions**:
|
||||
1. Check order state in Letzshop
|
||||
2. Verify inventory unit IDs are correct
|
||||
3. Check fulfillment queue for retry status
|
||||
4. Review error message in response
|
||||
|
||||
### Sync Logs
|
||||
|
||||
Check sync logs for detailed operation history:
|
||||
|
||||
```bash
|
||||
curl -X GET /api/v1/vendor/letzshop/logs \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### For Vendors
|
||||
|
||||
1. **Test connection** after setting up credentials
|
||||
2. **Import orders regularly** (or enable auto-sync)
|
||||
3. **Confirm orders promptly** to avoid delays
|
||||
4. **Set tracking** as soon as shipment is dispatched
|
||||
5. **Monitor sync logs** for any failures
|
||||
|
||||
### For Admins
|
||||
|
||||
1. **Review vendor status** regularly via admin dashboard
|
||||
2. **Assist vendors** with connection issues
|
||||
3. **Monitor sync logs** for platform-wide issues
|
||||
4. **Set up alerts** for failed syncs (optional)
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Marketplace Integration (CSV Import)](marketplace-integration.md)
|
||||
- [Vendor RBAC](../backend/vendor-rbac.md)
|
||||
- [Admin Integration Guide](../backend/admin-integration-guide.md)
|
||||
- [Exception Handling](../development/exception-handling.md)
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
- **v1.0** (2025-12-13): Initial Letzshop order integration
|
||||
- GraphQL client for order import
|
||||
- Encrypted credential storage
|
||||
- Fulfillment operations (confirm, reject, tracking)
|
||||
- Admin and vendor API endpoints
|
||||
- Sync logging and queue management
|
||||
Reference in New Issue
Block a user