Integrazione WayForPay in PHP: modulo di pagamento, firma e richiamata
WayForPay è il modo pratico per accettare pagamenti con carta in UAH, USD ed EUR da un'azienda ucraina. Questa guida illustra la completa integrazione in PHP: firma del modulo di pagamento, ricezione della richiamata del server, risposta corretta e conferma dello stato dell'ordine.
In breve
- Il flusso è composto da tre parti: un modulo di pagamento firmato che invia il cliente a WayForPay, una richiamata da server a server (
serviceUrl) e la pagina di stato dell'ordine. - Ogni richiesta e risposta è firmata con HMAC_MD5 utilizzando la chiave segreta del commerciante; l'ordine dei campi è importante.
- Contrassegna un ordine come pagato solo da una richiamata verificata o da una risposta
CHECK_STATUS, mai dal cliente che ritorna sul tuo sito. - È necessario rispondere alla richiamata con una risposta JSON "accetta" firmata, altrimenti WayForPay continua a riprovare.
- Mantieni gli importi, la valuta e il riferimento dell'ordine nel tuo database e confrontali con ciò che dice la richiamata.
Un'integrazione WayForPay in PHP richiede una piccola quantità di codice, ma presenta diversi punti in cui un errore silenzioso indica ordini non pagati contrassegnati come pagati o ordini pagati che non vengono mai evasi. Eseguiamo il pagamento WayForPay sui nostri siti, inclusi i servizi a pagamento su questo sito Web, e lo abbiamo collegato a diversi negozi clienti. Di seguito è riportata la struttura che utilizziamo, con i dettagli che la documentazione elenca ma che le persone tendono a scremare.
Come funziona il flusso di pagamento
- Il cliente fa clic su "Paga". Il tuo server crea un ordine nel database con un
orderReferenceunivoco, un importo fisso e una valuta. - Il tuo server crea i campi del modulo di pagamento, li firma ed esegue il rendering di un modulo HTML con invio automatico che viene pubblicato nella pagina di pagamento WayForPay.
- Il cliente paga sulla pagina WayForPay (carta, Apple Pay, Google Pay e altri metodi abilitati nel tuo account commerciante).
- WayForPay chiama il tuo
serviceUrlcon il risultato della transazione. Questa chiamata da server a server è ciò di cui ti fidi. - Il cliente viene reindirizzato al tuo
returnUrl, dove mostri lo stato corrente dell'ordine.
Il passaggio 5 può avvenire prima del completamento del passaggio 4, quindi la pagina di reso deve riportare la dicitura "conferma del pagamento" finché la richiamata non viene elaborata.
Ciò di cui hai bisogno dall'account commerciante
merchantAccount: il tuo login commerciante.merchantSecretKey: il segreto utilizzato per le firme. Conservalo in una variabile di ambiente o in un file di configurazione non tracciato, mai in git o nel codice front-end.merchantDomainName: il dominio del tuo sito, come registrato nell'account.- Le valute e i metodi di pagamento abilitati per il tuo account.
Passaggio 1. Compila e firma il modulo di pagamento
La firma è un HMAC con MD5 sui campi selezionati uniti da un punto e virgola. Per un acquisto l'ordine è: account commerciante, nome dominio commerciante, riferimento ordine, data ordine (timestamp Unix), importo, valuta, quindi tutti i nomi dei prodotti, tutti i conteggi dei prodotti e tutti i prezzi dei prodotti.
function wfpSignature(array $parts, string $secret): string
{
return hash_hmac('md5', implode(';', $parts), $secret);
}
$orderReference = 'ORD-' . $orderId . '-' . time();
$orderDate = time();
$amount = '50.00'; // string with a dot, the same value everywhere
$currency = 'EUR';
$products = [['name' => 'Consultation, 1 hour', 'count' => 1, 'price' => '50.00']];
$parts = [$merchantAccount, $merchantDomain, $orderReference, $orderDate, $amount, $currency];
foreach ($products as $p) { $parts[] = $p['name']; }
foreach ($products as $p) { $parts[] = (string) $p['count']; }
foreach ($products as $p) { $parts[] = $p['price']; }
$signature = wfpSignature($parts, $secretKey);
Quindi renderizzare il modulo. I campi prodotto sono array, quindi utilizzano il suffisso []:
<form id="wfp" method="post" action="https://secure.wayforpay.com/pay" accept-charset="utf-8">
<input type="hidden" name="merchantAccount" value="...">
<input type="hidden" name="merchantAuthType" value="SimpleSignature">
<input type="hidden" name="merchantDomainName" value="example.com">
<input type="hidden" name="merchantSignature" value="<?= htmlspecialchars($signature) ?>">
<input type="hidden" name="orderReference" value="<?= htmlspecialchars($orderReference) ?>">
<input type="hidden" name="orderDate" value="<?= $orderDate ?>">
<input type="hidden" name="amount" value="50.00">
<input type="hidden" name="currency" value="EUR">
<input type="hidden" name="productName[]" value="Consultation, 1 hour">
<input type="hidden" name="productCount[]" value="1">
<input type="hidden" name="productPrice[]" value="50.00">
<input type="hidden" name="returnUrl" value="https://example.com/pay/return">
<input type="hidden" name="serviceUrl" value="https://example.com/pay/callback">
</form>
<script>document.getElementById('wfp').submit();</script>
Gli errori di firma più comuni sono: un formato numerico diverso nella firma e nel modulo (50 vs 50.00), un nome di prodotto che viene modificato (ritagliato, con escape HTML) dopo la firma e dimenticare che l'ordine dei campi è fisso.
Passaggio 2. Ricevi la richiamata su serviceUrl
Quando il pagamento raggiunge uno stato finale, WayForPay pubblica il risultato su serviceUrl. Leggi il corpo grezzo (è JSON; alcune configurazioni forniscono anche campi del modulo, quindi gestiscili entrambi) e verifica la firma. Per la richiamata vengono calcolati: conto commerciante, riferimento dell'ordine, importo, valuta, codice di autorizzazione, maschera della carta, stato della transazione e codice motivo.
$raw = file_get_contents('php://input');
$data = json_decode($raw, true) ?: $_POST;
$line = implode(';', [
$data['merchantAccount'] ?? '',
$data['orderReference'] ?? '',
$data['amount'] ?? '',
$data['currency'] ?? '',
$data['authCode'] ?? '',
$data['cardPan'] ?? '',
$data['transactionStatus'] ?? '',
$data['reasonCode'] ?? '',
]);
$expected = hash_hmac('md5', $line, $secretKey);
if (!hash_equals(strtolower($expected), strtolower((string) ($data['merchantSignature'] ?? '')))) {
http_response_code(400);
exit;
}
Utilizzare hash_equals per il confronto per evitare perdite di temporizzazione. Solo dopo che la firma è valida dovresti cercare l'ordine e confrontare amount e currency con ciò che hai memorizzato. Se differiscono, registralo e non contrassegnare l'ordine come pagato.
Passaggio 3. Aggiorna l'ordine e rispondi correttamente
Il campo transactionStatus ti dice cosa è successo: Approved significa che il denaro è stato catturato; Valori Declined, Expired e simili indicano il fallimento; Pending o InProcessing significa attesa; Refunded e Voided significano che i soldi sono tornati indietro. Aggiorna il tuo ordine di conseguenza, in una transazione, e rendilo idempotente: se l'ordine è già stato pagato, non fare nulla e rispondi comunque con successo.
WayForPay richiede una risposta firmata in modo che sappia che hai ricevuto il messaggio. La firma è sopra il riferimento dell'ordine, la parola accept e l'ora corrente:
$time = time();
$status = 'accept';
$reply = [
'orderReference' => $data['orderReference'],
'status' => $status,
'time' => $time,
'signature' => hash_hmac('md5', "{$data['orderReference']};{$status};{$time}", $secretKey),
];
header('Content-Type: application/json');
echo json_encode($reply);
Se il tuo server non risponde in questo modo, WayForPay considera la consegna come fallita e riprova, il che significa che riceverai la stessa richiamata più volte. Questo è un altro motivo per il controllo di idempotenza.
Passaggio 4. Confermare lo stato con CHECK_STATUS
Le richiamate possono essere perse: il server era inattivo, un firewall ha bloccato la richiesta, una distribuzione ha interrotto il percorso. WayForPay offre una chiamata API CHECK_STATUS che restituisce lo stato corrente di un ordine tramite il suo riferimento. Usalo in due posti: nella pagina di ritorno, se la richiamata non è arrivata dopo pochi secondi, e in un piccolo lavoro cron che ricontrolla gli ordini bloccati in "in sospeso" per più di, diciamo, quindici minuti. La richiesta è firmata con HMAC_MD5 sul conto commerciante e sul riferimento dell'ordine.
$payload = [
'transactionType' => 'CHECK_STATUS',
'merchantAccount' => $merchantAccount,
'orderReference' => $orderReference,
'merchantSignature' => hash_hmac('md5', "{$merchantAccount};{$orderReference}", $secretKey),
'apiVersion' => 1,
];
$ch = curl_init('https://api.wayforpay.com/api');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
]);
$result = json_decode((string) curl_exec($ch), true);
Applica gli stessi controlli di una richiamata: verifica la firma della risposta quando presente, confronta l'importo e la valuta e aggiorna l'ordine in modo idempotente.
Test prima di andare in diretta
- Utilizza la modalità di test e le carte di prova dalla documentazione WayForPay fino a quando nei tuoi registri non vengono visualizzati un pagamento completo, un rifiuto e un rimborso.
- Il tuo
serviceUrldeve essere raggiungibile da Internet tramite HTTPS. Su localhost utilizzare un tunnel o eseguire la distribuzione in un dominio di staging. - Registra ogni richiamata con il suo corpo grezzo, il risultato della verifica e l'esito. Non registrare mai la chiave segreta.
- Assicurati che la protezione WAF o bot (ad esempio in Cloudflare) non contesti o blocchi le richieste di richiamata di WayForPay.
Errori comuni
- Affidarsi all'URL di ritorno. Il cliente può aprirlo senza pagare.
- Riutilizzare un riferimento d'ordine. Ogni tentativo necessita di un riferimento univoco, altrimenti confonderai i tentativi.
- Modificare l'importo dopo la firma oppure mostrare un prezzo e firmarne un altro.
- Non rispondere alla richiamata con la risposta di accettazione firmata.
- Contrassegna come pagato prima di controllare l'importo e la valuta.
- Diverse valute, un segreto condiviso e nessuna mappatura: mantieni la valuta nell'ordine e verificala al richiamo.
Come possiamo aiutare
Realizziamo integrazioni di pagamento end-to-end: WayForPay, Stripe, PayPal, fatturazione ricorrente, rimborsi e report di riconciliazione. Se disponi già di un checkout funzionante ma non sei sicuro delle sue modalità di errore, prenota un'analisi dell'architettura ed esamineremo la gestione del callback, l'idempotenza e la registrazione. Per una guida simile su un altro provider consulta i webhook Stripe in PHP e per l'approccio generale leggi il nostro playbook sulle integrazioni di avvio.
Domande frequenti
Quale firma utilizza WayForPay ?
HMAC_MD5 sui campi selezionati uniti con un punto e virgola, utilizzando la chiave segreta del commerciante. L'ordine dei campi è fisso e differisce per il modulo di pagamento, la richiamata e la risposta alla richiamata.
Cosa deve restituire l'endpoint serviceUrl?
Una risposta JSON con orderReference, lo stato "accept", l'ora Unix corrente e una firma calcolata come HMAC_MD5 di "orderReference;accept;time". Senza di esso WayForPay ritenta la richiamata.
Posso fidarmi della pagina returnUrl per contrassegnare un ordine come pagato?
No. Il browser del cliente può aprire quell'URL senza pagare. Contrassegna gli ordini come pagati solo dopo una richiamata serviceUrl verificata o una risposta CHECK_STATUS con stato Approvato e importo e valuta corrispondenti.
Come posso verificare lo stato di un ordine WayForPay ?
Invia una richiesta CHECK_STATUS firmata all'API WayForPay con il tuo account commerciante e il riferimento dell'ordine. Usalo nella pagina di ritorno e in un lavoro cron che ricontrolla gli ordini bloccati in sospeso.
Perché WayForPay dice che la firma non è valida?
Di solito il formato dell'importo tra la firma e il modulo è diverso (50 contro 50,00), il nome di un prodotto è stato alterato dopo la firma, l'ordine dei campi è sbagliato o è stata utilizzata la chiave segreta sbagliata.
Chiamata conoscitiva gratuita
Portate uno stack disordinato o un'integrazione bloccata - Integrono mapperà la prima mossa più intelligente.
Ottenete una lettura senior su trappole di budget, dipendenze e se conviene comprare, assemblare o costruire - prima che parta il lavoro a pagamento. Il CEO resta nel loop.