Integrare un POS virtuale in un software su misura non significa semplicemente «chiamare l'API di iyzico o di PayTR», come credono molti team. Un'integrazione pronta per la produzione deve risolvere quattro problemi distinti nello stesso momento: (1) configurare correttamente il flusso 3D Secure (3DS) — inviare l'utente alla schermata di verifica della banca e riportarlo indietro; (2) verificare in modo sicuro la callback o il webhook — dimostrare con una firma che la notifica in arrivo proviene davvero dal provider, essere idempotenti rispetto alle notifiche ripetute e restituire rapidamente un 2xx; (3) riconciliare il pagamento con l'ordine — collegare all'ordine giusto l'evento con cui il denaro viene effettivamente incassato; e (4) gestire gli scenari successivi alla transazione, come rimborsi, annullamenti e rate. Se manca anche uno solo di questi elementi, l'integrazione sembra «funzionare», ma in produzione resta esposta a errori silenziosi che fanno perdere denaro: un ordine elaborato due volte, un carrello che risulta «pagato» senza essere mai stato confermato, oppure un prodotto regalato tramite una callback falsificata. In questo articolo, come software house B2B / white-label con sede a Sakarya, condividiamo esattamente il metodo che applichiamo nelle integrazioni di POS virtuale che realizziamo: POS virtuale bancario o istituto di pagamento, modulo ospitato o API diretta, flusso 3DS, webhook sicuri, riconciliazione, rimborsi e rate, più una checklist prima della messa in produzione.
Questo articolo tratta l'integrazione dei pagamenti sia come un progetto di integrazione API sia come una questione di infrastruttura e-commerce. Abbiamo applicato la stessa disciplina sul fronte ERP e fatturazione elettronica; se sta valutando un'integrazione standard o su misura, legga il nostro articolo sull'integrazione di e-commerce, ERP e fatturazione elettronica. I dettagli tecnici che seguono si basano sulla documentazione ufficiale di iyzico e PayTR; poiché ogni provider può cambiare nel tempo, l'unica fonte attendibile è la documentazione ufficiale in vigore al momento dell'integrazione.
POS virtuale bancario o istituto di pagamento?
La prima decisione architetturale riguarda il canale con cui incassare i pagamenti con carta. In Turchia il POS virtuale è offerto in due forme: (a) il POS virtuale bancario, con contratto diretto con una banca, e (b) gli istituti di pagamento indipendenti dalle banche (come iyzico e PayTR). Gli istituti di pagamento operano ai sensi della legge turca n. 6493, la legge sui sistemi di pagamento e di regolamento titoli, sui servizi di pagamento e sugli istituti di moneta elettronica, e la loro licenza operativa non viene rilasciata dalla BDDK, l'autorità turca di vigilanza bancaria, ma dalla TCMB, la banca centrale turca (articolo 12 della legge) (TCMB — Istituti di pagamento).
La differenza pratica è questa: con un POS virtuale bancario servono in genere un'integrazione e un contratto separati per ogni banca; un istituto di pagamento indipendente, invece, consente di accettare con un'unica integrazione le carte della maggior parte delle banche. iyzico si distingue in particolare negli scenari marketplace / sub-esercente (submerchant). La regola di decisione è semplice:
- Se la priorità sono una sola banca, volumi elevati e la commissione più bassa e ha un solido rapporto con una banca, un POS virtuale bancario può avere senso; se però desidera il supporto multibanca, il carico di integrazione si moltiplica.
- Se desidera una messa in produzione rapida, l'accettazione multibanca e un'unica integrazione, un istituto di pagamento (iyzico/PayTR) comporta costi di manutenzione inferiori nella maggior parte degli scenari B2B e PMI.
- Se servono funzioni di marketplace, ripartizione delle commissioni o sub-esercente, un istituto di pagamento con supporto submerchant è quasi obbligatorio.
Modulo ospitato o API diretta? Qui si decide il suo ambito PCI-DSS
La seconda decisione, forse la più critica, riguarda il punto da cui passeranno i dati delle carte, perché determina direttamente il suo ambito PCI-DSS. Gli approcci sono due:
- Modulo di pagamento ospitato (hosted form): i dati della carta vengono raccolti in una pagina o in un iframe ospitati dal provider. Nell'iFrame API di PayTR l'utente inserisce la carta all'interno dell'iframe di PayTR; su iyzico, CheckoutForm è la pagina di pagamento ospitata da iyzico (CF-Initialize restituisce un
paymentPageUrl/token e la pagina può essere incorporata coniframe=true). In entrambi i casi i dati sensibili della carta non toccano mai il suo server, quindi il suo ambito PCI-DSS si riduce in modo significativo. - API diretta: raccoglie il pagamento con il suo modulo di pagamento e i dati della carta passano dalla sua infrastruttura. Questo offre pieno controllo e un'esperienza con il suo brand, ma aumenta notevolmente i suoi obblighi PCI-DSS.
Sia iyzico sia PayTR si fanno carico della protezione PCI sul lato provider; iyzico si presenta come fornitore di servizi PCI DSS Level 1. Ma mettiamo in chiaro una cosa: il suo esatto tipo di SAQ (SAQ A, SAQ A-EP ecc.) dipende dai dettagli dell'integrazione e in questo articolo non ne indichiamo uno; la verità generale è che l'ambito si riduce quando i dati delle carte non passano dal suo server. Il nostro consiglio pratico è chiaro:
A meno che non abbia un reale requisito di business per un checkout con il suo brand e sotto il suo pieno controllo, scelga il modulo ospitato. I pochi pixel di personalizzazione guadagnati non valgono il carico di audit PCI-DSS e di responsabilità che si assume.
Come funziona il flusso 3DS? (esempio con iyzico)
3D Secure è una verifica aggiuntiva eseguita dalla banca del titolare della carta (un codice monouso, un'approvazione nell'app della banca ecc.). Se questo passaggio viene saltato, il rischio di chargeback e la relativa responsabilità restano in gran parte a carico dell'azienda. In iyzico il 3DS è un flusso POST in due fasi:
- Fase 1 — Init 3DS: invia una richiesta POST all'endpoint
/payment/3dsecure/initializecon i dati della carta e dell'acquirente e unacallbackUrl. La risposta restituisce unthreeDSHtmlContentcodificato in base64. Il rendering di questo HTML reindirizza l'utente alla schermata di verifica 3DS della banca. - Fase 2 — Auth 3DS: dopo che l'utente ha completato la verifica presso la banca ed è tornato alla sua
callbackUrl, effettua una seconda richiesta POST all'endpoint auth con ilpaymentIdrestituito da Init. Qui iyzico raccomanda di usare l'endpoint v2 (/payment/v2/3dsecure/auth, ossia 3DS 2.0); l'endpoint legacy/payment/3dsecure/authè 3DS 1.0.
L'esito della verifica è indicato dal campo mdStatus: 1 significa esito positivo, mentre i valori 0 e 2–8 indicano una verifica non riuscita. Un'avvertenza importante: qui non forniamo come elenco definitivo lo schema esatto dei campi del corpo POST che la banca invia alla sua callbackUrl, perché può variare in base alla versione e all'integrazione; è un dettaglio da verificare nella documentazione ufficiale aggiornata prima della messa in produzione (iyzico — Init 3DS, iyzico — Auth 3DS).
Per tutto il flusso, conversationId serve ad associare la richiesta alla risposta. iyzico lo definisce un «ID univoco per la correlazione tra richiesta e risposta»; può inserirvi il suo numero d'ordine e il valore inviato nella richiesta viene restituito invariato nella risposta. Inoltre iyzico assegna da parte sua un paymentId a ogni pagamento riuscito; rimborsi e annullamenti vengono eseguiti tramite questo paymentId.
Verifica sicura di webhook e callback: firma, idempotenza, 2xx rapido
Questa è la parte più trascurata e più pericolosa di un'integrazione di pagamento. La callback o il webhook è il vero evento server-to-server in cui segnerà il denaro come incassato. Tre regole non sono negoziabili: verificare la firma, essere idempotenti, restituire subito un 2xx.
1) Verifica della firma — dimostrare che la notifica arriva davvero dal provider
Non consideri mai «pagata» una richiesta POST arrivata alla sua callback senza averne verificato la firma. Altrimenti un malintenzionato potrebbe inviare una falsa notifica di «pagamento riuscito» e farsi aprire un ordine gratuito. I meccanismi dei due provider sono diversi:
- PayTR (iFrame API): nella notifica inviata a
notify_url, l'hash si calcola comebase64_encode(hash_hmac('sha256', merchant_oid + merchant_salt + status + total_amount, merchant_key, true)). Se il campohashdella richiesta POST in arrivo non corrisponde esattamente a questo valore, la richiesta va considerata non proveniente da PayTR e respinta. Questa formula vale per l'iFrame API (quattro campi); altre API di PayTR possono usare un insieme di campi diverso, quindi non le confonda. - iyzico (webhook): la verifica avviene tramite l'header
X-IYZ-SIGNATURE-V3con HMAC-SHA256 (HEX); le versioni precedenti dell'header non sono più supportate. Il punto critico: l'ordine dei campi concatenati per la firma varia in base al tipo di webhook. Direct payment, HPP e subscription usano ordini di parametri diversi: non esiste un unico ordine «generico» della firma del webhook, quindi verifichi nella tabella ufficiale quello previsto per il suo tipo di integrazione.
iyzico offre anche un meccanismo separato di response signature (firma della risposta) per le risposte API sincrone: determinati parametri vengono concatenati con i due punti (:), sottoposti ad hashing con la secretKey tramite HMAC-SHA256 e codificati in HEX; gli zeri finali superflui nei prezzi vengono rimossi (10.50 → 10.5, 10.00 → 10). L'ordine dei parametri varia in base all'endpoint; ad esempio, per l'auth non 3DS l'ordine è paymentId:currency:basketId:conversationId:paidPrice:price, e l'auth 3DS v2 usa lo stesso ordine dei campi (iyzico — Response Signature Validation, iyzico — Webhook, PayTR — iFrame API, fase 2).
2) Idempotenza — se la stessa notifica arriva due volte, non va elaborata due volte
Entrambi i provider possono inviare più notifiche per la stessa transazione. Se non le deduplica in base al suo ID ordine, lo stesso ordine viene confermato due volte, lo stock viene scalato due volte e la spedizione viene aperta due volte. PayTR lo dice esplicitamente: il controllo delle ripetizioni deve basarsi sul campo univoco merchant_oid; per confermare o annullare l'ordine va elaborata solo la prima notifica, mentre a quelle ripetute basta rispondere. In pratica: conservi lo stato della notifica (elaborata/non elaborata) nel suo database, in un unico record univoco legato all'ID ordine, ed esegua l'elaborazione in una sola operazione atomica.
3) Restituire subito un 2xx — il lavoro pesante va eseguito in modo asincrono
Il provider si aspetta una risposta positiva alla notifica entro breve tempo; se non la riceve, ritenta. Con il webhook di iyzico, se l'esercente non restituisce un 2xx, il sistema ritenta ogni 15 minuti, fino a 3 volte. PayTR, se non riceve «OK», ritenta dopo 1 minuto e richiede che il corpo della risposta sia soltanto il testo semplice OK, senza HTML o altri contenuti. Ne derivano due regole:
- Prima verificare, poi salvare, poi rispondere: verifichi la firma, contrassegni l'ordine in modo univoco, quindi restituisca la risposta attesa. Sposti su una coda asincrona le operazioni pesanti, come l'invio di email, l'emissione di fatture o la scrittura nell'ERP; non le esegua in modo sincrono bloccando la risposta alla callback.
- Restituire il corpo della risposta nel formato atteso dal provider: per PayTR esattamente
OK; per iyzico un codice di stato HTTP 2xx. Altrimenti si ritroverà con tentativi superflui e pagamenti «persi».
| Aspetto | iyzico (webhook) | PayTR (callback dell'iFrame API) |
|---|---|---|
| Metodo di firma | HMAC-SHA256 (HEX), header X-IYZ-SIGNATURE-V3 |
HMAC-SHA256 + base64, campo hash nella richiesta POST |
| Ordine dei campi della firma | Varia in base al tipo di webhook (direct / HPP / subscription sono diversi) | merchant_oid + merchant_salt + status + total_amount (iFrame API) |
| Risposta attesa | Codice di stato HTTP 2xx | Testo semplice OK |
| Nuovi tentativi | Senza 2xx, ogni 15 min, fino a 3 volte | Senza OK, nuovo tentativo dopo 1 min |
| Chiave di deduplicazione | paymentId / conversationId |
merchant_oid (viene elaborata solo la prima notifica) |
Riconciliazione tra pagamento e ordine
La riconciliazione è il ponte tra «il denaro incassato dal provider» e «l'ordine nel suo sistema». Una riconciliazione costruita male è la parte che crea più problemi alla contabilità e al servizio clienti. Entrambi i provider costruiscono questo ponte con un ID ordine univoco generato dall'esercente:
- PayTR:
merchant_oid(fino a 64 caratteri alfanumerici) è il numero d'ordine univoco che assegna alla transazione; le viene restituito nella callback e rende possibile la riconciliazione. Inoltre, nella fase 1 ilpaytr_tokenviene generato con HMAC-SHA256 + base64 e la stringa concatenata include i campimerchant_id + user_ip + merchant_oid + email + payment_amount + user_basket + no_installment + max_installment + currency + test_mode + merchant_salt. - iyzico: usi
conversationIdcome suo numero d'ordine; il valore inviato nella richiesta viene restituito invariato nella risposta. Il corrispettivo sul lato provider èpaymentId: rimborsi e annullamenti passano da questo ID. Conservi entrambi gli ID nel suo record d'ordine.
Principi pratici per una riconciliazione solida:
- Dare autorità alla callback, non al browser: la pagina di «successo» a cui torna il browser dell'utente è un indizio, non una prova. Confermi l'ordine principalmente tramite la callback server-to-server con firma verificata.
- Confrontare importo e valuta: confronti l'importo della callback con quello atteso per l'ordine; se non coincidono, non elabori la notifica e generi un avviso.
- Chiudere gli stati in sospeso: se la callback non arriva mai, usi un job in background per controllare gli ordini con «pagamento avviato ma non concluso» tramite l'API di interrogazione del provider e chiuderli.
- Riconciliazione giornaliera: confronti regolarmente i report delle transazioni e dei regolamenti del provider con i suoi registri; non si affidi al solo webhook.
Rimborsi, rimborsi parziali e rate
La seconda metà del flusso di pagamento sono gli scenari successivi alla transazione; è qui che si conquistano l'esperienza del cliente e l'accuratezza contabile. Le distinzioni fondamentali specifiche di iyzico:
- Rimborso — totale o parziale: le strade sono due.
/payment/refundopera a livello di riga (conpaymentTransactionId, cioè l'ID di dettaglio della transazione relativo all'articolo del carrello, eprice)./v2/payment/refundopera a livello di pagamento (conpaymentIdeprice); è il sistema stesso a determinare quale articolo del carrello rimborsare. Entrambe le versioni supportano rimborsi totali e parziali e restituiscono un riferimento bancario (refundHostReference,authCode). - Annullamento:
/payment/cancelsi chiama con il solopaymentId, non supporta un importo parziale e annulla l'intero pagamento. Secondo la documentazione di iyzico, un annullamento può essere eseguito lo stesso giorno del pagamento e non genera movimenti di addebito o accredito sull'estratto conto della carta; un rimborso, invece, compare sull'estratto conto e può richiedere alcuni giorni a seconda della banca. Le regole esatte su stesso giorno e orari limite variano in base alla banca e alla data valuta; le consideri un principio generale, non una regola contabile precisa. - Interrogazione di rate e BIN: con
POST /payment/iyzipos/installmentpuò consultare opzioni di rateizzazione, tassi e importi, oltre a famiglia e circuito della carta e dati della banca, inviandoprice(obbligatorio) e unbinNumberfacoltativo (BIN a 8 cifre). I tassi delle rate si possono ottenere anche con il solo importo (senza BIN);conversationIdviene inviato nella richiesta e restituito invariato nella risposta (iyzico — Refund & Cancel, iyzico — Installment & BIN).
Come regola di business: integri nel suo sistema la logica «annullamento se nello stesso giorno, rimborso se dopo»; quando concede a un utente un rimborso parziale, registri quale articolo o importo è stato rimborsato e conservi i riferimenti bancari.
Checklist prima della messa in produzione
- Verifica della firma: la firma viene verificata per ogni callback o webhook? Una richiesta non verificata viene respinta in modo definitivo? Su iyzico, viene usato l'ordine dei campi corretto per il suo tipo di integrazione?
- Idempotenza: una notifica che arriva una seconda volta per lo stesso ID ordine (
merchant_oid/conversationId) non viene rielaborata e riceve solo la risposta appropriata? - Risposta rapida: la callback restituisce rapidamente un 2xx /
OKnel formato atteso? Le operazioni pesanti vengono messe in una coda asincrona? - Controllo dell'importo: importo e valuta della callback vengono confrontati con il valore atteso per l'ordine?
- Fonte di autorità: la conferma dell'ordine si basa sulla notifica server-to-server anziché sul reindirizzamento del browser?
- Chiusura degli stati in sospeso: esiste un'interrogazione in background per i pagamenti «avviati ma non conclusi» per i quali la callback non è mai arrivata?
- Rimborsi e annullamenti: sono stati testati gli scenari di rimborso totale e parziale, annullamento e rate? I riferimenti bancari vengono conservati?
- Segreti e HTTPS: segreti come
secretKey/merchant_key/merchant_saltsono conservati solo sul server, nelle variabili d'ambiente? L'URL della callback è HTTPS? - Log: ogni evento di pagamento (richiesta, risposta, callback, esito della firma) viene registrato nei log in modo tracciabile?
- Ambiente di test: è stata eseguita una verifica end-to-end (3DS incluso) nella sandbox o in modalità test del provider?
Come La aiuta Partnerfy
Come software house B2B / white-label con sede a Sakarya, trattiamo l'integrazione del POS virtuale non come una «chiamata API», ma come un problema di sicurezza e riconciliazione. In un progetto di software web su misura o di e-commerce chiariamo prima di tutto la decisione: istituto di pagamento o POS bancario, modulo ospitato o API diretta (in base al suo ambito PCI-DSS). Poi realizziamo e verifichiamo end-to-end in un ambiente di test il flusso 3DS, una callback idempotente con verifica della firma, la riconciliazione degli importi e gli scenari di rimborso e rate. Di solito colleghiamo il tutto ai suoi sistemi ERP e contabili esistenti tramite un'integrazione API.
Una nota onesta: nessuna integrazione può garantire che «non avrà mai problemi», perché banche, provider e documentazione cambiano nel tempo. La nostra promessa non è una garanzia, ma disciplina: verificare la firma, essere idempotenti, dare autorità alla callback, registrare nei log e riconciliare ogni passaggio. Se desidera valutare se per Lei sia più adatto un plugin pronto all'uso o un'integrazione su misura, anche il nostro articolo su software su misura e software standard può aiutarla a inquadrare la decisione.
Conclusione
Un'integrazione di POS virtuale pronta per la produzione comincia con la chiamata all'API, ma non finisce lì. Il vero lavoro consiste nello scegliere il canale giusto (istituto di pagamento o POS bancario), ridurre consapevolmente l'ambito PCI-DSS (modulo ospitato o API diretta), configurare correttamente il flusso 3DS, verificare la callback con una firma, essere idempotenti, restituire un 2xx/OK rapido, riconciliare importo e ordine e gestire in modo completo gli scenari di rimborso, annullamento e rate. Ciascuno di questi passaggi separa un'integrazione che «sembra funzionare» da una davvero sicura. Se ha bisogno di un partner mentre progetta la sua integrazione di pagamento o mette in sicurezza un impianto esistente, ci contatti: definiremo insieme il canale, l'ambito PCI e il piano di riconciliazione in base al suo scenario.
Fonti
- iyzico — Init 3DS (/payment/3dsecure/initialize, threeDSHtmlContent, conversationId)
- iyzico — Auth 3DS (v2 consigliata, completamento con paymentId, mdStatus)
- iyzico — Response Signature Validation (HMAC-SHA256 + HEX)
- iyzico — Webhook (X-IYZ-SIGNATURE-V3, obbligo di 2xx, nuovi tentativi)
- iyzico — Installment & BIN Service (/payment/iyzipos/installment)
- iyzico — Refund & Cancel (/payment/refund, /v2/payment/refund, /payment/cancel)
- iyzico — CheckoutForm (pagina di pagamento ospitata, ambito PCI ridotto)
- PayTR — iFrame API, fase 1 (paytr_token, iframe ospitato, merchant_oid)
- PayTR — iFrame API, fase 2 (hash della callback, risposta «OK», deduplicazione tramite merchant_oid)
- TCMB — Istituti di pagamento (legge n. 6493, art. 12, licenza operativa)