Перейти к содержимому


Фотография
- - - - -

Настройка интеркассы на новый API

рецепт

  • Закрытая тема Тема закрыта
Сообщений в теме: 12

#1 joejoker

joejoker

Отправлено 27 December 2013 - 14:56

Переключение интеркассы на новый API
 
Совсем недавно, платежный агрегатор Интеркасса, объявил о переходе на новый API. Хоть на сайте interkassa.com и нет соответствующих новостей, но регистрация новых магазинов, уже осуществляется только на новом сайте new.interkassa.com. Сначала мы хотели переделать интеграцию в следующем релизе JBZoo, но столкнулись с рядом недоработок нового API, плохо написанной документацией и «гробовым» молчанием техподдержки. В связи с чем, было решено пока не переделывать существующую реализацию, а опубликовать рецепт, при помощи которого каждый сможет, при желании самостоятельно сделать нормально работающее подключение к новому API. 
 
Внимание! Все изменения вы делаете на свой страх и риск, так как безошибочная работа нового API вызывает сомнения.
 
Итак. Все что нужно для того что бы подключиться к новому API, это зарегистрировать и настроить кассу и отредактировать три файла в сборке JBZoo
 
1. Регистрация
 
Зарегистрировать новую кассу, не сложнее чем завести почтовый ящик. Идем на сайт https://new.interkassa.com/  и видим такую картину
f9c_1280x0.png
Пугаться этого не стоит, так как скорее всего сертификат настроен на старый домен. Жмем кнопку «Продолжить все равно» и попадаем на главную страницу сайта.
 
В верхней части сайта жмем ссылку «Зарегистрироваться»
7jm.png
и проходим несложную процедуру регистрации.
nky.png
На следующей странице, вам будет предложено авторизоваться. Вводим свой логин и пароль, который был выслан в письме на почтовый ящик или в СМС на номер мобильного телефона и попадаем на такую страничку
yvh.png
Как мы видим, система предлагает зарегистрировать нам кошелек, но нам нужно зарегистрировать кассу. Для этого нужно кликнуть по ссылке своего профиля и нажать эту кнопку
o2e.png
Далее вводим адрес своего интернет магазина, произвольное название кассы и выбираем валюту, в которой будет работать касса
h29.png
и нажимаем кнопку «Создать».
 
2. Настройка кассы.
 
Под именем вашей кассы, будет стоять идентификационный номер, который нужно скопировать и вставить в настройки интеркассы, корзины вашего магазина.
 
Для того что бы перейти в настройки кассы, нажимаем эту кнопку
rpn.png
 
На первой вкладке «Общие настройки», вводим необходимую информацию.
m6r.png
 
На настройке следующих двух вкладок «Настройки платежей» и «Платежные системы» останавливаться не будем, так как настройки интуитивно понятны и не критичны для нашего подключения. Единственное, на чем стоит заострить внимание, так это на том, что после того как система будет подключена, настоятельно рекомендуется отключить во вкладке «Платежные системы» систему «Тестовая платежная система» 
oro.png
 
Далее, на следующей вкладке «Интерфейс» Нам нужно вбить три ссылки и указать режим передачи данных «POST»
xr6.png
 
Где взять ссылки? Ссылки берутся в настройках корзины вашего интернет — магазина. Здесь
g02.png
после того как корзина вашего магазина будет полностью в рабочем состоянии, нужно нажать ссылку «Получить ссылки для API». Откроется вот такое окно
0df.png
из которого нужно скопировать ссылки и вставить их в соответствующие поля, в настройках кассы, на вкладке «Интерфейс».
 
На вкладке «Безопасность», нужно установить значение «Алгоритм подписи» - MD5, скопировать «Секретный ключ» и вставить его в соответствующее поле настроек платежных систем в настройке корзины. 
 
Внимание! Настоятельно не рекомендуется включать настройку «Проверять подпись в форме запроса платежа»
 
bj5.png
 Именно с включением этой опции возникают проблемы с новым API.
 
На этом обзор настройки кассы можно закончить.
 
3. Редактирование файлов JBZoo
 
Для того что бы инеркасса заработала с новым API нужно отредактировать три файла. Это: 
  • /media/zoo/applications/jbuniversal/framework/controllers/payment.php
  • /media/zoo/applications/jbuniversal/templates/catalog/renderer/payment_ikassa/_default.php
  • /media/zoo/applications/jbuniversal/config/basket.xml
 
Открываем в первую очередь файл /media/zoo/applications/jbuniversal/config/basket.xml находим там следующий кусок кода:
 
<!-- IKassa -->
        <param name="@spacer-ikassa" type="jbspacer" default="JBZOO_IKASSA_CONFIG_TITLE"/>

        <param name="ikassa-enabled" type="jbbool" default="0" label="JBZOO_IKASSA_ENABLED"
               description="JBZOO_IKASSA_ENABLED_DESC"/>

        <param name="ikassa-shopid" type="text" default="" label="JBZOO_IKASSA_SHOPID"
               description="JBZOO_IKASSA_SHOPID_DESC"/>

        <param name="ikassa-key" type="text" default="" label="JBZOO_IKASSA_KEY" description="JBZOO_IKASSA_KEY_DESC"/>
 
заменяем его этим кодом:
 
<!-- IKassa -->
        <param name="@spacer-ikassa" type="jbspacer" default="JBZOO_IKASSA_CONFIG_TITLE"/>

        <param name="ikassa-enabled" type="jbbool" default="0" label="JBZOO_IKASSA_ENABLED"
               description="JBZOO_IKASSA_ENABLED_DESC"/>

<param name="ikassa-debug" type="jbbool" default="0" label="JBZOO_IKASSA_DEBUG"
               description="JBZOO_IKASSA_DEBUG_DESC"/>
  
        <param name="ikassa-shopid" type="text" default="" label="JBZOO_IKASSA_SHOPID"
               description="JBZOO_IKASSA_SHOPID_DESC"/>

        <param name="ikassa-key" type="text" default="" label="JBZOO_IKASSA_KEY" description="JBZOO_IKASSA_KEY_DESC"/>

        <param name="ikassa-key-test" type="text" default="" label="JBZOO_IKASSA_KEY_TEST" description="JBZOO_IKASSA_KEY_TEST_DESC"/>
 
Далее, открываем файл /media/zoo/applications/jbuniversal/templates/catalog/renderer/payment_ikassa/_default.php и замеяем код формы данным кодом:
<form name="payment" action="https://sci.interkassa.com/"" method="post" accept-charset="UTF-8">

    <input type="hidden" name="ik_co_id"" value="<?php echo $data->get('shopid'); ?>">
    <input type="hidden" name="ik_am" value="<?php echo $data->get('summ'); ?>">
    <input type="hidden" name="ik_pm_no" value="<?php echo $data->get('orderId'); ?>">
    <input type="hidden" name="ik_desc""
           value="Order #<?php echo $data->get('orderId'); ?> form <?php echo JUri::getInstance()->getHost(); ?>">
    <input type="hidden" name="ik_sign" value="<?php echo $data->get('secretKey'); ?>">

    <input type="submit" style="display:inline-block;" class="add-to-cart"
           value="<?php echo JText::_('JBZOO_PAYMENT_BUTTON'); ?>"/>
</form>
И последнее. Открываем файл:  /media/zoo/applications/jbuniversal/framework/controllers/payment.php и заменяем весь код, этим:
<?php
/**
 * JBZoo App is universal Joomla CCK, application for YooTheme Zoo component
 *
 * @package     jbzoo
 * @version     2.x Pro
 * @author      JBZoo App http://jbzoo.com
 * @copyright   Copyright (C) JBZoo.com,  All rights reserved.
 * @license     http://jbzoo.com/license-pro.php JBZoo Licence
 * @coder       Denis Smetannikov <denis@jbzoo.com>
 */

// no direct access
defined('_JEXEC') or die('Restricted access');


/**
 * Class paymentJBUniversalController
 */
class paymentJBUniversalController extends JBUniversalController
{

    const TYPE_ROBOX  = 'Robokassa.ru';
    const TYPE_IKASSA = 'Interkassa.com';
    const TYPE_MANUAL = 'Manual';
    /**
     * @var Int
     */
    public $appId = null;

    /**
     * @var Item
     */
    public $order = null;

    /**
     * @var Int
     */
    public $orderId = null;

    /**
     * @var Int
     */
    public $itemId = null;

    /**
     * @var ElementJBBasketItems
     */
    public $orderDetails = null;

    /**
     * @var AppTemplate
     */
    public $template = null;

    /**
     * @var ParameterData
     */
    public $appParams = null;

    /**
     * @var JBUniversalApplication
     */
    public $application = null;

    /**
     * @var JBModelOrder
     */
    public $orderModel = null;

    /**
     * @var BasketRenderer
     */
    public $renderer = null;

    /**
     * @var String
     */
    public $systemType = null;

    /**
     * Init controller
     * @throws AppException
     */
    protected function _init()
    {
        $this->orderId = (int)$this->_jbrequest->get('order_id');
        $this->appId   = (int)$this->_jbrequest->get('app_id');

        $this->appParams = $this->application->getParams();

        if ($invId = (int)$this->_jbrequest->get('InvId')) {
            $this->systemType = self::TYPE_ROBOX;
            $this->orderId    = $invId;

        } else if ($ikPaymentId = (int)$this->_jbrequest->get('ik_pm_no')) {
            $this->systemType = self::TYPE_IKASSA;
            $this->orderId    = $ikPaymentId;

        } else if ($orderId = (int)$this->_jbrequest->get('order_id')) {
            $this->systemType = self::TYPE_MANUAL;
            $this->orderId    = $orderId;
        }

        if (!$this->appId) {
            throw new AppException('Applciation id is no set');
        }

        if (!$this->template = $this->application->getTemplate()) {
            throw new AppException('No template selected');
        }

        if ((int)$this->appParams->get('global.jbzoo_cart_config.enable', 0) == 0) {
            throw new AppException('Application is not a basket');
        }

        if ((int)$this->appParams->get('global.jbzoo_cart_config.payment-enabled') == 0) {
            throw new AppException('Payment is not enabled');
        }

        if ($this->orderId) {

            $this->orderModel = JBModelOrder::model();
            if (!$this->order = $this->orderModel->getById($this->orderId)) {
                throw new AppException('Order #' . $this->orderId . ' no exists');
            }

            if (!$this->orderDetails = $this->orderModel->getDetails($this->order)) {
                throw new AppException('This type don\'t have JBPrice element');
            }
        }

        if (!$this->orderDetails) {
            throw new AppException('Order not found');
            die;
        }

        // set renderer
        $this->renderer = $this->app->renderer->create('basket')->addPath(array(
            $this->app->path->path('component.site:'),
            $this->template->getPath()
        ));
    }

    /**
     * Index action
     */
    function index()
    {
        if ((int)JFactory::getConfig()->get('debug') == 0) {
            //error_reporting(0);
        }

        $this->_init();

        $totalSumm         = $this->orderDetails->getTotalPrice();
        $totalSummFormated = $this->orderDetails->getTotalPrice(true);

        $appParams      = $this->app->data->create($this->appParams->get('global.jbzoo_cart_config.', array()));
        $this->payments = array();

        if ($this->orderDetails->getOrderStatus() == ElementJBBasketItems::ORDER_STATUS_PAID) {
            throw new AppException('Order has already been paid');
        }
        if ($totalSumm == 0) {
            throw new AppException('To pay for the cost should be greater than zero');
        }

        // robox
        if ((int)$appParams->get('robox-enabled', 0)) {
            $params               = new stdClass();
            $params->login        = JString::trim($appParams->get('robox-login'));
            $params->password1    = JString::trim($appParams->get('robox-password1'));
            $params->hash         = md5(implode(':', array($params->login, $totalSumm, $this->orderId, $params->password1)));
            $params->summ         = $totalSumm;
            $params->orderId      = $this->orderId;
            $params->summFormated = $totalSummFormated;
            $params->debug        = (int)$appParams->get('robox-debug', 0);

            $this->payments['robox'] = $this->app->data->create($params);
        }

        // ikassa
        if ((int)$appParams->get('ikassa-enabled', 0)) {
            $params               = new stdClass();
            $params->shopid       = JString::trim($appParams->get('ikassa-shopid'));
            $params->summ         = $totalSumm;
            $params->orderId      = $this->orderId;
            $params->summFormated = $totalSummFormated;

            $this->payments['ikassa'] = $this->app->data->create($params);
            $params->secretKey    = $this->getSecretKey($appParams, $params->summ);
        }

        // manual
        if ((int)$appParams->get('manual-enabled', 0)) {
            $params          = new stdClass();
            $params->title   = $appParams->get('manual-title');
            $params->text    = $appParams->get('manual-text');
            $params->message = $appParams->get('manual-message');

            $this->payments['manual'] = $this->app->data->create($params);
        }

        // display
        $this->getview('payment')->addTemplatePath($this->template->getPath())->setLayout('payment')->display();
    }

    /**
     * @throws AppException
     */
    public function paymentCallback()
    {
        $this->_init();

        if ($this->orderDetails->getOrderStatus() == ElementJBBasketItems::ORDER_STATUS_PAID) {
            throw new AppException('Order has already been paid');
        }

        $totalsumm = $this->orderDetails->getTotalPrice();

        if ($this->systemType == self::TYPE_ROBOX) {

            if ((float)$totalsumm != (float)$_REQUEST['OutSum']) {
                throw new AppException('No valid summ');
            }

            $password2 = JString::trim($this->appParams->get('global.jbzoo_cart_config.robox-password2'));
            $crc       = strtoupper($_REQUEST["SignatureValue"]);
            $myCrc     = strtoupper(md5(implode(':', array($_REQUEST['OutSum'], $this->orderId, $password2))));

            if ($crc === $myCrc) {

                // get request vars
                $args = array(
                    'date'            => $this->app->date->create()->toSQL(),
                    'system'          => $this->systemType,
                    'additionalState' => null
                );

                // execute callback method
                $this->orderDetails->callback('paymentCallback', $args);

                jexit('OK' . $this->orderId);

            } else {
                throw new AppException('No valid hash');
            }

        } else if ($this->systemType == self::TYPE_IKASSA) {

            if($this->appParams->get('global.jbzoo_cart_config.ikassa-debug',0 || $this->_jbrequest->get('ik_pw_via', '') === 'test_interkassa_test_xts')){

                $key = JString::trim($this->appParams->get('global.jbzoo_cart_config.ikassa-key-test',0));
            }else{
                $key = JString::trim($this->appParams->get('global.jbzoo_cart_config.ikassa-key',0));
            }

            $myCrcData = array(
                'ik_co_id'     => $this->_jbrequest->get('ik_co_id', ''),
                'ik_am'        => $this->_jbrequest->get('ik_am', ''),
                'ik_co_prs_id' => $this->_jbrequest->get('ik_co_prs_id', ''),
                'ik_inv_id'    => $this->_jbrequest->get('ik_inv_id', ''),
                'ik_inv_st'    => $this->_jbrequest->get('ik_inv_st', ''),
                'ik_inv_crt'   => $this->_jbrequest->get('ik_inv_crt', ''),
                'ik_inv_prc'   => $this->_jbrequest->get('ik_inv_prc', ''),
                'ik_trn_id'    => $this->_jbrequest->get('ik_trn_id', ''),
                'ik_pm_no'     => $this->_jbrequest->get('ik_pm_no', ''),
                'ik_desc'      => $this->_jbrequest->get('ik_desc', ''),
                'ik_pw_via'    => $this->_jbrequest->get('ik_pw_via', ''),
                'ik_cur'       => $this->_jbrequest->get('ik_cur', ''),
                'ik_co_rfn'    => $this->_jbrequest->get('ik_co_rfn', ''),
                'ik_ps_price'  => $this->_jbrequest->get('ik_ps_price', '')
            );

            ksort($myCrcData, SORT_STRING);
            array_push($myCrcData, $key);

            $sgnString     = implode(':', $myCrcData);
            $myCrc         = base64_encode(md5($sgnString, true));
            $crc           = $this->_jbrequest->get('ik_sign');
            $shopid        = $this->appParams->get('global.jbzoo_cart_config.ikassa-shopid');
            $requestShopid = $this->_jbrequest->get('ik_co_id');
            $totalSumm     = (float)$this->orderDetails->getTotalPrice();
            $requestAmount = (float)$this->_jbrequest->get('ik_am');

            if ($crc === $myCrc &&
                $totalSumm == $requestAmount &&
                $requestShopid === $shopid
            ) {
                // get request vars
                $args = array(
                    'date'            => $this->app->date->create()->toSQL(),
                    'system'          => $this->systemType,
                    'additionalState' => $this->_jbrequest->get('ik_payment_state')
                );

                // execute callback method
                $this->orderDetails->callback('paymentCallback', $args);

                jexit('OK' . $this->orderId);

            } else {
                throw new AppException('No valid hash');
            }

        } else {
            throw new AppException('Unknown system');
        }
    }

    /**
     * Payment success page
     */
    public function paymentSuccess()
    {
        $this->_init();

        // display
        $this->getview('payment_success')->addtemplatepath($this->template->getpath())->setlayout('payment_success')->display();
    }

    /**
     * Payment success page (manual)
     */
    public function paymentManual()
    {
        $this->_init();

        $appParams = $this->app->data->create($this->appParams->get('global.jbzoo_cart_config.', array()));

        if ((int)$appParams->get('manual-enabled', 0)) {

            $this->manual = $this->app->data->create(array(
                'title'   => $appParams->get('manual-title'),
                'text'    => $appParams->get('manual-text'),
                'message' => $appParams->get('manual-message'),
            ));

            $this->orderDetails->callback('paymentCallback', array(
                'date'   => $this->app->date->create()->toSQL(),
                'system' => self::TYPE_MANUAL,
            ));

            if ($appParams->get('manual-message')) {
                $this->app->jbnotify->notice($appParams->get('manual-message'));
            }

        } else {
            $this->app->jbnotify->error('Manual paymant is disabled');
        }

        // display
        $this->getview('payment_success')->addTemplatepath($this->template->getpath())->setlayout('payment_success')->display();
    }

    /**
     * Payment fail page
     */
    public function paymentFail()
    {
        $this->_init();
        $this->app->document->setTitle(JText::_('JBZOO_PAYMENT_FAIL_PAGE_TITLE'));

        // display
        $this->getview('payment_fail')->addtemplatepath($this->template->getpath())->setlayout('payment_fail')->display();
    }


    /**
     * Action for success order page without payment
     */
    public function paymentNotPaid()
    {
        $this->_init();

        $appParams = $this->app->data->create($this->appParams->get('global.jbzoo_cart_config.', array()));

        // check custom success page
        $successPage = JString::trim($appParams->get('payment-page-success'));
        if (!empty($successPage)) {
            $successPage = $this->app->jbrouter->addParamsToUrl($successPage, array('order_id' => $this->order->id));
            $this->setRedirect($successPage);

            return;
        }

        $this->getview('payment_success')->addtemplatepath($this->template->getpath())->setlayout('payment_success')->display();
    }


    /**
     * @param $appParams
     * @param $summ
     * @return string
     */
    public function getSecretKey($appParams, $summ)
    {
        $arrProp = array(
            'ik_co_id'  => JString::trim($appParams->get('ikassa-shopid')),
            'ik_am'     => $summ,
            'ik_pm_no'  => $this->orderId,
            'ik_desc'   => 'Order #' . $this->orderId . ' form ' . JUri::getInstance()->getHost()
        );

        if($appParams->get('ikassa-debug',0)){

            $key = $appParams->get('ikassa-key-test',0);
        }else{
            $key = $appParams->get('ikassa-key',0);
        }

        ksort($arrProp, SORT_STRING);
        array_push($arrProp, $key);
        $signString = implode(':', $arrProp);
        $sign = base64_encode(md5($signString, true));
        return $sign;

    }
}

После проведения данных манипуляций, интеркасса должна нормально работать с новым API. 
 
Документация нового API: https://new.interkas...ocol.v0.9.5.pdf
Инструкция по подключению: https://new.interkas...cal-integration
 
Нормальная тех. поддержка ИНТЕРКАССЫ, будет осуществляться с января 2014 года. Надеемся, что к тому времени сотрудники доведут сервис "до ума" и избавятся от критических ошибок, а пока, еще раз повторяем:
 
Все изменения вы вносите на свой страх и риск. Администрация проекта JBZoo не несет никакой ответственности, если, по причине некорректной работы ИНТЕРКАССЫ, пользователю будет нанесен прямой или косвенный вред. 
 
Все вопросы, связанные с подключением, можете задавать в данном топике. Чем сможем, попытаемся помочь :)

Сообщение отредактировал SmetDenis: 07 June 2014 - 13:36

  • 2
В мире все совсем не сложно, если самому не усложнять.
Разработка индивидуальных решений для ZOO /JBZoo /Joomla

#2 SmetDenis

SmetDenis

Отправлено 27 December 2013 - 16:03


Так же предлагаю к просмотру познавательное видео о конвертирование существующего магазина



  • 0
JBZoo v4.0 и новый чудный мир Open Source GPL
Отключайте проверку лицензий как можно скорее!



— Есть два типа людей: Кто еще не делает бекапы и кто уже делает бекапы.


#3 andrey.koch

andrey.koch

Отправлено 27 December 2013 - 19:11

Добрый день, почему-то после проведения тестового платежа и перенаправления на страницу с информацией об успешном платеже - ошибка 400.

Когда в настройках интеркассы ставлю тип запроса GET, то в адресной строке вижу: 

http://trytyty.ru/index.php?ik_co_id=52b8809cbf4efc486526f69f&ik_inv_id=20163949&ik_inv_st=success&ik_inv_crt=2013-12-27+20%3A07%3A38&ik_inv_prc=2013-12-27+20%3A07%3A38&ik_pm_no=212&ik_pw_via=test_interkassa_test_xts&ik_am=3290.00&ik_co_rfn=3290.0000&ik_ps_price=3490.37&ik_desc=Order+%23212+form+trytyty.ru

  • 0

#4 joejoker

joejoker

Отправлено 27 December 2013 - 19:53

Когда в настройках интеркассы ставлю тип запроса GET, то в адресной строке вижу: 
Доброго времени суток. Все правильно. Это ответ сервера об успешном совешении платежа, он должен обрабатываться по ссылке, которая предусмотрена для страницы взаимодействия RESULT URL. Скорее всего в настройках кассы, у вас стоит не верная ссылка. 
  • 0
В мире все совсем не сложно, если самому не усложнять.
Разработка индивидуальных решений для ZOO /JBZoo /Joomla

#5 andrey.koch

andrey.koch

Отправлено 28 December 2013 - 08:55

Ссылки формировал в JBZoo:

В "URL успешной оплаты" вводил "Success URL" из JBZoo;

В "URL неуспешной оплаты" вводил "Fail URL" из JBZoo;

В "URL взаимодействия" вводил "Result URL (Status URL)" из JBZoo;

А вот в поле "URL ожидания проведения платежа" ничего не вводил, может в этом причина?


  • 0

#6 joejoker

joejoker

Отправлено 29 December 2013 - 18:15

А вот в поле "URL ожидания проведения платежа" ничего не вводил, может в этом причина?

 

ankoch, доброго времени суток.  Нет, причина не в этом. Вы заполняли поле, в настройках корзины, для тестового ключа? Дело в том, что при проведении тестовых платежей, система использует тестовый ключ для формирования цифровой подписи. Если вводили, то тогда сложно сказать что то конкретное, не видя вашего сайта и кода. Если не получится самостоятельно решить проблему, скиньте мне доступы, в личку, к админке и ftp.


  • 0
В мире все совсем не сложно, если самому не усложнять.
Разработка индивидуальных решений для ZOO /JBZoo /Joomla

#7 andrey.koch

andrey.koch

Отправлено 30 December 2013 - 17:40

Вы заполняли поле, в настройках корзины, для тестового ключа?
 К сожалению, нет, вводил обычный ключ. Сейчас обновил JBZoo до 2.1.2, проделаю все заново и попробую с тестовым ключом.
  • 0

#8 joejoker

joejoker

Отправлено 30 December 2013 - 18:00

В настройках корзины JBZoo, после того как вы сделаете изменения в файле basket.xml, у вас появится новое поле, специально для тестового ключа. Т.е. в настройки, нужно вбить оба ключа, боевой и тестовый.


  • 0
В мире все совсем не сложно, если самому не усложнять.
Разработка индивидуальных решений для ZOO /JBZoo /Joomla

#9 andrey.koch

andrey.koch

Отправлено 30 December 2013 - 18:11

joejoker, спасибо. Проблему так и не удалось решить. Не буду пороть горячку, подожду вашего релиза со стабильно работающей интеркассой.

 

P.S. Оказывается я так и вводил ранее - основной ключ в свое поле, а тестовый - в появившееся тестовое.


Сообщение отредактировал ankoch: 30 December 2013 - 18:13

  • 0

#10 i001

i001

Отправлено 22 January 2014 - 13:53

Будет ли хак для 1.6.1?


  • 0





Темы с аналогичным тегами рецепт

Click to return to top of page in style!