PHP Paypal Auth / Capture Проблемы с интеграцией NVP

Задний план:

Мы внедрили PayPal Authorization и Capture flow, используя интеграцию NVP и phpcurl.
Полный процесс описан на веб-сайте разработчика PayPal: https://developer.paypal.com/webapps/developer/docs/classic/express-checkout/ht_ec-singleAuthPayment-curl-etc/

На нашем веб-сайте текущий сценарий оплаты:
– Во-первых, пользователь нажимает кнопку, чтобы инициировать авторизацию платежа, перенаправляя его на веб-сайт PayPal (SetExpressCheckout с оплатой = Авторизация)
– Если пользователь успешно подтвердил платеж на веб-сайте PayPal, он перенаправляется на наш сайт с определенной страницей успеха
– Эта страница успеха получает токен и PayerID с веб-сайта PayPal. Затем мы вызываем GetExpressCheckoutDetails для проверки состояния и объема этого разрешения
– Если все в порядке, мы сообщаем PayPal подтвердить это авторизацию (DoExpressCheckoutPayment с оплатой = авторизация), и мы получаем идентификатор авторизации для хранения в нашей базе данных
– Позже кто-то может урегулировать транзакцию, нажав на кнопку, используя идентификатор авторизации, который мы сохранили (DoCapture)

Дополнительная информация:

Согласно документации PayPal:

PayPal награждает 100% уставных средств на три дня
Счета покупателей и торговцев не могут быть закрыты, если существует незавершенное (неурегулированное) разрешение
https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/authcapture/

На нашем веб-сайте разрешения автоматически аннулируются, если они не урегулированы в течение 24 часов. (с использованием crontab)

Проблема:

Проблема возникает в последней части (когда мы вызываем функцию «подтвердить»): когда пользователь нажимает кнопку «подтвердить», кажется, что иногда запрос на завиток занимает время, чтобы вернуть идентификатор транзакции из PayPal.
Когда это происходит, пользователь обычно закрывает веб-страницу, PayPal подтверждает авторизацию (и, следовательно, денежный перевод), но наш сайт не уведомлен об этом, потому что следующий код (из раздела «Исходный код» ниже) не был выполнен или не достигнут:

if ($transaction_id) { /* * [...] * Everything is ok, payment has been performed * so we do everything to give our user what he asked for */ } else { // Error : No transaction id } 

Потому что сценарий остановился, прежде чем получить завиток.
Более того, если мы попытаемся снова нажать кнопку, PayPal сообщит нам, что идентификатор авторизации не существует (поскольку уже выполнено).

Но иногда все работает хорошо, без каких-либо проблем или отставания.

Исходный код:

 /* * This is our main function, called when * we have to settle our transaction * when an user click on a "confirm" button **/ public function confirm($cart_id) { /* * [...] * We check lot of stuff to be sure this user * can perform this action */ // We get theses values from the database authorization_id = "lorem ipsum"; $amount = 10; // We tell PayPal to settle the transaction $transaction_id = $this->settle_transaction($authorization_id, $amount); if ($transaction_id) { /* * [...] * Everything is ok, payment has been performed * so we do everything to give our user what he asked for */ } else { // Error : No transaction id } } private function settle_transaction($authorization_id, $amount) { // Our credentials $params = array( "USER" => $this->paypal_user, "PWD" => $this->paypal_pwd, "SIGNATURE" => $this->paypal_signature, "VERSION" => 95 ); $params["METHOD"] = "DoCapture"; $params["AUTHORIZATIONID"] = $authorization_id; $params["AMT"] = $amount; $params["CURRENCYCODE"] = "EUR"; $params["COMPLETETYPE"] = "Complete"; $result = $this->curl($params); if ($result) { // We check that this PayPal request has been successful if ($result["ACK"] == "Success") { $transaction_id = $result["TRANSACTIONID"]; if ($result["PAYMENTSTATUS"] == "Completed") { return $transaction_id; } } } return NULL; } private function curl($params) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->paypal_endpoint); curl_setopt($ch, CURLOPT_POST, count($params)); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); parse_str(curl_exec($ch), $result); curl_close($ch); return $result; } 

У вас есть идея решить эту проблему?
Я думал об урегулировании транзакции в конце сценария, потому что PayPal награждает 100% уставных средств в течение трех дней, и мне нужно, чтобы они держались в течение 1 дня, но я не уверен в этом так или иначе …

Изменить 1:

Мой apache2 error.log сообщил об этом, когда эта проблема возникает:

 [Mon Aug 08 20:42:55.959330 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:42:56.960453 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:42:57.961188 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:42:58.962230 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:42:59.963297 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:00.964384 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:01.965476 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:02.966478 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:03.967595 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:04.968713 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:05.969783 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:06.970877 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:07.972002 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:08.972749 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:09.973847 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:10.974926 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:11.976080 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:12.977168 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:13.978244 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:14.979320 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:15.980414 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:16.981493 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:17.982578 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:18.983673 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:19.984762 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:20.985841 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:21.986650 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:22.987725 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:23.988826 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:24.989939 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:25.991061 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:26.992181 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:27.993305 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:28.994422 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:29.995556 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:30.996661 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:31.997774 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:32.998905 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:34.000089 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:35.001202 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:36.002326 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:37.003424 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:38.004551 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:39.005677 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:40.006799 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:41.007902 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:42.009021 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:43.010132 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:44.011245 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:45.012361 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:46.013479 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:47.014577 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:48.015685 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:49.016801 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:50.017906 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:51.018980 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:52.020049 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:53.021158 2016] [mpm_event:error] [pid 141:tid 3779940374400] AH00485: scoreboard is full, not at MaxRequestWorkers [Mon Aug 08 20:43:53.391316 2016] [:error] [pid 980:tid 3779386513152] (104)Connection reset by peer: [client MY-IP:55236] FastCGI: failed to read from backend server, referer: http://#### [Mon Aug 08 21:18:04.748237 2016] [:error] [pid 1287:tid 3779782977280] (104)Connection reset by peer: [client MY-IP:37196] FastCGI: failed to read from backend server 

Изменить 2:

Я нашел эту тему, которая, похоже, имеет аналогичную проблему:

Что особенно странно, так это то, что платеж был обработан правильно.

И сейчас я не могу воспроизвести эту ошибку.
Считаете ли вы, что это может быть проблема PayPal или что-то в этом роде?
Даже если бы это было так, я не был уверен, что эта проблема не повторится, но как я могу проверить, не могу ли я воспроизвести это?

Solutions Collecting From Web of "PHP Paypal Auth / Capture Проблемы с интеграцией NVP"

Примечание. Не все платежи будут мгновенными. Если у покупателя есть только банковский счет, связанный с их учетной записью PayPal, передача не будет мгновенной. Поэтому лучше использовать IPN если хотите автоматически получать уведомления обо всех платежах и связанных с ними мероприятиях.

Согласно официальным документам PayPal:

Мгновенное уведомление об оплате (IPN) – это служба сообщений, которая уведомляет вас о событиях, связанных с транзакциями PayPal. Вы можете использовать сообщения IPN для автоматизации бэк-офиса и административных функций, таких как выполнение заказов, отслеживание клиентов или предоставление статуса и другой связанной с транзакцией информации.

Как наилучшая практика, установите транзакционный скрипт в своем IPN Listener . Для руководства по интеграции вы можете обратиться сюда: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/

Я расширил PHP class для PayPal IPN Listener несколько месяцев назад. Надеюсь, это поможет в качестве отправной точки. Не стесняйтесь развиваться: https://github.com/datumradix/PayPal-IPN-PHP-Class-

Изменить: (PayPal Documentation is not clear at many places and seems confusing to many first time readers)

IPN может пригодиться в качестве вторичного механизма для подтверждения успешного DoCapture. IPN-переменные, такие как txn_type , txn_id , auth_id , auth_amount и payer_id , уведомляются об IPN. Пожалуйста, перейдите сюда для полного списка: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/

Примечание. Мы можем либо указать NOTIFYURL в каждом вызове, либо мы можем настроить его с помощью back-end. Для шагов по настройке этого параметра из настроек профиля PayPal, ссылка: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNSetup/

вам нужно узнать об ignore_user_abort(true); (и, возможно, set_time_limit(0); ), используйте это, чтобы избежать проблемы с выходом скриптов на полпути через код. во-вторых, могу ли я предложить базу данных недавно подтвержденных токенов, которая обновляется до вызова curl, так что, если пользователь уйдет, попробуйте снова нажать «подтвердить», вы поймете, что это уже подтвержденный токен, и не повторите -пропустить код завитка и может сразу сообщить пользователю? – http://php.net/manual/en/function.ignore-user-abort.php

  • И ПРЕДУПРЕЖДЕНИЕ, НЕКОТОРЫЕ ОБЩИЕ ПРЕДОСТАВЛЕНИЯ ХОСТИНГА НЕ ДОПУСКАЮТ ignore_user_abort / set_time_limit ДЛЯ МОДИФИЦИРОВАНО В ПОРЯДКЕ