> ## Documentation Index
> Fetch the complete documentation index at: https://dkkangoyazlmteknolojiticareta.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Build a robust, production-ready integration

## Authentication

### Store Credentials Securely

<Warning>
  Never hard-code API credentials in your source code!
</Warning>

<Tabs>
  <Tab title="✅ Good">
    ```javascript theme={null}
    // Use environment variables
    const appSecret = process.env.DUKKANGO_APP_SECRET;
    const restSecret = process.env.DUKKANGO_REST_SECRET;

    // Or secure key management
    const credentials = await keyVault.getSecret('dukkango-api-keys');
    ```
  </Tab>

  <Tab title="❌ Bad">
    ```javascript theme={null}
    // Never do this!
    const appSecret = 'my-secret-key-123';
    const restSecret = 'restaurant-key-456';
    ```
  </Tab>
</Tabs>

### Implement Token Refresh

<AccordionGroup>
  <Accordion title="Proactive Refresh Strategy">
    Don't wait for tokens to expire. Refresh them **before** expiration.

    ```javascript theme={null}
    class TokenManager {
      constructor() {
        this.token = null;
        this.expiresAt = null;
      }
      
      async getToken() {
        // Refresh if expired or expiring within 1 hour
        if (!this.token || Date.now() > this.expiresAt - 3600000) {
          await this.refresh();
        }
        return this.token;
      }
      
      async refresh() {
        const response = await login();
        this.token = response.access_token;
        this.expiresAt = new Date(response.expiration_date).getTime();
      }
    }
    ```
  </Accordion>

  <Accordion title="Handle 401 Errors">
    Always catch authentication errors and retry after refreshing token.

    ```javascript theme={null}
    async function apiCall(endpoint, options) {
      try {
        return await fetch(endpoint, {
          ...options,
          headers: {
            ...options.headers,
            'Access-Token': await tokenManager.getToken()
          }
        });
      } catch (error) {
        if (error.status === 401) {
          // Force refresh and retry once
          await tokenManager.refresh();
          return await fetch(endpoint, {
            ...options,
            headers: {
              ...options.headers,
              'Access-Token': await tokenManager.getToken()
            }
          });
        }
        throw error;
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Order Management

### Always Acknowledge Orders

<Steps>
  <Step title="Fetch Order">
    ```bash theme={null}
    GET /orders/get-current
    ```
  </Step>

  <Step title="Import to POS">
    Store order in your local database/system
  </Step>

  <Step title="Acknowledge Immediately">
    ```bash theme={null}
    PUT /orders/success/{payment_key}
    ```

    Don't wait! Acknowledge right after importing.
  </Step>
</Steps>

<Warning>
  If you don't acknowledge orders, they will repeat in every `/get-current` call, causing duplicate imports!
</Warning>

### Use Correct IDs

<Tabs>
  <Tab title="✅ Correct">
    ```javascript theme={null}
    // Use payment_key (UUID) for API operations
    const order = await getOrders();
    await acceptOrder(order.payment_key);  // ✅
    await successOrder(order.payment_key); // ✅
    await completeOrder(order.payment_key); // ✅
    ```
  </Tab>

  <Tab title="❌ Wrong">
    ```javascript theme={null}
    // Don't use id (integer)
    const order = await getOrders();
    await acceptOrder(order.id);  // ❌ Will fail!
    ```
  </Tab>
</Tabs>

<Info>
  **Display:** Use `id` (integer) for showing to users\
  **API Calls:** Use `payment_key` (UUID) for all operations
</Info>

### Handle All Statuses

Your POS should gracefully handle orders in any status:

```javascript theme={null}
function handleOrder(order) {
  switch (order.status_id) {
    case 1: // RECEIVED
      return displayNewOrder(order);
    
    case 2: // CONFIRMED
      return displayPreparingOrder(order);
    
    case 16: // IN_DELIVERY
      return displayDeliveryOrder(order);
    
    case 5: // COMPLETE
      return archiveOrder(order);
    
    case 6: // CANCELED
      return markCanceled(order);
    
    default:
      console.warn(`Unknown status: ${order.status_id}`);
      return displayOrder(order); // Fallback
  }
}
```

## Polling Strategy

### Respect Rate Limits

<Warning>
  `/orders/get-current` and `/foods/get-foods` have **30-second** cooldowns.
</Warning>

```javascript theme={null}
// ✅ Good: Poll every 30+ seconds
setInterval(pollOrders, 30000);

// ❌ Bad: Too frequent
setInterval(pollOrders, 5000); // Will hit rate limit!
```

### Implement Exponential Backoff

```javascript theme={null}
async function pollWithBackoff() {
  let delay = 30000; // Start with 30s
  const maxDelay = 300000; // Max 5 minutes
  
  while (true) {
    try {
      await pollOrders();
      delay = 30000; // Reset on success
    } catch (error) {
      if (error.status === 429) {
        // Rate limited - increase delay
        delay = Math.min(delay * 2, maxDelay);
        console.log(`Rate limited. Waiting ${delay}ms`);
      }
    }
    
    await sleep(delay);
  }
}
```

### Cache Menu Data

<Tip>
  Don't fetch the full menu on every order! Cache it locally.
</Tip>

```javascript theme={null}
class MenuCache {
  constructor() {
    this.menu = null;
    this.lastFetch = null;
    this.cacheDuration = 30 * 60 * 1000; // 30 minutes
  }
  
  async getMenu() {
    const now = Date.now();
    
    // Return cached if recent
    if (this.menu && (now - this.lastFetch) < this.cacheDuration) {
      return this.menu;
    }
    
    // Fetch fresh menu
    this.menu = await fetchMenu();
    this.lastFetch = now;
    return this.menu;
  }
  
  invalidate() {
    this.menu = null;
    this.lastFetch = null;
  }
}
```

## Error Handling

### Implement Comprehensive Error Handling

```javascript theme={null}
async function makeApiCall(endpoint, options) {
  try {
    const response = await fetch(endpoint, options);
    const data = await response.json();
    
    if (!data.status) {
      // API returned error
      throw new ApiError(data.error, data.message, response.status);
    }
    
    return data.data;
  } catch (error) {
    // Handle specific errors
    if (error instanceof ApiError) {
      handleApiError(error);
    } else if (error.name === 'TypeError') {
      // Network error
      console.error('Network error:', error);
      throw new Error('Unable to connect to API');
    } else {
      // Unknown error
      console.error('Unexpected error:', error);
      throw error;
    }
  }
}

function handleApiError(error) {
  switch (error.status) {
    case 401:
      // Token expired - refresh and retry
      return refreshTokenAndRetry();
    
    case 429:
      // Rate limited - wait and retry
      return waitAndRetry(30000);
    
    case 404:
      // Not found - log and notify
      logError('Resource not found', error);
      notifyUser('Order not found');
      break;
    
    case 422:
      // Validation error - fix and retry
      logError('Validation failed', error);
      notifyUser('Invalid request');
      break;
    
    default:
      // Other errors
      logError('API error', error);
      notifyUser('Something went wrong');
  }
}
```

### Log Errors with Context

```javascript theme={null}
function logError(message, error, context = {}) {
  console.error({
    message,
    error: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
    context: {
      ...context,
      userId: currentUser?.id,
      restaurantId: currentRestaurant?.id
    }
  });
  
  // Send to error tracking service
  errorTracker.captureException(error, context);
}
```

## Data Validation

### Validate Before Sending

<Tabs>
  <Tab title="Accept Order">
    ```javascript theme={null}
    function validateAcceptRequest(preparingTime) {
      if (typeof preparingTime !== 'number') {
        throw new Error('preparing_time must be a number');
      }
      
      if (preparingTime < 5 || preparingTime > 120) {
        throw new Error('preparing_time must be between 5 and 120 minutes');
      }
      
      return true;
    }

    // Use before API call
    validateAcceptRequest(preparingTime);
    await acceptOrder(orderId, preparingTime);
    ```
  </Tab>

  <Tab title="Cancel Order">
    ```javascript theme={null}
    function validateCancelRequest(reasonId) {
      const validReasons = [1, 2, 3, 4, 5, 6, 7, 8];
      
      if (!validReasons.includes(reasonId)) {
        throw new Error(`Invalid reason_id: ${reasonId}`);
      }
      
      return true;
    }

    // Use before API call
    validateCancelRequest(reasonId);
    await cancelOrder(orderId, reasonId);
    ```
  </Tab>
</Tabs>

### Validate Received Data

```javascript theme={null}
function validateOrder(order) {
  const required = ['id', 'payment_key', 'status_id', 'customer', 'foods'];
  
  for (const field of required) {
    if (!(field in order)) {
      throw new Error(`Missing required field: ${field}`);
    }
  }
  
  // Validate UUIDs
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
  if (!uuidRegex.test(order.payment_key)) {
    throw new Error('Invalid payment_key format');
  }
  
  return true;
}
```

## Performance

### Use Connection Pooling

```javascript theme={null}
// Reuse HTTP connections
const agent = new https.Agent({
  keepAlive: true,
  maxSockets: 10
});

const apiClient = axios.create({
  httpsAgent: agent,
  timeout: 30000
});
```

### Implement Request Timeouts

```javascript theme={null}
async function apiCallWithTimeout(endpoint, options, timeout = 30000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(endpoint, {
      ...options,
      signal: controller.signal
    });
    return response;
  } finally {
    clearTimeout(timeoutId);
  }
}
```

### Batch Operations When Possible

```javascript theme={null}
// ❌ Bad: Update items one by one
for (const item of menuItems) {
  await updateItemStatus(item.id, 'passive');
}

// ✅ Good: Collect and process in batches
const updates = menuItems.map(item => ({
  id: item.id,
  status: 'passive'
}));

// Process updates efficiently
await batchUpdateItems(updates);
```

## Testing

### Test All Scenarios

<AccordionGroup>
  <Accordion title="Happy Path Testing">
    * Place test order
    * Accept order
    * Mark on the way
    * Complete order
    * Verify status at each step
  </Accordion>

  <Accordion title="Error Scenario Testing">
    * Invalid credentials
    * Expired token
    * Rate limiting
    * Network timeouts
    * Invalid order IDs
    * Missing required fields
  </Accordion>

  <Accordion title="Edge Case Testing">
    * Orders with no modifiers
    * Orders with multiple modifier groups
    * Scheduled orders
    * Cash vs card payments
    * Different courier types
    * Order repetition after status change
  </Accordion>
</AccordionGroup>

### Test Environment

```javascript theme={null}
const config = {
  development: {
    baseUrl: 'https://posapitest.fuudy.co',
    logLevel: 'debug'
  },
  production: {
    baseUrl: 'https://www.xn--dkkango-n2a.com/api/integrations',
    logLevel: 'error'
  }
};

const env = process.env.NODE_ENV || 'development';
const apiConfig = config[env];
```

## Monitoring

### Track Key Metrics

```javascript theme={null}
class ApiMetrics {
  constructor() {
    this.metrics = {
      ordersReceived: 0,
      ordersAccepted: 0,
      ordersCanceled: 0,
      ordersCompleted: 0,
      apiErrors: 0,
      responseTime: []
    };
  }
  
  recordOrder(status) {
    this.metrics[status]++;
  }
  
  recordResponseTime(duration) {
    this.metrics.responseTime.push(duration);
  }
  
  recordError() {
    this.metrics.apiErrors++;
  }
  
  getStats() {
    return {
      ...this.metrics,
      avgResponseTime: average(this.metrics.responseTime),
      errorRate: this.metrics.apiErrors / this.getTotalRequests()
    };
  }
}
```

### Set Up Alerts

Monitor for:

* High error rates (> 5%)
* Slow response times (> 5 seconds)
* Missing orders (gaps in order IDs)
* Authentication failures
* Rate limit hits

## Security Checklist

<Accordion title="Complete Security Checklist">
  * [ ] API credentials stored in environment variables
  * [ ] Credentials encrypted at rest
  * [ ] HTTPS only (no HTTP fallback)
  * [ ] SSL certificates validated
  * [ ] Access tokens never logged
  * [ ] Error messages don't expose sensitive data
  * [ ] Request/response logging excludes credentials
  * [ ] Regular credential rotation policy
  * [ ] Monitoring for unauthorized access
  * [ ] Rate limiting handled gracefully
</Accordion>

## Summary

<CardGroup cols={2}>
  <Card title="Do's" icon="check">
    * Store credentials securely
    * Acknowledge orders immediately
    * Use payment\_key for API calls
    * Respect rate limits (30s)
    * Cache menu data locally
    * Implement error handling
    * Validate all data
    * Log errors with context
    * Monitor API health
    * Test thoroughly
  </Card>

  <Card title="Don'ts" icon="xmark">
    * Hard-code credentials
    * Forget to acknowledge orders
    * Use integer ID for operations
    * Poll more than every 30s
    * Fetch menu on every request
    * Ignore error responses
    * Skip data validation
    * Log sensitive data
    * Deploy without monitoring
    * Skip testing
  </Card>
</CardGroup>

<Card title="Need Help?" icon="question" href="mailto:support@dkkango.com">
  Have questions about best practices? Contact our integration support team.
</Card>
