PHP Paypal Auth / Capture NVP Integration Troubles

Fundo:

Implementamos a Autorização Paypal e o stream de Captura usando integração NVP e phpcurl.
O processo completo é descrito no site do desenvolvedor do PayPal: https://developer.paypal.com/webapps/developer/docs/classic/express-checkout/ht_ec-singleAuthPayment-curl-etc/

No nosso site, o cenário de pagamento atual é:
– Primeiro, um usuário clique em um botão para iniciar uma autorização de pagamento, redirecionando-o no site do PayPal (SetExpressCheckout com paymentaction = Autorização)
– Se o usuário confirmou com sucesso o pagamento no site do PayPal, ele será redirecionado para o nosso site em uma página de sucesso específica
– Esta “página de sucesso” recebe um token e um PayerID no site do PayPal, então chamamos GetExpressCheckoutDetails para verificar o status e o valor desta autorização
– Se tudo estiver bem, informamos o PayPal para confirmar esta autorização (DoExpressCheckoutPayment com paymentaction = Autorização) e obtemos uma ID de autorização para armazenar em nosso database
– Mais tarde, outra pessoa pode resolver a transação clicando em um botão, usando a ID de autorização que armazenamos (DoCapture)

Informações adicionais:

De acordo com a documentação do PayPal:

O PayPal honra 100% dos fundos autorizados por três dias
As contas de compradores e comerciantes não podem ser fechadas se houver uma autorização pendente (não determinada)
https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/authcapture/

No nosso site, as autorizações são anuladas automaticamente se não forem resolvidas dentro de 24 horas. (usando crontab)

O problema:

Ocorre um problema na última parte (quando chamamos a function “confirmar”): quando um usuário clicar em um botão “confirmar”, parece que às vezes a solicitação de curling leva tempo para recuperar uma ID de transação do PayPal.
Quando isso acontece, o usuário costuma fechar a página, o PayPal confirma a autorização (e, portanto, o transferência de dinheiro), mas o nosso site não é notificado sobre isso porque o código a seguir (da seção “Código-fonte” abaixo) não foi executado ou atingido:

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 } 

Como o script parou antes de obter a resposta de curl.
Além disso, se tentarmos clicar novamente no botão, o PayPal nos diz que a ID da autorização não existe (porque já foi executada).

Mas às vezes tudo funciona bem sem nenhum problema ou atraso.

Código fonte:

 /* * 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; } 

Você tem alguma idéia para resolver esse problema?
Eu estava pensando em liquidar transactions no final do script porque o PayPal honra 100% dos fundos autorizados por três dias, e eu só preciso deles para segurar por 1 dia, mas não tenho certeza disso de qualquer maneira …

Editar 1:

Meu erro apache2.log relatou isso quando esse problema acontece:

 [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 

Editar 2:

Eu achei esse tópico que parece ter um problema semelhante:

O que é particularmente estranho é que o pagamento foi processado corretamente.

E agora não consigo reproduzir este erro.
Você acha que poderia ter sido uma questão do PayPal ou algo assim?
Mesmo que fosse, não quero garantir que esse problema não aconteça novamente, mas como posso testar se não consigo reproduzir isso?

   

Nota: nem todos os pagamentos serão instantâneos. Se o comprador tiver apenas uma conta bancária associada à sua conta do PayPal, a transferência não será instantânea. Portanto, é melhor usar o IPN se desejar notifications automáticas de todos os pagamentos e atividades relacionadas.

De acordo com o PayPal Official Docs:

Notificação de pagamento instantâneo (IPN) é um serviço de mensagens que notifica os events relacionados às transactions do PayPal. Você pode usar mensagens IPN para automatizar funções administrativas e administrativas, como cumprir ordens, rastrear clientes ou fornecer status e outras informações relacionadas a transactions.

Como melhor prática, configure o script transacional em seu IPN Listener . Para o guia de integração, você pode consultar aqui: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/

Eu estendi uma PHP class para PayPal IPN Listener alguns meses atrás. Espero que isso possa ajudar como ponto de partida. Sinta-se livre para garfo: https://github.com/datumradix/PayPal-IPN-PHP-Class-

Editar: (PayPal Documentation is not clear at many places and seems confusing to many first time readers)

O IPN pode ser útil como mecanismo secundário para confirmar se o DoCapture foi bem sucedido. Variáveis ​​IPN como txn_type , txn_id , auth_id , auth_amount e payer_id são todas notificadas IPN. Por favor, ref para a lista completa: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/

Nota: podemos especificar o NOTIFYURL em cada chamada ou podemos configurar o mesmo do back-end do paypal. Para obter as etapas para configurar o mesmo nas configurações de perfil do PayPal, ref: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNSetup/

você precisa aprender sobre ignore_user_abort(true); (e, possivelmente, set_time_limit(0); ), use isso para evitar o problema dos scripts saindo a meio do código. Em segundo lugar, posso sugerir um database de tokens recentemente confirmados que é atualizado ANTES da chamada curl, de modo que, se um usuário encerrar, tente pressionar novamente “confirmar”, você saberá que já é um token confirmado e não re -run o código curl, e pode informar imediatamente o usuário? – http://php.net/manual/en/function.ignore-user-abort.php

  • E ADVERTÊNCIA, ALGUNS FORNECIMENTOS DE HOSPEDAGEM COMPARTILHADOS NÃO PERMITEM ignore_user_abort / set_time_limit PARA SER MODIFICADO NO FUNCIONAMENTO