В выборе банка Эквайера, в данном случае не мне приходится решать.... Клиенту "Навязывают" этот банк Отписал в банк, что документация не соответствует действительности... Вариант который сейчас пробуем реализовать, у них называется двухфазным. Типа самый простой! Есть ещё варианты (как вы и описали в своём последнем ответе) и ещё ++, когда сначала запрашиваем токен, потом с токеном отправляем запрос на старт платежа, потом все тоже самое что и при двухфазном варианте. Но! там есть подводные камни тоже, например с вариантом хранения данных карт! Уж очень не хочется хранить такие персональные данные в своей системе, так как вскоре может последовать какое нить новое положение по сертификации системы согласно "Стандарт PCI DSS", чему сейчас могут быть подвержены существующие Интернет магазины. Уж лучше, пусть всё на их стороне пусть будет!
Верно. С сертификацией PCI лучше не связываться и данные карт не хранить. Но есть еще два подводных камня - 3D Secure и блокировка зачисления на счёт получателя после завершения транзакции. На территории ЕС это обязательные оптии. 1. В случае применения 3D Secure, платёжный сервис в процессе акцептирования, в начале присылает ответ со статусами "отклонён" или "на запросе", а затем после подтверждения кода через SMS, отправляется ответ со статусами "отклонён" или "успех". Это сильно усложняет реализацию акцептирования платежей в реальном времени на стороне клиента. 2. После завершения транзакции срок зачисления на счёт получателя определяется в договоре между клиентом и платёжным сервисов и может быть различным (от 60 до 120 дней). Очевидно, что выгоднее выбрать такой сервис, у которого этот срок минимальным. Это связано с тем, что период возврата платежа (Chargeback) - восемь недель, для платежей дебетовыми и кредитными картами Mastercard, Visa и American Express.
Доброго времени всем! В общем, продвинулся немного! Запрос от банка не приходил по банальной причине, протух сертификат SSL. Точнее скриптовый сертификат у нас обновился, а у них его не обновили. Получив доступ в консоль магазина на стороне Банка, была видна ошибка, что запрос не прошел проверку. Вот только не понятно почему со стороны банка скрипт не останавливал работу не понятно... Не уведомлял что сертификат не соответствует... отправлял второй запрос о не удавшемся платеже... Настроили (вместе с банком) чтоб первый запрос приходил на нужный нам адрес, сейчас он vircgpb/cpareg Они предлагали сделать vircgpb/cpareg.php Но почитав в мануалах усвоил, что обращаться к исполняемому скрипту это плохой тон... (правильно усвоил, или есть мнения?) Настроил на адрес... Сейчас вопрос больше базовых знаний, GET запрос приходит, в нем проверяются параметры, нужно теперь сравнить одинаковые параметры из моего запроса, с таким же, параметром из запроса к нам. например, o_order_id Наш GET запрос через форму: Код (Text): Array ( [merch_id] => 946AD4CD0F6B946B0EF3 [back_url_s] => 2https://nanaca.ru/lk [back_url_f] => https://nanaca.ru/lk [o_order_id] => 70500770-486BBD44F3F8F6E2AE6B-1031233115 ) GET запрос банка: Код (Text): Array ( [merch_id] => 946AD4CD0F6B946B0EF3 [trx_id] => 8MC8Y3G4RZ8AIQEK [o_order_id] => 70500770-486BBD44F3F8F6E2AE6B-1031233115 [ts] => 20241031 23:31:20 [q] => vircgpb/cpareg ) Мне при сравнении нужно значение параметра из базы выдергивать, или он в памяти ещё есть после запроса через форму?
Добрый день! В памяти после перезапуска скрипта есть только то, что передаётся в скрипт - $_REQUEST или $_SESSION. Если нужно что-то сохранить, то перед отправкой запроса через форму сохраните данные в $_SESSION Удачи!
Дополнение При работе с платёжными системами, клиентские скрипты, обрабатывающие данные транзакций, запускются асинхронно. Продолжительность акцептирования на стороне сервиса может превышать время жизни сессии PHP. Поэтому, в этом случае безопасно передавать данные можно только сохраняя их в БД. Мною на практике проверено такое решении: На каждый платёж в БД в таблице payment заводится запись с данными транзакций и ссылкой на таблицу заказов (order_id). Ежедневно через Cron Job запускается PHP-скрипт проверяющий, данные заказов и их оплату. В том случае, если оплата заказа не была успешной, из этого скрипта отправляются мэйлы бухгалтеру фирмы и клиенту-плательщику. В письмо клиента, кроме текста с требованием оплаты, встраивается линк на оплату заказа.
День добрый! В документации написано, что данный этап выполняется синхронно. В общем, у них гибридный вариант, первый запрос от нас, и первый и второй ответ от них - асинхронные, но на первый и второй ответ от них, нужно отвечать синхронно. В базе сохраняется конечно сгенерированный номер "платежного счета", и сумма платежа по нему. Про безопасность услышал, будем вытаскивать из базы инфу о платежном счете... Но, если ответ синхронный, значит нужно ответить ответом типа "$response = ", тут тоже ещё один вопрос возник, через форму отправлять или можно как то по другому, в документации нет такого описании. Задал им вопрос, пока молчат...
На первом подготовительном этапе (пока на стороне сервиса фомудяр оплаты для клиента ещё не открылся), да м.б. синхронно. Но я написал Вам об ответах, которые приходят с сервиса после того как клиент нажал кнопку "оплатить". Причём если оплата проходит по регламенту 3-D Secure, ответа может быть два. Это работает только асинхронно. По опыту, бывает длительная задержка во времени (десятки минут) между ответом со стасом "payment on request" и вторым ответом со статусами "success" или "rejected"
Огромное спасибо всем участникам, кто помог разобраться! В результате закончил недели 2 назад, но не смог написать сюда, потому что форум висел намертво! разобрался потом что и как с SSL делать, как сигнатуру сравнивать, как xml ответ отправлять на GET запрос, подписанный сигнатурой. В общем, заработало! SSL сертификат пришлось в БД подкладывать, и выводить как переменную. В интернет на самом деле очень мало таких примеров, всё через букварь пришлось догонять. Конечно марафет нужно в коде навести, для чего любую критику и рекомендации готов выслушать! Буду приставать с другими вопросами, сейчас изложу и опубликую. Код (Text): // //CPAReg - контроллер запроса в VIRC на регистрацию платежа. Ответ - CPARes // function vircgpb_cpareg_page() { vircgpb_log('CPAReg:', true); $url = $GLOBALS['base_url'] . $_SERVER['REQUEST_URI']; //vircgpb_log('$url = ' . $url, true); if(isset($_GET['merch_id'])){ $merch_id = $_GET['merch_id']; } if(isset($_GET['trx_id'])){ $trx_id = $_GET['trx_id']; } if(isset($_GET['o_amount'])){ $amount = $_GET['o_amount']; } if(isset($_GET['o_order_id'])){ $order_id = $_GET['o_order_id']; } if(isset($_GET['ts'])){ $ts = $_GET['ts']; } require_once dirname(__FILE__) . '/../vircabo/zscripts/payorder-lib.php'; $order = virc_payorder_get($order_id); // //Проверка наличия заказа на платеж от абонента в VIRC // if (!empty ($_GET['o_order_id']) == ( $order )) { if( $order ){ //vircgpb_get_param($order['paysys_id']); $paysysParam = zQuery('pay_online,_pay_online', 'select', $order['paysys_id'], array(), 2); $shortdesc = ('Услуги ЖКХ'); ////Вытащить наименование платежей программно //пока кастыль $longdesc = ('Оплата коммунальных услуг'); ////Вытащить наименование платежей программно //пока кастыль $shop_id = $paysysParam[0]['id_shop']; $amount = ($order ['amount']) * 100; //print_r( $paysysParam ); //print_r( $shop_id ); //print_r( $amount ); //print_r( $pay_for_name ); $response = '<?xml version="1.0" standalone="yes"?>'; $response .='<payment-avail-response>'; $response .='<result>'; $response .='<code>1</code>'; $response .='<desc>OK</desc>'; $response .='</result>'; $response .='<purchase>'; $response .='<shortDesc>'.$shortdesc.'</shortDesc>'; $response .='<longDesc>'.$longdesc.'</longDesc>'; $response .='<account-amount>'; $response .='<id>'.$shop_id.'</id>'; $response .='<amount>'.$amount.'</amount>'; $response .='<currency>643</currency>'; $response .='<exponent>2</exponent>'; $response .='</account-amount>'; $response .='</purchase>'; $response .='</payment-avail-response>'; header('Content-type: application/xml; charset=utf-8'); echo $response; exit; } } else { $response = '<?xml version="1.0" standalone="yes"?>'; $response .='<payment-avail-response>'; $response .='<result>'; $response .='<code>2</code>'; $response .='<desc>Нет такого заказа на платеж</desc>'; $response .='</result>'; $response .='</payment-avail-response>'; header('Content-type: application/xml; charset=utf-8'); echo $response; } exit; } // // Нотификация платежа/Регистрация платежа в VIRC //RPReg запрос в VIRC на регистрацию платежа, ответ RPRes. // function vircgpb() { global $base_url, $user; //vircgpb_log('RPReg', true); $url = $GLOBALS['base_url'] . $_SERVER['REQUEST_URI']; //vircgpb_log('$url = ' . $url, true); //print_r( $url ); if(isset($_GET['trx_id'])){ $trx_id = $_GET['trx_id']; } if(!empty($_GET['result_code'])){ $result_code = $_GET['result_code']; } //print_r('result_code: '. $result_code ); if(isset($_GET['signature'])){ $sign = $_GET['signature']; } // Регистрация платежа в VIRC $error = false; $err_msg = ''; $status = 0; if ( !empty ($_GET['o_order_id']) ) { $order_id = $_GET['o_order_id']; require_once dirname(__FILE__) . '/../vircabo/zscripts/payorder-lib.php'; $order = virc_payorder_get($order_id); $paysysSecret = zQuery('pay_online,_pay_online', 'select', $order['paysys_id'], array('zlist'=>1), 2); $cert_val = $paysysSecret[0]['oper_val']; $data_key = openssl_pkey_get_public($cert_val); //print_r( $data_key ); // Извлекаем публичный ключ $keyData=openssl_pkey_get_details($data_key); $pubkeyid=$keyData['key']; // Get check string. Получчаем контрольную строку $sign_position = strpos($url, '&signature='); $verify_string = substr($url, 0, $sign_position); // Сравниваем сигнатуры $resp = openssl_verify($verify_string, base64_decode($sign), $pubkeyid, OPENSSL_ALGO_SHA1); //print_r(PHP_EOL.'result: '.$resp . "\n\n"); //при корректной проверке возвращает result: 1 if ( $order ) { if ($resp !== 1 ) { vircgpb_log( 'Invalid signature', true ); virc_payorder_status( $order_id, array( 'status' => -3, 'error' => 'Invalid signature ' . $sign ) ); $response = '<?xml version="1.0" standalone="yes"?>'; $response .= '<register-payment-response>'; $response .= '<result>'; $response .= '<code>2</code>'; $response .= '<desc>Не верная сигнатура платежа</desc>'; $response .= '</result>'; $response .= '</register-payment-response>'; header("Content-type: application/xml; charset=utf-8"); echo $response; exit(); } // ВАЖНО ! Магазин должен отправить в ответе на запрос регистрации платежа result.сode=1, // если платеж не был успешным, но его результат зарегистрирован магазином. // Нужно доделать статусы и ошибки по таблице, повторный запрос на статус платежа (может быть и не нужно.) // virc_payorder_status( $order_id, array( 'acq_id' => $api->paymentId ) ); //вариант запроса статуса в Тинькофф ('acq_id' => trx_id) else if ($resp == 1 and $result_code == 2) { virc_payorder_status( $order_id, array( 'status' => -4, 'error' => 'Internal error' ) ); $response = '<?xml version="1.0" standalone="yes"?>'; $response .= '<register-payment-response>'; $response .= '<result>'; $response .= '<code>1</code>'; $response .= '<desc>Операция оплаты прошла с ошибкой</desc>'; $response .= '</result>'; $response .= '</register-payment-response>'; header("Content-type: application/xml; charset=utf-8"); echo $response; exit(); } else { if ( $resp == 1 and $result_code == 1 ) { $pay_id = virc_payorder_commit( $order_id ); if ( !$pay_id ) { vircgpb_log('COMMIT ERROR '.$order_id ); } else if ( defined("VIRC_AUTOFISCAL") && VIRC_AUTOFISCAL && function_exists("virc_fiscal_payment") ) { virc_fiscal_payment($pay_id); } $response = '<?xml version="1.0" standalone="yes"?>'; $response .= '<register-payment-response>'; $response .= '<result>'; $response .= '<code>1</code>'; $response .= '<desc>OK</desc>'; $response .= '</result>'; $response .= '</register-payment-response>'; header("Content-type: application/xml; charset=utf-8"); echo $response; exit(); } } } } else { vircgpb_log('Empty order id', true); } exit('OK'); }
Вам отдельное спасибо! Сейчас перечитал ваши советы с примерами кода, действительно помогли бы мне, если бы я знал как их применить. Теперь немного появилось "просветления", в работе таких функций и сценариев.