
We connect Facebook Credits for online stores

Historical reference
Facebook Credits is the internal currency of the Facebook social network, which first appeared in 2009, and became the only one allowed, according to Facebook rules, from June 1 of this year. For a long time, it was impossible to connect a Russian bank to withdraw earned loans - Russia simply was not in the list of countries for connecting Facebook Credits.
The only possible ways to withdraw earned loans was either withdrawal through PayPal ( there is currently no legal, white and fluffy way to withdraw from PayPal to Russia ), or opening an account with a foreign bank. However, on June 27, Russia appeared in the form of connecting payments, although it is not yet on the official list . But we really hope, and are ready :-)
We connect payments to the application
We will proceed from the fact that you already have a working application for Facebook, and your task is to connect credit reception. So, go to the settings of your application
and click "Edit." Go to the section "On Facebook" -> Credits. We follow the link to register a new company . Carefully fill in the fields for your organization. The most interesting tab is the third. This is a bank account setup.

Just a few tips. The form is somewhat buggy, and if you fill it out incorrectly, it erases part of the fields. Therefore, I recommend not clicking on the “OK” button, but clicking “Open in a separate window” - so even in case of errors you will not have to interrupt the data again. Fill in all fields in English. Enter the SWIFT field without spaces.

After registering an organization, you can add it to receive loans in your application.
On a note

Facebook commission is 30% (this is a lot, but less than, for example, in Odnoklassniki). In general, there are nuances that need to be considered when planning a business sales strategy on social networks.
The general scheme of the work of Facebook Credits
So, let's see what happens in your application at the moment when the user decides to pay something and clicks on the treasured button.

Your application using the JavaScript api call Facebook Api creates a dialog box for the payment of goods:
FB.init({appId: , status: true, cookie: true});
function placeOrder() {
// Вызываем диалог оплаты
var obj = {
method: 'pay',
order_info: {"order_id": ""},
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 sends a request to the server side of your application to receive order details:
- item_id - product identifier;
- title - product name;
- description - description;
- image_url - picture;
- product_url - link to the product page;
- price - price;
- data - additional data.
As you noticed, Facebook assumes that a user can only buy one product per transaction. But nothing prevents you from creating the entire contents of the basket in the form of goods.
Client callback will be called immediately after closing the payment window. In it, you must process the error / cancellation code, if any, or congratulate the user on a successful purchase.
Accept order
As soon as the user confirms the payment, a repeated request is sent to your server about changing the order status. If everything is ok, your task is to implement an order processor. For example, send an order notification to the store owner, or display the status “paid” in the database.
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");?>
Register a payment system

We wrote the payment specifically for orders through 1C-Bitrix, therefore there is a trifle: register the payment system in the list of payment systems 1C-Bitrix when installing the module:
// Устанавливаем платежную систему 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;
}
Voila! Our store is ready to receive virtual money from Facebook Credits, including