Для подключения скрипта PayQR к интернет-сайту скопируйте в секцию head
подключаемого интернет-сайта код скрипта вашего «Магазина» из личного кабинета. Найти его можно в разделе «Магазины» в настройках подключаемого «Магазина» на вкладке «Интеграция» в блоке «Код скрипта для интернет-сайта».
В коде скрипта будет присутствовать уникальный номер подключаемого «Магазина».
Этот скрипт должен присутствовать в head
каждой страницы интернет-сайта, где планируется размещение кнопки PayQR
Пример кода скрипта PayQR в коде интернет-сайта
<!DOCTYPE html>
<html>
<head>
<!-- Это пример кода скрипта PayQR. Код для вашего «Магазина» скопируйте из своего личного кабинета в PayQR -->
<script src="https://payqr.ru/popup.js?merchId=000000-00000"></script>
</head>
<body>
</body>
</html>
Маленький скрипт PayQR располагается на «облачных» серверах PayQR, что позволяет осуществлять доработку и самого скрипта PayQR, и popup-окон PayQR без необходимости переделывать подключение интернет-сайтов к PayQR, что очень удобно. Дополнительно этот подход позволяет снять нагрузку с серверов интернет-сайта, так как интернет-сайт в загрузке скрипта PayQR вообще не участвует. Также архитектура скрипта PayQR спроектирована так, чтобы не увеличивать время загрузки страниц интернет-сайта у покупателей – основные элементы скрипта начинают загружаться только тогда, когда весь базовый контент интернет-сайта уже загружен. На мобильных устройствах скрипт тоже ведет себя корректно, адаптируя свою логику под возможности конкретного мобильного устройства.
Кнопка PayQR представляет собой стандартную кнопку интернет-сайта, которая вызывает popup-окно PayQR для быстрого завершения покупки. Рекомендуемые дизайн и название кнопки PayQR позволяют а) узнавать ее тем, кто уже покупал что-то через PayQR, б) не идентифицировать ее однозначно с каким-то сторонним сервисом тем, кто еще не пользовался PayQR. И то, и то другое работает в интересах интернет-сайта на увеличение конверсии в успешные покупки, позволяя покупателям проходить этапы регистрации, оформления заказа и его оплаты с гораздо меньшим сопротивлением.
Разместите коды кнопок PayQR везде, где вы хотите предоставить возможность посетителям быстрее покупать товары/услуги.
В интернет-магазинах кнопка PayQR должна располагаться в корзине, на самом первом этапе совершения покупки – еще до того, как пользователь заполнил хотя бы одно поле в корзине или выбрал какую-нибудь опцию (как правило, на этих же страницах размещаются классические кнопки интернет-магазина «Оформить заказ» или «Зарегистрироваться»). Кнопка должна располагаться в отдельной заметной зоне, вне классических блоков с формами и кнопками, чтобы позиционироваться как альтернатива стандартному процессу совершения покупки. Наиболее эффективное расположение кнопки PayQR в корзине – под суммой «Итого к оплате» или над полями ввода данных покупателем (имя, фамилия, телефон, e-mail, адрес и так далее), если они присутствуют в корзине. Обязательно настраивайте размеры и дизайн кнопки PayQR так, чтобы он соответствовал общей стилистике интернет-сайта, но цвет кнопки должен отличаться от кнопок стандартного процесса оформления заказа («Оформить заказ», «Пересчитать», «Купить в 1 клик», «Купить в кредит», «Продолжить покупки» и тому подобное).
Дополнительно к корзине рекомендуется размещать кнопки PayQR на страницах товаров и в карточках товаров на главной странице интернет-сайта.
Атрибуты кнопок задают параметры совершения покупки. Интернет-сайт самостоятельно «заказывает» какие данные будут получены и сохранены в заказах через PayQR с помощью соответствующих атрибутов кнопок PayQR (data-firstname-required, data-phone-required и другие). Имейте в виду, чем меньше данных будет запрашиваться с покупателей, тем выше будет конверсия вашего интернет-сайта.
Для настройки атрибутов и внешнего вида кнопок PayQR воспользуйтесь специальным конструктором в своем личном кабинете в PayQR. Он расположен в разделе «Магазины» в настройках подключаемого «Магазина» на вкладке «Конструктор и опции кнопки».
Кнопки PayQR идентифицируются скриптом PayQR по классу payqr-button
, не удаляйте его для корректной работы сервиса.
Код кнопок PayQR выглядит примерно так:
Имейте в виду, что кнопки PayQR должны быть настроены таким образом, чтобы интернет-сайт смог в дальнейшем на основе полученных данных сформировать заказ так же, как если бы покупатель оформлял заказ в интернет-браузере. Например, если для создания заказа в учетной системе интернет-сайта требуется посчитать содержание заказа (корзины) по артикулам позиций в заказе (в корзине), то необходимо обязательно передавать артикулы всех позиций заказа в кнопки PayQR в предназначенные для этого атрибуты article
объекта data-cart
каждой позиции заказа.
После того, как вы определились с размещением кнопок и их содержанием, необходимо реализовать передачу в параметры кнопок соответствующих данных, в частности, содержаний заказов. Если вы используете рекомендованную PHP-библиотеку PayQR, то там предусмотрен файл, дополнительно упрощающий процесс генерации кнопок PayQR на страницах интернет-магазина. Это payqr_button.php
в папке classes
, а также payqr_product.php
из той же папки для формирования товарных позиций в кодах кнопок PayQR.
Не забудьте указать «URL для уведомлений» в личном кабинете PayQR и реализовать обработку уведомлений от PayQR после завершения работ над размещением кнопки PayQR. В противном случае совершение покупок через PayQR на вашем интернет-сайте работать не будет. Двигайтесь по нижеследующим разделам инструкции.
Примеры автоматического заполнения кнопок
Как динамически собирать данные о заказах и передавать их в кнопки PayQR зависит от учетной системы вашего интернет-сайта. Можно обратиться к базе данных (что рекомендуется), а можно с помощью JavaScript собрать данные на стороне клиента.
Пример как мы делали генерацию кнопок PayQR:
<button class="payqr-button payqr-btn-template"
data-scenario="<?=$arParams['TYPE']?>"
<?if (!empty($arResult['PQR_ORDER_GROUP'])):?> data-orderGroup='<?=$arResult['PQR_ORDER_GROUP']?>'<?endif;?>
<?if (!empty($arResult['ORDER_ID'])):?> data-orderId='<?=$arResult['ORDER_ID']?>'<?endif;?>
<?if ($arParams['FIRSTNAME_REQUIRED'] == 'Y'): ?> data-firstname-required="required"<?endif;?>
<?if ($arParams['LASTNAME_REQUIRED'] == 'Y'): ?> data-lastname-required="required"<?endif;?>
<?if ($arParams['PHONE_REQUIRED'] == 'Y'): ?> data-phone-required="required"<?endif;?>
<?if ($arParams['EMAIL_REQUIRED'] == 'Y'): ?> data-email-required="required"<?endif;?>
<?if ($arParams['DELIVERY_REQUIRED'] == 'Y'): ?> data-delivery-required="required"<?endif;?>
<?if (!empty($arResult['PQR_CART'])):?> data-cart='<?=$arResult['PQR_CART']?>'<?endif;?>
<?if (!empty($arResult['PQR_AMOUNT'])):?> data-amount="<?=$arResult['PQR_AMOUNT']?>"<?endif;?>
><?if ($arParams['TYPE'] == 'buy') echo GetMessage('BTN_BUY'); else echo GetMessage('BTN_PAY'); ?></button>
Практически все атрибуты кнопки PayQR являются необязательными, поэтому вы можете передавать в кнопке только те атрибуты, которые актуальны для того или иного расположения кнопки PayQR. Значения отсутствующих атрибутов будут восприниматься как default
. Кнопок на странице может быть много, но логику заполнения атрибутов в кодах кнопок нужно реализовать всего один раз, чтобы она автоматически распространялась на все кнопки PayQR.
Определите расположение обработчика уведомлений PayQR на хостинге своего интернет-сайта, куда PayQR будет направлять уведомления обо всех событиях. И укажите URL-адрес на этот файл в личном кабинете в разделе «Магазины» в настройках подключаемого «Магазина» на вкладке «Интеграция» в поле «URL для уведомлений». Адрес может быть указан с протоколом http:// или https://.
Пожалуйста, не путайте URL для уведомлений с URL страницы успешной оплаты (это понятие в рамках PayQR вообще не используется, так как покупка совершается не в интернете, а в мобильном приложении). URL для уведомлений — это ссылка на обработчик уведомлений от PayQR, который использует интернет-сайт. Сюда поступают уведомления о тех или иных событиях PayQR, которые описаны в следующем разделе инструкции. В этом обработчике интернет-сайт должен принимать уведомления, интерпретировать их и вызывать на своей стороне соответствующую логику, чтобы корректно отвечать PayQR на полученные уведомления.
Вы можете собрать обработчик уведомлений PayQR самостоятельно, но мы рекомендуем воспользоваться готовой PHP-библиотекой PayQR. Архив с этой библиотекой можно скачать в разделе готовых решений. В составе PHP-библиотеки файлом, который должен принимать уведомления от PayQR, является payqr_receiver.php
из корня библиотеки – именно ссылку на этот файл на вашем интернет-сайте нужно указывать в личном кабинете PayQR в поле «URL для уведомлений». Также библиотека PayQR содержит инструменты для формирования ответов на уведомления от PayQR и много полезных примеров работы с API PayQR.
Для того, чтобы библиотека начала взаимодействовать с PayQR после распаковки архива библиотеки и размещения папки библиотеки на вашем интернет-сайте вам останется только указать данные «Магазина» из своего личного кабинета в PayQR в файле payqr_config.php
. К таким данным «Магазина» относятся номер «Магазина» и секретные ключи.
Обязательно: разместите обработчик уведомлений PayQR на стороне интернет-сайта и укажите ссылку на него в поле «URL для уведомлений» в своем личном кабинете в PayQR, иначе совершение покупок через PayQR на вашем интернет-сайте будет невозможным.
Если вы решите собирать обработчик уведомлений PayQR самостоятельно или ваш интернет-сайт написан не на PHP, ориентируйтесь на примеры написания обработчика, представленные ниже.
Чтобы однозначно отличать уведомления PayQR от других, в заголовке каждого уведомления будет присутствовать код, известный только PayQR и подключаемому интернет-сайту. Содержание этого кода вы можете получить в личном кабинете в разделе «Магазины» в настройках подключаемого «Магазина» на вкладке «Интеграция» из поля SecretKeyIn.
Аналогично PayQR отличает уведомления интернет-сайта от других по наличию подобного кода в ответах интернет-сайта, который известен только PayQR и подключаемому интернет-сайту. Содержание этого кода вы можете получить в личном кабинете в разделе «Магазины» в настройках подключаемого «Магазина» на вкладке «Интеграция» из поля SecretKeyOut.
Примеры проверки header и заполнения header
Один из вариантов проверки в header определенного значения SecretKeyIn
:
<?php
// Вместо XXX должен быть входящий ключ SecretKeyIn этого «Магазина», указанный в личном кабинете в PayQR
$secretKeyIn = "XXX";
// Проверить, что запрос действительно от сервера PayQR по входящему ключу
if (getallheaders()["PQRSecretKey"] == $secretKeyIn) {
}
?>
# .config.secretKeyIn
# Значение берется для текущего environments, проверка не будет работать если сервер запущен в production, а значения указаны в development
if request.headers["PQRSecretKey"] == CONFIG["secretKeyIn"]
# Условие выполняется
else
# Не выполняется
end
import httplib
host = 'example.com'
connection = httplib.HTTPConnection(host)
connection.request("GET", "")
HTTPResponse = connection.getresponse()
###
# Вместо XXX должен быть входящий ключ SecretKeyIn этого «Магазина», указанный в личном кабинете в PayQR
secretKeyIn = "XXX"
# Проверить, что запрос от сервера PayQR
if HTTPResponse.getheader("PQRSecretKey") == secretKeyIn:
pass
Один из вариантов заполнения header в ответах определенным значением SecretKeyOut
:
<?php
// Вместо YYY должен быть исходящий ключ SecretKeyOut этого «Магазина», указанный в личном кабинете в PayQR
$secretKeyOut = "YYY";
// Подписать ответ исходящим ключом для подтверждения, что ответ действительно от «Магазина»
header("PQRSecretKey:" . $secretKeyOut);
?>
# Задать заголовок сервера для всего сайта или для определенного контроллера:
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
after_filter :payqrheaders
private
def payqrheaders
response.headers["PQRSecretKey"]= CONFIG["secretKeyOut"]
end
end
# Или добавить header в определеннный экшн-контроллер:
def show
@product = Product.find(params[:id])
response.headers["PQRSecretKeyOut-only-for-specefic-controller"]= CONFIG["secretKeyOut"]
end
import httplib
host = 'example.com'
conn = httplib.HTTPConnection(host)
###
# Вместо YYY должен быть исходящий ключ SecretKeyOut этого «Магазина», указанный в личном кабинете в PayQR
secretKeyOut = "YYY"
headers = {"PQRSecretKey": secretKeyOut}
conn.request("GET", "", headers=headers)
Каждое уведомление о событии PayQR имеет определенную структуру. В атрибуте type
объекта события присутствует однозначное определение того или иного события в PayQR, о котором сейчас уведомляется интернет-сайт (например, invoice.order.creating
). Обрабатывайте значения атрибута type
, чтобы правильно реагировать на те или иные события.
Примеры определения типа события PayQR
Один из вариантов выделения события PayQR по значению type:
<?php
// Получаем уведомление о событии из тела пришедшего POST-запроса от PayQR
$event = json_decode($HTTP_RAW_POST_DATA);
// Проверяем тип события на соответствие типу события для создания заказа
if (isset($event->type) && $event->type == "invoice.order.creating") {
// Успех!
}
?>
# Проверяем тип события на соответствие типу события для создания заказа
def postfrompayqr
@payqrpostrequest = JSON.parse(request.raw_post)
respond_to do |format|
if @payqrpostrequest.has_key?('type') && @payqrpostrequest['type'] == 'invoice.order.creating'
format.json { render json: @payqrpostrequest, status: :created, location: @payqrpostrequest }
else
format.any { redirect_to :action => "new"}
end
end
# Посылаем POST:
curl -XPOST 'http://yourdomain:port/products/postfrompayqr' --data '{"foo":"bar","type":"invoice.order.creating","abcdefghijklmnop":"qrstuvwxyz"}'
# содержащий "type":"invoice.order.creating" в ответ рендерится посланный JSON (условие выполняется)
# А если посылаем POST:
curl -XPOST 'http://yourdomain:port/products/postfrompayqr' --data '{"foo":"bar","blah":"wat","abcdefghijklmnop":"qrstuvwxyz"}'
# не содержащий нужного параметра, проверка не выполняется и происходит редирект.
import json
###
# request.POST - достаточно условная запись, конкретный способ получения содержимого POST будет зависеть от способа запуска приложения и фреймворка
event = json.loads(request.POST)
# Проверяем тип события на соответствие типу события для создания заказа
if "type" in event and event["type"] == "invoice.order.creating":
pass
Структура объекта «Событие» (event) в уведомлении PayQR, но может прийти больше параметров:
{
"id": "evt_14EccV2eZvKYlo2C2j1B18pF", // Уникальный идентификатор события, создаваемый PayQR
"object": "event", // Имя объекта, в данном случае «Событие»
"created": "2015-08-09T18:31:42.201+04:00", // Дата генерации события
"type": "invoice.order.creating", // Тип события
"data": {
... // Объект, по которому произошло событие
}
}
Для обработки уведомлений PayQR интернет-сайту необходимо разбирать (десериализовывать) и собирать (сериализовывать) классы объектов, содержащихся в уведомлениях PayQR. В перечисленных в этой инструкции уведомлениях о событиях PayQR содержатся только объекты вида invoice
(условно называются в PayQR «Счет на оплату»). Все объекты в PayQR используются в формате JSON.
Примеры десериализации уведомлений и сериализации ответов
Один из вариантов десериализации объекта «Событие» (event) с вложенным объектом «Счет на оплату» (invoice) – для принятия уведомлений PayQR:
<?php
// Получаем уведомление о событии из тела пришедшего POST-запроса от PayQR
$event = json_decode($HTTP_RAW_POST_DATA);
if (isset($event->data) && isset($event->data->object) && $event->data->object == "invoice") {
// Объект «Счет на оплату» (invoice)
$invoice = $event->data;
}
?>
def postfrompayqr
@payqrpostrequest = JSON.parse(request.raw_post)
respond_to do |format|
if @payqrpostrequest.has_key ? ('data') && @payqrpostrequest.has_key ? ('data').include ? ('object') && @payqrpostrequest['data', 'object'] == 'invoice'
@invoice = @payqrpostrequest['data']
end
end
import json
###
# request.POST - достаточно условная запись, конкретный способ получения содержимого POST будет зависеть от способа запуска приложения и фреймворка
# Получаем уведомление о событии из тела пришедшего POST-запроса от PayQR
event = json.loads(request.POST)
if "data" in event and "object" in event["data"] and event["data"]["object"] == "invoice":
# Объект «Счет на оплату» (invoice)
invoice = event["data"]
Один из вариантов сериализации ответов на уведомление PayQR – объект «Событие» (event) с вложенным объектом «Счет на оплату» (invoice):
<?php
// Получаем уведомление о событии из тела пришедшего POST-запроса от PayQR
$event = json_decode($HTTP_RAW_POST_DATA);
var_dump(json_encode(array(
"id" => $event->id,
"data" => array(
"orderId" => "00000000" /* В данном случае в качестве сериализуемого ответа интернет-сайт направил параметр orderId со значением 00000000(и, тем самым, либо дополнил, либо обновил этот параметр в объекте «Счет на оплату») */
))));
?>
def postfrompayqr
@payqrpostrequest = JSON.parse(request.raw_post)
serialize = Array({
:id => "#{@payqrpostrequest['id']}",
:data => Array({
:order_id => "0000000"})
}).to_json
puts YAML::dump(serialize)
end
import json
###
# request.POST - достаточно условная запись, конкретный способ получения содержимого POST будет зависеть от способа запуска приложения и фреймворка
event = json.loads(request.POST)
# Получаем уведомление о событии из тела пришедшего POST-запроса от PayQR
json.dumps({
"id": event["id"],
"data": {
"orderId": "00000000"
}
})
Структура объекта «Счет на оплату» (invoice), но может прийти больше параметров:
{
"id": "inv_14EeCA2eZvKYlo2C8nDrcXdp", // Номер счета в PayQR(номера счетов создаются PayQR)
"object": "invoice", // Имя объекта, в данном случае «Счет на оплату»
"livemode": false, // Режим тестирования с эмуляцией платежей (false) или реальные платежи (true)
"created": "2015-08-09T18:31:42.201+04:00", // Дата и время создания объекта по московскому времени
"modified": "2015-08-09T18:33:42.201+04:00", // Дата и время изменения объекта по московскому времени
"payqrNumber": "3213326680056410", // Уникальный номер конкретного объекта «Счет на оплату», отличается от id тем, что id является технической сущностью, а с данным номером работают покупатели (номера счетов создаются PayQR)
"payqrUserId": "000000000000", // Уникальный номер абонента в PayQR, который совершает операции по этому объекту «Счет на оплату», т.е. оформляет заказ и совершает оплату (номера абонентов создаются PayQR)
"orderGroup": "3241.8954", // Какие-то дополнительные идентификаторы конкретного интернет-сайта, которые понадобятся интернет-сайту на стадии оформления заказа или оплаты заказа
"amount": 5000.00, // Сумма заказа
"cart":[ // Содержание заказа в PayQR
{
"article": "5675657", // Артикул товара/услуги в учетной системе интернет-сайта
"name": "Товар 1", // Название товара/услуги
"imageUrl": "http://goods.ru/item1.jpg", // Абсолютная ссылка на изображение товара/услуги
"quantity": 5, // Количество товара/услуги
"amount": 5000.00 // Стоимость товара/услуги с учетом количества
},
{
"article": "0", // Артикул товара/услуги в учетной системе интернет-сайта
"name": "PROMO акция", // Название товара/услуги
"imageUrl": "http://goods.ru/promo.jpg" // Абсолютная ссылка на изображение товара/услуги
}
],
"customer": { // Данные о покупателе, который передал сам покупатель через PayQR (набор запрашиваемых данных интернет-сайт определил на уровне кнопки PayQR)
"firstName": "Иван", // Имя покупателя
"lastName": "Иванов", // Фамилия покупателя
"phone": "+79111111111", // Номер телефона покупателя
"email": "test@user.com" // E-mail покупателя
},
"delivery": { // Адрес доставки покупки покупателю, который передал сам покупатель через PayQR (интернет-сайт запросил получение адреса доставки покупателя на уровне кнопки PayQR)
"country": "Россия", // Страна адреса доставки
"region": "Москва", // Регион адреса доставки
"city": "Москва", // Населенный пункт адреса доставки
"zip": "115093", // Индекс адреса доставки
"street": "Дубининская ул.", // Улица адреса доставки
"house": "80", // Дом адреса доставки
"comment": "У входа в автосалон Хонда" // Комментарий к адресу доставки
},
"validityInMinutes": 129600, // Срок действия счета, в течение которого покупатель может оплатить счет после его получения в PayQR, в минутах, не более 259200 минут (с момента подтверждения счета в PayQR покупателем до его оплаты)
"confirmWaitingInMinutes": 4320, // Срок автоматического подтверждения оплаты по счету, по истечении которого возможные возвраты от интернет-сайта будут осуществляться не на первоначальное средство платежа покупателя, а на его остаток в PayQR, в минутах, не более 4320 минут (с момента технического осуществления операций списания до момента запуска расчетов с интернет-сайтом)
"promo": "TRADE", // Промо-идентификатор, который передал сам покупатель через PayQR, может быть промо-кодом или номером карты лояльности (интернет-сайт предусмотрел возможность указать покупателю промо-идентификатор на уровне кнопки PayQR)
"status": "new" // Текущий статус объекта «Счет на оплату», в данном случае «Создан»
}
Объекты «Счет на оплату» содержат в себе большое количество различных данных, которые могут учитываться или не учитываться интернет-сайтом. Интернет-сайт самостоятельно выбирает значимые для него данные для реализации необходимой бизнес-логики интернет-сайтом. В уведомлениях интернет-сайта от PayQR объект «Счет на оплату» всегда приходит целиком, но в ответах интернет-сайт может использовать как объект «Счет на оплату» целиком, так и только ту часть объекта, по которой интернет-сайт направляет дополнения или которую интернет-сайт желает изменить.
PayQR для интернет-сайта является всего лишь интерфейсом получения информации от покупателя и передачи информации покупателю. По сути, PayQR выступает аналогом обычного интернет-браузера:
1. Раньше после наполнения корзины покупатели регистрировались на интернет-сайте и заполняли данные о себе в браузере. И туда же в браузер интернет-сайт возвращал результат обработки данных покупателя и его корзины, а именно номер заказа и сумму к оплате. Сейчас эту же информацию от покупателя интернет-сайт может получать еще и через PayQR, и в PayQR же интернет-сайт потом возвращает номер заказа и сумму к оплате. Все бизнес-процессы интернет-сайта остаются без изменений – он все так же создает заказ на основе данных покупателя и содержания корзины, осуществляет какие-то свои проверки и процедуры в процессе создания заказа, в том числе пересчитывает стоимость заказа, и так далее. И только после создания заказа и передачи его параметров в PayQR покупатель приступает к оплате.
2. По самой оплате аналогичная параллель – раньше покупатель выбирал способ оплаты и оплачивал покупки в браузере, сейчас за него эту процедуру может осуществить PayQR. Интернет-сайт точно так же, как из браузера, получает в свою учетную систему информацию об оплате, чтобы изменить статус конкретного заказа на «Оплачен» (или любой другой для такого случая).
PayQR необходимо взаимодействовать с интернет-сайтом на определенных этапах осуществления покупки покупателем. Такое взаимодействие реализуется посредством направления интернет-сайту обычных уведомлений (http/https-запросов типа POST) от PayQR и ответов интернет-сайта на такие уведомления. Уведомления отправляются на адрес, указанный в поле «URL для уведомлений» в личном кабинете PayQR (описано в предыдущем разделе инструкции).
Основными двумя этапами, по которым PayQR направляет уведомления, являются события:
invoice.order.creating
(интернет-сайту требуется создать заказ)invoice.paid
(интернет-сайту требуется пометить созданный заказ как оплаченный)Как реагировать на уведомления об этих событиях описано далее.
Ответы на уведомления PayQR должны носить чисто технический характер и формироваться без какой-либо сложной логики на стороне интернет-сайта, иначе в PayQR может сработать таймаут. Уведомления PayQR должны обрабатываться так же быстро, как стандартные аналогичные действия покупателя в интернет-браузере (формирование нового заказа).
Событие invoice.order.creating
означает, что PayQR собрал все данные с покупателя, которые запрашивал интернет-сайт через параметры кнопки PayQR, и теперь интернет-сайту нужно сформировать заказ на основе полученных данных от PayQR.
После получения уведомления о событии invoice.order.creating
интернет-сайту требуется создать заказ на своей стороне по стандартному механизму, то есть выполнить все необходимые проверки в рамках процедуры создания заказа и другие возможные процессы. Если учетная система интернет-сайта функционирует так, что заказ создается уже на уровне генерации кнопки PayQR, то после получения invoice.order.creating
можно просто актуализировать его на основе данных, пришедших от PayQR.
Вместе с уведомлением invoice.order.creating
будут переданы а) все данные, которые интернет-сайт сообщил в кнопке PayQR, б) все данные, которые покупатель сообщил о себе интернет-сайту в соответствии с требованиями, сформированными интернет-сайтом на уровне кода кнопки PayQR.
Важно, что если на уровне кнопки интернет-сайт не передавал номер заказа (data-orderid
), потому что заказа еще не было, а была только корзина с каким-то содержимым, то в ответе на уведомление о событии invoice.order.creating
номер заказа должен присутствовать обязательно (в атрибуте orderId
объекта «Счет на оплату»). По правилам PayQR покупатель не может приступить к оплате заказа, у которого неизвестен номер.
Если вы используете рекомендованную PHP-библиотеку PayQR, то там уже предусмотрен файл, который запускается при получении уведомления о событии invoice.order.creating
. Это invoice.order.creating.php
в папке handlers
. Также в библиотеке предусмотрены некоторые методы по объекту «Счет на оплату», содержащемуся в уведомлении invoice.order.creating
, которые могут существенно облегчить написания своего кода реакции на это событие. Они описаны в файле payqr_invoice.php
в папке classes
.
Обязательно: в ответе на уведомление о событии invoice.order.creating
от PayQR возвращайте номер заказа в вашей учетной системе в параметре orderId
объекта PayQR «Счет на оплату», если на этапе формирование кнопки PayQR номер заказа был еще неизвестен, иначе совершение покупок через PayQR на вашем интернет-сайте будет невозможным.
Ответ на уведомление о событии invoice.order.creating
– это первая и последняя возможность интернет-сайта изменить какие-либо данные в объекте «Счет на оплату» в процессе покупки (увеличить или уменьшить сумму, добавить позиции в содержание заказа и тому подобное). Дальше начнется процесс оплаты и все изменения будут игнорироваться сервером PayQR.
Примеры обработки уведомления для создания заказа
Как формировать заказы в учетной системе вашего интернет-сайта зависит от этой учетной системы (обратитесь к документации учетной системы, если затрудняетесь дифференцировать этот функционал). Можно создать сущность заказа в базе данных учетной системы и поочередно заполнять ее значениями, а можно сразу вызвать штатную команду создания заказа со всеми известными значениями. Фактически это всегда имитация заполнения всех необходимых полей покупателем на самом интернет-сайте (как если бы покупатель совершал покупку старым способом в интернет-браузере) и нажатия покупателем кнопки «Оформить заказ» (или другой похожей). Если бизнес-логикой вашей учетной системы предусмотрена обязательная регистрация покупателей перед формированием заказов, то на основе запрошенных в кнопке PayQR и полученных сейчас данных покупателя запустите такую регистрацию перед обработкой формирования заказа.
Пример уведомления о событии, направленное интернет-сайту:
{
"id": "evt_14EccV2eZvKYlo2C2j1B18pF",
"object": "event",
"created": "2015-08-09T18:31:42.201+04:00",
"type": "invoice.order.creating",
"data": {
"id": "inv_14EeCA2eZvKYlo2C8nDrcXdp",
"object": "invoice",
"livemode": false,
"created": "2015-08-09T18:31:42.201+04:00",
"modified": "2015-08-09T18:33:42.201+04:00",
"payqrNumber": "3213326680056410",
"payqrUserId": "000000000000",
"orderGroup": "123.456",
"amount": 5000.00,
"cart": [
{
"article": "5675657",
"name": "Товар 1",
"imageUrl": "http://goods.ru/item1.jpg",
"quantity": 5,
"amount": 5000.00
}
],
"customer": {
"firstName": "Иван",
"lastName": "Иванов",
"phone": "+79111111111",
"email": "test@user.com"
},
"delivery": {
"country": "Россия",
"region": "Москва",
"city": "Москва",
"zip": "115093",
"street": "Партийный пер.",
"house": "1",
"unit": "46",
"hallway": "2",
"floor": "4",
"intercom": "#1234",
"comment": "Сказать охране, что в PayQR"
},
"validityInMinutes": 129600,
"confirmWaitingInMinutes": 4320,
"status": "new"
}
}
Пример ответа на уведомление о событии от интернет-сайта:
{
"id": "evt_14EccV2eZvKYlo2C2j1B18pF",
"data": {
"orderId": "0000000000",
"amount": 5000.00
}
}
Пример как мы делали оплату заказа:
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true)
die();
$raw_data = file_get_contents("php://input");
//кастомная функция логирования, для отладки
payQrLogToFile($raw_data);
$event = json_decode($raw_data);
$secretKeyIn = $arParams['SECRET_KEY_IN']; // Входящий ключ SecretKeyIn этого «Магазина», указанный в личном кабинете
$secretKeyOut = $arParams['SECRET_KEY_OUT']; // Исходящий ключ SecretKeyOut этого «Магазина», указанный в личном кабинете
header("PQRSecretKey:" . $secretKeyOut); // Подписать ответ исходящим ключом для подтверждения, что ответ действительно от «Магазина»
if ($event->type == "invoice.order.creating" && CModule::IncludeModule("iblock") && CModule::IncludeModule("sale") && CModule::IncludeModule("catalog")) /* Проверяем тип события на соответствие типу события для создания заказа и задействуем нужные компоненты*/ {
$invoice = $event->data; // Объект «Счет на оплату» (invoice)
$orderGroupParts = explode(".", $invoice->orderGroup);
/* Получаем пришедшие идентификаторы учетной системы ИД сайта ИД пользователя на сайте и ИД пользователя в учетной системе (это редкая особенность «1С-Битрикс», часто в учетных системах понятия ИД сайта и ИД пользователя отсутствуют, а в формировании участвуют только артикулы позиций в заказе) */
$siteId = $orderGroupParts[0]; // ИД сайта (был сохранен на уровне кнопки PayQR в data-orderGroup первым параметром)
$fuserId = $orderGroupParts[1]; // ИД пользователя на сайте (был сохранен на уровне кнопки PayQR в data-orderGroup вторым параметром)
$userId = $orderGroupParts[2]; // ИД пользователя в учетной системе (был сохранен на уровне кнопки PayQR в data-orderGroup третьим параметром)
if (empty($userId))
$userId = $arParams['DEFAULT_USER_ID'];
$arFieldsOrder = array();
$arFieldsOrder[$arParams['FIELD_FIRSTNAME_ID']] .= ', ' . $invoice->customer->firstName;
$arFieldsOrder[$arParams['FIELD_LASTNAME_ID']] .= ', ' . $invoice->customer->lastName;
$arFieldsOrder[$arParams['FIELD_PHONE_ID']] .= ', ' . $invoice->customer->phone;
$arFieldsOrder[$arParams['FIELD_EMAIL_ID']] .= ', ' . $invoice->customer->email;
$arFieldsOrder[$arParams['FIELD_COUNTRY_ID']] .= ', ' . $invoice->delivery->country;
$arFieldsOrder[$arParams['FIELD_CITY_ID']] .= ', ' . $invoice->delivery->city;
$arFieldsOrder[$arParams['FIELD_ZIP_ID']] .= ', ' . $invoice->delivery->zip;
if ($arParams['FIELD_ADDRESS_ID'] > 0):
$arFieldsOrder[$arParams['FIELD_ADDRESS_ID']] .= ', ';
if (!empty($invoice->delivery->street))
$arAddress[] = 'ул. ' . $invoice->delivery->street;
if (!empty($invoice->delivery->house))
$arAddress[] = 'д. ' . $invoice->delivery->house;
if (!empty($invoice->delivery->unit))
$arAddress[] = 'корп.. ' . $invoice->delivery->unit;
if (!empty($invoice->delivery->building))
$arAddress[] = 'стр. ' . $invoice->delivery->building;
if (!empty($invoice->delivery->flat))
$arAddress[] = 'кв. ' . $invoice->delivery->flat;
if (!empty($invoice->delivery->hallway))
$arAddress[] = 'подъезд ' . $invoice->delivery->hallway;
if (!empty($invoice->delivery->floor))
$arAddress[] = 'этаж ' . $invoice->delivery->floor;
if (!empty($invoice->delivery->intercom))
$arAddress[] = 'домофон ' . $invoice->delivery->intercom;
$arFieldsOrder[$arParams['FIELD_ADDRESS_ID']] .= implode(', ', $arAddress);
endif;
//$arFieldsOrder[$arParams['FIELD_PAYQR_ID']] = $invoice->payqrNumber;
$commentOrder = $invoice->delivery->comment;
AddMessage2Log('FUSERID: ' . $fuserId . ' | USERID: ' . $userId . ' | ');
$dbBasketItems = CSaleBasket::GetList( // Получаем корзину этого пользователя из интернет-сайта
array(
"NAME" => "ASC",
"ID" => "ASC"
), array(
"FUSER_ID" => $fuserId,
"LID" => SITE_ID,
"ORDER_ID" => "NULL"
), false, false, array(
"ID",
"PRODUCT_ID",
"PRODUCT_PRICE_ID",
"NAME",
"QUANTITY",
"CAN_BUY",
"PRICE"
));
$arBasketItems = array();
while ($arBasketItem = $dbBasketItems->Fetch()) /* На всякий случай, приводим состояние корзины на интернет-сайте в соответствие с содержанием заказа в PayQR (заказ, который начал оформляться в PayQR, приоритетнее) */
$arBasketItems[] = $arBasketItem;
$removingPositionKeys = array();
/* Массив удаляемых из корзины PayQR элементов, которые не удалось поместить в корзину магазина (нет такого товара или не удалось добавить к корзину) */
$invoice->amount = 0; // Очищаем итоговую сумму счета для пересчета по текущим ценам интернет-сайта, так как они могли измениться
payQrLogToFile("\\narBasketItems: " . var_export($arBasketItems, true));
payQrLogToFile("\\ninvoice->cart: " . var_export($invoice->cart, true));
foreach ($invoice->cart as $positionKey => $position) // Проверяем корзину PayQR и обновляем корзину магазина
{
$arPrice = CPrice::GetByID(intval($position->article));
$arDiscounts = CCatalogDiscount::GetDiscountByPrice($arPrice["ID"], $USER->GetUserGroupArray(), "N", SITE_ID);
$discountPrice = CCatalogProduct::CountPriceWithDiscount($arPrice["PRICE"], $arPrice["CURRENCY"], $arDiscounts);
$arPrice["DISCOUNT_PRICE"] = $discountPrice;
payQrLogToFile("\\narPrice: " . var_export($arPrice, true));
if ($arProduct = CIBlockElement::GetByID(intval($arPrice["PRODUCT_ID"]))->Fetch()) {
$position->amount = $arPrice["DISCOUNT_PRICE"] * $position->quantity; // Актуализируем сумму по позиции
$position->name = $arProduct["NAME"]; // Актуализируем название позиции
$removingIndex = -1;
foreach ($arBasketItems as $index => $arBasketItem) /* Проверяем текущую корзину на интернет-сайте на соответствие заказу в приложении */
if ($arBasketItem["PRODUCT_PRICE_ID"] == $position->article) {
if ($arBasketItem["QUANTITY"] != $position->quantity) // Проверяем совпадение количества
CSaleBasket::Update($arBasketItem["ID"], array(
"QUANTITY" => $position->quantity
));
/* Приводим в соответствие с тем, что в приложении, если есть расхождения */
$removingIndex = $index;
break;
}
if ($removingIndex >= 0)
unset($arBasketItems[$removingIndex]);
/* Удаляем элементы из списка, которые успешно обработали, за ненадобностью */
else if (!CSaleBasket::Add(array(
/* Заполняем корзину в учетной системе для создания заказа (это редкая особенность «1С-Битрикс», часто в учетных системах можно сразу создать заказ, а потом заполнить его содержимое, без необходимости эмуляции предварительного создания корзины) */
"PRODUCT_ID" => $arPrice["PRODUCT_ID"],
"PRODUCT_PRICE_ID" => $arPrice["ID"],
"PRICE" => $arPrice["PRICE"],
"CURRENCY" => $arParams['CURRENCY'],
"QUANTITY" => $position->quantity,
"LID" => $siteId,
"FUSER_ID" => $fuserId,
"DELAY" => "N",
"CAN_BUY" => "Y",
"NAME" => $arProduct["NAME"],
"CALLBACK_FUNC" => "",
"MODULE" => "sale",
"NOTES" => "",
"ORDER_CALLBACK_FUNC" => "",
"DETAIL_PAGE_URL" => "",
"CANCEL_CALLBACK_FUNC" => "",
"PAY_CALLBACK_FUNC" => ""
)))
$removingPositionKeys[] = $positionKey;
/* Удаляем позицию из заказа в приложении, если учетная система отказала в добавлении этой позиции в корзину по любым причинам */
$invoice->amount += $position->amount; // Пересчитываем итоговую сумму заказа на основе данных учетной системы
} else
$removingPositionKeys[] = $positionKey;
/* Удаляем позицию из заказа в приложении, если указанный товар не был найден в учетной системе (например, закончился на складе) */
}
foreach ($arBasketItems as $arBasketItem) /* Удаляем из корзины на интернет-сайте те товары, которые отсутствуют в заказе в приложении PayQR */
CSaleBasket::Delete($arBasketItem["ID"]);
foreach ($removingPositionKeys as $positionKey) /* Удаляем из корзины на интернет-сайте те товары, которые отсутствуют в учетной системе интернет-сайта */
unset($invoice->cart[$positionKey]);
payQrLogToFile("\\narBasketItems: " . var_export($arBasketItems, true));
payQrLogToFile("\\ninvoice->amount: " . var_export($invoice->amount, true));
$dbBasketItems = CSaleBasket::GetList( // Получаем корзину этого пользователя из интернет-сайта
array(
"NAME" => "ASC",
"ID" => "ASC"
), array(
"FUSER_ID" => $fuserId,
"LID" => SITE_ID,
"ORDER_ID" => "NULL"
), false, false, array(
"ID",
"PRODUCT_ID",
"PRODUCT_PRICE_ID",
"NAME",
"QUANTITY",
"CAN_BUY",
"PRICE"
));
$arBasketItems = array();
while ($arBasketItem = $dbBasketItems->Fetch()) /* На всякий случай, приводим состояние корзины на интернет-сайте в соответствие с содержанием заказа в PayQR (заказ, который начал оформляться в PayQR, приоритетнее) */
$arBasketItems[] = $arBasketItem;
payQrLogToFile("\\narBasketItems: " . var_export($arBasketItems, true));
//exit;
if (count($invoice->cart) > 0) /* Создаем заказ в учетной системе интернет-сайта, если оформляемая корзина после всех проверок осталась непустой, иначе не создаем заказ */ {
$invoice->orderId = CSaleOrder::Add(array( // Добавляем заказ в учетную систему
"LID" => $siteId,
"PERSON_TYPE_ID" => 1,
"PAYED" => "N",
"CANCELED" => "N",
"STATUS_ID" => "N",
"PRICE" => $invoice->amount,
"CURRENCY" => $arParams['CURRENCY'],
"USER_ID" => intval($userId),
"USER_DESCRIPTION" => $commentOrder
));
if ($invoice->orderId == false)
AddMessage2Log('Ошибка добавления заказа');
if (count($arFieldsOrder) > 0) {
foreach ($arFieldsOrder as $id => $value) {
$value = substr($value, 2);
if ($id > 0 && !empty($value) && $arOrderProps = CSaleOrderProps::GetByID($id)) {
CSaleOrderPropsValue::Add(array(
"ORDER_ID" => $invoice->orderId,
"ORDER_PROPS_ID" => $id,
"NAME" => $arOrderProps['NAME'],
"CODE" => $arOrderProps['CODE'],
"VALUE" => $value
));
}
}
}
CSaleBasket::OrderBasket($invoice->orderId, $fuserId, $siteId);
/* Связываем только что созданный заказ с текущей корзиной на интернет-сайте, чтобы корзина на интернет-сайте не осталась «брошенной» */
}
//------- Формирование заказа завершено в 1C-Битрикс -------
$raw_data = json_encode($event);
payQrLogToFile($raw_data);
echo $raw_data;
} else if ($event->type == "invoice.paid" && CModule::IncludeModule("sale")) /* Проверяем тип события на соответствие типу события для оплаты заказа и задействуем нужные компоненты этой учетной системы */ {
/* логика оплаты заказа */
} else
echo json_encode(array(
"id" => $event->id
));
?>
<?php
function commerce_payqr_reciver() {
payqr_config::$enabledLog = FALSE;
payqr_config::$merchantID = variable_get('commerce_payqr_merchant_id', '');
payqr_config::$secretKeyIn = variable_get('commerce_payqr_secret_key_in', '');
payqr_config::$secretKeyOut = variable_get('commerce_payqr_secret_key_out', '');
try{
$Payqr = new payqr_receiver(); // создаем объект payqr_receiver
$Payqr->receiving(); // получаем идентификатор счета на оплату в PayQR
// проверяем тип уведомления от PayQR
$type = $Payqr->getType();
$handler = '_commerce_payqr_'. str_replace('.', '_', $type);
watchdog('$handler', '<pre>!data</pre>', array('!data' => print_r($handler, 1)));
module_load_include('inc','commerce_payqr','includes/handlers');
if (function_exists($handler)) {
$handler($Payqr);
}
watchdog('pre response', '<pre>!data</pre>', array('!data' => print_r($Payqr, 1)));
$Payqr->response();
}
catch (payqr_exeption $e){
if(file_exists(PAYQR_ERROR_HANDLER.'invoice_action_error.php'))
{
$response = $e->response;
require PAYQR_ERROR_HANDLER.'receiver_error.php';
}
}
}
/**
* нужно создать заказ в своей учетной системе, если заказ еще не был создан, и вернуть в PayQR полученный номер заказа (orderId), если его еще не было
*
* @param payqr_receiver $Payqr
*/
function _commerce_payqr_invoice_order_creating(payqr_receiver $Payqr) {
watchdog('debug', '<pre>!data</pre>', array('!data' => print_r($Payqr, 1)));
global $user;
// @todo create anon\\auth logic set order owner
$cartObject = $Payqr->objectOrder->getCart();
if ($cartObject) {
// Create the new order in checkout; you might also check first to
// see if your user already has an order to use instead of a new one.
$order = commerce_order_new($user->uid, 'checkout_checkout');
// Save the order to get its ID.
commerce_order_save($order);
// Link anonymous user session to the cart
if (!$user->uid) {
// $customerObject = $Payqr->objectOrder->getCustomer();
// if ($customerObject) {
// $order->$customerObject
// }
commerce_cart_order_session_save($order->order_id);
}
foreach ($cartObject as $payqr_product) {
// Load whatever product represents the item the customer will be
// paying for and create a line item for it.0
$product = commerce_product_load_by_sku($payqr_product->article);
$line_item = commerce_product_line_item_new($product, $payqr_product->quantity, $order->order_id);
// Save the line item to get its ID.
commerce_line_item_save($line_item);
// Add the line item to the order using fago's rockin' wrapper.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$order_wrapper->commerce_line_items[] = $line_item;
}
// $profile_types = commerce_customer_profile_types();
$profile = commerce_customer_profile_new('billing', $user->uid);
// // Address field
// $profile->commerce_customer_address['und'][0]['country'] = "BE";
// $profile->commerce_customer_address['und'][0]['administrative_area'] = NULL;
// $profile->commerce_customer_address['und'][0]['sub_administrative_area'] = NULL;
// $profile->commerce_customer_address['und'][0]['locality'] = "Antwerpen";
// $profile->commerce_customer_address['und'][0]['dependent_locality'] = NULL;
// $profile->commerce_customer_address['und'][0]['postal_code'] = "2000";
// $profile->commerce_customer_address['und'][0]['thoroughfare'] = "Markt 1";
// $profile->commerce_customer_address['und'][0]['premise'] = "";
// $profile->commerce_customer_address['und'][0]['sub_premise'] = "";
// $profile->commerce_customer_address['und'][0]['organisation_name'] = "";
// $profile->commerce_customer_address['und'][0]['name_line'] = "Jimmy Henderickx";
// $profile->commerce_customer_address['und'][0]['first_name'] = "Jimmy";
// $profile->commerce_customer_address['und'][0]['last_name'] = "Henderickx";
// $profile->commerce_customer_address['und'][0]['data'] = NULL;
commerce_customer_profile_save($profile);
//create the right array for the save controller
$profile_object = array (
'und' => array (
array (
'profile_id' => $profile->profile_id,
),
),
);
$order->commerce_customer_billing = $profile_object;
if (module_exists('commerce_shipping')) {
$profile = commerce_customer_profile_new('shipping', $user->uid);
commerce_customer_profile_save($profile);
//create the right array for the save controller
$profile_object = array (
'und' => array (
array (
'profile_id' => $profile->profile_id,
),
),
);
$order->commerce_customer_shipping = $profile_object;
// Add shipping to order if any selected.
$shipping_service = $Payqr->objectOrder->getDeliveryCasesSelected();
if ($shipping_service) {
$service_name = $shipping_service->article;
// Make the chosen service available to the order.
commerce_shipping_service_rate_order($service_name, $order);
// Delete any existing shipping line items from the order.
commerce_shipping_delete_shipping_line_items($order, TRUE);
// Extract the unit price from the calculated rate.
$rate_line_item = $order->shipping_rates[$service_name];
$rate_line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $rate_line_item);
$unit_price = $rate_line_item_wrapper->commerce_unit_price->value();
// Create a new shipping line item with the calculated rate from the form.
$line_item = commerce_shipping_line_item_new($service_name, $unit_price, $order->order_id, $rate_line_item->data, $rate_line_item->type);
// Save and add the line item to the order.
commerce_shipping_add_shipping_line_item($line_item, $order, TRUE);
}
}
// Save the order again to update its line item reference field.
commerce_order_save($order);
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$transaction = commerce_payment_transaction_new('commerce_payqr', $order->order_id);
$transaction->instance_id = 'commerce_payqr|commerce_payment_commerce_payqr';
$transaction->amount = commerce_currency_decimal_to_amount($Payqr->objectOrder->getAmount(), $wrapper->commerce_order_total->currency_code->value());
$transaction->currency_code = $wrapper->commerce_order_total->currency_code->value();
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_id = $Payqr->objectOrder->getInvId();
$transaction->message = $Payqr->getType();
$transaction->remote_status = $Payqr->getType();
$transaction->payload = $Payqr;
$transaction->revision = TRUE;
commerce_payment_transaction_save($transaction);
$Payqr->objectOrder->setOrderId($order->order_id);
$Payqr->objectOrder->setOrderGroup($transaction->transaction_id);
}
}
?>
Ответы на уведомления PayQR должны носить чисто технический характер и формироваться без какой-либо сложной логики на стороне интернет-сайта, иначе в PayQR может сработать таймаут. Уведомления PayQR должны обрабатываться так же быстро, как стандартные аналогичные действия покупателя в интернет-браузере (оплата сформированного заказа).
Событие invoice.paid
означает, что PayQR успешно обработал оплату заказа покупателем и теперь интернет-сайту нужно обработать оплату конкретного заказа на своей стороне.
После получения уведомления о событии invoice.paid
интернет-сайту необходимо изменить статус конкретного заказа на «Оплачен» или любой другой, предназначенный для фиксации оплаты по заказу в учетной системе интернет-сайта.
Если вы используете рекомендованную PHP-библиотеку PayQR, то там уже предусмотрен файл, который запускается при получении уведомления о событии invoice.paid
. Это invoice.paid.php
в папке handlers
. Также в библиотеке предусмотрены некоторые методы по объекту «Счет на оплату», содержащемуся в уведомлении invoice.paid
, которые могут существенно облегчить написания своего кода реакции на это событие. Они описаны в файле payqr_invoice.php
в папке classes
.
Обязательно: подтверждайте получение уведомления о событии invoice.paid
ответом интернет-сайта на это уведомление от PayQR (после фиксирования оплаты конкретного заказа в своей учетной системе), иначе PayQR будет направлять это уведомление о событии invoice.paid
на URL для уведомлений до тех пор, пока не получит нужный ответ от вашего интернет-сайта.
Примеры обработки уведомления для оплаты заказа
Как обрабатывать оплату заказов в учетной системе вашего интернет-сайта зависит от этой учетной системы (обратитесь к документации учетной системы, если затрудняетесь дифференцировать этот функционал). Если на интернет-сайте уже используются какие-то системы безналичных платежей (интернет-эквайринг, электронные кошельки и тому подобное), то обработка уведомлений об оплате от PayQR должна осуществляться аналогично этим системам.
Пример уведомления о событии, направленное интернет-сайту:
{
"id": "evt_8902534ko0435i2F34kd04K",
"object": "event",
"created": "2015-08-09T18:31:42.201+04:00",
"type": "invoice.paid",
"data": {
"id": "inv_14EeCA2eZvKYlo2C8nDrcXdp",
"object": "invoice",
"livemode": false,
"created": "2015-08-09T18:31:42.201+04:00",
"modified": "2015-08-09T18:33:42.201+04:00",
"payqrNumber": "3213326680056410",
"payqrUserId": "000000000000",
"orderGroup": "123.456",
"orderId": "789",
"amount": 5000.00,
"cart": [
{
"article": "5675657",
"name": "Товар 1",
"imageUrl": "http://goods.ru/item1.jpg",
"quantity": 5,
"amount": 5000.00
}
],
"customer": {
"firstName": "Иван",
"lastName": "Иванов",
"phone": "+79111111111",
"email": "test@user.com"
},
"delivery": {
"country": "Россия",
"region": "Москва",
"city": "Москва",
"zip": "115093",
"street": "Партийный пер.",
"house": "1",
"unit": "46",
"hallway": "2",
"floor": "4",
"intercom": "#1234",
"comment": "Сказать охране, что в PayQR"
},
"validityInMinutes": 129600,
"confirmWaitingInMinutes": 4320,
"status": "paid"
}
}
Пример ответа на уведомление о событии от интернет-сайта:
{
"id": "evt_8902534ko0435i2F34kd04K",
}
Пример как мы делали оплату заказа
<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
$raw_data = file_get_contents("php://input");
//кастомная функция логирования, для отладки
payQrLogToFile($raw_data);
$event = json_decode($raw_data);
$secretKeyIn = $arParams['SECRET_KEY_IN']; // Входящий ключ SecretKeyIn этого «Магазина», указанный в личном кабинете
$secretKeyOut = $arParams['SECRET_KEY_OUT']; // Исходящий ключ SecretKeyOut этого «Магазина», указанный в личном кабинете
header("PQRSecretKey:" . $secretKeyOut); // Подписать ответ исходящим ключом для подтверждения, что ответ действительно от «Магазина»
if ($event->type == "invoice.order.creating" &&
CModule::IncludeModule("iblock") && CModule::IncludeModule("sale") && CModule::IncludeModule("catalog")) /* Проверяем тип события на соответствие типу события для создания заказа и задействуем нужные компоненты*/
{
/* логика формирования заказа */
}
else if ($event->type == "invoice.paid" && CModule::IncludeModule("sale")) /* Проверяем тип события на соответствие типу события для оплаты заказа и задействуем нужные компоненты этой учетной системы */
{
$invoice = $event->data; // Объект «Счет на оплату» (invoice)
//------- Начинаем процедуру оплаты заказа в 1C-Битрикс -------
$orderGroupParts = explode(".", $invoice->orderGroup); /* Получаем пришедшие идентификаторы учетной системы ИД сайта ИД пользователя на сайте и ИД пользователя в учетной системе (это редкая особенность «1С-Битрикс», часто в учетных системах понятия ИД сайта и ИД пользователя отсутствуют, а в формировании участвуют только артикулы позиций в заказе) */
$fuserId = $orderGroupParts[1]; // ИД пользователя на сайте (был сохранен на уровне кнопки PayQR в data-orderGroup вторым параметром)
CSaleOrder::PayOrder(intval($invoice->orderId), "Y"); // Пометить заказ как оплаченный в учетной системе интернет-сайта
CSaleBasket::DeleteAll(intval($fuserId), false); // Очищаем корзину на интернет-сайте для удобства покупателя
//------- Оплата заказа завершена в 1C-Битрикс -------
$raw_data = json_encode($event);
payQrLogToFile($raw_data);
echo $raw_data;
}
else
echo json_encode(array("id" => $event->id));
?>
<?php
/**
* нужно зафиксировать успешную оплату конкретного заказа
*
* @param payqr_receiver $Payqr
*/
function _commerce_payqr_invoice_paid(payqr_receiver $Payqr) {
watchdog("debug", "<pre>!data</pre>", array("!data" => print_r($Payqr, 1)));
$transaction_id = $Payqr->objectOrder->getOrderGroup();
$transaction = commerce_payment_transaction_load($transaction_id);
if ($transaction) {
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$transaction->remote_id = $Payqr->objectOrder->getInvId();
$transaction->message = $Payqr->getType();
$transaction->remote_status = $Payqr->getType();
$transaction->payload = $Payqr;
$transaction->revision = TRUE;
$transaction->log = $Payqr->getType();
commerce_payment_transaction_save($transaction);
$order = commerce_order_load($transaction->order_id);
if ($order) {
commerce_order_status_update($order,'checkout_complete');
commerce_checkout_complete($order);
}
}
}
?>
Вы можете совершить тестовую покупку через PayQR со специальной консолью, которая будет сопровождать процесс обмена информацией между виртуальным магазином и PayQR по ходу совершения покупки. Это позволит ознакомиться с примерным циклом получения интернет-сайтом уведомлений от PayQR и типовых ответов интернет-сайта на такие уведомления.