Привет, хабр. Не так давно мы написали модуль, который подключает магазины на 1С-Битрикс к социальной сети Facebook. Хочу поделиться опытом, а также особенностями настройки приема Facebook Credits в вашем магазине — не суть важно, на какой CMS он реализован. Помчались!
Facebook Credits — внутренняя валюта социальной сети Facebook, впервые появившаяся в 2009 году, и ставшая единственной разрешенной, по правилам Facebook, с 1 июня этого года. Долгое время подключить российский банк для вывода заработанных кредитов было невозможно — Россия просто отсутствовала в списке стран для подключения вывода Facebook Credits.
Единственными возможными способами вывести заработанные кредиты был либо вывод через PayPal (легального, белого и пушистого способа вывод из PayPal в Россию на данный момент нет), либо открытие счета в иностранном банке. Однако 27 июня Россия появилась в форме подключения платежей, хотя в официальном списке ее еще нет. Но мы очень надеемся, и уже готовы :-)
Будем исходить из того, что у вас уже есть рабочее приложение для Facebook, и ваша задача — подключить прием кредитов. Итак, идем в настройки вашего приложения
и жмем «Редактировать». Переходим в раздел «On Facebook» -> Credits. Идем по ссылке регистрации новой компании. Аккуратно заполняем поля на свою организацию. Самая интересная вкладка — третья. Это настройка банковского счета.
Сходу несколько советов. Форма несколько подглючивает, и если вы ее неправильно заполнили — стирает часть полей. Поэтому рекомендую не кликать на кнопку «ОК», а нажимать «Открыть в отдельном окне» — так даже в случае возникновения ошибок вам не придется перебивать данные заново. Заполняйте все поля на английском языке. Поле SWIFT вводите без пробелов.
После регистрации организации вы сможете добавить ее для приема кредитов в своем приложении.
По правилам Facebook вы можете продавать за Facebook Credits исключительно виртуальные товары. Лично мне было бы интересно узнать, является ли продажа QR-кодов виртуальным товаром (если, допустим, по этому QR-коду можно сходить в кино, или заказать пиццу :-)).
Комиссия Facebook составляет 30% (это очень много, но меньше, чем, например, в Одноклассниках). В общем, есть нюансы, которые нужно учитывать при планировании бизнес-стратегии продаж в социальных сетях.
Итак, давайте посмотрим, что происходит в вашем приложении в тот момент, когда пользователь решается чего-нибудь оплатить и кликает на заветную кнопку.
Ваше приложение с помощью JavaScript-вызова Facebook Api создает диалоговое окно оплаты товара:
Facebook посылает запрос на серверную часть вашего приложения для получения деталей о заказе:
Как вы заметили, Facebook предполагает, что за одну транзакцию пользователь может купить только один товар. Но ничто не мешает вам формировать все содержимое корзины в виде товара.
Клиентский callback будет вызван сразу после закрытия окна оплаты. В нем вы должны обработать код ошибки/отмены, если такие возникли, либо поздравить пользователя с успешной покупкой.
Как только пользователь подтверждает оплату, на ваш сервер отправляется повторный запрос об изменении статуса заказа. Если все ок, ваша задача — реализовать обработчик заказов. Например, отправлять владельцу магазина уведомление о заказе, или выставлять статус “оплачено” в базе данных.
Мы писали оплату именно для заказов через 1С-Битрикс, поэтому остается мелочь: зарегистрировать платежную систему в списке платежных систем 1С-Битрикс при инсталляции модуля:
Вуаля! Наш магазин готов к приему виртуальных денег от Facebook Credits, ч.т.д.
Историческая справка
Facebook Credits — внутренняя валюта социальной сети Facebook, впервые появившаяся в 2009 году, и ставшая единственной разрешенной, по правилам Facebook, с 1 июня этого года. Долгое время подключить российский банк для вывода заработанных кредитов было невозможно — Россия просто отсутствовала в списке стран для подключения вывода Facebook Credits.
Единственными возможными способами вывести заработанные кредиты был либо вывод через PayPal (легального, белого и пушистого способа вывод из PayPal в Россию на данный момент нет), либо открытие счета в иностранном банке. Однако 27 июня Россия появилась в форме подключения платежей, хотя в официальном списке ее еще нет. Но мы очень надеемся, и уже готовы :-)
Подключаем платежи к приложению
Будем исходить из того, что у вас уже есть рабочее приложение для Facebook, и ваша задача — подключить прием кредитов. Итак, идем в настройки вашего приложения
и жмем «Редактировать». Переходим в раздел «On Facebook» -> Credits. Идем по ссылке регистрации новой компании. Аккуратно заполняем поля на свою организацию. Самая интересная вкладка — третья. Это настройка банковского счета.
Сходу несколько советов. Форма несколько подглючивает, и если вы ее неправильно заполнили — стирает часть полей. Поэтому рекомендую не кликать на кнопку «ОК», а нажимать «Открыть в отдельном окне» — так даже в случае возникновения ошибок вам не придется перебивать данные заново. Заполняйте все поля на английском языке. Поле SWIFT вводите без пробелов.
После регистрации организации вы сможете добавить ее для приема кредитов в своем приложении.
На заметку
По правилам Facebook вы можете продавать за Facebook Credits исключительно виртуальные товары. Лично мне было бы интересно узнать, является ли продажа QR-кодов виртуальным товаром (если, допустим, по этому QR-коду можно сходить в кино, или заказать пиццу :-)).
Комиссия Facebook составляет 30% (это очень много, но меньше, чем, например, в Одноклассниках). В общем, есть нюансы, которые нужно учитывать при планировании бизнес-стратегии продаж в социальных сетях.
Общая схема работы Facebook Credits
Итак, давайте посмотрим, что происходит в вашем приложении в тот момент, когда пользователь решается чего-нибудь оплатить и кликает на заветную кнопку.
Ваше приложение с помощью JavaScript-вызова Facebook Api создает диалоговое окно оплаты товара:
FB.init({appId: <?=$facebookAppID?>, status: true, cookie: true});
function placeOrder() {
// Вызываем диалог оплаты
var obj = {
method: 'pay',
order_info: {"order_id": "<?=$orderId?>"},
purchase_type: 'item'
};
FB.ui(obj, callback);
}
var callback = function(data) {
if (data['order_id']) {
$("#payment").hide();
$("#result-success").show();
} else if ((data['error_code']) && (data['error_message'].indexOf("User canceled", 0) == -1)) {
$("#payment").hide();
$("#result-failure").show();
} else if ((data['error_code']) && (data['error_message'].indexOf("User canceled", 0) != -1)) {
$("#result-cancel").show();
} else {
$("#result-failure").show();
}
};
Facebook посылает запрос на серверную часть вашего приложения для получения деталей о заказе:
- item_id — идентификатор товара;
- title — название товара;
- description — описание;
- image_url — картинка;
- product_url — ссылка на страницу с товаром;
- price — цена;
- data — дополнительные данные.
Как вы заметили, Facebook предполагает, что за одну транзакцию пользователь может купить только один товар. Но ничто не мешает вам формировать все содержимое корзины в виде товара.
Клиентский callback будет вызван сразу после закрытия окна оплаты. В нем вы должны обработать код ошибки/отмены, если такие возникли, либо поздравить пользователя с успешной покупкой.
Принимаем заказ
Как только пользователь подтверждает оплату, на ваш сервер отправляется повторный запрос об изменении статуса заказа. Если все ок, ваша задача — реализовать обработчик заказов. Например, отправлять владельцу магазина уведомление о заказе, или выставлять статус “оплачено” в базе данных.
<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
if (!CModule::IncludeModule("sale") || !CModule::IncludeModule("iblock")) {
echo("Модуль интернет-магазина не установлен!");
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/epilog_after.php");
die();
}
// Получим настройки модуля
$frontendPath = COption::GetOptionString("sibirix.freshshop", "frontend");
$catalogIblockId = COption::GetOptionString("sibirix.freshshop", "catalogIblockId");
$fbcExchange = COption::GetOptionString("sibirix.freshshop", "exchange");
$api_key = COption::GetOptionString("sibirix.freshshop", "facebook_appid");
$secret = COption::GetOptionString("sibirix.freshshop", "facebook_appsecret");
require_once('facebook.php');
// prepare the return data array
$data = array('content' => array());
$request = parse_signed_request($_REQUEST['signed_request'], $secret);
if ($request == null) {
// handle an unauthenticated request here
die("empty request\n");
}
// при получении фейсбуковский order_id преобразуется из строки во float и это скорее всего может стать огромной проблемой (потеряется пара последних знаков в номере заказа, который нам возвращает FB). Выдернем его руками
$payloadData = explode('.', $_REQUEST['signed_request'], 2);
$payloadData = base64_url_decode($payloadData[1]);
preg_match('/\"order_id\"\:([0-9]*)/', $payloadData, $matches);
$stringOrderId = $matches[1];
$payload = $request['credits'];
// retrieve all params passed in
$func = $_REQUEST['method'];
$order_info = json_decode($payload['order_info']);
if (!empty($order_info) && isset($order_info->order_id)) {
$orderId = (int)$order_info->order_id;
}
if (empty($orderId)) {
// Повторный запрос (оплата прошла), в нем нашего внутреннего order_id уже нет. Надо взять заказ по фейсбуковскому order_id
$orderRes = CSaleOrder::GetList(array(), array("COMMENTS" => $stringOrderId, "PAYED" => "N"), false, false, array("*"));
$orderData = $orderRes->GetNext();
$orderId = $orderData['ID'];
} else {
// Взять из БД заказ по идентификатору
$orderData = CSaleOrder::GetByID($orderId);
}
$basketRes = CSaleBasket::GetList(array(), array( "LID" => SITE_ID, "ORDER_ID" => $orderId), false, false, array("*"));
$basket = array();
$description = array();
$itemIds = array();
while ($item = $basketRes->GetNext()) {
$basket[] = $item;
$description[] = iconv(SITE_CHARSET, "UTF-8", $item['~NAME']) . " - " . number_format($item['QUANTITY'], 0) . "шт.";
if (!empty($item['PRODUCT_ID'])) $itemIds[] = $item['PRODUCT_ID'];
}
if (count($itemIds)) {
$res = CIBlockElement::GetList(array(), array("IBLOCK_ID" => $catalogIblockId, "ID" => $itemIds), false, false, array("DETAIL_PICTURE", "PREVIEW_PICTURE"));
while ($item = $res->GetNext()) {
if (!empty($item['DETAIL_PICTURE'])) {
$pictureId = $item['DETAIL_PICTURE'];
break;
}
if (!empty($item['PREVIEW_PICTURE'])) {
$pictureId = $item['PREVIEW_PICTURE'];
break;
}
}
}
if (isset($pictureId)) {
$uploadDir = "/" . COption::GetOptionString("main", "upload_dir", "upload") . "/";
$resFile = CFile::GetList(array(), array("ID" => $pictureId));
$ifile = $resFile->Fetch();
$picture = $ifile;
$picture['SRC'] = $uploadDir . $ifile["SUBDIR"] . "/" . $ifile['FILE_NAME'];
}
$picture = CFacebookShop::getThumbImage($picture, CFacebookShop::PRODUCT_PREVIEW_IMG, SITE_TEMPLATE_PATH.'/images');
if ($orderData['PAYED'] == "Y") {
// Заказ уже был оплачен
$data['content']['status'] = 'settled';
} elseif ($func == 'payments_status_update') {
// FB говорит нам, что пользователь оплатил заказ
$status = $payload['status'];
// write your logic here, determine the state you wanna move to
if ($status == 'placed') {
$next_state = 'settled';
$data['content']['status'] = $next_state;
// Обновить заказ
$ret = CSaleOrder::PayOrder($orderId, "Y");
}
// compose returning data array_change_key_case
$data['content']['order_id'] = $orderId;
} else if ($func == 'payments_get_items') {
// FB запрашивает у нас описание заказа
$item['title'] = 'Заказ №' . $orderId . " в интернет магазине " . COption::GetOptionString("main", "site_name");
$item['price'] = ceil($orderData['PRICE'] / $fbcExchange);
$item['description'] = implode(", ", $description);
$item['image_url'] = "http://" . COption::GetOptionString("main", "server_name") . $picture;
$item['product_url'] = "http://" . COption::GetOptionString("main", "server_name") . $picture;
$item['order_id'] = $orderId;
CSaleOrder::Update($orderId, array("COMMENTS" => $stringOrderId));
$data['content'] = array($item);
}
// required by api_fetch_response()
$data['method'] = $func;
// send data back
echo json_encode($data);
// you can find the following functions and more details
// on http://developers.facebook.com/docs/authentication/canvas
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return null;
}
// check signature
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog_after.php");?>
Регистрируем платежную систему
Мы писали оплату именно для заказов через 1С-Битрикс, поэтому остается мелочь: зарегистрировать платежную систему в списке платежных систем 1С-Битрикс при инсталляции модуля:
// Устанавливаем платежную систему Facebook Credits
function InstallPaysystem() {
if (!CModule::IncludeModule("sale") || !CModule::IncludeModule("catalog")) {
throw new Exception("Can't include sale and catalog modules");
}
$paysystemRes = CSalePaySystem::GetList(array(), array("NAME" => "Facebook Credits"));
$paysystem = $paysystemRes->GetNext();
if (!empty($paysystem)) {
// Уже установлена, просто сохраним ID
COption::SetOptionString($this->MODULE_ID, "paysystemId", $paysystem['ID']);
return true;
} else {
$paysystemId = CSalePaySystem::Add(array(
"LID" => SITE_ID,
"CURRENCY" => CCurrency::GetBaseCurrency(),
"NAME" => "Facebook Credits",
"ACTIVE" => "N",
"DESCRIPTION" => GetMessage("SHOPBOOK_INSTALL_FBC_DESCR")
));
COption::SetOptionString($this->MODULE_ID, "paysystemId", $paysystem['ID']);
}
return true;
}
Вуаля! Наш магазин готов к приему виртуальных денег от Facebook Credits, ч.т.д.