PHP implementácia TatraPay a CardPay

V dnešnom príspevku nájdete jednoduchú PHP implementáciu TatraPay a CardPay od TatraBanky.

Implementácia sa skladá z troch tried. Trieda TatraBanka obsahuje deklaráciou premenných a metódy GetSign() na výpočet bezpečnostného kľúča zo zadaného reťazca. Z tejto triedy su odvodené triedy TatraPay a CardPay, ktoré implementujú konštruktor a metódy GetUrl(), ktorá vracia URL pre vykonanie platby a VerifyReply(), ktorá slúži na overenie odpovede z banky.

Triedy boli implementované podľa technických príručiek od TatraBanky. Príručky si môžete stiahnuť tu: TatraPay, CardPay.

Použitie je veľmi jednoduché. Presmerovanie na stránku TB pre vykonanie platby:

<br />
require('TatraBanka.class.php');</p>
<p>// parametre: suma, mena, VS, návratová URL<br />
$objTP = new TatraPay('123.4', '978', '1234', 'http://www.example.com/');</p>
<p>header('Location: ' . $objTP-&gt;GetUrl());<br />
exit;<br />

Overenie odpovede z banky:

<br />
require('TatraBanka.class.php');</p>
<p>$objTP = new TatraPay();</p>
<p>if($objTP-&gt;VerifyReply()) {<br />
     echo &quot;Odpoveď je OK&quot;;<br />
} else {<br />
     echo &quot;Pozor odpoveď nie je platná!&quot;;<br />
}<br />

Spomínané triedy sú tu:

<br />
&lt;?php<br />
/*<br />
 * Zakladna trieda pre triedy TatraPay a CardPay<br />
 */<br />
class TatraBanka {<br />
        const KEY = 'tajny_kluc';<br />
        const MID = '1234';</p>
<p>        const RSMS = ''; // cislo kam sa maju posielat SMS notifikacie o platbe<br />
        const REM = ''; // mail kam sa maju posielat notifikacie o platbe</p>
<p>        /**<br />
         * Suma. Max 13+2 číslic. Desatinná čiarka musí byť '.'<br />
         * @var Float<br />
         */<br />
        protected $strAMT;                                     </p>
<p>        /**<br />
         * Mena transakcie. Kód 978 je pre EURO.<br />
         * @var String<br />
         */<br />
        protected $strCURR;                     </p>
<p>        /**<br />
         * Variabilný symbol. Max 10 číslic.<br />
         * @var String<br />
         */<br />
        protected $strVS;                   </p>
<p>        /**<br />
         * Špecifický symbol - nepovinný. Max 10 číslic.<br />
         * @var String<br />
         */<br />
        protected $strSS;                               </p>
<p>        /**<br />
         * Konštantný symbol. Max 4 číslice.<br />
         * @var String<br />
         */<br />
        protected $strCS;                   </p>
<p>        /**<br />
         * Návratová URL<br />
         * @var String<br />
         */<br />
        protected $strRURL;</p>
<p>        /**<br />
         * Bezpečnostný podpis<br />
         * @var String<br />
         */<br />
        protected $strSIGN;   </p>
<p>        /**<br />
         * GetSign vracia bezpečnostný podpis zo zadaného reťazca<br />
         * @param $str reťazec z ktorého sa vypočíta podpis<br />
         * @return string<br />
         */<br />
        public function GetSign($str) {<br />
                $strSIGN = sha1($str, true);<br />
                $strSIGN = substr($strSIGN, 0, 8);<br />
                $des = mcrypt_module_open(MCRYPT_DES, &quot;&quot;, MCRYPT_MODE_ECB, &quot;&quot;);</p>
<p>                $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($des), MCRYPT_RAND);<br />
                mcrypt_generic_init($des, self::KEY, $iv);                        </p>
<p>                $strSIGN = mcrypt_generic($des, substr($strSIGN, 0, 8));<br />
                mcrypt_generic_deinit($des);<br />
                mcrypt_module_close($des);                              </p>
<p>                $strSIGN = strtoupper(bin2hex($strSIGN));<br />
                return $strSIGN;<br />
        }                                                </p>
<p>        protected function SanitizeFloat($flt) {<br />
                return str_replace(',', '.', sprintf('%.2f', $flt));<br />
        }<br />
}                                                                   </p>
<p>/**<br />
*       Trieda pre CardPay<br />
*/<br />
class TatraPay extends TatraBanka {<br />
        /**<br />
         * Constructor<br />
         * @param $AMT suma<br />
         * @param $CURR mena<br />
         * @param $VS variabilný symbol<br />
         * @param $RURL návratová URL<br />
         * @return New TatraPay object<br />
         */<br />
        public function __construct($AMT = null, $CURR = null, $VS = null, $RURL = null, $SS = null, $CS = '0308') {<br />
                $this-&gt;strAMT = $this-&gt;SanitizeFloat($AMT);<br />
                $this-&gt;strCURR = $CURR;<br />
                $this-&gt;strVS = $VS;<br />
                $this-&gt;strRURL = $RURL;<br />
                $this-&gt;strSS = $SS;<br />
                $this-&gt;strCS = $CS;<br />
                $this-&gt;strSS = $SS;                                                                                 </p>
<p>                $strSIGN = $this-&gt;GetSign(self::MID . $this-&gt;strAMT . $CURR . $VS . $CS . $RURL);<br />
                $this-&gt;strSIGN = $strSIGN;<br />
        }                                                                                        </p>
<p>        public function GetUrl() {<br />
                $url = sprintf('https://moja.tatrabanka.sk/cgi-bin/e-commerce/start/e-commerce.jsp?PT=TatraPay&amp;MID=%s&amp;AMT=%s&amp;CURR=%s&amp;VS=%s&amp;CS=%s&amp;RURL=%s&amp;SIGN=%s',<br />
                        self::MID,<br />
                        $this-&gt;strAMT,<br />
                        $this-&gt;strCURR,<br />
                        $this-&gt;strVS,<br />
                        $this-&gt;strCS,<br />
                        $this-&gt;strRURL,<br />
                        $this-&gt;strSIGN<br />
                );<br />
                if(self::RSMS != '')<br />
                        $url .= '&amp;RSMS=' . self::RSMS;<br />
                if(self::REM != '')<br />
                        $url .= '&amp;REM=' . self::REM;<br />
                return $url;<br />
        }                                                                                                                                                         </p>
<p>        public function VerifyReply() {<br />
                if(!isset($_GET['VS']))<br />
                        return false;<br />
                if(!isset($_GET['RES']))<br />
                        return false;<br />
                if(!isset($_GET['SIGN']))<br />
                        return false;<br />
                $strToSign = $_GET['VS'] . $_GET['RES'];<br />
                if($_GET['SIGN'] == $this-&gt;GetSign($strToSign)) {<br />
                        return true;<br />
                }<br />
                return false;<br />
        }<br />
}                                                                </p>
<p>/**<br />
 * Trieda pre CardPay<br />
 */<br />
class CardPay extends TatraBanka {<br />
        /**<br />
         * IP adresa klienta<br />
         * @var String<br />
         */<br />
        protected $strIPC;        </p>
<p>        /**<br />
         * Meno klienta<br />
         * @var String<br />
         */<br />
        protected $strNAME;</p>
<p>        /**<br />
         * Constructor<br />
         * @param $AMT suma<br />
         * @param $CURR mena<br />
         * @param $VS variabilný symbol<br />
         * @param $RURL návratová URL<br />
         * @return new CardPay object<br />
         */<br />
        public function __construct($NAME = null, $AMT = null, $CURR = null, $VS = null, $RURL = null, $SS = null, $CS = '0308') {<br />
                setlocale(LC_CTYPE, 'en_US.UTF-8');<br />
                $this-&gt;strNAME = strtr(iconv('UTF-8', 'US-ASCII//TRANSLIT', $NAME), array(&quot;'&quot; =&gt; ''));<br />
                $this-&gt;strAMT = $this-&gt;SanitizeFloat($AMT);<br />
                $this-&gt;strCURR = $CURR;<br />
                $this-&gt;strVS = $VS;<br />
                $this-&gt;strRURL = $RURL;<br />
                $this-&gt;strSS = $SS;<br />
                $this-&gt;strCS = $CS;                                                                                               </p>
<p>                if(array_key_exists('X_FORWARDED_FOR', $_SERVER)) {<br />
                        $this-&gt;strIPC = $_SERVER['X_FORWARDED_FOR'];<br />
                } else {<br />
                        $this-&gt;strIPC = $_SERVER['REMOTE_ADDR'];<br />
                }</p>
<p>                $strSIGN = $this-&gt;GetSign(self::MID . $this-&gt;strAMT . $this-&gt;strCURR . $this-&gt;strVS . $this-&gt;strCS . $RURL . $this-&gt;strIPC . $this-&gt;strNAME);<br />
                $this-&gt;strSIGN = $strSIGN;<br />
        }</p>
<p>        public function GetUrl() {<br />
                $url = sprintf('https://moja.tatrabanka.sk/cgi-bin/e-commerce/start/e-commerce.jsp?PT=CardPay&amp;MID=%s&amp;AMT=%s&amp;CURR=%d&amp;VS=%s&amp;CS=%s&amp;RURL=%s&amp;SIGN=%s&amp;IPC=%s&amp;NAME=%s',<br />
                        self::MID,<br />
                        $this-&gt;strAMT,<br />
                        $this-&gt;strCURR,<br />
                        $this-&gt;strVS,<br />
                        $this-&gt;strCS,<br />
                        $this-&gt;strRURL,<br />
                        $this-&gt;strSIGN,<br />
                        $this-&gt;strIPC,<br />
                        urlencode($this-&gt;strNAME)<br />
                );<br />
                if(self::RSMS != '')<br />
                        $url .= '&amp;RSMS=' . self::RSMS;<br />
                if(self::REM != '')<br />
                        $url .= '&amp;REM=' . self::REM;<br />
                return $url;<br />
        }</p>
<p>        public function VerifyReply() {<br />
                if(!isset($_GET['VS']))<br />
                        return false;<br />
                if(!isset($_GET['RES']))<br />
                        return false;<br />
                if(!isset($_GET['SIGN']))<br />
                        return false;<br />
                if(!isset($_GET['AC']))<br />
                        return false;<br />
                $strToSign = $_GET['VS'] . $_GET['RES'] . $_GET['AC'];<br />
                if($_GET['SIGN'] == $this-&gt;GetSign($strToSign)) {<br />
                        return true;<br />
                }<br />
                return false;<br />
        }<br />
}<br />

Ako vidíte tak implementácia je veľmi jednoduchá a nesnaží sa byť univerzálnym riešením. Ak by ste mali záujem o univerzálnu knižnicu a dokonca aj pre iné banky ako TatraBanka, tak môžete použiť knižnicu MONOGRAM EPayment.

32 komentárov k “PHP implementácia TatraPay a CardPay”

  1. Ahoj,

    dakujem za uzitocnu kniznicu, no mam jednu otazku – pri implementacii ma po kliknuti na odkaz presmeruje na stranku TB kde je sprava

    Spojenie je zrušené. Maximálny čas nečinnosti (10 min.) bol prekročený.
    Session time-out.

    Podla overovacej stranky TB mam SIGN kluc vygenerovany spravne.. Nestretol si sa s podobnym problemom?

    A dalsia z mojich otazok je , na akej adrese je volany verify callback – ak som to spravne pochopil , RURL je stranka na ktoru bude uzivatel presmerovany po vykonani platby..

    Dakujem za odpoved,

    misko

  2. Cau,

    S tym timeoutom som sa nestretol. Nie som si isty preco ti to vyhadzuje. Bol si predtym ako si to testoval prihlaseny na stranke TB ? Sice by to na to asi nemalo mat vplyv, ale nic ine ma nenapada. Robi ti to pri oboch platbach, alebo len jednu z CardPay, alebo TatraPay ?

    Co sa tyka verifikacie, tak mas pravdu. RURL je stranka kam bude uzivatel presmerovany a na tej sa aj robi verifikacia. Tatrabanka ta presmeruje na RURL a ku nej prida parametre na overenie platby. Ty len vytvoris novu instanciu objektu podla typu platby a zavolas metodu VerifyReply().

  3. Dakujem za rychlu odpoved – problem s timeoutom pravdepodobne spociva v case requestu, resp. ze tam existuje urcity offset v lokalnom case na strane TB voci mojmu serveru (to je take moje momentalne nepotvrdene podozrenie ktore potrebujem este overit), kedze web ktory vyvijam je hostovany v zahranici (USA).

    Co sa tyka callbacku – jedina informacia o spracovani platby je teda posielana len pri navrate na stranku merchanta? Tzn. ze info o platbe sa do systemu nedostane, pokial klient neklikne na „Pokracovat“ na stranke TB? (ak teda nie je nastaveny automaticky redirect, alebo zavrie okno este pred redirectom)

    este raz dakujem za pomoc

  4. S časom by nemal byť problém, keďže sa nikde nepoužíva na generovanie SIGN… Tá chyba znamená, že predošlá session vytvorená pri návšteve stránky internet bankingu už nieje platná, ale prečo to robí to neviem.

    A s callbackom máš pravdu. Keď klient po zaplatení zavrie okno a nebude presmerovaný na tvoju stránku, tak ty sa nedozvieš (teda tvoj skript) o tom či prebehla platba. Preto je ešte vhodné nastaviť si SMS a mail notifikáciu.

  5. @misko: Tiež dostávam pri presmerovaní na cardpay/tatrapay session timeout aj napriek tomu že mám správne vypočítaný podpis. Podarilo sa Ti tento problém vyriešiť?

  6. Nie, mám vlastnú implementáciu. Toto je jediný hit z googlu kde sa diskutuje o tejto chybe tak som dúfal že tu nájdem aj radu :) S tou session je to veľmi divné, určite som žiadnu session v internetbankingu nemal a nejde to ani v úplne novej session inej inštancie iného webbrowsera.

  7. Zdravim, mam ten isty problem „Spojenie je zrušené. Maximálny čas nečinnosti (10 min.) bol prekročený. Session time-out.“ Pracujem s testovacimi udajmi, ale ani cez POST ani GET sa mi nedari dostat inu hlasku ako toto. Neupouzivam tb internet banking ani nic podobne, skusal som to vo vsetky prehliadacoch… vsetky vstupne data mam spravne. Vygeneroval som si SIGN a vytvoril formular v cistom html kode bez php a stale to iste… netusite nahodou kde je problem?

  8. Programoval som TatraPay a CardPay pre jeden e-commerce system, cize som nevychadzal z navodu vyssie, ale programoval som vlastne riesenie. Mozem iba potvrdit to, ze moduly sa spravaju na roznych domenach mojich klientov odlisne. Iba na jednom mi nabehne stranka pre vyzvu k uhrade. Na ostatnych „TatraPay Spojenie je zrušené. Maximálny čas nečinnosti (10 min.) bol prekročený.“

    Problem preto bude zreme na strane TB a asi ich tento problem nijako netrapi.

  9. Zdravim, potreboval by som pomoc s tym overenim pri navrate… Nerozumiem tej poslednej casti. Pouzivam CardPay a pri navrate mi banka vlastne posle SIGN a ta funkcia ho ma porovnat so SIGNom, ktory generovala moja stranka pri presmerovani na stranku TB? Nerozumiem co mam vlozit do tychto funkcii…

    $objTP = new CardPay();

    if($objTP->VerifyReply())
    {
    echo „Odpoveď je OK“;
    }
    else
    {
    echo „Pozor odpoveď nie je platná!“;
    }

  10. Hlasku
    “TatraPay Spojenie je zrušené. Maximálny čas nečinnosti (10 min.) bol prekročený.”
    obdrzite vzdy, ak skusate funkcnost modulu v ostrej prevadzke, avsak na strane banky este nebola sluzba pe dany obchod aktivovana.

    Treba sa najskor spojit s TB a nechat modul testovat pracovnikom Tatra banky. Po uspesnom teste banka aktivuje sluzbu pre dany obchod na svojej strane a vsetko zacne fungovat tak, ako ma.

  11. Vdaka za vysvetlenie. To je dovod, preco som ju nikdy nevidel… ;)

    Moze to niekto potvrdit, ze po aktivovani to uz funguje ok ?

  12. general: do tej funkcie netreba vkladat nic, ta funckia zoberie parametre z $_GET a verifikuje odpoved… to je vsetko

  13. Ano aj ja potvrdzujem, treba poziadat banku aby to pustila do ostrej prevadzky a potom to bude fungovat korektne. Vdaka head aj som si myslel :) len mi to pride take, ze keby to nebolo zabezpecne spojenie staci prepisat jednu premennu v url na OK…

  14. zdravim
    snazim sa o implementaciu podla navodu vytvoril som si jeden subor kde som vlozil to presmerovanie na banku.jeden soburo s tymi zakladnymi triedami ,len neviem kde mam umiestnit tu overenie odpovede z banky.
    mohli by ste mi nejako pomoct???
    dakujem

  15. Vdaka autorovi. Velmi uzitocna kniznicka. Z hladania ekvivalentnych php funkcii k tym im javascriptovskym popisom som bol uz zufaly. :) Velmi velmi ocenujem.

  16. Dostaval uz niekto takuto hlasku?
    Ľutujeme, ale Vašu platbu nemožno z dôvodu neplatného bezpečnostného podpisu uskutočniť!

    Nechapem. S TB som si overil ze mi generuje dobre sign aj cely retazec a jednoducho sa neviem cez tuto veticku dostat. Uz sa s tym se…kam nejaky cas, za kazdu ideu o co go by som bol vdacny.

    Dik

  17. Ahoj, díky za implementáciu. A tiež ťa upozorním na jednu chybyčku, ktorá tam je:
    Tvoj kód:
    if(RSMS != “) $url .= ‚&RSMS=‘ . self::RSMS;
    if(REM != “) $url .= ‚&REM=‘ . self::REM;

    Správne má byť:
    if(self::RSMS != “) $url .= ‚&RSMS=‘ . self::RSMS;
    if(self::REM != “) $url .= ‚&REM=‘ . self::REM;

    Inak fajn :-)

  18. sorry uz mam:
    require(‚TatraBanka.class.php‘);

    $objTP = new TatraPay();

    if($objTP->VerifyReply()) {
    echo „Odpoveď je OK“;
    } esle {
    echo „Pozor odpoveď nie je platná!“;
    }

    mas zle napisane namiesto else mas esle

  19. Opravdu velke diky za toto pekne popsane reseni. Souhlasim vsemi deseti s Flavierem, ze jejich javascriptovy popis vcetne snahy domoct se neceho telefonicky selhalo. Adekvatni PHP funkce zvlastne na DES algoritmus se fakt dohledavaji velmi tezko. Velke diky autorovi i za me.

  20. Pravdupovediac ten DES som si tiez niekde vyguglil, ale uz neviem kde. Sam by som to asi nedal ;). Potom som to len obalil do triedy a pouzil a zverejnil.

    A som rad, ze je to stale funkcne a niekomu to pomohlo.

  21. No jako doopravdy jakekoli jednani s Tatrabankou je totalni peklo. Slecny maji naucene basnicky, ktere omilaji dokola a kdyz se zeptate na neco mimo jejich chapani, radeji zopakuji uz to co uz vam jednou rekly. A co je nejvetsi prdel, tak toto jsem dostal jako jediny link na technickou podporu sluzby CardPay
    http://www.shoppingzona.sk/sk/shoppingzona/co-je-shopping-zona.html#ako-si-zalozit-obchod
    Mam si zavolat nekterym z nize uvedenych partneru at mi poradi. MAZEC NEJVETSI !!! Nic proti Slovakum, ale proc se mi podobne silenosti stavaji hlavne pri jednani se Slovaky, stale stale stale. Bratri nasi, delejte s tim neco :)

  22. Jinak jeste vidim jednu nevyznamnou chybku….. nad tridou TatraPay mas comment
    /**
    * Trieda pre CardPay
    */

    Ale jinak jeste jednou velke diky.

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *