API Paypal: come implementare un pagamento online? Le procedure specifiche (4/6)
Proseguiamo con la guida riguardante l’implementazione di pagamenti tramite le API Paypal. Come avrai avuto modo di leggere nei precedenti articoli, in questa guida stiamo realizzando un’applicazione grazie alla quale é possibile attivare degli account a pagamento (istant access, ovvero le risorse sono disponibili immediatamente dopo il pagamento).
Nell’articolo precedente abbiamo concluso la preparazione della classe IPNListener, la quale ci fornisce lo strumento per verificare se è possibile procedere (isReadyTransaction) all’attivazione dell’account.
Come abbiamo visto questa classe sarà utilizzabile in qualunque tipo di applicazione che necessita di un pagamento. Si tratta infatti di una raccolta di tutte le procedure necessarie alla verifica della notifica di pagamento.
Ora dovremo sviluppare il resto, ovvero la classe YIIListener che sarà un’estensione di IPNListener e conterrà i metodi che si occuperanno della creazione dell’account come pure della definizione dei metodi astratti contenuti nella classe parent.
API Paypal: La classe YIIListener per gestire il pagamento online
Creiamo il file YIIListener.php ed iniziamo ad includere IPNListenr.php, a dichiarare la classe e la proprietà $conn che conterrà la risorsa di connessione del database.
<?php require_once 'IPNListener.php'; class YIIListener extends IPNListener { protected $conn;
Ed ora scriviamo il metodo che provvederà alla connessione al database:
protected function dbConnect() { $this->conn = mysql_connect(HOST,DB_USER,DB_PASSWORD) OR die(); mysql_select_db(DB_NAME,$this->conn) OR die(); }
A questo punto implementiamo il metodo astratto isVerifiedAmmount().
Nel nostro caso sarà molto semplice. Come detto abbiamo un solo prodotto ed un solo prezzo.
Verificheremo dunque che il pagato (senza deduzione per le commissioni) corrisponda a quanto abbiamo indicato nella costante AMMOUNT.
protected function isVerifiedAmmount() { if($_POST['mc_gross'] == AMMOUNT) { return TRUE; } return FALSE; }
Ed ora procediamo ad implementare il secondo metodo astratto isNotProcessed(), che si occupa di verificare che la transazione non sia già stata processata. Per fare questo controlleremo la presenza dell’id della transazione (txn_id) nel database:
protected function isNotProcessed() { $this->dbConnect(); $sql = "SELECT * FROM utenti WHERE idTransazione=’$_POST[txn_id]’"; $res = mysql_query($sql, $this->conn); if(mysql_num_rows($res)) { return FALSE; } return TRUE; }
Ora sviluppiamo un piccolo metodo grazie al quale otterremo una password casuale da attribuire al nuovo utente.
protected function getRandPassword() { $result = ""; for($i = 0;$i < 10; $i++) { $chr = rand(40,126); $result .= chr($chr); } return $result; }
Come vedi con questo metodo andiamo a scegliere 10 caratteri casuali compresi tra il carattere 40 ed il carattere 126 del codice ASCII. Partiamo dal 40 in modo da evitare fastidiosi apici e virgolette.
Ora sviluppiamo il metodo sendLoginData() resposabile di inviare all’utente i dati del suo nuovo account. Questo metodo sarà invocato poi all’interno di un altro metodo che si occuperà di inserire il nuovo account nel database.
protected function sendLoginData($password) { if(SIMULATION) { $to = ADMIN_MAIL; $add = "- SIMULAZIONE -"; } else { $to = $_POST['payer_email']; $add = ""; } $subject = "$add Attivazione account su Your Inspiration Images"; $from = NO_REPLY; $message = "Ciao $_POST[first_name] e benvenuto su YII\r\n"; $message .= "Ecco i tuoi dati di autenticazione:\r\n\r\n"; $message .= "Nome utente: $_POST[payer_email] \r\n"; $message .= "Password: $password \r\n\r\n"; $message .= "Your Inspiration Images Team"; mail($to,$subject,$message,"From: noreply<$from>"); }
Anche in questo caso, iniziamo con il verificare se ci trovaimo in ambiente di simulazione. In caso affermativo aggiungiamo la parola simulazione all’oggetto dell’email sempre per non fare confusione. Inoltre, in simulazione, faremo in modo che l’email venga inviata a noi stessi.
Infatti, l’email dell’utente di Sandbox non esiste, o meglio può ricevere messaggi solo dall’interno.
In ogni caso non è utilizzabile. Dunque, per verificare l’effettivo e corretto invo dell’email con i dati di autenticazione, farò in modo che venga inviata a me. Mentre nell’ambiente di produzione sarà correttamente inviata al pagante (payer_email).
Siamo così giunti alla definizione dell’ultimo metodo (insertNewUser) che, dopo aver verificato la bontà della notifica, procederà all’inserimento del nuovo utente nel database ed all’invio dell’email con i dati di autenticazione.
public function insertNewUser() { if($this->isReadyTransaction()) { $password = $this->getRandPassword(); $md5password = md5($password); $sql = "INSERT INTO utenti (nome,cognome,email,username,password,idTransazione) VALUES ('$_POST[first_name]','$_POST[last_name]','$_POST[payer_email]','$_POST[payer_email]','$md5password','$_POST[txn_id]')"; mysql_query($sql,$this->conn); $this->sendLoginData($password); } }
Come prima cosa, chiaramente verificheremo l’esito del metodo isReadyTransaction(). Solo se l’esito è positivo, generiamo una password casule con il metodo getRandPassword().
Creiamo l’hash di questa password da inserire nel database.
Scriviamo la query di inserimento e la eseguiamo.
Ed infine inviamo i dati di autenticazione tramite il metodo sendLoginData() avendo cura di passare la password (in chiaro ovviamente).
Come vedi non ho fatto nessun escape delle stringhe in entrata. Infatti, a questo punto siamo certi (in quanto lo abbiamo verificato) che i dati ci arrivano da PayPal e dunque li considero dati sicuri (non credo proprio che PayPal si diverta ad inviarci delle sql injection).
Non ci resta che instanziare la classe ed invocare questo ultimo metodo. Il risultato finale sarà questo:
<?php require_once 'IPNListener.php'; class YIIListener extends IPNListener { protected $conn; protected function isVerifiedAmmount() { if($_POST['mc_gross'] == AMMOUNT) { return TRUE; } return FALSE; } protected function isNotProcessed() { $this->dbConnect(); $sql = "SELECT * FROM utenti WHERE idTransazione=’$_POST[txn_id]’"; $res = mysql_query($sql, $this->conn); if(mysql_num_rows($res)) { return FALSE; } return TRUE; } protected function dbConnect() { $this->conn = @mysql_connect(HOST,DB_USER,DB_PASSWORD) OR die(); @mysql_select_db(DB_NAME,$this->conn) OR die(); } protected function getRandPassword() { $result = ""; for($i = 0;$i < 10; $i++) { $chr = rand(40,126); $result .= chr($chr); } return $result; } protected function sendLoginData($password) { if(SIMULATION) { $to = ADMIN_MAIL; $add = "- SIMULAZIONE -"; } else { $to = $_POST['payer_email']; $add = ""; } $subject = "$add Attivazione account su Your Inspiration Images"; $from = NO_REPLY; $message = "Ciao $_POST[first_name] e benvenuto su YII\r\n"; $message .= "Ecco i tuoi dati di autenticazione:\r\n\r\n"; $message .= "Nome utente: $_POST[payer_email] \r\n"; $message .= "Password: $password \r\n\r\n"; $message .= "Your Inspiration Images Team"; mail($to,$subject,$message,"From: noreply<$from>"); } public function insertNewUser() { if($this->isReadyTransaction()) { $password = $this->getRandPassword(); $md5password = md5($password); $sql = "INSERT INTO utenti (nome,cognome,email,username,password,idTransazione) VALUES ('$_POST[first_name]','$_POST[last_name]','$_POST[payer_email]','$_POST[payer_email]','$md5password','$_POST[txn_id]')"; mysql_query($sql,$this->conn); $this->sendLoginData($password); } } } $ipn = new YIIListener(); $ipn->insertNewUser(); ?>
Questo è il file al quale dovrà puntare PayPal che avevamo genericamente chiamato lettoreIPN.php. Ora modifichiamolo con il nome e percorso corretto così come descritto all’inizio del secondo articolo di questa guida.
Conclusione
Se hai seguito correttamente gli articoli fino ad ora, il tuo sistema di pagamento realizzato sfruttanto le API PayPal dovrebbe funzionare senza problemi. Potrai eseguire il pagamento con l’utente utente ed il tutto dovrebbe funzionare. Dovrebbe. Nel prossimo articolo procederemo quindi ad un’accurata sessione di test.
Fino ad ora é tutto chiaro? Sei riuscito a seguire e a comprendere i vari passaggi anche se oggettivamente complessi?
Usare le API Paypal: Articoli di questa guida
28 commenti
Trackback e pingback
-
Tweets that mention Come implementare un pagamento online: Le procedure specifiche (4/6) | Your Inspiration Web -- Topsy.com
[...] This post was mentioned on Twitter by mtx_maurizio, Simone D'Amico. Simone D'Amico said: Come implementare un pagamento online: Le… -
Come implementare un pagamento online: Preparazione (1/6) | Your Inspiration Web
[...] Le procedure specifiche [...] -
Come implementare un pagamento online: chiarirsi le idee (2/6) | Your Inspiration Web
[...] Le procedure specifiche [...]
Bellissima guida, proprio quello che stavo cercando. A leggere la documentazione messa a disposizione da PayPal avevo capito davvero poco.
Complimenti per la chiarezza e grazie di cuore per la condivisione!
Felice di esere stato utile Salvatore :-)
Ciao Maurizio,
ho testato l’aplicazione ma non effettua la transizione perchè nel dettaglio (nel panello dell’amministrazione) c’è scritto “Nome: Test User (Il mittente del pagamento è Non statunitense – Verificato)”
Che significa? Cosa devo fare per fare in modo che non si verifichi? Non so se dipende da questo ma non salva neanche i dati nel db.
Dipende dal tipo di scenario che ti sei creato nell’ambiente di simulazione. Anche a me una volta é capitato, ma non ricordo più da cosa dipendeva
Ciao, forse è la volta buona che ci capisco qualche cosa stavolta, leggere passo passo aiuta parecchio se non sai dove mettere mano……. :D
p.s. un’amica che sta realizzando un piccolo e-commerce per una fiorista, ha utilizzato dei plug-in belli che pronti presi da wordpress.org
Il dubbio è……..ma in questi plug-in “precotti” la procedura per il pagamento online è già compresa ed è solo da impostare, oppure sono da intendersi solo come una base alla quale applicare questo tutorial??………sai se mi dovesse capitare….almeno so da che parte sbattere la testa almeno :D
Credo dipenda dal plugin; ce ne sono diversi ed ognuno ha le sue caratteristiche.
Mi serviva proprio!! grazie mille! ;)
:-)
Ciao Maurizio,
ho da poco completato il mio primo esempio di e-commerce come da guida. Testando con diversi browser ho trovato un porblema che riguarda il solo IE 8.0. Ovvero cliccando sulla tasto di compra subito vengo rediretto ad una pagina di Paypal di errore che dice:
Sorry – Your last action could not be completed
Questo mi accade solo con IE, mentre sia Chrome che FireFox non danno problemi.
Hai idea di cosa sia questo errore?
Grazie
Ciao
Nessuna idea; tra l’altro a me funziona. Misteri di ie….
Ciao Maurizio!
Sono arrivato alla fine e non ci crederai… ho capito tutto!!! :D
(non ho ancora testato però perchè sandbox mi da sempre quel problema che sull’account dell’admin non mi accredita la pecunia.)
Una domanduzza:
Nel commento che ti ho lasciato nel primo capitolo ti chiedevo se fosse possibile modificare gli script per adattarli ai casi specifici… io ad esempio ho gli utenti che sono già iscritti e pagando apparirebbero nelle ricerche.
In pratica una volta che hanno pagato in un campo del database passo il valore da 0 a 1… e fin quì il tuo tutorial è chiarissimo.
Ma come faccio a farmi restituire da PayPal un parametro identificativo per l’utente che ha pagato, proveniendo questi da una pagina del mio sito o (in caso di pagamento posticipato) da un link inviato tramite email?
Devi creare il bottone dinamicamente, così come mostrerò nell’ultimo articolo.
A quel punto puoi far passare ad esempio l’id dell’utente nella variabile custom. Se non ti sarà chiaro alla fine dell’utimo articolo, rifammi la domanda li.
Ciao! Fantastica guida! Una domanda: Avrei bisogno di adattare questa procedura in 2 aspetti: 1) Non necessito di un database, in quanto l’unica cosa che mi serve sono i dati degli utenti, ma quelli li prendo direttamente da paypal 2) avrei bisogno di inoltrare all’utente un file (si tratta di eCommerce di eBook) via mail immediatamente dopo che pay pal mi invia conferma dell’avvenuto pagamento. Come posso fare?
Spero di essere stato chiaro!
Ciao Stiven e grazie.
1) Se non ti serve il database non utilizzarlo
2) Se devi inviare all’utente, in allegato, l’eBook che ha acquistato, io procederei così:
– Passo un riferimento al prodotto acquistato nella variabile custom (guarda l’ultimo articolo di questa serie)
– Alla ricezione dei dati vado a prendere questo prodotto e lo allego all’email che invierò all’indirizzo email fornito da paypal
Una cosa mi domando: perché non tenere traccia degli ordini e degli utenti? Potrebbe essere utile.
Ciao, intanto ti faccio i complimenti per la guida, che è molto utile e molto chiara, poi ti ringrazio perchè mi è stata molto utile.
Mi permetto di segnalare un piccolo problema, non so se è solo un problema mio o se è così per tutti, comunque nel metodo insertNewUser() del fine YIIListener.php, la query non funziona se prima non aggiungo la chiamata al database $this->dbConnect();
Vorrei anche fare una domanda.
Io nel form iniziale specifico anche le variabili first_name e last_name, tanto che nella pagina di sandbox vengono correttamente riconosciute, però poi nel database viene salvato sempre Test come first_name e User come last_name. Hai idea se è un problema legato a qualcosa che ho scritto io o è relativo al codice che ho preso dal blog? Perchè in realtà non riesco a capire, però allo stesso tempo non trovo riferimenti del genere nel tuo codice.
Grazie
Ciao
Salve!
non so che dire! :/
Cioè ho creato i 3 file, ho modificato con i valori “che io credo siano giusti :D”, ma questo credo sia secondario, il punto è qua, copiando “identico” nel primo file, e poi nel secondo, Le procedure generali e
Le procedure specifiche (i file li ho chiamati il primo IPNListener.php e il secondo lettoreIPN.php), però quando provo a fare una Instant Payment Notification (IPN) simulator, mi da l’errore IPN delivery failed. HTTP error code 500: Internal Server Error.
La cosa che più mi da fastidio è che provando con un file generico che ho sul server, mi dice che è tutto ok!! -.-
Cmq sul log di cpanel, non mi da nessun errore.
Cmq consigli?
Grazie!
Ho dimenticato di dirti grazie per la giuda! :D
correggo una cosa, in cpanel mi da questo errore:
SoftException in Application.cpp:256: File “/home/ildel214/public_html/lettoreIPN.php” is writeable by group
Aggiungo anche: [21-Oct-2011 17:02:05] PHP Parse error: syntax error, unexpected T_VARIABLE, expecting T_FUNCTION in /home/ildel214/public_html/lettoreIPN.php on line 6
Trovato un file error_log, nella cartella dove ci sono i miei 3 file con dentro quello sopra.
ciao e complimenti per l’ottima guida, sai mica se è fattibile una cosa del genere anche con il pagamento First pay data global gateway? ciao:)
Ciao Maurizio grandissima guida spiegata benissimo, il problema sono io:),
la transazione avviene regolarmente ma non viene inserito il nuovo utente nel database, l’url ipn è corretto in sandbox. Questo è l’hosting gratuito dove faccio le prove 000webhost.com, magari è l’hosting che non permette la comunicazione Server to server, nel log non sono presenti errori.
Grazie
Valentino
ciao intanto grazie mille per la guida vorrei chiederti : invece di creare un nuovo utente posso magari modificare lo stato dell utente? nel senso che da utente Registrato diventi utente premium
Grazie in anticipo per la risposta
Ciao Maurizio,
ho provato il tuo script e direi che funziona.
Ho però alcuni problemi:
– Su Bluehost mi funziona BENINO addirittura integrandolo a wordpress.
Mi inserisce 7/8 record alla volta nella tabella del database tutti uguali.
Ma qui con un distinct riesco a filtrare (anche se rimane il database sporco).
– Su Godaddy non funziona non capisco cosa non va.
ho inserito delle mail() appena prima dei return false ma arrivano email a iosa 8/9 emaail alla volta!!
Come posso fare per implementare dei report quando uno dei 5 metodi di isNotProcessed
restituisce false e quindi fa restituire false anche a isNotProcessed?
Su Godaddy.com ci sono un sacco di noie per leggere il file error_log che invece con bluehost è facilmente scaricabile via FTP.
Ciao Maurizio,
innanzitutto volevo ringraziarti per questa ottima guida, ben fatta davvero.
Solo una domanda. Io avrei la necessità di inserire nel DB dei dati precedentemente salvati all’interno di una sessione (es: $_SESSION[‘firstname’]) invece che i dati passati da Paypal ($_POST[‘first_name’]). E’ possibile lavorando con le classi? Grazie mille!
Perdona Maurizio, sto studiando la classe ma non mi è chiara una cosa in questa riga if($_POST[‘mc_gross’] == AMMOUNT)nella funzione IsVerifieAmmount; cosa è il post di “mc_gross”?, è un post fisso o è il nome del tuo pulsante