diff --git a/README.md b/README.md index b059291..3de17e3 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This repository contains the WeArePlanet plugin that enables WooCommerce to proc ## Documentation -* [Documentation](https://plugin-documentation.weareplanet.com/weareplanet/woocommerce/3.0.11/docs/en/documentation.html) +* [Documentation](https://plugin-documentation.weareplanet.com/weareplanet/woocommerce/3.0.12/docs/en/documentation.html) ## Support @@ -33,4 +33,4 @@ ____________________________________________________________________________ ## License -Please see the [license file](https://github.com/weareplanet/woocommerce/blob/3.0.11/LICENSE) for more information. +Please see the [license file](https://github.com/weareplanet/woocommerce/blob/3.0.12/LICENSE) for more information. diff --git a/assets/css/admin.css b/assets/css/admin.css index 732ccd7..5e20056 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -1,10 +1,13 @@ /** * WeArePlanet WooCommerce * - * This WooCommerce plugin enables to process payments with WeArePlanet (https://www.weareplanet.com/). + * WeArePlanet + * This plugin will add support for all WeArePlanet payments methods and connect the WeArePlanet servers to your WooCommerce webshop (https://www.weareplanet.com/). * - * @author Planet Merchant Services Ltd (https://www.weareplanet.com) - * @license http://www.apache.org/licenses/LICENSE-2.0 Apache Software License (ASL 2.0) + * @category Class + * @package WeArePlanet + * @author Planet Merchant Services Ltd (https://www.weareplanet.com) + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache Software License (ASL 2.0) */ /* Icons for status */ @font-face { diff --git a/assets/css/checkout.css b/assets/css/checkout.css index de8133f..7e7db0e 100644 --- a/assets/css/checkout.css +++ b/assets/css/checkout.css @@ -1,10 +1,13 @@ /** * WeArePlanet WooCommerce * - * This WooCommerce plugin enables to process payments with WeArePlanet (https://www.weareplanet.com/). + * WeArePlanet + * This plugin will add support for all WeArePlanet payments methods and connect the WeArePlanet servers to your WooCommerce webshop (https://www.weareplanet.com/). * - * @author Planet Merchant Services Ltd (https://www.weareplanet.com) - * @license http://www.apache.org/licenses/LICENSE-2.0 Apache Software License (ASL 2.0) + * @category Class + * @package WeArePlanet + * @author Planet Merchant Services Ltd (https://www.weareplanet.com) + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache Software License (ASL 2.0) */ /* Order button disabled */ #place_order.weareplanet-disabled{ diff --git a/build/index.asset.php b/build/index.asset.php new file mode 100644 index 0000000..b8b5579 --- /dev/null +++ b/build/index.asset.php @@ -0,0 +1 @@ + array('@woocommerce/blocks-registry', 'react', 'wp-element', 'wp-polyfill'), 'version' => 'b53687c38b284a0c8c13'); diff --git a/build/index.css b/build/index.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/build/index.css @@ -0,0 +1 @@ + diff --git a/build/index.js b/build/index.js new file mode 100644 index 0000000..8552c0d --- /dev/null +++ b/build/index.js @@ -0,0 +1 @@ +!function(){"use strict";var e=window.wp.element,n=window.React,o=function({paymentMethodConfigurationId:o,eventRegistration:t}){const{onCheckoutSuccess:i,onCheckoutFail:a,onCheckoutValidation:c,onPaymentProcessing:r,onPaymentSetup:s}=t,[l,u]=(0,n.useState)(!0),d=`payment-method-${o}`;let m=window.IframeCheckoutHandler(o);const g=(0,n.useCallback)((()=>{m.create(d),u(!1)}),[o,d]);return(0,n.useEffect)((()=>{document.getElementById(d)&&g();const e=i((()=>(console.log("onCheckoutSuccess"),new Promise((e=>{console.log("Running submit"),m.submit(),setTimeout((function(){e(!1)}),3e4)}))))),n=c((()=>{console.log("onCheckoutValidation");let e=new Promise((e=>{m.setValidationCallback((n=>{console.log("Validation was ",n),void 0!==n.success?e(n.success):(console.error("Validation result did not return a success status."),e(!1))}))}));return m.validate(),e})),o=a((()=>{console.log("onCheckoutFail")})),t=r((()=>{console.log("onPaymentProcessing")})),l=s((()=>{console.log("onPaymentSetup")}));return()=>{e(),o(),n(),t(),l()}}),[g,d,i,a,c,r,s]),(0,e.createElement)("div",null,l&&(0,e.createElement)("div",null,"Loading payment method..."),(0,e.createElement)("div",{id:d}))},t=function({paymentMethodConfigurationId:e,eventRegistration:o}){console.log("LightboxComponent");const{onCheckoutSuccess:t,onCheckoutValidationBeforeProcessing:i,onCheckoutError:a}=o,[c,r]=(0,n.useState)(!0);(0,n.useEffect)((()=>{const n=t((()=>new Promise(((n,o)=>{console.log("onCheckoutSuccess"),window.LightboxCheckoutHandler.startPayment(e,(function(e,t){!e||e.error?o(e):n(e)}))})).then((e=>{console.log("Payment process finished",e)})).catch((e=>{alert("An error occurred during the initialization of the payment lightbox."),console.error(e)}))));return()=>{n()}}),[t,e])},i=window.wc.wcBlocksRegistry;jQuery((function(n){const a=wp.apiFetch.nonceEndpoint;n.post(a,{action:"get_payment_methods"},(function(c){c.map((function(c){let r;r="iframe"===c.integration_mode?(0,e.createElement)(o,{paymentMethodConfigurationId:c.configuration_id}):(0,e.createElement)(t,{paymentMethodConfigurationId:c.configuration_id});let s={name:c.name,label:c.label,content:r,edit:(0,e.createElement)("div",null,c.description),ariaLabel:c.ariaLabel,canMakePayment:async e=>await async function(){try{return new Promise(((e,o)=>{n.post(a,{action:"is_payment_method_available",payment_method:c.name,configuration_id:c.configuration_id},(function(n){e(n)})).fail((function(e){console.error("Error:",e),o(!1)}))}))}catch(e){return console.error("Error:",e),!1}}()};(0,i.registerPaymentMethod)(s)}))})).fail((function(e){console.error("Error getting payment methods. ",e)}))}))}(); \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index b44a8f3..4b602cf 100644 --- a/changelog.txt +++ b/changelog.txt @@ -804,3 +804,10 @@ Tested against: - [Tested Against] Woocommerce 9.1.4 - [Tested Against] PHP SDK 4.5.0 += 3.0.12 - Aug 21 2024 = +- [Feature] Implemented the Webhook Signing Mechanism +- [Tested Against] PHP 8.0 and PHP 7.4 +- [Tested Against] Wordpress 6.6 +- [Tested Against] Woocommerce 9.1.4 +- [Tested Against] PHP SDK 4.5.0 + diff --git a/docs/en/documentation.html b/docs/en/documentation.html index 4764649..cb84f08 100644 --- a/docs/en/documentation.html +++ b/docs/en/documentation.html @@ -23,7 +23,7 @@

Documentation

  • - + Source
  • diff --git a/docs/en/resource/method.png b/docs/en/resource/method.png index a29d1b9..166965f 100644 Binary files a/docs/en/resource/method.png and b/docs/en/resource/method.png differ diff --git a/includes/admin/class-wc-weareplanet-admin-settings-page.php b/includes/admin/class-wc-weareplanet-admin-settings-page.php index 67b9abf..5538412 100644 --- a/includes/admin/class-wc-weareplanet-admin-settings-page.php +++ b/includes/admin/class-wc-weareplanet-admin-settings-page.php @@ -187,7 +187,7 @@ public function get_settings() { $settings = array( array( 'links' => array( - 'https://plugin-documentation.weareplanet.com/weareplanet/woocommerce/3.0.11/docs/en/documentation.html' => __( 'Documentation', 'woo-weareplanet' ), + 'https://plugin-documentation.weareplanet.com/weareplanet/woocommerce/3.0.12/docs/en/documentation.html' => __( 'Documentation', 'woo-weareplanet' ), 'https://www.weareplanet.com/contact/sales' => __( 'Sign Up', 'woo-weareplanet' ), ), 'type' => 'weareplanet_links', diff --git a/includes/class-wc-weareplanet-autoloader.php b/includes/class-wc-weareplanet-autoloader.php index 2107143..597d9e2 100644 --- a/includes/class-wc-weareplanet-autoloader.php +++ b/includes/class-wc-weareplanet-autoloader.php @@ -52,7 +52,8 @@ public function __construct() { * @return string */ private function get_file_name_from_class( $class ) { - return 'class-' . str_replace( '_', '-', $class ) . '.php'; + $class = preg_replace( '/(?include_path . 'provider/'; } elseif ( strpos( $class, 'wc_weareplanet_webhook' ) === 0 ) { - $path = $this->include_path . 'webhook/'; + if (strpos($class, 'strategy') !== false) { + $path = $this->include_path . 'webhook/strategies/'; + } else { + $path = $this->include_path . 'webhook/'; + } } elseif ( strpos( $class, 'wc_weareplanet_exception' ) === 0 ) { $path = $this->include_path . 'exception/'; } elseif ( strpos( $class, 'wc_weareplanet_admin' ) === 0 ) { @@ -101,8 +106,6 @@ public function autoload( $class ) { if ( empty( $path ) || ! $this->load_file( $path . $file ) ) { $this->load_file( $this->include_path . $file ); } - - $this->load_file( $this->include_path . $file ); } } diff --git a/includes/class-wc-weareplanet-migration.php b/includes/class-wc-weareplanet-migration.php index ebe80fd..1a00694 100644 --- a/includes/class-wc-weareplanet-migration.php +++ b/includes/class-wc-weareplanet-migration.php @@ -249,7 +249,7 @@ public static function check_version() { public static function plugin_row_meta( $links, $file ) { if ( WC_WEAREPLANET_PLUGIN_BASENAME === $file ) { $row_meta = array( - 'docs' => '' . esc_html__( 'Documentation', 'woo-weareplanet' ) . '', + 'docs' => '' . esc_html__( 'Documentation', 'woo-weareplanet' ) . '', ); return array_merge( $links, $row_meta ); @@ -584,10 +584,20 @@ public static function update_1_0_6_shorten_table_names() { * @return void */ public static function supported_payments_integration_notice(): void { + if (!class_exists( 'WooCommerce' )) { + add_action('admin_notices', function() { + ?> +
    +

    +
    + ' )) { - $notice_id = "weareplanet-{$woocommerce_data['Version']}-not-yet-supported"; + $notice_id = "weareplanet-{$woocommerce_data['Version']}-not-yet-supported"; if (!WC_Admin_Notices::user_has_dismissed_notice($notice_id)) { $message = sprintf(__( 'The plugin WeArePlanet has been tested up to WooCommerce %1$s but you have installed the version %2$s. Please notice that this is not recommended.' , 'woo-weareplanet'), WC_WEAREPLANET_REQUIRED_WC_MAXIMUM_VERSION, $woocommerce_data['Version']); WC_Admin_Notices::add_custom_notice($notice_id, esc_html( $message )); diff --git a/includes/class-wc-weareplanet-webhook-handler.php b/includes/class-wc-weareplanet-webhook-handler.php index 4da8f84..4b6925c 100644 --- a/includes/class-wc-weareplanet-webhook-handler.php +++ b/includes/class-wc-weareplanet-webhook-handler.php @@ -57,32 +57,47 @@ public static function handle_webhook_errors( $errno, $errstr, $errfile, $errlin } /** - * Process the webhook call. + * Processes incoming webhook calls. + * This method handles both signed and unsigned payloads by determining the presence of a digital signature. + * It sets an initial HTTP 500 status to indicate a failure if the process crashes unexpectedly. */ public static function process() { - $webhook_service = WC_WeArePlanet_Service_Webhook::instance(); + global $wp_filesystem; + if ( empty( $wp_filesystem ) ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + WP_Filesystem(); + } // We set the status to 500, so if we encounter a state where the process crashes the webhook is marked as failed. header( 'HTTP/1.1 500 Internal Server Error' ); - $request_body = trim( file_get_contents( 'php://input' ) ); + $raw_post_data = $wp_filesystem->get_contents( 'php://input' ); + $signature = isset( $_SERVER['HTTP_X_SIGNATURE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_SIGNATURE'] ) ) : ''; set_error_handler( array( __CLASS__, 'handle_webhook_errors' ) ); try { - $request = new WC_WeArePlanet_Webhook_Request( json_decode( $request_body ) ); - $webhook_model = $webhook_service->get_webhook_entity_for_id( $request->get_listener_entity_id() ); - if ( null === $webhook_model ) { - WooCommerce_WeArePlanet::instance()->log( sprintf( 'Could not retrieve webhook model for listener entity id: %s', $request->get_listener_entity_id() ), WC_Log_Levels::ERROR ); - // phpcs:ignore - echo esc_html__( sprintf( 'Could not retrieve webhook model for listener entity id: %s', $request->get_listener_entity_id() ) ); - exit(); + $clean_data = wp_kses_post( wp_unslash( $raw_post_data ) ); + $request = new WC_WeArePlanet_Webhook_Request( json_decode( $clean_data ) ); + $client = WC_Wallee_Helper::instance()->get_api_client(); + $webhook_service = WC_WeArePlanet_Service_Webhook::instance(); + + // Handling of payloads without a signature (legacy method). + // Deprecated since 3.0.12 + if ( empty( $signature ) ) { + $webhook_model = $webhook_service->get_webhook_entity_for_id( $request->get_listener_entity_id() ); + $webhook_handler_class_name = $webhook_model->get_handler_class_name(); + $webhook_handler = $webhook_handler_class_name::instance(); + $webhook_handler->process( $request ); + } + // Handling of payloads with a valid signature. + // This payload signed has the transaction state + if ( !empty( $signature ) && $client->getWebhookEncryptionService()->isContentValid( $signature, $clean_data ) ) { + WC_WeArePlanet_Webhook_Strategy_Manager::instance()->process( $request ); } - $webhook_handler_class_name = $webhook_model->get_handler_class_name(); - $webhook_handler = $webhook_handler_class_name::instance(); - $webhook_handler->process( $request ); + header( 'HTTP/1.1 200 OK' ); } catch ( Exception $e ) { WooCommerce_WeArePlanet::instance()->log( $e->getMessage(), WC_Log_Levels::ERROR ); - // phpcs:ignore + // phpcs:ignore echo esc_textarea($e->getMessage()); exit(); } diff --git a/includes/service/class-wc-weareplanet-service-line-item.php b/includes/service/class-wc-weareplanet-service-line-item.php index 46a67d0..44e5132 100644 --- a/includes/service/class-wc-weareplanet-service-line-item.php +++ b/includes/service/class-wc-weareplanet-service-line-item.php @@ -241,7 +241,6 @@ protected function create_shipping_line_items_from_session( $packages, $chosen_m */ protected function create_coupons_line_items_from_session() { $coupons = array(); - $currency = get_woocommerce_currency(); $cart = WC()->cart; if ( empty( $cart->get_applied_coupons() ) ) { @@ -249,8 +248,10 @@ protected function create_coupons_line_items_from_session() { } $discount = $cart->get_discount_total() + $cart->get_discount_tax(); - $line_item = $this->create_coupon_line_item( current($cart->get_coupons()), $discount ); - $coupons[] = $line_item; + $line_items = $this->create_coupon_line_items( current( $cart->get_coupons() ), $discount ); + if ( is_array( $line_items ) ) { + $coupons = array_merge( $coupons, $line_items ); + } return $coupons; } @@ -260,27 +261,86 @@ protected function create_coupons_line_items_from_session() { * @param float $amount * @return \WeArePlanet\Sdk\Model\LineItemCreate|null */ - private function create_coupon_line_item( $coupon, float $amount = 0 ) { - //check if the coupon is valid + private function create_coupon_line_items( $coupon, float $total_discount_amount = 0 ) { if ( !$coupon instanceof WC_Coupon && !$coupon instanceof WC_Order_Item_Coupon ) { - return null; + return []; } - + $coupon = new WC_Coupon( $coupon->get_code() ); - - $amount = $amount * -1; $sku = $this->fix_length( $coupon->get_discount_type(), 150 ); $sku = str_replace( array( "\n", "\r", ), '', $sku ); + + // Calculate the proportional discount amounts for each tax rate + $discounts = $this->calculate_discount_rates_proportionally( $total_discount_amount ); + + $line_items = []; + + foreach ( $discounts as $discount ) { + $line_item = new \WeArePlanet\Sdk\Model\LineItemCreate(); + $line_item->setAmountIncludingTax( $discount['amount'] * -1 ); + $line_item->setName( sprintf( '%s: %s (%s%% tax)', WC_WeArePlanet_Packages_Coupon_Discount::COUPON, $coupon->get_code(), $discount['rate_id']) ); + $line_item->setQuantity( 1 ); + $line_item->setShippingRequired( false ); + $line_item->setSku( $sku, 200 ); + $line_item->setType( \WeArePlanet\Sdk\Model\LineItemType::DISCOUNT ); + $line_item->setUniqueId( 'coupon-' . $coupon->get_id() . '-' . $discount['rate_id'] ); + + $tax_rate = new \WeArePlanet\Sdk\Model\TaxCreate([ + 'title' => 'Discount Tax: ' . $discount['rate_id'], + 'rate' => $discount['rate_id'], + ]); + + $line_item->setTaxes( [$tax_rate] ); + + $line_items[] = $line_item; + } + + return $line_items; + } - $line_item = new \WeArePlanet\Sdk\Model\LineItemCreate(); - $line_item->setAmountIncludingTax( $amount ); - $line_item->setName( sprintf( '%s: %s', WC_WeArePlanet_Packages_Coupon_Discount::COUPON, $coupon->get_code() ) ); - $line_item->setQuantity( 1 ); - $line_item->setShippingRequired( false ); - $line_item->setSku( $sku, 200 ); - $line_item->setType( \WeArePlanet\Sdk\Model\LineItemType::DISCOUNT ); - $line_item->setUniqueId( 'coupon-' . $coupon->get_id() ); - return $line_item; + /** + * @param float $total_discount_amount + * @return array + */ + private function calculate_discount_rates_proportionally(float $total_discount_amount): array { + $cart = WC()->cart; + $tax_totals = []; + $total_amount = 0; + + foreach ( $cart->get_cart() as $cart_item ) { + $product = $cart_item['data']; + $tax_class = $product->get_tax_class(); + $tax_rates_class = WC_Tax::get_rates( $tax_class ); + + foreach ( $tax_rates_class as $rate ) { + $rate_id = $rate['rate']; + $line_total_with_tax = $cart_item['line_total'] + $cart_item['line_tax']; + + if ( !isset($tax_totals[$rate_id]) ) { + $tax_totals[$rate_id] = [ + 'total' => 0, + 'rate_percentage' => $rate_id + ]; + } + + $tax_totals[$rate_id]['total'] += $line_total_with_tax; + $total_amount += $line_total_with_tax; + } + } + + $discounts = []; + + foreach ($tax_totals as $rate_id => $data) { + $proportional_discount_amount = $total_discount_amount * ($data['total'] / $total_amount); + + $discounts[] = [ + 'rate_id' => $rate_id, + 'amount' => $proportional_discount_amount, + 'rate_percentage' => $rate_id, + ]; + } + + return $discounts; } /** * Returns the line items from the given cart @@ -514,8 +574,11 @@ protected function create_coupons_line_items_from_order( WC_Order $order ) { /** @var WC_Order_Item_Coupon $coupon */ $discount += (float)$coupon->get_discount() + (float)$coupon->get_discount_tax(); } - $line_item = $this->create_coupon_line_item( current($order->get_coupons()), $discount ); - $coupons[] = $this->clean_line_item( $line_item ); + + $line_items = $this->create_coupon_line_items( current($order->get_coupons()), $discount ); + foreach ($line_items as $line_item) { + $coupons[] = $this->clean_line_item( $line_item ); + } return $coupons; } @@ -568,11 +631,11 @@ protected function create_product_line_items_from_backend( array $backend_items, //At this point, if there is a discount applied by coupon, the price already has the discount applied, //and to be able to send the discount to the portal, it is necessary to restore the discounted amount, - //the original price must be restored before being applied, otherwise it would be discounting twice in the portal. + //the original price must be restored before being applied, otherwise it would be discounting twice in the portal. $item_data_coupon = $item->get_meta( '_weareplanet_coupon_discount_line_item_discounts' ); if ( !empty ( $item_data_coupon ) ) { - $discount_tax = $item->get_subtotal_tax() - $item->get_total_tax(); - $discount_amount = $item->get_subtotal() - $item->get_total(); + $discount_tax = $item->get_subtotal_tax() - $item->get_total_tax(); + $discount_amount = $item->get_subtotal() - $item->get_total(); $discounts = $discount_tax + $discount_amount; } diff --git a/includes/service/class-wc-weareplanet-service-webhook.php b/includes/service/class-wc-weareplanet-service-webhook.php index 092f342..88b8063 100644 --- a/includes/service/class-wc-weareplanet-service-webhook.php +++ b/includes/service/class-wc-weareplanet-service-webhook.php @@ -20,6 +20,17 @@ */ class WC_WeArePlanet_Service_Webhook extends WC_WeArePlanet_Service_Abstract { + const WEAREPLANET_MANUAL_TASK = 1487165678181; + const WEAREPLANET_PAYMENT_METHOD_CONFIGURATION = 1472041857405; + const WEAREPLANET_TRANSACTION = 1472041829003; + const WEAREPLANET_DELIVERY_INDICATION = 1472041819799; + const WEAREPLANET_TRANSACTION_INVOICE = 1472041816898; + const WEAREPLANET_TRANSACTION_COMPLETION = 1472041831364; + const WEAREPLANET_TRANSACTION_VOID = 1472041867364; + const WEAREPLANET_REFUND = 1472041839405; + const WEAREPLANET_TOKEN = 1472041806455; + const WEAREPLANET_TOKEN_VERSION = 1472041811051; + /** * The webhook listener API service. * @@ -48,8 +59,15 @@ class WC_WeArePlanet_Service_Webhook extends WC_WeArePlanet_Service_Abstract { * Constructor to register the webhook entites. */ public function __construct() { - $this->webhook_entities[1487165678181] = new WC_WeArePlanet_Webhook_Entity( - 1487165678181, + $this->init_webhook_entities(); + } + + /** + * Initializes webhook entities with their specific configurations. + */ + private function init_webhook_entities() { + $this->webhook_entities[self::WEAREPLANET_MANUAL_TASK] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_MANUAL_TASK, 'Manual Task', array( \WeArePlanet\Sdk\Model\ManualTaskState::DONE, @@ -58,8 +76,8 @@ public function __construct() { ), 'WC_WeArePlanet_Webhook_Manual_Task' ); - $this->webhook_entities[1472041857405] = new WC_WeArePlanet_Webhook_Entity( - 1472041857405, + $this->webhook_entities[self::WEAREPLANET_PAYMENT_METHOD_CONFIGURATION] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_PAYMENT_METHOD_CONFIGURATION, 'Payment Method Configuration', array( \WeArePlanet\Sdk\Model\CreationEntityState::ACTIVE, @@ -70,8 +88,8 @@ public function __construct() { 'WC_WeArePlanet_Webhook_Method_Configuration', true ); - $this->webhook_entities[1472041829003] = new WC_WeArePlanet_Webhook_Entity( - 1472041829003, + $this->webhook_entities[self::WEAREPLANET_TRANSACTION] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_TRANSACTION, 'Transaction', array( \WeArePlanet\Sdk\Model\TransactionState::CONFIRMED, @@ -85,8 +103,8 @@ public function __construct() { ), 'WC_WeArePlanet_Webhook_Transaction' ); - $this->webhook_entities[1472041819799] = new WC_WeArePlanet_Webhook_Entity( - 1472041819799, + $this->webhook_entities[self::WEAREPLANET_DELIVERY_INDICATION] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_DELIVERY_INDICATION, 'Delivery Indication', array( \WeArePlanet\Sdk\Model\DeliveryIndicationState::MANUAL_CHECK_REQUIRED, @@ -94,8 +112,8 @@ public function __construct() { 'WC_WeArePlanet_Webhook_Delivery_Indication' ); - $this->webhook_entities[1472041816898] = new WC_WeArePlanet_Webhook_Entity( - 1472041816898, + $this->webhook_entities[self::WEAREPLANET_TRANSACTION_INVOICE] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_TRANSACTION_INVOICE, 'Transaction Invoice', array( \WeArePlanet\Sdk\Model\TransactionInvoiceState::NOT_APPLICABLE, @@ -105,8 +123,8 @@ public function __construct() { 'WC_WeArePlanet_Webhook_Transaction_Invoice' ); - $this->webhook_entities[1472041831364] = new WC_WeArePlanet_Webhook_Entity( - 1472041831364, + $this->webhook_entities[self::WEAREPLANET_TRANSACTION_COMPLETION] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_TRANSACTION_COMPLETION, 'Transaction Completion', array( \WeArePlanet\Sdk\Model\TransactionCompletionState::FAILED, @@ -115,8 +133,8 @@ public function __construct() { 'WC_WeArePlanet_Webhook_Transaction_Completion' ); - $this->webhook_entities[1472041867364] = new WC_WeArePlanet_Webhook_Entity( - 1472041867364, + $this->webhook_entities[self::WEAREPLANET_TRANSACTION_VOID] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_TRANSACTION_VOID, 'Transaction Void', array( \WeArePlanet\Sdk\Model\TransactionVoidState::FAILED, @@ -125,8 +143,8 @@ public function __construct() { 'WC_WeArePlanet_Webhook_Transaction_Void' ); - $this->webhook_entities[1472041839405] = new WC_WeArePlanet_Webhook_Entity( - 1472041839405, + $this->webhook_entities[self::WEAREPLANET_REFUND] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_REFUND, 'Refund', array( \WeArePlanet\Sdk\Model\RefundState::FAILED, @@ -134,8 +152,8 @@ public function __construct() { ), 'WC_WeArePlanet_Webhook_Refund' ); - $this->webhook_entities[1472041806455] = new WC_WeArePlanet_Webhook_Entity( - 1472041806455, + $this->webhook_entities[self::WEAREPLANET_TOKEN] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_TOKEN, 'Token', array( \WeArePlanet\Sdk\Model\CreationEntityState::ACTIVE, @@ -145,8 +163,8 @@ public function __construct() { ), 'WC_WeArePlanet_Webhook_Token' ); - $this->webhook_entities[1472041811051] = new WC_WeArePlanet_Webhook_Entity( - 1472041811051, + $this->webhook_entities[self::WEAREPLANET_TOKEN_VERSION] = new WC_WeArePlanet_Webhook_Entity( + self::WEAREPLANET_TOKEN_VERSION, 'Token Version', array( \WeArePlanet\Sdk\Model\TokenVersionState::ACTIVE, @@ -183,16 +201,18 @@ public function install() { } /** - * Get webhook entity for id. + * Get the webhook entity for a specific ID or throws an exception if not found. * - * @param int|string $id id. - * @return WC_WeArePlanet_Webhook_Entity + * @param mixed $id The ID of the webhook entity to retrieve. + * @return WC_WeArePlanet_Webhook_Entity The webhook entity associated with the given ID. + * @throws Exception If the webhook entity cannot be found. */ public function get_webhook_entity_for_id( $id ) { - if ( isset( $this->webhook_entities[ $id ] ) ) { - return $this->webhook_entities[ $id ]; + if ( !isset( $this->webhook_entities[ $id ] ) ) { + throw new Exception( sprintf( 'Could not retrieve webhook model for listener entity id: %s', $id ) ); } - return null; + + return $this->webhook_entities[ $id ]; } /** @@ -213,6 +233,7 @@ protected function create_webhook_listener( WC_WeArePlanet_Webhook_Entity $entit $webhook_listener->setState( \WeArePlanet\Sdk\Model\CreationEntityState::ACTIVE ); $webhook_listener->setUrl( $webhook_url->getId() ); $webhook_listener->setNotifyEveryChange( $entity->is_notify_every_change() ); + $webhook_listener->setEnablePayloadSignatureAndState( true ); return $this->get_webhook_listener_service()->create( $space_id, $webhook_listener ); } diff --git a/includes/webhook/class-wc-weareplanet-webhook-abstract.php b/includes/webhook/class-wc-weareplanet-webhook-abstract.php index e9486c2..457907b 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-abstract.php +++ b/includes/webhook/class-wc-weareplanet-webhook-abstract.php @@ -17,6 +17,8 @@ } /** * Abstract webhook processor. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Strategy_Interface */ abstract class WC_WeArePlanet_Webhook_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-delivery-indication.php b/includes/webhook/class-wc-weareplanet-webhook-delivery-indication.php index 1741828..7410305 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-delivery-indication.php +++ b/includes/webhook/class-wc-weareplanet-webhook-delivery-indication.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle delivery indication state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Delivery_Indication_Strategy */ class WC_WeArePlanet_Webhook_Delivery_Indication extends WC_WeArePlanet_Webhook_Order_Related_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-entity.php b/includes/webhook/class-wc-weareplanet-webhook-entity.php index 6d7d777..dcb73ac 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-entity.php +++ b/includes/webhook/class-wc-weareplanet-webhook-entity.php @@ -111,6 +111,7 @@ public function is_notify_every_change() { * Get Handler class name. * * @return mixed + * @deprecated This method will be deprecated in a future version as it is no longer necessary for webhook strategies. */ public function get_handler_class_name() { return $this->handler_class_name; diff --git a/includes/webhook/class-wc-weareplanet-webhook-manual-task.php b/includes/webhook/class-wc-weareplanet-webhook-manual-task.php index 7a33a4a..591d1f4 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-manual-task.php +++ b/includes/webhook/class-wc-weareplanet-webhook-manual-task.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle manual task state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Manual_Task_Strategy */ class WC_WeArePlanet_Webhook_Manual_Task extends WC_WeArePlanet_Webhook_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-method-configuration.php b/includes/webhook/class-wc-weareplanet-webhook-method-configuration.php index 05cc958..0be426a 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-method-configuration.php +++ b/includes/webhook/class-wc-weareplanet-webhook-method-configuration.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle payment method configuration state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Method_Configuration_Strategy */ class WC_WeArePlanet_Webhook_Method_Configuration extends WC_WeArePlanet_Webhook_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-order-related-abstract.php b/includes/webhook/class-wc-weareplanet-webhook-order-related-abstract.php index 13498c9..a29335e 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-order-related-abstract.php +++ b/includes/webhook/class-wc-weareplanet-webhook-order-related-abstract.php @@ -17,6 +17,8 @@ } /** * Abstract webhook processor for order related entities. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Strategy_Base */ abstract class WC_WeArePlanet_Webhook_Order_Related_Abstract extends WC_WeArePlanet_Webhook_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-refund.php b/includes/webhook/class-wc-weareplanet-webhook-refund.php index b3a2641..b1e7da9 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-refund.php +++ b/includes/webhook/class-wc-weareplanet-webhook-refund.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle refund state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Service_Refund */ class WC_WeArePlanet_Webhook_Refund extends WC_WeArePlanet_Webhook_Order_Related_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-request.php b/includes/webhook/class-wc-weareplanet-webhook-request.php index 1550d58..f4390b6 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-request.php +++ b/includes/webhook/class-wc-weareplanet-webhook-request.php @@ -68,6 +68,13 @@ class WC_WeArePlanet_Webhook_Request { */ private $timestamp; + /** + * Entity state. + * + * @var mixed + */ + private $state; + /** * Constructor. * @@ -75,17 +82,18 @@ class WC_WeArePlanet_Webhook_Request { */ public function __construct( $model ) { $this->event_id = $model->eventId; - // phpcs:ignore + // phpcs:ignore $this->entity_id = $model->entityId; - // phpcs:ignore + // phpcs:ignore $this->listener_entity_id = $model->listenerEntityId; - // phpcs:ignore + // phpcs:ignore $this->listener_entity_technical_name = $model->listenerEntityTechnicalName; - // phpcs:ignore + // phpcs:ignore $this->space_id = $model->spaceId; - // phpcs:ignore + // phpcs:ignore $this->webhook_listener_id = $model->webhookListenerId; $this->timestamp = $model->timestamp; + $this->state = $model->state; } /** @@ -150,4 +158,13 @@ public function get_webhook_listener_id() { public function get_timestamp() { return $this->timestamp; } + + /** + * Returns the state of the webhook event's entity. + * + * @return string + */ + public function get_state() { + return $this->state; + } } diff --git a/includes/webhook/class-wc-weareplanet-webhook-token-version.php b/includes/webhook/class-wc-weareplanet-webhook-token-version.php index 314bd7f..8f78e16 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-token-version.php +++ b/includes/webhook/class-wc-weareplanet-webhook-token-version.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle token version state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Token_Version_Strategy */ class WC_WeArePlanet_Webhook_Token_Version extends WC_WeArePlanet_Webhook_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-token.php b/includes/webhook/class-wc-weareplanet-webhook-token.php index cef3dc5..49647a8 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-token.php +++ b/includes/webhook/class-wc-weareplanet-webhook-token.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle token state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Token_Strategy */ class WC_WeArePlanet_Webhook_Token extends WC_WeArePlanet_Webhook_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-transaction-completion.php b/includes/webhook/class-wc-weareplanet-webhook-transaction-completion.php index d260e31..057b46c 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-transaction-completion.php +++ b/includes/webhook/class-wc-weareplanet-webhook-transaction-completion.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle transaction completion state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Transaction_Completion_Strategy */ class WC_WeArePlanet_Webhook_Transaction_Completion extends WC_WeArePlanet_Webhook_Order_Related_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-transaction-invoice.php b/includes/webhook/class-wc-weareplanet-webhook-transaction-invoice.php index aee0e1c..c0d6f19 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-transaction-invoice.php +++ b/includes/webhook/class-wc-weareplanet-webhook-transaction-invoice.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle transaction completion state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Transaction_Invoice_Strategy */ class WC_WeArePlanet_Webhook_Transaction_Invoice extends WC_WeArePlanet_Webhook_Order_Related_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-transaction-void.php b/includes/webhook/class-wc-weareplanet-webhook-transaction-void.php index 037b4b9..f9b2355 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-transaction-void.php +++ b/includes/webhook/class-wc-weareplanet-webhook-transaction-void.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle transaction void state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Transaction_Void_Strategy */ class WC_WeArePlanet_Webhook_Transaction_Void extends WC_WeArePlanet_Webhook_Order_Related_Abstract { diff --git a/includes/webhook/class-wc-weareplanet-webhook-transaction.php b/includes/webhook/class-wc-weareplanet-webhook-transaction.php index f117b0a..56469a6 100644 --- a/includes/webhook/class-wc-weareplanet-webhook-transaction.php +++ b/includes/webhook/class-wc-weareplanet-webhook-transaction.php @@ -17,6 +17,8 @@ } /** * Webhook processor to handle transaction state transitions. + * @deprecated 3.0.12 No longer used by internal code and not recommended. + * @see WC_WeArePlanet_Webhook_Transaction_Strategy */ class WC_WeArePlanet_Webhook_Transaction extends WC_WeArePlanet_Webhook_Order_Related_Abstract { diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-delivery-indication-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-delivery-indication-strategy.php new file mode 100644 index 0000000..a01c6f0 --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-delivery-indication-strategy.php @@ -0,0 +1,100 @@ +get_api_client() ); + return $transaction_invoice_service->read( $request->get_space_id(), $request->get_entity_id() ); + } + + /** + * @inheritDoc + */ + protected function get_order_id( $object ) { + /* @var \Wallee\Sdk\Model\DeliveryIndication $object */ + return WC_WeArePlanet_Entity_Transaction_Info::load_by_transaction( + $object->getTransaction()->getLinkedSpaceId(), + $object->getTransaction()->getId() + )->get_order_id(); + } + + /** + * Processes the incoming webhook request pertaining to delivery indications. + * + * This method retrieves the delivery indication details from the API and updates the associated + * WooCommerce order based on the indication state. + * + * @param WC_WeArePlanet_Webhook_Request $request The webhook request object. + * @return void + */ + public function process( WC_WeArePlanet_Webhook_Request $request ) { + /* @var \WeArePlanet\Sdk\Model\DeliveryIndication $delivery_indication */ + $delivery_indication = $this->load_entity( $request ); + $order = $this->get_order( $delivery_indication ); + if ( false != $order && $order->get_id() ) { + $this->process_order_related_inner( $order, $delivery_indication, $request ); + } + } + + /** + * Additional processing on the order based on the state of the delivery indication. + * + * @param WC_Order $order The WooCommerce order linked to the delivery indication. + * @param \WeArePlanet\Sdk\Model\DeliveryIndication $delivery_indication The delivery indication object. + * @param WC_WeArePlanet_Webhook_Request $request The webhook request. + * @return void + */ + protected function process_order_related_inner( WC_Order $order, \WeArePlanet\Sdk\Model\DeliveryIndication $delivery_indication, WC_WeArePlanet_Webhook_Request $request ) { + switch ( $request->get_state() ) { + case \WeArePlanet\Sdk\Model\DeliveryIndicationState::MANUAL_CHECK_REQUIRED: + $this->review( $order ); + break; + default: + // Nothing to do. + break; + } + } + + /** + * Review and potentially update the order status based on manual review requirements. + * + * @param WC_Order $order The associated WooCommerce order. + * @return void + */ + protected function review( WC_Order $order ) { + $status = apply_filters( 'wc_weareplanet_manual_task_status', 'wearep-manual', $order ); + $order->add_meta_data( '_weareplanet_manual_check', true ); + $order->update_status( $status, __( 'A manual decision about whether to accept the payment is required.', 'woo-weareplanet' ) ); + } +} \ No newline at end of file diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-manual-task-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-manual-task-strategy.php new file mode 100644 index 0000000..3bd4d1e --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-manual-task-strategy.php @@ -0,0 +1,47 @@ +update(); + } +} diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-method-configuration-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-method-configuration-strategy.php new file mode 100644 index 0000000..2c87396 --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-method-configuration-strategy.php @@ -0,0 +1,47 @@ +synchronize(); + } +} diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-refund-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-refund-strategy.php new file mode 100644 index 0000000..5e4d928 --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-refund-strategy.php @@ -0,0 +1,145 @@ +get_api_client() ); + return $refund_service->read( $request->get_space_id(), $request->get_entity_id() ); + } + + /** + * @inheritDoc + */ + protected function get_order_id( $object ) { + /* @var \Wallee\Sdk\Model\Refund $object */ + return WC_WeArePlanet_Entity_Transaction_Info::load_by_transaction( + $object->getTransaction()->getLinkedSpaceId(), + $object->getTransaction()->getId() + )->get_order_id(); + } + + /** + * Processes the incoming webhook request related to refunds. + * + * This method retrieves the refund details from the API and updates the associated order + * based on the refund's state. + * + * @param WC_WeArePlanet_Webhook_Request $request The webhook request object. + * @return void + */ + public function process( WC_WeArePlanet_Webhook_Request $request ) { + /* @var \WeArePlanet\Sdk\Model\Refund $refund */ + $refund = $this->load_entity( $request ); + $order = $this->get_order( $refund ); + if ( false != $order && $order->get_id() ) { + $this->process_order_related_inner( $order, $refund, $request ); + } + } + + /** + * Performs additional order-related processing based on the refund state. + * + * @param WC_Order $order The WooCommerce order associated with the refund. + * @param \WeArePlanet\Sdk\Model\Refund $refund The transaction refund object. + * @param WC_WeArePlanet_Webhook_Request $request The webhook request object. + * @return void + */ + protected function process_order_related_inner( WC_Order $order, \WeArePlanet\Sdk\Model\Refund $refund, WC_WeArePlanet_Webhook_Request $request ) { + /* @var \WeArePlanet\Sdk\Model\Refund $refund */ + switch ( $request->get_state() ) { + case \WeArePlanet\Sdk\Model\RefundState::FAILED: + // fallback. + $this->failed( $refund, $order ); + break; + case \WeArePlanet\Sdk\Model\RefundState::SUCCESSFUL: + $this->refunded( $refund, $order ); + // Nothing to do. + default: + // Nothing to do. + break; + } + } + + /** + * Handles actions to be performed when a refund transaction fails. + * + * @param \WeArePlanet\Sdk\Model\Refund $refund refund. + * @param WC_Order $order order. + * @return void + * @throws Exception Exception. + */ + protected function failed( \WeArePlanet\Sdk\Model\Refund $refund, WC_Order $order ) { + $refund_job = WC_WeArePlanet_Entity_Refund_Job::load_by_external_id( $refund->getLinkedSpaceId(), $refund->getExternalId() ); + if ( $refund_job->get_id() ) { + $refund_job->set_state( WC_WeArePlanet_Entity_Refund_Job::STATE_FAILURE ); + if ( $refund->getFailureReason() != null ) { + $refund_job->set_failure_reason( $refund->getFailureReason()->getDescription() ); + } + $refund_job->save(); + $refunds = $order->get_refunds(); + foreach ( $refunds as $wc_refund ) { + if ( $wc_refund->get_meta( '_weareplanet_refund_job_id', true ) == $refund_job->get_id() ) { + $wc_refund->set_status( 'failed' ); + $wc_refund->save(); + break; + } + } + } + } + + /** + * Handles actions to be performed when a refund transaction is successful. + * + * @param \WeArePlanet\Sdk\Model\Refund $refund refund. + * @param WC_Order $order order. + * @return void + * @throws Exception Exception. + */ + protected function refunded( \WeArePlanet\Sdk\Model\Refund $refund, WC_Order $order ) { + $refund_job = WC_WeArePlanet_Entity_Refund_Job::load_by_external_id( $refund->getLinkedSpaceId(), $refund->getExternalId() ); + + if ( $refund_job->get_id() ) { + $refund_job->set_state( WC_WeArePlanet_Entity_Refund_Job::STATE_SUCCESS ); + $refund_job->save(); + $refunds = $order->get_refunds(); + foreach ( $refunds as $wc_refund ) { + if ( $wc_refund->get_meta( '_weareplanet_refund_job_id', true ) == $refund_job->get_id() ) { + $wc_refund->set_status( 'completed' ); + $wc_refund->save(); + break; + } + } + } + } +} diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-strategy-base.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-strategy-base.php new file mode 100644 index 0000000..b89e85b --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-strategy-base.php @@ -0,0 +1,65 @@ +get_api_client() ); + return $transaction_service->read( $request->get_space_id(), $request->get_entity_id() ); + } + + /** + * Get the WooCommerce order associated with the webhook request. + * + * This method uses the Order Factory to fetch the order based on the ID retrieved from the transaction linked to the webhook request. + * + * @param WC_WeArePlanet_Webhook_Request|mixed $object The webhook request or transaction that containing data needed to identify the order. + * @return \WC_Order The WooCommerce order object associated with the request. + */ + protected function get_order( $object ) { + return WC_Order_Factory::get_order( $this->get_order_id( $object ) ); + } + + /** + * Extracts the order ID from a transaction. + * + * This method fetches the order ID by using the transaction information available in the webhook request. + * It is typically used to link the transaction data retrieved via API to a specific WooCommerce order. + * + * @param WC_WeArePlanet_Webhook_Request|mixed $object. + * @return int|string + */ + protected function get_order_id( $object ) { + return WC_WeArePlanet_Entity_Transaction_Info::load_by_transaction( $object->get_space_id(), $object->get_entity_id() )->get_order_id(); + } +} \ No newline at end of file diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-strategy-interface.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-strategy-interface.php new file mode 100644 index 0000000..984a0ca --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-strategy-interface.php @@ -0,0 +1,42 @@ +webhook_service = WC_WeArePlanet_Service_Webhook::instance(); + $this->helper = WC_WeArePlanet_Helper::instance(); + $this->configure_strategies_to_handle_webhooks(); + } + + /** + * Initializes the list of strategies for handling different types of webhook events. + * + * This method populates the 'strategies' array with instances of various webhook handling strategies. + * Each strategy corresponds to a specific type of webhook event, ensuring that the appropriate + * processing logic is applied based on the type of the incoming webhook request. + * + * @return void + */ + private function configure_strategies_to_handle_webhooks() { + $this->strategies[] = new WC_WeArePlanet_Webhook_Manual_Task_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Method_Configuration_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Transaction_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Delivery_Indication_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Transaction_Invoice_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Transaction_Completion_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Transaction_Void_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Refund_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Token_Strategy(); + $this->strategies[] = new WC_WeArePlanet_Webhook_Token_Version_Strategy(); + } + + /** + * Resolves the appropriate strategy for handling the given webhook request based on webhook type. + * + * This method fetches the webhook entity using the listener entity ID from the request, checks if a corresponding + * strategy exists, and returns the strategy if found. + * + * @param WC_WeArePlanet_Webhook_Request $request The incoming webhook request. + * @return WC_WeArePlanet_Webhook_Strategy_Interface The strategy to handle the request. + * @throws Exception If no strategy can be resolved. + */ + private function resolve_strategy( WC_WeArePlanet_Webhook_Request $request ) { + /** @var WC_WeArePlanet_Webhook_Entity $webhook_model */ + $webhook_model = $this->webhook_service->get_webhook_entity_for_id( $request->get_listener_entity_id() ); + + // Check if the webhook model exists for this listener entity ID. + if ( is_null( $webhook_model ) ) { + throw new Exception( sprintf( 'Could not retrieve webhook model for listener entity id: %s', $request->get_listener_entity_id() ) ); + } + + $webhook_transaction_id = $webhook_model->get_id(); + + // Check if the strategy exists for the retrieved transaction ID. + foreach ($this->strategies as $strategy) { + /** @var WC_WeArePlanet_Webhook_Strategy_Interface $strategy */ + if ( $strategy->match( $webhook_transaction_id ) ) { + return $strategy; + } + } + + // No strategy found for the transaction ID + throw new Exception( sprintf( 'No strategy available for the transaction ID: %s', $webhook_transaction_id ) ); + } + + /** + * Processes the incoming webhook by delegating to the appropriate strategy. + * + * This method determines the type of the incoming webhook request and uses it + * to look up the corresponding strategy. If a strategy is found, it delegates the + * request processing to that strategy. If no strategy is found for the type, it + * throws an exception. + * + * @param WC_WeArePlanet_Webhook_Request $request The incoming webhook request object. + * @throws Exception If no strategy is available for the webhook type provided in the request. + */ + public function process( WC_WeArePlanet_Webhook_Request $request ) { + $this->helper->start_database_transaction(); + + try { + $this->helper->lock_by_transaction_id( $request->get_space_id(), $request->get_entity_id() ); + + $strategy = $this->resolve_strategy( $request ); + $strategy->process( $request ); + + $this->helper->commit_database_transaction(); + } catch ( Exception $e ) { + $this->helper->rollback_database_transaction(); + throw $e; + } + } +} \ No newline at end of file diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-token-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-token-strategy.php new file mode 100644 index 0000000..2d8abef --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-token-strategy.php @@ -0,0 +1,48 @@ +update_token( $request->get_space_id(), $request->get_entity_id() ); + } +} diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-token-version-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-token-version-strategy.php new file mode 100644 index 0000000..60e1bbb --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-token-version-strategy.php @@ -0,0 +1,48 @@ +update_token_version( $request->get_space_id(), $request->get_entity_id() ); + } +} diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-completion-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-completion-strategy.php new file mode 100644 index 0000000..4dd7238 --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-completion-strategy.php @@ -0,0 +1,272 @@ +get_api_client() ); + return $transaction_invoice_service->read( $request->get_space_id(), $request->get_entity_id() ); + } + + /** + * @inheritDoc + */ + protected function get_order_id( $object ) { + /* @var \Wallee\Sdk\Model\TransactionCompletion $object */ + return WC_WeArePlanet_Entity_Transaction_Info::load_by_transaction( + $object->getLineItemVersion()->getTransaction()->getLinkedSpaceId(), + $object->getLineItemVersion()->getTransaction()->getId() + )->get_order_id(); + } + + /** + * Processes the incoming webhook request pertaining to transaction completions. + * + * This method retrieves the transaction completion details from the API and updates the associated + * WooCommerce order based on the state of the completion. + * + * @param WC_WeArePlanet_Webhook_Request $request The webhook request object. + * @return void + */ + public function process( WC_WeArePlanet_Webhook_Request $request ) { + /* @var \WeArePlanet\Sdk\Model\TransactionCompletion $completion */ + $completion = $this->load_entity( $request ); + $order = $this->get_order( $completion ); + if ( false != $order && $order->get_id() ) { + $this->process_order_related_inner( $order, $completion, $request ); + } + } + + /** + * Additional processing on the order based on the state of the transaction completion. + * + * @param WC_Order $order The WooCommerce order linked to the completion. + * @param \WeArePlanet\Sdk\Model\TransactionCompletion $completion The transaction completion object. + * @param WC_WeArePlanet_Webhook_Request $request The webhook request. + * @return void + */ + protected function process_order_related_inner( WC_Order $order, \WeArePlanet\Sdk\Model\TransactionCompletion $completion, WC_WeArePlanet_Webhook_Request $request ) { + switch ( $request->get_state() ) { + case \WeArePlanet\Sdk\Model\TransactionCompletionState::FAILED: + $this->failed( $order, $completion ); + break; + case \WeArePlanet\Sdk\Model\TransactionCompletionState::SUCCESSFUL: + $this->success( $order, $completion ); + break; + default: + // Nothing to do. + break; + } + } + + /** + * Handles successful transaction completion. + * + * @param WC_Order $order The associated WooCommerce order. + * @param \WeArePlanet\Sdk\Model\TransactionCompletion $completion The transaction completion data. + * @return void + */ + protected function success( WC_Order $order, \WeArePlanet\Sdk\Model\TransactionCompletion $completion ) { + $completion_job = WC_WeArePlanet_Entity_Completion_Job::load_by_completion( $completion->getLinkedSpaceId(), $completion->getId() ); + if ( ! $completion_job->get_id() ) { + // We have no completion job with this id -> the server could not store the id of the completion after sending the request. (e.g. connection issue or crash) + // We only have on running completion which was not yet processed successfully and use it as it should be the one the webhook is for. + $completion_job = WC_WeArePlanet_Entity_Completion_Job::load_running_completion_for_transaction( + $completion->getLinkedSpaceId(), + $completion->getLinkedTransaction() + ); + if ( ! $completion_job->get_id() ) { + // completion not initiated in shop backend ignore. + return; + } + $completion_job->set_completion_id( $completion->getId() ); + } + $completion_job->set_state( WC_WeArePlanet_Entity_Completion_Job::STATE_DONE ); + + if ( $completion_job->get_restock() ) { + $this->restock_non_completed_items( (array) $completion_job->get_items(), $order ); + } + $this->adapt_order_items( (array) $completion_job->get_items(), $order ); + $completion_job->save(); + } + + /** + * Restock non completed items. + * + * @param array $completed_items completed items. + * @param WC_Order $order order. + * @return void + */ + private function restock_non_completed_items( array $completed_items, WC_Order $order ) { + if ( 'yes' === get_option( 'woocommerce_manage_stock' ) && $order && count( $order->get_items() ) > 0 ) { + foreach ( $order->get_items() as $item_id => $item ) { + $product = $item->get_product(); + if ( $item->is_type( 'line_item' ) && $product && $product->managing_stock() ) { + + $changed_qty = $item->get_quantity(); + if ( isset( $completed_items[ $item_id ] ) ) { + $changed_qty = $changed_qty - $completed_items[ $item_id ]['qty']; + } + if ( $changed_qty > 0 ) { + $item_name = esc_attr( $product->get_formatted_name() ); + $new_stock = wc_update_product_stock( $product, $changed_qty, 'increase' ); + $old_stock = $new_stock - $changed_qty; + + $order->add_order_note( + /* translators: %1$s, %2$s, %3$s are replaced with "string" */ + sprintf( __( '%1$s stock increased from %2$s to %3$s.', 'woo-weareplanet' ), $item_name, $old_stock, $new_stock ) + ); + do_action( 'wc_weareplanet_restock_not_completed_item', $product->get_id(), $old_stock, $new_stock, $order, $product ); + } + } + } + } + } + + /** + * Adapt order items. + * + * @param array $completed_items completed items. + * @param WC_Order $order order. + * @return void + */ + private function adapt_order_items( array $completed_items, WC_Order $order ) { + foreach ( $order->get_items() as $item_id => $item ) { + if ( ! isset( $completed_items[ $item_id ] ) || + $completed_items[ $item_id ]['completion_total'] + array_sum( $completed_items[ $item_id ]['completion_tax'] ) == 0 ) { + $order_item = $order->get_item( $item_id ); + $order_item->delete( true ); + continue; + } + $old_total = $item->get_total(); + $subtotal = $item->get_subtotal(); + $ratio = $old_total / $completed_items[ $item_id ]['completion_total']; + if ( 0 != $ratio ) { + $subtotal = $subtotal / $ratio; + } + $old_taxes = $item->get_taxes(); + $new_taxes = array( + 'total' => array(), + 'subtotal' => array(), + ); + foreach ( array_keys( $old_taxes['total'] ) as $id ) { + $old_tax = $old_taxes['total'][ $id ]; + $subtax = $old_taxes['subtotal'][ $id ]; + if ( 0 != $completed_items[ $item_id ]['completion_tax'][ $id ] ) { + $ration = $old_tax / $completed_items[ $item_id ]['completion_tax'][ $id ]; + if ( 0 != $ration ) { + $subtax = $subtax / $ratio; + } + } + $new_taxes['total'][ $id ] = wc_format_decimal( $completed_items[ $item_id ]['completion_tax'][ $id ], wc_get_price_decimals() ); + $new_taxes['subtotal'][ $id ] = wc_format_decimal( $subtax, wc_get_price_decimals() ); + } + + $item->set_props( + array( + 'quantity' => $completed_items[ $item_id ]['qty'], + 'total' => wc_format_decimal( $completed_items[ $item_id ]['completion_total'], wc_get_price_decimals() ), + 'subtotal' => wc_format_decimal( $subtotal, wc_get_price_decimals() ), + 'taxes' => $new_taxes, + ) + ); + $item->save(); + } + foreach ( $order->get_fees() as $fee_id => $fee ) { + if ( ! isset( $completed_items[ $fee_id ] ) || + $completed_items[ $fee_id ]['completion_total'] + array_sum( $completed_items[ $fee_id ]['completion_tax'] ) == 0 ) { + $order_fee = $order->get_item( $fee_id ); + $order_fee->delete(); + continue; + } + $fee->set_props( + array( + 'total' => $completed_items[ $fee_id ]['completion_total'], + 'taxes' => array( + 'total' => $completed_items[ $fee_id ]['completion_tax'], + ), + ) + ); + $fee->save(); + } + foreach ( $order->get_shipping_methods() as $shipping_id => $shipping ) { + + if ( ! isset( $completed_items[ $shipping_id ] ) || + $completed_items[ $shipping_id ]['completion_total'] + array_sum( $completed_items[ $shipping_id ]['completion_tax'] ) == 0 ) { + $order_shipping = $order->get_item( $shipping_id ); + $order_shipping->delete(); + continue; + } + $shipping->set_props( + array( + 'total' => $completed_items[ $shipping_id ]['completion_total'], + 'taxes' => array( + 'total' => $completed_items[ $shipping_id ]['completion_tax'], + ), + ) + ); + + $shipping->save(); + } + $order->save(); + $order = WC_Order_Factory::get_order( $order->get_id() ); + $order->update_taxes(); + $order->calculate_totals( false ); + } + + /** + * Handles failed transaction completion. + * + * @param WC_Order $order The associated WooCommerce order. + * @param \WeArePlanet\Sdk\Model\TransactionCompletion $completion The transaction completion data that failed. + * @return void + */ + protected function failed( WC_Order $order, \WeArePlanet\Sdk\Model\TransactionCompletion $completion ) { + $completion_job = WC_WeArePlanet_Entity_Completion_Job::load_by_completion( $completion->getLinkedSpaceId(), $completion->getId() ); + if ( ! $completion_job->get_id() ) { + $completion_job = WC_WeArePlanet_Entity_Completion_Job::load_running_completion_for_transaction( + $completion->getLinkedSpaceId(), + $completion->getLinkedTransaction() + ); + if ( ! $completion_job->get_id() ) { + return; + } + $completion_job->set_completion_id( $completion->getId() ); + } + if ( $completion->getFailureReason() != null ) { + $completion_job->set_failure_reason( $completion->getFailureReason()->getDescription() ); + } + $completion_job->set_state( WC_WeArePlanet_Entity_Completion_Job::STATE_DONE ); + $completion_job->save(); + } +} \ No newline at end of file diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-invoice-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-invoice-strategy.php new file mode 100644 index 0000000..1309ef5 --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-invoice-strategy.php @@ -0,0 +1,92 @@ +get_api_client() ); + return $transaction_invoice_service->read( $request->get_space_id(), $request->get_entity_id() ); + } + + /** + * @inheritDoc + */ + protected function get_order_id( $object ) { + /* @var \Wallee\Sdk\Model\TransactionInvoice $object */ + return WC_WeArePlanet_Entity_Transaction_Info::load_by_transaction( + $object->getLinkedSpaceId(), + $object->getCompletion()->getLineItemVersion()->getTransaction()->getId() + )->get_order_id(); + } + + /** + * Processes the incoming webhook request pertaining to transaction invoices. + * + * This method retrieves the transaction invoice details from the API and updates the associated + * WooCommerce order based on the state of the invoice. + * + * @param WC_WeArePlanet_Webhook_Request $request The webhook request object. + * @return void + */ + public function process( WC_WeArePlanet_Webhook_Request $request ) { + /* @var \WeArePlanet\Sdk\Model\TransactionInvoice $transaction_invoice */ + $transaction_invoice = $this->load_entity( $request ); + $order = $this->get_order( $transaction_invoice ); + if ( false != $order && $order->get_id() ) { + $this->process_order_related_inner( $order, $transaction_invoice, $request ); + } + } + + /** + * Additional processing on the order based on the state of the transaction invoice. + * + * @param WC_Order $order The WooCommerce order linked to the invoice. + * @param \WeArePlanet\Sdk\Model\TransactionInvoice $transaction_invoice The transaction invoice object. + * @param WC_WeArePlanet_Webhook_Request $request The webhook request object. + * @return void + */ + protected function process_order_related_inner( WC_Order $order, \WeArePlanet\Sdk\Model\TransactionInvoice $transaction_invoice, WC_WeArePlanet_Webhook_Request $request ) { + switch ( $request->get_state() ) { + case \WeArePlanet\Sdk\Model\TransactionInvoiceState::DERECOGNIZED: + $order->add_order_note( __( 'Invoice Not Settled' ) ); + break; + case \WeArePlanet\Sdk\Model\TransactionInvoiceState::NOT_APPLICABLE: + case \WeArePlanet\Sdk\Model\TransactionInvoiceState::PAID: + $order->add_order_note( __( 'Invoice Settled' ) ); + break; + default: + // Nothing to do. + break; + } + } +} diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-strategy.php new file mode 100644 index 0000000..78068b3 --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-strategy.php @@ -0,0 +1,197 @@ +get_order( $request ); + if ( false != $order && $order->get_id() ) { + $this->process_order_related_inner( $order, $request ); + } + } + + /** + * Process order related inner. + * + * @param WC_Order $order order. + * @param WC_WeArePlanet_Webhook_Request $request request. + * @return void + * @throws Exception Exception. + */ + protected function process_order_related_inner( WC_Order $order, WC_WeArePlanet_Webhook_Request $request ) { + $transaction_info = WC_WeArePlanet_Entity_Transaction_Info::load_by_order_id( $order->get_id() ); + if ( $request->get_state() != $transaction_info->get_state() ) { + switch ( $request->get_state() ) { + case \WeArePlanet\Sdk\Model\TransactionState::CONFIRMED: + case \WeArePlanet\Sdk\Model\TransactionState::PROCESSING: + $this->confirm( $request, $order ); + break; + case \WeArePlanet\Sdk\Model\TransactionState::AUTHORIZED: + $this->authorize( $request, $order ); + break; + case \WeArePlanet\Sdk\Model\TransactionState::DECLINE: + $this->decline( $request, $order ); + break; + case \WeArePlanet\Sdk\Model\TransactionState::FAILED: + $this->failed( $request, $order ); + break; + case \WeArePlanet\Sdk\Model\TransactionState::FULFILL: + $this->authorize( $request, $order ); + $this->fulfill( $request, $order ); + break; + case \WeArePlanet\Sdk\Model\TransactionState::VOIDED: + $this->voided( $request, $order ); + break; + case \WeArePlanet\Sdk\Model\TransactionState::COMPLETED: + $this->authorize( $request, $order ); + $this->waiting( $request, $order ); + break; + default: + // Nothing to do. + break; + } + } + + WC_WeArePlanet_Service_Transaction::instance()->update_transaction_info( $this->load_entity( $request ), $order ); + } + + /** + * Confirm. + * + * @param WC_WeArePlanet_Webhook_Request $request request. + * @param WC_Order $order order. + * @return void + */ + protected function confirm( WC_WeArePlanet_Webhook_Request $request, WC_Order $order ) { + if ( ! $order->get_meta( '_weareplanet_confirmed', true ) && ! $order->get_meta( '_weareplanet_authorized', true ) ) { + do_action( 'wc_weareplanet_confirmed', $this->load_entity( $request ), $order ); + $order->add_meta_data( '_weareplanet_confirmed', 'true', true ); + $status = apply_filters( 'wc_weareplanet_confirmed_status', 'wearep-redirected', $order ); + $order->update_status( $status ); + wc_maybe_reduce_stock_levels( $order->get_id() ); + } + } + + /** + * Authorize. + * + * @param WC_WeArePlanet_Webhook_Request $request request. + * @param \WC_Order $order order. + */ + protected function authorize( WC_WeArePlanet_Webhook_Request $request, WC_Order $order ) { + if ( ! $order->get_meta( '_weareplanet_authorized', true ) ) { + do_action( 'wc_weareplanet_authorized', $this->load_entity( $request ), $order ); + $status = apply_filters( 'wc_weareplanet_authorized_status', 'on-hold', $order ); + $order->add_meta_data( '_weareplanet_authorized', 'true', true ); + $order->update_status( $status ); + wc_maybe_reduce_stock_levels( $order->get_id() ); + if ( isset( WC()->cart ) ) { + WC()->cart->empty_cart(); + } + } + } + + /** + * Waiting. + * + * @param WC_WeArePlanet_Webhook_Request $request request. + * @param WC_Order $order order. + * @return void + */ + protected function waiting( WC_WeArePlanet_Webhook_Request $request, WC_Order $order ) { + if ( ! $order->get_meta( '_weareplanet_manual_check', true ) ) { + do_action( 'wc_weareplanet_completed', $this->load_entity( $request ), $order ); + $status = apply_filters( 'wc_weareplanet_completed_status', 'wearep-waiting', $order ); + $order->update_status( $status ); + } + } + + /** + * Decline. + * + * @param WC_WeArePlanet_Webhook_Request $request request. + * @param WC_Order $order order. + * @return void + */ + protected function decline( WC_WeArePlanet_Webhook_Request $request, WC_Order $order ) { + do_action( 'wc_weareplanet_declined', $this->load_entity( $request ), $order ); + $status = apply_filters( 'wc_weareplanet_decline_status', 'cancelled', $order ); + $order->update_status( $status ); + WC_WeArePlanet_Helper::instance()->maybe_restock_items_for_order( $order ); + } + + /** + * Failed. + * + * @param WC_WeArePlanet_Webhook_Request $request request. + * @param WC_Order $order order. + * @return void + */ + protected function failed( WC_WeArePlanet_Webhook_Request $request, WC_Order $order ) { + do_action( 'wc_weareplanet_failed', $this->load_entity( $request ), $order ); + if ( $order->get_status( 'edit' ) == 'pending' || $order->get_status( 'edit' ) == 'wearep-redirected' ) { + $status = apply_filters( 'wc_weareplanet_failed_status', 'failed', $order ); + $order->update_status( $status ); + WC_WeArePlanet_Helper::instance()->maybe_restock_items_for_order( $order ); + } + } + + /** + * Fulfill. + * + * @param WC_WeArePlanet_Webhook_Request $request request. + * @param WC_Order $order order. + * @return void + */ + protected function fulfill( WC_WeArePlanet_Webhook_Request $request, WC_Order $order ) { + do_action( 'wc_weareplanet_fulfill', $this->load_entity( $request ), $order ); + // Sets the status to procesing or complete depending on items. + $order->payment_complete( $request->get_entity_id() ); + } + + /** + * Voided. + * + * @param WC_WeArePlanet_Webhook_Request $request request. + * @param WC_Order $order order. + * @return void + */ + protected function voided( WC_WeArePlanet_Webhook_Request $request, WC_Order $order ) { + $status = apply_filters( 'wc_weareplanet_voided_status', 'cancelled', $order ); + $order->update_status( $status ); + do_action( 'wc_weareplanet_voided', $this->load_entity( $request ), $order ); + } +} diff --git a/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-void-strategy.php b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-void-strategy.php new file mode 100644 index 0000000..12a43c0 --- /dev/null +++ b/includes/webhook/strategies/class-wc-weareplanet-webhook-transaction-void-strategy.php @@ -0,0 +1,146 @@ +get_api_client() ); + return $void_service->read( $request->get_space_id(), $request->get_entity_id() ); + } + + /** + * @inheritDoc + */ + protected function get_order_id( $object ) { + /* @var \Wallee\Sdk\Model\TransactionVoid $object */ + return WC_WeArePlanet_Entity_Transaction_Info::load_by_transaction( + $object->getTransaction()->getLinkedSpaceId(), + $object->getTransaction()->getId() + )->get_order_id(); + } + + /** + * Processes the incoming webhook request related to transaction voids. + * + * This method checks if the corresponding order exists and if so, it further processes the order + * based on the transaction void state obtained from the webhook request. + * + * @param WC_WeArePlanet_Webhook_Request $request The webhook request. + * @return void + */ + public function process( WC_WeArePlanet_Webhook_Request $request ) { + /* @var \WeArePlanet\Sdk\Model\TransactionVoid $void_transaction */ + $void = $this->load_entity( $request ); + $order = $this->get_order( $void ); + if ( false != $order && $order->get_id() ) { + $this->process_order_related_inner( $order, $void, $request ); + } + } + + /** + * Processes additional order-related operations based on the transaction void's state. + * + * @param WC_Order $order The WooCommerce order associated with the void request. + * @param \WeArePlanet\Sdk\Model\TransactionVoid $void The transaction void object. + * @param WC_WeArePlanet_Webhook_Request $request The webhook request object. + * @return void + */ + protected function process_order_related_inner( WC_Order $order, \WeArePlanet\Sdk\Model\TransactionVoid $void, WC_WeArePlanet_Webhook_Request $request ) { + + switch ( $request->get_state() ) { + case \WeArePlanet\Sdk\Model\TransactionVoidState::FAILED: + $this->failed( $order, $void ); + break; + case \WeArePlanet\Sdk\Model\TransactionVoidState::SUCCESSFUL: + $this->success( $order, $void ); + break; + default: + // Nothing to do. + break; + } + } + + /** + * Successfully processes a transaction void. + * + * @param WC_Order $order The order to process. + * @param \WeArePlanet\Sdk\Model\TransactionVoid $void The transaction void. + * @return void + */ + protected function success( WC_Order $order, \WeArePlanet\Sdk\Model\TransactionVoid $void ) { + $void_job = WC_WeArePlanet_Entity_Void_Job::load_by_void( $void->getLinkedSpaceId(), $void->getId() ); + if ( ! $void_job->get_id() ) { + // We have no void job with this id -> the server could not store the id of the void after sending the request. (e.g. connection issue or crash) + // We only have on running void which was not yet processed successfully and use it as it should be the one the webhook is for. + $void_job = WC_WeArePlanet_Entity_Void_Job::load_running_void_for_transaction( $void->getLinkedSpaceId(), $void->getLinkedTransaction() ); + if ( ! $void_job->get_id() ) { + // void not initiated in shop backend ignore. + return; + } + $void_job->set_void_id( $void->getId() ); + } + $void_job->set_state( WC_WeArePlanet_Entity_Void_Job::STATE_DONE ); + + if ( $void_job->get_restock() ) { + WC_WeArePlanet_Helper::instance()->maybe_restock_items_for_order( $order ); + } + $void_job->save(); + } + + /** + * Handles a failed transaction void. + * + * @param WC_Order $order The order linked to the failed void. + * @param \WeArePlanet\Sdk\Model\TransactionVoid $void The transaction void. + * @return void + */ + protected function failed( WC_Order $order, \WeArePlanet\Sdk\Model\TransactionVoid $void ) { + $void_job = WC_WeArePlanet_Entity_Void_Job::load_by_void( $void->getLinkedSpaceId(), $void->getId() ); + + if ( ! $void_job->get_id() ) { + // We have no void job with this id -> the server could not store the id of the void after sending the request. (e.g. connection issue or crash) + // We only have on running void which was not yet processed successfully and use it as it should be the one the webhook is for. + $void_job = WC_WeArePlanet_Entity_Void_Job::load_running_void_for_transaction( $void->getLinkedSpaceId(), $void->getLinkedTransaction() ); + if ( ! $void_job->get_id() ) { + // void not initiated in shop backend ignore. + return; + } + $void_job->set_void_id( $void->getId() ); + } + if ( $void_job->getFailureReason() != null ) { + $void_job->set_failure_reason( $void->getFailureReason()->getDescription() ); + } + $void_job->set_state( WC_WeArePlanet_Entity_Void_Job::STATE_DONE ); + $void_job->save(); + } +} diff --git a/languages/woo-weareplanet-de_DE.mo b/languages/woo-weareplanet-de_DE.mo index 79f48bb..e11b2eb 100644 Binary files a/languages/woo-weareplanet-de_DE.mo and b/languages/woo-weareplanet-de_DE.mo differ diff --git a/languages/woo-weareplanet-de_DE.po b/languages/woo-weareplanet-de_DE.po index ec23b98..875d6c6 100644 --- a/languages/woo-weareplanet-de_DE.po +++ b/languages/woo-weareplanet-de_DE.po @@ -67,6 +67,10 @@ msgstr "Dokumentation" msgid "A version of the WeArePlanet plugin is yet to be released for this version of WooCommerce." msgstr "Eine Version des WeArePlanet Plugin ist für diese Version von WooCommerce noch nicht freigegeben." +#: includes/class-wc-weareplanet-migration.php:602 +msgid "WooCommerce is not activated. Please activate WooCommerce to use the payment integration." +msgstr "WooCommerce ist nicht aktiviert. Bitte aktivieren Sie WooCommerce, um die Zahlungsintegration zu nutzen." + #: includes/class-wc-weareplanet-customer-document.php:57 msgid "Order Documents" msgstr "Bestelldokumente" diff --git a/languages/woo-weareplanet.pot b/languages/woo-weareplanet.pot index 06bdd5f..bb002c2 100644 --- a/languages/woo-weareplanet.pot +++ b/languages/woo-weareplanet.pot @@ -34,7 +34,7 @@ msgstr "PHP %s+ ist erforderlich. (Sie verwenden die Version %s)" msgid "Wordpress %s+ is required. (You're running version %s)" msgstr "Wordpress %s+ ist erforderlich. (Sie verwenden die Version %s)" -#: includes/class-wc-weareplanet-migration.php:98 +#: includes/class-wc-weareplanet-migration.php:98 #, php-format msgid "Woocommerce %s+ has to be active." msgstr "Woocommerce %s+ muss aktiviert sein." @@ -66,6 +66,10 @@ msgstr "Dokumentation" msgid "A version of the WeArePlanet plugin is yet to be released for this version of WooCommerce." msgstr "Eine Version des WeArePlanet Plugin ist für diese Version von WooCommerce noch nicht freigegeben." +#: includes/class-wc-weareplanet-migration.php:602 +msgid "WooCommerce is not activated. Please activate WooCommerce to use the payment integration." +msgstr "WooCommerce ist nicht aktiviert. Bitte aktivieren Sie WooCommerce, um die Zahlungsintegration zu nutzen." + #: includes/class-wc-weareplanet-customer-document.php:57 msgid "Order Documents" msgstr "Bestellungsdokumente" diff --git a/readme.txt b/readme.txt index f697509..8a90cf7 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: Planet Merchant Services Ltd Tags: woocommerce WeArePlanet, woocommerce, WeArePlanet, payment, e-commerce, webshop, psp, invoice, packing slips, pdf, customer invoice, processing Requires at least: 4.7 Tested up to: 6.5 -Stable tag: 3.0.11 +Stable tag: 3.0.12 License: Apache 2 License URI: http://www.apache.org/licenses/LICENSE-2.0 @@ -23,7 +23,7 @@ To use this extension, a WeArePlanet account is required. Sign up on [WeArePlane == Documentation == -Additional documentation for this plugin is available [here](https://plugin-documentation.weareplanet.com/weareplanet/woocommerce/3.0.11/docs/en/documentation.html). +Additional documentation for this plugin is available [here](https://plugin-documentation.weareplanet.com/weareplanet/woocommerce/3.0.12/docs/en/documentation.html). == Support == @@ -58,8 +58,8 @@ Support queries can be issued on the [WeArePlanet support site](https://payments == Changelog == -= 3.0.11 - Aug 16 2024 = -- [Bugfix] Fixed errors of undefined payment methods += 3.0.12 - Aug 21 2024 = +- [Feature] Implemented the Webhook Signing Mechanism - [Tested Against] PHP 8.0 and PHP 7.4 - [Tested Against] Wordpress 6.6 - [Tested Against] Woocommerce 9.1.4 diff --git a/woocommerce-weareplanet.php b/woocommerce-weareplanet.php index 3fcc607..db49fa5 100644 --- a/woocommerce-weareplanet.php +++ b/woocommerce-weareplanet.php @@ -4,14 +4,14 @@ * * Description: Process WooCommerce payments with WeArePlanet. * License: Apache2 - * Version: 3.0.11 + * Version: 3.0.12 * License URI: http://www.apache.org/licenses/LICENSE-2.0 * Author: Planet Merchant Services Ltd * Author URI: https://www.weareplanet.com * Requires at least: 6.0 * Requires PHP: 7.4 * WC requires at least: 8.0.0 - * WC tested up to: 9.0.2 + * WC tested up to: 9.2.3 * * Text Domain: weareplanet * Domain Path: /languages/ @@ -39,14 +39,14 @@ final class WooCommerce_WeArePlanet { const CK_INTEGRATION = 'wc_weareplanet_integration'; const CK_ORDER_REFERENCE = 'wc_weareplanet_order_reference'; const CK_ENFORCE_CONSISTENCY = 'wc_weareplanet_enforce_consistency'; - const WC_MAXIMUM_VERSION = '9.1.4'; + const WC_MAXIMUM_VERSION = '9.2.3'; /** * WooCommerce WeArePlanet version. * * @var string */ - private $version = '3.0.11'; + private $version = '3.0.12'; /** * The single instance of the class. @@ -1054,8 +1054,10 @@ public function woocommerce_rest_insert_product_attribute( $attribute, $request, * @return mixed */ public function add_cache_no_store( $headers ) { - if ( is_checkout() && isset( $headers['Cache-Control'] ) && stripos( $headers['Cache-Control'], 'no-store' ) === false ) { - $headers['Cache-Control'] .= ', no-store '; + if ( class_exists( 'WooCommerce' ) ) { + if ( is_checkout() && isset( $headers['Cache-Control'] ) && stripos( $headers['Cache-Control'], 'no-store' ) === false ) { + $headers['Cache-Control'] .= ', no-store '; + } } return $headers; }