venerdì 27 aprile 2012

Gateway Consorzio Triveneto per WP e-commerce

Recentemente ho dovuto integrare il gateway di pagamento del Consorzio Triveneto nel plugin WP e-commerce, uno dei più noti plugin di commercio elettronico per Wordpress.
All'inizio pensavo di utilizzare l'ottima classe PgConsTriv di Davide Gullo, poi ho realizzato un'integrazione veramente minimale partendo dagli esempi forniti dal Consorzio e dalla documentazione di WP e-commerce.
E' puro spaghetti-code ma forse a qualcuno può tornare utile :)
Di seguito il codice del nuovo gateway da salvare nel folder /wp-content/plugins/wp-e-commerce/wpsc-merchants/ del vostro server.
Tutte le funzioni andrebbero raccolte in un unico file (compresi i callback di ritorno dal gateway), per intenderci $responseURL dovrebbe essere qualcosa del genere:

get_option('siteurl')."/?constriv=ok";


e quindi tutte le azioni verrebbero gestite dalla funzione gateway_constriv_gateway, purtroppo ho avuto qualche problema nella gestione degli indirizzi da parte del gateway e siccome sono pigro, non ho investigato più di tanto:
Alla fine ho utilizzato un work-around che segue passo-passo l'esempio fornito nella documentazione: ho usato i file receipt.php ed error.php per gestire la comunicazione con il gateway, questi poi fanno un redirect per completare la gestione dell'ordine e la visualizzazione di eventuali messaggi.
Nel file receipt.php dovreste implementare eventuali meccanismi di verifica della provenienza della transazione tramite i parametri udf*, io ho usato udf3 per trasportare l'id di sessione necessario a completare le operzioni sull'ordine.

constriv.merchant.php

1:  <?php  
2:  /**  
3:    * Gateway wp e-commerce per Consorzio Triveneto  
4:    * Marco Montesi 2012-04-26  
5:    * marco.montesi@gmail.com  
6:   *  
7:   * @package wp-e-comemrce  
8:   * @since 3.7.6  
9:   * @subpackage wpsc-merchants  
10:   */  
11:  $nzshpcrt_gateways[$num]['name'] = 'Consorzio Triveneto';  
12:  $nzshpcrt_gateways[$num]['internalname'] = 'constriv_gateway';  
13:  $nzshpcrt_gateways[$num]['function'] = 'gateway_constriv_gateway';  
14:  $nzshpcrt_gateways[$num]['form'] = "form_constriv_gateway";  
15:  $nzshpcrt_gateways[$num]['submit_function'] = "submit_constriv_gateway";  
16:  // admin form per memorizzare ID e password dell'account del cliente  
17:  function form_constriv_gateway()  
18:  {  
19:    $output ='<tr><td>';  
20:    $output.='<input name="constriv_tranportalid" type="text" value="'.get_option('constriv_tranportalid').'"/>';  
21:    $output.='TranPortalID';  
22:    $output.='<input name="constriv_password" type="text" value="'.get_option('constriv_password').'"/>';  
23:    $output.='Password';  
24:    $output .='</tr></td>';  
25:    return $output;  
26:  }  
27:  //  
28:  function submit_constriv_gateway()  
29:  {  
30:    if($_POST['constriv_tranportalid'] != null) {  
31:      update_option('constriv_tranportalid', $_POST['constriv_tranportalid']);  
32:    }  
33:    if($_POST['constriv_password'] != null) {  
34:      update_option('constriv_password', $_POST['constriv_password']);  
35:    }  
36:    return true;  
37:  }  
38:  // gateway   
39:  function gateway_constriv_gateway($seperator, $sessionid)  
40:  {  
41:    global $wpdb, $wpsc_cart;  
42:    //This grabs the purchase log id from the database  
43:    //that refers to the $sessionid  
44:    $purchase_log = $wpdb->get_row( "SELECT * FROM `".WPSC_TABLE_PURCHASE_LOGS."` WHERE `sessionid`= ".$sessionid." LIMIT 1", ARRAY_A) ;  
45:    // costruisco la stringa da passare al gateway per l'auth  
46:    $ID     = get_option('constriv_tranportalid');  
47:    $Password = get_option('constriv_password');  
48:    $Action   = "4";  
49:    $Amt    = number_format($wpsc_cart->total_price,2);  
50:    // sostituire con il proprio dominio  
51:    $ResponseURL = "<il_mio_dominio>/constriv/receipt.php";  
52:    $ErrorURL =  "<il_mio_dominio>/constriv/error.php";  
53:    $TrackId = "<il_mio_dominio>-".$purchase_log['id'];  
54:    $DataToSend ="id=$ID&password=$Password&action=$Action&amt=$Amt&currencycode=978&langid=ITA&responseURL= $ResponseURL&errorURL=$ErrorURL&trackid=$TrackId&udf1=AA&udf2=BB&udf3=$sessionid&udf4=DD&udf5=EE";   
55:    // ambiente di test, da sostituire con URL di produzione  
56:    $URL = "https://test4.constriv.com/cg301/servlet/PaymentInitHTTPServlet";  
57:    //Apro la connessione  
58:    $ch=curl_init($URL);   
59:    //Imposto gli headers HTTP  
60:    //imposto curl per protocollo https  
61:    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, FALSE);  
62:    curl_setopt($ch,CURLOPT_POST,1);  
63:    //Invio i dati  
64:    curl_setopt($ch,CURLOPT_POSTFIELDS,$DataToSend);  
65:    //imposta la variabile PHP   
66:    curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);   
67:    //Ricevo la risposta dal server  
68:    $varResponse=curl_exec($ch);   
69:    //chiudo la connessione   
70:    curl_close($ch);   
71:    if (substr($varResponse,0,7) == '!ERROR!') {  
72:      echo $varResponse;  
73:    } else {  
74:      //Separo il contenuto della stringa ricevuta (PaymentID:RedirectURL)  
75:      $varPosiz= strpos($varResponse, ':http');  
76:      $varPaymentId= substr($varResponse,0,$varPosiz);  
77:      $nc=strlen($varResponse);  
78:      $nc=($nc-17);  
79:      $varRedirectURL=substr($varResponse,$varPosiz+1);  
80:      //Creo l'URL di redirezione  
81:      $varRedirectURL ="$varRedirectURL?PaymentID=$varPaymentId";  
82:      //echo $varRedirectURL;  
83:      //Redirezione finale del browser sulla HPP  
84:      //echo $varRedirectURL;  
85:      echo "<meta http-equiv=\"refresh\" content=\"0;URL=$varRedirectURL\">";  
86:    }  
87:  }  
88:  function nzshpcrt_constriv_callback()  
89:  {  
90:   global $wpdb;  
91:   if( isset($_GET['constriv']) && ($_GET['constriv'] == 'transok')) {  
92:      $sessionid = $_REQUEST["udf3"];  
93:      $PayID=$_REQUEST["paymentid"];  
94:      $TransID=$_REQUEST["tranid"];  
95:      $ResCode=$_REQUEST["resultcode"];  
96:      $AutCode=$_REQUEST["auth"];  
97:      $PosDate=$_REQUEST["postdate"];  
98:      $TrckID=$_REQUEST["trackid"];  
99:      $Brand = $_REQUEST["cardtype"];  
100:      $Protocol = $_REQUEST["payinst"];  
101:      $Protection = $_REQUEST["liability"];  
102:      $HostResponseCode = $_REQUEST["responsecode"];  
103:      $Mistake = $_REQUEST["Error"];  
104:      $MistakeText = $_REQUEST["ErrorText"];  
105:      if( strlen(trim($AutCode)) and $ResCode == 'APPROVED' ) {  
106:        $sql = "UPDATE `".WPSC_TABLE_PURCHASE_LOGS."` SET `processed`= '3' WHERE `sessionid`=".$sessionid;  
107:        $wpdb->query($sql);  
108:        $transact_url = get_option('transact_url');  
109:        unset($_SESSION['WpscGatewayErrorMessage']);  
110:        echo '<script>document.location="'.$transact_url.'&sessionid='.$sessionid.'&status=ok";</script>';        
111:      } else {  
112:        $sql = "UPDATE `".WPSC_TABLE_PURCHASE_LOGS."` SET `processed`= '5' WHERE `sessionid`=".$sessionid;  
113:        $wpdb->query($sql);  
114:        $transact_url = get_option('transact_url');  
115:        $_SESSION['WpscGatewayErrorMessage'] = $Mistake.' '.$MistakeText;  
116:        echo '<script>document.location="'.$transact_url.'&sessionid='.$sessionid.'&status=ko";</script>';        
117:      }  
118:    }  
119:    if( isset($_GET['constriv']) && ($_GET['constriv'] == 'transko')) {  
120:      $sql = "UPDATE `".WPSC_TABLE_PURCHASE_LOGS."` SET `processed`= '5' WHERE `sessionid`=".$sessionid;  
121:      $wpdb->query($sql);  
122:      $transact_url = get_option('transact_url');  
123:      $_SESSION['WpscGatewayErrorMessage'] = $Mistake.' '.$MistakeText;  
124:      echo '<script>document.location="'.$transact_url.'&sessionid='.$sessionid.'";</script>';    
125:    }  
126:  }  
127:  //  
128:  add_action('init', 'nzshpcrt_constriv_callback');  
129:  ?>  

receipt.php
1:  <?php   
2:  $PayID=$_POST["paymentid"];  
3:  $TransID=$_POST["tranid"];  
4:  $ResCode=$_POST["result"];  
5:  $AutCode=$_POST["auth"];  
6:  $PosDate=$_POST["postdate"];  
7:  $TrckID=$_POST["trackid"];  
8:  $UD1=$_POST["udf1"];  
9:  $UD2=$_POST["udf2"];  
10:  $UD3=$_POST["udf3"];  
11:  $UD4=$_POST["udf4"];  
12:  $UD5=$_POST["udf5"];  
13:  $Brand = $_POST["cardtype"];  
14:  $Protocol = $_POST["payinst"];  
15:  $Protection = $_POST["liability"];  
16:  $HostResponseCode = $_POST["responsecode"];  
17:  $Mistake = $_POST["Error"];  
18:  $MistakeText = $_POST["ErrorText"];  
19:  // Nell URL seguente inserire l'indirizzo corretto del proprio server  
20:  if( strlen(trim($AutCode)) and $ResCode == 'APPROVED' and $UD1=='AA' and $UD2 == 'BB' ) {  
21:    $ReceiptURL="REDIRECT=http://<mio_dominio>/?page_id=<id_carrello>&constriv=transok&PaymentID=".$PayID."&TransID=".$TransID."&TrackID=".$TrckID."&postdate=".$PosDate."&resultcode=".$ResCode."&auth=".$AutCode."&Error=".$Mistake."&ErrorText=".$MistakeText."&cardtype=".$Brand."&payinst=".$Protocol."&liability=".$Protection."&responsecode=".$HostResponseCode."&udf3=".$UD3;       
22:  } else {  
23:    $ReceiptURL="REDIRECT=http://<mio_dominio>/?page_id=<id_carrello>&constriv=transko&PaymentID=".$PayID."&TransID=".$TransID."&TrackID=".$TrckID."&postdate=".$PosDate."&resultcode=".$ResCode."&auth=".$AutCode."&Error=".$Mistake."&ErrorText=".$MistakeText."&cardtype=".$Brand."&payinst=".$Protocol."&liability=".$Protection."&responsecode=".$HostResponseCode."&udf3=".$UD3;       
24:  }  
25:  echo $ReceiptURL;   
26:  ?>  


error.php
1:  <?php   
2:  $PayID=$_POST["paymentid"];  
3:  $TransID=$_POST["tranid"];  
4:  $ResCode=$_POST["result"];  
5:  $AutCode=$_POST["auth"];  
6:  $PosDate=$_POST["postdate"];  
7:  $TrckID=$_POST["trackid"];  
8:  $UD1=$_POST["udf1"];  
9:  $UD2=$_POST["udf2"];  
10:  $UD3=$_POST["udf3"];  
11:  $UD4=$_POST["udf4"];  
12:  $UD5=$_POST["udf5"];  
13:  $Brand = $_POST["cardtype"];  
14:  $Protocol = $_POST["payinst"];  
15:  $Protection = $_POST["liability"];  
16:  $HostResponseCode = $_POST["responsecode"];  
17:  $Mistake = $_POST["Error"];  
18:  $MistakeText = $_POST["ErrorText"];  
19:  $ReceiptURL="REDIRECT=http://<mio_dominio>/?page_id=<id_carrello>&constriv=transko&PaymentID=".$PayID."&TransID=".$TransID."&TrackID=".$TrckID."&TransID=".$TransID."&TrackID=".$TrckID."&postdate=".$PosDate."&resultcode=".$ResCode."&auth=".$AutCode."&Error=".$Mistake."&ErrorText=".$MistakeText."&cardtype=".$Brand."&payinst=".$Protocol."&liability=".$Protection."&responsecode=".$HostResponseCode."&udf3=".$UD3;       
20:  echo $ReceiptURL;   
21:  ?>  


giovedì 1 dicembre 2011

Gingerbread (Android 2.3) su Toshiba Folio

Salve a tutti, qualche settimana fa mi hanno praticamente regalato un Toshiba Folio.
Toshiba ha prodotto questo bel tablet Tegra2 e ci ha messo su un Android 2.2 castrato del market e delle applicazioni Google.
La frustrazione di avere tra le mani un hardware ancora quasi al top con un software deprimente è stata tale che mi sono deciso a provare la via del modding.
La cosa non è stata tanto facile perché Toshiba, oltre ad aver fatto questa scelta a dir poco discutibile sul sistema installato, ha pensato bene di aggiornare il firmware in modo tale che non fosse possibile flashare ROM alternative :)
Ovviamente l'esemplare in mio possesso aveva il firmware aggiornato...
Comunque, per farla breve, alla fine sono riuscito ad installare Cyanogen 7.1, ovvero Android 2.3.7, e adesso il tablet è una bomba: tutte le Google Apps, Flash, Firefox, giochini 3D, etc etc



Di seguito riassumo i passi da fare se volete cimentarvi, e siccome sono un po' bastardo dentro non metto le istruzioni passo passo (troppa fatica) ma solo i link delle risorse ;)

  1. Rootare il Folio;
    di guide ce ne sono una marea;
  2. Scaricare e flashare un firmware pre-aggiornamento
    ho usato la recovery della FolioMod più o meno come dice qua  http://forum.xda-developers.com/showthread.php?t=961826 ;
  3. opzionale: flashare ClockWorkMod per Folio;
    probabilmente non è necessario ma poi avrete molte più opzioni: http://www.arctablet.com/blog/featured/clockworkmod-recovery-v3-0-0-5-toshiba-folio100/
  4. scaricare e flashare Cyanogen per Folio per update a 2.3.5;
    avevo provato a mettere direttamente la 2.3.7 ma non riuscivo a flashare;
    http://forum.xda-developers.com/showthread.php?t=933989&page=53
  5. scaricare e flashare le Google Apps;ovviamente scegliete la versione per Cyanogen 7:
    http://wiki.cyanogenmod.com/index.php?title=Latest_Version/Google_Apps
  6. aggiornare a 2.3.7;
    addirittura forse adesso c'è qualcosa di più aggiornato, andate a spulciare qua:
    http://forum.xda-developers.com/showthread.php?t=933989
  7. enjoy.

giovedì 25 agosto 2011

Widget jQuery su Ampoliros

Un piccolo esempio dell'utilizzo della libreria jQuery e di alcuni widget jQuery UI.
La piattaforma di sviluppo è Ampoliros, la libreria AJAX utilizzata è XAJAX.

PS ho fatto lo screencast per provare SOM: consigliato!

domenica 17 luglio 2011

Feed stream pubblico Google+ (unofficial)

Ancora Google non ha pubblicato le API di G+ ma gli smanettoni si sono dati da fare da subito.
Ad esempio è possibile ottenere in formato JSON lo stream dei post pubblici di un utente.
Dopo aver visto questo articolo (grazie Gian Angelo) ho provato subito con qualche riga di PHP.
Ci sono già progetti promettenti in rete, ma io mi sono limitato allo stretto indispensabile.
Accertatevi di avere attivato il supporto SSL in PHP e sostituite $googleplus_id con l'id utente che vi interessa (basta guardare l'url del profilo):

$googleplus_id = '110373656360084352107';
$url = 'https://plus.google.com/_/stream/getactivities/'.$googleplus_id.'/?sp=%5B1%2C2%2C%22'.$googleplus_id.'%22%2Cnull%2Cnull%2Cnull%2Cnull%2C%22social.google.com%22%2C%5B%5D%5D';


per recuperare i dati basta usare file_get_contents (a patto che in php.ini abbiate settato allow_url_fopen):

$output = file_get_contents( $url );


La stringa JSON ottenuta va "ripulita" come indicato nell'articolo citato:

$json = substr( $output, 4 );
$json = str_replace( '[,', '["",', $json );
while( strpos( $json, ',,' ) ) $json = str_replace( ',,', ',"",', $json );




Finalmente abbiamo una stringa che possiamo dare in pasto a json_decode per ottenere un array contenete i nostri post pubblici su Google+:

$feedArray = json_decode($json);
foreach( $feedArray[1][0] as $currPost ) {
echo 'Autore: '.$currPost[3].'<br>';
echo 'Titolo: '.$currPost[4].'<br>';
foreach( $currPost[11] as $postLinks ) {
echo ' -> Titolo link: '.$postLinks[3].'<br>';
echo ' -> Descr. link: '.$postLinks[21].'<br>';
echo ' -> Url link: '.$postLinks[24][1].'<br>';
}
echo '<br>';
}



That's all!

Nota 
Se non volete abilitare allow_url_fopen potete provare con cURL, sostituendo la chiamata a file_get_contents con questo codice:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url );
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1) ;
$output = curl_exec($ch);
curl_close($ch);

mercoledì 13 luglio 2011

Smartphone e drag&drop

Al giorno d'oggi se le tue applicazioni non supportano in qualche modo i dispositivi mobili non sei nessuno!
Mi ritengo fortunato perché da circa 10 anni sviluppo quasi esclusivamente in ambiente web, utilizzando tecnologie standard, quindi le applicazioni sono direttamente visualizzabili su qualunque aggeggio abbia un browser degno di questo nome.
Ci sono però alcune situazioni che richiedono uno sforzo ulteriore.
E' il caso di tutti i dispositivi dotati di touch screen e delle funzionalità drag&drop.
Su iPhone, iPad o sul vostro smartphone Android l'applicazione viene visualizzata correttamente ma se provate a trascinare... il browser scrolla o esegue un pan nella pagina.
Ad esempio in Kroneos MP gli utenti si possono scambiare dei post-it, ovvero semplici messaggi che rimangono "appiccicati" al loro desktop virtuale piuttosto che al monitor reale ;)
Questi oggetti anche graficamente riprendono l'aspetto di un post-it, il classico quadrato di colori fosforescenti.
Gli utenti possono spostare i posti-it sullo schermo ed ancorarli in posizioni comode.

Con il mouse il tutto avviene in maniera molto naturale.
Ma su un touch screen è semplicemente impossibile.
Dei ragazzi italiani hanno scritto una bella classe Javascript che implementa dei div "draggabili" su dispositivi touch: Drag Drop Library for Ipad & Iphone (funziona anche con Android).
L'ho provata, ma su pagine particolarmente cariche ho avuto problemi di prestazioni.
Per ora l'unica soluzione è stato il classico uovo di Colombo, ovvero ho dovuto rinunciare al drag&drop :P
Al posto dei foglietti svolazzanti i messaggi li ho impilati in un accordion jQuery UI (nel caso lo user agent corrisponda ad un iQualcosa o ad un Droide).
Qualcuno ha avuto un'idea più brillante?

venerdì 8 luglio 2011

Una bella sorpresa: jQuery Mobile

Volete realizzare la versione "mobile" del vostro sito?
Volete che l'aspetto sia il più possibile simile ad una app nativa?
Volete gradevoli effetti grafici?
Volete tempi di sviluppo rapidi e semplice integrazione con altri tools?
Ok, jQuery Mobile fa per voi.
Poche ore di lavoro per leggere i tutorial, integrare la libreria nel cms ed ecco il risultato: http://www.efficienza-professionale.it/mobile/index.php
Tutto in alpha stage, ma il primo contatto è stato ottimo.

mercoledì 6 luglio 2011

Gruppi su GMail con Zend GData

Da qualche tempo ho realizzato un'integrazione tra l'anagrafica di Kroneos MP ed i contatti di GMail.
Ho utilizzato Zend Gdata visto che avevo già ottenuto buoni risultati nell'integrazione del calendario con GCalendar.
Per la gestione dei contatti però non ho trovato documentazione ed esempi come nel caso degli eventi.
La fonte migliore che ho trovato in rete è questa.
Nell'articolo non viene trattata la gestione dei gruppi, per cui condivido quello che ho concluso spulciando direttamente le API del protocollo di Google.
Supponendo che abbiate a disposizione un oggetto DOMDocument come descritto nell'articolo:


$doc = new DOMDocument();
$doc->formatOutput = true;
$entry = $doc->createElement('atom:entry');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:atom', 'http://www.w3.org/2005/Atom');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:gd', 'http://schemas.google.com/g/2005');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:gContact', 'http://schemas.google.com/contact/2008');
$doc->appendChild($entry);

per associare il contatto ad un gruppo basta aggiungere questo codice:


$gMembership = $doc->createElement('gContact:groupMembershipInfo');
$gMembership->setAttribute('href', $groupUrl );
$gMembership->setAttribute('deleted', 'false' );
$entry->appendChild($gMembership);

La variabile $groupUrl contiene il riferimento al gruppo che ci interessa.
Se il gruppo non esiste occorre crearlo con una procedura del genere:

$doc = new DOMDocument();
$doc->formatOutput = true;
$entry = $doc->createElement('atom:entry');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' , 'xmlns:atom', 'http://www.w3.org/2005/Atom');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' , 'xmlns:gd', 'http://schemas.google.com/g/2005');
$doc->appendChild($entry);
// add category
$cat = $doc->createElement('atom:category');
$cat->setAttribute('scheme', 'http://schemas.google.com/g/2005#kind' );
$cat->setAttribute('term', 'http://schemas.google.com/contact/2008#group' );
$entry->appendChild($cat);
// add group name element
$name = $doc->createElement('atom:title', );
$name->setAttribute('type', 'text' );
$entry->appendChild($name);
//
$extProp = $doc->createElement('gd:extendedProperty');
$extProp->setAttribute('name', );
$entry->appendChild($extProp);
$info = $doc->createElement('info', );
$extProp->appendChild($info);
//
$entryResult = $gdata->insertEntry($doc->saveXML(), 'http://www.google.com/m8/feeds/groups/default/full' );

Il riferimento al gruppo si ottiene con

$groupUrl = $entryResult->id;