【问题标题】:Paypal IPN, Not getting all the transactions responses after changing the ipn url in the accountPaypal IPN,更改帐户中的 ipn url 后未​​收到所有交易响应
【发布时间】:2018-04-29 14:58:00
【问题描述】:

我正在我的项目中实现 ipnlistner。我在我的贝宝帐户中设置了 ipn url。但我没有收到对该 url 的所有事务 ipn 响应。但是当我在我的帐户中检查 ipn 历史记录时,它显示所有 ipn 都已发送。例如昨天它显示所有 112 ipn 已发送。但我的数据库中只有 7 个。这是我的 ipn 监听器代码。我只在第一行插入我在数据库中获取的所有数据。

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Response;

class PaypalIPN extends Controller {

private $use_sandbox = null;

const VALID = 'VERIFIED';

const INVALID = 'INVALID';

public function useSandbox() {
    $this->use_sandbox = env( 'USE_SANDBOX' );
}

public function getPaypalUri() {
    if ( $this->use_sandbox ) {
        return env( 'SANDBOX_VERIFY_URI' );
    } else {
        return env( 'VERIFY_URI' );
    }
}

public function verifyIPN() {
    try {
        DB::table( 'ipn_response' )->insert( [ 'data' => json_encode( $_POST, true ) ] );
        if ( ! count( $_POST ) ) {
            throw new \Exception( "Missing POST Data" );
        }
        $raw_post_data  = file_get_contents( 'php://input' );
        $raw_post_array = explode( '&', $raw_post_data );
        $myPost         = array();
        foreach ( $raw_post_array as $keyval ) {
            $keyval = explode( '=', $keyval );
            if ( count( $keyval ) == 2 ) {
                // Since we do not want the plus in the datetime string to be encoded to a space, we manually encode it.
                if ( $keyval[0] === 'payment_date' ) {
                    if ( substr_count( $keyval[1], '+' ) === 1 ) {
                        $keyval[1] = str_replace( '+', '%2B', $keyval[1] );
                    }
                }
                $myPost[ $keyval[0] ] = urldecode( $keyval[1] );
            }
        }
        // Build the body of the verification post request, adding the _notify-validate command.
        $req                     = 'cmd=_notify-validate';
        $get_magic_quotes_exists = false;
        if ( function_exists( 'get_magic_quotes_gpc' ) ) {
            $get_magic_quotes_exists = true;
        }
        foreach ( $myPost as $key => $value ) {
            if ( $get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1 ) {
                $value = urlencode( stripslashes( $value ) );
            } else {
                $value = urlencode( $value );
            }
            $req .= "&$key=$value";
        }

        // Use the sandbox endpoint during testing.
        $this->useSandbox();

        // Post the data back to PayPal, using curl. Throw exceptions if errors occur.
        $ch = curl_init( $this->getPaypalUri() );
        curl_setopt( $ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
        curl_setopt( $ch, CURLOPT_POST, 1 );
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt( $ch, CURLOPT_POSTFIELDS, $req );
        curl_setopt( $ch, CURLOPT_SSLVERSION, 6 );
        curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 1 );
        curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
        curl_setopt( $ch, CURLOPT_FORBID_REUSE, 1 );
        curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 30 );
        curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Connection: Close' ) );
        $res = curl_exec( $ch );
        if ( ! ( $res ) ) {
            $errno  = curl_errno( $ch );
            $errstr = curl_error( $ch );
            curl_close( $ch );
            throw new \Exception( "cURL error: [$errno] $errstr" );
        }
        $info      = curl_getinfo( $ch );
        $http_code = $info['http_code'];
        if ( $http_code != 200 ) {
            throw new \Exception( "PayPal responded with http code $http_code" );
        }
        curl_close( $ch );

        // Check if PayPal verifies the IPN data, and if so, return true.
        if ( $res == self::VALID ) {
            DB::table( 'ipn_response' )->insert( [ 'data' => json_encode( $res, true ) ] );
        } else {
            DB::table( 'ipn_response' )->insert( [ 'data' => json_encode( $res, true ) ] );
        }

        // Reply with an empty 200 response to indicate to paypal the IPN was received correctly.
        header( "HTTP/1.1 200 OK" );
    }catch (\Exception $e){
        DB::table( 'ipn_response' )->insert( [ 'data' => json_encode( ["Exception"=>$e->getMessage()]) ] );
    }
}
}

我非常喜欢这个网址上的 IPN

https://ipnpb.paypal.com/cgi-bin/webscr

我的 ipn 网址是

https://www.myproject.com/api/verify-ipn

注意:之前我在此帐户上创建了一些贝宝按钮,我没有收到该按钮付款的 ipn 响应

请帮助或指导为此做些什么..

【问题讨论】:

  • 什么时候写入数据库? -- 显示代码
  • 你的 PHP 错误日志是怎么说的?
  • 数据库写入是通过DB 类完成的。不确定它是什么库,但可能是问题的根源。
  • @smcjones 因为我们看不到它,我们无法知道这不是问题所在。
  • @Martin 我们也不知道这不是问题所在。我跟着你。然而,快速浏览告诉我这是 Laravel 的 DB 类(如此标记)。语法对我来说很熟悉。可能不是 Laravel 错误。

标签: php mysql laravel-5 paypal paypal-ipn


【解决方案1】:

请在您的控制器中创建以下函数,并在您的 Paypal 帐户中添加 IPN URL 并检查 IPN 的每一次点击。

<?php
 function paymentIpnlistener(){
    $req = 'cmd=_notify-validate';
    foreach ($_POST as $key => $value) {
        $value = urlencode(stripslashes($value));
        $req .= "&$key=$value";
    }
    // post back to PayPal system to validate
    $header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
    // If testing on Sandbox use: 
    $header .= "Host: www.sandbox.paypal.com:443\r\n";
    //$header .= "Host: ipnpb.paypal.com:443\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
    if (strpos('ssl://www.sandbox.paypal.com', 'sandbox') !== FALSE && function_exists('openssl_open')) {
    $fp = fsockopen('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
  }
    else{
    // The old "normal" way of validating an IPN.
     $fp = fsockopen('ssl://www.sandbox.paypal.com', 80, $errno, $errstr, 30);
    }
    // If testing on Sandbox use:
    //$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
    //$fp = fsockopen ('ssl://ipnpb.paypal.com', 443, $errno, $errstr, 30);
    // assign posted variables to local variables
    $item_name          = $_POST['item_name'];
    $item_number        = $_POST['item_number'];
    $payment_status     = $_POST['payment_status'];
    $payment_amount     = $_POST['mc_gross'];
    $payment_currency   = $_POST['mc_currency'];
    $txn_id             = $_POST['txn_id'];
    $receiver_email     = $_POST['receiver_email'];
    $payer_email        = $_POST['payer_email'];
    if (!$fp) {
     // HTTP ERROR
    } else {
        fputs ($fp, $header . $req);
        while (!feof($fp)) {
            $res = fgets ($fp, 1024);
            if (strcmp ($res, "VERIFIED") == 0) {
                // check the payment_status is Completed
                // check that txn_id has not been previously processed
                // check that receiver_email is your Primary PayPal email
                // check that payment_amount/payment_currency are correct
                // process payment

                // Add your live email address    
                $mail_From = "From: me@mybiz.com";
                // Add your live email address 
                $mail_To = "raghbendra.nayak@systematixindia.com";
                $mail_Subject = "VERIFIED IPN";
                $mail_Body = $req;
                foreach ($_POST as $key => $value){
                    $emailtext .= $key . " = " .$value ."\n\n";
                }

                mail($mail_To, $mail_Subject, $emailtext . "\n\n" . $mail_Body, $mail_From);

            }
            else if (strcmp ($res, "INVALID") == 0) {
                // log for manual investigation

                $mail_From = "From: me@mybiz.com";
                $mail_To = "raghbendra.nayak@systematixindia.com";
                $mail_Subject = "INVALID IPN";
                $mail_Body = $req;

                foreach ($_POST as $key => $value){
                    $emailtext .= $key . " = " .$value ."\n\n";
                }

                mail($mail_To, $mail_Subject, $emailtext . "\n\n" . $mail_Body, $mail_From);

            }
        }   // while end
     fclose ($fp);
    }

    }
?>

每当 IPN 侦听器触发时,上述功能都会向您发送电子邮件。简单地说,如果它工作,那么您可以根据您的要求对其进行管理。试着告诉我。

【讨论】:

  • 只是检查 - 这与 OP 的问题和问题有什么关系?它看起来不像我的答案。
  • 如果他将创建函数并放置此代码,那么他将在提供的电子邮件中获得失败或成功的 IPN 响应.....然后他可以根据他的要求使用相同的...我面临它之前并获得与我分享的相同的解决方案。
【解决方案2】:

听起来您的代码可能有错误。需要进一步调试。当我遇到这样的事情时,我通常会检查错误日志以了解发生了什么。

PayPal 将继续尝试发送请求,直到收到正文中没有内容的200 OK HTTP 状态响应。如果 PayPal 显示端点接收成功,那么数据库输入数据失败的地方可能就在你的 header 函数被调用之前。

我的下一步调试是尝试确定我的数据库插入是否由于某种数据完整性错误/警告而失败。

捕获数据库错误并触发某种非传递响应可能会有所帮助,因此 PayPal 会重新发送,直到您找出脚本问题为止。

还有:

  • 尝试使用 Monolog 甚至只是 error_log 之类的内容添加额外的日志记录,以了解脚本终止的位置或未按预期运行的位置。

我应该注意到我现在也在实施 PayPal IPN,而且这个库很难使用。我用订阅者模式推出了自己的模式。它还没有准备好上市,但主要是为了将逻辑与验证分开,以实现更好的测试和可读性。

【讨论】:

  • OP 正在使用沙盒 sdo 点关于 live 版本在这里不需要
  • 删除了沙盒上的注释
  • @smcjones 我才知道,对于同一个帐户,我们之前创建了一些贝宝按钮用于定期付款。现在我们更改了帐户设置中的 ipn url。因此,现在通过该按钮进行的​​付款不会将 ipn 响应发送到我的新网址。任何克服这种情况的方法
  • 对于新付款,我收到了 ipn 响应
【解决方案3】:

首先,写一个日志作为web服务的第一行,这样你就知道什么时候收到请求了。

然后,有时你没有设置所有的请求参数,所以当参数没有找到时,你的 sql 可能会出错。

例如,在我的 paypal 表单中,我发送 invoice_number。然后,当我收到来自 IPN 的请求时,我将检查 invoice_number 以将付款与发票联系起来。 但是我也从其他来源通过同一个贝宝帐户获得付款,因此在其他情况下未设置 invoice_number。

您可以将在 IPN 调用中找到的所有参数写入日志,以便检查缺少的内容。

【讨论】:

    猜你喜欢
    • 2011-09-12
    • 2015-06-21
    • 2011-12-18
    • 2013-06-01
    • 2012-11-02
    • 2012-01-08
    • 2012-08-14
    • 2014-10-22
    • 2012-11-16
    相关资源
    最近更新 更多