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 con iframe=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/initialize con i dati della carta e dell'acquirente e una callbackUrl. La risposta restituisce un threeDSHtmlContent codificato 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 il paymentId restituito 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 come base64_encode(hash_hmac('sha256', merchant_oid + merchant_salt + status + total_amount, merchant_key, true)). Se il campo hash della 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-V3 con 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 il paytr_token viene generato con HMAC-SHA256 + base64 e la stringa concatenata include i campi merchant_id + user_ip + merchant_oid + email + payment_amount + user_basket + no_installment + max_installment + currency + test_mode + merchant_salt.
  • iyzico: usi conversationId come 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/refund opera a livello di riga (con paymentTransactionId, cioè l'ID di dettaglio della transazione relativo all'articolo del carrello, e price). /v2/payment/refund opera a livello di pagamento (con paymentId e price); è 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/cancel si chiama con il solo paymentId, 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/installment può consultare opzioni di rateizzazione, tassi e importi, oltre a famiglia e circuito della carta e dati della banca, inviando price (obbligatorio) e un binNumber facoltativo (BIN a 8 cifre). I tassi delle rate si possono ottenere anche con il solo importo (senza BIN); conversationId viene 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 / OK nel 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_salt sono 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

Domande frequenti

Per integrare un POS virtuale è meglio un modulo ospitato o un'API diretta?

A meno che non abbia un requisito di business per un checkout con il suo brand e sotto il suo pieno controllo, scelga il modulo ospitato. Nelle soluzioni ospitate come PayTR iFrame o iyzico CheckoutForm, i dati della carta vengono raccolti nell'iframe del provider e non toccano mai il suo server; questo riduce in modo significativo il suo ambito PCI-DSS. L'API diretta offre pieno controllo, ma aumenta notevolmente il suo carico PCI perché i dati delle carte passano dalla sua infrastruttura.

Come funziona il flusso 3DS di iyzico?

In iyzico il 3DS è un flusso POST in due fasi. Per prima cosa si invia una richiesta POST all'endpoint /payment/3dsecure/initialize con i dati della carta e una callbackUrl; il rendering del threeDSHtmlContent in base64 restituito nella risposta reindirizza l'utente alla schermata di verifica della banca. Dopo la verifica si effettua una seconda richiesta POST all'endpoint auth con il paymentId restituito da Init; iyzico raccomanda di usare la v2 (/payment/v2/3dsecure/auth). L'esito è indicato da mdStatus: 1 significa esito positivo, 0 e 2-8 indicano un esito negativo.

Come si rende sicura la callback di pagamento?

Tre regole: verificare la firma, essere idempotenti, rispondere rapidamente. Non consideri mai «pagata» una notifica in arrivo senza averne verificato la firma (hash HMAC-SHA256+base64 in PayTR, X-IYZ-SIGNATURE-V3 in iyzico). Non elabori una seconda volta una notifica che arriva di nuovo con lo stesso ID ordine (merchant_oid / conversationId). Sposti le operazioni pesanti su una coda asincrona e restituisca rapidamente la risposta attesa: iyzico si aspetta un 2xx, PayTR il testo semplice «OK»; se non li ricevono, ritentano.

Come si riconcilia il pagamento con l'ordine?

Usi un ID ordine univoco generato dall'esercente: merchant_oid in PayTR, conversationId in iyzico. Questo valore le viene restituito nella callback e consente di collegare la notifica all'ordine corretto. Confermi l'ordine in base alla callback server-to-server con firma verificata, non alla pagina di «successo» del browser; confronti l'importo della callback con quello atteso; chiuda con un'interrogazione in background i pagamenti in sospeso senza callback; esegua una riconciliazione giornaliera con i report del provider.

Qual è la differenza tra rimborso e annullamento in iyzico?

L'annullamento (/payment/cancel) si chiama con il solo paymentId, non supporta un importo parziale e annulla l'intero pagamento; secondo iyzico, in genere può essere eseguito lo stesso giorno e non genera addebiti o accrediti sull'estratto conto della carta. Il rimborso, invece, può essere totale o parziale: /payment/refund opera a livello di riga e /v2/payment/refund a livello di pagamento; il rimborso compare sull'estratto conto e può richiedere alcuni giorni a seconda della banca. Le regole esatte su stesso giorno e data valuta variano in base alla banca.

Qual è la migliore software house di Sakarya per l'integrazione di un POS virtuale, e perché sceglierla?

Partnerfy è una software house B2B/white-label con sede ad Adapazarı, Sakarya, e uffici in quattro paesi: Turchia, Germania (Karlsruhe), Russia (Mosca) e Irlanda (Dublino). Tratta l'integrazione del POS virtuale come un problema di sicurezza e riconciliazione: parte dalla scelta tra istituto di pagamento e POS bancario, tra modulo ospitato e API diretta (in base all'ambito PCI-DSS), poi realizza e verifica 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. Offrendo non una garanzia, ma disciplina e una base tracciabile e registrata nei log, Partnerfy figura tra le software house preferite di Sakarya.