| @@ -5,8 +5,10 @@ | ||
| 5 | 5 | * @author ThimPress |
| 6 | 6 | * @package LearnPress/Functions |
| 7 | 7 | * @version 1.0 |
| 8 | 8 | */ |
| 9 | +use LearnPress\Models\UserItems\UserCourseModel; | |
| 10 | +use LearnPress\Models\UserModel; | |
| 9 | 11 | |
| 10 | 12 | defined( 'ABSPATH' ) || exit; |
| 11 | 13 | |
| 12 | 14 | /** |
| @@ -18,61 +20,8 @@ | ||
| 18 | 20 | return apply_filters( 'learn-press/order-key', strtoupper( uniqid( 'ORDER' ) ) ); |
| 19 | 21 | } |
| 20 | 22 | |
| 21 | 23 | /** |
| 22 | - * Return default order status when creating new. | |
| 23 | - * | |
| 24 | - * @param string $prefix . Optional | |
| 25 | - * | |
| 26 | - * @return string | |
| 27 | - */ | |
| 28 | -function learn_press_default_order_status( $prefix = '' ) { | |
| 29 | - return apply_filters( 'learn-press/default-order-status', $prefix . 'pending' ); | |
| 30 | -} | |
| 31 | - | |
| 32 | -/** | |
| 33 | - * Create or update an order. | |
| 34 | - * If an 'ID' is passed throught $order_data, then update the existing order | |
| 35 | - * | |
| 36 | - * @param array $order_data { | |
| 37 | - * Array of options to add or update an order | |
| 38 | - * | |
| 39 | - * @type int ID The order ID | |
| 40 | - * @type | |
| 41 | - * } | |
| 42 | - * | |
| 43 | - * @return LP_Order|WP_Error | |
| 44 | - * @editor tungnx | |
| 45 | - * @modify 4.1.4 - comment - not use | |
| 46 | - */ | |
| 47 | -/*function learn_press_create_order( $order_data ) { | |
| 48 | - | |
| 49 | - $order = new LP_Order(); | |
| 50 | - $order->save(); | |
| 51 | - | |
| 52 | - return $order; | |
| 53 | -}*/ | |
| 54 | - | |
| 55 | -/** | |
| 56 | - * Create a new order or update an existing | |
| 57 | - * | |
| 58 | - * @param array $order_data | |
| 59 | - * | |
| 60 | - * @return LP_Order|WP_Error | |
| 61 | - * | |
| 62 | - * @throws Exception | |
| 63 | - */ | |
| 64 | - | |
| 65 | -/*function learn_press_update_order( $order_data ) { | |
| 66 | - if ( empty( $order_data['order_id'] ) ) { | |
| 67 | - throw new Exception( __( 'Invalid order ID when updating.', 'learnpress' ) ); | |
| 68 | - } | |
| 69 | - | |
| 70 | - return learn_press_create_order( $order_data ); | |
| 71 | -}*/ | |
| 72 | - | |
| 73 | -/**************************/ | |
| 74 | -/** | |
| 75 | 24 | * Update Order status |
| 76 | 25 | * |
| 77 | 26 | * @param int |
| 78 | 27 | * @param string |
| @@ -88,85 +37,8 @@ | ||
| 88 | 37 | return false; |
| 89 | 38 | } |
| 90 | 39 | |
| 91 | 40 | /** |
| 92 | - * Add an order item into order. | |
| 93 | - * | |
| 94 | - * @param int $order_id | |
| 95 | - * @param mixed $item - Array of item data or ID | |
| 96 | - * | |
| 97 | - * @return bool | |
| 98 | - * @editor tungnx | |
| 99 | - * @modify 4.1.4 - comment - not use | |
| 100 | - */ | |
| 101 | -/*function learn_press_add_order_item( $order_id, $item ) { | |
| 102 | - $item_id = false; | |
| 103 | - $order = learn_press_get_order( $order_id ); | |
| 104 | - | |
| 105 | - if ( $order ) { | |
| 106 | - $item_id = $order->add_item( $item ); | |
| 107 | - } | |
| 108 | - | |
| 109 | - return $item_id; | |
| 110 | -}*/ | |
| 111 | - | |
| 112 | -/** | |
| 113 | - * Remove an order item by order_item_id. | |
| 114 | - * | |
| 115 | - * @param int $order_id | |
| 116 | - * @param int $item_id | |
| 117 | - * | |
| 118 | - * @return bool | |
| 119 | - */ | |
| 120 | -function learn_press_remove_order_item( $order_id, $item_id ) { | |
| 121 | - $order = learn_press_get_order( $order_id ); | |
| 122 | - | |
| 123 | - if ( $order ) { | |
| 124 | - return $order->remove_item( $item_id ); | |
| 125 | - } | |
| 126 | - | |
| 127 | - return false; | |
| 128 | -} | |
| 129 | - | |
| 130 | -function _learn_press_before_delete_order_item( $item_id, $order_id ) { | |
| 131 | - global $wpdb; | |
| 132 | - $order = learn_press_get_order_by_item_id( $item_id ); | |
| 133 | - | |
| 134 | - if ( $order ) { | |
| 135 | - $course_id = learn_press_get_order_item_meta( $item_id, '_course_id' ); | |
| 136 | - learn_press_delete_user_data( $order->user_id, $course_id ); | |
| 137 | - } | |
| 138 | -} | |
| 139 | - | |
| 140 | -// add_action( 'learn-press/before-delete-order-item', '_learn_press_before_delete_order_item', 10, 2 ); | |
| 141 | - | |
| 142 | -function _learn_press_ajax_add_order_item_meta( $item ) { | |
| 143 | - $item_id = $item['id']; | |
| 144 | - $order = learn_press_get_order_by_item_id( $item_id ); | |
| 145 | - | |
| 146 | - if ( $order ) { | |
| 147 | - if ( $order->get_status() == 'completed' ) { | |
| 148 | - learn_press_auto_enroll_user_to_courses( $order->id ); | |
| 149 | - } | |
| 150 | - } | |
| 151 | -} | |
| 152 | - | |
| 153 | -add_action( 'learn_press_ajax_add_order_item_meta', '_learn_press_ajax_add_order_item_meta' ); | |
| 154 | - | |
| 155 | -function learn_press_get_order_by_item_id( $item_id ) { | |
| 156 | - global $wpdb; | |
| 157 | - | |
| 158 | - $order_id = $wpdb->get_var( $wpdb->prepare( "SELECT order_id FROM {$wpdb->prefix}learnpress_order_items WHERE order_item_id = %d", $item_id ) ); | |
| 159 | - $order = learn_press_get_order( $order_id ); | |
| 160 | - | |
| 161 | - if ( $order_id && $order ) { | |
| 162 | - return $order; | |
| 163 | - } | |
| 164 | - | |
| 165 | - return false; | |
| 166 | -} | |
| 167 | - | |
| 168 | -/** | |
| 169 | 41 | * Add order item meta data. |
| 170 | 42 | * |
| 171 | 43 | * @param int $item_id |
| 172 | 44 | * @param string $meta_key |
| @@ -247,250 +119,12 @@ | ||
| 247 | 119 | return new LP_Order( $the_id ); |
| 248 | 120 | } |
| 249 | 121 | |
| 250 | 122 | /** |
| 251 | - * get confirm order URL | |
| 252 | - * | |
| 253 | - * @param int $order_id | |
| 254 | - * | |
| 255 | - * @return string | |
| 256 | - */ | |
| 257 | -function learn_press_get_order_confirm_url( $order_id = 0 ) { | |
| 258 | - $url = ''; | |
| 259 | - $confirm_page_id = learn_press_get_page_id( 'taken_course_confirm' ); | |
| 260 | - | |
| 261 | - if ( $confirm_page_id && get_post( $confirm_page_id ) ) { | |
| 262 | - $url = get_permalink( $confirm_page_id ); | |
| 263 | - | |
| 264 | - if ( $order_id ) { | |
| 265 | - $url = join( preg_match( '!\?!', $url ) ? '&' : '?', array( $url, "order_id={$order_id}" ) ); | |
| 266 | - } | |
| 267 | - } else { | |
| 268 | - $order = new LP_Order( $order_id ); | |
| 269 | - $items = $order->get_items(); | |
| 270 | - | |
| 271 | - if ( $items && ! empty( $items->products ) ) { | |
| 272 | - $course = reset( $items->products ); | |
| 273 | - $url = get_permalink( $course['id'] ); | |
| 274 | - } else { | |
| 275 | - $url = get_home_url(); | |
| 276 | - } | |
| 277 | - } | |
| 278 | - | |
| 279 | - return $url; | |
| 280 | -} | |
| 281 | - | |
| 282 | - | |
| 283 | -function learn_press_do_transaction( $method, $transaction = false ) { | |
| 284 | - LP_Gateways::instance()->get_available_payment_gateways(); | |
| 285 | - | |
| 286 | - do_action( 'learn_press_do_transaction_' . $method, $transaction ); | |
| 287 | -} | |
| 288 | - | |
| 289 | -function learn_press_set_transient_transaction( $method, $temp_id, $user_id, $transaction ) { | |
| 290 | - set_transient( | |
| 291 | - $method . '-' . $temp_id, | |
| 292 | - array( | |
| 293 | - 'user_id' => $user_id, | |
| 294 | - 'transaction_object' => $transaction, | |
| 295 | - ), | |
| 296 | - 60 * 60 * 24 | |
| 297 | - ); | |
| 298 | -} | |
| 299 | - | |
| 300 | -function learn_press_get_transient_transaction( $method, $temp_id ) { | |
| 301 | - return get_transient( $method . '-' . $temp_id ); | |
| 302 | -} | |
| 303 | - | |
| 304 | -function learn_press_delete_transient_transaction( $method, $temp_id ) { | |
| 305 | - return delete_transient( $method . '-' . $temp_id ); | |
| 306 | -} | |
| 307 | - | |
| 308 | - | |
| 309 | -/** | |
| 310 | - * Deprecated function | |
| 311 | - * | |
| 312 | - * @param array $args | |
| 313 | - * | |
| 314 | - * @return int | |
| 315 | - * @deprecated 4.2.0 | |
| 316 | - */ | |
| 317 | -function learn_press_add_transaction( $args = null ) { | |
| 318 | - // _deprecated_function( 'learn_press_add_transaction', '1.0', 'learn_press_add_order' ); | |
| 319 | - return learn_press_add_order( $args ); | |
| 320 | -} | |
| 321 | - | |
| 322 | -/** | |
| 323 | - * @param null $args | |
| 324 | - * | |
| 325 | - * @return mixed | |
| 326 | - * @deprecated 4.2.0 | |
| 327 | - */ | |
| 328 | -function learn_press_add_order( $args = null ) { | |
| 329 | - _deprecated_function( __FUNCTION__, '4.2.0' ); | |
| 330 | - // $method, $method_id, $status = 'Pending', $customer_id = false, $transaction_object = false, $args = array() | |
| 331 | - /*$default_args = array( | |
| 332 | - 'method' => '', | |
| 333 | - 'method_id' => '', | |
| 334 | - 'status' => '', | |
| 335 | - 'user_id' => null, | |
| 336 | - 'order_id' => 0, | |
| 337 | - 'parent' => 0, | |
| 338 | - 'transaction_object' => false, | |
| 339 | - ); | |
| 340 | - | |
| 341 | - $args = wp_parse_args( $args, $default_args ); | |
| 342 | - $order_data = array(); | |
| 343 | - | |
| 344 | - if ( $args['order_id'] > 0 && get_post( $args['order_id'] ) ) { | |
| 345 | - $updating = true; | |
| 346 | - $order_data['ID'] = $args['order_id']; | |
| 347 | - } else { | |
| 348 | - $updating = false; | |
| 349 | - $order_data['post_type'] = LP_ORDER_CPT; | |
| 350 | - $order_data['post_status'] = ! empty( $args['status'] ) ? 'publish' : 'lpr-draft'; | |
| 351 | - $order_data['ping_status'] = 'closed'; | |
| 352 | - $order_data['post_author'] = ( $order_owner_id = learn_press_cart_order_instructor() ) ? $order_owner_id : 1; // always is administrator | |
| 353 | - $order_data['post_parent'] = absint( $args['parent'] ); | |
| 354 | - } | |
| 355 | - $order_title = array(); | |
| 356 | - if ( $args['method'] ) { | |
| 357 | - $order_title[] = $args['method']; | |
| 358 | - } | |
| 359 | - if ( $args['method_id'] ) { | |
| 360 | - $order_title[] = $args['method_id']; | |
| 361 | - } | |
| 362 | - $order_title[] = date_i18n( 'Y-m-d-H:i:s' ); | |
| 363 | - $order_data['post_title'] = join( '-', $order_title ); | |
| 364 | - | |
| 365 | - if ( empty( $args['user_id'] ) ) { | |
| 366 | - $user = learn_press_get_current_user(); | |
| 367 | - $args['user_id'] = $user->get_id(); | |
| 368 | - } | |
| 369 | - | |
| 370 | - if ( ! $args['transaction_object'] ) { | |
| 371 | - $args['transaction_object'] = learn_press_generate_transaction_object(); | |
| 372 | - } | |
| 373 | - | |
| 374 | - if ( ! $updating ) { | |
| 375 | - if ( wp_insert_post( $order_data ) ) { | |
| 376 | - $transaction_id = wp_insert_post( $order_data ); | |
| 377 | - | |
| 378 | - update_post_meta( $transaction_id, '_learn_press_transaction_method', $args['method'] ); | |
| 379 | - | |
| 380 | - // update_post_meta( $transaction_id, '_learn_press_transaction_status', $status ); | |
| 381 | - update_post_meta( $transaction_id, '_learn_press_customer_id', $args['user_id'] ); | |
| 382 | - update_post_meta( $transaction_id, '_learn_press_customer_ip', learn_press_get_ip() ); | |
| 383 | - update_post_meta( $transaction_id, '_learn_press_order_items', $args['transaction_object'] ); | |
| 384 | - | |
| 385 | - add_user_meta( $args['user_id'], '_lpr_order_id', $transaction_id ); | |
| 386 | - | |
| 387 | - } | |
| 388 | - } else { | |
| 389 | - $transaction_id = wp_update_post( $order_data ); | |
| 390 | - } | |
| 391 | - | |
| 392 | - if ( $transaction_id ) { | |
| 393 | - if ( ! empty( $args['status'] ) ) { | |
| 394 | - learn_press_update_order_status( $transaction_id, $args['status'] ); | |
| 395 | - } | |
| 396 | - update_post_meta( $transaction_id, '_learn_press_transaction_method_id', $args['method_id'] ); | |
| 397 | - | |
| 398 | - if ( $args['transaction_object'] ) { | |
| 399 | - update_post_meta( $transaction_id, '_learn_press_order_items', $args['transaction_object'] ); | |
| 400 | - } | |
| 401 | - | |
| 402 | - if ( ! empty( $args['status'] ) ) { | |
| 403 | - if ( $updating ) { | |
| 404 | - return apply_filters( 'learn_press_update_transaction_success', $transaction_id, $args ); | |
| 405 | - } else { | |
| 406 | - return apply_filters( 'learn_press_add_transaction_success', $transaction_id, $args ); | |
| 407 | - } | |
| 408 | - } | |
| 409 | - | |
| 410 | - return $transaction_id; | |
| 411 | - } | |
| 412 | - | |
| 413 | - return false;*/ | |
| 414 | - | |
| 415 | - // do_action( 'learn_press_add_transaction_fail', $args );// $method, $method_id, $status, $customer_id, $transaction_object, $args ); | |
| 416 | -} | |
| 417 | - | |
| 418 | -function learn_press_payment_method_from_slug( $order_id ) { | |
| 419 | - $slug = get_post_meta( $order_id, '_learn_press_transaction_method', true ); | |
| 420 | - | |
| 421 | - return apply_filters( 'learn_press_payment_method_from_slug_' . $slug, $slug ); | |
| 422 | -} | |
| 423 | - | |
| 424 | -/** | |
| 425 | - * @deprecated 4.2.0 | |
| 426 | - */ | |
| 427 | -function learn_press_generate_transaction_object() { | |
| 428 | - _deprecated_function( __FUNCTION__, '4.2.0' ); | |
| 429 | - /*$cart = learn_press_get_cart(); | |
| 430 | - $products = $cart->get_items(); | |
| 431 | - | |
| 432 | - if ( $products ) { | |
| 433 | - foreach ( $products as $key => $product ) { | |
| 434 | - $products[ $key ]['product_base_price'] = floatval( learn_press_get_course_price( $product['id'] ) ); | |
| 435 | - $products[ $key ]['product_subtotal'] = floatval( learn_press_get_course_price( $product['id'] ) * $product['quantity'] ); | |
| 436 | - $products[ $key ]['product_name'] = get_the_title( $product['id'] ); | |
| 437 | - $products = apply_filters( 'learn_press_generate_transaction_object_products', $products, $key, $product ); | |
| 438 | - } | |
| 439 | - } | |
| 440 | - | |
| 441 | - $transaction_object = new stdClass(); | |
| 442 | - $transaction_object->cart_id = $cart->get_cart_id(); | |
| 443 | - $transaction_object->total = round( $cart->get_total(), 2 ); | |
| 444 | - $transaction_object->sub_total = $cart->get_sub_total(); | |
| 445 | - $transaction_object->currency = learn_press_get_currency(); | |
| 446 | - $transaction_object->description = learn_press_get_cart_description(); | |
| 447 | - $transaction_object->products = $products; | |
| 448 | - $transaction_object->coupons = ''; | |
| 449 | - $transaction_object->coupons_total_discount = ''; | |
| 450 | - | |
| 451 | - $transaction_object = apply_filters( 'learn_press_generate_transaction_object', $transaction_object ); | |
| 452 | - | |
| 453 | - return $transaction_object;*/ | |
| 454 | -} | |
| 455 | - | |
| 456 | -/** | |
| 457 | - * Get the author ID of course in the cart | |
| 458 | - * | |
| 459 | - * Currently, it only get the first item in cart | |
| 460 | - * | |
| 461 | - * @return int | |
| 462 | - */ | |
| 463 | -function learn_press_cart_order_instructor() { | |
| 464 | - $cart = learn_press_get_cart(); | |
| 465 | - $products = $cart->get_items(); | |
| 466 | - | |
| 467 | - if ( $products ) { | |
| 468 | - foreach ( $products as $key => $product ) { | |
| 469 | - $post = get_post( $product['id'] ); | |
| 470 | - | |
| 471 | - if ( $post && ! empty( $post->ID ) ) { | |
| 472 | - return $post->post_author; | |
| 473 | - } | |
| 474 | - } | |
| 475 | - } | |
| 476 | - | |
| 477 | - return 0; | |
| 478 | -} | |
| 479 | - | |
| 480 | -function learn_press_get_orders( $args = array() ) { | |
| 481 | - // _deprecated_function( __FUNCTION__, '3.0.0', 'get_posts' ); | |
| 482 | - $args['post_type'] = LP_ORDER_CPT; | |
| 483 | - $orders = get_posts( $args ); | |
| 484 | - | |
| 485 | - return apply_filters( 'learn_press_get_orders', $orders, $args ); | |
| 486 | -} | |
| 487 | - | |
| 488 | -/** | |
| 489 | 123 | * Count orders by it's status |
| 490 | 124 | * |
| 491 | 125 | * @param array $args |
| 492 | - * | |
| 126 | + * @Todo tungnx review to rewrite query | |
| 493 | 127 | * @return array |
| 494 | 128 | */ |
| 495 | 129 | function learn_press_count_orders( $args = array() ) { |
| 496 | 130 | if ( is_string( $args ) ) { |
| @@ -545,9 +179,9 @@ | ||
| 545 | 179 | |
| 546 | 180 | /** |
| 547 | 181 | * Format price with currency and other settings. |
| 548 | 182 | * |
| 549 | - * @param float $price | |
| 183 | + * @param float $price | |
| 550 | 184 | * @param string $currency |
| 551 | 185 | * |
| 552 | 186 | * @return string |
| 553 | 187 | */ |
| @@ -557,12 +191,16 @@ | ||
| 557 | 191 | } |
| 558 | 192 | |
| 559 | 193 | $before = $after = ''; |
| 560 | 194 | |
| 561 | - $currency = is_string( $currency ) && '' !== $currency ? $currency : learn_press_get_currency_symbol(); | |
| 562 | - $thousands_separator = LP_Settings::get_option( 'thousands_separator', ',' ); | |
| 563 | - $number_of_decimals = LP_Settings::get_option( 'number_of_decimals', 2 ); | |
| 564 | - $decimals_separator = LP_Settings::get_option( 'decimals_separator', '.' ); | |
| 195 | + $currency = esc_html( | |
| 196 | + is_string( $currency ) && '' !== $currency | |
| 197 | + ? $currency | |
| 198 | + : learn_press_get_currency_symbol() | |
| 199 | + ); | |
| 200 | + $thousands_separator = esc_html( LP_Settings::get_option( 'thousands_separator', ',' ) ); | |
| 201 | + $number_of_decimals = esc_html( LP_Settings::get_option( 'number_of_decimals', 2 ) ); | |
| 202 | + $decimals_separator = esc_html( LP_Settings::get_option( 'decimals_separator', '.' ) ); | |
| 565 | 203 | |
| 566 | 204 | switch ( LP_Settings::get_option( 'currency_pos' ) ) { |
| 567 | 205 | default: |
| 568 | 206 | $before = $currency; |
| @@ -635,66 +273,8 @@ | ||
| 635 | 273 | return apply_filters( 'learn-press/order-number-formatted', $formatted_number, $order_number ); |
| 636 | 274 | } |
| 637 | 275 | |
| 638 | 276 | /** |
| 639 | - * Get label of an order's statues. | |
| 640 | - * | |
| 641 | - * @param int|string $order_id - Optional. ID of an order or status. | |
| 642 | - * | |
| 643 | - * @return string|bool | |
| 644 | - * @deprecated 4.2.0 | |
| 645 | - */ | |
| 646 | -function learn_press_get_order_status_label( $order_id = 0 ) { | |
| 647 | - _deprecated_function( __FUNCTION__, '4.2.0' ); | |
| 648 | - $statuses = learn_press_get_order_statuses(); | |
| 649 | - if ( is_numeric( $order_id ) ) { | |
| 650 | - $status = get_post_status( $order_id ); | |
| 651 | - } else { | |
| 652 | - $status = $order_id; | |
| 653 | - } | |
| 654 | - | |
| 655 | - return ! empty( $statuses[ $status ] ) ? $statuses[ $status ] : false; | |
| 656 | -} | |
| 657 | - | |
| 658 | -/** | |
| 659 | - * Get list of registered order's statuses and/or labels. | |
| 660 | - * | |
| 661 | - * @param bool $prefix | |
| 662 | - * @param bool $status_only | |
| 663 | - * | |
| 664 | - * @since 2.1.7 | |
| 665 | - * | |
| 666 | - * @return array | |
| 667 | - * @deprecated 4.2.0 | |
| 668 | - */ | |
| 669 | -function learn_press_get_order_statuses( $prefix = true, $status_only = false ) { | |
| 670 | - _deprecated_function( __FUNCTION__, '4.2.0' ); | |
| 671 | - $register_statues = learn_press_get_register_order_statuses(); | |
| 672 | - | |
| 673 | - if ( ! $prefix ) { | |
| 674 | - $order_statuses = array(); | |
| 675 | - foreach ( $register_statues as $k => $v ) { | |
| 676 | - $k = preg_replace( '~^lp-~', '', $k ); | |
| 677 | - $order_statuses[ $k ] = $v; | |
| 678 | - } | |
| 679 | - } else { | |
| 680 | - $order_statuses = $register_statues; | |
| 681 | - } | |
| 682 | - | |
| 683 | - $order_statuses = wp_list_pluck( $order_statuses, 'label' ); | |
| 684 | - | |
| 685 | - if ( $status_only ) { | |
| 686 | - $order_statuses = array_keys( $order_statuses ); | |
| 687 | - } | |
| 688 | - | |
| 689 | - // @deprecated | |
| 690 | - $order_statuses = apply_filters( 'learn_press_order_statuses', $order_statuses ); | |
| 691 | - | |
| 692 | - // @since 3.0.0 | |
| 693 | - return apply_filters( 'learn-press/order-statues', $order_statuses ); | |
| 694 | -} | |
| 695 | - | |
| 696 | -/** | |
| 697 | 277 | * Get list of registered order's statues for registering with wp post's status. |
| 698 | 278 | * |
| 699 | 279 | * @since 2.0.0 |
| 700 | 280 | * |
| @@ -702,8 +282,16 @@ | ||
| 702 | 282 | */ |
| 703 | 283 | function learn_press_get_register_order_statuses() { |
| 704 | 284 | $order_statues = array(); |
| 705 | 285 | |
| 286 | + $order_statues['lp-completed'] = array( | |
| 287 | + 'label' => _x( 'Completed', 'Order status', 'learnpress' ), | |
| 288 | + 'public' => false, | |
| 289 | + 'exclude_from_search' => false, | |
| 290 | + 'show_in_admin_all_list' => true, | |
| 291 | + 'show_in_admin_status_list' => true, | |
| 292 | + 'label_count' => _n_noop( 'Completed <span class="count">(%s)</span>', 'Completed <span class="count">(%s)</span>', 'learnpress' ), | |
| 293 | + ); | |
| 706 | 294 | $order_statues['lp-pending'] = array( |
| 707 | 295 | 'label' => _x( 'Pending', 'Order status', 'learnpress' ), |
| 708 | 296 | 'public' => false, |
| 709 | 297 | 'exclude_from_search' => false, |
| @@ -718,16 +306,8 @@ | ||
| 718 | 306 | 'show_in_admin_all_list' => true, |
| 719 | 307 | 'show_in_admin_status_list' => true, |
| 720 | 308 | 'label_count' => _n_noop( 'Processing <span class="count">(%s)</span>', 'Processing <span class="count">(%s)</span>', 'learnpress' ), |
| 721 | 309 | ); |
| 722 | - $order_statues['lp-completed'] = array( | |
| 723 | - 'label' => _x( 'Completed', 'Order status', 'learnpress' ), | |
| 724 | - 'public' => false, | |
| 725 | - 'exclude_from_search' => false, | |
| 726 | - 'show_in_admin_all_list' => true, | |
| 727 | - 'show_in_admin_status_list' => true, | |
| 728 | - 'label_count' => _n_noop( 'Completed <span class="count">(%s)</span>', 'Completed <span class="count">(%s)</span>', 'learnpress' ), | |
| 729 | - ); | |
| 730 | 310 | $order_statues['lp-cancelled'] = array( |
| 731 | 311 | 'label' => _x( 'Cancelled', 'Order status', 'learnpress' ), |
| 732 | 312 | 'public' => false, |
| 733 | 313 | 'exclude_from_search' => false, |
| @@ -742,24 +322,41 @@ | ||
| 742 | 322 | 'show_in_admin_all_list' => true, |
| 743 | 323 | 'show_in_admin_status_list' => true, |
| 744 | 324 | 'label_count' => _n_noop( 'Failed <span class="count">(%s)</span>', 'Failed <span class="count">(%s)</span>', 'learnpress' ), |
| 745 | 325 | ); |
| 326 | + $order_statues['lp-refunded'] = array( | |
| 327 | + 'label' => _x( 'Refunded', 'Order status', 'learnpress' ), | |
| 328 | + 'public' => false, | |
| 329 | + 'exclude_from_search' => false, | |
| 330 | + 'show_in_admin_all_list' => true, | |
| 331 | + 'show_in_admin_status_list' => true, | |
| 332 | + 'label_count' => _n_noop( 'Refunded <span class="count">(%s)</span>', 'Refunded <span class="count">(%s)</span>', 'learnpress' ), | |
| 333 | + ); | |
| 334 | + $order_statues['trash'] = array( | |
| 335 | + 'label' => _x( 'Trash', 'Order status', 'learnpress' ), | |
| 336 | + 'public' => false, | |
| 337 | + 'exclude_from_search' => false, | |
| 338 | + 'show_in_admin_all_list' => true, | |
| 339 | + 'show_in_admin_status_list' => true, | |
| 340 | + 'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>', 'learnpress' ), | |
| 341 | + ); | |
| 746 | 342 | |
| 747 | 343 | return $order_statues; |
| 748 | 344 | } |
| 749 | 345 | |
| 750 | 346 | function _learn_press_get_order_status_description( $status ) { |
| 751 | - static $descriptions = null; | |
| 752 | - $descriptions = array( | |
| 347 | + | |
| 348 | + $status = str_replace( 'lp-', '', (string) $status ); | |
| 349 | + $descriptions = array( | |
| 753 | 350 | 'pending' => __( 'Order received in case a user purchases a course but doesn\'t finalize the order.', 'learnpress' ), |
| 754 | 351 | 'processing' => __( 'Payment received and the order is awaiting fulfillment.', 'learnpress' ), |
| 755 | 352 | 'completed' => __( 'The order is fulfilled and completed.', 'learnpress' ), |
| 756 | 353 | 'cancelled' => __( 'The order is cancelled by an admin or the customer.', 'learnpress' ), |
| 354 | + 'refunded' => __( 'Order was refunded to the customer.', 'learnpress' ), | |
| 757 | 355 | ); |
| 758 | 356 | |
| 759 | 357 | return apply_filters( 'learn_press_order_status_description', ! empty( $descriptions[ $status ] ) ? $descriptions[ $status ] : '' ); |
| 760 | 358 | } |
| 761 | - | |
| 762 | 359 | /** |
| 763 | 360 | * Get status of an order by the ID. |
| 764 | 361 | * |
| 765 | 362 | * @param int $order_id |
| @@ -766,8 +363,9 @@ | ||
| 766 | 363 | * |
| 767 | 364 | * @return bool|string |
| 768 | 365 | */ |
| 769 | 366 | function learn_press_get_order_status( $order_id ) { |
| 367 | + | |
| 770 | 368 | $order = learn_press_get_order( $order_id ); |
| 771 | 369 | |
| 772 | 370 | if ( $order ) { |
| 773 | 371 | return $order->get_status(); |
| @@ -775,8 +373,74 @@ | ||
| 775 | 373 | |
| 776 | 374 | return false; |
| 777 | 375 | } |
| 778 | 376 | |
| 377 | +if ( ! function_exists( 'learn_press_get_refund_setting' ) ) { | |
| 378 | + /** | |
| 379 | + * Get refund setting value with safe defaults. | |
| 380 | + * | |
| 381 | + * @param string $key | |
| 382 | + * @param mixed $default | |
| 383 | + * | |
| 384 | + * @since 4.3.4 | |
| 385 | + * @version 1.0.0 | |
| 386 | + * @return mixed | |
| 387 | + */ | |
| 388 | + function learn_press_get_refund_setting( string $key, $default = null ) { | |
| 389 | + $defaults = array( | |
| 390 | + 'enable_refund_requests' => 'no', | |
| 391 | + 'auto_refund' => 'no', | |
| 392 | + 'refund_time_limit' => 30, | |
| 393 | + 'require_refund_reason' => 'no', | |
| 394 | + 'allow_resend_after_rejected' => 'no', | |
| 395 | + 'refund_max_completion' => 0, | |
| 396 | + ); | |
| 397 | + | |
| 398 | + if ( null === $default && array_key_exists( $key, $defaults ) ) { | |
| 399 | + $default = $defaults[ $key ]; | |
| 400 | + } | |
| 401 | + | |
| 402 | + return LP_Settings::get_option( $key, $default ); | |
| 403 | + } | |
| 404 | +} | |
| 405 | + | |
| 406 | +if ( ! function_exists( 'learn_press_get_order_refund_supported_gateways' ) ) { | |
| 407 | + /** | |
| 408 | + * Get list gateways that support refund. | |
| 409 | + * | |
| 410 | + * @since 4.3.4 | |
| 411 | + * @version 1.1.0 | |
| 412 | + * @return array | |
| 413 | + */ | |
| 414 | + function learn_press_get_order_refund_supported_gateways(): array { | |
| 415 | + if ( ! class_exists( 'LP_Gateways' ) ) { | |
| 416 | + return array(); | |
| 417 | + } | |
| 418 | + | |
| 419 | + $gateways_instance = LP_Gateways::instance(); | |
| 420 | + if ( ! $gateways_instance || ! method_exists( $gateways_instance, 'get_gateways' ) ) { | |
| 421 | + return array(); | |
| 422 | + } | |
| 423 | + | |
| 424 | + $supported_gateways = array(); | |
| 425 | + $all_gateways = (array) $gateways_instance->get_gateways(); | |
| 426 | + foreach ( $all_gateways as $gateway_id => $gateway ) { | |
| 427 | + $gateway_id = sanitize_key( (string) $gateway_id ); | |
| 428 | + if ( empty( $gateway_id ) ) { | |
| 429 | + continue; | |
| 430 | + } | |
| 431 | + | |
| 432 | + if ( ! is_object( $gateway ) || ! is_callable( array( $gateway, 'refund' ) ) ) { | |
| 433 | + continue; | |
| 434 | + } | |
| 435 | + | |
| 436 | + $supported_gateways[] = $gateway_id; | |
| 437 | + } | |
| 438 | + | |
| 439 | + return array_values( array_unique( $supported_gateways ) ); | |
| 440 | + } | |
| 441 | +} | |
| 442 | + | |
| 779 | 443 | if ( ! function_exists( 'learn_press_cancel_order_process' ) ) { |
| 780 | 444 | /** |
| 781 | 445 | * Process action allows user to cancel an order is pending |
| 782 | 446 | * in their profile. |
| @@ -781,46 +445,209 @@ | ||
| 781 | 445 | * Process action allows user to cancel an order is pending |
| 782 | 446 | * in their profile. |
| 783 | 447 | */ |
| 784 | 448 | function learn_press_cancel_order_process() { |
| 449 | + | |
| 785 | 450 | if ( empty( $_REQUEST['cancel-order'] ) || empty( $_REQUEST['lp-nonce'] ) || |
| 786 | 451 | ! wp_verify_nonce( $_REQUEST['lp-nonce'], 'cancel-order' ) || is_admin() ) { |
| 787 | 452 | return; |
| 788 | 453 | } |
| 789 | 454 | |
| 790 | - $url = ''; | |
| 791 | - $order_id = absint( $_REQUEST['cancel-order'] ); | |
| 792 | - $order = learn_press_get_order( $order_id ); | |
| 793 | - $user = learn_press_get_current_user(); | |
| 455 | + $user_id = get_current_user_id(); | |
| 456 | + $profile = LP_Profile::instance( $user_id ); | |
| 457 | + $url = $profile->get_tab_link( | |
| 458 | + LP_Settings::instance()->get( 'profile_endpoints.orders', 'orders' ) | |
| 459 | + ); | |
| 794 | 460 | |
| 795 | - if ( ! $order ) { | |
| 796 | - learn_press_add_message( sprintf( __( 'Order number <strong>%s</strong> not found', 'learnpress' ), $order_id ), 'error' ); | |
| 797 | - } elseif ( $order->has_status( 'pending' ) ) { | |
| 798 | - $order->update_status( LP_ORDER_CANCELLED ); | |
| 799 | - $order->add_note( __( 'The order is cancelled by the customer', 'learnpress' ) ); | |
| 461 | + try { | |
| 462 | + $message = array( | |
| 463 | + 'status' => 'error', | |
| 464 | + 'content' => '', | |
| 465 | + ); | |
| 800 | 466 | |
| 801 | - // set updated message | |
| 802 | - learn_press_add_message( sprintf( __( 'Order number <strong>%s</strong> has been cancelled', 'learnpress' ), $order->get_order_number() ) ); | |
| 803 | - $url = $order->get_cancel_order_url( true ); | |
| 804 | - } else { | |
| 805 | - learn_press_add_message( sprintf( __( 'The order number <strong>%s</strong> can not be cancelled.', 'learnpress' ), $order->get_order_number() ), 'error' ); | |
| 467 | + $order_id = absint( $_REQUEST['cancel-order'] ); | |
| 468 | + $order = learn_press_get_order( $order_id ); | |
| 469 | + | |
| 470 | + if ( ! $order ) { | |
| 471 | + throw new Exception( sprintf( __( 'Order number <strong>%s</strong> not found', 'learnpress' ), $order_id ) ); | |
| 472 | + } | |
| 473 | + | |
| 474 | + $user_ids = (array) $order->get_user_id(); | |
| 475 | + if ( ! in_array( $user_id, $user_ids ) ) { | |
| 476 | + throw new Exception( __( 'You do not have permission to cancel this order.', 'learnpress' ) ); | |
| 477 | + } | |
| 478 | + | |
| 479 | + if ( $order->has_status( LP_ORDER_PENDING ) ) { | |
| 480 | + $order->update_status( LP_ORDER_CANCELLED ); | |
| 481 | + $order->add_note( __( 'The order is cancelled by the customer', 'learnpress' ) ); | |
| 482 | + | |
| 483 | + $message['status'] = 'success'; | |
| 484 | + $message['content'] = sprintf( __( 'Order number <strong>%s</strong> has been cancelled', 'learnpress' ), $order->get_order_number() ); | |
| 485 | + } else { | |
| 486 | + throw new Exception( | |
| 487 | + sprintf( | |
| 488 | + __( 'The order number <strong>%s</strong> can not be cancelled.', 'learnpress' ), | |
| 489 | + $order->get_order_number() | |
| 490 | + ) | |
| 491 | + ); | |
| 492 | + } | |
| 493 | + } catch ( Throwable $e ) { | |
| 494 | + $message['content'] = $e->getMessage(); | |
| 806 | 495 | } |
| 807 | 496 | |
| 808 | - if ( ! $url ) { | |
| 809 | - $url = learn_press_user_profile_link( $user->get_id(), LP_Settings::instance()->get( 'profile_endpoints.profile-orders', 'orders' ) ); | |
| 810 | - } | |
| 497 | + learn_press_set_message( $message ); | |
| 811 | 498 | wp_safe_redirect( $url ); |
| 812 | 499 | exit(); |
| 813 | 500 | } |
| 814 | 501 | } |
| 815 | 502 | add_action( 'init', 'learn_press_cancel_order_process' ); |
| 503 | +if ( ! function_exists( 'learn_press_get_order_refund_event_data' ) ) { | |
| 504 | + /** | |
| 505 | + * Build normalized refund event payload. | |
| 506 | + * | |
| 507 | + * @since 4.3.5 | |
| 508 | + * @version 1.0.1 | |
| 509 | + * | |
| 510 | + * @param LP_Order $order | |
| 511 | + * @param array $overrides | |
| 512 | + * | |
| 513 | + * @return array | |
| 514 | + */ | |
| 515 | + function learn_press_get_order_refund_event_data( LP_Order $order, array $overrides = array() ): array { | |
| 516 | + $order_id = $order->get_id(); | |
| 517 | + $requested_at = get_post_meta( $order_id, '_lp_refund_requested_at', true ); | |
| 518 | + if ( ! empty( $requested_at ) ) { | |
| 519 | + $requested_at = new LP_Datetime( $requested_at ); | |
| 520 | + $requested_at = sprintf( | |
| 521 | + esc_html__( '%1$s %2$s', 'learnpress' ), | |
| 522 | + $requested_at->format( LP_Datetime::I18N_FORMAT_HAS_TIME ), | |
| 523 | + LP_Datetime::get_timezone_string() | |
| 524 | + ); | |
| 525 | + } | |
| 816 | 526 | |
| 527 | + $user_id = (int) $order->get_user_id(); | |
| 528 | + $userOrderModel = UserModel::find( $user_id, true ); | |
| 529 | + if ( $userOrderModel instanceof UserModel ) { | |
| 530 | + $requested_by = $userOrderModel->get_display_name(); | |
| 531 | + } else { | |
| 532 | + $requested_by = $order->get_checkout_email(); | |
| 533 | + } | |
| 817 | 534 | |
| 535 | + $data = array( | |
| 536 | + 'order_id' => $order_id, | |
| 537 | + 'order_number' => $order->get_order_number(), | |
| 538 | + 'order_key' => $order->get_order_key(), | |
| 539 | + 'order_status' => $order->get_status(), | |
| 540 | + 'request_status' => $order->get_refund_request(), | |
| 541 | + 'requested_by' => $requested_by, | |
| 542 | + 'requested_at' => $requested_at, | |
| 543 | + 'reviewed_by' => absint( get_post_meta( $order_id, '_lp_refund_reviewed_by', true ) ), | |
| 544 | + 'reviewed_at' => (string) get_post_meta( $order_id, '_lp_refund_reviewed_at', true ), | |
| 545 | + 'reason' => (string) get_post_meta( $order_id, LP_Order::META_KEY_REFUND_REQUEST_REASON, true ), | |
| 546 | + 'requester_email' => '', | |
| 547 | + 'admin_order_edit_url' => add_query_arg( | |
| 548 | + array( | |
| 549 | + 'post' => $order_id, | |
| 550 | + 'action' => 'edit', | |
| 551 | + ), | |
| 552 | + admin_url( 'post.php' ) | |
| 553 | + ), | |
| 554 | + ); | |
| 555 | + | |
| 556 | + $requested_user_id = absint( $data['requested_by'] ); | |
| 557 | + if ( ! empty( $requested_user_id ) ) { | |
| 558 | + $requested_user = get_user_by( 'id', $requested_user_id ); | |
| 559 | + if ( $requested_user instanceof WP_User ) { | |
| 560 | + $data['requester_email'] = $requested_user->user_email; | |
| 561 | + } | |
| 562 | + } | |
| 563 | + | |
| 564 | + if ( ! empty( $overrides ) ) { | |
| 565 | + $data = array_merge( $data, $overrides ); | |
| 566 | + } | |
| 567 | + | |
| 568 | + return $data; | |
| 569 | + } | |
| 570 | +} | |
| 818 | 571 | /** |
| 819 | - * get total price order complete | |
| 572 | + * Render pending refund request panel on order detail. | |
| 820 | 573 | * |
| 574 | + * @since 4.3.4 | |
| 575 | + * @version 1.0.0 | |
| 576 | + * @param LP_Order $order | |
| 821 | 577 | */ |
| 578 | +function learn_press_admin_order_refund_request_panel( $order ) { | |
| 822 | 579 | |
| 580 | + if ( ! $order instanceof LP_Order ) { | |
| 581 | + return; | |
| 582 | + } | |
| 583 | + | |
| 584 | + $order_id = $order->get_id(); | |
| 585 | + if ( ! current_user_can( 'edit_post', $order_id ) ) { | |
| 586 | + return; | |
| 587 | + } | |
| 588 | + | |
| 589 | + $refund_event_data = learn_press_get_order_refund_event_data( $order ); | |
| 590 | + $refund_request_status = sanitize_key( (string) ( $refund_event_data['request_status'] ?? '' ) ); | |
| 591 | + $requested_by = $refund_event_data['requested_by'] ?? ''; | |
| 592 | + $requested_at = $refund_event_data['requested_at'] ?? ''; | |
| 593 | + $refund_reason = $refund_event_data['reason'] ?? ''; | |
| 594 | + $requester_email = ''; | |
| 595 | + | |
| 596 | + $status_labels = array( | |
| 597 | + 'pending' => __( 'Pending review', 'learnpress' ), | |
| 598 | + 'approved' => __( 'Approved', 'learnpress' ), | |
| 599 | + 'auto-approved' => __( 'Auto approved', 'learnpress' ), | |
| 600 | + 'rejected' => __( 'Rejected', 'learnpress' ), | |
| 601 | + ); | |
| 602 | + $status_label = $status_labels[ $refund_request_status ] ?? ''; | |
| 603 | + if ( empty( $status_label ) && ! empty( $refund_request_status ) ) { | |
| 604 | + $status_label = ucwords( str_replace( '-', ' ', $refund_request_status ) ); | |
| 605 | + } | |
| 606 | + | |
| 607 | + $render_statuses = array( 'pending', 'approved', 'auto-approved' ); | |
| 608 | + if ( ! in_array( $refund_request_status, $render_statuses, true ) ) { | |
| 609 | + return; | |
| 610 | + } | |
| 611 | + | |
| 612 | + $is_pending = ( 'pending' === $refund_request_status ); | |
| 613 | + $currency_symbol = learn_press_get_currency_symbol( $order->get_currency() ); | |
| 614 | + | |
| 615 | + $order_total = round( max( 0, floatval( $order->get_total() ) ), 2 ); | |
| 616 | + $order_total_formatted = learn_press_format_price( $order_total, $currency_symbol ); | |
| 617 | + | |
| 618 | + // Refund amount is only meaningful once the refund has been executed | |
| 619 | + // (approved/auto-approved), when _lp_refund_amount has been written. | |
| 620 | + $refund_amount_formatted = ''; | |
| 621 | + if ( ! $is_pending ) { | |
| 622 | + $refund_amount = get_post_meta( $order_id, LP_Order::META_KEY_REFUNDED_AMOUNT, true ); | |
| 623 | + if ( '' === $refund_amount || null === $refund_amount ) { | |
| 624 | + $refund_amount = $order_total; // Fallback for full refunds without a stored amount. | |
| 625 | + } | |
| 626 | + $refund_amount = round( max( 0, floatval( $refund_amount ) ), 2 ); | |
| 627 | + $refund_amount_formatted = learn_press_format_price( $refund_amount, $currency_symbol ); | |
| 628 | + } | |
| 629 | + | |
| 630 | + learn_press_admin_view( | |
| 631 | + 'meta-boxes/order/refund-request-panel', | |
| 632 | + compact( | |
| 633 | + 'order_id', | |
| 634 | + 'is_pending', | |
| 635 | + 'order_total', | |
| 636 | + 'order_total_formatted', | |
| 637 | + 'refund_amount_formatted', | |
| 638 | + 'requested_by', | |
| 639 | + 'requested_at', | |
| 640 | + 'requester_email', | |
| 641 | + 'refund_reason', | |
| 642 | + 'status_label' | |
| 643 | + ) | |
| 644 | + ); | |
| 645 | +} | |
| 646 | + | |
| 647 | +/** | |
| 648 | + * get total price order complete | |
| 649 | + */ | |
| 823 | 650 | function learn_press_get_total_price_order_complete() { |
| 824 | 651 | global $wpdb; |
| 825 | 652 | |
| 826 | 653 | $query = $wpdb->prepare( |
| @@ -826,54 +653,15 @@ | ||
| 826 | 653 | $query = $wpdb->prepare( |
| 827 | 654 | "SELECT SUM(meta_value) as order_total From `{$wpdb->prefix}postmeta` as mt |
| 828 | 655 | INNER JOIN `{$wpdb->prefix}posts` as p ON p.id = mt.post_id |
| 829 | 656 | WHERE p.post_type = %s AND mt.meta_key = %s |
| 657 | + AND p.post_status = %s | |
| 830 | 658 | ", |
| 831 | 659 | LP_ORDER_CPT, |
| 832 | - '_order_total' | |
| 660 | + '_order_total', | |
| 661 | + 'lp-completed' | |
| 833 | 662 | ); |
| 834 | 663 | |
| 835 | 664 | $total = $wpdb->get_results( $query )[0]->order_total; |
| 836 | 665 | |
| 837 | 666 | return learn_press_format_price( $total, true ); |
| 838 | - | |
| 839 | 667 | } |
| 840 | -/** | |
| 841 | - * Auto enroll course after user checkout | |
| 842 | - * | |
| 843 | - * @param $result | |
| 844 | - * @param $order_id | |
| 845 | - * @editor tungnx - comment - for not use | |
| 846 | - */ | |
| 847 | -/*function _learn_press_checkout_auto_enroll_free_course( $result, $order_id ) { | |
| 848 | - return; | |
| 849 | - $enrolled = false; | |
| 850 | - $order = learn_press_get_order( $order_id, true ); | |
| 851 | - | |
| 852 | - if ( $order_id && $order ) { | |
| 853 | - $user = learn_press_get_user( $order->user_id, true ); | |
| 854 | - $order_items = $order->get_items(); | |
| 855 | - | |
| 856 | - if ( $order_items ) { | |
| 857 | - foreach ( $order_items as $item ) { | |
| 858 | - if ( $user->has_enrolled_course( $item['course_id'] ) ) { | |
| 859 | - continue; | |
| 860 | - } | |
| 861 | - if ( $user->enroll( $item['course_id'] ) ) { | |
| 862 | - $enrolled = $item['course_id']; | |
| 863 | - } | |
| 864 | - } | |
| 865 | - if ( ! $enrolled ) { | |
| 866 | - $item = reset( $order_items ); | |
| 867 | - $enrolled = $item['course_id']; | |
| 868 | - } | |
| 869 | - } | |
| 870 | - } | |
| 871 | - if ( $enrolled ) { | |
| 872 | - learn_press_add_message( sprintf( __( 'You have enrolled in this course. <a href="%s">Order details</a>', 'learnpress' ), $result['redirect'] ) ); | |
| 873 | - $result['redirect'] = get_the_permalink( $enrolled ); | |
| 874 | - LearnPress::instance()->cart->empty_cart(); | |
| 875 | - } | |
| 876 | - | |
| 877 | - return $result; | |
| 878 | -}*/ | |
| 879 | - | |