Paypal-Bestellungen werden nach dem Zufallsprinzip storniert

7

Ich habe festgestellt, dass Paypal-Bestellungen auf meiner Website nach dem Zufallsprinzip storniert werden, obwohl der Kunde die Transaktion bezahlt und abgeschlossen hat. Zahlungsinformationen werden ausgefüllt und ich kann auch die IPN-registrierte Nachricht sehen.

  • 2 Apr 2017 09: 24: 21 | Abgebrochen
  • Kundenbenachrichtigung Nicht zutreffend
  • 2. April 2017 08: 44: 17 | Ausstehend
  • Kundenbenachrichtigung Nicht zutreffend
  • IPN "Abgeschlossen". Registrierte Benachrichtigung über den erfassten Betrag von £ 30,95. Transaktions-ID: ############# 2 Apr 2017 08: 43: 23 | Ausstehende Zahlung
  • Kunde nicht benachrichtigt

Dieser Auszug aus dem Kommentarverlauf zeigt 08:43, dass sich die Bestellung im Status "Ausstehende Zahlung" befand. Dann bestätigte 8,44 ipn die Erfassung der Zahlung. Die Bestellung steht noch aus. Ich habe Code geschrieben, um die automatische Rechnung zu stoppen.

Aber 40 Minuten später wurde abgesagt. Der Kunde hat nichts storniert. Die Zahlung erfolgt in Paypal. Wir auch nicht. Warum sollte das System dies tun?

Aktualisieren:

Hier ist ein weiteres Beispiel, bei dem es 2 Minuten und 52 Sekunden später abgebrochen wurde.

8 Dec 2017 00: 00: 51 | Abgebrochene Kundenbenachrichtigung nicht anwendbar 7 Dec 2017 23: 57: 59 | Ausstehende Kundenbenachrichtigung nicht anwendbar IPN "Abgeschlossen". Registrierte Benachrichtigung über den erfassten Betrag von £ xx.xx. Transaktions-ID: "xxxxxxxxxxx". 7. Dezember 2017 23: 57: 59 | Ausstehender Kunde benachrichtigt Benachrichtigter Kunde 7. Dezember 2017 23: 56: 56 | Ausstehende Zahlung Kunde nicht benachrichtigt

Mein Code zum Stoppen der automatischen Rechnungsstellung lautet wie folgt:

config.xml:

    <?xml version="1.0"?>
<config>
<modules>
    <Wfh_DisableAutoInvoice>
        <version>0.1.0</version>
    </Wfh_DisableAutoInvoice>
</modules>
<global>
    <models>
        <disableautoinvoice>
            <class>Wfh_DisableAutoInvoice_Model</class>
            <resourceModel>disableautoinvoice_mysql4</resourceModel>
        </disableautoinvoice>
        <sales>
            <rewrite>
        <order_payment>Wfh_DisableAutoInvoice_Model_Sales_Order_Payment</order_payment>
            </rewrite>
        </sales>
    </models>
</global>
</config> 

Meine Zahlung.php

<?php
    class Wfh_DisableAutoInvoice_Model_Sales_Order_Payment extends Mage_Sales_Model_Order_Payment
{   

/**
 * Authorize or authorize and capture payment on gateway, if applicable
 * This method is supposed to be called only when order is placed
 *
 * @return Mage_Sales_Model_Order_Payment
 */
public function place()
{
    Mage::dispatchEvent('sales_order_payment_place_start', array('payment' => $this));
    $order = $this->getOrder();

    $this->setAmountOrdered($order->getTotalDue());
    $this->setBaseAmountOrdered($order->getBaseTotalDue());
    $this->setShippingAmount($order->getShippingAmount());
    $this->setBaseShippingAmount($order->getBaseShippingAmount());

    $methodInstance = $this->getMethodInstance();
    $methodInstance->setStore($order->getStoreId());
    $orderState = Mage_Sales_Model_Order::STATE_NEW;
    $stateObject = new Varien_Object();

    /**
     * Do order payment validation on payment method level
     */
    $methodInstance->validate();
    $action = $methodInstance->getConfigPaymentAction();
    if ($action) {
        if ($methodInstance->isInitializeNeeded()) {
            /**
             * For method initialization we have to use original config value for payment action
             */
            $methodInstance->initialize($methodInstance->getConfigData('payment_action'), $stateObject);
        } else {
            $orderState = Mage_Sales_Model_Order::STATE_NEW;
            switch ($action) {
                case Mage_Payment_Model_Method_Abstract::ACTION_ORDER:
                    $this->_order($order->getBaseTotalDue());
                    break;
                case Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE:
                    $this->_authorize(true, $order->getBaseTotalDue()); // base amount will be set inside
                    $this->setAmountAuthorized($order->getTotalDue());
                    break;
                case Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE_CAPTURE:
                    $this->setAmountAuthorized($order->getTotalDue());
                    $this->setBaseAmountAuthorized($order->getBaseTotalDue());
                    $this->capture(null);
                    break;
                default:
                    break;
            }
        }
    }

    $this->_createBillingAgreement();

    $orderIsNotified = null;
    if ($stateObject->getState() && $stateObject->getStatus()) {
        $orderState      = $stateObject->getState();
        $orderStatus     = $stateObject->getStatus();
        $orderIsNotified = $stateObject->getIsNotified();
    } else {
        $orderStatus = $methodInstance->getConfigData('order_status');
        if (!$orderStatus) {
            $orderStatus = $order->getConfig()->getStateDefaultStatus($orderState);
        } else {
            // check if $orderStatus has assigned a state
            $states = $order->getConfig()->getStatusStates($orderStatus);
            if (count($states) == 0) {
                $orderStatus = $order->getConfig()->getStateDefaultStatus($orderState);
            }
        }
    }
    $isCustomerNotified = (null !== $orderIsNotified) ? $orderIsNotified : $order->getCustomerNoteNotify();
    $message = $order->getCustomerNote();

    // add message if order was put into review during authorization or capture
    if ($order->getState() == Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW) {
        if ($message) {
            $order->addStatusToHistory($order->getStatus(), $message, $isCustomerNotified);
        }
    } elseif ($order->getState() && ($orderStatus !== $order->getStatus() || $message)) {
        // add message to history if order state already declared
        $order->setState($orderState, $orderStatus, $message, $isCustomerNotified);
    } elseif (($order->getState() != $orderState) || ($order->getStatus() != $orderStatus) || $message) {
        // set order state
        $order->setState($orderState, $orderStatus, $message, $isCustomerNotified);
    }

    Mage::dispatchEvent('sales_order_payment_place_end', array('payment' => $this));

    return $this;
}


/**
 * Capture the payment online
 * Does not create invoice automatically for order
 * Updates transactions hierarchy, if required
 * Updates payment totals, updates order status and adds proper comments
 *
 * TODO: eliminate logic duplication with registerCaptureNotification()
 *
 * @return Mage_Sales_Model_Order_Payment
 * @throws Mage_Core_Exception
 */
public function capture($invoice)
{

    $invoice = null;
    $order = $this->getOrder();

    $amountToCapture = $this->_formatAmount($order->getBaseGrandTotal());

    $paidWorkaround = (float)$amountToCapture;
    $this->_isCaptureFinal($paidWorkaround);

    $this->_generateTransactionId(
        Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE,
        $this->getAuthorizationTransaction()
    );

    Mage::dispatchEvent('sales_order_payment_capture', array('payment' => $this, 'invoice' => $invoice));

    $status = true;
    if (!$this->getIsTransactionPending()) {
        // attempt to capture: this can trigger "is_transaction_pending"
        $this->getMethodInstance()->setStore($order->getStoreId())->capture($this, $amountToCapture);

        $transaction = $this->_addTransaction(
            Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE,
            $invoice,
            true
        );

        if ($this->getIsTransactionPending()) {
            $message = Mage::helper('sales')->__('Capturing amount of %s is pending approval on gateway.', $this->_formatPrice($amountToCapture));
            $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
            if ($this->getIsFraudDetected()) {
                $status = Mage_Sales_Model_Order::STATUS_FRAUD;
            }
        } else { // normal online capture: invoice is marked as "paid"
            $message = Mage::helper('sales')->__('Captured amount of %s online.', $this->_formatPrice($amountToCapture));
            $state = Mage_Sales_Model_Order::STATE_NEW;
            $this->_updateTotals(array('base_amount_paid_online' => $amountToCapture));
        }
        if ($order->isNominal()) {
            $message = $this->_prependMessage(Mage::helper('sales')->__('Nominal order registered.'));
        } else {
            $message = $this->_prependMessage($message);
            $message = $this->_appendTransactionToMessage($transaction, $message);
        }
        $order->setState($state, $status, $message);

        return $this;
    }
    Mage::throwException(
        Mage::helper('sales')->__('The transaction "%s" cannot be captured yet.', $amountToCapture)
    );
}

/**
 * Process a capture notification from a payment gateway for specified amount
 * Does not create an invoice automatically if the amount if the amount covers the order base grand total completely
 * Updates transactions hierarchy, if required
 * Prevents transaction double processing
 * Updates payment totals, updates order status and adds proper comments
 *
 * TODO: eliminate logic duplication with capture()
 *
 * @param float $amount
 * @param bool $skipFraudDetection
 * @return Mage_Sales_Model_Order_Payment
 */
public function registerCaptureNotification($amount, $skipFraudDetection = false)
{

    $this->_generateTransactionId(Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE,
        $this->getAuthorizationTransaction()
    );

    $order   = $this->getOrder();
    $amount  = (float)$amount;
    $invoice = null;

    // register new capture
    if (!$invoice) {
        $isSameCurrency = $this->_isSameCurrency();
        if (!$isSameCurrency && !$this->_isCaptureFinal($amount)) {
            if (!$skipFraudDetection || !$isSameCurrency) {
                $this->setIsFraudDetected(true);
            }
            $this->_updateTotals(array('base_amount_paid_online' => $amount));
        }
    }

    $status = true;
    $letsEmail = false;
    if ($this->getIsTransactionPending()) {
        $message = Mage::helper('sales')->__('Capturing amount of %s is pending approval on gateway.', $this->_formatPrice($amount));
        $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
        $letsEmail = true;
        if ($this->getIsFraudDetected()) {
            $message = Mage::helper('sales')->__('Order is suspended as its capture amount %s is suspected to be fraudulent.', $this->_formatPrice($amount, $this->getCurrencyCode()));
            $status = Mage_Sales_Model_Order::STATUS_FRAUD;
            $letsEmail = false;
        }
    } else {
        $message = Mage::helper('sales')->__('Registered notification about captured amount of %s.', $this->_formatPrice($amount));
        $state = Mage_Sales_Model_Order::STATE_NEW;
        $letsEmail = true;
        if ($this->getIsFraudDetected()) {
            $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
            $message = Mage::helper('sales')->__('Order is suspended as its capture amount %s is suspected to be fraudulent.', $this->_formatPrice($amount, $this->getCurrencyCode()));
            $status = Mage_Sales_Model_Order::STATUS_FRAUD;
            $letsEmail = false;
        }
    }

    $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE, $invoice, true);
    $message = $this->_prependMessage($message);
    $message = $this->_appendTransactionToMessage($transaction, $message);
    $order->setState($state, $status, $message);
    if ($letsEmail === true && !$order->getEmailSent()){
         $order->queueNewOrderEmail()->addStatusHistoryComment(
                Mage::helper('paypal')->__('Notified Customer')
            )
            ->setIsCustomerNotified(true)
            ->save();
    }

    return $this;
}
}
?>
Vaishal Patel
quelle
Haben Sie jemals eine Lösung für dieses Problem bekommen?
ProxiBlue
ProxiBlue bis zu diesem Datum noch kein Soliton. Ich habe gerade ein Skript zum Abbrechen erstellt
Vaishal Patel
Ich benutze CCAvenue und bekomme den gleichen Fehler. Haben Sie eine Lösung gefunden?
zus
@zus Bisher keine Lösung. Platzieren Sie eine Debug-Ablaufverfolgung und protokollieren Sie das Ergebnis in einer Datei. Wenn es für Sie erneut passiert, wird angezeigt, wo es zur Stornierung aufgerufen wird.
Vaishal Patel

Antworten:

1

Wir haben dieses Problem gelegentlich auftreten

Nachdem ich die Paypal-Abbruchaktion mit einem Debug-Trace versehen habe, habe ich die folgenden Informationen erhalten:

Geben Sie hier die Bildbeschreibung ein

Dies zeigt deutlich, dass die Abbruchaktion über das aufgerufen wurde Mage_Paypal_StandardController

Dies ist also ein externer Anruf, um die Bestellung zu stornieren.

Wenn ich die eigentlichen Webserver-Protokolle finde, kann ich sehen, dass der Anruf tatsächlich von extern kam (Paypal).

Geben Sie hier die Bildbeschreibung ein

Das Interessante daran ist, dass es sich um eine 302 (Weiterleitung) handelte.

Wenn ich diese Referrer-URL lade, wird eine Paypal-Anzeige angezeigt, für die die Sitzung abgelaufen ist.

Ich konnte auch feststellen, dass dies von einem iPad war. Ich habe nicht genug davon, um andere Geräte zu bestimmen, also könnte das Kniebeugen bedeuten.

Aus meiner Sicht gehe ich davon aus, dass diese Abbruchaktion von Paypal aus initiiert wird, wenn / wenn die Benutzersitzung auf der Paypal-Seite eine Zeitüberschreitung aufweist oder Probleme beim Aufrechterhalten der Sitzung während des Transaktionszeitraums aufgetreten sind.

Paypal handelt dann eine Stornierungsanfrage?

Geben Sie hier die Bildbeschreibung ein

UPDATE: Trace festlegen:

Laut Kommentaranforderung habe ich hier die Ablaufverfolgung platziert. Wenn die Bestellung storniert wird, wird ein Kommentar in die Bestellhistorie / Kommentare zu dieser Stornierung eingefügt. Hier habe ich meinen Trace-Code eingefügt und so den Code-Pfad zurückverfolgt, bis ich diese Kommentareinfügung erreicht habe.

Es kann genauso gut in eine Protokolldatei verschoben werden.

Ich habe dafür ein kleines Modul erstellt:

Ich habe einfach das Kernmodul für Kundenaufträge neu geschrieben (beachten Sie, dass ein Umschreibungskonflikt entsteht, wenn ein anderes Modul eines Drittanbieters diese Klasse ebenfalls neu schreibt. In meinem Fall war dies kein Problem:

Im Modul config.xml

<global>
        <models>
            <enjo_sales>
                <class>Enjo_Sales_Model</class>
            </enjo_sales>
            <sales>
                <rewrite>
                    <order>Enjo_Sales_Model_Order</order>
                </rewrite>
            </sales>
        </models>
    </global>

und dann in der Modellklasse:

<?php

class Enjo_Sales_Model_Order extends Mage_Sales_Model_Order
{
    protected function _setState($state, $status = false, $comment = '',
                                 $isCustomerNotified = null, $shouldProtectState = false)
    {
        if ($this->getState() != $state && $state == self::STATE_CANCELED) {
            $comment .= Varien_Debug::backtrace(true,true, true);
        }
        return parent::_setState($state, $status, $comment, $isCustomerNotified, $shouldProtectState);

    }
}
ProxiBlue
quelle
Interessanterweise handelt es sich bei 302 Weiterleitungen um Nicht-Permenet-Weiterleitungen. Könnte es wert sein, Paypal zu kontaktieren.
Vaishal Patel
In einem Zeitraum von 6 Monaten sind nur zwei Fälle aufgetreten. Ich habe den Debug-Trace nach dem ersten platziert, da ich nicht herausfinden konnte, warum es passiert. Unsere derzeitige Ansicht ist es, etwas zu überwachen und zu handeln, wenn es häufiger wird. Wir haben täglich Hunderte von PayPal-Transaktionen. Es wäre von Interesse, anhand Ihrer häufiger auftretenden Instanzen zu überprüfen, ob dies für bestimmte Geräte spezifisch ist, z.
B
Möchten Sie, dass ich etwas Bestimmtes debugge?
Vaishal Patel
Nur neugierig, ob das Problem für eine Reihe von Geräten spezifisch ist, zum Beispiel nur für IPads, oder ob es über Desktops, Android usw. verteilt ist
ProxiBlue
Hauptsächlich habe ich Desktop-Benutzer, aber es kommt nicht häufig vor, dass es zeitweise auftritt.
Vaishal Patel
0

Durch die Integration von Magento in PayPal Express (nicht PayPal-Standard) können Benutzer die Produkte unseres Shops kaufen.

Es gibt jedoch zwei verschiedene Situationen: 1) Wenn der Kunde ein Paypal-Konto hat, kann er den Kaufvorgang ohne Probleme abschließen. 2) Wenn der Kunde jedoch kein Paypal-Konto hat oder es nicht verwenden möchte und die Produkte lieber mit seiner Kreditkarte ohne Verwendung der PayPal-Anmeldeinformationen kaufen möchte, wird der Vorgang wie folgt abgeschlossen Fehler: "Diese Zahlung kann derzeit nicht über Ihr Paypal-Konto verarbeitet werden."

Das Problem ist, dass der Verkäufer das Geld erhält, der Kunde jedoch genau das Gegenteil denkt.

Dieses Problem wurde vom PayPal-Supportteam behoben und bestätigt.

Das eigentliche Problem ist, dass während des Kaufvorgangs die Funktion DoExpressCheckoutPayment PayPal aufgerufen wird. Wenn der Kaufvorgang jedoch mit diesem Fehler abgeschlossen ist, wird ein Parameter namens successpageredirectrequested auf true gesetzt . Dieses Flag zeigt an, ob Sie den Kunden nach Abschluss der Transaktion zu PayPal umleiten müssen. Dies ist jedoch ein veraltetes PayPal-Verhalten und sollte nicht verwendet werden.

Da Magento die Funktion dieses Parameters verwendet, gibt PayPal diesen Fehler zurück , wenn er versucht, erneut zu PayPal umzuleiten (dies ist der Vorgang , bei dem successpageredirectrequested auf true gesetzt ist).

Wir können das Problem beheben, indem wir den Quellcode von Magento unter ändern

App / Code / Core / Magier / Paypal / Controller / Express / Abstract.php

Einfach die Funktion ändern public function placeOrderAction(). Suchen Sie in dieser Funktion nach folgendem Code:

// umleiten, wenn PayPal eine URL angegeben hat (z. B. zur Giropay Bank)

$url = $this->_checkout->getRedirectUrl();
  if ($url) {
  $this->getResponse()->setRedirect($url);
  return;
  }
  $this->_initToken(false); // no need in token anymore
  $this->_redirect(?checkout/onepage/success?);
  return;
  }

Wir müssen nur das "Wenn" kommentieren, einfach so:

//if ($url) {
//$this->getResponse()->setRedirect($url);
// return;
//}

Das reicht aus, um das Problem zu beheben. Dies ist jedoch kein aktueller Fix, und dies sollte von den Programmierern von Magento überprüft werden, um ihn in den neuen Versionen von Magento zu beheben.

Riddhish Bhayani
quelle
Dies ist für Paypal Express, benötigen eine Lösung für den Standard.
Vaishal Patel
noch keine lösung?
Vaishal Patel
Ich verwende CCAvenue PG und erhalte den gleichen Fehler. Kann ich Hilfe erhalten, danke magento.stackexchange.com/q/300609/57334 @Riddhish Bhayani, Piyush
zus