Magento PayPal重复发票错误

Bri*_*VPS 11 php paypal magento

在尝试提交说明PayPal网关已拒绝请求的订单时,客户偶尔会遇到错误.由于提供了重复的发票ID,交易被拒绝. 在深入研究这一点之后,我相信我已经缩小了问题范围.在最近的一些案例中,客户曾试图在4个月前下订单,并从PayPal 收到内部错误.我从与PayPal的交谈中了解到,该客户的信用卡已被标记.当他们试图下第一个订单时,PayPal拒绝了它,但仍然认为我们的Magento商店提供的"使用"的发票ID.

快进到今天......同样的客户,新的订单.Magento STILL在sales_flat_quote表中列出了9月份的旧报价.当他们登录时,它加载了客户报价(仍然有效)并尝试将其用于此订单.这导致重复发票ID错误.

我在Mage_Sales_Model_Observer类中看到有一个cleanExpiredQuotes从cron作业调用的方法.但是,这仅影响"is_active"= 0的引号.由于此引用被认为是活动的,因此它永远不会被清除.

很明显,Magento代码和PayPal之间存在脱节.但就我所知,这就是我的意思.还有其他人经历过这个吗?如果有,有什么建议吗?

编辑:

我已经进一步了解这一点.我已经添加代码到结账的IndexController捕获错误,如果它是一个重复的发票错误,它会取消reserved_order_id在帖中一个调用saveOrderAction一次.这会导致报价保留新的订单ID,然后提交给PayPal.我现在遇到的问题是,当它第二次尝试使用新的发票编号时,所有总数都是0.我尝试将totals_collected_flag设置为false,以便重新收集总数,但它们在第二次总是为0通过.更具体地说,Mage_Sales_Model_Quote_Address中的总数是0,这是Mage_Sales_Model_Order最终使用的.Mage_Sales_Model_Quote中的总数是正确的,但它们会被collectTotals()引用的方法覆盖.

显然,在第一次尝试之后,某些东西正在取消所有的价值,但我不知道是什么或在哪里.如果有人有任何想法,我很乐意听到他们!

eas*_*00b 0

本质上发生的事情是,magento 正在向 paypal 发送一个已经在系统中付款的 orderId(发票号码)。这会导致 PayPal 返回一个响应,表明该发票号码重复。因此,我在这里所做的就是尝试检测该消息响应,生成新的 orderId,然后重新提交给 paypal 进行重新处理。

这是启动向 magento 发送信息的整个链的操作。它位于“Mage_Paypal_Controller_Express_Abstract”。我修改了贝宝响应生成的“令牌”。该令牌将包含有关发生的错误的信息。

startAction(){
    ...
    $token = $this->_checkout->start(Mage::getUrl('*/*/return'), Mage::getUrl('*/*/cancel'));
    if ($token && $url = $this->_checkout->getRedirectUrl()) {
        $this->_initToken($token);
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

到:

startAction(){
    ...
    $token = $this->_checkout->start(Mage::getUrl('*/*/return'), Mage::getUrl('*/*/cancel'));

    //while this token is invalid
    while (isset($token['error'])) {
        //generate a new token
        $token = $this->_checkout->start(Mage::getUrl('*/*/return'),Mage::getUrl('*/*/cancel'), TRUE);
    }
    if ($token['token'] && $url = $this->_checkout->getRedirectUrl()) {
        $this->_initToken($token['token']);
         ...
    }

}
Run Code Online (Sandbox Code Playgroud)

该令牌由“Mage_Paypal_Model_Express_Checkout”中的 start() 方法生成。start() 还处理对象操作的整个过程。在这里,我们将有条件地更改productId。

修改后的函数将如下所示:

public function start($returnUrl, $cancelUrl, $errorAgain = FALSE)
{
    $this->_quote->collectTotals();

    if (!$this->_quote->getGrandTotal() && !$this->_quote->hasNominalItems()) {
        Mage::throwException(Mage::helper('paypal')->__('PayPal does not support processing orders with zero amount. To complete your purchase, proceed to the standard checkout process.'));
    }
    if ($errorAgain) {
        Mage::log('why is this running?');
        $this->_quote->setReservedOrderId($this->_quote->getReservedOrderId($this->_quote));
        $this->_quote->reserveOrderId()->save();
    }
    //$this->_quote->setReservedOrderId($this->_quote->_getResource()->getReservedOrderId($this->_quote));
    //$this->_quote->setReservedOrderId($this->_quote->getReservedOrderId($this->_quote));
    $this->_quote->reserveOrderId()->save();
    // prepare API
    $this->_getApi();
    $this->_api->setAmount($this->_quote->getBaseGrandTotal())
        ->setCurrencyCode($this->_quote->getBaseCurrencyCode())
        ->setInvNum($this->_quote->getReservedOrderId())
        ->setReturnUrl($returnUrl)
        ->setCancelUrl($cancelUrl)
        ->setSolutionType($this->_config->solutionType)
        ->setPaymentAction($this->_config->paymentAction)
    ;
    if ($this->_giropayUrls) {
        list($successUrl, $cancelUrl, $pendingUrl) = $this->_giropayUrls;
        $this->_api->addData(array(
            'giropay_cancel_url' => $cancelUrl,
            'giropay_success_url' => $successUrl,
            'giropay_bank_txn_pending_url' => $pendingUrl,
        ));
    }

    $this->_setBillingAgreementRequest();

    if ($this->_config->requireBillingAddress == Mage_Paypal_Model_Config::REQUIRE_BILLING_ADDRESS_ALL) {
        $this->_api->setRequireBillingAddress(1);
    }

    // supress or export shipping address
    if ($this->_quote->getIsVirtual()) {
        if ($this->_config->requireBillingAddress == Mage_Paypal_Model_Config::REQUIRE_BILLING_ADDRESS_VIRTUAL) {
            $this->_api->setRequireBillingAddress(1);
        }
        $this->_api->setSuppressShipping(true);
    } else {
        $address = $this->_quote->getShippingAddress();
        $isOverriden = 0;
        if (true === $address->validate()) {
            $isOverriden = 1;
            $this->_api->setAddress($address);
        }
        $this->_quote->getPayment()->setAdditionalInformation(
            self::PAYMENT_INFO_TRANSPORT_SHIPPING_OVERRIDEN, $isOverriden
        );
        $this->_quote->getPayment()->save();
    }

    // add line items
    $paypalCart = Mage::getModel('paypal/cart', array($this->_quote));
    $this->_api->setPaypalCart($paypalCart)
        ->setIsLineItemsEnabled($this->_config->lineItemsEnabled)
    ;

    // add shipping options if needed and line items are available
    if ($this->_config->lineItemsEnabled && $this->_config->transferShippingOptions && $paypalCart->getItems()) {
        if (!$this->_quote->getIsVirtual() && !$this->_quote->hasNominalItems()) {
            if ($options = $this->_prepareShippingOptions($address, true)) {
                $this->_api->setShippingOptionsCallbackUrl(
                    Mage::getUrl('*/*/shippingOptionsCallback', array('quote_id' => $this->_quote->getId()))
                )->setShippingOptions($options);
            }
        }
    }

    // add recurring payment profiles information
    if ($profiles = $this->_quote->prepareRecurringPaymentProfiles()) {
        foreach ($profiles as $profile) {
            $profile->setMethodCode(Mage_Paypal_Model_Config::METHOD_WPP_EXPRESS);
            if (!$profile->isValid()) {
                Mage::throwException($profile->getValidationErrors(true, true));
            }
        }
        $this->_api->addRecurringPaymentProfiles($profiles);
    }

    $this->_config->exportExpressCheckoutStyleSettings($this->_api);

    // call API and redirect with token
    $response = $this->_api->callSetExpressCheckout();
    $token['token'] = $this->_api->getToken();
    $this->_redirectUrl = $this->_config->getExpressCheckoutStartUrl($token['token']);
    if ($response == 'duplicate') {
        $token['error'] = 'duplicate';
        return $token;
    } elseif (isset($token['error'])) {
        unset($token['error']);
    }
    $this->_quote->getPayment()->unsAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_BILLING_AGREEMENT);
    $this->_quote->getPayment()->save();
    return $token;
}
Run Code Online (Sandbox Code Playgroud)

现在,最后一部分处理实际的 paypal 调用和响应。这是通过位于“Mage_Paypal_Model_Api_Nvp”的 call() 函数完成的。

生成响应后,我们将检查错误响应,而不是重定向,我们只需将其返回到链上。

位于997号线附近:

if ($response['L_SHORTMESSAGE0'] == 'Duplicate invoice') {
    return $response;
}
Run Code Online (Sandbox Code Playgroud)

所以事情是这样的:

startaction()->start()->call()->start()->startaction()->redirect();
Run Code Online (Sandbox Code Playgroud)

如果存在重复输入错误,它将执行此操作。

startaction()->start()->call(error)->start()->call()->start()->staraction()->redirect();
Run Code Online (Sandbox Code Playgroud)

如果您有任何疑问,请告诉我。