Skip to content

Switch use of WC_Order::set_date_paid for WC_Order::maybe_set_date_paid in maybe_record_subscription_payment() #791

Open
@billrobbins

Description

We recently ran into a situation where the paid dates on several orders were being updated sometimes many months after the order was actually paid for. Here's the flow we have seen using WooCommerce, Subscriptions, and Stripe.

  1. A renewal order is created and paid for via the Stripe gateway.
  2. The customer files a dispute with their credit card company. The order's status is changed to on-hold while the dispute is investigated.
  3. When the dispute is won, the order status is returned to its original state prior to the dispute. In this case, the updated status is completed.
  4. At this point the paid date is also updated to the current date/time.

After digging into this for a bit, it appears the date is being updated in WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment. This method is hooked into the woocommerce_order_status_changed action and is designed to only impact renewal orders.

public static function maybe_record_subscription_payment( $order_id, $orders_old_status, $orders_new_status ) {
if ( ! wcs_order_contains_renewal( $order_id ) ) {
return;
}
$subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id );
$was_activated = false;
$order = wc_get_order( $order_id );
$order_completed = in_array( $orders_new_status, array( apply_filters( 'woocommerce_payment_complete_order_status', 'processing', $order_id, $order ), 'processing', 'completed' ) );
$order_needed_payment = in_array( $orders_old_status, apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'on-hold', 'failed' ), $order ) );
if ( $order_completed && $order_needed_payment ) {
if ( wcs_is_woocommerce_pre( '3.0' ) ) {
$update_post_data = array(
'ID' => $order_id,
'post_date' => current_time( 'mysql', 0 ),
'post_date_gmt' => current_time( 'mysql', 1 ),
);
wp_update_post( $update_post_data );
update_post_meta( $order_id, '_paid_date', current_time( 'mysql' ) );
} else {
$current_time = current_time( 'timestamp', 1 );
// Prior to WC 3.0, we need to update the post date (i.e. the date created) to have a reliable representation of the paid date (both because it was in GMT and because it was always set). That's not needed in WC 3.0, but some plugins and store owners still rely on it being updated, so we want to make it possible to update it with 3.0 also.
if ( apply_filters( 'wcs_renewal_order_payment_update_date_created', false, $order, $subscriptions ) ) {
$order->set_date_created( $current_time );
}
// In WC 3.0, only the paid date prop represents the paid date, the post date isn't used anymore, also the paid date is stored and referenced as a MySQL date string in site timezone and a GMT timestamp
$order->set_date_paid( $current_time );
$order->save();
}
}

Would it be possible to swap out the call to $order->set_date_paid() for $order->maybe_set_date_paid()? The latter method checks to see if the paid date is already set and only updates it if it hasn't been set already.

https://github.com/woocommerce/woocommerce/blob/8309364c4f0ac56dda0f42047d91895960c5f9f7/plugins/woocommerce/includes/class-wc-order.php#L339-L361

Thanks for your consideration.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

type: bugThe issue is a confirmed bug.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions