We connect Facebook Credits for online stores

    Hi, Habr. Not so long ago, we wrote a module that connects stores on 1C-Bitrix to the social network Facebook. I want to share my experience, as well as the features of setting up Facebook Credits reception in your store - it doesn’t matter which CMS it is implemented on. We rushed off!

    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

    According to the rules of Facebook, you can only sell virtual goods for Facebook Credits . Personally, I would be interested to know if selling QR codes is a virtual product (if, let’s say, using this QR code you can go to the movies or order pizza :-)).
    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']) {
            } else if ((data['error_code']) && (data['error_message'].indexOf("User canceled", 0) == -1)) {
            } else if ((data['error_code']) && (data['error_message'].indexOf("User canceled", 0) != -1)) {
            } else {

    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.

    $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'];
            if (!empty($item['PREVIEW_PICTURE'])) {
                $pictureId = $item['PREVIEW_PICTURE'];
    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, '-_', '+/'));

    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

    Also popular now: