update_status( $status );
}
return false;
}
/**
* Add order item meta data.
*
* @param int $item_id
* @param string $meta_key
* @param mixed $meta_value
* @param string $prev_value
*
* @return false|int
*/
function learn_press_add_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) {
return add_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $prev_value );
}
/**
* Update order item meta data.
*
* @param int $item_id
* @param string $meta_key
* @param mixed $meta_value
* @param string $prev_value
*
* @return bool|int
*/
function learn_press_update_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) {
return update_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $prev_value );
}
/**
* Delete order item meta data.
*
* @param int $item_id
* @param string $meta_key
* @param mixed $meta_value
* @param bool $delete_all
*
* @return bool
*/
function learn_press_delete_order_item_meta( $item_id, $meta_key, $meta_value, $delete_all = false ) {
return delete_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $delete_all );
}
/**
* Get order item meta data.
*
* @param int $item_id
* @param string $meta_key
* @param bool $single
*
* @return mixed
*/
function learn_press_get_order_item_meta( $item_id, $meta_key, $single = true ) {
return get_metadata( 'learnpress_order_item', $item_id, $meta_key, $single );
}
/**
* Get order
*
* @param mixed $the_order
*
* @return LP_Order|bool object instance
*/
function learn_press_get_order( $the_order = false ) {
global $post;
$the_id = 0;
if ( false === $the_order && is_a( $post, 'WP_Post' ) && LP_ORDER_CPT === get_post_type( $post ) ) {
$the_id = $post->ID;
} elseif ( is_numeric( $the_order ) ) {
$the_id = $the_order;
} elseif ( $the_order instanceof LP_Order ) {
$the_id = $the_order->get_id();
} elseif ( ! empty( $the_order->ID ) ) {
$the_id = $the_order->ID;
}
if ( LP_ORDER_CPT != get_post_type( $the_id ) ) {
return false;
}
return new LP_Order( $the_id );
}
/**
* Count orders by it's status
*
* @param array $args
* @Todo tungnx review to rewrite query
* @return array
*/
function learn_press_count_orders( $args = array() ) {
if ( is_string( $args ) ) {
$args = array( 'status' => $args );
} else {
$args = wp_parse_args(
$args,
array(
'status' => '',
)
);
}
global $wpdb;
$statuses = $args['status'];
if ( ! $statuses ) {
$statuses = array_keys( LP_Order::get_order_statuses() );
}
settype( $statuses, 'array' );
$size_of_status = sizeof( $statuses );
foreach ( $statuses as $k => $status ) {
$statuses[ $k ] = ! preg_match( '~^lp-~', $status ) ? 'lp-' . $status : $status;
}
$format = array_fill( 0, $size_of_status, '%s' );
$counts = array_fill_keys( $statuses, 0 );
$statuses[] = LP_ORDER_CPT;
$query = $wpdb->prepare(
"
SELECT COUNT(ID) AS count, post_status AS status
FROM {$wpdb->posts} o
WHERE post_status IN(" . join( ',', $format ) . ')
AND post_type = %s
GROUP BY o.post_status
',
$statuses
);
$results = $wpdb->get_results( $query );
if ( $results ) {
foreach ( $results as $result ) {
if ( array_key_exists( $result->status, $counts ) ) {
$counts[ $result->status ] = absint( $result->count );
}
}
}
return $size_of_status > 1 ? $counts : reset( $counts );
}
/**
* Format price with currency and other settings.
*
* @param float $price
* @param string $currency
*
* @return string
*/
function learn_press_format_price( $price = 0, $currency = '' ): string {
if ( ! is_numeric( $price ) ) {
$price = 0;
}
$before = $after = '';
$currency = esc_html(
is_string( $currency ) && '' !== $currency
? $currency
: learn_press_get_currency_symbol()
);
$thousands_separator = esc_html( LP_Settings::get_option( 'thousands_separator', ',' ) );
$number_of_decimals = esc_html( LP_Settings::get_option( 'number_of_decimals', 2 ) );
$decimals_separator = esc_html( LP_Settings::get_option( 'decimals_separator', '.' ) );
switch ( LP_Settings::get_option( 'currency_pos' ) ) {
default:
$before = $currency;
break;
case 'left_with_space':
$before = $currency . ' ';
break;
case 'right':
$after = $currency;
break;
case 'right_with_space':
$after = ' ' . $currency;
}
return $before . number_format( $price, $number_of_decimals, $decimals_separator, $thousands_separator ) . $after;
}
/**
* Update
*
* @param $order_id
*
* @return array|bool
*/
function learn_press_update_order_items( $order_id ) {
$order = learn_press_get_order( $order_id );
if ( ! $order ) {
return false;
}
$subtotal = 0;
$total = 0;
$items = $order->get_items();
if ( $items ) {
foreach ( $items as $item ) {
$subtotal += $item['subtotal'];
$total += $item['total'];
}
}
update_post_meta( $order_id, '_order_currency', learn_press_get_currency() );
update_post_meta( $order_id, '_prices_include_tax', 'no' );
update_post_meta( $order_id, '_order_subtotal', $subtotal );
update_post_meta( $order_id, '_order_total', $total );
update_post_meta( $order_id, '_order_key', learn_press_generate_order_key() );
update_post_meta( $order_id, '_payment_method', '' );
update_post_meta( $order_id, '_payment_method_title', '' );
update_post_meta( $order_id, '_order_version', '1.0' );
return array(
'subtotal' => $subtotal,
'total' => $total,
'currency' => learn_press_get_currency(),
);
}
/**
* Format order's ID in ten numbers. Eg: 0000000XXX.
*
* @param int $order_number
*
* @since 2.0.0
*
* @return string
*/
function learn_press_transaction_order_number( $order_number ) {
$formatted_number = apply_filters( 'learn_press_get_order_number', '#' . sprintf( "%'.010d", $order_number ), $order_number );
return apply_filters( 'learn-press/order-number-formatted', $formatted_number, $order_number );
}
/**
* Get list of registered order's statues for registering with wp post's status.
*
* @since 2.0.0
*
* @return array
*/
function learn_press_get_register_order_statuses() {
$order_statues = array();
$order_statues['lp-completed'] = array(
'label' => _x( 'Completed', 'Order status', 'learnpress' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Completed (%s)', 'Completed (%s)', 'learnpress' ),
);
$order_statues['lp-pending'] = array(
'label' => _x( 'Pending', 'Order status', 'learnpress' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Pending Payment (%s)', 'Pending Payment (%s)', 'learnpress' ),
);
$order_statues['lp-processing'] = array(
'label' => _x( 'Processing', 'Order status', 'learnpress' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Processing (%s)', 'Processing (%s)', 'learnpress' ),
);
$order_statues['lp-cancelled'] = array(
'label' => _x( 'Cancelled', 'Order status', 'learnpress' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Cancelled (%s)', 'Cancelled (%s)', 'learnpress' ),
);
$order_statues['lp-failed'] = array(
'label' => _x( 'Failed', 'Order status', 'learnpress' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Failed (%s)', 'Failed (%s)', 'learnpress' ),
);
$order_statues['lp-refunded'] = array(
'label' => _x( 'Refunded', 'Order status', 'learnpress' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Refunded (%s)', 'Refunded (%s)', 'learnpress' ),
);
$order_statues['trash'] = array(
'label' => _x( 'Trash', 'Order status', 'learnpress' ),
'public' => false,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Trash (%s)', 'Trash (%s)', 'learnpress' ),
);
return $order_statues;
}
function _learn_press_get_order_status_description( $status ) {
$status = str_replace( 'lp-', '', (string) $status );
$descriptions = array(
'pending' => __( 'Order received in case a user purchases a course but doesn\'t finalize the order.', 'learnpress' ),
'processing' => __( 'Payment received and the order is awaiting fulfillment.', 'learnpress' ),
'completed' => __( 'The order is fulfilled and completed.', 'learnpress' ),
'cancelled' => __( 'The order is cancelled by an admin or the customer.', 'learnpress' ),
'refunded' => __( 'Order was refunded to the customer.', 'learnpress' ),
);
return apply_filters( 'learn_press_order_status_description', ! empty( $descriptions[ $status ] ) ? $descriptions[ $status ] : '' );
}
/**
* Get status of an order by the ID.
*
* @param int $order_id
*
* @return bool|string
*/
function learn_press_get_order_status( $order_id ) {
$order = learn_press_get_order( $order_id );
if ( $order ) {
return $order->get_status();
}
return false;
}
if ( ! function_exists( 'learn_press_get_refund_setting' ) ) {
/**
* Get refund setting value with safe defaults.
*
* @param string $key
* @param mixed $default
*
* @since 4.3.4
* @version 1.0.0
* @return mixed
*/
function learn_press_get_refund_setting( string $key, $default = null ) {
$defaults = array(
'enable_refund_requests' => 'no',
'auto_refund' => 'no',
'refund_time_limit' => 30,
'require_refund_reason' => 'no',
'allow_resend_after_rejected' => 'no',
'refund_max_completion' => 0,
);
if ( null === $default && array_key_exists( $key, $defaults ) ) {
$default = $defaults[ $key ];
}
return LP_Settings::get_option( $key, $default );
}
}
if ( ! function_exists( 'learn_press_get_order_refund_supported_gateways' ) ) {
/**
* Get list gateways that support refund.
*
* @since 4.3.4
* @version 1.1.0
* @return array
*/
function learn_press_get_order_refund_supported_gateways(): array {
if ( ! class_exists( 'LP_Gateways' ) ) {
return array();
}
$gateways_instance = LP_Gateways::instance();
if ( ! $gateways_instance || ! method_exists( $gateways_instance, 'get_gateways' ) ) {
return array();
}
$supported_gateways = array();
$all_gateways = (array) $gateways_instance->get_gateways();
foreach ( $all_gateways as $gateway_id => $gateway ) {
$gateway_id = sanitize_key( (string) $gateway_id );
if ( empty( $gateway_id ) ) {
continue;
}
if ( ! is_object( $gateway ) || ! is_callable( array( $gateway, 'refund' ) ) ) {
continue;
}
$supported_gateways[] = $gateway_id;
}
return array_values( array_unique( $supported_gateways ) );
}
}
if ( ! function_exists( 'learn_press_cancel_order_process' ) ) {
/**
* Process action allows user to cancel an order is pending
* in their profile.
*/
function learn_press_cancel_order_process() {
if ( empty( $_REQUEST['cancel-order'] ) || empty( $_REQUEST['lp-nonce'] ) ||
! wp_verify_nonce( $_REQUEST['lp-nonce'], 'cancel-order' ) || is_admin() ) {
return;
}
$user_id = get_current_user_id();
$profile = LP_Profile::instance( $user_id );
$url = $profile->get_tab_link(
LP_Settings::instance()->get( 'profile_endpoints.orders', 'orders' )
);
try {
$message = array(
'status' => 'error',
'content' => '',
);
$order_id = absint( $_REQUEST['cancel-order'] );
$order = learn_press_get_order( $order_id );
if ( ! $order ) {
throw new Exception( sprintf( __( 'Order number %s not found', 'learnpress' ), $order_id ) );
}
$user_ids = (array) $order->get_user_id();
if ( ! in_array( $user_id, $user_ids ) ) {
throw new Exception( __( 'You do not have permission to cancel this order.', 'learnpress' ) );
}
if ( $order->has_status( LP_ORDER_PENDING ) ) {
$order->update_status( LP_ORDER_CANCELLED );
$order->add_note( __( 'The order is cancelled by the customer', 'learnpress' ) );
$message['status'] = 'success';
$message['content'] = sprintf( __( 'Order number %s has been cancelled', 'learnpress' ), $order->get_order_number() );
} else {
throw new Exception(
__( 'The order number %s can not be cancelled.', 'learnpress' ),
$order->get_order_number()
);
}
} catch ( Throwable $e ) {
$message['content'] = $e->getMessage();
}
learn_press_set_message( $message );
wp_safe_redirect( $url );
exit();
}
}
add_action( 'init', 'learn_press_cancel_order_process' );
if ( ! function_exists( 'learn_press_get_order_refund_event_data' ) ) {
/**
* Build normalized refund event payload.
*
* @since 4.3.5
* @version 1.0.1
*
* @param LP_Order $order
* @param array $overrides
*
* @return array
*/
function learn_press_get_order_refund_event_data( LP_Order $order, array $overrides = array() ): array {
$order_id = $order->get_id();
$requested_at = get_post_meta( $order_id, '_lp_refund_requested_at', true );
if ( ! empty( $requested_at ) ) {
$requested_at = new LP_Datetime( $requested_at );
$requested_at = sprintf(
esc_html__( '%1$s %2$s', 'learnpress' ),
$requested_at->format( LP_Datetime::I18N_FORMAT_HAS_TIME ),
LP_Datetime::get_timezone_string()
);
}
$user_id = (int) $order->get_user_id();
$userOrderModel = UserModel::find( $user_id, true );
if ( $userOrderModel instanceof UserModel ) {
$requested_by = $userOrderModel->get_display_name();
} else {
$requested_by = $order->get_checkout_email();
}
$data = array(
'order_id' => $order_id,
'order_number' => $order->get_order_number(),
'order_key' => $order->get_order_key(),
'order_status' => $order->get_status(),
'request_status' => $order->get_refund_request(),
'requested_by' => $requested_by,
'requested_at' => $requested_at,
'reviewed_by' => absint( get_post_meta( $order_id, '_lp_refund_reviewed_by', true ) ),
'reviewed_at' => (string) get_post_meta( $order_id, '_lp_refund_reviewed_at', true ),
'reason' => (string) get_post_meta( $order_id, LP_Order::META_KEY_REFUND_REQUEST_REASON, true ),
'requester_email' => '',
'admin_order_edit_url' => add_query_arg(
array(
'post' => $order_id,
'action' => 'edit',
),
admin_url( 'post.php' )
),
);
$requested_user_id = absint( $data['requested_by'] );
if ( ! empty( $requested_user_id ) ) {
$requested_user = get_user_by( 'id', $requested_user_id );
if ( $requested_user instanceof WP_User ) {
$data['requester_email'] = $requested_user->user_email;
}
}
if ( ! empty( $overrides ) ) {
$data = array_merge( $data, $overrides );
}
return $data;
}
}
/**
* Render pending refund request panel on order detail.
*
* @since 4.3.4
* @version 1.0.0
* @param LP_Order $order
*/
function learn_press_admin_order_refund_request_panel( $order ) {
if ( ! $order instanceof LP_Order ) {
return;
}
$order_id = $order->get_id();
if ( ! current_user_can( 'edit_post', $order_id ) ) {
return;
}
$refund_event_data = learn_press_get_order_refund_event_data( $order );
$refund_request_status = sanitize_key( (string) ( $refund_event_data['request_status'] ?? '' ) );
$requested_by = $refund_event_data['requested_by'] ?? '';
$requested_at = $refund_event_data['requested_at'] ?? '';
$refund_reason = $refund_event_data['reason'] ?? '';
$requester_email = '';
$status_labels = array(
'pending' => __( 'Pending review', 'learnpress' ),
'approved' => __( 'Approved', 'learnpress' ),
'auto-approved' => __( 'Auto approved', 'learnpress' ),
'rejected' => __( 'Rejected', 'learnpress' ),
);
$status_label = $status_labels[ $refund_request_status ] ?? '';
if ( empty( $status_label ) && ! empty( $refund_request_status ) ) {
$status_label = ucwords( str_replace( '-', ' ', $refund_request_status ) );
}
$render_statuses = array( 'pending', 'approved', 'auto-approved' );
if ( ! in_array( $refund_request_status, $render_statuses, true ) ) {
return;
}
$is_pending = ( 'pending' === $refund_request_status );
$currency_symbol = learn_press_get_currency_symbol( $order->get_currency() );
$order_total = round( max( 0, floatval( $order->get_total() ) ), 2 );
$order_total_formatted = learn_press_format_price( $order_total, $currency_symbol );
// Refund amount is only meaningful once the refund has been executed
// (approved/auto-approved), when _lp_refund_amount has been written.
$refund_amount_formatted = '';
if ( ! $is_pending ) {
$refund_amount = get_post_meta( $order_id, LP_Order::META_KEY_REFUNDED_AMOUNT, true );
if ( '' === $refund_amount || null === $refund_amount ) {
$refund_amount = $order_total; // Fallback for full refunds without a stored amount.
}
$refund_amount = round( max( 0, floatval( $refund_amount ) ), 2 );
$refund_amount_formatted = learn_press_format_price( $refund_amount, $currency_symbol );
}
learn_press_admin_view(
'meta-boxes/order/refund-request-panel',
compact(
'order_id',
'is_pending',
'order_total',
'order_total_formatted',
'refund_amount_formatted',
'requested_by',
'requested_at',
'requester_email',
'refund_reason',
'status_label'
)
);
}
/**
* get total price order complete
*/
function learn_press_get_total_price_order_complete() {
global $wpdb;
$query = $wpdb->prepare(
"SELECT SUM(meta_value) as order_total From `{$wpdb->prefix}postmeta` as mt
INNER JOIN `{$wpdb->prefix}posts` as p ON p.id = mt.post_id
WHERE p.post_type = %s AND mt.meta_key = %s
AND p.post_status = %s
",
LP_ORDER_CPT,
'_order_total',
'lp-completed'
);
$total = $wpdb->get_results( $query )[0]->order_total;
return learn_press_format_price( $total, true );
}