Files
kupshop/class/payments/class.PayU.php
2025-08-02 16:30:27 +02:00

355 lines
10 KiB
PHP

<?php
use KupShop\KupShopBundle\Config;
class PayU extends Payment
{
public static $name = 'PayU platební brána';
public $template = 'payment.PayU.tpl';
protected $templateCart = 'payment.PayU.cart.tpl';
public $class = 'PayU';
public $url = 'https://secure.payu.com/paygw/';
// Session
public $ts;
public $session_id;
public $method;
protected $pay_method = Payment::METHOD_ONLINE;
public function setOrder($order)
{
if (empty($order)) {
$this->readParams();
$order = $this->getOrderId($this->session_id);
}
if (empty($order)) {
$payu_response = $this->makeStatusRequest();
$trans = $this->readResponse($payu_response);
$result = $this->readStatus($trans);
logError(__FILE__, __LINE__, 'Not Found: result:'.print_r($result, true));
if ($result['code'] == 2 || $result['code'] == 1) {
// If payment canceled or not paid yet, confirm
echo 'OK';
exit;
}
if ($result['code'] == 99) {
logError(__FILE__, __LINE__, 'Missed payment!! Creating ...');
$order = intval($trans->order_id);
}
logError(__FILE__, __LINE__, 'Not Found: payu_response:'.$payu_response);
}
return parent::setOrder($order);
}
public function storePaymentInfo()
{
$data = parent::storePaymentInfo();
$data['method'] = explode('-', getVal('payment_id'))[1];
return $data;
}
public function checkAuth()
{
$this->readParams();
if ($this->session_id && !getVal('cf')) {
$parts = explode('_', $this->session_id);
$_GET['cf'] = getVal(0, $parts);
}
}
public function readParams()
{
$this->session_id = getVal('session_id');
$this->ts = time();
}
public function readResponse($payu_response)
{
$xml = simplexml_load_string($payu_response);
if (!$xml) {
logError(__FILE__, __LINE__, "Chyba parsovani xml: {$payu_response}");
}
if (strval($xml->status) != 'OK') {
logError(__FILE__, __LINE__, 'Transakce vratila chybu');
}
return $xml->trans;
}
public function readStatus($trans)
{
// incorrect POS ID number specified in response
if ($trans->pos_id != $this->config['pos']) {
return ['code' => false, 'message' => 'incorrect POS number'];
}
// calculating signature for comparison with sig sent by PayU
$sig = md5($trans->pos_id.$trans->session_id.$trans->order_id.$trans->status.$trans->amount.$trans->desc.$trans->ts.$this->config['key2']);
// incorrect signature in response in comparison to locally calculated one
if ($trans->sig != $sig) {
return ['code' => false, 'message' => 'incorrect signature'];
}
// different messages depending on transaction status. For status description, see documentation
switch ($trans->status) {
case 1:
return ['code' => $trans->status, 'message' => 'new'];
case 2:
return ['code' => $trans->status, 'message' => 'cancelled'];
case 3:
return ['code' => $trans->status, 'message' => 'rejected'];
case 4:
return ['code' => $trans->status, 'message' => 'started'];
case 5:
return ['code' => $trans->status, 'message' => 'awaiting receipt'];
case 6:
return ['code' => $trans->status, 'message' => 'no authorization'];
case 7:
return ['code' => $trans->status, 'message' => 'payment rejected'];
case 99:
return ['code' => $trans->status, 'message' => 'payment received - ended'];
case 888:
return ['code' => $trans->status, 'message' => 'incorrect status'];
default:
return ['code' => false, 'message' => 'no status'];
}
}
public function checkReceivedSignature()
{
// some parameters are missing
if (!isset($_POST['pos_id']) || !isset($_POST['session_id']) || !isset($_POST['ts']) || !isset($_POST['sig'])) {
logError(__FILE__, __LINE__, 'ERROR: EMPTY PARAMETERS');
}
// received POS ID is different than expected
if ($_POST['pos_id'] != $this->config['pos']) {
logError(__FILE__, __LINE__, 'ERROR: INCORRECT POS ID');
}
// verification of received signature
$sig = md5($_POST['pos_id'].$_POST['session_id'].$_POST['ts'].$this->config['key2']);
// incorrect signature
if ($_POST['sig'] != $sig) {
logError(__FILE__, __LINE__, 'ERROR: INCORRECT SIGNATURE');
}
return true;
}
public function makeStatusRequest()
{
// signature that will be sent to PayU with request
$sig = md5($this->config['pos'].$this->session_id.$this->ts.$this->config['key1']);
// preparing parameters string to be sent to PayU
$parameters = 'pos_id='.$this->config['pos'].'&session_id='.$this->session_id.'&ts='.$this->ts.'&sig='.$sig;
// sending request via CURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->url.'UTF/Payment/get');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$payu_response = curl_exec($ch);
curl_close($ch);
return $payu_response;
}
public function step($index, $message, $data = [])
{
if ($index == 1) {
if (!empty($this->method)) {
$data['method'] = $this->method;
} else {
$data['method'] = getVal('payu_method');
}
}
parent::step($index, $message, $data);
}
public function processStep_1()
{
$this->ts = strval(time());
$this->session_id = "{$this->order->getSecurityCode()}_{$this->ts}";
$this->method = getVal('method');
return true;
}
public function processStep_5()
{
$this->error('Platba byla zrušena.');
}
public function processStep_6()
{
$this->updatePaymentStatus();
if ($this->status == Payment::STATUS_FINISHED) {
$this->success('Platba proběhla v pořádku.');
} else {
$this->step(2, 'Platba byla zaznamenána, čeká se na zaplacení. O přijetí platby Vám zašleme email.');
}
}
public function processStep_7()
{
$this->checkReceivedSignature();
if ($this->updatePaymentStatus()) {
logError(__FILE__, __LINE__, 'PayU step 7 - OK');
echo 'OK';
} else {
logError(__FILE__, __LINE__, 'PayU step 7 - KO');
echo 'KO';
}
exit;
}
public function ensurePaymentExists($trans)
{
if (!$this->getStatus($this->session_id)) {
$this->createPayment($this->session_id, intval($trans->amount) / 100, ['id_payu' => $trans->id]);
}
}
public function updatePaymentStatus()
{
$payu_response = $this->makeStatusRequest();
logError(__FILE__, __LINE__, 'updatePaymentStatus: payu_response:'.$payu_response);
$trans = $this->readResponse($payu_response);
$result = $this->readStatus($trans);
logError(__FILE__, __LINE__, 'updatePaymentStatus: response:'.print_r($result, true));
if ($result['code']) {
if ($result['code'] == '2') {
// transaction canceled
return true;
}
$this->ensurePaymentExists($trans);
// change of transaction status in system of the shop
if ($result['code'] == '99') {
// payment sucessful so we send back OK
if (!$this->setStatus(Payment::STATUS_FINISHED, $this->session_id)) {
logError(__FILE__, __LINE__, 'Payment::updatePaymentStatus: setStatus failed!');
}
return true;
} elseif ($result['code'] == '4') {
// transaction pending
if (!$this->setStatus(Payment::STATUS_PENDING, $this->session_id)) {
logError(__FILE__, __LINE__, 'Payment::updatePaymentStatus: setStatus failed!');
}
return true;
} elseif ($result['code'] == '1') {
// transaction started
if (!$this->setStatus(Payment::STATUS_CREATED, $this->session_id)) {
logError(__FILE__, __LINE__, 'Payment::updatePaymentStatus: setStatus failed!');
}
return true;
}
logError(__FILE__, __LINE__, 'updatePaymentStatus: not handled');
return false;
} else {
logError(__FILE__, __LINE__, 'updatePaymentStatus: error: code='.$result['code'].' message='.$result['message']."\n{$payu_response}");
}
return false;
}
public function getName()
{
$methods = $this->getAvailableMethods();
return parent::getName().' - '.getVal($this->method, $methods, ['name' => 'Neznámý typ PayU platby'])['name'];
}
public function getAvailableMethods()
{
if (!($methods = getCache('payu-methods'))) {
$methods = $this->fetchAvailableMethods();
setCache('payu-methods', $methods);
}
return $methods;
}
public function fetchAvailableMethods()
{
$key = substr($this->config['key1'], 0, 2);
$xml = new SimpleXMLElement($this->url."UTF/xml/{$this->config['pos']}/{$key}/paytype.xml", 0, true);
$payTypes = $xml->xpath('//paytype');
$methods = [];
foreach ($payTypes as $payment) {
$payment = (array) $payment;
$payment['enable'] = $payment['enable'] == 'true';
$methods[$payment['type']] = $payment;
}
return $methods;
}
public function requiresEET()
{
return true;
}
public function hasOnlinePayment()
{
return true;
}
public static function isEnabled($className)
{
$cfg = Config::get();
if (empty($cfg['Modules']['payments'][$className])) {
return false;
}
return true;
}
}