je předemnou nelehký úkol a chtěl bych se zeptat, jak byste jej řešili vy.
Chtěl bych si napsat php script, který by získával informace o příchozích platbách a podle nich by se nějak choval.
Původně jsem si myslel, že to půjde v pohodě jako u ebanky, kde to chodí emailem myslím v clear text podobě, takže se skoro nic moc nemusí řešit.
mbanka posílá emaily trochu komplikovaně - email obsahuje přílohu, takže zpracování je trošičku náročnější, ale to by nevadilo, hlavní problém je, že v nich není variabilní symbol, takže se dostáváme k problému.
Zatím nejjednodušší metoda se mi zdá napsat si script, kterej se bude chovat jako klient a ošklivě si bude parsovat výpisy, co mbanka poskytuje ve webovém rozhraní.
Napadá Vás něco lepšího? Bohužel se mi nepodařilo najít žádné jiné rozhraní (ideálně přes nějaké api) a věřím, že nejsem první, kdo řeší tento problém.
Jinak výslednou třídu zkusím potom zveřejnit, třeba to bude mít úspěch.
Tak toto jsem už kdysi řešil, takže parser mám. :) Přesně jak píšeš, nebylo to nic jednoduchého, řadu hodin jsem nad tím strávil, ale už jsem s psaním podobných věcí měl dřívější zkušenosti, takže jsem se tolik nazasekával jako kdysi.
Pokud bys měl zájem si ušetřit spoustu práce, tak se můžeme domluvit na prodeji mého PHP skriptu. :kava:
Tak toto jsem už kdysi řešil, takže parser mám. :) Přesně jak píšeš, nebylo to nic jednoduchého, řadu hodin jsem nad tím strávil, ale už jsem s psaním podobných věcí měl dřívější zkušenosti, takže jsem se tolik nazasekával jako kdysi.
Pokud bys měl zájem si ušetřit spoustu práce, tak se můžeme domluvit na prodeji mého PHP skriptu. :kava:
sry, ale chtěl jsem to dát volně dostupný. Otázkou je teď, co s tím? :\
sry, ale chtěl jsem to dát volně dostupný. Otázkou je teď, co s tím? :\
Já jsem jen nabízel, ale můžeš hledat dál (případně sám programovat). Každopádně ti přeju hodně štěstí a pevné nervy (a hlavně to nevzdat!). :thumbup:
2 DaliborF & Dominik: Na takový zájem jsem nebyl připraven, takže je stále v takové mé pracovní verzi (tedy jsou tam některé ne moc hezké věci a pár natvrdo zakódovaných údajů). Tento týden mám docela fofr, ale ten příští k tomu sednu a dám to do pěkné formy a vystavím ve vlastním vláknu. :) Budu se snažit na nikoho, kdo si mi už o něj napsal, nezapomenout (ale PM to jistí ;)).
Dobrý den,
e-mail Push má stejně znaků jako SMS. Symboly (KS a VS) se zobrazují, ale pouze v tom případě, že není zadána žádná zpráva pro příjemce. Veškeré údaje o platbě jsou pochopitelně uvedeny v internetovém bankovnictví v "Historie transakcí" u účtu.
V případě jakýchkoliv otázek nebo nejasností prosíme o kontakt.
to je super, opravdu to funguje skvele. Treba to nekdy vyuziju, spise me ale zajima neco podobneho ale pro Komercni banku. Hledal jsem vsude mozne ale nejakou vetsi diskuzi na toto tema jsem nenasel. Nevite o nejakem parseru i pro komercni banku?
je to uz asi 2 roky co jsem to z te URL stahoval, takze nevim, jestli jsem v te tride nedelal nejake upravy.. mozna jo, mozna ne, kadopadne me to funguje.
ve slozce se tridou mam jeste prazdny soubor bez pripomy s nazvem cookiefile, nevim jeslti si ho trida vytvari nebo je tam opravdu nutne ho mit, koukam, cele 2 roky nebyl modifikovan..
a kod tridy:
Kód:
<?php
/**
* mBANK HTTP API
*
* @author Martin Zvarík
*
* @license http://www.teplaky.net/mbank-http-api
*
* @version 2011-07-25
*/
class mBank
{
private $ucty; // seznam účtů
private $ch; // curl
public $url_logon = 'https://cz.mbank.eu/logon.aspx';
public $url_accounts_list = 'https://cz.mbank.eu/accounts_list.aspx';
public $url_view_settings = 'https://cz.mbank.eu/account_oper_list.aspx'; // formulář s nastavením výpisu transakcí
public $url_printout = 'https://cz.mbank.eu/printout_oper_list.aspx'; // export transakcí do souboru
/**
* SAFE_MODE nepodporuje CURLOPT_FOLLOWLOCATION
*/
function curl_redir_exec(&$ch, $request_url, $post_data = null, $loops = 0)
{
if ($loops == 0) {
curl_setopt($ch, CURLOPT_URL, $request_url);
if (isset($post_data)) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
} else {
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, '');
}
}
$curl_max_loops = 20;
if ($loops++ >= $curl_max_loops) return false;
$res = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
/*echo $http_code."<br>";
echo $request_url."<br>";
echo $res."<br>";
echo "--------------------<br>\n<br>\n";
*/
if ($http_code > 300 && $http_code < 400) //--- přesměrování
{
preg_match('~location: (.*?)\n~i', $res, $m);
$url = trim($m[1]);
if (!$url) {
die("Chyba redirect");
}
if (substr($url, 0, 4) != 'http') {
$request_url = substr($request_url, 0, strrpos($request_url, '/'));
$url = $request_url . ($url{0} == '/' ? '' : '/') . $url;
}
curl_setopt($ch, CURLOPT_URL, $url);
//echo "redirect: ".$url."<br>\n";
return $this->curl_redir_exec($ch, $url, null, $loops);
}
else
return $res;
}
/**
* LOGIN
*
* @param (string) Uživatelské jméno
* @param (string) Heslo
*/
function __construct($customer, $password)
{
$fp = fopen('cookiefile', 'w');
fclose($fp);
//--------------------- globální nastavení CURL
$ch =& $this->ch;
$ch = curl_init();
curl_setopt($ch, CURLOPT_COOKIEFILE, "cookiefile");
curl_setopt($ch, CURLOPT_COOKIEJAR, "cookiefile");
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 40);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_REFERER, ''); // referer není vyžadován
//--- načteme přihlašovací stránku
$res = $this->curl_redir_exec($ch, $this->url_logon);
//--- vytáhneme údaje z formu
preg_match('~<input.+__EVENTVALIDATION.+value=("|\')(.*)\1~isU', $res, $m);
$_eventvalidation = $m[2];
preg_match('~<input.+seed.+value=("|\')(.*)\1~isU', $res, $m);
$seed = $m[2];
preg_match('~<input.+__STATE.+value=("|\')(.*)\1~isU', $res, $m);
$_state = $m[2];
//--- odešleme login
$data = array(
'customer' => $customer,
'password' => $password,
'localDT' => date("D M d Y H:i:s")." GMT +0100 (Central Europe Standard Time)",
'__PARAMETERS' => '', // prazdne
'__VIEWSTATE' => '', // prazdne
'seed' => $seed,
'__STATE' => $_state,
'__EVENTVALIDATION' => $_eventvalidation,
);
$res = $this->curl_redir_exec($ch, $this->url_logon, $data);
// jsme přihlášeni
}
/**
* Odpojení
*/
function __destruct()
{
curl_close($this->ch);
unset($this->ch);
}
/**
* Seznam účtů
*
* @return (array) účty
*/
function ucty()
{
if (isset($this->ucty)) return $this->ucty;
$res = $this->curl_redir_exec($this->ch, $this->url_accounts_list);
//-----------------------------------
$dm = new DataMiner($res);
$dm->setBound('účtů', '</ul>');
$ucty = array();
$s = $dm->get('li'); // první jsou nadpisy
while ($s = $dm->get('li'))
{
$dm2 = new DataMiner($s);
$s = strip_tags($dm2->get('p'));
preg_match('~(.*)(\d+-\d+)/6210~iU', $s, $m);
if(!count($m)) continue;
$ucet = array();
$ucet['nazev'] = $m[1];
$ucet['cislo'] = $m[2];
$ucet['zustatek_ucetni'] = $dm2->get('p', 'number');
$ucet['zustatek'] = $dm2->get('p', 'number');
//--- najdeme odkaz na historii transakcí
$akce = $dm2->get('p');
$akce = $dm2->get('a'); // někdy je: provest prikaz
if (strip_tags($akce) != 'Historie transakcí') {
$akce = $dm2->get('a'); // obvykle na druhém místě: historie transakci
}
preg_match('~POST\',\'(.*)\'~iUm', $akce, $m);
$_parameters = $m[1];
$ucet['_parameters'] = $_parameters;
$ucty[$ucet['cislo']] = $ucet;
}
array_pop($ucty); // poslední je součet
$this->ucty = $ucty;
return $ucty;
}
/**
* Výpis transakcí
*
* @param (mixed) číslo účtu (string) / čísla účtů (array) -- včetně předčíslí 670100 a bez /6210
* @param (string) od kdy - výchozí posledních 31 dní, jinak lze zadat YYYY-MM-DD nebo záporné číslo (počet posledních dní)
* @param (string) do kdy - výchozí je dnes, jinak YYYY-MM-DD
* @param (mixed) null (ponechá výchozí řazení mBank), 'datum' (podle datumu transakcí), 'zauctovani' (podle datumu zaúčtování)
*
* @return (array) transakce
*/
function transakce($ucty, $od = null, $do = null, $sortby = null)
{
if (!is_array($ucty)) {
$ucty = array($ucty);
}
$data = array();
$i = 0;
foreach ($ucty as $ucet_k => $ucet_v)
{
$ucet = strlen($ucet_k) > 6 ? $ucet_k : $ucet_v; // číslo účtu můžeme dát jako "key"
if (!$ucet) continue;
$csv = $this->csv_transakce($ucet, $od, $do);
$rows = explode("\n", $csv);
$start = false;
foreach ($rows as $r)
{
$cols = explode(';', $r);
//--- struktura CSV
#0, Datum uskutečnění transakce
#1, Datum zaúčtování transakce
#2, Popis transakce
#3, Zpráva pro příjemce
#4, Plátce/Příjemce
#5, Číslo účtu plátce/příjemce
#6, KS
#7, VS
#8, SS
#9, Částka transakce
#10, Účetní zůstatek po transakci
if ($start)
{
foreach ($cols as &$col) {
$col = trim($this->csv_string($col));
}
if (!$cols[0]
&& isset($cols[1]) && !$cols[1]
&& isset($cols[2]) && !$cols[2]) {
break; // konec údajů
}
if (!$cols[0]) continue; // vynechaný řádek
list($d, $m, $Y) = explode('-', $cols[0]); // #0, Datum uskutečnění transakce
$datum = mktime(0, 0, 0, (int)$m, (int)$d, (int)$Y);
list($d, $m, $Y) = explode('-', $cols[1]); // #1, Datum zaúčtování transakce
$datum2 = mktime(0, 0, 0, (int)$m, (int)$d, (int)$Y);
$i++;
if ($sortby == 'datum') {
// transakce ve stejný den řadíme podle částky
$key = $datum.'_'.abs($this->format_num($cols[9])).($this->format_num($cols[9]) < 0 ? 'a' : 'b');
} elseif ($sortby == 'zauctovani') {
// transakce ve stejný den řadíme podle částky
$key = $datum2.'_'.abs($this->format_num($cols[9])).($this->format_num($cols[9]) < 0 ? 'a' : 'b');
} else {
$key = $i;
}
//---
$popis = $cols[2]; // #2, Popis transakce
if ($cols[3]) {
$popis.= ($popis ? ' - ' : '').$cols[3]; // připojíme "#3, Zpráva pro příjemce"
}
if (isset($data[$key])) $key.= '_'.$i;
// Vydaj nebo prijem
$typ_transakce = 'PRIJEM';
$castka = (float) $this->format_num($cols[9]);
if($castka<0) $typ_transakce = 'VYDAJ';
$data[$key] = array(
'datum' => $datum, // vracíme jako 'timestamp'
'zauctovani' => $datum2,
'popis' => iconv("windows-1250", "utf-8", $popis),
'jmeno' => iconv("windows-1250", "utf-8", ($cols[4] && $cols[4] != '-') ? $cols[4] : ''), // platce / prijemce
'ks' => $cols[6] ? preg_replace('~^0+~', '', $cols[6]) : '',
'vs' => $cols[7] ? preg_replace('~^0+~', '', $cols[7]) : '', // odstraní počáteční nuly
'ss' => $cols[8] ? preg_replace('~^0+~', '', $cols[8]) : '',
'castka' => (float) $this->format_num($cols[9]), // #9, Částka transakce
'zustatek' => (float) $this->format_num($cols[10]), // #10, Účetní zůstatek po transakci
'ucet' => $ucet,
'ucet_prijemce' => $cols[5],
'type_transakce' => $typ_transakce,
);
}
elseif (substr($cols[0], 0, 13) == '#Datum uskute')
{
$start = true;
}
}
}
if (!isset($sortby)) {
$data = array_reverse($data); // mbank seřazuje od nejstaršího data => jenom přehodíme
}
else {
krsort($data); // seřadíme podle klíče
}
return $data;
}
/**
* PDF výpis
*
* @param (mixed) číslo účtu (string) / čísla účtů (array) -- včetně předčíslí 670100 a bez /6210
* @param (string) od kdy - výchozí posledních 31 dní, jinak lze zadat YYYY-MM-DD nebo záporné číslo (počet posledních dní)
* @param (string) do kdy - výchozí je dnes, jinak YYYY-MM-DD
* @param (string) složka kam PDF uložit - výchozí je "" (aktuální složka)
*
* @example
* pdf_export('670100-xxxxxx');
* pdf_export(array('670100-xxxxxx1' => 'nazev_souboru', '670100-xxx2'));
*
* @return (array) transakce
*/
function pdf_export($ucty, $od = null, $do = null, $folder = "")
{
if (!is_array($ucty)) {
$ucty = array($ucty);
}
//----
if (!isset($od) || $od < 0) {
$days_ago = $od < 0 ? (int)$od : -31;
$od = date("Y-m-d", strtotime(($days_ago+1)." days")); // včetně toho dnešního, proto +1
}
if (!isset($do)) {
$do = date("Y-m-d");
}
$od = explode('-', $od);
$do = explode('-', $do);
//----
if ($folder != "" && substr($folder, -1) != '/') {
$folder.= "/";
}
if (!isset($this->ucty)) {
$this->ucty(); // musíme najít účty
}
foreach ($ucty as $ucet_k => $ucet_v)
{
$ucet = strlen($ucet_k) > 6 ? $ucet_k : $ucet_v; // jako "key" můžeme dát číslo účtu a jako "value" název PDF souboru
if (!$ucet) continue;
$filepath = $folder.$ucet_v."_".implode("-", $od)."_".implode("-", $do).".pdf"; // cesta k souboru
if (!isset($this->ucty[$ucet])) {
die('Chyba! Účet "'.$ucet.'" neexistuje.');
}
//---------
$data = array(
'__PARAMETERS' => $this->ucty[$ucet]['_parameters'],
);
$res = $this->curl_redir_exec($this->ch, $this->url_view_settings, $data);
preg_match('~<input.+__STATE.+value=("|\')(.*)\1~isU', $res, $m);
$_state = $m[2];
preg_match('~<input.+__EVENTVALIDATION.+value=("|\')(.*)\1~isU', $res, $m);
$_eventvalidation = $m[2];
//---------
$data = array(
'__PARAMETERS' => '',
'__STATE' => $_state,
'__VIEWSTATE' => '',
'__EVENTVALIDATION' => $_eventvalidation,
'rangepanel_group' => 'daterange_radio',
'daterange_from_day' => (int)$od[2],
'daterange_from_month' => (int)$od[1],
'daterange_from_year' => (int)$od[0],
'daterange_to_day' => (int)$do[2],
'daterange_to_month' => (int)$do[1],
'daterange_to_year' => (int)$do[0],
'accoperlist_typefilter_group' => 'ALL',
'accoperlist_amountfilter_amountmin' => '',
'accoperlist_amountfilter_amountmax' => '',
'export_oper_history_check' => 'on',
'export_oper_history_format' => 'PDF',
);
$res = $this->curl_redir_exec($this->ch, $this->url_printout, $data);
file_put_contents($filepath, $res); // uložíme
}
}
/**
* Odstraní uvozovky
*/
function csv_string($str)
{
$str = preg_replace('~^["\'](.*)["\']$~', '\1', $str);
$str = preg_replace('~\s{2,}~', ' ', $str); // více mezer nahradí jednou
$str = trim($str);
return $str;
}
/**
* Z řetězce vybere číslo
*
* @param (string) číslo
*
* @return (double) číslo
*/
function format_num($s)
{
$s = strip_tags($s);
$s = preg_replace('~[^0-9,-]+~', '', $s);
$s = str_replace(',', '.', $s);
//$s = (double)$s; -- vrací s čárkou, což nelze použít pro DB (zřejmě je to podle locale)
return $s;
}
/**
* Výpis transakcí v CSV (originál výstup z mBank)
*
* @param (mixed) číslo účtu (string) / čísla účtů (array) -- včetně předčíslí 670100 a bez /6210
* @param (string) od kdy - výchozí posledních 31 dní, jinak lze zadat YYYY-MM-DD nebo záporné číslo (počet posledních dní)
* @param (string) do kdy - výchozí je dnes, jinak YYYY-MM-DD
*
* @return (string) transakce
*/
function csv_transakce($ucet, $od = null, $do = null)
{
if (!isset($od) || $od < 0) {
$days_ago = $od < 0 ? (int)$od : -31;
$od = date("Y-m-d", strtotime(($days_ago+1)." days")); // včetně toho dnešního, proto +1
}
if (!isset($do)) {
$do = date("Y-m-d");
}
$od = explode('-', $od);
$do = explode('-', $do);
if (!isset($this->ucty)) {
$this->ucty(); // musíme najít účty
}
if (!isset($this->ucty[$ucet])) {
die('Chyba! Účet "'.$ucet.'" neexistuje.');
}
//---------
$data = array(
'__PARAMETERS' => $this->ucty[$ucet]['_parameters'],
);
$res = $this->curl_redir_exec($this->ch, $this->url_view_settings, $data);
preg_match('~<input.+__STATE.+value=("|\')(.*)\1~isU', $res, $m);
$_state = $m[2];
preg_match('~<input.+__EVENTVALIDATION.+value=("|\')(.*)\1~isU', $res, $m);
$_eventvalidation = $m[2];
//---------
$data = array(
'__PARAMETERS' => '',
'__STATE' => $_state,
'__VIEWSTATE' => '',
'__EVENTVALIDATION' => $_eventvalidation,
'rangepanel_group' => 'daterange_radio',
'daterange_from_day' => (int)$od[2],
'daterange_from_month' => (int)$od[1],
'daterange_from_year' => (int)$od[0],
'daterange_to_day' => (int)$do[2],
'daterange_to_month' => (int)$do[1],
'daterange_to_year' => (int)$do[0],
'accoperlist_typefilter_group' => 'ALL',
'accoperlist_amountfilter_amountmin' => '',
'accoperlist_amountfilter_amountmax' => '',
'export_oper_history_check' => 'on',
'export_oper_history_format' => 'CSV',
);
$res = $this->curl_redir_exec($this->ch, $this->url_printout, $data);
return $res;
}
}
/**
* Jednoduchý extraktor dat z HTML
*
* @author Martin Zvarík - www.Teplaky.NET
*/
class DataMiner
{
public $c;
public $c2;
public $c2_bound;
public $cur = 0;
public $start = 0;
public $end;
function __construct($content)
{
$content = trim($content);
$this->c = $content;
$this->c2 = strtolower($content);
}
function setBound($start, $end = null)
{
if (isset($start)) {
//$s = strtolower($start); // nefunguje na diakritiku!!
$s = iconv('utf-8','iso-8859-2',mb_strtolower($start,'utf-8')); // nefunguje na diakritiku!!
$pos = strpos($this->c2, $s);
if ($pos !== false) {
$this->start = $pos;
}
}
if (isset($end)) {
$s = strtolower($end);
$pos = strpos($this->c2, $s, $this->start);
if ($pos !== false) {
$this->end = $pos;
}
}
if ($this->start > 0 || isset($this->end)) {
$this->c2_bound = substr($this->c2, $this->start, $this->end - $this->start);
}
}
function get($tag, $format = false)
{
$tag = strtolower($tag);
$c2 = isset($this->c2_bound) ? $this->c2_bound : $this->c2;
$pos = strpos($c2, '<'.$tag, $this->cur);
$this->cur = $pos + strlen($tag);
if ($pos === false) return false;
$end = strpos($c2, '</'.$tag.'>', $pos); // konec tagu - pro naše potřeby stačí takto
$entry = substr($this->c, $this->start + $pos, $end - $pos + strlen($tag) + 3);
if ($format == 'number')
{
$entry = strip_tags($entry);
$entry = preg_replace('~[^0-9,-]*~', '', $entry);
$entry = str_replace(',', '.', $entry);
$entry = (double)$entry;
}
return $entry;
}
}
pouziti (koukam do presenteru co s tou tridou delam.. nezkoumam kazdou metodu, takze jeslti tady bude nekde nejaka chybka tak se predem omlouvam. nektere parametry metod si taham z databaze a nemam ted cas zkoukat jak mam cislo uctu v DB ulozeno, jeslti i s tim /6210 nebo bez treba...):
Kód:
// XXXX = tvoje ID do mbanky (integer), PASS = heslo
$mb = new \mBank(XXXX, 'PASS');
// seznam tvych uctu
$ucty = $mb->ucty();
// zustatek nejakeho uctu (nevim jestli tam je nutne mit to /6210)
$zustatek = $ucty['6210-123456789/6210'];
// senzam transakci k nejakemu uctu za poslednich 30 dni. opet nevim jestli tam je nutne mit to /6210
$mb->transakce('6210-123456789/6210', date('Y-n-j', strtotime("-30 days")))
snad to pomohlo... co s tim jde delat dal vyctes z metod tridy mBank :o) tady je jen cast kterou pouzivam ja... mam to uz vice nez 2 roky a stale to funguje :o) kdyby neco neslo muzu pomoct nebo ti to nekam cele implementovat, napis zpravu, dohodnem se..
Jo, jo, díky, rozjel jsem to, vše funguje, jen musim nejak vychytat čestinu, místo háčku a čárek mi to tam píše čtverce .) Bude to asi nejakym spatnym kodovanim;)