Bei den Zahlungsarten wie PayPal, Kreditkarten usw. wird ja nach der Bestellbestätigung auf die Bezahlseite verzweigt. In der checkout_process.php wird dabei eine Bestellung angelegt, und die Lagerbestände werden aktualisiert. Wenn die Zahlung dann aber abgebrochen wird, sind m.E. die Lagerbestände falsch, da die ja schon vor der Verzweigung zur Zahlungsseite aktualisiert, und bei Fehlschlagen der Zahlung nicht korrigiert werden. Habe ich etwas übersehen? EDIT: Diese Daten dürfen also erst nach wirklich positivem Bestellabschluss aktualisiert werden. Ebenso die Zuordnung zu Kampagnen.
Keine Meinung dazu von der Gambio GmbH? Wenn meine Erkenntnis stimmt (wovon ich mal ausgehe), muss das sicher schnell gelöst werden!
Das gleiche Problem wie bei den Bestellbestätigungsmails! Diese werden teils versendet obwohl die Zahlung nicht durch ist! Ich hab das bei uns in die checkout_succsess.php ausgelagert!
Das wundert mich eigentlich..... Weil im Fehlerfall die "checkout_process.php" nicht gestartet wird (in der die eMails verschickt werden), sondern die Zahlungsseite..
Ja, genau das ist das Problem,,,, m.E. kann es aber keine unterschiedlichen Ansichten geben, ob das richtig oder falsch ist: es ist definitiv falsch! Es darf natürlich nicht sein, dass der Lagerbestand verändert wird, bevor sicher ist, dass der Kunde auch bezahlt hat. Und das Problem ist auch einfach lösbar: man darf den Bestand erst dann anpassen, wenn die Bezahlung erfolgt ist. Und das ist einfach realisierbar, auch innerhalb der "checkout_process.php". In der "checkout_succes.php" ist es insofern schwieriger, weil der Warenkorb nicht mehr existiert.
Das ist ja richtig, aber das senden der Mails ist auch in der checkout_process.php und die wird ja aufgerufen! Wir haben dies geändert, sodas die mails erst in der checkout_succsess.php versendet werden. Keine Zahlung, keine Mails!
Aber im Fehlerfall wird ja die "checkout_process.php" nicht aktiviert, sondern die Zahlungsseite "checkout_payment.php" Und nur in der "checkout_process.php" werden die Mails versendet, und nur nach erfolgreicher Zahlung! Aus meiner Sicht gibt es nur das Problem mit der Lagerbestandsführung, die zu früh erfolgt. Verwendest Du Deinen "one-page-checkout"? Gibt es evtl. da ein Problem?
Es könnte am OPC liegen, naja wir haben es geändert und nu passt es ja. Das mit dem Lagerbestand ist aber auch beim OPC der Fall, was etwas nervt!
Ich habe mir das mal angesehen, und folgende Problemlösung erarbeitet: die Idee ist, dass man im Falle einer externen Zahlungsart die Lagerbestand-SQLs, die erzeugt werden, nicht direkt anwendet, sondern in der Session zwischenspeichert. Und die erst dann anwendet, wenn der Zahlungs-Prozess erfolgreich abgeschlossen ist. Dann kann man die vorhandene Logik weitestgehend beibehalten, und muss nur moderat in den Code eingreifen. In "checkout_process.php" folgendes ändern: Die beiden folgenden Änderungen sind notwendig, weil bei mir das verwendete "$$_SESSION['payment']" "NULL" ist, und so durch "$GLOBALS[$_SESSION['payment']]" ersetzt wird... (Mit "$$_SESSION['payment']" verlässt man sich m.E. darauf, dass "register_globals" auf "on" gesetzt ist.) Vor PHP: if (isset ($_SESSION['tmp_oID']) && is_int($_SESSION['tmp_oID'])) { einfügen PHP: //Avenger$payment=$GLOBALS[$_SESSION['payment']];//Avenger Alle PHP: $$_SESSION['payment'] ersetzen mit PHP: $payment PHP: xtc_db_query("update ".TABLE_PRODUCTS." set products_quantity = '".$stock_left."' where products_id = '".xtc_get_prid($order->products[$i]['id'])."'"); // BOF GM_MOD: if (($stock_left < 1) && (STOCK_ALLOW_CHECKOUT == 'false') && GM_SET_OUT_OF_STOCK_PRODUCTS == 'true') { xtc_db_query("update ".TABLE_PRODUCTS." set products_status = '0' where products_id = '".xtc_get_prid($order->products[$i]['id'])."'"); } ersetzen mit PHP: //Avenger //Delayed stock updates //For external payment-methods adjust stock only after real payment! //In that case store updates sql in session and do not apply directly! $update="update ".TABLE_PRODUCTS." set "; $where=" where products_id = '".xtc_get_prid($order->products[$i]['id'])."'"; $stock_update_sql=$update."products_quantity = '".$stock_left."'".$where; $status_update_sql=$update."products_status = '0' ".$where; $no_stock_left=$stock_left < 1 && (STOCK_ALLOW_CHECKOUT == 'false') && GM_SET_OUT_OF_STOCK_PRODUCTS == 'true'; if ($tmp) { $_SESSION{'stock_update_sql'}[]=$stock_update_sql; // BOF GM_MOD: if ($no_stock_left) { $_SESSION{'status_update_sql'}[]=$status_update_sql; } } else { xtc_db_query($stock_update_sql); // BOF GM_MOD: if ($no_stock_left) { xtc_db_query("update ".TABLE_PRODUCTS." set products_status = '0' where products_id = '".xtc_get_prid($order->products[$i]['id'])."'"); } } //Avenger Nach PHP: if (!$tmp) { einfügen PHP: //Avenger //Delayed stock updates //For external payment-methods adjust stock only after real payment! //In that case updates sql have been stored in session and not have been applied directly! $updates_sql_names=array('stock_update_sql','status_update_sql'); foreach ($updates_sql_names as $updates_sql_name) { $update_sqls=$_SESSION{$updates_sql_name}; if ($update_sqls) { foreach ($update_sqls as $update_sql) { xtc_db_query($update_sqls); } unset($_SESSION{$updates_sql_name}); } } //Avenger Wie immer gilt: Anwendung ausschließlich auf eigenes Risiko des Verwenders. Eine Gewährleistung jeglicher Art ist ausgeschlossen. Vor Einbau eine Sicherung der Shop-Programme vornehmen. Erst im Testshop testen.
Das war noch nicht ganz vollständig..... In "checkout_process.php" folgendes ändern: Nach PHP: Copyright (c) 2012 Gambio GmbH einfügen PHP: Copyright (c) 2013 Avenger, entwicklung@powertemplate.de Delayed stock updates For external payment-methods adjust stock only after real payment! In that case store updates sql in session and do not apply directly! Die beiden folgenden Änderungen sind notwendig, weil bei mir das verwendete "$$_SESSION['payment']" "NULL" ist, und so durch "$GLOBALS[$_SESSION['payment']]" ersetzt wird... (Mit "$$_SESSION['payment']" verlässt man sich m.E. darauf, dass "register_globals" auf "on" gesetzt ist.) Vor PHP: if (isset ($_SESSION['tmp_oID']) && is_int($_SESSION['tmp_oID'])) { einfügen PHP: //Avenger$payment=$GLOBALS[$_SESSION['payment']];$updates_names=array('stock_update_sql','status_update_sql','properties_update_data','attributes_update_sql');//Avenger Alle PHP: $$_SESSION['payment'] ersetzen mit PHP: $payment Nach PHP: $tmp_status = $payment->tmpStatus; einfügen PHP: //Avenger //Initialize stock update parameters arrays foreach ($updates_names as $updates_name) { $_SESSION{$updates_name}=array(); } //Avenger PHP: xtc_db_query("update ".TABLE_PRODUCTS." set products_quantity = '".$stock_left."' where products_id = '".xtc_get_prid($order->products[$i]['id'])."'"); // BOF GM_MOD: if (($stock_left < 1) && (STOCK_ALLOW_CHECKOUT == 'false') && GM_SET_OUT_OF_STOCK_PRODUCTS == 'true') { xtc_db_query("update ".TABLE_PRODUCTS." set products_status = '0' where products_id = '".xtc_get_prid($order->products[$i]['id'])."'"); } ersetzen mit PHP: //Avenger //Delayed stock updates //For external payment-methods adjust stock only after real payment! //In that case store updates sql in session and do not apply directly! $update="UPDATE ".TABLE_PRODUCTS." SET "; $products_id=xtc_get_prid($order->products[$i]['id']); $where=" WHERE products_id = '".$products_id."'"; $stock_update_sql=$update."products_quantity = '".$stock_left."'".$where; $status_update_sql=$update."products_status = '0' ".$where; $no_stock_left=$stock_left < 1 && (STOCK_ALLOW_CHECKOUT == 'false') && GM_SET_OUT_OF_STOCK_PRODUCTS == 'true'; if ($tmp) { $_SESSION{'stock_update_sql'}[]=$stock_update_sql; // BOF GM_MOD: if ($no_stock_left) { $_SESSION{'status_update_sql'}[]=$status_update_sql; } } else { xtc_db_query($stock_update_sql); // BOF GM_MOD: if ($no_stock_left) { xtc_db_query(xtc_db_query($status_update_sql)); } } //Avenger PHP: if(($t_use_combis_quantity == 0 && STOCK_CHECK == 'true' && ATTRIBUTES_STOCK_CHECK == 'true') || $t_use_combis_quantity == 2){ $t_quantity_change = $order->products[$i]['qty'] * -1; $val = $coo_properties->change_combis_quantity($t_combis_id, $t_quantity_change); } ersetzen mit PHP: if(($t_use_combis_quantity == 0 && STOCK_CHECK == 'true' && ATTRIBUTES_STOCK_CHECK == 'true') || $t_use_combis_quantity == 2){ //Avenger //Delayed stock updates //For external payment-methods adjust stock only after real payment! $t_quantity_change = -$order->products[$i]['qty']; if ($tmp) { $_SESSION['properties_update_data'][$t_combis_id]=$t_quantity_change; } else { $val = $coo_properties->change_combis_quantity($t_combis_id, $t_quantity_change); } //Avenger } PHP: xtc_db_query("UPDATE ".TABLE_PRODUCTS_ATTRIBUTES." set attributes_stock=attributes_stock - '".$order->products[$i]['qty']."' where products_id='".$order->products[$i]['id']."' and options_values_id='".$order->products[$i]['attributes'][$j]['value_id']."' and options_id='".$order->products[$i]['attributes'][$j]['option_id']."' "); ersetzen mit PHP: //Avenger //Delayed stock updates //For external payment-methods adjust stock only after real payment! $sql=" UPDATE ".TABLE_PRODUCTS_ATTRIBUTES." SET attributes_stock=attributes_stock - '".$order->products[$i]['qty']."' WHERE products_id='".$products_id."' AND options_values_id='".$order->products[$i]['attributes'][$j]['value_id']."' AND options_id='".$order->products[$i]['attributes'][$j]['option_id']."'"; if ($tmp) { $_SESSION['attributes_update_sql'][]=$sql; } else { xtc_db_query($sql); } //Avenger Nach PHP: if (!$tmp) { einfügen PHP: //Avenger //Delayed stock updates //For external payment-methods adjust stock only after real payment! //In that case updates sql have been stored in session and not have been applied directly! foreach ($updates_names as $updates_name) { $update_sqls=$_SESSION{$updates_name}; if ($update_sqls) { if ($updates_name=='properties_update_data') { $coo_properties = MainFactory::create_object('PropertiesControl'); foreach ($update_sqls as $t_combis_id=>$t_quantity_change) { $val = $coo_properties->change_combis_quantity($t_combis_id, $t_quantity_change); } } else { foreach ($update_sqls as $update_sql) { xtc_db_query($update_sqls); } } } unset($_SESSION{$updates_name}); } //Avenger Wie immer gilt: Anwendung ausschließlich auf eigenes Risiko des Verwenders. Eine Gewährleistung jeglicher Art ist ausgeschlossen. Vor Einbau eine Sicherung der Shop-Programme vornehmen. Erst im Testshop testen.
Ist das updatesicher? Ha, erwischt! Ich hoffe ja, dass Gambio da in sehr naher Zukunft eine Lösung bringt. Beschrieben wurde die hier: Nach Zahlungsabbrüchen - Wann Lagerbestand berechnen, wann nicht? Es wäre toll, wenn Du dort auch Deine Meinung nochmal kundtust und den Vorschlag von Daniel kommentierst. Danke.
Ich kann ja schon ganz schön viel, aber zaubern (noch?) nicht. Allerdings mit meinem USERMOD-Konzept kann man auch das updatesicher machen, da man im USERMOD-Bereich ein modifizierte Kopie davon anlegen kann. Die Lösung liegt ja vor....
Ich habe mich heute mal drangesetzt und im Testshop Deine Erweiterung installiert. Leider bekomme ich einen SQL-Fehler: ================================================================================ 2014-03-15 12-51-02 (0.0.0.0) WARNING(2): ltrim() expects parameter 1 to be string, array given in /www/htdocs/localhost/gx2/inc/xtc_db_query.inc.php:48 Backtrace: #0 ltrim called at [/www/htdocs/localhost/gx2/inc/xtc_db_query.inc.php:48] #1 xtc_db_query called at [/www/htdocs/localhost/gx2/checkout_process.php:683] ================================================================================ 2014-03-15 12-51-02 (0.0.0.0) WARNING(2): mysql_query() expects parameter 1 to be string, array given in /www/htdocs/localhost/gx2/inc/xtc_db_query.inc.php:68 Backtrace: #0 mysql_query called at [/www/htdocs/localhost/gx2/inc/xtc_db_query.inc.php:68] #1 xtc_db_query called at [/www/htdocs/localhost/gx2/checkout_process.php:683] ================================================================================ ================================================================================ Query: Array Error: (error 0) ================================================================================ 2014-03-15 12-51-02 (0.0.0.0) WARNING(512): SQL Error in /www/htdocs/localhost/gx2/inc/xtc_db_error.inc.php:33 Backtrace: #0 trigger_error called at [/www/htdocs/localhost/gx2/inc/xtc_db_error.inc.php:33] #1 xtc_db_error called at [/www/htdocs/localhost/gx2/inc/xtc_db_query.inc.php:68] #2 xtc_db_query called at [/www/htdocs/localhost/gx2/checkout_process.php:683] Ich habe 2.0.14.4 und es handelte sich um eine Paypal-Sandbox-Zahlung. Vorkasse/Überweisung funktionierte übrigens. Hast Du ne Idee, woran das liegen könnte?
Avenger, endlich mal einer, der mich versteht! Das mit den Lagerbeständen ist ein riesiges Problem, wenn man nicht als Dropshipper mit unendlichen Lagerbeständen arbeitet. Was mir auch aufgefallen ist - was ich bisher aber nicht kapiert habe - ist folgendes: Wir haben das PayPal-Gold Modul. Selbst damit werden im Shop Bestellungen generiert, die bold bleiben (also die Bestellbestätigungen nicht verschickt wurden). Die Lagerbestände werden abgezogen. Das Spiel können Kunden bis zum Ende des Lagerbestandes spielen. Dein Ansatz könnte die Lösung auch dafür sein. Allerdings verstehe ich es immer noch nicht so richtig, denn die Kunden müssen die Bestellung erst abschicken, dann erst kommen sie zum PP-Link. Also müsste die Bestellbestätigung doch definitiv versendet werden?
Marco hat dazu (Link nur für registrierte Nutzer sichtbar.) was geschrieben. Bei den Zahlarten die extern ablaufen wird die Bestätigung erst verschickt, wenn der Kunde wieder zurück im Shop ist.
Das hatten wir ja auf dem User-Meeting schon diskutiert.... Dein Ansatz ist eigentlich logisch, weil die Bestellung im Prinzip nicht mit der Zahlung verbunden ist. Daher wäre es logisch direkt nach Bestellung die Bestätigung zu versenden, statt auf die erfolgreiche Zahlung zu warten. Gambio arbeitet aber nicht so, sondern bestätigt bei externen Zahlungen erst, wenn die Zahlung Erfolg hatte.
Ist es nicht so, das sobald der Kunde auf "bestellen" gedrückt hat, auch der Vertrag gilt? Egal ob die Zahlung abbricht oder nicht? Dann ist doch der Kunde dazu verpflichtet zu zahlen egal wie... (mal abgesehen vom Widerrufsrecht) Also macht es doch sinn, das der Lagerbestand singt.. oder steh ich da auf dem Schlauch?
In vielen Fällen sind die Shop-AGB als "Invitatio ad offerendum" ausgelegt. D.h., der Kunde gibt mit der Bestellung lediglich seine Willenserklärung ab. Der Verkäufer behält sich das Recht vor, die Bestellung anzunehmen oder abzulehnen. Jedoch macht es meiner Meinung nach dennoch auch in vielen Fällen des Invitatio ad offerendum Sinn, den Lagerbestand sofort mit Bestellung sinken zu lassen - es kommt auf die Art der Produkte und der Konfektionierung an. Gambio hatte doch mal den Vorschlag gemacht, dem Shop-Betreiber beide Varianten zur Auswahl zu stellen - wahlweise Lagerbestand sinken bei Bestellung oder bei externer Zahlung. Diese Auswahl scheint mir die beste Lösung.
Im Moment führt es aber dazu, dass der Lagerbestand auch sinkt, wenn die Bezahlung abgebrochen wird, und der Besteller auf Nimmerwiedersehen.verschwindet....