WordPress e API Esterne: Integrazioni Sicure

Collegare WordPress al gestionale aziendale, al CRM o a servizi esterni è sempre più comune. Ma farlo male può esporre dati sensibili e rallentare il sito. Ecco il modo corretto.

Architettura di Integrazione

# Pattern consigliato
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  WordPress  │────▶│  Middleware │────▶│   CRM/ERP   │
│   (REST)    │◀────│  (Queue)    │◀────│   (API)     │
└─────────────┘     └─────────────┘     └─────────────┘

# Vantaggi:
# - WordPress non esposto direttamente
# - Retry automatico in caso di errore
# - Rate limiting gestito
# - Log centralizzato

1. REST API Sicure in WordPress

Autenticazione Endpoint Custom

// Registra endpoint protetto
add_action( 'rest_api_init', function() {
    register_rest_route( 'wpboutique/v1', '/sync-orders', [
        'methods'             => 'POST',
        'callback'            => 'sync_orders_callback',
        'permission_callback' => 'verify_api_key',
    ]);
});

// Verifica API key
function verify_api_key( $request ) {
    $api_key = $request->get_header( 'X-API-Key' );
    $valid_key = get_option( 'external_api_key' );
    
    if ( ! $api_key || ! hash_equals( $valid_key, $api_key ) ) {
        return new wp_Error( 
            'unauthorized', 
            'API key non valida', 
            [ 'status' => 401 ] 
        );
    }
    return true;
}

// Callback protetto
function sync_orders_callback( $request ) {
    $data = $request->get_json_params();
    
    // Sanitizza TUTTO
    $order_id = absint( $data['order_id'] ?? 0 );
    $status = sanitize_text_field( $data['status'] ?? '' );
    
    // Processa...
    return rest_ensure_response( [ 'success' => true ] );
}

Rate Limiting

// Limita richieste per IP
function rate_limit_check( $request ) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $transient_key = 'rate_limit_' . md5( $ip );
    
    $count = get_transient( $transient_key ) ?: 0;
    
    if ( $count >= 100 ) { // Max 100 richieste/minuto
        return new wp_Error( 
            'rate_limited', 
            'Troppe richieste', 
            [ 'status' => 429 ] 
        );
    }
    
    set_transient( $transient_key, $count + 1, MINUTE_IN_SECONDS );
    return true;
}

2. Chiamate API Esterne da WordPress

Pattern Corretto con wp_remote

// SEMPRE usare wp_remote_* invece di cURL diretto
function call_external_api( $endpoint, $data ) {
    $response = wp_remote_post( $endpoint, [
        'timeout'     => 15, // Timeout ragionevole
        'redirection' => 0,  // No redirect automatici
        'httpversion' => '1.1',
        'headers'     => [
            'Content-Type'  => 'application/json',
            'Authorization' => 'Bearer ' . EXTERNAL_API_TOKEN,
        ],
        'body'        => wp_json_encode( $data ),
        'sslverify'   => true, // MAI disabilitare in produzione!
    ]);
    
    if ( is_wp_error( $response ) ) {
        // Log errore
        error_log( 'API Error: ' . $response->get_error_message() );
        return false;
    }
    
    $code = wp_remote_retrieve_response_code( $response );
    $body = wp_remote_retrieve_body( $response );
    
    if ( $code !== 200 ) {
        error_log( "API returned {$code}: {$body}" );
        return false;
    }
    
    return json_decode( $body, true );
}

Caching Risposte API

// Cache risposte per evitare chiamate ripetute
function get_external_data( $resource_id ) {
    $cache_key = 'external_data_' . $resource_id;
    $cached = get_transient( $cache_key );
    
    if ( $cached !== false ) {
        return $cached;
    }
    
    $data = call_external_api( 
        "https://api.example.com/resource/{$resource_id}" 
    );
    
    if ( $data ) {
        set_transient( $cache_key, $data, HOUR_IN_SECONDS );
    }
    
    return $data;
}

3. Webhook In Ingresso

Verificare Firma Webhook

// Verifica HMAC signature (standard per Stripe, GitHub, etc)
function verify_webhook_signature( $request ) {
    $payload = $request->get_body();
    $signature = $request->get_header( 'X-Signature' );
    $secret = WEBHOOK_SECRET;
    
    $expected = hash_hmac( 'sha256', $payload, $secret );
    
    if ( ! hash_equals( $expected, $signature ) ) {
        return new wp_Error( 
            'invalid_signature', 
            'Firma webhook non valida', 
            [ 'status' => 401 ] 
        );
    }
    
    return true;
}

4. Gestione Asincrona con Action Scheduler

Per operazioni lunghe, non bloccare la richiesta HTTP:

// Installa Action Scheduler (incluso in WooCommerce)
// oppure: composer require woocommerce/action-scheduler

// Schedula task invece di eseguirlo subito
function handle_order_webhook( $request ) {
    $order_data = $request->get_json_params();
    
    // Schedula processing asincrono
    as_schedule_single_action( 
        time(), 
        'process_external_order', 
        [ 'order_data' => $order_data ],
        'wpboutique'
    );
    
    // Rispondi subito (webhook non va in timeout)
    return rest_ensure_response( [ 'queued' => true ] );
}

// Handler asincrono
add_action( 'process_external_order', function( $order_data ) {
    // Elaborazione lunga qui...
    // Se fallisce, Action Scheduler ritenta automaticamente
}, 10, 1 );

5. Sicurezza: Checklist

□ API key in variabili d'ambiente, MAI nel codice
□ HTTPS obbligatorio (sslverify = true)
□ Verifica firma per webhook
□ Rate limiting su endpoint esposti
□ Sanitizza TUTTO l'input
□ Log tutte le richieste per audit
□ Timeout ragionevoli (10-30s)
□ Retry con exponential backoff
□ Whitelist IP se possibile

Variabili d’Ambiente

// wp-config.php - Credenziali da environment
define( 'EXTERNAL_API_TOKEN', getenv( 'EXTERNAL_API_TOKEN' ) );
define( 'WEBHOOK_SECRET', getenv( 'WEBHOOK_SECRET' ) );

// In .env (non committare!)
EXTERNAL_API_TOKEN=your-secret-token
WEBHOOK_SECRET=your-webhook-secret

Devi integrare WordPress con il tuo gestionale? Progettiamo integrazioni sicure e scalabili. Contattaci per una consulenza architetturale.