Completed
Branch BUG/fix-abandoned-payment-reg-... (49a545)
by
unknown
18:00 queued 09:21
created
core/EE_Cron_Tasks.core.php 1 patch
Indentation   +594 added lines, -594 removed lines patch added patch discarded remove patch
@@ -15,598 +15,598 @@
 block discarded – undo
15 15
 class EE_Cron_Tasks extends EE_Base
16 16
 {
17 17
 
18
-    /**
19
-     * WordPress doesn't allow duplicate crons within 10 minutes of the original,
20
-     * so we'll set our retry time for just over 10 minutes to avoid that
21
-     */
22
-    const reschedule_timeout = 605;
23
-
24
-
25
-    /**
26
-     * @var EE_Cron_Tasks
27
-     */
28
-    private static $_instance;
29
-
30
-
31
-    /**
32
-     * @return EE_Cron_Tasks
33
-     * @throws ReflectionException
34
-     * @throws EE_Error
35
-     * @throws InvalidArgumentException
36
-     * @throws InvalidInterfaceException
37
-     * @throws InvalidDataTypeException
38
-     */
39
-    public static function instance()
40
-    {
41
-        if (! self::$_instance instanceof EE_Cron_Tasks) {
42
-            self::$_instance = new self();
43
-        }
44
-        return self::$_instance;
45
-    }
46
-
47
-
48
-    /**
49
-     * @access private
50
-     * @throws InvalidDataTypeException
51
-     * @throws InvalidInterfaceException
52
-     * @throws InvalidArgumentException
53
-     * @throws EE_Error
54
-     * @throws ReflectionException
55
-     */
56
-    private function __construct()
57
-    {
58
-        do_action('AHEE_log', __CLASS__, __FUNCTION__);
59
-        // verify that WP Cron is enabled
60
-        if (defined('DISABLE_WP_CRON')
61
-            && DISABLE_WP_CRON
62
-            && is_admin()
63
-            && ! get_option('ee_disabled_wp_cron_check')
64
-        ) {
65
-            /**
66
-             * This needs to be delayed until after the config is loaded because EE_Cron_Tasks is constructed before
67
-             * config is loaded.
68
-             * This is intentionally using a anonymous function so that its not easily de-registered.  Client code
69
-             * wanting to not have this functionality can just register its own action at a priority after this one to
70
-             * reverse any changes.
71
-             */
72
-            add_action(
73
-                'AHEE__EE_System__load_core_configuration__complete',
74
-                function () {
75
-                    EE_Registry::instance()->NET_CFG->core->do_messages_on_same_request = true;
76
-                    EE_Registry::instance()->NET_CFG->update_config(true, false);
77
-                    add_option('ee_disabled_wp_cron_check', 1, '', false);
78
-                }
79
-            );
80
-        }
81
-        // UPDATE TRANSACTION WITH PAYMENT
82
-        add_action(
83
-            'AHEE__EE_Cron_Tasks__update_transaction_with_payment_2',
84
-            array('EE_Cron_Tasks', 'setup_update_for_transaction_with_payment'),
85
-            10,
86
-            2
87
-        );
88
-        // ABANDONED / EXPIRED TRANSACTION CHECK
89
-        add_action(
90
-            'AHEE__EE_Cron_Tasks__expired_transaction_check',
91
-            array('EE_Cron_Tasks', 'expired_transaction_check'),
92
-            10,
93
-            1
94
-        );
95
-        // CLEAN OUT JUNK TRANSACTIONS AND RELATED DATA
96
-        add_action(
97
-            'AHEE__EE_Cron_Tasks__clean_up_junk_transactions',
98
-            array('EE_Cron_Tasks', 'clean_out_junk_transactions')
99
-        );
100
-        // logging
101
-        add_action(
102
-            'AHEE__EE_System__load_core_configuration__complete',
103
-            array('EE_Cron_Tasks', 'log_scheduled_ee_crons')
104
-        );
105
-        EE_Registry::instance()->load_lib('Messages_Scheduler');
106
-        // clean out old gateway logs
107
-        add_action(
108
-            'AHEE_EE_Cron_Tasks__clean_out_old_gateway_logs',
109
-            array('EE_Cron_Tasks', 'clean_out_old_gateway_logs')
110
-        );
111
-    }
112
-
113
-
114
-    /**
115
-     * @access protected
116
-     * @return void
117
-     */
118
-    public static function log_scheduled_ee_crons()
119
-    {
120
-        $ee_crons = array(
121
-            'AHEE__EE_Cron_Tasks__update_transaction_with_payment',
122
-            'AHEE__EE_Cron_Tasks__finalize_abandoned_transactions',
123
-            'AHEE__EE_Cron_Tasks__clean_up_junk_transactions',
124
-        );
125
-        $crons = (array) get_option('cron');
126
-        if (! is_array($crons)) {
127
-            return;
128
-        }
129
-        foreach ($crons as $timestamp => $cron) {
130
-            /** @var array[] $cron */
131
-            foreach ($ee_crons as $ee_cron) {
132
-                if (isset($cron[ $ee_cron ]) && is_array($cron[ $ee_cron ])) {
133
-                    do_action('AHEE_log', __CLASS__, __FUNCTION__, $ee_cron, 'scheduled EE cron');
134
-                    foreach ($cron[ $ee_cron ] as $ee_cron_details) {
135
-                        if (! empty($ee_cron_details['args'])) {
136
-                            do_action(
137
-                                'AHEE_log',
138
-                                __CLASS__,
139
-                                __FUNCTION__,
140
-                                print_r($ee_cron_details['args'], true),
141
-                                "{$ee_cron} args"
142
-                            );
143
-                        }
144
-                    }
145
-                }
146
-            }
147
-        }
148
-    }
149
-
150
-
151
-    /**
152
-     * reschedule_cron_for_transactions_if_maintenance_mode
153
-     * if Maintenance Mode is active, this will reschedule a cron to run again in 10 minutes
154
-     *
155
-     * @param string $cron_task
156
-     * @param array  $TXN_IDs
157
-     * @return bool
158
-     * @throws DomainException
159
-     */
160
-    public static function reschedule_cron_for_transactions_if_maintenance_mode($cron_task, array $TXN_IDs)
161
-    {
162
-        if (! method_exists('EE_Cron_Tasks', $cron_task)) {
163
-            throw new DomainException(
164
-                sprintf(
165
-                    __('"%1$s" is not valid method on EE_Cron_Tasks.', 'event_espresso'),
166
-                    $cron_task
167
-                )
168
-            );
169
-        }
170
-        // reschedule the cron if we can't hit the db right now
171
-        if (! EE_Maintenance_Mode::instance()->models_can_query()) {
172
-            foreach ($TXN_IDs as $TXN_ID => $additional_vars) {
173
-                // ensure $additional_vars is an array
174
-                $additional_vars = is_array($additional_vars) ? $additional_vars : array($additional_vars);
175
-                // reset cron job for the TXN
176
-                call_user_func_array(
177
-                    array('EE_Cron_Tasks', $cron_task),
178
-                    array_merge(
179
-                        array(
180
-                            time() + (10 * MINUTE_IN_SECONDS),
181
-                            $TXN_ID,
182
-                        ),
183
-                        $additional_vars
184
-                    )
185
-                );
186
-            }
187
-            return true;
188
-        }
189
-        return false;
190
-    }
191
-
192
-
193
-
194
-
195
-    /****************  UPDATE TRANSACTION WITH PAYMENT ****************/
196
-
197
-
198
-    /**
199
-     * array of TXN IDs and the payment
200
-     *
201
-     * @var array
202
-     */
203
-    protected static $_update_transactions_with_payment = array();
204
-
205
-
206
-    /**
207
-     * schedule_update_transaction_with_payment
208
-     * sets a wp_schedule_single_event() for updating any TXNs that may
209
-     * require updating due to recently received payments
210
-     *
211
-     * @param int $timestamp
212
-     * @param int $TXN_ID
213
-     * @param int $PAY_ID
214
-     */
215
-    public static function schedule_update_transaction_with_payment(
216
-        $timestamp,
217
-        $TXN_ID,
218
-        $PAY_ID
219
-    ) {
220
-        do_action('AHEE_log', __CLASS__, __FUNCTION__);
221
-        // validate $TXN_ID and $timestamp
222
-        $TXN_ID = absint($TXN_ID);
223
-        $timestamp = absint($timestamp);
224
-        if ($TXN_ID && $timestamp) {
225
-            wp_schedule_single_event(
226
-                $timestamp,
227
-                'AHEE__EE_Cron_Tasks__update_transaction_with_payment_2',
228
-                array($TXN_ID, $PAY_ID)
229
-            );
230
-        }
231
-    }
232
-
233
-
234
-    /**
235
-     * setup_update_for_transaction_with_payment
236
-     * this is the callback for the action hook:
237
-     * 'AHEE__EE_Cron_Tasks__update_transaction_with_payment'
238
-     * which is setup by EE_Cron_Tasks::schedule_update_transaction_with_payment().
239
-     * The passed TXN_ID and associated payment gets added to an array, and then
240
-     * the EE_Cron_Tasks::update_transaction_with_payment() function is hooked into
241
-     * 'shutdown' which will actually handle the processing of any
242
-     * transactions requiring updating, because doing so now would be too early
243
-     * and the required resources may not be available
244
-     *
245
-     * @param int $TXN_ID
246
-     * @param int $PAY_ID
247
-     */
248
-    public static function setup_update_for_transaction_with_payment($TXN_ID = 0, $PAY_ID = 0)
249
-    {
250
-        do_action('AHEE_log', __CLASS__, __FUNCTION__, $TXN_ID, '$TXN_ID');
251
-        if (absint($TXN_ID)) {
252
-            self::$_update_transactions_with_payment[ $TXN_ID ] = $PAY_ID;
253
-            add_action(
254
-                'shutdown',
255
-                array('EE_Cron_Tasks', 'update_transaction_with_payment'),
256
-                5
257
-            );
258
-        }
259
-    }
260
-
261
-
262
-    /**
263
-     * update_transaction_with_payment
264
-     * loops through the self::$_abandoned_transactions array
265
-     * and attempts to finalize any TXNs that have not been completed
266
-     * but have had their sessions expired, most likely due to a user not
267
-     * returning from an off-site payment gateway
268
-     *
269
-     * @throws EE_Error
270
-     * @throws DomainException
271
-     * @throws InvalidDataTypeException
272
-     * @throws InvalidInterfaceException
273
-     * @throws InvalidArgumentException
274
-     * @throws ReflectionException
275
-     * @throws RuntimeException
276
-     */
277
-    public static function update_transaction_with_payment()
278
-    {
279
-        do_action('AHEE_log', __CLASS__, __FUNCTION__);
280
-        if (// are there any TXNs that need cleaning up ?
281
-            empty(self::$_update_transactions_with_payment)
282
-            // reschedule the cron if we can't hit the db right now
283
-            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
284
-                'schedule_update_transaction_with_payment',
285
-                self::$_update_transactions_with_payment
286
-            )
287
-        ) {
288
-            return;
289
-        }
290
-        /** @type EE_Payment_Processor $payment_processor */
291
-        $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
292
-        // set revisit flag for payment processor
293
-        $payment_processor->set_revisit();
294
-        // load EEM_Transaction
295
-        EE_Registry::instance()->load_model('Transaction');
296
-        foreach (self::$_update_transactions_with_payment as $TXN_ID => $PAY_ID) {
297
-            // reschedule the cron if we can't hit the db right now
298
-            if (! EE_Maintenance_Mode::instance()->models_can_query()) {
299
-                // reset cron job for updating the TXN
300
-                EE_Cron_Tasks::schedule_update_transaction_with_payment(
301
-                    time() + EE_Cron_Tasks::reschedule_timeout,
302
-                    $TXN_ID,
303
-                    $PAY_ID
304
-                );
305
-                continue;
306
-            }
307
-            $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
308
-            $payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
309
-            // verify transaction
310
-            if ($transaction instanceof EE_Transaction && $payment instanceof EE_Payment) {
311
-                // now try to update the TXN with any payments
312
-                $payment_processor->update_txn_based_on_payment($transaction, $payment, true, true);
313
-            }
314
-            unset(self::$_update_transactions_with_payment[ $TXN_ID ]);
315
-        }
316
-    }
317
-
318
-
319
-
320
-    /************  END OF UPDATE TRANSACTION WITH PAYMENT  ************/
321
-
322
-
323
-    /*****************  EXPIRED TRANSACTION CHECK *****************/
324
-
325
-
326
-    /**
327
-     * array of TXN IDs
328
-     *
329
-     * @var array
330
-     */
331
-    protected static $_expired_transactions = array();
332
-
333
-
334
-    /**
335
-     * schedule_expired_transaction_check
336
-     * sets a wp_schedule_single_event() for following up on TXNs after their session has expired
337
-     *
338
-     * @param int $timestamp
339
-     * @param int $TXN_ID
340
-     */
341
-    public static function schedule_expired_transaction_check(
342
-        $timestamp,
343
-        $TXN_ID
344
-    ) {
345
-        // validate $TXN_ID and $timestamp
346
-        $TXN_ID = absint($TXN_ID);
347
-        $timestamp = absint($timestamp);
348
-        if ($TXN_ID && $timestamp) {
349
-            wp_schedule_single_event(
350
-                $timestamp,
351
-                'AHEE__EE_Cron_Tasks__expired_transaction_check',
352
-                array($TXN_ID)
353
-            );
354
-        }
355
-    }
356
-
357
-
358
-    /**
359
-     * expired_transaction_check
360
-     * this is the callback for the action hook:
361
-     * 'AHEE__EE_Cron_Tasks__transaction_session_expiration_check'
362
-     * which is utilized by wp_schedule_single_event()
363
-     * in \EED_Single_Page_Checkout::_initialize_transaction().
364
-     * The passed TXN_ID gets added to an array, and then the
365
-     * process_expired_transactions() function is hooked into
366
-     * 'AHEE__EE_System__core_loaded_and_ready' which will actually handle the
367
-     * processing of any failed transactions, because doing so now would be
368
-     * too early and the required resources may not be available
369
-     *
370
-     * @param int $TXN_ID
371
-     */
372
-    public static function expired_transaction_check($TXN_ID = 0)
373
-    {
374
-        if (absint($TXN_ID)) {
375
-            self::$_expired_transactions[ $TXN_ID ] = $TXN_ID;
376
-            add_action(
377
-                'shutdown',
378
-                array('EE_Cron_Tasks', 'process_expired_transactions'),
379
-                5
380
-            );
381
-        }
382
-    }
383
-
384
-
385
-    /**
386
-     * process_expired_transactions
387
-     * loops through the self::$_expired_transactions array and processes any failed TXNs
388
-     *
389
-     * @throws EE_Error
390
-     * @throws InvalidDataTypeException
391
-     * @throws InvalidInterfaceException
392
-     * @throws InvalidArgumentException
393
-     * @throws ReflectionException
394
-     * @throws DomainException
395
-     * @throws RuntimeException
396
-     */
397
-    public static function process_expired_transactions()
398
-    {
399
-        if (// are there any TXNs that need cleaning up ?
400
-            empty(self::$_expired_transactions)
401
-            // reschedule the cron if we can't hit the db right now
402
-            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
403
-                'schedule_expired_transaction_check',
404
-                self::$_expired_transactions
405
-            )
406
-        ) {
407
-            return;
408
-        }
409
-        /** @type EE_Transaction_Processor $transaction_processor */
410
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
411
-        // set revisit flag for txn processor
412
-        $transaction_processor->set_revisit();
413
-        // load EEM_Transaction
414
-        EE_Registry::instance()->load_model('Transaction');
415
-        foreach (self::$_expired_transactions as $TXN_ID) {
416
-            $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
417
-            // verify transaction and whether it is failed or not
418
-            if ($transaction instanceof EE_Transaction) {
419
-                switch ($transaction->status_ID()) {
420
-                    // Completed TXNs
421
-                    case EEM_Transaction::complete_status_code:
422
-                        /** @type EE_Transaction_Processor $transaction_processor */
423
-                        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
424
-                        $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
425
-                            $transaction,
426
-                            $transaction->last_payment(),
427
-                            [],
428
-                            false
429
-                        );
430
-                        do_action(
431
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__completed_transaction',
432
-                            $transaction
433
-                        );
434
-                        break;
435
-                    // Overpaid TXNs
436
-                    case EEM_Transaction::overpaid_status_code:
437
-                        do_action(
438
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__overpaid_transaction',
439
-                            $transaction
440
-                        );
441
-                        break;
442
-                    // Incomplete TXNs
443
-                    case EEM_Transaction::incomplete_status_code:
444
-                        do_action(
445
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
446
-                            $transaction
447
-                        );
448
-                        // todo : move business logic into EE_Transaction_Processor for finalizing abandoned transactions
449
-                        break;
450
-                    // Abandoned TXNs
451
-                    case EEM_Transaction::abandoned_status_code:
452
-                        // run hook before updating transaction, primarily so
453
-                        // EED_Ticket_Sales_Monitor::process_abandoned_transactions() can release reserved tickets
454
-                        do_action(
455
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
456
-                            $transaction
457
-                        );
458
-                        // don't finalize the TXN if it has already been completed
459
-                        if ($transaction->all_reg_steps_completed() !== true) {
460
-                            /** @type EE_Payment_Processor $payment_processor */
461
-                            $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
462
-                            // let's simulate an IPN here which will trigger any notifications that need to go out
463
-                            $payment_processor->update_txn_based_on_payment(
464
-                                $transaction,
465
-                                $transaction->last_payment(),
466
-                                true,
467
-                                true
468
-                            );
469
-                        }
470
-                        break;
471
-                    // Failed TXNs
472
-                    case EEM_Transaction::failed_status_code:
473
-                        do_action(
474
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
475
-                            $transaction
476
-                        );
477
-                        // todo :
478
-                        // perform garbage collection here and remove clean_out_junk_transactions()
479
-                        // $registrations = $transaction->registrations();
480
-                        // if (! empty($registrations)) {
481
-                        //     foreach ($registrations as $registration) {
482
-                        //         if ($registration instanceof EE_Registration) {
483
-                        //             $delete_registration = true;
484
-                        //             if ($registration->attendee() instanceof EE_Attendee) {
485
-                        //                 $delete_registration = false;
486
-                        //             }
487
-                        //             if ($delete_registration) {
488
-                        //                 $registration->delete_permanently();
489
-                        //                 $registration->delete_related_permanently();
490
-                        //             }
491
-                        //         }
492
-                        //     }
493
-                        // }
494
-                        break;
495
-                }
496
-            }
497
-            unset(self::$_expired_transactions[ $TXN_ID ]);
498
-        }
499
-    }
500
-
501
-
502
-
503
-    /*************  END OF EXPIRED TRANSACTION CHECK  *************/
504
-
505
-
506
-    /************* START CLEAN UP BOT TRANSACTIONS **********************/
507
-
508
-
509
-    /**
510
-     * callback for 'AHEE__EE_Cron_Tasks__clean_up_junk_transactions'
511
-     * which is setup during activation to run on an hourly cron
512
-     *
513
-     * @throws EE_Error
514
-     * @throws InvalidArgumentException
515
-     * @throws InvalidDataTypeException
516
-     * @throws InvalidInterfaceException
517
-     * @throws DomainException
518
-     */
519
-    public static function clean_out_junk_transactions()
520
-    {
521
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
522
-            EED_Ticket_Sales_Monitor::reset_reservation_counts();
523
-            EEM_Transaction::instance('')->delete_junk_transactions();
524
-            EEM_Registration::instance('')->delete_registrations_with_no_transaction();
525
-            EEM_Line_Item::instance('')->delete_line_items_with_no_transaction();
526
-        }
527
-    }
528
-
529
-
530
-    /**
531
-     * Deletes old gateway logs. After about a week we usually don't need them for debugging. But folks can filter that.
532
-     *
533
-     * @throws EE_Error
534
-     * @throws InvalidDataTypeException
535
-     * @throws InvalidInterfaceException
536
-     * @throws InvalidArgumentException
537
-     */
538
-    public static function clean_out_old_gateway_logs()
539
-    {
540
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
541
-            $reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
542
-            $time_diff_for_comparison = apply_filters(
543
-                'FHEE__EE_Cron_Tasks__clean_out_old_gateway_logs__time_diff_for_comparison',
544
-                '-' . $reg_config->gateway_log_lifespan
545
-            );
546
-            EEM_Change_Log::instance()->delete_gateway_logs_older_than(new DateTime($time_diff_for_comparison));
547
-        }
548
-    }
549
-
550
-
551
-    /*****************  FINALIZE ABANDONED TRANSACTIONS *****************/
552
-
553
-
554
-    /**
555
-     * @var array
556
-     */
557
-    protected static $_abandoned_transactions = array();
558
-
559
-
560
-    /**
561
-     * @deprecated
562
-     * @param int $timestamp
563
-     * @param int $TXN_ID
564
-     */
565
-    public static function schedule_finalize_abandoned_transactions_check($timestamp, $TXN_ID)
566
-    {
567
-        EE_Cron_Tasks::schedule_expired_transaction_check($timestamp, $TXN_ID);
568
-    }
569
-
570
-
571
-    /**
572
-     * @deprecated
573
-     * @param int $TXN_ID
574
-     */
575
-    public static function check_for_abandoned_transactions($TXN_ID = 0)
576
-    {
577
-        EE_Cron_Tasks::expired_transaction_check($TXN_ID);
578
-    }
579
-
580
-
581
-    /**
582
-     * @deprecated
583
-     * @throws EE_Error
584
-     * @throws DomainException
585
-     * @throws InvalidDataTypeException
586
-     * @throws InvalidInterfaceException
587
-     * @throws InvalidArgumentException
588
-     * @throws ReflectionException
589
-     * @throws RuntimeException
590
-     */
591
-    public static function finalize_abandoned_transactions()
592
-    {
593
-        do_action('AHEE_log', __CLASS__, __FUNCTION__);
594
-        if (// are there any TXNs that need cleaning up ?
595
-            empty(self::$_abandoned_transactions)
596
-            // reschedule the cron if we can't hit the db right now
597
-            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
598
-                'schedule_expired_transaction_check',
599
-                self::$_abandoned_transactions
600
-            )
601
-        ) {
602
-            return;
603
-        }
604
-        // combine our arrays of transaction IDs
605
-        self::$_expired_transactions = self::$_abandoned_transactions + self::$_expired_transactions;
606
-        // and deal with abandoned transactions here now...
607
-        EE_Cron_Tasks::process_expired_transactions();
608
-    }
609
-
610
-
611
-    /*************  END OF FINALIZE ABANDONED TRANSACTIONS  *************/
18
+	/**
19
+	 * WordPress doesn't allow duplicate crons within 10 minutes of the original,
20
+	 * so we'll set our retry time for just over 10 minutes to avoid that
21
+	 */
22
+	const reschedule_timeout = 605;
23
+
24
+
25
+	/**
26
+	 * @var EE_Cron_Tasks
27
+	 */
28
+	private static $_instance;
29
+
30
+
31
+	/**
32
+	 * @return EE_Cron_Tasks
33
+	 * @throws ReflectionException
34
+	 * @throws EE_Error
35
+	 * @throws InvalidArgumentException
36
+	 * @throws InvalidInterfaceException
37
+	 * @throws InvalidDataTypeException
38
+	 */
39
+	public static function instance()
40
+	{
41
+		if (! self::$_instance instanceof EE_Cron_Tasks) {
42
+			self::$_instance = new self();
43
+		}
44
+		return self::$_instance;
45
+	}
46
+
47
+
48
+	/**
49
+	 * @access private
50
+	 * @throws InvalidDataTypeException
51
+	 * @throws InvalidInterfaceException
52
+	 * @throws InvalidArgumentException
53
+	 * @throws EE_Error
54
+	 * @throws ReflectionException
55
+	 */
56
+	private function __construct()
57
+	{
58
+		do_action('AHEE_log', __CLASS__, __FUNCTION__);
59
+		// verify that WP Cron is enabled
60
+		if (defined('DISABLE_WP_CRON')
61
+			&& DISABLE_WP_CRON
62
+			&& is_admin()
63
+			&& ! get_option('ee_disabled_wp_cron_check')
64
+		) {
65
+			/**
66
+			 * This needs to be delayed until after the config is loaded because EE_Cron_Tasks is constructed before
67
+			 * config is loaded.
68
+			 * This is intentionally using a anonymous function so that its not easily de-registered.  Client code
69
+			 * wanting to not have this functionality can just register its own action at a priority after this one to
70
+			 * reverse any changes.
71
+			 */
72
+			add_action(
73
+				'AHEE__EE_System__load_core_configuration__complete',
74
+				function () {
75
+					EE_Registry::instance()->NET_CFG->core->do_messages_on_same_request = true;
76
+					EE_Registry::instance()->NET_CFG->update_config(true, false);
77
+					add_option('ee_disabled_wp_cron_check', 1, '', false);
78
+				}
79
+			);
80
+		}
81
+		// UPDATE TRANSACTION WITH PAYMENT
82
+		add_action(
83
+			'AHEE__EE_Cron_Tasks__update_transaction_with_payment_2',
84
+			array('EE_Cron_Tasks', 'setup_update_for_transaction_with_payment'),
85
+			10,
86
+			2
87
+		);
88
+		// ABANDONED / EXPIRED TRANSACTION CHECK
89
+		add_action(
90
+			'AHEE__EE_Cron_Tasks__expired_transaction_check',
91
+			array('EE_Cron_Tasks', 'expired_transaction_check'),
92
+			10,
93
+			1
94
+		);
95
+		// CLEAN OUT JUNK TRANSACTIONS AND RELATED DATA
96
+		add_action(
97
+			'AHEE__EE_Cron_Tasks__clean_up_junk_transactions',
98
+			array('EE_Cron_Tasks', 'clean_out_junk_transactions')
99
+		);
100
+		// logging
101
+		add_action(
102
+			'AHEE__EE_System__load_core_configuration__complete',
103
+			array('EE_Cron_Tasks', 'log_scheduled_ee_crons')
104
+		);
105
+		EE_Registry::instance()->load_lib('Messages_Scheduler');
106
+		// clean out old gateway logs
107
+		add_action(
108
+			'AHEE_EE_Cron_Tasks__clean_out_old_gateway_logs',
109
+			array('EE_Cron_Tasks', 'clean_out_old_gateway_logs')
110
+		);
111
+	}
112
+
113
+
114
+	/**
115
+	 * @access protected
116
+	 * @return void
117
+	 */
118
+	public static function log_scheduled_ee_crons()
119
+	{
120
+		$ee_crons = array(
121
+			'AHEE__EE_Cron_Tasks__update_transaction_with_payment',
122
+			'AHEE__EE_Cron_Tasks__finalize_abandoned_transactions',
123
+			'AHEE__EE_Cron_Tasks__clean_up_junk_transactions',
124
+		);
125
+		$crons = (array) get_option('cron');
126
+		if (! is_array($crons)) {
127
+			return;
128
+		}
129
+		foreach ($crons as $timestamp => $cron) {
130
+			/** @var array[] $cron */
131
+			foreach ($ee_crons as $ee_cron) {
132
+				if (isset($cron[ $ee_cron ]) && is_array($cron[ $ee_cron ])) {
133
+					do_action('AHEE_log', __CLASS__, __FUNCTION__, $ee_cron, 'scheduled EE cron');
134
+					foreach ($cron[ $ee_cron ] as $ee_cron_details) {
135
+						if (! empty($ee_cron_details['args'])) {
136
+							do_action(
137
+								'AHEE_log',
138
+								__CLASS__,
139
+								__FUNCTION__,
140
+								print_r($ee_cron_details['args'], true),
141
+								"{$ee_cron} args"
142
+							);
143
+						}
144
+					}
145
+				}
146
+			}
147
+		}
148
+	}
149
+
150
+
151
+	/**
152
+	 * reschedule_cron_for_transactions_if_maintenance_mode
153
+	 * if Maintenance Mode is active, this will reschedule a cron to run again in 10 minutes
154
+	 *
155
+	 * @param string $cron_task
156
+	 * @param array  $TXN_IDs
157
+	 * @return bool
158
+	 * @throws DomainException
159
+	 */
160
+	public static function reschedule_cron_for_transactions_if_maintenance_mode($cron_task, array $TXN_IDs)
161
+	{
162
+		if (! method_exists('EE_Cron_Tasks', $cron_task)) {
163
+			throw new DomainException(
164
+				sprintf(
165
+					__('"%1$s" is not valid method on EE_Cron_Tasks.', 'event_espresso'),
166
+					$cron_task
167
+				)
168
+			);
169
+		}
170
+		// reschedule the cron if we can't hit the db right now
171
+		if (! EE_Maintenance_Mode::instance()->models_can_query()) {
172
+			foreach ($TXN_IDs as $TXN_ID => $additional_vars) {
173
+				// ensure $additional_vars is an array
174
+				$additional_vars = is_array($additional_vars) ? $additional_vars : array($additional_vars);
175
+				// reset cron job for the TXN
176
+				call_user_func_array(
177
+					array('EE_Cron_Tasks', $cron_task),
178
+					array_merge(
179
+						array(
180
+							time() + (10 * MINUTE_IN_SECONDS),
181
+							$TXN_ID,
182
+						),
183
+						$additional_vars
184
+					)
185
+				);
186
+			}
187
+			return true;
188
+		}
189
+		return false;
190
+	}
191
+
192
+
193
+
194
+
195
+	/****************  UPDATE TRANSACTION WITH PAYMENT ****************/
196
+
197
+
198
+	/**
199
+	 * array of TXN IDs and the payment
200
+	 *
201
+	 * @var array
202
+	 */
203
+	protected static $_update_transactions_with_payment = array();
204
+
205
+
206
+	/**
207
+	 * schedule_update_transaction_with_payment
208
+	 * sets a wp_schedule_single_event() for updating any TXNs that may
209
+	 * require updating due to recently received payments
210
+	 *
211
+	 * @param int $timestamp
212
+	 * @param int $TXN_ID
213
+	 * @param int $PAY_ID
214
+	 */
215
+	public static function schedule_update_transaction_with_payment(
216
+		$timestamp,
217
+		$TXN_ID,
218
+		$PAY_ID
219
+	) {
220
+		do_action('AHEE_log', __CLASS__, __FUNCTION__);
221
+		// validate $TXN_ID and $timestamp
222
+		$TXN_ID = absint($TXN_ID);
223
+		$timestamp = absint($timestamp);
224
+		if ($TXN_ID && $timestamp) {
225
+			wp_schedule_single_event(
226
+				$timestamp,
227
+				'AHEE__EE_Cron_Tasks__update_transaction_with_payment_2',
228
+				array($TXN_ID, $PAY_ID)
229
+			);
230
+		}
231
+	}
232
+
233
+
234
+	/**
235
+	 * setup_update_for_transaction_with_payment
236
+	 * this is the callback for the action hook:
237
+	 * 'AHEE__EE_Cron_Tasks__update_transaction_with_payment'
238
+	 * which is setup by EE_Cron_Tasks::schedule_update_transaction_with_payment().
239
+	 * The passed TXN_ID and associated payment gets added to an array, and then
240
+	 * the EE_Cron_Tasks::update_transaction_with_payment() function is hooked into
241
+	 * 'shutdown' which will actually handle the processing of any
242
+	 * transactions requiring updating, because doing so now would be too early
243
+	 * and the required resources may not be available
244
+	 *
245
+	 * @param int $TXN_ID
246
+	 * @param int $PAY_ID
247
+	 */
248
+	public static function setup_update_for_transaction_with_payment($TXN_ID = 0, $PAY_ID = 0)
249
+	{
250
+		do_action('AHEE_log', __CLASS__, __FUNCTION__, $TXN_ID, '$TXN_ID');
251
+		if (absint($TXN_ID)) {
252
+			self::$_update_transactions_with_payment[ $TXN_ID ] = $PAY_ID;
253
+			add_action(
254
+				'shutdown',
255
+				array('EE_Cron_Tasks', 'update_transaction_with_payment'),
256
+				5
257
+			);
258
+		}
259
+	}
260
+
261
+
262
+	/**
263
+	 * update_transaction_with_payment
264
+	 * loops through the self::$_abandoned_transactions array
265
+	 * and attempts to finalize any TXNs that have not been completed
266
+	 * but have had their sessions expired, most likely due to a user not
267
+	 * returning from an off-site payment gateway
268
+	 *
269
+	 * @throws EE_Error
270
+	 * @throws DomainException
271
+	 * @throws InvalidDataTypeException
272
+	 * @throws InvalidInterfaceException
273
+	 * @throws InvalidArgumentException
274
+	 * @throws ReflectionException
275
+	 * @throws RuntimeException
276
+	 */
277
+	public static function update_transaction_with_payment()
278
+	{
279
+		do_action('AHEE_log', __CLASS__, __FUNCTION__);
280
+		if (// are there any TXNs that need cleaning up ?
281
+			empty(self::$_update_transactions_with_payment)
282
+			// reschedule the cron if we can't hit the db right now
283
+			|| EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
284
+				'schedule_update_transaction_with_payment',
285
+				self::$_update_transactions_with_payment
286
+			)
287
+		) {
288
+			return;
289
+		}
290
+		/** @type EE_Payment_Processor $payment_processor */
291
+		$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
292
+		// set revisit flag for payment processor
293
+		$payment_processor->set_revisit();
294
+		// load EEM_Transaction
295
+		EE_Registry::instance()->load_model('Transaction');
296
+		foreach (self::$_update_transactions_with_payment as $TXN_ID => $PAY_ID) {
297
+			// reschedule the cron if we can't hit the db right now
298
+			if (! EE_Maintenance_Mode::instance()->models_can_query()) {
299
+				// reset cron job for updating the TXN
300
+				EE_Cron_Tasks::schedule_update_transaction_with_payment(
301
+					time() + EE_Cron_Tasks::reschedule_timeout,
302
+					$TXN_ID,
303
+					$PAY_ID
304
+				);
305
+				continue;
306
+			}
307
+			$transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
308
+			$payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
309
+			// verify transaction
310
+			if ($transaction instanceof EE_Transaction && $payment instanceof EE_Payment) {
311
+				// now try to update the TXN with any payments
312
+				$payment_processor->update_txn_based_on_payment($transaction, $payment, true, true);
313
+			}
314
+			unset(self::$_update_transactions_with_payment[ $TXN_ID ]);
315
+		}
316
+	}
317
+
318
+
319
+
320
+	/************  END OF UPDATE TRANSACTION WITH PAYMENT  ************/
321
+
322
+
323
+	/*****************  EXPIRED TRANSACTION CHECK *****************/
324
+
325
+
326
+	/**
327
+	 * array of TXN IDs
328
+	 *
329
+	 * @var array
330
+	 */
331
+	protected static $_expired_transactions = array();
332
+
333
+
334
+	/**
335
+	 * schedule_expired_transaction_check
336
+	 * sets a wp_schedule_single_event() for following up on TXNs after their session has expired
337
+	 *
338
+	 * @param int $timestamp
339
+	 * @param int $TXN_ID
340
+	 */
341
+	public static function schedule_expired_transaction_check(
342
+		$timestamp,
343
+		$TXN_ID
344
+	) {
345
+		// validate $TXN_ID and $timestamp
346
+		$TXN_ID = absint($TXN_ID);
347
+		$timestamp = absint($timestamp);
348
+		if ($TXN_ID && $timestamp) {
349
+			wp_schedule_single_event(
350
+				$timestamp,
351
+				'AHEE__EE_Cron_Tasks__expired_transaction_check',
352
+				array($TXN_ID)
353
+			);
354
+		}
355
+	}
356
+
357
+
358
+	/**
359
+	 * expired_transaction_check
360
+	 * this is the callback for the action hook:
361
+	 * 'AHEE__EE_Cron_Tasks__transaction_session_expiration_check'
362
+	 * which is utilized by wp_schedule_single_event()
363
+	 * in \EED_Single_Page_Checkout::_initialize_transaction().
364
+	 * The passed TXN_ID gets added to an array, and then the
365
+	 * process_expired_transactions() function is hooked into
366
+	 * 'AHEE__EE_System__core_loaded_and_ready' which will actually handle the
367
+	 * processing of any failed transactions, because doing so now would be
368
+	 * too early and the required resources may not be available
369
+	 *
370
+	 * @param int $TXN_ID
371
+	 */
372
+	public static function expired_transaction_check($TXN_ID = 0)
373
+	{
374
+		if (absint($TXN_ID)) {
375
+			self::$_expired_transactions[ $TXN_ID ] = $TXN_ID;
376
+			add_action(
377
+				'shutdown',
378
+				array('EE_Cron_Tasks', 'process_expired_transactions'),
379
+				5
380
+			);
381
+		}
382
+	}
383
+
384
+
385
+	/**
386
+	 * process_expired_transactions
387
+	 * loops through the self::$_expired_transactions array and processes any failed TXNs
388
+	 *
389
+	 * @throws EE_Error
390
+	 * @throws InvalidDataTypeException
391
+	 * @throws InvalidInterfaceException
392
+	 * @throws InvalidArgumentException
393
+	 * @throws ReflectionException
394
+	 * @throws DomainException
395
+	 * @throws RuntimeException
396
+	 */
397
+	public static function process_expired_transactions()
398
+	{
399
+		if (// are there any TXNs that need cleaning up ?
400
+			empty(self::$_expired_transactions)
401
+			// reschedule the cron if we can't hit the db right now
402
+			|| EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
403
+				'schedule_expired_transaction_check',
404
+				self::$_expired_transactions
405
+			)
406
+		) {
407
+			return;
408
+		}
409
+		/** @type EE_Transaction_Processor $transaction_processor */
410
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
411
+		// set revisit flag for txn processor
412
+		$transaction_processor->set_revisit();
413
+		// load EEM_Transaction
414
+		EE_Registry::instance()->load_model('Transaction');
415
+		foreach (self::$_expired_transactions as $TXN_ID) {
416
+			$transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
417
+			// verify transaction and whether it is failed or not
418
+			if ($transaction instanceof EE_Transaction) {
419
+				switch ($transaction->status_ID()) {
420
+					// Completed TXNs
421
+					case EEM_Transaction::complete_status_code:
422
+						/** @type EE_Transaction_Processor $transaction_processor */
423
+						$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
424
+						$transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
425
+							$transaction,
426
+							$transaction->last_payment(),
427
+							[],
428
+							false
429
+						);
430
+						do_action(
431
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__completed_transaction',
432
+							$transaction
433
+						);
434
+						break;
435
+					// Overpaid TXNs
436
+					case EEM_Transaction::overpaid_status_code:
437
+						do_action(
438
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__overpaid_transaction',
439
+							$transaction
440
+						);
441
+						break;
442
+					// Incomplete TXNs
443
+					case EEM_Transaction::incomplete_status_code:
444
+						do_action(
445
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
446
+							$transaction
447
+						);
448
+						// todo : move business logic into EE_Transaction_Processor for finalizing abandoned transactions
449
+						break;
450
+					// Abandoned TXNs
451
+					case EEM_Transaction::abandoned_status_code:
452
+						// run hook before updating transaction, primarily so
453
+						// EED_Ticket_Sales_Monitor::process_abandoned_transactions() can release reserved tickets
454
+						do_action(
455
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
456
+							$transaction
457
+						);
458
+						// don't finalize the TXN if it has already been completed
459
+						if ($transaction->all_reg_steps_completed() !== true) {
460
+							/** @type EE_Payment_Processor $payment_processor */
461
+							$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
462
+							// let's simulate an IPN here which will trigger any notifications that need to go out
463
+							$payment_processor->update_txn_based_on_payment(
464
+								$transaction,
465
+								$transaction->last_payment(),
466
+								true,
467
+								true
468
+							);
469
+						}
470
+						break;
471
+					// Failed TXNs
472
+					case EEM_Transaction::failed_status_code:
473
+						do_action(
474
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
475
+							$transaction
476
+						);
477
+						// todo :
478
+						// perform garbage collection here and remove clean_out_junk_transactions()
479
+						// $registrations = $transaction->registrations();
480
+						// if (! empty($registrations)) {
481
+						//     foreach ($registrations as $registration) {
482
+						//         if ($registration instanceof EE_Registration) {
483
+						//             $delete_registration = true;
484
+						//             if ($registration->attendee() instanceof EE_Attendee) {
485
+						//                 $delete_registration = false;
486
+						//             }
487
+						//             if ($delete_registration) {
488
+						//                 $registration->delete_permanently();
489
+						//                 $registration->delete_related_permanently();
490
+						//             }
491
+						//         }
492
+						//     }
493
+						// }
494
+						break;
495
+				}
496
+			}
497
+			unset(self::$_expired_transactions[ $TXN_ID ]);
498
+		}
499
+	}
500
+
501
+
502
+
503
+	/*************  END OF EXPIRED TRANSACTION CHECK  *************/
504
+
505
+
506
+	/************* START CLEAN UP BOT TRANSACTIONS **********************/
507
+
508
+
509
+	/**
510
+	 * callback for 'AHEE__EE_Cron_Tasks__clean_up_junk_transactions'
511
+	 * which is setup during activation to run on an hourly cron
512
+	 *
513
+	 * @throws EE_Error
514
+	 * @throws InvalidArgumentException
515
+	 * @throws InvalidDataTypeException
516
+	 * @throws InvalidInterfaceException
517
+	 * @throws DomainException
518
+	 */
519
+	public static function clean_out_junk_transactions()
520
+	{
521
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
522
+			EED_Ticket_Sales_Monitor::reset_reservation_counts();
523
+			EEM_Transaction::instance('')->delete_junk_transactions();
524
+			EEM_Registration::instance('')->delete_registrations_with_no_transaction();
525
+			EEM_Line_Item::instance('')->delete_line_items_with_no_transaction();
526
+		}
527
+	}
528
+
529
+
530
+	/**
531
+	 * Deletes old gateway logs. After about a week we usually don't need them for debugging. But folks can filter that.
532
+	 *
533
+	 * @throws EE_Error
534
+	 * @throws InvalidDataTypeException
535
+	 * @throws InvalidInterfaceException
536
+	 * @throws InvalidArgumentException
537
+	 */
538
+	public static function clean_out_old_gateway_logs()
539
+	{
540
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
541
+			$reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
542
+			$time_diff_for_comparison = apply_filters(
543
+				'FHEE__EE_Cron_Tasks__clean_out_old_gateway_logs__time_diff_for_comparison',
544
+				'-' . $reg_config->gateway_log_lifespan
545
+			);
546
+			EEM_Change_Log::instance()->delete_gateway_logs_older_than(new DateTime($time_diff_for_comparison));
547
+		}
548
+	}
549
+
550
+
551
+	/*****************  FINALIZE ABANDONED TRANSACTIONS *****************/
552
+
553
+
554
+	/**
555
+	 * @var array
556
+	 */
557
+	protected static $_abandoned_transactions = array();
558
+
559
+
560
+	/**
561
+	 * @deprecated
562
+	 * @param int $timestamp
563
+	 * @param int $TXN_ID
564
+	 */
565
+	public static function schedule_finalize_abandoned_transactions_check($timestamp, $TXN_ID)
566
+	{
567
+		EE_Cron_Tasks::schedule_expired_transaction_check($timestamp, $TXN_ID);
568
+	}
569
+
570
+
571
+	/**
572
+	 * @deprecated
573
+	 * @param int $TXN_ID
574
+	 */
575
+	public static function check_for_abandoned_transactions($TXN_ID = 0)
576
+	{
577
+		EE_Cron_Tasks::expired_transaction_check($TXN_ID);
578
+	}
579
+
580
+
581
+	/**
582
+	 * @deprecated
583
+	 * @throws EE_Error
584
+	 * @throws DomainException
585
+	 * @throws InvalidDataTypeException
586
+	 * @throws InvalidInterfaceException
587
+	 * @throws InvalidArgumentException
588
+	 * @throws ReflectionException
589
+	 * @throws RuntimeException
590
+	 */
591
+	public static function finalize_abandoned_transactions()
592
+	{
593
+		do_action('AHEE_log', __CLASS__, __FUNCTION__);
594
+		if (// are there any TXNs that need cleaning up ?
595
+			empty(self::$_abandoned_transactions)
596
+			// reschedule the cron if we can't hit the db right now
597
+			|| EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
598
+				'schedule_expired_transaction_check',
599
+				self::$_abandoned_transactions
600
+			)
601
+		) {
602
+			return;
603
+		}
604
+		// combine our arrays of transaction IDs
605
+		self::$_expired_transactions = self::$_abandoned_transactions + self::$_expired_transactions;
606
+		// and deal with abandoned transactions here now...
607
+		EE_Cron_Tasks::process_expired_transactions();
608
+	}
609
+
610
+
611
+	/*************  END OF FINALIZE ABANDONED TRANSACTIONS  *************/
612 612
 }
Please login to merge, or discard this patch.