epay.bg интеграция

Това е класа, за който споменах преди време. Идеята е да се капсулират детайлите и в бъдеще, ако ми се наложи пак да го интегрирам по-лесно. Добре е преди това да се обясни как работят нещата – глобално, а това се случва на няколко стъпки:

– Клиент идва в магазина (сайта който разработваме) и поръчва продукт. Ние създаваме една форма, която сочи към epay.bg и има няколко hidden полета, които описват поръчката. Когато клиента натисне на бутона информацията от формата, заедно с него (клиента) отиват на epay.bg.
– Epay.bg чете, това което сме пратили и го показва на клиента. За него остава само да потвърди плащането, което не е задължително да стане веднага, т.е. може да го потвърди и по-късно.
– След като клиента потвърди, epay.bg изпраща request към нас, за да ни уведоми, че това плащане е осъществено. Ние го отбелязваме като такова, а клиента бива redirectнат обратно към магазина, на предварително зададена от нас страница.

Ето и какво ни трябва, за да минем успешно през стъпките:

КИН – клиентски идентификационен номер, получава се от epay.bg и ни идентифицира пред тях.
секретна дума – Също се взима от epya.bg и се използва се за хеширане на информацията, която изпращаме.
url ok – Подва се заедно с url cancel в стъпка 1. Epay.bg го използва, за да прапрати клиента на него при успешна поръчка.
url cancel – Клиента се препраща тук, в случай че откаже плащането.
expiration time – С него указваме, колко време има клиента за да плати поръчката. Ако това време изтече поръчката автоматично се отказва.
response страници – Това нещо се попълва в сайта на epay. След като влзете в него с регистрираните от вас потр. име и парола. Използва се от epay – на него изпраща потвържденията от стъпка 3

За се справим със задачата, ще са ни необходими две странички – една от която да попълваме информацията за плащането и да изпращаме клиента към epay.bg (checkout.php) и една към която да се обръща epay, с информация за направените преди това заявки (response.php).

checkout.php
[geshi lang=php]
$request_id = 1; // has to be unique for each request
$total = 100; // the sum for charging
$exp_time = 3600 \* 2; // 2 hours

$payment = new Payment('KIN', 'SECRET_WORD', 'http://mysite.com/ok/', 'http://mysite.com/cancel/');
$input_fields = $payment->get_form_data($request_id, $total, $exp_time, ‘test payment’);
$form_url = $payment->get_form_url(true); // true means work in test mode – you will access epay.bg development url for testing
?>

[/geshi]

response.php
[geshi lang=php]
$payment = new Payment('KIN', 'SECRET_WORD', 'http://mysite.com/ok/', 'http://mysite.com/cancel/');
$payment->handle_response($_POST['encoded'], $_POST['checksum']);
?>
[/geshi]

Важно е да се спомене, че $request_id трябва да е уникално при всяко обръщане към epay.bg, то се използва и от epay, когато се обръщат към нас, за да идентифицира поръчката.
handle_response() в response.php трябва да се погрижи за обработката на данните изпратени от epay.bg. Класа който използваме тук – Payment наследява EpayPayment и дефинира четири метода: validate_request_id, handle_valid_request, handle_error_request и handle_not_recognise_request.

payment.php
[geshi lang=php]
class Payment extends EpayPayment {

protected function validate_request_id($request_id) {
$dbh = DbInstance::get();
$query = "SELECT COUNT(*) FROM orders WHERE id = ? AND status = 0";
$stmt = $dbh->prepare($query);
$stmt->execute(array($request_id));
$result = $stmt->fetchColumn();
unset($stmt);

if ($result == 1) {
return self::response_valid;
}else {
return self::response_no;
}
}

protected function handle_valid_request($request_id, $status, $pay_date, $stan, $bcode) {
if ($status == ‘PAID’) {
$dbh = DbInstance::get();
$query = „UPDATE orders SET status = 1 WHERE id = ?“;
$stmt = $dbh->prepare($query);
$stmt->execute(array($request_id));
unset($stmt);
}
}

protected function handle_error_request($request_id) {
return true;
}

protected function handle_not_recognise_request($request_id) {
return true;
}
}
[/geshi]

validate_request_id() трябва да валидира номера на поръчката. Проверяваме дали имаме записана поръчка, с такъв номер при нас и в зависимост от това определяме дали е валидна или не. handle_valid_request() Се изпълнява за всяка една валидна поръчка, т.е. платена от клиента, когато тя е такава променяме статуса на поръчката, за да отбележим, че е платена.

epay_payment.php
[geshi lang=php]
abstract class EpayPayment {

const response_valid = 1; // request id is Ok
const response_error = 2; // error occured for the given request id
const response_no = 3; // request id is not recognised

const submit_url = 'https://www.epay.bg/';
const submit_url_dev = 'https://devep2.datamax.bg/ep2/epay2_demo/';

protected $min;
protected $secret;
protected $url_ok;
protected $url_cancel;

// you have to validate the request_id here and
// return response_valid, response_error or response_no const
abstract protected function validate_request_id($request_id);

abstract protected function handle_valid_request($request_id, $status, $pay_date, $stan, $bcode);
abstract protected function handle_error_request($request_id);
abstract protected function handle_not_recognise_request($request_id);

/**
\*
\* @param string $min Merchant identification number
\* @param string $secret Secret string defined for the given merchant
**/
public function __construct($min, $secret, $url_ok = false, $url_cancel = false) {
if (!isset($min) || !isset($secret)) {
throw new Exception(__CLASS__ . ' ' . __METHOD__ . ' invalid argumets passed.');
}

$this->min = $min;
$this->secret = $secret;
$this->url_ok = $url_ok;
$this->url_cancel = $url_cancel;
}

/**
\* Accepts response from the epay for a given order
\* and sends message which confirms that the response
\* is recieved
\*
\* @param string $encoded Should be $_POST['encoded'] variable
\* @param string $checksum Should be $_POST['checksum'] variable
\*
**/
public function handle_response($encoded, $checksum) {
if (!isset($encoded) || !isset($checksum)) {
throw new Exception(__CLASS__ . ‘ ‘ . __METHOD__ . ‘ invalid argumets passed.’);
}

$hmac = $this->hmac($encoded, $this->secret);
if ($hmac == $checksum) {

$data = base64_decode($encoded);
$lines_arr = split(„\n“, $data);
$info_data = “;

foreach ($lines_arr as $line) {
if (preg_match(„/^INVOICE=(\d+):STATUS=(PAID|DENIED|EXPIRED)(:PAY_TIME=(\d+):STAN=(\d+):BCODE=([0-9a-zA-Z]+))?$/“, $line, $regs)) {
$invoice = $regs[1];
$status = $regs[2];
$pay_date = $regs[4];
$stan = $regs[5];
$bcode = $regs[6];

// process $invoice, $status, $pay_date, $stan, $bcode here
switch ($this->validate_request_id($invoice)) {

case self::response_valid:
$info_data .= „INVOICE=$invoice:STATUS=OK\n“;
$this->handle_valid_request($invoice, $status, $pay_date, $stan, $bcode);
break;

case self::response_error:
$info_data .= „INVOICE=$invoice:STATUS=ERR\n“;
$this->handle_error_request($invoice);
break;

case self::response_no:
$info_data .= „INVOICE=$invoice:STATUS=NO\n“;
$this->handle_not_recognise_request($invoice);
break;

default:
$info_data .= „INVOICE=$invoice:STATUS=ERR\n“;
$this->handle_error_request($invoice);
break;
}
}
}

echo $info_data, „\n“;
}
}

/**
\* Return’s the epay.bg URL
\*
\* @param boolean $test_mode
\* @return string $url
**/
public function get_form_url($test_mode = false) {
if ($test_mode) {
return self::submit_url_dev;
} else {
return self::submit_url;
}
}

/**
\* Returns array with input hidden fields which should be
\* assigned to a form with submir url generated with get_form_url()
\*
\* @param integer $request_id Unique request id (invoice number)
\* @param float $sum Total sum for charging
\* @param integer $exp_time In seconds
\* @param string $description Transaction description
**/
public function get_form_data($request_id, $sum, $exp_time, $description = “) {
if (!isset($request_id) || !isset($sum) || !is_numeric($sum)) {
throw new Exception(__CLASS__ . ‘ ‘ . __METHOD__ . ‘ invalid argumets passed.’);
}

$exp_date = date(‘d.m.Y’, time() + $exp_time);
$data = „MIN=$this->min\nINVOICE=$request_id\nAMOUNT=$sum\nEXP_TIME=$exp_date\nDESCR=$description“;

$encoded = base64_encode($data);
$checksum = $this->hmac($encoded, $this->secret);

$result = array();
$result[] = „
„;
$result[] = „
„;
$result[] = „
„;
$result[] = „
url_cancel’ />“;

return $result;
}

/\* md5 and sha1 only \*/
protected function hmac($data,$passwd, $algo = ‘sha1′){

$algo=strtolower($algo);
$p=array(‘md5′=>’H32′,’sha1′=>’H40′);

if(strlen($passwd)>64) $passwd=pack($p[$algo],$algo($passwd));
if(strlen($passwd)<64) $passwd=str_pad($passwd,64,chr(0));

$ipad=substr($passwd,0,64) ^ str_repeat(chr(0×36),64);
$opad=substr($passwd,0,64) ^ str_repeat(chr(0x5C),64);

return($algo($opad.pack($p[$algo],$algo($ipad.$data))));
}
}
[/geshi]

Сподели:
Edno23 Favit Svejo Twitter Facebook Google Buzz Delicious Google Bookmarks Digg
Публикувано в PHP, Компютри. Постоянна връзка.

7 коментара по epay.bg интеграция

  1. Валяк каза:

    10x Саймане, ше ми влезе в употреба :)

  2. alabala каза:

    Супер, но как може Payment да онаследява EpayPayment? Това е все едно Animal() да онаследява Cat().

  3. saiman каза:

    Абсолютно съм съгласен, не е структурирано както трябва! Би трябвало да е обратното, ама не е :) Ако ти е влязло в употреба и го имаш този код – ичистен/поправен, метни го на emaila, за го публикувам?

  4. Мартин каза:

    Привет, като изключим имената на класовете, кода актуален ли е още? Знаете ли дали epay.bg са сменяли нещо в начина си на работа от както е писан?
    Благодаря предварително! :)

  5. saiman каза:

    Здравей!
    Нямам представа дали epay.bg са променяли нещо или не. Ако не се лъжа обаче, предлагаха нещо като тест среда, така че би трябвало лесно да може да се провеи дали работи или не.
    Поздрави и успех с интеграцията!

  6. asd каза:

    Здравейте :)
    Имам следния въпрос: Възможно ли е да се изпратят custom полета към epay.bg с цел след като се върне response тази информация да се използва за идентификация на поръчката и на потребителя, който си е купил стоката или услугата?

  7. saiman каза:

    Здравей! Защо просто не използващ request id-то за тази цел? Може да запишеш преди поръчка, за вече генерираното request id, кой е потребителя, каква е поръчката и въобще всичко друго, което ти е необходимо. На response страницата след това, просто ще изваждаш вече записаната инфромация, за върнатото от epay request id.

Вашият коментар

Вашият email адрес няма да бъде публикуван Задължителните полета са отбелязани с *

*

Можете да използвате тези HTML тагове и атрибути: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>