Ответ 1
Что в основном происходит, так это то, что magento отправляет paypal номер заказа (номер счета-фактуры), который уже был оплачен в системе. Это заставляет paypal возвращать ответ, который указывает, что этот номер счета-фактуры является дубликатом. Итак, то, что я делаю здесь, пытается обнаружить этот ответ сообщения, сгенерировать новый orderId и повторно отправить на paypal для переработки.
Вот действие, которое запускает целую цепочку отправки информации в magento. Он расположен в 'Mage_Paypal_Controller_Express_Abstract'. Я изменил "токен", сгенерированный paypal-ответом. Этот токен будет содержать информацию об ошибке, которая произошла.
startAction(){
...
$token = $this->_checkout->start(Mage::getUrl('*/*/return'), Mage::getUrl('*/*/cancel'));
if ($token && $url = $this->_checkout->getRedirectUrl()) {
$this->_initToken($token);
...
}
}
в
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']);
...
}
}
Этот токен генерируется методом start() в 'Mage_Paypal_Model_Express_Checkout'. 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;
}
Теперь, заключительная часть обрабатывает фактический вызов и ответ в PayPal. Это делается с помощью функции call(), расположенной в 'Mage_Paypal_Model_Api_Nvp'.
После генерации ответа мы проверим ответ на ошибку и вместо перенаправления просто вернем его в цепочку.
расположенный вокруг строки 997:
if ($response['L_SHORTMESSAGE0'] == 'Duplicate invoice') {
return $response;
}
Итак, это выглядит так:
startaction()->start()->call()->start()->startaction()->redirect();
если есть повторяющаяся ошибка ввода, это сделает это.
startaction()->start()->call(error)->start()->call()->start()->staraction()->redirect();
Сообщите мне, если у вас есть какие-либо вопросы.