Completed
Branch master (724475)
by
unknown
05:49
created
caffeinated/admin/new/tickets/Tickets_Admin_Page_Init.core.php 1 patch
Indentation   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -15,48 +15,48 @@
 block discarded – undo
15 15
  */
16 16
 class Tickets_Admin_Page_Init extends EE_Admin_Page_Init
17 17
 {
18
-    /**
19
-     *        constructor
20
-     *
21
-     * @Constructor
22
-     * @access public
23
-     * @return void
24
-     */
25
-    public function __construct()
26
-    {
27
-        if (! defined('TICKETS_PG_SLUG')) {
28
-            define('TICKETS_PG_SLUG', 'tickets');
29
-            define('TICKETS_LABEL', esc_html__('Default Tickets', 'event_espresso'));
30
-            define('TICKETS_ADMIN', EE_CORE_CAF_ADMIN . 'new/' . TICKETS_PG_SLUG . '/');
31
-            define('TICKETS_ADMIN_URL', admin_url('admin.php?page=' . TICKETS_PG_SLUG));
32
-            define('TICKETS_ASSETS_PATH', TICKETS_ADMIN . 'assets/');
33
-            define('TICKETS_ASSETS_URL', EE_CORE_CAF_ADMIN_URL . 'new/' . TICKETS_PG_SLUG . '/assets/');
34
-            define('TICKETS_TEMPLATE_PATH', TICKETS_ADMIN . 'templates/');
35
-            define('TICKETS_TEMPLATE_URL', EE_CORE_CAF_ADMIN_URL . 'new/' . TICKETS_PG_SLUG . '/templates/');
36
-        }
37
-        parent::__construct();
38
-        $this->_folder_path = EE_CORE_CAF_ADMIN . 'new/' . $this->_folder_name . DS;
39
-    }
18
+	/**
19
+	 *        constructor
20
+	 *
21
+	 * @Constructor
22
+	 * @access public
23
+	 * @return void
24
+	 */
25
+	public function __construct()
26
+	{
27
+		if (! defined('TICKETS_PG_SLUG')) {
28
+			define('TICKETS_PG_SLUG', 'tickets');
29
+			define('TICKETS_LABEL', esc_html__('Default Tickets', 'event_espresso'));
30
+			define('TICKETS_ADMIN', EE_CORE_CAF_ADMIN . 'new/' . TICKETS_PG_SLUG . '/');
31
+			define('TICKETS_ADMIN_URL', admin_url('admin.php?page=' . TICKETS_PG_SLUG));
32
+			define('TICKETS_ASSETS_PATH', TICKETS_ADMIN . 'assets/');
33
+			define('TICKETS_ASSETS_URL', EE_CORE_CAF_ADMIN_URL . 'new/' . TICKETS_PG_SLUG . '/assets/');
34
+			define('TICKETS_TEMPLATE_PATH', TICKETS_ADMIN . 'templates/');
35
+			define('TICKETS_TEMPLATE_URL', EE_CORE_CAF_ADMIN_URL . 'new/' . TICKETS_PG_SLUG . '/templates/');
36
+		}
37
+		parent::__construct();
38
+		$this->_folder_path = EE_CORE_CAF_ADMIN . 'new/' . $this->_folder_name . DS;
39
+	}
40 40
 
41 41
 
42
-    protected function _set_init_properties()
43
-    {
44
-        $this->label = TICKETS_LABEL;
45
-    }
42
+	protected function _set_init_properties()
43
+	{
44
+		$this->label = TICKETS_LABEL;
45
+	}
46 46
 
47 47
 
48
-    public function getMenuProperties(): array
49
-    {
50
-        return [
51
-            'menu_type'       => AdminMenuItem::TYPE_MENU_SUB_ITEM,
52
-            'menu_group'      => AdminMenuGroup::MENU_SLUG_MANAGEMENT,
53
-            'menu_order'      => 15,
54
-            'show_on_menu'    => AdminMenuItem::DISPLAY_NONE,
55
-            'parent_slug'     => AdminMenuTopLevel::MENU_PARENT_ACTIVE,
56
-            'menu_slug'       => TICKETS_PG_SLUG,
57
-            'menu_label'      => TICKETS_LABEL,
58
-            'capability'      => 'manage_options',
59
-            'admin_init_page' => $this,
60
-        ];
61
-    }
48
+	public function getMenuProperties(): array
49
+	{
50
+		return [
51
+			'menu_type'       => AdminMenuItem::TYPE_MENU_SUB_ITEM,
52
+			'menu_group'      => AdminMenuGroup::MENU_SLUG_MANAGEMENT,
53
+			'menu_order'      => 15,
54
+			'show_on_menu'    => AdminMenuItem::DISPLAY_NONE,
55
+			'parent_slug'     => AdminMenuTopLevel::MENU_PARENT_ACTIVE,
56
+			'menu_slug'       => TICKETS_PG_SLUG,
57
+			'menu_label'      => TICKETS_LABEL,
58
+			'capability'      => 'manage_options',
59
+			'admin_init_page' => $this,
60
+		];
61
+	}
62 62
 }
Please login to merge, or discard this patch.
modules/event_single/EED_Event_Single.module.php 2 patches
Indentation   +471 added lines, -471 removed lines patch added patch discarded remove patch
@@ -14,476 +14,476 @@  discard block
 block discarded – undo
14 14
  */
15 15
 class EED_Event_Single extends EED_Module
16 16
 {
17
-    const EVENT_DETAILS_PRIORITY = 100;
18
-    const EVENT_DATETIMES_PRIORITY = 110;
19
-    const EVENT_TICKETS_PRIORITY = 120;
20
-    const EVENT_VENUES_PRIORITY = 130;
21
-
22
-    /**
23
-     * @type bool $using_get_the_excerpt
24
-     */
25
-    protected static $using_get_the_excerpt = false;
26
-
27
-
28
-    /**
29
-     * @type EE_Template_Part_Manager $template_parts
30
-     */
31
-    protected $template_parts;
32
-
33
-
34
-    /**
35
-     * @return EED_Module|EED_Event_Single
36
-     */
37
-    public static function instance()
38
-    {
39
-        return parent::get_instance(__CLASS__);
40
-    }
41
-
42
-
43
-    /**
44
-     * set_hooks - for hooking into EE Core, other modules, etc
45
-     *
46
-     * @return void
47
-     * @throws InvalidArgumentException
48
-     * @throws InvalidDataTypeException
49
-     * @throws InvalidInterfaceException
50
-     */
51
-    public static function set_hooks()
52
-    {
53
-        add_filter('FHEE_run_EE_wp', '__return_true');
54
-        add_action('wp_loaded', array('EED_Event_Single', 'set_definitions'), 2);
55
-        /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_type_definitions */
56
-        $custom_post_type_definitions = LoaderFactory::getLoader()->getShared(
57
-            'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
58
-        );
59
-        $custom_post_types = $custom_post_type_definitions->getDefinitions();
60
-        EED_Module::registerRoute(
61
-            $custom_post_types[ EspressoPostType::EVENTS ]['singular_slug'],
62
-            'Event_Single',
63
-            'run'
64
-        );
65
-    }
66
-
67
-    /**
68
-     * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
69
-     *
70
-     * @return    void
71
-     */
72
-    public static function set_hooks_admin()
73
-    {
74
-        add_action('wp_loaded', array('EED_Event_Single', 'set_definitions'), 2);
75
-    }
76
-
77
-
78
-    /**
79
-     * set_definitions
80
-     *
81
-     * @static
82
-     * @return void
83
-     */
84
-    public static function set_definitions()
85
-    {
86
-        define('EVENT_SINGLE_ASSETS_URL', plugin_dir_url(__FILE__) . 'assets/');
87
-        define('EVENT_SINGLE_TEMPLATES_PATH', plugin_dir_path(__FILE__) . 'templates/');
88
-    }
89
-
90
-
91
-    /**
92
-     * set_config
93
-     *
94
-     * @void
95
-     */
96
-    protected function set_config()
97
-    {
98
-        $this->set_config_section('template_settings');
99
-        $this->set_config_class('EE_Event_Single_Config');
100
-        $this->set_config_name('EED_Event_Single');
101
-    }
102
-
103
-
104
-    /**
105
-     * initialize_template_parts
106
-     *
107
-     * @param EE_Config_Base|EE_Event_Single_Config $config
108
-     * @return EE_Template_Part_Manager
109
-     * @throws EE_Error
110
-     */
111
-    public function initialize_template_parts(EE_Event_Single_Config $config = null)
112
-    {
113
-        /** @type EE_Event_Single_Config $config */
114
-        $config = $config instanceof EE_Event_Single_Config ? $config : $this->config();
115
-        EEH_Autoloader::register_template_part_autoloaders();
116
-        $template_parts = new EE_Template_Part_Manager();
117
-        $template_parts->add_template_part(
118
-            'tickets',
119
-            esc_html__('Ticket Selector', 'event_espresso'),
120
-            'content-espresso_events-tickets.php',
121
-            $config->display_order_tickets
122
-        );
123
-        $template_parts->add_template_part(
124
-            'datetimes',
125
-            esc_html__('Dates and Times', 'event_espresso'),
126
-            'content-espresso_events-datetimes.php',
127
-            $config->display_order_datetimes
128
-        );
129
-        $template_parts->add_template_part(
130
-            'event',
131
-            esc_html__('Event Description', 'event_espresso'),
132
-            'content-espresso_events-details.php',
133
-            $config->display_order_event
134
-        );
135
-        $template_parts->add_template_part(
136
-            'venue',
137
-            esc_html__('Venue Information', 'event_espresso'),
138
-            'content-espresso_events-venues.php',
139
-            $config->display_order_venue
140
-        );
141
-        do_action('AHEE__EED_Event_Single__initialize_template_parts', $template_parts);
142
-        return $template_parts;
143
-    }
144
-
145
-
146
-    /**
147
-     * run - initial module setup
148
-     *
149
-     * @param WP $WP
150
-     * @return    void
151
-     */
152
-    public function run($WP)
153
-    {
154
-        // ensure valid EE_Events_Single_Config() object exists
155
-        $this->set_config();
156
-        // check what template is loaded
157
-        add_filter('template_include', array($this, 'template_include'), 999, 1);
158
-        add_filter('FHEE__EED_Ticket_Selector__load_tckt_slctr_assets', '__return_true');
159
-        // load css
160
-        add_action('wp_enqueue_scripts', array($this, 'wp_enqueue_scripts'), 10);
161
-    }
162
-
163
-
164
-    /**
165
-     * template_include
166
-     *
167
-     * @param    string $template
168
-     * @return    string
169
-     */
170
-    public function template_include($template)
171
-    {
172
-        global $post;
173
-        /** @type EE_Event_Single_Config $config */
174
-        $config = $this->config();
175
-        if ($config->display_status_banner_single) {
176
-            add_filter('the_title', array('EED_Event_Single', 'the_title'), 100, 2);
177
-        }
178
-        // not a custom template?
179
-        if (
180
-            ! post_password_required($post)
181
-            && (
182
-                apply_filters('FHEE__EED_Event_Single__template_include__allow_custom_selected_template', false)
183
-                || EE_Registry::instance()
184
-                              ->load_core('Front_Controller')
185
-                              ->get_selected_template() !== 'single-espresso_events.php'
186
-            )
187
-        ) {
188
-            EEH_Template::load_espresso_theme_functions();
189
-            // then add extra event data via hooks
190
-            add_action('loop_start', array('EED_Event_Single', 'loop_start'));
191
-            add_filter('get_the_excerpt', array('EED_Event_Single', 'get_the_excerpt'), 1, 1);
192
-            add_filter(
193
-                'the_content',
194
-                array('EED_Event_Single', 'event_details'),
195
-                EED_Event_Single::EVENT_DETAILS_PRIORITY
196
-            );
197
-            add_action('loop_end', array('EED_Event_Single', 'loop_end'));
198
-            // don't display entry meta because the existing theme will take car of that
199
-            add_filter('FHEE__content_espresso_events_details_template__display_entry_meta', '__return_false');
200
-        }
201
-        return $template;
202
-    }
203
-
204
-
205
-    /**
206
-     * @param WP_Query $query
207
-     * @return void
208
-     */
209
-    public static function loop_start(WP_Query $query)
210
-    {
211
-        if (! $query->is_main_query()) {
212
-            return;
213
-        }
214
-        global $post;
215
-        do_action('AHEE_event_details_before_post', $post, $query);
216
-    }
217
-
218
-
219
-    /**
220
-     * the_title
221
-     *
222
-     * @param    string $title
223
-     * @param    int    $id
224
-     * @return    string
225
-     */
226
-    public static function the_title($title = '', $id = 0)
227
-    {
228
-        global $post;
229
-        return ((function_exists('wp_is_block_theme') && wp_is_block_theme()) || in_the_loop()) && $post->ID === (int) $id
230
-            ? espresso_event_status_banner($post->ID) . $title
231
-            : $title;
232
-    }
233
-
234
-
235
-    /**
236
-     * get_the_excerpt
237
-     * kinda hacky, but if a theme is using get_the_excerpt(),
238
-     * then we need to remove our filters on the_content()
239
-     *
240
-     * @param        string $excerpt
241
-     * @return        string
242
-     */
243
-    public static function get_the_excerpt($excerpt = '')
244
-    {
245
-        EED_Event_Single::$using_get_the_excerpt = true;
246
-        add_filter('wp_trim_excerpt', array('EED_Event_Single', 'end_get_the_excerpt'), 999, 1);
247
-        return $excerpt;
248
-    }
249
-
250
-
251
-    /**
252
-     * end_get_the_excerpt
253
-     *
254
-     * @param  string $text
255
-     * @return string
256
-     */
257
-    public static function end_get_the_excerpt($text = '')
258
-    {
259
-        EED_Event_Single::$using_get_the_excerpt = false;
260
-        return $text;
261
-    }
262
-
263
-
264
-    /**
265
-     * event_details
266
-     *
267
-     * @param    string $content
268
-     * @return    string
269
-     */
270
-    public static function event_details($content)
271
-    {
272
-        global $post;
273
-        static $current_post_ID = 0;
274
-        if (
275
-            $current_post_ID !== $post->ID
276
-            && $post->post_type === EspressoPostType::EVENTS
277
-            && ! EED_Event_Single::$using_get_the_excerpt
278
-            && ! post_password_required()
279
-        ) {
280
-            // Set current post ID to prevent showing content twice, but only if headers have definitely been sent.
281
-            // Reason being is that some plugins, like Yoast, need to run through a copy of the loop early
282
-            // BEFORE headers are sent in order to examine the post content and generate content for the HTML header.
283
-            // We want to allow those plugins to still do their thing and have access to our content, but depending on
284
-            // how your event content is being displayed (shortcode, CPT route, etc), this filter can get applied twice,
285
-            // so the following allows this filter to be applied multiple times, but only once for real
286
-            $current_post_ID = did_action('loop_start') ? $post->ID : 0;
287
-            if (EE_Registry::instance()->CFG->template_settings->EED_Event_Single->use_sortable_display_order) {
288
-                // we need to first remove this callback from being applied to the_content()
289
-                // (otherwise it will recurse and blow up the interweb)
290
-                remove_filter(
291
-                    'the_content',
292
-                    array('EED_Event_Single', 'event_details'),
293
-                    EED_Event_Single::EVENT_DETAILS_PRIORITY
294
-                );
295
-                EED_Event_Single::instance()->template_parts = EED_Event_Single::instance()->initialize_template_parts(
296
-                );
297
-                $content = EEH_Template::locate_template('content-espresso_events-details.php');
298
-                $content = EED_Event_Single::instance()->template_parts->apply_template_part_filters($content);
299
-                add_filter(
300
-                    'the_content',
301
-                    array('EED_Event_Single', 'event_details'),
302
-                    EED_Event_Single::EVENT_DETAILS_PRIORITY
303
-                );
304
-            } else {
305
-                $content = EED_Event_Single::use_filterable_display_order();
306
-            }
307
-        }
308
-        return $content;
309
-    }
310
-
311
-
312
-    /**
313
-     * use_filterable_display_order
314
-     *
315
-     * @return string
316
-     */
317
-    protected static function use_filterable_display_order()
318
-    {
319
-        // since the 'content-espresso_events-details.php' template might be used directly from within a theme,
320
-        // it uses the_content() for displaying the $post->post_content
321
-        // so in order to load a template that uses the_content()
322
-        // from within a callback being used to filter the_content(),
323
-        // we need to first remove this callback from being applied to the_content()
324
-        // (otherwise it will recurse and blow up the interweb)
325
-        remove_filter(
326
-            'the_content',
327
-            array('EED_Event_Single', 'event_details'),
328
-            EED_Event_Single::EVENT_DETAILS_PRIORITY
329
-        );
330
-        // now add additional content
331
-        add_filter(
332
-            'the_content',
333
-            array('EED_Event_Single', 'event_datetimes'),
334
-            EED_Event_Single::EVENT_DATETIMES_PRIORITY,
335
-            1
336
-        );
337
-        add_filter(
338
-            'the_content',
339
-            array('EED_Event_Single', 'event_tickets'),
340
-            EED_Event_Single::EVENT_TICKETS_PRIORITY,
341
-            1
342
-        );
343
-        add_filter(
344
-            'the_content',
345
-            array('EED_Event_Single', 'event_venues'),
346
-            EED_Event_Single::EVENT_VENUES_PRIORITY,
347
-            1
348
-        );
349
-        do_action('AHEE__EED_Event_Single__use_filterable_display_order__after_add_filters');
350
-        // now load our template
351
-        $content = EEH_Template::locate_template('content-espresso_events-details.php');
352
-        // now add our filter back in, plus some others
353
-        add_filter(
354
-            'the_content',
355
-            array('EED_Event_Single', 'event_details'),
356
-            EED_Event_Single::EVENT_DETAILS_PRIORITY
357
-        );
358
-        remove_filter(
359
-            'the_content',
360
-            array('EED_Event_Single', 'event_datetimes'),
361
-            EED_Event_Single::EVENT_DATETIMES_PRIORITY
362
-        );
363
-        remove_filter(
364
-            'the_content',
365
-            array('EED_Event_Single', 'event_tickets'),
366
-            EED_Event_Single::EVENT_TICKETS_PRIORITY
367
-        );
368
-        remove_filter(
369
-            'the_content',
370
-            array('EED_Event_Single', 'event_venues'),
371
-            EED_Event_Single::EVENT_VENUES_PRIORITY
372
-        );
373
-        do_action('AHEE__EED_Event_Single__use_filterable_display_order__after_remove_filters');
374
-        // we're not returning the $content directly because the template we are loading uses the_content (or the_excerpt)
375
-        return $content;
376
-    }
377
-
378
-
379
-    /**
380
-     * event_datetimes - adds datetimes ABOVE content
381
-     *
382
-     * @param        string $content
383
-     * @return        string
384
-     */
385
-    public static function event_datetimes($content)
386
-    {
387
-        return EEH_Template::locate_template('content-espresso_events-datetimes.php') . $content;
388
-    }
389
-
390
-
391
-    /**
392
-     * event_tickets - adds tickets ABOVE content (which includes datetimes)
393
-     *
394
-     * @param        string $content
395
-     * @return        string
396
-     */
397
-    public static function event_tickets($content)
398
-    {
399
-        return EEH_Template::locate_template('content-espresso_events-tickets.php') . $content;
400
-    }
401
-
402
-
403
-    /**
404
-     * event_venues
405
-     *
406
-     * @param    string $content
407
-     * @return    string
408
-     */
409
-    public static function event_venue($content)
410
-    {
411
-        return EED_Event_Single::event_venues($content);
412
-    }
413
-
414
-
415
-    /**
416
-     * event_venues - adds venues BELOW content
417
-     *
418
-     * @param        string $content
419
-     * @return        string
420
-     */
421
-    public static function event_venues($content)
422
-    {
423
-        return $content . EEH_Template::locate_template('content-espresso_events-venues.php');
424
-    }
425
-
426
-
427
-    /**
428
-     * loop_end
429
-     *
430
-     * @param        array $wp_query_array an array containing the WP_Query object
431
-     * @return        void
432
-     */
433
-    public static function loop_end($wp_query_array)
434
-    {
435
-        global $post;
436
-        do_action('AHEE_event_details_after_post', $post, $wp_query_array);
437
-    }
438
-
439
-
440
-    /**
441
-     * wp_enqueue_scripts
442
-     *
443
-     * @return    void
444
-     */
445
-    public function wp_enqueue_scripts()
446
-    {
447
-        // get some style
448
-        if (
449
-            apply_filters('FHEE_enable_default_espresso_css', true)
450
-            && apply_filters('FHEE__EED_Event_Single__wp_enqueue_scripts__enable_css', true)
451
-        ) {
452
-            // first check uploads folder
453
-            if (is_readable(get_stylesheet_directory() . $this->theme . '/style.css')) {
454
-                wp_register_style(
455
-                    $this->theme,
456
-                    get_stylesheet_directory_uri() . $this->theme . '/style.css',
457
-                    array('dashicons', 'espresso_default')
458
-                );
459
-            } else {
460
-                wp_register_style(
461
-                    $this->theme,
462
-                    EE_TEMPLATES_URL . $this->theme . '/style.css',
463
-                    array('dashicons', 'espresso_default')
464
-                );
465
-            }
466
-            wp_enqueue_script($this->theme);
467
-            if (EE_Registry::instance()->CFG->map_settings->use_google_maps) {
468
-                add_action('wp_enqueue_scripts', array('EEH_Maps', 'espresso_google_map_js'), 11);
469
-            }
470
-        }
471
-    }
472
-
473
-
474
-    /**
475
-     * display_venue
476
-     *
477
-     * @return    bool
478
-     */
479
-    public static function display_venue()
480
-    {
481
-        /** @type EE_Event_Single_Config $config */
482
-        $config = EED_Event_Single::instance()->config();
483
-        $display_venue = $config->display_venue === null ? true : $config->display_venue;
484
-        $venue_name = EEH_Venue_View::venue_name();
485
-        return $display_venue && ! empty($venue_name);
486
-    }
17
+	const EVENT_DETAILS_PRIORITY = 100;
18
+	const EVENT_DATETIMES_PRIORITY = 110;
19
+	const EVENT_TICKETS_PRIORITY = 120;
20
+	const EVENT_VENUES_PRIORITY = 130;
21
+
22
+	/**
23
+	 * @type bool $using_get_the_excerpt
24
+	 */
25
+	protected static $using_get_the_excerpt = false;
26
+
27
+
28
+	/**
29
+	 * @type EE_Template_Part_Manager $template_parts
30
+	 */
31
+	protected $template_parts;
32
+
33
+
34
+	/**
35
+	 * @return EED_Module|EED_Event_Single
36
+	 */
37
+	public static function instance()
38
+	{
39
+		return parent::get_instance(__CLASS__);
40
+	}
41
+
42
+
43
+	/**
44
+	 * set_hooks - for hooking into EE Core, other modules, etc
45
+	 *
46
+	 * @return void
47
+	 * @throws InvalidArgumentException
48
+	 * @throws InvalidDataTypeException
49
+	 * @throws InvalidInterfaceException
50
+	 */
51
+	public static function set_hooks()
52
+	{
53
+		add_filter('FHEE_run_EE_wp', '__return_true');
54
+		add_action('wp_loaded', array('EED_Event_Single', 'set_definitions'), 2);
55
+		/** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_type_definitions */
56
+		$custom_post_type_definitions = LoaderFactory::getLoader()->getShared(
57
+			'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
58
+		);
59
+		$custom_post_types = $custom_post_type_definitions->getDefinitions();
60
+		EED_Module::registerRoute(
61
+			$custom_post_types[ EspressoPostType::EVENTS ]['singular_slug'],
62
+			'Event_Single',
63
+			'run'
64
+		);
65
+	}
66
+
67
+	/**
68
+	 * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
69
+	 *
70
+	 * @return    void
71
+	 */
72
+	public static function set_hooks_admin()
73
+	{
74
+		add_action('wp_loaded', array('EED_Event_Single', 'set_definitions'), 2);
75
+	}
76
+
77
+
78
+	/**
79
+	 * set_definitions
80
+	 *
81
+	 * @static
82
+	 * @return void
83
+	 */
84
+	public static function set_definitions()
85
+	{
86
+		define('EVENT_SINGLE_ASSETS_URL', plugin_dir_url(__FILE__) . 'assets/');
87
+		define('EVENT_SINGLE_TEMPLATES_PATH', plugin_dir_path(__FILE__) . 'templates/');
88
+	}
89
+
90
+
91
+	/**
92
+	 * set_config
93
+	 *
94
+	 * @void
95
+	 */
96
+	protected function set_config()
97
+	{
98
+		$this->set_config_section('template_settings');
99
+		$this->set_config_class('EE_Event_Single_Config');
100
+		$this->set_config_name('EED_Event_Single');
101
+	}
102
+
103
+
104
+	/**
105
+	 * initialize_template_parts
106
+	 *
107
+	 * @param EE_Config_Base|EE_Event_Single_Config $config
108
+	 * @return EE_Template_Part_Manager
109
+	 * @throws EE_Error
110
+	 */
111
+	public function initialize_template_parts(EE_Event_Single_Config $config = null)
112
+	{
113
+		/** @type EE_Event_Single_Config $config */
114
+		$config = $config instanceof EE_Event_Single_Config ? $config : $this->config();
115
+		EEH_Autoloader::register_template_part_autoloaders();
116
+		$template_parts = new EE_Template_Part_Manager();
117
+		$template_parts->add_template_part(
118
+			'tickets',
119
+			esc_html__('Ticket Selector', 'event_espresso'),
120
+			'content-espresso_events-tickets.php',
121
+			$config->display_order_tickets
122
+		);
123
+		$template_parts->add_template_part(
124
+			'datetimes',
125
+			esc_html__('Dates and Times', 'event_espresso'),
126
+			'content-espresso_events-datetimes.php',
127
+			$config->display_order_datetimes
128
+		);
129
+		$template_parts->add_template_part(
130
+			'event',
131
+			esc_html__('Event Description', 'event_espresso'),
132
+			'content-espresso_events-details.php',
133
+			$config->display_order_event
134
+		);
135
+		$template_parts->add_template_part(
136
+			'venue',
137
+			esc_html__('Venue Information', 'event_espresso'),
138
+			'content-espresso_events-venues.php',
139
+			$config->display_order_venue
140
+		);
141
+		do_action('AHEE__EED_Event_Single__initialize_template_parts', $template_parts);
142
+		return $template_parts;
143
+	}
144
+
145
+
146
+	/**
147
+	 * run - initial module setup
148
+	 *
149
+	 * @param WP $WP
150
+	 * @return    void
151
+	 */
152
+	public function run($WP)
153
+	{
154
+		// ensure valid EE_Events_Single_Config() object exists
155
+		$this->set_config();
156
+		// check what template is loaded
157
+		add_filter('template_include', array($this, 'template_include'), 999, 1);
158
+		add_filter('FHEE__EED_Ticket_Selector__load_tckt_slctr_assets', '__return_true');
159
+		// load css
160
+		add_action('wp_enqueue_scripts', array($this, 'wp_enqueue_scripts'), 10);
161
+	}
162
+
163
+
164
+	/**
165
+	 * template_include
166
+	 *
167
+	 * @param    string $template
168
+	 * @return    string
169
+	 */
170
+	public function template_include($template)
171
+	{
172
+		global $post;
173
+		/** @type EE_Event_Single_Config $config */
174
+		$config = $this->config();
175
+		if ($config->display_status_banner_single) {
176
+			add_filter('the_title', array('EED_Event_Single', 'the_title'), 100, 2);
177
+		}
178
+		// not a custom template?
179
+		if (
180
+			! post_password_required($post)
181
+			&& (
182
+				apply_filters('FHEE__EED_Event_Single__template_include__allow_custom_selected_template', false)
183
+				|| EE_Registry::instance()
184
+							  ->load_core('Front_Controller')
185
+							  ->get_selected_template() !== 'single-espresso_events.php'
186
+			)
187
+		) {
188
+			EEH_Template::load_espresso_theme_functions();
189
+			// then add extra event data via hooks
190
+			add_action('loop_start', array('EED_Event_Single', 'loop_start'));
191
+			add_filter('get_the_excerpt', array('EED_Event_Single', 'get_the_excerpt'), 1, 1);
192
+			add_filter(
193
+				'the_content',
194
+				array('EED_Event_Single', 'event_details'),
195
+				EED_Event_Single::EVENT_DETAILS_PRIORITY
196
+			);
197
+			add_action('loop_end', array('EED_Event_Single', 'loop_end'));
198
+			// don't display entry meta because the existing theme will take car of that
199
+			add_filter('FHEE__content_espresso_events_details_template__display_entry_meta', '__return_false');
200
+		}
201
+		return $template;
202
+	}
203
+
204
+
205
+	/**
206
+	 * @param WP_Query $query
207
+	 * @return void
208
+	 */
209
+	public static function loop_start(WP_Query $query)
210
+	{
211
+		if (! $query->is_main_query()) {
212
+			return;
213
+		}
214
+		global $post;
215
+		do_action('AHEE_event_details_before_post', $post, $query);
216
+	}
217
+
218
+
219
+	/**
220
+	 * the_title
221
+	 *
222
+	 * @param    string $title
223
+	 * @param    int    $id
224
+	 * @return    string
225
+	 */
226
+	public static function the_title($title = '', $id = 0)
227
+	{
228
+		global $post;
229
+		return ((function_exists('wp_is_block_theme') && wp_is_block_theme()) || in_the_loop()) && $post->ID === (int) $id
230
+			? espresso_event_status_banner($post->ID) . $title
231
+			: $title;
232
+	}
233
+
234
+
235
+	/**
236
+	 * get_the_excerpt
237
+	 * kinda hacky, but if a theme is using get_the_excerpt(),
238
+	 * then we need to remove our filters on the_content()
239
+	 *
240
+	 * @param        string $excerpt
241
+	 * @return        string
242
+	 */
243
+	public static function get_the_excerpt($excerpt = '')
244
+	{
245
+		EED_Event_Single::$using_get_the_excerpt = true;
246
+		add_filter('wp_trim_excerpt', array('EED_Event_Single', 'end_get_the_excerpt'), 999, 1);
247
+		return $excerpt;
248
+	}
249
+
250
+
251
+	/**
252
+	 * end_get_the_excerpt
253
+	 *
254
+	 * @param  string $text
255
+	 * @return string
256
+	 */
257
+	public static function end_get_the_excerpt($text = '')
258
+	{
259
+		EED_Event_Single::$using_get_the_excerpt = false;
260
+		return $text;
261
+	}
262
+
263
+
264
+	/**
265
+	 * event_details
266
+	 *
267
+	 * @param    string $content
268
+	 * @return    string
269
+	 */
270
+	public static function event_details($content)
271
+	{
272
+		global $post;
273
+		static $current_post_ID = 0;
274
+		if (
275
+			$current_post_ID !== $post->ID
276
+			&& $post->post_type === EspressoPostType::EVENTS
277
+			&& ! EED_Event_Single::$using_get_the_excerpt
278
+			&& ! post_password_required()
279
+		) {
280
+			// Set current post ID to prevent showing content twice, but only if headers have definitely been sent.
281
+			// Reason being is that some plugins, like Yoast, need to run through a copy of the loop early
282
+			// BEFORE headers are sent in order to examine the post content and generate content for the HTML header.
283
+			// We want to allow those plugins to still do their thing and have access to our content, but depending on
284
+			// how your event content is being displayed (shortcode, CPT route, etc), this filter can get applied twice,
285
+			// so the following allows this filter to be applied multiple times, but only once for real
286
+			$current_post_ID = did_action('loop_start') ? $post->ID : 0;
287
+			if (EE_Registry::instance()->CFG->template_settings->EED_Event_Single->use_sortable_display_order) {
288
+				// we need to first remove this callback from being applied to the_content()
289
+				// (otherwise it will recurse and blow up the interweb)
290
+				remove_filter(
291
+					'the_content',
292
+					array('EED_Event_Single', 'event_details'),
293
+					EED_Event_Single::EVENT_DETAILS_PRIORITY
294
+				);
295
+				EED_Event_Single::instance()->template_parts = EED_Event_Single::instance()->initialize_template_parts(
296
+				);
297
+				$content = EEH_Template::locate_template('content-espresso_events-details.php');
298
+				$content = EED_Event_Single::instance()->template_parts->apply_template_part_filters($content);
299
+				add_filter(
300
+					'the_content',
301
+					array('EED_Event_Single', 'event_details'),
302
+					EED_Event_Single::EVENT_DETAILS_PRIORITY
303
+				);
304
+			} else {
305
+				$content = EED_Event_Single::use_filterable_display_order();
306
+			}
307
+		}
308
+		return $content;
309
+	}
310
+
311
+
312
+	/**
313
+	 * use_filterable_display_order
314
+	 *
315
+	 * @return string
316
+	 */
317
+	protected static function use_filterable_display_order()
318
+	{
319
+		// since the 'content-espresso_events-details.php' template might be used directly from within a theme,
320
+		// it uses the_content() for displaying the $post->post_content
321
+		// so in order to load a template that uses the_content()
322
+		// from within a callback being used to filter the_content(),
323
+		// we need to first remove this callback from being applied to the_content()
324
+		// (otherwise it will recurse and blow up the interweb)
325
+		remove_filter(
326
+			'the_content',
327
+			array('EED_Event_Single', 'event_details'),
328
+			EED_Event_Single::EVENT_DETAILS_PRIORITY
329
+		);
330
+		// now add additional content
331
+		add_filter(
332
+			'the_content',
333
+			array('EED_Event_Single', 'event_datetimes'),
334
+			EED_Event_Single::EVENT_DATETIMES_PRIORITY,
335
+			1
336
+		);
337
+		add_filter(
338
+			'the_content',
339
+			array('EED_Event_Single', 'event_tickets'),
340
+			EED_Event_Single::EVENT_TICKETS_PRIORITY,
341
+			1
342
+		);
343
+		add_filter(
344
+			'the_content',
345
+			array('EED_Event_Single', 'event_venues'),
346
+			EED_Event_Single::EVENT_VENUES_PRIORITY,
347
+			1
348
+		);
349
+		do_action('AHEE__EED_Event_Single__use_filterable_display_order__after_add_filters');
350
+		// now load our template
351
+		$content = EEH_Template::locate_template('content-espresso_events-details.php');
352
+		// now add our filter back in, plus some others
353
+		add_filter(
354
+			'the_content',
355
+			array('EED_Event_Single', 'event_details'),
356
+			EED_Event_Single::EVENT_DETAILS_PRIORITY
357
+		);
358
+		remove_filter(
359
+			'the_content',
360
+			array('EED_Event_Single', 'event_datetimes'),
361
+			EED_Event_Single::EVENT_DATETIMES_PRIORITY
362
+		);
363
+		remove_filter(
364
+			'the_content',
365
+			array('EED_Event_Single', 'event_tickets'),
366
+			EED_Event_Single::EVENT_TICKETS_PRIORITY
367
+		);
368
+		remove_filter(
369
+			'the_content',
370
+			array('EED_Event_Single', 'event_venues'),
371
+			EED_Event_Single::EVENT_VENUES_PRIORITY
372
+		);
373
+		do_action('AHEE__EED_Event_Single__use_filterable_display_order__after_remove_filters');
374
+		// we're not returning the $content directly because the template we are loading uses the_content (or the_excerpt)
375
+		return $content;
376
+	}
377
+
378
+
379
+	/**
380
+	 * event_datetimes - adds datetimes ABOVE content
381
+	 *
382
+	 * @param        string $content
383
+	 * @return        string
384
+	 */
385
+	public static function event_datetimes($content)
386
+	{
387
+		return EEH_Template::locate_template('content-espresso_events-datetimes.php') . $content;
388
+	}
389
+
390
+
391
+	/**
392
+	 * event_tickets - adds tickets ABOVE content (which includes datetimes)
393
+	 *
394
+	 * @param        string $content
395
+	 * @return        string
396
+	 */
397
+	public static function event_tickets($content)
398
+	{
399
+		return EEH_Template::locate_template('content-espresso_events-tickets.php') . $content;
400
+	}
401
+
402
+
403
+	/**
404
+	 * event_venues
405
+	 *
406
+	 * @param    string $content
407
+	 * @return    string
408
+	 */
409
+	public static function event_venue($content)
410
+	{
411
+		return EED_Event_Single::event_venues($content);
412
+	}
413
+
414
+
415
+	/**
416
+	 * event_venues - adds venues BELOW content
417
+	 *
418
+	 * @param        string $content
419
+	 * @return        string
420
+	 */
421
+	public static function event_venues($content)
422
+	{
423
+		return $content . EEH_Template::locate_template('content-espresso_events-venues.php');
424
+	}
425
+
426
+
427
+	/**
428
+	 * loop_end
429
+	 *
430
+	 * @param        array $wp_query_array an array containing the WP_Query object
431
+	 * @return        void
432
+	 */
433
+	public static function loop_end($wp_query_array)
434
+	{
435
+		global $post;
436
+		do_action('AHEE_event_details_after_post', $post, $wp_query_array);
437
+	}
438
+
439
+
440
+	/**
441
+	 * wp_enqueue_scripts
442
+	 *
443
+	 * @return    void
444
+	 */
445
+	public function wp_enqueue_scripts()
446
+	{
447
+		// get some style
448
+		if (
449
+			apply_filters('FHEE_enable_default_espresso_css', true)
450
+			&& apply_filters('FHEE__EED_Event_Single__wp_enqueue_scripts__enable_css', true)
451
+		) {
452
+			// first check uploads folder
453
+			if (is_readable(get_stylesheet_directory() . $this->theme . '/style.css')) {
454
+				wp_register_style(
455
+					$this->theme,
456
+					get_stylesheet_directory_uri() . $this->theme . '/style.css',
457
+					array('dashicons', 'espresso_default')
458
+				);
459
+			} else {
460
+				wp_register_style(
461
+					$this->theme,
462
+					EE_TEMPLATES_URL . $this->theme . '/style.css',
463
+					array('dashicons', 'espresso_default')
464
+				);
465
+			}
466
+			wp_enqueue_script($this->theme);
467
+			if (EE_Registry::instance()->CFG->map_settings->use_google_maps) {
468
+				add_action('wp_enqueue_scripts', array('EEH_Maps', 'espresso_google_map_js'), 11);
469
+			}
470
+		}
471
+	}
472
+
473
+
474
+	/**
475
+	 * display_venue
476
+	 *
477
+	 * @return    bool
478
+	 */
479
+	public static function display_venue()
480
+	{
481
+		/** @type EE_Event_Single_Config $config */
482
+		$config = EED_Event_Single::instance()->config();
483
+		$display_venue = $config->display_venue === null ? true : $config->display_venue;
484
+		$venue_name = EEH_Venue_View::venue_name();
485
+		return $display_venue && ! empty($venue_name);
486
+	}
487 487
 }
488 488
 
489 489
 
@@ -495,5 +495,5 @@  discard block
 block discarded – undo
495 495
  */
496 496
 function espresso_display_venue_in_event_details()
497 497
 {
498
-    return EED_Event_Single::display_venue();
498
+	return EED_Event_Single::display_venue();
499 499
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -58,7 +58,7 @@  discard block
 block discarded – undo
58 58
         );
59 59
         $custom_post_types = $custom_post_type_definitions->getDefinitions();
60 60
         EED_Module::registerRoute(
61
-            $custom_post_types[ EspressoPostType::EVENTS ]['singular_slug'],
61
+            $custom_post_types[EspressoPostType::EVENTS]['singular_slug'],
62 62
             'Event_Single',
63 63
             'run'
64 64
         );
@@ -83,8 +83,8 @@  discard block
 block discarded – undo
83 83
      */
84 84
     public static function set_definitions()
85 85
     {
86
-        define('EVENT_SINGLE_ASSETS_URL', plugin_dir_url(__FILE__) . 'assets/');
87
-        define('EVENT_SINGLE_TEMPLATES_PATH', plugin_dir_path(__FILE__) . 'templates/');
86
+        define('EVENT_SINGLE_ASSETS_URL', plugin_dir_url(__FILE__).'assets/');
87
+        define('EVENT_SINGLE_TEMPLATES_PATH', plugin_dir_path(__FILE__).'templates/');
88 88
     }
89 89
 
90 90
 
@@ -208,7 +208,7 @@  discard block
 block discarded – undo
208 208
      */
209 209
     public static function loop_start(WP_Query $query)
210 210
     {
211
-        if (! $query->is_main_query()) {
211
+        if ( ! $query->is_main_query()) {
212 212
             return;
213 213
         }
214 214
         global $post;
@@ -227,7 +227,7 @@  discard block
 block discarded – undo
227 227
     {
228 228
         global $post;
229 229
         return ((function_exists('wp_is_block_theme') && wp_is_block_theme()) || in_the_loop()) && $post->ID === (int) $id
230
-            ? espresso_event_status_banner($post->ID) . $title
230
+            ? espresso_event_status_banner($post->ID).$title
231 231
             : $title;
232 232
     }
233 233
 
@@ -384,7 +384,7 @@  discard block
 block discarded – undo
384 384
      */
385 385
     public static function event_datetimes($content)
386 386
     {
387
-        return EEH_Template::locate_template('content-espresso_events-datetimes.php') . $content;
387
+        return EEH_Template::locate_template('content-espresso_events-datetimes.php').$content;
388 388
     }
389 389
 
390 390
 
@@ -396,7 +396,7 @@  discard block
 block discarded – undo
396 396
      */
397 397
     public static function event_tickets($content)
398 398
     {
399
-        return EEH_Template::locate_template('content-espresso_events-tickets.php') . $content;
399
+        return EEH_Template::locate_template('content-espresso_events-tickets.php').$content;
400 400
     }
401 401
 
402 402
 
@@ -420,7 +420,7 @@  discard block
 block discarded – undo
420 420
      */
421 421
     public static function event_venues($content)
422 422
     {
423
-        return $content . EEH_Template::locate_template('content-espresso_events-venues.php');
423
+        return $content.EEH_Template::locate_template('content-espresso_events-venues.php');
424 424
     }
425 425
 
426 426
 
@@ -450,16 +450,16 @@  discard block
 block discarded – undo
450 450
             && apply_filters('FHEE__EED_Event_Single__wp_enqueue_scripts__enable_css', true)
451 451
         ) {
452 452
             // first check uploads folder
453
-            if (is_readable(get_stylesheet_directory() . $this->theme . '/style.css')) {
453
+            if (is_readable(get_stylesheet_directory().$this->theme.'/style.css')) {
454 454
                 wp_register_style(
455 455
                     $this->theme,
456
-                    get_stylesheet_directory_uri() . $this->theme . '/style.css',
456
+                    get_stylesheet_directory_uri().$this->theme.'/style.css',
457 457
                     array('dashicons', 'espresso_default')
458 458
                 );
459 459
             } else {
460 460
                 wp_register_style(
461 461
                     $this->theme,
462
-                    EE_TEMPLATES_URL . $this->theme . '/style.css',
462
+                    EE_TEMPLATES_URL.$this->theme.'/style.css',
463 463
                     array('dashicons', 'espresso_default')
464 464
                 );
465 465
             }
Please login to merge, or discard this patch.
modules/single_page_checkout/EED_Single_Page_Checkout.module.php 2 patches
Indentation   +1679 added lines, -1679 removed lines patch added patch discarded remove patch
@@ -24,1683 +24,1683 @@
 block discarded – undo
24 24
  */
25 25
 class EED_Single_Page_Checkout extends EED_Module
26 26
 {
27
-    /**
28
-     * $_initialized - has the SPCO controller already been initialized ?
29
-     */
30
-    private static bool $_initialized = false;
31
-
32
-
33
-    /**
34
-     * $_checkout_verified - is the EE_Checkout verified as correct for this request ?
35
-     */
36
-    private static bool $_checkout_verified = true;
37
-
38
-    /**
39
-     * $_reg_steps_array - holds initial array of reg steps
40
-     *
41
-     * @var array $_reg_steps_array
42
-     */
43
-    private static array $_reg_steps_array = [];
44
-
45
-    /**
46
-     * $checkout - EE_Checkout object for handling the properties of the current checkout process
47
-     */
48
-    public ?EE_Checkout $checkout = null;
49
-
50
-    protected ?RequestInterface $request = null;
51
-
52
-    public ?EE_Session $session = null;
53
-
54
-    private bool $debug = false;    //  true    false
55
-
56
-
57
-    /**
58
-     * @return EED_Single_Page_Checkout|EED_Module
59
-     * @throws EE_Error
60
-     * @throws ReflectionException
61
-     */
62
-    public static function instance()
63
-    {
64
-        add_filter('EED_Single_Page_Checkout__SPCO_active', '__return_true');
65
-        return parent::get_instance(__CLASS__);
66
-    }
67
-
68
-
69
-    /**
70
-     * @return EE_CART
71
-     */
72
-    public function cart(): EE_CART
73
-    {
74
-        return $this->checkout->cart;
75
-    }
76
-
77
-
78
-    /**
79
-     * @return RequestInterface
80
-     * @since   4.10.14.p
81
-     */
82
-    public static function getRequest(): RequestInterface
83
-    {
84
-        return LoaderFactory::getLoader()->getShared(RequestInterface::class);
85
-    }
86
-
87
-
88
-    /**
89
-     * @return EE_Transaction
90
-     */
91
-    public function transaction(): EE_Transaction
92
-    {
93
-        return $this->checkout->transaction;
94
-    }
95
-
96
-
97
-    /**
98
-     *    set_hooks - for hooking into EE Core, other modules, etc
99
-     *
100
-     * @return    void
101
-     * @throws EE_Error
102
-     */
103
-    public static function set_hooks()
104
-    {
105
-        EED_Single_Page_Checkout::set_definitions();
106
-    }
107
-
108
-
109
-    /**
110
-     *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
111
-     *
112
-     * @return    void
113
-     * @throws EE_Error
114
-     */
115
-    public static function set_hooks_admin()
116
-    {
117
-        EED_Single_Page_Checkout::set_definitions();
118
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
119
-            return;
120
-        }
121
-        // going to start an output buffer in case anything gets accidentally output
122
-        // that might disrupt our JSON response
123
-        ob_start();
124
-        EED_Single_Page_Checkout::load_reg_steps();
125
-        // set ajax hooks
126
-        add_action('wp_ajax_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
127
-        add_action('wp_ajax_nopriv_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
128
-        add_action('wp_ajax_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
129
-        add_action('wp_ajax_nopriv_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
130
-        add_action('wp_ajax_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
131
-        add_action('wp_ajax_nopriv_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
132
-    }
133
-
134
-
135
-    /**
136
-     *    process ajax request
137
-     *
138
-     * @param string $ajax_action
139
-     * @throws EE_Error
140
-     * @throws ReflectionException
141
-     */
142
-    public static function process_ajax_request(string $ajax_action)
143
-    {
144
-        $request = EED_Single_Page_Checkout::getRequest();
145
-        $request->setRequestParam('action', $ajax_action);
146
-        EED_Single_Page_Checkout::instance()->_initialize();
147
-    }
148
-
149
-
150
-    /**
151
-     * ajax display registration step
152
-     *
153
-     * @throws EE_Error
154
-     * @throws ReflectionException
155
-     */
156
-    public static function display_reg_step()
157
-    {
158
-        EED_Single_Page_Checkout::process_ajax_request('display_spco_reg_step');
159
-    }
160
-
161
-
162
-    /**
163
-     * ajax process registration step
164
-     *
165
-     * @throws EE_Error
166
-     * @throws ReflectionException
167
-     */
168
-    public static function process_reg_step()
169
-    {
170
-        EED_Single_Page_Checkout::process_ajax_request('process_reg_step');
171
-    }
172
-
173
-
174
-    /**
175
-     * ajax process registration step
176
-     *
177
-     * @throws EE_Error
178
-     * @throws ReflectionException
179
-     */
180
-    public static function update_reg_step()
181
-    {
182
-        EED_Single_Page_Checkout::process_ajax_request('update_reg_step');
183
-    }
184
-
185
-
186
-    /**
187
-     * update_checkout
188
-     *
189
-     * @return void
190
-     * @throws ReflectionException
191
-     * @throws EE_Error
192
-     */
193
-    public static function update_checkout()
194
-    {
195
-        EED_Single_Page_Checkout::process_ajax_request('update_checkout');
196
-    }
197
-
198
-
199
-    /**
200
-     *    set_definitions
201
-     *
202
-     * @return    void
203
-     * @throws EE_Error
204
-     */
205
-    public static function set_definitions()
206
-    {
207
-        if (defined('SPCO_BASE_PATH')) {
208
-            return;
209
-        }
210
-        define(
211
-            'SPCO_BASE_PATH',
212
-            rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/') . '/'
213
-        );
214
-        define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
215
-        define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
216
-        define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
217
-        define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
218
-        define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
219
-        define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
220
-        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
221
-        EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
222
-            EED_Single_Page_Checkout::getRegistrationExpirationNotice();
223
-    }
224
-
225
-
226
-    /**
227
-     * load_reg_steps
228
-     * loads and instantiates each reg step based on the EE_Registry::instance()->CFG->registration->reg_steps array
229
-     *
230
-     * @throws EE_Error
231
-     */
232
-    public static function load_reg_steps()
233
-    {
234
-        static $reg_steps_loaded = false;
235
-        if ($reg_steps_loaded) {
236
-            return;
237
-        }
238
-        // filter list of reg_steps
239
-        $reg_steps_to_load = (array) apply_filters(
240
-            'AHEE__SPCO__load_reg_steps__reg_steps_to_load',
241
-            EED_Single_Page_Checkout::get_reg_steps()
242
-        );
243
-        // sort by key (order)
244
-        ksort($reg_steps_to_load);
245
-        // loop through folders
246
-        foreach ($reg_steps_to_load as $order => $reg_step) {
247
-            // we need a
248
-            if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
249
-                // copy over to the reg_steps_array
250
-                EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
251
-                // register custom key route for each reg step
252
-                // ie: step=>"slug" - this is the entire reason we load the reg steps array now
253
-                EED_Module::registerRoute(
254
-                    $reg_step['slug'],
255
-                    'EED_Single_Page_Checkout',
256
-                    'run',
257
-                    'step'
258
-                );
259
-                // add AJAX or other hooks
260
-                if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
261
-                    // setup autoloaders if necessary
262
-                    if (! class_exists($reg_step['class_name'])) {
263
-                        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
264
-                            $reg_step['file_path'],
265
-                            true
266
-                        );
267
-                    }
268
-                    if (is_callable($reg_step['class_name'], 'set_hooks')) {
269
-                        call_user_func([$reg_step['class_name'], 'set_hooks']);
270
-                    }
271
-                }
272
-            }
273
-        }
274
-        $reg_steps_loaded = true;
275
-    }
276
-
277
-
278
-    /**
279
-     *    get_reg_steps
280
-     *
281
-     * @return    array
282
-     */
283
-    public static function get_reg_steps(): array
284
-    {
285
-        $reg_steps = EE_Registry::instance()->CFG->registration->reg_steps;
286
-        if (empty($reg_steps)) {
287
-            $reg_steps = [
288
-                10  => [
289
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
290
-                    'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
291
-                    'slug'       => 'attendee_information',
292
-                    'has_hooks'  => false,
293
-                ],
294
-                30  => [
295
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
296
-                    'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
297
-                    'slug'       => 'payment_options',
298
-                    'has_hooks'  => true,
299
-                ],
300
-                999 => [
301
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
302
-                    'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
303
-                    'slug'       => 'finalize_registration',
304
-                    'has_hooks'  => false,
305
-                ],
306
-            ];
307
-        }
308
-        return $reg_steps;
309
-    }
310
-
311
-
312
-    /**
313
-     * @return array|string
314
-     * @throws EE_Error
315
-     * @throws ReflectionException
316
-     */
317
-    public static function registration_checkout_for_admin()
318
-    {
319
-        $request = EED_Single_Page_Checkout::getRequest();
320
-        $request->setRequestParam('step', 'attendee_information');
321
-        $request->setRequestParam('action', 'display_spco_reg_step');
322
-        $request->setRequestParam('process_form_submission', false);
323
-        EED_Single_Page_Checkout::instance()->_initialize();
324
-        EED_Single_Page_Checkout::instance()->_display_spco_reg_form();
325
-        return EED_Single_Page_Checkout::getResponse()->getOutput();
326
-    }
327
-
328
-
329
-    /**
330
-     * @return EE_Transaction|null
331
-     * @throws EE_Error
332
-     * @throws ReflectionException
333
-     */
334
-    public static function process_registration_from_admin(): ?EE_Transaction
335
-    {
336
-        $request = EED_Single_Page_Checkout::getRequest();
337
-        $request->setRequestParam('step', 'attendee_information');
338
-        $request->setRequestParam('action', 'process_reg_step');
339
-        $request->setRequestParam('process_form_submission', true);
340
-        EED_Single_Page_Checkout::instance()->_initialize();
341
-        if (EED_Single_Page_Checkout::instance()->checkout->current_step->completed()) {
342
-            $final_reg_step = end(EED_Single_Page_Checkout::instance()->checkout->reg_steps);
343
-            if ($final_reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
344
-                EED_Single_Page_Checkout::instance()->checkout->set_reg_step_initiated($final_reg_step);
345
-                if ($final_reg_step->process_reg_step()) {
346
-                    $final_reg_step->set_completed();
347
-                    EED_Single_Page_Checkout::instance()->checkout->update_txn_reg_steps_array();
348
-                    return EED_Single_Page_Checkout::instance()->checkout->transaction;
349
-                }
350
-            }
351
-        }
352
-        return null;
353
-    }
354
-
355
-
356
-    /**
357
-     *    run
358
-     *
359
-     * @param WP_Query|null $WP_Query
360
-     * @return    void
361
-     */
362
-    public function run($WP_Query)
363
-    {
364
-        if (
365
-            $WP_Query instanceof WP_Query
366
-            && $WP_Query->is_main_query()
367
-            && apply_filters('FHEE__EED_Single_Page_Checkout__run', true)
368
-            && $this->_is_reg_checkout()
369
-        ) {
370
-            $this->_initialize();
371
-        }
372
-    }
373
-
374
-
375
-    /**
376
-     * determines whether current url matches reg page url
377
-     *
378
-     * @return bool
379
-     */
380
-    protected function _is_reg_checkout(): bool
381
-    {
382
-        // get current permalink for reg page without any extra query args
383
-        $reg_page_url = get_permalink(EE_Config::instance()->core->reg_page_id);
384
-        // get request URI for current request, but without the scheme or host
385
-        $current_request_uri = EEH_URL::filter_input_server_url();
386
-        $current_request_uri = html_entity_decode($current_request_uri);
387
-        // get array of query args from the current request URI
388
-        $query_args = EEH_URL::get_query_string($current_request_uri);
389
-        // grab page id if it is set
390
-        $page_id = isset($query_args['page_id']) ? absint($query_args['page_id']) : 0;
391
-        // and remove the page id from the query args (we will re-add it later)
392
-        unset($query_args['page_id']);
393
-        // now strip all query args from current request URI
394
-        $current_request_uri = remove_query_arg(array_keys($query_args), $current_request_uri);
395
-        // and re-add the page id if it was set
396
-        if ($page_id) {
397
-            $current_request_uri = add_query_arg('page_id', $page_id, $current_request_uri);
398
-        }
399
-        // remove slashes and ?
400
-        $current_request_uri = trim($current_request_uri, '?/');
401
-        // is current request URI part of the known full reg page URL ?
402
-        return ! empty($current_request_uri) && strpos($reg_page_url, $current_request_uri) !== false;
403
-    }
404
-
405
-
406
-    /**
407
-     * @param WP_Query $wp_query
408
-     * @return    void
409
-     * @throws EE_Error
410
-     * @throws ReflectionException
411
-     */
412
-    public static function init(WP_Query $wp_query)
413
-    {
414
-        EED_Single_Page_Checkout::instance()->run($wp_query);
415
-    }
416
-
417
-
418
-    /**
419
-     * @return void
420
-     */
421
-    private function _initialize()
422
-    {
423
-        // ensure SPCO doesn't run twice
424
-        if (EED_Single_Page_Checkout::$_initialized) {
425
-            return;
426
-        }
427
-        try {
428
-            $this->request = EED_Single_Page_Checkout::getRequest();
429
-            EED_Single_Page_Checkout::load_reg_steps();
430
-            $this->_verify_session();
431
-            // set up the EE_Checkout object
432
-            $this->checkout = $this->_initialize_checkout();
433
-            // filter checkout
434
-            $this->checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize__checkout', $this->checkout);
435
-            // get the $_GET
436
-            $this->_get_request_vars();
437
-            if ($this->_block_bots()) {
438
-                return;
439
-            }
440
-            // filter continue_reg
441
-            $this->checkout->continue_reg = apply_filters(
442
-                'FHEE__EED_Single_Page_Checkout__init___continue_reg',
443
-                true,
444
-                $this->checkout
445
-            );
446
-            // load the reg steps array
447
-            if (! $this->_load_and_instantiate_reg_steps()) {
448
-                EED_Single_Page_Checkout::$_initialized = true;
449
-                return;
450
-            }
451
-            // set the current step
452
-            $this->checkout->set_current_step($this->checkout->step);
453
-            // and the next step
454
-            $this->checkout->set_next_step();
455
-            // verify that everything has been set up correctly
456
-            if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
457
-                EED_Single_Page_Checkout::$_initialized = true;
458
-                return;
459
-            }
460
-            do_action('AHEE__Single_Page_Checkout___initialize__after_final_verifications', $this->checkout);
461
-            $this->lockTransaction();
462
-            // make sure all of our cached objects are added to their respective model entity mappers
463
-            $this->checkout->refresh_all_entities();
464
-            // set amount owing
465
-            $this->checkout->amount_owing = $this->checkout->transaction->remaining();
466
-            // initialize each reg step, which gives them the chance to potentially alter the process
467
-            $this->_initialize_reg_steps();
468
-            // DEBUG LOG
469
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
470
-            // get reg form
471
-            if (! $this->_check_form_submission()) {
472
-                EED_Single_Page_Checkout::$_initialized = true;
473
-                return;
474
-            }
475
-            // checkout the action!!!
476
-            $this->_process_form_action();
477
-            // add some style and make it dance
478
-            $this->add_styles_and_scripts($this);
479
-            // kk... SPCO has successfully run
480
-            EED_Single_Page_Checkout::$_initialized = true;
481
-            // set no cache headers and constants
482
-            EE_System::do_not_cache();
483
-            // add anchor
484
-            add_action('loop_start', [$this, 'set_checkout_anchor'], 1);
485
-        } catch (Exception $e) {
486
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
487
-        }
488
-    }
489
-
490
-
491
-    /**
492
-     * checks that the session is valid and not expired
493
-     *
494
-     * @throws EE_Error
495
-     * @throws ReflectionException
496
-     */
497
-    private function _verify_session()
498
-    {
499
-        if (! $this->session instanceof EE_Session) {
500
-            $this->session = LoaderFactory::getShared(EE_Session::class);
501
-            if (! $this->session instanceof EE_Session) {
502
-                throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
503
-            }
504
-        }
505
-        $clear_session_requested = $this->request->getRequestParam('clear_session', false, 'bool');
506
-        // is session still valid ?
507
-        if (
508
-            $clear_session_requested
509
-            || (
510
-                $this->session->expired()
511
-                && $this->request->getRequestParam('e_reg_url_link') === ''
512
-            )
513
-        ) {
514
-            $this->checkout = new EE_Checkout();
515
-            $this->session->clear_session(__CLASS__, __FUNCTION__);
516
-            if (! $clear_session_requested) {
517
-                EE_Error::add_attention(
518
-                    EE_Registry::$i18n_js_strings['registration_expiration_notice'],
519
-                    __FILE__,
520
-                    __FUNCTION__,
521
-                    __LINE__
522
-                );
523
-            }
524
-        }
525
-    }
526
-
527
-
528
-    /**
529
-     * loads and instantiates EE_Checkout
530
-     *
531
-     * @return EE_Checkout
532
-     * @throws EE_Error
533
-     * @throws ReflectionException
534
-     */
535
-    private function _initialize_checkout(): EE_Checkout
536
-    {
537
-        // look in session for existing checkout
538
-        /** @type EE_Checkout $checkout */
539
-        $checkout = $this->session->checkout();
540
-        // verify
541
-        if (! $checkout instanceof EE_Checkout) {
542
-            // instantiate EE_Checkout object for handling the properties of the current checkout process
543
-            $checkout = EE_Registry::instance()->load_file(
544
-                SPCO_INC_PATH,
545
-                'EE_Checkout',
546
-                'class',
547
-                [],
548
-                false
549
-            );
550
-        } else {
551
-            if ($checkout->current_step->is_final_step() && $checkout->exit_spco() === true) {
552
-                $this->unlock_transaction();
553
-                wp_safe_redirect($checkout->redirect_url);
554
-                exit();
555
-            }
556
-        }
557
-        $checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
558
-        // verify again
559
-        if (! $checkout instanceof EE_Checkout) {
560
-            throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
561
-        }
562
-        // reset anything that needs a clean slate for each request
563
-        $checkout->reset_for_current_request();
564
-        return $checkout;
565
-    }
566
-
567
-
568
-    /**
569
-     * @return void
570
-     */
571
-    private function _get_request_vars()
572
-    {
573
-        // make sure this request is marked as belonging to EE
574
-        /** @var CurrentPage $current_page */
575
-        $current_page = LoaderFactory::getLoader()->getShared(CurrentPage::class);
576
-        $current_page->setEspressoPage(true);
577
-        // which step is being requested ?
578
-        $this->checkout->step = $this->request->getRequestParam('step', $this->_get_first_step());
579
-        // which step is being edited ?
580
-        $this->checkout->edit_step = $this->request->getRequestParam('edit_step');
581
-        // and what we're doing on the current step
582
-        $this->checkout->action = $this->request->getRequestParam('action', 'display_spco_reg_step');
583
-        // timestamp
584
-        $this->checkout->uts = $this->request->getRequestParam('uts', 0, 'int');
585
-        // returning to edit ?
586
-        $this->checkout->reg_url_link = $this->request->getRequestParam('e_reg_url_link');
587
-        // add reg url link to registration query params
588
-        if ($this->checkout->reg_url_link && strpos($this->checkout->reg_url_link, '1-') !== 0) {
589
-            $this->checkout->reg_cache_where_params[0]['REG_url_link'] = $this->checkout->reg_url_link;
590
-        }
591
-        // or some other kind of revisit ?
592
-        $this->checkout->revisit = $this->request->getRequestParam('revisit', false, 'bool');
593
-        // and whether to generate a reg form for this request
594
-        $this->checkout->generate_reg_form = $this->request->getRequestParam('generate_reg_form', true, 'bool');
595
-        // and whether to process a reg form submission for this request
596
-        $this->checkout->process_form_submission = $this->request->getRequestParam(
597
-            'process_form_submission',
598
-            $this->checkout->action === 'process_reg_step',
599
-            'bool'
600
-        );
601
-        $this->checkout->process_form_submission = filter_var(
602
-            $this->checkout->action !== 'display_spco_reg_step'
603
-                ? $this->checkout->process_form_submission
604
-                : false,
605
-            FILTER_VALIDATE_BOOLEAN
606
-        );
607
-        $this->_display_request_vars();
608
-    }
609
-
610
-
611
-    /**
612
-     * @return void
613
-     */
614
-    protected function _display_request_vars()
615
-    {
616
-        if (! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
617
-            return;
618
-        }
619
-        EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
620
-        EEH_Debug_Tools::printr($this->checkout->step, '$this->checkout->step', __FILE__, __LINE__);
621
-        EEH_Debug_Tools::printr($this->checkout->edit_step, '$this->checkout->edit_step', __FILE__, __LINE__);
622
-        EEH_Debug_Tools::printr($this->checkout->action, '$this->checkout->action', __FILE__, __LINE__);
623
-        EEH_Debug_Tools::printr($this->checkout->reg_url_link, '$this->checkout->reg_url_link', __FILE__, __LINE__);
624
-        EEH_Debug_Tools::printr($this->checkout->revisit, '$this->checkout->revisit', __FILE__, __LINE__);
625
-        EEH_Debug_Tools::printr(
626
-            $this->checkout->generate_reg_form,
627
-            '$this->checkout->generate_reg_form',
628
-            __FILE__,
629
-            __LINE__
630
-        );
631
-        EEH_Debug_Tools::printr(
632
-            $this->checkout->process_form_submission,
633
-            '$this->checkout->process_form_submission',
634
-            __FILE__,
635
-            __LINE__
636
-        );
637
-    }
638
-
639
-
640
-    /**
641
-     * _block_bots
642
-     * checks that the incoming request has either of the following set:
643
-     *  a UTS (unix timestamp) which indicates that the request was redirected from the Ticket Selector
644
-     *  a REG URL Link, which indicates that the request is a return visit to SPCO for a valid TXN
645
-     * so if you're not coming from the Ticket Selector nor returning for a valid IP...
646
-     * then where you coming from man?
647
-     *
648
-     * @return boolean
649
-     */
650
-    private function _block_bots(): bool
651
-    {
652
-        return EED_Invalid_Checkout_Access::getInvalidCheckoutAccess()->checkoutAccessIsInvalid($this->checkout);
653
-    }
654
-
655
-
656
-    /**
657
-     *  gets slug for first step in $_reg_steps_array
658
-     *
659
-     * @return string
660
-     */
661
-    private function _get_first_step(): string
662
-    {
663
-        $first_step = reset(EED_Single_Page_Checkout::$_reg_steps_array);
664
-        return $first_step['slug'] ?? 'attendee_information';
665
-    }
666
-
667
-
668
-    /**
669
-     * instantiates each reg step based on the loaded reg_steps array
670
-     *
671
-     * @return bool
672
-     * @throws EE_Error
673
-     * @throws InvalidArgumentException
674
-     * @throws InvalidDataTypeException
675
-     * @throws InvalidInterfaceException
676
-     * @throws ReflectionException
677
-     */
678
-    private function _load_and_instantiate_reg_steps(): bool
679
-    {
680
-        do_action('AHEE__Single_Page_Checkout___load_and_instantiate_reg_steps__start', $this->checkout);
681
-        // have reg_steps already been instantiated ?
682
-        if (
683
-            empty($this->checkout->reg_steps)
684
-            || apply_filters('FHEE__Single_Page_Checkout__load_reg_steps__reload_reg_steps', false, $this->checkout)
685
-        ) {
686
-            // if not, then loop through raw reg steps array
687
-            foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
688
-                if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
689
-                    return false;
690
-                }
691
-            }
692
-            if (isset($this->checkout->reg_steps['registration_confirmation'])) {
693
-                // skip the registration_confirmation page ?
694
-                // just remove it from the reg steps array
695
-                $this->checkout->remove_reg_step('registration_confirmation', false);
696
-            }
697
-            // filter the array for good luck
698
-            $this->checkout->reg_steps = apply_filters(
699
-                'FHEE__Single_Page_Checkout__load_reg_steps__reg_steps',
700
-                $this->checkout->reg_steps
701
-            );
702
-            // finally re-sort based on the reg step class order properties
703
-            $this->checkout->sort_reg_steps();
704
-        } else {
705
-            foreach ($this->checkout->reg_steps as $reg_step) {
706
-                // set all current step stati to FALSE
707
-                $reg_step->set_is_current_step(false);
708
-            }
709
-        }
710
-        if (empty($this->checkout->reg_steps)) {
711
-            EE_Error::add_error(
712
-                esc_html__('No Reg Steps were loaded..', 'event_espresso'),
713
-                __FILE__,
714
-                __FUNCTION__,
715
-                __LINE__
716
-            );
717
-            return false;
718
-        }
719
-        // make reg step details available to JS
720
-        $this->checkout->set_reg_step_JSON_info();
721
-        return true;
722
-    }
723
-
724
-
725
-    /**
726
-     * @param array $reg_step
727
-     * @param int   $order
728
-     * @return bool
729
-     * @throws EE_Error
730
-     * @throws ReflectionException
731
-     */
732
-    private function _load_and_instantiate_reg_step(array $reg_step = [], int $order = 0): bool
733
-    {
734
-        // we need a file_path, class_name, and slug to add a reg step
735
-        if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
736
-            // if editing a specific step, but this is NOT that step... (and it's not the 'finalize_registration' step)
737
-            if (
738
-                $this->checkout->reg_url_link
739
-                && $this->checkout->step !== $reg_step['slug']
740
-                && $reg_step['slug'] !== 'finalize_registration'
741
-                // normally at this point we would NOT load the reg step, but this filter can change that
742
-                && apply_filters(
743
-                    'FHEE__Single_Page_Checkout___load_and_instantiate_reg_step__bypass_reg_step',
744
-                    true,
745
-                    $reg_step,
746
-                    $this->checkout
747
-                )
748
-            ) {
749
-                return true;
750
-            }
751
-
752
-            // instantiate step class using file path and class name
753
-            $reg_step_obj = EE_Registry::instance()->load_file(
754
-                $reg_step['file_path'],
755
-                $reg_step['class_name'],
756
-                'class',
757
-                [$this->checkout],
758
-                false
759
-            );
760
-            // did we get the goods ?
761
-            if ($reg_step_obj instanceof EE_SPCO_Reg_Step) {
762
-                // set reg step order based on config
763
-                $reg_step_obj->set_order($order);
764
-                // add instantiated reg step object to the master reg steps array
765
-                $this->checkout->add_reg_step($reg_step_obj);
766
-            } else {
767
-                EE_Error::add_error(
768
-                    esc_html__('The current step could not be set.', 'event_espresso'),
769
-                    __FILE__,
770
-                    __FUNCTION__,
771
-                    __LINE__
772
-                );
773
-                return false;
774
-            }
775
-        } else {
776
-            if (WP_DEBUG) {
777
-                EE_Error::add_error(
778
-                    sprintf(
779
-                        esc_html__(
780
-                            'A registration step could not be loaded. One or more of the following data points is invalid:%4$s%5$sFile Path: %1$s%6$s%5$sClass Name: %2$s%6$s%5$sSlug: %3$s%6$s%7$s',
781
-                            'event_espresso'
782
-                        ),
783
-                        $reg_step['file_path'] ?? '',
784
-                        $reg_step['class_name'] ?? '',
785
-                        $reg_step['slug'] ?? '',
786
-                        '<ul>',
787
-                        '<li>',
788
-                        '</li>',
789
-                        '</ul>'
790
-                    ),
791
-                    __FILE__,
792
-                    __FUNCTION__,
793
-                    __LINE__
794
-                );
795
-            }
796
-            return false;
797
-        }
798
-        return true;
799
-    }
800
-
801
-
802
-    /**
803
-     * @return bool
804
-     * @throws EE_Error
805
-     * @throws ReflectionException
806
-     */
807
-    private function _verify_transaction_and_get_registrations(): bool
808
-    {
809
-        // was there already a valid transaction in the checkout from the session ?
810
-        if (! $this->checkout->transaction instanceof EE_Transaction) {
811
-            // get transaction from db or session
812
-            $this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
813
-                ? $this->_get_transaction_and_cart_for_previous_visit()
814
-                : $this->_get_cart_for_current_session_and_setup_new_transaction();
815
-            if (! $this->checkout->transaction instanceof EE_Transaction) {
816
-                EE_Error::add_error(
817
-                    esc_html__(
818
-                        'Your Registration and Transaction information could not be retrieved from the db.',
819
-                        'event_espresso'
820
-                    ),
821
-                    __FILE__,
822
-                    __FUNCTION__,
823
-                    __LINE__
824
-                );
825
-                $this->checkout->transaction = EE_Transaction::new_instance();
826
-                // add some style and make it dance
827
-                $this->add_styles_and_scripts($this);
828
-                EED_Single_Page_Checkout::$_initialized = true;
829
-                return false;
830
-            }
831
-            // and the registrations for the transaction
832
-            $this->_get_registrations($this->checkout->transaction);
833
-        }
834
-        return true;
835
-    }
836
-
837
-
838
-    /**
839
-     * @return EE_Transaction|null
840
-     * @throws EE_Error
841
-     * @throws ReflectionException
842
-     */
843
-    private function _get_transaction_and_cart_for_previous_visit(): ?EE_Transaction
844
-    {
845
-        /** @var $TXN_model EEM_Transaction */
846
-        $TXN_model = EE_Registry::instance()->load_model('Transaction');
847
-        // because the reg_url_link is present in the request,
848
-        // this is a return visit to SPCO, so we'll get the transaction data from the db
849
-        $transaction = $TXN_model->get_transaction_from_reg_url_link($this->checkout->reg_url_link);
850
-        // verify transaction
851
-        if ($transaction instanceof EE_Transaction) {
852
-            // and get the cart that was used for that transaction
853
-            $this->checkout->cart = $this->_get_cart_for_transaction($transaction);
854
-            return $transaction;
855
-        }
856
-        EE_Error::add_error(
857
-            esc_html__(
858
-                'Your Registration and Transaction information could not be retrieved from the db.',
859
-                'event_espresso'
860
-            ),
861
-            __FILE__,
862
-            __FUNCTION__,
863
-            __LINE__
864
-        );
865
-        return null;
866
-    }
867
-
868
-
869
-    /**
870
-     * @param EE_Transaction|null $transaction
871
-     * @return EE_Cart
872
-     */
873
-    private function _get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
874
-    {
875
-        return $this->checkout->get_cart_for_transaction($transaction);
876
-    }
877
-
878
-
879
-    /**
880
-     * @param EE_Transaction|null $transaction
881
-     * @return EE_Cart
882
-     */
883
-    public function get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
884
-    {
885
-        return $this->checkout->get_cart_for_transaction($transaction);
886
-    }
887
-
888
-
889
-    /**
890
-     * generates a new EE_Transaction object and adds it to the $_transaction property.
891
-     *
892
-     * @return EE_Transaction|null
893
-     * @throws EE_Error
894
-     * @throws ReflectionException
895
-     */
896
-    private function _get_cart_for_current_session_and_setup_new_transaction(): ?EE_Transaction
897
-    {
898
-        //  if there's no transaction, then this is the FIRST visit to SPCO
899
-        // so load up the cart ( passing nothing for the TXN because it doesn't exist yet )
900
-        $this->checkout->cart = $this->_get_cart_for_transaction(null);
901
-        // and then create a new transaction
902
-        $transaction = $this->_initialize_transaction();
903
-        // verify transaction
904
-        if ($transaction instanceof EE_Transaction) {
905
-            // and save TXN data to the cart
906
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn($transaction->ID());
907
-        } else {
908
-            EE_Error::add_error(
909
-                esc_html__('A Valid Transaction could not be initialized.', 'event_espresso'),
910
-                __FILE__,
911
-                __FUNCTION__,
912
-                __LINE__
913
-            );
914
-        }
915
-        return $transaction;
916
-    }
917
-
918
-
919
-    /**
920
-     * generates a new EE_Transaction object and adds it to the $_transaction property.
921
-     *
922
-     * @return EE_Transaction|null
923
-     */
924
-    private function _initialize_transaction(): ?EE_Transaction
925
-    {
926
-        try {
927
-            // ensure cart totals have been calculated
928
-            $this->checkout->cart->get_grand_total()->recalculate_total_including_taxes();
929
-            // grab the cart grand total
930
-            $cart_total = $this->checkout->cart->get_cart_grand_total();
931
-            // create new TXN
932
-            $transaction = EE_Transaction::new_instance(
933
-                [
934
-                    'TXN_reg_steps' => $this->checkout->initialize_txn_reg_steps_array(),
935
-                    'TXN_total'     => max($cart_total, 0),
936
-                    'TXN_paid'      => 0,
937
-                    'STS_ID'        => EEM_Transaction::failed_status_code,
938
-                ]
939
-            );
940
-            // save it so that we have an ID for other objects to use
941
-            $transaction->save();
942
-            // set cron job for following up on TXNs after their session has expired
943
-            EE_Cron_Tasks::schedule_expired_transaction_check(
944
-                $this->session->expiration() + 1,
945
-                $transaction->ID()
946
-            );
947
-            return $transaction;
948
-        } catch (Exception $e) {
949
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
950
-        }
951
-        return null;
952
-    }
953
-
954
-
955
-    /**
956
-     * _get_registrations
957
-     *
958
-     * @param EE_Transaction $transaction
959
-     * @return void
960
-     * @throws EE_Error
961
-     * @throws ReflectionException
962
-     */
963
-    private function _get_registrations(EE_Transaction $transaction)
964
-    {
965
-        // first step: grab the registrants  { : o
966
-        $registrations                      = $transaction->registrations($this->checkout->reg_cache_where_params);
967
-        $this->checkout->total_ticket_count = count($registrations);
968
-        // verify registrations have been set
969
-        if (empty($registrations)) {
970
-            // if no cached registrations, then check the db
971
-            $registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
972
-            // still nothing ? well as long as this isn't a revisit
973
-            if (empty($registrations) && ! $this->checkout->revisit) {
974
-                // generate new registrations from scratch
975
-                $registrations = $this->_initialize_registrations($transaction);
976
-            }
977
-        }
978
-        // sort by their original registration order
979
-        usort($registrations, ['EED_Single_Page_Checkout', 'sort_registrations_by_REG_count']);
980
-        // then loop thru the array
981
-        foreach ($registrations as $registration) {
982
-            // verify each registration
983
-            if ($registration instanceof EE_Registration) {
984
-                // we display all attendee info for the primary registrant
985
-                if (
986
-                    $this->checkout->reg_url_link === $registration->reg_url_link()
987
-                    && $registration->is_primary_registrant()
988
-                ) {
989
-                    $this->checkout->primary_revisit = true;
990
-                    break;
991
-                }
992
-                if ($this->checkout->revisit && $this->checkout->reg_url_link !== $registration->reg_url_link()) {
993
-                    // but hide info if it doesn't belong to you
994
-                    $transaction->clear_cache('Registration', $registration->ID());
995
-                    $this->checkout->total_ticket_count--;
996
-                }
997
-                $this->checkout->set_reg_status_updated($registration->ID(), false);
998
-            }
999
-        }
1000
-    }
1001
-
1002
-
1003
-    /**
1004
-     * adds related EE_Registration objects for each ticket in the cart to the current EE_Transaction object
1005
-     *
1006
-     * @param EE_Transaction $transaction
1007
-     * @return array
1008
-     * @throws EE_Error
1009
-     * @throws ReflectionException
1010
-     */
1011
-    private function _initialize_registrations(EE_Transaction $transaction): array
1012
-    {
1013
-        $att_nmbr      = 0;
1014
-        $registrations = [];
1015
-        /** @type EE_Registration_Processor $registration_processor */
1016
-        $registration_processor             = EE_Registry::instance()->load_class('Registration_Processor');
1017
-        $this->checkout->total_ticket_count = $this->checkout->cart->all_ticket_quantity_count();
1018
-        // now let's add the cart items to the $transaction
1019
-        foreach ($this->checkout->cart->get_tickets() as $line_item) {
1020
-            // do the following for each ticket of this type they selected
1021
-            for ($x = 1; $x <= $line_item->quantity(); $x++) {
1022
-                $att_nmbr++;
1023
-                /** @var CreateRegistrationCommand $CreateRegistrationCommand */
1024
-                $CreateRegistrationCommand = EE_Registry::instance()->create(
1025
-                    CreateRegistrationCommand::class,
1026
-                    [
1027
-                        $transaction,
1028
-                        $line_item,
1029
-                        $att_nmbr,
1030
-                        $this->checkout->total_ticket_count,
1031
-                    ]
1032
-                );
1033
-                // override capabilities for frontend registrations
1034
-                if ($this->request->isFrontend()) {
1035
-                    $CreateRegistrationCommand->setCapCheck(
1036
-                        new PublicCapabilities('', 'create_new_registration')
1037
-                    );
1038
-                }
1039
-                $registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1040
-                if (! $registration instanceof EE_Registration) {
1041
-                    throw new InvalidEntityException($registration, 'EE_Registration');
1042
-                }
1043
-                $registrations[ $registration->ID() ] = $registration;
1044
-            }
1045
-        }
1046
-        $registration_processor->fix_reg_final_price_rounding_issue($transaction);
1047
-        return $registrations;
1048
-    }
1049
-
1050
-
1051
-    /**
1052
-     * @param EE_Registration $reg_A
1053
-     * @param EE_Registration $reg_B
1054
-     * @return int
1055
-     * @throws EE_Error
1056
-     * @throws ReflectionException
1057
-     */
1058
-    public static function sort_registrations_by_REG_count(EE_Registration $reg_A, EE_Registration $reg_B): int
1059
-    {
1060
-        // this shouldn't ever happen within the same TXN, but oh well
1061
-        if ($reg_A->count() === $reg_B->count()) {
1062
-            return 0;
1063
-        }
1064
-        return ($reg_A->count() > $reg_B->count()) ? 1 : -1;
1065
-    }
1066
-
1067
-
1068
-    /**
1069
-     * just makes sure that everything is set up correctly before proceeding
1070
-     *
1071
-     * @return bool
1072
-     * @throws EE_Error
1073
-     * @throws ReflectionException
1074
-     */
1075
-    private function _final_verifications(): bool
1076
-    {
1077
-        // filter checkout
1078
-        $this->checkout = apply_filters(
1079
-            'FHEE__EED_Single_Page_Checkout___final_verifications__checkout',
1080
-            $this->checkout
1081
-        );
1082
-        // verify that current step is still set correctly
1083
-        if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1084
-            EE_Error::add_error(
1085
-                esc_html__(
1086
-                    'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
1087
-                    'event_espresso'
1088
-                ),
1089
-                __FILE__,
1090
-                __FUNCTION__,
1091
-                __LINE__
1092
-            );
1093
-            return false;
1094
-        }
1095
-        // if returning to SPCO, then verify that primary registrant is set
1096
-        if (! empty($this->checkout->reg_url_link)) {
1097
-            $valid_registrant = $this->checkout->transaction->primary_registration();
1098
-            if (! $valid_registrant instanceof EE_Registration) {
1099
-                EE_Error::add_error(
1100
-                    esc_html__(
1101
-                        'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
1102
-                        'event_espresso'
1103
-                    ),
1104
-                    __FILE__,
1105
-                    __FUNCTION__,
1106
-                    __LINE__
1107
-                );
1108
-                return false;
1109
-            }
1110
-            $valid_registrant = null;
1111
-            foreach (
1112
-                $this->checkout->transaction->registrations(
1113
-                    $this->checkout->reg_cache_where_params
1114
-                ) as $registration
1115
-            ) {
1116
-                if (
1117
-                    $registration instanceof EE_Registration
1118
-                    && $registration->reg_url_link() === $this->checkout->reg_url_link
1119
-                ) {
1120
-                    $valid_registrant = $registration;
1121
-                }
1122
-            }
1123
-            if (! $valid_registrant instanceof EE_Registration) {
1124
-                // hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1125
-                if (EED_Single_Page_Checkout::$_checkout_verified) {
1126
-                    // clear the session, mark the checkout as unverified, and try again
1127
-                    $this->session->clear_session(__CLASS__, __FUNCTION__);
1128
-                    EED_Single_Page_Checkout::$_initialized       = false;
1129
-                    EED_Single_Page_Checkout::$_checkout_verified = false;
1130
-                    $this->_initialize();
1131
-                    EE_Error::reset_notices();
1132
-                    return false;
1133
-                }
1134
-                EE_Error::add_error(
1135
-                    esc_html__(
1136
-                        'We\'re sorry but there appears to be an error with the "reg_url_link" or the transaction itself. Please refresh the page and try again or contact support.',
1137
-                        'event_espresso'
1138
-                    ),
1139
-                    __FILE__,
1140
-                    __FUNCTION__,
1141
-                    __LINE__
1142
-                );
1143
-                return false;
1144
-            }
1145
-        }
1146
-        // now that things have been kinda sufficiently verified,
1147
-        // let's add the checkout to the session so that it's available to other systems
1148
-        $this->session->set_checkout($this->checkout);
1149
-        return true;
1150
-    }
1151
-
1152
-
1153
-    /**
1154
-     * first makes sure that EE_Transaction_Processor::set_reg_step_initiated() is called as required
1155
-     * then loops thru all the active reg steps and calls the initialize_reg_step() method
1156
-     *
1157
-     * @param bool $reinitializing
1158
-     * @throws EE_Error
1159
-     * @throws ReflectionException
1160
-     */
1161
-    private function _initialize_reg_steps(bool $reinitializing = false)
1162
-    {
1163
-        $this->checkout->set_reg_step_initiated($this->checkout->current_step);
1164
-        // loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1165
-        foreach ($this->checkout->reg_steps as $reg_step) {
1166
-            if (! $reg_step->initialize_reg_step()) {
1167
-                // if not initialized then maybe this step is being removed...
1168
-                if (! $reinitializing && $reg_step->is_current_step()) {
1169
-                    // if it was the current step, then we need to start over here
1170
-                    $this->_initialize_reg_steps(true);
1171
-                    return;
1172
-                }
1173
-                continue;
1174
-            }
1175
-            // add css and JS for current step
1176
-            $this->add_styles_and_scripts($reg_step);
1177
-            if ($reg_step->is_current_step()) {
1178
-                // the text that appears on the reg step form submit button
1179
-                $reg_step->set_submit_button_text();
1180
-            }
1181
-        }
1182
-        // dynamically creates hook point like: AHEE__Single_Page_Checkout___initialize_reg_step__attendee_information
1183
-        do_action(
1184
-            "AHEE__Single_Page_Checkout___initialize_reg_step__{$this->checkout->current_step->slug()}",
1185
-            $this->checkout->current_step
1186
-        );
1187
-        $this->checkout->json_response->set_return_data(
1188
-            [
1189
-                'action'        => $this->checkout->action,
1190
-                'admin_request' => $this->checkout->admin_request,
1191
-                'current_step'  => $this->checkout->current_step->slug(),
1192
-                'revisit'       => $this->checkout->revisit,
1193
-            ]
1194
-        );
1195
-    }
1196
-
1197
-
1198
-    /**
1199
-     * @return boolean
1200
-     * @throws EE_Error
1201
-     * @throws ReflectionException
1202
-     */
1203
-    private function _check_form_submission(): bool
1204
-    {
1205
-        // does this request require the reg form to be generated ?
1206
-        if ($this->checkout->generate_reg_form) {
1207
-            // ever heard that song by Blue Rodeo ?
1208
-            try {
1209
-                $this->checkout->current_step->reg_form = $this->checkout->current_step->generate_reg_form();
1210
-                // if not displaying a form, then check for form submission
1211
-                if (
1212
-                    $this->checkout->process_form_submission
1213
-                    && $this->checkout->current_step->reg_form->was_submitted()
1214
-                ) {
1215
-                    // clear out any old data in case this step is being run again
1216
-                    $this->checkout->current_step->set_valid_data([]);
1217
-                    // capture submitted form data
1218
-                    $request_data = $this->request->requestParams();
1219
-                    $this->checkout->current_step->reg_form->receive_form_submission(
1220
-                        (array) apply_filters(
1221
-                            'FHEE__Single_Page_Checkout___check_form_submission__request_params',
1222
-                            $request_data,
1223
-                            $this->checkout
1224
-                        )
1225
-                    );
1226
-                    // validate submitted form data
1227
-                    if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1228
-                        // thou shall not pass !!!
1229
-                        $this->checkout->continue_reg = false;
1230
-                        // any form validation errors?
1231
-                        if ($this->checkout->current_step->reg_form->submission_error_message() !== '') {
1232
-                            EE_Error::add_error(
1233
-                                $this->checkout->current_step->reg_form->submission_error_message(),
1234
-                                __FILE__,
1235
-                                __FUNCTION__,
1236
-                                __LINE__
1237
-                            );
1238
-                        }
1239
-                        // well not really... what will happen is
1240
-                        // we'll just get redirected back to redo the current step
1241
-                        $this->go_to_next_step();
1242
-                        return false;
1243
-                    }
1244
-                }
1245
-            } catch (EE_Error $e) {
1246
-                $e->get_error();
1247
-            }
1248
-        }
1249
-        return true;
1250
-    }
1251
-
1252
-
1253
-    /**
1254
-     * @return void
1255
-     * @throws EE_Error
1256
-     * @throws ReflectionException
1257
-     */
1258
-    private function _process_form_action()
1259
-    {
1260
-        // dynamically creates hook point like:
1261
-        //   AHEE__Single_Page_Checkout__before_attendee_information__process_reg_step
1262
-        do_action(
1263
-            "AHEE__Single_Page_Checkout__before_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1264
-            $this->checkout->current_step
1265
-        );
1266
-        // what cha wanna do?
1267
-        switch ($this->checkout->action) {
1268
-            // AJAX next step reg form
1269
-            case 'display_spco_reg_step':
1270
-                $this->checkout->redirect = false;
1271
-                if ($this->request->isAjax()) {
1272
-                    $this->checkout->json_response->set_reg_step_html(
1273
-                        $this->checkout->current_step->display_reg_form()
1274
-                    );
1275
-                }
1276
-                break;
1277
-            default:
1278
-                // meh... do one of those other steps first
1279
-                if (
1280
-                    ! empty($this->checkout->action)
1281
-                    && is_callable([$this->checkout->current_step, $this->checkout->action])
1282
-                ) {
1283
-                    $process_reg_step = apply_filters(
1284
-                        "AHEE__Single_Page_Checkout__process_reg_step__{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1285
-                        true,
1286
-                        $this->checkout->current_step,
1287
-                        $this
1288
-                    );
1289
-                    // call action on current step
1290
-                    if ($process_reg_step && call_user_func([$this->checkout->current_step, $this->checkout->action])) {
1291
-                        // good registrant, you get to proceed
1292
-                        if (
1293
-                            $this->checkout->current_step->success_message() !== ''
1294
-                            && apply_filters(
1295
-                                'FHEE__Single_Page_Checkout___process_form_action__display_success',
1296
-                                false
1297
-                            )
1298
-                        ) {
1299
-                            EE_Error::add_success(
1300
-                                $this->checkout->current_step->success_message()
1301
-                                . '<br />' . $this->checkout->next_step->_instructions()
1302
-                            );
1303
-                        }
1304
-                        // pack it up, pack it in...
1305
-                        $this->_setup_redirect();
1306
-                    }
1307
-                } else {
1308
-                    EE_Error::add_error(
1309
-                        sprintf(
1310
-                            esc_html__(
1311
-                                'The requested form action "%s" does not exist for the current "%s" registration step.',
1312
-                                'event_espresso'
1313
-                            ),
1314
-                            $this->checkout->action,
1315
-                            $this->checkout->current_step->name()
1316
-                        ),
1317
-                        __FILE__,
1318
-                        __FUNCTION__,
1319
-                        __LINE__
1320
-                    );
1321
-                }
1322
-            // end default
1323
-        }
1324
-        // dynamically creates hook point like:
1325
-        //  AHEE__Single_Page_Checkout__after_payment_options__process_reg_step
1326
-        do_action(
1327
-            "AHEE__Single_Page_Checkout__after_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1328
-            $this->checkout->current_step
1329
-        );
1330
-        // store our progress so far
1331
-        $this->checkout->stash_transaction_and_checkout();
1332
-        // advance to the next step! If you pass GO, collect $200
1333
-        $this->go_to_next_step();
1334
-    }
1335
-
1336
-
1337
-    /**
1338
-     * @param EED_Single_Page_Checkout|EE_SPCO_Reg_Step $target an object with the method `translate_js_strings` and
1339
-     *                                                          `enqueue_styles_and_scripts`.
1340
-     * @return void
1341
-     */
1342
-    public function add_styles_and_scripts($target)
1343
-    {
1344
-        // i18n
1345
-        $target->translate_js_strings();
1346
-        if ($this->checkout->admin_request) {
1347
-            add_action('admin_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1348
-        } else {
1349
-            add_action('wp_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1350
-        }
1351
-    }
1352
-
1353
-
1354
-    /**
1355
-     * @return void
1356
-     */
1357
-    public function translate_js_strings()
1358
-    {
1359
-        EE_Registry::$i18n_js_strings['revisit']                        = $this->checkout->revisit;
1360
-        EE_Registry::$i18n_js_strings['e_reg_url_link']                 = $this->checkout->reg_url_link;
1361
-        EE_Registry::$i18n_js_strings['server_error']                   = esc_html__(
1362
-            'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1363
-            'event_espresso'
1364
-        );
1365
-        EE_Registry::$i18n_js_strings['invalid_json_response']          = esc_html__(
1366
-            'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1367
-            'event_espresso'
1368
-        );
1369
-        EE_Registry::$i18n_js_strings['validation_error']               = esc_html__(
1370
-            'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1371
-            'event_espresso'
1372
-        );
1373
-        EE_Registry::$i18n_js_strings['invalid_payment_method']         = esc_html__(
1374
-            'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1375
-            'event_espresso'
1376
-        );
1377
-        EE_Registry::$i18n_js_strings['reg_step_error']                 = esc_html__(
1378
-            'This registration step could not be completed. Please refresh the page and try again.',
1379
-            'event_espresso'
1380
-        );
1381
-        EE_Registry::$i18n_js_strings['invalid_coupon']                 = esc_html__(
1382
-            'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1383
-            'event_espresso'
1384
-        );
1385
-        EE_Registry::$i18n_js_strings['process_registration']           = sprintf(
1386
-            esc_html__(
1387
-                'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1388
-                'event_espresso'
1389
-            ),
1390
-            '<br/>',
1391
-            '<br/>'
1392
-        );
1393
-        EE_Registry::$i18n_js_strings['language']                       = get_bloginfo('language');
1394
-        EE_Registry::$i18n_js_strings['EESID']                          = $this->session->id();
1395
-        EE_Registry::$i18n_js_strings['currency']                       = EE_Registry::instance()->CFG->currency;
1396
-        EE_Registry::$i18n_js_strings['datepicker_yearRange']           = '-150:+20';
1397
-        EE_Registry::$i18n_js_strings['timer_years']                    = esc_html__('years', 'event_espresso');
1398
-        EE_Registry::$i18n_js_strings['timer_months']                   = esc_html__('months', 'event_espresso');
1399
-        EE_Registry::$i18n_js_strings['timer_weeks']                    = esc_html__('weeks', 'event_espresso');
1400
-        EE_Registry::$i18n_js_strings['timer_days']                     = esc_html__('days', 'event_espresso');
1401
-        EE_Registry::$i18n_js_strings['timer_hours']                    = esc_html__('hours', 'event_espresso');
1402
-        EE_Registry::$i18n_js_strings['timer_minutes']                  = esc_html__('minutes', 'event_espresso');
1403
-        EE_Registry::$i18n_js_strings['timer_seconds']                  = esc_html__('seconds', 'event_espresso');
1404
-        EE_Registry::$i18n_js_strings['timer_year']                     = esc_html__('year', 'event_espresso');
1405
-        EE_Registry::$i18n_js_strings['timer_month']                    = esc_html__('month', 'event_espresso');
1406
-        EE_Registry::$i18n_js_strings['timer_week']                     = esc_html__('week', 'event_espresso');
1407
-        EE_Registry::$i18n_js_strings['timer_day']                      = esc_html__('day', 'event_espresso');
1408
-        EE_Registry::$i18n_js_strings['timer_hour']                     = esc_html__('hour', 'event_espresso');
1409
-        EE_Registry::$i18n_js_strings['timer_minute']                   = esc_html__('minute', 'event_espresso');
1410
-        EE_Registry::$i18n_js_strings['timer_second']                   = esc_html__('second', 'event_espresso');
1411
-        EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
1412
-            EED_Single_Page_Checkout::getRegistrationExpirationNotice();
1413
-        EE_Registry::$i18n_js_strings['ajax_submit']                    = apply_filters(
1414
-            'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1415
-            true
1416
-        );
1417
-        EE_Registry::$i18n_js_strings['session_extension']              = absint(
1418
-            apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1419
-        );
1420
-        EE_Registry::$i18n_js_strings['session_expiration']    = $this->session->expiration();
1421
-        EE_Registry::$i18n_js_strings['use_session_countdown'] = EE_Registry::instance()->CFG->registration->useSessionCountdown();
1422
-        EE_Registry::$i18n_js_strings['no_copy_paste_email_confirm'] = esc_html__("We're sorry but copy and paste is disabled for email confirmation inputs. Please enter the email address manually.", 'event_espresso');
1423
-    }
1424
-
1425
-
1426
-    /**
1427
-     * @return void
1428
-     * @throws EE_Error
1429
-     */
1430
-    public function enqueue_styles_and_scripts()
1431
-    {
1432
-        // load css
1433
-        wp_register_style(
1434
-            'single_page_checkout',
1435
-            SPCO_CSS_URL . 'single_page_checkout.css',
1436
-            ['espresso_default'],
1437
-            EVENT_ESPRESSO_VERSION
1438
-        );
1439
-        wp_enqueue_style('single_page_checkout');
1440
-        // load JS
1441
-        wp_register_script(
1442
-            'single_page_checkout',
1443
-            SPCO_JS_URL . 'single_page_checkout.js',
1444
-            ['espresso_core', 'underscore', 'ee_form_section_validation'],
1445
-            EVENT_ESPRESSO_VERSION,
1446
-            true
1447
-        );
1448
-        if ($this->checkout->registration_form instanceof EE_Form_Section_Proper) {
1449
-            $this->checkout->registration_form->enqueue_js();
1450
-        }
1451
-        if ($this->checkout->current_step->reg_form instanceof EE_Form_Section_Proper) {
1452
-            $this->checkout->current_step->reg_form->enqueue_js();
1453
-        }
1454
-        wp_enqueue_script('single_page_checkout');
1455
-        /**
1456
-         * global action hook for enqueueing styles and scripts with
1457
-         * spco calls.
1458
-         */
1459
-        do_action('AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts', $this);
1460
-        /**
1461
-         * dynamic action hook for enqueueing styles and scripts with spco calls.
1462
-         * The hook will end up being something like:
1463
-         *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1464
-         */
1465
-        do_action(
1466
-            'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1467
-            $this
1468
-        );
1469
-    }
1470
-
1471
-
1472
-    /**
1473
-     * display the Registration Single Page Checkout Form
1474
-     *
1475
-     * @return void
1476
-     * @throws EE_Error
1477
-     * @throws ReflectionException
1478
-     */
1479
-    private function _display_spco_reg_form()
1480
-    {
1481
-        // if registering via the admin, just display the reg form for the current step
1482
-        if ($this->checkout->admin_request) {
1483
-            EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->current_step->display_reg_form());
1484
-            return;
1485
-        }
1486
-
1487
-        // add powered by EE msg
1488
-        add_action('AHEE__SPCO__reg_form_footer', ['EED_Single_Page_Checkout', 'display_registration_footer']);
1489
-        $empty_cart = count(
1490
-                $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params)
1491
-            ) < 1;
1492
-        EE_Registry::$i18n_js_strings['empty_cart'] = $empty_cart;
1493
-
1494
-        /** @var FeatureFlags $feature */
1495
-        $feature = LoaderFactory::getShared(FeatureFlags::class);
1496
-
1497
-        $this->checkout->registration_form = $feature->allowed(FeatureFlag::USE_SPCO_FORM_REFACTOR)
1498
-            ? new SinglePageCheckoutForm($this->checkout, $empty_cart)
1499
-            : new LegacySpcoForm($this->checkout, $empty_cart, $this->session->expiration());
1500
-
1501
-        // load template and add to output sent that gets filtered into the_content()
1502
-        EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->registration_form->get_html());
1503
-
1504
-    }
1505
-
1506
-
1507
-    /**
1508
-     * @param $next_step
1509
-     * @return void
1510
-     */
1511
-    public function add_extra_finalize_registration_inputs($next_step)
1512
-    {
1513
-        if ($next_step === 'finalize_registration') {
1514
-            echo '<div id="spco-extra-finalize_registration-inputs-dv"></div>';
1515
-        }
1516
-    }
1517
-
1518
-
1519
-    /**
1520
-     * @return void
1521
-     */
1522
-    public static function display_registration_footer()
1523
-    {
1524
-        if (
1525
-            apply_filters(
1526
-                'FHEE__EE_Front__Controller__show_reg_footer',
1527
-                EE_Registry::instance()->CFG->admin->show_reg_footer
1528
-            )
1529
-        ) {
1530
-            add_filter(
1531
-                'FHEE__EEH_Template__powered_by_event_espresso__url',
1532
-                function ($url) {
1533
-                    return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1534
-                }
1535
-            );
1536
-            echo apply_filters(
1537
-                'FHEE__EE_Front_Controller__display_registration_footer',
1538
-                EEH_Template::powered_by_event_espresso(
1539
-                    '',
1540
-                    'espresso-registration-footer-dv',
1541
-                    ['utm_content' => 'registration_checkout']
1542
-                )
1543
-            );
1544
-        }
1545
-    }
1546
-
1547
-
1548
-    /**
1549
-     * @return void
1550
-     * @throws EE_Error
1551
-     * @throws ReflectionException
1552
-     */
1553
-    private function lockTransaction()
1554
-    {
1555
-        if ($this->checkout instanceof EE_Checkout && $this->checkout->transaction instanceof EE_Transaction) {
1556
-            // lock the transaction
1557
-            $this->checkout->transaction->lock(false);
1558
-            // set up transaction lock removal
1559
-            add_action('shutdown', [$this, 'unlock_transaction'], 1);
1560
-            add_filter(
1561
-                'wp_redirect',
1562
-                function (string $location) {
1563
-                    // this is hacky, but WP doesn't supply an action to hook into before the redirect
1564
-                    $this->unlock_transaction();
1565
-                    return $location;
1566
-                }
1567
-            );
1568
-        }
1569
-    }
1570
-
1571
-
1572
-    /**
1573
-     * @return void
1574
-     * @throws EE_Error
1575
-     * @throws ReflectionException
1576
-     */
1577
-    public function unlock_transaction()
1578
-    {
1579
-        if ($this->checkout instanceof EE_Checkout && $this->checkout->transaction instanceof EE_Transaction) {
1580
-            $this->checkout->transaction->unlock();
1581
-        }
1582
-    }
1583
-
1584
-
1585
-    /**
1586
-     * @return void
1587
-     */
1588
-    private function _setup_redirect()
1589
-    {
1590
-        if ($this->checkout->continue_reg && $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
1591
-            $this->checkout->redirect = true;
1592
-            if (empty($this->checkout->redirect_url)) {
1593
-                $this->checkout->redirect_url = $this->checkout->next_step->reg_step_url();
1594
-            }
1595
-            $this->checkout->redirect_url = apply_filters(
1596
-                'FHEE__EED_Single_Page_Checkout___setup_redirect__checkout_redirect_url',
1597
-                $this->checkout->redirect_url,
1598
-                $this->checkout
1599
-            );
1600
-        }
1601
-    }
1602
-
1603
-
1604
-    /**
1605
-     * handle ajax message responses and redirects
1606
-     *
1607
-     * @return void
1608
-     * @throws EE_Error
1609
-     * @throws ReflectionException
1610
-     */
1611
-    public function go_to_next_step()
1612
-    {
1613
-        if ($this->request->isAjax()) {
1614
-            // capture contents of output buffer we started earlier in the request, and insert into JSON response
1615
-            $this->checkout->json_response->set_unexpected_errors(ob_get_clean());
1616
-        }
1617
-        $this->unlock_transaction();
1618
-        // just return for these conditions
1619
-        if (
1620
-            $this->checkout->admin_request
1621
-            || $this->checkout->action === 'redirect_form'
1622
-            || $this->checkout->action === 'update_checkout'
1623
-        ) {
1624
-            return;
1625
-        }
1626
-        // AJAX response
1627
-        $this->_handle_json_response();
1628
-        // redirect to next step or the Thank-You page
1629
-        $this->_handle_html_redirects();
1630
-        // hmmm... must be something wrong, so let's just display the form again !
1631
-        $this->_display_spco_reg_form();
1632
-    }
1633
-
1634
-
1635
-    /**
1636
-     * @return void
1637
-     */
1638
-    protected function _handle_json_response()
1639
-    {
1640
-        // if this is an ajax request
1641
-        if ($this->request->isAjax()) {
1642
-            $this->checkout->json_response->set_registration_time_limit(
1643
-                $this->checkout->get_registration_time_limit()
1644
-            );
1645
-            $this->checkout->json_response->set_payment_amount($this->checkout->amount_owing);
1646
-            /** @var EE_SPCO_JSON_Response $json_response */
1647
-            $json_response = apply_filters(
1648
-                'FHEE__EE_Single_Page_Checkout__JSON_response',
1649
-                $this->checkout->json_response
1650
-            );
1651
-            // just send the ajax
1652
-            $json_response->sendResponse();
1653
-        }
1654
-    }
1655
-
1656
-
1657
-    /**
1658
-     * @return void
1659
-     */
1660
-    protected function _handle_html_redirects()
1661
-    {
1662
-        // going somewhere ?
1663
-        if ($this->checkout->redirect && ! empty($this->checkout->redirect_url)) {
1664
-            // store notices in a transient
1665
-            EE_Error::get_notices(false, true);
1666
-            wp_safe_redirect($this->checkout->redirect_url);
1667
-            exit();
1668
-        }
1669
-    }
1670
-
1671
-
1672
-    /**
1673
-     * @param WP_Query $query
1674
-     * @return void
1675
-     */
1676
-    public function set_checkout_anchor(WP_Query $query)
1677
-    {
1678
-        if (! $query->is_main_query()) {
1679
-            return;
1680
-        }
1681
-        echo '<a id="checkout" class="screen-reader-text"></a>';
1682
-    }
1683
-
1684
-
1685
-    /**
1686
-     * @return string
1687
-     * @since 4.9.59.p
1688
-     */
1689
-    public static function getRegistrationExpirationNotice(): string
1690
-    {
1691
-        return sprintf(
1692
-            esc_html__(
1693
-                '%1$sWe\'re sorry, but your registration time has expired.%2$s%3$s%4$sIf you still wish to complete your registration, please return to the %5$sEvent List%6$sEvent List%7$s and reselect your tickets if available. Please accept our apologies for any inconvenience this may have caused.%8$s',
1694
-                'event_espresso'
1695
-            ),
1696
-            '<h4 class="important-notice">',
1697
-            '</h4>',
1698
-            '<br />',
1699
-            '<p>',
1700
-            '<a href="' . get_post_type_archive_link(EspressoPostType::EVENTS) . '" title="',
1701
-            '">',
1702
-            '</a>',
1703
-            '</p>'
1704
-        );
1705
-    }
27
+	/**
28
+	 * $_initialized - has the SPCO controller already been initialized ?
29
+	 */
30
+	private static bool $_initialized = false;
31
+
32
+
33
+	/**
34
+	 * $_checkout_verified - is the EE_Checkout verified as correct for this request ?
35
+	 */
36
+	private static bool $_checkout_verified = true;
37
+
38
+	/**
39
+	 * $_reg_steps_array - holds initial array of reg steps
40
+	 *
41
+	 * @var array $_reg_steps_array
42
+	 */
43
+	private static array $_reg_steps_array = [];
44
+
45
+	/**
46
+	 * $checkout - EE_Checkout object for handling the properties of the current checkout process
47
+	 */
48
+	public ?EE_Checkout $checkout = null;
49
+
50
+	protected ?RequestInterface $request = null;
51
+
52
+	public ?EE_Session $session = null;
53
+
54
+	private bool $debug = false;    //  true    false
55
+
56
+
57
+	/**
58
+	 * @return EED_Single_Page_Checkout|EED_Module
59
+	 * @throws EE_Error
60
+	 * @throws ReflectionException
61
+	 */
62
+	public static function instance()
63
+	{
64
+		add_filter('EED_Single_Page_Checkout__SPCO_active', '__return_true');
65
+		return parent::get_instance(__CLASS__);
66
+	}
67
+
68
+
69
+	/**
70
+	 * @return EE_CART
71
+	 */
72
+	public function cart(): EE_CART
73
+	{
74
+		return $this->checkout->cart;
75
+	}
76
+
77
+
78
+	/**
79
+	 * @return RequestInterface
80
+	 * @since   4.10.14.p
81
+	 */
82
+	public static function getRequest(): RequestInterface
83
+	{
84
+		return LoaderFactory::getLoader()->getShared(RequestInterface::class);
85
+	}
86
+
87
+
88
+	/**
89
+	 * @return EE_Transaction
90
+	 */
91
+	public function transaction(): EE_Transaction
92
+	{
93
+		return $this->checkout->transaction;
94
+	}
95
+
96
+
97
+	/**
98
+	 *    set_hooks - for hooking into EE Core, other modules, etc
99
+	 *
100
+	 * @return    void
101
+	 * @throws EE_Error
102
+	 */
103
+	public static function set_hooks()
104
+	{
105
+		EED_Single_Page_Checkout::set_definitions();
106
+	}
107
+
108
+
109
+	/**
110
+	 *    set_hooks_admin - for hooking into EE Admin Core, other modules, etc
111
+	 *
112
+	 * @return    void
113
+	 * @throws EE_Error
114
+	 */
115
+	public static function set_hooks_admin()
116
+	{
117
+		EED_Single_Page_Checkout::set_definitions();
118
+		if (! (defined('DOING_AJAX') && DOING_AJAX)) {
119
+			return;
120
+		}
121
+		// going to start an output buffer in case anything gets accidentally output
122
+		// that might disrupt our JSON response
123
+		ob_start();
124
+		EED_Single_Page_Checkout::load_reg_steps();
125
+		// set ajax hooks
126
+		add_action('wp_ajax_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
127
+		add_action('wp_ajax_nopriv_process_reg_step', ['EED_Single_Page_Checkout', 'process_reg_step']);
128
+		add_action('wp_ajax_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
129
+		add_action('wp_ajax_nopriv_display_spco_reg_step', ['EED_Single_Page_Checkout', 'display_reg_step']);
130
+		add_action('wp_ajax_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
131
+		add_action('wp_ajax_nopriv_update_reg_step', ['EED_Single_Page_Checkout', 'update_reg_step']);
132
+	}
133
+
134
+
135
+	/**
136
+	 *    process ajax request
137
+	 *
138
+	 * @param string $ajax_action
139
+	 * @throws EE_Error
140
+	 * @throws ReflectionException
141
+	 */
142
+	public static function process_ajax_request(string $ajax_action)
143
+	{
144
+		$request = EED_Single_Page_Checkout::getRequest();
145
+		$request->setRequestParam('action', $ajax_action);
146
+		EED_Single_Page_Checkout::instance()->_initialize();
147
+	}
148
+
149
+
150
+	/**
151
+	 * ajax display registration step
152
+	 *
153
+	 * @throws EE_Error
154
+	 * @throws ReflectionException
155
+	 */
156
+	public static function display_reg_step()
157
+	{
158
+		EED_Single_Page_Checkout::process_ajax_request('display_spco_reg_step');
159
+	}
160
+
161
+
162
+	/**
163
+	 * ajax process registration step
164
+	 *
165
+	 * @throws EE_Error
166
+	 * @throws ReflectionException
167
+	 */
168
+	public static function process_reg_step()
169
+	{
170
+		EED_Single_Page_Checkout::process_ajax_request('process_reg_step');
171
+	}
172
+
173
+
174
+	/**
175
+	 * ajax process registration step
176
+	 *
177
+	 * @throws EE_Error
178
+	 * @throws ReflectionException
179
+	 */
180
+	public static function update_reg_step()
181
+	{
182
+		EED_Single_Page_Checkout::process_ajax_request('update_reg_step');
183
+	}
184
+
185
+
186
+	/**
187
+	 * update_checkout
188
+	 *
189
+	 * @return void
190
+	 * @throws ReflectionException
191
+	 * @throws EE_Error
192
+	 */
193
+	public static function update_checkout()
194
+	{
195
+		EED_Single_Page_Checkout::process_ajax_request('update_checkout');
196
+	}
197
+
198
+
199
+	/**
200
+	 *    set_definitions
201
+	 *
202
+	 * @return    void
203
+	 * @throws EE_Error
204
+	 */
205
+	public static function set_definitions()
206
+	{
207
+		if (defined('SPCO_BASE_PATH')) {
208
+			return;
209
+		}
210
+		define(
211
+			'SPCO_BASE_PATH',
212
+			rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/') . '/'
213
+		);
214
+		define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
215
+		define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
216
+		define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
217
+		define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
218
+		define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
219
+		define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
220
+		EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
221
+		EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
222
+			EED_Single_Page_Checkout::getRegistrationExpirationNotice();
223
+	}
224
+
225
+
226
+	/**
227
+	 * load_reg_steps
228
+	 * loads and instantiates each reg step based on the EE_Registry::instance()->CFG->registration->reg_steps array
229
+	 *
230
+	 * @throws EE_Error
231
+	 */
232
+	public static function load_reg_steps()
233
+	{
234
+		static $reg_steps_loaded = false;
235
+		if ($reg_steps_loaded) {
236
+			return;
237
+		}
238
+		// filter list of reg_steps
239
+		$reg_steps_to_load = (array) apply_filters(
240
+			'AHEE__SPCO__load_reg_steps__reg_steps_to_load',
241
+			EED_Single_Page_Checkout::get_reg_steps()
242
+		);
243
+		// sort by key (order)
244
+		ksort($reg_steps_to_load);
245
+		// loop through folders
246
+		foreach ($reg_steps_to_load as $order => $reg_step) {
247
+			// we need a
248
+			if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
249
+				// copy over to the reg_steps_array
250
+				EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
251
+				// register custom key route for each reg step
252
+				// ie: step=>"slug" - this is the entire reason we load the reg steps array now
253
+				EED_Module::registerRoute(
254
+					$reg_step['slug'],
255
+					'EED_Single_Page_Checkout',
256
+					'run',
257
+					'step'
258
+				);
259
+				// add AJAX or other hooks
260
+				if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
261
+					// setup autoloaders if necessary
262
+					if (! class_exists($reg_step['class_name'])) {
263
+						EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
264
+							$reg_step['file_path'],
265
+							true
266
+						);
267
+					}
268
+					if (is_callable($reg_step['class_name'], 'set_hooks')) {
269
+						call_user_func([$reg_step['class_name'], 'set_hooks']);
270
+					}
271
+				}
272
+			}
273
+		}
274
+		$reg_steps_loaded = true;
275
+	}
276
+
277
+
278
+	/**
279
+	 *    get_reg_steps
280
+	 *
281
+	 * @return    array
282
+	 */
283
+	public static function get_reg_steps(): array
284
+	{
285
+		$reg_steps = EE_Registry::instance()->CFG->registration->reg_steps;
286
+		if (empty($reg_steps)) {
287
+			$reg_steps = [
288
+				10  => [
289
+					'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
290
+					'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
291
+					'slug'       => 'attendee_information',
292
+					'has_hooks'  => false,
293
+				],
294
+				30  => [
295
+					'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
296
+					'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
297
+					'slug'       => 'payment_options',
298
+					'has_hooks'  => true,
299
+				],
300
+				999 => [
301
+					'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
302
+					'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
303
+					'slug'       => 'finalize_registration',
304
+					'has_hooks'  => false,
305
+				],
306
+			];
307
+		}
308
+		return $reg_steps;
309
+	}
310
+
311
+
312
+	/**
313
+	 * @return array|string
314
+	 * @throws EE_Error
315
+	 * @throws ReflectionException
316
+	 */
317
+	public static function registration_checkout_for_admin()
318
+	{
319
+		$request = EED_Single_Page_Checkout::getRequest();
320
+		$request->setRequestParam('step', 'attendee_information');
321
+		$request->setRequestParam('action', 'display_spco_reg_step');
322
+		$request->setRequestParam('process_form_submission', false);
323
+		EED_Single_Page_Checkout::instance()->_initialize();
324
+		EED_Single_Page_Checkout::instance()->_display_spco_reg_form();
325
+		return EED_Single_Page_Checkout::getResponse()->getOutput();
326
+	}
327
+
328
+
329
+	/**
330
+	 * @return EE_Transaction|null
331
+	 * @throws EE_Error
332
+	 * @throws ReflectionException
333
+	 */
334
+	public static function process_registration_from_admin(): ?EE_Transaction
335
+	{
336
+		$request = EED_Single_Page_Checkout::getRequest();
337
+		$request->setRequestParam('step', 'attendee_information');
338
+		$request->setRequestParam('action', 'process_reg_step');
339
+		$request->setRequestParam('process_form_submission', true);
340
+		EED_Single_Page_Checkout::instance()->_initialize();
341
+		if (EED_Single_Page_Checkout::instance()->checkout->current_step->completed()) {
342
+			$final_reg_step = end(EED_Single_Page_Checkout::instance()->checkout->reg_steps);
343
+			if ($final_reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
344
+				EED_Single_Page_Checkout::instance()->checkout->set_reg_step_initiated($final_reg_step);
345
+				if ($final_reg_step->process_reg_step()) {
346
+					$final_reg_step->set_completed();
347
+					EED_Single_Page_Checkout::instance()->checkout->update_txn_reg_steps_array();
348
+					return EED_Single_Page_Checkout::instance()->checkout->transaction;
349
+				}
350
+			}
351
+		}
352
+		return null;
353
+	}
354
+
355
+
356
+	/**
357
+	 *    run
358
+	 *
359
+	 * @param WP_Query|null $WP_Query
360
+	 * @return    void
361
+	 */
362
+	public function run($WP_Query)
363
+	{
364
+		if (
365
+			$WP_Query instanceof WP_Query
366
+			&& $WP_Query->is_main_query()
367
+			&& apply_filters('FHEE__EED_Single_Page_Checkout__run', true)
368
+			&& $this->_is_reg_checkout()
369
+		) {
370
+			$this->_initialize();
371
+		}
372
+	}
373
+
374
+
375
+	/**
376
+	 * determines whether current url matches reg page url
377
+	 *
378
+	 * @return bool
379
+	 */
380
+	protected function _is_reg_checkout(): bool
381
+	{
382
+		// get current permalink for reg page without any extra query args
383
+		$reg_page_url = get_permalink(EE_Config::instance()->core->reg_page_id);
384
+		// get request URI for current request, but without the scheme or host
385
+		$current_request_uri = EEH_URL::filter_input_server_url();
386
+		$current_request_uri = html_entity_decode($current_request_uri);
387
+		// get array of query args from the current request URI
388
+		$query_args = EEH_URL::get_query_string($current_request_uri);
389
+		// grab page id if it is set
390
+		$page_id = isset($query_args['page_id']) ? absint($query_args['page_id']) : 0;
391
+		// and remove the page id from the query args (we will re-add it later)
392
+		unset($query_args['page_id']);
393
+		// now strip all query args from current request URI
394
+		$current_request_uri = remove_query_arg(array_keys($query_args), $current_request_uri);
395
+		// and re-add the page id if it was set
396
+		if ($page_id) {
397
+			$current_request_uri = add_query_arg('page_id', $page_id, $current_request_uri);
398
+		}
399
+		// remove slashes and ?
400
+		$current_request_uri = trim($current_request_uri, '?/');
401
+		// is current request URI part of the known full reg page URL ?
402
+		return ! empty($current_request_uri) && strpos($reg_page_url, $current_request_uri) !== false;
403
+	}
404
+
405
+
406
+	/**
407
+	 * @param WP_Query $wp_query
408
+	 * @return    void
409
+	 * @throws EE_Error
410
+	 * @throws ReflectionException
411
+	 */
412
+	public static function init(WP_Query $wp_query)
413
+	{
414
+		EED_Single_Page_Checkout::instance()->run($wp_query);
415
+	}
416
+
417
+
418
+	/**
419
+	 * @return void
420
+	 */
421
+	private function _initialize()
422
+	{
423
+		// ensure SPCO doesn't run twice
424
+		if (EED_Single_Page_Checkout::$_initialized) {
425
+			return;
426
+		}
427
+		try {
428
+			$this->request = EED_Single_Page_Checkout::getRequest();
429
+			EED_Single_Page_Checkout::load_reg_steps();
430
+			$this->_verify_session();
431
+			// set up the EE_Checkout object
432
+			$this->checkout = $this->_initialize_checkout();
433
+			// filter checkout
434
+			$this->checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize__checkout', $this->checkout);
435
+			// get the $_GET
436
+			$this->_get_request_vars();
437
+			if ($this->_block_bots()) {
438
+				return;
439
+			}
440
+			// filter continue_reg
441
+			$this->checkout->continue_reg = apply_filters(
442
+				'FHEE__EED_Single_Page_Checkout__init___continue_reg',
443
+				true,
444
+				$this->checkout
445
+			);
446
+			// load the reg steps array
447
+			if (! $this->_load_and_instantiate_reg_steps()) {
448
+				EED_Single_Page_Checkout::$_initialized = true;
449
+				return;
450
+			}
451
+			// set the current step
452
+			$this->checkout->set_current_step($this->checkout->step);
453
+			// and the next step
454
+			$this->checkout->set_next_step();
455
+			// verify that everything has been set up correctly
456
+			if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
457
+				EED_Single_Page_Checkout::$_initialized = true;
458
+				return;
459
+			}
460
+			do_action('AHEE__Single_Page_Checkout___initialize__after_final_verifications', $this->checkout);
461
+			$this->lockTransaction();
462
+			// make sure all of our cached objects are added to their respective model entity mappers
463
+			$this->checkout->refresh_all_entities();
464
+			// set amount owing
465
+			$this->checkout->amount_owing = $this->checkout->transaction->remaining();
466
+			// initialize each reg step, which gives them the chance to potentially alter the process
467
+			$this->_initialize_reg_steps();
468
+			// DEBUG LOG
469
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
470
+			// get reg form
471
+			if (! $this->_check_form_submission()) {
472
+				EED_Single_Page_Checkout::$_initialized = true;
473
+				return;
474
+			}
475
+			// checkout the action!!!
476
+			$this->_process_form_action();
477
+			// add some style and make it dance
478
+			$this->add_styles_and_scripts($this);
479
+			// kk... SPCO has successfully run
480
+			EED_Single_Page_Checkout::$_initialized = true;
481
+			// set no cache headers and constants
482
+			EE_System::do_not_cache();
483
+			// add anchor
484
+			add_action('loop_start', [$this, 'set_checkout_anchor'], 1);
485
+		} catch (Exception $e) {
486
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
487
+		}
488
+	}
489
+
490
+
491
+	/**
492
+	 * checks that the session is valid and not expired
493
+	 *
494
+	 * @throws EE_Error
495
+	 * @throws ReflectionException
496
+	 */
497
+	private function _verify_session()
498
+	{
499
+		if (! $this->session instanceof EE_Session) {
500
+			$this->session = LoaderFactory::getShared(EE_Session::class);
501
+			if (! $this->session instanceof EE_Session) {
502
+				throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
503
+			}
504
+		}
505
+		$clear_session_requested = $this->request->getRequestParam('clear_session', false, 'bool');
506
+		// is session still valid ?
507
+		if (
508
+			$clear_session_requested
509
+			|| (
510
+				$this->session->expired()
511
+				&& $this->request->getRequestParam('e_reg_url_link') === ''
512
+			)
513
+		) {
514
+			$this->checkout = new EE_Checkout();
515
+			$this->session->clear_session(__CLASS__, __FUNCTION__);
516
+			if (! $clear_session_requested) {
517
+				EE_Error::add_attention(
518
+					EE_Registry::$i18n_js_strings['registration_expiration_notice'],
519
+					__FILE__,
520
+					__FUNCTION__,
521
+					__LINE__
522
+				);
523
+			}
524
+		}
525
+	}
526
+
527
+
528
+	/**
529
+	 * loads and instantiates EE_Checkout
530
+	 *
531
+	 * @return EE_Checkout
532
+	 * @throws EE_Error
533
+	 * @throws ReflectionException
534
+	 */
535
+	private function _initialize_checkout(): EE_Checkout
536
+	{
537
+		// look in session for existing checkout
538
+		/** @type EE_Checkout $checkout */
539
+		$checkout = $this->session->checkout();
540
+		// verify
541
+		if (! $checkout instanceof EE_Checkout) {
542
+			// instantiate EE_Checkout object for handling the properties of the current checkout process
543
+			$checkout = EE_Registry::instance()->load_file(
544
+				SPCO_INC_PATH,
545
+				'EE_Checkout',
546
+				'class',
547
+				[],
548
+				false
549
+			);
550
+		} else {
551
+			if ($checkout->current_step->is_final_step() && $checkout->exit_spco() === true) {
552
+				$this->unlock_transaction();
553
+				wp_safe_redirect($checkout->redirect_url);
554
+				exit();
555
+			}
556
+		}
557
+		$checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
558
+		// verify again
559
+		if (! $checkout instanceof EE_Checkout) {
560
+			throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
561
+		}
562
+		// reset anything that needs a clean slate for each request
563
+		$checkout->reset_for_current_request();
564
+		return $checkout;
565
+	}
566
+
567
+
568
+	/**
569
+	 * @return void
570
+	 */
571
+	private function _get_request_vars()
572
+	{
573
+		// make sure this request is marked as belonging to EE
574
+		/** @var CurrentPage $current_page */
575
+		$current_page = LoaderFactory::getLoader()->getShared(CurrentPage::class);
576
+		$current_page->setEspressoPage(true);
577
+		// which step is being requested ?
578
+		$this->checkout->step = $this->request->getRequestParam('step', $this->_get_first_step());
579
+		// which step is being edited ?
580
+		$this->checkout->edit_step = $this->request->getRequestParam('edit_step');
581
+		// and what we're doing on the current step
582
+		$this->checkout->action = $this->request->getRequestParam('action', 'display_spco_reg_step');
583
+		// timestamp
584
+		$this->checkout->uts = $this->request->getRequestParam('uts', 0, 'int');
585
+		// returning to edit ?
586
+		$this->checkout->reg_url_link = $this->request->getRequestParam('e_reg_url_link');
587
+		// add reg url link to registration query params
588
+		if ($this->checkout->reg_url_link && strpos($this->checkout->reg_url_link, '1-') !== 0) {
589
+			$this->checkout->reg_cache_where_params[0]['REG_url_link'] = $this->checkout->reg_url_link;
590
+		}
591
+		// or some other kind of revisit ?
592
+		$this->checkout->revisit = $this->request->getRequestParam('revisit', false, 'bool');
593
+		// and whether to generate a reg form for this request
594
+		$this->checkout->generate_reg_form = $this->request->getRequestParam('generate_reg_form', true, 'bool');
595
+		// and whether to process a reg form submission for this request
596
+		$this->checkout->process_form_submission = $this->request->getRequestParam(
597
+			'process_form_submission',
598
+			$this->checkout->action === 'process_reg_step',
599
+			'bool'
600
+		);
601
+		$this->checkout->process_form_submission = filter_var(
602
+			$this->checkout->action !== 'display_spco_reg_step'
603
+				? $this->checkout->process_form_submission
604
+				: false,
605
+			FILTER_VALIDATE_BOOLEAN
606
+		);
607
+		$this->_display_request_vars();
608
+	}
609
+
610
+
611
+	/**
612
+	 * @return void
613
+	 */
614
+	protected function _display_request_vars()
615
+	{
616
+		if (! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
617
+			return;
618
+		}
619
+		EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
620
+		EEH_Debug_Tools::printr($this->checkout->step, '$this->checkout->step', __FILE__, __LINE__);
621
+		EEH_Debug_Tools::printr($this->checkout->edit_step, '$this->checkout->edit_step', __FILE__, __LINE__);
622
+		EEH_Debug_Tools::printr($this->checkout->action, '$this->checkout->action', __FILE__, __LINE__);
623
+		EEH_Debug_Tools::printr($this->checkout->reg_url_link, '$this->checkout->reg_url_link', __FILE__, __LINE__);
624
+		EEH_Debug_Tools::printr($this->checkout->revisit, '$this->checkout->revisit', __FILE__, __LINE__);
625
+		EEH_Debug_Tools::printr(
626
+			$this->checkout->generate_reg_form,
627
+			'$this->checkout->generate_reg_form',
628
+			__FILE__,
629
+			__LINE__
630
+		);
631
+		EEH_Debug_Tools::printr(
632
+			$this->checkout->process_form_submission,
633
+			'$this->checkout->process_form_submission',
634
+			__FILE__,
635
+			__LINE__
636
+		);
637
+	}
638
+
639
+
640
+	/**
641
+	 * _block_bots
642
+	 * checks that the incoming request has either of the following set:
643
+	 *  a UTS (unix timestamp) which indicates that the request was redirected from the Ticket Selector
644
+	 *  a REG URL Link, which indicates that the request is a return visit to SPCO for a valid TXN
645
+	 * so if you're not coming from the Ticket Selector nor returning for a valid IP...
646
+	 * then where you coming from man?
647
+	 *
648
+	 * @return boolean
649
+	 */
650
+	private function _block_bots(): bool
651
+	{
652
+		return EED_Invalid_Checkout_Access::getInvalidCheckoutAccess()->checkoutAccessIsInvalid($this->checkout);
653
+	}
654
+
655
+
656
+	/**
657
+	 *  gets slug for first step in $_reg_steps_array
658
+	 *
659
+	 * @return string
660
+	 */
661
+	private function _get_first_step(): string
662
+	{
663
+		$first_step = reset(EED_Single_Page_Checkout::$_reg_steps_array);
664
+		return $first_step['slug'] ?? 'attendee_information';
665
+	}
666
+
667
+
668
+	/**
669
+	 * instantiates each reg step based on the loaded reg_steps array
670
+	 *
671
+	 * @return bool
672
+	 * @throws EE_Error
673
+	 * @throws InvalidArgumentException
674
+	 * @throws InvalidDataTypeException
675
+	 * @throws InvalidInterfaceException
676
+	 * @throws ReflectionException
677
+	 */
678
+	private function _load_and_instantiate_reg_steps(): bool
679
+	{
680
+		do_action('AHEE__Single_Page_Checkout___load_and_instantiate_reg_steps__start', $this->checkout);
681
+		// have reg_steps already been instantiated ?
682
+		if (
683
+			empty($this->checkout->reg_steps)
684
+			|| apply_filters('FHEE__Single_Page_Checkout__load_reg_steps__reload_reg_steps', false, $this->checkout)
685
+		) {
686
+			// if not, then loop through raw reg steps array
687
+			foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
688
+				if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
689
+					return false;
690
+				}
691
+			}
692
+			if (isset($this->checkout->reg_steps['registration_confirmation'])) {
693
+				// skip the registration_confirmation page ?
694
+				// just remove it from the reg steps array
695
+				$this->checkout->remove_reg_step('registration_confirmation', false);
696
+			}
697
+			// filter the array for good luck
698
+			$this->checkout->reg_steps = apply_filters(
699
+				'FHEE__Single_Page_Checkout__load_reg_steps__reg_steps',
700
+				$this->checkout->reg_steps
701
+			);
702
+			// finally re-sort based on the reg step class order properties
703
+			$this->checkout->sort_reg_steps();
704
+		} else {
705
+			foreach ($this->checkout->reg_steps as $reg_step) {
706
+				// set all current step stati to FALSE
707
+				$reg_step->set_is_current_step(false);
708
+			}
709
+		}
710
+		if (empty($this->checkout->reg_steps)) {
711
+			EE_Error::add_error(
712
+				esc_html__('No Reg Steps were loaded..', 'event_espresso'),
713
+				__FILE__,
714
+				__FUNCTION__,
715
+				__LINE__
716
+			);
717
+			return false;
718
+		}
719
+		// make reg step details available to JS
720
+		$this->checkout->set_reg_step_JSON_info();
721
+		return true;
722
+	}
723
+
724
+
725
+	/**
726
+	 * @param array $reg_step
727
+	 * @param int   $order
728
+	 * @return bool
729
+	 * @throws EE_Error
730
+	 * @throws ReflectionException
731
+	 */
732
+	private function _load_and_instantiate_reg_step(array $reg_step = [], int $order = 0): bool
733
+	{
734
+		// we need a file_path, class_name, and slug to add a reg step
735
+		if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
736
+			// if editing a specific step, but this is NOT that step... (and it's not the 'finalize_registration' step)
737
+			if (
738
+				$this->checkout->reg_url_link
739
+				&& $this->checkout->step !== $reg_step['slug']
740
+				&& $reg_step['slug'] !== 'finalize_registration'
741
+				// normally at this point we would NOT load the reg step, but this filter can change that
742
+				&& apply_filters(
743
+					'FHEE__Single_Page_Checkout___load_and_instantiate_reg_step__bypass_reg_step',
744
+					true,
745
+					$reg_step,
746
+					$this->checkout
747
+				)
748
+			) {
749
+				return true;
750
+			}
751
+
752
+			// instantiate step class using file path and class name
753
+			$reg_step_obj = EE_Registry::instance()->load_file(
754
+				$reg_step['file_path'],
755
+				$reg_step['class_name'],
756
+				'class',
757
+				[$this->checkout],
758
+				false
759
+			);
760
+			// did we get the goods ?
761
+			if ($reg_step_obj instanceof EE_SPCO_Reg_Step) {
762
+				// set reg step order based on config
763
+				$reg_step_obj->set_order($order);
764
+				// add instantiated reg step object to the master reg steps array
765
+				$this->checkout->add_reg_step($reg_step_obj);
766
+			} else {
767
+				EE_Error::add_error(
768
+					esc_html__('The current step could not be set.', 'event_espresso'),
769
+					__FILE__,
770
+					__FUNCTION__,
771
+					__LINE__
772
+				);
773
+				return false;
774
+			}
775
+		} else {
776
+			if (WP_DEBUG) {
777
+				EE_Error::add_error(
778
+					sprintf(
779
+						esc_html__(
780
+							'A registration step could not be loaded. One or more of the following data points is invalid:%4$s%5$sFile Path: %1$s%6$s%5$sClass Name: %2$s%6$s%5$sSlug: %3$s%6$s%7$s',
781
+							'event_espresso'
782
+						),
783
+						$reg_step['file_path'] ?? '',
784
+						$reg_step['class_name'] ?? '',
785
+						$reg_step['slug'] ?? '',
786
+						'<ul>',
787
+						'<li>',
788
+						'</li>',
789
+						'</ul>'
790
+					),
791
+					__FILE__,
792
+					__FUNCTION__,
793
+					__LINE__
794
+				);
795
+			}
796
+			return false;
797
+		}
798
+		return true;
799
+	}
800
+
801
+
802
+	/**
803
+	 * @return bool
804
+	 * @throws EE_Error
805
+	 * @throws ReflectionException
806
+	 */
807
+	private function _verify_transaction_and_get_registrations(): bool
808
+	{
809
+		// was there already a valid transaction in the checkout from the session ?
810
+		if (! $this->checkout->transaction instanceof EE_Transaction) {
811
+			// get transaction from db or session
812
+			$this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
813
+				? $this->_get_transaction_and_cart_for_previous_visit()
814
+				: $this->_get_cart_for_current_session_and_setup_new_transaction();
815
+			if (! $this->checkout->transaction instanceof EE_Transaction) {
816
+				EE_Error::add_error(
817
+					esc_html__(
818
+						'Your Registration and Transaction information could not be retrieved from the db.',
819
+						'event_espresso'
820
+					),
821
+					__FILE__,
822
+					__FUNCTION__,
823
+					__LINE__
824
+				);
825
+				$this->checkout->transaction = EE_Transaction::new_instance();
826
+				// add some style and make it dance
827
+				$this->add_styles_and_scripts($this);
828
+				EED_Single_Page_Checkout::$_initialized = true;
829
+				return false;
830
+			}
831
+			// and the registrations for the transaction
832
+			$this->_get_registrations($this->checkout->transaction);
833
+		}
834
+		return true;
835
+	}
836
+
837
+
838
+	/**
839
+	 * @return EE_Transaction|null
840
+	 * @throws EE_Error
841
+	 * @throws ReflectionException
842
+	 */
843
+	private function _get_transaction_and_cart_for_previous_visit(): ?EE_Transaction
844
+	{
845
+		/** @var $TXN_model EEM_Transaction */
846
+		$TXN_model = EE_Registry::instance()->load_model('Transaction');
847
+		// because the reg_url_link is present in the request,
848
+		// this is a return visit to SPCO, so we'll get the transaction data from the db
849
+		$transaction = $TXN_model->get_transaction_from_reg_url_link($this->checkout->reg_url_link);
850
+		// verify transaction
851
+		if ($transaction instanceof EE_Transaction) {
852
+			// and get the cart that was used for that transaction
853
+			$this->checkout->cart = $this->_get_cart_for_transaction($transaction);
854
+			return $transaction;
855
+		}
856
+		EE_Error::add_error(
857
+			esc_html__(
858
+				'Your Registration and Transaction information could not be retrieved from the db.',
859
+				'event_espresso'
860
+			),
861
+			__FILE__,
862
+			__FUNCTION__,
863
+			__LINE__
864
+		);
865
+		return null;
866
+	}
867
+
868
+
869
+	/**
870
+	 * @param EE_Transaction|null $transaction
871
+	 * @return EE_Cart
872
+	 */
873
+	private function _get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
874
+	{
875
+		return $this->checkout->get_cart_for_transaction($transaction);
876
+	}
877
+
878
+
879
+	/**
880
+	 * @param EE_Transaction|null $transaction
881
+	 * @return EE_Cart
882
+	 */
883
+	public function get_cart_for_transaction(?EE_Transaction $transaction): EE_Cart
884
+	{
885
+		return $this->checkout->get_cart_for_transaction($transaction);
886
+	}
887
+
888
+
889
+	/**
890
+	 * generates a new EE_Transaction object and adds it to the $_transaction property.
891
+	 *
892
+	 * @return EE_Transaction|null
893
+	 * @throws EE_Error
894
+	 * @throws ReflectionException
895
+	 */
896
+	private function _get_cart_for_current_session_and_setup_new_transaction(): ?EE_Transaction
897
+	{
898
+		//  if there's no transaction, then this is the FIRST visit to SPCO
899
+		// so load up the cart ( passing nothing for the TXN because it doesn't exist yet )
900
+		$this->checkout->cart = $this->_get_cart_for_transaction(null);
901
+		// and then create a new transaction
902
+		$transaction = $this->_initialize_transaction();
903
+		// verify transaction
904
+		if ($transaction instanceof EE_Transaction) {
905
+			// and save TXN data to the cart
906
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn($transaction->ID());
907
+		} else {
908
+			EE_Error::add_error(
909
+				esc_html__('A Valid Transaction could not be initialized.', 'event_espresso'),
910
+				__FILE__,
911
+				__FUNCTION__,
912
+				__LINE__
913
+			);
914
+		}
915
+		return $transaction;
916
+	}
917
+
918
+
919
+	/**
920
+	 * generates a new EE_Transaction object and adds it to the $_transaction property.
921
+	 *
922
+	 * @return EE_Transaction|null
923
+	 */
924
+	private function _initialize_transaction(): ?EE_Transaction
925
+	{
926
+		try {
927
+			// ensure cart totals have been calculated
928
+			$this->checkout->cart->get_grand_total()->recalculate_total_including_taxes();
929
+			// grab the cart grand total
930
+			$cart_total = $this->checkout->cart->get_cart_grand_total();
931
+			// create new TXN
932
+			$transaction = EE_Transaction::new_instance(
933
+				[
934
+					'TXN_reg_steps' => $this->checkout->initialize_txn_reg_steps_array(),
935
+					'TXN_total'     => max($cart_total, 0),
936
+					'TXN_paid'      => 0,
937
+					'STS_ID'        => EEM_Transaction::failed_status_code,
938
+				]
939
+			);
940
+			// save it so that we have an ID for other objects to use
941
+			$transaction->save();
942
+			// set cron job for following up on TXNs after their session has expired
943
+			EE_Cron_Tasks::schedule_expired_transaction_check(
944
+				$this->session->expiration() + 1,
945
+				$transaction->ID()
946
+			);
947
+			return $transaction;
948
+		} catch (Exception $e) {
949
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
950
+		}
951
+		return null;
952
+	}
953
+
954
+
955
+	/**
956
+	 * _get_registrations
957
+	 *
958
+	 * @param EE_Transaction $transaction
959
+	 * @return void
960
+	 * @throws EE_Error
961
+	 * @throws ReflectionException
962
+	 */
963
+	private function _get_registrations(EE_Transaction $transaction)
964
+	{
965
+		// first step: grab the registrants  { : o
966
+		$registrations                      = $transaction->registrations($this->checkout->reg_cache_where_params);
967
+		$this->checkout->total_ticket_count = count($registrations);
968
+		// verify registrations have been set
969
+		if (empty($registrations)) {
970
+			// if no cached registrations, then check the db
971
+			$registrations = $transaction->registrations($this->checkout->reg_cache_where_params);
972
+			// still nothing ? well as long as this isn't a revisit
973
+			if (empty($registrations) && ! $this->checkout->revisit) {
974
+				// generate new registrations from scratch
975
+				$registrations = $this->_initialize_registrations($transaction);
976
+			}
977
+		}
978
+		// sort by their original registration order
979
+		usort($registrations, ['EED_Single_Page_Checkout', 'sort_registrations_by_REG_count']);
980
+		// then loop thru the array
981
+		foreach ($registrations as $registration) {
982
+			// verify each registration
983
+			if ($registration instanceof EE_Registration) {
984
+				// we display all attendee info for the primary registrant
985
+				if (
986
+					$this->checkout->reg_url_link === $registration->reg_url_link()
987
+					&& $registration->is_primary_registrant()
988
+				) {
989
+					$this->checkout->primary_revisit = true;
990
+					break;
991
+				}
992
+				if ($this->checkout->revisit && $this->checkout->reg_url_link !== $registration->reg_url_link()) {
993
+					// but hide info if it doesn't belong to you
994
+					$transaction->clear_cache('Registration', $registration->ID());
995
+					$this->checkout->total_ticket_count--;
996
+				}
997
+				$this->checkout->set_reg_status_updated($registration->ID(), false);
998
+			}
999
+		}
1000
+	}
1001
+
1002
+
1003
+	/**
1004
+	 * adds related EE_Registration objects for each ticket in the cart to the current EE_Transaction object
1005
+	 *
1006
+	 * @param EE_Transaction $transaction
1007
+	 * @return array
1008
+	 * @throws EE_Error
1009
+	 * @throws ReflectionException
1010
+	 */
1011
+	private function _initialize_registrations(EE_Transaction $transaction): array
1012
+	{
1013
+		$att_nmbr      = 0;
1014
+		$registrations = [];
1015
+		/** @type EE_Registration_Processor $registration_processor */
1016
+		$registration_processor             = EE_Registry::instance()->load_class('Registration_Processor');
1017
+		$this->checkout->total_ticket_count = $this->checkout->cart->all_ticket_quantity_count();
1018
+		// now let's add the cart items to the $transaction
1019
+		foreach ($this->checkout->cart->get_tickets() as $line_item) {
1020
+			// do the following for each ticket of this type they selected
1021
+			for ($x = 1; $x <= $line_item->quantity(); $x++) {
1022
+				$att_nmbr++;
1023
+				/** @var CreateRegistrationCommand $CreateRegistrationCommand */
1024
+				$CreateRegistrationCommand = EE_Registry::instance()->create(
1025
+					CreateRegistrationCommand::class,
1026
+					[
1027
+						$transaction,
1028
+						$line_item,
1029
+						$att_nmbr,
1030
+						$this->checkout->total_ticket_count,
1031
+					]
1032
+				);
1033
+				// override capabilities for frontend registrations
1034
+				if ($this->request->isFrontend()) {
1035
+					$CreateRegistrationCommand->setCapCheck(
1036
+						new PublicCapabilities('', 'create_new_registration')
1037
+					);
1038
+				}
1039
+				$registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1040
+				if (! $registration instanceof EE_Registration) {
1041
+					throw new InvalidEntityException($registration, 'EE_Registration');
1042
+				}
1043
+				$registrations[ $registration->ID() ] = $registration;
1044
+			}
1045
+		}
1046
+		$registration_processor->fix_reg_final_price_rounding_issue($transaction);
1047
+		return $registrations;
1048
+	}
1049
+
1050
+
1051
+	/**
1052
+	 * @param EE_Registration $reg_A
1053
+	 * @param EE_Registration $reg_B
1054
+	 * @return int
1055
+	 * @throws EE_Error
1056
+	 * @throws ReflectionException
1057
+	 */
1058
+	public static function sort_registrations_by_REG_count(EE_Registration $reg_A, EE_Registration $reg_B): int
1059
+	{
1060
+		// this shouldn't ever happen within the same TXN, but oh well
1061
+		if ($reg_A->count() === $reg_B->count()) {
1062
+			return 0;
1063
+		}
1064
+		return ($reg_A->count() > $reg_B->count()) ? 1 : -1;
1065
+	}
1066
+
1067
+
1068
+	/**
1069
+	 * just makes sure that everything is set up correctly before proceeding
1070
+	 *
1071
+	 * @return bool
1072
+	 * @throws EE_Error
1073
+	 * @throws ReflectionException
1074
+	 */
1075
+	private function _final_verifications(): bool
1076
+	{
1077
+		// filter checkout
1078
+		$this->checkout = apply_filters(
1079
+			'FHEE__EED_Single_Page_Checkout___final_verifications__checkout',
1080
+			$this->checkout
1081
+		);
1082
+		// verify that current step is still set correctly
1083
+		if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1084
+			EE_Error::add_error(
1085
+				esc_html__(
1086
+					'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
1087
+					'event_espresso'
1088
+				),
1089
+				__FILE__,
1090
+				__FUNCTION__,
1091
+				__LINE__
1092
+			);
1093
+			return false;
1094
+		}
1095
+		// if returning to SPCO, then verify that primary registrant is set
1096
+		if (! empty($this->checkout->reg_url_link)) {
1097
+			$valid_registrant = $this->checkout->transaction->primary_registration();
1098
+			if (! $valid_registrant instanceof EE_Registration) {
1099
+				EE_Error::add_error(
1100
+					esc_html__(
1101
+						'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
1102
+						'event_espresso'
1103
+					),
1104
+					__FILE__,
1105
+					__FUNCTION__,
1106
+					__LINE__
1107
+				);
1108
+				return false;
1109
+			}
1110
+			$valid_registrant = null;
1111
+			foreach (
1112
+				$this->checkout->transaction->registrations(
1113
+					$this->checkout->reg_cache_where_params
1114
+				) as $registration
1115
+			) {
1116
+				if (
1117
+					$registration instanceof EE_Registration
1118
+					&& $registration->reg_url_link() === $this->checkout->reg_url_link
1119
+				) {
1120
+					$valid_registrant = $registration;
1121
+				}
1122
+			}
1123
+			if (! $valid_registrant instanceof EE_Registration) {
1124
+				// hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1125
+				if (EED_Single_Page_Checkout::$_checkout_verified) {
1126
+					// clear the session, mark the checkout as unverified, and try again
1127
+					$this->session->clear_session(__CLASS__, __FUNCTION__);
1128
+					EED_Single_Page_Checkout::$_initialized       = false;
1129
+					EED_Single_Page_Checkout::$_checkout_verified = false;
1130
+					$this->_initialize();
1131
+					EE_Error::reset_notices();
1132
+					return false;
1133
+				}
1134
+				EE_Error::add_error(
1135
+					esc_html__(
1136
+						'We\'re sorry but there appears to be an error with the "reg_url_link" or the transaction itself. Please refresh the page and try again or contact support.',
1137
+						'event_espresso'
1138
+					),
1139
+					__FILE__,
1140
+					__FUNCTION__,
1141
+					__LINE__
1142
+				);
1143
+				return false;
1144
+			}
1145
+		}
1146
+		// now that things have been kinda sufficiently verified,
1147
+		// let's add the checkout to the session so that it's available to other systems
1148
+		$this->session->set_checkout($this->checkout);
1149
+		return true;
1150
+	}
1151
+
1152
+
1153
+	/**
1154
+	 * first makes sure that EE_Transaction_Processor::set_reg_step_initiated() is called as required
1155
+	 * then loops thru all the active reg steps and calls the initialize_reg_step() method
1156
+	 *
1157
+	 * @param bool $reinitializing
1158
+	 * @throws EE_Error
1159
+	 * @throws ReflectionException
1160
+	 */
1161
+	private function _initialize_reg_steps(bool $reinitializing = false)
1162
+	{
1163
+		$this->checkout->set_reg_step_initiated($this->checkout->current_step);
1164
+		// loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1165
+		foreach ($this->checkout->reg_steps as $reg_step) {
1166
+			if (! $reg_step->initialize_reg_step()) {
1167
+				// if not initialized then maybe this step is being removed...
1168
+				if (! $reinitializing && $reg_step->is_current_step()) {
1169
+					// if it was the current step, then we need to start over here
1170
+					$this->_initialize_reg_steps(true);
1171
+					return;
1172
+				}
1173
+				continue;
1174
+			}
1175
+			// add css and JS for current step
1176
+			$this->add_styles_and_scripts($reg_step);
1177
+			if ($reg_step->is_current_step()) {
1178
+				// the text that appears on the reg step form submit button
1179
+				$reg_step->set_submit_button_text();
1180
+			}
1181
+		}
1182
+		// dynamically creates hook point like: AHEE__Single_Page_Checkout___initialize_reg_step__attendee_information
1183
+		do_action(
1184
+			"AHEE__Single_Page_Checkout___initialize_reg_step__{$this->checkout->current_step->slug()}",
1185
+			$this->checkout->current_step
1186
+		);
1187
+		$this->checkout->json_response->set_return_data(
1188
+			[
1189
+				'action'        => $this->checkout->action,
1190
+				'admin_request' => $this->checkout->admin_request,
1191
+				'current_step'  => $this->checkout->current_step->slug(),
1192
+				'revisit'       => $this->checkout->revisit,
1193
+			]
1194
+		);
1195
+	}
1196
+
1197
+
1198
+	/**
1199
+	 * @return boolean
1200
+	 * @throws EE_Error
1201
+	 * @throws ReflectionException
1202
+	 */
1203
+	private function _check_form_submission(): bool
1204
+	{
1205
+		// does this request require the reg form to be generated ?
1206
+		if ($this->checkout->generate_reg_form) {
1207
+			// ever heard that song by Blue Rodeo ?
1208
+			try {
1209
+				$this->checkout->current_step->reg_form = $this->checkout->current_step->generate_reg_form();
1210
+				// if not displaying a form, then check for form submission
1211
+				if (
1212
+					$this->checkout->process_form_submission
1213
+					&& $this->checkout->current_step->reg_form->was_submitted()
1214
+				) {
1215
+					// clear out any old data in case this step is being run again
1216
+					$this->checkout->current_step->set_valid_data([]);
1217
+					// capture submitted form data
1218
+					$request_data = $this->request->requestParams();
1219
+					$this->checkout->current_step->reg_form->receive_form_submission(
1220
+						(array) apply_filters(
1221
+							'FHEE__Single_Page_Checkout___check_form_submission__request_params',
1222
+							$request_data,
1223
+							$this->checkout
1224
+						)
1225
+					);
1226
+					// validate submitted form data
1227
+					if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1228
+						// thou shall not pass !!!
1229
+						$this->checkout->continue_reg = false;
1230
+						// any form validation errors?
1231
+						if ($this->checkout->current_step->reg_form->submission_error_message() !== '') {
1232
+							EE_Error::add_error(
1233
+								$this->checkout->current_step->reg_form->submission_error_message(),
1234
+								__FILE__,
1235
+								__FUNCTION__,
1236
+								__LINE__
1237
+							);
1238
+						}
1239
+						// well not really... what will happen is
1240
+						// we'll just get redirected back to redo the current step
1241
+						$this->go_to_next_step();
1242
+						return false;
1243
+					}
1244
+				}
1245
+			} catch (EE_Error $e) {
1246
+				$e->get_error();
1247
+			}
1248
+		}
1249
+		return true;
1250
+	}
1251
+
1252
+
1253
+	/**
1254
+	 * @return void
1255
+	 * @throws EE_Error
1256
+	 * @throws ReflectionException
1257
+	 */
1258
+	private function _process_form_action()
1259
+	{
1260
+		// dynamically creates hook point like:
1261
+		//   AHEE__Single_Page_Checkout__before_attendee_information__process_reg_step
1262
+		do_action(
1263
+			"AHEE__Single_Page_Checkout__before_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1264
+			$this->checkout->current_step
1265
+		);
1266
+		// what cha wanna do?
1267
+		switch ($this->checkout->action) {
1268
+			// AJAX next step reg form
1269
+			case 'display_spco_reg_step':
1270
+				$this->checkout->redirect = false;
1271
+				if ($this->request->isAjax()) {
1272
+					$this->checkout->json_response->set_reg_step_html(
1273
+						$this->checkout->current_step->display_reg_form()
1274
+					);
1275
+				}
1276
+				break;
1277
+			default:
1278
+				// meh... do one of those other steps first
1279
+				if (
1280
+					! empty($this->checkout->action)
1281
+					&& is_callable([$this->checkout->current_step, $this->checkout->action])
1282
+				) {
1283
+					$process_reg_step = apply_filters(
1284
+						"AHEE__Single_Page_Checkout__process_reg_step__{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1285
+						true,
1286
+						$this->checkout->current_step,
1287
+						$this
1288
+					);
1289
+					// call action on current step
1290
+					if ($process_reg_step && call_user_func([$this->checkout->current_step, $this->checkout->action])) {
1291
+						// good registrant, you get to proceed
1292
+						if (
1293
+							$this->checkout->current_step->success_message() !== ''
1294
+							&& apply_filters(
1295
+								'FHEE__Single_Page_Checkout___process_form_action__display_success',
1296
+								false
1297
+							)
1298
+						) {
1299
+							EE_Error::add_success(
1300
+								$this->checkout->current_step->success_message()
1301
+								. '<br />' . $this->checkout->next_step->_instructions()
1302
+							);
1303
+						}
1304
+						// pack it up, pack it in...
1305
+						$this->_setup_redirect();
1306
+					}
1307
+				} else {
1308
+					EE_Error::add_error(
1309
+						sprintf(
1310
+							esc_html__(
1311
+								'The requested form action "%s" does not exist for the current "%s" registration step.',
1312
+								'event_espresso'
1313
+							),
1314
+							$this->checkout->action,
1315
+							$this->checkout->current_step->name()
1316
+						),
1317
+						__FILE__,
1318
+						__FUNCTION__,
1319
+						__LINE__
1320
+					);
1321
+				}
1322
+			// end default
1323
+		}
1324
+		// dynamically creates hook point like:
1325
+		//  AHEE__Single_Page_Checkout__after_payment_options__process_reg_step
1326
+		do_action(
1327
+			"AHEE__Single_Page_Checkout__after_{$this->checkout->current_step->slug()}__{$this->checkout->action}",
1328
+			$this->checkout->current_step
1329
+		);
1330
+		// store our progress so far
1331
+		$this->checkout->stash_transaction_and_checkout();
1332
+		// advance to the next step! If you pass GO, collect $200
1333
+		$this->go_to_next_step();
1334
+	}
1335
+
1336
+
1337
+	/**
1338
+	 * @param EED_Single_Page_Checkout|EE_SPCO_Reg_Step $target an object with the method `translate_js_strings` and
1339
+	 *                                                          `enqueue_styles_and_scripts`.
1340
+	 * @return void
1341
+	 */
1342
+	public function add_styles_and_scripts($target)
1343
+	{
1344
+		// i18n
1345
+		$target->translate_js_strings();
1346
+		if ($this->checkout->admin_request) {
1347
+			add_action('admin_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1348
+		} else {
1349
+			add_action('wp_enqueue_scripts', [$target, 'enqueue_styles_and_scripts']);
1350
+		}
1351
+	}
1352
+
1353
+
1354
+	/**
1355
+	 * @return void
1356
+	 */
1357
+	public function translate_js_strings()
1358
+	{
1359
+		EE_Registry::$i18n_js_strings['revisit']                        = $this->checkout->revisit;
1360
+		EE_Registry::$i18n_js_strings['e_reg_url_link']                 = $this->checkout->reg_url_link;
1361
+		EE_Registry::$i18n_js_strings['server_error']                   = esc_html__(
1362
+			'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1363
+			'event_espresso'
1364
+		);
1365
+		EE_Registry::$i18n_js_strings['invalid_json_response']          = esc_html__(
1366
+			'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1367
+			'event_espresso'
1368
+		);
1369
+		EE_Registry::$i18n_js_strings['validation_error']               = esc_html__(
1370
+			'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1371
+			'event_espresso'
1372
+		);
1373
+		EE_Registry::$i18n_js_strings['invalid_payment_method']         = esc_html__(
1374
+			'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1375
+			'event_espresso'
1376
+		);
1377
+		EE_Registry::$i18n_js_strings['reg_step_error']                 = esc_html__(
1378
+			'This registration step could not be completed. Please refresh the page and try again.',
1379
+			'event_espresso'
1380
+		);
1381
+		EE_Registry::$i18n_js_strings['invalid_coupon']                 = esc_html__(
1382
+			'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1383
+			'event_espresso'
1384
+		);
1385
+		EE_Registry::$i18n_js_strings['process_registration']           = sprintf(
1386
+			esc_html__(
1387
+				'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1388
+				'event_espresso'
1389
+			),
1390
+			'<br/>',
1391
+			'<br/>'
1392
+		);
1393
+		EE_Registry::$i18n_js_strings['language']                       = get_bloginfo('language');
1394
+		EE_Registry::$i18n_js_strings['EESID']                          = $this->session->id();
1395
+		EE_Registry::$i18n_js_strings['currency']                       = EE_Registry::instance()->CFG->currency;
1396
+		EE_Registry::$i18n_js_strings['datepicker_yearRange']           = '-150:+20';
1397
+		EE_Registry::$i18n_js_strings['timer_years']                    = esc_html__('years', 'event_espresso');
1398
+		EE_Registry::$i18n_js_strings['timer_months']                   = esc_html__('months', 'event_espresso');
1399
+		EE_Registry::$i18n_js_strings['timer_weeks']                    = esc_html__('weeks', 'event_espresso');
1400
+		EE_Registry::$i18n_js_strings['timer_days']                     = esc_html__('days', 'event_espresso');
1401
+		EE_Registry::$i18n_js_strings['timer_hours']                    = esc_html__('hours', 'event_espresso');
1402
+		EE_Registry::$i18n_js_strings['timer_minutes']                  = esc_html__('minutes', 'event_espresso');
1403
+		EE_Registry::$i18n_js_strings['timer_seconds']                  = esc_html__('seconds', 'event_espresso');
1404
+		EE_Registry::$i18n_js_strings['timer_year']                     = esc_html__('year', 'event_espresso');
1405
+		EE_Registry::$i18n_js_strings['timer_month']                    = esc_html__('month', 'event_espresso');
1406
+		EE_Registry::$i18n_js_strings['timer_week']                     = esc_html__('week', 'event_espresso');
1407
+		EE_Registry::$i18n_js_strings['timer_day']                      = esc_html__('day', 'event_espresso');
1408
+		EE_Registry::$i18n_js_strings['timer_hour']                     = esc_html__('hour', 'event_espresso');
1409
+		EE_Registry::$i18n_js_strings['timer_minute']                   = esc_html__('minute', 'event_espresso');
1410
+		EE_Registry::$i18n_js_strings['timer_second']                   = esc_html__('second', 'event_espresso');
1411
+		EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
1412
+			EED_Single_Page_Checkout::getRegistrationExpirationNotice();
1413
+		EE_Registry::$i18n_js_strings['ajax_submit']                    = apply_filters(
1414
+			'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1415
+			true
1416
+		);
1417
+		EE_Registry::$i18n_js_strings['session_extension']              = absint(
1418
+			apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1419
+		);
1420
+		EE_Registry::$i18n_js_strings['session_expiration']    = $this->session->expiration();
1421
+		EE_Registry::$i18n_js_strings['use_session_countdown'] = EE_Registry::instance()->CFG->registration->useSessionCountdown();
1422
+		EE_Registry::$i18n_js_strings['no_copy_paste_email_confirm'] = esc_html__("We're sorry but copy and paste is disabled for email confirmation inputs. Please enter the email address manually.", 'event_espresso');
1423
+	}
1424
+
1425
+
1426
+	/**
1427
+	 * @return void
1428
+	 * @throws EE_Error
1429
+	 */
1430
+	public function enqueue_styles_and_scripts()
1431
+	{
1432
+		// load css
1433
+		wp_register_style(
1434
+			'single_page_checkout',
1435
+			SPCO_CSS_URL . 'single_page_checkout.css',
1436
+			['espresso_default'],
1437
+			EVENT_ESPRESSO_VERSION
1438
+		);
1439
+		wp_enqueue_style('single_page_checkout');
1440
+		// load JS
1441
+		wp_register_script(
1442
+			'single_page_checkout',
1443
+			SPCO_JS_URL . 'single_page_checkout.js',
1444
+			['espresso_core', 'underscore', 'ee_form_section_validation'],
1445
+			EVENT_ESPRESSO_VERSION,
1446
+			true
1447
+		);
1448
+		if ($this->checkout->registration_form instanceof EE_Form_Section_Proper) {
1449
+			$this->checkout->registration_form->enqueue_js();
1450
+		}
1451
+		if ($this->checkout->current_step->reg_form instanceof EE_Form_Section_Proper) {
1452
+			$this->checkout->current_step->reg_form->enqueue_js();
1453
+		}
1454
+		wp_enqueue_script('single_page_checkout');
1455
+		/**
1456
+		 * global action hook for enqueueing styles and scripts with
1457
+		 * spco calls.
1458
+		 */
1459
+		do_action('AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts', $this);
1460
+		/**
1461
+		 * dynamic action hook for enqueueing styles and scripts with spco calls.
1462
+		 * The hook will end up being something like:
1463
+		 *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1464
+		 */
1465
+		do_action(
1466
+			'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1467
+			$this
1468
+		);
1469
+	}
1470
+
1471
+
1472
+	/**
1473
+	 * display the Registration Single Page Checkout Form
1474
+	 *
1475
+	 * @return void
1476
+	 * @throws EE_Error
1477
+	 * @throws ReflectionException
1478
+	 */
1479
+	private function _display_spco_reg_form()
1480
+	{
1481
+		// if registering via the admin, just display the reg form for the current step
1482
+		if ($this->checkout->admin_request) {
1483
+			EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->current_step->display_reg_form());
1484
+			return;
1485
+		}
1486
+
1487
+		// add powered by EE msg
1488
+		add_action('AHEE__SPCO__reg_form_footer', ['EED_Single_Page_Checkout', 'display_registration_footer']);
1489
+		$empty_cart = count(
1490
+				$this->checkout->transaction->registrations($this->checkout->reg_cache_where_params)
1491
+			) < 1;
1492
+		EE_Registry::$i18n_js_strings['empty_cart'] = $empty_cart;
1493
+
1494
+		/** @var FeatureFlags $feature */
1495
+		$feature = LoaderFactory::getShared(FeatureFlags::class);
1496
+
1497
+		$this->checkout->registration_form = $feature->allowed(FeatureFlag::USE_SPCO_FORM_REFACTOR)
1498
+			? new SinglePageCheckoutForm($this->checkout, $empty_cart)
1499
+			: new LegacySpcoForm($this->checkout, $empty_cart, $this->session->expiration());
1500
+
1501
+		// load template and add to output sent that gets filtered into the_content()
1502
+		EED_Single_Page_Checkout::getResponse()->addOutput($this->checkout->registration_form->get_html());
1503
+
1504
+	}
1505
+
1506
+
1507
+	/**
1508
+	 * @param $next_step
1509
+	 * @return void
1510
+	 */
1511
+	public function add_extra_finalize_registration_inputs($next_step)
1512
+	{
1513
+		if ($next_step === 'finalize_registration') {
1514
+			echo '<div id="spco-extra-finalize_registration-inputs-dv"></div>';
1515
+		}
1516
+	}
1517
+
1518
+
1519
+	/**
1520
+	 * @return void
1521
+	 */
1522
+	public static function display_registration_footer()
1523
+	{
1524
+		if (
1525
+			apply_filters(
1526
+				'FHEE__EE_Front__Controller__show_reg_footer',
1527
+				EE_Registry::instance()->CFG->admin->show_reg_footer
1528
+			)
1529
+		) {
1530
+			add_filter(
1531
+				'FHEE__EEH_Template__powered_by_event_espresso__url',
1532
+				function ($url) {
1533
+					return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1534
+				}
1535
+			);
1536
+			echo apply_filters(
1537
+				'FHEE__EE_Front_Controller__display_registration_footer',
1538
+				EEH_Template::powered_by_event_espresso(
1539
+					'',
1540
+					'espresso-registration-footer-dv',
1541
+					['utm_content' => 'registration_checkout']
1542
+				)
1543
+			);
1544
+		}
1545
+	}
1546
+
1547
+
1548
+	/**
1549
+	 * @return void
1550
+	 * @throws EE_Error
1551
+	 * @throws ReflectionException
1552
+	 */
1553
+	private function lockTransaction()
1554
+	{
1555
+		if ($this->checkout instanceof EE_Checkout && $this->checkout->transaction instanceof EE_Transaction) {
1556
+			// lock the transaction
1557
+			$this->checkout->transaction->lock(false);
1558
+			// set up transaction lock removal
1559
+			add_action('shutdown', [$this, 'unlock_transaction'], 1);
1560
+			add_filter(
1561
+				'wp_redirect',
1562
+				function (string $location) {
1563
+					// this is hacky, but WP doesn't supply an action to hook into before the redirect
1564
+					$this->unlock_transaction();
1565
+					return $location;
1566
+				}
1567
+			);
1568
+		}
1569
+	}
1570
+
1571
+
1572
+	/**
1573
+	 * @return void
1574
+	 * @throws EE_Error
1575
+	 * @throws ReflectionException
1576
+	 */
1577
+	public function unlock_transaction()
1578
+	{
1579
+		if ($this->checkout instanceof EE_Checkout && $this->checkout->transaction instanceof EE_Transaction) {
1580
+			$this->checkout->transaction->unlock();
1581
+		}
1582
+	}
1583
+
1584
+
1585
+	/**
1586
+	 * @return void
1587
+	 */
1588
+	private function _setup_redirect()
1589
+	{
1590
+		if ($this->checkout->continue_reg && $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
1591
+			$this->checkout->redirect = true;
1592
+			if (empty($this->checkout->redirect_url)) {
1593
+				$this->checkout->redirect_url = $this->checkout->next_step->reg_step_url();
1594
+			}
1595
+			$this->checkout->redirect_url = apply_filters(
1596
+				'FHEE__EED_Single_Page_Checkout___setup_redirect__checkout_redirect_url',
1597
+				$this->checkout->redirect_url,
1598
+				$this->checkout
1599
+			);
1600
+		}
1601
+	}
1602
+
1603
+
1604
+	/**
1605
+	 * handle ajax message responses and redirects
1606
+	 *
1607
+	 * @return void
1608
+	 * @throws EE_Error
1609
+	 * @throws ReflectionException
1610
+	 */
1611
+	public function go_to_next_step()
1612
+	{
1613
+		if ($this->request->isAjax()) {
1614
+			// capture contents of output buffer we started earlier in the request, and insert into JSON response
1615
+			$this->checkout->json_response->set_unexpected_errors(ob_get_clean());
1616
+		}
1617
+		$this->unlock_transaction();
1618
+		// just return for these conditions
1619
+		if (
1620
+			$this->checkout->admin_request
1621
+			|| $this->checkout->action === 'redirect_form'
1622
+			|| $this->checkout->action === 'update_checkout'
1623
+		) {
1624
+			return;
1625
+		}
1626
+		// AJAX response
1627
+		$this->_handle_json_response();
1628
+		// redirect to next step or the Thank-You page
1629
+		$this->_handle_html_redirects();
1630
+		// hmmm... must be something wrong, so let's just display the form again !
1631
+		$this->_display_spco_reg_form();
1632
+	}
1633
+
1634
+
1635
+	/**
1636
+	 * @return void
1637
+	 */
1638
+	protected function _handle_json_response()
1639
+	{
1640
+		// if this is an ajax request
1641
+		if ($this->request->isAjax()) {
1642
+			$this->checkout->json_response->set_registration_time_limit(
1643
+				$this->checkout->get_registration_time_limit()
1644
+			);
1645
+			$this->checkout->json_response->set_payment_amount($this->checkout->amount_owing);
1646
+			/** @var EE_SPCO_JSON_Response $json_response */
1647
+			$json_response = apply_filters(
1648
+				'FHEE__EE_Single_Page_Checkout__JSON_response',
1649
+				$this->checkout->json_response
1650
+			);
1651
+			// just send the ajax
1652
+			$json_response->sendResponse();
1653
+		}
1654
+	}
1655
+
1656
+
1657
+	/**
1658
+	 * @return void
1659
+	 */
1660
+	protected function _handle_html_redirects()
1661
+	{
1662
+		// going somewhere ?
1663
+		if ($this->checkout->redirect && ! empty($this->checkout->redirect_url)) {
1664
+			// store notices in a transient
1665
+			EE_Error::get_notices(false, true);
1666
+			wp_safe_redirect($this->checkout->redirect_url);
1667
+			exit();
1668
+		}
1669
+	}
1670
+
1671
+
1672
+	/**
1673
+	 * @param WP_Query $query
1674
+	 * @return void
1675
+	 */
1676
+	public function set_checkout_anchor(WP_Query $query)
1677
+	{
1678
+		if (! $query->is_main_query()) {
1679
+			return;
1680
+		}
1681
+		echo '<a id="checkout" class="screen-reader-text"></a>';
1682
+	}
1683
+
1684
+
1685
+	/**
1686
+	 * @return string
1687
+	 * @since 4.9.59.p
1688
+	 */
1689
+	public static function getRegistrationExpirationNotice(): string
1690
+	{
1691
+		return sprintf(
1692
+			esc_html__(
1693
+				'%1$sWe\'re sorry, but your registration time has expired.%2$s%3$s%4$sIf you still wish to complete your registration, please return to the %5$sEvent List%6$sEvent List%7$s and reselect your tickets if available. Please accept our apologies for any inconvenience this may have caused.%8$s',
1694
+				'event_espresso'
1695
+			),
1696
+			'<h4 class="important-notice">',
1697
+			'</h4>',
1698
+			'<br />',
1699
+			'<p>',
1700
+			'<a href="' . get_post_type_archive_link(EspressoPostType::EVENTS) . '" title="',
1701
+			'">',
1702
+			'</a>',
1703
+			'</p>'
1704
+		);
1705
+	}
1706 1706
 }
Please login to merge, or discard this patch.
Spacing   +50 added lines, -50 removed lines patch added patch discarded remove patch
@@ -51,7 +51,7 @@  discard block
 block discarded – undo
51 51
 
52 52
     public ?EE_Session $session = null;
53 53
 
54
-    private bool $debug = false;    //  true    false
54
+    private bool $debug = false; //  true    false
55 55
 
56 56
 
57 57
     /**
@@ -115,7 +115,7 @@  discard block
 block discarded – undo
115 115
     public static function set_hooks_admin()
116 116
     {
117 117
         EED_Single_Page_Checkout::set_definitions();
118
-        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
118
+        if ( ! (defined('DOING_AJAX') && DOING_AJAX)) {
119 119
             return;
120 120
         }
121 121
         // going to start an output buffer in case anything gets accidentally output
@@ -209,14 +209,14 @@  discard block
 block discarded – undo
209 209
         }
210 210
         define(
211 211
             'SPCO_BASE_PATH',
212
-            rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/') . '/'
212
+            rtrim(str_replace(['\\', '/'], '/', plugin_dir_path(__FILE__)), '/').'/'
213 213
         );
214
-        define('SPCO_CSS_URL', plugin_dir_url(__FILE__) . 'css/');
215
-        define('SPCO_IMG_URL', plugin_dir_url(__FILE__) . 'img/');
216
-        define('SPCO_JS_URL', plugin_dir_url(__FILE__) . 'js/');
217
-        define('SPCO_INC_PATH', SPCO_BASE_PATH . 'inc/');
218
-        define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH . 'reg_steps/');
219
-        define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH . 'templates/');
214
+        define('SPCO_CSS_URL', plugin_dir_url(__FILE__).'css/');
215
+        define('SPCO_IMG_URL', plugin_dir_url(__FILE__).'img/');
216
+        define('SPCO_JS_URL', plugin_dir_url(__FILE__).'js/');
217
+        define('SPCO_INC_PATH', SPCO_BASE_PATH.'inc/');
218
+        define('SPCO_REG_STEPS_PATH', SPCO_BASE_PATH.'reg_steps/');
219
+        define('SPCO_TEMPLATES_PATH', SPCO_BASE_PATH.'templates/');
220 220
         EEH_Autoloader::register_autoloaders_for_each_file_in_folder(SPCO_BASE_PATH, true);
221 221
         EE_Registry::$i18n_js_strings['registration_expiration_notice'] =
222 222
             EED_Single_Page_Checkout::getRegistrationExpirationNotice();
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
             // we need a
248 248
             if (isset($reg_step['file_path'], $reg_step['class_name'], $reg_step['slug'])) {
249 249
                 // copy over to the reg_steps_array
250
-                EED_Single_Page_Checkout::$_reg_steps_array[ $order ] = $reg_step;
250
+                EED_Single_Page_Checkout::$_reg_steps_array[$order] = $reg_step;
251 251
                 // register custom key route for each reg step
252 252
                 // ie: step=>"slug" - this is the entire reason we load the reg steps array now
253 253
                 EED_Module::registerRoute(
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
                 // add AJAX or other hooks
260 260
                 if (isset($reg_step['has_hooks']) && $reg_step['has_hooks']) {
261 261
                     // setup autoloaders if necessary
262
-                    if (! class_exists($reg_step['class_name'])) {
262
+                    if ( ! class_exists($reg_step['class_name'])) {
263 263
                         EEH_Autoloader::register_autoloaders_for_each_file_in_folder(
264 264
                             $reg_step['file_path'],
265 265
                             true
@@ -286,19 +286,19 @@  discard block
 block discarded – undo
286 286
         if (empty($reg_steps)) {
287 287
             $reg_steps = [
288 288
                 10  => [
289
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'attendee_information',
289
+                    'file_path'  => SPCO_REG_STEPS_PATH.'attendee_information',
290 290
                     'class_name' => 'EE_SPCO_Reg_Step_Attendee_Information',
291 291
                     'slug'       => 'attendee_information',
292 292
                     'has_hooks'  => false,
293 293
                 ],
294 294
                 30  => [
295
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'payment_options',
295
+                    'file_path'  => SPCO_REG_STEPS_PATH.'payment_options',
296 296
                     'class_name' => 'EE_SPCO_Reg_Step_Payment_Options',
297 297
                     'slug'       => 'payment_options',
298 298
                     'has_hooks'  => true,
299 299
                 ],
300 300
                 999 => [
301
-                    'file_path'  => SPCO_REG_STEPS_PATH . 'finalize_registration',
301
+                    'file_path'  => SPCO_REG_STEPS_PATH.'finalize_registration',
302 302
                     'class_name' => 'EE_SPCO_Reg_Step_Finalize_Registration',
303 303
                     'slug'       => 'finalize_registration',
304 304
                     'has_hooks'  => false,
@@ -444,7 +444,7 @@  discard block
 block discarded – undo
444 444
                 $this->checkout
445 445
             );
446 446
             // load the reg steps array
447
-            if (! $this->_load_and_instantiate_reg_steps()) {
447
+            if ( ! $this->_load_and_instantiate_reg_steps()) {
448 448
                 EED_Single_Page_Checkout::$_initialized = true;
449 449
                 return;
450 450
             }
@@ -453,7 +453,7 @@  discard block
 block discarded – undo
453 453
             // and the next step
454 454
             $this->checkout->set_next_step();
455 455
             // verify that everything has been set up correctly
456
-            if (! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
456
+            if ( ! ($this->_verify_transaction_and_get_registrations() && $this->_final_verifications())) {
457 457
                 EED_Single_Page_Checkout::$_initialized = true;
458 458
                 return;
459 459
             }
@@ -468,7 +468,7 @@  discard block
 block discarded – undo
468 468
             // DEBUG LOG
469 469
             // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
470 470
             // get reg form
471
-            if (! $this->_check_form_submission()) {
471
+            if ( ! $this->_check_form_submission()) {
472 472
                 EED_Single_Page_Checkout::$_initialized = true;
473 473
                 return;
474 474
             }
@@ -496,9 +496,9 @@  discard block
 block discarded – undo
496 496
      */
497 497
     private function _verify_session()
498 498
     {
499
-        if (! $this->session instanceof EE_Session) {
499
+        if ( ! $this->session instanceof EE_Session) {
500 500
             $this->session = LoaderFactory::getShared(EE_Session::class);
501
-            if (! $this->session instanceof EE_Session) {
501
+            if ( ! $this->session instanceof EE_Session) {
502 502
                 throw new EE_Error(esc_html__('The EE_Session class could not be loaded.', 'event_espresso'));
503 503
             }
504 504
         }
@@ -513,7 +513,7 @@  discard block
 block discarded – undo
513 513
         ) {
514 514
             $this->checkout = new EE_Checkout();
515 515
             $this->session->clear_session(__CLASS__, __FUNCTION__);
516
-            if (! $clear_session_requested) {
516
+            if ( ! $clear_session_requested) {
517 517
                 EE_Error::add_attention(
518 518
                     EE_Registry::$i18n_js_strings['registration_expiration_notice'],
519 519
                     __FILE__,
@@ -538,7 +538,7 @@  discard block
 block discarded – undo
538 538
         /** @type EE_Checkout $checkout */
539 539
         $checkout = $this->session->checkout();
540 540
         // verify
541
-        if (! $checkout instanceof EE_Checkout) {
541
+        if ( ! $checkout instanceof EE_Checkout) {
542 542
             // instantiate EE_Checkout object for handling the properties of the current checkout process
543 543
             $checkout = EE_Registry::instance()->load_file(
544 544
                 SPCO_INC_PATH,
@@ -556,7 +556,7 @@  discard block
 block discarded – undo
556 556
         }
557 557
         $checkout = apply_filters('FHEE__EED_Single_Page_Checkout___initialize_checkout__checkout', $checkout);
558 558
         // verify again
559
-        if (! $checkout instanceof EE_Checkout) {
559
+        if ( ! $checkout instanceof EE_Checkout) {
560 560
             throw new EE_Error(esc_html__('The EE_Checkout class could not be loaded.', 'event_espresso'));
561 561
         }
562 562
         // reset anything that needs a clean slate for each request
@@ -613,7 +613,7 @@  discard block
 block discarded – undo
613 613
      */
614 614
     protected function _display_request_vars()
615 615
     {
616
-        if (! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
616
+        if ( ! ($this->debug && defined('WP_DEBUG') && WP_DEBUG)) {
617 617
             return;
618 618
         }
619 619
         EEH_Debug_Tools::printr($this->request->requestParams(), 'requestParams', __FILE__, __LINE__);
@@ -685,7 +685,7 @@  discard block
 block discarded – undo
685 685
         ) {
686 686
             // if not, then loop through raw reg steps array
687 687
             foreach (EED_Single_Page_Checkout::$_reg_steps_array as $order => $reg_step) {
688
-                if (! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
688
+                if ( ! $this->_load_and_instantiate_reg_step($reg_step, $order)) {
689 689
                     return false;
690 690
                 }
691 691
             }
@@ -807,12 +807,12 @@  discard block
 block discarded – undo
807 807
     private function _verify_transaction_and_get_registrations(): bool
808 808
     {
809 809
         // was there already a valid transaction in the checkout from the session ?
810
-        if (! $this->checkout->transaction instanceof EE_Transaction) {
810
+        if ( ! $this->checkout->transaction instanceof EE_Transaction) {
811 811
             // get transaction from db or session
812 812
             $this->checkout->transaction = $this->checkout->reg_url_link && ! is_admin()
813 813
                 ? $this->_get_transaction_and_cart_for_previous_visit()
814 814
                 : $this->_get_cart_for_current_session_and_setup_new_transaction();
815
-            if (! $this->checkout->transaction instanceof EE_Transaction) {
815
+            if ( ! $this->checkout->transaction instanceof EE_Transaction) {
816 816
                 EE_Error::add_error(
817 817
                     esc_html__(
818 818
                         'Your Registration and Transaction information could not be retrieved from the db.',
@@ -1037,10 +1037,10 @@  discard block
 block discarded – undo
1037 1037
                     );
1038 1038
                 }
1039 1039
                 $registration = EE_Registry::instance()->BUS->execute($CreateRegistrationCommand);
1040
-                if (! $registration instanceof EE_Registration) {
1040
+                if ( ! $registration instanceof EE_Registration) {
1041 1041
                     throw new InvalidEntityException($registration, 'EE_Registration');
1042 1042
                 }
1043
-                $registrations[ $registration->ID() ] = $registration;
1043
+                $registrations[$registration->ID()] = $registration;
1044 1044
             }
1045 1045
         }
1046 1046
         $registration_processor->fix_reg_final_price_rounding_issue($transaction);
@@ -1080,7 +1080,7 @@  discard block
 block discarded – undo
1080 1080
             $this->checkout
1081 1081
         );
1082 1082
         // verify that current step is still set correctly
1083
-        if (! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1083
+        if ( ! $this->checkout->current_step instanceof EE_SPCO_Reg_Step) {
1084 1084
             EE_Error::add_error(
1085 1085
                 esc_html__(
1086 1086
                     'We\'re sorry but the registration process can not proceed because one or more registration steps were not setup correctly. Please refresh the page and try again or contact support.',
@@ -1093,9 +1093,9 @@  discard block
 block discarded – undo
1093 1093
             return false;
1094 1094
         }
1095 1095
         // if returning to SPCO, then verify that primary registrant is set
1096
-        if (! empty($this->checkout->reg_url_link)) {
1096
+        if ( ! empty($this->checkout->reg_url_link)) {
1097 1097
             $valid_registrant = $this->checkout->transaction->primary_registration();
1098
-            if (! $valid_registrant instanceof EE_Registration) {
1098
+            if ( ! $valid_registrant instanceof EE_Registration) {
1099 1099
                 EE_Error::add_error(
1100 1100
                     esc_html__(
1101 1101
                         'We\'re sorry but there appears to be an error with the "reg_url_link" or the primary registrant for this transaction. Please refresh the page and try again or contact support.',
@@ -1120,7 +1120,7 @@  discard block
 block discarded – undo
1120 1120
                     $valid_registrant = $registration;
1121 1121
                 }
1122 1122
             }
1123
-            if (! $valid_registrant instanceof EE_Registration) {
1123
+            if ( ! $valid_registrant instanceof EE_Registration) {
1124 1124
                 // hmmm... maybe we have the wrong session because the user is opening multiple tabs ?
1125 1125
                 if (EED_Single_Page_Checkout::$_checkout_verified) {
1126 1126
                     // clear the session, mark the checkout as unverified, and try again
@@ -1163,9 +1163,9 @@  discard block
 block discarded – undo
1163 1163
         $this->checkout->set_reg_step_initiated($this->checkout->current_step);
1164 1164
         // loop thru all steps to call their individual "initialize" methods and set i18n strings for JS
1165 1165
         foreach ($this->checkout->reg_steps as $reg_step) {
1166
-            if (! $reg_step->initialize_reg_step()) {
1166
+            if ( ! $reg_step->initialize_reg_step()) {
1167 1167
                 // if not initialized then maybe this step is being removed...
1168
-                if (! $reinitializing && $reg_step->is_current_step()) {
1168
+                if ( ! $reinitializing && $reg_step->is_current_step()) {
1169 1169
                     // if it was the current step, then we need to start over here
1170 1170
                     $this->_initialize_reg_steps(true);
1171 1171
                     return;
@@ -1224,7 +1224,7 @@  discard block
 block discarded – undo
1224 1224
                         )
1225 1225
                     );
1226 1226
                     // validate submitted form data
1227
-                    if (! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1227
+                    if ( ! $this->checkout->continue_reg || ! $this->checkout->current_step->reg_form->is_valid()) {
1228 1228
                         // thou shall not pass !!!
1229 1229
                         $this->checkout->continue_reg = false;
1230 1230
                         // any form validation errors?
@@ -1298,7 +1298,7 @@  discard block
 block discarded – undo
1298 1298
                         ) {
1299 1299
                             EE_Error::add_success(
1300 1300
                                 $this->checkout->current_step->success_message()
1301
-                                . '<br />' . $this->checkout->next_step->_instructions()
1301
+                                . '<br />'.$this->checkout->next_step->_instructions()
1302 1302
                             );
1303 1303
                         }
1304 1304
                         // pack it up, pack it in...
@@ -1362,27 +1362,27 @@  discard block
 block discarded – undo
1362 1362
             'An unknown error occurred on the server while attempting to process your request. Please refresh the page and try again or contact support.',
1363 1363
             'event_espresso'
1364 1364
         );
1365
-        EE_Registry::$i18n_js_strings['invalid_json_response']          = esc_html__(
1365
+        EE_Registry::$i18n_js_strings['invalid_json_response'] = esc_html__(
1366 1366
             'An invalid response was returned from the server while attempting to process your request. Please refresh the page and try again or contact support.',
1367 1367
             'event_espresso'
1368 1368
         );
1369
-        EE_Registry::$i18n_js_strings['validation_error']               = esc_html__(
1369
+        EE_Registry::$i18n_js_strings['validation_error'] = esc_html__(
1370 1370
             'There appears to be a problem with the form validation configuration! Please check the admin settings or contact support.',
1371 1371
             'event_espresso'
1372 1372
         );
1373
-        EE_Registry::$i18n_js_strings['invalid_payment_method']         = esc_html__(
1373
+        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
1374 1374
             'There appears to be a problem with the payment method configuration! Please refresh the page and try again or contact support.',
1375 1375
             'event_espresso'
1376 1376
         );
1377
-        EE_Registry::$i18n_js_strings['reg_step_error']                 = esc_html__(
1377
+        EE_Registry::$i18n_js_strings['reg_step_error'] = esc_html__(
1378 1378
             'This registration step could not be completed. Please refresh the page and try again.',
1379 1379
             'event_espresso'
1380 1380
         );
1381
-        EE_Registry::$i18n_js_strings['invalid_coupon']                 = esc_html__(
1381
+        EE_Registry::$i18n_js_strings['invalid_coupon'] = esc_html__(
1382 1382
             'We\'re sorry but that coupon code does not appear to be valid. If this is incorrect, please contact the site administrator.',
1383 1383
             'event_espresso'
1384 1384
         );
1385
-        EE_Registry::$i18n_js_strings['process_registration']           = sprintf(
1385
+        EE_Registry::$i18n_js_strings['process_registration'] = sprintf(
1386 1386
             esc_html__(
1387 1387
                 'Please wait while we process your registration.%sDo not refresh the page or navigate away while this is happening.%sThank you for your patience.',
1388 1388
                 'event_espresso'
@@ -1414,7 +1414,7 @@  discard block
 block discarded – undo
1414 1414
             'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
1415 1415
             true
1416 1416
         );
1417
-        EE_Registry::$i18n_js_strings['session_extension']              = absint(
1417
+        EE_Registry::$i18n_js_strings['session_extension'] = absint(
1418 1418
             apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', 10 * MINUTE_IN_SECONDS)
1419 1419
         );
1420 1420
         EE_Registry::$i18n_js_strings['session_expiration']    = $this->session->expiration();
@@ -1432,7 +1432,7 @@  discard block
 block discarded – undo
1432 1432
         // load css
1433 1433
         wp_register_style(
1434 1434
             'single_page_checkout',
1435
-            SPCO_CSS_URL . 'single_page_checkout.css',
1435
+            SPCO_CSS_URL.'single_page_checkout.css',
1436 1436
             ['espresso_default'],
1437 1437
             EVENT_ESPRESSO_VERSION
1438 1438
         );
@@ -1440,7 +1440,7 @@  discard block
 block discarded – undo
1440 1440
         // load JS
1441 1441
         wp_register_script(
1442 1442
             'single_page_checkout',
1443
-            SPCO_JS_URL . 'single_page_checkout.js',
1443
+            SPCO_JS_URL.'single_page_checkout.js',
1444 1444
             ['espresso_core', 'underscore', 'ee_form_section_validation'],
1445 1445
             EVENT_ESPRESSO_VERSION,
1446 1446
             true
@@ -1463,7 +1463,7 @@  discard block
 block discarded – undo
1463 1463
          *      AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__attendee_information
1464 1464
          */
1465 1465
         do_action(
1466
-            'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__' . $this->checkout->current_step->slug(),
1466
+            'AHEE__EED_Single_Page_Checkout__enqueue_styles_and_scripts__'.$this->checkout->current_step->slug(),
1467 1467
             $this
1468 1468
         );
1469 1469
     }
@@ -1529,7 +1529,7 @@  discard block
 block discarded – undo
1529 1529
         ) {
1530 1530
             add_filter(
1531 1531
                 'FHEE__EEH_Template__powered_by_event_espresso__url',
1532
-                function ($url) {
1532
+                function($url) {
1533 1533
                     return apply_filters('FHEE__EE_Front_Controller__registration_footer__url', $url);
1534 1534
                 }
1535 1535
             );
@@ -1559,7 +1559,7 @@  discard block
 block discarded – undo
1559 1559
             add_action('shutdown', [$this, 'unlock_transaction'], 1);
1560 1560
             add_filter(
1561 1561
                 'wp_redirect',
1562
-                function (string $location) {
1562
+                function(string $location) {
1563 1563
                     // this is hacky, but WP doesn't supply an action to hook into before the redirect
1564 1564
                     $this->unlock_transaction();
1565 1565
                     return $location;
@@ -1675,7 +1675,7 @@  discard block
 block discarded – undo
1675 1675
      */
1676 1676
     public function set_checkout_anchor(WP_Query $query)
1677 1677
     {
1678
-        if (! $query->is_main_query()) {
1678
+        if ( ! $query->is_main_query()) {
1679 1679
             return;
1680 1680
         }
1681 1681
         echo '<a id="checkout" class="screen-reader-text"></a>';
@@ -1697,7 +1697,7 @@  discard block
 block discarded – undo
1697 1697
             '</h4>',
1698 1698
             '<br />',
1699 1699
             '<p>',
1700
-            '<a href="' . get_post_type_archive_link(EspressoPostType::EVENTS) . '" title="',
1700
+            '<a href="'.get_post_type_archive_link(EspressoPostType::EVENTS).'" title="',
1701 1701
             '">',
1702 1702
             '</a>',
1703 1703
             '</p>'
Please login to merge, or discard this patch.
modules/single_page_checkout/inc/EE_Checkout.class.php 1 patch
Indentation   +1432 added lines, -1432 removed lines patch added patch discarded remove patch
@@ -14,1437 +14,1437 @@
 block discarded – undo
14 14
  */
15 15
 class EE_Checkout
16 16
 {
17
-    /**
18
-     *    whether current request originated from the EE admin
19
-     *
20
-     * @var bool
21
-     */
22
-    public $admin_request = false;
23
-
24
-    /**
25
-     * whether returning to edit attendee information or to retry a payment
26
-     *
27
-     * @var bool
28
-     */
29
-    public $revisit = false;
30
-
31
-    /**
32
-     * whether the primary registrant is returning to edit attendee information or to retry a payment
33
-     *
34
-     * @var bool
35
-     */
36
-    public $primary_revisit = false;
37
-
38
-    /**
39
-     * is registration allowed to progress or halted for some reason such as failing to pass recaptcha?
40
-     *
41
-     * @var bool
42
-     */
43
-    public $continue_reg = true;
44
-
45
-    /**
46
-     * redirect to thank you page ?
47
-     *
48
-     * @var bool
49
-     */
50
-    public $redirect = false;
51
-
52
-    /**
53
-     * generate the reg form or not ?
54
-     *
55
-     * @var bool
56
-     */
57
-    public $generate_reg_form = true;
58
-
59
-    /**
60
-     * process a reg form submission or not ?
61
-     *
62
-     * @var bool
63
-     */
64
-    public $process_form_submission = false;
65
-
66
-    /**
67
-     * tracks whether the TXN status modified during this checkout
68
-     *
69
-     * @var bool
70
-     */
71
-    public $txn_status_updated = false;
72
-
73
-    /**
74
-     * only triggered to true after absolutely everything has finished.
75
-     *
76
-     * @var bool
77
-     */
78
-    protected $exit_spco = false;
79
-
80
-    /**
81
-     * tracks whether any of the TXN's Registrations statuses modified during this checkout
82
-     * indexed by registration ID
83
-     *
84
-     * @var array
85
-     */
86
-    protected $reg_status_updated = array();
87
-
88
-    /**
89
-     * timestamp when redirected from Ticket Selector to the checkout
90
-     *
91
-     * @var int
92
-     */
93
-    public $uts = 0;
94
-
95
-    /**
96
-     * total number of tickets that were in the cart
97
-     *
98
-     * @var int
99
-     */
100
-    public $total_ticket_count = 0;
101
-
102
-    /**
103
-     * corresponds loosely to EE_Transaction::remaining()
104
-     * but can be modified by SPCO
105
-     *
106
-     * @var float
107
-     */
108
-    public $amount_owing = 0;
109
-
110
-    /**
111
-     * the reg step slug from the incoming request
112
-     *
113
-     * @var string
114
-     */
115
-    public $step = '';
116
-
117
-    /**
118
-     * the reg step slug for a step being edited
119
-     *
120
-     * @var string
121
-     */
122
-    public $edit_step = '';
123
-
124
-    /**
125
-     * the action being performed on the current step
126
-     *
127
-     * @var string
128
-     */
129
-    public $action = '';
130
-
131
-    /**
132
-     * reg_url_link for a previously saved registration
133
-     *
134
-     * @var string
135
-     */
136
-    public $reg_url_link = '';
137
-
138
-    /**
139
-     * string slug for the payment method that was selected during the payment options step
140
-     *
141
-     * @var string
142
-     */
143
-    public $selected_method_of_payment = '';
144
-
145
-    /**
146
-     * base url for the site's registration checkout page - additional url params will be added to this
147
-     *
148
-     * @var string
149
-     */
150
-    public $reg_page_base_url = '';
151
-
152
-    /**
153
-     * base url for the site's registration cancelled page - additional url params will be added to this
154
-     *
155
-     * @var string
156
-     */
157
-    public $cancel_page_url = '';
158
-
159
-    /**
160
-     * base url for the site's thank you page - additional url params will be added to this
161
-     *
162
-     * @var string
163
-     */
164
-    public $thank_you_page_url = '';
165
-
166
-    /**
167
-     * base url for any redirects - additional url params will be added to this
168
-     *
169
-     * @var string
170
-     */
171
-    public $redirect_url = '';
172
-
173
-    /**
174
-     * form of POST data for use with off-site gateways
175
-     *
176
-     * @var string
177
-     */
178
-    public $redirect_form = '';
179
-
180
-    /**
181
-     * array of query where params to use when retrieving cached registrations from $this->checkout->transaction
182
-     *
183
-     * @var array
184
-     */
185
-    public $reg_cache_where_params = array();
186
-
187
-    /**
188
-     * a class for managing and creating the JSON encoded array of data that gets passed back to the client during AJAX
189
-     * requests
190
-     *
191
-     * @var EE_SPCO_JSON_Response
192
-     */
193
-    public $json_response;
194
-
195
-    /**
196
-     * where we are going next in the reg process
197
-     *
198
-     * @var EE_SPCO_Reg_Step
199
-     */
200
-    public $next_step;
201
-
202
-    /**
203
-     * where we are in the reg process
204
-     *
205
-     * @var EE_SPCO_Reg_Step
206
-     */
207
-    public $current_step;
208
-
209
-    /**
210
-     *    $_cart - the current cart object
211
-     *
212
-     * @var EE_CART
213
-     */
214
-    public $cart;
215
-
216
-    /**
217
-     *    $_transaction - the current transaction object
218
-     *
219
-     * @var EE_Transaction
220
-     */
221
-    public $transaction;
222
-
223
-    /**
224
-     *    the related attendee object for the primary registrant
225
-     *
226
-     * @var EE_Attendee
227
-     */
228
-    public $primary_attendee_obj;
229
-
230
-    /**
231
-     *    $payment_method - the payment method object for the selected method of payment
232
-     *
233
-     * @var EE_Payment_Method
234
-     */
235
-    public $payment_method;
236
-
237
-    /**
238
-     *    $payment - if a payment was successfully made during the reg process,
239
-     *    then here it is !!!
240
-     *
241
-     * @var EE_Payment
242
-     */
243
-    public $payment;
244
-
245
-    /**
246
-     *    if a payment method was selected that uses an on-site gateway, then this is the billing form
247
-     *
248
-     * @var EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form
249
-     */
250
-    public $billing_form;
251
-
252
-    /**
253
-     *    the entire registration form composed of ALL of the subsections generated by the various reg steps
254
-     *
255
-     * @var EE_Form_Section_Proper
256
-     */
257
-    public $registration_form;
258
-
259
-    /**
260
-     * array of EE_SPCO_Reg_Step objects
261
-     *
262
-     * @var EE_SPCO_Reg_Step[]
263
-     */
264
-    public $reg_steps = array();
265
-
266
-    /**
267
-     * array of EE_Payment_Method objects
268
-     *
269
-     * @var EE_Payment_Method[]
270
-     */
271
-    public $available_payment_methods = array();
272
-
273
-    public ?EE_Payment_Method $default_payment_method = null;
274
-
275
-
276
-    /**
277
-     *    class constructor
278
-     *
279
-     * @access    public
280
-     */
281
-    public function __construct()
282
-    {
283
-        $this->reg_page_base_url = EE_Registry::instance()->CFG->core->reg_page_url();
284
-        $this->thank_you_page_url = EE_Registry::instance()->CFG->core->thank_you_page_url();
285
-        $this->cancel_page_url = EE_Registry::instance()->CFG->core->cancel_page_url();
286
-        $this->continue_reg = apply_filters('FHEE__EE_Checkout___construct___continue_reg', true);
287
-
288
-        $this->admin_request = is_admin() && ! EED_Single_Page_Checkout::getRequest()->isAjax();
289
-        $this->reg_cache_where_params = array(
290
-            0          => array('REG_deleted' => false),
291
-            'order_by' => array('REG_count' => 'ASC'),
292
-        );
293
-    }
294
-
295
-
296
-    /**
297
-     * returns true if ANY reg status was updated during checkout
298
-     *
299
-     * @return boolean
300
-     */
301
-    public function any_reg_status_updated()
302
-    {
303
-        foreach ($this->reg_status_updated as $reg_status) {
304
-            if ($reg_status) {
305
-                return true;
306
-            }
307
-        }
308
-        return false;
309
-    }
310
-
311
-
312
-    /**
313
-     * @param $REG_ID
314
-     * @return boolean
315
-     */
316
-    public function reg_status_updated($REG_ID)
317
-    {
318
-        return isset($this->reg_status_updated[ $REG_ID ]) ? $this->reg_status_updated[ $REG_ID ] : false;
319
-    }
320
-
321
-
322
-    /**
323
-     * @param $REG_ID
324
-     * @param $reg_status
325
-     */
326
-    public function set_reg_status_updated($REG_ID, $reg_status)
327
-    {
328
-        $this->reg_status_updated[ $REG_ID ] = filter_var($reg_status, FILTER_VALIDATE_BOOLEAN);
329
-    }
330
-
331
-
332
-    /**
333
-     * exit_spco
334
-     *
335
-     * @return bool
336
-     */
337
-    public function exit_spco()
338
-    {
339
-        return $this->exit_spco;
340
-    }
341
-
342
-
343
-    /**
344
-     * set_exit_spco
345
-     * can ONLY be set by the  Finalize_Registration reg step
346
-     */
347
-    public function set_exit_spco()
348
-    {
349
-        if ($this->current_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
350
-            $this->exit_spco = true;
351
-        }
352
-    }
353
-
354
-
355
-    /**
356
-     *    reset_for_current_request
357
-     *
358
-     * @access    public
359
-     * @return    void
360
-     */
361
-    public function reset_for_current_request()
362
-    {
363
-        $this->process_form_submission = false;
364
-        $this->continue_reg = apply_filters('FHEE__EE_Checkout___construct___continue_reg', true);
365
-        $this->admin_request = is_admin() && ! EED_Single_Page_Checkout::getRequest()->isFrontAjax();
366
-        $this->continue_reg = true;
367
-        $this->redirect = false;
368
-        // don't reset the cached redirect form if we're about to be asked to display it !!!
369
-        $action = EED_Single_Page_Checkout::getRequest()->getRequestParam('action', 'display_spco_reg_step');
370
-        if ($action !== 'redirect_form') {
371
-            $this->redirect_form = '';
372
-        }
373
-        $this->redirect_url = '';
374
-        $this->json_response = new EE_SPCO_JSON_Response();
375
-        EE_Form_Section_Proper::reset_js_localization();
376
-    }
377
-
378
-
379
-    /**
380
-     *    add_reg_step
381
-     *
382
-     * @access    public
383
-     * @param EE_SPCO_Reg_Step $reg_step_obj
384
-     * @return    void
385
-     */
386
-    public function add_reg_step(EE_SPCO_Reg_Step $reg_step_obj)
387
-    {
388
-        $this->reg_steps[ $reg_step_obj->slug() ] = $reg_step_obj;
389
-    }
390
-
391
-
392
-    /**
393
-     * skip_reg_step
394
-     * if the current reg step does not need to run for some reason,
395
-     * then this will advance SPCO to the next reg step,
396
-     * and mark the skipped step as completed
397
-     *
398
-     * @access    public
399
-     * @param string $reg_step_slug
400
-     * @return    void
401
-     * @throws \EE_Error
402
-     */
403
-    public function skip_reg_step($reg_step_slug = '')
404
-    {
405
-        $step_to_skip = $this->find_reg_step($reg_step_slug);
406
-        if ($step_to_skip instanceof EE_SPCO_Reg_Step && $step_to_skip->is_current_step()) {
407
-            $step_to_skip->set_is_current_step(false);
408
-            $step_to_skip->set_completed();
409
-            // advance to the next step
410
-            $this->set_current_step($this->next_step->slug());
411
-            // also reset the step param in the request in case any other code references that directly
412
-            EED_Single_Page_Checkout::getRequest()->setRequestParam('step', $this->current_step->slug());
413
-            // since we are skipping a step and setting the current step to be what was previously the next step,
414
-            // we need to check that the next step is now correct, and not still set to the current step.
415
-            if ($this->current_step->slug() === $this->next_step->slug()) {
416
-                // correctly setup the next step
417
-                $this->set_next_step();
418
-            }
419
-            $this->set_reg_step_initiated($this->current_step);
420
-        }
421
-    }
422
-
423
-
424
-    /**
425
-     *    remove_reg_step
426
-     *
427
-     * @access    public
428
-     * @param string $reg_step_slug
429
-     * @param bool   $reset whether to reset reg steps after removal
430
-     * @throws EE_Error
431
-     */
432
-    public function remove_reg_step($reg_step_slug = '', $reset = true)
433
-    {
434
-        unset($this->reg_steps[ $reg_step_slug ]);
435
-        if ($this->transaction instanceof EE_Transaction) {
436
-            // now remove reg step from TXN and save
437
-            $this->transaction->remove_reg_step($reg_step_slug);
438
-            $this->transaction->save();
439
-        }
440
-        if ($reset) {
441
-            $this->reset_reg_steps();
442
-        }
443
-    }
444
-
445
-
446
-    /**
447
-     *    set_reg_step_order
448
-     *
449
-     * @access    public
450
-     * @param string $reg_step_slug
451
-     * @param int    $order
452
-     * @return    void
453
-     */
454
-    public function set_reg_step_order($reg_step_slug = '', $order = 100)
455
-    {
456
-        if (isset($this->reg_steps[ $reg_step_slug ])) {
457
-            $this->reg_steps[ $reg_step_slug ]->set_order($order);
458
-        }
459
-    }
460
-
461
-
462
-    /**
463
-     * @param string $current_step
464
-     * @return void
465
-     */
466
-    public function set_current_step(string $current_step)
467
-    {
468
-        // grab what step we're on
469
-        $this->current_step = $this->reg_steps[ $current_step ] ?? reset($this->reg_steps);
470
-        // verify instance
471
-        if (! $this->current_step instanceof EE_SPCO_Reg_Step) {
472
-            EE_Error::add_error(
473
-                esc_html__('The current step could not be set.', 'event_espresso'),
474
-                __FILE__,
475
-                __FUNCTION__,
476
-                __LINE__
477
-            );
478
-        }
479
-        // we don't want to repeat completed steps if this is the first time through SPCO
480
-        if ($this->continue_reg && ! $this->revisit && $this->current_step->completed()) {
481
-            // so advance to the next step
482
-            $this->set_next_step();
483
-            if ($this->next_step instanceof EE_SPCO_Reg_Step) {
484
-                // and attempt to set it as the current step
485
-                $this->set_current_step($this->next_step->slug());
486
-            }
487
-            return;
488
-        }
489
-        $this->current_step->set_is_current_step(true);
490
-        $this->current_step->setRequest(EED_Single_Page_Checkout::getRequest());
491
-    }
492
-
493
-
494
-    /**
495
-     *    set_next_step
496
-     * advances the reg_steps array pointer and sets the next step, then reverses pointer back to the current step
497
-     *
498
-     * @access    public
499
-     * @return    void
500
-     */
501
-    public function set_next_step()
502
-    {
503
-        // set pointer to start of array
504
-        reset($this->reg_steps);
505
-        // if there is more than one step
506
-        if (count($this->reg_steps) > 1) {
507
-            // advance to the current step and set pointer
508
-            while (key($this->reg_steps) !== $this->current_step->slug() && key($this->reg_steps) !== '') {
509
-                next($this->reg_steps);
510
-            }
511
-        }
512
-        // advance one more spot ( if it exists )
513
-        $this->next_step = next($this->reg_steps);
514
-        // verify instance
515
-        $this->next_step = $this->next_step instanceof EE_SPCO_Reg_Step ? $this->next_step : null;
516
-        // then back to current step to reset
517
-        prev($this->reg_steps);
518
-    }
519
-
520
-
521
-    /**
522
-     *    get_next_reg_step
523
-     *    this simply returns the next step from reg_steps array
524
-     *
525
-     * @access    public
526
-     * @return    EE_SPCO_Reg_Step | null
527
-     */
528
-    public function get_next_reg_step()
529
-    {
530
-        $next = next($this->reg_steps);
531
-        prev($this->reg_steps);
532
-        return $next instanceof EE_SPCO_Reg_Step ? $next : null;
533
-    }
534
-
535
-
536
-    /**
537
-     * get_prev_reg_step
538
-     *    this simply returns the previous step from reg_steps array
539
-     *
540
-     * @access    public
541
-     * @return    EE_SPCO_Reg_Step | null
542
-     */
543
-    public function get_prev_reg_step()
544
-    {
545
-        $prev = prev($this->reg_steps);
546
-        next($this->reg_steps);
547
-        return $prev instanceof EE_SPCO_Reg_Step ? $prev : null;
548
-    }
549
-
550
-
551
-    /**
552
-     * sort_reg_steps
553
-     *
554
-     * @access public
555
-     * @return void
556
-     */
557
-    public function sort_reg_steps()
558
-    {
559
-        $reg_step_sorting_callback = apply_filters(
560
-            'FHEE__EE_Checkout__sort_reg_steps__reg_step_sorting_callback',
561
-            'reg_step_sorting_callback'
562
-        );
563
-        uasort($this->reg_steps, array($this, $reg_step_sorting_callback));
564
-    }
565
-
566
-
567
-    /**
568
-     * find_reg_step
569
-     * finds a reg step by the given slug
570
-     *
571
-     * @access    public
572
-     * @param string $reg_step_slug
573
-     * @return EE_SPCO_Reg_Step|null
574
-     */
575
-    public function find_reg_step($reg_step_slug = '')
576
-    {
577
-        if (! empty($reg_step_slug)) {
578
-            // copy reg step array
579
-            $reg_steps = $this->reg_steps;
580
-            // set pointer to start of array
581
-            reset($reg_steps);
582
-            // if there is more than one step
583
-            if (count($reg_steps) > 1) {
584
-                // advance to the current step and set pointer
585
-                while (key($reg_steps) !== $reg_step_slug && key($reg_steps) !== '') {
586
-                    next($reg_steps);
587
-                }
588
-                return current($reg_steps);
589
-            }
590
-        }
591
-        return null;
592
-    }
593
-
594
-
595
-    /**
596
-     * reg_step_sorting_callback
597
-     *
598
-     * @access public
599
-     * @param EE_SPCO_Reg_Step $reg_step_A
600
-     * @param EE_SPCO_Reg_Step $reg_step_B
601
-     * @return int
602
-     */
603
-    public function reg_step_sorting_callback(EE_SPCO_Reg_Step $reg_step_A, EE_SPCO_Reg_Step $reg_step_B)
604
-    {
605
-        // send finalize_registration step to the end of the array
606
-        if ($reg_step_A->slug() === 'finalize_registration') {
607
-            return 1;
608
-        } elseif ($reg_step_B->slug() === 'finalize_registration') {
609
-            return -1;
610
-        }
611
-        if ($reg_step_A->order() === $reg_step_B->order()) {
612
-            return 0;
613
-        }
614
-        return ($reg_step_A->order() > $reg_step_B->order()) ? 1 : -1;
615
-    }
616
-
617
-
618
-    /**
619
-     * set_reg_step_initiated
620
-     *
621
-     * @access    public
622
-     * @param    EE_SPCO_Reg_Step $reg_step
623
-     * @throws \EE_Error
624
-     */
625
-    public function set_reg_step_initiated(EE_SPCO_Reg_Step $reg_step)
626
-    {
627
-        // call set_reg_step_initiated ???
628
-        if (
17
+	/**
18
+	 *    whether current request originated from the EE admin
19
+	 *
20
+	 * @var bool
21
+	 */
22
+	public $admin_request = false;
23
+
24
+	/**
25
+	 * whether returning to edit attendee information or to retry a payment
26
+	 *
27
+	 * @var bool
28
+	 */
29
+	public $revisit = false;
30
+
31
+	/**
32
+	 * whether the primary registrant is returning to edit attendee information or to retry a payment
33
+	 *
34
+	 * @var bool
35
+	 */
36
+	public $primary_revisit = false;
37
+
38
+	/**
39
+	 * is registration allowed to progress or halted for some reason such as failing to pass recaptcha?
40
+	 *
41
+	 * @var bool
42
+	 */
43
+	public $continue_reg = true;
44
+
45
+	/**
46
+	 * redirect to thank you page ?
47
+	 *
48
+	 * @var bool
49
+	 */
50
+	public $redirect = false;
51
+
52
+	/**
53
+	 * generate the reg form or not ?
54
+	 *
55
+	 * @var bool
56
+	 */
57
+	public $generate_reg_form = true;
58
+
59
+	/**
60
+	 * process a reg form submission or not ?
61
+	 *
62
+	 * @var bool
63
+	 */
64
+	public $process_form_submission = false;
65
+
66
+	/**
67
+	 * tracks whether the TXN status modified during this checkout
68
+	 *
69
+	 * @var bool
70
+	 */
71
+	public $txn_status_updated = false;
72
+
73
+	/**
74
+	 * only triggered to true after absolutely everything has finished.
75
+	 *
76
+	 * @var bool
77
+	 */
78
+	protected $exit_spco = false;
79
+
80
+	/**
81
+	 * tracks whether any of the TXN's Registrations statuses modified during this checkout
82
+	 * indexed by registration ID
83
+	 *
84
+	 * @var array
85
+	 */
86
+	protected $reg_status_updated = array();
87
+
88
+	/**
89
+	 * timestamp when redirected from Ticket Selector to the checkout
90
+	 *
91
+	 * @var int
92
+	 */
93
+	public $uts = 0;
94
+
95
+	/**
96
+	 * total number of tickets that were in the cart
97
+	 *
98
+	 * @var int
99
+	 */
100
+	public $total_ticket_count = 0;
101
+
102
+	/**
103
+	 * corresponds loosely to EE_Transaction::remaining()
104
+	 * but can be modified by SPCO
105
+	 *
106
+	 * @var float
107
+	 */
108
+	public $amount_owing = 0;
109
+
110
+	/**
111
+	 * the reg step slug from the incoming request
112
+	 *
113
+	 * @var string
114
+	 */
115
+	public $step = '';
116
+
117
+	/**
118
+	 * the reg step slug for a step being edited
119
+	 *
120
+	 * @var string
121
+	 */
122
+	public $edit_step = '';
123
+
124
+	/**
125
+	 * the action being performed on the current step
126
+	 *
127
+	 * @var string
128
+	 */
129
+	public $action = '';
130
+
131
+	/**
132
+	 * reg_url_link for a previously saved registration
133
+	 *
134
+	 * @var string
135
+	 */
136
+	public $reg_url_link = '';
137
+
138
+	/**
139
+	 * string slug for the payment method that was selected during the payment options step
140
+	 *
141
+	 * @var string
142
+	 */
143
+	public $selected_method_of_payment = '';
144
+
145
+	/**
146
+	 * base url for the site's registration checkout page - additional url params will be added to this
147
+	 *
148
+	 * @var string
149
+	 */
150
+	public $reg_page_base_url = '';
151
+
152
+	/**
153
+	 * base url for the site's registration cancelled page - additional url params will be added to this
154
+	 *
155
+	 * @var string
156
+	 */
157
+	public $cancel_page_url = '';
158
+
159
+	/**
160
+	 * base url for the site's thank you page - additional url params will be added to this
161
+	 *
162
+	 * @var string
163
+	 */
164
+	public $thank_you_page_url = '';
165
+
166
+	/**
167
+	 * base url for any redirects - additional url params will be added to this
168
+	 *
169
+	 * @var string
170
+	 */
171
+	public $redirect_url = '';
172
+
173
+	/**
174
+	 * form of POST data for use with off-site gateways
175
+	 *
176
+	 * @var string
177
+	 */
178
+	public $redirect_form = '';
179
+
180
+	/**
181
+	 * array of query where params to use when retrieving cached registrations from $this->checkout->transaction
182
+	 *
183
+	 * @var array
184
+	 */
185
+	public $reg_cache_where_params = array();
186
+
187
+	/**
188
+	 * a class for managing and creating the JSON encoded array of data that gets passed back to the client during AJAX
189
+	 * requests
190
+	 *
191
+	 * @var EE_SPCO_JSON_Response
192
+	 */
193
+	public $json_response;
194
+
195
+	/**
196
+	 * where we are going next in the reg process
197
+	 *
198
+	 * @var EE_SPCO_Reg_Step
199
+	 */
200
+	public $next_step;
201
+
202
+	/**
203
+	 * where we are in the reg process
204
+	 *
205
+	 * @var EE_SPCO_Reg_Step
206
+	 */
207
+	public $current_step;
208
+
209
+	/**
210
+	 *    $_cart - the current cart object
211
+	 *
212
+	 * @var EE_CART
213
+	 */
214
+	public $cart;
215
+
216
+	/**
217
+	 *    $_transaction - the current transaction object
218
+	 *
219
+	 * @var EE_Transaction
220
+	 */
221
+	public $transaction;
222
+
223
+	/**
224
+	 *    the related attendee object for the primary registrant
225
+	 *
226
+	 * @var EE_Attendee
227
+	 */
228
+	public $primary_attendee_obj;
229
+
230
+	/**
231
+	 *    $payment_method - the payment method object for the selected method of payment
232
+	 *
233
+	 * @var EE_Payment_Method
234
+	 */
235
+	public $payment_method;
236
+
237
+	/**
238
+	 *    $payment - if a payment was successfully made during the reg process,
239
+	 *    then here it is !!!
240
+	 *
241
+	 * @var EE_Payment
242
+	 */
243
+	public $payment;
244
+
245
+	/**
246
+	 *    if a payment method was selected that uses an on-site gateway, then this is the billing form
247
+	 *
248
+	 * @var EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form
249
+	 */
250
+	public $billing_form;
251
+
252
+	/**
253
+	 *    the entire registration form composed of ALL of the subsections generated by the various reg steps
254
+	 *
255
+	 * @var EE_Form_Section_Proper
256
+	 */
257
+	public $registration_form;
258
+
259
+	/**
260
+	 * array of EE_SPCO_Reg_Step objects
261
+	 *
262
+	 * @var EE_SPCO_Reg_Step[]
263
+	 */
264
+	public $reg_steps = array();
265
+
266
+	/**
267
+	 * array of EE_Payment_Method objects
268
+	 *
269
+	 * @var EE_Payment_Method[]
270
+	 */
271
+	public $available_payment_methods = array();
272
+
273
+	public ?EE_Payment_Method $default_payment_method = null;
274
+
275
+
276
+	/**
277
+	 *    class constructor
278
+	 *
279
+	 * @access    public
280
+	 */
281
+	public function __construct()
282
+	{
283
+		$this->reg_page_base_url = EE_Registry::instance()->CFG->core->reg_page_url();
284
+		$this->thank_you_page_url = EE_Registry::instance()->CFG->core->thank_you_page_url();
285
+		$this->cancel_page_url = EE_Registry::instance()->CFG->core->cancel_page_url();
286
+		$this->continue_reg = apply_filters('FHEE__EE_Checkout___construct___continue_reg', true);
287
+
288
+		$this->admin_request = is_admin() && ! EED_Single_Page_Checkout::getRequest()->isAjax();
289
+		$this->reg_cache_where_params = array(
290
+			0          => array('REG_deleted' => false),
291
+			'order_by' => array('REG_count' => 'ASC'),
292
+		);
293
+	}
294
+
295
+
296
+	/**
297
+	 * returns true if ANY reg status was updated during checkout
298
+	 *
299
+	 * @return boolean
300
+	 */
301
+	public function any_reg_status_updated()
302
+	{
303
+		foreach ($this->reg_status_updated as $reg_status) {
304
+			if ($reg_status) {
305
+				return true;
306
+			}
307
+		}
308
+		return false;
309
+	}
310
+
311
+
312
+	/**
313
+	 * @param $REG_ID
314
+	 * @return boolean
315
+	 */
316
+	public function reg_status_updated($REG_ID)
317
+	{
318
+		return isset($this->reg_status_updated[ $REG_ID ]) ? $this->reg_status_updated[ $REG_ID ] : false;
319
+	}
320
+
321
+
322
+	/**
323
+	 * @param $REG_ID
324
+	 * @param $reg_status
325
+	 */
326
+	public function set_reg_status_updated($REG_ID, $reg_status)
327
+	{
328
+		$this->reg_status_updated[ $REG_ID ] = filter_var($reg_status, FILTER_VALIDATE_BOOLEAN);
329
+	}
330
+
331
+
332
+	/**
333
+	 * exit_spco
334
+	 *
335
+	 * @return bool
336
+	 */
337
+	public function exit_spco()
338
+	{
339
+		return $this->exit_spco;
340
+	}
341
+
342
+
343
+	/**
344
+	 * set_exit_spco
345
+	 * can ONLY be set by the  Finalize_Registration reg step
346
+	 */
347
+	public function set_exit_spco()
348
+	{
349
+		if ($this->current_step instanceof EE_SPCO_Reg_Step_Finalize_Registration) {
350
+			$this->exit_spco = true;
351
+		}
352
+	}
353
+
354
+
355
+	/**
356
+	 *    reset_for_current_request
357
+	 *
358
+	 * @access    public
359
+	 * @return    void
360
+	 */
361
+	public function reset_for_current_request()
362
+	{
363
+		$this->process_form_submission = false;
364
+		$this->continue_reg = apply_filters('FHEE__EE_Checkout___construct___continue_reg', true);
365
+		$this->admin_request = is_admin() && ! EED_Single_Page_Checkout::getRequest()->isFrontAjax();
366
+		$this->continue_reg = true;
367
+		$this->redirect = false;
368
+		// don't reset the cached redirect form if we're about to be asked to display it !!!
369
+		$action = EED_Single_Page_Checkout::getRequest()->getRequestParam('action', 'display_spco_reg_step');
370
+		if ($action !== 'redirect_form') {
371
+			$this->redirect_form = '';
372
+		}
373
+		$this->redirect_url = '';
374
+		$this->json_response = new EE_SPCO_JSON_Response();
375
+		EE_Form_Section_Proper::reset_js_localization();
376
+	}
377
+
378
+
379
+	/**
380
+	 *    add_reg_step
381
+	 *
382
+	 * @access    public
383
+	 * @param EE_SPCO_Reg_Step $reg_step_obj
384
+	 * @return    void
385
+	 */
386
+	public function add_reg_step(EE_SPCO_Reg_Step $reg_step_obj)
387
+	{
388
+		$this->reg_steps[ $reg_step_obj->slug() ] = $reg_step_obj;
389
+	}
390
+
391
+
392
+	/**
393
+	 * skip_reg_step
394
+	 * if the current reg step does not need to run for some reason,
395
+	 * then this will advance SPCO to the next reg step,
396
+	 * and mark the skipped step as completed
397
+	 *
398
+	 * @access    public
399
+	 * @param string $reg_step_slug
400
+	 * @return    void
401
+	 * @throws \EE_Error
402
+	 */
403
+	public function skip_reg_step($reg_step_slug = '')
404
+	{
405
+		$step_to_skip = $this->find_reg_step($reg_step_slug);
406
+		if ($step_to_skip instanceof EE_SPCO_Reg_Step && $step_to_skip->is_current_step()) {
407
+			$step_to_skip->set_is_current_step(false);
408
+			$step_to_skip->set_completed();
409
+			// advance to the next step
410
+			$this->set_current_step($this->next_step->slug());
411
+			// also reset the step param in the request in case any other code references that directly
412
+			EED_Single_Page_Checkout::getRequest()->setRequestParam('step', $this->current_step->slug());
413
+			// since we are skipping a step and setting the current step to be what was previously the next step,
414
+			// we need to check that the next step is now correct, and not still set to the current step.
415
+			if ($this->current_step->slug() === $this->next_step->slug()) {
416
+				// correctly setup the next step
417
+				$this->set_next_step();
418
+			}
419
+			$this->set_reg_step_initiated($this->current_step);
420
+		}
421
+	}
422
+
423
+
424
+	/**
425
+	 *    remove_reg_step
426
+	 *
427
+	 * @access    public
428
+	 * @param string $reg_step_slug
429
+	 * @param bool   $reset whether to reset reg steps after removal
430
+	 * @throws EE_Error
431
+	 */
432
+	public function remove_reg_step($reg_step_slug = '', $reset = true)
433
+	{
434
+		unset($this->reg_steps[ $reg_step_slug ]);
435
+		if ($this->transaction instanceof EE_Transaction) {
436
+			// now remove reg step from TXN and save
437
+			$this->transaction->remove_reg_step($reg_step_slug);
438
+			$this->transaction->save();
439
+		}
440
+		if ($reset) {
441
+			$this->reset_reg_steps();
442
+		}
443
+	}
444
+
445
+
446
+	/**
447
+	 *    set_reg_step_order
448
+	 *
449
+	 * @access    public
450
+	 * @param string $reg_step_slug
451
+	 * @param int    $order
452
+	 * @return    void
453
+	 */
454
+	public function set_reg_step_order($reg_step_slug = '', $order = 100)
455
+	{
456
+		if (isset($this->reg_steps[ $reg_step_slug ])) {
457
+			$this->reg_steps[ $reg_step_slug ]->set_order($order);
458
+		}
459
+	}
460
+
461
+
462
+	/**
463
+	 * @param string $current_step
464
+	 * @return void
465
+	 */
466
+	public function set_current_step(string $current_step)
467
+	{
468
+		// grab what step we're on
469
+		$this->current_step = $this->reg_steps[ $current_step ] ?? reset($this->reg_steps);
470
+		// verify instance
471
+		if (! $this->current_step instanceof EE_SPCO_Reg_Step) {
472
+			EE_Error::add_error(
473
+				esc_html__('The current step could not be set.', 'event_espresso'),
474
+				__FILE__,
475
+				__FUNCTION__,
476
+				__LINE__
477
+			);
478
+		}
479
+		// we don't want to repeat completed steps if this is the first time through SPCO
480
+		if ($this->continue_reg && ! $this->revisit && $this->current_step->completed()) {
481
+			// so advance to the next step
482
+			$this->set_next_step();
483
+			if ($this->next_step instanceof EE_SPCO_Reg_Step) {
484
+				// and attempt to set it as the current step
485
+				$this->set_current_step($this->next_step->slug());
486
+			}
487
+			return;
488
+		}
489
+		$this->current_step->set_is_current_step(true);
490
+		$this->current_step->setRequest(EED_Single_Page_Checkout::getRequest());
491
+	}
492
+
493
+
494
+	/**
495
+	 *    set_next_step
496
+	 * advances the reg_steps array pointer and sets the next step, then reverses pointer back to the current step
497
+	 *
498
+	 * @access    public
499
+	 * @return    void
500
+	 */
501
+	public function set_next_step()
502
+	{
503
+		// set pointer to start of array
504
+		reset($this->reg_steps);
505
+		// if there is more than one step
506
+		if (count($this->reg_steps) > 1) {
507
+			// advance to the current step and set pointer
508
+			while (key($this->reg_steps) !== $this->current_step->slug() && key($this->reg_steps) !== '') {
509
+				next($this->reg_steps);
510
+			}
511
+		}
512
+		// advance one more spot ( if it exists )
513
+		$this->next_step = next($this->reg_steps);
514
+		// verify instance
515
+		$this->next_step = $this->next_step instanceof EE_SPCO_Reg_Step ? $this->next_step : null;
516
+		// then back to current step to reset
517
+		prev($this->reg_steps);
518
+	}
519
+
520
+
521
+	/**
522
+	 *    get_next_reg_step
523
+	 *    this simply returns the next step from reg_steps array
524
+	 *
525
+	 * @access    public
526
+	 * @return    EE_SPCO_Reg_Step | null
527
+	 */
528
+	public function get_next_reg_step()
529
+	{
530
+		$next = next($this->reg_steps);
531
+		prev($this->reg_steps);
532
+		return $next instanceof EE_SPCO_Reg_Step ? $next : null;
533
+	}
534
+
535
+
536
+	/**
537
+	 * get_prev_reg_step
538
+	 *    this simply returns the previous step from reg_steps array
539
+	 *
540
+	 * @access    public
541
+	 * @return    EE_SPCO_Reg_Step | null
542
+	 */
543
+	public function get_prev_reg_step()
544
+	{
545
+		$prev = prev($this->reg_steps);
546
+		next($this->reg_steps);
547
+		return $prev instanceof EE_SPCO_Reg_Step ? $prev : null;
548
+	}
549
+
550
+
551
+	/**
552
+	 * sort_reg_steps
553
+	 *
554
+	 * @access public
555
+	 * @return void
556
+	 */
557
+	public function sort_reg_steps()
558
+	{
559
+		$reg_step_sorting_callback = apply_filters(
560
+			'FHEE__EE_Checkout__sort_reg_steps__reg_step_sorting_callback',
561
+			'reg_step_sorting_callback'
562
+		);
563
+		uasort($this->reg_steps, array($this, $reg_step_sorting_callback));
564
+	}
565
+
566
+
567
+	/**
568
+	 * find_reg_step
569
+	 * finds a reg step by the given slug
570
+	 *
571
+	 * @access    public
572
+	 * @param string $reg_step_slug
573
+	 * @return EE_SPCO_Reg_Step|null
574
+	 */
575
+	public function find_reg_step($reg_step_slug = '')
576
+	{
577
+		if (! empty($reg_step_slug)) {
578
+			// copy reg step array
579
+			$reg_steps = $this->reg_steps;
580
+			// set pointer to start of array
581
+			reset($reg_steps);
582
+			// if there is more than one step
583
+			if (count($reg_steps) > 1) {
584
+				// advance to the current step and set pointer
585
+				while (key($reg_steps) !== $reg_step_slug && key($reg_steps) !== '') {
586
+					next($reg_steps);
587
+				}
588
+				return current($reg_steps);
589
+			}
590
+		}
591
+		return null;
592
+	}
593
+
594
+
595
+	/**
596
+	 * reg_step_sorting_callback
597
+	 *
598
+	 * @access public
599
+	 * @param EE_SPCO_Reg_Step $reg_step_A
600
+	 * @param EE_SPCO_Reg_Step $reg_step_B
601
+	 * @return int
602
+	 */
603
+	public function reg_step_sorting_callback(EE_SPCO_Reg_Step $reg_step_A, EE_SPCO_Reg_Step $reg_step_B)
604
+	{
605
+		// send finalize_registration step to the end of the array
606
+		if ($reg_step_A->slug() === 'finalize_registration') {
607
+			return 1;
608
+		} elseif ($reg_step_B->slug() === 'finalize_registration') {
609
+			return -1;
610
+		}
611
+		if ($reg_step_A->order() === $reg_step_B->order()) {
612
+			return 0;
613
+		}
614
+		return ($reg_step_A->order() > $reg_step_B->order()) ? 1 : -1;
615
+	}
616
+
617
+
618
+	/**
619
+	 * set_reg_step_initiated
620
+	 *
621
+	 * @access    public
622
+	 * @param    EE_SPCO_Reg_Step $reg_step
623
+	 * @throws \EE_Error
624
+	 */
625
+	public function set_reg_step_initiated(EE_SPCO_Reg_Step $reg_step)
626
+	{
627
+		// call set_reg_step_initiated ???
628
+		if (
629 629
 // first time visiting SPCO ?
630
-            ! $this->revisit
631
-            && (
632
-                // and displaying the reg step form for the first time ?
633
-                $this->action === 'display_spco_reg_step'
634
-                // or initializing the final step
635
-                || $reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration
636
-            )
637
-        ) {
638
-            // set the start time for this reg step
639
-            if (! $this->transaction->set_reg_step_initiated($reg_step->slug())) {
640
-                if (WP_DEBUG) {
641
-                    EE_Error::add_error(
642
-                        sprintf(
643
-                            esc_html__('The "%1$s" registration step was not initialized properly.', 'event_espresso'),
644
-                            $reg_step->name()
645
-                        ),
646
-                        __FILE__,
647
-                        __FUNCTION__,
648
-                        __LINE__
649
-                    );
650
-                }
651
-            }
652
-        }
653
-    }
654
-
655
-
656
-    /**
657
-     *    set_reg_step_JSON_info
658
-     *
659
-     * @access public
660
-     * @return    void
661
-     */
662
-    public function set_reg_step_JSON_info()
663
-    {
664
-        EE_Registry::$i18n_js_strings['reg_steps'] = array();
665
-        // pass basic reg step data to JS
666
-        foreach ($this->reg_steps as $reg_step) {
667
-            EE_Registry::$i18n_js_strings['reg_steps'][] = $reg_step->slug();
668
-        }
669
-        // reset reg step html
670
-        // $this->json_response->set_reg_step_html('');
671
-    }
672
-
673
-
674
-    /**
675
-     *    reset_reg_steps
676
-     *
677
-     * @access public
678
-     * @return void
679
-     */
680
-    public function reset_reg_steps()
681
-    {
682
-        $this->sort_reg_steps();
683
-        $this->set_current_step(EED_Single_Page_Checkout::getRequest()->getRequestParam('step'));
684
-        $this->set_next_step();
685
-        // the text that appears on the reg step form submit button
686
-        $this->current_step->set_submit_button_text();
687
-        $this->set_reg_step_JSON_info();
688
-    }
689
-
690
-
691
-    /**
692
-     *    get_registration_time_limit
693
-     *
694
-     * @access    public
695
-     * @return        string
696
-     */
697
-    public function get_registration_time_limit(): string
698
-    {
699
-        $registration_time_limit = (float) (EE_Registry::instance()->SSN->expiration() - time());
700
-        $time_limit_format = $registration_time_limit > 60 * MINUTE_IN_SECONDS ? 'H:i:s' : 'i:s';
701
-        $registration_time_limit = date($time_limit_format, $registration_time_limit);
702
-        return (string) apply_filters(
703
-            'FHEE__EE_Checkout__get_registration_time_limit__registration_time_limit',
704
-            $registration_time_limit
705
-        );
706
-    }
707
-
708
-
709
-    /**
710
-     * payment_required
711
-     *
712
-     * @return boolean
713
-     */
714
-    public function payment_required()
715
-    {
716
-        // if NOT:
717
-        //     registration via admin
718
-        //      completed TXN
719
-        //      overpaid TXN
720
-        //      free TXN(total = 0.00)
721
-        //      then payment required is TRUE
722
-        return ! ($this->admin_request
723
-                  || $this->transaction->is_completed()
724
-                  || $this->transaction->is_overpaid()
725
-                  || $this->transaction->is_free()) ? true : false;
726
-    }
727
-
728
-
729
-    /**
730
-     * get_cart_for_transaction
731
-     *
732
-     * @access public
733
-     * @param EE_Transaction $transaction
734
-     * @return EE_Cart
735
-     */
736
-    public function get_cart_for_transaction($transaction)
737
-    {
738
-        $session = EE_Registry::instance()->load_core('Session');
739
-        $cart = $transaction instanceof EE_Transaction ? EE_Cart::get_cart_from_txn($transaction, $session) : null;
740
-        // verify cart
741
-        if (! $cart instanceof EE_Cart) {
742
-            $cart = EE_Registry::instance()->load_core('Cart');
743
-        }
744
-
745
-        return $cart;
746
-    }
747
-
748
-
749
-    /**
750
-     *    initialize_txn_reg_steps_array
751
-     *
752
-     * @access public
753
-     * @return    array
754
-     */
755
-    public function initialize_txn_reg_steps_array()
756
-    {
757
-        $txn_reg_steps_array = array();
758
-        foreach ($this->reg_steps as $reg_step) {
759
-            $txn_reg_steps_array[ $reg_step->slug() ] = false;
760
-        }
761
-        return $txn_reg_steps_array;
762
-    }
763
-
764
-
765
-    /**
766
-     *    update_txn_reg_steps_array
767
-     *
768
-     * @access public
769
-     * @return    bool
770
-     * @throws \EE_Error
771
-     */
772
-    public function update_txn_reg_steps_array()
773
-    {
774
-        $updated = false;
775
-        foreach ($this->reg_steps as $reg_step) {
776
-            if ($reg_step->completed()) {
777
-                $updated = $this->transaction->set_reg_step_completed($reg_step->slug())
778
-                    ? true
779
-                    : $updated;
780
-            }
781
-        }
782
-        if ($updated) {
783
-            $this->transaction->save();
784
-        }
785
-        return $updated;
786
-    }
787
-
788
-
789
-    /**
790
-     *    stash_transaction_and_checkout
791
-     *
792
-     * @access public
793
-     * @return    void
794
-     * @throws \EE_Error
795
-     */
796
-    public function stash_transaction_and_checkout()
797
-    {
798
-        if (! $this->revisit) {
799
-            $this->update_txn_reg_steps_array();
800
-        }
801
-        $this->track_transaction_and_registration_status_updates();
802
-        // save all data to the db, but suppress errors
803
-        // $this->save_all_data( FALSE );
804
-        // cache the checkout in the session
805
-        EE_Registry::instance()->SSN->set_checkout($this);
806
-    }
807
-
808
-
809
-    /**
810
-     *    track_transaction_and_registration_status_updates
811
-     *    stores whether any updates were made to the TXN or it's related registrations
812
-     *
813
-     * @access public
814
-     * @return void
815
-     * @throws \EE_Error
816
-     */
817
-    public function track_transaction_and_registration_status_updates()
818
-    {
819
-        // verify the transaction
820
-        if ($this->transaction instanceof EE_Transaction) {
821
-            // has there been a TXN status change during this checkout?
822
-            $this->txn_status_updated = $this->transaction->txn_status_updated();
823
-            /** @type EE_Registration_Processor $registration_processor */
824
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
825
-            // grab the saved registrations from the transaction
826
-            foreach ($this->transaction->registrations($this->reg_cache_where_params) as $registration) {
827
-                if ($registration_processor->reg_status_updated($registration->ID())) {
828
-                    $this->set_reg_status_updated($registration->ID(), true);
829
-                }
830
-            }
831
-        }
832
-    }
833
-
834
-
835
-    /**
836
-     *    visit_allows_processing_of_this_registration
837
-     *    determines if the current SPCO visit should allow the passed EE_Registration to be used in processing.
838
-     *    one of the following conditions must be met:
839
-     *        EITHER:    A) first time thru SPCO -> process ALL registrations ( NOT a revisit )
840
-     *        OR :        B) primary registrant is editing info -> process ALL registrations ( primary_revisit )
841
-     *        OR :        C) another registrant is editing info -> ONLY process their registration ( revisit AND their
842
-     *        reg_url_link matches )
843
-     *
844
-     * @access public
845
-     * @param    EE_Registration $registration
846
-     * @return    bool
847
-     * @throws \EE_Error
848
-     */
849
-    public function visit_allows_processing_of_this_registration(EE_Registration $registration)
850
-    {
851
-        return ! $this->revisit
852
-               || $this->primary_revisit
853
-               || (
854
-                   $this->revisit && $this->reg_url_link === $registration->reg_url_link()
855
-               )
856
-            ? true
857
-            : false;
858
-    }
859
-
860
-
861
-    /**
862
-     *    _transaction_has_primary_registration
863
-     *
864
-     * @access        private
865
-     * @return        bool
866
-     */
867
-    public function transaction_has_primary_registrant()
868
-    {
869
-        return $this->primary_attendee_obj instanceof EE_Attendee ? true : false;
870
-    }
871
-
872
-
873
-    /**
874
-     *    save_all_data
875
-     *    simply loops through the current transaction and saves all data for each registration
876
-     *
877
-     * @access public
878
-     * @param bool $show_errors
879
-     * @return bool
880
-     * @throws \EE_Error
881
-     */
882
-    public function save_all_data($show_errors = true)
883
-    {
884
-        // verify the transaction
885
-        if ($this->transaction instanceof EE_Transaction) {
886
-            // save to ensure that TXN has ID
887
-            $this->transaction->save();
888
-            // grab the saved registrations from the transaction
889
-            foreach ($this->transaction->registrations($this->reg_cache_where_params) as $registration) {
890
-                $this->_save_registration($registration, $show_errors);
891
-            }
892
-        } else {
893
-            if ($show_errors) {
894
-                EE_Error::add_error(
895
-                    esc_html__(
896
-                        'A valid Transaction was not found when attempting to save your registration information.',
897
-                        'event_espresso'
898
-                    ),
899
-                    __FILE__,
900
-                    __FUNCTION__,
901
-                    __LINE__
902
-                );
903
-            }
904
-            return false;
905
-        }
906
-        return true;
907
-    }
908
-
909
-
910
-    /**
911
-     * _save_registration_attendee
912
-     *
913
-     * @param    EE_Registration $registration
914
-     * @param bool               $show_errors
915
-     * @return void
916
-     * @throws \EE_Error
917
-     */
918
-    private function _save_registration($registration, $show_errors = true)
919
-    {
920
-        // verify object
921
-        if ($registration instanceof EE_Registration) {
922
-            // should this registration be processed during this visit ?
923
-            if ($this->visit_allows_processing_of_this_registration($registration)) {
924
-                // set TXN ID
925
-                if (! $registration->transaction_ID()) {
926
-                    $registration->set_transaction_id($this->transaction->ID());
927
-                }
928
-                // verify and save the attendee
929
-                $this->_save_registration_attendee($registration, $show_errors);
930
-                // save answers to reg form questions
931
-                $this->_save_registration_answers($registration, $show_errors);
932
-                // save changes
933
-                $registration->save();
934
-                // update txn cache
935
-                if (! $this->transaction->update_cache_after_object_save('Registration', $registration)) {
936
-                    if ($show_errors) {
937
-                        EE_Error::add_error(
938
-                            esc_html__(
939
-                                'The newly saved Registration object could not be cached on the Transaction.',
940
-                                'event_espresso'
941
-                            ),
942
-                            __FILE__,
943
-                            __FUNCTION__,
944
-                            __LINE__
945
-                        );
946
-                    }
947
-                }
948
-            }
949
-        } else {
950
-            if ($show_errors) {
951
-                EE_Error::add_error(
952
-                    esc_html__(
953
-                        'An invalid Registration object was discovered when attempting to save your registration information.',
954
-                        'event_espresso'
955
-                    ),
956
-                    __FILE__,
957
-                    __FUNCTION__,
958
-                    __LINE__
959
-                );
960
-            }
961
-        }
962
-    }
963
-
964
-
965
-    /**
966
-     * _save_registration_attendee
967
-     *
968
-     * @param    EE_Registration $registration
969
-     * @param bool               $show_errors
970
-     * @return void
971
-     * @throws \EE_Error
972
-     */
973
-    private function _save_registration_attendee($registration, $show_errors = true)
974
-    {
975
-        if ($registration->attendee() instanceof EE_Attendee) {
976
-            // save so that ATT has ID
977
-            $registration->attendee()->save();
978
-            if (! $registration->update_cache_after_object_save('Attendee', $registration->attendee())) {
979
-                if ($show_errors) {
980
-                    EE_Error::add_error(
981
-                        esc_html__(
982
-                            'The newly saved Attendee object could not be cached on the registration.',
983
-                            'event_espresso'
984
-                        ),
985
-                        __FILE__,
986
-                        __FUNCTION__,
987
-                        __LINE__
988
-                    );
989
-                }
990
-            }
991
-        } else {
992
-            if ($show_errors) {
993
-                EE_Error::add_error(
994
-                    sprintf(
995
-                        '%1$s||%1$s $attendee = %2$s',
996
-                        esc_html__(
997
-                            'Either no Attendee information was found, or an invalid Attendee object was discovered when attempting to save your registration information.',
998
-                            'event_espresso'
999
-                        ),
1000
-                        var_export($registration->attendee(), true)
1001
-                    ),
1002
-                    __FILE__,
1003
-                    __FUNCTION__,
1004
-                    __LINE__
1005
-                );
1006
-            }
1007
-        }
1008
-    }
1009
-
1010
-
1011
-    /**
1012
-     * _save_question_answers
1013
-     *
1014
-     * @param    EE_Registration $registration
1015
-     * @param bool               $show_errors
1016
-     * @return void
1017
-     * @throws \EE_Error
1018
-     */
1019
-    private function _save_registration_answers($registration, $show_errors = true)
1020
-    {
1021
-        // now save the answers
1022
-        foreach ($registration->answers() as $cache_key => $answer) {
1023
-            // verify object
1024
-            if ($answer instanceof EE_Answer) {
1025
-                $answer->set_registration($registration->ID());
1026
-                $answer->save();
1027
-                if (! $registration->update_cache_after_object_save('Answer', $answer, $cache_key)) {
1028
-                    if ($show_errors) {
1029
-                        EE_Error::add_error(
1030
-                            esc_html__(
1031
-                                'The newly saved Answer object could not be cached on the registration.',
1032
-                                'event_espresso'
1033
-                            ),
1034
-                            __FILE__,
1035
-                            __FUNCTION__,
1036
-                            __LINE__
1037
-                        );
1038
-                    }
1039
-                }
1040
-            } else {
1041
-                if ($show_errors) {
1042
-                    EE_Error::add_error(
1043
-                        esc_html__(
1044
-                            'An invalid Answer object was discovered when attempting to save your registration information.',
1045
-                            'event_espresso'
1046
-                        ),
1047
-                        __FILE__,
1048
-                        __FUNCTION__,
1049
-                        __LINE__
1050
-                    );
1051
-                }
1052
-            }
1053
-        }
1054
-    }
1055
-
1056
-
1057
-    /**
1058
-     *    refresh_all_entities
1059
-     *   will either refresh the entity map with objects form the db or from the checkout cache
1060
-     *
1061
-     * @access public
1062
-     * @param bool $from_db
1063
-     * @return bool
1064
-     * @throws \EE_Error
1065
-     */
1066
-    public function refresh_all_entities($from_db = false)
1067
-    {
1068
-        $this->current_step->setRequest(EED_Single_Page_Checkout::getRequest());
1069
-        $from_db = $this->current_step->is_final_step() || $this->action === 'process_gateway_response'
1070
-            ? true
1071
-            : $from_db;
1072
-        // $this->log(
1073
-        //     __CLASS__,
1074
-        //     __FUNCTION__,
1075
-        //     __LINE__,
1076
-        //     array('from_db' => $from_db)
1077
-        // );
1078
-        return $from_db ? $this->refresh_from_db() : $this->refresh_entity_map();
1079
-    }
1080
-
1081
-
1082
-    /**
1083
-     *  refresh_entity_map
1084
-     *  simply loops through the current transaction and updates each
1085
-     *  model's entity map using EEM_Base::refresh_entity_map_from_db()
1086
-     *
1087
-     * @access public
1088
-     * @return bool
1089
-     * @throws \EE_Error
1090
-     */
1091
-    protected function refresh_from_db()
1092
-    {
1093
-        // verify the transaction
1094
-        if ($this->transaction instanceof EE_Transaction && $this->transaction->ID()) {
1095
-            // pull fresh TXN data from the db
1096
-            $this->transaction = $this->transaction->get_model()->refresh_entity_map_from_db($this->transaction->ID());
1097
-            // update EE_Checkout's cached primary_attendee object
1098
-            $this->primary_attendee_obj = $this->_refresh_primary_attendee_obj_from_db($this->transaction);
1099
-            // update EE_Checkout's cached payment object
1100
-            $payment = $this->transaction->last_payment();
1101
-            $this->payment = $payment instanceof EE_Payment ? $payment : $this->payment;
1102
-            // update EE_Checkout's cached payment_method object
1103
-            $payment_method = $this->payment instanceof EE_Payment ? $this->payment->payment_method() : null;
1104
-            $this->payment_method = $payment_method instanceof EE_Payment_Method ? $payment_method
1105
-                : $this->payment_method;
1106
-            // now refresh the cart, based on the TXN
1107
-            $this->cart = $this->get_cart_for_transaction($this->transaction);
1108
-        } else {
1109
-            EE_Error::add_error(
1110
-                esc_html__(
1111
-                    'A valid Transaction was not found when attempting to update the model entity mapper.',
1112
-                    'event_espresso'
1113
-                ),
1114
-                __FILE__,
1115
-                __FUNCTION__,
1116
-                __LINE__
1117
-            );
1118
-            return false;
1119
-        }
1120
-        return true;
1121
-    }
1122
-
1123
-
1124
-    /**
1125
-     * _refresh_primary_attendee_obj_from_db
1126
-     *
1127
-     * @param   EE_Transaction $transaction
1128
-     * @return  EE_Attendee | null
1129
-     * @throws \EE_Error
1130
-     */
1131
-    protected function _refresh_primary_attendee_obj_from_db(EE_Transaction $transaction)
1132
-    {
1133
-        $primary_attendee_obj = null;
1134
-        // grab the saved registrations from the transaction
1135
-        foreach ($transaction->registrations($this->reg_cache_where_params, true) as $registration) {
1136
-            // verify object
1137
-            if ($registration instanceof EE_Registration) {
1138
-                $attendee = $registration->attendee();
1139
-                // verify object && maybe cache primary_attendee_obj ?
1140
-                if ($attendee instanceof EE_Attendee && $registration->is_primary_registrant()) {
1141
-                    $primary_attendee_obj = $attendee;
1142
-                }
1143
-            } else {
1144
-                EE_Error::add_error(
1145
-                    esc_html__(
1146
-                        'An invalid Registration object was discovered when attempting to update the model entity mapper.',
1147
-                        'event_espresso'
1148
-                    ),
1149
-                    __FILE__,
1150
-                    __FUNCTION__,
1151
-                    __LINE__
1152
-                );
1153
-            }
1154
-        }
1155
-        return $primary_attendee_obj;
1156
-    }
1157
-
1158
-
1159
-    /**
1160
-     *  refresh_entity_map
1161
-     *  simply loops through the current transaction and updates
1162
-     *  each model's entity map using EEM_Base::refresh_entity_map_with()
1163
-     *
1164
-     * @access public
1165
-     * @return bool
1166
-     * @throws \EE_Error
1167
-     */
1168
-    protected function refresh_entity_map()
1169
-    {
1170
-        // verify the transaction
1171
-        if ($this->transaction instanceof EE_Transaction && $this->transaction->ID()) {
1172
-            // never cache payment info
1173
-            $this->transaction->clear_cache('Payment');
1174
-            // is the Payment Options Reg Step completed ?
1175
-            if ($this->transaction->reg_step_completed('payment_options')) {
1176
-                // then check for payments and update TXN accordingly
1177
-                /** @type EE_Transaction_Payments $transaction_payments */
1178
-                $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
1179
-                $transaction_payments->calculate_total_payments_and_update_status($this->transaction);
1180
-            }
1181
-            // grab the saved registrations from the transaction
1182
-            foreach ($this->transaction->registrations($this->reg_cache_where_params) as $reg_cache_ID => $registration) {
1183
-                $this->_refresh_registration($reg_cache_ID, $registration);
1184
-            }
1185
-            // make sure our cached TXN is added to the model entity mapper
1186
-            $this->transaction = $this->transaction->get_model()->refresh_entity_map_with(
1187
-                $this->transaction->ID(),
1188
-                $this->transaction
1189
-            );
1190
-        } else {
1191
-            EE_Error::add_error(
1192
-                esc_html__(
1193
-                    'A valid Transaction was not found when attempting to update the model entity mapper.',
1194
-                    'event_espresso'
1195
-                ),
1196
-                __FILE__,
1197
-                __FUNCTION__,
1198
-                __LINE__
1199
-            );
1200
-            return false;
1201
-        }
1202
-        // verify and update the cart because inaccurate totals are not so much fun
1203
-        if ($this->cart instanceof EE_Cart) {
1204
-            $grand_total = $this->cart->get_grand_total();
1205
-            if ($grand_total instanceof EE_Line_Item && $grand_total->ID()) {
1206
-                $grand_total->recalculate_total_including_taxes();
1207
-                $grand_total = $grand_total->get_model()->refresh_entity_map_with(
1208
-                    $this->cart->get_grand_total()->ID(),
1209
-                    $this->cart->get_grand_total()
1210
-                );
1211
-            }
1212
-            if ($grand_total instanceof EE_Line_Item) {
1213
-                $this->cart = EE_Cart::instance($grand_total);
1214
-            } else {
1215
-                EE_Error::add_error(
1216
-                    esc_html__(
1217
-                        'A valid Cart was not found when attempting to update the model entity mapper.',
1218
-                        'event_espresso'
1219
-                    ),
1220
-                    __FILE__,
1221
-                    __FUNCTION__,
1222
-                    __LINE__
1223
-                );
1224
-                return false;
1225
-            }
1226
-        }
1227
-        return true;
1228
-    }
1229
-
1230
-
1231
-    /**
1232
-     * _refresh_registration
1233
-     *
1234
-     * @param    string | int    $reg_cache_ID
1235
-     * @param    EE_Registration $registration
1236
-     * @return void
1237
-     * @throws \EE_Error
1238
-     */
1239
-    protected function _refresh_registration($reg_cache_ID, $registration)
1240
-    {
1241
-
1242
-        // verify object
1243
-        if ($registration instanceof EE_Registration) {
1244
-            // update the entity mapper attendee
1245
-            $this->_refresh_registration_attendee($registration);
1246
-            // update the entity mapper answers for reg form questions
1247
-            $this->_refresh_registration_answers($registration);
1248
-            // make sure the cached registration is added to the model entity mapper
1249
-            $registration->get_model()->refresh_entity_map_with($reg_cache_ID, $registration);
1250
-        } else {
1251
-            EE_Error::add_error(
1252
-                esc_html__(
1253
-                    'An invalid Registration object was discovered when attempting to update the model entity mapper.',
1254
-                    'event_espresso'
1255
-                ),
1256
-                __FILE__,
1257
-                __FUNCTION__,
1258
-                __LINE__
1259
-            );
1260
-        }
1261
-    }
1262
-
1263
-
1264
-    /**
1265
-     * _save_registration_attendee
1266
-     *
1267
-     * @param    EE_Registration $registration
1268
-     * @return void
1269
-     * @throws \EE_Error
1270
-     */
1271
-    protected function _refresh_registration_attendee($registration)
1272
-    {
1273
-        $attendee = $registration->attendee();
1274
-        // verify object
1275
-        if ($attendee instanceof EE_Attendee && $attendee->ID()) {
1276
-            // make sure the cached attendee is added to the model entity mapper
1277
-            $registration->attendee()->get_model()->refresh_entity_map_with($attendee->ID(), $attendee);
1278
-            // maybe cache primary_attendee_obj ?
1279
-            if ($registration->is_primary_registrant()) {
1280
-                $this->primary_attendee_obj = $attendee;
1281
-            }
1282
-        }
1283
-    }
1284
-
1285
-
1286
-    /**
1287
-     * _refresh_registration_answers
1288
-     *
1289
-     * @param    EE_Registration $registration
1290
-     * @return void
1291
-     * @throws \EE_Error
1292
-     */
1293
-    protected function _refresh_registration_answers($registration)
1294
-    {
1295
-
1296
-        // now update the answers
1297
-        foreach ($registration->answers() as $cache_key => $answer) {
1298
-            // verify object
1299
-            if ($answer instanceof EE_Answer) {
1300
-                if ($answer->ID()) {
1301
-                    // make sure the cached answer is added to the model entity mapper
1302
-                    $answer->get_model()->refresh_entity_map_with($answer->ID(), $answer);
1303
-                }
1304
-            } else {
1305
-                EE_Error::add_error(
1306
-                    esc_html__(
1307
-                        'An invalid Answer object was discovered when attempting to update the model entity mapper.',
1308
-                        'event_espresso'
1309
-                    ),
1310
-                    __FILE__,
1311
-                    __FUNCTION__,
1312
-                    __LINE__
1313
-                );
1314
-            }
1315
-        }
1316
-    }
1317
-
1318
-
1319
-    /**
1320
-     *    __sleep
1321
-     * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
1322
-     * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
1323
-     * reg form, because if needed, it will be regenerated anyways
1324
-     *
1325
-     * @return array
1326
-     * @throws \EE_Error
1327
-     */
1328
-    public function __sleep()
1329
-    {
1330
-        if ($this->primary_attendee_obj instanceof EE_Attendee && $this->primary_attendee_obj->ID()) {
1331
-            $this->primary_attendee_obj = $this->primary_attendee_obj->ID();
1332
-        }        // remove the reg form and the checkout
1333
-        if ($this->transaction instanceof EE_Transaction && $this->transaction->ID()) {
1334
-            $this->transaction = $this->transaction->ID();
1335
-        }        // remove the reg form and the checkout
1336
-        return array_diff(array_keys(get_object_vars($this)), array('billing_form', 'registration_form'));
1337
-    }
1338
-
1339
-
1340
-    /**
1341
-     *    __wakeup
1342
-     * to conserve db space, we are removing the EE_Checkout object from EE_SPCO_Reg_Step objects upon serialization
1343
-     * this will reinstate the EE_Checkout object on each EE_SPCO_Reg_Step object
1344
-     */
1345
-    public function __wakeup()
1346
-    {
1347
-        try {
1348
-            if (! $this->primary_attendee_obj instanceof EE_Attendee && absint($this->primary_attendee_obj) !== 0) {
1349
-                // $this->primary_attendee_obj is actually just an ID, so use it to get the object from the db
1350
-                $this->primary_attendee_obj = EEM_Attendee::instance()->get_one_by_ID($this->primary_attendee_obj);
1351
-            }
1352
-            if (! $this->transaction instanceof EE_Transaction && absint($this->transaction) !== 0) {
1353
-                // $this->transaction is actually just an ID, so use it to get the object from the db
1354
-                $this->transaction = EEM_Transaction::instance()->get_one_by_ID($this->transaction);
1355
-            }
1356
-            foreach ($this->reg_steps as $reg_step) {
1357
-                $reg_step->checkout = $this;
1358
-            }
1359
-        } catch (Throwable $e) {
1360
-            EE_Error::add_error(
1361
-                esc_html__(
1362
-                    'An error occurred while attempting to reinstate a EE_SPCO_Reg_Step object in EE_Checkout::__wakeup().',
1363
-                    'event_espresso'
1364
-                ),
1365
-                __FILE__,
1366
-                __FUNCTION__,
1367
-                __LINE__
1368
-            );
1369
-        }
1370
-    }
1371
-
1372
-
1373
-    /**
1374
-     * debug
1375
-     *
1376
-     * @param string $class
1377
-     * @param string $func
1378
-     * @param string $line
1379
-     * @param array  $info
1380
-     * @param bool   $display_request
1381
-     * @throws EE_Error
1382
-     * @throws ReflectionException
1383
-     */
1384
-    public function log($class = '', $func = '', $line = '', $info = array(), $display_request = false)
1385
-    {
1386
-        $disabled = true;
1387
-        if (WP_DEBUG && ! $disabled) {
1388
-            $debug_data = get_option('EE_DEBUG_SPCO_' . EE_Session::instance()->id(), array());
1389
-            $default_data = array(
1390
-                $class                    => $func . '() : ' . $line,
1391
-                'request->step'           => $this->step,
1392
-                'request->action'         => $this->action,
1393
-                'current_step->slug'      => $this->current_step instanceof EE_SPCO_Reg_Step ?
1394
-                    $this->current_step->slug() : '',
1395
-                'current_step->completed' => $this->current_step instanceof EE_SPCO_Reg_Step ?
1396
-                    $this->current_step->completed() : '',
1397
-                'txn_status_updated'      => $this->transaction->txn_status_updated(),
1398
-                'reg_status_updated'      => $this->reg_status_updated,
1399
-                'reg_url_link'            => $this->reg_url_link,
1400
-            );
1401
-            if ($this->transaction instanceof EE_Transaction) {
1402
-                $default_data['TXN_status'] = $this->transaction->status_ID();
1403
-                $default_data['TXN_reg_steps'] = $this->transaction->reg_steps();
1404
-                foreach ($this->transaction->registrations($this->reg_cache_where_params) as $REG_ID => $registration) {
1405
-                    $default_data['registrations'][ $REG_ID ] = $registration->status_ID();
1406
-                }
1407
-                if ($this->transaction->ID()) {
1408
-                    $TXN_ID = 'EE_Transaction: ' . $this->transaction->ID();
1409
-                    // don't serialize objects
1410
-                    $info = $this->_strip_objects($info);
1411
-                    if (! isset($debug_data[ $TXN_ID ])) {
1412
-                        $debug_data[ $TXN_ID ] = array();
1413
-                    }
1414
-                    $debug_data[ $TXN_ID ][ microtime() ] = array_merge(
1415
-                        $default_data,
1416
-                        $info
1417
-                    );
1418
-                    update_option('EE_DEBUG_SPCO_' . EE_Session::instance()->id(), $debug_data);
1419
-                }
1420
-            }
1421
-        }
1422
-    }
1423
-
1424
-
1425
-    /**
1426
-     * _strip_objects
1427
-     *
1428
-     * @param array $info
1429
-     * @return array
1430
-     */
1431
-    public function _strip_objects($info = array())
1432
-    {
1433
-        foreach ((array) $info as $key => $value) {
1434
-            if (is_array($value)) {
1435
-                $info[ $key ] = $this->_strip_objects($value);
1436
-            } elseif (is_object($value)) {
1437
-                $object_class = get_class($value);
1438
-                $info[ $object_class ] = array();
1439
-                $info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : 0;
1440
-                if (method_exists($value, 'status')) {
1441
-                    $info[ $object_class ]['status'] = $value->status();
1442
-                } elseif (method_exists($value, 'status_ID')) {
1443
-                    $info[ $object_class ]['status'] = $value->status_ID();
1444
-                }
1445
-                unset($info[ $key ]);
1446
-            }
1447
-        }
1448
-        return (array) $info;
1449
-    }
630
+			! $this->revisit
631
+			&& (
632
+				// and displaying the reg step form for the first time ?
633
+				$this->action === 'display_spco_reg_step'
634
+				// or initializing the final step
635
+				|| $reg_step instanceof EE_SPCO_Reg_Step_Finalize_Registration
636
+			)
637
+		) {
638
+			// set the start time for this reg step
639
+			if (! $this->transaction->set_reg_step_initiated($reg_step->slug())) {
640
+				if (WP_DEBUG) {
641
+					EE_Error::add_error(
642
+						sprintf(
643
+							esc_html__('The "%1$s" registration step was not initialized properly.', 'event_espresso'),
644
+							$reg_step->name()
645
+						),
646
+						__FILE__,
647
+						__FUNCTION__,
648
+						__LINE__
649
+					);
650
+				}
651
+			}
652
+		}
653
+	}
654
+
655
+
656
+	/**
657
+	 *    set_reg_step_JSON_info
658
+	 *
659
+	 * @access public
660
+	 * @return    void
661
+	 */
662
+	public function set_reg_step_JSON_info()
663
+	{
664
+		EE_Registry::$i18n_js_strings['reg_steps'] = array();
665
+		// pass basic reg step data to JS
666
+		foreach ($this->reg_steps as $reg_step) {
667
+			EE_Registry::$i18n_js_strings['reg_steps'][] = $reg_step->slug();
668
+		}
669
+		// reset reg step html
670
+		// $this->json_response->set_reg_step_html('');
671
+	}
672
+
673
+
674
+	/**
675
+	 *    reset_reg_steps
676
+	 *
677
+	 * @access public
678
+	 * @return void
679
+	 */
680
+	public function reset_reg_steps()
681
+	{
682
+		$this->sort_reg_steps();
683
+		$this->set_current_step(EED_Single_Page_Checkout::getRequest()->getRequestParam('step'));
684
+		$this->set_next_step();
685
+		// the text that appears on the reg step form submit button
686
+		$this->current_step->set_submit_button_text();
687
+		$this->set_reg_step_JSON_info();
688
+	}
689
+
690
+
691
+	/**
692
+	 *    get_registration_time_limit
693
+	 *
694
+	 * @access    public
695
+	 * @return        string
696
+	 */
697
+	public function get_registration_time_limit(): string
698
+	{
699
+		$registration_time_limit = (float) (EE_Registry::instance()->SSN->expiration() - time());
700
+		$time_limit_format = $registration_time_limit > 60 * MINUTE_IN_SECONDS ? 'H:i:s' : 'i:s';
701
+		$registration_time_limit = date($time_limit_format, $registration_time_limit);
702
+		return (string) apply_filters(
703
+			'FHEE__EE_Checkout__get_registration_time_limit__registration_time_limit',
704
+			$registration_time_limit
705
+		);
706
+	}
707
+
708
+
709
+	/**
710
+	 * payment_required
711
+	 *
712
+	 * @return boolean
713
+	 */
714
+	public function payment_required()
715
+	{
716
+		// if NOT:
717
+		//     registration via admin
718
+		//      completed TXN
719
+		//      overpaid TXN
720
+		//      free TXN(total = 0.00)
721
+		//      then payment required is TRUE
722
+		return ! ($this->admin_request
723
+				  || $this->transaction->is_completed()
724
+				  || $this->transaction->is_overpaid()
725
+				  || $this->transaction->is_free()) ? true : false;
726
+	}
727
+
728
+
729
+	/**
730
+	 * get_cart_for_transaction
731
+	 *
732
+	 * @access public
733
+	 * @param EE_Transaction $transaction
734
+	 * @return EE_Cart
735
+	 */
736
+	public function get_cart_for_transaction($transaction)
737
+	{
738
+		$session = EE_Registry::instance()->load_core('Session');
739
+		$cart = $transaction instanceof EE_Transaction ? EE_Cart::get_cart_from_txn($transaction, $session) : null;
740
+		// verify cart
741
+		if (! $cart instanceof EE_Cart) {
742
+			$cart = EE_Registry::instance()->load_core('Cart');
743
+		}
744
+
745
+		return $cart;
746
+	}
747
+
748
+
749
+	/**
750
+	 *    initialize_txn_reg_steps_array
751
+	 *
752
+	 * @access public
753
+	 * @return    array
754
+	 */
755
+	public function initialize_txn_reg_steps_array()
756
+	{
757
+		$txn_reg_steps_array = array();
758
+		foreach ($this->reg_steps as $reg_step) {
759
+			$txn_reg_steps_array[ $reg_step->slug() ] = false;
760
+		}
761
+		return $txn_reg_steps_array;
762
+	}
763
+
764
+
765
+	/**
766
+	 *    update_txn_reg_steps_array
767
+	 *
768
+	 * @access public
769
+	 * @return    bool
770
+	 * @throws \EE_Error
771
+	 */
772
+	public function update_txn_reg_steps_array()
773
+	{
774
+		$updated = false;
775
+		foreach ($this->reg_steps as $reg_step) {
776
+			if ($reg_step->completed()) {
777
+				$updated = $this->transaction->set_reg_step_completed($reg_step->slug())
778
+					? true
779
+					: $updated;
780
+			}
781
+		}
782
+		if ($updated) {
783
+			$this->transaction->save();
784
+		}
785
+		return $updated;
786
+	}
787
+
788
+
789
+	/**
790
+	 *    stash_transaction_and_checkout
791
+	 *
792
+	 * @access public
793
+	 * @return    void
794
+	 * @throws \EE_Error
795
+	 */
796
+	public function stash_transaction_and_checkout()
797
+	{
798
+		if (! $this->revisit) {
799
+			$this->update_txn_reg_steps_array();
800
+		}
801
+		$this->track_transaction_and_registration_status_updates();
802
+		// save all data to the db, but suppress errors
803
+		// $this->save_all_data( FALSE );
804
+		// cache the checkout in the session
805
+		EE_Registry::instance()->SSN->set_checkout($this);
806
+	}
807
+
808
+
809
+	/**
810
+	 *    track_transaction_and_registration_status_updates
811
+	 *    stores whether any updates were made to the TXN or it's related registrations
812
+	 *
813
+	 * @access public
814
+	 * @return void
815
+	 * @throws \EE_Error
816
+	 */
817
+	public function track_transaction_and_registration_status_updates()
818
+	{
819
+		// verify the transaction
820
+		if ($this->transaction instanceof EE_Transaction) {
821
+			// has there been a TXN status change during this checkout?
822
+			$this->txn_status_updated = $this->transaction->txn_status_updated();
823
+			/** @type EE_Registration_Processor $registration_processor */
824
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
825
+			// grab the saved registrations from the transaction
826
+			foreach ($this->transaction->registrations($this->reg_cache_where_params) as $registration) {
827
+				if ($registration_processor->reg_status_updated($registration->ID())) {
828
+					$this->set_reg_status_updated($registration->ID(), true);
829
+				}
830
+			}
831
+		}
832
+	}
833
+
834
+
835
+	/**
836
+	 *    visit_allows_processing_of_this_registration
837
+	 *    determines if the current SPCO visit should allow the passed EE_Registration to be used in processing.
838
+	 *    one of the following conditions must be met:
839
+	 *        EITHER:    A) first time thru SPCO -> process ALL registrations ( NOT a revisit )
840
+	 *        OR :        B) primary registrant is editing info -> process ALL registrations ( primary_revisit )
841
+	 *        OR :        C) another registrant is editing info -> ONLY process their registration ( revisit AND their
842
+	 *        reg_url_link matches )
843
+	 *
844
+	 * @access public
845
+	 * @param    EE_Registration $registration
846
+	 * @return    bool
847
+	 * @throws \EE_Error
848
+	 */
849
+	public function visit_allows_processing_of_this_registration(EE_Registration $registration)
850
+	{
851
+		return ! $this->revisit
852
+			   || $this->primary_revisit
853
+			   || (
854
+				   $this->revisit && $this->reg_url_link === $registration->reg_url_link()
855
+			   )
856
+			? true
857
+			: false;
858
+	}
859
+
860
+
861
+	/**
862
+	 *    _transaction_has_primary_registration
863
+	 *
864
+	 * @access        private
865
+	 * @return        bool
866
+	 */
867
+	public function transaction_has_primary_registrant()
868
+	{
869
+		return $this->primary_attendee_obj instanceof EE_Attendee ? true : false;
870
+	}
871
+
872
+
873
+	/**
874
+	 *    save_all_data
875
+	 *    simply loops through the current transaction and saves all data for each registration
876
+	 *
877
+	 * @access public
878
+	 * @param bool $show_errors
879
+	 * @return bool
880
+	 * @throws \EE_Error
881
+	 */
882
+	public function save_all_data($show_errors = true)
883
+	{
884
+		// verify the transaction
885
+		if ($this->transaction instanceof EE_Transaction) {
886
+			// save to ensure that TXN has ID
887
+			$this->transaction->save();
888
+			// grab the saved registrations from the transaction
889
+			foreach ($this->transaction->registrations($this->reg_cache_where_params) as $registration) {
890
+				$this->_save_registration($registration, $show_errors);
891
+			}
892
+		} else {
893
+			if ($show_errors) {
894
+				EE_Error::add_error(
895
+					esc_html__(
896
+						'A valid Transaction was not found when attempting to save your registration information.',
897
+						'event_espresso'
898
+					),
899
+					__FILE__,
900
+					__FUNCTION__,
901
+					__LINE__
902
+				);
903
+			}
904
+			return false;
905
+		}
906
+		return true;
907
+	}
908
+
909
+
910
+	/**
911
+	 * _save_registration_attendee
912
+	 *
913
+	 * @param    EE_Registration $registration
914
+	 * @param bool               $show_errors
915
+	 * @return void
916
+	 * @throws \EE_Error
917
+	 */
918
+	private function _save_registration($registration, $show_errors = true)
919
+	{
920
+		// verify object
921
+		if ($registration instanceof EE_Registration) {
922
+			// should this registration be processed during this visit ?
923
+			if ($this->visit_allows_processing_of_this_registration($registration)) {
924
+				// set TXN ID
925
+				if (! $registration->transaction_ID()) {
926
+					$registration->set_transaction_id($this->transaction->ID());
927
+				}
928
+				// verify and save the attendee
929
+				$this->_save_registration_attendee($registration, $show_errors);
930
+				// save answers to reg form questions
931
+				$this->_save_registration_answers($registration, $show_errors);
932
+				// save changes
933
+				$registration->save();
934
+				// update txn cache
935
+				if (! $this->transaction->update_cache_after_object_save('Registration', $registration)) {
936
+					if ($show_errors) {
937
+						EE_Error::add_error(
938
+							esc_html__(
939
+								'The newly saved Registration object could not be cached on the Transaction.',
940
+								'event_espresso'
941
+							),
942
+							__FILE__,
943
+							__FUNCTION__,
944
+							__LINE__
945
+						);
946
+					}
947
+				}
948
+			}
949
+		} else {
950
+			if ($show_errors) {
951
+				EE_Error::add_error(
952
+					esc_html__(
953
+						'An invalid Registration object was discovered when attempting to save your registration information.',
954
+						'event_espresso'
955
+					),
956
+					__FILE__,
957
+					__FUNCTION__,
958
+					__LINE__
959
+				);
960
+			}
961
+		}
962
+	}
963
+
964
+
965
+	/**
966
+	 * _save_registration_attendee
967
+	 *
968
+	 * @param    EE_Registration $registration
969
+	 * @param bool               $show_errors
970
+	 * @return void
971
+	 * @throws \EE_Error
972
+	 */
973
+	private function _save_registration_attendee($registration, $show_errors = true)
974
+	{
975
+		if ($registration->attendee() instanceof EE_Attendee) {
976
+			// save so that ATT has ID
977
+			$registration->attendee()->save();
978
+			if (! $registration->update_cache_after_object_save('Attendee', $registration->attendee())) {
979
+				if ($show_errors) {
980
+					EE_Error::add_error(
981
+						esc_html__(
982
+							'The newly saved Attendee object could not be cached on the registration.',
983
+							'event_espresso'
984
+						),
985
+						__FILE__,
986
+						__FUNCTION__,
987
+						__LINE__
988
+					);
989
+				}
990
+			}
991
+		} else {
992
+			if ($show_errors) {
993
+				EE_Error::add_error(
994
+					sprintf(
995
+						'%1$s||%1$s $attendee = %2$s',
996
+						esc_html__(
997
+							'Either no Attendee information was found, or an invalid Attendee object was discovered when attempting to save your registration information.',
998
+							'event_espresso'
999
+						),
1000
+						var_export($registration->attendee(), true)
1001
+					),
1002
+					__FILE__,
1003
+					__FUNCTION__,
1004
+					__LINE__
1005
+				);
1006
+			}
1007
+		}
1008
+	}
1009
+
1010
+
1011
+	/**
1012
+	 * _save_question_answers
1013
+	 *
1014
+	 * @param    EE_Registration $registration
1015
+	 * @param bool               $show_errors
1016
+	 * @return void
1017
+	 * @throws \EE_Error
1018
+	 */
1019
+	private function _save_registration_answers($registration, $show_errors = true)
1020
+	{
1021
+		// now save the answers
1022
+		foreach ($registration->answers() as $cache_key => $answer) {
1023
+			// verify object
1024
+			if ($answer instanceof EE_Answer) {
1025
+				$answer->set_registration($registration->ID());
1026
+				$answer->save();
1027
+				if (! $registration->update_cache_after_object_save('Answer', $answer, $cache_key)) {
1028
+					if ($show_errors) {
1029
+						EE_Error::add_error(
1030
+							esc_html__(
1031
+								'The newly saved Answer object could not be cached on the registration.',
1032
+								'event_espresso'
1033
+							),
1034
+							__FILE__,
1035
+							__FUNCTION__,
1036
+							__LINE__
1037
+						);
1038
+					}
1039
+				}
1040
+			} else {
1041
+				if ($show_errors) {
1042
+					EE_Error::add_error(
1043
+						esc_html__(
1044
+							'An invalid Answer object was discovered when attempting to save your registration information.',
1045
+							'event_espresso'
1046
+						),
1047
+						__FILE__,
1048
+						__FUNCTION__,
1049
+						__LINE__
1050
+					);
1051
+				}
1052
+			}
1053
+		}
1054
+	}
1055
+
1056
+
1057
+	/**
1058
+	 *    refresh_all_entities
1059
+	 *   will either refresh the entity map with objects form the db or from the checkout cache
1060
+	 *
1061
+	 * @access public
1062
+	 * @param bool $from_db
1063
+	 * @return bool
1064
+	 * @throws \EE_Error
1065
+	 */
1066
+	public function refresh_all_entities($from_db = false)
1067
+	{
1068
+		$this->current_step->setRequest(EED_Single_Page_Checkout::getRequest());
1069
+		$from_db = $this->current_step->is_final_step() || $this->action === 'process_gateway_response'
1070
+			? true
1071
+			: $from_db;
1072
+		// $this->log(
1073
+		//     __CLASS__,
1074
+		//     __FUNCTION__,
1075
+		//     __LINE__,
1076
+		//     array('from_db' => $from_db)
1077
+		// );
1078
+		return $from_db ? $this->refresh_from_db() : $this->refresh_entity_map();
1079
+	}
1080
+
1081
+
1082
+	/**
1083
+	 *  refresh_entity_map
1084
+	 *  simply loops through the current transaction and updates each
1085
+	 *  model's entity map using EEM_Base::refresh_entity_map_from_db()
1086
+	 *
1087
+	 * @access public
1088
+	 * @return bool
1089
+	 * @throws \EE_Error
1090
+	 */
1091
+	protected function refresh_from_db()
1092
+	{
1093
+		// verify the transaction
1094
+		if ($this->transaction instanceof EE_Transaction && $this->transaction->ID()) {
1095
+			// pull fresh TXN data from the db
1096
+			$this->transaction = $this->transaction->get_model()->refresh_entity_map_from_db($this->transaction->ID());
1097
+			// update EE_Checkout's cached primary_attendee object
1098
+			$this->primary_attendee_obj = $this->_refresh_primary_attendee_obj_from_db($this->transaction);
1099
+			// update EE_Checkout's cached payment object
1100
+			$payment = $this->transaction->last_payment();
1101
+			$this->payment = $payment instanceof EE_Payment ? $payment : $this->payment;
1102
+			// update EE_Checkout's cached payment_method object
1103
+			$payment_method = $this->payment instanceof EE_Payment ? $this->payment->payment_method() : null;
1104
+			$this->payment_method = $payment_method instanceof EE_Payment_Method ? $payment_method
1105
+				: $this->payment_method;
1106
+			// now refresh the cart, based on the TXN
1107
+			$this->cart = $this->get_cart_for_transaction($this->transaction);
1108
+		} else {
1109
+			EE_Error::add_error(
1110
+				esc_html__(
1111
+					'A valid Transaction was not found when attempting to update the model entity mapper.',
1112
+					'event_espresso'
1113
+				),
1114
+				__FILE__,
1115
+				__FUNCTION__,
1116
+				__LINE__
1117
+			);
1118
+			return false;
1119
+		}
1120
+		return true;
1121
+	}
1122
+
1123
+
1124
+	/**
1125
+	 * _refresh_primary_attendee_obj_from_db
1126
+	 *
1127
+	 * @param   EE_Transaction $transaction
1128
+	 * @return  EE_Attendee | null
1129
+	 * @throws \EE_Error
1130
+	 */
1131
+	protected function _refresh_primary_attendee_obj_from_db(EE_Transaction $transaction)
1132
+	{
1133
+		$primary_attendee_obj = null;
1134
+		// grab the saved registrations from the transaction
1135
+		foreach ($transaction->registrations($this->reg_cache_where_params, true) as $registration) {
1136
+			// verify object
1137
+			if ($registration instanceof EE_Registration) {
1138
+				$attendee = $registration->attendee();
1139
+				// verify object && maybe cache primary_attendee_obj ?
1140
+				if ($attendee instanceof EE_Attendee && $registration->is_primary_registrant()) {
1141
+					$primary_attendee_obj = $attendee;
1142
+				}
1143
+			} else {
1144
+				EE_Error::add_error(
1145
+					esc_html__(
1146
+						'An invalid Registration object was discovered when attempting to update the model entity mapper.',
1147
+						'event_espresso'
1148
+					),
1149
+					__FILE__,
1150
+					__FUNCTION__,
1151
+					__LINE__
1152
+				);
1153
+			}
1154
+		}
1155
+		return $primary_attendee_obj;
1156
+	}
1157
+
1158
+
1159
+	/**
1160
+	 *  refresh_entity_map
1161
+	 *  simply loops through the current transaction and updates
1162
+	 *  each model's entity map using EEM_Base::refresh_entity_map_with()
1163
+	 *
1164
+	 * @access public
1165
+	 * @return bool
1166
+	 * @throws \EE_Error
1167
+	 */
1168
+	protected function refresh_entity_map()
1169
+	{
1170
+		// verify the transaction
1171
+		if ($this->transaction instanceof EE_Transaction && $this->transaction->ID()) {
1172
+			// never cache payment info
1173
+			$this->transaction->clear_cache('Payment');
1174
+			// is the Payment Options Reg Step completed ?
1175
+			if ($this->transaction->reg_step_completed('payment_options')) {
1176
+				// then check for payments and update TXN accordingly
1177
+				/** @type EE_Transaction_Payments $transaction_payments */
1178
+				$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
1179
+				$transaction_payments->calculate_total_payments_and_update_status($this->transaction);
1180
+			}
1181
+			// grab the saved registrations from the transaction
1182
+			foreach ($this->transaction->registrations($this->reg_cache_where_params) as $reg_cache_ID => $registration) {
1183
+				$this->_refresh_registration($reg_cache_ID, $registration);
1184
+			}
1185
+			// make sure our cached TXN is added to the model entity mapper
1186
+			$this->transaction = $this->transaction->get_model()->refresh_entity_map_with(
1187
+				$this->transaction->ID(),
1188
+				$this->transaction
1189
+			);
1190
+		} else {
1191
+			EE_Error::add_error(
1192
+				esc_html__(
1193
+					'A valid Transaction was not found when attempting to update the model entity mapper.',
1194
+					'event_espresso'
1195
+				),
1196
+				__FILE__,
1197
+				__FUNCTION__,
1198
+				__LINE__
1199
+			);
1200
+			return false;
1201
+		}
1202
+		// verify and update the cart because inaccurate totals are not so much fun
1203
+		if ($this->cart instanceof EE_Cart) {
1204
+			$grand_total = $this->cart->get_grand_total();
1205
+			if ($grand_total instanceof EE_Line_Item && $grand_total->ID()) {
1206
+				$grand_total->recalculate_total_including_taxes();
1207
+				$grand_total = $grand_total->get_model()->refresh_entity_map_with(
1208
+					$this->cart->get_grand_total()->ID(),
1209
+					$this->cart->get_grand_total()
1210
+				);
1211
+			}
1212
+			if ($grand_total instanceof EE_Line_Item) {
1213
+				$this->cart = EE_Cart::instance($grand_total);
1214
+			} else {
1215
+				EE_Error::add_error(
1216
+					esc_html__(
1217
+						'A valid Cart was not found when attempting to update the model entity mapper.',
1218
+						'event_espresso'
1219
+					),
1220
+					__FILE__,
1221
+					__FUNCTION__,
1222
+					__LINE__
1223
+				);
1224
+				return false;
1225
+			}
1226
+		}
1227
+		return true;
1228
+	}
1229
+
1230
+
1231
+	/**
1232
+	 * _refresh_registration
1233
+	 *
1234
+	 * @param    string | int    $reg_cache_ID
1235
+	 * @param    EE_Registration $registration
1236
+	 * @return void
1237
+	 * @throws \EE_Error
1238
+	 */
1239
+	protected function _refresh_registration($reg_cache_ID, $registration)
1240
+	{
1241
+
1242
+		// verify object
1243
+		if ($registration instanceof EE_Registration) {
1244
+			// update the entity mapper attendee
1245
+			$this->_refresh_registration_attendee($registration);
1246
+			// update the entity mapper answers for reg form questions
1247
+			$this->_refresh_registration_answers($registration);
1248
+			// make sure the cached registration is added to the model entity mapper
1249
+			$registration->get_model()->refresh_entity_map_with($reg_cache_ID, $registration);
1250
+		} else {
1251
+			EE_Error::add_error(
1252
+				esc_html__(
1253
+					'An invalid Registration object was discovered when attempting to update the model entity mapper.',
1254
+					'event_espresso'
1255
+				),
1256
+				__FILE__,
1257
+				__FUNCTION__,
1258
+				__LINE__
1259
+			);
1260
+		}
1261
+	}
1262
+
1263
+
1264
+	/**
1265
+	 * _save_registration_attendee
1266
+	 *
1267
+	 * @param    EE_Registration $registration
1268
+	 * @return void
1269
+	 * @throws \EE_Error
1270
+	 */
1271
+	protected function _refresh_registration_attendee($registration)
1272
+	{
1273
+		$attendee = $registration->attendee();
1274
+		// verify object
1275
+		if ($attendee instanceof EE_Attendee && $attendee->ID()) {
1276
+			// make sure the cached attendee is added to the model entity mapper
1277
+			$registration->attendee()->get_model()->refresh_entity_map_with($attendee->ID(), $attendee);
1278
+			// maybe cache primary_attendee_obj ?
1279
+			if ($registration->is_primary_registrant()) {
1280
+				$this->primary_attendee_obj = $attendee;
1281
+			}
1282
+		}
1283
+	}
1284
+
1285
+
1286
+	/**
1287
+	 * _refresh_registration_answers
1288
+	 *
1289
+	 * @param    EE_Registration $registration
1290
+	 * @return void
1291
+	 * @throws \EE_Error
1292
+	 */
1293
+	protected function _refresh_registration_answers($registration)
1294
+	{
1295
+
1296
+		// now update the answers
1297
+		foreach ($registration->answers() as $cache_key => $answer) {
1298
+			// verify object
1299
+			if ($answer instanceof EE_Answer) {
1300
+				if ($answer->ID()) {
1301
+					// make sure the cached answer is added to the model entity mapper
1302
+					$answer->get_model()->refresh_entity_map_with($answer->ID(), $answer);
1303
+				}
1304
+			} else {
1305
+				EE_Error::add_error(
1306
+					esc_html__(
1307
+						'An invalid Answer object was discovered when attempting to update the model entity mapper.',
1308
+						'event_espresso'
1309
+					),
1310
+					__FILE__,
1311
+					__FUNCTION__,
1312
+					__LINE__
1313
+				);
1314
+			}
1315
+		}
1316
+	}
1317
+
1318
+
1319
+	/**
1320
+	 *    __sleep
1321
+	 * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
1322
+	 * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
1323
+	 * reg form, because if needed, it will be regenerated anyways
1324
+	 *
1325
+	 * @return array
1326
+	 * @throws \EE_Error
1327
+	 */
1328
+	public function __sleep()
1329
+	{
1330
+		if ($this->primary_attendee_obj instanceof EE_Attendee && $this->primary_attendee_obj->ID()) {
1331
+			$this->primary_attendee_obj = $this->primary_attendee_obj->ID();
1332
+		}        // remove the reg form and the checkout
1333
+		if ($this->transaction instanceof EE_Transaction && $this->transaction->ID()) {
1334
+			$this->transaction = $this->transaction->ID();
1335
+		}        // remove the reg form and the checkout
1336
+		return array_diff(array_keys(get_object_vars($this)), array('billing_form', 'registration_form'));
1337
+	}
1338
+
1339
+
1340
+	/**
1341
+	 *    __wakeup
1342
+	 * to conserve db space, we are removing the EE_Checkout object from EE_SPCO_Reg_Step objects upon serialization
1343
+	 * this will reinstate the EE_Checkout object on each EE_SPCO_Reg_Step object
1344
+	 */
1345
+	public function __wakeup()
1346
+	{
1347
+		try {
1348
+			if (! $this->primary_attendee_obj instanceof EE_Attendee && absint($this->primary_attendee_obj) !== 0) {
1349
+				// $this->primary_attendee_obj is actually just an ID, so use it to get the object from the db
1350
+				$this->primary_attendee_obj = EEM_Attendee::instance()->get_one_by_ID($this->primary_attendee_obj);
1351
+			}
1352
+			if (! $this->transaction instanceof EE_Transaction && absint($this->transaction) !== 0) {
1353
+				// $this->transaction is actually just an ID, so use it to get the object from the db
1354
+				$this->transaction = EEM_Transaction::instance()->get_one_by_ID($this->transaction);
1355
+			}
1356
+			foreach ($this->reg_steps as $reg_step) {
1357
+				$reg_step->checkout = $this;
1358
+			}
1359
+		} catch (Throwable $e) {
1360
+			EE_Error::add_error(
1361
+				esc_html__(
1362
+					'An error occurred while attempting to reinstate a EE_SPCO_Reg_Step object in EE_Checkout::__wakeup().',
1363
+					'event_espresso'
1364
+				),
1365
+				__FILE__,
1366
+				__FUNCTION__,
1367
+				__LINE__
1368
+			);
1369
+		}
1370
+	}
1371
+
1372
+
1373
+	/**
1374
+	 * debug
1375
+	 *
1376
+	 * @param string $class
1377
+	 * @param string $func
1378
+	 * @param string $line
1379
+	 * @param array  $info
1380
+	 * @param bool   $display_request
1381
+	 * @throws EE_Error
1382
+	 * @throws ReflectionException
1383
+	 */
1384
+	public function log($class = '', $func = '', $line = '', $info = array(), $display_request = false)
1385
+	{
1386
+		$disabled = true;
1387
+		if (WP_DEBUG && ! $disabled) {
1388
+			$debug_data = get_option('EE_DEBUG_SPCO_' . EE_Session::instance()->id(), array());
1389
+			$default_data = array(
1390
+				$class                    => $func . '() : ' . $line,
1391
+				'request->step'           => $this->step,
1392
+				'request->action'         => $this->action,
1393
+				'current_step->slug'      => $this->current_step instanceof EE_SPCO_Reg_Step ?
1394
+					$this->current_step->slug() : '',
1395
+				'current_step->completed' => $this->current_step instanceof EE_SPCO_Reg_Step ?
1396
+					$this->current_step->completed() : '',
1397
+				'txn_status_updated'      => $this->transaction->txn_status_updated(),
1398
+				'reg_status_updated'      => $this->reg_status_updated,
1399
+				'reg_url_link'            => $this->reg_url_link,
1400
+			);
1401
+			if ($this->transaction instanceof EE_Transaction) {
1402
+				$default_data['TXN_status'] = $this->transaction->status_ID();
1403
+				$default_data['TXN_reg_steps'] = $this->transaction->reg_steps();
1404
+				foreach ($this->transaction->registrations($this->reg_cache_where_params) as $REG_ID => $registration) {
1405
+					$default_data['registrations'][ $REG_ID ] = $registration->status_ID();
1406
+				}
1407
+				if ($this->transaction->ID()) {
1408
+					$TXN_ID = 'EE_Transaction: ' . $this->transaction->ID();
1409
+					// don't serialize objects
1410
+					$info = $this->_strip_objects($info);
1411
+					if (! isset($debug_data[ $TXN_ID ])) {
1412
+						$debug_data[ $TXN_ID ] = array();
1413
+					}
1414
+					$debug_data[ $TXN_ID ][ microtime() ] = array_merge(
1415
+						$default_data,
1416
+						$info
1417
+					);
1418
+					update_option('EE_DEBUG_SPCO_' . EE_Session::instance()->id(), $debug_data);
1419
+				}
1420
+			}
1421
+		}
1422
+	}
1423
+
1424
+
1425
+	/**
1426
+	 * _strip_objects
1427
+	 *
1428
+	 * @param array $info
1429
+	 * @return array
1430
+	 */
1431
+	public function _strip_objects($info = array())
1432
+	{
1433
+		foreach ((array) $info as $key => $value) {
1434
+			if (is_array($value)) {
1435
+				$info[ $key ] = $this->_strip_objects($value);
1436
+			} elseif (is_object($value)) {
1437
+				$object_class = get_class($value);
1438
+				$info[ $object_class ] = array();
1439
+				$info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : 0;
1440
+				if (method_exists($value, 'status')) {
1441
+					$info[ $object_class ]['status'] = $value->status();
1442
+				} elseif (method_exists($value, 'status_ID')) {
1443
+					$info[ $object_class ]['status'] = $value->status_ID();
1444
+				}
1445
+				unset($info[ $key ]);
1446
+			}
1447
+		}
1448
+		return (array) $info;
1449
+	}
1450 1450
 }
Please login to merge, or discard this patch.
reg_steps/payment_options/EE_SPCO_Reg_Step_Payment_Options.class.php 2 patches
Indentation   +2810 added lines, -2810 removed lines patch added patch discarded remove patch
@@ -23,2814 +23,2814 @@
 block discarded – undo
23 23
  */
24 24
 class EE_SPCO_Reg_Step_Payment_Options extends EE_SPCO_Reg_Step
25 25
 {
26
-    protected ?EE_Line_Item_Display $line_item_display = null;
27
-
28
-    protected bool $handle_IPN_in_this_request = false;
29
-
30
-    /**
31
-     * @var EEM_Payment_Method|EEM_Base
32
-     * @since 5.0.42
33
-     */
34
-    protected EEM_Payment_Method $payment_method_model;
35
-
36
-
37
-    /**
38
-     * set_hooks - for hooking into EE Core, other modules, etc
39
-     *
40
-     * @return    void
41
-     */
42
-    public static function set_hooks()
43
-    {
44
-        add_filter(
45
-            'FHEE__SPCO__EE_Line_Item_Filter_Collection',
46
-            ['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
47
-        );
48
-        add_action(
49
-            'wp_ajax_switch_spco_billing_form',
50
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
51
-        );
52
-        add_action(
53
-            'wp_ajax_nopriv_switch_spco_billing_form',
54
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
55
-        );
56
-        add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
57
-        add_action(
58
-            'wp_ajax_nopriv_save_payer_details',
59
-            ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
60
-        );
61
-        add_action(
62
-            'wp_ajax_get_transaction_details_for_gateways',
63
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
64
-        );
65
-        add_action(
66
-            'wp_ajax_nopriv_get_transaction_details_for_gateways',
67
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
68
-        );
69
-        add_filter(
70
-            'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
71
-            ['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method']
72
-        );
73
-    }
74
-
75
-
76
-    /**
77
-     * @throws EE_Error
78
-     * @throws ReflectionException
79
-     */
80
-    public static function switch_spco_billing_form()
81
-    {
82
-        EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
83
-    }
84
-
85
-
86
-    /**
87
-     * @throws EE_Error
88
-     * @throws ReflectionException
89
-     */
90
-    public static function save_payer_details()
91
-    {
92
-        EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
93
-    }
94
-
95
-
96
-    /**
97
-     * @throws EE_Error
98
-     * @throws ReflectionException
99
-     */
100
-    public static function get_transaction_details()
101
-    {
102
-        EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
103
-    }
104
-
105
-
106
-    /**
107
-     * @return array
108
-     */
109
-    public static function bypass_recaptcha_for_load_payment_method(): array
110
-    {
111
-        return [
112
-            'EESID'  => EE_Registry::instance()->SSN->id(),
113
-            'step'   => 'payment_options',
114
-            'action' => 'spco_billing_form',
115
-        ];
116
-    }
117
-
118
-
119
-    /**
120
-     * @param EE_Checkout $checkout
121
-     * @throws EE_Error
122
-     * @throws ReflectionException
123
-     */
124
-    public function __construct(EE_Checkout $checkout)
125
-    {
126
-        $this->request              = EED_Single_Page_Checkout::getRequest();
127
-        $this->_slug                = 'payment_options';
128
-        $this->_name                = esc_html__('Payment Options', 'event_espresso');
129
-        $this->_template            = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
130
-        $this->checkout             = $checkout;
131
-        $this->payment_method_model = EEM_Payment_Method::instance();
132
-        $this->_reset_success_message();
133
-        $this->set_instructions(
134
-            esc_html__(
135
-                'Please select a method of payment and provide any necessary billing information before proceeding.',
136
-                'event_espresso'
137
-            )
138
-        );
139
-    }
140
-
141
-
142
-    public function line_item_display(): ?EE_Line_Item_Display
143
-    {
144
-        return $this->line_item_display;
145
-    }
146
-
147
-
148
-    public function set_line_item_display(EE_Line_Item_Display $line_item_display)
149
-    {
150
-        $this->line_item_display = $line_item_display;
151
-    }
152
-
153
-
154
-    public function handle_IPN_in_this_request(): bool
155
-    {
156
-        return $this->handle_IPN_in_this_request;
157
-    }
158
-
159
-
160
-    public function set_handle_IPN_in_this_request(bool $handle_IPN_in_this_request)
161
-    {
162
-        $this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
163
-    }
164
-
165
-
166
-    /**
167
-     * @return void
168
-     */
169
-    public function translate_js_strings()
170
-    {
171
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
172
-            'Please select a method of payment in order to continue.',
173
-            'event_espresso'
174
-        );
175
-        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
176
-            'A valid method of payment could not be determined. Please refresh the page and try again.',
177
-            'event_espresso'
178
-        );
179
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
180
-            'Forwarding to Secure Payment Provider.',
181
-            'event_espresso'
182
-        );
183
-    }
184
-
185
-
186
-    /**
187
-     * @return void
188
-     * @throws EE_Error
189
-     * @throws ReflectionException
190
-     */
191
-    public function enqueue_styles_and_scripts()
192
-    {
193
-        $transaction = $this->checkout->transaction;
194
-        // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
195
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
196
-            return;
197
-        }
198
-        foreach (
199
-            $this->payment_method_model->get_all_for_transaction(
200
-                $transaction,
201
-                EEM_Payment_Method::scope_cart
202
-            ) as $payment_method
203
-        ) {
204
-            $type_obj = $payment_method->type_obj();
205
-            if ($type_obj instanceof EE_PMT_Base) {
206
-                $billing_form = $type_obj->generate_new_billing_form($transaction);
207
-                if ($billing_form instanceof EE_Form_Section_Proper) {
208
-                    $billing_form->enqueue_js();
209
-                }
210
-            }
211
-        }
212
-    }
213
-
214
-
215
-    /**
216
-     * @return bool
217
-     * @throws EE_Error
218
-     * @throws InvalidArgumentException
219
-     * @throws ReflectionException
220
-     * @throws InvalidDataTypeException
221
-     * @throws InvalidInterfaceException
222
-     */
223
-    public function initialize_reg_step(): bool
224
-    {
225
-        // TODO: if /when we implement donations, then this will need overriding
226
-        if (
227
-            // don't need payment options for:
228
-            // registrations made via the admin
229
-            // completed transactions
230
-            // overpaid transactions
231
-            // $ 0.00 transactions(no payment required)
232
-            ! $this->checkout->payment_required()
233
-            // but do NOT remove if current action being called belongs to this reg step
234
-            && ! is_callable([$this, $this->checkout->action])
235
-            && ! $this->completed()
236
-        ) {
237
-            // and if so, then we no longer need the Payment Options step
238
-            if ($this->is_current_step()) {
239
-                $this->checkout->generate_reg_form = false;
240
-            }
241
-            $this->checkout->remove_reg_step($this->_slug);
242
-            // DEBUG LOG
243
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
244
-            return false;
245
-        }
246
-        // get all active payment methods
247
-        $this->checkout->available_payment_methods = $this->payment_method_model->get_all_for_transaction(
248
-            $this->checkout->transaction,
249
-            EEM_Payment_Method::scope_cart
250
-        );
251
-        $this->setDefaultPaymentMethod($this->checkout->available_payment_methods);
252
-        return true;
253
-    }
254
-
255
-
256
-    /**
257
-     * @param array $payment_methods
258
-     * @return void
259
-     * @throws EE_Error
260
-     * @throws ReflectionException
261
-     * @since 5.0.42
262
-     */
263
-    private function setDefaultPaymentMethod(array $payment_methods): void {
264
-        foreach ($payment_methods as $payment_method) {
265
-            if ($payment_method instanceof EE_Payment_Method && $payment_method->open_by_default()) {
266
-                $this->checkout->default_payment_method = $payment_method;
267
-                return;
268
-            }
269
-        }
270
-    }
271
-
272
-
273
-    /**
274
-     * @return EE_Form_Section_Proper
275
-     * @throws EE_Error
276
-     * @throws InvalidArgumentException
277
-     * @throws ReflectionException
278
-     * @throws EntityNotFoundException
279
-     * @throws InvalidDataTypeException
280
-     * @throws InvalidInterfaceException
281
-     * @throws InvalidStatusException
282
-     */
283
-    public function generate_reg_form(): EE_Form_Section_Proper
284
-    {
285
-        // reset in case someone changes their mind
286
-        $this->_reset_selected_method_of_payment();
287
-        // set some defaults
288
-        $this->checkout->selected_method_of_payment = 'payments_closed';
289
-        $registrations_requiring_payment            = [];
290
-        $registrations_for_free_events              = [];
291
-        $registrations_requiring_pre_approval       = [];
292
-        $sold_out_events                            = [];
293
-        $insufficient_spaces_available              = [];
294
-        $no_payment_required                        = true;
295
-        // loop thru registrations to gather info
296
-        $registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
297
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
298
-            $registrations,
299
-            $this->checkout->revisit
300
-        );
301
-        foreach ($registrations as $REG_ID => $registration) {
302
-            /** @var $registration EE_Registration */
303
-            // Skip if the registration has been moved
304
-            if ($registration->wasMoved()) {
305
-                continue;
306
-            }
307
-            // has this registration lost it's space ?
308
-            if (isset($ejected_registrations[ $REG_ID ])) {
309
-                if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
310
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
311
-                } else {
312
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
313
-                }
314
-                continue;
315
-            }
316
-            // event requires admin approval
317
-            if ($registration->status_ID() === RegStatus::AWAITING_REVIEW) {
318
-                // add event to list of events with pre-approval reg status
319
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
320
-                do_action(
321
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
322
-                    $registration->event(),
323
-                    $this
324
-                );
325
-                continue;
326
-            }
327
-            if (
328
-                $this->checkout->revisit
329
-                && $registration->status_ID() !== RegStatus::APPROVED
330
-                && (
331
-                    $registration->event()->is_sold_out()
332
-                    || $registration->event()->is_sold_out(true)
333
-                )
334
-            ) {
335
-                // add event to list of events that are sold out
336
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
337
-                do_action(
338
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
339
-                    $registration->event(),
340
-                    $this
341
-                );
342
-                continue;
343
-            }
344
-            // are they allowed to pay now and is there monies owing?
345
-            if ($registration->owes_monies_and_can_pay()) {
346
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
347
-                do_action(
348
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
349
-                    $registration->event(),
350
-                    $this
351
-                );
352
-            } elseif (
353
-                ! $this->checkout->revisit
354
-                      && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
355
-                      && $registration->ticket()->is_free()
356
-            ) {
357
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
358
-            }
359
-        }
360
-        $subsections = [];
361
-        // now decide which template to load
362
-        if (! empty($sold_out_events)) {
363
-            $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
364
-        }
365
-        if (! empty($insufficient_spaces_available)) {
366
-            $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
367
-                $insufficient_spaces_available
368
-            );
369
-        }
370
-        if (! empty($registrations_requiring_pre_approval)) {
371
-            $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
372
-                $registrations_requiring_pre_approval
373
-            );
374
-        }
375
-        if (! empty($registrations_for_free_events)) {
376
-            $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
377
-        }
378
-        if (! empty($registrations_requiring_payment)) {
379
-            if ($this->checkout->amount_owing > 0) {
380
-                // check for method_of_payment before setting up line items
381
-                // so that surcharges can be applied to the line items based on the selected method of payment
382
-                $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
383
-                do_action(
384
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__registrations_requiring_payment',
385
-                    $this,
386
-                    $registrations_requiring_payment
387
-                );
388
-                // autoload Line_Item_Display classes
389
-                EEH_Autoloader::register_line_item_filter_autoloaders();
390
-                $line_item_filter_processor = new EE_Line_Item_Filter_Processor(
391
-                    apply_filters(
392
-                        'FHEE__SPCO__EE_Line_Item_Filter_Collection',
393
-                        new EE_Line_Item_Filter_Collection()
394
-                    ),
395
-                    $this->checkout->cart->get_grand_total()
396
-                );
397
-                /** @var EE_Line_Item $filtered_line_item_tree */
398
-                $filtered_line_item_tree = $line_item_filter_processor->process();
399
-                EEH_Autoloader::register_line_item_display_autoloaders();
400
-                $this->set_line_item_display(new EE_Line_Item_Display('spco'));
401
-                $subsections['payment_options'] = $this->_display_payment_options(
402
-                    $this->line_item_display->display_line_item(
403
-                        $filtered_line_item_tree,
404
-                        ['registrations' => $registrations]
405
-                    )
406
-                );
407
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
408
-                $this->_apply_registration_payments_to_amount_owing($registrations);
409
-            }
410
-            $no_payment_required = false;
411
-        } else {
412
-            $this->_hide_reg_step_submit_button_if_revisit();
413
-        }
414
-        $this->_save_selected_method_of_payment();
415
-
416
-        $subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
417
-        $subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
418
-
419
-        return new EE_Form_Section_Proper(
420
-            [
421
-                'name'            => $this->reg_form_name(),
422
-                'html_id'         => $this->reg_form_name(),
423
-                'subsections'     => $subsections,
424
-                'layout_strategy' => new EE_No_Layout(),
425
-            ]
426
-        );
427
-    }
428
-
429
-
430
-    /**
431
-     * add line item filters required for this reg step
432
-     * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
433
-     *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
434
-     *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
435
-     *        payment options reg step, can apply these filters via the following: apply_filters(
436
-     *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
437
-     *        filter collection by passing that instead of instantiating a new collection
438
-     *
439
-     * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
440
-     * @return EE_Line_Item_Filter_Collection
441
-     * @throws EE_Error
442
-     * @throws InvalidArgumentException
443
-     * @throws ReflectionException
444
-     * @throws EntityNotFoundException
445
-     * @throws InvalidDataTypeException
446
-     * @throws InvalidInterfaceException
447
-     * @throws InvalidStatusException
448
-     */
449
-    public static function add_spco_line_item_filters(
450
-        EE_Line_Item_Filter_Collection $line_item_filter_collection
451
-    ): EE_Line_Item_Filter_Collection {
452
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
453
-            return $line_item_filter_collection;
454
-        }
455
-        if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
456
-            return $line_item_filter_collection;
457
-        }
458
-        if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
459
-            return $line_item_filter_collection;
460
-        }
461
-        $line_item_filter_collection->add(
462
-            new EE_Billable_Line_Item_Filter(
463
-                EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
464
-                    EE_Registry::instance()->SSN->checkout()->transaction->registrations(
465
-                        EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
466
-                    )
467
-                )
468
-            )
469
-        );
470
-        $line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
471
-        return $line_item_filter_collection;
472
-    }
473
-
474
-
475
-    /**
476
-     * if a registrant has lost their potential space at an event due to lack of payment,
477
-     * then this method removes them from the list of registrations being paid for during this request
478
-     *
479
-     * @param EE_Registration[] $registrations
480
-     * @return EE_Registration[]
481
-     * @throws EE_Error
482
-     * @throws InvalidArgumentException
483
-     * @throws ReflectionException
484
-     * @throws EntityNotFoundException
485
-     * @throws InvalidDataTypeException
486
-     * @throws InvalidInterfaceException
487
-     * @throws InvalidStatusException
488
-     */
489
-    public static function remove_ejected_registrations(array $registrations): array
490
-    {
491
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
492
-            $registrations,
493
-            EE_Registry::instance()->SSN->checkout()->revisit
494
-        );
495
-        foreach ($registrations as $REG_ID => $registration) {
496
-            // has this registration lost it's space ?
497
-            if (isset($ejected_registrations[ $REG_ID ])) {
498
-                unset($registrations[ $REG_ID ]);
499
-            }
500
-        }
501
-        return $registrations;
502
-    }
503
-
504
-
505
-    /**
506
-     * If a registrant chooses an offline payment method like Invoice,
507
-     * then no space is reserved for them at the event until they fully pay fo that site
508
-     * (unless the event's default reg status is set to APPROVED)
509
-     * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
510
-     * then this method will determine which registrations have lost the ability to complete the reg process.
511
-     *
512
-     * @param EE_Registration[] $registrations
513
-     * @param bool              $revisit
514
-     * @return array
515
-     * @throws EE_Error
516
-     * @throws InvalidArgumentException
517
-     * @throws ReflectionException
518
-     * @throws EntityNotFoundException
519
-     * @throws InvalidDataTypeException
520
-     * @throws InvalidInterfaceException
521
-     * @throws InvalidStatusException
522
-     */
523
-    public static function find_registrations_that_lost_their_space(array $registrations, bool $revisit = false): array
524
-    {
525
-        // registrations per event
526
-        $event_reg_count = [];
527
-        // spaces left per event
528
-        $event_spaces_remaining = [];
529
-        // tickets left sorted by ID
530
-        $tickets_remaining = [];
531
-        // registrations that have lost their space
532
-        $ejected_registrations = [];
533
-        foreach ($registrations as $REG_ID => $registration) {
534
-            if (
535
-                $registration->status_ID() === RegStatus::APPROVED
536
-                || apply_filters(
537
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
538
-                    false,
539
-                    $registration,
540
-                    $revisit
541
-                )
542
-            ) {
543
-                continue;
544
-            }
545
-            $EVT_ID = $registration->event_ID();
546
-            $ticket = $registration->ticket();
547
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
548
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
549
-            }
550
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
551
-                if (! isset($event_reg_count[ $EVT_ID ])) {
552
-                    $event_reg_count[ $EVT_ID ] = 0;
553
-                }
554
-                $event_reg_count[ $EVT_ID ]++;
555
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
556
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
557
-                }
558
-            }
559
-            if (
560
-                $revisit
561
-                && ($tickets_remaining[ $ticket->ID() ] === 0
562
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
563
-                )
564
-            ) {
565
-                $ejected_registrations[ $REG_ID ] = $registration->event();
566
-                if ($registration->status_ID() !== RegStatus::WAIT_LIST) {
567
-                    /** @type EE_Registration_Processor $registration_processor */
568
-                    $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
569
-                    // at this point, we should have enough details about the registrant to consider the registration
570
-                    // NOT incomplete
571
-                    $registration_processor->manually_update_registration_status(
572
-                        $registration,
573
-                        RegStatus::WAIT_LIST
574
-                    );
575
-                }
576
-            }
577
-        }
578
-        return $ejected_registrations;
579
-    }
580
-
581
-
582
-    /**
583
-     * removes the HTML for the reg step submit button
584
-     * by replacing it with an empty string via filter callback
585
-     *
586
-     * @return void
587
-     */
588
-    protected function _hide_reg_step_submit_button_if_revisit()
589
-    {
590
-        if ($this->checkout->revisit) {
591
-            add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
592
-        }
593
-    }
594
-
595
-
596
-    /**
597
-     * displays notices regarding events that have sold out since the registrant first signed up
598
-     *
599
-     * @param EE_Event[] $sold_out_events_array
600
-     * @return EE_Form_Section_Proper
601
-     * @throws EE_Error
602
-     * @throws ReflectionException
603
-     */
604
-    private function _sold_out_events(array $sold_out_events_array = []): EE_Form_Section_Proper
605
-    {
606
-        // set some defaults
607
-        $this->checkout->selected_method_of_payment = 'events_sold_out';
608
-        $sold_out_events                            = '';
609
-        foreach ($sold_out_events_array as $sold_out_event) {
610
-            $sold_out_events .= EEH_HTML::li(
611
-                EEH_HTML::span(
612
-                    '  ' . $sold_out_event->name(),
613
-                    '',
614
-                    'dashicons dashicons-marker ee-icon-size-16 pink-text'
615
-                )
616
-            );
617
-        }
618
-        return new EE_Form_Section_Proper(
619
-            [
620
-                'layout_strategy' => new EE_Template_Layout(
621
-                    [
622
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
623
-                                                  . $this->_slug
624
-                                                  . '/sold_out_events.template.php',
625
-                        'template_args'        => apply_filters(
626
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
627
-                            [
628
-                                'sold_out_events'     => $sold_out_events,
629
-                                'sold_out_events_msg' => apply_filters(
630
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
631
-                                    sprintf(
632
-                                        esc_html__(
633
-                                            'It appears that the event you were about to make a payment for has sold out since you first registered. If you have already made a partial payment towards this event, please contact the event administrator for a refund.%3$s%3$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%2$s',
634
-                                            'event_espresso'
635
-                                        ),
636
-                                        '<strong>',
637
-                                        '</strong>',
638
-                                        '<br />'
639
-                                    )
640
-                                ),
641
-                            ]
642
-                        ),
643
-                    ]
644
-                ),
645
-            ]
646
-        );
647
-    }
648
-
649
-
650
-    /**
651
-     * displays notices regarding events that do not have enough remaining spaces
652
-     * to satisfy the current number of registrations looking to pay
653
-     *
654
-     * @param EE_Event[] $insufficient_spaces_events_array
655
-     * @return EE_Form_Section_Proper
656
-     * @throws EE_Error
657
-     * @throws ReflectionException
658
-     */
659
-    private function _insufficient_spaces_available(
660
-        array $insufficient_spaces_events_array = []
661
-    ): EE_Form_Section_Proper {
662
-        // set some defaults
663
-        $this->checkout->selected_method_of_payment = 'invoice';
664
-        $insufficient_space_events                  = '';
665
-        foreach ($insufficient_spaces_events_array as $event) {
666
-            if ($event instanceof EE_Event) {
667
-                $insufficient_space_events .= EEH_HTML::li(
668
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
669
-                );
670
-            }
671
-        }
672
-        return new EE_Form_Section_Proper(
673
-            [
674
-                'subsections'     => [
675
-                    'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
676
-                    'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
677
-                ],
678
-                'layout_strategy' => new EE_Template_Layout(
679
-                    [
680
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
681
-                                                  . $this->_slug
682
-                                                  . '/sold_out_events.template.php',
683
-                        'template_args'        => apply_filters(
684
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
685
-                            [
686
-                                'sold_out_events'     => $insufficient_space_events,
687
-                                'sold_out_events_msg' => apply_filters(
688
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
689
-                                    esc_html__(
690
-                                        'It appears that the event you were about to make a payment for has sold additional tickets since you first registered, and there are no longer enough spaces left to accommodate your selections. You may continue to pay and secure the available space(s) remaining, or simply cancel if you no longer wish to purchase. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
691
-                                        'event_espresso'
692
-                                    )
693
-                                ),
694
-                            ]
695
-                        ),
696
-                    ]
697
-                ),
698
-            ]
699
-        );
700
-    }
701
-
702
-
703
-    /**
704
-     * @param array $registrations_requiring_pre_approval
705
-     * @return EE_Form_Section_Proper
706
-     * @throws EE_Error
707
-     * @throws EntityNotFoundException
708
-     * @throws ReflectionException
709
-     */
710
-    private function _registrations_requiring_pre_approval(
711
-        array $registrations_requiring_pre_approval = []
712
-    ): EE_Form_Section_Proper {
713
-        $events_requiring_pre_approval = [];
714
-        foreach ($registrations_requiring_pre_approval as $registration) {
715
-            if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
716
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
717
-                    EEH_HTML::span(
718
-                        '',
719
-                        '',
720
-                        'dashicons dashicons-marker ee-icon-size-16 orange-text'
721
-                    )
722
-                    . EEH_HTML::span($registration->event()->name(), '', 'orange-text')
723
-                );
724
-            }
725
-        }
726
-        return new EE_Form_Section_Proper(
727
-            [
728
-                'layout_strategy' => new EE_Template_Layout(
729
-                    [
730
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
731
-                                                  . $this->_slug
732
-                                                  . '/events_requiring_pre_approval.template.php', // layout_template
733
-                        'template_args'        => apply_filters(
734
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
735
-                            [
736
-                                'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
737
-                                'events_requiring_pre_approval_msg' => apply_filters(
738
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
739
-                                    esc_html__(
740
-                                        'The following events do not require payment at this time and will not be billed during this transaction. Billing will only occur after the attendee has been approved by the event organizer. You will be notified when your registration has been processed. If this is a free event, then no billing will occur.',
741
-                                        'event_espresso'
742
-                                    )
743
-                                ),
744
-                            ]
745
-                        ),
746
-                    ]
747
-                ),
748
-            ]
749
-        );
750
-    }
751
-
752
-
753
-    /**
754
-     * @param EE_Event[] $registrations_for_free_events
755
-     * @return EE_Form_Section_Proper
756
-     * @throws EE_Error
757
-     */
758
-    private function _no_payment_required(array $registrations_for_free_events = []): EE_Form_Section_Proper
759
-    {
760
-        // set some defaults
761
-        $this->checkout->selected_method_of_payment = 'no_payment_required';
762
-        // generate no_payment_required form
763
-        return new EE_Form_Section_Proper(
764
-            [
765
-                'layout_strategy' => new EE_Template_Layout(
766
-                    [
767
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
768
-                                                  . $this->_slug
769
-                                                  . '/no_payment_required.template.php', // layout_template
770
-                        'template_args'        => apply_filters(
771
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
772
-                            [
773
-                                'revisit'                       => $this->checkout->revisit,
774
-                                'registrations'                 => [],
775
-                                'ticket_count'                  => [],
776
-                                'registrations_for_free_events' => $registrations_for_free_events,
777
-                                'no_payment_required_msg'       => EEH_HTML::p(
778
-                                    esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
779
-                                ),
780
-                            ]
781
-                        ),
782
-                    ]
783
-                ),
784
-            ]
785
-        );
786
-    }
787
-
788
-
789
-    /**
790
-     * @param string $transaction_details HTML from EE_SPCO_Line_Item_Display_Strategy
791
-     * @return EE_Form_Section_Proper
792
-     * @throws EE_Error
793
-     * @throws ReflectionException
794
-     */
795
-    private function _display_payment_options(string $transaction_details = ''): EE_Form_Section_Proper
796
-    {
797
-        // build payment options form
798
-        return apply_filters(
799
-            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
800
-            new EE_Form_Section_Proper(
801
-                [
802
-                    'subsections'     => [
803
-                        'before_payment_options' => apply_filters(
804
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
805
-                            new EE_Form_Section_Proper(
806
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
807
-                            )
808
-                        ),
809
-                        'payment_options'        => $this->_setup_payment_options(),
810
-                        'after_payment_options'  => apply_filters(
811
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
812
-                            new EE_Form_Section_Proper(
813
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
814
-                            )
815
-                        ),
816
-                    ],
817
-                    'layout_strategy' => new EE_Template_Layout(
818
-                        [
819
-                            'layout_template_file' => $this->_template,
820
-                            'template_args'        => apply_filters(
821
-                                'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
822
-                                [
823
-                                    'reg_count'                 => $this->line_item_display->total_items(),
824
-                                    'transaction_details'       => $transaction_details,
825
-                                    'available_payment_methods' => [],
826
-                                ]
827
-                            ),
828
-                        ]
829
-                    ),
830
-                ]
831
-            )
832
-        );
833
-    }
834
-
835
-
836
-    /**
837
-     * @param bool $no_payment_required
838
-     * @return EE_Form_Section_Proper
839
-     * @throws EE_Error
840
-     * @throws ReflectionException
841
-     */
842
-    private function _extra_hidden_inputs(bool $no_payment_required = true): EE_Form_Section_Proper
843
-    {
844
-        return new EE_Form_Section_Proper(
845
-            [
846
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
847
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
848
-                'subsections'     => [
849
-                    'spco_no_payment_required' => new EE_Hidden_Input(
850
-                        [
851
-                            'normalization_strategy' => new EE_Boolean_Normalization(),
852
-                            'html_name'              => 'spco_no_payment_required',
853
-                            'html_id'                => 'spco-no-payment-required-payment_options',
854
-                            'default'                => $no_payment_required,
855
-                        ]
856
-                    ),
857
-                    'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
858
-                        [
859
-                            'normalization_strategy' => new EE_Int_Normalization(),
860
-                            'html_name'              => 'spco_transaction_id',
861
-                            'html_id'                => 'spco-transaction-id',
862
-                            'default'                => $this->checkout->transaction->ID(),
863
-                        ]
864
-                    ),
865
-                ],
866
-            ]
867
-        );
868
-    }
869
-
870
-
871
-    /**
872
-     * @param array $registrations
873
-     * @throws EE_Error
874
-     * @throws ReflectionException
875
-     */
876
-    protected function _apply_registration_payments_to_amount_owing(array $registrations)
877
-    {
878
-        $payments = [];
879
-        foreach ($registrations as $registration) {
880
-            if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
881
-                $payments += $registration->registration_payments();
882
-            }
883
-        }
884
-        if (! empty($payments)) {
885
-            foreach ($payments as $payment) {
886
-                if ($payment instanceof EE_Registration_Payment) {
887
-                    $this->checkout->amount_owing -= $payment->amount();
888
-                }
889
-            }
890
-        }
891
-    }
892
-
893
-
894
-    /**
895
-     * @param bool $force_reset
896
-     * @return void
897
-     */
898
-    private function _reset_selected_method_of_payment(bool $force_reset = false)
899
-    {
900
-        /** @var RequestInterface $request */
901
-        $request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
902
-        $reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
903
-        if ($reset_payment_method) {
904
-            $this->checkout->selected_method_of_payment = null;
905
-            $this->checkout->payment_method             = null;
906
-            $this->checkout->billing_form               = null;
907
-            $this->_save_selected_method_of_payment();
908
-        }
909
-    }
910
-
911
-
912
-    /**
913
-     * stores the selected_method_of_payment in the session
914
-     * so that it's available for all subsequent requests including AJAX
915
-     *
916
-     * @param string $selected_method_of_payment
917
-     * @return void
918
-     */
919
-    private function _save_selected_method_of_payment(string $selected_method_of_payment = '')
920
-    {
921
-        $selected_method_of_payment = ! empty($selected_method_of_payment)
922
-            ? $selected_method_of_payment
923
-            : $this->checkout->selected_method_of_payment;
924
-        EE_Registry::instance()->SSN->set_session_data(
925
-            ['selected_method_of_payment' => $selected_method_of_payment]
926
-        );
927
-    }
928
-
929
-
930
-    /**
931
-     * @return EE_Form_Section_Proper
932
-     * @throws EE_Error
933
-     * @throws ReflectionException
934
-     */
935
-    public function _setup_payment_options(): EE_Form_Section_Proper
936
-    {
937
-        // load payment method classes
938
-        $this->checkout->available_payment_methods = $this->_get_available_payment_methods();
939
-        if (empty($this->checkout->available_payment_methods)) {
940
-            EE_Error::add_error(
941
-                apply_filters(
942
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
943
-                    sprintf(
944
-                        esc_html__(
945
-                            'Sorry, you cannot complete your purchase because a payment method is not active.%1$s Please contact %2$s for assistance and provide a description of the problem.',
946
-                            'event_espresso'
947
-                        ),
948
-                        '<br>',
949
-                        EE_Registry::instance()->CFG->organization->get_pretty('email')
950
-                    )
951
-                ),
952
-                __FILE__,
953
-                __FUNCTION__,
954
-                __LINE__
955
-            );
956
-        }
957
-        // switch up header depending on number of available payment methods
958
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
959
-            ? apply_filters(
960
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
961
-                esc_html__('Please Select Your Method of Payment', 'event_espresso')
962
-            )
963
-            : apply_filters(
964
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
965
-                esc_html__('Method of Payment', 'event_espresso')
966
-            );
967
-        $available_payment_methods = [
968
-            // display the "Payment Method" header
969
-            'payment_method_header' => new EE_Form_Section_HTML(
970
-                apply_filters(
971
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
972
-                    EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
973
-                    $payment_method_header
974
-                )
975
-            ),
976
-        ];
977
-        // the list of actual payment methods ( invoice, PayPal, etc ) in a  ( slug => HTML )  format
978
-        $available_payment_method_options = [];
979
-        $default_payment_method_option    = [];
980
-        // additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
981
-        $payment_methods_billing_info = [
982
-            new EE_Form_Section_HTML(
983
-                EEH_HTML::div('<br />', '', '', 'clear:both;')
984
-            ),
985
-        ];
986
-        // loop through payment methods
987
-        foreach ($this->checkout->available_payment_methods as $payment_method) {
988
-            if (! $payment_method instanceof EE_Payment_Method) {
989
-                continue;
990
-            }
991
-
992
-            $payment_method_button = EEH_HTML::img(
993
-                $payment_method->button_url(),
994
-                $payment_method->name(),
995
-                'spco-payment-method-' . $payment_method->slug() . '-btn-img',
996
-                'spco-payment-method-btn-img'
997
-            );
998
-            // check if any payment methods are set as default
999
-            // if payment method is already selected
1000
-            // OR nothing is selected and this payment method is the default
1001
-            if (
1002
-                ($this->checkout->selected_method_of_payment === $payment_method->slug())
1003
-                || (
1004
-                    ! $this->checkout->selected_method_of_payment
1005
-                    && $payment_method->open_by_default()
1006
-                )
1007
-            ) {
1008
-                $this->checkout->selected_method_of_payment = $payment_method->slug();
1009
-                $this->_save_selected_method_of_payment();
1010
-                $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1011
-            } else {
1012
-                $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1013
-            }
1014
-            $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1015
-                $this->_payment_method_billing_info(
1016
-                    $payment_method
1017
-                );
1018
-        }
1019
-        // prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1020
-        // of PMs
1021
-        $available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1022
-        // now generate the actual form  inputs
1023
-        $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1024
-            $available_payment_method_options
1025
-        );
1026
-        $available_payment_methods                              += $payment_methods_billing_info;
1027
-        // build the available payment methods form
1028
-        return new EE_Form_Section_Proper(
1029
-            [
1030
-                'html_id'         => 'spco-available-methods-of-payment-dv',
1031
-                'subsections'     => $available_payment_methods,
1032
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1033
-            ]
1034
-        );
1035
-    }
1036
-
1037
-
1038
-    /**
1039
-     * @return EE_Payment_Method[]
1040
-     * @throws EE_Error
1041
-     * @throws ReflectionException
1042
-     */
1043
-    protected function _get_available_payment_methods(): array
1044
-    {
1045
-        if (! empty($this->checkout->available_payment_methods)) {
1046
-            return $this->checkout->available_payment_methods;
1047
-        }
1048
-        $available_payment_methods = [];
1049
-        // get all active payment methods
1050
-        $payment_methods = $this->payment_method_model->get_all_for_transaction(
1051
-            $this->checkout->transaction,
1052
-            EEM_Payment_Method::scope_cart
1053
-        );
1054
-        foreach ($payment_methods as $payment_method) {
1055
-            if ($payment_method instanceof EE_Payment_Method) {
1056
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1057
-            }
1058
-        }
1059
-        return $available_payment_methods;
1060
-    }
1061
-
1062
-
1063
-    /**
1064
-     * @param array $available_payment_method_options
1065
-     * @return EE_Form_Section_Proper
1066
-     * @throws EE_Error
1067
-     * @throws EE_Error
1068
-     */
1069
-    private function _available_payment_method_inputs(
1070
-        array $available_payment_method_options = []
1071
-    ): EE_Form_Section_Proper {
1072
-        // generate inputs
1073
-        return new EE_Form_Section_Proper(
1074
-            [
1075
-                'html_id'         => 'ee-available-payment-method-inputs',
1076
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1077
-                'subsections'     => [
1078
-                    '' => new EE_Radio_Button_Input(
1079
-                        $available_payment_method_options,
1080
-                        [
1081
-                            'html_name'          => 'selected_method_of_payment',
1082
-                            'html_class'         => 'spco-payment-method',
1083
-                            'default'            => $this->checkout->selected_method_of_payment,
1084
-                            'label_size'         => 11,
1085
-                            'enforce_label_size' => true,
1086
-                        ]
1087
-                    ),
1088
-                ],
1089
-            ]
1090
-        );
1091
-    }
1092
-
1093
-
1094
-    /**
1095
-     * @param EE_Payment_Method $payment_method
1096
-     * @return EE_Form_Section_Proper
1097
-     * @throws EE_Error
1098
-     * @throws ReflectionException
1099
-     */
1100
-    private function _payment_method_billing_info(EE_Payment_Method $payment_method): EE_Form_Section_Proper
1101
-    {
1102
-        $currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1103
-        // generate the billing form for payment method
1104
-        $billing_form                 = $currently_selected
1105
-            ? $this->_get_billing_form_for_payment_method($payment_method)
1106
-            : new EE_Form_Section_HTML();
1107
-        $this->checkout->billing_form = $currently_selected
1108
-            ? $billing_form
1109
-            : $this->checkout->billing_form;
1110
-        // it's all in the details
1111
-        $info_html = EEH_HTML::h3(
1112
-            esc_html__('Important information regarding your payment', 'event_espresso'),
1113
-            '',
1114
-            'spco-payment-method-hdr'
1115
-        );
1116
-        // add some info regarding the step, either from what's saved in the admin,
1117
-        // or a default string depending on whether the PM has a billing form or not
1118
-        if ($payment_method->description()) {
1119
-            $payment_method_info = $payment_method->description();
1120
-        } elseif ($billing_form instanceof EE_Billing_Info_Form) {
1121
-            $payment_method_info = sprintf(
1122
-                esc_html__(
1123
-                    'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1124
-                    'event_espresso'
1125
-                ),
1126
-                $this->submit_button_text()
1127
-            );
1128
-        } else {
1129
-            $payment_method_info = sprintf(
1130
-                esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1131
-                $this->submit_button_text()
1132
-            );
1133
-        }
1134
-        $info_html .= EEH_HTML::div(
1135
-            apply_filters(
1136
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1137
-                $payment_method_info
1138
-            ),
1139
-            '',
1140
-            'spco-payment-method-desc ee-attention'
1141
-        );
1142
-        return new EE_Form_Section_Proper(
1143
-            [
1144
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1145
-                'html_class'      => 'spco-payment-method-info-dv',
1146
-                // only display the selected or default PM
1147
-                'html_style'      => $currently_selected ? '' : 'display:none;',
1148
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1149
-                'subsections'     => [
1150
-                    'info'         => new EE_Form_Section_HTML($info_html),
1151
-                    'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1152
-                ],
1153
-            ]
1154
-        );
1155
-    }
1156
-
1157
-
1158
-    /**
1159
-     * @return bool
1160
-     * @throws EE_Error
1161
-     * @throws InvalidArgumentException
1162
-     * @throws ReflectionException
1163
-     * @throws InvalidDataTypeException
1164
-     * @throws InvalidInterfaceException
1165
-     */
1166
-    public function get_billing_form_html_for_payment_method(): bool
1167
-    {
1168
-        // how have they chosen to pay?
1169
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1170
-        $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1171
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1172
-            return false;
1173
-        }
1174
-        if (
1175
-            apply_filters(
1176
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1177
-                false
1178
-            )
1179
-        ) {
1180
-            EE_Error::add_success(
1181
-                apply_filters(
1182
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1183
-                    sprintf(
1184
-                        esc_html__(
1185
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1186
-                            'event_espresso'
1187
-                        ),
1188
-                        $this->checkout->payment_method->name()
1189
-                    )
1190
-                )
1191
-            );
1192
-        }
1193
-        // now generate billing form for selected method of payment
1194
-        $payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1195
-        // fill form with attendee info if applicable
1196
-        if (
1197
-            $payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1198
-            && $this->checkout->transaction_has_primary_registrant()
1199
-        ) {
1200
-            $payment_method_billing_form->populate_from_attendee(
1201
-                $this->checkout->transaction->primary_registration()->attendee()
1202
-            );
1203
-        }
1204
-        // and debug content
1205
-        if (
1206
-            $payment_method_billing_form instanceof EE_Billing_Info_Form
1207
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1208
-        ) {
1209
-            $payment_method_billing_form =
1210
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1211
-                    $payment_method_billing_form
1212
-                );
1213
-        }
1214
-        $billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1215
-            ? $payment_method_billing_form->get_html()
1216
-            : '';
1217
-        $this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1218
-        // localize validation rules for main form
1219
-        $this->checkout->current_step->reg_form->localize_validation_rules();
1220
-        $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1221
-        return true;
1222
-    }
1223
-
1224
-
1225
-    /**
1226
-     * @param EE_Payment_Method $payment_method
1227
-     * @return EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form|EE_Form_Section_HTML
1228
-     * @throws EE_Error
1229
-     * @throws ReflectionException
1230
-     */
1231
-    private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1232
-    {
1233
-        $billing_form = $payment_method->type_obj()->billing_form(
1234
-            $this->checkout->transaction,
1235
-            ['amount_owing' => $this->checkout->amount_owing]
1236
-        );
1237
-        if ($billing_form instanceof EE_Billing_Info_Form) {
1238
-            if (
1239
-                apply_filters(
1240
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1241
-                    false
1242
-                )
1243
-                && $this->request->requestParamIsSet('payment_method')
1244
-            ) {
1245
-                EE_Error::add_success(
1246
-                    apply_filters(
1247
-                        'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1248
-                        sprintf(
1249
-                            esc_html__(
1250
-                                'You have selected "%s" as your method of payment. Please note the important payment information below.',
1251
-                                'event_espresso'
1252
-                            ),
1253
-                            $payment_method->name()
1254
-                        )
1255
-                    )
1256
-                );
1257
-            }
1258
-            return apply_filters(
1259
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1260
-                $billing_form,
1261
-                $payment_method
1262
-            );
1263
-        }
1264
-        // no actual billing form, so return empty HTML form section
1265
-        return new EE_Form_Section_HTML();
1266
-    }
1267
-
1268
-
1269
-    /**
1270
-     * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1271
-     *                          is not found in the incoming request
1272
-     * @param string  $request_param
1273
-     * @return NULL|string
1274
-     * @throws EE_Error
1275
-     * @throws ReflectionException
1276
-     */
1277
-    private function _get_selected_method_of_payment(
1278
-        bool $required = false,
1279
-        string $request_param = 'selected_method_of_payment'
1280
-    ): ?string {
1281
-        // is selected_method_of_payment set in the request ?
1282
-        $selected_method_of_payment = $this->request->getRequestParam($request_param);
1283
-        if ($selected_method_of_payment) {
1284
-            // sanitize it
1285
-            $selected_method_of_payment = is_array($selected_method_of_payment)
1286
-                ? array_shift($selected_method_of_payment)
1287
-                : $selected_method_of_payment;
1288
-            $selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1289
-            // store it in the session so that it's available for all subsequent requests including AJAX
1290
-            $this->_save_selected_method_of_payment($selected_method_of_payment);
1291
-        } else {
1292
-            // or is it set in the session ?
1293
-            $selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1294
-                'selected_method_of_payment'
1295
-            );
1296
-        }
1297
-        if (
1298
-            empty($selected_method_of_payment)
1299
-            && $this->checkout->default_payment_method instanceof EE_Payment_Method
1300
-        ) {
1301
-            $selected_method_of_payment = $this->checkout->default_payment_method->slug();
1302
-        }
1303
-        // still no payment method?
1304
-        if (empty($selected_method_of_payment) && $required) {
1305
-            EE_Error::add_error(
1306
-                sprintf(
1307
-                    esc_html__(
1308
-                        'The selected method of payment could not be determined.%sPlease ensure that you have selected one before proceeding.%sIf you continue to experience difficulties, then refresh your browser and try again, or contact %s for assistance.',
1309
-                        'event_espresso'
1310
-                    ),
1311
-                    '<br/>',
1312
-                    '<br/>',
1313
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
1314
-                ),
1315
-                __FILE__,
1316
-                __FUNCTION__,
1317
-                __LINE__
1318
-            );
1319
-            return null;
1320
-        }
1321
-        return $selected_method_of_payment;
1322
-    }
1323
-
1324
-
1325
-
1326
-
1327
-
1328
-
1329
-    /********************************************************************************************************/
1330
-    /***********************************  SWITCH PAYMENT METHOD  ************************************/
1331
-    /********************************************************************************************************/
1332
-
1333
-
1334
-    /**
1335
-     * @return bool
1336
-     * @throws EE_Error
1337
-     * @throws ReflectionException
1338
-     */
1339
-    public function switch_payment_method()
1340
-    {
1341
-        if (! $this->_verify_payment_method_is_set()) {
1342
-            return false;
1343
-        }
1344
-        if (
1345
-            apply_filters(
1346
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1347
-                false
1348
-            )
1349
-        ) {
1350
-            EE_Error::add_success(
1351
-                apply_filters(
1352
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1353
-                    sprintf(
1354
-                        esc_html__(
1355
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1356
-                            'event_espresso'
1357
-                        ),
1358
-                        $this->checkout->payment_method->name()
1359
-                    )
1360
-                )
1361
-            );
1362
-        }
1363
-        // generate billing form for selected method of payment if it hasn't been done already
1364
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1365
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1366
-                $this->checkout->payment_method
1367
-            );
1368
-        }
1369
-        // fill form with attendee info if applicable
1370
-        if (
1371
-            apply_filters(
1372
-                'FHEE__populate_billing_form_fields_from_attendee',
1373
-                (
1374
-                    $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1375
-                    && $this->checkout->transaction_has_primary_registrant()
1376
-                ),
1377
-                $this->checkout->billing_form,
1378
-                $this->checkout->transaction
1379
-            )
1380
-        ) {
1381
-            $this->checkout->billing_form->populate_from_attendee(
1382
-                $this->checkout->transaction->primary_registration()->attendee()
1383
-            );
1384
-        }
1385
-        // and debug content
1386
-        if (
1387
-            $this->checkout->billing_form instanceof EE_Billing_Info_Form
1388
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1389
-        ) {
1390
-            $this->checkout->billing_form =
1391
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1392
-                    $this->checkout->billing_form
1393
-                );
1394
-        }
1395
-        // get HTML and validation rules for form
1396
-        if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1397
-            $this->checkout->json_response->set_return_data(
1398
-                ['payment_method_info' => $this->checkout->billing_form->get_html()]
1399
-            );
1400
-            // localize validation rules for main form
1401
-            $this->checkout->billing_form->localize_validation_rules(true);
1402
-            $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1403
-        } else {
1404
-            $this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1405
-        }
1406
-        // prevents advancement to next step
1407
-        $this->checkout->continue_reg = false;
1408
-        return true;
1409
-    }
1410
-
1411
-
1412
-    /**
1413
-     * @return bool
1414
-     * @throws EE_Error
1415
-     * @throws InvalidArgumentException
1416
-     * @throws ReflectionException
1417
-     * @throws InvalidDataTypeException
1418
-     * @throws InvalidInterfaceException
1419
-     */
1420
-    protected function _verify_payment_method_is_set(): bool
1421
-    {
1422
-        // generate billing form for selected method of payment if it hasn't been done already
1423
-        if (empty($this->checkout->selected_method_of_payment)) {
1424
-            // how have they chosen to pay?
1425
-            $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1426
-        } else {
1427
-            // choose your own adventure based on method_of_payment
1428
-            switch ($this->checkout->selected_method_of_payment) {
1429
-                case 'events_sold_out':
1430
-                    EE_Error::add_attention(
1431
-                        apply_filters(
1432
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1433
-                            esc_html__(
1434
-                                'It appears that the event you were about to make a payment for has sold out since this form first loaded. Please contact the event administrator if you believe this is an error.',
1435
-                                'event_espresso'
1436
-                            )
1437
-                        ),
1438
-                        __FILE__,
1439
-                        __FUNCTION__,
1440
-                        __LINE__
1441
-                    );
1442
-                    return false;
1443
-                case 'payments_closed':
1444
-                    EE_Error::add_attention(
1445
-                        apply_filters(
1446
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1447
-                            esc_html__(
1448
-                                'It appears that the event you were about to make a payment for is not accepting payments at this time. Please contact the event administrator if you believe this is an error.',
1449
-                                'event_espresso'
1450
-                            )
1451
-                        ),
1452
-                        __FILE__,
1453
-                        __FUNCTION__,
1454
-                        __LINE__
1455
-                    );
1456
-                    return false;
1457
-                case 'no_payment_required':
1458
-                    EE_Error::add_attention(
1459
-                        apply_filters(
1460
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1461
-                            esc_html__(
1462
-                                'It appears that the event you were about to make a payment for does not require payment. Please contact the event administrator if you believe this is an error.',
1463
-                                'event_espresso'
1464
-                            )
1465
-                        ),
1466
-                        __FILE__,
1467
-                        __FUNCTION__,
1468
-                        __LINE__
1469
-                    );
1470
-                    return false;
1471
-                default:
1472
-            }
1473
-        }
1474
-        // verify payment method
1475
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1476
-            // get payment method for selected method of payment
1477
-            $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1478
-        }
1479
-        return $this->checkout->payment_method instanceof EE_Payment_Method;
1480
-    }
1481
-
1482
-
1483
-
1484
-    /********************************************************************************************************/
1485
-    /***************************************  SAVE PAYER DETAILS  ****************************************/
1486
-    /********************************************************************************************************/
1487
-
1488
-
1489
-    /**
1490
-     * @return void
1491
-     * @throws EE_Error
1492
-     * @throws InvalidArgumentException
1493
-     * @throws ReflectionException
1494
-     * @throws RuntimeException
1495
-     * @throws InvalidDataTypeException
1496
-     * @throws InvalidInterfaceException
1497
-     */
1498
-    public function save_payer_details_via_ajax()
1499
-    {
1500
-        if (! $this->_verify_payment_method_is_set()) {
1501
-            return;
1502
-        }
1503
-        // generate billing form for selected method of payment if it hasn't been done already
1504
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1505
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1506
-                $this->checkout->payment_method
1507
-            );
1508
-        }
1509
-        // generate primary attendee from payer info if applicable
1510
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1511
-            $attendee = $this->_create_attendee_from_request_data();
1512
-            if ($attendee instanceof EE_Attendee) {
1513
-                foreach ($this->checkout->transaction->registrations() as $registration) {
1514
-                    if ($registration->is_primary_registrant()) {
1515
-                        $this->checkout->primary_attendee_obj = $attendee;
1516
-                        $registration->_add_relation_to($attendee, 'Attendee');
1517
-                        $registration->set_attendee_id($attendee->ID());
1518
-                        $registration->update_cache_after_object_save('Attendee', $attendee);
1519
-                    }
1520
-                }
1521
-            }
1522
-        }
1523
-    }
1524
-
1525
-
1526
-    /**
1527
-     * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1528
-     *
1529
-     * @return EE_Attendee
1530
-     * @throws EE_Error
1531
-     * @throws InvalidArgumentException
1532
-     * @throws ReflectionException
1533
-     * @throws InvalidDataTypeException
1534
-     * @throws InvalidInterfaceException
1535
-     */
1536
-    protected function _create_attendee_from_request_data(): EE_Attendee
1537
-    {
1538
-        // get State ID
1539
-        $STA_ID = $this->request->getRequestParam('state');
1540
-        if (! empty($STA_ID)) {
1541
-            // can we get state object from name ?
1542
-            EE_Registry::instance()->load_model('State');
1543
-            $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1544
-            $STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1545
-        }
1546
-        // get Country ISO
1547
-        $CNT_ISO = $this->request->getRequestParam('country');
1548
-        if (! empty($CNT_ISO)) {
1549
-            // can we get country object from name ?
1550
-            EE_Registry::instance()->load_model('Country');
1551
-            $country = EEM_Country::instance()->get_col(
1552
-                [['CNT_name' => $CNT_ISO], 'limit' => 1],
1553
-                'CNT_ISO'
1554
-            );
1555
-            $CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1556
-        }
1557
-        // grab attendee data
1558
-        $attendee_data = [
1559
-            'ATT_fname'    => $this->request->getRequestParam('first_name'),
1560
-            'ATT_lname'    => $this->request->getRequestParam('last_name'),
1561
-            'ATT_email'    => $this->request->getRequestParam('email'),
1562
-            'ATT_address'  => $this->request->getRequestParam('address'),
1563
-            'ATT_address2' => $this->request->getRequestParam('address2'),
1564
-            'ATT_city'     => $this->request->getRequestParam('city'),
1565
-            'STA_ID'       => $STA_ID,
1566
-            'CNT_ISO'      => $CNT_ISO,
1567
-            'ATT_zip'      => $this->request->getRequestParam('zip'),
1568
-            'ATT_phone'    => $this->request->getRequestParam('phone'),
1569
-        ];
1570
-        // validate the email address since it is the most important piece of info
1571
-        if (empty($attendee_data['ATT_email'])) {
1572
-            EE_Error::add_error(
1573
-                esc_html__('An invalid email address was submitted.', 'event_espresso'),
1574
-                __FILE__,
1575
-                __FUNCTION__,
1576
-                __LINE__
1577
-            );
1578
-        }
1579
-        // does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1580
-        // AND email address
1581
-        if (
1582
-            ! empty($attendee_data['ATT_fname'])
1583
-            && ! empty($attendee_data['ATT_lname'])
1584
-            && ! empty($attendee_data['ATT_email'])
1585
-        ) {
1586
-            $existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1587
-                [
1588
-                    'ATT_fname' => $attendee_data['ATT_fname'],
1589
-                    'ATT_lname' => $attendee_data['ATT_lname'],
1590
-                    'ATT_email' => $attendee_data['ATT_email'],
1591
-                ]
1592
-            );
1593
-            if ($existing_attendee instanceof EE_Attendee) {
1594
-                return $existing_attendee;
1595
-            }
1596
-        }
1597
-        // no existing attendee? kk let's create a new one
1598
-        // kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1599
-        // don't exist
1600
-        $attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1601
-            ? $attendee_data['ATT_fname']
1602
-            : $attendee_data['ATT_email'];
1603
-        $attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1604
-            ? $attendee_data['ATT_lname']
1605
-            : $attendee_data['ATT_email'];
1606
-        return EE_Attendee::new_instance($attendee_data);
1607
-    }
1608
-
1609
-
1610
-
1611
-    /********************************************************************************************************/
1612
-    /****************************************  PROCESS REG STEP  *****************************************/
1613
-    /********************************************************************************************************/
1614
-
1615
-
1616
-    /**
1617
-     * @return bool
1618
-     * @throws EE_Error
1619
-     * @throws InvalidArgumentException
1620
-     * @throws ReflectionException
1621
-     * @throws EntityNotFoundException
1622
-     * @throws InvalidDataTypeException
1623
-     * @throws InvalidInterfaceException
1624
-     * @throws InvalidStatusException
1625
-     */
1626
-    public function process_reg_step(): bool
1627
-    {
1628
-        // how have they chosen to pay?
1629
-        $this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1630
-            ? 'no_payment_required'
1631
-            : $this->_get_selected_method_of_payment(true);
1632
-        // choose your own adventure based on method_of_payment
1633
-        switch ($this->checkout->selected_method_of_payment) {
1634
-            case 'events_sold_out':
1635
-                $this->checkout->redirect     = true;
1636
-                $this->checkout->redirect_url = $this->checkout->cancel_page_url;
1637
-                $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1638
-                // mark this reg step as completed
1639
-                $this->set_completed();
1640
-                return false;
1641
-
1642
-            case 'payments_closed':
1643
-                if (
1644
-                    apply_filters(
1645
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1646
-                        false
1647
-                    )
1648
-                ) {
1649
-                    EE_Error::add_success(
1650
-                        esc_html__('no payment required at this time.', 'event_espresso'),
1651
-                        __FILE__,
1652
-                        __FUNCTION__,
1653
-                        __LINE__
1654
-                    );
1655
-                }
1656
-                // mark this reg step as completed
1657
-                $this->set_completed();
1658
-                return true;
1659
-
1660
-            case 'no_payment_required':
1661
-                if (
1662
-                    apply_filters(
1663
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1664
-                        false
1665
-                    )
1666
-                ) {
1667
-                    EE_Error::add_success(
1668
-                        esc_html__('no payment required.', 'event_espresso'),
1669
-                        __FILE__,
1670
-                        __FUNCTION__,
1671
-                        __LINE__
1672
-                    );
1673
-                }
1674
-                // mark this reg step as completed
1675
-                $this->set_completed();
1676
-                return true;
1677
-
1678
-            default:
1679
-                $registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1680
-                    EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1681
-                );
1682
-                $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1683
-                    $registrations,
1684
-                    EE_Registry::instance()->SSN->checkout()->revisit
1685
-                );
1686
-                // calculate difference between the two arrays
1687
-                $registrations = array_diff($registrations, $ejected_registrations);
1688
-                if (empty($registrations)) {
1689
-                    $this->_redirect_because_event_sold_out();
1690
-                    return false;
1691
-                }
1692
-                $payment = $this->_process_payment();
1693
-                if ($payment instanceof EE_Payment) {
1694
-                    $this->checkout->continue_reg = true;
1695
-                    $this->_maybe_set_completed($payment);
1696
-                } else {
1697
-                    $this->checkout->continue_reg = false;
1698
-                }
1699
-                return $payment instanceof EE_Payment;
1700
-        }
1701
-    }
1702
-
1703
-
1704
-    /**
1705
-     * @return void
1706
-     */
1707
-    protected function _redirect_because_event_sold_out()
1708
-    {
1709
-        $this->checkout->continue_reg = false;
1710
-        // set redirect URL
1711
-        $this->checkout->redirect_url = add_query_arg(
1712
-            ['e_reg_url_link' => $this->checkout->reg_url_link],
1713
-            $this->checkout->current_step->reg_step_url()
1714
-        );
1715
-        $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1716
-    }
1717
-
1718
-
1719
-    /**
1720
-     * @param EE_Payment $payment
1721
-     * @return void
1722
-     * @throws EE_Error
1723
-     */
1724
-    protected function _maybe_set_completed(EE_Payment $payment)
1725
-    {
1726
-        // Do we need to redirect them? If so, there's more work to be done.
1727
-        if (! $payment->redirect_url()) {
1728
-            $this->set_completed();
1729
-        }
1730
-    }
1731
-
1732
-
1733
-    /**
1734
-     * this is the final step after a user  revisits the site to retry a payment
1735
-     *
1736
-     * @return bool
1737
-     * @throws EE_Error
1738
-     * @throws InvalidArgumentException
1739
-     * @throws ReflectionException
1740
-     * @throws EntityNotFoundException
1741
-     * @throws InvalidDataTypeException
1742
-     * @throws InvalidInterfaceException
1743
-     * @throws InvalidStatusException
1744
-     */
1745
-    public function update_reg_step(): bool
1746
-    {
1747
-        $success = true;
1748
-        // if payment required
1749
-        if ($this->checkout->transaction->total() > 0) {
1750
-            do_action(
1751
-                'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1752
-                $this->checkout->transaction
1753
-            );
1754
-            // attempt payment via payment method
1755
-            $success = $this->process_reg_step();
1756
-        }
1757
-        if ($success && ! $this->checkout->redirect) {
1758
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1759
-                $this->checkout->transaction->ID()
1760
-            );
1761
-            // set return URL
1762
-            $this->checkout->redirect_url = add_query_arg(
1763
-                ['e_reg_url_link' => $this->checkout->reg_url_link],
1764
-                $this->checkout->thank_you_page_url
1765
-            );
1766
-        }
1767
-        return $success;
1768
-    }
1769
-
1770
-
1771
-    /**
1772
-     * @return EE_Payment|bool|null
1773
-     * @throws EE_Error
1774
-     * @throws InvalidArgumentException
1775
-     * @throws ReflectionException
1776
-     * @throws RuntimeException
1777
-     * @throws InvalidDataTypeException
1778
-     * @throws InvalidInterfaceException
1779
-     */
1780
-    private function _process_payment()
1781
-    {
1782
-        // basically confirm that the event hasn't sold out since they hit the page
1783
-        if (! $this->_last_second_ticket_verifications()) {
1784
-            return null;
1785
-        }
1786
-        // ya gotta make a choice man
1787
-        if (empty($this->checkout->selected_method_of_payment)) {
1788
-            $this->checkout->json_response->set_plz_select_method_of_payment(
1789
-                esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1790
-            );
1791
-            return null;
1792
-        }
1793
-        // get EE_Payment_Method object
1794
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1795
-            return null;
1796
-        }
1797
-        // setup billing form
1798
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1799
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1800
-                $this->checkout->payment_method
1801
-            );
1802
-            // bad billing form ?
1803
-            if (! $this->_billing_form_is_valid()) {
1804
-                return null;
1805
-            }
1806
-        }
1807
-        // ensure primary registrant has been fully processed
1808
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1809
-            return null;
1810
-        }
1811
-        // if session is close to expiring (under 10 minutes by default)
1812
-        if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1813
-            // add some time to session expiration so that payment can be completed
1814
-            EE_Registry::instance()->SSN->extend_expiration();
1815
-        }
1816
-        /** @type EE_Transaction_Processor $transaction_processor */
1817
-        // $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1818
-        // in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1819
-        // for events with a default reg status of Approved
1820
-        // $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1821
-        //      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1822
-        // );
1823
-        // attempt payment
1824
-        $payment = $this->_attempt_payment($this->checkout->payment_method);
1825
-        // process results
1826
-        $payment = $this->_validate_payment($payment);
1827
-        $payment = $this->_post_payment_processing($payment);
1828
-        // verify payment
1829
-        if ($payment instanceof EE_Payment) {
1830
-            // store that for later
1831
-            $this->checkout->payment = $payment;
1832
-            // we can also consider the TXN to not have been failed, so temporarily upgrade its status to abandoned
1833
-            $this->checkout->transaction->toggle_failed_transaction_status();
1834
-            $payment_status = $payment->status();
1835
-            if (
1836
-                $payment_status === EEM_Payment::status_id_approved
1837
-                || $payment_status === EEM_Payment::status_id_pending
1838
-            ) {
1839
-                return $payment;
1840
-            }
1841
-            return null;
1842
-        }
1843
-        if ($payment === true) {
1844
-            // please note that offline payment methods will NOT make a payment,
1845
-            // but instead just mark themselves as the PMD_ID on the transaction, and return true
1846
-            $this->checkout->payment = true;
1847
-            return true;
1848
-        }
1849
-        // where's my money?
1850
-        return null;
1851
-    }
1852
-
1853
-
1854
-    /**
1855
-     * @return bool
1856
-     * @throws EE_Error
1857
-     * @throws ReflectionException
1858
-     */
1859
-    protected function _last_second_ticket_verifications(): bool
1860
-    {
1861
-        // don't bother re-validating if not a return visit
1862
-        if (! $this->checkout->revisit) {
1863
-            return true;
1864
-        }
1865
-        $registrations = $this->checkout->transaction->registrations();
1866
-        if (empty($registrations)) {
1867
-            return false;
1868
-        }
1869
-        foreach ($registrations as $registration) {
1870
-            if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1871
-                $event = $registration->event_obj();
1872
-                if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1873
-                    EE_Error::add_error(
1874
-                        apply_filters(
1875
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1876
-                            sprintf(
1877
-                                esc_html__(
1878
-                                    'It appears that the %1$s event that you were about to make a payment for has sold out since you first registered and/or arrived at this page. Please refresh the page and try again. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
1879
-                                    'event_espresso'
1880
-                                ),
1881
-                                $event->name()
1882
-                            )
1883
-                        ),
1884
-                        __FILE__,
1885
-                        __FUNCTION__,
1886
-                        __LINE__
1887
-                    );
1888
-                    return false;
1889
-                }
1890
-            }
1891
-        }
1892
-        return true;
1893
-    }
1894
-
1895
-
1896
-    /**
1897
-     * @return bool
1898
-     * @throws EE_Error
1899
-     * @throws InvalidArgumentException
1900
-     * @throws ReflectionException
1901
-     * @throws InvalidDataTypeException
1902
-     * @throws InvalidInterfaceException
1903
-     */
1904
-    public function redirect_form(): bool
1905
-    {
1906
-        $payment_method_billing_info = $this->_payment_method_billing_info(
1907
-            $this->_get_payment_method_for_selected_method_of_payment()
1908
-        );
1909
-        $html                        = $payment_method_billing_info->get_html();
1910
-        $html                        .= $this->checkout->redirect_form;
1911
-        /** @var ResponseInterface $response */
1912
-        $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1913
-        $response->addOutput($html);
1914
-        return true;
1915
-    }
1916
-
1917
-
1918
-    /**
1919
-     * @return bool
1920
-     * @throws EE_Error
1921
-     * @throws ReflectionException
1922
-     */
1923
-    private function _billing_form_is_valid(): bool
1924
-    {
1925
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1926
-            return true;
1927
-        }
1928
-        if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1929
-            if ($this->checkout->billing_form->was_submitted()) {
1930
-                $this->checkout->billing_form->receive_form_submission();
1931
-                if ($this->checkout->billing_form->is_valid()) {
1932
-                    return true;
1933
-                }
1934
-                $validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
1935
-                $error_strings     = [];
1936
-                foreach ($validation_errors as $validation_error) {
1937
-                    if ($validation_error instanceof EE_Validation_Error) {
1938
-                        $form_section = $validation_error->get_form_section();
1939
-                        if ($form_section instanceof EE_Form_Input_Base) {
1940
-                            $label = $form_section->html_label_text();
1941
-                        } elseif ($form_section instanceof EE_Form_Section_Base) {
1942
-                            $label = $form_section->name();
1943
-                        } else {
1944
-                            $label = esc_html__('Validation Error', 'event_espresso');
1945
-                        }
1946
-                        $error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
1947
-                    }
1948
-                }
1949
-                EE_Error::add_error(
1950
-                    sprintf(
1951
-                        esc_html__(
1952
-                            'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
1953
-                            'event_espresso'
1954
-                        ),
1955
-                        '<br/>',
1956
-                        implode('<br/>', $error_strings)
1957
-                    ),
1958
-                    __FILE__,
1959
-                    __FUNCTION__,
1960
-                    __LINE__
1961
-                );
1962
-            } else {
1963
-                EE_Error::add_error(
1964
-                    esc_html__(
1965
-                        'The billing form was not submitted or something prevented it\'s submission.',
1966
-                        'event_espresso'
1967
-                    ),
1968
-                    __FILE__,
1969
-                    __FUNCTION__,
1970
-                    __LINE__
1971
-                );
1972
-            }
1973
-        } else {
1974
-            EE_Error::add_error(
1975
-                esc_html__(
1976
-                    'The submitted billing form is invalid possibly due to a technical reason.',
1977
-                    'event_espresso'
1978
-                ),
1979
-                __FILE__,
1980
-                __FUNCTION__,
1981
-                __LINE__
1982
-            );
1983
-        }
1984
-        return false;
1985
-    }
1986
-
1987
-
1988
-    /**
1989
-     * ensures that the primary registrant has a valid attendee object created with the critical details populated
1990
-     * (first & last name & email) and that both the transaction object and primary registration object have been saved
1991
-     * plz note that any other registrations will NOT be saved at this point (because they may not have any details
1992
-     * yet)
1993
-     *
1994
-     * @return bool
1995
-     * @throws EE_Error
1996
-     * @throws InvalidArgumentException
1997
-     * @throws ReflectionException
1998
-     * @throws RuntimeException
1999
-     * @throws InvalidDataTypeException
2000
-     * @throws InvalidInterfaceException
2001
-     */
2002
-    private function _setup_primary_registrant_prior_to_payment(): bool
2003
-    {
2004
-        // check if transaction has a primary registrant and that it has a related Attendee object
2005
-        // if not, then we need to at least gather some primary registrant data before attempting payment
2006
-        if (
2007
-            $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2008
-            && ! $this->checkout->transaction_has_primary_registrant()
2009
-            && ! $this->_capture_primary_registration_data_from_billing_form()
2010
-        ) {
2011
-            return false;
2012
-        }
2013
-        // because saving an object clears its cache, we need to do the Chevy Shuffle
2014
-        // grab the primary_registration object
2015
-        $primary_registration = $this->checkout->transaction->primary_registration();
2016
-        // at this point we'll consider a TXN to not have been failed
2017
-        $this->checkout->transaction->toggle_failed_transaction_status();
2018
-        // save the TXN ( which clears cached copy of primary_registration)
2019
-        $this->checkout->transaction->save();
2020
-        // grab TXN ID and save it to the primary_registration
2021
-        $primary_registration->set_transaction_id($this->checkout->transaction->ID());
2022
-        // save what we have so far
2023
-        $primary_registration->save();
2024
-        return true;
2025
-    }
2026
-
2027
-
2028
-    /**
2029
-     * Captures primary registration data from the billing form.
2030
-     *
2031
-     * This method is used to gather the primary registrant data before attempting payment.
2032
-     * It checks if the billing form is an instance of EE_Billing_Attendee_Info_Form and if the transaction
2033
-     * has a primary registrant. If not, it captures the primary registrant data from the billing form.
2034
-     *
2035
-     * @return bool
2036
-     * @throws EE_Error
2037
-     * @throws InvalidArgumentException
2038
-     * @throws ReflectionException
2039
-     * @throws InvalidDataTypeException
2040
-     * @throws InvalidInterfaceException
2041
-     */
2042
-    private function _capture_primary_registration_data_from_billing_form(): bool
2043
-    {
2044
-        $primary_registration = $this->checkout->transaction->primary_registration();
2045
-        if (! $this->validatePrimaryRegistration($primary_registration)) {
2046
-            return false;
2047
-        }
2048
-
2049
-        $primary_attendee = $this->getPrimaryAttendee($primary_registration);
2050
-        if (! $this->validatePrimaryAttendee($primary_attendee)) {
2051
-            return false;
2052
-        }
2053
-
2054
-        if (! $this->addAttendeeToPrimaryRegistration($primary_attendee, $primary_registration)) {
2055
-            return false;
2056
-        }
2057
-        // both the primary registration and primary attendee objects should be valid entities at this point
2058
-        $this->checkout->primary_attendee_obj = $primary_attendee;
2059
-
2060
-        /** @type EE_Registration_Processor $registration_processor */
2061
-        $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2062
-        // at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2063
-        $registration_processor->toggle_incomplete_registration_status_to_default(
2064
-            $primary_registration,
2065
-            false,
2066
-            new Context(
2067
-                __METHOD__,
2068
-                esc_html__(
2069
-                    'Executed when the primary registrant\'s status is updated during the registration process when processing a billing form.',
2070
-                    'event_espresso'
2071
-                )
2072
-            )
2073
-        );
2074
-        return true;
2075
-    }
2076
-
2077
-
2078
-    /**
2079
-     * returns true if the primary registration is a valid entity
2080
-     *
2081
-     * @param $primary_registration
2082
-     * @return bool
2083
-     * @throws EE_Error
2084
-     * @since 5.0.21.p
2085
-     */
2086
-    private function validatePrimaryRegistration($primary_registration): bool
2087
-    {
2088
-        if ($primary_registration instanceof EE_Registration) {
2089
-            return true;
2090
-        }
2091
-        EE_Error::add_error(
2092
-            sprintf(
2093
-                esc_html__(
2094
-                    'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2095
-                    'event_espresso'
2096
-                ),
2097
-                '<br/>',
2098
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2099
-            ),
2100
-            __FILE__,
2101
-            __FUNCTION__,
2102
-            __LINE__
2103
-        );
2104
-        return false;
2105
-    }
2106
-
2107
-
2108
-    /**
2109
-     * retrieves the primary attendee object for the primary registration and copies the billing form data to it.
2110
-     * if the primary registration does not have an attendee object, then one is created from the billing form info
2111
-     *
2112
-     * @param EE_Registration $primary_registration
2113
-     * @return EE_Attendee|null
2114
-     * @throws EE_Error
2115
-     * @throws ReflectionException
2116
-     * @since 5.0.21.p
2117
-     */
2118
-    private function getPrimaryAttendee(EE_Registration $primary_registration): ?EE_Attendee
2119
-    {
2120
-        // if we have a primary registration, then we should have a primary attendee
2121
-        $attendee = $primary_registration->attendee();
2122
-        if ($attendee instanceof EE_Attendee) {
2123
-            return $this->checkout->billing_form->copy_billing_form_data_to_attendee($attendee);
2124
-        }
2125
-        // if not, then we need to create one from the billing form
2126
-        return $this->checkout->billing_form->create_attendee_from_billing_form_data();
2127
-    }
2128
-
2129
-
2130
-    /**
2131
-     * returns true if the primary attendee is a valid entity
2132
-     *
2133
-     * @param $primary_attendee
2134
-     * @return bool
2135
-     * @throws EE_Error
2136
-     * @since 5.0.21.p
2137
-     */
2138
-    private function validatePrimaryAttendee($primary_attendee): bool
2139
-    {
2140
-        if ($primary_attendee instanceof EE_Attendee) {
2141
-            return true;
2142
-        }
2143
-        EE_Error::add_error(
2144
-            sprintf(
2145
-                esc_html__(
2146
-                    'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2147
-                    'event_espresso'
2148
-                ),
2149
-                '<br/>',
2150
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2151
-            ),
2152
-            __FILE__,
2153
-            __FUNCTION__,
2154
-            __LINE__
2155
-        );
2156
-        return false;
2157
-    }
2158
-
2159
-
2160
-    /**
2161
-     * returns true if the attendee was successfully added to the primary registration
2162
-     *
2163
-     * @param EE_Attendee     $primary_attendee
2164
-     * @param EE_Registration $primary_registration
2165
-     * @return bool
2166
-     * @throws EE_Error
2167
-     * @throws ReflectionException
2168
-     * @since 5.0.21.p
2169
-     */
2170
-    private function addAttendeeToPrimaryRegistration(
2171
-        EE_Attendee $primary_attendee,
2172
-        EE_Registration $primary_registration
2173
-    ): bool {
2174
-        // ensure attendee has an ID by saving
2175
-        $primary_attendee->save();
2176
-
2177
-        // compare attendee IDs
2178
-        if ($primary_registration->attendee_id() === $primary_attendee->ID()) {
2179
-            return true;
2180
-        }
2181
-
2182
-        $primary_attendee = $primary_registration->_add_relation_to($primary_attendee, 'Attendee');
2183
-        if ($primary_attendee instanceof EE_Attendee) {
2184
-            return true;
2185
-        }
2186
-
2187
-        EE_Error::add_error(
2188
-            sprintf(
2189
-                esc_html__(
2190
-                    'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2191
-                    'event_espresso'
2192
-                ),
2193
-                '<br/>',
2194
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2195
-            ),
2196
-            __FILE__,
2197
-            __FUNCTION__,
2198
-            __LINE__
2199
-        );
2200
-        return false;
2201
-    }
2202
-
2203
-
2204
-    /**
2205
-     * retrieves a valid payment method
2206
-     *
2207
-     * @return EE_Payment_Method
2208
-     * @throws EE_Error
2209
-     * @throws InvalidArgumentException
2210
-     * @throws ReflectionException
2211
-     * @throws InvalidDataTypeException
2212
-     * @throws InvalidInterfaceException
2213
-     */
2214
-    private function _get_payment_method_for_selected_method_of_payment(): ?EE_Payment_Method
2215
-    {
2216
-        if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2217
-            $this->_redirect_because_event_sold_out();
2218
-            return null;
2219
-        }
2220
-        // get EE_Payment_Method object
2221
-        $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ]
2222
-            ?? $this->payment_method_model->get_one_by_slug($this->checkout->selected_method_of_payment);
2223
-        // verify $payment_method
2224
-        if (! $payment_method instanceof EE_Payment_Method) {
2225
-            // not a payment
2226
-            EE_Error::add_error(
2227
-                sprintf(
2228
-                    esc_html__(
2229
-                        'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2230
-                        'event_espresso'
2231
-                    ),
2232
-                    '<br/>',
2233
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2234
-                ),
2235
-                __FILE__,
2236
-                __FUNCTION__,
2237
-                __LINE__
2238
-            );
2239
-            return null;
2240
-        }
2241
-        // and verify it has a valid Payment_Method Type object
2242
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2243
-            // not a payment
2244
-            EE_Error::add_error(
2245
-                sprintf(
2246
-                    esc_html__(
2247
-                        'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2248
-                        'event_espresso'
2249
-                    ),
2250
-                    '<br/>',
2251
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2252
-                ),
2253
-                __FILE__,
2254
-                __FUNCTION__,
2255
-                __LINE__
2256
-            );
2257
-            return null;
2258
-        }
2259
-        return $payment_method;
2260
-    }
2261
-
2262
-
2263
-    /**
2264
-     * @param EE_Payment_Method $payment_method
2265
-     * @return EE_Payment|null
2266
-     * @throws EE_Error
2267
-     * @throws ReflectionException
2268
-     */
2269
-    private function _attempt_payment(EE_Payment_Method $payment_method): ?EE_Payment
2270
-    {
2271
-        $this->checkout->transaction->save();
2272
-        /** @var PaymentProcessor $payment_processor */
2273
-        $payment_processor = LoaderFactory::getShared(PaymentProcessor::class);
2274
-        if (! $payment_processor instanceof PaymentProcessor) {
2275
-            return null;
2276
-        }
2277
-        /** @var EE_Transaction_Processor $transaction_processor */
2278
-        $transaction_processor = LoaderFactory::getShared(EE_Transaction_Processor::class);
2279
-        if ($transaction_processor instanceof EE_Transaction_Processor) {
2280
-            $transaction_processor->set_revisit($this->checkout->revisit);
2281
-        }
2282
-        try {
2283
-            // generate payment object
2284
-            return $payment_processor->processPayment(
2285
-                $payment_method,
2286
-                $this->checkout->transaction,
2287
-                $this->checkout->billing_form instanceof EE_Billing_Info_Form
2288
-                    ? $this->checkout->billing_form
2289
-                    : null,
2290
-                $this->checkout->amount_owing,
2291
-                $this->checkout->admin_request,
2292
-                true,
2293
-                $this->_get_return_url($payment_method),
2294
-                $this->reg_step_url()
2295
-            );
2296
-        } catch (Exception $e) {
2297
-            $this->_handle_payment_processor_exception($e);
2298
-        }
2299
-        return null;
2300
-    }
2301
-
2302
-
2303
-    /**
2304
-     * @param Exception $e
2305
-     * @return void
2306
-     * @throws EE_Error
2307
-     */
2308
-    protected function _handle_payment_processor_exception(Exception $e)
2309
-    {
2310
-        EE_Error::add_error(
2311
-            sprintf(
2312
-                esc_html__(
2313
-                    'The payment could not br processed due to a technical issue.%1$sPlease try again or contact %2$s for assistance.||The following Exception was thrown in %4$s on line %5$s:%1$s%3$s',
2314
-                    'event_espresso'
2315
-                ),
2316
-                '<br/>',
2317
-                EE_Registry::instance()->CFG->organization->get_pretty('email'),
2318
-                $e->getMessage(),
2319
-                $e->getFile(),
2320
-                $e->getLine()
2321
-            ),
2322
-            __FILE__,
2323
-            __FUNCTION__,
2324
-            __LINE__
2325
-        );
2326
-    }
2327
-
2328
-
2329
-    /**
2330
-     * @param EE_Payment_Method $payment_method
2331
-     * @return string
2332
-     * @throws EE_Error
2333
-     * @throws ReflectionException
2334
-     */
2335
-    protected function _get_return_url(EE_Payment_Method $payment_method): string
2336
-    {
2337
-        switch ($payment_method->type_obj()->payment_occurs()) {
2338
-            case EE_PMT_Base::offsite:
2339
-                return add_query_arg(
2340
-                    [
2341
-                        'action'                     => 'process_gateway_response',
2342
-                        'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2343
-                        'spco_txn'                   => $this->checkout->transaction->ID(),
2344
-                    ],
2345
-                    $this->reg_step_url()
2346
-                );
2347
-
2348
-            case EE_PMT_Base::onsite:
2349
-            case EE_PMT_Base::offline:
2350
-                return $this->checkout->next_step->reg_step_url();
2351
-        }
2352
-        return '';
2353
-    }
2354
-
2355
-
2356
-    /**
2357
-     * @param EE_Payment|null $payment
2358
-     * @return EE_Payment|bool
2359
-     * @throws EE_Error
2360
-     * @throws ReflectionException
2361
-     */
2362
-    private function _validate_payment(?EE_Payment $payment = null)
2363
-    {
2364
-        if ($this->checkout->payment_method->is_off_line()) {
2365
-            return true;
2366
-        }
2367
-        // verify payment object
2368
-        if (! $payment instanceof EE_Payment) {
2369
-            // not a payment
2370
-            EE_Error::add_error(
2371
-                sprintf(
2372
-                    esc_html__(
2373
-                        'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2374
-                        'event_espresso'
2375
-                    ),
2376
-                    '<br/>',
2377
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2378
-                ),
2379
-                __FILE__,
2380
-                __FUNCTION__,
2381
-                __LINE__
2382
-            );
2383
-            return false;
2384
-        }
2385
-        return $payment;
2386
-    }
2387
-
2388
-
2389
-    /**
2390
-     * @param EE_Payment|bool $payment
2391
-     * @return bool|EE_Payment
2392
-     * @throws EE_Error
2393
-     * @throws ReflectionException
2394
-     */
2395
-    private function _post_payment_processing($payment = null)
2396
-    {
2397
-        // Off-Line payment?
2398
-        if ($payment === true) {
2399
-            return true;
2400
-        }
2401
-        if ($payment instanceof EE_Payment) {
2402
-            // Should the user be redirected?
2403
-            if ($payment->redirect_url()) {
2404
-                do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2405
-                $this->checkout->redirect      = true;
2406
-                $this->checkout->redirect_form = $payment->redirect_form();
2407
-                $this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2408
-                // set JSON response
2409
-                $this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2410
-                // and lastly, let's bump the payment status to pending
2411
-                $payment->set_status(EEM_Payment::status_id_pending);
2412
-                $payment->save();
2413
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2414
-                // User shouldn't be redirected. So let's process it here.
2415
-                // $this->_setup_redirect_for_next_step();
2416
-                $this->checkout->continue_reg = false;
2417
-            }
2418
-            return $payment;
2419
-        }
2420
-        // ummm ya... not Off-Line, not On-Site, not off-Site ????
2421
-        $this->checkout->continue_reg = false;
2422
-        return false;
2423
-    }
2424
-
2425
-
2426
-    /**
2427
-     * @param EE_Payment|null $payment
2428
-     * @param string          $payment_occurs
2429
-     * @return bool
2430
-     * @throws EE_Error
2431
-     */
2432
-    private function _process_payment_status(?EE_Payment $payment, string $payment_occurs = EE_PMT_Base::offline): bool
2433
-    {
2434
-        // off-line payment? carry on
2435
-        if ($payment_occurs === EE_PMT_Base::offline) {
2436
-            return true;
2437
-        }
2438
-        // verify payment validity
2439
-        if ($payment instanceof EE_Payment) {
2440
-            do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2441
-            $msg = $payment->gateway_response();
2442
-            // check results
2443
-            switch ($payment->status()) {
2444
-                // good payment
2445
-                case EEM_Payment::status_id_approved:
2446
-                    EE_Error::add_success(
2447
-                        esc_html__('Your payment was processed successfully.', 'event_espresso'),
2448
-                        __FILE__,
2449
-                        __FUNCTION__,
2450
-                        __LINE__
2451
-                    );
2452
-                    return true;
2453
-                // slow payment
2454
-                case EEM_Payment::status_id_pending:
2455
-                    if (empty($msg)) {
2456
-                        $msg = esc_html__(
2457
-                            'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2458
-                            'event_espresso'
2459
-                        );
2460
-                    }
2461
-                    EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2462
-                    return true;
2463
-                // don't wanna payment
2464
-                case EEM_Payment::status_id_cancelled:
2465
-                    if (empty($msg)) {
2466
-                        $msg = _n(
2467
-                            'Payment cancelled. Please try again.',
2468
-                            'Payment cancelled. Please try again or select another method of payment.',
2469
-                            count($this->checkout->available_payment_methods),
2470
-                            'event_espresso'
2471
-                        );
2472
-                    }
2473
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2474
-                    return false;
2475
-                // not enough payment
2476
-                case EEM_Payment::status_id_declined:
2477
-                    if (empty($msg)) {
2478
-                        $msg = _n(
2479
-                            'We\'re sorry but your payment was declined. Please try again.',
2480
-                            'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2481
-                            count($this->checkout->available_payment_methods),
2482
-                            'event_espresso'
2483
-                        );
2484
-                    }
2485
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2486
-                    return false;
2487
-                // bad payment
2488
-                case EEM_Payment::status_id_failed:
2489
-                    if (! empty($msg)) {
2490
-                        EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2491
-                        return false;
2492
-                    }
2493
-                    // default to error below
2494
-                    break;
2495
-            }
2496
-        }
2497
-        // off-site payment gateway responses are too unreliable, so let's just assume that
2498
-        // the payment processing is just running slower than the registrant's request
2499
-        if ($payment_occurs === EE_PMT_Base::offsite) {
2500
-            return true;
2501
-        }
2502
-        EE_Error::add_error(
2503
-            sprintf(
2504
-                esc_html__(
2505
-                    'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2506
-                    'event_espresso'
2507
-                ),
2508
-                '<br/>',
2509
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2510
-            ),
2511
-            __FILE__,
2512
-            __FUNCTION__,
2513
-            __LINE__
2514
-        );
2515
-        return false;
2516
-    }
2517
-
2518
-
2519
-
2520
-
2521
-
2522
-
2523
-    /********************************************************************************************************/
2524
-    /**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2525
-    /********************************************************************************************************/
2526
-
2527
-
2528
-    /**
2529
-     * This is the return point for Off-Site Payment Methods
2530
-     * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2531
-     * otherwise, it will load up the last payment made for the TXN.
2532
-     * If the payment retrieved looks good, it will then either:
2533
-     *    complete the current step and allow advancement to the next reg step
2534
-     *        or present the payment options again
2535
-     *
2536
-     * @return bool
2537
-     * @throws EE_Error
2538
-     * @throws InvalidArgumentException
2539
-     * @throws ReflectionException
2540
-     * @throws InvalidDataTypeException
2541
-     * @throws InvalidInterfaceException
2542
-     */
2543
-    public function process_gateway_response()
2544
-    {
2545
-        // how have they chosen to pay?
2546
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2547
-        // get EE_Payment_Method object
2548
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2549
-            $this->checkout->continue_reg = false;
2550
-            return false;
2551
-        }
2552
-        if (! $this->checkout->payment_method->is_off_site()) {
2553
-            return false;
2554
-        }
2555
-        $this->_validate_offsite_return();
2556
-        // verify TXN
2557
-        if ($this->checkout->transaction instanceof EE_Transaction) {
2558
-            $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2559
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2560
-                $this->checkout->continue_reg = false;
2561
-                return false;
2562
-            }
2563
-            $payment = $this->_process_off_site_payment($gateway);
2564
-            $payment = $this->_process_cancelled_payments($payment);
2565
-            $payment = $this->_validate_payment($payment);
2566
-            // if payment was not declined by the payment gateway or cancelled by the registrant
2567
-            if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2568
-                // $this->_setup_redirect_for_next_step();
2569
-                // store that for later
2570
-                $this->checkout->payment = $payment;
2571
-                // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2572
-                // because we will complete this step during the IPN processing then
2573
-                if (! $this->handle_IPN_in_this_request()) {
2574
-                    $this->set_completed();
2575
-                }
2576
-                return true;
2577
-            }
2578
-        }
2579
-        // DEBUG LOG
2580
-        // $this->checkout->log(
2581
-        //     __CLASS__,
2582
-        //     __FUNCTION__,
2583
-        //     __LINE__,
2584
-        //     array('payment' => $payment)
2585
-        // );
2586
-        $this->checkout->continue_reg = false;
2587
-        return false;
2588
-    }
2589
-
2590
-
2591
-    /**
2592
-     * @return void
2593
-     * @throws EE_Error
2594
-     * @throws ReflectionException
2595
-     */
2596
-    private function _validate_offsite_return()
2597
-    {
2598
-        $TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2599
-        if ($TXN_ID !== $this->checkout->transaction->ID()) {
2600
-            // Houston... we might have a problem
2601
-            $invalid_TXN = false;
2602
-            // first gather some info
2603
-            $valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2604
-            $primary_registrant = $valid_TXN instanceof EE_Transaction
2605
-                ? $valid_TXN->primary_registration()
2606
-                : null;
2607
-            // let's start by retrieving the cart for this TXN
2608
-            $cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2609
-            if ($cart instanceof EE_Cart) {
2610
-                // verify that the current cart has tickets
2611
-                $tickets = $cart->get_tickets();
2612
-                if (empty($tickets)) {
2613
-                    $invalid_TXN = true;
2614
-                }
2615
-            } else {
2616
-                $invalid_TXN = true;
2617
-            }
2618
-            $valid_TXN_SID = $primary_registrant instanceof EE_Registration
2619
-                ? $primary_registrant->session_ID()
2620
-                : null;
2621
-            // validate current Session ID and compare against valid TXN session ID
2622
-            if (
2623
-                $invalid_TXN // if this is already true, then skip other checks
2624
-                || EE_Session::instance()->id() === null
2625
-                || (
2626
-                    // WARNING !!!
2627
-                    // this could be PayPal sending back duplicate requests (ya they do that)
2628
-                    // or it **could** mean someone is simply registering AGAIN after having just done so,
2629
-                    // so now we need to determine if this current TXN looks valid or not
2630
-                    // and whether this reg step has even been started ?
2631
-                    EE_Session::instance()->id() === $valid_TXN_SID
2632
-                    // really? you're halfway through this reg step, but you never started it ?
2633
-                    && $this->checkout->transaction->reg_step_completed($this->slug()) === false
2634
-                )
2635
-            ) {
2636
-                $invalid_TXN = true;
2637
-            }
2638
-            if ($invalid_TXN) {
2639
-                // is the valid TXN completed ?
2640
-                if ($valid_TXN instanceof EE_Transaction) {
2641
-                    // has this step even been started ?
2642
-                    $reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2643
-                    if ($reg_step_completed !== false && $reg_step_completed !== true) {
2644
-                        // so it **looks** like this is a double request from PayPal
2645
-                        // so let's try to pick up where we left off
2646
-                        $this->checkout->transaction = $valid_TXN;
2647
-                        $this->checkout->refresh_all_entities(true);
2648
-                        return;
2649
-                    }
2650
-                }
2651
-                // you appear to be lost?
2652
-                $this->_redirect_wayward_request($primary_registrant);
2653
-            }
2654
-        }
2655
-    }
2656
-
2657
-
2658
-    /**
2659
-     * @param EE_Registration|null $primary_registrant
2660
-     * @return void
2661
-     * @throws EE_Error
2662
-     * @throws ReflectionException
2663
-     */
2664
-    private function _redirect_wayward_request(?EE_Registration $primary_registrant)
2665
-    {
2666
-        if (! $primary_registrant instanceof EE_Registration) {
2667
-            // try redirecting based on the current TXN
2668
-            $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2669
-                ? $this->checkout->transaction->primary_registration()
2670
-                : null;
2671
-        }
2672
-        if (! $primary_registrant instanceof EE_Registration) {
2673
-            EE_Error::add_error(
2674
-                sprintf(
2675
-                    esc_html__(
2676
-                        'Invalid information was received from the Off-Site Payment Processor and your Transaction details could not be retrieved from the database.%1$sPlease try again or contact %2$s for assistance.',
2677
-                        'event_espresso'
2678
-                    ),
2679
-                    '<br/>',
2680
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2681
-                ),
2682
-                __FILE__,
2683
-                __FUNCTION__,
2684
-                __LINE__
2685
-            );
2686
-            return;
2687
-        }
2688
-        // make sure transaction is not locked
2689
-        $this->checkout->transaction->unlock();
2690
-        wp_safe_redirect(
2691
-            add_query_arg(
2692
-                [
2693
-                    'e_reg_url_link' => $primary_registrant->reg_url_link(),
2694
-                ],
2695
-                $this->checkout->thank_you_page_url
2696
-            )
2697
-        );
2698
-        exit();
2699
-    }
2700
-
2701
-
2702
-    /**
2703
-     * @param EE_Offsite_Gateway $gateway
2704
-     * @return EE_Payment|null
2705
-     * @throws EE_Error
2706
-     * @throws ReflectionException
2707
-     */
2708
-    private function _process_off_site_payment(EE_Offsite_Gateway $gateway): ?EE_Payment
2709
-    {
2710
-        try {
2711
-            $request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2712
-            $request_data = $request->requestParams();
2713
-            // if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2714
-            $this->set_handle_IPN_in_this_request(
2715
-                $gateway->handle_IPN_in_this_request($request_data, false)
2716
-            );
2717
-            if ($this->handle_IPN_in_this_request()) {
2718
-                // get payment details and process results
2719
-                /** @var IpnHandler $payment_processor */
2720
-                $payment_processor = LoaderFactory::getShared(IpnHandler::class);
2721
-                $payment           = $payment_processor->processIPN(
2722
-                    $request_data,
2723
-                    $this->checkout->transaction,
2724
-                    $this->checkout->payment_method,
2725
-                    true,
2726
-                    false
2727
-                );
2728
-                // $payment_source = 'process_ipn';
2729
-            } else {
2730
-                $payment = $this->checkout->transaction->last_payment();
2731
-                // $payment_source = 'last_payment';
2732
-            }
2733
-        } catch (Exception $e) {
2734
-            // let's just eat the exception and try to move on using any previously set payment info
2735
-            $payment = $this->checkout->transaction->last_payment();
2736
-            // $payment_source = 'last_payment after Exception';
2737
-            // but if we STILL don't have a payment object
2738
-            if (! $payment instanceof EE_Payment) {
2739
-                // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2740
-                $this->_handle_payment_processor_exception($e);
2741
-            }
2742
-        }
2743
-        return $payment;
2744
-    }
2745
-
2746
-
2747
-    /**
2748
-     * just makes sure that the payment status gets updated correctly
2749
-     * so that an error isn't generated during payment validation
2750
-     *
2751
-     * @param EE_Payment|null $payment
2752
-     * @return EE_Payment|null
2753
-     * @throws EE_Error
2754
-     */
2755
-    private function _process_cancelled_payments(?EE_Payment $payment = null): ?EE_Payment
2756
-    {
2757
-        if (
2758
-            $payment instanceof EE_Payment
2759
-            && $this->request->requestParamIsSet('ee_cancel_payment')
2760
-            && $payment->status() === EEM_Payment::status_id_failed
2761
-        ) {
2762
-            $payment->set_status(EEM_Payment::status_id_cancelled);
2763
-        }
2764
-        return $payment;
2765
-    }
2766
-
2767
-
2768
-    /**
2769
-     * @return void
2770
-     * @throws EE_Error
2771
-     * @throws InvalidArgumentException
2772
-     * @throws ReflectionException
2773
-     * @throws InvalidDataTypeException
2774
-     * @throws InvalidInterfaceException
2775
-     */
2776
-    public function get_transaction_details_for_gateways()
2777
-    {
2778
-        $txn_details = [];
2779
-        // ya gotta make a choice man
2780
-        if (empty($this->checkout->selected_method_of_payment)) {
2781
-            $txn_details = [
2782
-                'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2783
-            ];
2784
-        }
2785
-        // get EE_Payment_Method object
2786
-        if (
2787
-            empty($txn_details)
2788
-            && ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2789
-        ) {
2790
-            $txn_details = [
2791
-                'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2792
-                'error'                      => esc_html__(
2793
-                    'A valid Payment Method could not be determined.',
2794
-                    'event_espresso'
2795
-                ),
2796
-            ];
2797
-        }
2798
-        if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2799
-            $return_url  = $this->_get_return_url($this->checkout->payment_method);
2800
-            $txn_details = [
2801
-                'TXN_ID'         => $this->checkout->transaction->ID(),
2802
-                'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2803
-                'TXN_total'      => $this->checkout->transaction->total(),
2804
-                'TXN_paid'       => $this->checkout->transaction->paid(),
2805
-                'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2806
-                'STS_ID'         => $this->checkout->transaction->status_ID(),
2807
-                'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2808
-                'payment_amount' => $this->checkout->amount_owing,
2809
-                'return_url'     => $return_url,
2810
-                'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2811
-                'notify_url'     => EE_Config::instance()->core->txn_page_url(
2812
-                    [
2813
-                        'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2814
-                        'ee_payment_method' => $this->checkout->payment_method->slug(),
2815
-                    ]
2816
-                ),
2817
-            ];
2818
-        }
2819
-        echo wp_json_encode($txn_details);
2820
-        exit();
2821
-    }
2822
-
2823
-
2824
-    /**
2825
-     * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2826
-     * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2827
-     * reg form, because if needed, it will be regenerated anyways
2828
-     *
2829
-     * @return array
2830
-     */
2831
-    public function __sleep()
2832
-    {
2833
-        // remove the reg form and the checkout
2834
-        return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2835
-    }
26
+	protected ?EE_Line_Item_Display $line_item_display = null;
27
+
28
+	protected bool $handle_IPN_in_this_request = false;
29
+
30
+	/**
31
+	 * @var EEM_Payment_Method|EEM_Base
32
+	 * @since 5.0.42
33
+	 */
34
+	protected EEM_Payment_Method $payment_method_model;
35
+
36
+
37
+	/**
38
+	 * set_hooks - for hooking into EE Core, other modules, etc
39
+	 *
40
+	 * @return    void
41
+	 */
42
+	public static function set_hooks()
43
+	{
44
+		add_filter(
45
+			'FHEE__SPCO__EE_Line_Item_Filter_Collection',
46
+			['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
47
+		);
48
+		add_action(
49
+			'wp_ajax_switch_spco_billing_form',
50
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
51
+		);
52
+		add_action(
53
+			'wp_ajax_nopriv_switch_spco_billing_form',
54
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
55
+		);
56
+		add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
57
+		add_action(
58
+			'wp_ajax_nopriv_save_payer_details',
59
+			['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
60
+		);
61
+		add_action(
62
+			'wp_ajax_get_transaction_details_for_gateways',
63
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
64
+		);
65
+		add_action(
66
+			'wp_ajax_nopriv_get_transaction_details_for_gateways',
67
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
68
+		);
69
+		add_filter(
70
+			'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
71
+			['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method']
72
+		);
73
+	}
74
+
75
+
76
+	/**
77
+	 * @throws EE_Error
78
+	 * @throws ReflectionException
79
+	 */
80
+	public static function switch_spco_billing_form()
81
+	{
82
+		EED_Single_Page_Checkout::process_ajax_request('switch_payment_method');
83
+	}
84
+
85
+
86
+	/**
87
+	 * @throws EE_Error
88
+	 * @throws ReflectionException
89
+	 */
90
+	public static function save_payer_details()
91
+	{
92
+		EED_Single_Page_Checkout::process_ajax_request('save_payer_details_via_ajax');
93
+	}
94
+
95
+
96
+	/**
97
+	 * @throws EE_Error
98
+	 * @throws ReflectionException
99
+	 */
100
+	public static function get_transaction_details()
101
+	{
102
+		EED_Single_Page_Checkout::process_ajax_request('get_transaction_details_for_gateways');
103
+	}
104
+
105
+
106
+	/**
107
+	 * @return array
108
+	 */
109
+	public static function bypass_recaptcha_for_load_payment_method(): array
110
+	{
111
+		return [
112
+			'EESID'  => EE_Registry::instance()->SSN->id(),
113
+			'step'   => 'payment_options',
114
+			'action' => 'spco_billing_form',
115
+		];
116
+	}
117
+
118
+
119
+	/**
120
+	 * @param EE_Checkout $checkout
121
+	 * @throws EE_Error
122
+	 * @throws ReflectionException
123
+	 */
124
+	public function __construct(EE_Checkout $checkout)
125
+	{
126
+		$this->request              = EED_Single_Page_Checkout::getRequest();
127
+		$this->_slug                = 'payment_options';
128
+		$this->_name                = esc_html__('Payment Options', 'event_espresso');
129
+		$this->_template            = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
130
+		$this->checkout             = $checkout;
131
+		$this->payment_method_model = EEM_Payment_Method::instance();
132
+		$this->_reset_success_message();
133
+		$this->set_instructions(
134
+			esc_html__(
135
+				'Please select a method of payment and provide any necessary billing information before proceeding.',
136
+				'event_espresso'
137
+			)
138
+		);
139
+	}
140
+
141
+
142
+	public function line_item_display(): ?EE_Line_Item_Display
143
+	{
144
+		return $this->line_item_display;
145
+	}
146
+
147
+
148
+	public function set_line_item_display(EE_Line_Item_Display $line_item_display)
149
+	{
150
+		$this->line_item_display = $line_item_display;
151
+	}
152
+
153
+
154
+	public function handle_IPN_in_this_request(): bool
155
+	{
156
+		return $this->handle_IPN_in_this_request;
157
+	}
158
+
159
+
160
+	public function set_handle_IPN_in_this_request(bool $handle_IPN_in_this_request)
161
+	{
162
+		$this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
163
+	}
164
+
165
+
166
+	/**
167
+	 * @return void
168
+	 */
169
+	public function translate_js_strings()
170
+	{
171
+		EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
172
+			'Please select a method of payment in order to continue.',
173
+			'event_espresso'
174
+		);
175
+		EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
176
+			'A valid method of payment could not be determined. Please refresh the page and try again.',
177
+			'event_espresso'
178
+		);
179
+		EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
180
+			'Forwarding to Secure Payment Provider.',
181
+			'event_espresso'
182
+		);
183
+	}
184
+
185
+
186
+	/**
187
+	 * @return void
188
+	 * @throws EE_Error
189
+	 * @throws ReflectionException
190
+	 */
191
+	public function enqueue_styles_and_scripts()
192
+	{
193
+		$transaction = $this->checkout->transaction;
194
+		// if the transaction isn't set or nothing is owed on it, don't enqueue any JS
195
+		if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
196
+			return;
197
+		}
198
+		foreach (
199
+			$this->payment_method_model->get_all_for_transaction(
200
+				$transaction,
201
+				EEM_Payment_Method::scope_cart
202
+			) as $payment_method
203
+		) {
204
+			$type_obj = $payment_method->type_obj();
205
+			if ($type_obj instanceof EE_PMT_Base) {
206
+				$billing_form = $type_obj->generate_new_billing_form($transaction);
207
+				if ($billing_form instanceof EE_Form_Section_Proper) {
208
+					$billing_form->enqueue_js();
209
+				}
210
+			}
211
+		}
212
+	}
213
+
214
+
215
+	/**
216
+	 * @return bool
217
+	 * @throws EE_Error
218
+	 * @throws InvalidArgumentException
219
+	 * @throws ReflectionException
220
+	 * @throws InvalidDataTypeException
221
+	 * @throws InvalidInterfaceException
222
+	 */
223
+	public function initialize_reg_step(): bool
224
+	{
225
+		// TODO: if /when we implement donations, then this will need overriding
226
+		if (
227
+			// don't need payment options for:
228
+			// registrations made via the admin
229
+			// completed transactions
230
+			// overpaid transactions
231
+			// $ 0.00 transactions(no payment required)
232
+			! $this->checkout->payment_required()
233
+			// but do NOT remove if current action being called belongs to this reg step
234
+			&& ! is_callable([$this, $this->checkout->action])
235
+			&& ! $this->completed()
236
+		) {
237
+			// and if so, then we no longer need the Payment Options step
238
+			if ($this->is_current_step()) {
239
+				$this->checkout->generate_reg_form = false;
240
+			}
241
+			$this->checkout->remove_reg_step($this->_slug);
242
+			// DEBUG LOG
243
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
244
+			return false;
245
+		}
246
+		// get all active payment methods
247
+		$this->checkout->available_payment_methods = $this->payment_method_model->get_all_for_transaction(
248
+			$this->checkout->transaction,
249
+			EEM_Payment_Method::scope_cart
250
+		);
251
+		$this->setDefaultPaymentMethod($this->checkout->available_payment_methods);
252
+		return true;
253
+	}
254
+
255
+
256
+	/**
257
+	 * @param array $payment_methods
258
+	 * @return void
259
+	 * @throws EE_Error
260
+	 * @throws ReflectionException
261
+	 * @since 5.0.42
262
+	 */
263
+	private function setDefaultPaymentMethod(array $payment_methods): void {
264
+		foreach ($payment_methods as $payment_method) {
265
+			if ($payment_method instanceof EE_Payment_Method && $payment_method->open_by_default()) {
266
+				$this->checkout->default_payment_method = $payment_method;
267
+				return;
268
+			}
269
+		}
270
+	}
271
+
272
+
273
+	/**
274
+	 * @return EE_Form_Section_Proper
275
+	 * @throws EE_Error
276
+	 * @throws InvalidArgumentException
277
+	 * @throws ReflectionException
278
+	 * @throws EntityNotFoundException
279
+	 * @throws InvalidDataTypeException
280
+	 * @throws InvalidInterfaceException
281
+	 * @throws InvalidStatusException
282
+	 */
283
+	public function generate_reg_form(): EE_Form_Section_Proper
284
+	{
285
+		// reset in case someone changes their mind
286
+		$this->_reset_selected_method_of_payment();
287
+		// set some defaults
288
+		$this->checkout->selected_method_of_payment = 'payments_closed';
289
+		$registrations_requiring_payment            = [];
290
+		$registrations_for_free_events              = [];
291
+		$registrations_requiring_pre_approval       = [];
292
+		$sold_out_events                            = [];
293
+		$insufficient_spaces_available              = [];
294
+		$no_payment_required                        = true;
295
+		// loop thru registrations to gather info
296
+		$registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
297
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
298
+			$registrations,
299
+			$this->checkout->revisit
300
+		);
301
+		foreach ($registrations as $REG_ID => $registration) {
302
+			/** @var $registration EE_Registration */
303
+			// Skip if the registration has been moved
304
+			if ($registration->wasMoved()) {
305
+				continue;
306
+			}
307
+			// has this registration lost it's space ?
308
+			if (isset($ejected_registrations[ $REG_ID ])) {
309
+				if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
310
+					$sold_out_events[ $registration->event()->ID() ] = $registration->event();
311
+				} else {
312
+					$insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
313
+				}
314
+				continue;
315
+			}
316
+			// event requires admin approval
317
+			if ($registration->status_ID() === RegStatus::AWAITING_REVIEW) {
318
+				// add event to list of events with pre-approval reg status
319
+				$registrations_requiring_pre_approval[ $REG_ID ] = $registration;
320
+				do_action(
321
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
322
+					$registration->event(),
323
+					$this
324
+				);
325
+				continue;
326
+			}
327
+			if (
328
+				$this->checkout->revisit
329
+				&& $registration->status_ID() !== RegStatus::APPROVED
330
+				&& (
331
+					$registration->event()->is_sold_out()
332
+					|| $registration->event()->is_sold_out(true)
333
+				)
334
+			) {
335
+				// add event to list of events that are sold out
336
+				$sold_out_events[ $registration->event()->ID() ] = $registration->event();
337
+				do_action(
338
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
339
+					$registration->event(),
340
+					$this
341
+				);
342
+				continue;
343
+			}
344
+			// are they allowed to pay now and is there monies owing?
345
+			if ($registration->owes_monies_and_can_pay()) {
346
+				$registrations_requiring_payment[ $REG_ID ] = $registration;
347
+				do_action(
348
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
349
+					$registration->event(),
350
+					$this
351
+				);
352
+			} elseif (
353
+				! $this->checkout->revisit
354
+					  && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
355
+					  && $registration->ticket()->is_free()
356
+			) {
357
+				$registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
358
+			}
359
+		}
360
+		$subsections = [];
361
+		// now decide which template to load
362
+		if (! empty($sold_out_events)) {
363
+			$subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
364
+		}
365
+		if (! empty($insufficient_spaces_available)) {
366
+			$subsections['insufficient_space'] = $this->_insufficient_spaces_available(
367
+				$insufficient_spaces_available
368
+			);
369
+		}
370
+		if (! empty($registrations_requiring_pre_approval)) {
371
+			$subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
372
+				$registrations_requiring_pre_approval
373
+			);
374
+		}
375
+		if (! empty($registrations_for_free_events)) {
376
+			$subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
377
+		}
378
+		if (! empty($registrations_requiring_payment)) {
379
+			if ($this->checkout->amount_owing > 0) {
380
+				// check for method_of_payment before setting up line items
381
+				// so that surcharges can be applied to the line items based on the selected method of payment
382
+				$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
383
+				do_action(
384
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__registrations_requiring_payment',
385
+					$this,
386
+					$registrations_requiring_payment
387
+				);
388
+				// autoload Line_Item_Display classes
389
+				EEH_Autoloader::register_line_item_filter_autoloaders();
390
+				$line_item_filter_processor = new EE_Line_Item_Filter_Processor(
391
+					apply_filters(
392
+						'FHEE__SPCO__EE_Line_Item_Filter_Collection',
393
+						new EE_Line_Item_Filter_Collection()
394
+					),
395
+					$this->checkout->cart->get_grand_total()
396
+				);
397
+				/** @var EE_Line_Item $filtered_line_item_tree */
398
+				$filtered_line_item_tree = $line_item_filter_processor->process();
399
+				EEH_Autoloader::register_line_item_display_autoloaders();
400
+				$this->set_line_item_display(new EE_Line_Item_Display('spco'));
401
+				$subsections['payment_options'] = $this->_display_payment_options(
402
+					$this->line_item_display->display_line_item(
403
+						$filtered_line_item_tree,
404
+						['registrations' => $registrations]
405
+					)
406
+				);
407
+				$this->checkout->amount_owing   = $filtered_line_item_tree->total();
408
+				$this->_apply_registration_payments_to_amount_owing($registrations);
409
+			}
410
+			$no_payment_required = false;
411
+		} else {
412
+			$this->_hide_reg_step_submit_button_if_revisit();
413
+		}
414
+		$this->_save_selected_method_of_payment();
415
+
416
+		$subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
417
+		$subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
418
+
419
+		return new EE_Form_Section_Proper(
420
+			[
421
+				'name'            => $this->reg_form_name(),
422
+				'html_id'         => $this->reg_form_name(),
423
+				'subsections'     => $subsections,
424
+				'layout_strategy' => new EE_No_Layout(),
425
+			]
426
+		);
427
+	}
428
+
429
+
430
+	/**
431
+	 * add line item filters required for this reg step
432
+	 * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
433
+	 *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
434
+	 *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
435
+	 *        payment options reg step, can apply these filters via the following: apply_filters(
436
+	 *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
437
+	 *        filter collection by passing that instead of instantiating a new collection
438
+	 *
439
+	 * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
440
+	 * @return EE_Line_Item_Filter_Collection
441
+	 * @throws EE_Error
442
+	 * @throws InvalidArgumentException
443
+	 * @throws ReflectionException
444
+	 * @throws EntityNotFoundException
445
+	 * @throws InvalidDataTypeException
446
+	 * @throws InvalidInterfaceException
447
+	 * @throws InvalidStatusException
448
+	 */
449
+	public static function add_spco_line_item_filters(
450
+		EE_Line_Item_Filter_Collection $line_item_filter_collection
451
+	): EE_Line_Item_Filter_Collection {
452
+		if (! EE_Registry::instance()->SSN instanceof EE_Session) {
453
+			return $line_item_filter_collection;
454
+		}
455
+		if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
456
+			return $line_item_filter_collection;
457
+		}
458
+		if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
459
+			return $line_item_filter_collection;
460
+		}
461
+		$line_item_filter_collection->add(
462
+			new EE_Billable_Line_Item_Filter(
463
+				EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
464
+					EE_Registry::instance()->SSN->checkout()->transaction->registrations(
465
+						EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
466
+					)
467
+				)
468
+			)
469
+		);
470
+		$line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
471
+		return $line_item_filter_collection;
472
+	}
473
+
474
+
475
+	/**
476
+	 * if a registrant has lost their potential space at an event due to lack of payment,
477
+	 * then this method removes them from the list of registrations being paid for during this request
478
+	 *
479
+	 * @param EE_Registration[] $registrations
480
+	 * @return EE_Registration[]
481
+	 * @throws EE_Error
482
+	 * @throws InvalidArgumentException
483
+	 * @throws ReflectionException
484
+	 * @throws EntityNotFoundException
485
+	 * @throws InvalidDataTypeException
486
+	 * @throws InvalidInterfaceException
487
+	 * @throws InvalidStatusException
488
+	 */
489
+	public static function remove_ejected_registrations(array $registrations): array
490
+	{
491
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
492
+			$registrations,
493
+			EE_Registry::instance()->SSN->checkout()->revisit
494
+		);
495
+		foreach ($registrations as $REG_ID => $registration) {
496
+			// has this registration lost it's space ?
497
+			if (isset($ejected_registrations[ $REG_ID ])) {
498
+				unset($registrations[ $REG_ID ]);
499
+			}
500
+		}
501
+		return $registrations;
502
+	}
503
+
504
+
505
+	/**
506
+	 * If a registrant chooses an offline payment method like Invoice,
507
+	 * then no space is reserved for them at the event until they fully pay fo that site
508
+	 * (unless the event's default reg status is set to APPROVED)
509
+	 * if a registrant then later returns to pay, but the number of spaces available has been reduced due to sales,
510
+	 * then this method will determine which registrations have lost the ability to complete the reg process.
511
+	 *
512
+	 * @param EE_Registration[] $registrations
513
+	 * @param bool              $revisit
514
+	 * @return array
515
+	 * @throws EE_Error
516
+	 * @throws InvalidArgumentException
517
+	 * @throws ReflectionException
518
+	 * @throws EntityNotFoundException
519
+	 * @throws InvalidDataTypeException
520
+	 * @throws InvalidInterfaceException
521
+	 * @throws InvalidStatusException
522
+	 */
523
+	public static function find_registrations_that_lost_their_space(array $registrations, bool $revisit = false): array
524
+	{
525
+		// registrations per event
526
+		$event_reg_count = [];
527
+		// spaces left per event
528
+		$event_spaces_remaining = [];
529
+		// tickets left sorted by ID
530
+		$tickets_remaining = [];
531
+		// registrations that have lost their space
532
+		$ejected_registrations = [];
533
+		foreach ($registrations as $REG_ID => $registration) {
534
+			if (
535
+				$registration->status_ID() === RegStatus::APPROVED
536
+				|| apply_filters(
537
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__find_registrations_that_lost_their_space__allow_reg_payment',
538
+					false,
539
+					$registration,
540
+					$revisit
541
+				)
542
+			) {
543
+				continue;
544
+			}
545
+			$EVT_ID = $registration->event_ID();
546
+			$ticket = $registration->ticket();
547
+			if (! isset($tickets_remaining[ $ticket->ID() ])) {
548
+				$tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
549
+			}
550
+			if ($tickets_remaining[ $ticket->ID() ] > 0) {
551
+				if (! isset($event_reg_count[ $EVT_ID ])) {
552
+					$event_reg_count[ $EVT_ID ] = 0;
553
+				}
554
+				$event_reg_count[ $EVT_ID ]++;
555
+				if (! isset($event_spaces_remaining[ $EVT_ID ])) {
556
+					$event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
557
+				}
558
+			}
559
+			if (
560
+				$revisit
561
+				&& ($tickets_remaining[ $ticket->ID() ] === 0
562
+					|| $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
563
+				)
564
+			) {
565
+				$ejected_registrations[ $REG_ID ] = $registration->event();
566
+				if ($registration->status_ID() !== RegStatus::WAIT_LIST) {
567
+					/** @type EE_Registration_Processor $registration_processor */
568
+					$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
569
+					// at this point, we should have enough details about the registrant to consider the registration
570
+					// NOT incomplete
571
+					$registration_processor->manually_update_registration_status(
572
+						$registration,
573
+						RegStatus::WAIT_LIST
574
+					);
575
+				}
576
+			}
577
+		}
578
+		return $ejected_registrations;
579
+	}
580
+
581
+
582
+	/**
583
+	 * removes the HTML for the reg step submit button
584
+	 * by replacing it with an empty string via filter callback
585
+	 *
586
+	 * @return void
587
+	 */
588
+	protected function _hide_reg_step_submit_button_if_revisit()
589
+	{
590
+		if ($this->checkout->revisit) {
591
+			add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
592
+		}
593
+	}
594
+
595
+
596
+	/**
597
+	 * displays notices regarding events that have sold out since the registrant first signed up
598
+	 *
599
+	 * @param EE_Event[] $sold_out_events_array
600
+	 * @return EE_Form_Section_Proper
601
+	 * @throws EE_Error
602
+	 * @throws ReflectionException
603
+	 */
604
+	private function _sold_out_events(array $sold_out_events_array = []): EE_Form_Section_Proper
605
+	{
606
+		// set some defaults
607
+		$this->checkout->selected_method_of_payment = 'events_sold_out';
608
+		$sold_out_events                            = '';
609
+		foreach ($sold_out_events_array as $sold_out_event) {
610
+			$sold_out_events .= EEH_HTML::li(
611
+				EEH_HTML::span(
612
+					'  ' . $sold_out_event->name(),
613
+					'',
614
+					'dashicons dashicons-marker ee-icon-size-16 pink-text'
615
+				)
616
+			);
617
+		}
618
+		return new EE_Form_Section_Proper(
619
+			[
620
+				'layout_strategy' => new EE_Template_Layout(
621
+					[
622
+						'layout_template_file' => SPCO_REG_STEPS_PATH
623
+												  . $this->_slug
624
+												  . '/sold_out_events.template.php',
625
+						'template_args'        => apply_filters(
626
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
627
+							[
628
+								'sold_out_events'     => $sold_out_events,
629
+								'sold_out_events_msg' => apply_filters(
630
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
631
+									sprintf(
632
+										esc_html__(
633
+											'It appears that the event you were about to make a payment for has sold out since you first registered. If you have already made a partial payment towards this event, please contact the event administrator for a refund.%3$s%3$s%1$sPlease note that availability can change at any time due to cancellations, so please check back again later if registration for this event(s) is important to you.%2$s',
634
+											'event_espresso'
635
+										),
636
+										'<strong>',
637
+										'</strong>',
638
+										'<br />'
639
+									)
640
+								),
641
+							]
642
+						),
643
+					]
644
+				),
645
+			]
646
+		);
647
+	}
648
+
649
+
650
+	/**
651
+	 * displays notices regarding events that do not have enough remaining spaces
652
+	 * to satisfy the current number of registrations looking to pay
653
+	 *
654
+	 * @param EE_Event[] $insufficient_spaces_events_array
655
+	 * @return EE_Form_Section_Proper
656
+	 * @throws EE_Error
657
+	 * @throws ReflectionException
658
+	 */
659
+	private function _insufficient_spaces_available(
660
+		array $insufficient_spaces_events_array = []
661
+	): EE_Form_Section_Proper {
662
+		// set some defaults
663
+		$this->checkout->selected_method_of_payment = 'invoice';
664
+		$insufficient_space_events                  = '';
665
+		foreach ($insufficient_spaces_events_array as $event) {
666
+			if ($event instanceof EE_Event) {
667
+				$insufficient_space_events .= EEH_HTML::li(
668
+					EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
669
+				);
670
+			}
671
+		}
672
+		return new EE_Form_Section_Proper(
673
+			[
674
+				'subsections'     => [
675
+					'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
676
+					'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
677
+				],
678
+				'layout_strategy' => new EE_Template_Layout(
679
+					[
680
+						'layout_template_file' => SPCO_REG_STEPS_PATH
681
+												  . $this->_slug
682
+												  . '/sold_out_events.template.php',
683
+						'template_args'        => apply_filters(
684
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
685
+							[
686
+								'sold_out_events'     => $insufficient_space_events,
687
+								'sold_out_events_msg' => apply_filters(
688
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
689
+									esc_html__(
690
+										'It appears that the event you were about to make a payment for has sold additional tickets since you first registered, and there are no longer enough spaces left to accommodate your selections. You may continue to pay and secure the available space(s) remaining, or simply cancel if you no longer wish to purchase. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
691
+										'event_espresso'
692
+									)
693
+								),
694
+							]
695
+						),
696
+					]
697
+				),
698
+			]
699
+		);
700
+	}
701
+
702
+
703
+	/**
704
+	 * @param array $registrations_requiring_pre_approval
705
+	 * @return EE_Form_Section_Proper
706
+	 * @throws EE_Error
707
+	 * @throws EntityNotFoundException
708
+	 * @throws ReflectionException
709
+	 */
710
+	private function _registrations_requiring_pre_approval(
711
+		array $registrations_requiring_pre_approval = []
712
+	): EE_Form_Section_Proper {
713
+		$events_requiring_pre_approval = [];
714
+		foreach ($registrations_requiring_pre_approval as $registration) {
715
+			if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
716
+				$events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
717
+					EEH_HTML::span(
718
+						'',
719
+						'',
720
+						'dashicons dashicons-marker ee-icon-size-16 orange-text'
721
+					)
722
+					. EEH_HTML::span($registration->event()->name(), '', 'orange-text')
723
+				);
724
+			}
725
+		}
726
+		return new EE_Form_Section_Proper(
727
+			[
728
+				'layout_strategy' => new EE_Template_Layout(
729
+					[
730
+						'layout_template_file' => SPCO_REG_STEPS_PATH
731
+												  . $this->_slug
732
+												  . '/events_requiring_pre_approval.template.php', // layout_template
733
+						'template_args'        => apply_filters(
734
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
735
+							[
736
+								'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
737
+								'events_requiring_pre_approval_msg' => apply_filters(
738
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
739
+									esc_html__(
740
+										'The following events do not require payment at this time and will not be billed during this transaction. Billing will only occur after the attendee has been approved by the event organizer. You will be notified when your registration has been processed. If this is a free event, then no billing will occur.',
741
+										'event_espresso'
742
+									)
743
+								),
744
+							]
745
+						),
746
+					]
747
+				),
748
+			]
749
+		);
750
+	}
751
+
752
+
753
+	/**
754
+	 * @param EE_Event[] $registrations_for_free_events
755
+	 * @return EE_Form_Section_Proper
756
+	 * @throws EE_Error
757
+	 */
758
+	private function _no_payment_required(array $registrations_for_free_events = []): EE_Form_Section_Proper
759
+	{
760
+		// set some defaults
761
+		$this->checkout->selected_method_of_payment = 'no_payment_required';
762
+		// generate no_payment_required form
763
+		return new EE_Form_Section_Proper(
764
+			[
765
+				'layout_strategy' => new EE_Template_Layout(
766
+					[
767
+						'layout_template_file' => SPCO_REG_STEPS_PATH
768
+												  . $this->_slug
769
+												  . '/no_payment_required.template.php', // layout_template
770
+						'template_args'        => apply_filters(
771
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
772
+							[
773
+								'revisit'                       => $this->checkout->revisit,
774
+								'registrations'                 => [],
775
+								'ticket_count'                  => [],
776
+								'registrations_for_free_events' => $registrations_for_free_events,
777
+								'no_payment_required_msg'       => EEH_HTML::p(
778
+									esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
779
+								),
780
+							]
781
+						),
782
+					]
783
+				),
784
+			]
785
+		);
786
+	}
787
+
788
+
789
+	/**
790
+	 * @param string $transaction_details HTML from EE_SPCO_Line_Item_Display_Strategy
791
+	 * @return EE_Form_Section_Proper
792
+	 * @throws EE_Error
793
+	 * @throws ReflectionException
794
+	 */
795
+	private function _display_payment_options(string $transaction_details = ''): EE_Form_Section_Proper
796
+	{
797
+		// build payment options form
798
+		return apply_filters(
799
+			'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
800
+			new EE_Form_Section_Proper(
801
+				[
802
+					'subsections'     => [
803
+						'before_payment_options' => apply_filters(
804
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
805
+							new EE_Form_Section_Proper(
806
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
807
+							)
808
+						),
809
+						'payment_options'        => $this->_setup_payment_options(),
810
+						'after_payment_options'  => apply_filters(
811
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
812
+							new EE_Form_Section_Proper(
813
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
814
+							)
815
+						),
816
+					],
817
+					'layout_strategy' => new EE_Template_Layout(
818
+						[
819
+							'layout_template_file' => $this->_template,
820
+							'template_args'        => apply_filters(
821
+								'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
822
+								[
823
+									'reg_count'                 => $this->line_item_display->total_items(),
824
+									'transaction_details'       => $transaction_details,
825
+									'available_payment_methods' => [],
826
+								]
827
+							),
828
+						]
829
+					),
830
+				]
831
+			)
832
+		);
833
+	}
834
+
835
+
836
+	/**
837
+	 * @param bool $no_payment_required
838
+	 * @return EE_Form_Section_Proper
839
+	 * @throws EE_Error
840
+	 * @throws ReflectionException
841
+	 */
842
+	private function _extra_hidden_inputs(bool $no_payment_required = true): EE_Form_Section_Proper
843
+	{
844
+		return new EE_Form_Section_Proper(
845
+			[
846
+				'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
847
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
848
+				'subsections'     => [
849
+					'spco_no_payment_required' => new EE_Hidden_Input(
850
+						[
851
+							'normalization_strategy' => new EE_Boolean_Normalization(),
852
+							'html_name'              => 'spco_no_payment_required',
853
+							'html_id'                => 'spco-no-payment-required-payment_options',
854
+							'default'                => $no_payment_required,
855
+						]
856
+					),
857
+					'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
858
+						[
859
+							'normalization_strategy' => new EE_Int_Normalization(),
860
+							'html_name'              => 'spco_transaction_id',
861
+							'html_id'                => 'spco-transaction-id',
862
+							'default'                => $this->checkout->transaction->ID(),
863
+						]
864
+					),
865
+				],
866
+			]
867
+		);
868
+	}
869
+
870
+
871
+	/**
872
+	 * @param array $registrations
873
+	 * @throws EE_Error
874
+	 * @throws ReflectionException
875
+	 */
876
+	protected function _apply_registration_payments_to_amount_owing(array $registrations)
877
+	{
878
+		$payments = [];
879
+		foreach ($registrations as $registration) {
880
+			if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
881
+				$payments += $registration->registration_payments();
882
+			}
883
+		}
884
+		if (! empty($payments)) {
885
+			foreach ($payments as $payment) {
886
+				if ($payment instanceof EE_Registration_Payment) {
887
+					$this->checkout->amount_owing -= $payment->amount();
888
+				}
889
+			}
890
+		}
891
+	}
892
+
893
+
894
+	/**
895
+	 * @param bool $force_reset
896
+	 * @return void
897
+	 */
898
+	private function _reset_selected_method_of_payment(bool $force_reset = false)
899
+	{
900
+		/** @var RequestInterface $request */
901
+		$request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
902
+		$reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
903
+		if ($reset_payment_method) {
904
+			$this->checkout->selected_method_of_payment = null;
905
+			$this->checkout->payment_method             = null;
906
+			$this->checkout->billing_form               = null;
907
+			$this->_save_selected_method_of_payment();
908
+		}
909
+	}
910
+
911
+
912
+	/**
913
+	 * stores the selected_method_of_payment in the session
914
+	 * so that it's available for all subsequent requests including AJAX
915
+	 *
916
+	 * @param string $selected_method_of_payment
917
+	 * @return void
918
+	 */
919
+	private function _save_selected_method_of_payment(string $selected_method_of_payment = '')
920
+	{
921
+		$selected_method_of_payment = ! empty($selected_method_of_payment)
922
+			? $selected_method_of_payment
923
+			: $this->checkout->selected_method_of_payment;
924
+		EE_Registry::instance()->SSN->set_session_data(
925
+			['selected_method_of_payment' => $selected_method_of_payment]
926
+		);
927
+	}
928
+
929
+
930
+	/**
931
+	 * @return EE_Form_Section_Proper
932
+	 * @throws EE_Error
933
+	 * @throws ReflectionException
934
+	 */
935
+	public function _setup_payment_options(): EE_Form_Section_Proper
936
+	{
937
+		// load payment method classes
938
+		$this->checkout->available_payment_methods = $this->_get_available_payment_methods();
939
+		if (empty($this->checkout->available_payment_methods)) {
940
+			EE_Error::add_error(
941
+				apply_filters(
942
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
943
+					sprintf(
944
+						esc_html__(
945
+							'Sorry, you cannot complete your purchase because a payment method is not active.%1$s Please contact %2$s for assistance and provide a description of the problem.',
946
+							'event_espresso'
947
+						),
948
+						'<br>',
949
+						EE_Registry::instance()->CFG->organization->get_pretty('email')
950
+					)
951
+				),
952
+				__FILE__,
953
+				__FUNCTION__,
954
+				__LINE__
955
+			);
956
+		}
957
+		// switch up header depending on number of available payment methods
958
+		$payment_method_header     = count($this->checkout->available_payment_methods) > 1
959
+			? apply_filters(
960
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
961
+				esc_html__('Please Select Your Method of Payment', 'event_espresso')
962
+			)
963
+			: apply_filters(
964
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
965
+				esc_html__('Method of Payment', 'event_espresso')
966
+			);
967
+		$available_payment_methods = [
968
+			// display the "Payment Method" header
969
+			'payment_method_header' => new EE_Form_Section_HTML(
970
+				apply_filters(
971
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
972
+					EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
973
+					$payment_method_header
974
+				)
975
+			),
976
+		];
977
+		// the list of actual payment methods ( invoice, PayPal, etc ) in a  ( slug => HTML )  format
978
+		$available_payment_method_options = [];
979
+		$default_payment_method_option    = [];
980
+		// additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
981
+		$payment_methods_billing_info = [
982
+			new EE_Form_Section_HTML(
983
+				EEH_HTML::div('<br />', '', '', 'clear:both;')
984
+			),
985
+		];
986
+		// loop through payment methods
987
+		foreach ($this->checkout->available_payment_methods as $payment_method) {
988
+			if (! $payment_method instanceof EE_Payment_Method) {
989
+				continue;
990
+			}
991
+
992
+			$payment_method_button = EEH_HTML::img(
993
+				$payment_method->button_url(),
994
+				$payment_method->name(),
995
+				'spco-payment-method-' . $payment_method->slug() . '-btn-img',
996
+				'spco-payment-method-btn-img'
997
+			);
998
+			// check if any payment methods are set as default
999
+			// if payment method is already selected
1000
+			// OR nothing is selected and this payment method is the default
1001
+			if (
1002
+				($this->checkout->selected_method_of_payment === $payment_method->slug())
1003
+				|| (
1004
+					! $this->checkout->selected_method_of_payment
1005
+					&& $payment_method->open_by_default()
1006
+				)
1007
+			) {
1008
+				$this->checkout->selected_method_of_payment = $payment_method->slug();
1009
+				$this->_save_selected_method_of_payment();
1010
+				$default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1011
+			} else {
1012
+				$available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1013
+			}
1014
+			$payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1015
+				$this->_payment_method_billing_info(
1016
+					$payment_method
1017
+				);
1018
+		}
1019
+		// prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1020
+		// of PMs
1021
+		$available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1022
+		// now generate the actual form  inputs
1023
+		$available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1024
+			$available_payment_method_options
1025
+		);
1026
+		$available_payment_methods                              += $payment_methods_billing_info;
1027
+		// build the available payment methods form
1028
+		return new EE_Form_Section_Proper(
1029
+			[
1030
+				'html_id'         => 'spco-available-methods-of-payment-dv',
1031
+				'subsections'     => $available_payment_methods,
1032
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1033
+			]
1034
+		);
1035
+	}
1036
+
1037
+
1038
+	/**
1039
+	 * @return EE_Payment_Method[]
1040
+	 * @throws EE_Error
1041
+	 * @throws ReflectionException
1042
+	 */
1043
+	protected function _get_available_payment_methods(): array
1044
+	{
1045
+		if (! empty($this->checkout->available_payment_methods)) {
1046
+			return $this->checkout->available_payment_methods;
1047
+		}
1048
+		$available_payment_methods = [];
1049
+		// get all active payment methods
1050
+		$payment_methods = $this->payment_method_model->get_all_for_transaction(
1051
+			$this->checkout->transaction,
1052
+			EEM_Payment_Method::scope_cart
1053
+		);
1054
+		foreach ($payment_methods as $payment_method) {
1055
+			if ($payment_method instanceof EE_Payment_Method) {
1056
+				$available_payment_methods[ $payment_method->slug() ] = $payment_method;
1057
+			}
1058
+		}
1059
+		return $available_payment_methods;
1060
+	}
1061
+
1062
+
1063
+	/**
1064
+	 * @param array $available_payment_method_options
1065
+	 * @return EE_Form_Section_Proper
1066
+	 * @throws EE_Error
1067
+	 * @throws EE_Error
1068
+	 */
1069
+	private function _available_payment_method_inputs(
1070
+		array $available_payment_method_options = []
1071
+	): EE_Form_Section_Proper {
1072
+		// generate inputs
1073
+		return new EE_Form_Section_Proper(
1074
+			[
1075
+				'html_id'         => 'ee-available-payment-method-inputs',
1076
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1077
+				'subsections'     => [
1078
+					'' => new EE_Radio_Button_Input(
1079
+						$available_payment_method_options,
1080
+						[
1081
+							'html_name'          => 'selected_method_of_payment',
1082
+							'html_class'         => 'spco-payment-method',
1083
+							'default'            => $this->checkout->selected_method_of_payment,
1084
+							'label_size'         => 11,
1085
+							'enforce_label_size' => true,
1086
+						]
1087
+					),
1088
+				],
1089
+			]
1090
+		);
1091
+	}
1092
+
1093
+
1094
+	/**
1095
+	 * @param EE_Payment_Method $payment_method
1096
+	 * @return EE_Form_Section_Proper
1097
+	 * @throws EE_Error
1098
+	 * @throws ReflectionException
1099
+	 */
1100
+	private function _payment_method_billing_info(EE_Payment_Method $payment_method): EE_Form_Section_Proper
1101
+	{
1102
+		$currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1103
+		// generate the billing form for payment method
1104
+		$billing_form                 = $currently_selected
1105
+			? $this->_get_billing_form_for_payment_method($payment_method)
1106
+			: new EE_Form_Section_HTML();
1107
+		$this->checkout->billing_form = $currently_selected
1108
+			? $billing_form
1109
+			: $this->checkout->billing_form;
1110
+		// it's all in the details
1111
+		$info_html = EEH_HTML::h3(
1112
+			esc_html__('Important information regarding your payment', 'event_espresso'),
1113
+			'',
1114
+			'spco-payment-method-hdr'
1115
+		);
1116
+		// add some info regarding the step, either from what's saved in the admin,
1117
+		// or a default string depending on whether the PM has a billing form or not
1118
+		if ($payment_method->description()) {
1119
+			$payment_method_info = $payment_method->description();
1120
+		} elseif ($billing_form instanceof EE_Billing_Info_Form) {
1121
+			$payment_method_info = sprintf(
1122
+				esc_html__(
1123
+					'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1124
+					'event_espresso'
1125
+				),
1126
+				$this->submit_button_text()
1127
+			);
1128
+		} else {
1129
+			$payment_method_info = sprintf(
1130
+				esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1131
+				$this->submit_button_text()
1132
+			);
1133
+		}
1134
+		$info_html .= EEH_HTML::div(
1135
+			apply_filters(
1136
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1137
+				$payment_method_info
1138
+			),
1139
+			'',
1140
+			'spco-payment-method-desc ee-attention'
1141
+		);
1142
+		return new EE_Form_Section_Proper(
1143
+			[
1144
+				'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1145
+				'html_class'      => 'spco-payment-method-info-dv',
1146
+				// only display the selected or default PM
1147
+				'html_style'      => $currently_selected ? '' : 'display:none;',
1148
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1149
+				'subsections'     => [
1150
+					'info'         => new EE_Form_Section_HTML($info_html),
1151
+					'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1152
+				],
1153
+			]
1154
+		);
1155
+	}
1156
+
1157
+
1158
+	/**
1159
+	 * @return bool
1160
+	 * @throws EE_Error
1161
+	 * @throws InvalidArgumentException
1162
+	 * @throws ReflectionException
1163
+	 * @throws InvalidDataTypeException
1164
+	 * @throws InvalidInterfaceException
1165
+	 */
1166
+	public function get_billing_form_html_for_payment_method(): bool
1167
+	{
1168
+		// how have they chosen to pay?
1169
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1170
+		$this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1171
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1172
+			return false;
1173
+		}
1174
+		if (
1175
+			apply_filters(
1176
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1177
+				false
1178
+			)
1179
+		) {
1180
+			EE_Error::add_success(
1181
+				apply_filters(
1182
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1183
+					sprintf(
1184
+						esc_html__(
1185
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1186
+							'event_espresso'
1187
+						),
1188
+						$this->checkout->payment_method->name()
1189
+					)
1190
+				)
1191
+			);
1192
+		}
1193
+		// now generate billing form for selected method of payment
1194
+		$payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1195
+		// fill form with attendee info if applicable
1196
+		if (
1197
+			$payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1198
+			&& $this->checkout->transaction_has_primary_registrant()
1199
+		) {
1200
+			$payment_method_billing_form->populate_from_attendee(
1201
+				$this->checkout->transaction->primary_registration()->attendee()
1202
+			);
1203
+		}
1204
+		// and debug content
1205
+		if (
1206
+			$payment_method_billing_form instanceof EE_Billing_Info_Form
1207
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1208
+		) {
1209
+			$payment_method_billing_form =
1210
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1211
+					$payment_method_billing_form
1212
+				);
1213
+		}
1214
+		$billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1215
+			? $payment_method_billing_form->get_html()
1216
+			: '';
1217
+		$this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1218
+		// localize validation rules for main form
1219
+		$this->checkout->current_step->reg_form->localize_validation_rules();
1220
+		$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1221
+		return true;
1222
+	}
1223
+
1224
+
1225
+	/**
1226
+	 * @param EE_Payment_Method $payment_method
1227
+	 * @return EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form|EE_Form_Section_HTML
1228
+	 * @throws EE_Error
1229
+	 * @throws ReflectionException
1230
+	 */
1231
+	private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1232
+	{
1233
+		$billing_form = $payment_method->type_obj()->billing_form(
1234
+			$this->checkout->transaction,
1235
+			['amount_owing' => $this->checkout->amount_owing]
1236
+		);
1237
+		if ($billing_form instanceof EE_Billing_Info_Form) {
1238
+			if (
1239
+				apply_filters(
1240
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1241
+					false
1242
+				)
1243
+				&& $this->request->requestParamIsSet('payment_method')
1244
+			) {
1245
+				EE_Error::add_success(
1246
+					apply_filters(
1247
+						'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1248
+						sprintf(
1249
+							esc_html__(
1250
+								'You have selected "%s" as your method of payment. Please note the important payment information below.',
1251
+								'event_espresso'
1252
+							),
1253
+							$payment_method->name()
1254
+						)
1255
+					)
1256
+				);
1257
+			}
1258
+			return apply_filters(
1259
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1260
+				$billing_form,
1261
+				$payment_method
1262
+			);
1263
+		}
1264
+		// no actual billing form, so return empty HTML form section
1265
+		return new EE_Form_Section_HTML();
1266
+	}
1267
+
1268
+
1269
+	/**
1270
+	 * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1271
+	 *                          is not found in the incoming request
1272
+	 * @param string  $request_param
1273
+	 * @return NULL|string
1274
+	 * @throws EE_Error
1275
+	 * @throws ReflectionException
1276
+	 */
1277
+	private function _get_selected_method_of_payment(
1278
+		bool $required = false,
1279
+		string $request_param = 'selected_method_of_payment'
1280
+	): ?string {
1281
+		// is selected_method_of_payment set in the request ?
1282
+		$selected_method_of_payment = $this->request->getRequestParam($request_param);
1283
+		if ($selected_method_of_payment) {
1284
+			// sanitize it
1285
+			$selected_method_of_payment = is_array($selected_method_of_payment)
1286
+				? array_shift($selected_method_of_payment)
1287
+				: $selected_method_of_payment;
1288
+			$selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1289
+			// store it in the session so that it's available for all subsequent requests including AJAX
1290
+			$this->_save_selected_method_of_payment($selected_method_of_payment);
1291
+		} else {
1292
+			// or is it set in the session ?
1293
+			$selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1294
+				'selected_method_of_payment'
1295
+			);
1296
+		}
1297
+		if (
1298
+			empty($selected_method_of_payment)
1299
+			&& $this->checkout->default_payment_method instanceof EE_Payment_Method
1300
+		) {
1301
+			$selected_method_of_payment = $this->checkout->default_payment_method->slug();
1302
+		}
1303
+		// still no payment method?
1304
+		if (empty($selected_method_of_payment) && $required) {
1305
+			EE_Error::add_error(
1306
+				sprintf(
1307
+					esc_html__(
1308
+						'The selected method of payment could not be determined.%sPlease ensure that you have selected one before proceeding.%sIf you continue to experience difficulties, then refresh your browser and try again, or contact %s for assistance.',
1309
+						'event_espresso'
1310
+					),
1311
+					'<br/>',
1312
+					'<br/>',
1313
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
1314
+				),
1315
+				__FILE__,
1316
+				__FUNCTION__,
1317
+				__LINE__
1318
+			);
1319
+			return null;
1320
+		}
1321
+		return $selected_method_of_payment;
1322
+	}
1323
+
1324
+
1325
+
1326
+
1327
+
1328
+
1329
+	/********************************************************************************************************/
1330
+	/***********************************  SWITCH PAYMENT METHOD  ************************************/
1331
+	/********************************************************************************************************/
1332
+
1333
+
1334
+	/**
1335
+	 * @return bool
1336
+	 * @throws EE_Error
1337
+	 * @throws ReflectionException
1338
+	 */
1339
+	public function switch_payment_method()
1340
+	{
1341
+		if (! $this->_verify_payment_method_is_set()) {
1342
+			return false;
1343
+		}
1344
+		if (
1345
+			apply_filters(
1346
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1347
+				false
1348
+			)
1349
+		) {
1350
+			EE_Error::add_success(
1351
+				apply_filters(
1352
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1353
+					sprintf(
1354
+						esc_html__(
1355
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1356
+							'event_espresso'
1357
+						),
1358
+						$this->checkout->payment_method->name()
1359
+					)
1360
+				)
1361
+			);
1362
+		}
1363
+		// generate billing form for selected method of payment if it hasn't been done already
1364
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1365
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1366
+				$this->checkout->payment_method
1367
+			);
1368
+		}
1369
+		// fill form with attendee info if applicable
1370
+		if (
1371
+			apply_filters(
1372
+				'FHEE__populate_billing_form_fields_from_attendee',
1373
+				(
1374
+					$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1375
+					&& $this->checkout->transaction_has_primary_registrant()
1376
+				),
1377
+				$this->checkout->billing_form,
1378
+				$this->checkout->transaction
1379
+			)
1380
+		) {
1381
+			$this->checkout->billing_form->populate_from_attendee(
1382
+				$this->checkout->transaction->primary_registration()->attendee()
1383
+			);
1384
+		}
1385
+		// and debug content
1386
+		if (
1387
+			$this->checkout->billing_form instanceof EE_Billing_Info_Form
1388
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1389
+		) {
1390
+			$this->checkout->billing_form =
1391
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1392
+					$this->checkout->billing_form
1393
+				);
1394
+		}
1395
+		// get HTML and validation rules for form
1396
+		if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1397
+			$this->checkout->json_response->set_return_data(
1398
+				['payment_method_info' => $this->checkout->billing_form->get_html()]
1399
+			);
1400
+			// localize validation rules for main form
1401
+			$this->checkout->billing_form->localize_validation_rules(true);
1402
+			$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1403
+		} else {
1404
+			$this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1405
+		}
1406
+		// prevents advancement to next step
1407
+		$this->checkout->continue_reg = false;
1408
+		return true;
1409
+	}
1410
+
1411
+
1412
+	/**
1413
+	 * @return bool
1414
+	 * @throws EE_Error
1415
+	 * @throws InvalidArgumentException
1416
+	 * @throws ReflectionException
1417
+	 * @throws InvalidDataTypeException
1418
+	 * @throws InvalidInterfaceException
1419
+	 */
1420
+	protected function _verify_payment_method_is_set(): bool
1421
+	{
1422
+		// generate billing form for selected method of payment if it hasn't been done already
1423
+		if (empty($this->checkout->selected_method_of_payment)) {
1424
+			// how have they chosen to pay?
1425
+			$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1426
+		} else {
1427
+			// choose your own adventure based on method_of_payment
1428
+			switch ($this->checkout->selected_method_of_payment) {
1429
+				case 'events_sold_out':
1430
+					EE_Error::add_attention(
1431
+						apply_filters(
1432
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1433
+							esc_html__(
1434
+								'It appears that the event you were about to make a payment for has sold out since this form first loaded. Please contact the event administrator if you believe this is an error.',
1435
+								'event_espresso'
1436
+							)
1437
+						),
1438
+						__FILE__,
1439
+						__FUNCTION__,
1440
+						__LINE__
1441
+					);
1442
+					return false;
1443
+				case 'payments_closed':
1444
+					EE_Error::add_attention(
1445
+						apply_filters(
1446
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1447
+							esc_html__(
1448
+								'It appears that the event you were about to make a payment for is not accepting payments at this time. Please contact the event administrator if you believe this is an error.',
1449
+								'event_espresso'
1450
+							)
1451
+						),
1452
+						__FILE__,
1453
+						__FUNCTION__,
1454
+						__LINE__
1455
+					);
1456
+					return false;
1457
+				case 'no_payment_required':
1458
+					EE_Error::add_attention(
1459
+						apply_filters(
1460
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1461
+							esc_html__(
1462
+								'It appears that the event you were about to make a payment for does not require payment. Please contact the event administrator if you believe this is an error.',
1463
+								'event_espresso'
1464
+							)
1465
+						),
1466
+						__FILE__,
1467
+						__FUNCTION__,
1468
+						__LINE__
1469
+					);
1470
+					return false;
1471
+				default:
1472
+			}
1473
+		}
1474
+		// verify payment method
1475
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1476
+			// get payment method for selected method of payment
1477
+			$this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1478
+		}
1479
+		return $this->checkout->payment_method instanceof EE_Payment_Method;
1480
+	}
1481
+
1482
+
1483
+
1484
+	/********************************************************************************************************/
1485
+	/***************************************  SAVE PAYER DETAILS  ****************************************/
1486
+	/********************************************************************************************************/
1487
+
1488
+
1489
+	/**
1490
+	 * @return void
1491
+	 * @throws EE_Error
1492
+	 * @throws InvalidArgumentException
1493
+	 * @throws ReflectionException
1494
+	 * @throws RuntimeException
1495
+	 * @throws InvalidDataTypeException
1496
+	 * @throws InvalidInterfaceException
1497
+	 */
1498
+	public function save_payer_details_via_ajax()
1499
+	{
1500
+		if (! $this->_verify_payment_method_is_set()) {
1501
+			return;
1502
+		}
1503
+		// generate billing form for selected method of payment if it hasn't been done already
1504
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1505
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1506
+				$this->checkout->payment_method
1507
+			);
1508
+		}
1509
+		// generate primary attendee from payer info if applicable
1510
+		if (! $this->checkout->transaction_has_primary_registrant()) {
1511
+			$attendee = $this->_create_attendee_from_request_data();
1512
+			if ($attendee instanceof EE_Attendee) {
1513
+				foreach ($this->checkout->transaction->registrations() as $registration) {
1514
+					if ($registration->is_primary_registrant()) {
1515
+						$this->checkout->primary_attendee_obj = $attendee;
1516
+						$registration->_add_relation_to($attendee, 'Attendee');
1517
+						$registration->set_attendee_id($attendee->ID());
1518
+						$registration->update_cache_after_object_save('Attendee', $attendee);
1519
+					}
1520
+				}
1521
+			}
1522
+		}
1523
+	}
1524
+
1525
+
1526
+	/**
1527
+	 * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1528
+	 *
1529
+	 * @return EE_Attendee
1530
+	 * @throws EE_Error
1531
+	 * @throws InvalidArgumentException
1532
+	 * @throws ReflectionException
1533
+	 * @throws InvalidDataTypeException
1534
+	 * @throws InvalidInterfaceException
1535
+	 */
1536
+	protected function _create_attendee_from_request_data(): EE_Attendee
1537
+	{
1538
+		// get State ID
1539
+		$STA_ID = $this->request->getRequestParam('state');
1540
+		if (! empty($STA_ID)) {
1541
+			// can we get state object from name ?
1542
+			EE_Registry::instance()->load_model('State');
1543
+			$state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1544
+			$STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1545
+		}
1546
+		// get Country ISO
1547
+		$CNT_ISO = $this->request->getRequestParam('country');
1548
+		if (! empty($CNT_ISO)) {
1549
+			// can we get country object from name ?
1550
+			EE_Registry::instance()->load_model('Country');
1551
+			$country = EEM_Country::instance()->get_col(
1552
+				[['CNT_name' => $CNT_ISO], 'limit' => 1],
1553
+				'CNT_ISO'
1554
+			);
1555
+			$CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1556
+		}
1557
+		// grab attendee data
1558
+		$attendee_data = [
1559
+			'ATT_fname'    => $this->request->getRequestParam('first_name'),
1560
+			'ATT_lname'    => $this->request->getRequestParam('last_name'),
1561
+			'ATT_email'    => $this->request->getRequestParam('email'),
1562
+			'ATT_address'  => $this->request->getRequestParam('address'),
1563
+			'ATT_address2' => $this->request->getRequestParam('address2'),
1564
+			'ATT_city'     => $this->request->getRequestParam('city'),
1565
+			'STA_ID'       => $STA_ID,
1566
+			'CNT_ISO'      => $CNT_ISO,
1567
+			'ATT_zip'      => $this->request->getRequestParam('zip'),
1568
+			'ATT_phone'    => $this->request->getRequestParam('phone'),
1569
+		];
1570
+		// validate the email address since it is the most important piece of info
1571
+		if (empty($attendee_data['ATT_email'])) {
1572
+			EE_Error::add_error(
1573
+				esc_html__('An invalid email address was submitted.', 'event_espresso'),
1574
+				__FILE__,
1575
+				__FUNCTION__,
1576
+				__LINE__
1577
+			);
1578
+		}
1579
+		// does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1580
+		// AND email address
1581
+		if (
1582
+			! empty($attendee_data['ATT_fname'])
1583
+			&& ! empty($attendee_data['ATT_lname'])
1584
+			&& ! empty($attendee_data['ATT_email'])
1585
+		) {
1586
+			$existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1587
+				[
1588
+					'ATT_fname' => $attendee_data['ATT_fname'],
1589
+					'ATT_lname' => $attendee_data['ATT_lname'],
1590
+					'ATT_email' => $attendee_data['ATT_email'],
1591
+				]
1592
+			);
1593
+			if ($existing_attendee instanceof EE_Attendee) {
1594
+				return $existing_attendee;
1595
+			}
1596
+		}
1597
+		// no existing attendee? kk let's create a new one
1598
+		// kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1599
+		// don't exist
1600
+		$attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1601
+			? $attendee_data['ATT_fname']
1602
+			: $attendee_data['ATT_email'];
1603
+		$attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1604
+			? $attendee_data['ATT_lname']
1605
+			: $attendee_data['ATT_email'];
1606
+		return EE_Attendee::new_instance($attendee_data);
1607
+	}
1608
+
1609
+
1610
+
1611
+	/********************************************************************************************************/
1612
+	/****************************************  PROCESS REG STEP  *****************************************/
1613
+	/********************************************************************************************************/
1614
+
1615
+
1616
+	/**
1617
+	 * @return bool
1618
+	 * @throws EE_Error
1619
+	 * @throws InvalidArgumentException
1620
+	 * @throws ReflectionException
1621
+	 * @throws EntityNotFoundException
1622
+	 * @throws InvalidDataTypeException
1623
+	 * @throws InvalidInterfaceException
1624
+	 * @throws InvalidStatusException
1625
+	 */
1626
+	public function process_reg_step(): bool
1627
+	{
1628
+		// how have they chosen to pay?
1629
+		$this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1630
+			? 'no_payment_required'
1631
+			: $this->_get_selected_method_of_payment(true);
1632
+		// choose your own adventure based on method_of_payment
1633
+		switch ($this->checkout->selected_method_of_payment) {
1634
+			case 'events_sold_out':
1635
+				$this->checkout->redirect     = true;
1636
+				$this->checkout->redirect_url = $this->checkout->cancel_page_url;
1637
+				$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1638
+				// mark this reg step as completed
1639
+				$this->set_completed();
1640
+				return false;
1641
+
1642
+			case 'payments_closed':
1643
+				if (
1644
+					apply_filters(
1645
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1646
+						false
1647
+					)
1648
+				) {
1649
+					EE_Error::add_success(
1650
+						esc_html__('no payment required at this time.', 'event_espresso'),
1651
+						__FILE__,
1652
+						__FUNCTION__,
1653
+						__LINE__
1654
+					);
1655
+				}
1656
+				// mark this reg step as completed
1657
+				$this->set_completed();
1658
+				return true;
1659
+
1660
+			case 'no_payment_required':
1661
+				if (
1662
+					apply_filters(
1663
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1664
+						false
1665
+					)
1666
+				) {
1667
+					EE_Error::add_success(
1668
+						esc_html__('no payment required.', 'event_espresso'),
1669
+						__FILE__,
1670
+						__FUNCTION__,
1671
+						__LINE__
1672
+					);
1673
+				}
1674
+				// mark this reg step as completed
1675
+				$this->set_completed();
1676
+				return true;
1677
+
1678
+			default:
1679
+				$registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1680
+					EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1681
+				);
1682
+				$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1683
+					$registrations,
1684
+					EE_Registry::instance()->SSN->checkout()->revisit
1685
+				);
1686
+				// calculate difference between the two arrays
1687
+				$registrations = array_diff($registrations, $ejected_registrations);
1688
+				if (empty($registrations)) {
1689
+					$this->_redirect_because_event_sold_out();
1690
+					return false;
1691
+				}
1692
+				$payment = $this->_process_payment();
1693
+				if ($payment instanceof EE_Payment) {
1694
+					$this->checkout->continue_reg = true;
1695
+					$this->_maybe_set_completed($payment);
1696
+				} else {
1697
+					$this->checkout->continue_reg = false;
1698
+				}
1699
+				return $payment instanceof EE_Payment;
1700
+		}
1701
+	}
1702
+
1703
+
1704
+	/**
1705
+	 * @return void
1706
+	 */
1707
+	protected function _redirect_because_event_sold_out()
1708
+	{
1709
+		$this->checkout->continue_reg = false;
1710
+		// set redirect URL
1711
+		$this->checkout->redirect_url = add_query_arg(
1712
+			['e_reg_url_link' => $this->checkout->reg_url_link],
1713
+			$this->checkout->current_step->reg_step_url()
1714
+		);
1715
+		$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1716
+	}
1717
+
1718
+
1719
+	/**
1720
+	 * @param EE_Payment $payment
1721
+	 * @return void
1722
+	 * @throws EE_Error
1723
+	 */
1724
+	protected function _maybe_set_completed(EE_Payment $payment)
1725
+	{
1726
+		// Do we need to redirect them? If so, there's more work to be done.
1727
+		if (! $payment->redirect_url()) {
1728
+			$this->set_completed();
1729
+		}
1730
+	}
1731
+
1732
+
1733
+	/**
1734
+	 * this is the final step after a user  revisits the site to retry a payment
1735
+	 *
1736
+	 * @return bool
1737
+	 * @throws EE_Error
1738
+	 * @throws InvalidArgumentException
1739
+	 * @throws ReflectionException
1740
+	 * @throws EntityNotFoundException
1741
+	 * @throws InvalidDataTypeException
1742
+	 * @throws InvalidInterfaceException
1743
+	 * @throws InvalidStatusException
1744
+	 */
1745
+	public function update_reg_step(): bool
1746
+	{
1747
+		$success = true;
1748
+		// if payment required
1749
+		if ($this->checkout->transaction->total() > 0) {
1750
+			do_action(
1751
+				'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1752
+				$this->checkout->transaction
1753
+			);
1754
+			// attempt payment via payment method
1755
+			$success = $this->process_reg_step();
1756
+		}
1757
+		if ($success && ! $this->checkout->redirect) {
1758
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1759
+				$this->checkout->transaction->ID()
1760
+			);
1761
+			// set return URL
1762
+			$this->checkout->redirect_url = add_query_arg(
1763
+				['e_reg_url_link' => $this->checkout->reg_url_link],
1764
+				$this->checkout->thank_you_page_url
1765
+			);
1766
+		}
1767
+		return $success;
1768
+	}
1769
+
1770
+
1771
+	/**
1772
+	 * @return EE_Payment|bool|null
1773
+	 * @throws EE_Error
1774
+	 * @throws InvalidArgumentException
1775
+	 * @throws ReflectionException
1776
+	 * @throws RuntimeException
1777
+	 * @throws InvalidDataTypeException
1778
+	 * @throws InvalidInterfaceException
1779
+	 */
1780
+	private function _process_payment()
1781
+	{
1782
+		// basically confirm that the event hasn't sold out since they hit the page
1783
+		if (! $this->_last_second_ticket_verifications()) {
1784
+			return null;
1785
+		}
1786
+		// ya gotta make a choice man
1787
+		if (empty($this->checkout->selected_method_of_payment)) {
1788
+			$this->checkout->json_response->set_plz_select_method_of_payment(
1789
+				esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1790
+			);
1791
+			return null;
1792
+		}
1793
+		// get EE_Payment_Method object
1794
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1795
+			return null;
1796
+		}
1797
+		// setup billing form
1798
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1799
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1800
+				$this->checkout->payment_method
1801
+			);
1802
+			// bad billing form ?
1803
+			if (! $this->_billing_form_is_valid()) {
1804
+				return null;
1805
+			}
1806
+		}
1807
+		// ensure primary registrant has been fully processed
1808
+		if (! $this->_setup_primary_registrant_prior_to_payment()) {
1809
+			return null;
1810
+		}
1811
+		// if session is close to expiring (under 10 minutes by default)
1812
+		if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1813
+			// add some time to session expiration so that payment can be completed
1814
+			EE_Registry::instance()->SSN->extend_expiration();
1815
+		}
1816
+		/** @type EE_Transaction_Processor $transaction_processor */
1817
+		// $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1818
+		// in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1819
+		// for events with a default reg status of Approved
1820
+		// $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1821
+		//      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1822
+		// );
1823
+		// attempt payment
1824
+		$payment = $this->_attempt_payment($this->checkout->payment_method);
1825
+		// process results
1826
+		$payment = $this->_validate_payment($payment);
1827
+		$payment = $this->_post_payment_processing($payment);
1828
+		// verify payment
1829
+		if ($payment instanceof EE_Payment) {
1830
+			// store that for later
1831
+			$this->checkout->payment = $payment;
1832
+			// we can also consider the TXN to not have been failed, so temporarily upgrade its status to abandoned
1833
+			$this->checkout->transaction->toggle_failed_transaction_status();
1834
+			$payment_status = $payment->status();
1835
+			if (
1836
+				$payment_status === EEM_Payment::status_id_approved
1837
+				|| $payment_status === EEM_Payment::status_id_pending
1838
+			) {
1839
+				return $payment;
1840
+			}
1841
+			return null;
1842
+		}
1843
+		if ($payment === true) {
1844
+			// please note that offline payment methods will NOT make a payment,
1845
+			// but instead just mark themselves as the PMD_ID on the transaction, and return true
1846
+			$this->checkout->payment = true;
1847
+			return true;
1848
+		}
1849
+		// where's my money?
1850
+		return null;
1851
+	}
1852
+
1853
+
1854
+	/**
1855
+	 * @return bool
1856
+	 * @throws EE_Error
1857
+	 * @throws ReflectionException
1858
+	 */
1859
+	protected function _last_second_ticket_verifications(): bool
1860
+	{
1861
+		// don't bother re-validating if not a return visit
1862
+		if (! $this->checkout->revisit) {
1863
+			return true;
1864
+		}
1865
+		$registrations = $this->checkout->transaction->registrations();
1866
+		if (empty($registrations)) {
1867
+			return false;
1868
+		}
1869
+		foreach ($registrations as $registration) {
1870
+			if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1871
+				$event = $registration->event_obj();
1872
+				if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1873
+					EE_Error::add_error(
1874
+						apply_filters(
1875
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1876
+							sprintf(
1877
+								esc_html__(
1878
+									'It appears that the %1$s event that you were about to make a payment for has sold out since you first registered and/or arrived at this page. Please refresh the page and try again. If you have already made a partial payment towards this event, please contact the event administrator for a refund.',
1879
+									'event_espresso'
1880
+								),
1881
+								$event->name()
1882
+							)
1883
+						),
1884
+						__FILE__,
1885
+						__FUNCTION__,
1886
+						__LINE__
1887
+					);
1888
+					return false;
1889
+				}
1890
+			}
1891
+		}
1892
+		return true;
1893
+	}
1894
+
1895
+
1896
+	/**
1897
+	 * @return bool
1898
+	 * @throws EE_Error
1899
+	 * @throws InvalidArgumentException
1900
+	 * @throws ReflectionException
1901
+	 * @throws InvalidDataTypeException
1902
+	 * @throws InvalidInterfaceException
1903
+	 */
1904
+	public function redirect_form(): bool
1905
+	{
1906
+		$payment_method_billing_info = $this->_payment_method_billing_info(
1907
+			$this->_get_payment_method_for_selected_method_of_payment()
1908
+		);
1909
+		$html                        = $payment_method_billing_info->get_html();
1910
+		$html                        .= $this->checkout->redirect_form;
1911
+		/** @var ResponseInterface $response */
1912
+		$response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1913
+		$response->addOutput($html);
1914
+		return true;
1915
+	}
1916
+
1917
+
1918
+	/**
1919
+	 * @return bool
1920
+	 * @throws EE_Error
1921
+	 * @throws ReflectionException
1922
+	 */
1923
+	private function _billing_form_is_valid(): bool
1924
+	{
1925
+		if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1926
+			return true;
1927
+		}
1928
+		if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1929
+			if ($this->checkout->billing_form->was_submitted()) {
1930
+				$this->checkout->billing_form->receive_form_submission();
1931
+				if ($this->checkout->billing_form->is_valid()) {
1932
+					return true;
1933
+				}
1934
+				$validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
1935
+				$error_strings     = [];
1936
+				foreach ($validation_errors as $validation_error) {
1937
+					if ($validation_error instanceof EE_Validation_Error) {
1938
+						$form_section = $validation_error->get_form_section();
1939
+						if ($form_section instanceof EE_Form_Input_Base) {
1940
+							$label = $form_section->html_label_text();
1941
+						} elseif ($form_section instanceof EE_Form_Section_Base) {
1942
+							$label = $form_section->name();
1943
+						} else {
1944
+							$label = esc_html__('Validation Error', 'event_espresso');
1945
+						}
1946
+						$error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
1947
+					}
1948
+				}
1949
+				EE_Error::add_error(
1950
+					sprintf(
1951
+						esc_html__(
1952
+							'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
1953
+							'event_espresso'
1954
+						),
1955
+						'<br/>',
1956
+						implode('<br/>', $error_strings)
1957
+					),
1958
+					__FILE__,
1959
+					__FUNCTION__,
1960
+					__LINE__
1961
+				);
1962
+			} else {
1963
+				EE_Error::add_error(
1964
+					esc_html__(
1965
+						'The billing form was not submitted or something prevented it\'s submission.',
1966
+						'event_espresso'
1967
+					),
1968
+					__FILE__,
1969
+					__FUNCTION__,
1970
+					__LINE__
1971
+				);
1972
+			}
1973
+		} else {
1974
+			EE_Error::add_error(
1975
+				esc_html__(
1976
+					'The submitted billing form is invalid possibly due to a technical reason.',
1977
+					'event_espresso'
1978
+				),
1979
+				__FILE__,
1980
+				__FUNCTION__,
1981
+				__LINE__
1982
+			);
1983
+		}
1984
+		return false;
1985
+	}
1986
+
1987
+
1988
+	/**
1989
+	 * ensures that the primary registrant has a valid attendee object created with the critical details populated
1990
+	 * (first & last name & email) and that both the transaction object and primary registration object have been saved
1991
+	 * plz note that any other registrations will NOT be saved at this point (because they may not have any details
1992
+	 * yet)
1993
+	 *
1994
+	 * @return bool
1995
+	 * @throws EE_Error
1996
+	 * @throws InvalidArgumentException
1997
+	 * @throws ReflectionException
1998
+	 * @throws RuntimeException
1999
+	 * @throws InvalidDataTypeException
2000
+	 * @throws InvalidInterfaceException
2001
+	 */
2002
+	private function _setup_primary_registrant_prior_to_payment(): bool
2003
+	{
2004
+		// check if transaction has a primary registrant and that it has a related Attendee object
2005
+		// if not, then we need to at least gather some primary registrant data before attempting payment
2006
+		if (
2007
+			$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2008
+			&& ! $this->checkout->transaction_has_primary_registrant()
2009
+			&& ! $this->_capture_primary_registration_data_from_billing_form()
2010
+		) {
2011
+			return false;
2012
+		}
2013
+		// because saving an object clears its cache, we need to do the Chevy Shuffle
2014
+		// grab the primary_registration object
2015
+		$primary_registration = $this->checkout->transaction->primary_registration();
2016
+		// at this point we'll consider a TXN to not have been failed
2017
+		$this->checkout->transaction->toggle_failed_transaction_status();
2018
+		// save the TXN ( which clears cached copy of primary_registration)
2019
+		$this->checkout->transaction->save();
2020
+		// grab TXN ID and save it to the primary_registration
2021
+		$primary_registration->set_transaction_id($this->checkout->transaction->ID());
2022
+		// save what we have so far
2023
+		$primary_registration->save();
2024
+		return true;
2025
+	}
2026
+
2027
+
2028
+	/**
2029
+	 * Captures primary registration data from the billing form.
2030
+	 *
2031
+	 * This method is used to gather the primary registrant data before attempting payment.
2032
+	 * It checks if the billing form is an instance of EE_Billing_Attendee_Info_Form and if the transaction
2033
+	 * has a primary registrant. If not, it captures the primary registrant data from the billing form.
2034
+	 *
2035
+	 * @return bool
2036
+	 * @throws EE_Error
2037
+	 * @throws InvalidArgumentException
2038
+	 * @throws ReflectionException
2039
+	 * @throws InvalidDataTypeException
2040
+	 * @throws InvalidInterfaceException
2041
+	 */
2042
+	private function _capture_primary_registration_data_from_billing_form(): bool
2043
+	{
2044
+		$primary_registration = $this->checkout->transaction->primary_registration();
2045
+		if (! $this->validatePrimaryRegistration($primary_registration)) {
2046
+			return false;
2047
+		}
2048
+
2049
+		$primary_attendee = $this->getPrimaryAttendee($primary_registration);
2050
+		if (! $this->validatePrimaryAttendee($primary_attendee)) {
2051
+			return false;
2052
+		}
2053
+
2054
+		if (! $this->addAttendeeToPrimaryRegistration($primary_attendee, $primary_registration)) {
2055
+			return false;
2056
+		}
2057
+		// both the primary registration and primary attendee objects should be valid entities at this point
2058
+		$this->checkout->primary_attendee_obj = $primary_attendee;
2059
+
2060
+		/** @type EE_Registration_Processor $registration_processor */
2061
+		$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2062
+		// at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2063
+		$registration_processor->toggle_incomplete_registration_status_to_default(
2064
+			$primary_registration,
2065
+			false,
2066
+			new Context(
2067
+				__METHOD__,
2068
+				esc_html__(
2069
+					'Executed when the primary registrant\'s status is updated during the registration process when processing a billing form.',
2070
+					'event_espresso'
2071
+				)
2072
+			)
2073
+		);
2074
+		return true;
2075
+	}
2076
+
2077
+
2078
+	/**
2079
+	 * returns true if the primary registration is a valid entity
2080
+	 *
2081
+	 * @param $primary_registration
2082
+	 * @return bool
2083
+	 * @throws EE_Error
2084
+	 * @since 5.0.21.p
2085
+	 */
2086
+	private function validatePrimaryRegistration($primary_registration): bool
2087
+	{
2088
+		if ($primary_registration instanceof EE_Registration) {
2089
+			return true;
2090
+		}
2091
+		EE_Error::add_error(
2092
+			sprintf(
2093
+				esc_html__(
2094
+					'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2095
+					'event_espresso'
2096
+				),
2097
+				'<br/>',
2098
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2099
+			),
2100
+			__FILE__,
2101
+			__FUNCTION__,
2102
+			__LINE__
2103
+		);
2104
+		return false;
2105
+	}
2106
+
2107
+
2108
+	/**
2109
+	 * retrieves the primary attendee object for the primary registration and copies the billing form data to it.
2110
+	 * if the primary registration does not have an attendee object, then one is created from the billing form info
2111
+	 *
2112
+	 * @param EE_Registration $primary_registration
2113
+	 * @return EE_Attendee|null
2114
+	 * @throws EE_Error
2115
+	 * @throws ReflectionException
2116
+	 * @since 5.0.21.p
2117
+	 */
2118
+	private function getPrimaryAttendee(EE_Registration $primary_registration): ?EE_Attendee
2119
+	{
2120
+		// if we have a primary registration, then we should have a primary attendee
2121
+		$attendee = $primary_registration->attendee();
2122
+		if ($attendee instanceof EE_Attendee) {
2123
+			return $this->checkout->billing_form->copy_billing_form_data_to_attendee($attendee);
2124
+		}
2125
+		// if not, then we need to create one from the billing form
2126
+		return $this->checkout->billing_form->create_attendee_from_billing_form_data();
2127
+	}
2128
+
2129
+
2130
+	/**
2131
+	 * returns true if the primary attendee is a valid entity
2132
+	 *
2133
+	 * @param $primary_attendee
2134
+	 * @return bool
2135
+	 * @throws EE_Error
2136
+	 * @since 5.0.21.p
2137
+	 */
2138
+	private function validatePrimaryAttendee($primary_attendee): bool
2139
+	{
2140
+		if ($primary_attendee instanceof EE_Attendee) {
2141
+			return true;
2142
+		}
2143
+		EE_Error::add_error(
2144
+			sprintf(
2145
+				esc_html__(
2146
+					'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2147
+					'event_espresso'
2148
+				),
2149
+				'<br/>',
2150
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2151
+			),
2152
+			__FILE__,
2153
+			__FUNCTION__,
2154
+			__LINE__
2155
+		);
2156
+		return false;
2157
+	}
2158
+
2159
+
2160
+	/**
2161
+	 * returns true if the attendee was successfully added to the primary registration
2162
+	 *
2163
+	 * @param EE_Attendee     $primary_attendee
2164
+	 * @param EE_Registration $primary_registration
2165
+	 * @return bool
2166
+	 * @throws EE_Error
2167
+	 * @throws ReflectionException
2168
+	 * @since 5.0.21.p
2169
+	 */
2170
+	private function addAttendeeToPrimaryRegistration(
2171
+		EE_Attendee $primary_attendee,
2172
+		EE_Registration $primary_registration
2173
+	): bool {
2174
+		// ensure attendee has an ID by saving
2175
+		$primary_attendee->save();
2176
+
2177
+		// compare attendee IDs
2178
+		if ($primary_registration->attendee_id() === $primary_attendee->ID()) {
2179
+			return true;
2180
+		}
2181
+
2182
+		$primary_attendee = $primary_registration->_add_relation_to($primary_attendee, 'Attendee');
2183
+		if ($primary_attendee instanceof EE_Attendee) {
2184
+			return true;
2185
+		}
2186
+
2187
+		EE_Error::add_error(
2188
+			sprintf(
2189
+				esc_html__(
2190
+					'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2191
+					'event_espresso'
2192
+				),
2193
+				'<br/>',
2194
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2195
+			),
2196
+			__FILE__,
2197
+			__FUNCTION__,
2198
+			__LINE__
2199
+		);
2200
+		return false;
2201
+	}
2202
+
2203
+
2204
+	/**
2205
+	 * retrieves a valid payment method
2206
+	 *
2207
+	 * @return EE_Payment_Method
2208
+	 * @throws EE_Error
2209
+	 * @throws InvalidArgumentException
2210
+	 * @throws ReflectionException
2211
+	 * @throws InvalidDataTypeException
2212
+	 * @throws InvalidInterfaceException
2213
+	 */
2214
+	private function _get_payment_method_for_selected_method_of_payment(): ?EE_Payment_Method
2215
+	{
2216
+		if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2217
+			$this->_redirect_because_event_sold_out();
2218
+			return null;
2219
+		}
2220
+		// get EE_Payment_Method object
2221
+		$payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ]
2222
+			?? $this->payment_method_model->get_one_by_slug($this->checkout->selected_method_of_payment);
2223
+		// verify $payment_method
2224
+		if (! $payment_method instanceof EE_Payment_Method) {
2225
+			// not a payment
2226
+			EE_Error::add_error(
2227
+				sprintf(
2228
+					esc_html__(
2229
+						'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2230
+						'event_espresso'
2231
+					),
2232
+					'<br/>',
2233
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2234
+				),
2235
+				__FILE__,
2236
+				__FUNCTION__,
2237
+				__LINE__
2238
+			);
2239
+			return null;
2240
+		}
2241
+		// and verify it has a valid Payment_Method Type object
2242
+		if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2243
+			// not a payment
2244
+			EE_Error::add_error(
2245
+				sprintf(
2246
+					esc_html__(
2247
+						'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2248
+						'event_espresso'
2249
+					),
2250
+					'<br/>',
2251
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2252
+				),
2253
+				__FILE__,
2254
+				__FUNCTION__,
2255
+				__LINE__
2256
+			);
2257
+			return null;
2258
+		}
2259
+		return $payment_method;
2260
+	}
2261
+
2262
+
2263
+	/**
2264
+	 * @param EE_Payment_Method $payment_method
2265
+	 * @return EE_Payment|null
2266
+	 * @throws EE_Error
2267
+	 * @throws ReflectionException
2268
+	 */
2269
+	private function _attempt_payment(EE_Payment_Method $payment_method): ?EE_Payment
2270
+	{
2271
+		$this->checkout->transaction->save();
2272
+		/** @var PaymentProcessor $payment_processor */
2273
+		$payment_processor = LoaderFactory::getShared(PaymentProcessor::class);
2274
+		if (! $payment_processor instanceof PaymentProcessor) {
2275
+			return null;
2276
+		}
2277
+		/** @var EE_Transaction_Processor $transaction_processor */
2278
+		$transaction_processor = LoaderFactory::getShared(EE_Transaction_Processor::class);
2279
+		if ($transaction_processor instanceof EE_Transaction_Processor) {
2280
+			$transaction_processor->set_revisit($this->checkout->revisit);
2281
+		}
2282
+		try {
2283
+			// generate payment object
2284
+			return $payment_processor->processPayment(
2285
+				$payment_method,
2286
+				$this->checkout->transaction,
2287
+				$this->checkout->billing_form instanceof EE_Billing_Info_Form
2288
+					? $this->checkout->billing_form
2289
+					: null,
2290
+				$this->checkout->amount_owing,
2291
+				$this->checkout->admin_request,
2292
+				true,
2293
+				$this->_get_return_url($payment_method),
2294
+				$this->reg_step_url()
2295
+			);
2296
+		} catch (Exception $e) {
2297
+			$this->_handle_payment_processor_exception($e);
2298
+		}
2299
+		return null;
2300
+	}
2301
+
2302
+
2303
+	/**
2304
+	 * @param Exception $e
2305
+	 * @return void
2306
+	 * @throws EE_Error
2307
+	 */
2308
+	protected function _handle_payment_processor_exception(Exception $e)
2309
+	{
2310
+		EE_Error::add_error(
2311
+			sprintf(
2312
+				esc_html__(
2313
+					'The payment could not br processed due to a technical issue.%1$sPlease try again or contact %2$s for assistance.||The following Exception was thrown in %4$s on line %5$s:%1$s%3$s',
2314
+					'event_espresso'
2315
+				),
2316
+				'<br/>',
2317
+				EE_Registry::instance()->CFG->organization->get_pretty('email'),
2318
+				$e->getMessage(),
2319
+				$e->getFile(),
2320
+				$e->getLine()
2321
+			),
2322
+			__FILE__,
2323
+			__FUNCTION__,
2324
+			__LINE__
2325
+		);
2326
+	}
2327
+
2328
+
2329
+	/**
2330
+	 * @param EE_Payment_Method $payment_method
2331
+	 * @return string
2332
+	 * @throws EE_Error
2333
+	 * @throws ReflectionException
2334
+	 */
2335
+	protected function _get_return_url(EE_Payment_Method $payment_method): string
2336
+	{
2337
+		switch ($payment_method->type_obj()->payment_occurs()) {
2338
+			case EE_PMT_Base::offsite:
2339
+				return add_query_arg(
2340
+					[
2341
+						'action'                     => 'process_gateway_response',
2342
+						'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2343
+						'spco_txn'                   => $this->checkout->transaction->ID(),
2344
+					],
2345
+					$this->reg_step_url()
2346
+				);
2347
+
2348
+			case EE_PMT_Base::onsite:
2349
+			case EE_PMT_Base::offline:
2350
+				return $this->checkout->next_step->reg_step_url();
2351
+		}
2352
+		return '';
2353
+	}
2354
+
2355
+
2356
+	/**
2357
+	 * @param EE_Payment|null $payment
2358
+	 * @return EE_Payment|bool
2359
+	 * @throws EE_Error
2360
+	 * @throws ReflectionException
2361
+	 */
2362
+	private function _validate_payment(?EE_Payment $payment = null)
2363
+	{
2364
+		if ($this->checkout->payment_method->is_off_line()) {
2365
+			return true;
2366
+		}
2367
+		// verify payment object
2368
+		if (! $payment instanceof EE_Payment) {
2369
+			// not a payment
2370
+			EE_Error::add_error(
2371
+				sprintf(
2372
+					esc_html__(
2373
+						'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2374
+						'event_espresso'
2375
+					),
2376
+					'<br/>',
2377
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2378
+				),
2379
+				__FILE__,
2380
+				__FUNCTION__,
2381
+				__LINE__
2382
+			);
2383
+			return false;
2384
+		}
2385
+		return $payment;
2386
+	}
2387
+
2388
+
2389
+	/**
2390
+	 * @param EE_Payment|bool $payment
2391
+	 * @return bool|EE_Payment
2392
+	 * @throws EE_Error
2393
+	 * @throws ReflectionException
2394
+	 */
2395
+	private function _post_payment_processing($payment = null)
2396
+	{
2397
+		// Off-Line payment?
2398
+		if ($payment === true) {
2399
+			return true;
2400
+		}
2401
+		if ($payment instanceof EE_Payment) {
2402
+			// Should the user be redirected?
2403
+			if ($payment->redirect_url()) {
2404
+				do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2405
+				$this->checkout->redirect      = true;
2406
+				$this->checkout->redirect_form = $payment->redirect_form();
2407
+				$this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2408
+				// set JSON response
2409
+				$this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2410
+				// and lastly, let's bump the payment status to pending
2411
+				$payment->set_status(EEM_Payment::status_id_pending);
2412
+				$payment->save();
2413
+			} elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2414
+				// User shouldn't be redirected. So let's process it here.
2415
+				// $this->_setup_redirect_for_next_step();
2416
+				$this->checkout->continue_reg = false;
2417
+			}
2418
+			return $payment;
2419
+		}
2420
+		// ummm ya... not Off-Line, not On-Site, not off-Site ????
2421
+		$this->checkout->continue_reg = false;
2422
+		return false;
2423
+	}
2424
+
2425
+
2426
+	/**
2427
+	 * @param EE_Payment|null $payment
2428
+	 * @param string          $payment_occurs
2429
+	 * @return bool
2430
+	 * @throws EE_Error
2431
+	 */
2432
+	private function _process_payment_status(?EE_Payment $payment, string $payment_occurs = EE_PMT_Base::offline): bool
2433
+	{
2434
+		// off-line payment? carry on
2435
+		if ($payment_occurs === EE_PMT_Base::offline) {
2436
+			return true;
2437
+		}
2438
+		// verify payment validity
2439
+		if ($payment instanceof EE_Payment) {
2440
+			do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2441
+			$msg = $payment->gateway_response();
2442
+			// check results
2443
+			switch ($payment->status()) {
2444
+				// good payment
2445
+				case EEM_Payment::status_id_approved:
2446
+					EE_Error::add_success(
2447
+						esc_html__('Your payment was processed successfully.', 'event_espresso'),
2448
+						__FILE__,
2449
+						__FUNCTION__,
2450
+						__LINE__
2451
+					);
2452
+					return true;
2453
+				// slow payment
2454
+				case EEM_Payment::status_id_pending:
2455
+					if (empty($msg)) {
2456
+						$msg = esc_html__(
2457
+							'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2458
+							'event_espresso'
2459
+						);
2460
+					}
2461
+					EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2462
+					return true;
2463
+				// don't wanna payment
2464
+				case EEM_Payment::status_id_cancelled:
2465
+					if (empty($msg)) {
2466
+						$msg = _n(
2467
+							'Payment cancelled. Please try again.',
2468
+							'Payment cancelled. Please try again or select another method of payment.',
2469
+							count($this->checkout->available_payment_methods),
2470
+							'event_espresso'
2471
+						);
2472
+					}
2473
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2474
+					return false;
2475
+				// not enough payment
2476
+				case EEM_Payment::status_id_declined:
2477
+					if (empty($msg)) {
2478
+						$msg = _n(
2479
+							'We\'re sorry but your payment was declined. Please try again.',
2480
+							'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2481
+							count($this->checkout->available_payment_methods),
2482
+							'event_espresso'
2483
+						);
2484
+					}
2485
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2486
+					return false;
2487
+				// bad payment
2488
+				case EEM_Payment::status_id_failed:
2489
+					if (! empty($msg)) {
2490
+						EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2491
+						return false;
2492
+					}
2493
+					// default to error below
2494
+					break;
2495
+			}
2496
+		}
2497
+		// off-site payment gateway responses are too unreliable, so let's just assume that
2498
+		// the payment processing is just running slower than the registrant's request
2499
+		if ($payment_occurs === EE_PMT_Base::offsite) {
2500
+			return true;
2501
+		}
2502
+		EE_Error::add_error(
2503
+			sprintf(
2504
+				esc_html__(
2505
+					'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2506
+					'event_espresso'
2507
+				),
2508
+				'<br/>',
2509
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2510
+			),
2511
+			__FILE__,
2512
+			__FUNCTION__,
2513
+			__LINE__
2514
+		);
2515
+		return false;
2516
+	}
2517
+
2518
+
2519
+
2520
+
2521
+
2522
+
2523
+	/********************************************************************************************************/
2524
+	/**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2525
+	/********************************************************************************************************/
2526
+
2527
+
2528
+	/**
2529
+	 * This is the return point for Off-Site Payment Methods
2530
+	 * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2531
+	 * otherwise, it will load up the last payment made for the TXN.
2532
+	 * If the payment retrieved looks good, it will then either:
2533
+	 *    complete the current step and allow advancement to the next reg step
2534
+	 *        or present the payment options again
2535
+	 *
2536
+	 * @return bool
2537
+	 * @throws EE_Error
2538
+	 * @throws InvalidArgumentException
2539
+	 * @throws ReflectionException
2540
+	 * @throws InvalidDataTypeException
2541
+	 * @throws InvalidInterfaceException
2542
+	 */
2543
+	public function process_gateway_response()
2544
+	{
2545
+		// how have they chosen to pay?
2546
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2547
+		// get EE_Payment_Method object
2548
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2549
+			$this->checkout->continue_reg = false;
2550
+			return false;
2551
+		}
2552
+		if (! $this->checkout->payment_method->is_off_site()) {
2553
+			return false;
2554
+		}
2555
+		$this->_validate_offsite_return();
2556
+		// verify TXN
2557
+		if ($this->checkout->transaction instanceof EE_Transaction) {
2558
+			$gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2559
+			if (! $gateway instanceof EE_Offsite_Gateway) {
2560
+				$this->checkout->continue_reg = false;
2561
+				return false;
2562
+			}
2563
+			$payment = $this->_process_off_site_payment($gateway);
2564
+			$payment = $this->_process_cancelled_payments($payment);
2565
+			$payment = $this->_validate_payment($payment);
2566
+			// if payment was not declined by the payment gateway or cancelled by the registrant
2567
+			if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2568
+				// $this->_setup_redirect_for_next_step();
2569
+				// store that for later
2570
+				$this->checkout->payment = $payment;
2571
+				// mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2572
+				// because we will complete this step during the IPN processing then
2573
+				if (! $this->handle_IPN_in_this_request()) {
2574
+					$this->set_completed();
2575
+				}
2576
+				return true;
2577
+			}
2578
+		}
2579
+		// DEBUG LOG
2580
+		// $this->checkout->log(
2581
+		//     __CLASS__,
2582
+		//     __FUNCTION__,
2583
+		//     __LINE__,
2584
+		//     array('payment' => $payment)
2585
+		// );
2586
+		$this->checkout->continue_reg = false;
2587
+		return false;
2588
+	}
2589
+
2590
+
2591
+	/**
2592
+	 * @return void
2593
+	 * @throws EE_Error
2594
+	 * @throws ReflectionException
2595
+	 */
2596
+	private function _validate_offsite_return()
2597
+	{
2598
+		$TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2599
+		if ($TXN_ID !== $this->checkout->transaction->ID()) {
2600
+			// Houston... we might have a problem
2601
+			$invalid_TXN = false;
2602
+			// first gather some info
2603
+			$valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2604
+			$primary_registrant = $valid_TXN instanceof EE_Transaction
2605
+				? $valid_TXN->primary_registration()
2606
+				: null;
2607
+			// let's start by retrieving the cart for this TXN
2608
+			$cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2609
+			if ($cart instanceof EE_Cart) {
2610
+				// verify that the current cart has tickets
2611
+				$tickets = $cart->get_tickets();
2612
+				if (empty($tickets)) {
2613
+					$invalid_TXN = true;
2614
+				}
2615
+			} else {
2616
+				$invalid_TXN = true;
2617
+			}
2618
+			$valid_TXN_SID = $primary_registrant instanceof EE_Registration
2619
+				? $primary_registrant->session_ID()
2620
+				: null;
2621
+			// validate current Session ID and compare against valid TXN session ID
2622
+			if (
2623
+				$invalid_TXN // if this is already true, then skip other checks
2624
+				|| EE_Session::instance()->id() === null
2625
+				|| (
2626
+					// WARNING !!!
2627
+					// this could be PayPal sending back duplicate requests (ya they do that)
2628
+					// or it **could** mean someone is simply registering AGAIN after having just done so,
2629
+					// so now we need to determine if this current TXN looks valid or not
2630
+					// and whether this reg step has even been started ?
2631
+					EE_Session::instance()->id() === $valid_TXN_SID
2632
+					// really? you're halfway through this reg step, but you never started it ?
2633
+					&& $this->checkout->transaction->reg_step_completed($this->slug()) === false
2634
+				)
2635
+			) {
2636
+				$invalid_TXN = true;
2637
+			}
2638
+			if ($invalid_TXN) {
2639
+				// is the valid TXN completed ?
2640
+				if ($valid_TXN instanceof EE_Transaction) {
2641
+					// has this step even been started ?
2642
+					$reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2643
+					if ($reg_step_completed !== false && $reg_step_completed !== true) {
2644
+						// so it **looks** like this is a double request from PayPal
2645
+						// so let's try to pick up where we left off
2646
+						$this->checkout->transaction = $valid_TXN;
2647
+						$this->checkout->refresh_all_entities(true);
2648
+						return;
2649
+					}
2650
+				}
2651
+				// you appear to be lost?
2652
+				$this->_redirect_wayward_request($primary_registrant);
2653
+			}
2654
+		}
2655
+	}
2656
+
2657
+
2658
+	/**
2659
+	 * @param EE_Registration|null $primary_registrant
2660
+	 * @return void
2661
+	 * @throws EE_Error
2662
+	 * @throws ReflectionException
2663
+	 */
2664
+	private function _redirect_wayward_request(?EE_Registration $primary_registrant)
2665
+	{
2666
+		if (! $primary_registrant instanceof EE_Registration) {
2667
+			// try redirecting based on the current TXN
2668
+			$primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2669
+				? $this->checkout->transaction->primary_registration()
2670
+				: null;
2671
+		}
2672
+		if (! $primary_registrant instanceof EE_Registration) {
2673
+			EE_Error::add_error(
2674
+				sprintf(
2675
+					esc_html__(
2676
+						'Invalid information was received from the Off-Site Payment Processor and your Transaction details could not be retrieved from the database.%1$sPlease try again or contact %2$s for assistance.',
2677
+						'event_espresso'
2678
+					),
2679
+					'<br/>',
2680
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2681
+				),
2682
+				__FILE__,
2683
+				__FUNCTION__,
2684
+				__LINE__
2685
+			);
2686
+			return;
2687
+		}
2688
+		// make sure transaction is not locked
2689
+		$this->checkout->transaction->unlock();
2690
+		wp_safe_redirect(
2691
+			add_query_arg(
2692
+				[
2693
+					'e_reg_url_link' => $primary_registrant->reg_url_link(),
2694
+				],
2695
+				$this->checkout->thank_you_page_url
2696
+			)
2697
+		);
2698
+		exit();
2699
+	}
2700
+
2701
+
2702
+	/**
2703
+	 * @param EE_Offsite_Gateway $gateway
2704
+	 * @return EE_Payment|null
2705
+	 * @throws EE_Error
2706
+	 * @throws ReflectionException
2707
+	 */
2708
+	private function _process_off_site_payment(EE_Offsite_Gateway $gateway): ?EE_Payment
2709
+	{
2710
+		try {
2711
+			$request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2712
+			$request_data = $request->requestParams();
2713
+			// if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2714
+			$this->set_handle_IPN_in_this_request(
2715
+				$gateway->handle_IPN_in_this_request($request_data, false)
2716
+			);
2717
+			if ($this->handle_IPN_in_this_request()) {
2718
+				// get payment details and process results
2719
+				/** @var IpnHandler $payment_processor */
2720
+				$payment_processor = LoaderFactory::getShared(IpnHandler::class);
2721
+				$payment           = $payment_processor->processIPN(
2722
+					$request_data,
2723
+					$this->checkout->transaction,
2724
+					$this->checkout->payment_method,
2725
+					true,
2726
+					false
2727
+				);
2728
+				// $payment_source = 'process_ipn';
2729
+			} else {
2730
+				$payment = $this->checkout->transaction->last_payment();
2731
+				// $payment_source = 'last_payment';
2732
+			}
2733
+		} catch (Exception $e) {
2734
+			// let's just eat the exception and try to move on using any previously set payment info
2735
+			$payment = $this->checkout->transaction->last_payment();
2736
+			// $payment_source = 'last_payment after Exception';
2737
+			// but if we STILL don't have a payment object
2738
+			if (! $payment instanceof EE_Payment) {
2739
+				// then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2740
+				$this->_handle_payment_processor_exception($e);
2741
+			}
2742
+		}
2743
+		return $payment;
2744
+	}
2745
+
2746
+
2747
+	/**
2748
+	 * just makes sure that the payment status gets updated correctly
2749
+	 * so that an error isn't generated during payment validation
2750
+	 *
2751
+	 * @param EE_Payment|null $payment
2752
+	 * @return EE_Payment|null
2753
+	 * @throws EE_Error
2754
+	 */
2755
+	private function _process_cancelled_payments(?EE_Payment $payment = null): ?EE_Payment
2756
+	{
2757
+		if (
2758
+			$payment instanceof EE_Payment
2759
+			&& $this->request->requestParamIsSet('ee_cancel_payment')
2760
+			&& $payment->status() === EEM_Payment::status_id_failed
2761
+		) {
2762
+			$payment->set_status(EEM_Payment::status_id_cancelled);
2763
+		}
2764
+		return $payment;
2765
+	}
2766
+
2767
+
2768
+	/**
2769
+	 * @return void
2770
+	 * @throws EE_Error
2771
+	 * @throws InvalidArgumentException
2772
+	 * @throws ReflectionException
2773
+	 * @throws InvalidDataTypeException
2774
+	 * @throws InvalidInterfaceException
2775
+	 */
2776
+	public function get_transaction_details_for_gateways()
2777
+	{
2778
+		$txn_details = [];
2779
+		// ya gotta make a choice man
2780
+		if (empty($this->checkout->selected_method_of_payment)) {
2781
+			$txn_details = [
2782
+				'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2783
+			];
2784
+		}
2785
+		// get EE_Payment_Method object
2786
+		if (
2787
+			empty($txn_details)
2788
+			&& ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2789
+		) {
2790
+			$txn_details = [
2791
+				'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2792
+				'error'                      => esc_html__(
2793
+					'A valid Payment Method could not be determined.',
2794
+					'event_espresso'
2795
+				),
2796
+			];
2797
+		}
2798
+		if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2799
+			$return_url  = $this->_get_return_url($this->checkout->payment_method);
2800
+			$txn_details = [
2801
+				'TXN_ID'         => $this->checkout->transaction->ID(),
2802
+				'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2803
+				'TXN_total'      => $this->checkout->transaction->total(),
2804
+				'TXN_paid'       => $this->checkout->transaction->paid(),
2805
+				'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2806
+				'STS_ID'         => $this->checkout->transaction->status_ID(),
2807
+				'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2808
+				'payment_amount' => $this->checkout->amount_owing,
2809
+				'return_url'     => $return_url,
2810
+				'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2811
+				'notify_url'     => EE_Config::instance()->core->txn_page_url(
2812
+					[
2813
+						'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2814
+						'ee_payment_method' => $this->checkout->payment_method->slug(),
2815
+					]
2816
+				),
2817
+			];
2818
+		}
2819
+		echo wp_json_encode($txn_details);
2820
+		exit();
2821
+	}
2822
+
2823
+
2824
+	/**
2825
+	 * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2826
+	 * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2827
+	 * reg form, because if needed, it will be regenerated anyways
2828
+	 *
2829
+	 * @return array
2830
+	 */
2831
+	public function __sleep()
2832
+	{
2833
+		// remove the reg form and the checkout
2834
+		return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2835
+	}
2836 2836
 }
Please login to merge, or discard this patch.
Spacing   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
         $this->request              = EED_Single_Page_Checkout::getRequest();
127 127
         $this->_slug                = 'payment_options';
128 128
         $this->_name                = esc_html__('Payment Options', 'event_espresso');
129
-        $this->_template            = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
129
+        $this->_template            = SPCO_REG_STEPS_PATH.$this->_slug.'/payment_options_main.template.php';
130 130
         $this->checkout             = $checkout;
131 131
         $this->payment_method_model = EEM_Payment_Method::instance();
132 132
         $this->_reset_success_message();
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
      */
169 169
     public function translate_js_strings()
170 170
     {
171
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
171
+        EE_Registry::$i18n_js_strings['no_payment_method'] = esc_html__(
172 172
             'Please select a method of payment in order to continue.',
173 173
             'event_espresso'
174 174
         );
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
             'A valid method of payment could not be determined. Please refresh the page and try again.',
177 177
             'event_espresso'
178 178
         );
179
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
179
+        EE_Registry::$i18n_js_strings['forwarding_to_offsite'] = esc_html__(
180 180
             'Forwarding to Secure Payment Provider.',
181 181
             'event_espresso'
182 182
         );
@@ -192,7 +192,7 @@  discard block
 block discarded – undo
192 192
     {
193 193
         $transaction = $this->checkout->transaction;
194 194
         // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
195
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
195
+        if ( ! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
196 196
             return;
197 197
         }
198 198
         foreach (
@@ -305,18 +305,18 @@  discard block
 block discarded – undo
305 305
                 continue;
306 306
             }
307 307
             // has this registration lost it's space ?
308
-            if (isset($ejected_registrations[ $REG_ID ])) {
308
+            if (isset($ejected_registrations[$REG_ID])) {
309 309
                 if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
310
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
310
+                    $sold_out_events[$registration->event()->ID()] = $registration->event();
311 311
                 } else {
312
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
312
+                    $insufficient_spaces_available[$registration->event()->ID()] = $registration->event();
313 313
                 }
314 314
                 continue;
315 315
             }
316 316
             // event requires admin approval
317 317
             if ($registration->status_ID() === RegStatus::AWAITING_REVIEW) {
318 318
                 // add event to list of events with pre-approval reg status
319
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
319
+                $registrations_requiring_pre_approval[$REG_ID] = $registration;
320 320
                 do_action(
321 321
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
322 322
                     $registration->event(),
@@ -333,7 +333,7 @@  discard block
 block discarded – undo
333 333
                 )
334 334
             ) {
335 335
                 // add event to list of events that are sold out
336
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
336
+                $sold_out_events[$registration->event()->ID()] = $registration->event();
337 337
                 do_action(
338 338
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
339 339
                     $registration->event(),
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
             }
344 344
             // are they allowed to pay now and is there monies owing?
345 345
             if ($registration->owes_monies_and_can_pay()) {
346
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
346
+                $registrations_requiring_payment[$REG_ID] = $registration;
347 347
                 do_action(
348 348
                     'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
349 349
                     $registration->event(),
@@ -354,28 +354,28 @@  discard block
 block discarded – undo
354 354
                       && $registration->status_ID() !== RegStatus::AWAITING_REVIEW
355 355
                       && $registration->ticket()->is_free()
356 356
             ) {
357
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
357
+                $registrations_for_free_events[$registration->ticket()->ID()] = $registration;
358 358
             }
359 359
         }
360 360
         $subsections = [];
361 361
         // now decide which template to load
362
-        if (! empty($sold_out_events)) {
362
+        if ( ! empty($sold_out_events)) {
363 363
             $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
364 364
         }
365
-        if (! empty($insufficient_spaces_available)) {
365
+        if ( ! empty($insufficient_spaces_available)) {
366 366
             $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
367 367
                 $insufficient_spaces_available
368 368
             );
369 369
         }
370
-        if (! empty($registrations_requiring_pre_approval)) {
370
+        if ( ! empty($registrations_requiring_pre_approval)) {
371 371
             $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
372 372
                 $registrations_requiring_pre_approval
373 373
             );
374 374
         }
375
-        if (! empty($registrations_for_free_events)) {
375
+        if ( ! empty($registrations_for_free_events)) {
376 376
             $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
377 377
         }
378
-        if (! empty($registrations_requiring_payment)) {
378
+        if ( ! empty($registrations_requiring_payment)) {
379 379
             if ($this->checkout->amount_owing > 0) {
380 380
                 // check for method_of_payment before setting up line items
381 381
                 // so that surcharges can be applied to the line items based on the selected method of payment
@@ -404,7 +404,7 @@  discard block
 block discarded – undo
404 404
                         ['registrations' => $registrations]
405 405
                     )
406 406
                 );
407
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
407
+                $this->checkout->amount_owing = $filtered_line_item_tree->total();
408 408
                 $this->_apply_registration_payments_to_amount_owing($registrations);
409 409
             }
410 410
             $no_payment_required = false;
@@ -449,13 +449,13 @@  discard block
 block discarded – undo
449 449
     public static function add_spco_line_item_filters(
450 450
         EE_Line_Item_Filter_Collection $line_item_filter_collection
451 451
     ): EE_Line_Item_Filter_Collection {
452
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
452
+        if ( ! EE_Registry::instance()->SSN instanceof EE_Session) {
453 453
             return $line_item_filter_collection;
454 454
         }
455
-        if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
455
+        if ( ! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
456 456
             return $line_item_filter_collection;
457 457
         }
458
-        if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
458
+        if ( ! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
459 459
             return $line_item_filter_collection;
460 460
         }
461 461
         $line_item_filter_collection->add(
@@ -494,8 +494,8 @@  discard block
 block discarded – undo
494 494
         );
495 495
         foreach ($registrations as $REG_ID => $registration) {
496 496
             // has this registration lost it's space ?
497
-            if (isset($ejected_registrations[ $REG_ID ])) {
498
-                unset($registrations[ $REG_ID ]);
497
+            if (isset($ejected_registrations[$REG_ID])) {
498
+                unset($registrations[$REG_ID]);
499 499
             }
500 500
         }
501 501
         return $registrations;
@@ -544,25 +544,25 @@  discard block
 block discarded – undo
544 544
             }
545 545
             $EVT_ID = $registration->event_ID();
546 546
             $ticket = $registration->ticket();
547
-            if (! isset($tickets_remaining[ $ticket->ID() ])) {
548
-                $tickets_remaining[ $ticket->ID() ] = $ticket->remaining();
547
+            if ( ! isset($tickets_remaining[$ticket->ID()])) {
548
+                $tickets_remaining[$ticket->ID()] = $ticket->remaining();
549 549
             }
550
-            if ($tickets_remaining[ $ticket->ID() ] > 0) {
551
-                if (! isset($event_reg_count[ $EVT_ID ])) {
552
-                    $event_reg_count[ $EVT_ID ] = 0;
550
+            if ($tickets_remaining[$ticket->ID()] > 0) {
551
+                if ( ! isset($event_reg_count[$EVT_ID])) {
552
+                    $event_reg_count[$EVT_ID] = 0;
553 553
                 }
554
-                $event_reg_count[ $EVT_ID ]++;
555
-                if (! isset($event_spaces_remaining[ $EVT_ID ])) {
556
-                    $event_spaces_remaining[ $EVT_ID ] = $registration->event()->spaces_remaining_for_sale();
554
+                $event_reg_count[$EVT_ID]++;
555
+                if ( ! isset($event_spaces_remaining[$EVT_ID])) {
556
+                    $event_spaces_remaining[$EVT_ID] = $registration->event()->spaces_remaining_for_sale();
557 557
                 }
558 558
             }
559 559
             if (
560 560
                 $revisit
561
-                && ($tickets_remaining[ $ticket->ID() ] === 0
562
-                    || $event_reg_count[ $EVT_ID ] > $event_spaces_remaining[ $EVT_ID ]
561
+                && ($tickets_remaining[$ticket->ID()] === 0
562
+                    || $event_reg_count[$EVT_ID] > $event_spaces_remaining[$EVT_ID]
563 563
                 )
564 564
             ) {
565
-                $ejected_registrations[ $REG_ID ] = $registration->event();
565
+                $ejected_registrations[$REG_ID] = $registration->event();
566 566
                 if ($registration->status_ID() !== RegStatus::WAIT_LIST) {
567 567
                     /** @type EE_Registration_Processor $registration_processor */
568 568
                     $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
@@ -609,7 +609,7 @@  discard block
 block discarded – undo
609 609
         foreach ($sold_out_events_array as $sold_out_event) {
610 610
             $sold_out_events .= EEH_HTML::li(
611 611
                 EEH_HTML::span(
612
-                    '  ' . $sold_out_event->name(),
612
+                    '  '.$sold_out_event->name(),
613 613
                     '',
614 614
                     'dashicons dashicons-marker ee-icon-size-16 pink-text'
615 615
                 )
@@ -665,7 +665,7 @@  discard block
 block discarded – undo
665 665
         foreach ($insufficient_spaces_events_array as $event) {
666 666
             if ($event instanceof EE_Event) {
667 667
                 $insufficient_space_events .= EEH_HTML::li(
668
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
668
+                    EEH_HTML::span(' '.$event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
669 669
                 );
670 670
             }
671 671
         }
@@ -713,7 +713,7 @@  discard block
 block discarded – undo
713 713
         $events_requiring_pre_approval = [];
714 714
         foreach ($registrations_requiring_pre_approval as $registration) {
715 715
             if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
716
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
716
+                $events_requiring_pre_approval[$registration->event()->ID()] = EEH_HTML::li(
717 717
                     EEH_HTML::span(
718 718
                         '',
719 719
                         '',
@@ -843,7 +843,7 @@  discard block
 block discarded – undo
843 843
     {
844 844
         return new EE_Form_Section_Proper(
845 845
             [
846
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
846
+                'html_id'         => 'ee-'.$this->slug().'-extra-hidden-inputs',
847 847
                 'layout_strategy' => new EE_Div_Per_Section_Layout(),
848 848
                 'subsections'     => [
849 849
                     'spco_no_payment_required' => new EE_Hidden_Input(
@@ -881,7 +881,7 @@  discard block
 block discarded – undo
881 881
                 $payments += $registration->registration_payments();
882 882
             }
883 883
         }
884
-        if (! empty($payments)) {
884
+        if ( ! empty($payments)) {
885 885
             foreach ($payments as $payment) {
886 886
                 if ($payment instanceof EE_Registration_Payment) {
887 887
                     $this->checkout->amount_owing -= $payment->amount();
@@ -955,7 +955,7 @@  discard block
 block discarded – undo
955 955
             );
956 956
         }
957 957
         // switch up header depending on number of available payment methods
958
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
958
+        $payment_method_header = count($this->checkout->available_payment_methods) > 1
959 959
             ? apply_filters(
960 960
                 'FHEE__registration_page_payment_options__method_of_payment_hdr',
961 961
                 esc_html__('Please Select Your Method of Payment', 'event_espresso')
@@ -985,14 +985,14 @@  discard block
 block discarded – undo
985 985
         ];
986 986
         // loop through payment methods
987 987
         foreach ($this->checkout->available_payment_methods as $payment_method) {
988
-            if (! $payment_method instanceof EE_Payment_Method) {
988
+            if ( ! $payment_method instanceof EE_Payment_Method) {
989 989
                 continue;
990 990
             }
991 991
 
992 992
             $payment_method_button = EEH_HTML::img(
993 993
                 $payment_method->button_url(),
994 994
                 $payment_method->name(),
995
-                'spco-payment-method-' . $payment_method->slug() . '-btn-img',
995
+                'spco-payment-method-'.$payment_method->slug().'-btn-img',
996 996
                 'spco-payment-method-btn-img'
997 997
             );
998 998
             // check if any payment methods are set as default
@@ -1007,11 +1007,11 @@  discard block
 block discarded – undo
1007 1007
             ) {
1008 1008
                 $this->checkout->selected_method_of_payment = $payment_method->slug();
1009 1009
                 $this->_save_selected_method_of_payment();
1010
-                $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1010
+                $default_payment_method_option[$payment_method->slug()] = $payment_method_button;
1011 1011
             } else {
1012
-                $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1012
+                $available_payment_method_options[$payment_method->slug()] = $payment_method_button;
1013 1013
             }
1014
-            $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1014
+            $payment_methods_billing_info[$payment_method->slug().'-info'] =
1015 1015
                 $this->_payment_method_billing_info(
1016 1016
                     $payment_method
1017 1017
                 );
@@ -1023,7 +1023,7 @@  discard block
 block discarded – undo
1023 1023
         $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1024 1024
             $available_payment_method_options
1025 1025
         );
1026
-        $available_payment_methods                              += $payment_methods_billing_info;
1026
+        $available_payment_methods += $payment_methods_billing_info;
1027 1027
         // build the available payment methods form
1028 1028
         return new EE_Form_Section_Proper(
1029 1029
             [
@@ -1042,7 +1042,7 @@  discard block
 block discarded – undo
1042 1042
      */
1043 1043
     protected function _get_available_payment_methods(): array
1044 1044
     {
1045
-        if (! empty($this->checkout->available_payment_methods)) {
1045
+        if ( ! empty($this->checkout->available_payment_methods)) {
1046 1046
             return $this->checkout->available_payment_methods;
1047 1047
         }
1048 1048
         $available_payment_methods = [];
@@ -1053,7 +1053,7 @@  discard block
 block discarded – undo
1053 1053
         );
1054 1054
         foreach ($payment_methods as $payment_method) {
1055 1055
             if ($payment_method instanceof EE_Payment_Method) {
1056
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1056
+                $available_payment_methods[$payment_method->slug()] = $payment_method;
1057 1057
             }
1058 1058
         }
1059 1059
         return $available_payment_methods;
@@ -1141,7 +1141,7 @@  discard block
 block discarded – undo
1141 1141
         );
1142 1142
         return new EE_Form_Section_Proper(
1143 1143
             [
1144
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1144
+                'html_id'         => 'spco-payment-method-info-'.$payment_method->slug(),
1145 1145
                 'html_class'      => 'spco-payment-method-info-dv',
1146 1146
                 // only display the selected or default PM
1147 1147
                 'html_style'      => $currently_selected ? '' : 'display:none;',
@@ -1168,7 +1168,7 @@  discard block
 block discarded – undo
1168 1168
         // how have they chosen to pay?
1169 1169
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1170 1170
         $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1171
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1171
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1172 1172
             return false;
1173 1173
         }
1174 1174
         if (
@@ -1338,7 +1338,7 @@  discard block
 block discarded – undo
1338 1338
      */
1339 1339
     public function switch_payment_method()
1340 1340
     {
1341
-        if (! $this->_verify_payment_method_is_set()) {
1341
+        if ( ! $this->_verify_payment_method_is_set()) {
1342 1342
             return false;
1343 1343
         }
1344 1344
         if (
@@ -1472,7 +1472,7 @@  discard block
 block discarded – undo
1472 1472
             }
1473 1473
         }
1474 1474
         // verify payment method
1475
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1475
+        if ( ! $this->checkout->payment_method instanceof EE_Payment_Method) {
1476 1476
             // get payment method for selected method of payment
1477 1477
             $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1478 1478
         }
@@ -1497,7 +1497,7 @@  discard block
 block discarded – undo
1497 1497
      */
1498 1498
     public function save_payer_details_via_ajax()
1499 1499
     {
1500
-        if (! $this->_verify_payment_method_is_set()) {
1500
+        if ( ! $this->_verify_payment_method_is_set()) {
1501 1501
             return;
1502 1502
         }
1503 1503
         // generate billing form for selected method of payment if it hasn't been done already
@@ -1507,7 +1507,7 @@  discard block
 block discarded – undo
1507 1507
             );
1508 1508
         }
1509 1509
         // generate primary attendee from payer info if applicable
1510
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1510
+        if ( ! $this->checkout->transaction_has_primary_registrant()) {
1511 1511
             $attendee = $this->_create_attendee_from_request_data();
1512 1512
             if ($attendee instanceof EE_Attendee) {
1513 1513
                 foreach ($this->checkout->transaction->registrations() as $registration) {
@@ -1537,7 +1537,7 @@  discard block
 block discarded – undo
1537 1537
     {
1538 1538
         // get State ID
1539 1539
         $STA_ID = $this->request->getRequestParam('state');
1540
-        if (! empty($STA_ID)) {
1540
+        if ( ! empty($STA_ID)) {
1541 1541
             // can we get state object from name ?
1542 1542
             EE_Registry::instance()->load_model('State');
1543 1543
             $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
@@ -1545,7 +1545,7 @@  discard block
 block discarded – undo
1545 1545
         }
1546 1546
         // get Country ISO
1547 1547
         $CNT_ISO = $this->request->getRequestParam('country');
1548
-        if (! empty($CNT_ISO)) {
1548
+        if ( ! empty($CNT_ISO)) {
1549 1549
             // can we get country object from name ?
1550 1550
             EE_Registry::instance()->load_model('Country');
1551 1551
             $country = EEM_Country::instance()->get_col(
@@ -1724,7 +1724,7 @@  discard block
 block discarded – undo
1724 1724
     protected function _maybe_set_completed(EE_Payment $payment)
1725 1725
     {
1726 1726
         // Do we need to redirect them? If so, there's more work to be done.
1727
-        if (! $payment->redirect_url()) {
1727
+        if ( ! $payment->redirect_url()) {
1728 1728
             $this->set_completed();
1729 1729
         }
1730 1730
     }
@@ -1780,7 +1780,7 @@  discard block
 block discarded – undo
1780 1780
     private function _process_payment()
1781 1781
     {
1782 1782
         // basically confirm that the event hasn't sold out since they hit the page
1783
-        if (! $this->_last_second_ticket_verifications()) {
1783
+        if ( ! $this->_last_second_ticket_verifications()) {
1784 1784
             return null;
1785 1785
         }
1786 1786
         // ya gotta make a choice man
@@ -1791,7 +1791,7 @@  discard block
 block discarded – undo
1791 1791
             return null;
1792 1792
         }
1793 1793
         // get EE_Payment_Method object
1794
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1794
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1795 1795
             return null;
1796 1796
         }
1797 1797
         // setup billing form
@@ -1800,12 +1800,12 @@  discard block
 block discarded – undo
1800 1800
                 $this->checkout->payment_method
1801 1801
             );
1802 1802
             // bad billing form ?
1803
-            if (! $this->_billing_form_is_valid()) {
1803
+            if ( ! $this->_billing_form_is_valid()) {
1804 1804
                 return null;
1805 1805
             }
1806 1806
         }
1807 1807
         // ensure primary registrant has been fully processed
1808
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1808
+        if ( ! $this->_setup_primary_registrant_prior_to_payment()) {
1809 1809
             return null;
1810 1810
         }
1811 1811
         // if session is close to expiring (under 10 minutes by default)
@@ -1859,7 +1859,7 @@  discard block
 block discarded – undo
1859 1859
     protected function _last_second_ticket_verifications(): bool
1860 1860
     {
1861 1861
         // don't bother re-validating if not a return visit
1862
-        if (! $this->checkout->revisit) {
1862
+        if ( ! $this->checkout->revisit) {
1863 1863
             return true;
1864 1864
         }
1865 1865
         $registrations = $this->checkout->transaction->registrations();
@@ -1907,7 +1907,7 @@  discard block
 block discarded – undo
1907 1907
             $this->_get_payment_method_for_selected_method_of_payment()
1908 1908
         );
1909 1909
         $html                        = $payment_method_billing_info->get_html();
1910
-        $html                        .= $this->checkout->redirect_form;
1910
+        $html .= $this->checkout->redirect_form;
1911 1911
         /** @var ResponseInterface $response */
1912 1912
         $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1913 1913
         $response->addOutput($html);
@@ -1922,7 +1922,7 @@  discard block
 block discarded – undo
1922 1922
      */
1923 1923
     private function _billing_form_is_valid(): bool
1924 1924
     {
1925
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1925
+        if ( ! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1926 1926
             return true;
1927 1927
         }
1928 1928
         if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
@@ -2042,16 +2042,16 @@  discard block
 block discarded – undo
2042 2042
     private function _capture_primary_registration_data_from_billing_form(): bool
2043 2043
     {
2044 2044
         $primary_registration = $this->checkout->transaction->primary_registration();
2045
-        if (! $this->validatePrimaryRegistration($primary_registration)) {
2045
+        if ( ! $this->validatePrimaryRegistration($primary_registration)) {
2046 2046
             return false;
2047 2047
         }
2048 2048
 
2049 2049
         $primary_attendee = $this->getPrimaryAttendee($primary_registration);
2050
-        if (! $this->validatePrimaryAttendee($primary_attendee)) {
2050
+        if ( ! $this->validatePrimaryAttendee($primary_attendee)) {
2051 2051
             return false;
2052 2052
         }
2053 2053
 
2054
-        if (! $this->addAttendeeToPrimaryRegistration($primary_attendee, $primary_registration)) {
2054
+        if ( ! $this->addAttendeeToPrimaryRegistration($primary_attendee, $primary_registration)) {
2055 2055
             return false;
2056 2056
         }
2057 2057
         // both the primary registration and primary attendee objects should be valid entities at this point
@@ -2218,10 +2218,10 @@  discard block
 block discarded – undo
2218 2218
             return null;
2219 2219
         }
2220 2220
         // get EE_Payment_Method object
2221
-        $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ]
2221
+        $payment_method = $this->checkout->available_payment_methods[$this->checkout->selected_method_of_payment]
2222 2222
             ?? $this->payment_method_model->get_one_by_slug($this->checkout->selected_method_of_payment);
2223 2223
         // verify $payment_method
2224
-        if (! $payment_method instanceof EE_Payment_Method) {
2224
+        if ( ! $payment_method instanceof EE_Payment_Method) {
2225 2225
             // not a payment
2226 2226
             EE_Error::add_error(
2227 2227
                 sprintf(
@@ -2239,7 +2239,7 @@  discard block
 block discarded – undo
2239 2239
             return null;
2240 2240
         }
2241 2241
         // and verify it has a valid Payment_Method Type object
2242
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2242
+        if ( ! $payment_method->type_obj() instanceof EE_PMT_Base) {
2243 2243
             // not a payment
2244 2244
             EE_Error::add_error(
2245 2245
                 sprintf(
@@ -2271,7 +2271,7 @@  discard block
 block discarded – undo
2271 2271
         $this->checkout->transaction->save();
2272 2272
         /** @var PaymentProcessor $payment_processor */
2273 2273
         $payment_processor = LoaderFactory::getShared(PaymentProcessor::class);
2274
-        if (! $payment_processor instanceof PaymentProcessor) {
2274
+        if ( ! $payment_processor instanceof PaymentProcessor) {
2275 2275
             return null;
2276 2276
         }
2277 2277
         /** @var EE_Transaction_Processor $transaction_processor */
@@ -2365,7 +2365,7 @@  discard block
 block discarded – undo
2365 2365
             return true;
2366 2366
         }
2367 2367
         // verify payment object
2368
-        if (! $payment instanceof EE_Payment) {
2368
+        if ( ! $payment instanceof EE_Payment) {
2369 2369
             // not a payment
2370 2370
             EE_Error::add_error(
2371 2371
                 sprintf(
@@ -2410,7 +2410,7 @@  discard block
 block discarded – undo
2410 2410
                 // and lastly, let's bump the payment status to pending
2411 2411
                 $payment->set_status(EEM_Payment::status_id_pending);
2412 2412
                 $payment->save();
2413
-            } elseif (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2413
+            } elseif ( ! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2414 2414
                 // User shouldn't be redirected. So let's process it here.
2415 2415
                 // $this->_setup_redirect_for_next_step();
2416 2416
                 $this->checkout->continue_reg = false;
@@ -2486,7 +2486,7 @@  discard block
 block discarded – undo
2486 2486
                     return false;
2487 2487
                 // bad payment
2488 2488
                 case EEM_Payment::status_id_failed:
2489
-                    if (! empty($msg)) {
2489
+                    if ( ! empty($msg)) {
2490 2490
                         EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2491 2491
                         return false;
2492 2492
                     }
@@ -2545,18 +2545,18 @@  discard block
 block discarded – undo
2545 2545
         // how have they chosen to pay?
2546 2546
         $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2547 2547
         // get EE_Payment_Method object
2548
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2548
+        if ( ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2549 2549
             $this->checkout->continue_reg = false;
2550 2550
             return false;
2551 2551
         }
2552
-        if (! $this->checkout->payment_method->is_off_site()) {
2552
+        if ( ! $this->checkout->payment_method->is_off_site()) {
2553 2553
             return false;
2554 2554
         }
2555 2555
         $this->_validate_offsite_return();
2556 2556
         // verify TXN
2557 2557
         if ($this->checkout->transaction instanceof EE_Transaction) {
2558 2558
             $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2559
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2559
+            if ( ! $gateway instanceof EE_Offsite_Gateway) {
2560 2560
                 $this->checkout->continue_reg = false;
2561 2561
                 return false;
2562 2562
             }
@@ -2570,7 +2570,7 @@  discard block
 block discarded – undo
2570 2570
                 $this->checkout->payment = $payment;
2571 2571
                 // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2572 2572
                 // because we will complete this step during the IPN processing then
2573
-                if (! $this->handle_IPN_in_this_request()) {
2573
+                if ( ! $this->handle_IPN_in_this_request()) {
2574 2574
                     $this->set_completed();
2575 2575
                 }
2576 2576
                 return true;
@@ -2663,13 +2663,13 @@  discard block
 block discarded – undo
2663 2663
      */
2664 2664
     private function _redirect_wayward_request(?EE_Registration $primary_registrant)
2665 2665
     {
2666
-        if (! $primary_registrant instanceof EE_Registration) {
2666
+        if ( ! $primary_registrant instanceof EE_Registration) {
2667 2667
             // try redirecting based on the current TXN
2668 2668
             $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2669 2669
                 ? $this->checkout->transaction->primary_registration()
2670 2670
                 : null;
2671 2671
         }
2672
-        if (! $primary_registrant instanceof EE_Registration) {
2672
+        if ( ! $primary_registrant instanceof EE_Registration) {
2673 2673
             EE_Error::add_error(
2674 2674
                 sprintf(
2675 2675
                     esc_html__(
@@ -2735,7 +2735,7 @@  discard block
 block discarded – undo
2735 2735
             $payment = $this->checkout->transaction->last_payment();
2736 2736
             // $payment_source = 'last_payment after Exception';
2737 2737
             // but if we STILL don't have a payment object
2738
-            if (! $payment instanceof EE_Payment) {
2738
+            if ( ! $payment instanceof EE_Payment) {
2739 2739
                 // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2740 2740
                 $this->_handle_payment_processor_exception($e);
2741 2741
             }
Please login to merge, or discard this patch.
PaymentMethods/PayPalCommerce/tools/extra_meta/PayPalExtraMetaManager.php 2 patches
Indentation   +388 added lines, -388 removed lines patch added patch discarded remove patch
@@ -23,392 +23,392 @@
 block discarded – undo
23 23
  */
24 24
 class PayPalExtraMetaManager
25 25
 {
26
-    private static ?OpenSSLEncryption $encryptor = null;
27
-
28
-    private static ?PayPalExtraMeta $pay_pal_extra_meta = null;
29
-
30
-
31
-    /**
32
-     * Get PayPal extra meta helper.
33
-     *
34
-     * @param EE_Payment_Method $paypal_pm
35
-     * @return PayPalExtraMeta
36
-     * @throws EE_Error
37
-     * @throws ReflectionException
38
-     */
39
-    public static function extraMeta(EE_Payment_Method $paypal_pm): PayPalExtraMeta
40
-    {
41
-        $post_params = LoaderFactory::getLoader()->getShared(RequestInterface::class)->postParams();
42
-        if (
43
-            ! PayPalExtraMetaManager::$pay_pal_extra_meta instanceof PayPalExtraMeta
44
-            || PayPalExtraMetaManager::$pay_pal_extra_meta->pm->slug() !== $paypal_pm->slug()
45
-            || (! empty($post_params['sandbox_mode'])
46
-                && in_array($post_params['sandbox_mode'], ['0', '1'], true)
47
-                && $paypal_pm->debug_mode() !== (bool) $post_params['sandbox_mode']
48
-            )
49
-        ) {
50
-            PayPalExtraMetaManager::$pay_pal_extra_meta = new PayPalExtraMeta($paypal_pm);
51
-        }
52
-        return PayPalExtraMetaManager::$pay_pal_extra_meta;
53
-    }
54
-
55
-
56
-    /**
57
-     * Get OpenSSLEncryption instance.
58
-     *
59
-     * @return OpenSSLEncryption
60
-     */
61
-    public static function encryptor(): OpenSSLEncryption
62
-    {
63
-        if (! PayPalExtraMetaManager::$encryptor instanceof OpenSSLEncryption) {
64
-            PayPalExtraMetaManager::$encryptor = LoaderFactory::getLoader()->getShared(
65
-                OpenSSLEncryption::class,
66
-                [new Base64Encoder()]
67
-            );
68
-        }
69
-        return PayPalExtraMetaManager::$encryptor;
70
-    }
71
-
72
-
73
-    /**
74
-     * Get payment method option/extra meta
75
-     *
76
-     * @param EE_Payment_Method $paypal_pm
77
-     * @param string            $option_name
78
-     * @return mixed
79
-     * @throws EE_Error
80
-     * @throws ReflectionException
81
-     */
82
-    public static function getPmOption(EE_Payment_Method $paypal_pm, string $option_name)
83
-    {
84
-        $option_value = PayPalExtraMetaManager::extraMeta($paypal_pm)->getOption($option_name);
85
-        // Decrypt the encrypted options.
86
-        if (
87
-            $option_name === Domain::META_KEY_ACCESS_TOKEN
88
-            || $option_name === Domain::META_KEY_PARTNER_MERCHANT_ID
89
-            || $option_name === Domain::META_KEY_CLIENT_SECRET
90
-        ) {
91
-            $option_value = PayPalExtraMetaManager::decryptString($option_value, $paypal_pm);
92
-        }
93
-        return $option_value;
94
-    }
95
-
96
-
97
-    /**
98
-     * Save payment method option/extra meta
99
-     *
100
-     * @param EE_Payment_Method $paypal_pm
101
-     * @param string            $option_name
102
-     * @param                   $option_value
103
-     * @return bool
104
-     * @throws EE_Error
105
-     * @throws ReflectionException
106
-     */
107
-    public static function savePmOption(EE_Payment_Method $paypal_pm, string $option_name, $option_value): bool
108
-    {
109
-        return PayPalExtraMetaManager::extraMeta($paypal_pm)->saveOption($option_name, $option_value);
110
-    }
111
-
112
-
113
-    /**
114
-     * Save a list of payment method options/extra meta.
115
-     *
116
-     * @param EE_Payment_Method $paypal_pm
117
-     * @param array             $options_list
118
-     * @return bool
119
-     * @throws EE_Error
120
-     * @throws ReflectionException
121
-     */
122
-    public static function savePmOptions(EE_Payment_Method $paypal_pm, array $options_list): bool
123
-    {
124
-        return PayPalExtraMetaManager::extraMeta($paypal_pm)->saveBatch($options_list);
125
-    }
126
-
127
-
128
-    /**
129
-     * Delete payment method option/extra meta
130
-     *
131
-     * @param EE_Payment_Method $paypal_pm
132
-     * @param string            $option_name
133
-     * @return bool
134
-     * @throws EE_Error
135
-     * @throws ReflectionException
136
-     */
137
-    public static function deletePmOption(EE_Payment_Method $paypal_pm, string $option_name): bool
138
-    {
139
-        return PayPalExtraMetaManager::extraMeta($paypal_pm)->deleteOption($option_name);
140
-    }
141
-
142
-
143
-    /**
144
-     * Get all options for payment method.
145
-     *
146
-     * @param EE_Payment_Method $paypal_pm
147
-     * @return array
148
-     * @throws EE_Error
149
-     * @throws ReflectionException
150
-     */
151
-    public static function getAllData(EE_Payment_Method $paypal_pm): array
152
-    {
153
-        return PayPalExtraMetaManager::extraMeta($paypal_pm)->getMetaData();
154
-    }
155
-
156
-
157
-    /**
158
-     * Delete payment method metadata.
159
-     *
160
-     * @param EE_Payment_Method $paypal_pm
161
-     * @return bool
162
-     * @throws EE_Error
163
-     * @throws ReflectionException
164
-     */
165
-    public static function deleteData(EE_Payment_Method $paypal_pm): bool
166
-    {
167
-        return PayPalExtraMetaManager::extraMeta($paypal_pm)->deleteMetaData();
168
-    }
169
-
170
-
171
-    /**
172
-     * Delete all payment method metadata.
173
-     *
174
-     * @param EE_Payment_Method $paypal_pm
175
-     * @return bool
176
-     * @throws EE_Error
177
-     * @throws ReflectionException
178
-     */
179
-    public static function deleteAllData(EE_Payment_Method $paypal_pm): bool
180
-    {
181
-        return PayPalExtraMetaManager::extraMeta($paypal_pm)->deleteAllMetaData();
182
-    }
183
-
184
-    /**
185
-     * Save the debug mode option if it changed.
186
-     *
187
-     * @param EE_Payment_Method $paypal_pm
188
-     * @param array             $request_data
189
-     * @return bool             Updated or not.
190
-     * @throws EE_Error
191
-     * @throws ReflectionException
192
-     */
193
-    public static function updateDebugMode(EE_Payment_Method $paypal_pm, array $request_data): bool
194
-    {
195
-        if (
196
-            isset($request_data['sandbox_mode'])
197
-            && in_array($request_data['sandbox_mode'], ['0', '1'], true)
198
-            && $paypal_pm->debug_mode() !== (bool) $request_data['sandbox_mode']
199
-        ) {
200
-            try {
201
-                $paypal_pm->save(['PMD_debug_mode' => (bool) $request_data['sandbox_mode']]);
202
-            } catch (EE_Error $e) {
203
-                PayPalLogger::errorLog(
204
-                    sprintf(
205
-                        esc_html__('Note, debug mode not saved ! %1$s', 'event_espresso'),
206
-                        $e->getMessage()
207
-                    ),
208
-                    ['request_data' => $request_data, 'trace' => $e->getTrace()],
209
-                    $paypal_pm
210
-                );
211
-                return false;
212
-            }
213
-            return true;
214
-        }
215
-        return false;
216
-    }
217
-
218
-
219
-    /**
220
-     * Save partner access token and parameters.
221
-     *
222
-     * @param EE_Payment_Method $paypal_pm
223
-     * @param array             $response
224
-     * @return bool
225
-     * @throws EE_Error
226
-     * @throws ReflectionException
227
-     */
228
-    public static function savePartnerAccessToken(EE_Payment_Method $paypal_pm, array $response): bool
229
-    {
230
-        $paypal_data         = [];
231
-        $expected_parameters = [
232
-            Domain::META_KEY_ACCESS_TOKEN,
233
-            Domain::META_KEY_TOKEN_EXPIRES_IN,
234
-            Domain::META_KEY_APP_ID,
235
-            Domain::META_KEY_PARTNER_CLIENT_ID,
236
-            Domain::META_KEY_PARTNER_MERCHANT_ID,
237
-            Domain::META_KEY_BN_CODE,
238
-        ];
239
-        foreach ($expected_parameters as $api_key) {
240
-            if (! isset($response[ $api_key ])) {
241
-                // Don't want to try saving data that doesn't exist.
242
-                continue;
243
-            }
244
-            try {
245
-                switch ($api_key) {
246
-                    case Domain::META_KEY_ACCESS_TOKEN:
247
-                    case Domain::META_KEY_PARTNER_MERCHANT_ID:
248
-                        $paypal_data[ $api_key ] = PayPalExtraMetaManager::encryptString(
249
-                            $response[ $api_key ],
250
-                            $paypal_pm
251
-                        );
252
-                        break;
253
-                    case Domain::META_KEY_TOKEN_EXPIRES_IN:
254
-                        $paypal_data[ $api_key ] = time() + (int) sanitize_key($response[ $api_key ]);
255
-                        break;
256
-                    default:
257
-                        $paypal_data[ $api_key ] = sanitize_text_field($response[ $api_key ]);
258
-                }
259
-            } catch (Exception $e) {
260
-                PayPalLogger::errorLog(
261
-                    $e->getMessage(),
262
-                    ['response' => $response, 'trace' => $e->getTrace()],
263
-                    $paypal_pm
264
-                );
265
-                return false;
266
-            }
267
-        }
268
-        return PayPalExtraMetaManager::savePmOptions($paypal_pm, $paypal_data);
269
-    }
270
-
271
-
272
-    /**
273
-     * Save merchant/seller API credentials.
274
-     *
275
-     * @param EE_Payment_Method $paypal_pm
276
-     * @param array             $response
277
-     * @return bool
278
-     * @throws EE_Error
279
-     * @throws ReflectionException
280
-     */
281
-    public static function saveSellerApiCredentials(EE_Payment_Method $paypal_pm, array $response): bool
282
-    {
283
-        $api_credentials     = [];
284
-        $expected_parameters = [Domain::META_KEY_SELLER_MERCHANT_ID];
285
-        foreach ($expected_parameters as $api_key) {
286
-            if (! isset($response[ $api_key ])) {
287
-                // Don't want to try saving data that doesn't exist.
288
-                continue;
289
-            }
290
-            $api_credentials[ $api_key ] = $response[ $api_key ];
291
-        }
292
-        return PayPalExtraMetaManager::savePmOptions($paypal_pm, $api_credentials);
293
-    }
294
-
295
-
296
-    /**
297
-     * Save other payment method related settings from a data array.
298
-     *
299
-     * @param EE_Payment_Method $paypal_pm
300
-     * @param array             $data
301
-     * @param array             $get_params
302
-     * @return bool
303
-     * @throws EE_Error
304
-     * @throws ReflectionException
305
-     */
306
-    public static function parseAndSaveOptions(EE_Payment_Method $paypal_pm, array $data, array $get_params): bool
307
-    {
308
-        $allowed_checkout_type = 'express_checkout';
309
-        // Did the merchant onboard with PPCP enabled or no.
310
-        if (! empty($get_params['selected_payment']) && $get_params['selected_payment'] === 'PPCP') {
311
-            // Make sure that merchant's account really supports advanced card fields (included in the PPCP scope).
312
-            // Has to include "PPCP_CUSTOM" product for ACDC support. "EXPRESS_CHECKOUT" otherwise.
313
-            if (! empty($data['response']['products'][0]['name'])) {
314
-                foreach ($data['response']['products'] as $product) {
315
-                    if ($product['name'] === 'PPCP_CUSTOM') {
316
-                        // This merchant has PPCP in the products list, so we can enable both (all supported) checkout types.
317
-                        $allowed_checkout_type = 'all';
318
-                        break;
319
-                    }
320
-                }
321
-            }
322
-        }
323
-        // Set the PM option Checkout type, just in case merchant doesn't save PM options manually.
324
-        $checkout_type_setting = $paypal_pm->get_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, true, false);
325
-        if (! $checkout_type_setting || $checkout_type_setting !== $allowed_checkout_type) {
326
-            $paypal_pm->update_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, $allowed_checkout_type);
327
-        }
328
-        // Save the scopes that were authorized.
329
-        if (! empty($data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'])) {
330
-            $scopes = [];
331
-            foreach ($data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'] as $scope) {
332
-                // Scope will look like: 'https://uri.paypal.com/services/payments/partnerfee'
333
-                $split       = explode('/', $scope);
334
-                $split_count = count($split);
335
-                // Get the scope itself.
336
-                $scopes[] = $split[ $split_count - 1 ];
337
-            }
338
-            if (empty($scopes)) {
339
-                // In case the there's a change in how scopes come in just save the list.
340
-                $scopes = $data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'];
341
-            }
342
-            PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_AUTHORIZED_SCOPES, $scopes);
343
-        }
344
-        return PayPalExtraMetaManager::savePmOption(
345
-            $paypal_pm,
346
-            Domain::META_KEY_ALLOWED_CHECKOUT_TYPE,
347
-            $allowed_checkout_type
348
-        );
349
-    }
350
-
351
-
352
-    /**
353
-     * Encrypt a text field.
354
-     *
355
-     * @param string            $text
356
-     * @param EE_Payment_Method $paypal_pm
357
-     * @return string|null
358
-     * @throws Exception
359
-     */
360
-    public static function encryptString(string $text, EE_Payment_Method $paypal_pm): ?string
361
-    {
362
-        // We sure we are getting something ?
363
-        if (! $text) {
364
-            return $text;
365
-        }
366
-
367
-        try {
368
-            // Do encrypt.
369
-            $sanitized_text = sanitize_text_field($text);
370
-            $key_identifier = $paypal_pm->debug_mode()
371
-                ? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
372
-                : PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
373
-            $encrypted      = PayPalExtraMetaManager::encryptor()->encrypt($sanitized_text, $key_identifier);
374
-        } catch (Exception $e) {
375
-            PayPalLogger::errorLog(
376
-                $e->getMessage(),
377
-                ['trace' => $e->getTrace()],
378
-                $paypal_pm
379
-            );
380
-        }
381
-        return $encrypted ?? null;
382
-    }
383
-
384
-
385
-    /**
386
-     * Decrypt a string.
387
-     *
388
-     * @param string            $text
389
-     * @param EE_Payment_Method $paypal_pm
390
-     * @return string
391
-     */
392
-    public static function decryptString(string $text, EE_Payment_Method $paypal_pm): string
393
-    {
394
-        // Are we even getting something ?
395
-        if (! $text) {
396
-            return $text;
397
-        }
398
-        // Try decrypting.
399
-        try {
400
-            $key_identifier = $paypal_pm->debug_mode()
401
-                ? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
402
-                : PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
403
-            $decrypted      = PayPalExtraMetaManager::encryptor()->decrypt($text, $key_identifier);
404
-        } catch (Exception $e) {
405
-            PayPalLogger::errorLog(
406
-                $e->getMessage(),
407
-                ['trace' => $e->getTrace()],
408
-                $paypal_pm
409
-            );
410
-            return $text;
411
-        }
412
-        return $decrypted ?? $text;
413
-    }
26
+	private static ?OpenSSLEncryption $encryptor = null;
27
+
28
+	private static ?PayPalExtraMeta $pay_pal_extra_meta = null;
29
+
30
+
31
+	/**
32
+	 * Get PayPal extra meta helper.
33
+	 *
34
+	 * @param EE_Payment_Method $paypal_pm
35
+	 * @return PayPalExtraMeta
36
+	 * @throws EE_Error
37
+	 * @throws ReflectionException
38
+	 */
39
+	public static function extraMeta(EE_Payment_Method $paypal_pm): PayPalExtraMeta
40
+	{
41
+		$post_params = LoaderFactory::getLoader()->getShared(RequestInterface::class)->postParams();
42
+		if (
43
+			! PayPalExtraMetaManager::$pay_pal_extra_meta instanceof PayPalExtraMeta
44
+			|| PayPalExtraMetaManager::$pay_pal_extra_meta->pm->slug() !== $paypal_pm->slug()
45
+			|| (! empty($post_params['sandbox_mode'])
46
+				&& in_array($post_params['sandbox_mode'], ['0', '1'], true)
47
+				&& $paypal_pm->debug_mode() !== (bool) $post_params['sandbox_mode']
48
+			)
49
+		) {
50
+			PayPalExtraMetaManager::$pay_pal_extra_meta = new PayPalExtraMeta($paypal_pm);
51
+		}
52
+		return PayPalExtraMetaManager::$pay_pal_extra_meta;
53
+	}
54
+
55
+
56
+	/**
57
+	 * Get OpenSSLEncryption instance.
58
+	 *
59
+	 * @return OpenSSLEncryption
60
+	 */
61
+	public static function encryptor(): OpenSSLEncryption
62
+	{
63
+		if (! PayPalExtraMetaManager::$encryptor instanceof OpenSSLEncryption) {
64
+			PayPalExtraMetaManager::$encryptor = LoaderFactory::getLoader()->getShared(
65
+				OpenSSLEncryption::class,
66
+				[new Base64Encoder()]
67
+			);
68
+		}
69
+		return PayPalExtraMetaManager::$encryptor;
70
+	}
71
+
72
+
73
+	/**
74
+	 * Get payment method option/extra meta
75
+	 *
76
+	 * @param EE_Payment_Method $paypal_pm
77
+	 * @param string            $option_name
78
+	 * @return mixed
79
+	 * @throws EE_Error
80
+	 * @throws ReflectionException
81
+	 */
82
+	public static function getPmOption(EE_Payment_Method $paypal_pm, string $option_name)
83
+	{
84
+		$option_value = PayPalExtraMetaManager::extraMeta($paypal_pm)->getOption($option_name);
85
+		// Decrypt the encrypted options.
86
+		if (
87
+			$option_name === Domain::META_KEY_ACCESS_TOKEN
88
+			|| $option_name === Domain::META_KEY_PARTNER_MERCHANT_ID
89
+			|| $option_name === Domain::META_KEY_CLIENT_SECRET
90
+		) {
91
+			$option_value = PayPalExtraMetaManager::decryptString($option_value, $paypal_pm);
92
+		}
93
+		return $option_value;
94
+	}
95
+
96
+
97
+	/**
98
+	 * Save payment method option/extra meta
99
+	 *
100
+	 * @param EE_Payment_Method $paypal_pm
101
+	 * @param string            $option_name
102
+	 * @param                   $option_value
103
+	 * @return bool
104
+	 * @throws EE_Error
105
+	 * @throws ReflectionException
106
+	 */
107
+	public static function savePmOption(EE_Payment_Method $paypal_pm, string $option_name, $option_value): bool
108
+	{
109
+		return PayPalExtraMetaManager::extraMeta($paypal_pm)->saveOption($option_name, $option_value);
110
+	}
111
+
112
+
113
+	/**
114
+	 * Save a list of payment method options/extra meta.
115
+	 *
116
+	 * @param EE_Payment_Method $paypal_pm
117
+	 * @param array             $options_list
118
+	 * @return bool
119
+	 * @throws EE_Error
120
+	 * @throws ReflectionException
121
+	 */
122
+	public static function savePmOptions(EE_Payment_Method $paypal_pm, array $options_list): bool
123
+	{
124
+		return PayPalExtraMetaManager::extraMeta($paypal_pm)->saveBatch($options_list);
125
+	}
126
+
127
+
128
+	/**
129
+	 * Delete payment method option/extra meta
130
+	 *
131
+	 * @param EE_Payment_Method $paypal_pm
132
+	 * @param string            $option_name
133
+	 * @return bool
134
+	 * @throws EE_Error
135
+	 * @throws ReflectionException
136
+	 */
137
+	public static function deletePmOption(EE_Payment_Method $paypal_pm, string $option_name): bool
138
+	{
139
+		return PayPalExtraMetaManager::extraMeta($paypal_pm)->deleteOption($option_name);
140
+	}
141
+
142
+
143
+	/**
144
+	 * Get all options for payment method.
145
+	 *
146
+	 * @param EE_Payment_Method $paypal_pm
147
+	 * @return array
148
+	 * @throws EE_Error
149
+	 * @throws ReflectionException
150
+	 */
151
+	public static function getAllData(EE_Payment_Method $paypal_pm): array
152
+	{
153
+		return PayPalExtraMetaManager::extraMeta($paypal_pm)->getMetaData();
154
+	}
155
+
156
+
157
+	/**
158
+	 * Delete payment method metadata.
159
+	 *
160
+	 * @param EE_Payment_Method $paypal_pm
161
+	 * @return bool
162
+	 * @throws EE_Error
163
+	 * @throws ReflectionException
164
+	 */
165
+	public static function deleteData(EE_Payment_Method $paypal_pm): bool
166
+	{
167
+		return PayPalExtraMetaManager::extraMeta($paypal_pm)->deleteMetaData();
168
+	}
169
+
170
+
171
+	/**
172
+	 * Delete all payment method metadata.
173
+	 *
174
+	 * @param EE_Payment_Method $paypal_pm
175
+	 * @return bool
176
+	 * @throws EE_Error
177
+	 * @throws ReflectionException
178
+	 */
179
+	public static function deleteAllData(EE_Payment_Method $paypal_pm): bool
180
+	{
181
+		return PayPalExtraMetaManager::extraMeta($paypal_pm)->deleteAllMetaData();
182
+	}
183
+
184
+	/**
185
+	 * Save the debug mode option if it changed.
186
+	 *
187
+	 * @param EE_Payment_Method $paypal_pm
188
+	 * @param array             $request_data
189
+	 * @return bool             Updated or not.
190
+	 * @throws EE_Error
191
+	 * @throws ReflectionException
192
+	 */
193
+	public static function updateDebugMode(EE_Payment_Method $paypal_pm, array $request_data): bool
194
+	{
195
+		if (
196
+			isset($request_data['sandbox_mode'])
197
+			&& in_array($request_data['sandbox_mode'], ['0', '1'], true)
198
+			&& $paypal_pm->debug_mode() !== (bool) $request_data['sandbox_mode']
199
+		) {
200
+			try {
201
+				$paypal_pm->save(['PMD_debug_mode' => (bool) $request_data['sandbox_mode']]);
202
+			} catch (EE_Error $e) {
203
+				PayPalLogger::errorLog(
204
+					sprintf(
205
+						esc_html__('Note, debug mode not saved ! %1$s', 'event_espresso'),
206
+						$e->getMessage()
207
+					),
208
+					['request_data' => $request_data, 'trace' => $e->getTrace()],
209
+					$paypal_pm
210
+				);
211
+				return false;
212
+			}
213
+			return true;
214
+		}
215
+		return false;
216
+	}
217
+
218
+
219
+	/**
220
+	 * Save partner access token and parameters.
221
+	 *
222
+	 * @param EE_Payment_Method $paypal_pm
223
+	 * @param array             $response
224
+	 * @return bool
225
+	 * @throws EE_Error
226
+	 * @throws ReflectionException
227
+	 */
228
+	public static function savePartnerAccessToken(EE_Payment_Method $paypal_pm, array $response): bool
229
+	{
230
+		$paypal_data         = [];
231
+		$expected_parameters = [
232
+			Domain::META_KEY_ACCESS_TOKEN,
233
+			Domain::META_KEY_TOKEN_EXPIRES_IN,
234
+			Domain::META_KEY_APP_ID,
235
+			Domain::META_KEY_PARTNER_CLIENT_ID,
236
+			Domain::META_KEY_PARTNER_MERCHANT_ID,
237
+			Domain::META_KEY_BN_CODE,
238
+		];
239
+		foreach ($expected_parameters as $api_key) {
240
+			if (! isset($response[ $api_key ])) {
241
+				// Don't want to try saving data that doesn't exist.
242
+				continue;
243
+			}
244
+			try {
245
+				switch ($api_key) {
246
+					case Domain::META_KEY_ACCESS_TOKEN:
247
+					case Domain::META_KEY_PARTNER_MERCHANT_ID:
248
+						$paypal_data[ $api_key ] = PayPalExtraMetaManager::encryptString(
249
+							$response[ $api_key ],
250
+							$paypal_pm
251
+						);
252
+						break;
253
+					case Domain::META_KEY_TOKEN_EXPIRES_IN:
254
+						$paypal_data[ $api_key ] = time() + (int) sanitize_key($response[ $api_key ]);
255
+						break;
256
+					default:
257
+						$paypal_data[ $api_key ] = sanitize_text_field($response[ $api_key ]);
258
+				}
259
+			} catch (Exception $e) {
260
+				PayPalLogger::errorLog(
261
+					$e->getMessage(),
262
+					['response' => $response, 'trace' => $e->getTrace()],
263
+					$paypal_pm
264
+				);
265
+				return false;
266
+			}
267
+		}
268
+		return PayPalExtraMetaManager::savePmOptions($paypal_pm, $paypal_data);
269
+	}
270
+
271
+
272
+	/**
273
+	 * Save merchant/seller API credentials.
274
+	 *
275
+	 * @param EE_Payment_Method $paypal_pm
276
+	 * @param array             $response
277
+	 * @return bool
278
+	 * @throws EE_Error
279
+	 * @throws ReflectionException
280
+	 */
281
+	public static function saveSellerApiCredentials(EE_Payment_Method $paypal_pm, array $response): bool
282
+	{
283
+		$api_credentials     = [];
284
+		$expected_parameters = [Domain::META_KEY_SELLER_MERCHANT_ID];
285
+		foreach ($expected_parameters as $api_key) {
286
+			if (! isset($response[ $api_key ])) {
287
+				// Don't want to try saving data that doesn't exist.
288
+				continue;
289
+			}
290
+			$api_credentials[ $api_key ] = $response[ $api_key ];
291
+		}
292
+		return PayPalExtraMetaManager::savePmOptions($paypal_pm, $api_credentials);
293
+	}
294
+
295
+
296
+	/**
297
+	 * Save other payment method related settings from a data array.
298
+	 *
299
+	 * @param EE_Payment_Method $paypal_pm
300
+	 * @param array             $data
301
+	 * @param array             $get_params
302
+	 * @return bool
303
+	 * @throws EE_Error
304
+	 * @throws ReflectionException
305
+	 */
306
+	public static function parseAndSaveOptions(EE_Payment_Method $paypal_pm, array $data, array $get_params): bool
307
+	{
308
+		$allowed_checkout_type = 'express_checkout';
309
+		// Did the merchant onboard with PPCP enabled or no.
310
+		if (! empty($get_params['selected_payment']) && $get_params['selected_payment'] === 'PPCP') {
311
+			// Make sure that merchant's account really supports advanced card fields (included in the PPCP scope).
312
+			// Has to include "PPCP_CUSTOM" product for ACDC support. "EXPRESS_CHECKOUT" otherwise.
313
+			if (! empty($data['response']['products'][0]['name'])) {
314
+				foreach ($data['response']['products'] as $product) {
315
+					if ($product['name'] === 'PPCP_CUSTOM') {
316
+						// This merchant has PPCP in the products list, so we can enable both (all supported) checkout types.
317
+						$allowed_checkout_type = 'all';
318
+						break;
319
+					}
320
+				}
321
+			}
322
+		}
323
+		// Set the PM option Checkout type, just in case merchant doesn't save PM options manually.
324
+		$checkout_type_setting = $paypal_pm->get_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, true, false);
325
+		if (! $checkout_type_setting || $checkout_type_setting !== $allowed_checkout_type) {
326
+			$paypal_pm->update_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, $allowed_checkout_type);
327
+		}
328
+		// Save the scopes that were authorized.
329
+		if (! empty($data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'])) {
330
+			$scopes = [];
331
+			foreach ($data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'] as $scope) {
332
+				// Scope will look like: 'https://uri.paypal.com/services/payments/partnerfee'
333
+				$split       = explode('/', $scope);
334
+				$split_count = count($split);
335
+				// Get the scope itself.
336
+				$scopes[] = $split[ $split_count - 1 ];
337
+			}
338
+			if (empty($scopes)) {
339
+				// In case the there's a change in how scopes come in just save the list.
340
+				$scopes = $data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'];
341
+			}
342
+			PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_AUTHORIZED_SCOPES, $scopes);
343
+		}
344
+		return PayPalExtraMetaManager::savePmOption(
345
+			$paypal_pm,
346
+			Domain::META_KEY_ALLOWED_CHECKOUT_TYPE,
347
+			$allowed_checkout_type
348
+		);
349
+	}
350
+
351
+
352
+	/**
353
+	 * Encrypt a text field.
354
+	 *
355
+	 * @param string            $text
356
+	 * @param EE_Payment_Method $paypal_pm
357
+	 * @return string|null
358
+	 * @throws Exception
359
+	 */
360
+	public static function encryptString(string $text, EE_Payment_Method $paypal_pm): ?string
361
+	{
362
+		// We sure we are getting something ?
363
+		if (! $text) {
364
+			return $text;
365
+		}
366
+
367
+		try {
368
+			// Do encrypt.
369
+			$sanitized_text = sanitize_text_field($text);
370
+			$key_identifier = $paypal_pm->debug_mode()
371
+				? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
372
+				: PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
373
+			$encrypted      = PayPalExtraMetaManager::encryptor()->encrypt($sanitized_text, $key_identifier);
374
+		} catch (Exception $e) {
375
+			PayPalLogger::errorLog(
376
+				$e->getMessage(),
377
+				['trace' => $e->getTrace()],
378
+				$paypal_pm
379
+			);
380
+		}
381
+		return $encrypted ?? null;
382
+	}
383
+
384
+
385
+	/**
386
+	 * Decrypt a string.
387
+	 *
388
+	 * @param string            $text
389
+	 * @param EE_Payment_Method $paypal_pm
390
+	 * @return string
391
+	 */
392
+	public static function decryptString(string $text, EE_Payment_Method $paypal_pm): string
393
+	{
394
+		// Are we even getting something ?
395
+		if (! $text) {
396
+			return $text;
397
+		}
398
+		// Try decrypting.
399
+		try {
400
+			$key_identifier = $paypal_pm->debug_mode()
401
+				? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
402
+				: PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
403
+			$decrypted      = PayPalExtraMetaManager::encryptor()->decrypt($text, $key_identifier);
404
+		} catch (Exception $e) {
405
+			PayPalLogger::errorLog(
406
+				$e->getMessage(),
407
+				['trace' => $e->getTrace()],
408
+				$paypal_pm
409
+			);
410
+			return $text;
411
+		}
412
+		return $decrypted ?? $text;
413
+	}
414 414
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -42,7 +42,7 @@  discard block
 block discarded – undo
42 42
         if (
43 43
             ! PayPalExtraMetaManager::$pay_pal_extra_meta instanceof PayPalExtraMeta
44 44
             || PayPalExtraMetaManager::$pay_pal_extra_meta->pm->slug() !== $paypal_pm->slug()
45
-            || (! empty($post_params['sandbox_mode'])
45
+            || ( ! empty($post_params['sandbox_mode'])
46 46
                 && in_array($post_params['sandbox_mode'], ['0', '1'], true)
47 47
                 && $paypal_pm->debug_mode() !== (bool) $post_params['sandbox_mode']
48 48
             )
@@ -60,7 +60,7 @@  discard block
 block discarded – undo
60 60
      */
61 61
     public static function encryptor(): OpenSSLEncryption
62 62
     {
63
-        if (! PayPalExtraMetaManager::$encryptor instanceof OpenSSLEncryption) {
63
+        if ( ! PayPalExtraMetaManager::$encryptor instanceof OpenSSLEncryption) {
64 64
             PayPalExtraMetaManager::$encryptor = LoaderFactory::getLoader()->getShared(
65 65
                 OpenSSLEncryption::class,
66 66
                 [new Base64Encoder()]
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
             Domain::META_KEY_BN_CODE,
238 238
         ];
239 239
         foreach ($expected_parameters as $api_key) {
240
-            if (! isset($response[ $api_key ])) {
240
+            if ( ! isset($response[$api_key])) {
241 241
                 // Don't want to try saving data that doesn't exist.
242 242
                 continue;
243 243
             }
@@ -245,16 +245,16 @@  discard block
 block discarded – undo
245 245
                 switch ($api_key) {
246 246
                     case Domain::META_KEY_ACCESS_TOKEN:
247 247
                     case Domain::META_KEY_PARTNER_MERCHANT_ID:
248
-                        $paypal_data[ $api_key ] = PayPalExtraMetaManager::encryptString(
249
-                            $response[ $api_key ],
248
+                        $paypal_data[$api_key] = PayPalExtraMetaManager::encryptString(
249
+                            $response[$api_key],
250 250
                             $paypal_pm
251 251
                         );
252 252
                         break;
253 253
                     case Domain::META_KEY_TOKEN_EXPIRES_IN:
254
-                        $paypal_data[ $api_key ] = time() + (int) sanitize_key($response[ $api_key ]);
254
+                        $paypal_data[$api_key] = time() + (int) sanitize_key($response[$api_key]);
255 255
                         break;
256 256
                     default:
257
-                        $paypal_data[ $api_key ] = sanitize_text_field($response[ $api_key ]);
257
+                        $paypal_data[$api_key] = sanitize_text_field($response[$api_key]);
258 258
                 }
259 259
             } catch (Exception $e) {
260 260
                 PayPalLogger::errorLog(
@@ -283,11 +283,11 @@  discard block
 block discarded – undo
283 283
         $api_credentials     = [];
284 284
         $expected_parameters = [Domain::META_KEY_SELLER_MERCHANT_ID];
285 285
         foreach ($expected_parameters as $api_key) {
286
-            if (! isset($response[ $api_key ])) {
286
+            if ( ! isset($response[$api_key])) {
287 287
                 // Don't want to try saving data that doesn't exist.
288 288
                 continue;
289 289
             }
290
-            $api_credentials[ $api_key ] = $response[ $api_key ];
290
+            $api_credentials[$api_key] = $response[$api_key];
291 291
         }
292 292
         return PayPalExtraMetaManager::savePmOptions($paypal_pm, $api_credentials);
293 293
     }
@@ -307,10 +307,10 @@  discard block
 block discarded – undo
307 307
     {
308 308
         $allowed_checkout_type = 'express_checkout';
309 309
         // Did the merchant onboard with PPCP enabled or no.
310
-        if (! empty($get_params['selected_payment']) && $get_params['selected_payment'] === 'PPCP') {
310
+        if ( ! empty($get_params['selected_payment']) && $get_params['selected_payment'] === 'PPCP') {
311 311
             // Make sure that merchant's account really supports advanced card fields (included in the PPCP scope).
312 312
             // Has to include "PPCP_CUSTOM" product for ACDC support. "EXPRESS_CHECKOUT" otherwise.
313
-            if (! empty($data['response']['products'][0]['name'])) {
313
+            if ( ! empty($data['response']['products'][0]['name'])) {
314 314
                 foreach ($data['response']['products'] as $product) {
315 315
                     if ($product['name'] === 'PPCP_CUSTOM') {
316 316
                         // This merchant has PPCP in the products list, so we can enable both (all supported) checkout types.
@@ -322,18 +322,18 @@  discard block
 block discarded – undo
322 322
         }
323 323
         // Set the PM option Checkout type, just in case merchant doesn't save PM options manually.
324 324
         $checkout_type_setting = $paypal_pm->get_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, true, false);
325
-        if (! $checkout_type_setting || $checkout_type_setting !== $allowed_checkout_type) {
325
+        if ( ! $checkout_type_setting || $checkout_type_setting !== $allowed_checkout_type) {
326 326
             $paypal_pm->update_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, $allowed_checkout_type);
327 327
         }
328 328
         // Save the scopes that were authorized.
329
-        if (! empty($data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'])) {
329
+        if ( ! empty($data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'])) {
330 330
             $scopes = [];
331 331
             foreach ($data['response']['oauth_integrations'][0]['oauth_third_party'][0]['scopes'] as $scope) {
332 332
                 // Scope will look like: 'https://uri.paypal.com/services/payments/partnerfee'
333 333
                 $split       = explode('/', $scope);
334 334
                 $split_count = count($split);
335 335
                 // Get the scope itself.
336
-                $scopes[] = $split[ $split_count - 1 ];
336
+                $scopes[] = $split[$split_count - 1];
337 337
             }
338 338
             if (empty($scopes)) {
339 339
                 // In case the there's a change in how scopes come in just save the list.
@@ -360,7 +360,7 @@  discard block
 block discarded – undo
360 360
     public static function encryptString(string $text, EE_Payment_Method $paypal_pm): ?string
361 361
     {
362 362
         // We sure we are getting something ?
363
-        if (! $text) {
363
+        if ( ! $text) {
364 364
             return $text;
365 365
         }
366 366
 
@@ -392,7 +392,7 @@  discard block
 block discarded – undo
392 392
     public static function decryptString(string $text, EE_Payment_Method $paypal_pm): string
393 393
     {
394 394
         // Are we even getting something ?
395
-        if (! $text) {
395
+        if ( ! $text) {
396 396
             return $text;
397 397
         }
398 398
         // Try decrypting.
Please login to merge, or discard this patch.
PaymentMethods/PayPalCommerce/PayPalCheckout/forms/BillingForm.php 1 patch
Indentation   +571 added lines, -571 removed lines patch added patch discarded remove patch
@@ -37,575 +37,575 @@
 block discarded – undo
37 37
  */
38 38
 class BillingForm extends EE_Billing_Attendee_Info_Form
39 39
 {
40
-    protected EE_Payment_Method $paypal_pmt;
41
-
42
-    protected ?EE_Transaction $transaction = null;
43
-
44
-    protected string $checkout_type;
45
-
46
-    /**
47
-     * Filepath to template files
48
-     *
49
-     * @var string $template_path
50
-     */
51
-    protected string $template_path;
52
-
53
-
54
-    /**
55
-     * Class constructor.
56
-     *
57
-     * @param EE_Payment_Method    $payment_method
58
-     * @param array                $options
59
-     * @throws EE_Error
60
-     * @throws ReflectionException
61
-     */
62
-    public function __construct(EE_Payment_Method $payment_method, array $options)
63
-    {
64
-        $this->paypal_pmt    = $payment_method;
65
-        // Can't be too careful.
66
-        $this->transaction   = $options['transaction'] ?? null;
67
-        $this->template_path = $options['template_path'] ?? '';
68
-        $this->checkout_type = $payment_method->get_extra_meta(
69
-            Domain::META_KEY_CHECKOUT_TYPE,
70
-            true,
71
-            'express_checkout'
72
-        );
73
-        $pm_slug             = $payment_method->slug();
74
-        $parameters          = array_replace_recursive(
75
-            $options,
76
-            [
77
-                'name'        => 'PayPalCommerceBillingForm',
78
-                'html_id'     => 'pp-' . $pm_slug . '-billing-form',
79
-                'html_class'  => 'pp_commerce_billing_form',
80
-                'subsections' => [
81
-                    'eea_paypal_commerce_token' => new EE_Hidden_Input(
82
-                        [
83
-                            'html_id'   => 'eea-paypal-commerce-token',
84
-                            'html_name' => 'EEA_paymentToken',
85
-                            'default'   => '',
86
-                        ]
87
-                    ),
88
-                    'pp_order_nonce'            => new EE_Hidden_Input(
89
-                        [
90
-                            'html_id'   => 'eea-' . $pm_slug . '-order-nonce',
91
-                            'html_name' => 'pp_order_nonce',
92
-                            'default'   => '',
93
-                        ]
94
-                    ),
95
-                    'pp_order_id'               => new EE_Hidden_Input(
96
-                        [
97
-                            'html_id'   => 'eea-' . $pm_slug . '-order-id',
98
-                            'html_name' => 'pp_order_id',
99
-                            'default'   => '',
100
-                        ]
101
-                    ),
102
-                    'pp_order_status'           => new EE_Hidden_Input(
103
-                        [
104
-                            'html_id'   => 'eea-' . $pm_slug . '-order-status',
105
-                            'html_name' => 'pp_order_status',
106
-                            'default'   => '',
107
-                        ]
108
-                    ),
109
-                ],
110
-            ]
111
-        );
112
-        // Add data tags to the PP script.
113
-        add_filter('script_loader_tag', [$this, 'addDataTagsToScript'], 10, 2);
114
-        parent::__construct($payment_method, $parameters);
115
-        // Add and exclude other sections.
116
-        $this->addPaymentSections();
117
-        // Additional actions and/or filters.
118
-        $this->loadActionsAndFilters();
119
-    }
120
-
121
-
122
-    /**
123
-     * Add PayPal payment sections.
124
-     *
125
-     * @return void
126
-     * @throws EE_Error
127
-     * @throws ReflectionException
128
-     */
129
-    public function addPaymentSections(): void
130
-    {
131
-        // Exclude the default billing form fields.
132
-        $this->exclude(
133
-            [
134
-                'first_name',
135
-                'last_name',
136
-                'email',
137
-            ]
138
-        );
139
-        // Add PayPal Hosted Fields.
140
-        if (! empty($this->checkout_type) && $this->checkout_type !== 'express_checkout') {
141
-            $this->addAdvancedCardFields();
142
-        }
143
-        // Add payment types separator, if both are enabled.
144
-        if ($this->checkout_type === 'all') {
145
-            $this->addTypesSeparator();
146
-        }
147
-        // Add PayPal Buttons section.
148
-        if ($this->checkout_type !== 'ppcp') {
149
-            $this->add_subsections(
150
-                [
151
-                    'paypal_commerce_pm_form' => $this->addPayPalCheckout(),
152
-                ]
153
-            );
154
-        }
155
-        // Exclude the rest billing form fields if the payment type is express checkout.
156
-        if ($this->checkout_type === 'express_checkout') {
157
-            $this->exclude(
158
-                [
159
-                    'address',
160
-                    'address2',
161
-                    'state',
162
-                    'phone',
163
-                    'city',
164
-                    'country',
165
-                    'zip',
166
-                ]
167
-            );
168
-            // Remove the Info subsection.
169
-            add_filter('FHEE__EE_Form_Section_Proper___construct__options_array', [$this, 'excludeInfoSubsection']);
170
-        }
171
-        $this->add_subsections(
172
-            [
173
-                'debug_content' => $this->addDebugContent($this->paypal_pmt),
174
-            ]
175
-        );
176
-    }
177
-
178
-
179
-    /**
180
-     * Additional actions and/or filters.
181
-     *
182
-     * @return void
183
-     */
184
-    public function loadActionsAndFilters(): void
185
-    {
186
-        add_filter(
187
-            'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
188
-            [__CLASS__, 'excludeBillingFormFields'],
189
-            10,
190
-            2
191
-        );
192
-    }
193
-
194
-
195
-    /**
196
-     * Filter out billing form fields if pay button was used.
197
-     *
198
-     * @param EE_Billing_Info_Form $billing_form
199
-     * @param EE_Payment_Method    $payment_method
200
-     * @return EE_Billing_Info_Form
201
-     */
202
-    public static function excludeBillingFormFields(
203
-        EE_Billing_Info_Form $billing_form,
204
-        EE_Payment_Method $payment_method
205
-    ): EE_Billing_Info_Form {
206
-        $request        = LoaderFactory::getShared(Request::class);
207
-        $request_params = $request->requestParams();
208
-        // Only the PPC billing form.
209
-        if (! $billing_form instanceof BillingForm) {
210
-            return $billing_form;
211
-        }
212
-        // Make sure the billing form subsections have correct names.
213
-        $inputs = $billing_form->inputs_in_subsections();
214
-        if (
215
-            ! empty($request_params['process_form_submission'])
216
-            && $request_params['process_form_submission'] === '1'
217
-            && ! empty($request_params['eep_ppc_skip_form_validation'])
218
-        ) {
219
-            // Hide card info fields.
220
-            $billing_form->exclude(
221
-                [
222
-                    'pp_name_on_card',
223
-                    'address',
224
-                    'address2',
225
-                    'state',
226
-                    'phone',
227
-                    'city',
228
-                    'country',
229
-                    'zip',
230
-                ]
231
-            );
232
-        }
233
-        return $billing_form;
234
-    }
235
-
236
-
237
-    /**
238
-     * Add advanced card & debit card fields.
239
-     *
240
-     * @return void
241
-     * @throws EE_Error|ReflectionException
242
-     */
243
-    public function addAdvancedCardFields(): void
244
-    {
245
-        $pm_slug = $this->paypal_pmt->slug();
246
-        $this->add_subsections(
247
-            [
248
-                'pp_card_number'     => new EE_Form_Section_HTML(
249
-                    EEH_HTML::label(
250
-                        esc_html__('Card Number', 'event_espresso'),
251
-                        "$pm_slug-card-number-lbl",
252
-                        "$pm_slug-card-fields",
253
-                        "",
254
-                        'for="' . $pm_slug . '-card-number"'
255
-                    ) .
256
-                    EEH_HTML::p(
257
-                        "",
258
-                        "$pm_slug-card-number",
259
-                        "card_field $pm_slug-card-fields"
260
-                    )
261
-                ),
262
-                'pp_expiration_date' => new EE_Form_Section_HTML(
263
-                    EEH_HTML::label(
264
-                        esc_html__('Expiration Date', 'event_espresso'),
265
-                        "$pm_slug-expiration-date-lbl",
266
-                        "$pm_slug-card-fields",
267
-                        "",
268
-                        'for="' . $pm_slug . '-expiration-date"'
269
-                    ) .
270
-                    EEH_HTML::p(
271
-                        "",
272
-                        "$pm_slug-expiration-date",
273
-                        "card_field $pm_slug-card-fields"
274
-                    )
275
-                ),
276
-                'pp_card_cvv'        => new EE_Form_Section_HTML(
277
-                    EEH_HTML::label(
278
-                        esc_html__('CVV', 'event_espresso'),
279
-                        "$pm_slug-cvv-lbl",
280
-                        "$pm_slug-card-fields",
281
-                        "",
282
-                        'for="' . $pm_slug . '-cvv"'
283
-                    ) .
284
-                    EEH_HTML::p(
285
-                        "",
286
-                        "$pm_slug-cvv",
287
-                        "card_field $pm_slug-card-fields"
288
-                    )
289
-                ),
290
-                'pp_name_on_card'    => new EE_Text_Input(
291
-                    [
292
-                        'html_label_text' => esc_html__('Name On Card', 'event_espresso'),
293
-                        'html_id'         => $pm_slug . '-card-holder-name',
294
-                        'html_name'       => 'card-holder-name',
295
-                        'html_class'      => '',
296
-                        'required'        => true,
297
-                    ]
298
-                ),
299
-            ]
300
-        );
301
-        // Add the submit button at the end.
302
-        $this->add_subsections(
303
-            [
304
-                'pp_cc_submit' => new EE_Submit_Input(
305
-                    [
306
-                        'html_label_text' => esc_html__('Submit', 'event_espresso'),
307
-                        'html_id'         => $pm_slug,
308
-                        'html_class'      => 'eep-ppc-btn',
309
-                    ]
310
-                ),
311
-            ],
312
-            'phone',
313
-            false
314
-        );
315
-    }
316
-
317
-
318
-    /**
319
-     * Add advanced card & debit card fields.
320
-     *
321
-     * @return void
322
-     * @throws EE_Error|ReflectionException
323
-     */
324
-    public function addTypesSeparator(): void
325
-    {
326
-        $this->add_subsections(
327
-            [
328
-                'pp_payment_types_separator' => new EE_Form_Section_HTML(
329
-                    EEH_HTML::div(
330
-                        EEH_HTML::div(
331
-                            ' ',
332
-                            'eep-' . $this->paypal_pmt->slug() . '-payments-separator',
333
-                            'eep-ppc-separator-line eep-left-floating'
334
-                        ) .
335
-                        EEH_HTML::div(
336
-                            esc_html__(' or ', 'event_espresso'),
337
-                            'eep-' . $this->paypal_pmt->slug() . '-separator-text',
338
-                            'eep-ppc-separator-text1 eep-mid-floating'
339
-                        ) . EEH_HTML::div(
340
-                            ' ',
341
-                            'eep-' . $this->paypal_pmt->slug() . '-payments-separator',
342
-                            'eep-ppc-separator-line eep-right-floating'
343
-                        ),
344
-                        'eep-ppc-separator-holder',
345
-                        'eep-ppc-separator-holder'
346
-                    )
347
-                ),
348
-            ]
349
-        );
350
-    }
351
-
352
-
353
-    /**
354
-     * Exclude the info subsection from the PPC checkout form.
355
-     *
356
-     * @param array $options_array
357
-     * @return array
358
-     * @throws EE_Error
359
-     * @throws ReflectionException
360
-     */
361
-    public function excludeInfoSubsection(array $options_array): array
362
-    {
363
-        if (
364
-            ! empty($options_array['html_id'])
365
-            && $options_array['html_id'] === 'spco-payment-method-info-' . $this->paypal_pmt->slug()
366
-        ) {
367
-            if (! empty($options_array['subsections']) && isset($options_array['subsections']['info'])) {
368
-                unset($options_array['subsections']['info']);
369
-            }
370
-        }
371
-        return $options_array;
372
-    }
373
-
374
-
375
-    /**
376
-     * Possibly adds debug content to PayPal commerce billing form.
377
-     *
378
-     * @param EE_Payment_Method $paypal_pm
379
-     * @return EE_Form_Section_Base
380
-     * @throws EE_Error|ReflectionException
381
-     */
382
-    public function addDebugContent(EE_Payment_Method $paypal_pm): EE_Form_Section_Base
383
-    {
384
-        if ($paypal_pm->debug_mode()) {
385
-            return new EE_Form_Section_Proper(
386
-                [
387
-                    'layout_strategy' => new EE_Template_Layout(
388
-                        [
389
-                            'layout_template_file' => $this->template_path . 'debugInfo.template.php',
390
-                            'template_args'        => [],
391
-                        ]
392
-                    ),
393
-                ]
394
-            );
395
-        }
396
-        return new EE_Form_Section_HTML();
397
-    }
398
-
399
-
400
-    /**
401
-     * Add PayPal checkout buttons.
402
-     *
403
-     * @return EE_Form_Section_Proper
404
-     * @throws EE_Error
405
-     * @throws ReflectionException
406
-     * @throws Exception
407
-     */
408
-    public function addPayPalCheckout(): EE_Form_Section_Proper
409
-    {
410
-        $template_args['pm_slug']     = $this->paypal_pmt->slug();
411
-        return new EE_Form_Section_Proper(
412
-            [
413
-                'layout_strategy' => new EE_Template_Layout(
414
-                    [
415
-                        'layout_template_file' => $this->template_path . 'paymentButtons.template.php',
416
-                        'template_args'        => $template_args,
417
-                    ]
418
-                ),
419
-            ]
420
-        );
421
-    }
422
-
423
-
424
-    /**
425
-     * Load scripts and localize data needed for this form.
426
-     *
427
-     * @param $tag
428
-     * @param $handle
429
-     * @return string
430
-     * @throws EE_Error
431
-     * @throws ReflectionException
432
-     */
433
-    public function addDataTagsToScript($tag, $handle): string
434
-    {
435
-        if ($handle === 'eea_paypal_commerce_js_lib') {
436
-            $bn_code  = PayPalExtraMetaManager::getPmOption($this->_pm_instance, Domain::META_KEY_BN_CODE);
437
-            $response = EED_PayPalCommerce::requestClientToken($this->paypal_pmt);
438
-            if (empty($response['client_token'])) {
439
-                return $tag;
440
-            }
441
-            $client_token = $response['client_token'];
442
-            $attributes   = " data-partner-attribution-id=\"$bn_code\" data-client-token=\"$client_token\"";
443
-            $tag          = str_replace('></script>', $attributes . '></script>', $tag);
444
-        }
445
-        return $tag;
446
-    }
447
-
448
-
449
-    /**
450
-     * Load scripts and localize data needed for this form.
451
-     *
452
-     * @return void
453
-     * @throws EE_Error
454
-     * @throws ReflectionException
455
-     * @throws Exception
456
-     */
457
-    public function enqueue_js(): void
458
-    {
459
-        // Setup default values
460
-        $client_id_key = Domain::META_KEY_CLIENT_ID;
461
-        $merchant_id   = false;
462
-        $funding_options = ['venmo', 'paylater'];
463
-
464
-        // Override the above if third party integration
465
-        if (EED_PayPalCommerce::isThirdParty($this->_pm_instance)) {
466
-            $client_id_key = Domain::META_KEY_PARTNER_CLIENT_ID;
467
-            $merchant_id   = PayPalExtraMetaManager::getPmOption(
468
-                $this->_pm_instance,
469
-                Domain::META_KEY_SELLER_MERCHANT_ID
470
-            );
471
-        }
472
-
473
-        // Setup query args
474
-        $url_params            = [
475
-            'client-id'        => PayPalExtraMetaManager::getPmOption($this->_pm_instance, $client_id_key),
476
-            'currency'         => CurrencyManager::currencyCode(),
477
-            'components'       => implode(',', ['buttons','hosted-fields']),
478
-            'intent'           => 'capture',
479
-            'merchant-id'      => $merchant_id,
480
-        ];
481
-
482
-        // Which funding methods are active?
483
-        $enabled_funding = $this->_pm_instance->get_extra_meta(Domain::META_KEY_FUNDING_OPTIONS, true, $funding_options);
484
-
485
-        // Any funding method not enabled should be disabled.
486
-        $disabled_funding = array_diff(
487
-            $funding_options,
488
-            $enabled_funding
489
-        );
490
-
491
-        // Any funding options enabled?
492
-        if (count($enabled_funding) > 0) {
493
-            $url_params['enable-funding'] = implode(',', $enabled_funding);
494
-        }
495
-
496
-        // Any funding options disabled?
497
-        if (count($disabled_funding) > 0) {
498
-            $url_params['disable-funding'] = implode(',', $disabled_funding);
499
-        }
500
-
501
-        // Enqueue the PayPal JS
502
-        wp_enqueue_script(
503
-            'eea_paypal_commerce_js_lib',
504
-            add_query_arg($url_params, 'https://www.paypal.com/sdk/js'),
505
-            [],
506
-            null
507
-        );
508
-
509
-        wp_enqueue_script(
510
-            'eea_paypal_commerce_js',
511
-            EEP_PAYPAL_COMMERCE_URL . 'assets/js/paypal-commerce-payments.js',
512
-            ['eea_paypal_commerce_js_lib'],
513
-            EVENT_ESPRESSO_VERSION,
514
-            true
515
-        );
516
-        // Styles.
517
-        wp_enqueue_style(
518
-            'eea_paypal_checkout_form_styles',
519
-            EEP_PAYPAL_COMMERCE_URL . 'assets' . DS . 'css' . DS . 'eea-paypal-checkout.css',
520
-            [],
521
-            EVENT_ESPRESSO_VERSION
522
-        );
523
-        // Localize the script with our transaction data.
524
-        $parameters = $this->localizeParameters();
525
-        wp_localize_script('eea_paypal_commerce_js', 'eeaPPCommerceParameters', $parameters);
526
-        parent::enqueue_js();
527
-    }
528
-
529
-
530
-    /**
531
-     * Form and return PayPal commerce parameters for script localization.
532
-     *
533
-     * @return array
534
-     * @throws EE_Error
535
-     * @throws ReflectionException
536
-     * @throws Exception
537
-     */
538
-    public function localizeParameters(): array
539
-    {
540
-        // Also tell the script about each instance of this PM.
541
-        $pm_versions            = [];
542
-        $active_payment_methods = EEM_Payment_Method::instance()->get_all_active(
543
-            EEM_Payment_Method::scope_cart,
544
-            [['PMD_slug' => ['LIKE', '%paypalcheckout%']]]
545
-        );
546
-        foreach ($active_payment_methods as $payment_method) {
547
-            $pm_versions[ $payment_method->slug() ] = [
548
-                'pm_slug' => $payment_method->slug(),
549
-            ];
550
-        }
551
-        // Convert money for a display format.
552
-        $decimal_places = CurrencyManager::getDecimalPlaces();
553
-        $org_country    = isset(EE_Registry::instance()->CFG->organization)
554
-        && EE_Registry::instance()->CFG->organization instanceof EE_Organization_Config
555
-            ? EE_Registry::instance()->CFG->organization->CNT_ISO
556
-            : 'US';
557
-        $transaction_id = $this->transaction instanceof EE_Transaction ? $this->transaction->ID() : 0;
558
-        $currency_code  = CurrencyManager::currencyCode();
559
-        return [
560
-            'pm_versions'            => $pm_versions,
561
-            'payment_currency'       => $currency_code,
562
-            'checkout_type'          => $this->checkout_type,
563
-            'currency_sign'          => EE_Registry::instance()->CFG->currency->sign,
564
-            'pp_order_nonce'         => wp_create_nonce(Domain::CAPTURE_ORDER_NONCE_NAME),
565
-            // The transaction ID is only used for logging errors.
566
-            'txn_id'                 => $transaction_id,
567
-            'org_country'            => $org_country,
568
-            'decimal_places'         => $decimal_places,
569
-            'site_name'              => get_bloginfo('name'),
570
-            'active_states'          => EED_PayPalCommerce::getActiveStates(),
571
-            'no_spco_error'          => esc_html__(
572
-                'It appears the SDK script was not loaded properly! Please refresh the page and try again or contact support.',
573
-                'event_espresso'
574
-            ),
575
-            'no_pm_error'            => esc_html__(
576
-                'It appears that PayPal Commerce checkout JavaScript was not loaded properly! Please refresh the page and try again or contact support. PayPal Commerce payments can\'t be processed.',
577
-                'event_espresso'
578
-            ),
579
-            'browser_not_supported'  => esc_html__(
580
-                'It appears that this browser is not supported by PayPal scripts. We apologize, but PayPal payments won\'t work in this browser version.',
581
-                'event_espresso'
582
-            ),
583
-            'get_token_error'        => esc_html__(
584
-                'There was an error while trying to get the payment token. Please refresh the page and try again or contact support.',
585
-                'event_espresso'
586
-            ),
587
-            'form_validation_notice' => esc_html__('Billing form information not valid.', 'event_espresso'),
588
-            'no_verification_token'  => esc_html__('Missing the Verification token.', 'event_espresso'),
589
-            'error_response'         => esc_html__('Error response received', 'event_espresso'),
590
-            'payment_error'          => esc_html__(
591
-                'There was an error with this payment. See the logs for details.',
592
-                'event_espresso'
593
-            ),
594
-            'no_order_id'            => esc_html__('No Order ID found.', 'event_espresso'),
595
-            'general_pp_error'       => esc_html__('PayPal form threw an error.', 'event_espresso'),
596
-            'hf_render_error'        => esc_html__('Hosted fields could not be rendered!', 'event_espresso'),
597
-            'pm_capture_error'       => esc_html__('Payment could not be captured!', 'event_espresso'),
598
-            'contact_support_msg'    => sprintf(
599
-                /* translators: %1$s: organization email, %2$s: the transaction ID */
600
-                esc_html__('Please contact support (%1$s) for more details on this transaction #%2$s.', 'event_espresso'),
601
-                EE_Registry::instance()->CFG->organization->get_pretty('email'),
602
-                $transaction_id
603
-            ),
604
-            'not_acdc_eligible'      => esc_html__(
605
-                'This merchant is not eligible for Advanced Card Fields checkout type.',
606
-                'event_espresso'
607
-            ),
608
-            'processor_response'     => esc_html__('Processor response: ', 'event_espresso'),
609
-        ];
610
-    }
40
+	protected EE_Payment_Method $paypal_pmt;
41
+
42
+	protected ?EE_Transaction $transaction = null;
43
+
44
+	protected string $checkout_type;
45
+
46
+	/**
47
+	 * Filepath to template files
48
+	 *
49
+	 * @var string $template_path
50
+	 */
51
+	protected string $template_path;
52
+
53
+
54
+	/**
55
+	 * Class constructor.
56
+	 *
57
+	 * @param EE_Payment_Method    $payment_method
58
+	 * @param array                $options
59
+	 * @throws EE_Error
60
+	 * @throws ReflectionException
61
+	 */
62
+	public function __construct(EE_Payment_Method $payment_method, array $options)
63
+	{
64
+		$this->paypal_pmt    = $payment_method;
65
+		// Can't be too careful.
66
+		$this->transaction   = $options['transaction'] ?? null;
67
+		$this->template_path = $options['template_path'] ?? '';
68
+		$this->checkout_type = $payment_method->get_extra_meta(
69
+			Domain::META_KEY_CHECKOUT_TYPE,
70
+			true,
71
+			'express_checkout'
72
+		);
73
+		$pm_slug             = $payment_method->slug();
74
+		$parameters          = array_replace_recursive(
75
+			$options,
76
+			[
77
+				'name'        => 'PayPalCommerceBillingForm',
78
+				'html_id'     => 'pp-' . $pm_slug . '-billing-form',
79
+				'html_class'  => 'pp_commerce_billing_form',
80
+				'subsections' => [
81
+					'eea_paypal_commerce_token' => new EE_Hidden_Input(
82
+						[
83
+							'html_id'   => 'eea-paypal-commerce-token',
84
+							'html_name' => 'EEA_paymentToken',
85
+							'default'   => '',
86
+						]
87
+					),
88
+					'pp_order_nonce'            => new EE_Hidden_Input(
89
+						[
90
+							'html_id'   => 'eea-' . $pm_slug . '-order-nonce',
91
+							'html_name' => 'pp_order_nonce',
92
+							'default'   => '',
93
+						]
94
+					),
95
+					'pp_order_id'               => new EE_Hidden_Input(
96
+						[
97
+							'html_id'   => 'eea-' . $pm_slug . '-order-id',
98
+							'html_name' => 'pp_order_id',
99
+							'default'   => '',
100
+						]
101
+					),
102
+					'pp_order_status'           => new EE_Hidden_Input(
103
+						[
104
+							'html_id'   => 'eea-' . $pm_slug . '-order-status',
105
+							'html_name' => 'pp_order_status',
106
+							'default'   => '',
107
+						]
108
+					),
109
+				],
110
+			]
111
+		);
112
+		// Add data tags to the PP script.
113
+		add_filter('script_loader_tag', [$this, 'addDataTagsToScript'], 10, 2);
114
+		parent::__construct($payment_method, $parameters);
115
+		// Add and exclude other sections.
116
+		$this->addPaymentSections();
117
+		// Additional actions and/or filters.
118
+		$this->loadActionsAndFilters();
119
+	}
120
+
121
+
122
+	/**
123
+	 * Add PayPal payment sections.
124
+	 *
125
+	 * @return void
126
+	 * @throws EE_Error
127
+	 * @throws ReflectionException
128
+	 */
129
+	public function addPaymentSections(): void
130
+	{
131
+		// Exclude the default billing form fields.
132
+		$this->exclude(
133
+			[
134
+				'first_name',
135
+				'last_name',
136
+				'email',
137
+			]
138
+		);
139
+		// Add PayPal Hosted Fields.
140
+		if (! empty($this->checkout_type) && $this->checkout_type !== 'express_checkout') {
141
+			$this->addAdvancedCardFields();
142
+		}
143
+		// Add payment types separator, if both are enabled.
144
+		if ($this->checkout_type === 'all') {
145
+			$this->addTypesSeparator();
146
+		}
147
+		// Add PayPal Buttons section.
148
+		if ($this->checkout_type !== 'ppcp') {
149
+			$this->add_subsections(
150
+				[
151
+					'paypal_commerce_pm_form' => $this->addPayPalCheckout(),
152
+				]
153
+			);
154
+		}
155
+		// Exclude the rest billing form fields if the payment type is express checkout.
156
+		if ($this->checkout_type === 'express_checkout') {
157
+			$this->exclude(
158
+				[
159
+					'address',
160
+					'address2',
161
+					'state',
162
+					'phone',
163
+					'city',
164
+					'country',
165
+					'zip',
166
+				]
167
+			);
168
+			// Remove the Info subsection.
169
+			add_filter('FHEE__EE_Form_Section_Proper___construct__options_array', [$this, 'excludeInfoSubsection']);
170
+		}
171
+		$this->add_subsections(
172
+			[
173
+				'debug_content' => $this->addDebugContent($this->paypal_pmt),
174
+			]
175
+		);
176
+	}
177
+
178
+
179
+	/**
180
+	 * Additional actions and/or filters.
181
+	 *
182
+	 * @return void
183
+	 */
184
+	public function loadActionsAndFilters(): void
185
+	{
186
+		add_filter(
187
+			'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
188
+			[__CLASS__, 'excludeBillingFormFields'],
189
+			10,
190
+			2
191
+		);
192
+	}
193
+
194
+
195
+	/**
196
+	 * Filter out billing form fields if pay button was used.
197
+	 *
198
+	 * @param EE_Billing_Info_Form $billing_form
199
+	 * @param EE_Payment_Method    $payment_method
200
+	 * @return EE_Billing_Info_Form
201
+	 */
202
+	public static function excludeBillingFormFields(
203
+		EE_Billing_Info_Form $billing_form,
204
+		EE_Payment_Method $payment_method
205
+	): EE_Billing_Info_Form {
206
+		$request        = LoaderFactory::getShared(Request::class);
207
+		$request_params = $request->requestParams();
208
+		// Only the PPC billing form.
209
+		if (! $billing_form instanceof BillingForm) {
210
+			return $billing_form;
211
+		}
212
+		// Make sure the billing form subsections have correct names.
213
+		$inputs = $billing_form->inputs_in_subsections();
214
+		if (
215
+			! empty($request_params['process_form_submission'])
216
+			&& $request_params['process_form_submission'] === '1'
217
+			&& ! empty($request_params['eep_ppc_skip_form_validation'])
218
+		) {
219
+			// Hide card info fields.
220
+			$billing_form->exclude(
221
+				[
222
+					'pp_name_on_card',
223
+					'address',
224
+					'address2',
225
+					'state',
226
+					'phone',
227
+					'city',
228
+					'country',
229
+					'zip',
230
+				]
231
+			);
232
+		}
233
+		return $billing_form;
234
+	}
235
+
236
+
237
+	/**
238
+	 * Add advanced card & debit card fields.
239
+	 *
240
+	 * @return void
241
+	 * @throws EE_Error|ReflectionException
242
+	 */
243
+	public function addAdvancedCardFields(): void
244
+	{
245
+		$pm_slug = $this->paypal_pmt->slug();
246
+		$this->add_subsections(
247
+			[
248
+				'pp_card_number'     => new EE_Form_Section_HTML(
249
+					EEH_HTML::label(
250
+						esc_html__('Card Number', 'event_espresso'),
251
+						"$pm_slug-card-number-lbl",
252
+						"$pm_slug-card-fields",
253
+						"",
254
+						'for="' . $pm_slug . '-card-number"'
255
+					) .
256
+					EEH_HTML::p(
257
+						"",
258
+						"$pm_slug-card-number",
259
+						"card_field $pm_slug-card-fields"
260
+					)
261
+				),
262
+				'pp_expiration_date' => new EE_Form_Section_HTML(
263
+					EEH_HTML::label(
264
+						esc_html__('Expiration Date', 'event_espresso'),
265
+						"$pm_slug-expiration-date-lbl",
266
+						"$pm_slug-card-fields",
267
+						"",
268
+						'for="' . $pm_slug . '-expiration-date"'
269
+					) .
270
+					EEH_HTML::p(
271
+						"",
272
+						"$pm_slug-expiration-date",
273
+						"card_field $pm_slug-card-fields"
274
+					)
275
+				),
276
+				'pp_card_cvv'        => new EE_Form_Section_HTML(
277
+					EEH_HTML::label(
278
+						esc_html__('CVV', 'event_espresso'),
279
+						"$pm_slug-cvv-lbl",
280
+						"$pm_slug-card-fields",
281
+						"",
282
+						'for="' . $pm_slug . '-cvv"'
283
+					) .
284
+					EEH_HTML::p(
285
+						"",
286
+						"$pm_slug-cvv",
287
+						"card_field $pm_slug-card-fields"
288
+					)
289
+				),
290
+				'pp_name_on_card'    => new EE_Text_Input(
291
+					[
292
+						'html_label_text' => esc_html__('Name On Card', 'event_espresso'),
293
+						'html_id'         => $pm_slug . '-card-holder-name',
294
+						'html_name'       => 'card-holder-name',
295
+						'html_class'      => '',
296
+						'required'        => true,
297
+					]
298
+				),
299
+			]
300
+		);
301
+		// Add the submit button at the end.
302
+		$this->add_subsections(
303
+			[
304
+				'pp_cc_submit' => new EE_Submit_Input(
305
+					[
306
+						'html_label_text' => esc_html__('Submit', 'event_espresso'),
307
+						'html_id'         => $pm_slug,
308
+						'html_class'      => 'eep-ppc-btn',
309
+					]
310
+				),
311
+			],
312
+			'phone',
313
+			false
314
+		);
315
+	}
316
+
317
+
318
+	/**
319
+	 * Add advanced card & debit card fields.
320
+	 *
321
+	 * @return void
322
+	 * @throws EE_Error|ReflectionException
323
+	 */
324
+	public function addTypesSeparator(): void
325
+	{
326
+		$this->add_subsections(
327
+			[
328
+				'pp_payment_types_separator' => new EE_Form_Section_HTML(
329
+					EEH_HTML::div(
330
+						EEH_HTML::div(
331
+							' ',
332
+							'eep-' . $this->paypal_pmt->slug() . '-payments-separator',
333
+							'eep-ppc-separator-line eep-left-floating'
334
+						) .
335
+						EEH_HTML::div(
336
+							esc_html__(' or ', 'event_espresso'),
337
+							'eep-' . $this->paypal_pmt->slug() . '-separator-text',
338
+							'eep-ppc-separator-text1 eep-mid-floating'
339
+						) . EEH_HTML::div(
340
+							' ',
341
+							'eep-' . $this->paypal_pmt->slug() . '-payments-separator',
342
+							'eep-ppc-separator-line eep-right-floating'
343
+						),
344
+						'eep-ppc-separator-holder',
345
+						'eep-ppc-separator-holder'
346
+					)
347
+				),
348
+			]
349
+		);
350
+	}
351
+
352
+
353
+	/**
354
+	 * Exclude the info subsection from the PPC checkout form.
355
+	 *
356
+	 * @param array $options_array
357
+	 * @return array
358
+	 * @throws EE_Error
359
+	 * @throws ReflectionException
360
+	 */
361
+	public function excludeInfoSubsection(array $options_array): array
362
+	{
363
+		if (
364
+			! empty($options_array['html_id'])
365
+			&& $options_array['html_id'] === 'spco-payment-method-info-' . $this->paypal_pmt->slug()
366
+		) {
367
+			if (! empty($options_array['subsections']) && isset($options_array['subsections']['info'])) {
368
+				unset($options_array['subsections']['info']);
369
+			}
370
+		}
371
+		return $options_array;
372
+	}
373
+
374
+
375
+	/**
376
+	 * Possibly adds debug content to PayPal commerce billing form.
377
+	 *
378
+	 * @param EE_Payment_Method $paypal_pm
379
+	 * @return EE_Form_Section_Base
380
+	 * @throws EE_Error|ReflectionException
381
+	 */
382
+	public function addDebugContent(EE_Payment_Method $paypal_pm): EE_Form_Section_Base
383
+	{
384
+		if ($paypal_pm->debug_mode()) {
385
+			return new EE_Form_Section_Proper(
386
+				[
387
+					'layout_strategy' => new EE_Template_Layout(
388
+						[
389
+							'layout_template_file' => $this->template_path . 'debugInfo.template.php',
390
+							'template_args'        => [],
391
+						]
392
+					),
393
+				]
394
+			);
395
+		}
396
+		return new EE_Form_Section_HTML();
397
+	}
398
+
399
+
400
+	/**
401
+	 * Add PayPal checkout buttons.
402
+	 *
403
+	 * @return EE_Form_Section_Proper
404
+	 * @throws EE_Error
405
+	 * @throws ReflectionException
406
+	 * @throws Exception
407
+	 */
408
+	public function addPayPalCheckout(): EE_Form_Section_Proper
409
+	{
410
+		$template_args['pm_slug']     = $this->paypal_pmt->slug();
411
+		return new EE_Form_Section_Proper(
412
+			[
413
+				'layout_strategy' => new EE_Template_Layout(
414
+					[
415
+						'layout_template_file' => $this->template_path . 'paymentButtons.template.php',
416
+						'template_args'        => $template_args,
417
+					]
418
+				),
419
+			]
420
+		);
421
+	}
422
+
423
+
424
+	/**
425
+	 * Load scripts and localize data needed for this form.
426
+	 *
427
+	 * @param $tag
428
+	 * @param $handle
429
+	 * @return string
430
+	 * @throws EE_Error
431
+	 * @throws ReflectionException
432
+	 */
433
+	public function addDataTagsToScript($tag, $handle): string
434
+	{
435
+		if ($handle === 'eea_paypal_commerce_js_lib') {
436
+			$bn_code  = PayPalExtraMetaManager::getPmOption($this->_pm_instance, Domain::META_KEY_BN_CODE);
437
+			$response = EED_PayPalCommerce::requestClientToken($this->paypal_pmt);
438
+			if (empty($response['client_token'])) {
439
+				return $tag;
440
+			}
441
+			$client_token = $response['client_token'];
442
+			$attributes   = " data-partner-attribution-id=\"$bn_code\" data-client-token=\"$client_token\"";
443
+			$tag          = str_replace('></script>', $attributes . '></script>', $tag);
444
+		}
445
+		return $tag;
446
+	}
447
+
448
+
449
+	/**
450
+	 * Load scripts and localize data needed for this form.
451
+	 *
452
+	 * @return void
453
+	 * @throws EE_Error
454
+	 * @throws ReflectionException
455
+	 * @throws Exception
456
+	 */
457
+	public function enqueue_js(): void
458
+	{
459
+		// Setup default values
460
+		$client_id_key = Domain::META_KEY_CLIENT_ID;
461
+		$merchant_id   = false;
462
+		$funding_options = ['venmo', 'paylater'];
463
+
464
+		// Override the above if third party integration
465
+		if (EED_PayPalCommerce::isThirdParty($this->_pm_instance)) {
466
+			$client_id_key = Domain::META_KEY_PARTNER_CLIENT_ID;
467
+			$merchant_id   = PayPalExtraMetaManager::getPmOption(
468
+				$this->_pm_instance,
469
+				Domain::META_KEY_SELLER_MERCHANT_ID
470
+			);
471
+		}
472
+
473
+		// Setup query args
474
+		$url_params            = [
475
+			'client-id'        => PayPalExtraMetaManager::getPmOption($this->_pm_instance, $client_id_key),
476
+			'currency'         => CurrencyManager::currencyCode(),
477
+			'components'       => implode(',', ['buttons','hosted-fields']),
478
+			'intent'           => 'capture',
479
+			'merchant-id'      => $merchant_id,
480
+		];
481
+
482
+		// Which funding methods are active?
483
+		$enabled_funding = $this->_pm_instance->get_extra_meta(Domain::META_KEY_FUNDING_OPTIONS, true, $funding_options);
484
+
485
+		// Any funding method not enabled should be disabled.
486
+		$disabled_funding = array_diff(
487
+			$funding_options,
488
+			$enabled_funding
489
+		);
490
+
491
+		// Any funding options enabled?
492
+		if (count($enabled_funding) > 0) {
493
+			$url_params['enable-funding'] = implode(',', $enabled_funding);
494
+		}
495
+
496
+		// Any funding options disabled?
497
+		if (count($disabled_funding) > 0) {
498
+			$url_params['disable-funding'] = implode(',', $disabled_funding);
499
+		}
500
+
501
+		// Enqueue the PayPal JS
502
+		wp_enqueue_script(
503
+			'eea_paypal_commerce_js_lib',
504
+			add_query_arg($url_params, 'https://www.paypal.com/sdk/js'),
505
+			[],
506
+			null
507
+		);
508
+
509
+		wp_enqueue_script(
510
+			'eea_paypal_commerce_js',
511
+			EEP_PAYPAL_COMMERCE_URL . 'assets/js/paypal-commerce-payments.js',
512
+			['eea_paypal_commerce_js_lib'],
513
+			EVENT_ESPRESSO_VERSION,
514
+			true
515
+		);
516
+		// Styles.
517
+		wp_enqueue_style(
518
+			'eea_paypal_checkout_form_styles',
519
+			EEP_PAYPAL_COMMERCE_URL . 'assets' . DS . 'css' . DS . 'eea-paypal-checkout.css',
520
+			[],
521
+			EVENT_ESPRESSO_VERSION
522
+		);
523
+		// Localize the script with our transaction data.
524
+		$parameters = $this->localizeParameters();
525
+		wp_localize_script('eea_paypal_commerce_js', 'eeaPPCommerceParameters', $parameters);
526
+		parent::enqueue_js();
527
+	}
528
+
529
+
530
+	/**
531
+	 * Form and return PayPal commerce parameters for script localization.
532
+	 *
533
+	 * @return array
534
+	 * @throws EE_Error
535
+	 * @throws ReflectionException
536
+	 * @throws Exception
537
+	 */
538
+	public function localizeParameters(): array
539
+	{
540
+		// Also tell the script about each instance of this PM.
541
+		$pm_versions            = [];
542
+		$active_payment_methods = EEM_Payment_Method::instance()->get_all_active(
543
+			EEM_Payment_Method::scope_cart,
544
+			[['PMD_slug' => ['LIKE', '%paypalcheckout%']]]
545
+		);
546
+		foreach ($active_payment_methods as $payment_method) {
547
+			$pm_versions[ $payment_method->slug() ] = [
548
+				'pm_slug' => $payment_method->slug(),
549
+			];
550
+		}
551
+		// Convert money for a display format.
552
+		$decimal_places = CurrencyManager::getDecimalPlaces();
553
+		$org_country    = isset(EE_Registry::instance()->CFG->organization)
554
+		&& EE_Registry::instance()->CFG->organization instanceof EE_Organization_Config
555
+			? EE_Registry::instance()->CFG->organization->CNT_ISO
556
+			: 'US';
557
+		$transaction_id = $this->transaction instanceof EE_Transaction ? $this->transaction->ID() : 0;
558
+		$currency_code  = CurrencyManager::currencyCode();
559
+		return [
560
+			'pm_versions'            => $pm_versions,
561
+			'payment_currency'       => $currency_code,
562
+			'checkout_type'          => $this->checkout_type,
563
+			'currency_sign'          => EE_Registry::instance()->CFG->currency->sign,
564
+			'pp_order_nonce'         => wp_create_nonce(Domain::CAPTURE_ORDER_NONCE_NAME),
565
+			// The transaction ID is only used for logging errors.
566
+			'txn_id'                 => $transaction_id,
567
+			'org_country'            => $org_country,
568
+			'decimal_places'         => $decimal_places,
569
+			'site_name'              => get_bloginfo('name'),
570
+			'active_states'          => EED_PayPalCommerce::getActiveStates(),
571
+			'no_spco_error'          => esc_html__(
572
+				'It appears the SDK script was not loaded properly! Please refresh the page and try again or contact support.',
573
+				'event_espresso'
574
+			),
575
+			'no_pm_error'            => esc_html__(
576
+				'It appears that PayPal Commerce checkout JavaScript was not loaded properly! Please refresh the page and try again or contact support. PayPal Commerce payments can\'t be processed.',
577
+				'event_espresso'
578
+			),
579
+			'browser_not_supported'  => esc_html__(
580
+				'It appears that this browser is not supported by PayPal scripts. We apologize, but PayPal payments won\'t work in this browser version.',
581
+				'event_espresso'
582
+			),
583
+			'get_token_error'        => esc_html__(
584
+				'There was an error while trying to get the payment token. Please refresh the page and try again or contact support.',
585
+				'event_espresso'
586
+			),
587
+			'form_validation_notice' => esc_html__('Billing form information not valid.', 'event_espresso'),
588
+			'no_verification_token'  => esc_html__('Missing the Verification token.', 'event_espresso'),
589
+			'error_response'         => esc_html__('Error response received', 'event_espresso'),
590
+			'payment_error'          => esc_html__(
591
+				'There was an error with this payment. See the logs for details.',
592
+				'event_espresso'
593
+			),
594
+			'no_order_id'            => esc_html__('No Order ID found.', 'event_espresso'),
595
+			'general_pp_error'       => esc_html__('PayPal form threw an error.', 'event_espresso'),
596
+			'hf_render_error'        => esc_html__('Hosted fields could not be rendered!', 'event_espresso'),
597
+			'pm_capture_error'       => esc_html__('Payment could not be captured!', 'event_espresso'),
598
+			'contact_support_msg'    => sprintf(
599
+				/* translators: %1$s: organization email, %2$s: the transaction ID */
600
+				esc_html__('Please contact support (%1$s) for more details on this transaction #%2$s.', 'event_espresso'),
601
+				EE_Registry::instance()->CFG->organization->get_pretty('email'),
602
+				$transaction_id
603
+			),
604
+			'not_acdc_eligible'      => esc_html__(
605
+				'This merchant is not eligible for Advanced Card Fields checkout type.',
606
+				'event_espresso'
607
+			),
608
+			'processor_response'     => esc_html__('Processor response: ', 'event_espresso'),
609
+		];
610
+	}
611 611
 }
Please login to merge, or discard this patch.
PaymentMethods/PayPalCommerce/modules/EED_PayPalOnboard.module.php 2 patches
Indentation   +759 added lines, -759 removed lines patch added patch discarded remove patch
@@ -20,766 +20,766 @@
 block discarded – undo
20 20
  */
21 21
 class EED_PayPalOnboard extends EED_Module
22 22
 {
23
-    /**
24
-     * @return EED_Module
25
-     * @throws EE_Error
26
-     * @throws ReflectionException
27
-     */
28
-    public static function instance(): EED_Module
29
-    {
30
-        return parent::get_instance(__CLASS__);
31
-    }
32
-
33
-
34
-    /**
35
-     * Run - initial module setup.
36
-     *
37
-     * @param WP $WP
38
-     * @return void
39
-     */
40
-    public function run($WP)
41
-    {
42
-    }
43
-
44
-
45
-    /**
46
-     * For hooking into EE Admin Core and other modules.
47
-     *
48
-     * @return void
49
-     */
50
-    public static function set_hooks_admin(): void
51
-    {
52
-        // check for the most basic EE capability
53
-        /** @var EE_Capabilities $capabilities */
54
-        $capabilities = LoaderFactory::getLoader()->getShared(EE_Capabilities::class);
55
-        if (
56
-            DbStatus::isOffline()
57
-            || ! $capabilities->current_user_can('ee_manage_gateways', 'manage-paypal-onboarding')
58
-        ) {
59
-            return;
60
-        }
61
-        // Get onboarding URL.
62
-        add_action('wp_ajax_eeaPpGetOnboardingUrl', [__CLASS__, 'getOnboardingUrl']);
63
-        // Catch the return/redirect from PayPal onboarding page.
64
-        add_action('admin_init', [__CLASS__, 'updateOnboardingStatus']);
65
-        // Return the connection/onboard status.
66
-        add_action('wp_ajax_eeaPpGetOnboardStatus', [__CLASS__, 'getOnboardStatus']);
67
-        // Revoke access.
68
-        add_action('wp_ajax_eeaPpOffboard', [__CLASS__, 'offboard']);
69
-        // Clear all metadata.
70
-        add_action('wp_ajax_eeaPpClearMetaData', [__CLASS__, 'clearMetaData']);
71
-        add_action('wp_ajax_eeaPpSaveDebugMode', [__CLASS__, 'eeaPpSaveDebugMode']);
72
-        // Admin notice.
73
-        add_action('admin_init', [__CLASS__, 'adminNotice']);
74
-    }
75
-
76
-
77
-    /**
78
-     * Get the onboarding URL.
79
-     * (AJAX)
80
-     *
81
-     * @return void
82
-     */
83
-    public static function getOnboardingUrl(): void
84
-    {
85
-        $signup_link = '';
86
-        try {
87
-            $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
88
-            if (! $paypal_pm instanceof EE_Payment_Method) {
89
-                PayPalLogger::errorLogAndExit(
90
-                    esc_html__('No payment method.', 'event_espresso'),
91
-                    EED_Module::getRequest()->postParams(),
92
-                    $paypal_pm
93
-                );
94
-            }
95
-            PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
96
-            // Just get a new onboarding URL every time.
97
-            $signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
98
-            if (! $signup_link) {
99
-                $err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
100
-                PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
101
-            }
102
-        } catch (Exception $e) {
103
-            PayPalLogger::errorLogAndExit($e->getMessage(), ['trace' => $e->getTrace()]);
104
-        }
105
-        // Is it empty (can happen if we didn't get the URL through the API).
106
-        $signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
107
-        wp_send_json(
108
-            [
109
-                'signup_link' => $signup_link,
110
-            ]
111
-        );
112
-    }
113
-
114
-
115
-    /**
116
-     * Request the sign-up link from PayPal.
117
-     *
118
-     * @param EE_Payment_Method $paypal_pm
119
-     * @param bool              $one_time_request
120
-     * @return string
121
-     * @throws EE_Error
122
-     * @throws Exception
123
-     */
124
-    public static function requestOnboardingUrl(EE_Payment_Method $paypal_pm, bool $one_time_request = false): string
125
-    {
126
-        $signup_link = '';
127
-        // Get the access token.
128
-        $access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
129
-        if (! $access_token) {
130
-            $err_msg = esc_html__('Error! No access token.', 'event_espresso');
131
-            PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
132
-            return '';
133
-        }
134
-        // Request the access token.
135
-        $body_params = EED_PayPalOnboard::signupLinkRequestBody($paypal_pm);
136
-        $bn_code     = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_BN_CODE);
137
-        $post_params = [
138
-            'method'  => 'POST',
139
-            'headers' => [
140
-                'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
141
-                'Content-Type'                  => 'application/json',
142
-                'Authorization'                 => 'Bearer ' . $access_token,
143
-                'PayPal-Partner-Attribution-Id' => $bn_code,
144
-            ],
145
-            'body'    => $body_params,
146
-        ];
147
-        $request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
148
-        $response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
149
-        // Check the data we received.
150
-        if (isset($response['error']) || empty($response['links'])) {
151
-            // Did the original access token get replaced by any chance ?
152
-            if (
153
-                ! $one_time_request
154
-                && ! empty($response['message'])
155
-                && $response['message'] === 'Access Token not found in cache'
156
-            ) {
157
-                // Clear all PM metadata and try getting the access token One more time.
158
-                PayPalExtraMetaManager::deleteData($paypal_pm);
159
-                PayPalLogger::errorLog(
160
-                    esc_html__('Removing old metadata before new onboarding.', 'event_espresso'),
161
-                    $response,
162
-                    $paypal_pm
163
-                );
164
-                return EED_PayPalOnboard::requestOnboardingUrl($paypal_pm, true);
165
-            }
166
-            $err_msg = esc_html__('Incoming sign-up link parameter validation failed.', 'event_espresso');
167
-            PayPalLogger::errorLog($err_msg, $response, $paypal_pm);
168
-            return '';
169
-        }
170
-        // Now retrieve that sign-up link.
171
-        foreach ($response['links'] as $link) {
172
-            if ($link['rel'] === 'action_url') {
173
-                $signup_link = $link['href'] ?? '';
174
-            }
175
-        }
176
-        return $signup_link;
177
-    }
178
-
179
-
180
-    /**
181
-     * Get the return URL.
182
-     *
183
-     * @param EE_Payment_Method $paypal_pm
184
-     * @return string
185
-     * @throws Exception
186
-     */
187
-    public static function signupLinkRequestBody(EE_Payment_Method $paypal_pm): string
188
-    {
189
-        $identifier_string = new OneTimeString($paypal_pm->debug_mode());
190
-        $tracking_id       = $identifier_string->value();
191
-        $request           = LoaderFactory::getLoader()->getShared(RequestInterface::class);
192
-        $selected_payment  = $request->getRequestParam('selected_payment', 'EXPRESS_CHECKOUT');
193
-        // Save the identifier for future use.
194
-        PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_TRACKING_ID, $tracking_id);
195
-        // Assemble the return URL.
196
-        $return_url = EED_PayPalOnboard::getReturnUrl($paypal_pm, $selected_payment);
197
-        return json_encode(
198
-            [
199
-                'tracking_id'             => $tracking_id,
200
-                'operations'              => [
201
-                    [
202
-                        'operation'                  => 'API_INTEGRATION',
203
-                        'api_integration_preference' => [
204
-                            'rest_api_integration' => [
205
-                                'integration_method'  => 'PAYPAL',
206
-                                'integration_type'    => 'THIRD_PARTY',
207
-                                'third_party_details' => [
208
-                                    'features' => ['PAYMENT', 'REFUND', 'PARTNER_FEE'],
209
-                                ],
210
-                            ],
211
-                        ],
212
-                    ],
213
-                ],
214
-                'products'                => [$selected_payment],
215
-                'legal_consents'          => [
216
-                    [
217
-                        'type'    => 'SHARE_DATA_CONSENT',
218
-                        'granted' => true,
219
-                    ],
220
-                ],
221
-                'partner_config_override' => [
222
-                    'return_url' => $return_url,
223
-                ],
224
-            ]
225
-        );
226
-    }
227
-
228
-
229
-    /**
230
-     * Get the return URL.
231
-     *
232
-     * @param EE_Payment_Method $paypal_pm
233
-     * @param string            $selected_payment
234
-     * @return string
235
-     * @throws EE_Error
236
-     * @throws ReflectionException
237
-     */
238
-    public static function getReturnUrl(EE_Payment_Method $paypal_pm, string $selected_payment = ''): string
239
-    {
240
-        $wp_nonce = EED_Module::getRequest()->getRequestParam('wp_nonce');
241
-        $nonce    = wp_create_nonce(Domain::NONCE_NAME_ONBOARDING_RETURN);
242
-        return add_query_arg(
243
-            [
244
-                'page'                        => 'espresso_payment_settings',
245
-                'webhook_action'              => 'eepPpcMerchantOnboard',
246
-                'payment_method'              => $paypal_pm->slug(),
247
-                '_wpnonce'                    => $wp_nonce,
248
-                'nonce'                       => $nonce,
249
-                'selected_payment'            => $selected_payment,
250
-                Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
251
-            ],
252
-            admin_url('admin.php')
253
-        );
254
-    }
255
-
256
-
257
-    /**
258
-     * Redirect to the payment method (PP) settings home page.
259
-     *
260
-     * @return void
261
-     */
262
-    public static function redirectToPmSettingsHome(): void
263
-    {
264
-        $get_params = EED_Module::getRequest()->getParams();
265
-        if (empty($get_params['payment_method'])) {
266
-            // Simply do not redirect.
267
-            return;
268
-        }
269
-        $args_to_add = [
270
-            'page'           => 'espresso_payment_settings',
271
-            'payment_method' => $get_params['payment_method'],
272
-        ];
273
-        if (isset($get_params['sandbox_mode'])) {
274
-            $args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
275
-        }
276
-        $home_url = add_query_arg($args_to_add, admin_url('admin.php'));
277
-        wp_redirect($home_url);
278
-        exit;
279
-    }
280
-
281
-
282
-    /**
283
-     * Check user’s onboarding status.
284
-     * This will handle the user return from the auth page and also check the status via the API.
285
-     *
286
-     * @return void
287
-     * @throws EE_Error
288
-     * @throws ReflectionException
289
-     */
290
-    public static function updateOnboardingStatus(): void
291
-    {
292
-        // Check if this is the webhook from PayPal.
293
-        if (
294
-            ! isset($_GET['webhook_action'], $_GET['nonce'])
295
-            || $_GET['webhook_action'] !== 'eepPpcMerchantOnboard'
296
-        ) {
297
-            return;  // Ignore.
298
-        }
299
-        $get_params = EED_Module::getRequest()->getParams();
300
-        // Get the payment method.
301
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
302
-        if (! $paypal_pm instanceof EE_Payment_Method) {
303
-            PayPalLogger::errorLog(
304
-                esc_html__('Not able to validate the payment method.', 'event_espresso'),
305
-                $get_params,
306
-                $paypal_pm
307
-            );
308
-            EED_PayPalOnboard::redirectToPmSettingsHome();
309
-            return;
310
-        }
311
-        // Check the response (GET) parameters.
312
-        if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
313
-            // Missing parameters. Can't proceed.
314
-            PayPalLogger::errorLog(
315
-                esc_html__('Missing required onboarding parameters.', 'event_espresso'),
316
-                $get_params,
317
-                $paypal_pm
318
-            );
319
-            EED_PayPalOnboard::redirectToPmSettingsHome();
320
-            return;
321
-        }
322
-        // Check on the onboarding status (recommended by PP).
323
-        $onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
324
-            $paypal_pm,
325
-            $get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
326
-        );
327
-        if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
328
-            PayPalLogger::errorLog(
329
-                $onboarding_status['message'] ?? esc_html__('Failed to track seller onboarding.', 'event_espresso'),
330
-                array_merge($get_params, $onboarding_status),
331
-                $paypal_pm
332
-            );
333
-            EED_PayPalOnboard::redirectToPmSettingsHome();
334
-            return;
335
-        }
336
-        // Start saving the setup and info.
337
-        PayPalExtraMetaManager::parseAndSaveOptions($paypal_pm, $onboarding_status, $get_params);
338
-        // Save the credentials.
339
-        PayPalExtraMetaManager::saveSellerApiCredentials($paypal_pm, $get_params);
340
-        // Also clen GET params by redirecting, because PP auto redirects to the return_url on closing the onboarding window.
341
-        EED_PayPalOnboard::redirectToPmSettingsHome();
342
-    }
343
-
344
-
345
-    /**
346
-     * Check if all required parameters for the onboarding status check are present.
347
-     *
348
-     * @param array $data
349
-     * @param mixed $paypal_pm
350
-     * @return bool
351
-     */
352
-    public static function onboardingStatusResponseValid(array $data, $paypal_pm): bool
353
-    {
354
-        // Check that we have all the required parameters and the nonce is ok.
355
-        if (
356
-            $paypal_pm instanceof EE_Payment_Method
357
-            && wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
358
-            && ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
359
-            && ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
360
-            && isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
361
-        ) {
362
-            return true;
363
-        }
364
-        return false;
365
-    }
366
-
367
-
368
-    /**
369
-     * Get partner access token.
370
-     *
371
-     * @param EE_Payment_Method $paypal_pm
372
-     * @return string
373
-     * @throws EE_Error
374
-     * @throws ReflectionException
375
-     */
376
-    public static function getPartnerAccessToken(EE_Payment_Method $paypal_pm): string
377
-    {
378
-        // See if it's already saved.
379
-        $access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
380
-        // If we don't have it, request/update it.
381
-        if (! $access_token) {
382
-            return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
383
-        }
384
-        if (EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm)) {
385
-            return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
386
-        }
387
-        // Access token is saved as encrypted, but return decrypted.
388
-        return $access_token;
389
-    }
390
-
391
-
392
-    /**
393
-     * Get partner access token.
394
-     *
395
-     * @param EE_Payment_Method $paypal_pm
396
-     * @return bool
397
-     * @throws EE_Error
398
-     * @throws ReflectionException
399
-     */
400
-    public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
401
-    {
402
-        $expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_TOKEN_EXPIRES_IN);
403
-        if (! $expires_at) {
404
-            return true;
405
-        }
406
-        // Validate the token expiration date.
407
-        $minutes_left = round(($expires_at - time()) / 60);
408
-        // Refresh if less than 2 hours till expiration left. Access tokens have a life of 15 minutes or 8 hours.
409
-        if ($minutes_left <= 60 * 2) {
410
-            return true;
411
-        }
412
-        return false;
413
-    }
414
-
415
-
416
-    /**
417
-     * Request the partner access token from PayPal and save/update it.
418
-     *
419
-     * @param EE_Payment_Method $paypal_pm
420
-     * @return string
421
-     * @throws EE_Error
422
-     * @throws ReflectionException
423
-     */
424
-    public static function requestPartnerAccessToken(EE_Payment_Method $paypal_pm): string
425
-    {
426
-        $nonce = wp_create_nonce('eea_pp_commerce_get_access_token');
427
-        // Request the access token.
428
-        $post_args = [
429
-            'method' => 'POST',
430
-            'body'   => [
431
-                'nonce'                       => $nonce,
432
-                'api_version'                 => 'v1',
433
-                Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
434
-            ],
435
-        ];
436
-        if (defined('LOCAL_MIDDLEMAN_SERVER')) {
437
-            $post_args['sslverify'] = Manager::verifySSL();
438
-        }
439
-        $post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
440
-        $response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
441
-        if (isset($response['error'])) {
442
-            return '';
443
-        }
444
-        // Check the data we received.
445
-        if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
446
-            return '';
447
-        }
448
-        // If we are here all seems to be ok. Save the token and it's data.
449
-        $saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
450
-        if (! $saved) {
451
-            return '';
452
-        }
453
-        return $response['access_token'];
454
-    }
455
-
456
-
457
-    /**
458
-     * Request seller onboarding status from PayPal.
459
-     *
460
-     * @param EE_Payment_Method $paypal_pm
461
-     * @param string            $merchant_id
462
-     * @return array
463
-     * @throws EE_Error
464
-     * @throws ReflectionException
465
-     */
466
-    public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
467
-    {
468
-        $track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
469
-        if (! $track_onboarding instanceof TrackSellerOnboarding) {
470
-            return [
471
-                'error'   => 'TRACK_ONBOARDING_FAILED',
472
-                'message' => esc_html__('Failed to track seller onboarding.', 'event_espresso')
473
-            ];
474
-        }
475
-        return $track_onboarding->isValid();
476
-    }
477
-
478
-
479
-    /**
480
-     * Returns the Track Seller Onboarding API.
481
-     *
482
-     * @param EE_Payment_Method $paypal_pm
483
-     * @param string            $merchant_id
484
-     * @return TrackSellerOnboarding|null
485
-     * @throws EE_Error
486
-     * @throws ReflectionException
487
-     */
488
-    public static function getTrackOnboardingApi(
489
-        EE_Payment_Method $paypal_pm,
490
-        string $merchant_id
491
-    ): ?TrackSellerOnboarding {
492
-        $partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
493
-        $paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
494
-        if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
495
-            return null;
496
-        }
497
-        return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
498
-    }
499
-
500
-
501
-    /**
502
-     * Check the onboard status and return the result.
503
-     * (AJAX)
504
-     *
505
-     * @return void
506
-     * @throws EE_Error
507
-     * @throws ReflectionException
508
-     */
509
-    public static function getOnboardStatus(): void
510
-    {
511
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
512
-        if (! $paypal_pm instanceof EE_Payment_Method) {
513
-            $err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
514
-            PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
515
-            wp_send_json(['on_board' => false]);
516
-        }
517
-        try {
518
-            $seller_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_SELLER_MERCHANT_ID) ?? '--';
519
-        } catch (Exception $e) {
520
-            $seller_id = '--';
521
-            PayPalLogger::errorLog($e->getMessage(), ['trace' => $e->getTrace()]);
522
-        }
523
-        wp_send_json(
524
-            [
525
-                'on_board'  => EED_PayPalOnboard::isOnboard($paypal_pm),
526
-                'seller_id' => $seller_id,
527
-            ]
528
-        );
529
-    }
530
-
531
-
532
-    /**
533
-     * De-authorize the seller. Remove all API credentials.
534
-     * (AJAX)
535
-     *
536
-     * @return void
537
-     * @throws EE_Error
538
-     * @throws ReflectionException
539
-     */
540
-    public static function offboard(): void
541
-    {
542
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
543
-        EED_PayPalOnboard::validatePmAjax($paypal_pm);
544
-        PayPalExtraMetaManager::deleteData($paypal_pm);
545
-        PayPalLogger::errorLog(
546
-            esc_html__('Offboarding. Removing metadata.', 'event_espresso'),
547
-            EED_Module::getRequest()->postParams(),
548
-            $paypal_pm
549
-        );
550
-        wp_send_json(['success' => true]);
551
-    }
552
-
553
-
554
-    /**
555
-     * Clear all credentials metadata.
556
-     * (AJAX)
557
-     *
558
-     * @return void
559
-     * @throws EE_Error
560
-     * @throws ReflectionException
561
-     */
562
-    public static function clearMetaData(): void
563
-    {
564
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
565
-        EED_PayPalOnboard::validatePmAjax($paypal_pm);
566
-        PayPalExtraMetaManager::deleteAllData($paypal_pm);
567
-        PayPalLogger::errorLog(
568
-            esc_html__('Doing a Reset. Removing all PM settings metadata.', 'event_espresso'),
569
-            EED_Module::getRequest()->postParams(),
570
-            $paypal_pm
571
-        );
572
-        wp_send_json(['success' => true]);
573
-    }
574
-
575
-
576
-    /**
577
-     * Save the sandbox mode option.
578
-     * (AJAX)
579
-     *
580
-     * @return void
581
-     * @throws EE_Error
582
-     * @throws ReflectionException
583
-     */
584
-    public static function eeaPpSaveDebugMode(): void
585
-    {
586
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
587
-        EED_PayPalOnboard::validatePmAjax($paypal_pm);
588
-        // Reset the partner access token.
589
-        PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
590
-        // And do the data reset.
591
-        PayPalExtraMetaManager::deleteAllData($paypal_pm);
592
-        wp_send_json(['success' => true]);
593
-    }
594
-
595
-
596
-    /**
597
-     * Validate the PM instance, returning an ajax response on invalid.
598
-     *
599
-     * @param $paypal_pm
600
-     * @return void
601
-     */
602
-    public static function validatePmAjax($paypal_pm): void
603
-    {
604
-        if (! $paypal_pm instanceof EE_Payment_Method) {
605
-            wp_send_json(
606
-                [
607
-                    'error'   => 'INVALID_PM',
608
-                    'message' => esc_html__(
609
-                        'Invalid payment method. Please refresh the page and try again.',
610
-                        'event_espresso'
611
-                    ),
612
-                ]
613
-            );
614
-        }
615
-    }
616
-
617
-
618
-    /**
619
-     * Checks if already onboard.
620
-     *
621
-     * @param EE_Payment_Method $payment_method
622
-     * @return boolean
623
-     * @throws EE_Error
624
-     * @throws ReflectionException
625
-     */
626
-    public static function isOnboard(EE_Payment_Method $payment_method): bool
627
-    {
628
-        $pp_meta_data = PayPalExtraMetaManager::getAllData($payment_method);
629
-        return
630
-            (
631
-                // onboarded with a third party integration ?
632
-                ! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
633
-                && ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
634
-            ) || (
635
-                // or with the first party integration ?
636
-                ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
637
-                && ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
638
-                && ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
639
-            );
640
-    }
641
-
642
-
643
-    /**
644
-     * Send a request and return a decoded response body.
645
-     *
646
-     * @param EE_Payment_Method $paypal_pm
647
-     * @param string            $request_url
648
-     * @param array             $request_args
649
-     * @return array
650
-     */
651
-    public static function sendRequest(EE_Payment_Method $paypal_pm, string $request_url, array $request_args): array
652
-    {
653
-        $error_return = ['error' => true];
654
-        $response     = wp_remote_request($request_url, $request_args);
655
-        if (is_wp_error($response)) {
656
-            $message = $response->get_error_message();
657
-            PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
658
-            $error_return['message'] = $message;
659
-            return $error_return;
660
-        }
661
-        $response_body = (isset($response['body']) && $response['body']) ? json_decode($response['body'], true) : [];
662
-        if (empty($response_body) || isset($response_body['error'])) {
663
-            $message = $response_body['error_description']
664
-                ?? sprintf(
665
-                    esc_html__('Unknown response received while sending a request to: %1$s', 'event_espresso'),
666
-                    $request_url
667
-                );
668
-            PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
669
-            $error_return['message'] = $message;
670
-            return $error_return;
671
-        }
672
-        return $response_body;
673
-    }
674
-
675
-
676
-    /**
677
-     * Check the response for a partner token request.
678
-     *
679
-     * @param array             $response
680
-     * @param EE_Payment_Method $paypal_pm
681
-     * @return bool
682
-     */
683
-    public static function partnerTokenResponseValid(array $response, EE_Payment_Method $paypal_pm): bool
684
-    {
685
-        // Check the data we received.
686
-        if (
687
-            empty($response['nonce'])
688
-            || ! wp_verify_nonce($response['nonce'], 'eea_pp_commerce_get_access_token')
689
-            || empty($response['access_token'])
690
-            || empty($response['app_id'])
691
-            || empty($response['expires_in'])
692
-            || empty($response['partner_client_id'])
693
-            || empty($response['partner_merchant_id'])
694
-        ) {
695
-            // This is an error.
696
-            PayPalLogger::errorLog(
697
-                esc_html__('Incoming parameter validation failed.', 'event_espresso'),
698
-                $response,
699
-                $paypal_pm
700
-            );
701
-            return false;
702
-        }
703
-        return true;
704
-    }
705
-
706
-
707
-    /**
708
-     * Returns the base URL to the middleman server.
709
-     * If LOCAL_MIDDLEMAN_SERVER is defined, requests will be sent to connect.eventespresso.test
710
-     *
711
-     * @param EE_Payment_Method $payment_method
712
-     * @return string
713
-     * @throws EE_Error
714
-     * @throws ReflectionException
715
-     */
716
-    public static function getMiddlemanBaseUrl(EE_Payment_Method $payment_method): string
717
-    {
718
-        $target = defined('LOCAL_MIDDLEMAN_SERVER') ? 'test' : 'com';
719
-        // If this PM is used under different provider accounts, you might need an account indicator.
720
-        $account = defined('EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR') ? EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR : '';
721
-        $postfix = $payment_method->debug_mode() ? '_sandbox' : '';
722
-        return "https://connect.eventespresso.$target/paypal_commerce$account$postfix/";
723
-    }
724
-
725
-
726
-    /**
727
-     * This Payment Method admin notices.
728
-     *
729
-     * @return void
730
-     * @throws EE_Error
731
-     * @throws ReflectionException
732
-     */
733
-    public static function adminNotice()
734
-    {
735
-        // Show the notice if PayPal Commerce PM is active but merchant is not onboard.
736
-        $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug(Domain::PM_SLUG);
737
-        if (
738
-            $pp_commerce instanceof EE_Payment_Method
739
-            && $pp_commerce->active()
740
-            && ! EED_PayPalOnboard::isOnboard($pp_commerce)
741
-        ) {
742
-            add_action('admin_notices', [EED_PayPalOnboard::class, 'notOnboardNotice']);
743
-        }
744
-    }
745
-
746
-
747
-    /**
748
-     * Contents of the not onboard admin notice.
749
-     *
750
-     * @return void
751
-     * @throws EE_Error
752
-     * @throws ReflectionException
753
-     */
754
-    public static function notOnboardNotice()
755
-    {
756
-        $open_anchor = $close_anchor = '';
757
-        $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug(Domain::PM_SLUG);
758
-        if ($pp_commerce instanceof EE_Payment_Method) {
759
-            $pm_page      = add_query_arg(
760
-                [
761
-                    'page'           => 'espresso_payment_settings',
762
-                    'webhook_action' => 'eepPpcMerchantOnboard',
763
-                    'payment_method' => $pp_commerce->slug(),
764
-                ],
765
-                admin_url('admin.php')
766
-            );
767
-            $open_anchor  = "<a href='$pm_page'>";
768
-            $close_anchor = "</a>";
769
-        }
770
-        $notice = sprintf(
771
-            esc_html__(
772
-                '%1$sPayPal Commerce%2$s payment method was activated but is not connected to PayPal. Please %3$sfinish setting up%4$s this payment method.',
773
-                'event_espresso'
774
-            ),
775
-            '<strong>',
776
-            '</strong>',
777
-            $open_anchor,
778
-            $close_anchor
779
-        );
780
-        echo "
23
+	/**
24
+	 * @return EED_Module
25
+	 * @throws EE_Error
26
+	 * @throws ReflectionException
27
+	 */
28
+	public static function instance(): EED_Module
29
+	{
30
+		return parent::get_instance(__CLASS__);
31
+	}
32
+
33
+
34
+	/**
35
+	 * Run - initial module setup.
36
+	 *
37
+	 * @param WP $WP
38
+	 * @return void
39
+	 */
40
+	public function run($WP)
41
+	{
42
+	}
43
+
44
+
45
+	/**
46
+	 * For hooking into EE Admin Core and other modules.
47
+	 *
48
+	 * @return void
49
+	 */
50
+	public static function set_hooks_admin(): void
51
+	{
52
+		// check for the most basic EE capability
53
+		/** @var EE_Capabilities $capabilities */
54
+		$capabilities = LoaderFactory::getLoader()->getShared(EE_Capabilities::class);
55
+		if (
56
+			DbStatus::isOffline()
57
+			|| ! $capabilities->current_user_can('ee_manage_gateways', 'manage-paypal-onboarding')
58
+		) {
59
+			return;
60
+		}
61
+		// Get onboarding URL.
62
+		add_action('wp_ajax_eeaPpGetOnboardingUrl', [__CLASS__, 'getOnboardingUrl']);
63
+		// Catch the return/redirect from PayPal onboarding page.
64
+		add_action('admin_init', [__CLASS__, 'updateOnboardingStatus']);
65
+		// Return the connection/onboard status.
66
+		add_action('wp_ajax_eeaPpGetOnboardStatus', [__CLASS__, 'getOnboardStatus']);
67
+		// Revoke access.
68
+		add_action('wp_ajax_eeaPpOffboard', [__CLASS__, 'offboard']);
69
+		// Clear all metadata.
70
+		add_action('wp_ajax_eeaPpClearMetaData', [__CLASS__, 'clearMetaData']);
71
+		add_action('wp_ajax_eeaPpSaveDebugMode', [__CLASS__, 'eeaPpSaveDebugMode']);
72
+		// Admin notice.
73
+		add_action('admin_init', [__CLASS__, 'adminNotice']);
74
+	}
75
+
76
+
77
+	/**
78
+	 * Get the onboarding URL.
79
+	 * (AJAX)
80
+	 *
81
+	 * @return void
82
+	 */
83
+	public static function getOnboardingUrl(): void
84
+	{
85
+		$signup_link = '';
86
+		try {
87
+			$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
88
+			if (! $paypal_pm instanceof EE_Payment_Method) {
89
+				PayPalLogger::errorLogAndExit(
90
+					esc_html__('No payment method.', 'event_espresso'),
91
+					EED_Module::getRequest()->postParams(),
92
+					$paypal_pm
93
+				);
94
+			}
95
+			PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
96
+			// Just get a new onboarding URL every time.
97
+			$signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
98
+			if (! $signup_link) {
99
+				$err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
100
+				PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
101
+			}
102
+		} catch (Exception $e) {
103
+			PayPalLogger::errorLogAndExit($e->getMessage(), ['trace' => $e->getTrace()]);
104
+		}
105
+		// Is it empty (can happen if we didn't get the URL through the API).
106
+		$signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
107
+		wp_send_json(
108
+			[
109
+				'signup_link' => $signup_link,
110
+			]
111
+		);
112
+	}
113
+
114
+
115
+	/**
116
+	 * Request the sign-up link from PayPal.
117
+	 *
118
+	 * @param EE_Payment_Method $paypal_pm
119
+	 * @param bool              $one_time_request
120
+	 * @return string
121
+	 * @throws EE_Error
122
+	 * @throws Exception
123
+	 */
124
+	public static function requestOnboardingUrl(EE_Payment_Method $paypal_pm, bool $one_time_request = false): string
125
+	{
126
+		$signup_link = '';
127
+		// Get the access token.
128
+		$access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
129
+		if (! $access_token) {
130
+			$err_msg = esc_html__('Error! No access token.', 'event_espresso');
131
+			PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
132
+			return '';
133
+		}
134
+		// Request the access token.
135
+		$body_params = EED_PayPalOnboard::signupLinkRequestBody($paypal_pm);
136
+		$bn_code     = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_BN_CODE);
137
+		$post_params = [
138
+			'method'  => 'POST',
139
+			'headers' => [
140
+				'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
141
+				'Content-Type'                  => 'application/json',
142
+				'Authorization'                 => 'Bearer ' . $access_token,
143
+				'PayPal-Partner-Attribution-Id' => $bn_code,
144
+			],
145
+			'body'    => $body_params,
146
+		];
147
+		$request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
148
+		$response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
149
+		// Check the data we received.
150
+		if (isset($response['error']) || empty($response['links'])) {
151
+			// Did the original access token get replaced by any chance ?
152
+			if (
153
+				! $one_time_request
154
+				&& ! empty($response['message'])
155
+				&& $response['message'] === 'Access Token not found in cache'
156
+			) {
157
+				// Clear all PM metadata and try getting the access token One more time.
158
+				PayPalExtraMetaManager::deleteData($paypal_pm);
159
+				PayPalLogger::errorLog(
160
+					esc_html__('Removing old metadata before new onboarding.', 'event_espresso'),
161
+					$response,
162
+					$paypal_pm
163
+				);
164
+				return EED_PayPalOnboard::requestOnboardingUrl($paypal_pm, true);
165
+			}
166
+			$err_msg = esc_html__('Incoming sign-up link parameter validation failed.', 'event_espresso');
167
+			PayPalLogger::errorLog($err_msg, $response, $paypal_pm);
168
+			return '';
169
+		}
170
+		// Now retrieve that sign-up link.
171
+		foreach ($response['links'] as $link) {
172
+			if ($link['rel'] === 'action_url') {
173
+				$signup_link = $link['href'] ?? '';
174
+			}
175
+		}
176
+		return $signup_link;
177
+	}
178
+
179
+
180
+	/**
181
+	 * Get the return URL.
182
+	 *
183
+	 * @param EE_Payment_Method $paypal_pm
184
+	 * @return string
185
+	 * @throws Exception
186
+	 */
187
+	public static function signupLinkRequestBody(EE_Payment_Method $paypal_pm): string
188
+	{
189
+		$identifier_string = new OneTimeString($paypal_pm->debug_mode());
190
+		$tracking_id       = $identifier_string->value();
191
+		$request           = LoaderFactory::getLoader()->getShared(RequestInterface::class);
192
+		$selected_payment  = $request->getRequestParam('selected_payment', 'EXPRESS_CHECKOUT');
193
+		// Save the identifier for future use.
194
+		PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_TRACKING_ID, $tracking_id);
195
+		// Assemble the return URL.
196
+		$return_url = EED_PayPalOnboard::getReturnUrl($paypal_pm, $selected_payment);
197
+		return json_encode(
198
+			[
199
+				'tracking_id'             => $tracking_id,
200
+				'operations'              => [
201
+					[
202
+						'operation'                  => 'API_INTEGRATION',
203
+						'api_integration_preference' => [
204
+							'rest_api_integration' => [
205
+								'integration_method'  => 'PAYPAL',
206
+								'integration_type'    => 'THIRD_PARTY',
207
+								'third_party_details' => [
208
+									'features' => ['PAYMENT', 'REFUND', 'PARTNER_FEE'],
209
+								],
210
+							],
211
+						],
212
+					],
213
+				],
214
+				'products'                => [$selected_payment],
215
+				'legal_consents'          => [
216
+					[
217
+						'type'    => 'SHARE_DATA_CONSENT',
218
+						'granted' => true,
219
+					],
220
+				],
221
+				'partner_config_override' => [
222
+					'return_url' => $return_url,
223
+				],
224
+			]
225
+		);
226
+	}
227
+
228
+
229
+	/**
230
+	 * Get the return URL.
231
+	 *
232
+	 * @param EE_Payment_Method $paypal_pm
233
+	 * @param string            $selected_payment
234
+	 * @return string
235
+	 * @throws EE_Error
236
+	 * @throws ReflectionException
237
+	 */
238
+	public static function getReturnUrl(EE_Payment_Method $paypal_pm, string $selected_payment = ''): string
239
+	{
240
+		$wp_nonce = EED_Module::getRequest()->getRequestParam('wp_nonce');
241
+		$nonce    = wp_create_nonce(Domain::NONCE_NAME_ONBOARDING_RETURN);
242
+		return add_query_arg(
243
+			[
244
+				'page'                        => 'espresso_payment_settings',
245
+				'webhook_action'              => 'eepPpcMerchantOnboard',
246
+				'payment_method'              => $paypal_pm->slug(),
247
+				'_wpnonce'                    => $wp_nonce,
248
+				'nonce'                       => $nonce,
249
+				'selected_payment'            => $selected_payment,
250
+				Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
251
+			],
252
+			admin_url('admin.php')
253
+		);
254
+	}
255
+
256
+
257
+	/**
258
+	 * Redirect to the payment method (PP) settings home page.
259
+	 *
260
+	 * @return void
261
+	 */
262
+	public static function redirectToPmSettingsHome(): void
263
+	{
264
+		$get_params = EED_Module::getRequest()->getParams();
265
+		if (empty($get_params['payment_method'])) {
266
+			// Simply do not redirect.
267
+			return;
268
+		}
269
+		$args_to_add = [
270
+			'page'           => 'espresso_payment_settings',
271
+			'payment_method' => $get_params['payment_method'],
272
+		];
273
+		if (isset($get_params['sandbox_mode'])) {
274
+			$args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
275
+		}
276
+		$home_url = add_query_arg($args_to_add, admin_url('admin.php'));
277
+		wp_redirect($home_url);
278
+		exit;
279
+	}
280
+
281
+
282
+	/**
283
+	 * Check user’s onboarding status.
284
+	 * This will handle the user return from the auth page and also check the status via the API.
285
+	 *
286
+	 * @return void
287
+	 * @throws EE_Error
288
+	 * @throws ReflectionException
289
+	 */
290
+	public static function updateOnboardingStatus(): void
291
+	{
292
+		// Check if this is the webhook from PayPal.
293
+		if (
294
+			! isset($_GET['webhook_action'], $_GET['nonce'])
295
+			|| $_GET['webhook_action'] !== 'eepPpcMerchantOnboard'
296
+		) {
297
+			return;  // Ignore.
298
+		}
299
+		$get_params = EED_Module::getRequest()->getParams();
300
+		// Get the payment method.
301
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
302
+		if (! $paypal_pm instanceof EE_Payment_Method) {
303
+			PayPalLogger::errorLog(
304
+				esc_html__('Not able to validate the payment method.', 'event_espresso'),
305
+				$get_params,
306
+				$paypal_pm
307
+			);
308
+			EED_PayPalOnboard::redirectToPmSettingsHome();
309
+			return;
310
+		}
311
+		// Check the response (GET) parameters.
312
+		if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
313
+			// Missing parameters. Can't proceed.
314
+			PayPalLogger::errorLog(
315
+				esc_html__('Missing required onboarding parameters.', 'event_espresso'),
316
+				$get_params,
317
+				$paypal_pm
318
+			);
319
+			EED_PayPalOnboard::redirectToPmSettingsHome();
320
+			return;
321
+		}
322
+		// Check on the onboarding status (recommended by PP).
323
+		$onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
324
+			$paypal_pm,
325
+			$get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
326
+		);
327
+		if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
328
+			PayPalLogger::errorLog(
329
+				$onboarding_status['message'] ?? esc_html__('Failed to track seller onboarding.', 'event_espresso'),
330
+				array_merge($get_params, $onboarding_status),
331
+				$paypal_pm
332
+			);
333
+			EED_PayPalOnboard::redirectToPmSettingsHome();
334
+			return;
335
+		}
336
+		// Start saving the setup and info.
337
+		PayPalExtraMetaManager::parseAndSaveOptions($paypal_pm, $onboarding_status, $get_params);
338
+		// Save the credentials.
339
+		PayPalExtraMetaManager::saveSellerApiCredentials($paypal_pm, $get_params);
340
+		// Also clen GET params by redirecting, because PP auto redirects to the return_url on closing the onboarding window.
341
+		EED_PayPalOnboard::redirectToPmSettingsHome();
342
+	}
343
+
344
+
345
+	/**
346
+	 * Check if all required parameters for the onboarding status check are present.
347
+	 *
348
+	 * @param array $data
349
+	 * @param mixed $paypal_pm
350
+	 * @return bool
351
+	 */
352
+	public static function onboardingStatusResponseValid(array $data, $paypal_pm): bool
353
+	{
354
+		// Check that we have all the required parameters and the nonce is ok.
355
+		if (
356
+			$paypal_pm instanceof EE_Payment_Method
357
+			&& wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
358
+			&& ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
359
+			&& ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
360
+			&& isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
361
+		) {
362
+			return true;
363
+		}
364
+		return false;
365
+	}
366
+
367
+
368
+	/**
369
+	 * Get partner access token.
370
+	 *
371
+	 * @param EE_Payment_Method $paypal_pm
372
+	 * @return string
373
+	 * @throws EE_Error
374
+	 * @throws ReflectionException
375
+	 */
376
+	public static function getPartnerAccessToken(EE_Payment_Method $paypal_pm): string
377
+	{
378
+		// See if it's already saved.
379
+		$access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
380
+		// If we don't have it, request/update it.
381
+		if (! $access_token) {
382
+			return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
383
+		}
384
+		if (EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm)) {
385
+			return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
386
+		}
387
+		// Access token is saved as encrypted, but return decrypted.
388
+		return $access_token;
389
+	}
390
+
391
+
392
+	/**
393
+	 * Get partner access token.
394
+	 *
395
+	 * @param EE_Payment_Method $paypal_pm
396
+	 * @return bool
397
+	 * @throws EE_Error
398
+	 * @throws ReflectionException
399
+	 */
400
+	public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
401
+	{
402
+		$expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_TOKEN_EXPIRES_IN);
403
+		if (! $expires_at) {
404
+			return true;
405
+		}
406
+		// Validate the token expiration date.
407
+		$minutes_left = round(($expires_at - time()) / 60);
408
+		// Refresh if less than 2 hours till expiration left. Access tokens have a life of 15 minutes or 8 hours.
409
+		if ($minutes_left <= 60 * 2) {
410
+			return true;
411
+		}
412
+		return false;
413
+	}
414
+
415
+
416
+	/**
417
+	 * Request the partner access token from PayPal and save/update it.
418
+	 *
419
+	 * @param EE_Payment_Method $paypal_pm
420
+	 * @return string
421
+	 * @throws EE_Error
422
+	 * @throws ReflectionException
423
+	 */
424
+	public static function requestPartnerAccessToken(EE_Payment_Method $paypal_pm): string
425
+	{
426
+		$nonce = wp_create_nonce('eea_pp_commerce_get_access_token');
427
+		// Request the access token.
428
+		$post_args = [
429
+			'method' => 'POST',
430
+			'body'   => [
431
+				'nonce'                       => $nonce,
432
+				'api_version'                 => 'v1',
433
+				Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
434
+			],
435
+		];
436
+		if (defined('LOCAL_MIDDLEMAN_SERVER')) {
437
+			$post_args['sslverify'] = Manager::verifySSL();
438
+		}
439
+		$post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
440
+		$response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
441
+		if (isset($response['error'])) {
442
+			return '';
443
+		}
444
+		// Check the data we received.
445
+		if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
446
+			return '';
447
+		}
448
+		// If we are here all seems to be ok. Save the token and it's data.
449
+		$saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
450
+		if (! $saved) {
451
+			return '';
452
+		}
453
+		return $response['access_token'];
454
+	}
455
+
456
+
457
+	/**
458
+	 * Request seller onboarding status from PayPal.
459
+	 *
460
+	 * @param EE_Payment_Method $paypal_pm
461
+	 * @param string            $merchant_id
462
+	 * @return array
463
+	 * @throws EE_Error
464
+	 * @throws ReflectionException
465
+	 */
466
+	public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
467
+	{
468
+		$track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
469
+		if (! $track_onboarding instanceof TrackSellerOnboarding) {
470
+			return [
471
+				'error'   => 'TRACK_ONBOARDING_FAILED',
472
+				'message' => esc_html__('Failed to track seller onboarding.', 'event_espresso')
473
+			];
474
+		}
475
+		return $track_onboarding->isValid();
476
+	}
477
+
478
+
479
+	/**
480
+	 * Returns the Track Seller Onboarding API.
481
+	 *
482
+	 * @param EE_Payment_Method $paypal_pm
483
+	 * @param string            $merchant_id
484
+	 * @return TrackSellerOnboarding|null
485
+	 * @throws EE_Error
486
+	 * @throws ReflectionException
487
+	 */
488
+	public static function getTrackOnboardingApi(
489
+		EE_Payment_Method $paypal_pm,
490
+		string $merchant_id
491
+	): ?TrackSellerOnboarding {
492
+		$partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
493
+		$paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
494
+		if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
495
+			return null;
496
+		}
497
+		return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
498
+	}
499
+
500
+
501
+	/**
502
+	 * Check the onboard status and return the result.
503
+	 * (AJAX)
504
+	 *
505
+	 * @return void
506
+	 * @throws EE_Error
507
+	 * @throws ReflectionException
508
+	 */
509
+	public static function getOnboardStatus(): void
510
+	{
511
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
512
+		if (! $paypal_pm instanceof EE_Payment_Method) {
513
+			$err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
514
+			PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
515
+			wp_send_json(['on_board' => false]);
516
+		}
517
+		try {
518
+			$seller_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_SELLER_MERCHANT_ID) ?? '--';
519
+		} catch (Exception $e) {
520
+			$seller_id = '--';
521
+			PayPalLogger::errorLog($e->getMessage(), ['trace' => $e->getTrace()]);
522
+		}
523
+		wp_send_json(
524
+			[
525
+				'on_board'  => EED_PayPalOnboard::isOnboard($paypal_pm),
526
+				'seller_id' => $seller_id,
527
+			]
528
+		);
529
+	}
530
+
531
+
532
+	/**
533
+	 * De-authorize the seller. Remove all API credentials.
534
+	 * (AJAX)
535
+	 *
536
+	 * @return void
537
+	 * @throws EE_Error
538
+	 * @throws ReflectionException
539
+	 */
540
+	public static function offboard(): void
541
+	{
542
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
543
+		EED_PayPalOnboard::validatePmAjax($paypal_pm);
544
+		PayPalExtraMetaManager::deleteData($paypal_pm);
545
+		PayPalLogger::errorLog(
546
+			esc_html__('Offboarding. Removing metadata.', 'event_espresso'),
547
+			EED_Module::getRequest()->postParams(),
548
+			$paypal_pm
549
+		);
550
+		wp_send_json(['success' => true]);
551
+	}
552
+
553
+
554
+	/**
555
+	 * Clear all credentials metadata.
556
+	 * (AJAX)
557
+	 *
558
+	 * @return void
559
+	 * @throws EE_Error
560
+	 * @throws ReflectionException
561
+	 */
562
+	public static function clearMetaData(): void
563
+	{
564
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
565
+		EED_PayPalOnboard::validatePmAjax($paypal_pm);
566
+		PayPalExtraMetaManager::deleteAllData($paypal_pm);
567
+		PayPalLogger::errorLog(
568
+			esc_html__('Doing a Reset. Removing all PM settings metadata.', 'event_espresso'),
569
+			EED_Module::getRequest()->postParams(),
570
+			$paypal_pm
571
+		);
572
+		wp_send_json(['success' => true]);
573
+	}
574
+
575
+
576
+	/**
577
+	 * Save the sandbox mode option.
578
+	 * (AJAX)
579
+	 *
580
+	 * @return void
581
+	 * @throws EE_Error
582
+	 * @throws ReflectionException
583
+	 */
584
+	public static function eeaPpSaveDebugMode(): void
585
+	{
586
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
587
+		EED_PayPalOnboard::validatePmAjax($paypal_pm);
588
+		// Reset the partner access token.
589
+		PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
590
+		// And do the data reset.
591
+		PayPalExtraMetaManager::deleteAllData($paypal_pm);
592
+		wp_send_json(['success' => true]);
593
+	}
594
+
595
+
596
+	/**
597
+	 * Validate the PM instance, returning an ajax response on invalid.
598
+	 *
599
+	 * @param $paypal_pm
600
+	 * @return void
601
+	 */
602
+	public static function validatePmAjax($paypal_pm): void
603
+	{
604
+		if (! $paypal_pm instanceof EE_Payment_Method) {
605
+			wp_send_json(
606
+				[
607
+					'error'   => 'INVALID_PM',
608
+					'message' => esc_html__(
609
+						'Invalid payment method. Please refresh the page and try again.',
610
+						'event_espresso'
611
+					),
612
+				]
613
+			);
614
+		}
615
+	}
616
+
617
+
618
+	/**
619
+	 * Checks if already onboard.
620
+	 *
621
+	 * @param EE_Payment_Method $payment_method
622
+	 * @return boolean
623
+	 * @throws EE_Error
624
+	 * @throws ReflectionException
625
+	 */
626
+	public static function isOnboard(EE_Payment_Method $payment_method): bool
627
+	{
628
+		$pp_meta_data = PayPalExtraMetaManager::getAllData($payment_method);
629
+		return
630
+			(
631
+				// onboarded with a third party integration ?
632
+				! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
633
+				&& ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
634
+			) || (
635
+				// or with the first party integration ?
636
+				! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
637
+				&& ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
638
+				&& ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
639
+			);
640
+	}
641
+
642
+
643
+	/**
644
+	 * Send a request and return a decoded response body.
645
+	 *
646
+	 * @param EE_Payment_Method $paypal_pm
647
+	 * @param string            $request_url
648
+	 * @param array             $request_args
649
+	 * @return array
650
+	 */
651
+	public static function sendRequest(EE_Payment_Method $paypal_pm, string $request_url, array $request_args): array
652
+	{
653
+		$error_return = ['error' => true];
654
+		$response     = wp_remote_request($request_url, $request_args);
655
+		if (is_wp_error($response)) {
656
+			$message = $response->get_error_message();
657
+			PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
658
+			$error_return['message'] = $message;
659
+			return $error_return;
660
+		}
661
+		$response_body = (isset($response['body']) && $response['body']) ? json_decode($response['body'], true) : [];
662
+		if (empty($response_body) || isset($response_body['error'])) {
663
+			$message = $response_body['error_description']
664
+				?? sprintf(
665
+					esc_html__('Unknown response received while sending a request to: %1$s', 'event_espresso'),
666
+					$request_url
667
+				);
668
+			PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
669
+			$error_return['message'] = $message;
670
+			return $error_return;
671
+		}
672
+		return $response_body;
673
+	}
674
+
675
+
676
+	/**
677
+	 * Check the response for a partner token request.
678
+	 *
679
+	 * @param array             $response
680
+	 * @param EE_Payment_Method $paypal_pm
681
+	 * @return bool
682
+	 */
683
+	public static function partnerTokenResponseValid(array $response, EE_Payment_Method $paypal_pm): bool
684
+	{
685
+		// Check the data we received.
686
+		if (
687
+			empty($response['nonce'])
688
+			|| ! wp_verify_nonce($response['nonce'], 'eea_pp_commerce_get_access_token')
689
+			|| empty($response['access_token'])
690
+			|| empty($response['app_id'])
691
+			|| empty($response['expires_in'])
692
+			|| empty($response['partner_client_id'])
693
+			|| empty($response['partner_merchant_id'])
694
+		) {
695
+			// This is an error.
696
+			PayPalLogger::errorLog(
697
+				esc_html__('Incoming parameter validation failed.', 'event_espresso'),
698
+				$response,
699
+				$paypal_pm
700
+			);
701
+			return false;
702
+		}
703
+		return true;
704
+	}
705
+
706
+
707
+	/**
708
+	 * Returns the base URL to the middleman server.
709
+	 * If LOCAL_MIDDLEMAN_SERVER is defined, requests will be sent to connect.eventespresso.test
710
+	 *
711
+	 * @param EE_Payment_Method $payment_method
712
+	 * @return string
713
+	 * @throws EE_Error
714
+	 * @throws ReflectionException
715
+	 */
716
+	public static function getMiddlemanBaseUrl(EE_Payment_Method $payment_method): string
717
+	{
718
+		$target = defined('LOCAL_MIDDLEMAN_SERVER') ? 'test' : 'com';
719
+		// If this PM is used under different provider accounts, you might need an account indicator.
720
+		$account = defined('EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR') ? EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR : '';
721
+		$postfix = $payment_method->debug_mode() ? '_sandbox' : '';
722
+		return "https://connect.eventespresso.$target/paypal_commerce$account$postfix/";
723
+	}
724
+
725
+
726
+	/**
727
+	 * This Payment Method admin notices.
728
+	 *
729
+	 * @return void
730
+	 * @throws EE_Error
731
+	 * @throws ReflectionException
732
+	 */
733
+	public static function adminNotice()
734
+	{
735
+		// Show the notice if PayPal Commerce PM is active but merchant is not onboard.
736
+		$pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug(Domain::PM_SLUG);
737
+		if (
738
+			$pp_commerce instanceof EE_Payment_Method
739
+			&& $pp_commerce->active()
740
+			&& ! EED_PayPalOnboard::isOnboard($pp_commerce)
741
+		) {
742
+			add_action('admin_notices', [EED_PayPalOnboard::class, 'notOnboardNotice']);
743
+		}
744
+	}
745
+
746
+
747
+	/**
748
+	 * Contents of the not onboard admin notice.
749
+	 *
750
+	 * @return void
751
+	 * @throws EE_Error
752
+	 * @throws ReflectionException
753
+	 */
754
+	public static function notOnboardNotice()
755
+	{
756
+		$open_anchor = $close_anchor = '';
757
+		$pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug(Domain::PM_SLUG);
758
+		if ($pp_commerce instanceof EE_Payment_Method) {
759
+			$pm_page      = add_query_arg(
760
+				[
761
+					'page'           => 'espresso_payment_settings',
762
+					'webhook_action' => 'eepPpcMerchantOnboard',
763
+					'payment_method' => $pp_commerce->slug(),
764
+				],
765
+				admin_url('admin.php')
766
+			);
767
+			$open_anchor  = "<a href='$pm_page'>";
768
+			$close_anchor = "</a>";
769
+		}
770
+		$notice = sprintf(
771
+			esc_html__(
772
+				'%1$sPayPal Commerce%2$s payment method was activated but is not connected to PayPal. Please %3$sfinish setting up%4$s this payment method.',
773
+				'event_espresso'
774
+			),
775
+			'<strong>',
776
+			'</strong>',
777
+			$open_anchor,
778
+			$close_anchor
779
+		);
780
+		echo "
781 781
         <div class='error'>
782 782
             <p>$notice</p>
783 783
         </div>";
784
-    }
784
+	}
785 785
 }
Please login to merge, or discard this patch.
Spacing   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
         $signup_link = '';
86 86
         try {
87 87
             $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
88
-            if (! $paypal_pm instanceof EE_Payment_Method) {
88
+            if ( ! $paypal_pm instanceof EE_Payment_Method) {
89 89
                 PayPalLogger::errorLogAndExit(
90 90
                     esc_html__('No payment method.', 'event_espresso'),
91 91
                     EED_Module::getRequest()->postParams(),
@@ -95,7 +95,7 @@  discard block
 block discarded – undo
95 95
             PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
96 96
             // Just get a new onboarding URL every time.
97 97
             $signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
98
-            if (! $signup_link) {
98
+            if ( ! $signup_link) {
99 99
                 $err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
100 100
                 PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
101 101
             }
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
             PayPalLogger::errorLogAndExit($e->getMessage(), ['trace' => $e->getTrace()]);
104 104
         }
105 105
         // Is it empty (can happen if we didn't get the URL through the API).
106
-        $signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
106
+        $signup_link = $signup_link ? $signup_link.'?&displayMode=minibrowser' : '#';
107 107
         wp_send_json(
108 108
             [
109 109
                 'signup_link' => $signup_link,
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
         $signup_link = '';
127 127
         // Get the access token.
128 128
         $access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
129
-        if (! $access_token) {
129
+        if ( ! $access_token) {
130 130
             $err_msg = esc_html__('Error! No access token.', 'event_espresso');
131 131
             PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
132 132
             return '';
@@ -139,12 +139,12 @@  discard block
 block discarded – undo
139 139
             'headers' => [
140 140
                 'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
141 141
                 'Content-Type'                  => 'application/json',
142
-                'Authorization'                 => 'Bearer ' . $access_token,
142
+                'Authorization'                 => 'Bearer '.$access_token,
143 143
                 'PayPal-Partner-Attribution-Id' => $bn_code,
144 144
             ],
145 145
             'body'    => $body_params,
146 146
         ];
147
-        $request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
147
+        $request_url = Domain::getPayPalApiUrl($paypal_pm).'/v2/customer/partner-referrals';
148 148
         $response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
149 149
         // Check the data we received.
150 150
         if (isset($response['error']) || empty($response['links'])) {
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
             'payment_method' => $get_params['payment_method'],
272 272
         ];
273 273
         if (isset($get_params['sandbox_mode'])) {
274
-            $args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
274
+            $args_to_add[Domain::META_KEY_SANDBOX_MODE] = $get_params['sandbox_mode'];
275 275
         }
276 276
         $home_url = add_query_arg($args_to_add, admin_url('admin.php'));
277 277
         wp_redirect($home_url);
@@ -294,12 +294,12 @@  discard block
 block discarded – undo
294 294
             ! isset($_GET['webhook_action'], $_GET['nonce'])
295 295
             || $_GET['webhook_action'] !== 'eepPpcMerchantOnboard'
296 296
         ) {
297
-            return;  // Ignore.
297
+            return; // Ignore.
298 298
         }
299 299
         $get_params = EED_Module::getRequest()->getParams();
300 300
         // Get the payment method.
301 301
         $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
302
-        if (! $paypal_pm instanceof EE_Payment_Method) {
302
+        if ( ! $paypal_pm instanceof EE_Payment_Method) {
303 303
             PayPalLogger::errorLog(
304 304
                 esc_html__('Not able to validate the payment method.', 'event_espresso'),
305 305
                 $get_params,
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
             return;
310 310
         }
311 311
         // Check the response (GET) parameters.
312
-        if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
312
+        if ( ! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
313 313
             // Missing parameters. Can't proceed.
314 314
             PayPalLogger::errorLog(
315 315
                 esc_html__('Missing required onboarding parameters.', 'event_espresso'),
@@ -322,9 +322,9 @@  discard block
 block discarded – undo
322 322
         // Check on the onboarding status (recommended by PP).
323 323
         $onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
324 324
             $paypal_pm,
325
-            $get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
325
+            $get_params[Domain::META_KEY_SELLER_MERCHANT_ID]
326 326
         );
327
-        if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
327
+        if ( ! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
328 328
             PayPalLogger::errorLog(
329 329
                 $onboarding_status['message'] ?? esc_html__('Failed to track seller onboarding.', 'event_espresso'),
330 330
                 array_merge($get_params, $onboarding_status),
@@ -355,9 +355,9 @@  discard block
 block discarded – undo
355 355
         if (
356 356
             $paypal_pm instanceof EE_Payment_Method
357 357
             && wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
358
-            && ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
359
-            && ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
360
-            && isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
358
+            && ! empty($data[Domain::API_PARAM_PARTNER_ID])
359
+            && ! empty($data[Domain::META_KEY_SELLER_MERCHANT_ID])
360
+            && isset($data[Domain::API_PARAM_EMAIL_CONFIRMED])
361 361
         ) {
362 362
             return true;
363 363
         }
@@ -378,7 +378,7 @@  discard block
 block discarded – undo
378 378
         // See if it's already saved.
379 379
         $access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
380 380
         // If we don't have it, request/update it.
381
-        if (! $access_token) {
381
+        if ( ! $access_token) {
382 382
             return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
383 383
         }
384 384
         if (EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm)) {
@@ -400,7 +400,7 @@  discard block
 block discarded – undo
400 400
     public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
401 401
     {
402 402
         $expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_TOKEN_EXPIRES_IN);
403
-        if (! $expires_at) {
403
+        if ( ! $expires_at) {
404 404
             return true;
405 405
         }
406 406
         // Validate the token expiration date.
@@ -436,18 +436,18 @@  discard block
 block discarded – undo
436 436
         if (defined('LOCAL_MIDDLEMAN_SERVER')) {
437 437
             $post_args['sslverify'] = Manager::verifySSL();
438 438
         }
439
-        $post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
439
+        $post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm).'get_token';
440 440
         $response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
441 441
         if (isset($response['error'])) {
442 442
             return '';
443 443
         }
444 444
         // Check the data we received.
445
-        if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
445
+        if ( ! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
446 446
             return '';
447 447
         }
448 448
         // If we are here all seems to be ok. Save the token and it's data.
449 449
         $saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
450
-        if (! $saved) {
450
+        if ( ! $saved) {
451 451
             return '';
452 452
         }
453 453
         return $response['access_token'];
@@ -466,7 +466,7 @@  discard block
 block discarded – undo
466 466
     public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
467 467
     {
468 468
         $track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
469
-        if (! $track_onboarding instanceof TrackSellerOnboarding) {
469
+        if ( ! $track_onboarding instanceof TrackSellerOnboarding) {
470 470
             return [
471 471
                 'error'   => 'TRACK_ONBOARDING_FAILED',
472 472
                 'message' => esc_html__('Failed to track seller onboarding.', 'event_espresso')
@@ -491,7 +491,7 @@  discard block
 block discarded – undo
491 491
     ): ?TrackSellerOnboarding {
492 492
         $partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
493 493
         $paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
494
-        if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
494
+        if ( ! $paypal_api instanceof PayPalApi || ! $partner_id) {
495 495
             return null;
496 496
         }
497 497
         return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
@@ -509,7 +509,7 @@  discard block
 block discarded – undo
509 509
     public static function getOnboardStatus(): void
510 510
     {
511 511
         $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
512
-        if (! $paypal_pm instanceof EE_Payment_Method) {
512
+        if ( ! $paypal_pm instanceof EE_Payment_Method) {
513 513
             $err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
514 514
             PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
515 515
             wp_send_json(['on_board' => false]);
@@ -601,7 +601,7 @@  discard block
 block discarded – undo
601 601
      */
602 602
     public static function validatePmAjax($paypal_pm): void
603 603
     {
604
-        if (! $paypal_pm instanceof EE_Payment_Method) {
604
+        if ( ! $paypal_pm instanceof EE_Payment_Method) {
605 605
             wp_send_json(
606 606
                 [
607 607
                     'error'   => 'INVALID_PM',
@@ -629,13 +629,13 @@  discard block
 block discarded – undo
629 629
         return
630 630
             (
631 631
                 // onboarded with a third party integration ?
632
-                ! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
633
-                && ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
632
+                ! empty($pp_meta_data[Domain::META_KEY_SELLER_MERCHANT_ID])
633
+                && ! empty($pp_meta_data[Domain::META_KEY_ACCESS_TOKEN])
634 634
             ) || (
635 635
                 // or with the first party integration ?
636
-                ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
637
-                && ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
638
-                && ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
636
+                ! empty($pp_meta_data[Domain::META_KEY_CLIENT_ID])
637
+                && ! empty($pp_meta_data[Domain::META_KEY_CLIENT_SECRET])
638
+                && ! empty($pp_meta_data[Domain::META_KEY_PAYER_ID])
639 639
             );
640 640
     }
641 641
 
@@ -756,7 +756,7 @@  discard block
 block discarded – undo
756 756
         $open_anchor = $close_anchor = '';
757 757
         $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug(Domain::PM_SLUG);
758 758
         if ($pp_commerce instanceof EE_Payment_Method) {
759
-            $pm_page      = add_query_arg(
759
+            $pm_page = add_query_arg(
760 760
                 [
761 761
                     'page'           => 'espresso_payment_settings',
762 762
                     'webhook_action' => 'eepPpcMerchantOnboard',
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +107 added lines, -107 removed lines patch added patch discarded remove patch
@@ -37,138 +37,138 @@
 block discarded – undo
37 37
  * @since       4.0
38 38
  */
39 39
 if (function_exists('espresso_version')) {
40
-    if (! function_exists('espresso_duplicate_plugin_error')) {
41
-        /**
42
-         *    espresso_duplicate_plugin_error
43
-         *    displays if more than one version of EE is activated at the same time.
44
-         */
45
-        function espresso_duplicate_plugin_error()
46
-        {
47
-            ?>
40
+	if (! function_exists('espresso_duplicate_plugin_error')) {
41
+		/**
42
+		 *    espresso_duplicate_plugin_error
43
+		 *    displays if more than one version of EE is activated at the same time.
44
+		 */
45
+		function espresso_duplicate_plugin_error()
46
+		{
47
+			?>
48 48
 <div class="error">
49 49
     <p>
50 50
         <?php
51
-                    echo esc_html__(
52
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
53
-                        'event_espresso'
54
-                    ); ?>
51
+					echo esc_html__(
52
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
53
+						'event_espresso'
54
+					); ?>
55 55
     </p>
56 56
 </div>
57 57
 <?php
58
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
59
-        }
60
-    }
61
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
58
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
59
+		}
60
+	}
61
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
62 62
 } else {
63
-    define('EE_MIN_PHP_VER_REQUIRED', '7.4.0');
64
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
65
-        /**
66
-         * espresso_minimum_php_version_error
67
-         *
68
-         * @return void
69
-         */
70
-        function espresso_minimum_php_version_error()
71
-        {
72
-            ?>
63
+	define('EE_MIN_PHP_VER_REQUIRED', '7.4.0');
64
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
65
+		/**
66
+		 * espresso_minimum_php_version_error
67
+		 *
68
+		 * @return void
69
+		 */
70
+		function espresso_minimum_php_version_error()
71
+		{
72
+			?>
73 73
 <div class="error">
74 74
     <p>
75 75
         <?php
76
-                    printf(
77
-                        esc_html__(
78
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
79
-                            'event_espresso'
80
-                        ),
81
-                        EE_MIN_PHP_VER_REQUIRED,
82
-                        PHP_VERSION,
83
-                        '<br/>',
84
-                        '<a href="https://www.php.net/downloads.php">https://php.net/downloads.php</a>'
85
-                    );
86
-        ?>
76
+					printf(
77
+						esc_html__(
78
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
79
+							'event_espresso'
80
+						),
81
+						EE_MIN_PHP_VER_REQUIRED,
82
+						PHP_VERSION,
83
+						'<br/>',
84
+						'<a href="https://www.php.net/downloads.php">https://php.net/downloads.php</a>'
85
+					);
86
+		?>
87 87
     </p>
88 88
 </div>
89 89
 <?php
90
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
91
-        }
90
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
91
+		}
92 92
 
93
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
94
-    } else {
95
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
93
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
94
+	} else {
95
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
96 96
 
97
-        require_once __DIR__ . '/vendor/autoload.php';
97
+		require_once __DIR__ . '/vendor/autoload.php';
98 98
 
99
-        /**
100
-         * espresso_version
101
-         * Returns the plugin version
102
-         *
103
-         * @return string
104
-         */
105
-        function espresso_version(): string
106
-        {
107
-            return apply_filters('FHEE__espresso__espresso_version', '5.0.42');
108
-        }
99
+		/**
100
+		 * espresso_version
101
+		 * Returns the plugin version
102
+		 *
103
+		 * @return string
104
+		 */
105
+		function espresso_version(): string
106
+		{
107
+			return apply_filters('FHEE__espresso__espresso_version', '5.0.42');
108
+		}
109 109
 
110
-        /**
111
-         * espresso_plugin_activation
112
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
113
-         */
114
-        function espresso_plugin_activation()
115
-        {
116
-            update_option('ee_espresso_activation', true);
117
-            update_option('event-espresso-core_allow_tracking', 'no');
118
-            update_option('event-espresso-core_tracking_notice', 'hide');
119
-            // Run WP GraphQL activation callback
120
-            espressoLoadWpGraphQL();
121
-            graphql_activation_callback();
122
-        }
110
+		/**
111
+		 * espresso_plugin_activation
112
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
113
+		 */
114
+		function espresso_plugin_activation()
115
+		{
116
+			update_option('ee_espresso_activation', true);
117
+			update_option('event-espresso-core_allow_tracking', 'no');
118
+			update_option('event-espresso-core_tracking_notice', 'hide');
119
+			// Run WP GraphQL activation callback
120
+			espressoLoadWpGraphQL();
121
+			graphql_activation_callback();
122
+		}
123 123
 
124
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
124
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
125 125
 
126
-        /**
127
-         * espresso_plugin_deactivation
128
-         */
129
-        function espresso_plugin_deactivation()
130
-        {
131
-            // Run WP GraphQL deactivation callback
132
-            espressoLoadWpGraphQL();
133
-            graphql_deactivation_callback();
134
-            delete_option('event-espresso-core_allow_tracking');
135
-            delete_option('event-espresso-core_tracking_notice');
136
-        }
137
-        register_deactivation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_deactivation');
126
+		/**
127
+		 * espresso_plugin_deactivation
128
+		 */
129
+		function espresso_plugin_deactivation()
130
+		{
131
+			// Run WP GraphQL deactivation callback
132
+			espressoLoadWpGraphQL();
133
+			graphql_deactivation_callback();
134
+			delete_option('event-espresso-core_allow_tracking');
135
+			delete_option('event-espresso-core_tracking_notice');
136
+		}
137
+		register_deactivation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_deactivation');
138 138
 
139
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
140
-        bootstrap_espresso();
141
-    }
139
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
140
+		bootstrap_espresso();
141
+	}
142 142
 }
143 143
 
144 144
 if (! function_exists('espresso_deactivate_plugin')) {
145
-    /**
146
-     *    deactivate_plugin
147
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
148
-     *
149
-     * @access public
150
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
151
-     * @return    void
152
-     */
153
-    function espresso_deactivate_plugin(string $plugin_basename = '')
154
-    {
155
-        if (! function_exists('deactivate_plugins')) {
156
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
157
-        }
158
-        unset($_GET['activate'], $_REQUEST['activate']);
159
-        deactivate_plugins($plugin_basename);
160
-    }
145
+	/**
146
+	 *    deactivate_plugin
147
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
148
+	 *
149
+	 * @access public
150
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
151
+	 * @return    void
152
+	 */
153
+	function espresso_deactivate_plugin(string $plugin_basename = '')
154
+	{
155
+		if (! function_exists('deactivate_plugins')) {
156
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
157
+		}
158
+		unset($_GET['activate'], $_REQUEST['activate']);
159
+		deactivate_plugins($plugin_basename);
160
+	}
161 161
 }
162 162
 
163 163
 
164 164
 if (! function_exists('espressoLoadWpGraphQL')) {
165
-    function espressoLoadWpGraphQL()
166
-    {
167
-        if (
168
-            ! class_exists('WPGraphQL')
169
-            && is_readable(__DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php')
170
-        ) {
171
-            require_once __DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php';
172
-        }
173
-    }
165
+	function espressoLoadWpGraphQL()
166
+	{
167
+		if (
168
+			! class_exists('WPGraphQL')
169
+			&& is_readable(__DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php')
170
+		) {
171
+			require_once __DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php';
172
+		}
173
+	}
174 174
 }
Please login to merge, or discard this patch.