Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to support HPOS #71

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
31 changes: 19 additions & 12 deletions includes/abstracts/class-wc-gateway-mondido-abstract.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
exit;
} // Exit if accessed directly

use Automattic\WooCommerce\Utilities\OrderUtil;

abstract class WC_Gateway_Mondido_Abstract extends WC_Payment_Gateway {
protected $api;
protected $transaction;
protected $logger;
protected $orderStorage;

public function add_dependencies(WC_Mondido_Api $api, WC_Mondido_Transaction $transaction) {
$this->api = $api;
$this->transaction = $transaction;
$this->orderStorage = OrderStorage::current();
}

/**
Expand Down Expand Up @@ -331,20 +335,22 @@ public function handle_transaction( $order, $transaction_data ) {

// Check transaction was processed
$current_transaction_id = $order->get_transaction_id();
$current_status = get_post_meta( $order_id, '_mondido_transaction_status', true );
$current_status = $this->orderStorage->get_meta_data( $order, '_mondido_transaction_status', true );

if ( $current_transaction_id === $transaction_id && $current_status === $status ) {
throw new \Exception( "Transaction already applied. Order ID: {$order_id}. Transaction ID: {$transaction_id}. Transaction status: {$status}" );
}

// Save Transaction
delete_post_meta( $order_id, '_transaction_id' );
update_post_meta( $order_id, '_transaction_id', $transaction_id );
$this->orderStorage->delete_meta_data( $order, '_transaction_id' );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pair of lines with delete + update is repeating itself in multiple places. In order to reduce duplication we could introduce a replace_meta_data method on the order storage.

$this->orderStorage->update_meta_data( $order, '_transaction_id', $transaction_id );

$this->orderStorage->delete_meta_data( $order, '_mondido_transaction_status' );
$this->orderStorage->update_meta_data( $order, '_mondido_transaction_status', $status );

delete_post_meta( $order_id, '_mondido_transaction_status' );
update_post_meta( $order_id, '_mondido_transaction_status', $status );
$this->orderStorage->delete_meta_data( $order, '_mondido_transaction_data' );
$this->orderStorage->update_meta_data( $order, '_mondido_transaction_data', $transaction_data );

delete_post_meta( $order_id, '_mondido_transaction_data' );
update_post_meta( $order_id, '_mondido_transaction_data', $transaction_data );
$this->orderStorage->save( $order );

switch ( $status ) {
case 'pending':
Expand Down Expand Up @@ -388,11 +394,12 @@ public function handle_transaction( $order, $transaction_data ) {
'postcode' => $details['zip'],
'country' => $this->get_country_alpha2( $details['country_code'] ),
);
update_post_meta( $order_id, '_mondido_invoice_address', $address );
}
$this->orderStorage->update_meta_data( $order, '_mondido_invoice_address', $address );
$this->orderStorage->save( $order );
}
eric-thelin marked this conversation as resolved.
Show resolved Hide resolved

// Define address for Mondido Checkout
if ( (bool) get_post_meta( $order_id, '_mondido_checkout', TRUE ) ) {
if ( (bool) $this->orderStorage->get_meta_data( $order, '_mondido_checkout', TRUE ) ) {
$order->set_address( $address, 'billing' );

if ( $order->needs_shipping_address() ) {
Expand Down Expand Up @@ -556,7 +563,7 @@ public function getMondidoCustomerId( $customer_reference ) {

public function get_payment_method_name($value, $order, $default_value)
{
$transaction = get_post_meta( $order->get_id(), '_mondido_transaction_data', TRUE );
$transaction = $this->orderStorage->get_meta_data( $order, '_mondido_transaction_data', TRUE );

if (!$transaction) {
if ($order->get_transaction_id()) {
Expand Down
40 changes: 23 additions & 17 deletions includes/class-wc-gateway-mondido-hw.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class WC_Gateway_Mondido_HW extends WC_Gateway_Mondido_Abstract {

protected $preselected_method = null;
protected $orderStorage;

/**
* Init
Expand All @@ -16,6 +17,7 @@ public function __construct() {
$this->has_fields = true;
$this->method_title = __( 'Mondido', 'woocommerce-gateway-mondido' );
$this->method_description = '';
$this->orderStorage = OrderStorage::current();

$this->icon = apply_filters( 'woocommerce_mondido_hw_icon', plugins_url( '/assets/images/mondido.png', dirname( __FILE__ ) ) );
$this->supports = array(
Expand Down Expand Up @@ -239,8 +241,9 @@ public function process_payment( $order_id ) {

$token_id = isset( $_POST[$token_key] ) ? wc_clean( $_POST['token_key'] ) : 'new';

delete_post_meta( $order_id, '_mondido_use_store_card');
delete_post_meta( $order_id, '_mondido_store_card');
$this->orderStorage->delete_meta_data($order, '_mondido_use_store_card');
$this->orderStorage->delete_meta_data($order, '_mondido_store_card');
$this->orderStorage->save( $order );

// Try to load saved token
if ( $token_id !== 'new' ) {
Expand All @@ -257,15 +260,16 @@ public function process_payment( $order_id ) {

return false;
}

update_post_meta( $order_id, '_mondido_use_store_card', $token->get_id() );
$this->orderStorage->update_meta_data($order, '_mondido_use_store_card', $token->get_id());
$this->orderStorage->save( $order );
} elseif ( isset( $_POST[$new_card_key] ) && $_POST[$new_card_key] === 'true' ) {
update_post_meta( $order_id, '_mondido_store_card', 1 );
$this->orderStorage->update_meta_data($order, '_mondido_store_card', 1);
$this->orderStorage->save( $order );
}
}

$transaction_id = $order->get_transaction_id();
$store_card = (bool) get_post_meta($order_id, '_mondido_store_card', true);
$store_card = (bool) $this->orderStorage->get_meta_data($order, '_mondido_store_card', true);

if ($transaction_id) {
$transaction = $this->transaction->get($transaction_id);
Expand Down Expand Up @@ -299,7 +303,8 @@ public function process_payment( $order_id ) {
);

if (!is_wp_error($transaction)) {
update_post_meta( $order_id, '_transaction_id', $transaction->id );
$this->orderStorage->update_meta_data($order, '_transaction_id', $transaction->id);
$this->orderStorage->save( $order );
}

}
Expand Down Expand Up @@ -442,7 +447,7 @@ public function notification_callback() {
if ( count( $tokens ) > 0 ) {
$message = 'This Credit Card already stored: ' . $card_number;
header( sprintf( '%s %s %s', 'HTTP/1.1', '200', 'OK' ), TRUE, '200' );
$this->logger->notice( $this->id, sprintf( '[%s] IPN: %s', 'SUCCESS', $message ) );
$this->logger->notice( $this->id, array('status' => '[SUCCESS] IPN', 'message' => $message));
echo sprintf( 'IPN: %s', $message );
}

Expand All @@ -468,19 +473,19 @@ public function notification_callback() {
// Success
$message = 'Stored Credit Card: ' . $card_number;
header( sprintf( '%s %s %s', 'HTTP/1.1', '200', 'OK' ), TRUE, '200' );
$this->logger->notice( $this->id, sprintf( '[%s] IPN: %s', 'SUCCESS', $message ) );
$this->logger->notice($this->id, array('status' => '[SUCCESS] IPN', 'message' => $message));
echo sprintf( 'IPN: %s', $message );
return;
}

$this->logger->notice( $this->id, var_export($data, true) );
$this->logger->notice( $this->id, array( 'data' => $data) );

if ( empty( $data['id'] ) ) {
throw new \Exception( 'Invalid transaction ID' );
}

// Log transaction details
$this->logger->notice( $this->id, 'Incoming Transaction: ' . var_export( json_encode( $data, true ), true) );
$this->logger->notice( $this->id, array('message' => 'Incoming Transaction', 'data' => $data) );

// Wait for unlock
$times = 0;
Expand Down Expand Up @@ -523,11 +528,12 @@ public function notification_callback() {
'total' => $transaction_data['amount'],
'created_via' => 'mondido',
) );
add_post_meta( $order->get_id(), '_payment_method', $this->id );
update_post_meta( $order->get_id(), '_transaction_id', $transaction_data['id'] );
update_post_meta( $order->get_id(), '_mondido_transaction_status', $transaction_data['status'] );
update_post_meta( $order->get_id(), '_mondido_transaction_data', $transaction_data );
update_post_meta( $order->get_id(), '_mondido_subscription_id', $transaction_data['subscription']['id'] );

$this->orderStorage->add_meta_data($order, '_payment_method', $this->id);
$this->orderStorage->update_meta_data($order, '_transaction_id', $transaction_data['id']);
$this->orderStorage->update_meta_data($order, '_mondido_transaction_status', $transaction_data['status']);
$this->orderStorage->update_meta_data($order, '_mondido_transaction_data', $transaction_data);
$this->orderStorage->update_meta_data($order, '_mondido_subscription_id', $transaction_data['subscription']['id']);

// Add address
$order->set_address( $transaction_data['metadata']['customer'], 'billing' );
Expand Down Expand Up @@ -630,7 +636,7 @@ public function notification_callback() {

// Success
header( sprintf( '%s %s %s', 'HTTP/1.1', '200', 'OK' ), TRUE, '200' );
$this->logger->notice( $this->id, sprintf( '[%s] IPN: %s', 'SUCCESS', $message ) );
$this->logger->notice($this->id, array('status' => '[SUCCESS] IPN', 'message' => $message));
echo sprintf( 'IPN: %s', $message );
exit();
}
Expand Down
28 changes: 19 additions & 9 deletions includes/class-wc-mondido-admin-actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
exit;
} // Exit if accessed directly

use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;

class WC_Mondido_Admin_Actions {
/**
* Constructor
Expand All @@ -18,6 +20,8 @@ public function __construct() {
$this,
'ajax_mondido_capture'
) );

$this->orderStorage = OrderStorage::current();
}

/**
Expand All @@ -28,13 +32,17 @@ public static function add_meta_boxes() {
global $post_id;
$order = wc_get_order( $post_id );
if ( $order && strpos( $order->get_payment_method(), 'mondido' ) !== false ) {
$transaction = get_post_meta( $order->get_id(), '_mondido_transaction_data', TRUE );
$transaction = $this->orderStorage->get_meta_data( $order, '_mondido_transaction_data', TRUE );
if ( ! empty( $transaction ) ) {
$screen = class_exists( '\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController' ) && wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled()
eric-thelin marked this conversation as resolved.
Show resolved Hide resolved
? wc_get_page_screen_id( 'shop-order' )
: 'shop_order';

add_meta_box(
'mondido_payment_actions',
__( 'Mondido Payments', 'woocommerce-gateway-mondido' ),
__CLASS__ . '::order_meta_box_payment_actions',
'shop_order',
$screen,
'side',
'default'
);
Expand All @@ -46,10 +54,11 @@ public static function add_meta_boxes() {
* MetaBox for Payment Actions
* @return void
*/
public static function order_meta_box_payment_actions() {
public static function order_meta_box_payment_actions( $post_or_order_object ) {
global $post_id;
$order = wc_get_order( $post_id );
$transaction = get_post_meta( $order->get_id(), '_mondido_transaction_data', TRUE );
$order = ( $post_or_order_object instanceof WP_Post ) ? wc_get_order( $post_or_order_object->ID ) : $post_or_order_object;

$transaction = $this->orderStorage->get_meta_data( $order, '_mondido_transaction_data', TRUE );

wc_get_template(
'admin/payment-actions.php',
Expand Down Expand Up @@ -111,10 +120,11 @@ public function ajax_mondido_capture() {
}

if ( $transaction['status'] === 'approved' ) {
// Save Transaction
update_post_meta( $order->get_id(), '_transaction_id', $transaction['id'] );
update_post_meta( $order->get_id(), '_mondido_transaction_status', $transaction['status'] );
update_post_meta( $order->get_id(), '_mondido_transaction_data', $transaction );

$this->orderStorage->update_meta_data( $order, '_transaction_id', $transaction['id'] );
$this->orderStorage->update_meta_data( $order, '_mondido_transaction_status', $transaction['status'] );
$this->orderStorage->update_meta_data( $order, '_mondido_transaction_data', $transaction );
$this->orderStorage->save( $order );

$order->add_order_note( sprintf( __( 'Payment captured. Transaction Id: %s', 'woocommerce-gateway-mondido' ), $transaction['id'] ) );
$order->payment_complete( $transaction['id'] );
Expand Down
68 changes: 68 additions & 0 deletions includes/class-wc-mondido-hpos-compatibility.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
} // Exit if accessed directly

use Automattic\WooCommerce\Utilities\OrderUtil;

interface OrderStorageInterface {
Copy link

@eric-thelin eric-thelin Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having Interface in the name is ugly and violates the IS-A relationship to implementing classes. For instance, HPOSOrderStorage implements OrderStorageInterface but it is wrong to say that it "is a" OrderStorageInterface. HPOSOrderStorage is not an interface, it is a kind of order storage. The same is true for CPTOrderStorage. Since both those classes are examples of order storage, it makes sense to use the name OrderStorage for the shared interface.

If the problem is that we already use the name OrderStorage as a placeholder for the current() method, my suggestion is that we merge OrderStorageInterface and OrderStorage into an abstract class named OrderStorage.

abstract class OrderStorage
{
	public static function current(): OrderStorage
	{
		if (OrderUtil::custom_orders_table_usage_is_enabled()) {
			return new HPOSOrderStorage();
		} else {
			return new CPTOrderStorage();
		}
	}

	public abstract function get_meta_data($order, $meta_key, $meta_value);
	public abstract function add_meta_data($order, $meta_key, $meta_value);
	public abstract function update_meta_data($order, $meta_key, $meta_value);
	public abstract function delete_meta_data($order, $meta_key);
	public abstract function save($order);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's exactly the issue I saw with clashing names. Thanks for the input, learning a lot about OOP best practices here, great stuff :)

public function get_meta_data($order, $meta_key, $meta_value);
public function add_meta_data($order, $meta_key, $meta_value);
public function update_meta_data($order, $meta_key, $meta_value);
public function delete_meta_data($order, $meta_key);
public function save($order);
}

class HPOSOrderStorage implements OrderStorageInterface {
public function get_meta_data($order, $meta_key, $meta_value) {
return $order->get_meta($meta_key, $meta_value);
}

public function add_meta_data($order, $meta_key, $meta_value) {
$order->add_meta_data($meta_key, $meta_value);
}

public function update_meta_data($order, $meta_key, $meta_value) {
$order->update_meta_data($meta_key, $meta_value);
}

public function delete_meta_data($order, $meta_key) {
$order->delete_meta_data($meta_key);
}

public function save($order) {
$order->save();
}
}

class CPTOrderStorage implements OrderStorageInterface {
public function get_meta_data($order, $meta_key, $meta_value) {
return get_post_meta($order->get_id(), $meta_key, $meta_value);
}

public function add_meta_data($order, $meta_key, $meta_value) {
add_post_meta($order->get_id(), $meta_key, $meta_value);
}

public function update_meta_data($order, $meta_key, $meta_value) {
update_post_meta($order->get_id(), $meta_key, $meta_value);
}

public function delete_meta_data($order, $meta_key) {
delete_post_meta($order->get_id(), $meta_key);
}

public function save($order) {
return; // Do nothing, CPT doesn't use save
}
}

class OrderStorage {
public static function current() {
eric-thelin marked this conversation as resolved.
Show resolved Hide resolved
if (OrderUtil::custom_orders_table_usage_is_enabled()) {
return new HPOSOrderStorage();
} else {
return new CPTOrderStorage();
}
}
}
2 changes: 2 additions & 0 deletions includes/deprecated/class-wc-order-compatibility-mondido.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
exit;
} // Exit if accessed directly

// FILE SHOULD BE DELETED AFTER UPGRADE OF WOOCOMMERCE 8.7

/**
* Compatibility Layer for WC_Order on WooCommerce < 3.0
* @see https://woocommerce.wordpress.com/2017/04/04/say-hello-to-woocommerce-3-0-bionic-butterfly/
Expand Down
6 changes: 6 additions & 0 deletions woocommerce-gateway-mondido.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public function __construct() {
register_deactivation_hook( __FILE__, array( $this, 'flush_rewrite_rules' ) );

// Actions
add_action( 'before_woocommerce_init', function() {
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this call should move to either OrderStorageTechnology or OrderUtil so that we have all mentions of the custom orders table in a single location. However, if we move the logic we must pass __FILE__ as an argument to preserve the behavior, e.g. OrderStorageTechnology::declare_compatibility(__FILE__);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it fits here since it's something that should be added before woocommerce init.
A very similar add_action call is made further down in that function:
"// WC_Order Compatibility for WC < 3.0
add_action( 'woocommerce_init', CLASS . '::add_compatibility' );"

Which is also for a one-off compatibility file, so i feel like it fits here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea was just to move the mention of the custom orders table to either OrderStorage or OrderUtil since that is an implementation detail of HPOS. Thinking of it now, an even better location is probably HPOSOrderStorage. The call to add_action is probably best kept here.

add_action( 'before_woocommerce_init', function() {
	HPOSOrderStorage::declare_compatibility(__FILE__);
}

However, if you prefer the current solution I am fine with that.

\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
}
} );
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array(
$this,
'plugin_action_links'
Expand Down Expand Up @@ -124,6 +129,7 @@ public function woocommerce_loaded() {
include_once( dirname( __FILE__ ) . '/includes/class-wc-gateway-mondido-card.php' );
include_once( dirname( __FILE__ ) . '/includes/class-wc-mondido-api.php' );
include_once( dirname( __FILE__ ) . '/includes/class-wc-mondido-transaction.php' );
include_once( dirname( __FILE__ ) . '/includes/class-wc-mondido-hpos-compatibility.php' );

include_once( dirname( __FILE__ ) . '/includes/class-wc-mondido-admin-actions.php' );
include_once( dirname( __FILE__ ) . '/includes/class-wc-mondido-subscriptions.php' );
Expand Down