Integrácia platobnej brány Besteron

Besteron koncom minulého roka (2022) spustil novú verziu platobnej brány a ja som bol medzi prvými používateľmi, ktorý sa preklápali zo starého riešenia na nové. Nové riešenie je skutočne lepšie ako z pohľadu používateľa tak aj z pohľadu security a aj samotnej práce s API.

Besteron bohužiaľ neponúka žiadnu oficiálnu verziu PHP skriptu / class, ktorý by sa dal jednoducho naimportovať a použiť (komunikoval som o tom s nimi, zatiaľ to ale nespravili). Tak som takýto BesteronClass.php vyrobil. Je to len nevyhnutný základ, aby to fungovalo, bez akýchkoľvek validácií a garancií. Ako inšpirácia snáď bude postačovať. Na stránke Besteronu nájdete aj pluginy pre najpoužívanejšie e-commerce platformy. Bolo by ale fajn, keby takýto Class spravili oficiálne ľudia z Besteronu a doplnili tam aj validácie všetkých vstupných parametrov, aby sa založenie platby volalo len v prípade, že sú všetky parametre zadané správne. Minimálne by sa ušetrili chybné volania ešte pred volaním samotným 🙂 Napr. Superfaktúra to tam má a integrácia je potom skutočne hračka.

Platbu je potrebné rozdeliť na volanie platby (payment intent) a zistenie stavu plabu (payment intents status). Je dobré všetky kroky logovať, aby ste sa vedeli pozrieť čo sa pri pokuse o platbu udialo.

Založenie platby (payment intent)

Založenie platby zavoláte takto:

<?php
//
try {
  //inicializacia platobnej brany
  $besteron = new Besteron(array("cid"=>BESTERON_CID, "key"=>BESTERON_KEY, "api_key"=>BESTERON_API_KEY, "is_localhost"=>BESTERON_LOCALHOST));
  //
  $params = 
    array(
      'totalAmount' => number_format($total_price_100,0,'.',''),
      'currencyCode' => 'EUR',
      'orderNumber' => $order->id_order,
      'validityTime'=> 3600,
      'language' => 'SK',
      'paymentMethods' => $payment_methods, //
      'description' => $order_description,
      'callback' => [
        'returnUrl' => get_bloginfo('url')."/".BESTERON_RU,
        'notificationUrl'=> get_bloginfo('url')."/".BESTERON_RU,
      ],
      'buyer'=> [
        'email' => substr(strtolower($order->email), 0, 255),
        'phone' => $phone_number,
        'firstName' => substr($order->first_name, 0, 127),
        'lastName' => substr($order->last_name, 0, 127),
        "delivery" => [
          "streetLines" => [
              substr($order->street_2, 0, 127),
              substr($order->number_2, 0, 127)
          ],
          "postalCode" => substr($order->zip_2, 0, 15),
          "city" => substr($order->city_2, 0, 63),
          //"countrySubdivisionCode": "SK-BL",
          "countryCode" => $order->country_code_2,
          "recipient" => [
              "firstName" => substr($order->first_name, 0, 127),
              "lastName" => substr($order->last_name, 0, 127),
              "email" => substr(strtolower($order->email), 0, 255),
              "phone" => $phone_number
          ]
      ],
      "billing" => [
          "firstName" => substr($order->first_name, 0, 127),
          "lastName" => substr($order->last_name, 0, 127),
          "streetLines" => [
              substr($order->street_1, 0, 127),
              substr($order->number_1, 0, 127)
          ],
          "postalCode" => substr($order->zip_1, 0, 15),
          "city" => substr($order->city_1, 0, 63),
          //"countrySubdivisionCode" => "SK-BL",
          "countryCode" => $order->country_code_1
        ]
      ],
      'items' => $items,      
    );
  // ziskanie redirect url (vola sa payment intent)
  $result = $besteron->getPaymentUrl($params);
  //
}
//
catch (Exception $e) {    
  //log error
}

//otvorenie platobnej brany
echo '<script type="text/javascript">window.location.href = "'.$result->redirectUrl.'"</script>';?>

?>

V konštatne BESTERON_LOCALHOST posielam TRUE, ak volám platobnú bránu z localhostu v rámci testovania. Na základe tohto parametra mením nastavenie parametrov pre CURL, pretože overenie SSL sa vtedy nepodarí. V rámci BesteronClass nastavím teda:

if ($this->is_localhost) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

Besteron vám pri úspešnom založení platby vráti dva parametre:

{
  "redirectUrl": "https://url-k-platobnej-brane/",
  "transactionId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}

Do ‘paymentMethods‘ => $payment_methods, viete zadať typy platieb, ktoré sa v platobnej bráne zobrazia. Ja posielam všetky slovenské.

Na redirectUrl sa treba presmerovať a otvorí sa platobná brána, transactionId si treba uložiť k objednávke. Podľa tohto id potom viete kontrolovať stav platby. V prípade chyby príde chybová hláška.

Keď klient platbu dokončí úspešne alebo neúspešne, Besteron automaticky zavolá návratovú url, ktorú ste nastavili v parameteroch payment intent:

"callback": {
        "returnUrl": "https://vasa-navratova-url",
        "notificationUrl": "https://vasa-notifikacna-url/"
    },
  • returnUrl je zavolaná ihneď po dokončení platby (úspešnom, alebo neúspešnom)
  • notificationUrl je volaná pri každej ďalšej zmene stavu platby, potvrdenie platby trvá niekedy dlhšie, ja používam obidve rovnaké (treba na to ale myslieť pri implementácii)

Ak klient nechá otvorenú platobnú bránu “vyhniť” dlhšie ako validityTime (hodnota je v sekundách) tak sa mu už platbu neporadí zrealizovať a skončí to s chybou.

Ak klient platbu nedokončí, napr. zavrie prehliadač alebo mu spadne internet, tak sa o tom už nijako nedozviete. Preto je potrebné uložiť si stav úhrady objednávky už pred začiatkom volania. Ja si napr. na objednávke poznačím stav Prebieha online úhrada a vtedy sa objednávka už nedá uhradiť online znova. Ak skončí úhrada skutočne s chybou, stav vrátim na Čakanie na online úhradu a vtedy vie klient na detaile objednávky kliknúť znova na Zaplatiť online.

Overenie stavu platby (callback)

Overenie stavu platby som implementoval tak, že pokiaľ mám objednávku ešte v stave, že platba nebola potvrdená ako úspešná (Čakáme na potvrdenie platby), tak má aj klient možnosť kliknúť na button na objednávke, ktorý sa na stav platby opýta. Rovnako je táto URL volaná aj automaticky pri zmene stavu platbu. Besteron vždy po skončení platby užívateľa presmeruje na returnUrlURL a zároveň aj zavolá notificationUrl. Mne sa tak v logu objavia dva zápisy o stave platby, čo mi ale nevadí. S Besteronom sa dá ale dohodnúť, aby sa pri prvotnom redirecte na returnUrlURL už nevolala aj notificationUrl.

Stav platby zistíte takto:

<?php
//check payment status
try {
  $besteron = new Besteron(array("cid"=>BESTERON_CID, "key"=>BESTERON_KEY, "api_key"=>BESTERON_API_KEY, "is_localhost"=>BESTERON_LOCALHOST));

  //
  $result = $besteron->getPaymentStatus($id_online_trans);
  //
  $payment_status = $result->transaction->status;
  $payment_method = $result->transaction->selectedPaymentMethod;
  //echo $payment_status;
  //
}
catch (Exception $e) {    
  //log error
}

//check payment status and process it
//echo $payment_status;
//payment completed successfuly
if ($payment_status == "Completed"){
  fInsertOrderHistory($order->id_order, '12',"online úhrada potvrdená systémom Besteron $payment_method_name","Y","Y");
}

//created
else if ($payment_status == "Created"){
  fInsertOrderHistory($order->id_order, '11',"Platba bola úspešne vytvorená v systéme Besteron $payment_method_name. Čakáme na potvrdenie úhrady.","Y","Y");
}

else if ($payment_status == "WaitingForConfirmation"){
  fInsertOrderHistory($order->id_order, '11',"Čakáme na potvrdenie úhrady.","Y","Y");
}

//manual check needed
else if ($payment_status == "ManualAttentionRequired"){
  fInsertOrderHistory($order->id_order, '11',"Úhrada zatiaľ nebola potvrdená a musíme ju preveriť v platobnej spoločnosti Besteron. $payment_method_name","Y","Y");
}

//error status, get order status back to waiting for online payment
else if ($payment_status == "Invalid" || $payment_status == "Canceled" || $payment_status == "Timeouted" || $payment_status == "Error"){
  fInsertOrderHistory($order->id_order, '10',"Chyba pri vykonaní online úhrady $payment_method_name.","Y","Y");
}

else {
  fInsertOrderHistory($order->id_order, '10',"chyba pri vykonaní online úhrady","Y","Y");
}

Pár tipov a vychytávok

Upozorním ešte na pár vecí, na ktoré som prišiel metódou pokus omyl. Niečo som si nevšimol v dokumentácii, niečo v dokumentácii nie je a na veľa vecí mi odpovedali priamo z Besteronu. Inak musím pochváliť ich helpdesk, reagujú promptne a k veci.

Formát telefónneho čísla – za medzinárodnou predvoľbou musí byž medzera, teda napr. +421medzera123456789

Položky objednávky:
– celkový súčet položiek musí sedieť na celkovú sumu objednávky. Pozor, cena na položke je jednotková a až platobná brána robí výpočet celkovej ceny ako jednotková cena krát počet kusov. Pozor na zaokrúhľovanie, aby vám to sedelo.
– item type COUPON – je to zľavový kupón, suma sa zadáva absolútnou hodnotou a platobná brána túto sumu odrátava

Apple pay – platba je len pre používateľov Apple zariadenia + prehliadač Safari, je to štandardné nastavenie priamo od Apple, keď detegujeme, že sa splnili tieto podmienky, zobrazí sa Apple Pay v platobnej bráne, inak nie. Google pay v tomto nemá obmedzenia.

Povinné atribúty – povinný je Buyer – email, ale je dobré posielať aj meno, priezvisko a adresu v rámci Buyer, tie však nie sú povinné. Delivery a Billing nie sú povinné, ale odporúčam posielať aj to. Items sú povinné, ale v rámci tohto objektu sú niektoré parametre nepovinné, napr. EAN a Url.

Prechod stavov úhrady v Besterone

Zisťoval som ešte, akými stavmi úhrada prechádza a ktoré stavy môžem považovať za konečné. Možno sa to chodí na lepšie pochopenie celého procesu.

Finálne stavy ktoré by sa už nemali meniť sú: Completed, Error, Invalid,

Prechody stavov:
Created > Timeouted – už sa nebude v čase meniť
Created > WaitingForConfirmation >Timeouted – sa môže ešte zmeniť v prípade, že zákazník zaplatil bankovým tlačidlom a banka nedala hneď odpoveď (spárovať sa môže napr. po pár hodinách alebo až na druhý deň výpisom)
Created > WaitingForConfirmation > ManualAttentionRequired – špeciálny prípad kedy musíme skontrolovať, čo sa stalo s transakciou a vieme jej stav zmeniť ručne. Malo by to byť veľmi ojedinele, ak sa to stane, bude to skôr zmena ručne na Canceled.
Created > WaitingForConfirmation > Canceled – platbu je možné po neúspešnom pokuse opakovať, takže aj po Canceled môže byť opäť WaitingForConfirmation > Completed alebo znova Canceled a znova WaitingForConfirmation.

Leave a Reply

Your email address will not be published. Required fields are marked *