/** * Проверяем header уведомлений и ответов от PayQR на соответствие значению SecretKeyIn * * @param $secretKeyIn * @return bool */ public static function checkHeader($secretKeyIn, $headers = false) { if (!PayqrConfig::$checkHeader) { return true; } if (!$headers) { if (!function_exists('getallheaders')) { $headers = PayqrBase::getallheaders(); } else { $headers = getallheaders(); } } if (!$headers) { header("HTTP/1.0 404 Not Found"); PayqrLog::log(__FILE__ . "\n\r" . __METHOD__ . "\n\r L:" . __LINE__ . "\n\r Не удалось выполнить проверку входящего секретного ключа SecretKeyIn, отсутствует headers"); return false; } // Проверяем соответствие пришедшего значения поля PQRSecretKey значению SecretKeyIn из конфигурации библиотеки if (isset($headers['PQRSecretKey']) && $headers['PQRSecretKey'] == $secretKeyIn) { return true; } foreach ($headers as $key => $header) { $headers[strtolower($key)] = $header; } if (isset($headers['pqrsecretkey']) && $headers['pqrsecretkey'] == $secretKeyIn) { return true; } header("HTTP/1.0 404 Not Found"); PayqrLog::log(__FILE__ . "\n\r" . __METHOD__ . "\n\r L:" . __LINE__ . "\n\r Входящий секретный ключ из headers не совпадает с входящим ключом из файла конфигурации \n\r Текущее значение SecretKeyIn из вашего PayqrConfig.php: " . $secretKeyIn . " \n\r Содержание headers полученного уведомления от PayQR: " . print_r($headers, true)); return false; }
/** * Получаем роль пользователя * @var int userId * @return int */ public function _dfnGetUserRoleId($userId) { //см. таблицу {users_role} if ($userId) { PayqrLog::log("SELECT role_id FROM diafan_users WHERE id=" . $userId); return DB::query_result("SELECT role_id FROM {users} WHERE id=%d", $userId); } return 0; }
/** * * @param type $xml * @return boolean */ public function createOrder($xml) { $payqrCURLObject = new PayqrCurl(); $responceXML = $payqrCURLObject->sendXMLFile(PayqrConfig::$insalesURL . "orders.xml", $xml); if (!$responceXML) { PayqrLog::log("Ответ от сервера InSales не в формате xml"); //Помечаем ответ от InSales, как ошибочный \frontend\models\InvoiceTable::updateAll(['order_request' => -1], 'invoice_id = :invoice_id', [':invoice_id' => $this->invoiceId]); return false; } //PayqrLog::log("Ответ от сервера \r\n" . $responceXML); return $responceXML; }
/** * * @return boolean | float */ public function getTotal() { $orderResultAmount = $this->parseOrderXML()->xpath("/order/order-lines/order-line/total-price"); $totalPrice = 0; while (list(, $price) = each($orderResultAmount)) { $totalPrice += round((double) $price, 2); } if (empty($totalPrice)) { PayqrLog::log("ОШИБКА! Сумма заказа равна 0!"); return false; } return $totalPrice; }
public function check_insales_responce($rawResponse) { libxml_use_internal_errors(true); $elem = simplexml_load_string($rawResponse); if ($elem !== false) { return $rawResponse; } else { PayqrLog::log(print_r($rawResponse, true)); PayqrLog::log("Возникли ошибки при обработке поступившего ответа от сервера!"); PayqrLog::log(print_r(libxml_get_errors(), true)); return false; } }
public static function init() { //получаем информацию о настройках кнопки $marketObj = new Market(); $market = $marketObj->getUserMarkets(DEFAULT_USER_ID)->one(); if (!isset($market->settings)) { PayqrLog::log("Не смогли получить настройки кнопки, прекращаем работу!"); return false; } $settings = json_decode($market->getSettings(), true); if (!isset($settings['merchant_id'], $settings['secret_key_in'], $settings['secret_key_out'], $settings['insales_url']) && (empty($settings['merchant_id']) || empty($settings['secret_key_in']) || empty($settings['secret_key_out']) || empty($settings['insales_url']))) { return false; } self::$merchantID = $settings['merchant_id']; self::$secretKeyIn = $settings['secret_key_in']; self::$secretKeyOut = $settings['secret_key_out']; self::$insalesURL = $settings['insales_url']; }
/** * Makes an HTTP request of the specified $method to a $url with an optional array or string of $vars * * Returns a PayqrCurl object if the request was successful, false otherwise * * @param string $method * @param string $url * @param array|string $vars * @return PayqrRequest|boolean **/ public function request($method, $url, $vars = array()) { if (is_array($vars)) { $vars = http_build_query($vars, '', '&'); } $this->headers['Content-Length'] = strlen($vars); $url_obj = parse_url($url); if (!isset($url_obj['host'])) { PayqrLog::log(__FILE__ . "\n\r" . __METHOD__ . "\n\r L:" . __LINE__ . "\n\r Неверный параметр url: " . $url . " не удалось получить host для запроса"); return false; } $host = $url_obj['host']; // port 443 for ssl $fp = fsockopen("ssl://" . $host, 443, $errno, $errstr, intval(PayqrConfig::$maxTimeOut)); if (!$fp) { PayqrLog::log("{$errstr} ({$errno})\n"); } else { $rawRequest = "{$method} {$url} HTTP/1.1\r\n"; $rawRequest .= "Host: {$host}\r\n"; $rawRequest .= "Accept: */*\r\n"; if (is_array($this->headers)) { foreach ($this->headers as $key => $value) { $rawRequest .= "{$key}: {$value}\r\n"; } } $rawRequest .= "Connection: close\r\n\r\n"; if (!empty($vars)) { $rawRequest .= "{$vars}\r\n\r\n"; } fwrite($fp, $rawRequest); $rawResponse = ""; while (!feof($fp)) { $rawResponse .= fgets($fp, 1024); } fclose($fp); $response = $this->check_response($rawResponse, $method, $url, $vars); return $response; } }
<?php /** * Скрипт принимает и обрабатывает уведомления от PayQR */ require_once __DIR__ . "/PayqrConfig.php"; // подключаем основной класс try { $receiver = new PayqrReceiver(); $receiver->handle(); } catch (PayqrExeption $e) { PayqrLog::log($e->response); }
/** * Обработка входящего запроса */ public function handle() { $object = $this->receive(); switch ($this->type) { case self::INVOICE: $invoice = new InvoiceHandler($object); if (!$invoice->settings) { PayqrLog::log("PayqrInvoice. Не смогли получить информацию!"); break; } switch ($this->event) { case 'invoice.order.creating': // нужно создать заказ в своей учетной системе, если заказ еще не был создан, и вернуть в PayQR полученный номер заказа (orderId), если его еще не было $invoice->createOrder(); break; case 'invoice.paid': // нужно зафиксировать успешную оплату конкретного заказа $invoice->payOrder(); break; case 'invoice.reverted': // PayQR зафиксировал полную отмену конкретного счета (заказа) и возврат всей суммы денежных средств по нему $invoice->revertOrder(); break; case 'invoice.cancelled': // PayQR зафиксировал отмену конкретного заказа до его оплаты $invoice->cancelOrder(); break; case 'invoice.failed': // ошибка совершения покупки, операция дальше продолжаться не будет $invoice->failOrder(); break; case 'invoice.deliverycases.updating': // нужно вернуть в PayQR список способов доставки для покупателя $invoice->setDeliveryCases(); break; case 'invoice.pickpoints.updating': // нужно вернуть в PayQR список пунктов самовывоза для покупателя $invoice->setPickPoints(); break; } break; case self::REVERT: $revert = new RevertHandler($object); switch ($this->event) { case 'revert.failed': // PayQR отказал интернет-сайту в отмене счета и возврате денежных средств покупателю $revert->fail(); break; case 'revert.succeeded': // PayQR зафиксировал отмену счета интернет-сайтом и вернул денежные средства покупателю $revert->succeed(); break; } break; case self::OFFER: $offer = new OfferHandler($object); switch ($this->event) { case 'offer.invoices.creating': // PayQR отказал интернет-сайту в отмене счета и возврате денежных средств покупателю $offer->create(); break; } break; case self::RECEIPT: $receit = new ReceitHandler($object); switch ($this->event) { case 'receipt.paid': // PayQR отказал интернет-сайту в отмене счета и возврате денежных средств покупателю $receit->pay(); break; } break; default: } $this->response(); }
/** * Возвращает сумму возврата по счету из объекта PayQR "Возвраты" * @return float */ public function getAmount() { PayqrLog::log('payqr_revert::getAmount()'); return isset($this->data->amount) ? $this->data->amount : 0; }
/** * Default Constructor * * @param string|null $message * @param int $code */ public function __construct($message = null, $code = 0, $response = false) { parent::__construct($message, $code); $this->response = $response; PayqrLog::log('Вызвано исключение : ' . $this->errorMessage()); }
/** * Отправка POST-запроса * @param $url * @param $postdata * @return bool|PayqrCurl_response */ public function post($url, $postdata) { if (is_array($postdata)) { $postdata = PayqrBase::http_build_query($postdata, '', '&'); } $url_obj = parse_url($url); if (!isset($url_obj['host'])) { PayqrLog::log(__FILE__ . "\n\r" . __METHOD__ . "\n\r L:" . __LINE__ . "\n\r" . " Неверный параметр url: " . $url . " не удалось определить host для запроса"); return false; } $host = $url_obj['host']; $errno = ""; $errstr = ""; $fp = fsockopen("ssl://" . $host, 443, &$errno, &$errstr, intval(PayqrConfig::$maxTimeOut)); if (!$fp) { PayqrLog::log(__FILE__ . "\n\r" . __METHOD__ . "\n\r L:" . __LINE__ . "\n\r" . 'Ошибка при запросе ' . $url . ' ' . $errstr($errno) . "\n"); } else { $out = "POST {$url} HTTP/1.1\r\n"; $out .= "User-Agent: PayQr Lib\r\n"; $out .= "Host: {$host}\r\n"; $out .= "Accept: */*\r\n"; if (is_array($this->headers)) { foreach ($this->headers as $key => $value) { $out .= "{$key}: {$value}\r\n"; } } $out .= "Connection: Close\r\n\r\n"; $out .= "{$postdata}\n\n"; $out .= "\r\n"; fwrite($fp, $out); $output = ""; while (!feof($fp)) { $output .= fgets($fp, 1024); } fclose($fp); $response = false; if ($output) { $response = new PayqrCurl_response($output); } else { PayqrLog::log(__FILE__ . "\n\r" . __METHOD__ . "\n\r L:" . __LINE__ . "\n\r" . 'Ошибка при запросе ' . $url . ' пустой или неправильный ответ ' . print_r($output, true) . "\n"); } return $response; } }
/** * @param $cart_summ * @param null $userId * @return bool */ private function get_discount_total($cart_summ, $userId = null) { PayqrLog::log("get_discount_total"); $discount = false; $order_summ = 0; if ($userId) { $order_summ = DB::query_result("SELECT SUM(summ) FROM {shop_order} WHERE user_id=%d AND (status='1' OR status='3')", $userId); } PayqrLog::log("Order sum : " . $order_summ); //скидка на общую сумму заказа $person_discount_ids = $this->diafan->_shop->price_get_person_discounts(); PayqrLog::log("Получили скидки клиенту: ", print_r($person_discount_ids, true)); $userRoleId = dfnUserAuth::getInstance($this->diafan)->_dfnGetUserRoleId($userId); PayqrLog::log("Получили роль пользователя: " . $userRoleId); $rows = DB::query_fetch_all("SELECT id, discount, amount, deduction, threshold, threshold_cumulative FROM" . " {shop_discount} WHERE act='1' AND trash='0' AND (threshold_cumulative>0 OR threshold>0)" . " AND role_id" . ($userRoleId ? ' IN (0, ' . $userRoleId . ')' : '=0') . " AND (person='0'" . ($person_discount_ids ? " OR id IN(" . implode(",", $person_discount_ids) . ")" : "") . ")" . " AND date_start<=%d AND (date_finish=0 OR date_finish>=%d)" . " AND (threshold_cumulative>0 AND threshold_cumulative<=%f" . " OR threshold>0 AND threshold<=%f)", time(), time(), $order_summ, $cart_summ); PayqrLog::log("После получения скидки: ", print_r($rows, true)); foreach ($rows as $row) { $row["discount_id"] = $row["id"]; if ($row['deduction']) { if ($row['deduction'] < $cart_summ) { $row["discount_summ"] = $row["deduction"]; } else { $row["discount_summ"] = 0; } } else { $row["discount_summ"] = $cart_summ * $row["discount"] / 100; } if (empty($discount) || $discount["discount_summ"] < $row["discount_summ"]) { $discount = $row; } } return $discount; }
<?php /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ require_once __DIR__ . "/PayqrConfig.php"; if (isset($_GET["key"]) && $_GET["key"] == PayqrConfig::$logKey) { $text = PayqrLog::showLog(); } else { $text = "Введён неверный ключ доступа к логам"; } echo $text;
public function setUserMessage($userMessage) { PayqrLog::log('payqr_invoice::setUserMessage()'); $this->data->message = $userMessage; }
/** * Ответ на уведомление */ public function response() { header("PQRSecretKey:" . PayqrConfig::$secretKeyOut); // если счет отмечен как отмененный if ($this->objectHandler->cancel) { header("HTTP/1.1 409 Conflict"); PayqrLog::log('payqr_receiver::response() - Send HTTP/1.1 409 Conflict'); return; } header("HTTP/1.1 200 OK"); $json = json_encode($this->objectEvent); PayqrLog::log("RESPONSE\n" . $json); echo $json; }
public function setDeliveryCases() { PayqrLog::log("setDeliveryCases" . PHP_EOL); $result = \frontend\models\InvoiceTable::find()->where(["invoice_id" => $this->invoiceId])->one(); PayqrLog::log(print_r($result, true)); $deliveryData = json_decode($result->data); if ($deliveryData) { //проверяем данные в формате json, но в любом случае наличие данных говорит, о том, что запрос уже был PayqrLog::log("setDeliveryCases. Уже иммем все необходимые данные, возвращаем их!"); $this->invoice->setDeliveryCases($deliveryData); return true; } // PayqrLog::log("setDeliveryCases. Первый запрос, сохраняем данные!"); $invoiceTable = new \frontend\models\InvoiceTable(); $invoiceTable->invoice_id = $this->invoiceId; $invoiceTable->data = ""; $invoiceTable->save(); $payqrDelivery = $this->invoice->getDelivery(); if (empty($payqrDelivery)) { PayqrLog::log("Возвращаем пустой массив"); return array(); } //проверяем xml на валидность libxml_use_internal_errors(true); //Получаем способы доставки через запрос к API InSales $payqrCurl = new PayqrCurl(); //вначале производим получение всех способов оплаты, которые присутствуют в системе $responsePayqmetsXML = $payqrCurl->sendXMLFile(PayqrConfig::$insalesURL . "payment_gateways.xml", "", "GET"); PayqrLog::log(print_r($responsePayqmetsXML, true)); $elem = simplexml_load_string($responsePayqmetsXML); if ($elem == false) { //Не смогли получить информацию о способах доставки PayqrLog::log("Не смогли получить информацию о способах доставки \r\n" . $responsePayqmetsXML); return array(); } //производм разбор xml $xml = new SimpleXMLElement($responsePayqmetsXML); $paymentsVariants1 = $xml->xpath("/objects/object"); $id_payqr_payment = 0; foreach ($paymentsVariants1 as $payment) { if (strpos(strtolower((string) $payment->title), "payqr") !== false) { $id_payqr_payment = (int) $payment->id; break; } } if (!$id_payqr_payment) { $paymentsVariants = $xml->xpath("/payment-gateway-customs/payment-gateway-custom"); foreach ($paymentsVariants as $payment) { if (strpos(strtolower((string) $payment->title), "payqr") !== false) { $id_payqr_payment = (int) $payment->id; break; } } } if (!$id_payqr_payment) { //не смогли получить платежную систему PayqrLog::log("Не смогли получить способы оплаты"); return array(); } PayqrLog::log("Получили id способы оплаты " . $id_payqr_payment); //получаем способы доставки $responsedeliveriesXML = $payqrCurl->sendXMLFile(PayqrConfig::$insalesURL . "delivery_variants.xml", "", "GET"); PayqrLog::log(print_r($responsedeliveriesXML, true)); $elem = simplexml_load_string($responsedeliveriesXML); if ($elem == false) { //Не смогли получить информацию о способах доставки PayqrLog::log("Не смогли получить информацию о способах доставки \r\n" . $responsedeliveriesXML); return array(); } //производм разбор xml $xml = new SimpleXMLElement($responsedeliveriesXML); //получаем OrderId-внешний идентификатор $deliveryVariants = $xml->xpath("/delivery-variant-fixeds/delivery-variant-fixed"); if (empty($deliveryVariants)) { //не смогли получить варианты доставок PayqrLog::log("Не смогли получить варианты доставок"); return false; } $i = 1; foreach ($deliveryVariants as $delivery) { // получаем $isIvertedCity = false; //PayqrLog::log(print_r($delivery, true)); $deliveryPayqments = $delivery->xpath("payment-delivery-variants/payment-delivery-variant"); //PayqrLog::log(print_r($deliveryPayqments, true)); if (empty($deliveryPayqments)) { continue; } if (isset($delivery->inverted) && $delivery->inverted == "true") { $isIvertedCity = true; } //PayqrLog::log("Нашли варианты оплаты для данной доставки"); foreach ($deliveryPayqments as $deliveryPayment) { if ((int) $deliveryPayment->{"payment-gateway-id"} == $id_payqr_payment) { //Проверяем теперь город, для которого разрешена доставка $deliveryLocations = $delivery->xpath("delivery-locations/delivery-location"); if (!empty($deliveryLocations)) { foreach ($deliveryLocations as $deliveryLocation) { if (isset($payqrDelivery->city) && !empty($payqrDelivery->city) && (!$isIvertedCity && strtolower((string) $deliveryLocation->city) == strtolower($payqrDelivery->city))) { $delivery_cases[] = array('article' => (int) $delivery->id, 'number' => $i++, 'name' => (string) $delivery->title, 'description' => strip_tags((string) $delivery->description), 'amountFrom' => round((double) $delivery->price, 2), 'amountTo' => round((double) $delivery->price, 2)); } if (isset($payqrDelivery->city) && !empty($payqrDelivery->city) && ($isIvertedCity && strtolower((string) $deliveryLocation->city) != strtolower($payqrDelivery->city))) { $delivery_cases[] = array('article' => (int) $delivery->id, 'number' => $i++, 'name' => (string) $delivery->title, 'description' => strip_tags((string) $delivery->description), 'amountFrom' => round((double) $delivery->price, 2), 'amountTo' => round((double) $delivery->price, 2)); } } } else { $delivery_cases[] = array('article' => (int) $delivery->id, 'number' => $i++, 'name' => (string) $delivery->title, 'description' => strip_tags((string) $delivery->description), 'amountFrom' => round((double) $delivery->price, 2), 'amountTo' => round((double) $delivery->price, 2)); } } } } //PayqrLog::log("Передаем варианты доставок"); //PayqrLog::log(print_r($delivery_cases, true)); \frontend\models\InvoiceTable::updateAll(['data' => json_encode($delivery_cases)], 'invoice_id = :invoice_id', [':invoice_id' => $this->invoiceId]); $this->invoice->setDeliveryCases($delivery_cases); }
/** * Проверяем ответ от PayQR на отправленный запрос в PayQR */ public function check_response($rawResponse, $method, $url, $vars) { $message = "Method: {$method}\nUrl: {$url}\nParams: " . var_export($vars, true) . "\nResponse: {$rawResponse}"; PayqrLog::log($message); if ($rawResponse) { $this->trim_headers($rawResponse); } else { $error = curl_errno($this->request) . ' - ' . curl_error($this->request); throw new PayqrExeption("Ошибка при запросе {$method}, {$url}, " . var_export($vars, true) . ", {$error}", 0, $rawResponse); } // Проверяем что ответ не пустой if (empty($this)) { throw new PayqrExeption("Получен пустой ответ", 0); } // Проверяем код ответа if (!isset($this->headers['Status-Code'])) { throw new PayqrExeption("Отсутствует заголовок с кодом ответа " . var_export($this, true), 0, $this); } // Проверяем код ответа if ($this->headers['Status-Code'] != '200') { throw new PayqrExeption("Получен ответ с кодом ошибки " . $this->headers['Status-Code'] . " " . var_export($this, true), 0, $this); } // Проверяем заголовок ответа if (!PayqrAuth::checkHeader(PayqrConfig::$secretKeyIn, $this->headers)) { throw new PayqrExeption("Неверный параметр ['PQRSecretKey'] в headers ответа" . var_export($this, true), 0, $this); } return $this; }
/** * Код в этом файле будет выполнен, когда интернет-сайт получит уведомление от PayQR о необходимости предоставить покупателю способы доставки конкретного заказа. * Это означает, что интернет-сайт на уровне кнопки PayQR активировал этап выбора способа доставки покупателем, и сейчас покупатель дошел до этого этапа. * * $this->invoice содержит объект "Счет на оплату" (подробнее об объекте "Счет на оплату" на https://payqr.ru/api/ecommerce#invoice_object) * * Ниже можно вызвать функции своей учетной системы, чтобы особым образом отреагировать на уведомление от PayQR о событии invoice.deliverycases.updating. * * Важно: на уведомление от PayQR о событии invoice.deliverycases.updating нельзя реагировать как на уведомление о создании заказа, так как иногда оно будет поступать не от покупателей, а от PayQR для тестирования доступности функционала у конкретного интернет-сайта, т.е. оно никак не связано с реальным формированием заказов. Также важно, что в ответ на invoice.deliverycases.updating интернет-сайт может передать в PayQR только содержимое параметра deliveryCases объекта "Счет на оплату". Передаваемый в PayQR от интернет-сайта список способов доставки может быть многоуровневым. * * Пример массива способов доставки: * $delivery_cases = array( * array( * 'article' => '2001', * 'number' => '1.1', * 'name' => 'DHL', * 'description' => '1-2 дня', * 'amountFrom' => '0', * 'amountTo' => '70', * ), * ..... * ); * $this->invoice->setDeliveryCases($delivery_cases); */ public function setDeliveryCases() { $total = $this->invoice->getAmount(); $rows = DB::query_fetch_all("SELECT sd.id, sd.name1, sd.text1, sdt.price \n FROM {shop_delivery} sd \n LEFT JOIN {shop_delivery_thresholds} sdt ON sd.id=sdt.delivery_id \n WHERE sd.act1='1' AND sd.trash='0' AND sdt.trash='0' AND sdt.amount < %f", $total); $delivery_cases = array(); PayqrLog::log(print_r($rows, true)); $i = 1; foreach ($rows as $row) { $delivery_cases[] = array('article' => $row['id'], 'number' => $i++, 'name' => $row['name1'], 'description' => $row['text1'], 'amountFrom' => $row['price'], 'amountTo' => $row['price']); } PayqrLog::log(print_r($delivery_cases, true)); $this->invoice->setDeliveryCases($delivery_cases); }