PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.5
Tutor LMS – eLearning and online course solution v4.0.5
4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / ecommerce / HooksHandler.php
tutor / ecommerce Last commit date
Cart 1 year ago PaymentGateways 2 months ago AdminMenu.php 1 month ago BillingController.php 1 month ago CartController.php 1 month ago CheckoutController.php 1 month ago CouponController.php 1 month ago Ecommerce.php 1 month ago EmailController.php 1 year ago HooksHandler.php 3 days ago OptionKeys.php 1 year ago OrderActivitiesController.php 1 year ago OrderController.php 1 month ago PaymentHandler.php 10 months ago Settings.php 3 weeks ago Tax.php 10 months ago currency.php 6 months ago
HooksHandler.php
601 lines
1 <?php
2 /**
3 * Handle ecommerce hooks
4 *
5 * @package Tutor\Ecommerce
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 3.0.0
9 */
10
11 namespace Tutor\Ecommerce;
12
13 use TUTOR\Input;
14 use TUTOR\Course;
15 use TUTOR\Earnings;
16 use Tutor\Models\CartModel;
17 use Tutor\Models\OrderModel;
18 use Tutor\Helpers\QueryHelper;
19 use Tutor\Models\EnrollmentModel;
20 use Tutor\Models\OrderMetaModel;
21 use Tutor\Models\OrderActivitiesModel;
22 use TUTOR\User;
23 use TutorPro\CourseBundle\Models\BundleModel;
24 use TutorPro\CourseBundle\CustomPosts\CourseBundle;
25
26 /**
27 * Handle custom hooks
28 */
29 class HooksHandler {
30
31 /**
32 * OrderModel
33 *
34 * @since 3.0.0
35 *
36 * @var OrderModel
37 */
38 private $order_model;
39
40 /**
41 * OrderActivitiesModel
42 *
43 * @since 3.0.0
44 *
45 * @var OrderActivitiesModel
46 */
47 private $order_activities_model;
48
49 /**
50 * Coupon controller instance
51 *
52 * @since 3.5.0
53 *
54 * @var CouponController
55 */
56 private $coupon_ctrl;
57
58 /**
59 * Register hooks & resolve props
60 *
61 * @since 3.0.0
62 */
63 public function __construct() {
64 $this->order_activities_model = new OrderActivitiesModel();
65 $this->order_model = new OrderModel();
66 $this->coupon_ctrl = new CouponController( false );
67
68 // Register hooks.
69 add_filter( 'tutor_course_sell_by', array( $this, 'alter_course_sell_by' ) );
70 add_filter( 'get_tutor_course_price', array( $this, 'alter_course_price' ), 10, 2 );
71
72 // Order hooks.
73 add_action( 'tutor_order_payment_updated', array( $this, 'handle_payment_updated_webhook' ) );
74
75 add_action( 'tutor_order_payment_status_changed', array( $this, 'handle_payment_status_changed' ), 10, 3 );
76
77 add_action( 'tutor_order_placement_success', array( $this, 'handle_order_placement_success' ) );
78
79 /**
80 * Clear order menu badge count
81 *
82 * @since 3.0.0
83 */
84 add_action( 'tutor_order_placed', array( $this, 'clear_order_badge_count' ) );
85 add_action( 'tutor_order_payment_status_changed', array( $this, 'clear_order_badge_count' ) );
86 add_action( 'tutor_before_order_bulk_action', array( $this, 'clear_order_badge_count' ) );
87 add_filter( 'tutor_before_order_create', array( $this, 'update_order_data' ) );
88 add_action( 'tutor_order_placed', array( $this, 'handle_free_checkout' ) );
89 add_filter( 'tutor_redirect_url_after_checkout', array( $this, 'redirect_to_the_course' ), 10, 3 );
90
91 /**
92 * Store customer billing information for each order.
93 *
94 * @since 3.5.0
95 */
96 add_action( 'tutor_order_placed', array( $this, 'store_billing_address_for_order' ) );
97 add_action( 'tutor_order_updated', array( $this, 'store_billing_address_for_order' ) );
98 }
99
100 /**
101 * Clear order menu badge count
102 *
103 * @since 3.0.0
104 *
105 * @return void
106 */
107 public function clear_order_badge_count() {
108 delete_transient( OrderModel::TRANSIENT_ORDER_BADGE_COUNT );
109 }
110
111 /**
112 * Store order activity before bulk action.
113 *
114 * @since 3.0.0
115 *
116 * @param string $bulk_action The bulk action being performed.
117 * @param array $bulk_ids The IDs of the orders being acted upon.
118 *
119 * @return void
120 */
121 public function after_order_bulk_action( $bulk_action, $bulk_ids ) {
122 $order_status = $this->order_model->get_order_status_by_payment_status( $bulk_action );
123
124 $cancel_reason = Input::post( 'cancel_reason', '' );
125 foreach ( $bulk_ids as $order_id ) {
126 try {
127 $this->manage_earnings_and_enrollments( $order_status, $order_id );
128 $data = (object) array(
129 'order_id' => $order_id,
130 'meta_key' => $this->order_activities_model::META_KEY_HISTORY,
131 'meta_value' => "Order mark as {$bulk_action} {$cancel_reason}",
132 );
133 $this->order_activities_model->add_order_meta( $data );
134 } catch ( \Throwable $th ) {
135 // Log message with line & file.
136 error_log( $th->getMessage() . ' in ' . $th->getFile() . ' at line ' . $th->getLine() );
137 }
138 }
139 }
140
141 /**
142 * Alter course sell by value
143 *
144 * @since 3.0.0
145 *
146 * @param mixed $sell_by Default sell by.
147 *
148 * @return mixed
149 */
150 public function alter_course_sell_by( $sell_by ) {
151 if ( tutor_utils()->is_monetize_by_tutor() ) {
152 $sell_by = Ecommerce::MONETIZE_BY;
153 }
154
155 return $sell_by;
156 }
157
158 /**
159 * Alter course price to show price on the course
160 * entry box
161 *
162 * @since 3.0.0
163 *
164 * @param mixed $price Course price.
165 * @param int $course_id Course id.
166 *
167 * @return mixed
168 */
169 public function alter_course_price( $price, $course_id ) {
170 $price_type = tutor_utils()->price_type( $course_id );
171 if ( tutor_utils()->is_monetize_by_tutor() && Course::PRICE_TYPE_PAID === $price_type ) {
172 $price = tutor_get_course_formatted_price_html( $course_id, false );
173 }
174
175 return $price;
176 }
177
178 /**
179 * Handle payment updated webhook
180 *
181 * @since 3.0.0
182 *
183 * @param object $res Response data.
184 * {order_id, transaction_id, payment_status, payment_method, redirectUrl}.
185 *
186 * @return void
187 */
188 public function handle_payment_updated_webhook( $res ) {
189 $order_id = $res->id;
190 $new_payment_status = $res->payment_status;
191 $transaction_id = $res->transaction_id;
192
193 $order_details = $this->order_model->get_order_by_id( $order_id );
194
195 /**
196 * Ignore canceled/failed webhooks for old/failed payment session to avoid unenrolling paid users.
197 *
198 * @since 3.9.7
199 */
200 $is_valid_paid_order = OrderModel::ORDER_COMPLETED === $order_details->order_status && OrderModel::PAYMENT_PAID === $order_details->payment_status;
201
202 if ( $order_details && ! $is_valid_paid_order ) {
203 $prev_payment_status = $order_details->payment_status;
204
205 $order_data = array(
206 'order_status' => $order_details->order_status,
207 'payment_status' => $new_payment_status,
208 'payment_method' => $res->payment_method,
209 'payment_payloads' => $res->payment_payload,
210 'transaction_id' => $transaction_id,
211 'earnings' => $res->earnings,
212 'fees' => $res->fees,
213 'updated_at_gmt' => current_time( 'mysql', true ),
214 );
215
216 switch ( $new_payment_status ) {
217 case $this->order_model::PAYMENT_PAID:
218 $order_data['order_status'] = $this->order_model::ORDER_COMPLETED;
219 break;
220 case $this->order_model::PAYMENT_FAILED:
221 case $this->order_model::PAYMENT_REFUNDED:
222 $order_data['order_status'] = $this->order_model::ORDER_CANCELLED;
223 break;
224 case $this->order_model::PAYMENT_PENDING:
225 $order_data['order_status'] = $this->order_model::ORDER_PENDING;
226 break;
227 }
228
229 $update = $this->order_model->update_order( $order_id, $order_data );
230 if ( $update ) {
231 // Provide hook after update order.
232 do_action( 'tutor_order_payment_status_changed', $order_id, $prev_payment_status, $new_payment_status );
233 }
234 }
235 }
236
237 /**
238 * Update enrollment & earnings based on payment status
239 *
240 * @since 3.0.0
241 *
242 * @param int $order_id Order id.
243 * @param string $prev_payment_status previous payment status.
244 * @param string $new_payment_status new payment status.
245 *
246 * @return void
247 */
248 public function handle_payment_status_changed( $order_id, $prev_payment_status, $new_payment_status ) {
249 $cancel_reason = Input::post( 'cancel_reason' );
250 $order_status = $this->order_model->get_order_status_by_payment_status( $new_payment_status );
251
252 // Store activity.
253 $data = (object) array(
254 'order_id' => $order_id,
255 'meta_key' => $this->order_activities_model::META_KEY_HISTORY,
256 'meta_value' => 'Order marked as ' . $new_payment_status,
257 );
258
259 if ( $cancel_reason ) {
260 $meta_value = array(
261 'message' => 'Order marked as ' . $new_payment_status,
262 'cancel_reason' => $cancel_reason,
263 );
264 $data->meta_value = json_encode( $meta_value );
265 }
266
267 $this->order_activities_model->add_order_meta( $data );
268
269 $this->manage_earnings_and_enrollments( $order_status, $order_id );
270
271 // Store coupon usage.
272 $this->coupon_ctrl->store_coupon_usage( $order_id );
273 }
274
275 /**
276 * Handle new order placement
277 *
278 * Clear cart items, managing enrollment & earnings
279 *
280 * @since 3.0.0
281 *
282 * @param int $order_id Order id.
283 *
284 * @return void
285 */
286 public function handle_order_placement_success( int $order_id ) {
287 $order_data = $this->order_model->get_order_by_id( $order_id );
288 if ( $order_data ) {
289 $user_id = $order_data->student->id;
290
291 ( new CartModel() )->clear_user_cart( $user_id );
292
293 // Manage enrollment & earnings.
294 $order = ( new OrderModel() )->get_order_by_id( $order_id );
295 $payment_status = $order->payment_status;
296
297 $order_status = $this->order_model->get_order_status_by_payment_status( $payment_status );
298
299 $this->manage_earnings_and_enrollments( $order_status, $order_id );
300 }
301 }
302
303 /**
304 * Check if order is bundle order
305 *
306 * @since 3.0.0
307 *
308 * @param object $order order object.
309 * @param int $object_id object id.
310 *
311 * @return boolean
312 */
313 private function is_bundle_order( $order, $object_id ) {
314 return tutor_utils()->is_addon_enabled( 'course-bundle' )
315 && in_array( $order->order_type, array( $this->order_model::TYPE_SINGLE_ORDER, $this->order_model::TYPE_SUBSCRIPTION ), true )
316 && 'course-bundle' === get_post_type( $object_id );
317 }
318
319 /**
320 * Manage earnings after order bulk action
321 *
322 * @since 3.0.0
323 *
324 * @param string $order_status Order status.
325 * @param int $order_id Order ID.
326 *
327 * @return void
328 */
329 public function manage_earnings_and_enrollments( string $order_status, int $order_id ) {
330 $earnings = Earnings::get_instance();
331 $order = $this->order_model->get_order_by_id( $order_id );
332 $student_id = $order->student->id;
333
334 $enrollment_status = ( OrderModel::ORDER_COMPLETED === $order_status ? 'completed' : ( OrderModel::ORDER_INCOMPLETE === $order->order_status ? 'pending' : 'cancel' ) );
335
336 /**
337 * Handles enrollment updates from the admin refund modal.
338 *
339 * - No action is taken if the enrollment is already in the canceled state.
340 * - If the administrator explicitly chooses to remove the enrollment,
341 * the enrollment status is updated to canceled.
342 *
343 * @since 4.0.0
344 */
345 if ( 'backend_refund' === Input::post( 'context' ) && User::is_admin() ) {
346 $has_enrollment = isset( $order->enrollment ) && is_object( $order->enrollment );
347 if ( $has_enrollment ) {
348 if ( EnrollmentModel::STATUS_CANCEL === $order->enrollment->status ) {
349 $enrollment_status = EnrollmentModel::STATUS_CANCEL;
350 }
351
352 if ( EnrollmentModel::STATUS_COMPLETED === $order->enrollment->status && Input::has( 'is_remove_enrolment', Input::POST_REQUEST ) ) {
353 $remove_enrollment = Input::post( 'is_remove_enrolment', false, Input::TYPE_BOOL );
354 $enrollment_status = $remove_enrollment ? EnrollmentModel::STATUS_CANCEL : EnrollmentModel::STATUS_COMPLETED;
355 }
356 }
357 }
358
359 foreach ( $order->items as $item ) {
360 $object_id = $item->id; // It could be course/bundle/plan id.
361 $is_gift_item = apply_filters( 'tutor_is_gift_item', false, $item->primary_id );
362 if ( $is_gift_item ) {
363 continue;
364 }
365
366 if ( $this->order_model::TYPE_SINGLE_ORDER !== $order->order_type ) {
367 /**
368 * Do not process enrollment for membership plan.
369 *
370 * @since 3.2.0
371 */
372 $plan_info = apply_filters( 'tutor_get_plan_info', null, $object_id );
373 if ( $plan_info && isset( $plan_info->is_membership_plan ) && $plan_info->is_membership_plan ) {
374 continue;
375 } else {
376 $object_id = apply_filters( 'tutor_subscription_course_by_plan', $item->id, $order );
377 }
378
379 /**
380 * Do not process enrollment for subscription order refund.
381 * It will be handled by subscription controller's handle_order_refund method.
382 *
383 * @since 3.3.0
384 */
385 if ( Input::has( 'is_cancel_subscription' ) ) {
386 continue;
387 }
388 }
389
390 $has_enrollment = EnrollmentModel::is_enrolled( $object_id, $student_id, false );
391 $active_enrollment_order_id = isset( $has_enrollment->order_id ) ? $has_enrollment->order_id : null;
392 if ( $has_enrollment && $order_id === $active_enrollment_order_id ) {
393 // Update enrollment status based on order status.
394 $update = EnrollmentModel::update_enrollments( $enrollment_status, array( $has_enrollment->ID ) );
395 if ( $update ) {
396 if ( $this->is_bundle_order( $order, $object_id ) && $this->order_model->is_single_order( $order ) ) {
397 if ( 'completed' === $enrollment_status ) {
398 BundleModel::enroll_to_bundle_courses( $object_id, $student_id );
399 } else {
400 BundleModel::disenroll_from_bundle_courses( $object_id, $student_id );
401 }
402 }
403
404 /**
405 * For subscription, renewal no need to update order id.
406 */
407 if ( $this->order_model->is_single_order( $order ) ) {
408 update_post_meta( $has_enrollment->ID, EnrollmentModel::ENROLLMENT_ORDER_ID_META, $order_id );
409 if ( tutor_utils()->is_addon_enabled( 'course-bundle' ) ) {
410 $has_bundle_enrollment_meta = get_post_meta( $has_enrollment->ID, CourseBundle::BUNDLE_ENROLLMENT_META, true );
411 if ( $has_bundle_enrollment_meta ) {
412 delete_post_meta( $has_enrollment->ID, CourseBundle::BUNDLE_ENROLLMENT_META );
413 }
414 }
415 /**
416 * Update enrollment expiry date if it is set in a course.
417 */
418 if ( tutor()->course_post_type === get_post_type( $object_id ) ) {
419 $is_set_enrollment_expiry = (int) get_tutor_course_settings( $object_id, 'enrollment_expiry' );
420 $enrollment_expiry_enabled = (bool) get_tutor_option( 'enrollment_expiry_enabled' );
421 if ( $enrollment_expiry_enabled && $is_set_enrollment_expiry ) {
422 global $wpdb;
423 QueryHelper::update(
424 $wpdb->posts,
425 array(
426 'post_date' => current_time( 'mysql' ),
427 'post_date_gmt' => current_time( 'mysql', true ),
428 ),
429 array(
430 'ID' => $has_enrollment->ID,
431 'post_type' => tutor()->enrollment_post_type,
432 )
433 );
434 }
435 }
436
437 if ( OrderModel::ORDER_COMPLETED === $order_status && OrderModel::PAYMENT_PARTIALLY_REFUNDED !== $order->payment_status ) {
438 do_action( 'tutor_after_enrolled', $object_id, $student_id, $has_enrollment->ID );
439 }
440 }
441
442 if ( 'completed' === $enrollment_status ) {
443 do_action( 'tutor_order_enrolled', $order, $has_enrollment->ID );
444 }
445 }
446 } else {
447 if ( $order->order_status === $this->order_model::ORDER_COMPLETED ) {
448 // Insert enrollment.
449 add_filter( 'tutor_enroll_data', fn( $enroll_data) => array_merge( $enroll_data, array( 'post_status' => 'completed' ) ) );
450
451 $enrollment_id = EnrollmentModel::do_enroll( $object_id, $order_id, $student_id );
452 if ( $enrollment_id ) {
453 if ( $this->is_bundle_order( $order, $object_id ) && $this->order_model->is_single_order( $order ) ) {
454 BundleModel::enroll_to_bundle_courses( $object_id, $student_id );
455 }
456 update_post_meta( $enrollment_id, EnrollmentModel::ENROLLMENT_ORDER_ID_META, $order_id );
457
458 do_action( 'tutor_order_enrolled', $order, $enrollment_id );
459 } else {
460 // Log error message with student id and course id.
461 error_log( "Error updating enrollment for student {$student_id} and course {$object_id}" );
462 }
463 }
464 }
465 }
466
467 // Update earnings.
468 $earnings->prepare_order_earnings( $order_id );
469 $earnings->remove_before_store_earnings();
470 }
471
472 /**
473 * Update order data for the free checkout
474 *
475 * @since 3.4.0
476 *
477 * @param array $order_data Order data.
478 *
479 * @return array
480 */
481 public function update_order_data( array $order_data ) {
482 if ( empty( $order_data['total_price'] ) && OrderModel::TYPE_SINGLE_ORDER === $order_data['order_type'] ) {
483 $order_data['order_status'] = OrderModel::ORDER_COMPLETED;
484 $order_data['payment_status'] = OrderModel::PAYMENT_PAID;
485 $order_data['payment_method'] = 'free';
486 }
487 return $order_data;
488 }
489
490 /**
491 * Enroll user to the course when free checkout
492 *
493 * @since 3.4.0
494 *
495 * @param array $order_data Order data.
496 *
497 * @return array
498 */
499 public function handle_free_checkout( array $order_data ) {
500 if ( empty( $order_data['total_price'] ) && OrderModel::TYPE_SINGLE_ORDER === $order_data['order_type'] ) {
501 $order_id = $order_data['id'];
502 $user_id = $order_data['user_id'];
503 $items = $order_data['items'];
504 foreach ( $items as $item ) {
505 $is_gift_item = apply_filters( 'tutor_is_gift_item', false, $item );
506 if ( $is_gift_item ) {
507 continue;
508 }
509
510 add_filter( 'tutor_enroll_data', fn( $enroll_data ) => array_merge( $enroll_data, array( 'post_status' => 'completed' ) ) );
511
512 $enrolled_id = EnrollmentModel::do_enroll( $item['item_id'], $order_data['id'], $user_id );
513 if ( $enrolled_id && tutor_utils()->is_addon_enabled( 'course-bundle' ) && get_post_type( $item['item_id'] ) === CourseBundle::POST_TYPE ) {
514 BundleModel::enroll_to_bundle_courses( $item['item_id'], $user_id );
515 }
516 }
517
518 // Store coupon usage.
519 $this->coupon_ctrl->store_coupon_usage( $order_id );
520 }
521 return $order_data;
522 }
523
524 /**
525 * Redirect user to the course after free checkout when item is 1.
526 * If user checkout multiple items and keep the default behavior.
527 *
528 * @since 3.4.0
529 *
530 * @param string $url Default redirect url.
531 * @param string $status Order placement status.
532 * @param integer $order_id Order id.
533 *
534 * @return string
535 */
536 public function redirect_to_the_course( string $url, string $status, int $order_id ): string {
537 $user_id = get_current_user_id();
538 if ( OrderModel::ORDER_PLACEMENT_SUCCESS === $status ) {
539 $order = $this->order_model->get_order_by_id( $order_id );
540 if ( $order && count( $order->items ) === 1 && empty( $order->total_price ) && OrderModel::TYPE_SINGLE_ORDER === $order->order_type ) {
541
542 // Firing hook to clear cart.
543 do_action( 'tutor_order_placement_success', $order_id );
544
545 // Clear the alert message.
546 delete_transient( CheckoutController::PAY_NOW_ALERT_MSG_TRANSIENT_KEY . $user_id );
547 delete_transient( CheckoutController::PAY_NOW_ERROR_TRANSIENT_KEY . $user_id );
548 $course_id = $order->items[0]->id;
549 $url = get_the_permalink( $course_id );
550 }
551 }
552 return $url;
553 }
554
555 /**
556 * Store billing address for an order when order is placed.
557 *
558 * @since 3.5.0
559 *
560 * @param array $order_data order data.
561 *
562 * @return void
563 */
564 public function store_billing_address_for_order( array $order_data ) {
565 $order_id = $order_data['id'];
566 $user_id = $order_data['user_id'];
567 $billing_info = ( new BillingController( false ) )->get_billing_info( $user_id );
568
569 /**
570 * JSON_UNESCAPED_UNICODE is used to ensure that the billing info is stored in a readable format.
571 * This is important for languages that use non-ASCII characters like ñ, á, é, í, ó, ú, ü, etc.
572 *
573 * @since 3.7.1
574 */
575 $meta_value = '{}';
576 if ( $billing_info ) {
577 $meta_value = wp_json_encode( $billing_info, JSON_UNESCAPED_UNICODE );
578 } else {
579 /**
580 * Store user data as billing info
581 * If user has no billing info during order like manual enrollment from CSV.
582 */
583 $user_data = get_userdata( $user_id );
584 $meta_value = wp_json_encode(
585 array(
586 'billing_first_name' => $user_data->first_name,
587 'billing_last_name' => $user_data->last_name,
588 'billing_email' => $user_data->user_email,
589 ),
590 JSON_UNESCAPED_UNICODE
591 );
592 }
593
594 OrderMetaModel::add_meta(
595 $order_id,
596 OrderModel::META_KEY_BILLING_ADDRESS,
597 $meta_value
598 );
599 }
600 }
601