Pagamenti con Stripe in WordPress

Integrare i pagamenti con Stripe in WordPress è una delle scelte più comuni per vendere prodotti, servizi e abbonamenti online. Stripe offre un’infrastruttura completa per accettare carte, wallet e metodi di pagamento locali, con strumenti per la gestione di rimborsi, contestazioni, fatturazione e pagamenti ricorrenti. WordPress, dal canto suo, è estremamente flessibile: puoi implementare Stripe tramite plugin pronti, oppure con un’integrazione personalizzata via API.

In questo articolo trovi una panoramica approfondita delle opzioni disponibili, i passaggi operativi per configurare Stripe correttamente, e alcuni esempi di codice (server e client) utili per casi specifici. L’obiettivo è aiutarti a costruire un flusso di pagamento solido, conforme e facile da mantenere.

Quando usare Stripe in WordPress

  • E-commerce: vendita di prodotti fisici o digitali, con gestione di carrello, spedizioni e tassazione.
  • Pagamenti singoli: consulenze, prenotazioni, ticket, donazioni, acconti.
  • Abbonamenti: membership, software, servizi in abbonamento con rinnovi automatici.
  • Pagamenti in modalità “invoicing”: fatture e link di pagamento (Payment Links) per processi più manuali.

Prerequisiti e concetti fondamentali

Account Stripe e chiavi API

Stripe separa l’ambiente Test e l’ambiente Live. Avrai tipicamente:

  • Publishable key: usata lato browser (JavaScript) per inizializzare Stripe.
  • Secret key: usata lato server (PHP) per creare PaymentIntent, SetupIntent, abbonamenti, rimborsi, ecc.
  • Webhook signing secret: usata per verificare le chiamate in ingresso dai webhook.

Regola importante: la secret key non deve mai finire nel codice client o essere esposta pubblicamente.

SCA e 3D Secure

In Europa è frequente dover gestire l’autenticazione forte del cliente (SCA) con 3D Secure. La strategia consigliata è utilizzare PaymentIntent e/o Checkout di Stripe, che gestiscono automaticamente i passaggi di autenticazione quando necessari.

PCI Compliance

Una delle ragioni principali per usare Stripe Checkout o Stripe Elements è semplificare la conformità PCI: i dati della carta vengono raccolti da Stripe e non transitano direttamente dal tuo server (riducendo la superficie di rischio).

Le strade principali: plugin o integrazione custom

Approccio Ideale per Pro Contro
WooCommerce + gateway Stripe E-commerce con carrello Gestione completa (prodotti, tasse, email, spedizioni), ampia compatibilità Maggiore complessità e dipendenze
Plugin “form di pagamento” (es. WP Simple Pay e simili) Pagamenti singoli o ricorrenti senza carrello Configurazione rapida, checkout snello, spesso supporto a Apple Pay/Google Pay Personalizzazioni avanzate limitate ai punti previsti dal plugin/piano
Integrazione custom via API (PaymentIntent / Checkout) Flussi su misura, marketplace, logiche complesse Massima flessibilità, controllo totale su UX e logica Richiede sviluppo e manutenzione (webhook, sicurezza, aggiornamenti)

Opzione 1: WooCommerce con Stripe

Configurazione di base

  1. Installa e configura WooCommerce (valuta, tasse, spedizioni, pagine di checkout).
  2. Installa un gateway Stripe compatibile con WooCommerce.
  3. Inserisci le chiavi API Test e abilita la modalità test.
  4. Effettua un acquisto di prova con le carte test di Stripe e verifica l’intero flusso (ordine, email, stato pagamento).
  5. Configura i webhook se richiesti dal plugin per sincronizzare eventi (pagamenti riusciti, rimborsi, dispute, rinnovi).
  6. Passa a Live solo dopo aver verificato correttamente impostazioni e webhooks.

Pagamenti ricorrenti e abbonamenti

Se vendi abbonamenti, spesso userai un’estensione dedicata (per esempio WooCommerce Subscriptions) più un gateway Stripe che supporti rinnovi, metodi di pagamento salvati e gestione di SCA per i pagamenti off-session. In questi casi i webhook sono cruciali per mantenere allineati stati di pagamento e rinnovi.

Opzione 2: plugin per pagamenti senza carrello

Se non ti serve un carrello, un plugin “payment form” è spesso la scelta più rapida. In genere offre:

  • Form di pagamento in pagina o popup
  • Stripe Checkout oppure Elements
  • Campi personalizzati (nome, indirizzo, note)
  • Pagamenti ricorrenti e salvataggio metodo di pagamento (in base al plugin/piano)
  • Integrazioni con email marketing e CRM

Il punto chiave è valutare: quanto ti serve personalizzare il flusso e quanto vuoi delegare al plugin (tasse, fatture, ricevute, reportistica, automazioni).

Opzione 3: integrazione custom con Stripe Checkout

Stripe Checkout è un’interfaccia di pagamento ospitata da Stripe, ottimizzata per conversione e conformità. È spesso il compromesso migliore tra semplicità e robustezza: tu generi una “sessione” lato server, reindirizzi l’utente e poi gestisci conferme e webhook.

Architettura consigliata

  • Frontend WordPress: un pulsante “Paga” che chiama un endpoint sul tuo sito.
  • Endpoint server: crea la Checkout Session con la secret key e ritorna l’URL di checkout.
  • Webhook: riceve gli eventi e aggiorna stato ordine/abbonamento/utente nel database WordPress.

Esempio: endpoint WordPress REST API che crea una Checkout Session

Di seguito un esempio didattico che registra un endpoint REST e crea una sessione Checkout. In produzione devi aggiungere controlli su autenticazione, prezzi, permessi e protezioni anti-abuso (nonce, rate limit, validazione input).

<?php
/**
 * Plugin Name: Stripe Checkout Example
 * Description: Endpoint REST per creare una Checkout Session Stripe.
 */

add_action('rest_api_init', function () {
  register_rest_route('stripe/v1', '/checkout-session', [
    'methods'  => 'POST',
    'callback' => 'my_stripe_create_checkout_session',
    'permission_callback' => '__return_true',
  ]);
});

function my_stripe_create_checkout_session(WP_REST_Request $request) {
  $body = json_decode($request->get_body(), true);
  $amount = isset($body['amount']) ? (int) $body['amount'] : 0; // importo in centesimi
  $currency = isset($body['currency']) ? sanitize_text_field($body['currency']) : 'eur';

  if ($amount < 50) {
    return new WP_REST_Response(['error' => 'Importo non valido'], 400);
  }

  // Carica Stripe PHP SDK (via Composer) e imposta la chiave segreta
  // require_once __DIR__ . '/vendor/autoload.php';
  // \Stripe\Stripe::setApiKey(get_option('my_stripe_secret_key'));

  // Esempio: ritorniamo dati fittizi se non hai l'SDK installato.
  // In produzione, sostituisci con la chiamata reale a \Stripe\Checkout\Session::create(...)

  $success_url = add_query_arg(['stripe' => 'success'], home_url('/esito-pagamento/'));
  $cancel_url  = add_query_arg(['stripe' => 'cancel'],  home_url('/esito-pagamento/'));

  /*
  $session = \Stripe\Checkout\Session::create([
    'mode' => 'payment',
    'line_items' => [[
      'price_data' => [
        'currency' => $currency,
        'product_data' => ['name' => 'Servizio XYZ'],
        'unit_amount' => $amount,
      ],
      'quantity' => 1,
    ]],
    'success_url' => $success_url . '&session_id={CHECKOUT_SESSION_ID}',
    'cancel_url'  => $cancel_url,
  ]);

  return new WP_REST_Response(['url' => $session->url], 200);
  */

  return new WP_REST_Response(['url' => 'https://checkout.stripe.com/pay/cs_test_placeholder'], 200);
}

Chiamata dal frontend (fetch) e redirect al Checkout

async function avviaCheckout() {
  const res = await fetch('/wp-json/stripe/v1/checkout-session', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount: 4990, currency: 'eur' })
  });

  const data = await res.json();
  if (!res.ok) {
    alert(data.error || 'Errore durante la creazione della sessione');
    return;
  }

  window.location.href = data.url;
}

document.querySelector('#pagaOra')?.addEventListener('click', (e) => {
  e.preventDefault();
  avviaCheckout();
});

Questo approccio riduce al minimo le responsabilità del tuo frontend: Stripe gestisce UI, SCA, wallet, localizzazioni e aggiornamenti di sicurezza.

Integrazione custom con Stripe Elements e PaymentIntent

Se vuoi mantenere il pagamento nella tua pagina (senza redirect), puoi usare Stripe Elements. In questo caso:

  • Lato server crei un PaymentIntent e restituisci il client_secret.
  • Lato client monti Elements e confermi il pagamento con confirmPayment o funzioni equivalenti.
  • Usi i webhook per confermare in modo affidabile l’esito (evitando di fidarti solo del redirect).

Esempio (server): creazione PaymentIntent via endpoint REST

<?php
add_action('rest_api_init', function () {
  register_rest_route('stripe/v1', '/payment-intent', [
    'methods'  => 'POST',
    'callback' => 'my_stripe_create_payment_intent',
    'permission_callback' => '__return_true',
  ]);
});

function my_stripe_create_payment_intent(WP_REST_Request $request) {
  $body = json_decode($request->get_body(), true);
  $amount = isset($body['amount']) ? (int) $body['amount'] : 0;

  if ($amount < 50) {
    return new WP_REST_Response(['error' => 'Importo non valido'], 400);
  }

  // require_once __DIR__ . '/vendor/autoload.php';
  // \Stripe\Stripe::setApiKey(get_option('my_stripe_secret_key'));

  /*
  $intent = \Stripe\PaymentIntent::create([
    'amount' => $amount,
    'currency' => 'eur',
    'automatic_payment_methods' => ['enabled' => true],
    'metadata' => [
      'wp_user_id' => get_current_user_id(),
      'order_ref' => 'ORD-' . time(),
    ],
  ]);

  return new WP_REST_Response(['clientSecret' => $intent->client_secret], 200);
  */

  return new WP_REST_Response(['clientSecret' => 'pi_test_client_secret_placeholder'], 200);
}

Esempio (client): Stripe Elements + conferma pagamento

import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js';

const stripe = await loadStripe('pk_test_xxx');

async function preparaPagamento() {
  const res = await fetch('/wp-json/stripe/v1/payment-intent', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount: 4990 })
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || 'Errore PaymentIntent');

  // Usa data.clientSecret per inizializzare Elements
  return data.clientSecret;
}

// Nel submit del form, dopo aver raccolto i dettagli:
async function confermaPagamento(stripe, elements, returnUrl) {
  const { error } = await stripe.confirmPayment({
    elements,
    confirmParams: { return_url: returnUrl },
  });

  if (error) {
    // Mostra errore all’utente (es. carta rifiutata, autenticazione fallita)
    console.error(error.message);
  }
}

Nota: l’esempio usa import/React per brevità. In WordPress spesso si lavora con bundler (Vite, Webpack) o con script enqueue e moduli. In alternativa puoi usare Stripe.js in modalità “vanilla” e montare Elements direttamente nel DOM.

Webhook: la parte che rende l’integrazione affidabile

I webhook sono chiamate server-to-server inviate da Stripe quando accade un evento (pagamento riuscito, rimborso, contestazione, rinnovo, ecc.). Sono essenziali perché:

  • Non dipendono dal browser dell’utente (che può chiudere la pagina).
  • Sono il modo corretto per aggiornare lo stato di ordini o abbonamenti.
  • Gestiscono casi asincroni (pagamenti che passano in requires_action, bonifici, metodi locali).

Esempio: endpoint webhook in WordPress REST API con verifica firma

<?php
add_action('rest_api_init', function () {
  register_rest_route('stripe/v1', '/webhook', [
    'methods'  => 'POST',
    'callback' => 'my_stripe_webhook_handler',
    'permission_callback' => '__return_true',
  ]);
});

function my_stripe_webhook_handler(WP_REST_Request $request) {
  $payload = $request->get_body();
  $sig_header = isset($_SERVER['HTTP_STRIPE_SIGNATURE']) ? $_SERVER['HTTP_STRIPE_SIGNATURE'] : '';

  // require_once __DIR__ . '/vendor/autoload.php';
  $endpoint_secret = get_option('my_stripe_webhook_secret');

  try {
    // $event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret);
    // Simulazione senza SDK:
    $event = json_decode($payload, true);
  } catch (Exception $e) {
    return new WP_REST_Response(['error' => 'Webhook non valido'], 400);
  }

  // Esempio: gestisci l’evento principale
  $type = isset($event['type']) ? $event['type'] : '';

  switch ($type) {
    case 'checkout.session.completed':
      // Aggiorna ordine in WordPress, sblocca contenuti, invia email, ecc.
      break;

    case 'payment_intent.succeeded':
      // Conferma pagamento lato server, registra transazione.
      break;

    case 'charge.refunded':
      // Aggiorna stato rimborso.
      break;
  }

  return new WP_REST_Response(['received' => true], 200);
}

Buona pratica: registra in modo sicuro l’ID evento e implementa l’idempotenza (se Stripe ritenta lo stesso evento, il tuo sistema non deve duplicare ordini o accrediti).

Metodi di pagamento: carte, wallet e opzioni locali

Stripe può abilitare diversi metodi di pagamento in base al Paese, al settore e alle impostazioni dell’account. Due approcci comuni:

  • Automatic payment methods: Stripe propone e gestisce in automatico i metodi più adatti.
  • Selezione manuale: abiliti solo i metodi che desideri e gestisci UX e compatibilità.

Se vendi in Europa, considera anche la possibilità di offrire wallet (Apple Pay/Google Pay) e metodi locali quando utili al tuo pubblico. In molti casi Stripe Checkout semplifica moltissimo questo punto.

Abbonamenti e pagamenti ricorrenti

Per abbonamenti, le aree critiche sono:

  • On-session vs off-session: i rinnovi avvengono spesso senza l’utente presente. Serve gestire SCA e fallimenti di pagamento.
  • Metodo di pagamento salvato: Stripe usa Customer + PaymentMethod e spesso un SetupIntent per salvare in modo conforme.
  • Dunning: procedure di recupero crediti (email, retry schedule, aggiornamento carta).
  • Webhook: eventi come invoice.paid, invoice.payment_failed, customer.subscription.updated sono centrali.

Se usi plugin di membership, verifica che supportino rinnovi e gestione SCA in Europa. Se sviluppi custom, pianifica da subito la strategia per “pagamenti falliti” e “carta scaduta”, altrimenti perderai ricavi e tempo in assistenza.

Sicurezza e best practice in WordPress

  • HTTPS obbligatorio: sia per checkout che per API interne.
  • Non salvare dati sensibili: niente numeri di carta nel database o nei log.
  • Usa nonces e permessi: per endpoint REST che creano intent/sessioni, valida sempre origine e input.
  • Idempotency keys: quando crei PaymentIntent o rimborsi dal server, usa una chiave idempotente per evitare doppie transazioni.
  • Log controllati: registra solo ciò che serve (ID, importo, stato, errori), evita PII non necessaria.
  • Aggiornamenti: mantieni WordPress, plugin e librerie aggiornati.

GDPR e gestione dei dati

Stripe tratta dati personali (ad esempio email, indirizzo di fatturazione, IP in contesti specifici). In ottica GDPR:

  • Aggiorna informativa privacy indicando Stripe come fornitore e le finalità del trattamento.
  • Valuta basi giuridiche (contratto, obbligo legale, legittimo interesse) e retention dei dati.
  • Evita di raccogliere campi superflui nei form.
  • Gestisci correttamente richieste di accesso/cancellazione, compatibilmente con obblighi fiscali.

Test end-to-end: cosa verificare prima del go-live

  1. Pagamento riuscito: ordine/servizio erogato, email inviate, stato salvato correttamente.
  2. Pagamento con SCA: prova un flusso che richieda 3D Secure.
  3. Pagamento fallito: carta rifiutata, saldo insufficiente, autenticazione non completata.
  4. Webhook: ricezione, verifica firma, idempotenza e aggiornamento stati.
  5. Rimborsi: parziali e totali, e aggiornamento dei dati nel tuo backoffice.
  6. Abbonamenti: primo pagamento, rinnovo, pagamento fallito, recupero, cancellazione.

Risoluzione problemi comuni

“Il pagamento risulta riuscito su Stripe ma il sito non aggiorna lo stato”

  • Webhook non configurati o URL errato.
  • Firma webhook non verificata correttamente.
  • Firewall/WAF che blocca le chiamate in ingresso.
  • Timeout del server o errori PHP durante la gestione evento.

“In Europa alcuni pagamenti richiedono passaggi extra e gli utenti si bloccano”

  • Flusso SCA non gestito (mancano PaymentIntent/Checkout o confirmPayment lato client).
  • Return URL non raggiungibile o pagina di esito mal configurata.

“Doppie transazioni”

  • Mancanza di idempotenza lato server.
  • Ritenti del browser o doppio click su pulsante “Paga”.
  • Webhook gestiti due volte senza deduplica per event ID.

Checklist finale

  • Hai scelto l’approccio giusto: WooCommerce, plugin form, o integrazione custom.
  • Chiavi Test e Live configurate e separate.
  • Webhook configurati, verificati e con idempotenza.
  • Gestione SCA/3D Secure verificata con casi reali di test.
  • HTTPS, permessi e protezioni anti-abuso per gli endpoint.
  • Privacy policy aggiornata e raccolta dati minimizzata.
  • Monitoraggio: log, alert, e procedure di supporto per pagamenti falliti e rimborsi.

Con questi elementi puoi costruire un’integrazione Stripe in WordPress affidabile e scalabile. Se la tua esigenza è standard (pagamenti singoli o e-commerce classico), un plugin solido o Stripe Checkout spesso sono le opzioni più efficienti. Se invece hai logiche avanzate (pricing dinamico, onboarding, marketplace, workflow articolati), una integrazione custom con PaymentIntent e webhook ben progettati diventa la scelta più potente.

Torna su