# 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