php - developer - script paypal




Problemas de integraciĆ³n PHP Paypal Auth/Capture NVP (2)

Nota: No todos los pagos serán instantáneos. Si el comprador solo tiene una cuenta bancaria asociada con su cuenta de PayPal, la transferencia no será instantánea. Por lo tanto, es una buena práctica utilizar IPN si desea notificaciones automáticas de todos los pagos y actividades relacionadas.

Según los documentos oficiales de PayPal:

La Notificación de pago instantánea (IPN) es un servicio de mensajes que le notifica sobre eventos relacionados con transacciones de PayPal. Puede usar los mensajes de IPN para automatizar las funciones administrativas y administrativas, como cumplir con los pedidos, rastrear a los clientes o proporcionar información sobre el estado y otra información relacionada con las transacciones.

Como práctica recomendada, establezca un script transaccional en su IPN Listener . Para consultar la guía de integración, puede consultar aquí: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/

He extendido una PHP class para el servicio de IPN Listener PayPal hace unos meses. Espero que pueda ayudar como punto de partida. No dude en bifurcar: https://github.com/datumradix/PayPal-IPN-PHP-Class-

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

La IPN puede ser útil como mecanismo secundario para confirmar si DoCapture se realizó correctamente. Las variables de IPN como txn_type , txn_id , auth_id , auth_amount y payer_id se notifican a través de IPN. Consulte aquí la lista completa: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/

Nota: Podemos especificar el NOTIFYURL en cada llamada o podemos configurar el mismo desde el back-end de PayPal. Para conocer los pasos para configurar lo mismo desde la configuración del perfil de PayPal, ref: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNSetup/

Fondo:

Implementamos la autorización de Paypal y el flujo de captura mediante la integración de NVP y php-curl.
El proceso completo se describe en el sitio web para desarrolladores de PayPal: https://developer.paypal.com/webapps/developer/docs/classic/express-checkout/ht_ec-singleAuthPayment-curl-etc/

En nuestro sitio web, el escenario de pago actual es:
- Primero, un usuario hace clic en un botón para iniciar una autorización de pago, redirigiéndolo en el sitio web de PayPal (SetExpressCheckout con paymentaction = Autorización)
- Si el usuario confirmó exitosamente el pago en el sitio web de PayPal, será redirigido a nuestro sitio web en una página específica de éxito.
- Esta "página de éxito" obtiene un token y un PayerID del sitio web de PayPal, luego llamamos a GetExpressCheckoutDetails para verificar el estado y el monto de esta autorización
- Si todo está bien, le pedimos a PayPal que confirme esta autorización (DoExpressCheckoutPayment con paymentaction = Autorización) y obtenemos un ID de autorización para almacenar en nuestra base de datos
- Más tarde, alguien más puede liquidar la transacción haciendo clic en un botón, utilizando el ID de autorización que almacenamos (DoCapture)

Información adicional:

Según la documentación de PayPal:

PayPal honra el 100% de los fondos autorizados por tres días
Las cuentas de compradores y comerciantes no pueden cerrarse si hay una autorización pendiente (sin resolver)
https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/authcapture/

En nuestro sitio web, las autorizaciones se anulan automáticamente si no se liquidan dentro de las 24 horas. (usando crontab)

El problema:

Ocurre un problema en la última parte (cuando llamamos a la función "confirmar"): cuando un usuario hace clic en el botón "confirmar", parece que a veces la solicitud de curvatura se está demorando en volver a obtener un ID de transacción de PayPal.
Cuando esto sucede, el usuario generalmente cierra la página web, PayPal confirma la autorización (y, por lo tanto, la transferencia de dinero), pero a nuestro sitio web no se le notifica porque el siguiente código (de la sección "Código fuente" a continuación) no se ha ejecutado o alcanzado:

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
}

Porque el script se detuvo antes de obtener la respuesta de rizo.
Además, si intentamos volver a hacer clic en el botón, PayPal nos dice que la ID de autorización no existe (porque ya se realizó).

Pero a veces todo funciona bien sin ningún problema o retraso.

Código fuente:

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

¿Tienes alguna idea para resolver este problema?
Estaba pensando en liquidar la transacción al final de la secuencia de comandos porque PayPal respeta el 100% de los fondos autorizados durante tres días, y solo necesito que se retengan por 1 día, pero no estoy seguro de esto de todos modos ...

Edición 1:

Mi apache2 error.log informó de esto cuando ocurre este problema:

[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

Edición 2:

Encontré este tema que parece tener un problema similar:

Lo que es particularmente extraño es que el pago ha sido procesado correctamente.

Y ahora mismo parece que no puedo reproducir este error.
¿Crees que podría haber sido un problema de PayPal o algo así?
Incluso si lo fuera, no me aseguraría de que este problema no vuelva a ocurrir, pero ¿cómo puedo probar si no puedo reproducirlo?


necesitas aprender sobre ignore_user_abort(true); (y posiblemente set_time_limit(0); ), set_time_limit(0); para evitar el problema de que los scripts salgan a la mitad del código. en segundo lugar, ¿puedo sugerir una base de datos de tokens confirmados recientemente que se actualice ANTES de la llamada de rizo, de modo que si un usuario se cierra, intente presionar "confirmar" nuevamente, sabrá que ya es un token confirmado y no volverá a aparecer? ¿Ejecutar el código curl, y puede informar inmediatamente al usuario? - http://php.net/manual/en/function.ignore-user-abort.php

  • Y ADVERTENCIA, ALGUNOS PROBLEMAS DE HOSTING COMPARTIDO NO PERMITEN que ignore_user_abort / set_time_limit SE MODIFIQUE EN TIEMPO DE EJECUCIÓN