Ma futottam bele egy érdekes problémába, gondoltam megosztom másokkal is, mert szerintem elég nagy a zavar a fejekben – legalábbis az internet népét és a Stackoverflow felhasználóit tekintve.
Adott egy WooCommerce (4.8, de amúgy szerintem majdnem mindegy). A feladat, hogy a WooCommerce-ben leadott rendelés után hajtsunk végre valamit.
Erre több lehetőségünk is van. Mivel a WooCommerce rendelés gyakorlatilag egy egyedi WordPress bejegyzés (shop_order), ezért van neki post_status mezője. Ezeknek a szabványos WordPress bejegyzésekhez hasonlóan többféle státusza van, sőt lehet neki egyedit is adni. Általában alapértelmezetten a wc-processing (feldolgozás alatti) státuszt kapja meg. Egy SQL lekérdezéssel ellenőrizhetjük, hogy egy adott WooCommerce webáruházunkban hány fajta státuszú rendelés létezik:
SELECT post_status
FROM `wp_posts`
WHERE `post_type` = 'shop_order'
GROUP BY post_status
A Woocommerce rendelés státuszokról és magáról a Order objektumról itt olvashatsz bővebben.
Azért kellett ez a rövid bevezető, mert nem mindegy, hogy a rendelések mely státuszánál, mi fusson le. A Woocommerce erre többféle beépített hook-ot kínál. Mi az a hook? Ez gyakorlatilag egy callback. Mi az a callback? Na igen, ez az amikor 15 dolgot el kell magyarázni, hogy a végén egy szót megérts 🙂 A Callback-ről magyarul itt olvashatsz bővebben. A WordPress hook-okról pedig itt.
A rendelés leadása utáni tranzakciókezelésre a Woocommerce többféle hook megoldást is nyújt, például ezeket:
- woocommerce_checkout_order_processed
- woocommerce_payment_complete
- woocommerce_thankyou
Több, eltérő leírás van erről a neten, sőt a Woocommerce verziók között is vannak eltérések. Arra is kell vigyázni és ezt sokan elrontják, hogy minden hook-nak eltérő mennyiségű paramétere van. Ha többet adsz át az nem gond, de ha kevesebbet, akkor le sem fut. Sajnos vannak olyan hook-ok, amik nem dobnak hibát, csak egyszerűen nem működnek. Az alábbi példában például a 10, 3 a végén azt jelenti, hogy 3 paramétert vár és 10 prioritással fusson le. Mivel többet is egymásba ágyazhatsz, és az egyik felülírhatja a másikat, hogy ne legyen olyan egyszerű az élet. Főleg akkor jó ez, ha egy másik plugin már használja valami unintelligens 99999 prioritással és te meg nem jössz rá, hogy a tiéd miért nem működik… 😀
Amibe ma belefutottam, az egy elég egyszerűnek tűnő probléma. Én általában a woocommerce_checkout_order_processed hook-ot használom. Ez három paramétert vár. Az 1. a Woocommerce rendelés azonosító (wc_order_id), a 2. az elküldött személyes adatok (posted_data), a 3. pedig maga a rendelés objektum (order class):
add_action( 'woocommerce_checkout_order_processed', 'exibio_woocommerce_order_processed', 10, 3 );
function exibio_woocommerce_order_processed( $wc_order_id, $posted_data, $order ) {
// ez fut le a rendelés leadása után
}
Ez a hook akkor fut le, ha a rendelés a processing státuszára már átváltott (tehát a wc-processed után). Sajnos vannak olyan bővítmények, amelyek rosszul használják a rendeléshez tartozó és elmentett item meta data, azaz a rendelés tétel meta adatok (ezek általában a rendeléshez tartozó egyedi mezők, paraméterek, akár a felhasználók által átadott változók, stb.) elmentését és nem a woocommerce_checkout_order_processed
hook-ot használják, hanem a woocommerce_thankyou
hook-ot. Nem ugyanarra való, de sajnos sokan összekeverik vagy helytelenül használják.
Emiatt hiába vártam egy plugin által összegyűjtött mezőit a rendelés meta adatait között, az csak a rendelés leadása utáni pillanatban vált elérhetővé. Ez azért volt, mert a plugin nem a processed utáni pillanatban mentette el a meta adatokat, hanem a thank you endpoint utáni pillanatban. Ez azt okozta, hogy a hook utáni iterálásnál on-the-fly hiába kérdeztem le az order_itemmeta táblát, nyoma sem volt a plugin által elmentett adatoknak.
Először a woocommerce_checkout_order_processed hook után egy sima SQL lekérdezést használtam (az item_id egyébként az order objektumon belüli változó). Az $items objektum iterálása SQL lekérdezéssel:
global $wpdb;
$items = $order->get_items();
foreach ($items as $item_id => $item) {
$sql = "
SELECT meta_value
FROM wp_woocommerce_order_itemmeta
WHERE `order_item_id` = {$item_id}
AND (meta_key = '_sumo_pp_payment_id')
";
$result = $wpdb->get_results($sql, ARRAY_A);
if ( !empty($result) ) {
$sumo_pp_payment_obj = $result[0];
$sumo_pp_payment_id = (int) $sumo_pp_payment_obj['meta_value'];
}
}
Ugyanez, dedikált Woocommerce függvénnyel lényegesen egyszerűbb, de egyik sem adott vissza találatot.
$items = $order->get_items();
foreach ($items as $item_id => $item) {
$sumo_pp_payment_id = (int) wc_get_order_item_meta( $item_id, '_sumo_pp_payment_id', true );
}
Sehol semmi. Aztán amikor már lezajlott a rendelésleadás (és lefutott a woocommerce_thankyou hook filtere is, ami kb 1 mp múlva már megtörtént), akkor már ott volt a _sumo_pp_payment_id nevű rekord tartalma. Tanulság: ha legközelebb belefutsz egy ilyenbe és nem találod az adott meta mezőt, próbáld ki mindhárom hook-ot, hátha az adott megoldásban az lesz a nyerő.
Ajánlott irodalom:
- Egy WooCommerce hiba, amikor a woocommerce_checkout_order_processed hook előbb lefut, mint kéne: https://github.com/woocommerce/woocommerce/issues/28113
- A woocommerce-checkout-order-processed miért nem működik? https://stackoverflow.com/questions/45997729/any-idea-why-woocommerce-checkout-order-processed-not-firing-when-user-is-not-lo
- A woocommerce_thankyou hook használata https://stackoverflow.com/questions/43301940/woocommerce-thankyou-hook-not-working
- A woocommerce_payment_complete értelme, vagyis amikor az a fontos, hogy mikor van kifizetve egy rendelés: https://stackoverflow.com/questions/46686075/woocommerce-checkout-order-processed-hook-executing-function-twice