Laravel Integration
Synchronize customer data from Laravel applications to AsaHome Cloud.
Overview
When customers are created in your Laravel application (e.g., after signing a service agreement), they can be automatically provisioned as users in AsaHome Cloud. This enables seamless onboarding without requiring users to create separate accounts.
Authentication
Laravel integration uses internal API keys for service-to-service authentication.
Generate API Key
Create a secure API key:
openssl rand -base64 32
Configure AsaHome Cloud
Add the key to your .env file:
INTERNAL_API_KEYS=your-laravel-api-key-here,other-service-key
Multiple keys can be comma-separated for different services.
Configure Laravel
Add the AsaHome Cloud configuration to your Laravel app:
// config/services.php
return [
// ... other services
'asacloud' => [
'base_url' => env('ASACLOUD_BASE_URL', 'https://cloud.asahome.io/api/v1'),
'api_key' => env('ASACLOUD_API_KEY'),
],
];
# .env
ASACLOUD_BASE_URL=https://cloud.asahome.io/api/v1
ASACLOUD_API_KEY=your-laravel-api-key-here
Sync Endpoint
Request
POST /api/v1/auth/sync-customer
X-Internal-Api-Key: your-laravel-api-key
Content-Type: application/json
Request Body
{
"id": 123,
"user_id": 456,
"company_name": "Acme Corp",
"first_name": "John",
"last_name": "Doe",
"email": "john@acme.com",
"user_type": 1,
"mrr": 99.99,
"active": true,
"contract_language": "en",
"created_by": 1,
"status": 1
}
Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Customer ID in Laravel |
user_id | integer | No | Associated user ID |
company_name | string | No | Company/organization name |
first_name | string | Yes | Customer first name |
last_name | string | Yes | Customer last name |
email | string | Yes | Customer email (unique) |
user_type | integer | No | User type identifier |
mrr | decimal | No | Monthly recurring revenue |
active | boolean | Yes | Account active status |
contract_language | string | No | Preferred language (en, nl, etc.) |
created_by | integer | No | Admin who created the customer |
status | integer | No | Customer status code |
Response
Success
{
"success": true,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"sync_id": 123,
"email": "john@acme.com",
"firstName": "John",
"lastName": "Doe",
"isActive": true,
"createdAt": "2024-01-15T10:30:00.000Z"
},
"created": true
}
The created field indicates whether a new user was created (true) or an existing user was updated (false).
Error
{
"success": false,
"error": "Email already exists with different sync_id",
"code": "CONFLICT"
}
Laravel Implementation
Customer Sync Service
Create a service class to handle synchronization:
<?php
namespace App\Services;
use App\Models\Customer;
use App\Models\User;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class CustomerSyncService
{
private string $baseUrl;
private string $apiKey;
public function __construct()
{
$this->baseUrl = config('services.asacloud.base_url');
$this->apiKey = config('services.asacloud.api_key');
}
/**
* Sync a customer to AsaHome Cloud
*/
public function syncCustomer(Customer $customer, User $user): array
{
try {
$response = Http::withHeaders([
'X-Internal-Api-Key' => $this->apiKey,
'Content-Type' => 'application/json',
])->post("{$this->baseUrl}/auth/sync-customer", [
'id' => $customer->id,
'user_id' => $customer->user_id,
'company_name' => $user->name,
'first_name' => $user->fname,
'last_name' => $user->lname,
'email' => $user->email,
'user_type' => $customer->user_type,
'mrr' => $customer->mrr,
'active' => $customer->active,
'contract_language' => $customer->contract_language ?? 'en',
'created_by' => $customer->created_by,
'status' => $customer->status,
]);
if ($response->successful()) {
$data = $response->json();
Log::info('Customer synced to AsaHome Cloud', [
'customer_id' => $customer->id,
'cloud_user_id' => $data['user']['id'],
'created' => $data['created'],
]);
return [
'success' => true,
'data' => $data,
];
}
Log::error('Failed to sync customer', [
'customer_id' => $customer->id,
'status' => $response->status(),
'error' => $response->json(),
]);
return [
'success' => false,
'error' => $response->json()['error'] ?? 'Unknown error',
];
} catch (\Exception $e) {
Log::error('Exception syncing customer', [
'customer_id' => $customer->id,
'exception' => $e->getMessage(),
]);
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Sync multiple customers (batch)
*/
public function syncBatch(array $customerIds): array
{
$results = [];
foreach ($customerIds as $customerId) {
$customer = Customer::with('user')->find($customerId);
if ($customer && $customer->user) {
$results[$customerId] = $this->syncCustomer(
$customer,
$customer->user
);
} else {
$results[$customerId] = [
'success' => false,
'error' => 'Customer or user not found',
];
}
}
return $results;
}
}
Event-Based Sync
Automatically sync when customers are created:
<?php
namespace App\Listeners;
use App\Events\CustomerCreated;
use App\Services\CustomerSyncService;
class SyncCustomerToCloud
{
private CustomerSyncService $syncService;
public function __construct(CustomerSyncService $syncService)
{
$this->syncService = $syncService;
}
public function handle(CustomerCreated $event): void
{
$customer = $event->customer;
$user = $customer->user;
if ($user) {
$this->syncService->syncCustomer($customer, $user);
}
}
}
Register the listener:
// app/Providers/EventServiceProvider.php
protected $listen = [
CustomerCreated::class => [
SyncCustomerToCloud::class,
],
];
Artisan Command
Create a command for manual/batch sync:
<?php
namespace App\Console\Commands;
use App\Models\Customer;
use App\Services\CustomerSyncService;
use Illuminate\Console\Command;
class SyncCustomersToCloud extends Command
{
protected $signature = 'customers:sync-cloud
{--all : Sync all customers}
{--id=* : Specific customer IDs}';
protected $description = 'Sync customers to AsaHome Cloud';
public function handle(CustomerSyncService $syncService): int
{
if ($this->option('all')) {
$customers = Customer::with('user')
->where('active', true)
->get();
} elseif ($ids = $this->option('id')) {
$customers = Customer::with('user')
->whereIn('id', $ids)
->get();
} else {
$this->error('Specify --all or --id=<id>');
return 1;
}
$bar = $this->output->createProgressBar($customers->count());
$bar->start();
$success = 0;
$failed = 0;
foreach ($customers as $customer) {
if ($customer->user) {
$result = $syncService->syncCustomer($customer, $customer->user);
if ($result['success']) {
$success++;
} else {
$failed++;
$this->newLine();
$this->warn("Failed: Customer {$customer->id} - {$result['error']}");
}
}
$bar->advance();
}
$bar->finish();
$this->newLine(2);
$this->info("Synced: {$success} | Failed: {$failed}");
return $failed > 0 ? 1 : 0;
}
}
Usage:
# Sync all active customers
php artisan customers:sync-cloud --all
# Sync specific customers
php artisan customers:sync-cloud --id=123 --id=456
Sync Behavior
New Customer
When a customer doesn't exist in AsaHome Cloud (based on sync_id or email):
- New user account is created
- Random password is generated (user must reset)
sync_idlinks to Laravel customer ID
Existing Customer
When a customer already exists:
- User details are updated (name, active status, etc.)
- Email changes are handled if
sync_idmatches - MRR and other metadata are updated
Conflict Resolution
| Scenario | Behavior |
|---|---|
Same sync_id, different email | Update email |
Same email, different sync_id | Error (conflict) |
Same email, no sync_id in cloud | Link accounts |
Error Handling
Retry Logic
Implement exponential backoff for transient failures:
use Illuminate\Support\Facades\Http;
$response = Http::retry(3, 100, function ($exception, $request) {
// Retry on connection errors and 5xx responses
return $exception instanceof \Illuminate\Http\Client\ConnectionException
|| ($exception instanceof \Illuminate\Http\Client\RequestException
&& $exception->response->serverError());
})->withHeaders([
'X-Internal-Api-Key' => $this->apiKey,
])->post("{$this->baseUrl}/auth/sync-customer", $data);
Queue Failed Syncs
Use Laravel queues for reliability:
<?php
namespace App\Jobs;
use App\Models\Customer;
use App\Services\CustomerSyncService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SyncCustomerJob implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 60;
public function __construct(
public Customer $customer
) {}
public function handle(CustomerSyncService $syncService): void
{
$result = $syncService->syncCustomer(
$this->customer,
$this->customer->user
);
if (!$result['success']) {
throw new \Exception($result['error']);
}
}
}
Security Considerations
- API Key Rotation: Periodically rotate internal API keys
- HTTPS Only: Always use TLS for sync requests
- IP Whitelisting: Optionally restrict sync endpoint to known IPs
- Audit Logging: Log all sync operations for compliance
- Rate Limiting: Prevent abuse of sync endpoint
Next Steps
- API Reference: Auth - Sync endpoint details
- Security Guide - Security best practices
- Configuration - Environment setup