简体   繁体   English

为什么我运行订阅表格时Paypal出现错误?

[英]Why is Paypal giving an error when I run my subscription form?

I have never been able to successfully set up recurring paypal subscription buttons on my website because Paypal always gives me the error Paypal cannot process this transaction because of a problem with the seller's website. 我从未能够成功在我的网站上成功设置重复的贝宝订阅按钮,因为贝宝总是给我错误, Paypal cannot process this transaction because of a problem with the seller's website. I have tested the script locally and in the IPN simulator and all worked fine. 我已经在本地和IPN模拟器中测试了脚本,并且一切正常。 But whenever I try to test in sandbox mode it gives me this error. 但是,每当我尝试在沙盒模式下进行测试时,都会出现此错误。 Is it a problem with my code or with my Paypal account? 我的代码或Paypal帐户有问题吗? I am trying to set this up in wordpress. 我正在尝试在wordpress中进行设置。

Here is my form: 这是我的表格:

if(get_option('jz_payment_environment') == 'local'){
    $form_action = site_url('gateway/paypal');
} else if(get_option('jz_payment_environment') == 'sandbox'){
    $form_action = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
} else if(get_option('jz_payment_environment') == 'production'){
    $form_action = 'https://ipnpb.paypal.com/cgi-bin/webscr';
}?>
<form action="<?php echo $form_action;?>" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="">
<input type="hidden" name="custom" value="<?php echo get_current_user_id();?>">
<input type="hidden" name="business" value="<?php echo get_option('jz_paypal_email');?>">
<?php if(get_option('jz_payment_environment') == 'local'):?>
<input type="hidden" name="t3" value="M">
<input type="hidden" name="p3" value="1">
<input type="hidden" name="src" value="1">
<input type="hidden" name="srt" value="52">
<input type="hidden" name="currency_code" value="USD">
<?php endif;?>

Here is my IPN Shortcode: 这是我的IPN简码:

public static function paypal_ipn(){
        global $wpdb;
        ob_start();
        if($_SERVER['REQUEST_METHOD'] == 'POST'){
            $ipn = new PaypalIPN();
            if(get_option('jz_payment_environment') == 'sandbox'){
                $ipn->useSandbox();
                $verified = $ipn->verifyIPN();
            } else if(get_option('jz_payment_environment') == 'local'){
                $verified = true;
            } else if(get_option('jz_payment_environment') == 'production'){
                $ipn->usePHPCerts();
                $verified = $ipn->verifyIPN();
            }
            if($verified){

               $payment = array(
                   'post_author' => $_POST['custom'],
                   'post_type' => 'payment',
                   'post_status' => 'publish',
                   'meta_input' => array(
                       'payment_amount' => $_POST['a3'],
                       'payment_expiration' => ($_POST['t3'] == 'M') ? time() + (60 * 60 * 24 * 30 * $_POST['p3']) : time() + (60 * 60 * 24 * 365),
                       'payment_currency' => $_POST['currency_code'],
                       'payment_status' => 'pending',
                       'payment_tpt_no' => (!empty($_POST['txn_id'])) ? $_POST['txn_id'] : 'N/A'
                   )
               );
               $payment_post = wp_insert_post($payment);
               if(!is_wp_error($payment_post)){
                   $subscriber = get_userdata($_POST['custom']);
                   if($subscriber){
                       update_user_meta($subscriber, 'subscription_expiration', $payment['meta_input']['payment_expiration']);
                       $plan = $wpdb->get_row("
                           SELECT p.ID, pm1.meta_value as plan_monthly, pm2.meta_value as plan_quarterly, pm3.meta_value as plan_yearly
                           FROM {$wpdb->prefix}posts AS p
                           LEFT JOIN {$wpdb->prefix}postmeta AS pm1 ON p.ID = pm1.post_id AND pm1.meta_key = 'plan_monthly'
                           LEFT JOIN {$wpdb->prefix}postmeta AS pm2 ON p.ID = pm2.post_id AND pm2.meta_key = 'plan_quarterly'
                           LEFT JOIN {$wpdb->prefix}postmeta AS pm3 ON p.ID = pm3.post_id AND pm3.meta_key = 'plan_yearly'
                           WHERE post_type = 'plan'
                               AND pm1.meta_value = '{$_POST['a3']}'
                               OR pm2.meta_value = '{$_POST['a3']}'
                               OR pm3.meta_value = {$_POST['a3']}
                           LIMIT 1
                           ");
                       update_user_meta($subscriber, 'subscription_plan', $plan->ID);
                   }
               }
            }
        }
        header("HTTP/1.1 200 OK");
        $out = ob_get_contents();
        ob_end_clean();
        return $out;
    }

And here is the Paypal IPN Class that I am using from the Paypal developer site: 这是我在Paypal开发人员网站上使用的Paypal IPN类:

<?php
class PaypalIPN
{
    /** @var bool Indicates if the sandbox endpoint is used. */
    private $use_sandbox = false;
    /** @var bool Indicates if the local certificates are used. */
    private $use_local_certs = true;
    /** Production Postback URL */
    const VERIFY_URI = 'https://www.paypal.com/cgi-bin/webscr';
    /** Sandbox Postback URL */
    const SANDBOX_VERIFY_URI = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
    /** Response from PayPal indicating validation was successful */
    const VALID = 'VERIFIED';
    /** Response from PayPal indicating validation failed */
    const INVALID = 'INVALID';
    const RESULT = '';
    /**
     * Sets the IPN verification to sandbox mode (for use when testing,
     * should not be enabled in production).
     * @return void
     */
    public function useSandbox()
    {
        $this->use_sandbox = true;
    }
    /**
     * Sets curl to use php curl's built in certs (may be required in some
     * environments).
     * @return void
     */
    public function usePHPCerts()
    {
        $this->use_local_certs = false;
    }
    /**
     * Determine endpoint to post the verification data to.
     *
     * @return string
     */

    public function getResult(){
        return self::RESULT;
    }
    public function getPaypalUri()
    {
        if ($this->use_sandbox) {
            return self::SANDBOX_VERIFY_URI;
        } else {
            return self::VERIFY_URI;
        }
    }
    /**
     * Verification Function
     * Sends the incoming post data back to PayPal using the cURL library.
     *
     * @return bool
     * @throws Exception
     */
    public function verifyIPN()
    {
        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";
        }
        // 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);
        // This is often required if the server is missing a global cert bundle, or is using an outdated one.
        if ($this->use_local_certs) {
            curl_setopt($ch, CURLOPT_CAINFO, 'W:/laragon/etc/ssl/cacert.pem');
        }
        curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'User-Agent: PHP-IPN-Verification-Script',
            'Connection: Close',
        ));
        if ( ! ($res = curl_exec($ch))) {
            $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) {
            return true;
        } else {
            return false;
        }
    }
}

The hosted_button_id hidden input is changed with JS when a user clicks on a radio button. 当用户单击单选按钮时,hosted_button_id隐藏的输入将使用JS进行更改。 If you want to test my form, click here . 如果要测试我的表格,请单击此处 What am I doing wrong? 我究竟做错了什么?

As I can see, your hosted_button_id parameter is empty. 如我所见,您的hosted_button_id参数为空。 Consider removing it? 考虑删除它?

If it's filled up later, however: PayPal explicitly states that you can not change transaction amounts on hosted buttons: 但是,如果以后被填满:贝宝明确声明您不能更改托管按钮上的交易金额:

You should not write HTML button code for saved buttons. 您不应为已保存的按钮编写HTML按钮代码。 Always use the code that PayPal generates. 始终使用贝宝生成的代码。 However, you can enhance the generated code for saved buttons by adding hidden HTML variables that do not affect the transaction amount. 但是,您可以通过添加不影响交易金额的隐藏HTML变量来增强已保存按钮的生成代码。 For example, you can enhance saved buttons with automatic fill-out variables. 例如,您可以使用自动填充变量来增强已保存的按钮。

Source: https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/formbasics/#using-html-variables-with-saved-payment-buttons 来源: https//developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/formbasics/#using-html-variables-with-saved-payment-buttons

Don't use a hosted button if you are going to be changing values. 如果要更改值,请不要使用托管按钮。 Use an unhosted button. 使用未托管的按钮。 Create a new sample button on PayPal, in Step 2 uncheck the option to "Save" the button, and once the code is generated click to remove the code protection. 在PayPal上创建一个新的示例按钮,在步骤2中取消选中“保存”按钮的选项,并在生成代码后单击以删除代码保护。 Then you'll have something you can alter with dynamic code. 然后,您将可以使用动态代码进行更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM