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
- Installa e configura WooCommerce (valuta, tasse, spedizioni, pagine di checkout).
- Installa un gateway Stripe compatibile con WooCommerce.
- Inserisci le chiavi API Test e abilita la modalità test.
- Effettua un acquisto di prova con le carte test di Stripe e verifica l’intero flusso (ordine, email, stato pagamento).
- Configura i webhook se richiesti dal plugin per sincronizzare eventi (pagamenti riusciti, rimborsi, dispute, rinnovi).
- 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
confirmPaymento 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
- Pagamento riuscito: ordine/servizio erogato, email inviate, stato salvato correttamente.
- Pagamento con SCA: prova un flusso che richieda 3D Secure.
- Pagamento fallito: carta rifiutata, saldo insufficiente, autenticazione non completata.
- Webhook: ricezione, verifica firma, idempotenza e aggiornamento stati.
- Rimborsi: parziali e totali, e aggiornamento dei dati nel tuo backoffice.
- 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.