Skip to main content

Offline Sync API Reference

API endpoints untuk batch synchronization dan conflict resolution dalam offline-first architecture.

πŸ” Authentication

Semua endpoint memerlukan JWT authentication token.
Authorization: Bearer YOUR_JWT_TOKEN

πŸ“‘ Endpoints

1. Batch Sync

Sync multiple transactions, payments, dan voids dalam satu request.
POST /api/v1/pos/offline/batch-sync
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "transactions": [
    {
      "offline_id": "550e8400-e29b-41d4-a716-446655440000",
      "offline_reference": "DEVICE001-20251013-001",
      "device_id": "DEVICE001",
      "created_at_device": "2025-10-13T10:30:00Z",
      "branch_code": "BRN-MST-OSK00001",
      "payment_method": "CASH",
      "id_user_apps": 5,
      "type_transaction": 1,
      "subtotal": 30000,
      "discount_total": 0,
      "tax_total": 3000,
      "grand_total": 33000,
      "status": "paid",
      "payment_status": "paid",
      "sync_status": "pending"
    }
  ],
  "payments": [],
  "voids": [],
  "device_id": "DEVICE001",
  "synced_at": "2025-10-13T11:00:00Z"
}

Request Body

transactions
array
required
Array of offline transactions to sync
payments
array
default:"[]"
Array of offline payments to sync
voids
array
default:"[]"
Array of offline voids to sync
device_id
string
required
Device identifier
synced_at
datetime
required
ISO 8601 timestamp when sync initiated

Response (Success)

code
integer
HTTP status code (200)
data
object

Response Example

{
  "code": 200,
  "data": {
    "success": true,
    "total_items": 1,
    "success_count": 1,
    "failed_count": 0,
    "results": [
      {
        "offline_id": "550e8400-e29b-41d4-a716-446655440000",
        "type": "transaction",
        "success": true,
        "server_id": 185,
        "transaction_code": "TRX-MC01-BRN-MST-OSK00001-251013-5UJW-6SWG-7",
        "error": null,
        "conflict_detected": false
      }
    ],
    "conflicts": [],
    "synced_at": "2025-10-13T11:00:05Z"
  }
}

Response (With Conflict)

{
  "code": 200,
  "data": {
    "success": true,
    "total_items": 1,
    "success_count": 0,
    "failed_count": 1,
    "results": [
      {
        "offline_id": "550e8400-e29b-41d4-a716-446655440000",
        "type": "transaction",
        "success": false,
        "server_id": 185,
        "transaction_code": "TRX-MC01-BRN-MST-OSK00001-251013-5UJW-6SWG-7",
        "error": "Duplicate transaction detected",
        "conflict_detected": true
      }
    ],
    "conflicts": [
      {
        "conflict_id": "550e8400-e29b-41d4-a716-446655440000",
        "offline_id": "550e8400-e29b-41d4-a716-446655440000",
        "type": "transaction",
        "conflict_type": "duplicate",
        "local_data": {
          "offline_reference": "DEVICE001-20251013-001",
          "grand_total": 33000
        },
        "server_data": {
          "id": 185,
          "transaction_code": "TRX-MC01-BRN-MST-OSK00001-251013-5UJW-6SWG-7",
          "grand_total": 33000
        },
        "suggested_fix": "keep_server",
        "resolution_url": "/api/v1/pos/offline/conflicts/550e8400-e29b-41d4-a716-446655440000/resolve"
      }
    ],
    "synced_at": "2025-10-13T11:00:05Z"
  }
}

Error Responses

{
  "code": 400,
  "message": "Invalid request body",
  "errors": [
    {
      "field": "transactions[0].offline_reference",
      "message": "offline_reference is required"
    }
  ]
}

2. Resolve Conflict

Resolve detected conflict dengan strategy tertentu.
POST /api/v1/pos/offline/conflicts/550e8400-e29b-41d4-a716-446655440000/resolve
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "strategy": "keep_server"
}

Path Parameters

conflict_id
string
required
Conflict ID from batch sync response

Request Body

strategy
string
required
Resolution strategy:
  • keep_server: Keep server data, discard local
  • keep_local: Keep local data, overwrite server (not yet implemented)
  • merge: Merge both data (not yet implemented)
merged_data
object
Required if strategy is β€œmerge”

Response (Success)

{
  "code": 200,
  "data": {
    "conflict_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "resolved",
    "message": "Conflict resolved successfully",
    "resolved_at": "2025-10-13T11:05:00Z"
  }
}

Error Responses

{
  "code": 404,
  "message": "Conflict not found",
  "error": "No conflict with ID: 550e8400-e29b-41d4-a716-446655440000"
}

πŸ”„ Sync Flow Diagram


πŸ“Š Rate Limiting

EndpointRate LimitWindow
Batch Sync10 requests1 minute
Resolve Conflict30 requests1 minute

πŸ’‘ Best Practices

Batch Size

Limit batch size to 50 transactions per request untuk optimal performance.

Retry Logic

Implement exponential backoff untuk retry:
  • 1st retry: 5 seconds
  • 2nd retry: 10 seconds
  • 3rd retry: 30 seconds
  • Max retries: 3

Idempotency

Gunakan offline_reference sebagai idempotency key. Server akan detect duplicate dan return existing data.

Error Handling

try {
  final response = await apiService.batchSync(request);
  
  // Handle success
  for (final result in response.results) {
    if (result.success) {
      await updateLocalTransaction(result);
    } else {
      await markAsFailed(result);
    }
  }
  
  // Handle conflicts
  if (response.conflicts.isNotEmpty) {
    await showConflictDialog(response.conflicts);
  }
  
} on DioException catch (e) {
  if (e.response?.statusCode == 401) {
    // Token expired, refresh and retry
    await refreshToken();
    return retry();
  } else if (e.type == DioExceptionType.connectionTimeout) {
    // Network timeout, queue for retry
    await queueForRetry();
  } else {
    // Other errors
    await logError(e);
  }
}

πŸ§ͺ Testing

Postman Collection

Download Postman collection: offline-sync-api.postman_collection.json

REST Client Examples

See complete examples in: api/pos/offline-sync.http

πŸ†˜ Support

Need Help?