<?php
// phpcs:ignoreFile
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
* @package FacebookCommerce
*/
namespace SkyVerge\WooCommerce\Facebook\Commerce;
use SkyVerge\WooCommerce\Facebook\API\Orders\Cancel\Request as Cancellation_Request;
use SkyVerge\WooCommerce\Facebook\API\Orders\Order;
use SkyVerge\WooCommerce\Facebook\Products;
use SkyVerge\WooCommerce\Facebook\API\Orders\Refund\Request as Refund_Request;
use SkyVerge\WooCommerce\Facebook\Utilities;
use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_API_Exception;
use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Plugin_Exception;
defined( 'ABSPATH' ) or exit;
/**
* General Commerce orders handler.
*
* @since 2.1.0
*/
class Orders {
/** @var string the fetch orders action */
const ACTION_FETCH_ORDERS = 'wc_facebook_commerce_fetch_orders';
/** @var string the meta key used to store the remote order ID */
const REMOTE_ID_META_KEY = '_wc_facebook_commerce_remote_id';
/** @var string the meta key used to store the email remarketing option */
const EMAIL_REMARKETING_META_KEY = '_wc_facebook_commerce_email_remarketing';
/** @var string buyer's remorse refund reason */
const REFUND_REASON_BUYERS_REMORSE = 'BUYERS_REMORSE';
/** @var string damaged goods refund reason */
const REFUND_REASON_DAMAGED_GOODS = 'DAMAGED_GOODS';
/** @var string not as described refund reason */
const REFUND_REASON_NOT_AS_DESCRIBED = 'NOT_AS_DESCRIBED';
/** @var string quality issue refund reason */
const REFUND_REASON_QUALITY_ISSUE = 'QUALITY_ISSUE';
/** @var string wrong item refund reason */
const REFUND_REASON_WRONG_ITEM = 'WRONG_ITEM';
/** @var string other refund reason */
const REFUND_REASON_OTHER = 'REFUND_REASON_OTHER';
/** @var string customer requested cancellation */
const CANCEL_REASON_CUSTOMER_REQUESTED = 'CUSTOMER_REQUESTED';
/** @var string out of stock cancellation */
const CANCEL_REASON_OUT_OF_STOCK = 'OUT_OF_STOCK';
/** @var string invalid address cancellation */
const CANCEL_REASON_INVALID_ADDRESS = 'INVALID_ADDRESS';
/** @var string suspicious order cancellation */
const CANCEL_REASON_SUSPICIOUS_ORDER = 'SUSPICIOUS_ORDER';
/** @var string other reason cancellation */
const CANCEL_REASON_OTHER = 'CANCEL_REASON_OTHER';
/**
* Orders constructor.
*
* @since 2.1.0
*/
public function __construct() {
$this->add_hooks();
}
/**
* Returns whether or not the order is a pending Commerce order.
*
* @since 2.1.0
*
* @param \WC_Order $order order object
* @return bool
*/
public static function is_order_pending( \WC_Order $order ) {
return self::is_commerce_order( $order ) && 'pending' === $order->get_status();
}
/**
* Returns whether or not the order is a Commerce order.
*
* @since 2.1.0
*
* @param \WC_Order $order order object
* @return bool
*/
public static function is_commerce_order( \WC_Order $order ) {
return in_array( $order->get_created_via(), array( 'instagram', 'facebook' ), true );
}
/**
* Finds a local order based on the Commerce ID stored in REMOTE_ID_META_KEY.
*
* @since 2.1.0
*
* @param string $remote_id Commerce order ID
* @return \WC_Order|null
*/
public function find_local_order( $remote_id ) {
$orders = wc_get_orders(
array(
'limit' => 1,
'status' => 'any',
'meta_key' => self::REMOTE_ID_META_KEY,
'meta_value' => $remote_id,
)
);
return ! empty( $orders ) ? current( $orders ) : null;
}
/**
* Creates a local WooCommerce order based on an Orders API order object.
*
* @since 2.1.0
*
* @param Order $remote_order Orders API order object
* @return \WC_Order
* @throws SV_WC_Plugin_Exception|\WC_Data_Exception
*/
public function create_local_order( Order $remote_order ) {
$local_order = new \WC_Order();
$local_order->set_created_via( $remote_order->get_channel() );
$local_order->set_status( 'pending' );
$local_order->save();
$local_order = $this->update_local_order( $remote_order, $local_order );
return $local_order;
}
/**
* Updates a local WooCommerce order based on an Orders API order object.
*
* @since 2.1.0
*
* @param Order $remote_order Orders API order object
* @param \WC_Order $local_order local order object
* @return \WC_Order
* @throws SV_WC_Plugin_Exception|\WC_Data_Exception
*/
public function update_local_order( Order $remote_order, \WC_Order $local_order ) {
$total_items_tax = 0;
// add/update items
foreach ( $remote_order->get_items() as $item ) {
$product = Products::get_product_by_fb_product_id( $item['product_id'] );
if ( empty( $product ) ) {
$product = Products::get_product_by_fb_retailer_id( $item['retailer_id'] );
}
if ( ! $product instanceof \WC_Product ) {
// add a note and skip this item
$local_order->add_order_note( "Product with retailer ID {$item['retailer_id']} not found" );
continue;
}
$matching_wc_order_item = false;
// check if the local order already has this item
foreach ( $local_order->get_items() as $wc_order_item ) {
if ( ! $wc_order_item instanceof \WC_Order_Item_Product ) {
continue;
}
$order_item_product_id = $wc_order_item->get_variation_id() ?: $wc_order_item->get_product_id();
if ( $product->get_id() === $order_item_product_id ) {
$matching_wc_order_item = $wc_order_item;
break;
}
}
if ( empty( $matching_wc_order_item ) ) {
$matching_wc_order_item = new \WC_Order_Item_Product();
$matching_wc_order_item->set_product( $product );
$local_order->add_item( $matching_wc_order_item );
}
$matching_wc_order_item->set_quantity( $item['quantity'] );
$matching_wc_order_item->set_subtotal( $item['quantity'] * $item['price_per_unit']['amount'] );
$matching_wc_order_item->set_total( $item['quantity'] * $item['price_per_unit']['amount'] );
// we use the estimated_tax because the captured_tax represents the tax after the order/item has been shipped and we don't fulfill order at the line-item level
$matching_wc_order_item->set_taxes(
array(
'subtotal' => array( $item['tax_details']['estimated_tax']['amount'] ),
'total' => array( $item['tax_details']['estimated_tax']['amount'] ),
)
);
$matching_wc_order_item->save();
if ( ! empty( $item['tax_details']['estimated_tax']['amount'] ) ) {
$total_items_tax += $item['tax_details']['estimated_tax']['amount'];
}
}
// update information from selected_shipping_option
$selected_shipping_option = $remote_order->get_selected_shipping_option();
$matching_shipping_order_item = false;
// check if the local order already has this item
if ( ! empty( $shipping_order_items = $local_order->get_items( 'shipping' ) ) ) {
/** @var \WC_Order_Item_Shipping $shipping_order_item */
foreach ( $shipping_order_items as $shipping_order_item ) {
if ( $selected_shipping_option['name'] === $shipping_order_item->get_method_title() ) {
$matching_shipping_order_item = $shipping_order_item;
}
}
}
if ( empty( $matching_shipping_order_item ) ) {
$matching_shipping_order_item = new \WC_Order_Item_Shipping();
$matching_shipping_order_item->set_method_title( $selected_shipping_option['name'] );
$local_order->add_item( $matching_shipping_order_item );
}
$matching_shipping_order_item->set_total( $selected_shipping_option['price']['amount'] );
$matching_shipping_order_item->set_taxes(
array(
'total' => array( $selected_shipping_option['calculated_tax']['amount'] ),
)
);
$matching_shipping_order_item->save();
// add tax item
$matching_tax_order_item = false;
// check if the local order already has a tax item item
if ( ! empty( $tax_order_items = $local_order->get_items( 'tax' ) ) ) {
$matching_tax_order_item = current( $tax_order_items );
}
if ( empty( $matching_tax_order_item ) ) {
$matching_tax_order_item = new \WC_Order_Item_Tax();
$local_order->add_item( $matching_tax_order_item );
}
$matching_tax_order_item->set_tax_total( $total_items_tax );
$matching_tax_order_item->set_shipping_tax_total( $selected_shipping_option['calculated_tax']['amount'] );
$matching_tax_order_item->save();
$local_order->set_shipping_total( $selected_shipping_option['price']['amount'] );
$local_order->set_shipping_tax( $selected_shipping_option['calculated_tax']['amount'] );
// update information from shipping_address
$shipping_address = $remote_order->get_shipping_address();
if ( ! empty( $shipping_address['name'] ) ) {
if ( strpos( $shipping_address['name'], ' ' ) !== false ) {
list( $first_name, $last_name ) = explode( ' ', $shipping_address['name'], 2 );
$local_order->set_shipping_first_name( $first_name );
$local_order->set_shipping_last_name( $last_name );
} else {
$local_order->set_shipping_last_name( $shipping_address['name'] );
}
}
if ( ! empty( $shipping_address['street1'] ) ) {
$local_order->set_shipping_address_1( $shipping_address['street1'] );
}
if ( ! empty( $shipping_address['street2'] ) ) {
$local_order->set_shipping_address_2( $shipping_address['street2'] );
}
if ( ! empty( $shipping_address['city'] ) ) {
$local_order->set_shipping_city( $shipping_address['city'] );
}
if ( ! empty( $shipping_address['state'] ) ) {
$local_order->set_shipping_state( $shipping_address['state'] );
}
if ( ! empty( $shipping_address['postal_code'] ) ) {
$local_order->set_shipping_postcode( $shipping_address['postal_code'] );
}
if ( ! empty( $shipping_address['country'] ) ) {
$local_order->set_shipping_country( $shipping_address['country'] );
}
// update information from estimated_payment_details
$estimated_payment_details = $remote_order->get_estimated_payment_details();
// we do not use subtotal values from the API because WC calculates them on the fly based on the items
$local_order->set_total( $estimated_payment_details['total_amount']['amount'] );
$local_order->set_currency( $estimated_payment_details['total_amount']['currency'] );
// update information from buyer_details
$buyer_details = $remote_order->get_buyer_details();
if ( ! empty( $buyer_details ) ) {
if ( ! empty( $buyer_details['name'] ) ) {
if ( strpos( $buyer_details['name'], ' ' ) !== false ) {
list( $first_name, $last_name ) = explode( ' ', $buyer_details['name'], 2 );
$local_order->set_billing_first_name( $first_name );
$local_order->set_billing_last_name( $last_name );
} else {
$local_order->set_billing_last_name( $buyer_details['name'] );
}
}
if ( ! empty( $buyer_details['email'] ) ) {
$local_order->set_billing_email( $buyer_details['email'] );
}
$local_order->update_meta_data( self::EMAIL_REMARKETING_META_KEY, wc_bool_to_string( $buyer_details['email_remarketing_option'] ) );
}
// set remote ID
$local_order->update_meta_data( self::REMOTE_ID_META_KEY, $remote_order->get_id() );
// always reduce stock levels
wc_reduce_stock_levels( $local_order );
$local_order->save();
return $local_order;
}
/**
* Updates WooCommerce’s Orders by fetching orders from the API and either creating or updating local orders.
*
* @since 2.1.0
*/
public function update_local_orders() {
// sanity check for connection status
if ( ! facebook_for_woocommerce()->get_commerce_handler()->is_connected() ) {
return;
}
$page_id = facebook_for_woocommerce()->get_integration()->get_facebook_page_id();
try {
$response = facebook_for_woocommerce()->get_api( facebook_for_woocommerce()->get_connection_handler()->get_page_access_token() )->get_new_orders( $page_id );
} catch ( SV_WC_API_Exception $exception ) {
facebook_for_woocommerce()->log( 'Error fetching Commerce orders from the Orders API: ' . $exception->getMessage() );
return;
}
$remote_orders = $response->get_orders();
foreach ( $remote_orders as $remote_order ) {
$local_order = $this->find_local_order( $remote_order->get_id() );
try {
if ( empty( $local_order ) ) {
$local_order = $this->create_local_order( $remote_order );
} else {
$local_order = $this->update_local_order( $remote_order, $local_order );
}
} catch ( \Exception $exception ) {
if ( ! empty( $local_order ) ) {
// add note to order
$local_order->add_order_note( 'Error updating local order from Commerce order from the Orders API: ' . $exception->getMessage() );
} else {
facebook_for_woocommerce()->log( 'Error creating local order from Commerce order from the Orders API: ' . $exception->getMessage() );
}
continue;
}
if ( ! empty( $local_order ) && Order::STATUS_CREATED === $remote_order->get_status() ) {
// acknowledge the order
try {
facebook_for_woocommerce()->get_api( facebook_for_woocommerce()->get_connection_handler()->get_page_access_token() )->acknowledge_order( $remote_order->get_id(), $local_order->get_id() );
$local_order->set_status( 'processing' );
/* translators: Placeholders: %1$s - order remote id, %2$s - order created by */
$local_order->add_order_note(
sprintf(
__( 'Order %1$s paid in %2$s', 'facebook-for-woocommerce' ),
$remote_order->get_id(),
ucfirst( $remote_order->get_channel() )
)
);
} catch ( SV_WC_API_Exception $exception ) {
$local_order->add_order_note( 'Error acknowledging the order: ' . $exception->getMessage() );
// if we have a clear indication that the order was not found, cancel it locally
if ( 803 === (int) $exception->getCode() ) {
$local_order->set_status( 'cancelled' );
}
}
$local_order->save();
}
}
// update any local orders that have since been cancelled on Facebook
$this->update_cancelled_orders();
}
/**
* Updates any local orders that have since been cancelled on Facebook.
*
* @since 2.1.0
*/
public function update_cancelled_orders() {
$page_id = facebook_for_woocommerce()->get_integration()->get_facebook_page_id();
try {
$response = facebook_for_woocommerce()->get_api( facebook_for_woocommerce()->get_connection_handler()->get_page_access_token() )->get_cancelled_orders( $page_id );
} catch ( SV_WC_API_Exception $exception ) {
facebook_for_woocommerce()->log( 'Error fetching Commerce orders from the Orders API: ' . $exception->getMessage() );
return;
}
foreach ( $response->get_orders() as $remote_order ) {
$local_order = $this->find_local_order( $remote_order->get_id() );
if ( ! $local_order instanceof \WC_Order || 'cancelled' === $local_order->get_status() ) {
continue;
}
$local_order->set_status( 'cancelled' );
$local_order->save();
wc_increase_stock_levels( $local_order );
}
}
/**
* Frequency in seconds that orders are updated.
*
* @since 2.1.0
*
* @return int
*/
public function get_order_update_interval() {
$default_interval = 5 * MINUTE_IN_SECONDS;
/**
* Filters the interval between querying Facebook for new or updated orders.
*
* @since 2.1.0
*
* @param int $interval interval in seconds. Defaults to 5 minutes, and the minimum interval is 120 seconds.
*/
$interval = apply_filters( 'wc_facebook_commerce_order_update_interval', $default_interval );
// if given a valid number, ensure it's 120 seconds at a minimum
if ( is_numeric( $interval ) ) {
$interval = max( 2 * MINUTE_IN_SECONDS, $interval );
} else {
$interval = $default_interval; // invalid values should get the default
}
return $interval;
}
/**
* Schedules a recurring ACTION_FETCH_ORDERS action, if not already scheduled.
*
* @internal
*
* @since 2.1.0
*/
public function schedule_local_orders_update() {
if ( facebook_for_woocommerce()->get_commerce_handler()->is_connected() && false === as_next_scheduled_action( self::ACTION_FETCH_ORDERS, array(), \WC_Facebookcommerce::PLUGIN_ID ) ) {
$interval = $this->get_order_update_interval();
as_schedule_recurring_action( time() + $interval, $interval, self::ACTION_FETCH_ORDERS, array(), \WC_Facebookcommerce::PLUGIN_ID );
}
}
/**
* Adds the necessary action & filter hooks.
*
* @since 2.1.0
*/
public function add_hooks() {
// schedule a recurring ACTION_FETCH_ORDERS action, if not already scheduled
add_action( 'init', array( $this, 'schedule_local_orders_update' ) );
add_action( self::ACTION_FETCH_ORDERS, array( $this, 'update_local_orders' ) );
// prevent sending emails for Commerce orders
add_action( 'woocommerce_email_enabled_customer_completed_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
add_action( 'woocommerce_email_enabled_customer_processing_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
add_action( 'woocommerce_email_enabled_customer_refunded_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
add_action( 'woocommerce_email_enabled_customer_partially_refunded_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
}
/**
* Fulfills an order via API.
*
* In addition to the exceptions we throw for missing data, the API request will also fail if:
* - The stored remote ID is invalid
* - The order has an item with a retailer ID that was not originally part of the order
* - An item has a different quantity than what was originally ordered
* - The remote order was already fulfilled
*
* @since 2.1.0
*
* @param \WC_Order $order order object
* @param string $tracking_number shipping tracking number
* @param string $carrier shipping carrier
* @throws SV_WC_Plugin_Exception
*/
public function fulfill_order( \WC_Order $order, $tracking_number, $carrier ) {
try {
$remote_id = $order->get_meta( self::REMOTE_ID_META_KEY );
if ( ! $remote_id ) {
throw new SV_WC_Plugin_Exception( __( 'Remote ID not found.', 'facebook-for-woocommerce' ) );
}
$shipment_utilities = new Utilities\Shipment();
if ( ! $shipment_utilities->is_valid_carrier( $carrier ) ) {
/** translators: Placeholders: %s - shipping carrier code */
throw new SV_WC_Plugin_Exception( sprintf( __( '%s is not a valid shipping carrier code.', 'facebook-for-woocommerce' ), $carrier ) );
}
$items = array();
/** @var \WC_Order_Item_Product $item */
foreach ( $order->get_items() as $item ) {
if ( $product = $item->get_product() ) {
$items[] = array(
'retailer_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
'quantity' => $item->get_quantity(),
);
}
}
if ( empty( $items ) ) {
throw new SV_WC_Plugin_Exception( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
}
$fulfillment_data = array(
'items' => $items,
'tracking_info' => array(
'carrier' => $carrier,
'tracking_number' => $tracking_number,
),
);
$plugin = facebook_for_woocommerce();
$plugin->get_api( $plugin->get_connection_handler()->get_page_access_token() )->fulfill_order( $remote_id, $fulfillment_data );
$order->add_order_note(
sprintf(
/* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
__( '%s order fulfilled.', 'facebook-for-woocommerce' ),
ucfirst( $order->get_created_via() )
)
);
} catch ( SV_WC_Plugin_Exception $exception ) {
$order->add_order_note(
sprintf(
/* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
__( '%1$s order could not be fulfilled. %2$s', 'facebook-for-woocommerce' ),
ucfirst( $order->get_created_via() ),
$exception->getMessage()
)
);
throw $exception;
}
}
/**
* Refunds an order.
*
* @since 2.1.0
*
* @param \WC_Order_Refund $refund order refund object
* @param string $reason_code refund reason code
* @throws SV_WC_Plugin_Exception
*/
public function add_order_refund( \WC_Order_Refund $refund, $reason_code ) {
$plugin = facebook_for_woocommerce();
$api = $plugin->get_api( $plugin->get_connection_handler()->get_page_access_token() );
$valid_reason_codes = array(
self::REFUND_REASON_BUYERS_REMORSE,
self::REFUND_REASON_DAMAGED_GOODS,
self::REFUND_REASON_NOT_AS_DESCRIBED,
self::REFUND_REASON_QUALITY_ISSUE,
self::REFUND_REASON_OTHER,
self::REFUND_REASON_WRONG_ITEM,
);
if ( ! in_array( $reason_code, $valid_reason_codes, true ) ) {
$reason_code = self::REFUND_REASON_OTHER;
}
try {
$parent_order = wc_get_order( $refund->get_parent_id() );
if ( ! $parent_order instanceof \WC_Order ) {
throw new SV_WC_Plugin_Exception( __( 'Parent order not found.', 'facebook-for-woocommerce' ) );
}
$remote_id = $parent_order->get_meta( self::REMOTE_ID_META_KEY );
if ( ! $remote_id ) {
throw new SV_WC_Plugin_Exception( __( 'Remote ID for parent order not found.', 'facebook-for-woocommerce' ) );
}
$refund_data = array(
'reason_code' => $reason_code,
);
if ( ! empty( $reason_text = $refund->get_reason() ) ) {
$refund_data['reason_text'] = $reason_text;
}
// only send items for partial refunds
if ( $parent_order->get_total() - $refund->get_amount() > 0 ) {
$refund_data['items'] = $this->get_refund_items( $refund );
}
if ( ! empty( $refund->get_shipping_total() ) ) {
$refund_data['shipping'] = array(
'shipping_refund' => array(
'amount' => abs( $refund->get_shipping_total() ),
'currency' => $refund->get_currency(),
),
);
}
$api->add_order_refund( $remote_id, $refund_data );
$parent_order->add_order_note(
sprintf(
/* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
__( 'Order refunded on %s.', 'facebook-for-woocommerce' ),
ucfirst( $parent_order->get_created_via() )
)
);
} catch ( SV_WC_Plugin_Exception $exception ) {
if ( ! empty( $parent_order ) && $parent_order instanceof \WC_Order ) {
$parent_order->add_order_note(
sprintf(
/* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
__( 'Could not refund %1$s order: %2$s', 'facebook-for-woocommerce' ),
ucfirst( $parent_order->get_created_via() ),
$exception->getMessage()
)
);
} else {
facebook_for_woocommerce()->log( "Could not refund remote order for order refund {$refund->get_id()}: {$exception->getMessage()}" );
}
// re-throw the exception so the error halts refund creation
throw $exception;
}
}
/**
* Gets the Facebook items from the given refund.
*
* @since 2.1.0
*
* @param \WC_Order_Refund $refund refund object
* @return array
* @throws SV_WC_Plugin_Exception
*/
private function get_refund_items( \WC_Order_Refund $refund ) {
$items = array();
/** @var \WC_Order_Item_Product $item */
foreach ( $refund->get_items() as $item ) {
if ( $product = $item->get_product() ) {
$refund_item = array(
'retailer_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
);
if ( ! empty( $item->get_quantity() ) ) {
$refund_item['item_refund_quantity'] = abs( $item->get_quantity() );
} else {
$refund_item['item_refund_amount'] = array(
'amount' => abs( $item->get_total() ),
'currency' => $refund->get_currency(),
);
}
$items[] = $refund_item;
}
}
if ( empty( $items ) ) {
throw new SV_WC_Plugin_Exception( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
}
return $items;
}
/**
* Cancels an order.
*
* @since 2.1.0
*
* @param \WC_Order $order order object
* @param string $reason_code cancellation reason code
* @throws SV_WC_Plugin_Exception
*/
public function cancel_order( \WC_Order $order, $reason_code ) {
$plugin = facebook_for_woocommerce();
$api = $plugin->get_api( $plugin->get_connection_handler()->get_page_access_token() );
$valid_reason_codes = array_keys( $this->get_cancellation_reasons() );
if ( ! in_array( $reason_code, $valid_reason_codes, true ) ) {
$reason_code = self::CANCEL_REASON_OTHER;
}
try {
$remote_id = $order->get_meta( self::REMOTE_ID_META_KEY );
if ( ! $remote_id ) {
throw new SV_WC_Plugin_Exception( __( 'Remote ID not found.', 'facebook-for-woocommerce' ) );
}
$api->cancel_order( $remote_id, $reason_code );
$order->add_order_note(
sprintf(
/* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
__( '%s order cancelled.', 'facebook-for-woocommerce' ),
ucfirst( $order->get_created_via() )
)
);
} catch ( SV_WC_Plugin_Exception $exception ) {
$order->add_order_note(
sprintf(
/* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
__( '%1$s order could not be cancelled. %2$s', 'facebook-for-woocommerce' ),
ucfirst( $order->get_created_via() ),
$exception->getMessage()
)
);
throw $exception;
}
}
/**
* Gets the valid cancellation reasons.
*
* @since 2.1.0
*
* @return array key-value array with codes and their labels
*/
public function get_cancellation_reasons() {
return array(
self::CANCEL_REASON_CUSTOMER_REQUESTED => __( 'Customer requested cancellation', 'facebook-for-woocommerce' ),
self::CANCEL_REASON_OUT_OF_STOCK => __( 'Product(s) are out of stock', 'facebook-for-woocommerce' ),
self::CANCEL_REASON_INVALID_ADDRESS => __( 'Customer address is invalid', 'facebook-for-woocommerce' ),
self::CANCEL_REASON_SUSPICIOUS_ORDER => __( 'Suspicious order', 'facebook-for-woocommerce' ),
self::CANCEL_REASON_OTHER => __( 'Other', 'facebook-for-woocommerce' ),
);
}
/**
* Prevents sending emails for Commerce orders.
*
* @internal
*
* @since 2.1.0
*
* @param bool $is_enabled whether the email is enabled in the first place
* @param \WC_Order $order order object
* @return bool
*/
public function maybe_stop_order_email( $is_enabled, $order ) {
// will decide whether to allow $is_enabled to be filtered
$is_previously_enabled = $is_enabled;
// checks whether or not the order is a Commerce order
$is_commerce_order = $order instanceof \WC_Order && self::is_commerce_order( $order );
// decides whether to disable or to keep emails enabled
$is_enabled = $is_enabled && ! $is_commerce_order;
if ( $is_previously_enabled && $is_commerce_order ) {
/**
* Filters the flag used to determine whether the email is enabled.
*
* @since 2.1.0
*
* @param bool $is_enabled whether the email is enabled
* @param \WC_Order $order order object
* @param Orders $this admin orders instance
*/
$is_enabled = (bool) apply_filters( 'wc_facebook_commerce_send_woocommerce_emails', $is_enabled, $order, $this );
}
return $is_enabled;
}
}