Подаване на задължение за плащане през Easypay от WEB търговец

Алекс

Registered
Здравейте!
Опитвам се да създам система, която да генерира код за плащане на каса в EasyPay. Писах си със съпорта, но те не могат да ми помогнат до толкова, колкото искам аз, тъй като на всеки въпрос ми изпращат някакви предварително изготвени (отговори) шаблони. Ето какво ми изпратиха като документация

Код:
ePay.bg комуникационен пакет за търговци
     ePay.bg <office@epay.bg>, <epay2_demo@epay.bg>
    ______________________________________________________________________

  V. Подаване на задължение за плащане през Easypay от WEB търговец
    ______________________________________________________________________

    Схема:

    1.  Клиент на WEB търговец прави поръчка в електронния му магазин

    2.  След като е готов с поръчката, клиентът формира искане да плати в
        офис на Easypay

    3.  Търговецът изготвя пакет /заявка за плащане/, който изпраща към
        дадено URL като HTTP GET заявка скрито от клиента. В същата HTTP
        сесия, ако всичко със заявката е коректно, ePay.bg ще върне на
        търговеца "Код за плащане в Easypay". Търговецът показва на своя
        сайт този код, с който клиента да отиде в офис на Easypay и да
        извърши плащането

    4.  ePay.bg системата следи за статуса на регистрираните/записаните
        чакащи задължения и при плащане, отказване или изтичане се изпраща
        известие на търговеца за тях

    5.  При получаване на известие от ePay.bg, търговецът трябва да формира
        съответен отговор

     Търговец    --[ЗАЯВКА ЗА ПЛАЩАНЕ]-->            ePay.bg
     ePay.bg     --[КОД ЗА ПЛАЩАНЕ]-->               Търговец
     Клиент      --[ПЛАЩАНЕ]-->                      Easypay
     ePay.bg     --[ИЗВЕСТИЕ]-->                     Търговец
     Търговец    --[ОТГОВОР НА ИЗВЕСТИЕ]-->          ePay.bg

    Всеки регистриран в системата търговец има генерирани буквено-цифрена
    секретна дума с дължина 64 и идентификационен номер (КИН). Търговецът
    може да ги види в личните си данни без да може да ги променя.

    A. Заявката за плащане се изпраща като HTTP GET заявка към
    https://www.epay.bg/ezp/reg_bill.cgi * за тестове:
    https://demo.epay.bg/ezp/reg_bill.cgi

    Подавани параметри:

     ENCODED     - кодирана с base64 (RFC 3548) заявка за плащане, EOL=''
     CHECKSUM    - контролна сума върху ENCODED, генерирана като HMAC с
                   алгоритъм SHA-1 и секретната дума на търговеца.

    Perl примерен код: {

     # Кодиране на заявката
     $ENCODED  = encode_base64('DATA', ''); # '' за EOL (def. е "\n")

     # Генериране на контролна сума
     $CHECKSUM = hmac_hex($ENCODED, $secret, \&sha1);

    }

    PHP примерен код: {

     # Кодиране на заявката
     $ENCODED  = base64_encode('DATA');

     # Генериране на контролна сума
     $CHECKSUM = hmac('sha1', $ENCODED, $secret);
     # кода на функцията hmac може да видите в demo.php

    }

    Примерна заявка:

     MIN=1000000000        (зад. MIN или EMAIL     Идентификационен номер на търговеца)
     EMAIL=a@merch.bg      (зад. MIN или EMAIL     E-mail на търговеца в системата)
     INVOICE=123456        (задължително           Номер фактура)
     AMOUNT=22.80          (задължително           Сума)
     EXP_TIME=01.08.2020   (задължително           Крайна дата/час за плащане)
     DESCR=Test            (опционално             Описание до 100 символа)

    Не е задължитено да са подредени точно в този ред.

     MIN     - съответсва на КИН в личните данни на търговеца
     EMAIL   - e-mail на търговеца, с който той е регистриран
               (*) в заявката се подава MIN или EMAIL за идентифициране на търговеца
     INVOICE - само цифри
     AMOUNT  - валидна сума > 0.01 ( например: 22, 22.8, 22.80 )

    За полето EXP_TIME е валиден следния формат:

     EXP_TIME=DD.MM.YYYY[ hh:mm[:ss]]
 
     EXP_TIME=01.08.2020
     EXP_TIME=01.08.2020 23:15    (може да се подаде и с час:мин)
     EXP_TIME=01.08.2020 23:15:30 (може да се подаде и с час:мин:сек)

    Примерен отговор на заявка:

     IDN=1234567890 (Код за плащане - десет цифри)
     или
     ERR=Описание на грешка

    След като заявката за плащане е регистрирана, то системата ще извести
    търговеца за състоянието на плащането: 'Платено', 'Отказано' или
    'Изтекло'.Ако клиентът не потвърди или откаже искането преди подадената
    крайна дата, то ще се маркира като изтекло.

    Заявка с даден INVOICE може да влезе в системата ЕДИН единствен път.

    * За имитиране извършване на плащане в офис на Easypay правите следната
    HTTP GET заявка:
    https://demo.epay.bg/ezp/pay_bill.cgi?ACTION=PAY&IDN=получения_код_за_плащане

    B. Известието на системата се изпраща на зададено от търговеца URL като
    HTTP POST заявка, на която търговеца връща отговор в същата HTTP сесия.

    Подписване на известието за плащане

     ENCODED     - кодирано с base64 (RFC 3548) известие, EOL=''
     CHECKSUM    - контролна сума върху ENCODED, генерирана като HMAC с
                   алгоритъм SHA-1 и секретната дума на търговеца.

    Perl примерен код: {

     $data = decode_base64($ENCODED);
 
     # Калкулиране на контролната сума
     # Трябва $CHECKSUM_CALC == $CHECKSUM
     $CHECKSUM_CALC = hmac_hex($ENCODED, $secret, \&sha1);

    }

    PHP примерен код: {

     $data = base64_decode($ENCODED);

     # Калкулиране на контролната сума
     # Трябва $CHECKSUM_CALC == $CHECKSUM
     $CHECKSUM_CALC = hmac('sha1', $ENCODED, $secret);
     # кода на функцията hmac може да видите в demo.php

    }

    Примерно известие от ePay.bg:

     INVOICE=123456:STATUS=PAID:PAY_TIME=YYYYMMDDhhmmss:STAN=[6 числа]:BCODE=[6 числа/букви]
     INVOICE=123457:STATUS=DENIED
     INVOICE=123457:STATUS=EXPIRED

     STATUS=[PAID | DENIED | EXPIRED]   - Платено | Отказано | Изтекло
     PAY_TIME                           - Дата/час/сек на плащането
     STAN                               - Номер транзакция
     BCODE                              - Авторизационен код на БОРИКА

     STAN и BCODE ще са 000000, ако плащането не е извършено от карта
 
    За всеки номер на фактура в известието, търговеца трябва да върне
    статус: OK (ако всичко е наред), ERR (за грешка) или NO (ако не знае за
    тази фактура). При връщане на OK или NO системета спира да изпраща
    известие за съответната фактура.

    Примерен отговор на търговеца:

     INVOICE=123456:STATUS=OK
     INVOICE=123457:STATUS=ERR
     INVOICE=123458:STATUS=NO

    Ако нещо не е коректно в известието изпратено от ePay.bg системата
    търговецът връща ERR=описание.

    Пример:

     ERR=описание за глобалната грешка (примерно невярна CHECKSUM)

    Ако търговецът не е заявил URL, на което да получава известия за
    плащанията, не иска или няма възможност да обработва тези известия, то
    търговецът може да си вижда статуса на исканията за плащане в ePay.bg
    системата.

    Ако ePay.bg не маркира дадена фактура като получена от търговеца
    (примерно върнат статус ERR или пропаднала комуникация) системата ще се
    опита да изпрати пропадналите известия отново.

    Схема за изпращане на известия по дадена фактура:

     1) 5 опита през < 1 минута
     2) 4 опита през 15 минути
     3) 5 опита през 1 час
     4) 6 опита през 3 часа
     5) 4 опита през 6 часа
     6) 1 опит на ден

    Системата спира да изпраща известие за дадена фактура, ако то не бъде
    получено от търговеца в продължение на 30 дена.

    ______________________________________________________________________

    КРАЙ

Някой да даде някаква насока? Нямам точен проблем, за сега съм стигнал до тук (Laravel), но генерално нищо не разбирам от документацията им:

Код:
        $secret_seller = 'TUK SE POSTAVQ ID NA TURGOVECA';
        $data = base64_encode('MIN=1000000000&INVOICE=123456&AMOUNT=19.99&EXP_TIME=' . now()->addHours(24)->format("d.m.Y H:i:s") . '&DESCR=description&MERCHANT=' . config("app.name") . '&IBAN=BGFIVN943&BIC=BIC391&PSTATEMENT=34342&STATEMENT=buy cr&OBLIG_PERSON=' . auth()->user()->name . '&EGN=' . auth()->user()->egn . '&DOC_NO=340294985&DATE_BEGIN=21.03.2020&DATE_END=21.03.2030');
        $response = Http::get('https://demo.epay.bg/ezp/reg_bill.cgi?ENCODED=' . $data);
        dd('https://demo.epay.bg/ezp/reg_bill.cgi?ENCODED=' . $data, $response);

Благодаря предварително!
 
А проблема е?
Тоя код работи ли, какво връща?
Като цяло е супер елементарно
Бате, ако нещо подобно си правил, ще споделиш ли? Веднъж ми се наложи за един букинг сайт в бг, защото явно това с касите на Изибей е било удобно за капариране на стаи, но грам идея нямам, или не си спомням как се случи интеграцията. Беше преди 10 години. Спомням си, че имаше една документация, по която се движихме аз и бекендъра, но много ми е избледняло. Ако автора има нещо, което са му предоставили, за да работи с него. Дори в мейла не си намирам остатъчни материали по случая.
 
Момчета, без излишно напрежение. Това тепърва ми предстои да го сътворя, все още не съм стигнал до разработката му, но реших да съм една крачка напред и да разбера за какво иде реч преди да се захвана, защото предстои много скоро. Тепърва трябва да се регистрира търговец в системата им, за да имам $secret_seller и т.н.. Просто исках някой, който вече се е занимавал с това нещо да ми даде насоки, за да избегна губене на време в последствие. Аз както вече споменах се затруднявам четейки документацията, която са ми изпратили. Според мен не е добре описано и обяснено. Има доста неясни неща
PHP:
# Кодиране на заявката
$ENCODED  = base64_encode('DATA');

# Генериране на контролна сума
$CHECKSUM = hmac('sha1', $ENCODED, $secret);
# кода на функцията hmac може да видите в demo.php

Какво всъщност трябва да замести 'DATA' при base64_encode('DATA')? Пише, че кода на функцията hmac може да се види в demo.php, а такъв файл всъщност нито съществува в документацията, нито в имейлите, които получих от съпорта.
 
Момчета, без излишно напрежение. Това тепърва ми предстои да го сътворя, все още не съм стигнал до разработката му, но реших да съм една крачка напред и да разбера за какво иде реч преди да се захвана, защото предстои много скоро. Тепърва трябва да се регистрира търговец в системата им, за да имам $secret_seller и т.н.. Просто исках някой, който вече се е занимавал с това нещо да ми даде насоки, за да избегна губене на време в последствие. Аз както вече споменах се затруднявам четейки документацията, която са ми изпратили. Според мен не е добре описано и обяснено. Има доста неясни неща
PHP:
# Кодиране на заявката
$ENCODED  = base64_encode('DATA');

# Генериране на контролна сума
$CHECKSUM = hmac('sha1', $ENCODED, $secret);
# кода на функцията hmac може да видите в demo.php

Какво всъщност трябва да замести 'DATA' при base64_encode('DATA')? Пише, че кода на функцията hmac може да се види в demo.php, а такъв файл всъщност нито съществува в документацията, нито в имейлите, които получих от съпорта.
Поискай да ти пратят demo.php и ако може го сподели тук.

Малко повече инфо.
 
Прословутото demo.php

PHP:
<?php
function hmac($algo,$data,$passwd){
        /* md5 and sha1 only */
        $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(0x36),64);
        $opad=substr($passwd,0,64) ^ str_repeat(chr(0x5C),64);

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

# XXX ePay.bg URL (https://demo.epay.bg/ if POST to DEMO system)
$submit_url = 'https://www.epay.bg/';
# XXX Secret word with which merchant make CHECKSUM on the ENCODED packet
$secret     = 'SET_THIS_CORRECT';

$min        = 'SET_THIS_CORRECT';
$invoice    = sprintf("%.0f", rand(10) * 100000); # XXX Invoice
$sum        = '22.80';                            # XXX Ammount
$exp_date   = '01.08.2020';                       # XXX Expiration date
$descr      = 'Test';                             # XXX Description

$data = <<<DATA
MIN={$min}
INVOICE={$invoice}
AMOUNT={$sum}
EXP_TIME={$exp_date}
DESCR={$descr}
DATA;

# XXX Packet:
#     (MIN or EMAIL)=     REQUIRED
#     INVOICE=            REQUIRED
#     AMOUNT=             REQUIRED
#     EXP_TIME=           REQUIRED
#     DESCR=              OPTIONAL

$ENCODED  = base64_encode($data);
$CHECKSUM = hmac('sha1', $ENCODED, $secret); # XXX SHA-1 algorithm REQUIRED

echo <<<HTML
<HTML>
<head>
    <meta http-equiv="content-type" content="text/html; charset=windows-1251">
</head>
<BODY TEXT=#000000 BGCOLOR=#FFFFFF>
<BR>
<BR>
<CENTER><h1>DEMO</h1>
<TABLE border=1>

<form action="{$submit_url}" method=POST>
<input type=hidden name=PAGE value="paylogin">
<input type=hidden name=ENCODED value="{$ENCODED}">
<input type=hidden name=CHECKSUM value="{$CHECKSUM}">
<input type=hidden name=URL_OK value="SET_URL_OK_IF_NEEDED">
<input type=hidden name=URL_CANCEL value="SET_URL_CANCEL_IF_NEEDED">

<TR>
<TD>МИН: {$min}</TD>
</TR>

<TR>
<TD>Фактура номер: {$invoice}</TD>
</TR>

<TR>
<TD>Описание: {$descr}</TD>
</TR>

<TR>
<TD>

<xmp>
Продукти                    кол.  ед.цена   цена
------------------------------------------------
продукт1                     3      2.50    7.50
продукт2                     1      3.50    3.50
продукт3                     2      4.00    8.00
------------------------------------------------
общо   19.00
ДДС    3.80
Всичко   {$sum}
</xmp>

</TD>
</TR>

<TR>
<TD>Сума: {$sum}</TD>
</TR>


</table>

<table width=100%>
<TR align=center>
<TD><INPUT type=submit></TD>
</TR>


</TABLE>

</form>
</BODY>
</HTML>
HTML;
?>
 
В крайна сметка се регистрирах с демо акаунт и правя тестове в момента.
Код:
$secret = '6FZOEBRI18B62PNRIXAKD......7FIDEFD6TT31AKYKNNN6E';
$min = 'D023...624';
$invoice = 10000000; // sprintf("%.0f", rand(1, 10) * 100000)
$sum = 20.00;
$exp_date = now()->addHours(24)->format("d.m.Y");
$descr = 'Test';

$data = base64_encode('MIN=' . $min . '&INVOICE=' . $invoice . '&AMOUNT=' . $sum . '&EXP_TIME=' . $exp_date . '&DESCR=' . $descr . '&MERCHANT=' . config("app.name") . '&OBLIG_PERSON=' . auth()->user()->name);
$checksum = $this->hmac('sha1', $data, $secret);

$end_url = 'https://demo.epay.bg/ezp/reg_bill.cgi?ENCODED=' . $data . '&CHECKSUM=' . $checksum;

$response = Http::get($end_url);
dd($end_url, $response->body());

Но този мой опит връща

Код:
ERR=EMETHOD: Некоректно подадени данни от търговеца! [MIN][D023...624&INVOICE=10000000&AMOUNT=20.00&EXP_TIME=19.10.2022&DESCR=Test&MERCHANT=TestApp&OBLIG_PERSON=Ivan Ivanov Ivanov]
 
Нов опит:

Код:
$secret = '6FZOEBRI18B62PNRIX...KZ7FIDEFD6TT31AKYKNNN6E';
$min = 'D02..624';
$email = 'alex.....@protonmail.com';
$invoice = mt_rand(10000, 10000000); // sprintf("%.0f", rand(1, 10) * 100000)
$sum = 20.00;
$exp_date = now()->addHours(24)->format("d.m.Y");
$iban = 'BG93FINV915....551';
$iban_bic = 'FINVBGSF';
$user_egn = '9390....32';
$user_doc_no = 65....626;
$user_doc_date = '20.01.2020';
$description = 'Test';

$data = base64_encode(
    'MIN=' . $min .
    '&EMAIL=' . $email .
    '&INVOICE=' . $invoice .
    '&AMOUNT=' . $sum .
    '&EXP_TIME=' . $exp_date .
    '&MERCHANT=' . config("app.name") .
    '&IBAN=' . $iban .
    '&BIC=' . $iban_bic .
    '&OBLIG_PERSON=' . auth()->user()->name .
    '&EGN=' . $user_egn .
    '&DOC_NO=' . $user_doc_no .
    '&DOC_DATE=' . $user_doc_date .
    '&DESCR=' . $description
);

$checksum = $this->hmac('sha1', $data, $secret);

$end_url = 'https://demo.epay.bg/ezp/reg_bill.cgi?ENCODED=' . $data . '&CHECKSUM=' . $checksum;

$response = Http::get($end_url);
dd($end_url, $response->body());

Дава същата грешка

Код:
ERR=EMETHOD: Некоректно подадени данни от търговеца!
 
Следният код проработи (най-сетне).


Код:
$secret = '6FZOEBRI18B62PNRIXA.......DEFD6TT31AKYKNNN6E';
$min = 'D02...624';
$invoice = sprintf("%.0f", rand(1, 10) * 100000);
$sum = '1000';
$exp_date = now()->addHours(48)->format("d.m.Y");
$description = 'Test';

$data = <<<DATA
MIN={$min}
INVOICE={$invoice}
AMOUNT={$sum}
EXP_TIME={$exp_date}
DESCR={$description}
DATA;

$data = base64_encode($data);

$checksum = $this->hmac('sha1', $data, $secret);

$end_url = 'https://demo.epay.bg/ezp/reg_bill.cgi?ENCODED=' . $data . '&CHECKSUM=' . $checksum;

$response = Http::get($end_url);
dd($end_url, $response->body());

Благодаря на всички отзовали се. Ако имам още въпроси ще пиша пак.
 

Горе