Completed
Branch dev (117626)
by
unknown
15:07 queued 12:11
created
core/domain/services/registration/form/v1/StateOptions.php 2 patches
Indentation   +85 added lines, -85 removed lines patch added patch discarded remove patch
@@ -12,97 +12,97 @@
 block discarded – undo
12 12
 
13 13
 class StateOptions
14 14
 {
15
-    /**
16
-     * the action being performed on the current step
17
-     *
18
-     * @var string
19
-     */
20
-    public $action = '';
15
+	/**
16
+	 * the action being performed on the current step
17
+	 *
18
+	 * @var string
19
+	 */
20
+	public $action = '';
21 21
 
22
-    /**
23
-     * @var EEM_State
24
-     */
25
-    public $state_model;
22
+	/**
23
+	 * @var EEM_State
24
+	 */
25
+	public $state_model;
26 26
 
27
-    /**
28
-     * @var [][]
29
-     */
30
-    private $state_options = [];
27
+	/**
28
+	 * @var [][]
29
+	 */
30
+	private $state_options = [];
31 31
 
32 32
 
33
-    /**
34
-     * CountryOptions constructor.
35
-     *
36
-     * @param string    $action
37
-     * @param EEM_State $state_model
38
-     */
39
-    public function __construct(string $action, EEM_State $state_model)
40
-    {
41
-        $this->action      = $action;
42
-        $this->state_model = $state_model;
43
-        add_filter(
44
-            'FHEE__EE_Question__generate_form_input__state_options',
45
-            [$this, 'forLegacyFormInput'],
46
-            10,
47
-            4
48
-        );
49
-    }
33
+	/**
34
+	 * CountryOptions constructor.
35
+	 *
36
+	 * @param string    $action
37
+	 * @param EEM_State $state_model
38
+	 */
39
+	public function __construct(string $action, EEM_State $state_model)
40
+	{
41
+		$this->action      = $action;
42
+		$this->state_model = $state_model;
43
+		add_filter(
44
+			'FHEE__EE_Question__generate_form_input__state_options',
45
+			[$this, 'forLegacyFormInput'],
46
+			10,
47
+			4
48
+		);
49
+	}
50 50
 
51 51
 
52
-    /**
53
-     * Gets the list of states for the form input
54
-     *
55
-     * @param array|null           $states_list deprecated prop from an old hook
56
-     * @param EE_Question|null     $question
57
-     * @param EE_Registration|null $registration
58
-     * @param EE_Answer|null       $answer
59
-     * @return array 2d keys are state IDs, values are their names
60
-     * @throws EE_Error
61
-     * @throws ReflectionException
62
-     */
63
-    public function forLegacyFormInput(
64
-        array $states_list = null,
65
-        EE_Question $question = null,
66
-        EE_Registration $registration = null,
67
-        EE_Answer $answer = null
68
-    ): array {
69
-        if (! isset($this->state_options[ $this->action ])) {
70
-            $this->generateLegacyStateOptions($question, $registration, $answer);
71
-        }
72
-        return $this->state_options[ $this->action ];
73
-    }
52
+	/**
53
+	 * Gets the list of states for the form input
54
+	 *
55
+	 * @param array|null           $states_list deprecated prop from an old hook
56
+	 * @param EE_Question|null     $question
57
+	 * @param EE_Registration|null $registration
58
+	 * @param EE_Answer|null       $answer
59
+	 * @return array 2d keys are state IDs, values are their names
60
+	 * @throws EE_Error
61
+	 * @throws ReflectionException
62
+	 */
63
+	public function forLegacyFormInput(
64
+		array $states_list = null,
65
+		EE_Question $question = null,
66
+		EE_Registration $registration = null,
67
+		EE_Answer $answer = null
68
+	): array {
69
+		if (! isset($this->state_options[ $this->action ])) {
70
+			$this->generateLegacyStateOptions($question, $registration, $answer);
71
+		}
72
+		return $this->state_options[ $this->action ];
73
+	}
74 74
 
75 75
 
76
-    /**
77
-     * @param EE_Question|null     $question
78
-     * @param EE_Registration|null $registration
79
-     * @param EE_Answer|null       $answer
80
-     * @throws EE_Error
81
-     * @throws ReflectionException
82
-     */
83
-    private function generateLegacyStateOptions(
84
-        EE_Question $question = null,
85
-        EE_Registration $registration = null,
86
-        EE_Answer $answer = null
87
-    ) {
88
-        $state_options = ['' => ['' => '']];
89
-        $states        = $this->action === 'process_reg_step'
90
-            ? $this->state_model->get_all_states()
91
-            : $this->state_model->get_all_active_states();
92
-        if (! empty($states)) {
93
-            foreach ($states as $state) {
94
-                if ($state instanceof EE_State) {
95
-                    $state_options[ $state->country()->name() ][ $state->ID() ] = $state->name();
96
-                }
97
-            }
98
-        }
99
-        $this->state_options[ $this->action ] = apply_filters(
100
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_StateOptions__generateLegacyStateOptions__state_options',
101
-            $state_options,
102
-            $registration,
103
-            $question,
104
-            $answer,
105
-            $this
106
-        );
107
-    }
76
+	/**
77
+	 * @param EE_Question|null     $question
78
+	 * @param EE_Registration|null $registration
79
+	 * @param EE_Answer|null       $answer
80
+	 * @throws EE_Error
81
+	 * @throws ReflectionException
82
+	 */
83
+	private function generateLegacyStateOptions(
84
+		EE_Question $question = null,
85
+		EE_Registration $registration = null,
86
+		EE_Answer $answer = null
87
+	) {
88
+		$state_options = ['' => ['' => '']];
89
+		$states        = $this->action === 'process_reg_step'
90
+			? $this->state_model->get_all_states()
91
+			: $this->state_model->get_all_active_states();
92
+		if (! empty($states)) {
93
+			foreach ($states as $state) {
94
+				if ($state instanceof EE_State) {
95
+					$state_options[ $state->country()->name() ][ $state->ID() ] = $state->name();
96
+				}
97
+			}
98
+		}
99
+		$this->state_options[ $this->action ] = apply_filters(
100
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_StateOptions__generateLegacyStateOptions__state_options',
101
+			$state_options,
102
+			$registration,
103
+			$question,
104
+			$answer,
105
+			$this
106
+		);
107
+	}
108 108
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -66,10 +66,10 @@  discard block
 block discarded – undo
66 66
         EE_Registration $registration = null,
67 67
         EE_Answer $answer = null
68 68
     ): array {
69
-        if (! isset($this->state_options[ $this->action ])) {
69
+        if ( ! isset($this->state_options[$this->action])) {
70 70
             $this->generateLegacyStateOptions($question, $registration, $answer);
71 71
         }
72
-        return $this->state_options[ $this->action ];
72
+        return $this->state_options[$this->action];
73 73
     }
74 74
 
75 75
 
@@ -89,14 +89,14 @@  discard block
 block discarded – undo
89 89
         $states        = $this->action === 'process_reg_step'
90 90
             ? $this->state_model->get_all_states()
91 91
             : $this->state_model->get_all_active_states();
92
-        if (! empty($states)) {
92
+        if ( ! empty($states)) {
93 93
             foreach ($states as $state) {
94 94
                 if ($state instanceof EE_State) {
95
-                    $state_options[ $state->country()->name() ][ $state->ID() ] = $state->name();
95
+                    $state_options[$state->country()->name()][$state->ID()] = $state->name();
96 96
                 }
97 97
             }
98 98
         }
99
-        $this->state_options[ $this->action ] = apply_filters(
99
+        $this->state_options[$this->action] = apply_filters(
100 100
             'FHEE__EventEspresso_core_domain_services_registration_form_v1_StateOptions__generateLegacyStateOptions__state_options',
101 101
             $state_options,
102 102
             $registration,
Please login to merge, or discard this patch.
core/domain/services/registration/form/v1/RegForm.php 1 patch
Indentation   +220 added lines, -220 removed lines patch added patch discarded remove patch
@@ -32,251 +32,251 @@
 block discarded – undo
32 32
 class RegForm extends EE_Form_Section_Proper
33 33
 {
34 34
 
35
-    /**
36
-     * @var bool
37
-     */
38
-    private $print_copy_info = false;
35
+	/**
36
+	 * @var bool
37
+	 */
38
+	private $print_copy_info = false;
39 39
 
40
-    /**
41
-     * @var EE_Registration_Config
42
-     */
43
-    public $reg_config;
40
+	/**
41
+	 * @var EE_Registration_Config
42
+	 */
43
+	public $reg_config;
44 44
 
45
-    /**
46
-     * @var int
47
-     */
48
-    protected $reg_form_count = 0;
45
+	/**
46
+	 * @var int
47
+	 */
48
+	protected $reg_form_count = 0;
49 49
 
50
-    /**
51
-     * @var EE_SPCO_Reg_Step_Attendee_Information
52
-     */
53
-    public $reg_step;
50
+	/**
51
+	 * @var EE_SPCO_Reg_Step_Attendee_Information
52
+	 */
53
+	public $reg_step;
54 54
 
55
-    /**
56
-     * @var array
57
-     */
58
-    private $required_questions = [];
55
+	/**
56
+	 * @var array
57
+	 */
58
+	private $required_questions = [];
59 59
 
60
-    /**
61
-     * @var array
62
-     */
63
-    private $template_args = [];
60
+	/**
61
+	 * @var array
62
+	 */
63
+	private $template_args = [];
64 64
 
65 65
 
66
-    /**
67
-     * RegForm constructor.
68
-     *
69
-     * @param EE_SPCO_Reg_Step_Attendee_Information $reg_step
70
-     * @param EE_Registration_Config                $reg_config
71
-     * @throws ReflectionException
72
-     * @throws EE_Error
73
-     */
74
-    public function __construct(
75
-        EE_SPCO_Reg_Step_Attendee_Information $reg_step,
76
-        EE_Registration_Config $reg_config
77
-    ) {
78
-        $this->reg_step   = $reg_step;
79
-        $this->reg_config = $reg_config;
80
-        // setup some classes so that they are ready for loading during construction of other classes
81
-        LoaderFactory::getShared(CountryOptions::class, [$this->reg_step->checkout->action]);
82
-        LoaderFactory::getShared(StateOptions::class, [$this->reg_step->checkout->action]);
83
-        LoaderFactory::getShared(RegFormQuestionFactory::class, [[$this, 'addRequiredQuestion']]);
84
-        parent::__construct(
85
-            [
86
-                'name'            => $this->reg_step->reg_form_name(),
87
-                'html_id'         => $this->reg_step->reg_form_name(),
88
-                'subsections'     => $this->generateSubsections(),
89
-                'layout_strategy' => new EE_Template_Layout(
90
-                    [
91
-                        'layout_template_file' => $this->reg_step->template(), // layout_template
92
-                        'template_args'        => $this->template_args,
93
-                    ]
94
-                ),
95
-            ]
96
-        );
97
-    }
66
+	/**
67
+	 * RegForm constructor.
68
+	 *
69
+	 * @param EE_SPCO_Reg_Step_Attendee_Information $reg_step
70
+	 * @param EE_Registration_Config                $reg_config
71
+	 * @throws ReflectionException
72
+	 * @throws EE_Error
73
+	 */
74
+	public function __construct(
75
+		EE_SPCO_Reg_Step_Attendee_Information $reg_step,
76
+		EE_Registration_Config $reg_config
77
+	) {
78
+		$this->reg_step   = $reg_step;
79
+		$this->reg_config = $reg_config;
80
+		// setup some classes so that they are ready for loading during construction of other classes
81
+		LoaderFactory::getShared(CountryOptions::class, [$this->reg_step->checkout->action]);
82
+		LoaderFactory::getShared(StateOptions::class, [$this->reg_step->checkout->action]);
83
+		LoaderFactory::getShared(RegFormQuestionFactory::class, [[$this, 'addRequiredQuestion']]);
84
+		parent::__construct(
85
+			[
86
+				'name'            => $this->reg_step->reg_form_name(),
87
+				'html_id'         => $this->reg_step->reg_form_name(),
88
+				'subsections'     => $this->generateSubsections(),
89
+				'layout_strategy' => new EE_Template_Layout(
90
+					[
91
+						'layout_template_file' => $this->reg_step->template(), // layout_template
92
+						'template_args'        => $this->template_args,
93
+					]
94
+				),
95
+			]
96
+		);
97
+	}
98 98
 
99 99
 
100
-    /**
101
-     * @return void
102
-     */
103
-    public function enablePrintCopyInfo(): void
104
-    {
105
-        $this->print_copy_info = true;
106
-    }
100
+	/**
101
+	 * @return void
102
+	 */
103
+	public function enablePrintCopyInfo(): void
104
+	{
105
+		$this->print_copy_info = true;
106
+	}
107 107
 
108 108
 
109
-    /**
110
-     * @return bool
111
-     */
112
-    public function printCopyInfo(): bool
113
-    {
114
-        return $this->print_copy_info;
115
-    }
109
+	/**
110
+	 * @return bool
111
+	 */
112
+	public function printCopyInfo(): bool
113
+	{
114
+		return $this->print_copy_info;
115
+	}
116 116
 
117 117
 
118
-    /**
119
-     * @return int
120
-     */
121
-    public function regFormCount(): int
122
-    {
123
-        return $this->reg_form_count;
124
-    }
118
+	/**
119
+	 * @return int
120
+	 */
121
+	public function regFormCount(): int
122
+	{
123
+		return $this->reg_form_count;
124
+	}
125 125
 
126 126
 
127
-    /**
128
-     * @return array
129
-     */
130
-    public function requiredQuestions(): array
131
-    {
132
-        return $this->required_questions;
133
-    }
127
+	/**
128
+	 * @return array
129
+	 */
130
+	public function requiredQuestions(): array
131
+	{
132
+		return $this->required_questions;
133
+	}
134 134
 
135 135
 
136
-    /**
137
-     * @param string $identifier
138
-     * @param string $required_question
139
-     */
140
-    public function addRequiredQuestion(string $identifier, string $required_question): void
141
-    {
142
-        $this->required_questions[ $identifier ] = $required_question;
143
-    }
136
+	/**
137
+	 * @param string $identifier
138
+	 * @param string $required_question
139
+	 */
140
+	public function addRequiredQuestion(string $identifier, string $required_question): void
141
+	{
142
+		$this->required_questions[ $identifier ] = $required_question;
143
+	}
144 144
 
145 145
 
146
-    /**
147
-     * @return EE_Form_Section_Proper[]
148
-     * @throws DomainException
149
-     * @throws EE_Error
150
-     * @throws InvalidArgumentException
151
-     * @throws ReflectionException
152
-     * @throws EntityNotFoundException
153
-     * @throws InvalidDataTypeException
154
-     * @throws InvalidInterfaceException
155
-     */
156
-    private function generateSubsections(): array
157
-    {
158
-        // Init reg forms count.
159
-        $this->reg_form_count = 0;
146
+	/**
147
+	 * @return EE_Form_Section_Proper[]
148
+	 * @throws DomainException
149
+	 * @throws EE_Error
150
+	 * @throws InvalidArgumentException
151
+	 * @throws ReflectionException
152
+	 * @throws EntityNotFoundException
153
+	 * @throws InvalidDataTypeException
154
+	 * @throws InvalidInterfaceException
155
+	 */
156
+	private function generateSubsections(): array
157
+	{
158
+		// Init reg forms count.
159
+		$this->reg_form_count = 0;
160 160
 
161
-        $primary_registrant = null;
162
-        // autoload Line_Item_Display classes
163
-        EEH_Autoloader::register_line_item_display_autoloaders();
164
-        $Line_Item_Display = new EE_Line_Item_Display();
165
-        // calculate taxes
166
-        $Line_Item_Display->display_line_item(
167
-            $this->reg_step->checkout->cart->get_grand_total(),
168
-            ['set_tax_rate' => true]
169
-        );
170
-        $extra_inputs_section = $this->reg_step->reg_step_hidden_inputs();
171
-        $this->addPrivacyConsentCheckbox($extra_inputs_section);
172
-        $subsections = [
173
-            'default_hidden_inputs' => $extra_inputs_section,
174
-        ];
161
+		$primary_registrant = null;
162
+		// autoload Line_Item_Display classes
163
+		EEH_Autoloader::register_line_item_display_autoloaders();
164
+		$Line_Item_Display = new EE_Line_Item_Display();
165
+		// calculate taxes
166
+		$Line_Item_Display->display_line_item(
167
+			$this->reg_step->checkout->cart->get_grand_total(),
168
+			['set_tax_rate' => true]
169
+		);
170
+		$extra_inputs_section = $this->reg_step->reg_step_hidden_inputs();
171
+		$this->addPrivacyConsentCheckbox($extra_inputs_section);
172
+		$subsections = [
173
+			'default_hidden_inputs' => $extra_inputs_section,
174
+		];
175 175
 
176
-        $this->template_args = [
177
-            'revisit'       => $this->reg_step->checkout->revisit,
178
-            'registrations' => [],
179
-            'ticket_count'  => [],
180
-        ];
181
-        // grab the saved registrations from the transaction
182
-        $registrations = $this->reg_step->checkout->transaction->registrations(
183
-            $this->reg_step->checkout->reg_cache_where_params
184
-        );
185
-        if ($registrations) {
186
-            foreach ($registrations as $registration) {
187
-                // can this registration be processed during this visit ?
188
-                if (
189
-                    $registration instanceof EE_Registration
190
-                    && $this->reg_step->checkout->visit_allows_processing_of_this_registration($registration)
191
-                ) {
192
-                    $reg_url_link = $registration->reg_url_link();
193
-                    /** @var RegistrantForm $registrant_form */
194
-                    $registrant_form = LoaderFactory::getNew(
195
-                        RegistrantForm::class,
196
-                        [
197
-                            $registration,
198
-                            $this->reg_config->copyAttendeeInfo(),
199
-                            [$this, 'enablePrintCopyInfo'],
200
-                            $this->reg_step,
201
-                        ]
202
-                    );
203
-                    // Increment the reg forms number if form is valid.
204
-                    if ($registrant_form->hasQuestions()) {
205
-                        $this->reg_form_count++;
206
-                        $subsections[ $reg_url_link ] = $registrant_form;
207
-                    } else {
208
-                        // or just add a blank section if there are no questions
209
-                        $subsections[ $reg_url_link ] = new EE_Form_Section_HTML();
210
-                    }
176
+		$this->template_args = [
177
+			'revisit'       => $this->reg_step->checkout->revisit,
178
+			'registrations' => [],
179
+			'ticket_count'  => [],
180
+		];
181
+		// grab the saved registrations from the transaction
182
+		$registrations = $this->reg_step->checkout->transaction->registrations(
183
+			$this->reg_step->checkout->reg_cache_where_params
184
+		);
185
+		if ($registrations) {
186
+			foreach ($registrations as $registration) {
187
+				// can this registration be processed during this visit ?
188
+				if (
189
+					$registration instanceof EE_Registration
190
+					&& $this->reg_step->checkout->visit_allows_processing_of_this_registration($registration)
191
+				) {
192
+					$reg_url_link = $registration->reg_url_link();
193
+					/** @var RegistrantForm $registrant_form */
194
+					$registrant_form = LoaderFactory::getNew(
195
+						RegistrantForm::class,
196
+						[
197
+							$registration,
198
+							$this->reg_config->copyAttendeeInfo(),
199
+							[$this, 'enablePrintCopyInfo'],
200
+							$this->reg_step,
201
+						]
202
+					);
203
+					// Increment the reg forms number if form is valid.
204
+					if ($registrant_form->hasQuestions()) {
205
+						$this->reg_form_count++;
206
+						$subsections[ $reg_url_link ] = $registrant_form;
207
+					} else {
208
+						// or just add a blank section if there are no questions
209
+						$subsections[ $reg_url_link ] = new EE_Form_Section_HTML();
210
+					}
211 211
 
212
-                    $this->template_args['registrations'][ $reg_url_link ]                = $registration;
213
-                    $this->template_args['ticket_count'][ $registration->ticket()->ID() ] = isset(
214
-                        $this->template_args['ticket_count'][ $registration->ticket()->ID() ]
215
-                    )
216
-                        ? $this->template_args['ticket_count'][ $registration->ticket()->ID() ] + 1
217
-                        : 1;
218
-                    $ticket_line_item = EEH_Line_Item::get_line_items_by_object_type_and_IDs(
219
-                        $this->reg_step->checkout->cart->get_grand_total(),
220
-                        'Ticket',
221
-                        [$registration->ticket()->ID()]
222
-                    );
223
-                    $ticket_line_item = is_array($ticket_line_item)
224
-                        ? reset($ticket_line_item)
225
-                        : $ticket_line_item;
226
-                    $this->template_args['ticket_line_item'][ $registration->ticket()->ID() ] =
227
-                        $Line_Item_Display->display_line_item($ticket_line_item);
228
-                    if ($registration->is_primary_registrant()) {
229
-                        $primary_registrant = $reg_url_link;
230
-                    }
231
-                }
232
-            }
212
+					$this->template_args['registrations'][ $reg_url_link ]                = $registration;
213
+					$this->template_args['ticket_count'][ $registration->ticket()->ID() ] = isset(
214
+						$this->template_args['ticket_count'][ $registration->ticket()->ID() ]
215
+					)
216
+						? $this->template_args['ticket_count'][ $registration->ticket()->ID() ] + 1
217
+						: 1;
218
+					$ticket_line_item = EEH_Line_Item::get_line_items_by_object_type_and_IDs(
219
+						$this->reg_step->checkout->cart->get_grand_total(),
220
+						'Ticket',
221
+						[$registration->ticket()->ID()]
222
+					);
223
+					$ticket_line_item = is_array($ticket_line_item)
224
+						? reset($ticket_line_item)
225
+						: $ticket_line_item;
226
+					$this->template_args['ticket_line_item'][ $registration->ticket()->ID() ] =
227
+						$Line_Item_Display->display_line_item($ticket_line_item);
228
+					if ($registration->is_primary_registrant()) {
229
+						$primary_registrant = $reg_url_link;
230
+					}
231
+				}
232
+			}
233 233
 
234
-            if ($primary_registrant && count($registrations) > 1) {
235
-                if (
236
-                    isset($subsections[ $primary_registrant ])
237
-                    && $subsections[ $primary_registrant ] instanceof EE_Form_Section_Proper
238
-                ) {
239
-                    $copy_options['spco_copy_attendee_chk'] = $this->print_copy_info
240
-                        ? new CopyAttendeeInfoForm($registrations, $this->reg_step->slug())
241
-                        : new AutoCopyAttendeeInfoForm($this->reg_step->slug());
242
-                    $subsections[ $primary_registrant ]->add_subsections(
243
-                        $copy_options,
244
-                        'primary_registrant',
245
-                        false
246
-                    );
247
-                }
248
-            }
249
-        }
234
+			if ($primary_registrant && count($registrations) > 1) {
235
+				if (
236
+					isset($subsections[ $primary_registrant ])
237
+					&& $subsections[ $primary_registrant ] instanceof EE_Form_Section_Proper
238
+				) {
239
+					$copy_options['spco_copy_attendee_chk'] = $this->print_copy_info
240
+						? new CopyAttendeeInfoForm($registrations, $this->reg_step->slug())
241
+						: new AutoCopyAttendeeInfoForm($this->reg_step->slug());
242
+					$subsections[ $primary_registrant ]->add_subsections(
243
+						$copy_options,
244
+						'primary_registrant',
245
+						false
246
+					);
247
+				}
248
+			}
249
+		}
250 250
 
251
-        // Set the registration form template (default: one form per ticket details table).
252
-        // We decide the template to used based on the number of forms.
253
-        $template = $this->reg_form_count > 1
254
-            ? SPCO_REG_STEPS_PATH . $this->reg_step->slug() . '/attendee_info_main.template.php'
255
-            : SPCO_REG_STEPS_PATH . $this->reg_step->slug() . '/attendee_info_single.template.php';
256
-        $this->reg_step->setTemplate($template);
251
+		// Set the registration form template (default: one form per ticket details table).
252
+		// We decide the template to used based on the number of forms.
253
+		$template = $this->reg_form_count > 1
254
+			? SPCO_REG_STEPS_PATH . $this->reg_step->slug() . '/attendee_info_main.template.php'
255
+			: SPCO_REG_STEPS_PATH . $this->reg_step->slug() . '/attendee_info_single.template.php';
256
+		$this->reg_step->setTemplate($template);
257 257
 
258
-        return $subsections;
259
-    }
258
+		return $subsections;
259
+	}
260 260
 
261 261
 
262
-    /**
263
-     * @param EE_Form_Section_Proper $extra_inputs_section
264
-     * @throws EE_Error
265
-     */
266
-    private function addPrivacyConsentCheckbox(EE_Form_Section_Proper $extra_inputs_section)
267
-    {
268
-        // if this isn't a revisit, and they have the privacy consent box enabled, add it
269
-        if (! $this->reg_step->checkout->revisit && $this->reg_config->isConsentCheckboxEnabled()) {
270
-            $extra_inputs_section->add_subsections(
271
-                [
272
-                    'consent_box' => new PrivacyConsentCheckboxForm(
273
-                        $this->reg_step->slug(),
274
-                        $this->reg_config->getConsentCheckboxLabelText()
275
-                    )
276
-                ],
277
-                null,
278
-                false
279
-            );
280
-        }
281
-    }
262
+	/**
263
+	 * @param EE_Form_Section_Proper $extra_inputs_section
264
+	 * @throws EE_Error
265
+	 */
266
+	private function addPrivacyConsentCheckbox(EE_Form_Section_Proper $extra_inputs_section)
267
+	{
268
+		// if this isn't a revisit, and they have the privacy consent box enabled, add it
269
+		if (! $this->reg_step->checkout->revisit && $this->reg_config->isConsentCheckboxEnabled()) {
270
+			$extra_inputs_section->add_subsections(
271
+				[
272
+					'consent_box' => new PrivacyConsentCheckboxForm(
273
+						$this->reg_step->slug(),
274
+						$this->reg_config->getConsentCheckboxLabelText()
275
+					)
276
+				],
277
+				null,
278
+				false
279
+			);
280
+		}
281
+	}
282 282
 }
Please login to merge, or discard this patch.
core/domain/services/registration/form/v1/RegFormQuestionGroup.php 2 patches
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -17,170 +17,170 @@
 block discarded – undo
17 17
 class RegFormQuestionGroup extends EE_Form_Section_Proper
18 18
 {
19 19
 
20
-    /**
21
-     * @var EE_SPCO_Reg_Step_Attendee_Information
22
-     */
23
-    public $reg_step;
20
+	/**
21
+	 * @var EE_SPCO_Reg_Step_Attendee_Information
22
+	 */
23
+	public $reg_step;
24 24
 
25
-    /**
26
-     * @var RegFormQuestionFactory
27
-     */
28
-    public $reg_form_question_factory;
25
+	/**
26
+	 * @var RegFormQuestionFactory
27
+	 */
28
+	public $reg_form_question_factory;
29 29
 
30 30
 
31
-    /**
32
-     * RegFormQuestionGroup constructor.
33
-     *
34
-     * @param EE_Registration        $registration
35
-     * @param EE_Question_Group      $question_group
36
-     * @param EE_SPCO_Reg_Step_Attendee_Information $reg_step
37
-     * @param RegFormQuestionFactory $reg_form_question_factory
38
-     * @throws EE_Error
39
-     * @throws ReflectionException
40
-     */
41
-    public function __construct(
42
-        EE_Registration $registration,
43
-        EE_Question_Group $question_group,
44
-        EE_SPCO_Reg_Step_Attendee_Information $reg_step,
45
-        RegFormQuestionFactory $reg_form_question_factory
46
-    ) {
47
-        $this->reg_step = $reg_step;
48
-        $this->reg_form_question_factory = $reg_form_question_factory;
49
-        parent::__construct($this->generateFormArgs($registration, $question_group));
50
-    }
31
+	/**
32
+	 * RegFormQuestionGroup constructor.
33
+	 *
34
+	 * @param EE_Registration        $registration
35
+	 * @param EE_Question_Group      $question_group
36
+	 * @param EE_SPCO_Reg_Step_Attendee_Information $reg_step
37
+	 * @param RegFormQuestionFactory $reg_form_question_factory
38
+	 * @throws EE_Error
39
+	 * @throws ReflectionException
40
+	 */
41
+	public function __construct(
42
+		EE_Registration $registration,
43
+		EE_Question_Group $question_group,
44
+		EE_SPCO_Reg_Step_Attendee_Information $reg_step,
45
+		RegFormQuestionFactory $reg_form_question_factory
46
+	) {
47
+		$this->reg_step = $reg_step;
48
+		$this->reg_form_question_factory = $reg_form_question_factory;
49
+		parent::__construct($this->generateFormArgs($registration, $question_group));
50
+	}
51 51
 
52 52
 
53
-    /**
54
-     * @param EE_Registration   $registration
55
-     * @param EE_Question_Group $question_group
56
-     * @return array
57
-     * @throws EE_Error
58
-     * @throws ReflectionException
59
-     */
60
-    private function generateFormArgs(
61
-        EE_Registration $registration,
62
-        EE_Question_Group $question_group
63
-    ): array {
64
-        // array of params to pass to parent constructor
65
-        $form_args = [
66
-            'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
67
-            'html_class'      => $this->reg_step->checkout->admin_request
68
-                ? 'form-table ee-reg-form-qstn-grp-dv'
69
-                : 'ee-reg-form-qstn-grp-dv',
70
-            'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-'
71
-                                 . $registration->ID() . '-lbl',
72
-            'subsections'     => [
73
-                'reg_form_qstn_grp_hdr' => $this->questionGroupHeader(
74
-                    $question_group,
75
-                    $this->reg_step->checkout->admin_request
76
-                ),
77
-            ],
78
-            'layout_strategy' => $this->reg_step->checkout->admin_request
79
-                ? new EE_Admin_Two_Column_Layout()
80
-                : new EE_Div_Per_Section_Layout(),
81
-        ];
82
-        // where params
83
-        $query_params = ['QST_deleted' => 0];
84
-        // don't load admin only questions on the frontend
85
-        if (! $this->reg_step->checkout->admin_request) {
86
-            $query_params['QST_admin_only'] = ['!=', true];
87
-        }
88
-        $questions = $question_group->get_many_related(
89
-            'Question',
90
-            apply_filters(
91
-                'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__related_questions_query_params',
92
-                [
93
-                    $query_params,
94
-                    'order_by' => [
95
-                        'Question_Group_Question.QGQ_order' => 'ASC',
96
-                    ],
97
-                ],
98
-                $question_group,
99
-                $registration,
100
-                $this
101
-            )
102
-        );
103
-        // filter for additional content before questions
104
-        $form_args['subsections']['reg_form_questions_before'] = new EE_Form_Section_HTML(
105
-            apply_filters(
106
-                'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__before_question_group_questions',
107
-                '',
108
-                $registration,
109
-                $question_group,
110
-                $this->reg_step
111
-            )
112
-        );
113
-        // loop thru questions
114
-        foreach ($questions as $question) {
115
-            if ($question instanceof EE_Question) {
116
-                $identifier = $question->is_system_question()
117
-                    ? $question->system_ID()
118
-                    : $question->ID();
53
+	/**
54
+	 * @param EE_Registration   $registration
55
+	 * @param EE_Question_Group $question_group
56
+	 * @return array
57
+	 * @throws EE_Error
58
+	 * @throws ReflectionException
59
+	 */
60
+	private function generateFormArgs(
61
+		EE_Registration $registration,
62
+		EE_Question_Group $question_group
63
+	): array {
64
+		// array of params to pass to parent constructor
65
+		$form_args = [
66
+			'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
67
+			'html_class'      => $this->reg_step->checkout->admin_request
68
+				? 'form-table ee-reg-form-qstn-grp-dv'
69
+				: 'ee-reg-form-qstn-grp-dv',
70
+			'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-'
71
+								 . $registration->ID() . '-lbl',
72
+			'subsections'     => [
73
+				'reg_form_qstn_grp_hdr' => $this->questionGroupHeader(
74
+					$question_group,
75
+					$this->reg_step->checkout->admin_request
76
+				),
77
+			],
78
+			'layout_strategy' => $this->reg_step->checkout->admin_request
79
+				? new EE_Admin_Two_Column_Layout()
80
+				: new EE_Div_Per_Section_Layout(),
81
+		];
82
+		// where params
83
+		$query_params = ['QST_deleted' => 0];
84
+		// don't load admin only questions on the frontend
85
+		if (! $this->reg_step->checkout->admin_request) {
86
+			$query_params['QST_admin_only'] = ['!=', true];
87
+		}
88
+		$questions = $question_group->get_many_related(
89
+			'Question',
90
+			apply_filters(
91
+				'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__related_questions_query_params',
92
+				[
93
+					$query_params,
94
+					'order_by' => [
95
+						'Question_Group_Question.QGQ_order' => 'ASC',
96
+					],
97
+				],
98
+				$question_group,
99
+				$registration,
100
+				$this
101
+			)
102
+		);
103
+		// filter for additional content before questions
104
+		$form_args['subsections']['reg_form_questions_before'] = new EE_Form_Section_HTML(
105
+			apply_filters(
106
+				'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__before_question_group_questions',
107
+				'',
108
+				$registration,
109
+				$question_group,
110
+				$this->reg_step
111
+			)
112
+		);
113
+		// loop thru questions
114
+		foreach ($questions as $question) {
115
+			if ($question instanceof EE_Question) {
116
+				$identifier = $question->is_system_question()
117
+					? $question->system_ID()
118
+					: $question->ID();
119 119
 
120
-                $form_args['subsections'][ $identifier ] = $this->reg_form_question_factory->create(
121
-                    $registration,
122
-                    $question
123
-                );
124
-            }
125
-        }
126
-        $form_args['subsections'] = apply_filters(
127
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__subsections_array',
128
-            $form_args['subsections'],
129
-            $registration,
130
-            $question_group,
131
-            $this
132
-        );
133
-        // filter for additional content after questions
134
-        $form_args['subsections']['reg_form_questions_after'] = new EE_Form_Section_HTML(
135
-            apply_filters(
136
-                'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__after_question_group_questions',
137
-                '',
138
-                $registration,
139
-                $question_group,
140
-                $this
141
-            )
142
-        );
120
+				$form_args['subsections'][ $identifier ] = $this->reg_form_question_factory->create(
121
+					$registration,
122
+					$question
123
+				);
124
+			}
125
+		}
126
+		$form_args['subsections'] = apply_filters(
127
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__subsections_array',
128
+			$form_args['subsections'],
129
+			$registration,
130
+			$question_group,
131
+			$this
132
+		);
133
+		// filter for additional content after questions
134
+		$form_args['subsections']['reg_form_questions_after'] = new EE_Form_Section_HTML(
135
+			apply_filters(
136
+				'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__after_question_group_questions',
137
+				'',
138
+				$registration,
139
+				$question_group,
140
+				$this
141
+			)
142
+		);
143 143
 
144
-        return $form_args;
145
-    }
144
+		return $form_args;
145
+	}
146 146
 
147 147
 
148
-    /**
149
-     * @param EE_Question_Group $question_group
150
-     * @param bool              $admin_request
151
-     * @return EE_Form_Section_HTML
152
-     */
153
-    private function questionGroupHeader(EE_Question_Group $question_group, bool $admin_request): EE_Form_Section_HTML
154
-    {
155
-        $html = '';
156
-        // group_name
157
-        if ($question_group->show_group_name() && $question_group->name() !== '') {
158
-            if ($admin_request) {
159
-                $html .= EEH_HTML::br();
160
-                $html .= EEH_HTML::h3(
161
-                    $question_group->name(),
162
-                    '',
163
-                    'ee-reg-form-qstn-grp-title title',
164
-                    'font-size: 1.3em; padding-left:0;'
165
-                );
166
-            } else {
167
-                $html .= EEH_HTML::h4(
168
-                    $question_group->name(),
169
-                    '',
170
-                    'ee-reg-form-qstn-grp-title section-title'
171
-                );
172
-            }
173
-        }
174
-        // group_desc
175
-        if ($question_group->show_group_desc() && $question_group->desc() !== '') {
176
-            $html .= EEH_HTML::p(
177
-                $question_group->desc(),
178
-                '',
179
-                $admin_request
180
-                    ? 'ee-reg-form-qstn-grp-desc-pg'
181
-                    : 'ee-reg-form-qstn-grp-desc-pg small-text lt-grey-text'
182
-            );
183
-        }
184
-        return new EE_Form_Section_HTML($html);
185
-    }
148
+	/**
149
+	 * @param EE_Question_Group $question_group
150
+	 * @param bool              $admin_request
151
+	 * @return EE_Form_Section_HTML
152
+	 */
153
+	private function questionGroupHeader(EE_Question_Group $question_group, bool $admin_request): EE_Form_Section_HTML
154
+	{
155
+		$html = '';
156
+		// group_name
157
+		if ($question_group->show_group_name() && $question_group->name() !== '') {
158
+			if ($admin_request) {
159
+				$html .= EEH_HTML::br();
160
+				$html .= EEH_HTML::h3(
161
+					$question_group->name(),
162
+					'',
163
+					'ee-reg-form-qstn-grp-title title',
164
+					'font-size: 1.3em; padding-left:0;'
165
+				);
166
+			} else {
167
+				$html .= EEH_HTML::h4(
168
+					$question_group->name(),
169
+					'',
170
+					'ee-reg-form-qstn-grp-title section-title'
171
+				);
172
+			}
173
+		}
174
+		// group_desc
175
+		if ($question_group->show_group_desc() && $question_group->desc() !== '') {
176
+			$html .= EEH_HTML::p(
177
+				$question_group->desc(),
178
+				'',
179
+				$admin_request
180
+					? 'ee-reg-form-qstn-grp-desc-pg'
181
+					: 'ee-reg-form-qstn-grp-desc-pg small-text lt-grey-text'
182
+			);
183
+		}
184
+		return new EE_Form_Section_HTML($html);
185
+	}
186 186
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -63,12 +63,12 @@  discard block
 block discarded – undo
63 63
     ): array {
64 64
         // array of params to pass to parent constructor
65 65
         $form_args = [
66
-            'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
66
+            'html_id'         => 'ee-reg-form-qstn-grp-'.$question_group->identifier().'-'.$registration->ID(),
67 67
             'html_class'      => $this->reg_step->checkout->admin_request
68 68
                 ? 'form-table ee-reg-form-qstn-grp-dv'
69 69
                 : 'ee-reg-form-qstn-grp-dv',
70
-            'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-'
71
-                                 . $registration->ID() . '-lbl',
70
+            'html_label_id'   => 'ee-reg-form-qstn-grp-'.$question_group->identifier().'-'
71
+                                 . $registration->ID().'-lbl',
72 72
             'subsections'     => [
73 73
                 'reg_form_qstn_grp_hdr' => $this->questionGroupHeader(
74 74
                     $question_group,
@@ -82,7 +82,7 @@  discard block
 block discarded – undo
82 82
         // where params
83 83
         $query_params = ['QST_deleted' => 0];
84 84
         // don't load admin only questions on the frontend
85
-        if (! $this->reg_step->checkout->admin_request) {
85
+        if ( ! $this->reg_step->checkout->admin_request) {
86 86
             $query_params['QST_admin_only'] = ['!=', true];
87 87
         }
88 88
         $questions = $question_group->get_many_related(
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
                     ? $question->system_ID()
118 118
                     : $question->ID();
119 119
 
120
-                $form_args['subsections'][ $identifier ] = $this->reg_form_question_factory->create(
120
+                $form_args['subsections'][$identifier] = $this->reg_form_question_factory->create(
121 121
                     $registration,
122 122
                     $question
123 123
                 );
Please login to merge, or discard this patch.
core/domain/services/registration/form/v1/RegFormInputHandler.php 1 patch
Indentation   +216 added lines, -216 removed lines patch added patch discarded remove patch
@@ -14,223 +14,223 @@
 block discarded – undo
14 14
 class RegFormInputHandler
15 15
 {
16 16
 
17
-    /**
18
-     * @var EEM_Attendee
19
-     */
20
-    private $attendee_model;
21
-
22
-    /**
23
-     * @var string
24
-     */
25
-    private $checkout_reg_url_link;
26
-
27
-    /**
28
-     * @var RegistrantData
29
-     */
30
-    private $registrant_data;
31
-
32
-    /**
33
-     * @var array
34
-     */
35
-    private $required_questions;
36
-
37
-
38
-    /**
39
-     * RegFormHandler constructor.
40
-     */
41
-    public function __construct(
42
-        string $checkout_reg_url_link,
43
-        array $required_questions,
44
-        EEM_Attendee $attendee_model,
45
-        RegistrantData $registrant_data
46
-    ) {
47
-        $this->attendee_model        = $attendee_model;
48
-        $this->checkout_reg_url_link = $checkout_reg_url_link;
49
-        $this->registrant_data       = $registrant_data;
50
-        $this->required_questions    = $required_questions;
51
-    }
52
-
53
-
54
-    /**
55
-     * @param EE_Registration  $registration
56
-     * @param string           $reg_url_link
57
-     * @param int|string       $form_input
58
-     * @param float|int|string $input_value
59
-     * @return bool
60
-     * @throws EE_Error
61
-     * @throws ReflectionException
62
-     */
63
-    public function processFormInput(
64
-        EE_Registration $registration,
65
-        string $reg_url_link,
66
-        $form_input,
67
-        $input_value
68
-    ): bool {
69
-        // check for critical inputs
70
-        if (! $this->verifyCriticalAttendeeDetailsAreSetAndValidateEmail($form_input, $input_value)) {
71
-            return false;
72
-        }
73
-        $input_value = $this->registrant_data->saveOrCopyPrimaryRegistrantData(
74
-            $reg_url_link,
75
-            $form_input,
76
-            $input_value
77
-        );
78
-        if (! $this->saveRegistrationFormInput($registration, $reg_url_link, $form_input, $input_value)) {
79
-            EE_Error::add_error(
80
-                sprintf(
81
-                    esc_html_x(
82
-                        'Unable to save registration form data for the form input: "%1$s" with the submitted value: "%2$s"',
83
-                        'Unable to save registration form data for the form input: "form input name" with the submitted value: "form input value"',
84
-                        'event_espresso'
85
-                    ),
86
-                    $form_input,
87
-                    $input_value
88
-                ),
89
-                __FILE__,
90
-                __FUNCTION__,
91
-                __LINE__
92
-            );
93
-            return false;
94
-        }
95
-        return true;
96
-    }
97
-
98
-
99
-    /**
100
-     * @param EE_Registration  $registration
101
-     * @param string           $reg_url_link
102
-     * @param int|string       $form_input
103
-     * @param float|int|string $input_value
104
-     * @return bool
105
-     * @throws EE_Error
106
-     * @throws InvalidArgumentException
107
-     * @throws InvalidDataTypeException
108
-     * @throws InvalidInterfaceException
109
-     * @throws ReflectionException
110
-     */
111
-    private function saveRegistrationFormInput(
112
-        EE_Registration $registration,
113
-        string $reg_url_link,
114
-        $form_input = '',
115
-        $input_value = ''
116
-    ): bool {
117
-        // If email_confirm is sent it's not saved
118
-        if ((string) $form_input === 'email_confirm') {
119
-            return true;
120
-        }
121
-        // allow for plugins to hook in and do their own processing of the form input.
122
-        // For plugins to bypass normal processing here, they just need to return a truthy value.
123
-        if (
124
-            apply_filters(
125
-                'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormInputHandler__saveRegistrationFormInput',
126
-                false,
127
-                $registration,
128
-                $form_input,
129
-                $input_value,
130
-                $this
131
-            )
132
-        ) {
133
-            return true;
134
-        }
135
-        /*
17
+	/**
18
+	 * @var EEM_Attendee
19
+	 */
20
+	private $attendee_model;
21
+
22
+	/**
23
+	 * @var string
24
+	 */
25
+	private $checkout_reg_url_link;
26
+
27
+	/**
28
+	 * @var RegistrantData
29
+	 */
30
+	private $registrant_data;
31
+
32
+	/**
33
+	 * @var array
34
+	 */
35
+	private $required_questions;
36
+
37
+
38
+	/**
39
+	 * RegFormHandler constructor.
40
+	 */
41
+	public function __construct(
42
+		string $checkout_reg_url_link,
43
+		array $required_questions,
44
+		EEM_Attendee $attendee_model,
45
+		RegistrantData $registrant_data
46
+	) {
47
+		$this->attendee_model        = $attendee_model;
48
+		$this->checkout_reg_url_link = $checkout_reg_url_link;
49
+		$this->registrant_data       = $registrant_data;
50
+		$this->required_questions    = $required_questions;
51
+	}
52
+
53
+
54
+	/**
55
+	 * @param EE_Registration  $registration
56
+	 * @param string           $reg_url_link
57
+	 * @param int|string       $form_input
58
+	 * @param float|int|string $input_value
59
+	 * @return bool
60
+	 * @throws EE_Error
61
+	 * @throws ReflectionException
62
+	 */
63
+	public function processFormInput(
64
+		EE_Registration $registration,
65
+		string $reg_url_link,
66
+		$form_input,
67
+		$input_value
68
+	): bool {
69
+		// check for critical inputs
70
+		if (! $this->verifyCriticalAttendeeDetailsAreSetAndValidateEmail($form_input, $input_value)) {
71
+			return false;
72
+		}
73
+		$input_value = $this->registrant_data->saveOrCopyPrimaryRegistrantData(
74
+			$reg_url_link,
75
+			$form_input,
76
+			$input_value
77
+		);
78
+		if (! $this->saveRegistrationFormInput($registration, $reg_url_link, $form_input, $input_value)) {
79
+			EE_Error::add_error(
80
+				sprintf(
81
+					esc_html_x(
82
+						'Unable to save registration form data for the form input: "%1$s" with the submitted value: "%2$s"',
83
+						'Unable to save registration form data for the form input: "form input name" with the submitted value: "form input value"',
84
+						'event_espresso'
85
+					),
86
+					$form_input,
87
+					$input_value
88
+				),
89
+				__FILE__,
90
+				__FUNCTION__,
91
+				__LINE__
92
+			);
93
+			return false;
94
+		}
95
+		return true;
96
+	}
97
+
98
+
99
+	/**
100
+	 * @param EE_Registration  $registration
101
+	 * @param string           $reg_url_link
102
+	 * @param int|string       $form_input
103
+	 * @param float|int|string $input_value
104
+	 * @return bool
105
+	 * @throws EE_Error
106
+	 * @throws InvalidArgumentException
107
+	 * @throws InvalidDataTypeException
108
+	 * @throws InvalidInterfaceException
109
+	 * @throws ReflectionException
110
+	 */
111
+	private function saveRegistrationFormInput(
112
+		EE_Registration $registration,
113
+		string $reg_url_link,
114
+		$form_input = '',
115
+		$input_value = ''
116
+	): bool {
117
+		// If email_confirm is sent it's not saved
118
+		if ((string) $form_input === 'email_confirm') {
119
+			return true;
120
+		}
121
+		// allow for plugins to hook in and do their own processing of the form input.
122
+		// For plugins to bypass normal processing here, they just need to return a truthy value.
123
+		if (
124
+			apply_filters(
125
+				'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormInputHandler__saveRegistrationFormInput',
126
+				false,
127
+				$registration,
128
+				$form_input,
129
+				$input_value,
130
+				$this
131
+			)
132
+		) {
133
+			return true;
134
+		}
135
+		/*
136 136
          * $answer_cache_id is the key used to find the EE_Answer we want
137 137
          * @see https://events.codebasehq.com/projects/event-espresso/tickets/10477
138 138
          */
139
-        $answer_cache_id   = $this->checkout_reg_url_link
140
-            ? $form_input . '-' . $reg_url_link
141
-            : $form_input;
142
-        $registrant_answer = $this->registrant_data->getRegistrantAnswer($reg_url_link, $answer_cache_id);
143
-        $answer_is_obj     = $registrant_answer instanceof EE_Answer;
144
-        // rename form_inputs if they are EE_Attendee properties
145
-        switch ((string) $form_input) {
146
-            case 'state':
147
-            case 'STA_ID':
148
-                $attendee_property = true;
149
-                $form_input        = 'STA_ID';
150
-                break;
151
-
152
-            case 'country':
153
-            case 'CNT_ISO':
154
-                $attendee_property = true;
155
-                $form_input        = 'CNT_ISO';
156
-                break;
157
-
158
-            default:
159
-                $ATT_input         = 'ATT_' . $form_input;
160
-                $attendee_property = $this->attendee_model->has_field($ATT_input);
161
-                $form_input        = $attendee_property
162
-                    ? 'ATT_' . $form_input
163
-                    : $form_input;
164
-        }
165
-
166
-        // if this form input has a corresponding attendee property
167
-        if ($attendee_property) {
168
-            $this->registrant_data->addRegistrantDataValue($reg_url_link, $form_input, $input_value);
169
-            if ($answer_is_obj) {
170
-                // and delete the corresponding answer since we won't be storing this data in that object
171
-                $registration->_remove_relation_to($registrant_answer, 'Answer');
172
-                $registrant_answer->delete_permanently();
173
-            }
174
-            return true;
175
-        }
176
-        if ($answer_is_obj) {
177
-            // save this data to the answer object
178
-            $registrant_answer->set_value($input_value);
179
-            $result = $registrant_answer->save();
180
-            return $result !== false;
181
-        }
182
-        foreach ($this->registrant_data->registrantAnswers($reg_url_link) as $answer) {
183
-            if ($answer instanceof EE_Answer && $answer->question_ID() === $answer_cache_id) {
184
-                $answer->set_value($input_value);
185
-                $result = $answer->save();
186
-                return $result !== false;
187
-            }
188
-        }
189
-        return false;
190
-    }
191
-
192
-
193
-    /**
194
-     * @param int|string       $form_input
195
-     * @param float|int|string $input_value
196
-     * @return boolean
197
-     */
198
-    private function verifyCriticalAttendeeDetailsAreSetAndValidateEmail(
199
-        $form_input = '',
200
-        $input_value = ''
201
-    ): bool {
202
-        if (empty($input_value)) {
203
-            // if the form input isn't marked as being required, then just return
204
-            if (! isset($this->required_questions[ $form_input ]) || ! $this->required_questions[ $form_input ]) {
205
-                return true;
206
-            }
207
-            switch ($form_input) {
208
-                case 'fname':
209
-                    EE_Error::add_error(
210
-                        esc_html__('First Name is a required value.', 'event_espresso'),
211
-                        __FILE__,
212
-                        __FUNCTION__,
213
-                        __LINE__
214
-                    );
215
-                    return false;
216
-                case 'lname':
217
-                    EE_Error::add_error(
218
-                        esc_html__('Last Name is a required value.', 'event_espresso'),
219
-                        __FILE__,
220
-                        __FUNCTION__,
221
-                        __LINE__
222
-                    );
223
-                    return false;
224
-                case 'email':
225
-                    EE_Error::add_error(
226
-                        esc_html__('Please enter a valid email address.', 'event_espresso'),
227
-                        __FILE__,
228
-                        __FUNCTION__,
229
-                        __LINE__
230
-                    );
231
-                    return false;
232
-            }
233
-        }
234
-        return true;
235
-    }
139
+		$answer_cache_id   = $this->checkout_reg_url_link
140
+			? $form_input . '-' . $reg_url_link
141
+			: $form_input;
142
+		$registrant_answer = $this->registrant_data->getRegistrantAnswer($reg_url_link, $answer_cache_id);
143
+		$answer_is_obj     = $registrant_answer instanceof EE_Answer;
144
+		// rename form_inputs if they are EE_Attendee properties
145
+		switch ((string) $form_input) {
146
+			case 'state':
147
+			case 'STA_ID':
148
+				$attendee_property = true;
149
+				$form_input        = 'STA_ID';
150
+				break;
151
+
152
+			case 'country':
153
+			case 'CNT_ISO':
154
+				$attendee_property = true;
155
+				$form_input        = 'CNT_ISO';
156
+				break;
157
+
158
+			default:
159
+				$ATT_input         = 'ATT_' . $form_input;
160
+				$attendee_property = $this->attendee_model->has_field($ATT_input);
161
+				$form_input        = $attendee_property
162
+					? 'ATT_' . $form_input
163
+					: $form_input;
164
+		}
165
+
166
+		// if this form input has a corresponding attendee property
167
+		if ($attendee_property) {
168
+			$this->registrant_data->addRegistrantDataValue($reg_url_link, $form_input, $input_value);
169
+			if ($answer_is_obj) {
170
+				// and delete the corresponding answer since we won't be storing this data in that object
171
+				$registration->_remove_relation_to($registrant_answer, 'Answer');
172
+				$registrant_answer->delete_permanently();
173
+			}
174
+			return true;
175
+		}
176
+		if ($answer_is_obj) {
177
+			// save this data to the answer object
178
+			$registrant_answer->set_value($input_value);
179
+			$result = $registrant_answer->save();
180
+			return $result !== false;
181
+		}
182
+		foreach ($this->registrant_data->registrantAnswers($reg_url_link) as $answer) {
183
+			if ($answer instanceof EE_Answer && $answer->question_ID() === $answer_cache_id) {
184
+				$answer->set_value($input_value);
185
+				$result = $answer->save();
186
+				return $result !== false;
187
+			}
188
+		}
189
+		return false;
190
+	}
191
+
192
+
193
+	/**
194
+	 * @param int|string       $form_input
195
+	 * @param float|int|string $input_value
196
+	 * @return boolean
197
+	 */
198
+	private function verifyCriticalAttendeeDetailsAreSetAndValidateEmail(
199
+		$form_input = '',
200
+		$input_value = ''
201
+	): bool {
202
+		if (empty($input_value)) {
203
+			// if the form input isn't marked as being required, then just return
204
+			if (! isset($this->required_questions[ $form_input ]) || ! $this->required_questions[ $form_input ]) {
205
+				return true;
206
+			}
207
+			switch ($form_input) {
208
+				case 'fname':
209
+					EE_Error::add_error(
210
+						esc_html__('First Name is a required value.', 'event_espresso'),
211
+						__FILE__,
212
+						__FUNCTION__,
213
+						__LINE__
214
+					);
215
+					return false;
216
+				case 'lname':
217
+					EE_Error::add_error(
218
+						esc_html__('Last Name is a required value.', 'event_espresso'),
219
+						__FILE__,
220
+						__FUNCTION__,
221
+						__LINE__
222
+					);
223
+					return false;
224
+				case 'email':
225
+					EE_Error::add_error(
226
+						esc_html__('Please enter a valid email address.', 'event_espresso'),
227
+						__FILE__,
228
+						__FUNCTION__,
229
+						__LINE__
230
+					);
231
+					return false;
232
+			}
233
+		}
234
+		return true;
235
+	}
236 236
 }
Please login to merge, or discard this patch.
core/EE_Payment_Processor.core.php 1 patch
Indentation   +842 added lines, -842 removed lines patch added patch discarded remove patch
@@ -18,846 +18,846 @@
 block discarded – undo
18 18
 class EE_Payment_Processor extends EE_Processor_Base implements ResettableInterface
19 19
 {
20 20
 
21
-    /**
22
-     * @var EE_Payment_Processor $_instance
23
-     * @access    private
24
-     */
25
-    private static $_instance;
26
-
27
-
28
-    /**
29
-     * @singleton method used to instantiate class object
30
-     * @access    public
31
-     * @return EE_Payment_Processor instance
32
-     */
33
-    public static function instance()
34
-    {
35
-        // check if class object is instantiated
36
-        if (! self::$_instance instanceof EE_Payment_Processor) {
37
-            self::$_instance = new self();
38
-        }
39
-        return self::$_instance;
40
-    }
41
-
42
-
43
-    /**
44
-     * @return EE_Payment_Processor
45
-     */
46
-    public static function reset()
47
-    {
48
-        self::$_instance = null;
49
-        return self::instance();
50
-    }
51
-
52
-
53
-    /**
54
-     *private constructor to prevent direct creation
55
-     *
56
-     * @Constructor
57
-     * @access private
58
-     */
59
-    private function __construct()
60
-    {
61
-        do_action('AHEE__EE_Payment_Processor__construct');
62
-        add_action('http_api_curl', array($this, '_curl_requests_to_paypal_use_tls'), 10, 3);
63
-    }
64
-
65
-
66
-    /**
67
-     * Using the selected gateway, processes the payment for that transaction, and updates the transaction
68
-     * appropriately. Saves the payment that is generated
69
-     *
70
-     * @param EE_Payment_Method    $payment_method
71
-     * @param EE_Transaction       $transaction
72
-     * @param float|null                $amount       if only part of the transaction is to be paid for, how much.
73
-     *                                           Leave null if payment is for the full amount owing
74
-     * @param EE_Billing_Info_Form|null $billing_form (or probably null, if it's an offline or offsite payment method).
75
-     *                                           Receive_form_submission() should have
76
-     *                                           already been called on the billing form
77
-     *                                           (ie, its inputs should have their normalized values set).
78
-     * @param string|null          $return_url   string used mostly by offsite gateways to specify
79
-     *                                           where to go AFTER the offsite gateway
80
-     * @param string               $method       like 'CART', indicates who the client who called this was
81
-     * @param bool                 $by_admin     TRUE if payment is being attempted from the admin
82
-     * @param bool                 $update_txn   whether or not to call
83
-     *                                           EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
84
-     * @param string               $cancel_url   URL to return to if off-site payments are cancelled
85
-     * @return EE_Payment
86
-     * @throws EE_Error
87
-     * @throws InvalidArgumentException
88
-     * @throws ReflectionException
89
-     * @throws RuntimeException
90
-     * @throws InvalidDataTypeException
91
-     * @throws InvalidInterfaceException
92
-     */
93
-    public function process_payment(
94
-        EE_Payment_Method $payment_method,
95
-        EE_Transaction $transaction,
96
-        ?float $amount = null,
97
-        ?EE_Billing_Info_Form $billing_form = null,
98
-        ?string $return_url = null,
99
-        string $method = 'CART',
100
-        bool $by_admin = false,
101
-        bool $update_txn = true,
102
-        string $cancel_url = ''
103
-    ): ?EE_Payment {
104
-        if ((float) $amount < 0) {
105
-            throw new EE_Error(
106
-                sprintf(
107
-                    esc_html__(
108
-                        'Attempting to make a payment for a negative amount of %1$d for transaction %2$d. That should be a refund',
109
-                        'event_espresso'
110
-                    ),
111
-                    $amount,
112
-                    $transaction->ID()
113
-                )
114
-            );
115
-        }
116
-        // verify payment method
117
-        $payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
118
-            $payment_method,
119
-            true
120
-        );
121
-        // verify transaction
122
-        EEM_Transaction::instance()->ensure_is_obj($transaction);
123
-        $transaction->set_payment_method_ID($payment_method->ID());
124
-        // verify payment method type
125
-        if ($payment_method->type_obj() instanceof EE_PMT_Base) {
126
-            $payment = $payment_method->type_obj()->process_payment(
127
-                $transaction,
128
-                min($amount, $transaction->remaining()), // make sure we don't overcharge
129
-                $billing_form,
130
-                $return_url,
131
-                add_query_arg(array('ee_cancel_payment' => true), $cancel_url),
132
-                $method,
133
-                $by_admin
134
-            );
135
-            // check if payment method uses an off-site gateway
136
-            if ($payment_method->type_obj()->payment_occurs() !== EE_PMT_Base::offsite) {
137
-                // don't process payments for off-site gateways yet because no payment has occurred yet
138
-                $this->update_txn_based_on_payment($transaction, $payment, $update_txn);
139
-            }
140
-            return $payment;
141
-        }
142
-        EE_Error::add_error(
143
-            sprintf(
144
-                esc_html__(
145
-                    'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
146
-                    'event_espresso'
147
-                ),
148
-                '<br/>',
149
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
150
-            ),
151
-            __FILE__,
152
-            __FUNCTION__,
153
-            __LINE__
154
-        );
155
-        return null;
156
-    }
157
-
158
-
159
-    /**
160
-     * @param EE_Transaction|int $transaction
161
-     * @param EE_Payment_Method  $payment_method
162
-     * @return string
163
-     * @throws EE_Error
164
-     * @throws InvalidArgumentException
165
-     * @throws InvalidDataTypeException
166
-     * @throws InvalidInterfaceException
167
-     */
168
-    public function get_ipn_url_for_payment_method($transaction, $payment_method)
169
-    {
170
-        /** @type \EE_Transaction $transaction */
171
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
172
-        $primary_reg = $transaction->primary_registration();
173
-        if (! $primary_reg instanceof EE_Registration) {
174
-            throw new EE_Error(
175
-                sprintf(
176
-                    esc_html__(
177
-                        'Cannot get IPN URL for transaction with ID %d because it has no primary registration',
178
-                        'event_espresso'
179
-                    ),
180
-                    $transaction->ID()
181
-                )
182
-            );
183
-        }
184
-        $payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
185
-            $payment_method,
186
-            true
187
-        );
188
-        $url = add_query_arg(
189
-            array(
190
-                'e_reg_url_link'    => $primary_reg->reg_url_link(),
191
-                'ee_payment_method' => $payment_method->slug(),
192
-            ),
193
-            EE_Registry::instance()->CFG->core->txn_page_url()
194
-        );
195
-        return $url;
196
-    }
197
-
198
-
199
-    /**
200
-     * Process the IPN. Firstly, we'll hope we put the standard args into the IPN URL so
201
-     * we can easily find what registration the IPN is for and what payment method.
202
-     * However, if not, we'll give all payment methods a chance to claim it and process it.
203
-     * If a payment is found for the IPN info, it is saved.
204
-     *
205
-     * @param array              $_req_data            form post data
206
-     * @param EE_Transaction|int $transaction          optional (or a transactions id)
207
-     * @param EE_Payment_Method  $payment_method       (or a slug or id of one)
208
-     * @param boolean            $update_txn           whether or not to call
209
-     *                                                 EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
210
-     * @param bool               $separate_IPN_request whether the IPN uses a separate request (true, like PayPal)
211
-     *                                                 or is processed manually (false, like Authorize.net)
212
-     * @throws EE_Error
213
-     * @throws Exception
214
-     * @return EE_Payment
215
-     * @throws \RuntimeException
216
-     * @throws \ReflectionException
217
-     * @throws \InvalidArgumentException
218
-     * @throws InvalidInterfaceException
219
-     * @throws InvalidDataTypeException
220
-     */
221
-    public function process_ipn(
222
-        $_req_data,
223
-        $transaction = null,
224
-        $payment_method = null,
225
-        $update_txn = true,
226
-        $separate_IPN_request = true
227
-    ) {
228
-        EE_Registry::instance()->load_model('Change_Log');
229
-        $_req_data = $this->_remove_unusable_characters_from_array((array) $_req_data);
230
-        EE_Processor_Base::set_IPN($separate_IPN_request);
231
-        $obj_for_log = null;
232
-        if ($transaction instanceof EE_Transaction) {
233
-            $obj_for_log = $transaction;
234
-            if ($payment_method instanceof EE_Payment_Method) {
235
-                $obj_for_log = EEM_Payment::instance()->get_one(
236
-                    array(
237
-                        array('TXN_ID' => $transaction->ID(), 'PMD_ID' => $payment_method->ID()),
238
-                        'order_by' => array('PAY_timestamp' => 'desc'),
239
-                    )
240
-                );
241
-            }
242
-        } elseif ($payment_method instanceof EE_Payment) {
243
-            $obj_for_log = $payment_method;
244
-        }
245
-        $log = EEM_Change_Log::instance()->log(
246
-            EEM_Change_Log::type_gateway,
247
-            array('IPN data received' => $_req_data),
248
-            $obj_for_log
249
-        );
250
-        try {
251
-            /**
252
-             * @var EE_Payment $payment
253
-             */
254
-            $payment = null;
255
-            if ($transaction && $payment_method) {
256
-                /** @type EE_Transaction $transaction */
257
-                $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
258
-                /** @type EE_Payment_Method $payment_method */
259
-                $payment_method = EEM_Payment_Method::instance()->ensure_is_obj($payment_method);
260
-                if ($payment_method->type_obj() instanceof EE_PMT_Base) {
261
-                    try {
262
-                        $payment = $payment_method->type_obj()->handle_ipn($_req_data, $transaction);
263
-                        $log->set_object($payment);
264
-                    } catch (EventEspresso\core\exceptions\IpnException $e) {
265
-                        EEM_Change_Log::instance()->log(
266
-                            EEM_Change_Log::type_gateway,
267
-                            array(
268
-                                'message'     => 'IPN Exception: ' . $e->getMessage(),
269
-                                'current_url' => EEH_URL::current_url(),
270
-                                'payment'     => $e->getPaymentProperties(),
271
-                                'IPN_data'    => $e->getIpnData(),
272
-                            ),
273
-                            $obj_for_log
274
-                        );
275
-                        return $e->getPayment();
276
-                    }
277
-                } else {
278
-                    // not a payment
279
-                    EE_Error::add_error(
280
-                        sprintf(
281
-                            esc_html__(
282
-                                'A valid payment method could not be determined due to a technical issue.%sPlease refresh your browser and try again or contact %s for assistance.',
283
-                                'event_espresso'
284
-                            ),
285
-                            '<br/>',
286
-                            EE_Registry::instance()->CFG->organization->get_pretty('email')
287
-                        ),
288
-                        __FILE__,
289
-                        __FUNCTION__,
290
-                        __LINE__
291
-                    );
292
-                }
293
-            } else {
294
-                // that's actually pretty ok. The IPN just wasn't able
295
-                // to identify which transaction or payment method this was for
296
-                // give all active payment methods a chance to claim it
297
-                $active_payment_methods = EEM_Payment_Method::instance()->get_all_active();
298
-                foreach ($active_payment_methods as $active_payment_method) {
299
-                    try {
300
-                        $payment = $active_payment_method->type_obj()->handle_unclaimed_ipn($_req_data);
301
-                        $payment_method = $active_payment_method;
302
-                        EEM_Change_Log::instance()->log(
303
-                            EEM_Change_Log::type_gateway,
304
-                            array('IPN data' => $_req_data),
305
-                            $payment
306
-                        );
307
-                        break;
308
-                    } catch (EventEspresso\core\exceptions\IpnException $e) {
309
-                        EEM_Change_Log::instance()->log(
310
-                            EEM_Change_Log::type_gateway,
311
-                            array(
312
-                                'message'     => 'IPN Exception: ' . $e->getMessage(),
313
-                                'current_url' => EEH_URL::current_url(),
314
-                                'payment'     => $e->getPaymentProperties(),
315
-                                'IPN_data'    => $e->getIpnData(),
316
-                            ),
317
-                            $obj_for_log
318
-                        );
319
-                        return $e->getPayment();
320
-                    } catch (EE_Error $e) {
321
-                        // that's fine- it apparently couldn't handle the IPN
322
-                    }
323
-                }
324
-            }
325
-            // EEM_Payment_Log::instance()->log("got to 7",$transaction,$payment_method);
326
-            if ($payment instanceof EE_Payment) {
327
-                $payment->save();
328
-                //  update the TXN
329
-                $this->update_txn_based_on_payment(
330
-                    $transaction,
331
-                    $payment,
332
-                    $update_txn,
333
-                    $separate_IPN_request
334
-                );
335
-            } else {
336
-                // we couldn't find the payment for this IPN... let's try and log at least SOMETHING
337
-                if ($payment_method) {
338
-                    EEM_Change_Log::instance()->log(
339
-                        EEM_Change_Log::type_gateway,
340
-                        array('IPN data' => $_req_data),
341
-                        $payment_method
342
-                    );
343
-                } elseif ($transaction) {
344
-                    EEM_Change_Log::instance()->log(
345
-                        EEM_Change_Log::type_gateway,
346
-                        array('IPN data' => $_req_data),
347
-                        $transaction
348
-                    );
349
-                }
350
-            }
351
-            return $payment;
352
-        } catch (EE_Error $e) {
353
-            do_action(
354
-                'AHEE__log',
355
-                __FILE__,
356
-                __FUNCTION__,
357
-                sprintf(
358
-                    esc_html__(
359
-                        'Error occurred while receiving IPN. Transaction: %1$s, req data: %2$s. The error was "%3$s"',
360
-                        'event_espresso'
361
-                    ),
362
-                    print_r($transaction, true),
363
-                    print_r($_req_data, true),
364
-                    $e->getMessage()
365
-                )
366
-            );
367
-            throw $e;
368
-        }
369
-    }
370
-
371
-
372
-    /**
373
-     * Removes any non-printable illegal characters from the input,
374
-     * which might cause a raucous when trying to insert into the database
375
-     *
376
-     * @param  array $request_data
377
-     * @return array
378
-     */
379
-    protected function _remove_unusable_characters_from_array(array $request_data)
380
-    {
381
-        $return_data = array();
382
-        foreach ($request_data as $key => $value) {
383
-            $return_data[ $this->_remove_unusable_characters($key) ] = $this->_remove_unusable_characters(
384
-                $value
385
-            );
386
-        }
387
-        return $return_data;
388
-    }
389
-
390
-
391
-    /**
392
-     * Removes any non-printable illegal characters from the input,
393
-     * which might cause a raucous when trying to insert into the database
394
-     *
395
-     * @param string $request_data
396
-     * @return string
397
-     */
398
-    protected function _remove_unusable_characters($request_data)
399
-    {
400
-        return preg_replace('/[^[:print:]]/', '', $request_data);
401
-    }
402
-
403
-
404
-    /**
405
-     * Should be called just before displaying the payment attempt results to the user,
406
-     * when the payment attempt has finished. Some payment methods may have special
407
-     * logic to perform here. For example, if process_payment() happens on a special request
408
-     * and then the user is redirected to a page that displays the payment's status, this
409
-     * should be called while loading the page that displays the payment's status. If the user is
410
-     * sent to an offsite payment provider, this should be called upon returning from that offsite payment
411
-     * provider.
412
-     *
413
-     * @param EE_Transaction|int $transaction
414
-     * @param bool               $update_txn whether or not to call
415
-     *                                       EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
416
-     * @return EE_Payment
417
-     * @throws EE_Error
418
-     * @throws InvalidArgumentException
419
-     * @throws ReflectionException
420
-     * @throws RuntimeException
421
-     * @throws InvalidDataTypeException
422
-     * @throws InvalidInterfaceException
423
-     * @deprecated 4.6.24 method is no longer used. Instead it is up to client code, like SPCO,
424
-     *                                       to call handle_ipn() for offsite gateways that don't receive separate IPNs
425
-     */
426
-    public function finalize_payment_for($transaction, $update_txn = true)
427
-    {
428
-        /** @var $transaction EE_Transaction */
429
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
430
-        $last_payment_method = $transaction->payment_method();
431
-        if ($last_payment_method instanceof EE_Payment_Method) {
432
-            $payment = $last_payment_method->type_obj()->finalize_payment_for($transaction);
433
-            $this->update_txn_based_on_payment($transaction, $payment, $update_txn);
434
-            return $payment;
435
-        }
436
-        return null;
437
-    }
438
-
439
-
440
-    /**
441
-     * Processes a direct refund request, saves the payment, and updates the transaction appropriately.
442
-     *
443
-     * @param EE_Payment_Method $payment_method
444
-     * @param EE_Payment        $payment_to_refund
445
-     * @param array             $refund_info
446
-     * @return EE_Payment
447
-     * @throws EE_Error
448
-     * @throws InvalidArgumentException
449
-     * @throws ReflectionException
450
-     * @throws RuntimeException
451
-     * @throws InvalidDataTypeException
452
-     * @throws InvalidInterfaceException
453
-     */
454
-    public function process_refund(
455
-        EE_Payment_Method $payment_method,
456
-        EE_Payment $payment_to_refund,
457
-        array $refund_info = array()
458
-    ) {
459
-        if ($payment_method instanceof EE_Payment_Method && $payment_method->type_obj()->supports_sending_refunds()) {
460
-            $payment_method->type_obj()->process_refund($payment_to_refund, $refund_info);
461
-            $this->update_txn_based_on_payment($payment_to_refund->transaction(), $payment_to_refund);
462
-        }
463
-        return $payment_to_refund;
464
-    }
465
-
466
-
467
-    /**
468
-     * This should be called each time there may have been an update to a
469
-     * payment on a transaction (ie, we asked for a payment to process a
470
-     * payment for a transaction, or we told a payment method about an IPN, or
471
-     * we told a payment method to
472
-     * "finalize_payment_for" (a transaction), or we told a payment method to
473
-     * process a refund. This should handle firing the correct hooks to
474
-     * indicate
475
-     * what exactly happened and updating the transaction appropriately). This
476
-     * could be integrated directly into EE_Transaction upon save, but we want
477
-     * this logic to be separate from 'normal' plain-jane saving and updating
478
-     * of transactions and payments, and to be tied to payment processing.
479
-     * Note: this method DOES NOT save the payment passed into it. It is the responsibility
480
-     * of previous code to decide whether or not to save (because the payment passed into
481
-     * this method might be a temporary, never-to-be-saved payment from an offline gateway,
482
-     * in which case we only want that payment object for some temporary usage during this request,
483
-     * but we don't want it to be saved).
484
-     *
485
-     * @param EE_Transaction|int $transaction
486
-     * @param EE_Payment         $payment
487
-     * @param boolean            $update_txn
488
-     *                        whether or not to call
489
-     *                        EE_Transaction_Processor::
490
-     *                        update_transaction_and_registrations_after_checkout_or_payment()
491
-     *                        (you can save 1 DB query if you know you're going
492
-     *                        to save it later instead)
493
-     * @param bool               $IPN
494
-     *                        if processing IPNs or other similar payment
495
-     *                        related activities that occur in alternate
496
-     *                        requests than the main one that is processing the
497
-     *                        TXN, then set this to true to check whether the
498
-     *                        TXN is locked before updating
499
-     * @throws EE_Error
500
-     * @throws InvalidArgumentException
501
-     * @throws ReflectionException
502
-     * @throws RuntimeException
503
-     * @throws InvalidDataTypeException
504
-     * @throws InvalidInterfaceException
505
-     */
506
-    public function update_txn_based_on_payment($transaction, $payment, $update_txn = true, $IPN = false)
507
-    {
508
-        $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful';
509
-        /** @type EE_Transaction $transaction */
510
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
511
-        // can we freely update the TXN at this moment?
512
-        if ($IPN && $transaction->is_locked()) {
513
-            // don't update the transaction at this exact moment
514
-            // because the TXN is active in another request
515
-            EE_Cron_Tasks::schedule_update_transaction_with_payment(
516
-                time(),
517
-                $transaction->ID(),
518
-                $payment->ID()
519
-            );
520
-        } else {
521
-            // verify payment and that it has been saved
522
-            if ($payment instanceof EE_Payment && $payment->ID()) {
523
-                if (
524
-                    $payment->payment_method() instanceof EE_Payment_Method
525
-                    && $payment->payment_method()->type_obj() instanceof EE_PMT_Base
526
-                ) {
527
-                    $payment->payment_method()->type_obj()->update_txn_based_on_payment($payment);
528
-                    // update TXN registrations with payment info
529
-                    $this->process_registration_payments($transaction, $payment);
530
-                }
531
-                $do_action = $payment->just_approved()
532
-                    ? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful'
533
-                    : $do_action;
534
-            } else {
535
-                // send out notifications
536
-                add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
537
-                $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made';
538
-            }
539
-            if ($payment instanceof EE_Payment && $payment->status() !== EEM_Payment::status_id_failed) {
540
-                /** @type EE_Transaction_Payments $transaction_payments */
541
-                $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
542
-                // set new value for total paid
543
-                $transaction_payments->calculate_total_payments_and_update_status($transaction);
544
-                // call EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() ???
545
-                if ($update_txn) {
546
-                    $this->_post_payment_processing($transaction, $payment, $IPN);
547
-                }
548
-            }
549
-            // granular hook for others to use.
550
-            do_action($do_action, $transaction, $payment);
551
-            do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action');
552
-            // global hook for others to use.
553
-            do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment);
554
-        }
555
-    }
556
-
557
-
558
-    /**
559
-     * update registrations REG_paid field after successful payment and link registrations with payment
560
-     *
561
-     * @param EE_Transaction    $transaction
562
-     * @param EE_Payment        $payment
563
-     * @param EE_Registration[] $registrations
564
-     * @throws EE_Error
565
-     * @throws InvalidArgumentException
566
-     * @throws RuntimeException
567
-     * @throws InvalidDataTypeException
568
-     * @throws InvalidInterfaceException
569
-     */
570
-    public function process_registration_payments(
571
-        EE_Transaction $transaction,
572
-        EE_Payment $payment,
573
-        array $registrations = array()
574
-    ) {
575
-        // only process if payment was successful
576
-        if ($payment->status() !== EEM_Payment::status_id_approved) {
577
-            return;
578
-        }
579
-        // EEM_Registration::instance()->show_next_x_db_queries();
580
-        if (empty($registrations)) {
581
-            // find registrations with monies owing that can receive a payment
582
-            $registrations = $transaction->registrations(
583
-                array(
584
-                    array(
585
-                        // only these reg statuses can receive payments
586
-                        'STS_ID'           => array('IN', EEM_Registration::reg_statuses_that_allow_payment()),
587
-                        'REG_final_price'  => array('!=', 0),
588
-                        'REG_final_price*' => array('!=', 'REG_paid', true),
589
-                    ),
590
-                )
591
-            );
592
-        }
593
-        // still nothing ??!??
594
-        if (empty($registrations)) {
595
-            return;
596
-        }
597
-        // todo: break out the following logic into a separate strategy class
598
-        // todo: named something like "Sequential_Reg_Payment_Strategy"
599
-        // todo: which would apply payments using the capitalist "first come first paid" approach
600
-        // todo: then have another strategy class like "Distributed_Reg_Payment_Strategy"
601
-        // todo: which would be the socialist "everybody gets a piece of pie" approach,
602
-        // todo: which would be better for deposits, where you want a bit of the payment applied to each registration
603
-        $refund = $payment->is_a_refund();
604
-        // how much is available to apply to registrations?
605
-        $available_payment_amount = abs($payment->amount());
606
-        foreach ($registrations as $registration) {
607
-            if ($registration instanceof EE_Registration) {
608
-                // nothing left?
609
-                if ($available_payment_amount <= 0) {
610
-                    break;
611
-                }
612
-                if ($refund) {
613
-                    $available_payment_amount = $this->process_registration_refund(
614
-                        $registration,
615
-                        $payment,
616
-                        $available_payment_amount
617
-                    );
618
-                } else {
619
-                    $available_payment_amount = $this->process_registration_payment(
620
-                        $registration,
621
-                        $payment,
622
-                        $available_payment_amount
623
-                    );
624
-                }
625
-            }
626
-        }
627
-        if (
628
-            $available_payment_amount > 0
629
-            && apply_filters(
630
-                'FHEE__EE_Payment_Processor__process_registration_payments__display_notifications',
631
-                false
632
-            )
633
-        ) {
634
-            EE_Error::add_attention(
635
-                sprintf(
636
-                    esc_html__(
637
-                        'A remainder of %1$s exists after applying this payment to Registration(s) %2$s.%3$sPlease verify that the original payment amount of %4$s is correct. If so, you should edit this payment and select at least one additional registration in the "Registrations to Apply Payment to" section, so that the remainder of this payment can be applied to the additional registration(s).',
638
-                        'event_espresso'
639
-                    ),
640
-                    EEH_Template::format_currency($available_payment_amount),
641
-                    implode(', ', array_keys($registrations)),
642
-                    '<br/>',
643
-                    EEH_Template::format_currency($payment->amount())
644
-                ),
645
-                __FILE__,
646
-                __FUNCTION__,
647
-                __LINE__
648
-            );
649
-        }
650
-    }
651
-
652
-
653
-    /**
654
-     * update registration REG_paid field after successful payment and link registration with payment
655
-     *
656
-     * @param EE_Registration $registration
657
-     * @param EE_Payment      $payment
658
-     * @param float           $available_payment_amount
659
-     * @return float
660
-     * @throws EE_Error
661
-     * @throws InvalidArgumentException
662
-     * @throws RuntimeException
663
-     * @throws InvalidDataTypeException
664
-     * @throws InvalidInterfaceException
665
-     */
666
-    public function process_registration_payment(
667
-        EE_Registration $registration,
668
-        EE_Payment $payment,
669
-        $available_payment_amount = 0.00
670
-    ) {
671
-        $owing = $registration->final_price() - $registration->paid();
672
-        if ($owing > 0) {
673
-            // don't allow payment amount to exceed the available payment amount, OR the amount owing
674
-            $payment_amount = min($available_payment_amount, $owing);
675
-            // update $available_payment_amount
676
-            $available_payment_amount -= $payment_amount;
677
-            // calculate and set new REG_paid
678
-            $registration->set_paid($registration->paid() + $payment_amount);
679
-            // now save it
680
-            $this->_apply_registration_payment($registration, $payment, $payment_amount);
681
-        }
682
-        return $available_payment_amount;
683
-    }
684
-
685
-
686
-    /**
687
-     * update registration REG_paid field after successful payment and link registration with payment
688
-     *
689
-     * @param EE_Registration $registration
690
-     * @param EE_Payment      $payment
691
-     * @param float           $payment_amount
692
-     * @return void
693
-     * @throws EE_Error
694
-     * @throws InvalidArgumentException
695
-     * @throws InvalidDataTypeException
696
-     * @throws InvalidInterfaceException
697
-     */
698
-    protected function _apply_registration_payment(
699
-        EE_Registration $registration,
700
-        EE_Payment $payment,
701
-        $payment_amount = 0.00
702
-    ) {
703
-        // find any existing reg payment records for this registration and payment
704
-        $existing_reg_payment = EEM_Registration_Payment::instance()->get_one(
705
-            array(array('REG_ID' => $registration->ID(), 'PAY_ID' => $payment->ID()))
706
-        );
707
-        // if existing registration payment exists
708
-        if ($existing_reg_payment instanceof EE_Registration_Payment) {
709
-            // then update that record
710
-            $existing_reg_payment->set_amount($payment_amount);
711
-            $existing_reg_payment->save();
712
-        } else {
713
-            // or add new relation between registration and payment and set amount
714
-            $registration->_add_relation_to(
715
-                $payment,
716
-                'Payment',
717
-                array('RPY_amount' => $payment_amount)
718
-            );
719
-            // make it stick
720
-            $registration->save();
721
-        }
722
-    }
723
-
724
-
725
-    /**
726
-     * update registration REG_paid field after refund and link registration with payment
727
-     *
728
-     * @param EE_Registration $registration
729
-     * @param EE_Payment      $payment
730
-     * @param float           $available_refund_amount - IMPORTANT !!! SEND AVAILABLE REFUND AMOUNT AS A POSITIVE NUMBER
731
-     * @return float
732
-     * @throws EE_Error
733
-     * @throws InvalidArgumentException
734
-     * @throws RuntimeException
735
-     * @throws InvalidDataTypeException
736
-     * @throws InvalidInterfaceException
737
-     */
738
-    public function process_registration_refund(
739
-        EE_Registration $registration,
740
-        EE_Payment $payment,
741
-        $available_refund_amount = 0.00
742
-    ) {
743
-        // EEH_Debug_Tools::printr( $payment->amount(), '$payment->amount()', __FILE__, __LINE__ );
744
-        if ($registration->paid() > 0) {
745
-            // ensure $available_refund_amount is NOT negative
746
-            $available_refund_amount = (float) abs($available_refund_amount);
747
-            // don't allow refund amount to exceed the available payment amount, OR the amount paid
748
-            $refund_amount = min($available_refund_amount, (float) $registration->paid());
749
-            // update $available_payment_amount
750
-            $available_refund_amount -= $refund_amount;
751
-            // calculate and set new REG_paid
752
-            $registration->set_paid($registration->paid() - $refund_amount);
753
-            // convert payment amount back to a negative value for storage in the db
754
-            $refund_amount = (float) abs($refund_amount) * -1;
755
-            // now save it
756
-            $this->_apply_registration_payment($registration, $payment, $refund_amount);
757
-        }
758
-        return $available_refund_amount;
759
-    }
760
-
761
-
762
-    /**
763
-     * Process payments and transaction after payment process completed.
764
-     * ultimately this will send the TXN and payment details off so that notifications can be sent out.
765
-     * if this request happens to be processing an IPN,
766
-     * then we will also set the Payment Options Reg Step to completed,
767
-     * and attempt to completely finalize the TXN if all of the other Reg Steps are completed as well.
768
-     *
769
-     * @param EE_Transaction $transaction
770
-     * @param EE_Payment     $payment
771
-     * @param bool           $IPN
772
-     * @throws EE_Error
773
-     * @throws InvalidArgumentException
774
-     * @throws ReflectionException
775
-     * @throws RuntimeException
776
-     * @throws InvalidDataTypeException
777
-     * @throws InvalidInterfaceException
778
-     */
779
-    protected function _post_payment_processing(EE_Transaction $transaction, EE_Payment $payment, $IPN = false)
780
-    {
781
-        /** @type EE_Transaction_Processor $transaction_processor */
782
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
783
-        // is the Payment Options Reg Step completed ?
784
-        $payment_options_step_completed = $transaction->reg_step_completed('payment_options');
785
-        // if the Payment Options Reg Step is completed...
786
-        $revisit = $payment_options_step_completed === true;
787
-        // then this is kinda sorta a revisit with regards to payments at least
788
-        $transaction_processor->set_revisit($revisit);
789
-        // if this is an IPN, let's consider the Payment Options Reg Step completed if not already
790
-        if (
791
-            $IPN
792
-            && $payment_options_step_completed !== true
793
-            && ($payment->is_approved() || $payment->is_pending())
794
-        ) {
795
-            $payment_options_step_completed = $transaction->set_reg_step_completed(
796
-                'payment_options'
797
-            );
798
-        }
799
-        // maybe update status, but don't save transaction just yet
800
-        $transaction->update_status_based_on_total_paid(false);
801
-        // check if 'finalize_registration' step has been completed...
802
-        $finalized = $transaction->reg_step_completed('finalize_registration');
803
-        //  if this is an IPN and the final step has not been initiated
804
-        if ($IPN && $payment_options_step_completed && $finalized === false) {
805
-            // and if it hasn't already been set as being started...
806
-            $finalized = $transaction->set_reg_step_initiated('finalize_registration');
807
-        }
808
-        $transaction->save();
809
-        // because the above will return false if the final step was not fully completed, we need to check again...
810
-        if ($IPN && $finalized !== false) {
811
-            // and if we are all good to go, then send out notifications
812
-            add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
813
-            // ok, now process the transaction according to the payment
814
-            $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
815
-                $transaction,
816
-                $payment
817
-            );
818
-        }
819
-        // DEBUG LOG
820
-        $payment_method = $payment->payment_method();
821
-        if ($payment_method instanceof EE_Payment_Method) {
822
-            $payment_method_type_obj = $payment_method->type_obj();
823
-            if ($payment_method_type_obj instanceof EE_PMT_Base) {
824
-                $gateway = $payment_method_type_obj->get_gateway();
825
-                if ($gateway instanceof EE_Gateway) {
826
-                    $gateway->log(
827
-                        array(
828
-                            'message'               => (string) esc_html__('Post Payment Transaction Details', 'event_espresso'),
829
-                            'transaction'           => $transaction->model_field_array(),
830
-                            'finalized'             => $finalized,
831
-                            'IPN'                   => $IPN,
832
-                            'deliver_notifications' => has_filter(
833
-                                'FHEE__EED_Messages___maybe_registration__deliver_notifications'
834
-                            ),
835
-                        ),
836
-                        $payment
837
-                    );
838
-                }
839
-            }
840
-        }
841
-    }
842
-
843
-
844
-    /**
845
-     * Force posts to PayPal to use TLS v1.2. See:
846
-     * https://core.trac.wordpress.org/ticket/36320
847
-     * https://core.trac.wordpress.org/ticket/34924#comment:15
848
-     * https://www.paypal-knowledge.com/infocenter/index?page=content&widgetview=true&id=FAQ1914&viewlocale=en_US
849
-     * This will affect PayPal standard, pro, express, and Payflow.
850
-     *
851
-     * @param $handle
852
-     * @param $r
853
-     * @param $url
854
-     */
855
-    public static function _curl_requests_to_paypal_use_tls($handle, $r, $url)
856
-    {
857
-        if (strpos($url, 'https://') !== false && strpos($url, '.paypal.com') !== false) {
858
-            // Use the value of the constant CURL_SSLVERSION_TLSv1 = 1
859
-            // instead of the constant because it might not be defined
860
-            curl_setopt($handle, CURLOPT_SSLVERSION, 6);
861
-        }
862
-    }
21
+	/**
22
+	 * @var EE_Payment_Processor $_instance
23
+	 * @access    private
24
+	 */
25
+	private static $_instance;
26
+
27
+
28
+	/**
29
+	 * @singleton method used to instantiate class object
30
+	 * @access    public
31
+	 * @return EE_Payment_Processor instance
32
+	 */
33
+	public static function instance()
34
+	{
35
+		// check if class object is instantiated
36
+		if (! self::$_instance instanceof EE_Payment_Processor) {
37
+			self::$_instance = new self();
38
+		}
39
+		return self::$_instance;
40
+	}
41
+
42
+
43
+	/**
44
+	 * @return EE_Payment_Processor
45
+	 */
46
+	public static function reset()
47
+	{
48
+		self::$_instance = null;
49
+		return self::instance();
50
+	}
51
+
52
+
53
+	/**
54
+	 *private constructor to prevent direct creation
55
+	 *
56
+	 * @Constructor
57
+	 * @access private
58
+	 */
59
+	private function __construct()
60
+	{
61
+		do_action('AHEE__EE_Payment_Processor__construct');
62
+		add_action('http_api_curl', array($this, '_curl_requests_to_paypal_use_tls'), 10, 3);
63
+	}
64
+
65
+
66
+	/**
67
+	 * Using the selected gateway, processes the payment for that transaction, and updates the transaction
68
+	 * appropriately. Saves the payment that is generated
69
+	 *
70
+	 * @param EE_Payment_Method    $payment_method
71
+	 * @param EE_Transaction       $transaction
72
+	 * @param float|null                $amount       if only part of the transaction is to be paid for, how much.
73
+	 *                                           Leave null if payment is for the full amount owing
74
+	 * @param EE_Billing_Info_Form|null $billing_form (or probably null, if it's an offline or offsite payment method).
75
+	 *                                           Receive_form_submission() should have
76
+	 *                                           already been called on the billing form
77
+	 *                                           (ie, its inputs should have their normalized values set).
78
+	 * @param string|null          $return_url   string used mostly by offsite gateways to specify
79
+	 *                                           where to go AFTER the offsite gateway
80
+	 * @param string               $method       like 'CART', indicates who the client who called this was
81
+	 * @param bool                 $by_admin     TRUE if payment is being attempted from the admin
82
+	 * @param bool                 $update_txn   whether or not to call
83
+	 *                                           EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
84
+	 * @param string               $cancel_url   URL to return to if off-site payments are cancelled
85
+	 * @return EE_Payment
86
+	 * @throws EE_Error
87
+	 * @throws InvalidArgumentException
88
+	 * @throws ReflectionException
89
+	 * @throws RuntimeException
90
+	 * @throws InvalidDataTypeException
91
+	 * @throws InvalidInterfaceException
92
+	 */
93
+	public function process_payment(
94
+		EE_Payment_Method $payment_method,
95
+		EE_Transaction $transaction,
96
+		?float $amount = null,
97
+		?EE_Billing_Info_Form $billing_form = null,
98
+		?string $return_url = null,
99
+		string $method = 'CART',
100
+		bool $by_admin = false,
101
+		bool $update_txn = true,
102
+		string $cancel_url = ''
103
+	): ?EE_Payment {
104
+		if ((float) $amount < 0) {
105
+			throw new EE_Error(
106
+				sprintf(
107
+					esc_html__(
108
+						'Attempting to make a payment for a negative amount of %1$d for transaction %2$d. That should be a refund',
109
+						'event_espresso'
110
+					),
111
+					$amount,
112
+					$transaction->ID()
113
+				)
114
+			);
115
+		}
116
+		// verify payment method
117
+		$payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
118
+			$payment_method,
119
+			true
120
+		);
121
+		// verify transaction
122
+		EEM_Transaction::instance()->ensure_is_obj($transaction);
123
+		$transaction->set_payment_method_ID($payment_method->ID());
124
+		// verify payment method type
125
+		if ($payment_method->type_obj() instanceof EE_PMT_Base) {
126
+			$payment = $payment_method->type_obj()->process_payment(
127
+				$transaction,
128
+				min($amount, $transaction->remaining()), // make sure we don't overcharge
129
+				$billing_form,
130
+				$return_url,
131
+				add_query_arg(array('ee_cancel_payment' => true), $cancel_url),
132
+				$method,
133
+				$by_admin
134
+			);
135
+			// check if payment method uses an off-site gateway
136
+			if ($payment_method->type_obj()->payment_occurs() !== EE_PMT_Base::offsite) {
137
+				// don't process payments for off-site gateways yet because no payment has occurred yet
138
+				$this->update_txn_based_on_payment($transaction, $payment, $update_txn);
139
+			}
140
+			return $payment;
141
+		}
142
+		EE_Error::add_error(
143
+			sprintf(
144
+				esc_html__(
145
+					'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
146
+					'event_espresso'
147
+				),
148
+				'<br/>',
149
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
150
+			),
151
+			__FILE__,
152
+			__FUNCTION__,
153
+			__LINE__
154
+		);
155
+		return null;
156
+	}
157
+
158
+
159
+	/**
160
+	 * @param EE_Transaction|int $transaction
161
+	 * @param EE_Payment_Method  $payment_method
162
+	 * @return string
163
+	 * @throws EE_Error
164
+	 * @throws InvalidArgumentException
165
+	 * @throws InvalidDataTypeException
166
+	 * @throws InvalidInterfaceException
167
+	 */
168
+	public function get_ipn_url_for_payment_method($transaction, $payment_method)
169
+	{
170
+		/** @type \EE_Transaction $transaction */
171
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
172
+		$primary_reg = $transaction->primary_registration();
173
+		if (! $primary_reg instanceof EE_Registration) {
174
+			throw new EE_Error(
175
+				sprintf(
176
+					esc_html__(
177
+						'Cannot get IPN URL for transaction with ID %d because it has no primary registration',
178
+						'event_espresso'
179
+					),
180
+					$transaction->ID()
181
+				)
182
+			);
183
+		}
184
+		$payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
185
+			$payment_method,
186
+			true
187
+		);
188
+		$url = add_query_arg(
189
+			array(
190
+				'e_reg_url_link'    => $primary_reg->reg_url_link(),
191
+				'ee_payment_method' => $payment_method->slug(),
192
+			),
193
+			EE_Registry::instance()->CFG->core->txn_page_url()
194
+		);
195
+		return $url;
196
+	}
197
+
198
+
199
+	/**
200
+	 * Process the IPN. Firstly, we'll hope we put the standard args into the IPN URL so
201
+	 * we can easily find what registration the IPN is for and what payment method.
202
+	 * However, if not, we'll give all payment methods a chance to claim it and process it.
203
+	 * If a payment is found for the IPN info, it is saved.
204
+	 *
205
+	 * @param array              $_req_data            form post data
206
+	 * @param EE_Transaction|int $transaction          optional (or a transactions id)
207
+	 * @param EE_Payment_Method  $payment_method       (or a slug or id of one)
208
+	 * @param boolean            $update_txn           whether or not to call
209
+	 *                                                 EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
210
+	 * @param bool               $separate_IPN_request whether the IPN uses a separate request (true, like PayPal)
211
+	 *                                                 or is processed manually (false, like Authorize.net)
212
+	 * @throws EE_Error
213
+	 * @throws Exception
214
+	 * @return EE_Payment
215
+	 * @throws \RuntimeException
216
+	 * @throws \ReflectionException
217
+	 * @throws \InvalidArgumentException
218
+	 * @throws InvalidInterfaceException
219
+	 * @throws InvalidDataTypeException
220
+	 */
221
+	public function process_ipn(
222
+		$_req_data,
223
+		$transaction = null,
224
+		$payment_method = null,
225
+		$update_txn = true,
226
+		$separate_IPN_request = true
227
+	) {
228
+		EE_Registry::instance()->load_model('Change_Log');
229
+		$_req_data = $this->_remove_unusable_characters_from_array((array) $_req_data);
230
+		EE_Processor_Base::set_IPN($separate_IPN_request);
231
+		$obj_for_log = null;
232
+		if ($transaction instanceof EE_Transaction) {
233
+			$obj_for_log = $transaction;
234
+			if ($payment_method instanceof EE_Payment_Method) {
235
+				$obj_for_log = EEM_Payment::instance()->get_one(
236
+					array(
237
+						array('TXN_ID' => $transaction->ID(), 'PMD_ID' => $payment_method->ID()),
238
+						'order_by' => array('PAY_timestamp' => 'desc'),
239
+					)
240
+				);
241
+			}
242
+		} elseif ($payment_method instanceof EE_Payment) {
243
+			$obj_for_log = $payment_method;
244
+		}
245
+		$log = EEM_Change_Log::instance()->log(
246
+			EEM_Change_Log::type_gateway,
247
+			array('IPN data received' => $_req_data),
248
+			$obj_for_log
249
+		);
250
+		try {
251
+			/**
252
+			 * @var EE_Payment $payment
253
+			 */
254
+			$payment = null;
255
+			if ($transaction && $payment_method) {
256
+				/** @type EE_Transaction $transaction */
257
+				$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
258
+				/** @type EE_Payment_Method $payment_method */
259
+				$payment_method = EEM_Payment_Method::instance()->ensure_is_obj($payment_method);
260
+				if ($payment_method->type_obj() instanceof EE_PMT_Base) {
261
+					try {
262
+						$payment = $payment_method->type_obj()->handle_ipn($_req_data, $transaction);
263
+						$log->set_object($payment);
264
+					} catch (EventEspresso\core\exceptions\IpnException $e) {
265
+						EEM_Change_Log::instance()->log(
266
+							EEM_Change_Log::type_gateway,
267
+							array(
268
+								'message'     => 'IPN Exception: ' . $e->getMessage(),
269
+								'current_url' => EEH_URL::current_url(),
270
+								'payment'     => $e->getPaymentProperties(),
271
+								'IPN_data'    => $e->getIpnData(),
272
+							),
273
+							$obj_for_log
274
+						);
275
+						return $e->getPayment();
276
+					}
277
+				} else {
278
+					// not a payment
279
+					EE_Error::add_error(
280
+						sprintf(
281
+							esc_html__(
282
+								'A valid payment method could not be determined due to a technical issue.%sPlease refresh your browser and try again or contact %s for assistance.',
283
+								'event_espresso'
284
+							),
285
+							'<br/>',
286
+							EE_Registry::instance()->CFG->organization->get_pretty('email')
287
+						),
288
+						__FILE__,
289
+						__FUNCTION__,
290
+						__LINE__
291
+					);
292
+				}
293
+			} else {
294
+				// that's actually pretty ok. The IPN just wasn't able
295
+				// to identify which transaction or payment method this was for
296
+				// give all active payment methods a chance to claim it
297
+				$active_payment_methods = EEM_Payment_Method::instance()->get_all_active();
298
+				foreach ($active_payment_methods as $active_payment_method) {
299
+					try {
300
+						$payment = $active_payment_method->type_obj()->handle_unclaimed_ipn($_req_data);
301
+						$payment_method = $active_payment_method;
302
+						EEM_Change_Log::instance()->log(
303
+							EEM_Change_Log::type_gateway,
304
+							array('IPN data' => $_req_data),
305
+							$payment
306
+						);
307
+						break;
308
+					} catch (EventEspresso\core\exceptions\IpnException $e) {
309
+						EEM_Change_Log::instance()->log(
310
+							EEM_Change_Log::type_gateway,
311
+							array(
312
+								'message'     => 'IPN Exception: ' . $e->getMessage(),
313
+								'current_url' => EEH_URL::current_url(),
314
+								'payment'     => $e->getPaymentProperties(),
315
+								'IPN_data'    => $e->getIpnData(),
316
+							),
317
+							$obj_for_log
318
+						);
319
+						return $e->getPayment();
320
+					} catch (EE_Error $e) {
321
+						// that's fine- it apparently couldn't handle the IPN
322
+					}
323
+				}
324
+			}
325
+			// EEM_Payment_Log::instance()->log("got to 7",$transaction,$payment_method);
326
+			if ($payment instanceof EE_Payment) {
327
+				$payment->save();
328
+				//  update the TXN
329
+				$this->update_txn_based_on_payment(
330
+					$transaction,
331
+					$payment,
332
+					$update_txn,
333
+					$separate_IPN_request
334
+				);
335
+			} else {
336
+				// we couldn't find the payment for this IPN... let's try and log at least SOMETHING
337
+				if ($payment_method) {
338
+					EEM_Change_Log::instance()->log(
339
+						EEM_Change_Log::type_gateway,
340
+						array('IPN data' => $_req_data),
341
+						$payment_method
342
+					);
343
+				} elseif ($transaction) {
344
+					EEM_Change_Log::instance()->log(
345
+						EEM_Change_Log::type_gateway,
346
+						array('IPN data' => $_req_data),
347
+						$transaction
348
+					);
349
+				}
350
+			}
351
+			return $payment;
352
+		} catch (EE_Error $e) {
353
+			do_action(
354
+				'AHEE__log',
355
+				__FILE__,
356
+				__FUNCTION__,
357
+				sprintf(
358
+					esc_html__(
359
+						'Error occurred while receiving IPN. Transaction: %1$s, req data: %2$s. The error was "%3$s"',
360
+						'event_espresso'
361
+					),
362
+					print_r($transaction, true),
363
+					print_r($_req_data, true),
364
+					$e->getMessage()
365
+				)
366
+			);
367
+			throw $e;
368
+		}
369
+	}
370
+
371
+
372
+	/**
373
+	 * Removes any non-printable illegal characters from the input,
374
+	 * which might cause a raucous when trying to insert into the database
375
+	 *
376
+	 * @param  array $request_data
377
+	 * @return array
378
+	 */
379
+	protected function _remove_unusable_characters_from_array(array $request_data)
380
+	{
381
+		$return_data = array();
382
+		foreach ($request_data as $key => $value) {
383
+			$return_data[ $this->_remove_unusable_characters($key) ] = $this->_remove_unusable_characters(
384
+				$value
385
+			);
386
+		}
387
+		return $return_data;
388
+	}
389
+
390
+
391
+	/**
392
+	 * Removes any non-printable illegal characters from the input,
393
+	 * which might cause a raucous when trying to insert into the database
394
+	 *
395
+	 * @param string $request_data
396
+	 * @return string
397
+	 */
398
+	protected function _remove_unusable_characters($request_data)
399
+	{
400
+		return preg_replace('/[^[:print:]]/', '', $request_data);
401
+	}
402
+
403
+
404
+	/**
405
+	 * Should be called just before displaying the payment attempt results to the user,
406
+	 * when the payment attempt has finished. Some payment methods may have special
407
+	 * logic to perform here. For example, if process_payment() happens on a special request
408
+	 * and then the user is redirected to a page that displays the payment's status, this
409
+	 * should be called while loading the page that displays the payment's status. If the user is
410
+	 * sent to an offsite payment provider, this should be called upon returning from that offsite payment
411
+	 * provider.
412
+	 *
413
+	 * @param EE_Transaction|int $transaction
414
+	 * @param bool               $update_txn whether or not to call
415
+	 *                                       EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
416
+	 * @return EE_Payment
417
+	 * @throws EE_Error
418
+	 * @throws InvalidArgumentException
419
+	 * @throws ReflectionException
420
+	 * @throws RuntimeException
421
+	 * @throws InvalidDataTypeException
422
+	 * @throws InvalidInterfaceException
423
+	 * @deprecated 4.6.24 method is no longer used. Instead it is up to client code, like SPCO,
424
+	 *                                       to call handle_ipn() for offsite gateways that don't receive separate IPNs
425
+	 */
426
+	public function finalize_payment_for($transaction, $update_txn = true)
427
+	{
428
+		/** @var $transaction EE_Transaction */
429
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
430
+		$last_payment_method = $transaction->payment_method();
431
+		if ($last_payment_method instanceof EE_Payment_Method) {
432
+			$payment = $last_payment_method->type_obj()->finalize_payment_for($transaction);
433
+			$this->update_txn_based_on_payment($transaction, $payment, $update_txn);
434
+			return $payment;
435
+		}
436
+		return null;
437
+	}
438
+
439
+
440
+	/**
441
+	 * Processes a direct refund request, saves the payment, and updates the transaction appropriately.
442
+	 *
443
+	 * @param EE_Payment_Method $payment_method
444
+	 * @param EE_Payment        $payment_to_refund
445
+	 * @param array             $refund_info
446
+	 * @return EE_Payment
447
+	 * @throws EE_Error
448
+	 * @throws InvalidArgumentException
449
+	 * @throws ReflectionException
450
+	 * @throws RuntimeException
451
+	 * @throws InvalidDataTypeException
452
+	 * @throws InvalidInterfaceException
453
+	 */
454
+	public function process_refund(
455
+		EE_Payment_Method $payment_method,
456
+		EE_Payment $payment_to_refund,
457
+		array $refund_info = array()
458
+	) {
459
+		if ($payment_method instanceof EE_Payment_Method && $payment_method->type_obj()->supports_sending_refunds()) {
460
+			$payment_method->type_obj()->process_refund($payment_to_refund, $refund_info);
461
+			$this->update_txn_based_on_payment($payment_to_refund->transaction(), $payment_to_refund);
462
+		}
463
+		return $payment_to_refund;
464
+	}
465
+
466
+
467
+	/**
468
+	 * This should be called each time there may have been an update to a
469
+	 * payment on a transaction (ie, we asked for a payment to process a
470
+	 * payment for a transaction, or we told a payment method about an IPN, or
471
+	 * we told a payment method to
472
+	 * "finalize_payment_for" (a transaction), or we told a payment method to
473
+	 * process a refund. This should handle firing the correct hooks to
474
+	 * indicate
475
+	 * what exactly happened and updating the transaction appropriately). This
476
+	 * could be integrated directly into EE_Transaction upon save, but we want
477
+	 * this logic to be separate from 'normal' plain-jane saving and updating
478
+	 * of transactions and payments, and to be tied to payment processing.
479
+	 * Note: this method DOES NOT save the payment passed into it. It is the responsibility
480
+	 * of previous code to decide whether or not to save (because the payment passed into
481
+	 * this method might be a temporary, never-to-be-saved payment from an offline gateway,
482
+	 * in which case we only want that payment object for some temporary usage during this request,
483
+	 * but we don't want it to be saved).
484
+	 *
485
+	 * @param EE_Transaction|int $transaction
486
+	 * @param EE_Payment         $payment
487
+	 * @param boolean            $update_txn
488
+	 *                        whether or not to call
489
+	 *                        EE_Transaction_Processor::
490
+	 *                        update_transaction_and_registrations_after_checkout_or_payment()
491
+	 *                        (you can save 1 DB query if you know you're going
492
+	 *                        to save it later instead)
493
+	 * @param bool               $IPN
494
+	 *                        if processing IPNs or other similar payment
495
+	 *                        related activities that occur in alternate
496
+	 *                        requests than the main one that is processing the
497
+	 *                        TXN, then set this to true to check whether the
498
+	 *                        TXN is locked before updating
499
+	 * @throws EE_Error
500
+	 * @throws InvalidArgumentException
501
+	 * @throws ReflectionException
502
+	 * @throws RuntimeException
503
+	 * @throws InvalidDataTypeException
504
+	 * @throws InvalidInterfaceException
505
+	 */
506
+	public function update_txn_based_on_payment($transaction, $payment, $update_txn = true, $IPN = false)
507
+	{
508
+		$do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful';
509
+		/** @type EE_Transaction $transaction */
510
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
511
+		// can we freely update the TXN at this moment?
512
+		if ($IPN && $transaction->is_locked()) {
513
+			// don't update the transaction at this exact moment
514
+			// because the TXN is active in another request
515
+			EE_Cron_Tasks::schedule_update_transaction_with_payment(
516
+				time(),
517
+				$transaction->ID(),
518
+				$payment->ID()
519
+			);
520
+		} else {
521
+			// verify payment and that it has been saved
522
+			if ($payment instanceof EE_Payment && $payment->ID()) {
523
+				if (
524
+					$payment->payment_method() instanceof EE_Payment_Method
525
+					&& $payment->payment_method()->type_obj() instanceof EE_PMT_Base
526
+				) {
527
+					$payment->payment_method()->type_obj()->update_txn_based_on_payment($payment);
528
+					// update TXN registrations with payment info
529
+					$this->process_registration_payments($transaction, $payment);
530
+				}
531
+				$do_action = $payment->just_approved()
532
+					? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful'
533
+					: $do_action;
534
+			} else {
535
+				// send out notifications
536
+				add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
537
+				$do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made';
538
+			}
539
+			if ($payment instanceof EE_Payment && $payment->status() !== EEM_Payment::status_id_failed) {
540
+				/** @type EE_Transaction_Payments $transaction_payments */
541
+				$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
542
+				// set new value for total paid
543
+				$transaction_payments->calculate_total_payments_and_update_status($transaction);
544
+				// call EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() ???
545
+				if ($update_txn) {
546
+					$this->_post_payment_processing($transaction, $payment, $IPN);
547
+				}
548
+			}
549
+			// granular hook for others to use.
550
+			do_action($do_action, $transaction, $payment);
551
+			do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action');
552
+			// global hook for others to use.
553
+			do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment);
554
+		}
555
+	}
556
+
557
+
558
+	/**
559
+	 * update registrations REG_paid field after successful payment and link registrations with payment
560
+	 *
561
+	 * @param EE_Transaction    $transaction
562
+	 * @param EE_Payment        $payment
563
+	 * @param EE_Registration[] $registrations
564
+	 * @throws EE_Error
565
+	 * @throws InvalidArgumentException
566
+	 * @throws RuntimeException
567
+	 * @throws InvalidDataTypeException
568
+	 * @throws InvalidInterfaceException
569
+	 */
570
+	public function process_registration_payments(
571
+		EE_Transaction $transaction,
572
+		EE_Payment $payment,
573
+		array $registrations = array()
574
+	) {
575
+		// only process if payment was successful
576
+		if ($payment->status() !== EEM_Payment::status_id_approved) {
577
+			return;
578
+		}
579
+		// EEM_Registration::instance()->show_next_x_db_queries();
580
+		if (empty($registrations)) {
581
+			// find registrations with monies owing that can receive a payment
582
+			$registrations = $transaction->registrations(
583
+				array(
584
+					array(
585
+						// only these reg statuses can receive payments
586
+						'STS_ID'           => array('IN', EEM_Registration::reg_statuses_that_allow_payment()),
587
+						'REG_final_price'  => array('!=', 0),
588
+						'REG_final_price*' => array('!=', 'REG_paid', true),
589
+					),
590
+				)
591
+			);
592
+		}
593
+		// still nothing ??!??
594
+		if (empty($registrations)) {
595
+			return;
596
+		}
597
+		// todo: break out the following logic into a separate strategy class
598
+		// todo: named something like "Sequential_Reg_Payment_Strategy"
599
+		// todo: which would apply payments using the capitalist "first come first paid" approach
600
+		// todo: then have another strategy class like "Distributed_Reg_Payment_Strategy"
601
+		// todo: which would be the socialist "everybody gets a piece of pie" approach,
602
+		// todo: which would be better for deposits, where you want a bit of the payment applied to each registration
603
+		$refund = $payment->is_a_refund();
604
+		// how much is available to apply to registrations?
605
+		$available_payment_amount = abs($payment->amount());
606
+		foreach ($registrations as $registration) {
607
+			if ($registration instanceof EE_Registration) {
608
+				// nothing left?
609
+				if ($available_payment_amount <= 0) {
610
+					break;
611
+				}
612
+				if ($refund) {
613
+					$available_payment_amount = $this->process_registration_refund(
614
+						$registration,
615
+						$payment,
616
+						$available_payment_amount
617
+					);
618
+				} else {
619
+					$available_payment_amount = $this->process_registration_payment(
620
+						$registration,
621
+						$payment,
622
+						$available_payment_amount
623
+					);
624
+				}
625
+			}
626
+		}
627
+		if (
628
+			$available_payment_amount > 0
629
+			&& apply_filters(
630
+				'FHEE__EE_Payment_Processor__process_registration_payments__display_notifications',
631
+				false
632
+			)
633
+		) {
634
+			EE_Error::add_attention(
635
+				sprintf(
636
+					esc_html__(
637
+						'A remainder of %1$s exists after applying this payment to Registration(s) %2$s.%3$sPlease verify that the original payment amount of %4$s is correct. If so, you should edit this payment and select at least one additional registration in the "Registrations to Apply Payment to" section, so that the remainder of this payment can be applied to the additional registration(s).',
638
+						'event_espresso'
639
+					),
640
+					EEH_Template::format_currency($available_payment_amount),
641
+					implode(', ', array_keys($registrations)),
642
+					'<br/>',
643
+					EEH_Template::format_currency($payment->amount())
644
+				),
645
+				__FILE__,
646
+				__FUNCTION__,
647
+				__LINE__
648
+			);
649
+		}
650
+	}
651
+
652
+
653
+	/**
654
+	 * update registration REG_paid field after successful payment and link registration with payment
655
+	 *
656
+	 * @param EE_Registration $registration
657
+	 * @param EE_Payment      $payment
658
+	 * @param float           $available_payment_amount
659
+	 * @return float
660
+	 * @throws EE_Error
661
+	 * @throws InvalidArgumentException
662
+	 * @throws RuntimeException
663
+	 * @throws InvalidDataTypeException
664
+	 * @throws InvalidInterfaceException
665
+	 */
666
+	public function process_registration_payment(
667
+		EE_Registration $registration,
668
+		EE_Payment $payment,
669
+		$available_payment_amount = 0.00
670
+	) {
671
+		$owing = $registration->final_price() - $registration->paid();
672
+		if ($owing > 0) {
673
+			// don't allow payment amount to exceed the available payment amount, OR the amount owing
674
+			$payment_amount = min($available_payment_amount, $owing);
675
+			// update $available_payment_amount
676
+			$available_payment_amount -= $payment_amount;
677
+			// calculate and set new REG_paid
678
+			$registration->set_paid($registration->paid() + $payment_amount);
679
+			// now save it
680
+			$this->_apply_registration_payment($registration, $payment, $payment_amount);
681
+		}
682
+		return $available_payment_amount;
683
+	}
684
+
685
+
686
+	/**
687
+	 * update registration REG_paid field after successful payment and link registration with payment
688
+	 *
689
+	 * @param EE_Registration $registration
690
+	 * @param EE_Payment      $payment
691
+	 * @param float           $payment_amount
692
+	 * @return void
693
+	 * @throws EE_Error
694
+	 * @throws InvalidArgumentException
695
+	 * @throws InvalidDataTypeException
696
+	 * @throws InvalidInterfaceException
697
+	 */
698
+	protected function _apply_registration_payment(
699
+		EE_Registration $registration,
700
+		EE_Payment $payment,
701
+		$payment_amount = 0.00
702
+	) {
703
+		// find any existing reg payment records for this registration and payment
704
+		$existing_reg_payment = EEM_Registration_Payment::instance()->get_one(
705
+			array(array('REG_ID' => $registration->ID(), 'PAY_ID' => $payment->ID()))
706
+		);
707
+		// if existing registration payment exists
708
+		if ($existing_reg_payment instanceof EE_Registration_Payment) {
709
+			// then update that record
710
+			$existing_reg_payment->set_amount($payment_amount);
711
+			$existing_reg_payment->save();
712
+		} else {
713
+			// or add new relation between registration and payment and set amount
714
+			$registration->_add_relation_to(
715
+				$payment,
716
+				'Payment',
717
+				array('RPY_amount' => $payment_amount)
718
+			);
719
+			// make it stick
720
+			$registration->save();
721
+		}
722
+	}
723
+
724
+
725
+	/**
726
+	 * update registration REG_paid field after refund and link registration with payment
727
+	 *
728
+	 * @param EE_Registration $registration
729
+	 * @param EE_Payment      $payment
730
+	 * @param float           $available_refund_amount - IMPORTANT !!! SEND AVAILABLE REFUND AMOUNT AS A POSITIVE NUMBER
731
+	 * @return float
732
+	 * @throws EE_Error
733
+	 * @throws InvalidArgumentException
734
+	 * @throws RuntimeException
735
+	 * @throws InvalidDataTypeException
736
+	 * @throws InvalidInterfaceException
737
+	 */
738
+	public function process_registration_refund(
739
+		EE_Registration $registration,
740
+		EE_Payment $payment,
741
+		$available_refund_amount = 0.00
742
+	) {
743
+		// EEH_Debug_Tools::printr( $payment->amount(), '$payment->amount()', __FILE__, __LINE__ );
744
+		if ($registration->paid() > 0) {
745
+			// ensure $available_refund_amount is NOT negative
746
+			$available_refund_amount = (float) abs($available_refund_amount);
747
+			// don't allow refund amount to exceed the available payment amount, OR the amount paid
748
+			$refund_amount = min($available_refund_amount, (float) $registration->paid());
749
+			// update $available_payment_amount
750
+			$available_refund_amount -= $refund_amount;
751
+			// calculate and set new REG_paid
752
+			$registration->set_paid($registration->paid() - $refund_amount);
753
+			// convert payment amount back to a negative value for storage in the db
754
+			$refund_amount = (float) abs($refund_amount) * -1;
755
+			// now save it
756
+			$this->_apply_registration_payment($registration, $payment, $refund_amount);
757
+		}
758
+		return $available_refund_amount;
759
+	}
760
+
761
+
762
+	/**
763
+	 * Process payments and transaction after payment process completed.
764
+	 * ultimately this will send the TXN and payment details off so that notifications can be sent out.
765
+	 * if this request happens to be processing an IPN,
766
+	 * then we will also set the Payment Options Reg Step to completed,
767
+	 * and attempt to completely finalize the TXN if all of the other Reg Steps are completed as well.
768
+	 *
769
+	 * @param EE_Transaction $transaction
770
+	 * @param EE_Payment     $payment
771
+	 * @param bool           $IPN
772
+	 * @throws EE_Error
773
+	 * @throws InvalidArgumentException
774
+	 * @throws ReflectionException
775
+	 * @throws RuntimeException
776
+	 * @throws InvalidDataTypeException
777
+	 * @throws InvalidInterfaceException
778
+	 */
779
+	protected function _post_payment_processing(EE_Transaction $transaction, EE_Payment $payment, $IPN = false)
780
+	{
781
+		/** @type EE_Transaction_Processor $transaction_processor */
782
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
783
+		// is the Payment Options Reg Step completed ?
784
+		$payment_options_step_completed = $transaction->reg_step_completed('payment_options');
785
+		// if the Payment Options Reg Step is completed...
786
+		$revisit = $payment_options_step_completed === true;
787
+		// then this is kinda sorta a revisit with regards to payments at least
788
+		$transaction_processor->set_revisit($revisit);
789
+		// if this is an IPN, let's consider the Payment Options Reg Step completed if not already
790
+		if (
791
+			$IPN
792
+			&& $payment_options_step_completed !== true
793
+			&& ($payment->is_approved() || $payment->is_pending())
794
+		) {
795
+			$payment_options_step_completed = $transaction->set_reg_step_completed(
796
+				'payment_options'
797
+			);
798
+		}
799
+		// maybe update status, but don't save transaction just yet
800
+		$transaction->update_status_based_on_total_paid(false);
801
+		// check if 'finalize_registration' step has been completed...
802
+		$finalized = $transaction->reg_step_completed('finalize_registration');
803
+		//  if this is an IPN and the final step has not been initiated
804
+		if ($IPN && $payment_options_step_completed && $finalized === false) {
805
+			// and if it hasn't already been set as being started...
806
+			$finalized = $transaction->set_reg_step_initiated('finalize_registration');
807
+		}
808
+		$transaction->save();
809
+		// because the above will return false if the final step was not fully completed, we need to check again...
810
+		if ($IPN && $finalized !== false) {
811
+			// and if we are all good to go, then send out notifications
812
+			add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
813
+			// ok, now process the transaction according to the payment
814
+			$transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
815
+				$transaction,
816
+				$payment
817
+			);
818
+		}
819
+		// DEBUG LOG
820
+		$payment_method = $payment->payment_method();
821
+		if ($payment_method instanceof EE_Payment_Method) {
822
+			$payment_method_type_obj = $payment_method->type_obj();
823
+			if ($payment_method_type_obj instanceof EE_PMT_Base) {
824
+				$gateway = $payment_method_type_obj->get_gateway();
825
+				if ($gateway instanceof EE_Gateway) {
826
+					$gateway->log(
827
+						array(
828
+							'message'               => (string) esc_html__('Post Payment Transaction Details', 'event_espresso'),
829
+							'transaction'           => $transaction->model_field_array(),
830
+							'finalized'             => $finalized,
831
+							'IPN'                   => $IPN,
832
+							'deliver_notifications' => has_filter(
833
+								'FHEE__EED_Messages___maybe_registration__deliver_notifications'
834
+							),
835
+						),
836
+						$payment
837
+					);
838
+				}
839
+			}
840
+		}
841
+	}
842
+
843
+
844
+	/**
845
+	 * Force posts to PayPal to use TLS v1.2. See:
846
+	 * https://core.trac.wordpress.org/ticket/36320
847
+	 * https://core.trac.wordpress.org/ticket/34924#comment:15
848
+	 * https://www.paypal-knowledge.com/infocenter/index?page=content&widgetview=true&id=FAQ1914&viewlocale=en_US
849
+	 * This will affect PayPal standard, pro, express, and Payflow.
850
+	 *
851
+	 * @param $handle
852
+	 * @param $r
853
+	 * @param $url
854
+	 */
855
+	public static function _curl_requests_to_paypal_use_tls($handle, $r, $url)
856
+	{
857
+		if (strpos($url, 'https://') !== false && strpos($url, '.paypal.com') !== false) {
858
+			// Use the value of the constant CURL_SSLVERSION_TLSv1 = 1
859
+			// instead of the constant because it might not be defined
860
+			curl_setopt($handle, CURLOPT_SSLVERSION, 6);
861
+		}
862
+	}
863 863
 }
Please login to merge, or discard this patch.
core/libraries/payment_methods/EE_PMT_Base.lib.php 1 patch
Indentation   +830 added lines, -830 removed lines patch added patch discarded remove patch
@@ -20,834 +20,834 @@
 block discarded – undo
20 20
 abstract class EE_PMT_Base
21 21
 {
22 22
 
23
-    const onsite  = 'on-site';
24
-
25
-    const offsite = 'off-site';
26
-
27
-    const offline = 'off-line';
28
-
29
-    /**
30
-     * @var EE_Payment_Method
31
-     */
32
-    protected $_pm_instance = null;
33
-
34
-    /**
35
-     * @var boolean
36
-     */
37
-    protected $_requires_https = false;
38
-
39
-    /**
40
-     * @var boolean
41
-     */
42
-    protected $_has_billing_form;
43
-
44
-    /**
45
-     * @var EE_Gateway
46
-     */
47
-    protected $_gateway = null;
48
-
49
-    /**
50
-     * @var EE_Payment_Method_Form
51
-     */
52
-    protected $_settings_form = null;
53
-
54
-    /**
55
-     * @var EE_Form_Section_Proper
56
-     */
57
-    protected $_billing_form = null;
58
-
59
-    /**
60
-     * @var boolean
61
-     */
62
-    protected $_cache_billing_form = true;
63
-
64
-    /**
65
-     * String of the absolute path to the folder containing this file, with a trailing slash.
66
-     * eg '/public_html/wp-site/wp-content/plugins/event-espresso/payment_methods/Invoice/'
67
-     *
68
-     * @var string
69
-     */
70
-    protected $_file_folder = null;
71
-
72
-    /**
73
-     * String to the absolute URL to this file (useful for getting its web-accessible resources
74
-     * like images, js, or css)
75
-     *
76
-     * @var string
77
-     */
78
-    protected $_file_url = null;
79
-
80
-    /**
81
-     * Pretty name for the payment method
82
-     *
83
-     * @var string
84
-     */
85
-    protected $_pretty_name = null;
86
-
87
-    /**
88
-     *
89
-     * @var string
90
-     */
91
-    protected $_default_button_url = null;
92
-
93
-    /**
94
-     *
95
-     * @var string
96
-     */
97
-    protected $_default_description = null;
98
-
99
-
100
-    /**
101
-     * @param EE_Payment_Method|null $pm_instance
102
-     * @throws ReflectionException
103
-     * @throws EE_Error
104
-     */
105
-    public function __construct(EE_Payment_Method $pm_instance = null)
106
-    {
107
-        if ($pm_instance instanceof EE_Payment_Method) {
108
-            $this->set_instance($pm_instance);
109
-        }
110
-        if ($this->_gateway) {
111
-            $this->_gateway->set_payment_model(EEM_Payment::instance());
112
-            $this->_gateway->set_payment_log(EEM_Change_Log::instance());
113
-            $this->_gateway->set_template_helper(new EEH_Template());
114
-            $this->_gateway->set_line_item_helper(new EEH_Line_Item());
115
-            $this->_gateway->set_money_helper(new EEH_Money());
116
-            $this->_gateway->set_gateway_data_formatter(new GatewayDataFormatter());
117
-            $this->_gateway->set_unsupported_character_remover(new AsciiOnly());
118
-            do_action('AHEE__EE_PMT_Base___construct__done_initializing_gateway_class', $this, $this->_gateway);
119
-        }
120
-        if (! isset($this->_has_billing_form)) {
121
-            // by default, On Site gateways have a billing form
122
-            if ($this->payment_occurs() == EE_PMT_Base::onsite) {
123
-                $this->set_has_billing_form(true);
124
-            } else {
125
-                $this->set_has_billing_form(false);
126
-            }
127
-        }
128
-
129
-        if (! $this->_pretty_name) {
130
-            throw new EE_Error(
131
-                esc_html__(
132
-                    'You must set the pretty name for the Payment Method Type in the constructor (_pretty_name), and please make it internationalized',
133
-                    'event_espresso'
134
-                )
135
-            );
136
-        }
137
-        // if the child didn't specify a default button, use the credit card one
138
-        if ($this->_default_button_url === null) {
139
-            $this->_default_button_url = EE_PLUGIN_DIR_URL . 'payment_methods/pay-by-credit-card.png';
140
-        }
141
-    }
142
-
143
-
144
-    /**
145
-     * @param boolean $has_billing_form
146
-     */
147
-    public function set_has_billing_form(bool $has_billing_form)
148
-    {
149
-        $this->_has_billing_form = filter_var($has_billing_form, FILTER_VALIDATE_BOOLEAN);
150
-    }
151
-
152
-
153
-    /**
154
-     * sets the file_folder property
155
-     */
156
-    protected function _set_file_folder()
157
-    {
158
-        $reflector          = new ReflectionClass(get_class($this));
159
-        $fn                 = $reflector->getFileName();
160
-        $this->_file_folder = dirname($fn) . '/';
161
-    }
162
-
163
-
164
-    /**
165
-     * sets the file URL with a trailing slash for this PMT
166
-     */
167
-    protected function _set_file_url()
168
-    {
169
-        $plugins_dir_fixed = str_replace('\\', '/', WP_PLUGIN_DIR);
170
-        $file_folder_fixed = str_replace('\\', '/', $this->file_folder());
171
-        $file_path         = str_replace($plugins_dir_fixed, WP_PLUGIN_URL, $file_folder_fixed);
172
-        $this->_file_url   = set_url_scheme($file_path);
173
-    }
174
-
175
-
176
-    /**
177
-     * Gets the default description on all payment methods of this type
178
-     *
179
-     * @return string
180
-     */
181
-    public function default_description()
182
-    {
183
-        return $this->_default_description;
184
-    }
185
-
186
-
187
-    /**
188
-     * Returns the folder containing the PMT child class, with a trailing slash
189
-     *
190
-     * @return string
191
-     */
192
-    public function file_folder()
193
-    {
194
-        if (! $this->_file_folder) {
195
-            $this->_set_file_folder();
196
-        }
197
-        return $this->_file_folder;
198
-    }
199
-
200
-
201
-    /**
202
-     * @return string
203
-     */
204
-    public function file_url()
205
-    {
206
-        if (! $this->_file_url) {
207
-            $this->_set_file_url();
208
-        }
209
-        return $this->_file_url;
210
-    }
211
-
212
-
213
-    /**
214
-     * Sets the payment method instance this payment method type is for.
215
-     * Its important teh payment method instance is set before
216
-     *
217
-     * @param EE_Payment_Method $payment_method_instance
218
-     * @throws EE_Error
219
-     */
220
-    public function set_instance(EE_Payment_Method $payment_method_instance)
221
-    {
222
-        $this->_pm_instance = $payment_method_instance;
223
-        // if they have already requested the settings form, make sure its
224
-        // data matches this model object
225
-        if ($this->_settings_form) {
226
-            $this->settings_form()->populate_model_obj($payment_method_instance);
227
-        }
228
-        if ($this->_gateway instanceof EE_Gateway) {
229
-            $this->_gateway->set_settings($payment_method_instance->settings_array());
230
-        }
231
-    }
232
-
233
-
234
-    /**
235
-     * Gets teh form for displaying to admins where they setup the payment method
236
-     *
237
-     * @return EE_Payment_Method_Form
238
-     * @throws EE_Error
239
-     */
240
-    public function settings_form()
241
-    {
242
-        if (! $this->_settings_form) {
243
-            $this->_settings_form = $this->generate_new_settings_form();
244
-            $this->_settings_form->set_payment_method_type($this);
245
-            // if we have already assigned a model object to this pmt, make
246
-            // sure its reflected in teh form we just generated
247
-            if ($this->_pm_instance) {
248
-                $this->_settings_form->populate_model_obj($this->_pm_instance);
249
-            }
250
-        }
251
-        return $this->_settings_form;
252
-    }
253
-
254
-
255
-    /**
256
-     * Gets the form for all the settings related to this payment method type
257
-     *
258
-     * @return EE_Payment_Method_Form
259
-     */
260
-    abstract public function generate_new_settings_form();
261
-
262
-
263
-    /**
264
-     * Sets the form for settings. This may be useful if we have already received
265
-     * a form submission and have form data it in, and want to use it anytime we're showing
266
-     * this payment method type's settings form later in the request
267
-     *
268
-     * @param EE_Payment_Method_Form $form
269
-     */
270
-    public function set_settings_form(EE_Payment_Method_Form $form)
271
-    {
272
-        $this->_settings_form = $form;
273
-    }
274
-
275
-
276
-    /**
277
-     * @return boolean
278
-     */
279
-    public function has_billing_form()
280
-    {
281
-        return $this->_has_billing_form;
282
-    }
283
-
284
-
285
-    /**
286
-     * Gets the form for displaying to attendees where they can enter their billing info
287
-     * which will be sent to teh gateway (can be null)
288
-     *
289
-     * @param EE_Transaction|null $transaction
290
-     * @param array               $extra_args
291
-     * @return EE_Billing_Attendee_Info_Form|EE_Billing_Info_Form|null
292
-     * @throws EE_Error
293
-     * @throws ReflectionException
294
-     */
295
-    public function billing_form(EE_Transaction $transaction = null, array $extra_args = [])
296
-    {
297
-        // has billing form already been regenerated ? or overwrite cache?
298
-        if (! $this->_billing_form instanceof EE_Billing_Info_Form || ! $this->_cache_billing_form) {
299
-            $this->_billing_form = $this->generate_new_billing_form($transaction, $extra_args);
300
-        }
301
-        // if we know who the attendee is, and this is a billing form
302
-        // that uses attendee info, populate it
303
-        if (
304
-            apply_filters(
305
-                'FHEE__populate_billing_form_fields_from_attendee',
306
-                (
307
-                    $this->_billing_form instanceof EE_Billing_Attendee_Info_Form
308
-                    && $transaction instanceof EE_Transaction
309
-                    && $transaction->primary_registration() instanceof EE_Registration
310
-                    && $transaction->primary_registration()->attendee() instanceof EE_Attendee
311
-                ),
312
-                $this->_billing_form,
313
-                $transaction
314
-            )
315
-        ) {
316
-            $this->_billing_form->populate_from_attendee($transaction->primary_registration()->attendee());
317
-        }
318
-        return $this->_billing_form;
319
-    }
320
-
321
-
322
-    /**
323
-     * Creates the billing form for this payment method type
324
-     *
325
-     * @param EE_Transaction|null $transaction
326
-     * @return EE_Billing_Info_Form|null
327
-     */
328
-    abstract public function generate_new_billing_form(EE_Transaction $transaction = null);
329
-
330
-
331
-    /**
332
-     * apply_billing_form_debug_settings
333
-     * applies debug data to the form
334
-     *
335
-     * @param EE_Billing_Info_Form $billing_form
336
-     * @return EE_Billing_Info_Form|null
337
-     */
338
-    public function apply_billing_form_debug_settings(EE_Billing_Info_Form $billing_form)
339
-    {
340
-        return $billing_form;
341
-    }
342
-
343
-
344
-    /**
345
-     * Sets the billing form for this payment method type. You may want to use this
346
-     * if you have form
347
-     *
348
-     * @param EE_Payment_Method $form
349
-     */
350
-    public function set_billing_form(EE_Payment_Method $form)
351
-    {
352
-        $this->_billing_form = $form;
353
-    }
354
-
355
-
356
-    /**
357
-     * Returns whether or not this payment method requires HTTPS to be used
358
-     *
359
-     * @return boolean
360
-     */
361
-    public function requires_https()
362
-    {
363
-        return $this->_requires_https;
364
-    }
365
-
366
-
367
-    /**
368
-     *
369
-     * @param EE_Transaction            $transaction
370
-     * @param float|null                $amount
371
-     * @param EE_Billing_Info_Form|null $billing_info
372
-     * @param string|null               $return_url
373
-     * @param string                    $fail_url
374
-     * @param string                    $method
375
-     * @param bool                      $by_admin
376
-     * @return EE_Payment
377
-     * @throws EE_Error
378
-     * @throws ReflectionException
379
-     */
380
-    public function process_payment(
381
-        EE_Transaction $transaction,
382
-        float $amount = null,
383
-        ?EE_Billing_Info_Form $billing_info = null,
384
-        string $return_url = null,
385
-        string $fail_url = '',
386
-        string $method = 'CART',
387
-        bool $by_admin = false
388
-    ) {
389
-        // @todo: add surcharge for the payment method, if any
390
-        if ($this->_gateway) {
391
-            // there is a gateway, so we're going to make a payment object
392
-            // but wait! do they already have a payment in progress that we thought was failed?
393
-            $duplicate_properties = [
394
-                'STS_ID'               => EEM_Payment::status_id_failed,
395
-                'TXN_ID'               => $transaction->ID(),
396
-                'PMD_ID'               => $this->_pm_instance->ID(),
397
-                'PAY_source'           => $method,
398
-                'PAY_amount'           => $amount !== null ? $amount : $transaction->remaining(),
399
-                'PAY_gateway_response' => null,
400
-            ];
401
-            $payment              = EEM_Payment::instance()->get_one([$duplicate_properties]);
402
-            // if we didn't already have a payment in progress for the same thing,
403
-            // then we actually want to make a new payment
404
-            if (! $payment instanceof EE_Payment) {
405
-                $payment = EE_Payment::new_instance(
406
-                    array_merge(
407
-                        $duplicate_properties,
408
-                        [
409
-                            'PAY_timestamp'       => time(),
410
-                            'PAY_txn_id_chq_nmbr' => null,
411
-                            'PAY_po_number'       => null,
412
-                            'PAY_extra_accntng'   => null,
413
-                            'PAY_details'         => null,
414
-                        ]
415
-                    )
416
-                );
417
-            }
418
-            // make sure the payment has been saved to show we started it, and so it has an ID should the gateway try to log it
419
-            $payment->save();
420
-            $billing_values = $this->_get_billing_values_from_form($billing_info);
421
-
422
-            //  Offsite Gateway
423
-            if ($this->_gateway instanceof EE_Offsite_Gateway) {
424
-                $payment = $this->_gateway->set_redirection_info(
425
-                    $payment,
426
-                    $billing_values,
427
-                    $return_url,
428
-                    EE_Config::instance()->core->txn_page_url(
429
-                        [
430
-                            'e_reg_url_link'    => $transaction->primary_registration()->reg_url_link(),
431
-                            'ee_payment_method' => $this->_pm_instance->slug(),
432
-                        ]
433
-                    ),
434
-                    $fail_url
435
-                );
436
-                $payment->save();
437
-                //  Onsite Gateway
438
-            } elseif ($this->_gateway instanceof EE_Onsite_Gateway) {
439
-                $payment = $this->_gateway->do_direct_payment($payment, $billing_values);
440
-                $payment->save();
441
-            } else {
442
-                throw new EE_Error(
443
-                    sprintf(
444
-                        esc_html__(
445
-                            'Gateway for payment method type "%s" is "%s", not a subclass of either EE_Offsite_Gateway or EE_Onsite_Gateway, or null (to indicate NO gateway)',
446
-                            'event_espresso'
447
-                        ),
448
-                        get_class($this),
449
-                        gettype($this->_gateway)
450
-                    )
451
-                );
452
-            }
453
-        } else {
454
-            // no gateway provided
455
-            // there is no payment. Must be an offline gateway
456
-            // create a payment object anyways, but dont save it
457
-            $payment = EE_Payment::new_instance(
458
-                [
459
-                    'STS_ID'        => EEM_Payment::status_id_pending,
460
-                    'TXN_ID'        => $transaction->ID(),
461
-                    'PMD_ID'        => $transaction->payment_method_ID(),
462
-                    'PAY_amount'    => 0.00,
463
-                    'PAY_timestamp' => time(),
464
-                ]
465
-            );
466
-        }
467
-
468
-        // if there is billing info, clean it and save it now
469
-        if ($billing_info instanceof EE_Billing_Attendee_Info_Form) {
470
-            $this->_save_billing_info_to_attendee($billing_info, $transaction);
471
-        }
472
-
473
-        return $payment;
474
-    }
475
-
476
-
477
-    /**
478
-     * Gets the values we want to pass onto the gateway. Normally these
479
-     * are just the 'pretty' values, but there may be times the data may need
480
-     * a  little massaging. Proper subsections will become arrays of inputs
481
-     *
482
-     * @param EE_Billing_Info_Form|null $billing_form
483
-     * @return array
484
-     * @throws EE_Error
485
-     */
486
-    protected function _get_billing_values_from_form($billing_form)
487
-    {
488
-        return $billing_form instanceof EE_Form_Section_Proper
489
-            ? $billing_form->input_pretty_values(true)
490
-            : [];
491
-    }
492
-
493
-
494
-    /**
495
-     * Handles an instant payment notification when the transaction is known (by default).
496
-     *
497
-     * @param array          $req_data
498
-     * @param EE_Transaction $transaction
499
-     * @return EE_Payment
500
-     * @throws EE_Error
501
-     * @throws ReflectionException
502
-     */
503
-    public function handle_ipn(array $req_data, EE_Transaction $transaction)
504
-    {
505
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
506
-        if (! $this->_gateway instanceof EE_Offsite_Gateway) {
507
-            throw new EE_Error(
508
-                sprintf(
509
-                    esc_html__("Could not handle IPN because '%s' is not an offsite gateway", "event_espresso"),
510
-                    print_r($this->_gateway, true)
511
-                )
512
-            );
513
-        }
514
-        return $this->_gateway->handle_payment_update($req_data, $transaction);
515
-    }
516
-
517
-
518
-    /**
519
-     * Saves the billing info onto the attendee of the primary registrant on this transaction, and
520
-     * cleans it first.
521
-     *
522
-     * @param EE_Billing_Attendee_Info_Form $billing_form
523
-     * @param EE_Transaction|null           $transaction
524
-     * @return boolean success
525
-     * @throws EE_Error
526
-     * @throws ReflectionException
527
-     */
528
-    protected function _save_billing_info_to_attendee(
529
-        EE_Billing_Attendee_Info_Form $billing_form,
530
-        EE_Transaction $transaction
531
-    ) {
532
-        if (! $transaction instanceof EE_Transaction) {
533
-            EE_Error::add_error(
534
-                esc_html__("Cannot save billing info because no transaction was specified", "event_espresso"),
535
-                __FILE__,
536
-                __FUNCTION__,
537
-                __LINE__
538
-            );
539
-            return false;
540
-        }
541
-        $primary_reg = $transaction->primary_registration();
542
-        if (! $primary_reg) {
543
-            EE_Error::add_error(
544
-                esc_html__(
545
-                    "Cannot save billing info because the transaction has no primary registration",
546
-                    "event_espresso"
547
-                ),
548
-                __FILE__,
549
-                __FUNCTION__,
550
-                __LINE__
551
-            );
552
-            return false;
553
-        }
554
-        $attendee = $primary_reg->attendee();
555
-        if (! $attendee) {
556
-            EE_Error::add_error(
557
-                esc_html__(
558
-                    "Cannot save billing info because the transaction's primary registration has no attendee!",
559
-                    "event_espresso"
560
-                ),
561
-                __FILE__,
562
-                __FUNCTION__,
563
-                __LINE__
564
-            );
565
-            return false;
566
-        }
567
-        return $attendee->save_and_clean_billing_info_for_payment_method($billing_form, $transaction->payment_method());
568
-    }
569
-
570
-
571
-    /**
572
-     * Gets the payment this IPN is for. Children may often want to
573
-     * override this to inspect the request
574
-     *
575
-     * @param EE_Transaction $transaction
576
-     * @param array          $req_data
577
-     * @return EE_Payment
578
-     * @throws EE_Error
579
-     * @throws ReflectionException
580
-     */
581
-    protected function find_payment_for_ipn(EE_Transaction $transaction, array $req_data = [])
582
-    {
583
-        return $transaction->last_payment();
584
-    }
585
-
586
-
587
-    /**
588
-     * In case generic code cannot provide the payment processor with a specific payment method
589
-     * and transaction, it will try calling this method on each activate payment method.
590
-     * If the payment method is able to identify the request as being for it, it should fetch
591
-     * the payment its for and return it. If not, it should throw an EE_Error to indicate it cannot
592
-     * handle the IPN
593
-     *
594
-     * @param array $req_data
595
-     * @return EE_Payment only if this payment method can find the info its needs from $req_data
596
-     * and identifies the IPN as being for this payment method (not just fo ra payment method of this type)
597
-     * @throws EE_Error
598
-     */
599
-    public function handle_unclaimed_ipn(array $req_data = [])
600
-    {
601
-        throw new EE_Error(
602
-            sprintf(
603
-                esc_html__("Payment Method '%s' cannot handle unclaimed IPNs", "event_espresso"),
604
-                get_class($this)
605
-            )
606
-        );
607
-    }
608
-
609
-
610
-    /**
611
-     * Logic to be accomplished when the payment attempt is complete.
612
-     * Most payment methods don't need to do anything at this point; but some, like Mijireh, do.
613
-     * (Mijireh is an offsite gateway which doesn't send an IPN. So when the user returns to EE from
614
-     * mijireh, this method needs to be called so the Mijireh PM can ping Mijireh to know the status
615
-     * of the payment). Fed a transaction because it's always assumed to be the last payment that
616
-     * we're dealing with. Returns that last payment (if there is one)
617
-     *
618
-     * @param EE_Transaction $transaction
619
-     * @return EE_Payment
620
-     * @throws EE_Error
621
-     * @throws ReflectionException
622
-     */
623
-    public function finalize_payment_for(EE_Transaction $transaction)
624
-    {
625
-        return $transaction->last_payment();
626
-    }
627
-
628
-
629
-    /**
630
-     * Whether or not this payment method's gateway supports sending refund requests
631
-     *
632
-     * @return boolean
633
-     */
634
-    public function supports_sending_refunds()
635
-    {
636
-        return $this->_gateway instanceof EE_Gateway && $this->_gateway->supports_sending_refunds();
637
-    }
638
-
639
-
640
-    /**
641
-     *
642
-     * @param EE_Payment $payment
643
-     * @param array      $refund_info
644
-     * @return EE_Payment
645
-     * @throws EE_Error
646
-     */
647
-    public function process_refund(EE_Payment $payment, array $refund_info = [])
648
-    {
649
-        if ($this->_gateway instanceof EE_Gateway) {
650
-            return $this->_gateway->do_direct_refund($payment, $refund_info);
651
-        } else {
652
-            throw new EE_Error(
653
-                sprintf(
654
-                    esc_html__('Payment Method Type "%s" does not support sending refund requests', 'event_espresso'),
655
-                    get_class($this)
656
-                )
657
-            );
658
-        }
659
-    }
660
-
661
-
662
-    /**
663
-     * Returns one the class's constants onsite,offsite, or offline, depending on this
664
-     * payment method's gateway.
665
-     *
666
-     * @return string
667
-     * @throws EE_Error
668
-     */
669
-    public function payment_occurs()
670
-    {
671
-        if (! $this->_gateway) {
672
-            return EE_PMT_Base::offline;
673
-        } elseif ($this->_gateway instanceof EE_Onsite_Gateway) {
674
-            return EE_PMT_Base::onsite;
675
-        } elseif ($this->_gateway instanceof EE_Offsite_Gateway) {
676
-            return EE_PMT_Base::offsite;
677
-        } else {
678
-            throw new EE_Error(
679
-                sprintf(
680
-                    esc_html__(
681
-                        "Payment method type '%s's gateway isn't an instance of EE_Onsite_Gateway, EE_Offsite_Gateway, or null. It must be one of those",
682
-                        "event_espresso"
683
-                    ),
684
-                    get_class($this)
685
-                )
686
-            );
687
-        }
688
-    }
689
-
690
-
691
-    /**
692
-     * For adding any html output ab ove the payment overview.
693
-     * Many gateways won't want ot display anything, so this function just returns an empty string.
694
-     * Other gateways may want to override this, such as offline gateways.
695
-     *
696
-     * @param EE_Payment $payment
697
-     * @return string
698
-     */
699
-    public function payment_overview_content(EE_Payment $payment)
700
-    {
701
-        return EEH_Template::display_template(
702
-            EE_LIBRARIES . 'payment_methods/templates/payment_details_content.template.php',
703
-            ['payment_method' => $this->_pm_instance, 'payment' => $payment],
704
-            true
705
-        );
706
-    }
707
-
708
-
709
-    /**
710
-     * @return array where keys are the help tab name,
711
-     * values are: array {
712
-     * @type string $title         i18n name for the help tab
713
-     * @type string $filename      name of the file located in ./help_tabs/ (ie, in a folder next to this file)
714
-     * @type array  $template_args any arguments you want passed to the template file while rendering.
715
-     *                Keys will be variable names and values with be their values.
716
-     */
717
-    public function help_tabs_config()
718
-    {
719
-        return [];
720
-    }
721
-
722
-
723
-    /**
724
-     * The system name for this PMT (eg AIM, Paypal_Pro, Invoice... what gets put into
725
-     * the payment method's table's PMT_type column)
726
-     *
727
-     * @return string
728
-     */
729
-    public function system_name()
730
-    {
731
-        $classname = get_class($this);
732
-        return str_replace("EE_PMT_", '', $classname);
733
-    }
734
-
735
-
736
-    /**
737
-     * A pretty i18n version of the PMT name. Often the same as the "pretty_name", but you can change it by overriding
738
-     * this method.
739
-     *
740
-     * @return string
741
-     */
742
-    public function defaultFrontendName()
743
-    {
744
-        return $this->pretty_name();
745
-    }
746
-
747
-
748
-    /**
749
-     * A pretty i18n version of the PMT name
750
-     *
751
-     * @return string
752
-     */
753
-    public function pretty_name()
754
-    {
755
-        return $this->_pretty_name;
756
-    }
757
-
758
-
759
-    /**
760
-     * Gets the default absolute URL to the payment method type's button
761
-     *
762
-     * @return string
763
-     */
764
-    public function default_button_url()
765
-    {
766
-        return $this->_default_button_url;
767
-    }
768
-
769
-
770
-    /**
771
-     * Gets the gateway used by this payment method (if any)
772
-     *
773
-     * @return EE_Gateway
774
-     */
775
-    public function get_gateway()
776
-    {
777
-        return $this->_gateway;
778
-    }
779
-
780
-
781
-    /**
782
-     * @return string html for the link to a help tab
783
-     */
784
-    public function get_help_tab_link()
785
-    {
786
-        return EEH_Template::get_help_tab_link(
787
-            $this->get_help_tab_name(),
788
-            'espresso_payment_settings',
789
-            'default'
790
-        );
791
-    }
792
-
793
-
794
-    /**
795
-     * Returns the name of the help tab for this PMT
796
-     *
797
-     * @return string
798
-     */
799
-    public function get_help_tab_name()
800
-    {
801
-        return 'ee_' . strtolower($this->system_name()) . '_help_tab';
802
-    }
803
-
804
-
805
-    /**
806
-     * The name of the wp capability that should be associated with the usage of
807
-     * this PMT by an admin
808
-     *
809
-     * @return string
810
-     */
811
-    public function cap_name()
812
-    {
813
-        return 'ee_payment_method_' . strtolower($this->system_name());
814
-    }
815
-
816
-
817
-    /**
818
-     * Called by client code to tell the gateway that if it wants to change
819
-     * the transaction or line items or registrations related to teh payment it already
820
-     * processed (we think, but possibly not) that now's the time to do it.
821
-     * It is expected that gateways will store any info they need for this on the PAY_details,
822
-     * or maybe an extra meta value
823
-     *
824
-     * @param EE_Payment $payment
825
-     * @return void
826
-     */
827
-    public function update_txn_based_on_payment(EE_Payment $payment)
828
-    {
829
-        if ($this->_gateway instanceof EE_Gateway) {
830
-            $this->_gateway->update_txn_based_on_payment($payment);
831
-        }
832
-    }
833
-
834
-
835
-    /**
836
-     * Returns a string of HTML describing this payment method type for an admin,
837
-     * primarily intended for them to read before activating it.
838
-     * The easiest way to set this is to create a folder 'templates' alongside
839
-     * your EE_PMT_{System_Name} file, and in it create a file named "{system_name}_intro.template.php".
840
-     * Eg, if your payment method file is named "EE_PMT_Foo_Bar.pm.php",
841
-     * then you'd create a file named "templates" in the same folder as it, and name the file
842
-     * "foo_bar_intro.template.php", and its content will be returned by this method
843
-     *
844
-     * @return string
845
-     */
846
-    public function introductory_html()
847
-    {
848
-        return EEH_Template::locate_template(
849
-            $this->file_folder() . 'templates/' . strtolower($this->system_name()) . '_intro.template.php',
850
-            ['pmt_obj' => $this, 'pm_instance' => $this->_pm_instance]
851
-        );
852
-    }
23
+	const onsite  = 'on-site';
24
+
25
+	const offsite = 'off-site';
26
+
27
+	const offline = 'off-line';
28
+
29
+	/**
30
+	 * @var EE_Payment_Method
31
+	 */
32
+	protected $_pm_instance = null;
33
+
34
+	/**
35
+	 * @var boolean
36
+	 */
37
+	protected $_requires_https = false;
38
+
39
+	/**
40
+	 * @var boolean
41
+	 */
42
+	protected $_has_billing_form;
43
+
44
+	/**
45
+	 * @var EE_Gateway
46
+	 */
47
+	protected $_gateway = null;
48
+
49
+	/**
50
+	 * @var EE_Payment_Method_Form
51
+	 */
52
+	protected $_settings_form = null;
53
+
54
+	/**
55
+	 * @var EE_Form_Section_Proper
56
+	 */
57
+	protected $_billing_form = null;
58
+
59
+	/**
60
+	 * @var boolean
61
+	 */
62
+	protected $_cache_billing_form = true;
63
+
64
+	/**
65
+	 * String of the absolute path to the folder containing this file, with a trailing slash.
66
+	 * eg '/public_html/wp-site/wp-content/plugins/event-espresso/payment_methods/Invoice/'
67
+	 *
68
+	 * @var string
69
+	 */
70
+	protected $_file_folder = null;
71
+
72
+	/**
73
+	 * String to the absolute URL to this file (useful for getting its web-accessible resources
74
+	 * like images, js, or css)
75
+	 *
76
+	 * @var string
77
+	 */
78
+	protected $_file_url = null;
79
+
80
+	/**
81
+	 * Pretty name for the payment method
82
+	 *
83
+	 * @var string
84
+	 */
85
+	protected $_pretty_name = null;
86
+
87
+	/**
88
+	 *
89
+	 * @var string
90
+	 */
91
+	protected $_default_button_url = null;
92
+
93
+	/**
94
+	 *
95
+	 * @var string
96
+	 */
97
+	protected $_default_description = null;
98
+
99
+
100
+	/**
101
+	 * @param EE_Payment_Method|null $pm_instance
102
+	 * @throws ReflectionException
103
+	 * @throws EE_Error
104
+	 */
105
+	public function __construct(EE_Payment_Method $pm_instance = null)
106
+	{
107
+		if ($pm_instance instanceof EE_Payment_Method) {
108
+			$this->set_instance($pm_instance);
109
+		}
110
+		if ($this->_gateway) {
111
+			$this->_gateway->set_payment_model(EEM_Payment::instance());
112
+			$this->_gateway->set_payment_log(EEM_Change_Log::instance());
113
+			$this->_gateway->set_template_helper(new EEH_Template());
114
+			$this->_gateway->set_line_item_helper(new EEH_Line_Item());
115
+			$this->_gateway->set_money_helper(new EEH_Money());
116
+			$this->_gateway->set_gateway_data_formatter(new GatewayDataFormatter());
117
+			$this->_gateway->set_unsupported_character_remover(new AsciiOnly());
118
+			do_action('AHEE__EE_PMT_Base___construct__done_initializing_gateway_class', $this, $this->_gateway);
119
+		}
120
+		if (! isset($this->_has_billing_form)) {
121
+			// by default, On Site gateways have a billing form
122
+			if ($this->payment_occurs() == EE_PMT_Base::onsite) {
123
+				$this->set_has_billing_form(true);
124
+			} else {
125
+				$this->set_has_billing_form(false);
126
+			}
127
+		}
128
+
129
+		if (! $this->_pretty_name) {
130
+			throw new EE_Error(
131
+				esc_html__(
132
+					'You must set the pretty name for the Payment Method Type in the constructor (_pretty_name), and please make it internationalized',
133
+					'event_espresso'
134
+				)
135
+			);
136
+		}
137
+		// if the child didn't specify a default button, use the credit card one
138
+		if ($this->_default_button_url === null) {
139
+			$this->_default_button_url = EE_PLUGIN_DIR_URL . 'payment_methods/pay-by-credit-card.png';
140
+		}
141
+	}
142
+
143
+
144
+	/**
145
+	 * @param boolean $has_billing_form
146
+	 */
147
+	public function set_has_billing_form(bool $has_billing_form)
148
+	{
149
+		$this->_has_billing_form = filter_var($has_billing_form, FILTER_VALIDATE_BOOLEAN);
150
+	}
151
+
152
+
153
+	/**
154
+	 * sets the file_folder property
155
+	 */
156
+	protected function _set_file_folder()
157
+	{
158
+		$reflector          = new ReflectionClass(get_class($this));
159
+		$fn                 = $reflector->getFileName();
160
+		$this->_file_folder = dirname($fn) . '/';
161
+	}
162
+
163
+
164
+	/**
165
+	 * sets the file URL with a trailing slash for this PMT
166
+	 */
167
+	protected function _set_file_url()
168
+	{
169
+		$plugins_dir_fixed = str_replace('\\', '/', WP_PLUGIN_DIR);
170
+		$file_folder_fixed = str_replace('\\', '/', $this->file_folder());
171
+		$file_path         = str_replace($plugins_dir_fixed, WP_PLUGIN_URL, $file_folder_fixed);
172
+		$this->_file_url   = set_url_scheme($file_path);
173
+	}
174
+
175
+
176
+	/**
177
+	 * Gets the default description on all payment methods of this type
178
+	 *
179
+	 * @return string
180
+	 */
181
+	public function default_description()
182
+	{
183
+		return $this->_default_description;
184
+	}
185
+
186
+
187
+	/**
188
+	 * Returns the folder containing the PMT child class, with a trailing slash
189
+	 *
190
+	 * @return string
191
+	 */
192
+	public function file_folder()
193
+	{
194
+		if (! $this->_file_folder) {
195
+			$this->_set_file_folder();
196
+		}
197
+		return $this->_file_folder;
198
+	}
199
+
200
+
201
+	/**
202
+	 * @return string
203
+	 */
204
+	public function file_url()
205
+	{
206
+		if (! $this->_file_url) {
207
+			$this->_set_file_url();
208
+		}
209
+		return $this->_file_url;
210
+	}
211
+
212
+
213
+	/**
214
+	 * Sets the payment method instance this payment method type is for.
215
+	 * Its important teh payment method instance is set before
216
+	 *
217
+	 * @param EE_Payment_Method $payment_method_instance
218
+	 * @throws EE_Error
219
+	 */
220
+	public function set_instance(EE_Payment_Method $payment_method_instance)
221
+	{
222
+		$this->_pm_instance = $payment_method_instance;
223
+		// if they have already requested the settings form, make sure its
224
+		// data matches this model object
225
+		if ($this->_settings_form) {
226
+			$this->settings_form()->populate_model_obj($payment_method_instance);
227
+		}
228
+		if ($this->_gateway instanceof EE_Gateway) {
229
+			$this->_gateway->set_settings($payment_method_instance->settings_array());
230
+		}
231
+	}
232
+
233
+
234
+	/**
235
+	 * Gets teh form for displaying to admins where they setup the payment method
236
+	 *
237
+	 * @return EE_Payment_Method_Form
238
+	 * @throws EE_Error
239
+	 */
240
+	public function settings_form()
241
+	{
242
+		if (! $this->_settings_form) {
243
+			$this->_settings_form = $this->generate_new_settings_form();
244
+			$this->_settings_form->set_payment_method_type($this);
245
+			// if we have already assigned a model object to this pmt, make
246
+			// sure its reflected in teh form we just generated
247
+			if ($this->_pm_instance) {
248
+				$this->_settings_form->populate_model_obj($this->_pm_instance);
249
+			}
250
+		}
251
+		return $this->_settings_form;
252
+	}
253
+
254
+
255
+	/**
256
+	 * Gets the form for all the settings related to this payment method type
257
+	 *
258
+	 * @return EE_Payment_Method_Form
259
+	 */
260
+	abstract public function generate_new_settings_form();
261
+
262
+
263
+	/**
264
+	 * Sets the form for settings. This may be useful if we have already received
265
+	 * a form submission and have form data it in, and want to use it anytime we're showing
266
+	 * this payment method type's settings form later in the request
267
+	 *
268
+	 * @param EE_Payment_Method_Form $form
269
+	 */
270
+	public function set_settings_form(EE_Payment_Method_Form $form)
271
+	{
272
+		$this->_settings_form = $form;
273
+	}
274
+
275
+
276
+	/**
277
+	 * @return boolean
278
+	 */
279
+	public function has_billing_form()
280
+	{
281
+		return $this->_has_billing_form;
282
+	}
283
+
284
+
285
+	/**
286
+	 * Gets the form for displaying to attendees where they can enter their billing info
287
+	 * which will be sent to teh gateway (can be null)
288
+	 *
289
+	 * @param EE_Transaction|null $transaction
290
+	 * @param array               $extra_args
291
+	 * @return EE_Billing_Attendee_Info_Form|EE_Billing_Info_Form|null
292
+	 * @throws EE_Error
293
+	 * @throws ReflectionException
294
+	 */
295
+	public function billing_form(EE_Transaction $transaction = null, array $extra_args = [])
296
+	{
297
+		// has billing form already been regenerated ? or overwrite cache?
298
+		if (! $this->_billing_form instanceof EE_Billing_Info_Form || ! $this->_cache_billing_form) {
299
+			$this->_billing_form = $this->generate_new_billing_form($transaction, $extra_args);
300
+		}
301
+		// if we know who the attendee is, and this is a billing form
302
+		// that uses attendee info, populate it
303
+		if (
304
+			apply_filters(
305
+				'FHEE__populate_billing_form_fields_from_attendee',
306
+				(
307
+					$this->_billing_form instanceof EE_Billing_Attendee_Info_Form
308
+					&& $transaction instanceof EE_Transaction
309
+					&& $transaction->primary_registration() instanceof EE_Registration
310
+					&& $transaction->primary_registration()->attendee() instanceof EE_Attendee
311
+				),
312
+				$this->_billing_form,
313
+				$transaction
314
+			)
315
+		) {
316
+			$this->_billing_form->populate_from_attendee($transaction->primary_registration()->attendee());
317
+		}
318
+		return $this->_billing_form;
319
+	}
320
+
321
+
322
+	/**
323
+	 * Creates the billing form for this payment method type
324
+	 *
325
+	 * @param EE_Transaction|null $transaction
326
+	 * @return EE_Billing_Info_Form|null
327
+	 */
328
+	abstract public function generate_new_billing_form(EE_Transaction $transaction = null);
329
+
330
+
331
+	/**
332
+	 * apply_billing_form_debug_settings
333
+	 * applies debug data to the form
334
+	 *
335
+	 * @param EE_Billing_Info_Form $billing_form
336
+	 * @return EE_Billing_Info_Form|null
337
+	 */
338
+	public function apply_billing_form_debug_settings(EE_Billing_Info_Form $billing_form)
339
+	{
340
+		return $billing_form;
341
+	}
342
+
343
+
344
+	/**
345
+	 * Sets the billing form for this payment method type. You may want to use this
346
+	 * if you have form
347
+	 *
348
+	 * @param EE_Payment_Method $form
349
+	 */
350
+	public function set_billing_form(EE_Payment_Method $form)
351
+	{
352
+		$this->_billing_form = $form;
353
+	}
354
+
355
+
356
+	/**
357
+	 * Returns whether or not this payment method requires HTTPS to be used
358
+	 *
359
+	 * @return boolean
360
+	 */
361
+	public function requires_https()
362
+	{
363
+		return $this->_requires_https;
364
+	}
365
+
366
+
367
+	/**
368
+	 *
369
+	 * @param EE_Transaction            $transaction
370
+	 * @param float|null                $amount
371
+	 * @param EE_Billing_Info_Form|null $billing_info
372
+	 * @param string|null               $return_url
373
+	 * @param string                    $fail_url
374
+	 * @param string                    $method
375
+	 * @param bool                      $by_admin
376
+	 * @return EE_Payment
377
+	 * @throws EE_Error
378
+	 * @throws ReflectionException
379
+	 */
380
+	public function process_payment(
381
+		EE_Transaction $transaction,
382
+		float $amount = null,
383
+		?EE_Billing_Info_Form $billing_info = null,
384
+		string $return_url = null,
385
+		string $fail_url = '',
386
+		string $method = 'CART',
387
+		bool $by_admin = false
388
+	) {
389
+		// @todo: add surcharge for the payment method, if any
390
+		if ($this->_gateway) {
391
+			// there is a gateway, so we're going to make a payment object
392
+			// but wait! do they already have a payment in progress that we thought was failed?
393
+			$duplicate_properties = [
394
+				'STS_ID'               => EEM_Payment::status_id_failed,
395
+				'TXN_ID'               => $transaction->ID(),
396
+				'PMD_ID'               => $this->_pm_instance->ID(),
397
+				'PAY_source'           => $method,
398
+				'PAY_amount'           => $amount !== null ? $amount : $transaction->remaining(),
399
+				'PAY_gateway_response' => null,
400
+			];
401
+			$payment              = EEM_Payment::instance()->get_one([$duplicate_properties]);
402
+			// if we didn't already have a payment in progress for the same thing,
403
+			// then we actually want to make a new payment
404
+			if (! $payment instanceof EE_Payment) {
405
+				$payment = EE_Payment::new_instance(
406
+					array_merge(
407
+						$duplicate_properties,
408
+						[
409
+							'PAY_timestamp'       => time(),
410
+							'PAY_txn_id_chq_nmbr' => null,
411
+							'PAY_po_number'       => null,
412
+							'PAY_extra_accntng'   => null,
413
+							'PAY_details'         => null,
414
+						]
415
+					)
416
+				);
417
+			}
418
+			// make sure the payment has been saved to show we started it, and so it has an ID should the gateway try to log it
419
+			$payment->save();
420
+			$billing_values = $this->_get_billing_values_from_form($billing_info);
421
+
422
+			//  Offsite Gateway
423
+			if ($this->_gateway instanceof EE_Offsite_Gateway) {
424
+				$payment = $this->_gateway->set_redirection_info(
425
+					$payment,
426
+					$billing_values,
427
+					$return_url,
428
+					EE_Config::instance()->core->txn_page_url(
429
+						[
430
+							'e_reg_url_link'    => $transaction->primary_registration()->reg_url_link(),
431
+							'ee_payment_method' => $this->_pm_instance->slug(),
432
+						]
433
+					),
434
+					$fail_url
435
+				);
436
+				$payment->save();
437
+				//  Onsite Gateway
438
+			} elseif ($this->_gateway instanceof EE_Onsite_Gateway) {
439
+				$payment = $this->_gateway->do_direct_payment($payment, $billing_values);
440
+				$payment->save();
441
+			} else {
442
+				throw new EE_Error(
443
+					sprintf(
444
+						esc_html__(
445
+							'Gateway for payment method type "%s" is "%s", not a subclass of either EE_Offsite_Gateway or EE_Onsite_Gateway, or null (to indicate NO gateway)',
446
+							'event_espresso'
447
+						),
448
+						get_class($this),
449
+						gettype($this->_gateway)
450
+					)
451
+				);
452
+			}
453
+		} else {
454
+			// no gateway provided
455
+			// there is no payment. Must be an offline gateway
456
+			// create a payment object anyways, but dont save it
457
+			$payment = EE_Payment::new_instance(
458
+				[
459
+					'STS_ID'        => EEM_Payment::status_id_pending,
460
+					'TXN_ID'        => $transaction->ID(),
461
+					'PMD_ID'        => $transaction->payment_method_ID(),
462
+					'PAY_amount'    => 0.00,
463
+					'PAY_timestamp' => time(),
464
+				]
465
+			);
466
+		}
467
+
468
+		// if there is billing info, clean it and save it now
469
+		if ($billing_info instanceof EE_Billing_Attendee_Info_Form) {
470
+			$this->_save_billing_info_to_attendee($billing_info, $transaction);
471
+		}
472
+
473
+		return $payment;
474
+	}
475
+
476
+
477
+	/**
478
+	 * Gets the values we want to pass onto the gateway. Normally these
479
+	 * are just the 'pretty' values, but there may be times the data may need
480
+	 * a  little massaging. Proper subsections will become arrays of inputs
481
+	 *
482
+	 * @param EE_Billing_Info_Form|null $billing_form
483
+	 * @return array
484
+	 * @throws EE_Error
485
+	 */
486
+	protected function _get_billing_values_from_form($billing_form)
487
+	{
488
+		return $billing_form instanceof EE_Form_Section_Proper
489
+			? $billing_form->input_pretty_values(true)
490
+			: [];
491
+	}
492
+
493
+
494
+	/**
495
+	 * Handles an instant payment notification when the transaction is known (by default).
496
+	 *
497
+	 * @param array          $req_data
498
+	 * @param EE_Transaction $transaction
499
+	 * @return EE_Payment
500
+	 * @throws EE_Error
501
+	 * @throws ReflectionException
502
+	 */
503
+	public function handle_ipn(array $req_data, EE_Transaction $transaction)
504
+	{
505
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
506
+		if (! $this->_gateway instanceof EE_Offsite_Gateway) {
507
+			throw new EE_Error(
508
+				sprintf(
509
+					esc_html__("Could not handle IPN because '%s' is not an offsite gateway", "event_espresso"),
510
+					print_r($this->_gateway, true)
511
+				)
512
+			);
513
+		}
514
+		return $this->_gateway->handle_payment_update($req_data, $transaction);
515
+	}
516
+
517
+
518
+	/**
519
+	 * Saves the billing info onto the attendee of the primary registrant on this transaction, and
520
+	 * cleans it first.
521
+	 *
522
+	 * @param EE_Billing_Attendee_Info_Form $billing_form
523
+	 * @param EE_Transaction|null           $transaction
524
+	 * @return boolean success
525
+	 * @throws EE_Error
526
+	 * @throws ReflectionException
527
+	 */
528
+	protected function _save_billing_info_to_attendee(
529
+		EE_Billing_Attendee_Info_Form $billing_form,
530
+		EE_Transaction $transaction
531
+	) {
532
+		if (! $transaction instanceof EE_Transaction) {
533
+			EE_Error::add_error(
534
+				esc_html__("Cannot save billing info because no transaction was specified", "event_espresso"),
535
+				__FILE__,
536
+				__FUNCTION__,
537
+				__LINE__
538
+			);
539
+			return false;
540
+		}
541
+		$primary_reg = $transaction->primary_registration();
542
+		if (! $primary_reg) {
543
+			EE_Error::add_error(
544
+				esc_html__(
545
+					"Cannot save billing info because the transaction has no primary registration",
546
+					"event_espresso"
547
+				),
548
+				__FILE__,
549
+				__FUNCTION__,
550
+				__LINE__
551
+			);
552
+			return false;
553
+		}
554
+		$attendee = $primary_reg->attendee();
555
+		if (! $attendee) {
556
+			EE_Error::add_error(
557
+				esc_html__(
558
+					"Cannot save billing info because the transaction's primary registration has no attendee!",
559
+					"event_espresso"
560
+				),
561
+				__FILE__,
562
+				__FUNCTION__,
563
+				__LINE__
564
+			);
565
+			return false;
566
+		}
567
+		return $attendee->save_and_clean_billing_info_for_payment_method($billing_form, $transaction->payment_method());
568
+	}
569
+
570
+
571
+	/**
572
+	 * Gets the payment this IPN is for. Children may often want to
573
+	 * override this to inspect the request
574
+	 *
575
+	 * @param EE_Transaction $transaction
576
+	 * @param array          $req_data
577
+	 * @return EE_Payment
578
+	 * @throws EE_Error
579
+	 * @throws ReflectionException
580
+	 */
581
+	protected function find_payment_for_ipn(EE_Transaction $transaction, array $req_data = [])
582
+	{
583
+		return $transaction->last_payment();
584
+	}
585
+
586
+
587
+	/**
588
+	 * In case generic code cannot provide the payment processor with a specific payment method
589
+	 * and transaction, it will try calling this method on each activate payment method.
590
+	 * If the payment method is able to identify the request as being for it, it should fetch
591
+	 * the payment its for and return it. If not, it should throw an EE_Error to indicate it cannot
592
+	 * handle the IPN
593
+	 *
594
+	 * @param array $req_data
595
+	 * @return EE_Payment only if this payment method can find the info its needs from $req_data
596
+	 * and identifies the IPN as being for this payment method (not just fo ra payment method of this type)
597
+	 * @throws EE_Error
598
+	 */
599
+	public function handle_unclaimed_ipn(array $req_data = [])
600
+	{
601
+		throw new EE_Error(
602
+			sprintf(
603
+				esc_html__("Payment Method '%s' cannot handle unclaimed IPNs", "event_espresso"),
604
+				get_class($this)
605
+			)
606
+		);
607
+	}
608
+
609
+
610
+	/**
611
+	 * Logic to be accomplished when the payment attempt is complete.
612
+	 * Most payment methods don't need to do anything at this point; but some, like Mijireh, do.
613
+	 * (Mijireh is an offsite gateway which doesn't send an IPN. So when the user returns to EE from
614
+	 * mijireh, this method needs to be called so the Mijireh PM can ping Mijireh to know the status
615
+	 * of the payment). Fed a transaction because it's always assumed to be the last payment that
616
+	 * we're dealing with. Returns that last payment (if there is one)
617
+	 *
618
+	 * @param EE_Transaction $transaction
619
+	 * @return EE_Payment
620
+	 * @throws EE_Error
621
+	 * @throws ReflectionException
622
+	 */
623
+	public function finalize_payment_for(EE_Transaction $transaction)
624
+	{
625
+		return $transaction->last_payment();
626
+	}
627
+
628
+
629
+	/**
630
+	 * Whether or not this payment method's gateway supports sending refund requests
631
+	 *
632
+	 * @return boolean
633
+	 */
634
+	public function supports_sending_refunds()
635
+	{
636
+		return $this->_gateway instanceof EE_Gateway && $this->_gateway->supports_sending_refunds();
637
+	}
638
+
639
+
640
+	/**
641
+	 *
642
+	 * @param EE_Payment $payment
643
+	 * @param array      $refund_info
644
+	 * @return EE_Payment
645
+	 * @throws EE_Error
646
+	 */
647
+	public function process_refund(EE_Payment $payment, array $refund_info = [])
648
+	{
649
+		if ($this->_gateway instanceof EE_Gateway) {
650
+			return $this->_gateway->do_direct_refund($payment, $refund_info);
651
+		} else {
652
+			throw new EE_Error(
653
+				sprintf(
654
+					esc_html__('Payment Method Type "%s" does not support sending refund requests', 'event_espresso'),
655
+					get_class($this)
656
+				)
657
+			);
658
+		}
659
+	}
660
+
661
+
662
+	/**
663
+	 * Returns one the class's constants onsite,offsite, or offline, depending on this
664
+	 * payment method's gateway.
665
+	 *
666
+	 * @return string
667
+	 * @throws EE_Error
668
+	 */
669
+	public function payment_occurs()
670
+	{
671
+		if (! $this->_gateway) {
672
+			return EE_PMT_Base::offline;
673
+		} elseif ($this->_gateway instanceof EE_Onsite_Gateway) {
674
+			return EE_PMT_Base::onsite;
675
+		} elseif ($this->_gateway instanceof EE_Offsite_Gateway) {
676
+			return EE_PMT_Base::offsite;
677
+		} else {
678
+			throw new EE_Error(
679
+				sprintf(
680
+					esc_html__(
681
+						"Payment method type '%s's gateway isn't an instance of EE_Onsite_Gateway, EE_Offsite_Gateway, or null. It must be one of those",
682
+						"event_espresso"
683
+					),
684
+					get_class($this)
685
+				)
686
+			);
687
+		}
688
+	}
689
+
690
+
691
+	/**
692
+	 * For adding any html output ab ove the payment overview.
693
+	 * Many gateways won't want ot display anything, so this function just returns an empty string.
694
+	 * Other gateways may want to override this, such as offline gateways.
695
+	 *
696
+	 * @param EE_Payment $payment
697
+	 * @return string
698
+	 */
699
+	public function payment_overview_content(EE_Payment $payment)
700
+	{
701
+		return EEH_Template::display_template(
702
+			EE_LIBRARIES . 'payment_methods/templates/payment_details_content.template.php',
703
+			['payment_method' => $this->_pm_instance, 'payment' => $payment],
704
+			true
705
+		);
706
+	}
707
+
708
+
709
+	/**
710
+	 * @return array where keys are the help tab name,
711
+	 * values are: array {
712
+	 * @type string $title         i18n name for the help tab
713
+	 * @type string $filename      name of the file located in ./help_tabs/ (ie, in a folder next to this file)
714
+	 * @type array  $template_args any arguments you want passed to the template file while rendering.
715
+	 *                Keys will be variable names and values with be their values.
716
+	 */
717
+	public function help_tabs_config()
718
+	{
719
+		return [];
720
+	}
721
+
722
+
723
+	/**
724
+	 * The system name for this PMT (eg AIM, Paypal_Pro, Invoice... what gets put into
725
+	 * the payment method's table's PMT_type column)
726
+	 *
727
+	 * @return string
728
+	 */
729
+	public function system_name()
730
+	{
731
+		$classname = get_class($this);
732
+		return str_replace("EE_PMT_", '', $classname);
733
+	}
734
+
735
+
736
+	/**
737
+	 * A pretty i18n version of the PMT name. Often the same as the "pretty_name", but you can change it by overriding
738
+	 * this method.
739
+	 *
740
+	 * @return string
741
+	 */
742
+	public function defaultFrontendName()
743
+	{
744
+		return $this->pretty_name();
745
+	}
746
+
747
+
748
+	/**
749
+	 * A pretty i18n version of the PMT name
750
+	 *
751
+	 * @return string
752
+	 */
753
+	public function pretty_name()
754
+	{
755
+		return $this->_pretty_name;
756
+	}
757
+
758
+
759
+	/**
760
+	 * Gets the default absolute URL to the payment method type's button
761
+	 *
762
+	 * @return string
763
+	 */
764
+	public function default_button_url()
765
+	{
766
+		return $this->_default_button_url;
767
+	}
768
+
769
+
770
+	/**
771
+	 * Gets the gateway used by this payment method (if any)
772
+	 *
773
+	 * @return EE_Gateway
774
+	 */
775
+	public function get_gateway()
776
+	{
777
+		return $this->_gateway;
778
+	}
779
+
780
+
781
+	/**
782
+	 * @return string html for the link to a help tab
783
+	 */
784
+	public function get_help_tab_link()
785
+	{
786
+		return EEH_Template::get_help_tab_link(
787
+			$this->get_help_tab_name(),
788
+			'espresso_payment_settings',
789
+			'default'
790
+		);
791
+	}
792
+
793
+
794
+	/**
795
+	 * Returns the name of the help tab for this PMT
796
+	 *
797
+	 * @return string
798
+	 */
799
+	public function get_help_tab_name()
800
+	{
801
+		return 'ee_' . strtolower($this->system_name()) . '_help_tab';
802
+	}
803
+
804
+
805
+	/**
806
+	 * The name of the wp capability that should be associated with the usage of
807
+	 * this PMT by an admin
808
+	 *
809
+	 * @return string
810
+	 */
811
+	public function cap_name()
812
+	{
813
+		return 'ee_payment_method_' . strtolower($this->system_name());
814
+	}
815
+
816
+
817
+	/**
818
+	 * Called by client code to tell the gateway that if it wants to change
819
+	 * the transaction or line items or registrations related to teh payment it already
820
+	 * processed (we think, but possibly not) that now's the time to do it.
821
+	 * It is expected that gateways will store any info they need for this on the PAY_details,
822
+	 * or maybe an extra meta value
823
+	 *
824
+	 * @param EE_Payment $payment
825
+	 * @return void
826
+	 */
827
+	public function update_txn_based_on_payment(EE_Payment $payment)
828
+	{
829
+		if ($this->_gateway instanceof EE_Gateway) {
830
+			$this->_gateway->update_txn_based_on_payment($payment);
831
+		}
832
+	}
833
+
834
+
835
+	/**
836
+	 * Returns a string of HTML describing this payment method type for an admin,
837
+	 * primarily intended for them to read before activating it.
838
+	 * The easiest way to set this is to create a folder 'templates' alongside
839
+	 * your EE_PMT_{System_Name} file, and in it create a file named "{system_name}_intro.template.php".
840
+	 * Eg, if your payment method file is named "EE_PMT_Foo_Bar.pm.php",
841
+	 * then you'd create a file named "templates" in the same folder as it, and name the file
842
+	 * "foo_bar_intro.template.php", and its content will be returned by this method
843
+	 *
844
+	 * @return string
845
+	 */
846
+	public function introductory_html()
847
+	{
848
+		return EEH_Template::locate_template(
849
+			$this->file_folder() . 'templates/' . strtolower($this->system_name()) . '_intro.template.php',
850
+			['pmt_obj' => $this, 'pm_instance' => $this->_pm_instance]
851
+		);
852
+	}
853 853
 }
Please login to merge, or discard this patch.
modules/ticket_selector/templates/standard_ticket_selector.template.php 1 patch
Indentation   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -29,12 +29,12 @@  discard block
 block discarded – undo
29 29
         <tr>
30 30
             <th id="details-<?php echo esc_attr($EVT_ID); ?>" scope="col" class="ee-ticket-selector-ticket-details-th">
31 31
                 <?php
32
-                echo apply_filters(
33
-                    'FHEE__ticket_selector_chart_template__table_header_available_tickets',
34
-                    esc_html__('Details', 'event_espresso'),
35
-                    $EVT_ID
36
-                );
37
-                ?>
32
+				echo apply_filters(
33
+					'FHEE__ticket_selector_chart_template__table_header_available_tickets',
34
+					esc_html__('Details', 'event_espresso'),
35
+					$EVT_ID
36
+				);
37
+				?>
38 38
             </th>
39 39
             <?php if (apply_filters('FHEE__ticket_selector_chart_template__display_ticket_price_details', true)) { ?>
40 40
                 <th id="price-<?php echo esc_attr($EVT_ID); ?>" scope="col" class="ee-ticket-selector-ticket-price-th
@@ -53,12 +53,12 @@  discard block
 block discarded – undo
53 53
     </table>
54 54
 <?php
55 55
 if ($taxable_tickets && apply_filters('FHEE__ticket_selector_chart_template__display_ticket_price_details', true)) {
56
-    if ($prices_displayed_including_taxes) {
57
-        $ticket_price_includes_taxes = esc_html__('* price includes taxes', 'event_espresso');
58
-    } else {
59
-        $ticket_price_includes_taxes = esc_html__('* price does not include taxes', 'event_espresso');
60
-    }
61
-    echo '<p class="small-text lt-grey-text" style="text-align:right; margin: -1em 0 1em;">
56
+	if ($prices_displayed_including_taxes) {
57
+		$ticket_price_includes_taxes = esc_html__('* price includes taxes', 'event_espresso');
58
+	} else {
59
+		$ticket_price_includes_taxes = esc_html__('* price does not include taxes', 'event_espresso');
60
+	}
61
+	echo '<p class="small-text lt-grey-text" style="text-align:right; margin: -1em 0 1em;">
62 62
         ' . $ticket_price_includes_taxes . '
63 63
         </p>';
64 64
 }
@@ -68,12 +68,12 @@  discard block
 block discarded – undo
68 68
 
69 69
 <?php
70 70
 if ($max_atndz > 0) {
71
-    echo apply_filters(
72
-        'FHEE__ticket_selector_chart_template__maximum_tickets_purchased_footnote',
73
-        esc_html('')
74
-    );
71
+	echo apply_filters(
72
+		'FHEE__ticket_selector_chart_template__maximum_tickets_purchased_footnote',
73
+		esc_html('')
74
+	);
75 75
 }
76 76
 if (! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
77
-    add_filter('FHEE__EE_Ticket_Selector__no_ticket_selector_submit', '__return_true');
77
+	add_filter('FHEE__EE_Ticket_Selector__no_ticket_selector_submit', '__return_true');
78 78
 }
79 79
 do_action('AHEE__ticket_selector_chart__template__after_ticket_selector', $EVT_ID, $event);
Please login to merge, or discard this patch.
reg_steps/payment_options/EE_SPCO_Reg_Step_Payment_Options.class.php 1 patch
Indentation   +2858 added lines, -2858 removed lines patch added patch discarded remove patch
@@ -20,2862 +20,2862 @@
 block discarded – undo
20 20
 class EE_SPCO_Reg_Step_Payment_Options extends EE_SPCO_Reg_Step
21 21
 {
22 22
 
23
-    /**
24
-     * @var EE_Line_Item_Display $Line_Item_Display
25
-     */
26
-    protected $line_item_display;
27
-
28
-    /**
29
-     * @var boolean $handle_IPN_in_this_request
30
-     */
31
-    protected $handle_IPN_in_this_request = false;
32
-
33
-
34
-    /**
35
-     *    set_hooks - for hooking into EE Core, other modules, etc
36
-     *
37
-     * @access    public
38
-     * @return    void
39
-     */
40
-    public static function set_hooks()
41
-    {
42
-        add_filter(
43
-            'FHEE__SPCO__EE_Line_Item_Filter_Collection',
44
-            ['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
45
-        );
46
-        add_action(
47
-            'wp_ajax_switch_spco_billing_form',
48
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
49
-        );
50
-        add_action(
51
-            'wp_ajax_nopriv_switch_spco_billing_form',
52
-            ['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
53
-        );
54
-        add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
55
-        add_action(
56
-            'wp_ajax_nopriv_save_payer_details',
57
-            ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
58
-        );
59
-        add_action(
60
-            'wp_ajax_get_transaction_details_for_gateways',
61
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
62
-        );
63
-        add_action(
64
-            'wp_ajax_nopriv_get_transaction_details_for_gateways',
65
-            ['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
66
-        );
67
-        add_filter(
68
-            'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
69
-            ['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
70
-            10,
71
-            1
72
-        );
73
-    }
74
-
75
-
76
-    /**
77
-     *    ajax switch_spco_billing_form
78
-     *
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
-     *    ajax save_payer_details
88
-     *
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
-     *    ajax get_transaction_details
98
-     *
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
-     * bypass_recaptcha_for_load_payment_method
108
-     *
109
-     * @access public
110
-     * @return array
111
-     * @throws InvalidArgumentException
112
-     * @throws InvalidDataTypeException
113
-     * @throws InvalidInterfaceException
114
-     */
115
-    public static function bypass_recaptcha_for_load_payment_method()
116
-    {
117
-        return [
118
-            'EESID'  => EE_Registry::instance()->SSN->id(),
119
-            'step'   => 'payment_options',
120
-            'action' => 'spco_billing_form',
121
-        ];
122
-    }
123
-
124
-
125
-    /**
126
-     *    class constructor
127
-     *
128
-     * @access    public
129
-     * @param EE_Checkout $checkout
130
-     */
131
-    public function __construct(EE_Checkout $checkout)
132
-    {
133
-        $this->request   = EED_Single_Page_Checkout::getRequest();
134
-        $this->_slug     = 'payment_options';
135
-        $this->_name     = esc_html__('Payment Options', 'event_espresso');
136
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
137
-        $this->checkout  = $checkout;
138
-        $this->_reset_success_message();
139
-        $this->set_instructions(
140
-            esc_html__(
141
-                'Please select a method of payment and provide any necessary billing information before proceeding.',
142
-                'event_espresso'
143
-            )
144
-        );
145
-    }
146
-
147
-
148
-    /**
149
-     * @return null
150
-     */
151
-    public function line_item_display()
152
-    {
153
-        return $this->line_item_display;
154
-    }
155
-
156
-
157
-    /**
158
-     * @param null $line_item_display
159
-     */
160
-    public function set_line_item_display($line_item_display)
161
-    {
162
-        $this->line_item_display = $line_item_display;
163
-    }
164
-
165
-
166
-    /**
167
-     * @return boolean
168
-     */
169
-    public function handle_IPN_in_this_request()
170
-    {
171
-        return $this->handle_IPN_in_this_request;
172
-    }
173
-
174
-
175
-    /**
176
-     * @param boolean $handle_IPN_in_this_request
177
-     */
178
-    public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
179
-    {
180
-        $this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
181
-    }
182
-
183
-
184
-    /**
185
-     * translate_js_strings
186
-     *
187
-     * @return void
188
-     */
189
-    public function translate_js_strings()
190
-    {
191
-        EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
192
-            'Please select a method of payment in order to continue.',
193
-            'event_espresso'
194
-        );
195
-        EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
196
-            'A valid method of payment could not be determined. Please refresh the page and try again.',
197
-            'event_espresso'
198
-        );
199
-        EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
200
-            'Forwarding to Secure Payment Provider.',
201
-            'event_espresso'
202
-        );
203
-    }
204
-
205
-
206
-    /**
207
-     * enqueue_styles_and_scripts
208
-     *
209
-     * @return void
210
-     * @throws EE_Error
211
-     * @throws InvalidArgumentException
212
-     * @throws InvalidDataTypeException
213
-     * @throws InvalidInterfaceException
214
-     * @throws ReflectionException
215
-     */
216
-    public function enqueue_styles_and_scripts()
217
-    {
218
-        $transaction = $this->checkout->transaction;
219
-        // if the transaction isn't set or nothing is owed on it, don't enqueue any JS
220
-        if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
221
-            return;
222
-        }
223
-        foreach (
224
-            EEM_Payment_Method::instance()->get_all_for_transaction(
225
-                $transaction,
226
-                EEM_Payment_Method::scope_cart
227
-            ) as $payment_method
228
-        ) {
229
-            $type_obj = $payment_method->type_obj();
230
-            if ($type_obj instanceof EE_PMT_Base) {
231
-                $billing_form = $type_obj->generate_new_billing_form($transaction);
232
-                if ($billing_form instanceof EE_Form_Section_Proper) {
233
-                    $billing_form->enqueue_js();
234
-                }
235
-            }
236
-        }
237
-    }
238
-
239
-
240
-    /**
241
-     * initialize_reg_step
242
-     *
243
-     * @return bool
244
-     * @throws EE_Error
245
-     * @throws InvalidArgumentException
246
-     * @throws ReflectionException
247
-     * @throws InvalidDataTypeException
248
-     * @throws InvalidInterfaceException
249
-     */
250
-    public function initialize_reg_step()
251
-    {
252
-        // TODO: if /when we implement donations, then this will need overriding
253
-        if (
254
-            // don't need payment options for:
255
-            // registrations made via the admin
256
-            // completed transactions
257
-            // overpaid transactions
258
-            // $ 0.00 transactions(no payment required)
259
-            ! $this->checkout->payment_required()
260
-            // but do NOT remove if current action being called belongs to this reg step
261
-            && ! is_callable([$this, $this->checkout->action])
262
-            && ! $this->completed()
263
-        ) {
264
-            // and if so, then we no longer need the Payment Options step
265
-            if ($this->is_current_step()) {
266
-                $this->checkout->generate_reg_form = false;
267
-            }
268
-            $this->checkout->remove_reg_step($this->_slug);
269
-            // DEBUG LOG
270
-            // $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
271
-            return false;
272
-        }
273
-        // load EEM_Payment_Method
274
-        EE_Registry::instance()->load_model('Payment_Method');
275
-        // get all active payment methods
276
-        $this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
277
-            $this->checkout->transaction,
278
-            EEM_Payment_Method::scope_cart
279
-        );
280
-        return true;
281
-    }
282
-
283
-
284
-    /**
285
-     * @return EE_Form_Section_Proper
286
-     * @throws EE_Error
287
-     * @throws InvalidArgumentException
288
-     * @throws ReflectionException
289
-     * @throws EntityNotFoundException
290
-     * @throws InvalidDataTypeException
291
-     * @throws InvalidInterfaceException
292
-     * @throws InvalidStatusException
293
-     */
294
-    public function generate_reg_form()
295
-    {
296
-        // reset in case someone changes their mind
297
-        $this->_reset_selected_method_of_payment();
298
-        // set some defaults
299
-        $this->checkout->selected_method_of_payment = 'payments_closed';
300
-        $registrations_requiring_payment            = [];
301
-        $registrations_for_free_events              = [];
302
-        $registrations_requiring_pre_approval       = [];
303
-        $sold_out_events                            = [];
304
-        $insufficient_spaces_available              = [];
305
-        $no_payment_required                        = true;
306
-        // loop thru registrations to gather info
307
-        $registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
308
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
309
-            $registrations,
310
-            $this->checkout->revisit
311
-        );
312
-        foreach ($registrations as $REG_ID => $registration) {
313
-            /** @var $registration EE_Registration */
314
-            // has this registration lost it's space ?
315
-            if (isset($ejected_registrations[ $REG_ID ])) {
316
-                if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
317
-                    $sold_out_events[ $registration->event()->ID() ] = $registration->event();
318
-                } else {
319
-                    $insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
320
-                }
321
-                continue;
322
-            }
323
-            // event requires admin approval
324
-            if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
325
-                // add event to list of events with pre-approval reg status
326
-                $registrations_requiring_pre_approval[ $REG_ID ] = $registration;
327
-                do_action(
328
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
329
-                    $registration->event(),
330
-                    $this
331
-                );
332
-                continue;
333
-            }
334
-            if (
335
-                $this->checkout->revisit
336
-                && $registration->status_ID() !== EEM_Registration::status_id_approved
337
-                && (
338
-                    $registration->event()->is_sold_out()
339
-                    || $registration->event()->is_sold_out(true)
340
-                )
341
-            ) {
342
-                // add event to list of events that are sold out
343
-                $sold_out_events[ $registration->event()->ID() ] = $registration->event();
344
-                do_action(
345
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
346
-                    $registration->event(),
347
-                    $this
348
-                );
349
-                continue;
350
-            }
351
-            // are they allowed to pay now and is there monies owing?
352
-            if ($registration->owes_monies_and_can_pay()) {
353
-                $registrations_requiring_payment[ $REG_ID ] = $registration;
354
-                do_action(
355
-                    'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
356
-                    $registration->event(),
357
-                    $this
358
-                );
359
-            } elseif (
360
-                ! $this->checkout->revisit
361
-                      && $registration->status_ID() !== EEM_Registration::status_id_not_approved
362
-                      && $registration->ticket()->is_free()
363
-            ) {
364
-                $registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
365
-            }
366
-        }
367
-        $subsections = [];
368
-        // now decide which template to load
369
-        if (! empty($sold_out_events)) {
370
-            $subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
371
-        }
372
-        if (! empty($insufficient_spaces_available)) {
373
-            $subsections['insufficient_space'] = $this->_insufficient_spaces_available(
374
-                $insufficient_spaces_available
375
-            );
376
-        }
377
-        if (! empty($registrations_requiring_pre_approval)) {
378
-            $subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
379
-                $registrations_requiring_pre_approval
380
-            );
381
-        }
382
-        if (! empty($registrations_for_free_events)) {
383
-            $subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
384
-        }
385
-        if (! empty($registrations_requiring_payment)) {
386
-            if ($this->checkout->amount_owing > 0) {
387
-                // autoload Line_Item_Display classes
388
-                EEH_Autoloader::register_line_item_filter_autoloaders();
389
-                $line_item_filter_processor = new EE_Line_Item_Filter_Processor(
390
-                    apply_filters(
391
-                        'FHEE__SPCO__EE_Line_Item_Filter_Collection',
392
-                        new EE_Line_Item_Filter_Collection()
393
-                    ),
394
-                    $this->checkout->cart->get_grand_total()
395
-                );
396
-                /** @var EE_Line_Item $filtered_line_item_tree */
397
-                $filtered_line_item_tree = $line_item_filter_processor->process();
398
-                EEH_Autoloader::register_line_item_display_autoloaders();
399
-                $this->set_line_item_display(new EE_Line_Item_Display('spco'));
400
-                $subsections['payment_options'] = $this->_display_payment_options(
401
-                    $this->line_item_display->display_line_item(
402
-                        $filtered_line_item_tree,
403
-                        ['registrations' => $registrations]
404
-                    )
405
-                );
406
-                $this->checkout->amount_owing   = $filtered_line_item_tree->total();
407
-                $this->_apply_registration_payments_to_amount_owing($registrations);
408
-            }
409
-            $no_payment_required = false;
410
-        } else {
411
-            $this->_hide_reg_step_submit_button_if_revisit();
412
-        }
413
-        $this->_save_selected_method_of_payment();
414
-
415
-        $subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
416
-        $subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
417
-
418
-        return new EE_Form_Section_Proper(
419
-            [
420
-                'name'            => $this->reg_form_name(),
421
-                'html_id'         => $this->reg_form_name(),
422
-                'subsections'     => $subsections,
423
-                'layout_strategy' => new EE_No_Layout(),
424
-            ]
425
-        );
426
-    }
427
-
428
-
429
-    /**
430
-     * add line item filters required for this reg step
431
-     * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
432
-     *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
433
-     *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
434
-     *        payment options reg step, can apply these filters via the following: apply_filters(
435
-     *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
436
-     *        filter collection by passing that instead of instantiating a new collection
437
-     *
438
-     * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
439
-     * @return EE_Line_Item_Filter_Collection
440
-     * @throws EE_Error
441
-     * @throws InvalidArgumentException
442
-     * @throws ReflectionException
443
-     * @throws EntityNotFoundException
444
-     * @throws InvalidDataTypeException
445
-     * @throws InvalidInterfaceException
446
-     * @throws InvalidStatusException
447
-     */
448
-    public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
449
-    {
450
-        if (! EE_Registry::instance()->SSN instanceof EE_Session) {
451
-            return $line_item_filter_collection;
452
-        }
453
-        if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
454
-            return $line_item_filter_collection;
455
-        }
456
-        if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
457
-            return $line_item_filter_collection;
458
-        }
459
-        $line_item_filter_collection->add(
460
-            new EE_Billable_Line_Item_Filter(
461
-                EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
462
-                    EE_Registry::instance()->SSN->checkout()->transaction->registrations(
463
-                        EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
464
-                    )
465
-                )
466
-            )
467
-        );
468
-        $line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
469
-        return $line_item_filter_collection;
470
-    }
471
-
472
-
473
-    /**
474
-     * remove_ejected_registrations
475
-     * if a registrant has lost their potential space at an event due to lack of payment,
476
-     * then this method removes them from the list of registrations being paid for during this request
477
-     *
478
-     * @param EE_Registration[] $registrations
479
-     * @return EE_Registration[]
480
-     * @throws EE_Error
481
-     * @throws InvalidArgumentException
482
-     * @throws ReflectionException
483
-     * @throws EntityNotFoundException
484
-     * @throws InvalidDataTypeException
485
-     * @throws InvalidInterfaceException
486
-     * @throws InvalidStatusException
487
-     */
488
-    public static function remove_ejected_registrations(array $registrations)
489
-    {
490
-        $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
491
-            $registrations,
492
-            EE_Registry::instance()->SSN->checkout()->revisit
493
-        );
494
-        foreach ($registrations as $REG_ID => $registration) {
495
-            // has this registration lost it's space ?
496
-            if (isset($ejected_registrations[ $REG_ID ])) {
497
-                unset($registrations[ $REG_ID ]);
498
-            }
499
-        }
500
-        return $registrations;
501
-    }
502
-
503
-
504
-    /**
505
-     * find_registrations_that_lost_their_space
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, $revisit = false)
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() === EEM_Registration::status_id_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() !== EEM_Registration::status_id_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
-                        EEM_Registration::status_id_wait_list
574
-                    );
575
-                }
576
-            }
577
-        }
578
-        return $ejected_registrations;
579
-    }
580
-
581
-
582
-    /**
583
-     * _hide_reg_step_submit_button
584
-     * removes the html for the reg step submit button
585
-     * by replacing it with an empty string via filter callback
586
-     *
587
-     * @return void
588
-     */
589
-    protected function _adjust_registration_status_if_event_old_sold()
590
-    {
591
-    }
592
-
593
-
594
-    /**
595
-     * _hide_reg_step_submit_button
596
-     * removes the html for the reg step submit button
597
-     * by replacing it with an empty string via filter callback
598
-     *
599
-     * @return void
600
-     */
601
-    protected function _hide_reg_step_submit_button_if_revisit()
602
-    {
603
-        if ($this->checkout->revisit) {
604
-            add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
605
-        }
606
-    }
607
-
608
-
609
-    /**
610
-     * sold_out_events
611
-     * displays notices regarding events that have sold out since hte registrant first signed up
612
-     *
613
-     * @param EE_Event[] $sold_out_events_array
614
-     * @return EE_Form_Section_Proper
615
-     * @throws EE_Error
616
-     */
617
-    private function _sold_out_events($sold_out_events_array = [])
618
-    {
619
-        // set some defaults
620
-        $this->checkout->selected_method_of_payment = 'events_sold_out';
621
-        $sold_out_events                            = '';
622
-        foreach ($sold_out_events_array as $sold_out_event) {
623
-            $sold_out_events .= EEH_HTML::li(
624
-                EEH_HTML::span(
625
-                    '  ' . $sold_out_event->name(),
626
-                    '',
627
-                    'dashicons dashicons-marker ee-icon-size-16 pink-text'
628
-                )
629
-            );
630
-        }
631
-        return new EE_Form_Section_Proper(
632
-            [
633
-                'layout_strategy' => new EE_Template_Layout(
634
-                    [
635
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
636
-                                                  . $this->_slug
637
-                                                  . '/sold_out_events.template.php',
638
-                        'template_args'        => apply_filters(
639
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
640
-                            [
641
-                                'sold_out_events'     => $sold_out_events,
642
-                                'sold_out_events_msg' => apply_filters(
643
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
644
-                                    sprintf(
645
-                                        esc_html__(
646
-                                            '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',
647
-                                            'event_espresso'
648
-                                        ),
649
-                                        '<strong>',
650
-                                        '</strong>',
651
-                                        '<br />'
652
-                                    )
653
-                                ),
654
-                            ]
655
-                        ),
656
-                    ]
657
-                ),
658
-            ]
659
-        );
660
-    }
661
-
662
-
663
-    /**
664
-     * _insufficient_spaces_available
665
-     * displays notices regarding events that do not have enough remaining spaces
666
-     * to satisfy the current number of registrations looking to pay
667
-     *
668
-     * @param EE_Event[] $insufficient_spaces_events_array
669
-     * @return EE_Form_Section_Proper
670
-     * @throws EE_Error
671
-     * @throws ReflectionException
672
-     */
673
-    private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
674
-    {
675
-        // set some defaults
676
-        $this->checkout->selected_method_of_payment = 'invoice';
677
-        $insufficient_space_events                  = '';
678
-        foreach ($insufficient_spaces_events_array as $event) {
679
-            if ($event instanceof EE_Event) {
680
-                $insufficient_space_events .= EEH_HTML::li(
681
-                    EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
682
-                );
683
-            }
684
-        }
685
-        return new EE_Form_Section_Proper(
686
-            [
687
-                'subsections'     => [
688
-                    'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
689
-                    'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
690
-                ],
691
-                'layout_strategy' => new EE_Template_Layout(
692
-                    [
693
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
694
-                                                  . $this->_slug
695
-                                                  . '/sold_out_events.template.php',
696
-                        'template_args'        => apply_filters(
697
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
698
-                            [
699
-                                'sold_out_events'     => $insufficient_space_events,
700
-                                'sold_out_events_msg' => apply_filters(
701
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
702
-                                    esc_html__(
703
-                                        '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.',
704
-                                        'event_espresso'
705
-                                    )
706
-                                ),
707
-                            ]
708
-                        ),
709
-                    ]
710
-                ),
711
-            ]
712
-        );
713
-    }
714
-
715
-
716
-    /**
717
-     * registrations_requiring_pre_approval
718
-     *
719
-     * @param array $registrations_requiring_pre_approval
720
-     * @return EE_Form_Section_Proper
721
-     * @throws EE_Error
722
-     * @throws EntityNotFoundException
723
-     * @throws ReflectionException
724
-     */
725
-    private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
726
-    {
727
-        $events_requiring_pre_approval = [];
728
-        foreach ($registrations_requiring_pre_approval as $registration) {
729
-            if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
730
-                $events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
731
-                    EEH_HTML::span(
732
-                        '',
733
-                        '',
734
-                        'dashicons dashicons-marker ee-icon-size-16 orange-text'
735
-                    )
736
-                    . EEH_HTML::span($registration->event()->name(), '', 'orange-text')
737
-                );
738
-            }
739
-        }
740
-        return new EE_Form_Section_Proper(
741
-            [
742
-                'layout_strategy' => new EE_Template_Layout(
743
-                    [
744
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
745
-                                                  . $this->_slug
746
-                                                  . '/events_requiring_pre_approval.template.php', // layout_template
747
-                        'template_args'        => apply_filters(
748
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
749
-                            [
750
-                                'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
751
-                                'events_requiring_pre_approval_msg' => apply_filters(
752
-                                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
753
-                                    esc_html__(
754
-                                        '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.',
755
-                                        'event_espresso'
756
-                                    )
757
-                                ),
758
-                            ]
759
-                        ),
760
-                    ]
761
-                ),
762
-            ]
763
-        );
764
-    }
765
-
766
-
767
-    /**
768
-     * _no_payment_required
769
-     *
770
-     * @param EE_Event[] $registrations_for_free_events
771
-     * @return EE_Form_Section_Proper
772
-     * @throws EE_Error
773
-     */
774
-    private function _no_payment_required($registrations_for_free_events = [])
775
-    {
776
-        // set some defaults
777
-        $this->checkout->selected_method_of_payment = 'no_payment_required';
778
-        // generate no_payment_required form
779
-        return new EE_Form_Section_Proper(
780
-            [
781
-                'layout_strategy' => new EE_Template_Layout(
782
-                    [
783
-                        'layout_template_file' => SPCO_REG_STEPS_PATH
784
-                                                  . $this->_slug
785
-                                                  . '/no_payment_required.template.php', // layout_template
786
-                        'template_args'        => apply_filters(
787
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
788
-                            [
789
-                                'revisit'                       => $this->checkout->revisit,
790
-                                'registrations'                 => [],
791
-                                'ticket_count'                  => [],
792
-                                'registrations_for_free_events' => $registrations_for_free_events,
793
-                                'no_payment_required_msg'       => EEH_HTML::p(
794
-                                    esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
795
-                                ),
796
-                            ]
797
-                        ),
798
-                    ]
799
-                ),
800
-            ]
801
-        );
802
-    }
803
-
804
-
805
-    /**
806
-     * _display_payment_options
807
-     *
808
-     * @param string $transaction_details
809
-     * @return EE_Form_Section_Proper
810
-     * @throws EE_Error
811
-     * @throws InvalidArgumentException
812
-     * @throws InvalidDataTypeException
813
-     * @throws InvalidInterfaceException
814
-     */
815
-    private function _display_payment_options($transaction_details = '')
816
-    {
817
-        // has method_of_payment been set by no-js user?
818
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
819
-        // build payment options form
820
-        return apply_filters(
821
-            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
822
-            new EE_Form_Section_Proper(
823
-                [
824
-                    'subsections'     => [
825
-                        'before_payment_options' => apply_filters(
826
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
827
-                            new EE_Form_Section_Proper(
828
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
829
-                            )
830
-                        ),
831
-                        'payment_options'        => $this->_setup_payment_options(),
832
-                        'after_payment_options'  => apply_filters(
833
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
834
-                            new EE_Form_Section_Proper(
835
-                                ['layout_strategy' => new EE_Div_Per_Section_Layout()]
836
-                            )
837
-                        ),
838
-                    ],
839
-                    'layout_strategy' => new EE_Template_Layout(
840
-                        [
841
-                            'layout_template_file' => $this->_template,
842
-                            'template_args'        => apply_filters(
843
-                                'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
844
-                                [
845
-                                    'reg_count'                 => $this->line_item_display->total_items(),
846
-                                    'transaction_details'       => $transaction_details,
847
-                                    'available_payment_methods' => [],
848
-                                ]
849
-                            ),
850
-                        ]
851
-                    ),
852
-                ]
853
-            )
854
-        );
855
-    }
856
-
857
-
858
-    /**
859
-     * _extra_hidden_inputs
860
-     *
861
-     * @param bool $no_payment_required
862
-     * @return EE_Form_Section_Proper
863
-     * @throws EE_Error
864
-     * @throws ReflectionException
865
-     */
866
-    private function _extra_hidden_inputs($no_payment_required = true)
867
-    {
868
-        return new EE_Form_Section_Proper(
869
-            [
870
-                'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
871
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
872
-                'subsections'     => [
873
-                    'spco_no_payment_required' => new EE_Hidden_Input(
874
-                        [
875
-                            'normalization_strategy' => new EE_Boolean_Normalization(),
876
-                            'html_name'              => 'spco_no_payment_required',
877
-                            'html_id'                => 'spco-no-payment-required-payment_options',
878
-                            'default'                => $no_payment_required,
879
-                        ]
880
-                    ),
881
-                    'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
882
-                        [
883
-                            'normalization_strategy' => new EE_Int_Normalization(),
884
-                            'html_name'              => 'spco_transaction_id',
885
-                            'html_id'                => 'spco-transaction-id',
886
-                            'default'                => $this->checkout->transaction->ID(),
887
-                        ]
888
-                    ),
889
-                ],
890
-            ]
891
-        );
892
-    }
893
-
894
-
895
-    /**
896
-     *    _apply_registration_payments_to_amount_owing
897
-     *
898
-     * @param array $registrations
899
-     * @throws EE_Error
900
-     */
901
-    protected function _apply_registration_payments_to_amount_owing(array $registrations)
902
-    {
903
-        $payments = [];
904
-        foreach ($registrations as $registration) {
905
-            if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
906
-                $payments += $registration->registration_payments();
907
-            }
908
-        }
909
-        if (! empty($payments)) {
910
-            foreach ($payments as $payment) {
911
-                if ($payment instanceof EE_Registration_Payment) {
912
-                    $this->checkout->amount_owing -= $payment->amount();
913
-                }
914
-            }
915
-        }
916
-    }
917
-
918
-
919
-    /**
920
-     *    _reset_selected_method_of_payment
921
-     *
922
-     * @access    private
923
-     * @param bool $force_reset
924
-     * @return void
925
-     * @throws InvalidArgumentException
926
-     * @throws InvalidDataTypeException
927
-     * @throws InvalidInterfaceException
928
-     */
929
-    private function _reset_selected_method_of_payment($force_reset = false)
930
-    {
931
-        /** @var RequestInterface $request */
932
-        $request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
933
-        $reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
934
-        if ($reset_payment_method) {
935
-            $this->checkout->selected_method_of_payment = null;
936
-            $this->checkout->payment_method             = null;
937
-            $this->checkout->billing_form               = null;
938
-            $this->_save_selected_method_of_payment();
939
-        }
940
-    }
941
-
942
-
943
-    /**
944
-     * _save_selected_method_of_payment
945
-     * stores the selected_method_of_payment in the session
946
-     * so that it's available for all subsequent requests including AJAX
947
-     *
948
-     * @access        private
949
-     * @param string $selected_method_of_payment
950
-     * @return void
951
-     * @throws InvalidArgumentException
952
-     * @throws InvalidDataTypeException
953
-     * @throws InvalidInterfaceException
954
-     */
955
-    private function _save_selected_method_of_payment($selected_method_of_payment = '')
956
-    {
957
-        $selected_method_of_payment = ! empty($selected_method_of_payment)
958
-            ? $selected_method_of_payment
959
-            : $this->checkout->selected_method_of_payment;
960
-        EE_Registry::instance()->SSN->set_session_data(
961
-            ['selected_method_of_payment' => $selected_method_of_payment]
962
-        );
963
-    }
964
-
965
-
966
-    /**
967
-     * _setup_payment_options
968
-     *
969
-     * @return EE_Form_Section_Proper
970
-     * @throws EE_Error
971
-     * @throws InvalidArgumentException
972
-     * @throws InvalidDataTypeException
973
-     * @throws InvalidInterfaceException
974
-     */
975
-    public function _setup_payment_options()
976
-    {
977
-        // load payment method classes
978
-        $this->checkout->available_payment_methods = $this->_get_available_payment_methods();
979
-        if (empty($this->checkout->available_payment_methods)) {
980
-            EE_Error::add_error(
981
-                apply_filters(
982
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
983
-                    sprintf(
984
-                        esc_html__(
985
-                            '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.',
986
-                            'event_espresso'
987
-                        ),
988
-                        '<br>',
989
-                        EE_Registry::instance()->CFG->organization->get_pretty('email')
990
-                    )
991
-                ),
992
-                __FILE__,
993
-                __FUNCTION__,
994
-                __LINE__
995
-            );
996
-        }
997
-        // switch up header depending on number of available payment methods
998
-        $payment_method_header     = count($this->checkout->available_payment_methods) > 1
999
-            ? apply_filters(
1000
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1001
-                esc_html__('Please Select Your Method of Payment', 'event_espresso')
1002
-            )
1003
-            : apply_filters(
1004
-                'FHEE__registration_page_payment_options__method_of_payment_hdr',
1005
-                esc_html__('Method of Payment', 'event_espresso')
1006
-            );
1007
-        $available_payment_methods = [
1008
-            // display the "Payment Method" header
1009
-            'payment_method_header' => new EE_Form_Section_HTML(
1010
-                apply_filters(
1011
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1012
-                    EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1013
-                    $payment_method_header
1014
-                )
1015
-            ),
1016
-        ];
1017
-        // the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1018
-        $available_payment_method_options = [];
1019
-        $default_payment_method_option    = [];
1020
-        // additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1021
-        $payment_methods_billing_info = [
1022
-            new EE_Form_Section_HTML(
1023
-                EEH_HTML::div('<br />', '', '', 'clear:both;')
1024
-            ),
1025
-        ];
1026
-        // loop through payment methods
1027
-        foreach ($this->checkout->available_payment_methods as $payment_method) {
1028
-            if ($payment_method instanceof EE_Payment_Method) {
1029
-                $payment_method_button = EEH_HTML::img(
1030
-                    $payment_method->button_url(),
1031
-                    $payment_method->name(),
1032
-                    'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1033
-                    'spco-payment-method-btn-img'
1034
-                );
1035
-                // check if any payment methods are set as default
1036
-                // if payment method is already selected OR nothing is selected and this payment method should be
1037
-                // open_by_default
1038
-                if (
1039
-                    ($this->checkout->selected_method_of_payment === $payment_method->slug())
1040
-                    || (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1041
-                ) {
1042
-                    $this->checkout->selected_method_of_payment = $payment_method->slug();
1043
-                    $this->_save_selected_method_of_payment();
1044
-                    $default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1045
-                } else {
1046
-                    $available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1047
-                }
1048
-                $payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1049
-                    $this->_payment_method_billing_info(
1050
-                        $payment_method
1051
-                    );
1052
-            }
1053
-        }
1054
-        // prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1055
-        // of PMs
1056
-        $available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1057
-        // now generate the actual form  inputs
1058
-        $available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1059
-            $available_payment_method_options
1060
-        );
1061
-        $available_payment_methods                              += $payment_methods_billing_info;
1062
-        // build the available payment methods form
1063
-        return new EE_Form_Section_Proper(
1064
-            [
1065
-                'html_id'         => 'spco-available-methods-of-payment-dv',
1066
-                'subsections'     => $available_payment_methods,
1067
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1068
-            ]
1069
-        );
1070
-    }
1071
-
1072
-
1073
-    /**
1074
-     * _get_available_payment_methods
1075
-     *
1076
-     * @return EE_Payment_Method[]
1077
-     * @throws EE_Error
1078
-     * @throws InvalidArgumentException
1079
-     * @throws InvalidDataTypeException
1080
-     * @throws InvalidInterfaceException
1081
-     */
1082
-    protected function _get_available_payment_methods()
1083
-    {
1084
-        if (! empty($this->checkout->available_payment_methods)) {
1085
-            return $this->checkout->available_payment_methods;
1086
-        }
1087
-        $available_payment_methods = [];
1088
-        $EEM_Payment_Method        = EEM_Payment_Method::instance();
1089
-        // get all active payment methods
1090
-        $payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1091
-            $this->checkout->transaction,
1092
-            EEM_Payment_Method::scope_cart
1093
-        );
1094
-        foreach ($payment_methods as $payment_method) {
1095
-            if ($payment_method instanceof EE_Payment_Method) {
1096
-                $available_payment_methods[ $payment_method->slug() ] = $payment_method;
1097
-            }
1098
-        }
1099
-        return $available_payment_methods;
1100
-    }
1101
-
1102
-
1103
-    /**
1104
-     *    _available_payment_method_inputs
1105
-     *
1106
-     * @access    private
1107
-     * @param array $available_payment_method_options
1108
-     * @return    EE_Form_Section_Proper
1109
-     * @throws EE_Error
1110
-     * @throws EE_Error
1111
-     */
1112
-    private function _available_payment_method_inputs($available_payment_method_options = [])
1113
-    {
1114
-        // generate inputs
1115
-        return new EE_Form_Section_Proper(
1116
-            [
1117
-                'html_id'         => 'ee-available-payment-method-inputs',
1118
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1119
-                'subsections'     => [
1120
-                    '' => new EE_Radio_Button_Input(
1121
-                        $available_payment_method_options,
1122
-                        [
1123
-                            'html_name'          => 'selected_method_of_payment',
1124
-                            'html_class'         => 'spco-payment-method',
1125
-                            'default'            => $this->checkout->selected_method_of_payment,
1126
-                            'label_size'         => 11,
1127
-                            'enforce_label_size' => true,
1128
-                        ]
1129
-                    ),
1130
-                ],
1131
-            ]
1132
-        );
1133
-    }
1134
-
1135
-
1136
-    /**
1137
-     *    _payment_method_billing_info
1138
-     *
1139
-     * @access    private
1140
-     * @param EE_Payment_Method $payment_method
1141
-     * @return EE_Form_Section_Proper
1142
-     * @throws EE_Error
1143
-     * @throws InvalidArgumentException
1144
-     * @throws InvalidDataTypeException
1145
-     * @throws InvalidInterfaceException
1146
-     */
1147
-    private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1148
-    {
1149
-        $currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1150
-        // generate the billing form for payment method
1151
-        $billing_form                 = $currently_selected
1152
-            ? $this->_get_billing_form_for_payment_method($payment_method)
1153
-            : new EE_Form_Section_HTML();
1154
-        $this->checkout->billing_form = $currently_selected
1155
-            ? $billing_form
1156
-            : $this->checkout->billing_form;
1157
-        // it's all in the details
1158
-        $info_html = EEH_HTML::h3(
1159
-            esc_html__('Important information regarding your payment', 'event_espresso'),
1160
-            '',
1161
-            'spco-payment-method-hdr'
1162
-        );
1163
-        // add some info regarding the step, either from what's saved in the admin,
1164
-        // or a default string depending on whether the PM has a billing form or not
1165
-        if ($payment_method->description()) {
1166
-            $payment_method_info = $payment_method->description();
1167
-        } elseif ($billing_form instanceof EE_Billing_Info_Form) {
1168
-            $payment_method_info = sprintf(
1169
-                esc_html__(
1170
-                    'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1171
-                    'event_espresso'
1172
-                ),
1173
-                $this->submit_button_text()
1174
-            );
1175
-        } else {
1176
-            $payment_method_info = sprintf(
1177
-                esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1178
-                $this->submit_button_text()
1179
-            );
1180
-        }
1181
-        $info_html .= EEH_HTML::div(
1182
-            apply_filters(
1183
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1184
-                $payment_method_info
1185
-            ),
1186
-            '',
1187
-            'spco-payment-method-desc ee-attention'
1188
-        );
1189
-        return new EE_Form_Section_Proper(
1190
-            [
1191
-                'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1192
-                'html_class'      => 'spco-payment-method-info-dv',
1193
-                // only display the selected or default PM
1194
-                'html_style'      => $currently_selected ? '' : 'display:none;',
1195
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
1196
-                'subsections'     => [
1197
-                    'info'         => new EE_Form_Section_HTML($info_html),
1198
-                    'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1199
-                ],
1200
-            ]
1201
-        );
1202
-    }
1203
-
1204
-
1205
-    /**
1206
-     * get_billing_form_html_for_payment_method
1207
-     *
1208
-     * @return bool
1209
-     * @throws EE_Error
1210
-     * @throws InvalidArgumentException
1211
-     * @throws ReflectionException
1212
-     * @throws InvalidDataTypeException
1213
-     * @throws InvalidInterfaceException
1214
-     */
1215
-    public function get_billing_form_html_for_payment_method()
1216
-    {
1217
-        // how have they chosen to pay?
1218
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1219
-        $this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1220
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1221
-            return false;
1222
-        }
1223
-        if (
1224
-            apply_filters(
1225
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1226
-                false
1227
-            )
1228
-        ) {
1229
-            EE_Error::add_success(
1230
-                apply_filters(
1231
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1232
-                    sprintf(
1233
-                        esc_html__(
1234
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1235
-                            'event_espresso'
1236
-                        ),
1237
-                        $this->checkout->payment_method->name()
1238
-                    )
1239
-                )
1240
-            );
1241
-        }
1242
-        // now generate billing form for selected method of payment
1243
-        $payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1244
-        // fill form with attendee info if applicable
1245
-        if (
1246
-            $payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1247
-            && $this->checkout->transaction_has_primary_registrant()
1248
-        ) {
1249
-            $payment_method_billing_form->populate_from_attendee(
1250
-                $this->checkout->transaction->primary_registration()->attendee()
1251
-            );
1252
-        }
1253
-        // and debug content
1254
-        if (
1255
-            $payment_method_billing_form instanceof EE_Billing_Info_Form
1256
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1257
-        ) {
1258
-            $payment_method_billing_form =
1259
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1260
-                    $payment_method_billing_form
1261
-                );
1262
-        }
1263
-        $billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1264
-            ? $payment_method_billing_form->get_html()
1265
-            : '';
1266
-        $this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1267
-        // localize validation rules for main form
1268
-        $this->checkout->current_step->reg_form->localize_validation_rules();
1269
-        $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1270
-        return true;
1271
-    }
1272
-
1273
-
1274
-    /**
1275
-     * _get_billing_form_for_payment_method
1276
-     *
1277
-     * @param EE_Payment_Method $payment_method
1278
-     * @return EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form|EE_Form_Section_HTML
1279
-     * @throws EE_Error
1280
-     * @throws InvalidArgumentException
1281
-     * @throws InvalidDataTypeException
1282
-     * @throws InvalidInterfaceException
1283
-     */
1284
-    private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1285
-    {
1286
-        $billing_form = $payment_method->type_obj()->billing_form(
1287
-            $this->checkout->transaction,
1288
-            ['amount_owing' => $this->checkout->amount_owing]
1289
-        );
1290
-        if ($billing_form instanceof EE_Billing_Info_Form) {
1291
-            if (
1292
-                apply_filters(
1293
-                    'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1294
-                    false
1295
-                )
1296
-                && $this->request->requestParamIsSet('payment_method')
1297
-            ) {
1298
-                EE_Error::add_success(
1299
-                    apply_filters(
1300
-                        'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1301
-                        sprintf(
1302
-                            esc_html__(
1303
-                                'You have selected "%s" as your method of payment. Please note the important payment information below.',
1304
-                                'event_espresso'
1305
-                            ),
1306
-                            $payment_method->name()
1307
-                        )
1308
-                    )
1309
-                );
1310
-            }
1311
-            return apply_filters(
1312
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1313
-                $billing_form,
1314
-                $payment_method
1315
-            );
1316
-        }
1317
-        // no actual billing form, so return empty HTML form section
1318
-        return new EE_Form_Section_HTML();
1319
-    }
1320
-
1321
-
1322
-    /**
1323
-     * _get_selected_method_of_payment
1324
-     *
1325
-     * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1326
-     *                          is not found in the incoming request
1327
-     * @param string  $request_param
1328
-     * @return NULL|string
1329
-     * @throws EE_Error
1330
-     * @throws InvalidArgumentException
1331
-     * @throws InvalidDataTypeException
1332
-     * @throws InvalidInterfaceException
1333
-     */
1334
-    private function _get_selected_method_of_payment(
1335
-        $required = false,
1336
-        $request_param = 'selected_method_of_payment'
1337
-    ) {
1338
-        // is selected_method_of_payment set in the request ?
1339
-        $selected_method_of_payment = $this->request->getRequestParam($request_param);
1340
-        if ($selected_method_of_payment) {
1341
-            // sanitize it
1342
-            $selected_method_of_payment = is_array($selected_method_of_payment)
1343
-                ? array_shift($selected_method_of_payment)
1344
-                : $selected_method_of_payment;
1345
-            $selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1346
-            // store it in the session so that it's available for all subsequent requests including AJAX
1347
-            $this->_save_selected_method_of_payment($selected_method_of_payment);
1348
-        } else {
1349
-            // or is is set in the session ?
1350
-            $selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1351
-                'selected_method_of_payment'
1352
-            );
1353
-        }
1354
-        // do ya really really gotta have it?
1355
-        if (empty($selected_method_of_payment) && $required) {
1356
-            EE_Error::add_error(
1357
-                sprintf(
1358
-                    esc_html__(
1359
-                        '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.',
1360
-                        'event_espresso'
1361
-                    ),
1362
-                    '<br/>',
1363
-                    '<br/>',
1364
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
1365
-                ),
1366
-                __FILE__,
1367
-                __FUNCTION__,
1368
-                __LINE__
1369
-            );
1370
-            return null;
1371
-        }
1372
-        return $selected_method_of_payment;
1373
-    }
1374
-
1375
-
1376
-
1377
-
1378
-
1379
-
1380
-    /********************************************************************************************************/
1381
-    /***********************************  SWITCH PAYMENT METHOD  ************************************/
1382
-    /********************************************************************************************************/
1383
-    /**
1384
-     * switch_payment_method
1385
-     *
1386
-     * @return bool
1387
-     * @throws EE_Error
1388
-     * @throws InvalidArgumentException
1389
-     * @throws InvalidDataTypeException
1390
-     * @throws InvalidInterfaceException
1391
-     * @throws ReflectionException
1392
-     */
1393
-    public function switch_payment_method()
1394
-    {
1395
-        if (! $this->_verify_payment_method_is_set()) {
1396
-            return false;
1397
-        }
1398
-        if (
1399
-            apply_filters(
1400
-                'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1401
-                false
1402
-            )
1403
-        ) {
1404
-            EE_Error::add_success(
1405
-                apply_filters(
1406
-                    'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1407
-                    sprintf(
1408
-                        esc_html__(
1409
-                            'You have selected "%s" as your method of payment. Please note the important payment information below.',
1410
-                            'event_espresso'
1411
-                        ),
1412
-                        $this->checkout->payment_method->name()
1413
-                    )
1414
-                )
1415
-            );
1416
-        }
1417
-        // generate billing form for selected method of payment if it hasn't been done already
1418
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1419
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1420
-                $this->checkout->payment_method
1421
-            );
1422
-        }
1423
-        // fill form with attendee info if applicable
1424
-        if (
1425
-            apply_filters(
1426
-                'FHEE__populate_billing_form_fields_from_attendee',
1427
-                (
1428
-                    $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1429
-                    && $this->checkout->transaction_has_primary_registrant()
1430
-                ),
1431
-                $this->checkout->billing_form,
1432
-                $this->checkout->transaction
1433
-            )
1434
-        ) {
1435
-            $this->checkout->billing_form->populate_from_attendee(
1436
-                $this->checkout->transaction->primary_registration()->attendee()
1437
-            );
1438
-        }
1439
-        // and debug content
1440
-        if (
1441
-            $this->checkout->billing_form instanceof EE_Billing_Info_Form
1442
-            && $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1443
-        ) {
1444
-            $this->checkout->billing_form =
1445
-                $this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1446
-                    $this->checkout->billing_form
1447
-                );
1448
-        }
1449
-        // get html and validation rules for form
1450
-        if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1451
-            $this->checkout->json_response->set_return_data(
1452
-                ['payment_method_info' => $this->checkout->billing_form->get_html()]
1453
-            );
1454
-            // localize validation rules for main form
1455
-            $this->checkout->billing_form->localize_validation_rules(true);
1456
-            $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1457
-        } else {
1458
-            $this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1459
-        }
1460
-        // prevents advancement to next step
1461
-        $this->checkout->continue_reg = false;
1462
-        return true;
1463
-    }
1464
-
1465
-
1466
-    /**
1467
-     * _verify_payment_method_is_set
1468
-     *
1469
-     * @return bool
1470
-     * @throws EE_Error
1471
-     * @throws InvalidArgumentException
1472
-     * @throws ReflectionException
1473
-     * @throws InvalidDataTypeException
1474
-     * @throws InvalidInterfaceException
1475
-     */
1476
-    protected function _verify_payment_method_is_set()
1477
-    {
1478
-        // generate billing form for selected method of payment if it hasn't been done already
1479
-        if (empty($this->checkout->selected_method_of_payment)) {
1480
-            // how have they chosen to pay?
1481
-            $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1482
-        } else {
1483
-            // choose your own adventure based on method_of_payment
1484
-            switch ($this->checkout->selected_method_of_payment) {
1485
-                case 'events_sold_out':
1486
-                    EE_Error::add_attention(
1487
-                        apply_filters(
1488
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1489
-                            esc_html__(
1490
-                                '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.',
1491
-                                'event_espresso'
1492
-                            )
1493
-                        ),
1494
-                        __FILE__,
1495
-                        __FUNCTION__,
1496
-                        __LINE__
1497
-                    );
1498
-                    return false;
1499
-                case 'payments_closed':
1500
-                    EE_Error::add_attention(
1501
-                        apply_filters(
1502
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1503
-                            esc_html__(
1504
-                                '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.',
1505
-                                'event_espresso'
1506
-                            )
1507
-                        ),
1508
-                        __FILE__,
1509
-                        __FUNCTION__,
1510
-                        __LINE__
1511
-                    );
1512
-                    return false;
1513
-                case 'no_payment_required':
1514
-                    EE_Error::add_attention(
1515
-                        apply_filters(
1516
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1517
-                            esc_html__(
1518
-                                '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.',
1519
-                                'event_espresso'
1520
-                            )
1521
-                        ),
1522
-                        __FILE__,
1523
-                        __FUNCTION__,
1524
-                        __LINE__
1525
-                    );
1526
-                    return false;
1527
-                default:
1528
-            }
1529
-        }
1530
-        // verify payment method
1531
-        if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1532
-            // get payment method for selected method of payment
1533
-            $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1534
-        }
1535
-        return $this->checkout->payment_method instanceof EE_Payment_Method;
1536
-    }
1537
-
1538
-
1539
-
1540
-    /********************************************************************************************************/
1541
-    /***************************************  SAVE PAYER DETAILS  ****************************************/
1542
-    /********************************************************************************************************/
1543
-    /**
1544
-     * save_payer_details_via_ajax
1545
-     *
1546
-     * @return void
1547
-     * @throws EE_Error
1548
-     * @throws InvalidArgumentException
1549
-     * @throws ReflectionException
1550
-     * @throws RuntimeException
1551
-     * @throws InvalidDataTypeException
1552
-     * @throws InvalidInterfaceException
1553
-     */
1554
-    public function save_payer_details_via_ajax()
1555
-    {
1556
-        if (! $this->_verify_payment_method_is_set()) {
1557
-            return;
1558
-        }
1559
-        // generate billing form for selected method of payment if it hasn't been done already
1560
-        if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1561
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1562
-                $this->checkout->payment_method
1563
-            );
1564
-        }
1565
-        // generate primary attendee from payer info if applicable
1566
-        if (! $this->checkout->transaction_has_primary_registrant()) {
1567
-            $attendee = $this->_create_attendee_from_request_data();
1568
-            if ($attendee instanceof EE_Attendee) {
1569
-                foreach ($this->checkout->transaction->registrations() as $registration) {
1570
-                    if ($registration->is_primary_registrant()) {
1571
-                        $this->checkout->primary_attendee_obj = $attendee;
1572
-                        $registration->_add_relation_to($attendee, 'Attendee');
1573
-                        $registration->set_attendee_id($attendee->ID());
1574
-                        $registration->update_cache_after_object_save('Attendee', $attendee);
1575
-                    }
1576
-                }
1577
-            }
1578
-        }
1579
-    }
1580
-
1581
-
1582
-    /**
1583
-     * create_attendee_from_request_data
1584
-     * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1585
-     *
1586
-     * @return EE_Attendee
1587
-     * @throws EE_Error
1588
-     * @throws InvalidArgumentException
1589
-     * @throws ReflectionException
1590
-     * @throws InvalidDataTypeException
1591
-     * @throws InvalidInterfaceException
1592
-     */
1593
-    protected function _create_attendee_from_request_data()
1594
-    {
1595
-        // get State ID
1596
-        $STA_ID = $this->request->getRequestParam('state');
1597
-        if (! empty($STA_ID)) {
1598
-            // can we get state object from name ?
1599
-            EE_Registry::instance()->load_model('State');
1600
-            $state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1601
-            $STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1602
-        }
1603
-        // get Country ISO
1604
-        $CNT_ISO = $this->request->getRequestParam('country');
1605
-        if (! empty($CNT_ISO)) {
1606
-            // can we get country object from name ?
1607
-            EE_Registry::instance()->load_model('Country');
1608
-            $country = EEM_Country::instance()->get_col(
1609
-                [['CNT_name' => $CNT_ISO], 'limit' => 1],
1610
-                'CNT_ISO'
1611
-            );
1612
-            $CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1613
-        }
1614
-        // grab attendee data
1615
-        $attendee_data = [
1616
-            'ATT_fname'    => $this->request->getRequestParam('first_name'),
1617
-            'ATT_lname'    => $this->request->getRequestParam('last_name'),
1618
-            'ATT_email'    => $this->request->getRequestParam('email'),
1619
-            'ATT_address'  => $this->request->getRequestParam('address'),
1620
-            'ATT_address2' => $this->request->getRequestParam('address2'),
1621
-            'ATT_city'     => $this->request->getRequestParam('city'),
1622
-            'STA_ID'       => $STA_ID,
1623
-            'CNT_ISO'      => $CNT_ISO,
1624
-            'ATT_zip'      => $this->request->getRequestParam('zip'),
1625
-            'ATT_phone'    => $this->request->getRequestParam('phone'),
1626
-        ];
1627
-        // validate the email address since it is the most important piece of info
1628
-        if (empty($attendee_data['ATT_email'])) {
1629
-            EE_Error::add_error(
1630
-                esc_html__('An invalid email address was submitted.', 'event_espresso'),
1631
-                __FILE__,
1632
-                __FUNCTION__,
1633
-                __LINE__
1634
-            );
1635
-        }
1636
-        // does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1637
-        // AND email address
1638
-        if (
1639
-            ! empty($attendee_data['ATT_fname'])
1640
-            && ! empty($attendee_data['ATT_lname'])
1641
-            && ! empty($attendee_data['ATT_email'])
1642
-        ) {
1643
-            $existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1644
-                [
1645
-                    'ATT_fname' => $attendee_data['ATT_fname'],
1646
-                    'ATT_lname' => $attendee_data['ATT_lname'],
1647
-                    'ATT_email' => $attendee_data['ATT_email'],
1648
-                ]
1649
-            );
1650
-            if ($existing_attendee instanceof EE_Attendee) {
1651
-                return $existing_attendee;
1652
-            }
1653
-        }
1654
-        // no existing attendee? kk let's create a new one
1655
-        // kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1656
-        // don't exist
1657
-        $attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1658
-            ? $attendee_data['ATT_fname']
1659
-            : $attendee_data['ATT_email'];
1660
-        $attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1661
-            ? $attendee_data['ATT_lname']
1662
-            : $attendee_data['ATT_email'];
1663
-        return EE_Attendee::new_instance($attendee_data);
1664
-    }
1665
-
1666
-
1667
-
1668
-    /********************************************************************************************************/
1669
-    /****************************************  PROCESS REG STEP  *****************************************/
1670
-    /********************************************************************************************************/
1671
-    /**
1672
-     * process_reg_step
1673
-     *
1674
-     * @return bool
1675
-     * @throws EE_Error
1676
-     * @throws InvalidArgumentException
1677
-     * @throws ReflectionException
1678
-     * @throws EntityNotFoundException
1679
-     * @throws InvalidDataTypeException
1680
-     * @throws InvalidInterfaceException
1681
-     * @throws InvalidStatusException
1682
-     */
1683
-    public function process_reg_step()
1684
-    {
1685
-        // how have they chosen to pay?
1686
-        $this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1687
-            ? 'no_payment_required'
1688
-            : $this->_get_selected_method_of_payment(true);
1689
-        // choose your own adventure based on method_of_payment
1690
-        switch ($this->checkout->selected_method_of_payment) {
1691
-            case 'events_sold_out':
1692
-                $this->checkout->redirect     = true;
1693
-                $this->checkout->redirect_url = $this->checkout->cancel_page_url;
1694
-                $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1695
-                // mark this reg step as completed
1696
-                $this->set_completed();
1697
-                return false;
1698
-
1699
-            case 'payments_closed':
1700
-                if (
1701
-                    apply_filters(
1702
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1703
-                        false
1704
-                    )
1705
-                ) {
1706
-                    EE_Error::add_success(
1707
-                        esc_html__('no payment required at this time.', 'event_espresso'),
1708
-                        __FILE__,
1709
-                        __FUNCTION__,
1710
-                        __LINE__
1711
-                    );
1712
-                }
1713
-                // mark this reg step as completed
1714
-                $this->set_completed();
1715
-                return true;
1716
-
1717
-            case 'no_payment_required':
1718
-                if (
1719
-                    apply_filters(
1720
-                        'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1721
-                        false
1722
-                    )
1723
-                ) {
1724
-                    EE_Error::add_success(
1725
-                        esc_html__('no payment required.', 'event_espresso'),
1726
-                        __FILE__,
1727
-                        __FUNCTION__,
1728
-                        __LINE__
1729
-                    );
1730
-                }
1731
-                // mark this reg step as completed
1732
-                $this->set_completed();
1733
-                return true;
1734
-
1735
-            default:
1736
-                $registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1737
-                    EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1738
-                );
1739
-                $ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1740
-                    $registrations,
1741
-                    EE_Registry::instance()->SSN->checkout()->revisit
1742
-                );
1743
-                // calculate difference between the two arrays
1744
-                $registrations = array_diff($registrations, $ejected_registrations);
1745
-                if (empty($registrations)) {
1746
-                    $this->_redirect_because_event_sold_out();
1747
-                    return false;
1748
-                }
1749
-                $payment_successful = $this->_process_payment();
1750
-                if ($payment_successful) {
1751
-                    $this->checkout->continue_reg = true;
1752
-                    $this->_maybe_set_completed($this->checkout->payment_method);
1753
-                } else {
1754
-                    $this->checkout->continue_reg = false;
1755
-                }
1756
-                return $payment_successful;
1757
-        }
1758
-    }
1759
-
1760
-
1761
-    /**
1762
-     * _redirect_because_event_sold_out
1763
-     *
1764
-     * @return void
1765
-     */
1766
-    protected function _redirect_because_event_sold_out()
1767
-    {
1768
-        $this->checkout->continue_reg = false;
1769
-        // set redirect URL
1770
-        $this->checkout->redirect_url = add_query_arg(
1771
-            ['e_reg_url_link' => $this->checkout->reg_url_link],
1772
-            $this->checkout->current_step->reg_step_url()
1773
-        );
1774
-        $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1775
-    }
1776
-
1777
-
1778
-    /**
1779
-     * _maybe_set_completed
1780
-     *
1781
-     * @param EE_Payment_Method $payment_method
1782
-     * @return void
1783
-     * @throws EE_Error
1784
-     */
1785
-    protected function _maybe_set_completed(EE_Payment_Method $payment_method)
1786
-    {
1787
-        switch ($payment_method->type_obj()->payment_occurs()) {
1788
-            case EE_PMT_Base::offsite:
1789
-                break;
1790
-            case EE_PMT_Base::onsite:
1791
-            case EE_PMT_Base::offline:
1792
-                // mark this reg step as completed
1793
-                $this->set_completed();
1794
-                break;
1795
-        }
1796
-    }
1797
-
1798
-
1799
-    /**
1800
-     *    update_reg_step
1801
-     *    this is the final step after a user  revisits the site to retry a payment
1802
-     *
1803
-     * @return bool
1804
-     * @throws EE_Error
1805
-     * @throws InvalidArgumentException
1806
-     * @throws ReflectionException
1807
-     * @throws EntityNotFoundException
1808
-     * @throws InvalidDataTypeException
1809
-     * @throws InvalidInterfaceException
1810
-     * @throws InvalidStatusException
1811
-     */
1812
-    public function update_reg_step()
1813
-    {
1814
-        $success = true;
1815
-        // if payment required
1816
-        if ($this->checkout->transaction->total() > 0) {
1817
-            do_action(
1818
-                'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1819
-                $this->checkout->transaction
1820
-            );
1821
-            // attempt payment via payment method
1822
-            $success = $this->process_reg_step();
1823
-        }
1824
-        if ($success && ! $this->checkout->redirect) {
1825
-            $this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1826
-                $this->checkout->transaction->ID()
1827
-            );
1828
-            // set return URL
1829
-            $this->checkout->redirect_url = add_query_arg(
1830
-                ['e_reg_url_link' => $this->checkout->reg_url_link],
1831
-                $this->checkout->thank_you_page_url
1832
-            );
1833
-        }
1834
-        return $success;
1835
-    }
1836
-
1837
-
1838
-    /**
1839
-     *    _process_payment
1840
-     *
1841
-     * @return bool
1842
-     * @throws EE_Error
1843
-     * @throws InvalidArgumentException
1844
-     * @throws ReflectionException
1845
-     * @throws RuntimeException
1846
-     * @throws InvalidDataTypeException
1847
-     * @throws InvalidInterfaceException
1848
-     */
1849
-    private function _process_payment()
1850
-    {
1851
-        // basically confirm that the event hasn't sold out since they hit the page
1852
-        if (! $this->_last_second_ticket_verifications()) {
1853
-            return false;
1854
-        }
1855
-        // ya gotta make a choice man
1856
-        if (empty($this->checkout->selected_method_of_payment)) {
1857
-            $this->checkout->json_response->set_plz_select_method_of_payment(
1858
-                esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1859
-            );
1860
-            return false;
1861
-        }
1862
-        // get EE_Payment_Method object
1863
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1864
-            return false;
1865
-        }
1866
-        // setup billing form
1867
-        if ($this->checkout->payment_method->is_on_site()) {
1868
-            $this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1869
-                $this->checkout->payment_method
1870
-            );
1871
-            // bad billing form ?
1872
-            if (! $this->_billing_form_is_valid()) {
1873
-                return false;
1874
-            }
1875
-        }
1876
-        // ensure primary registrant has been fully processed
1877
-        if (! $this->_setup_primary_registrant_prior_to_payment()) {
1878
-            return false;
1879
-        }
1880
-        // if session is close to expiring (under 10 minutes by default)
1881
-        if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1882
-            // add some time to session expiration so that payment can be completed
1883
-            EE_Registry::instance()->SSN->extend_expiration();
1884
-        }
1885
-        /** @type EE_Transaction_Processor $transaction_processor */
1886
-        // $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1887
-        // in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1888
-        // for events with a default reg status of Approved
1889
-        // $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1890
-        //      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1891
-        // );
1892
-        // attempt payment
1893
-        $payment = $this->_attempt_payment($this->checkout->payment_method);
1894
-        // process results
1895
-        $payment = $this->_validate_payment($payment);
1896
-        $payment = $this->_post_payment_processing($payment);
1897
-        // verify payment
1898
-        if ($payment instanceof EE_Payment) {
1899
-            // store that for later
1900
-            $this->checkout->payment = $payment;
1901
-            // we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1902
-            $this->checkout->transaction->toggle_failed_transaction_status();
1903
-            $payment_status = $payment->status();
1904
-            return $payment_status === EEM_Payment::status_id_approved
1905
-                   || $payment_status === EEM_Payment::status_id_pending;
1906
-        }
1907
-        if ($payment === true) {
1908
-            // please note that offline payment methods will NOT make a payment,
1909
-            // but instead just mark themselves as the PMD_ID on the transaction, and return true
1910
-            $this->checkout->payment = $payment;
1911
-            return true;
1912
-        }
1913
-        // where's my money?
1914
-        return false;
1915
-    }
1916
-
1917
-
1918
-    /**
1919
-     * _last_second_ticket_verifications
1920
-     *
1921
-     * @return bool
1922
-     * @throws EE_Error
1923
-     * @throws ReflectionException
1924
-     */
1925
-    protected function _last_second_ticket_verifications()
1926
-    {
1927
-        // don't bother re-validating if not a return visit
1928
-        if (! $this->checkout->revisit) {
1929
-            return true;
1930
-        }
1931
-        $registrations = $this->checkout->transaction->registrations();
1932
-        if (empty($registrations)) {
1933
-            return false;
1934
-        }
1935
-        foreach ($registrations as $registration) {
1936
-            if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1937
-                $event = $registration->event_obj();
1938
-                if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1939
-                    EE_Error::add_error(
1940
-                        apply_filters(
1941
-                            'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1942
-                            sprintf(
1943
-                                esc_html__(
1944
-                                    '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.',
1945
-                                    'event_espresso'
1946
-                                ),
1947
-                                $event->name()
1948
-                            )
1949
-                        ),
1950
-                        __FILE__,
1951
-                        __FUNCTION__,
1952
-                        __LINE__
1953
-                    );
1954
-                    return false;
1955
-                }
1956
-            }
1957
-        }
1958
-        return true;
1959
-    }
1960
-
1961
-
1962
-    /**
1963
-     * redirect_form
1964
-     *
1965
-     * @return bool
1966
-     * @throws EE_Error
1967
-     * @throws InvalidArgumentException
1968
-     * @throws ReflectionException
1969
-     * @throws InvalidDataTypeException
1970
-     * @throws InvalidInterfaceException
1971
-     */
1972
-    public function redirect_form()
1973
-    {
1974
-        $payment_method_billing_info = $this->_payment_method_billing_info(
1975
-            $this->_get_payment_method_for_selected_method_of_payment()
1976
-        );
1977
-        $html                        = $payment_method_billing_info->get_html();
1978
-        $html                        .= $this->checkout->redirect_form;
1979
-        /** @var ResponseInterface $response */
1980
-        $response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1981
-        $response->addOutput($html);
1982
-        return true;
1983
-    }
1984
-
1985
-
1986
-    /**
1987
-     * _billing_form_is_valid
1988
-     *
1989
-     * @return bool
1990
-     * @throws EE_Error
1991
-     */
1992
-    private function _billing_form_is_valid()
1993
-    {
1994
-        if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1995
-            return true;
1996
-        }
1997
-        if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1998
-            if ($this->checkout->billing_form->was_submitted()) {
1999
-                $this->checkout->billing_form->receive_form_submission();
2000
-                if ($this->checkout->billing_form->is_valid()) {
2001
-                    return true;
2002
-                }
2003
-                $validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
2004
-                $error_strings     = [];
2005
-                foreach ($validation_errors as $validation_error) {
2006
-                    if ($validation_error instanceof EE_Validation_Error) {
2007
-                        $form_section = $validation_error->get_form_section();
2008
-                        if ($form_section instanceof EE_Form_Input_Base) {
2009
-                            $label = $form_section->html_label_text();
2010
-                        } elseif ($form_section instanceof EE_Form_Section_Base) {
2011
-                            $label = $form_section->name();
2012
-                        } else {
2013
-                            $label = esc_html__('Validation Error', 'event_espresso');
2014
-                        }
2015
-                        $error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2016
-                    }
2017
-                }
2018
-                EE_Error::add_error(
2019
-                    sprintf(
2020
-                        esc_html__(
2021
-                            'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2022
-                            'event_espresso'
2023
-                        ),
2024
-                        '<br/>',
2025
-                        implode('<br/>', $error_strings)
2026
-                    ),
2027
-                    __FILE__,
2028
-                    __FUNCTION__,
2029
-                    __LINE__
2030
-                );
2031
-            } else {
2032
-                EE_Error::add_error(
2033
-                    esc_html__(
2034
-                        'The billing form was not submitted or something prevented it\'s submission.',
2035
-                        'event_espresso'
2036
-                    ),
2037
-                    __FILE__,
2038
-                    __FUNCTION__,
2039
-                    __LINE__
2040
-                );
2041
-            }
2042
-        } else {
2043
-            EE_Error::add_error(
2044
-                esc_html__(
2045
-                    'The submitted billing form is invalid possibly due to a technical reason.',
2046
-                    'event_espresso'
2047
-                ),
2048
-                __FILE__,
2049
-                __FUNCTION__,
2050
-                __LINE__
2051
-            );
2052
-        }
2053
-        return false;
2054
-    }
2055
-
2056
-
2057
-    /**
2058
-     * _setup_primary_registrant_prior_to_payment
2059
-     * ensures that the primary registrant has a valid attendee object created with the critical details populated
2060
-     * (first & last name & email) and that both the transaction object and primary registration object have been saved
2061
-     * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2062
-     * yet)
2063
-     *
2064
-     * @return bool
2065
-     * @throws EE_Error
2066
-     * @throws InvalidArgumentException
2067
-     * @throws ReflectionException
2068
-     * @throws RuntimeException
2069
-     * @throws InvalidDataTypeException
2070
-     * @throws InvalidInterfaceException
2071
-     */
2072
-    private function _setup_primary_registrant_prior_to_payment()
2073
-    {
2074
-        // check if transaction has a primary registrant and that it has a related Attendee object
2075
-        // if not, then we need to at least gather some primary registrant data before attempting payment
2076
-        if (
2077
-            $this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2078
-            && ! $this->checkout->transaction_has_primary_registrant()
2079
-            && ! $this->_capture_primary_registration_data_from_billing_form()
2080
-        ) {
2081
-            return false;
2082
-        }
2083
-        // because saving an object clears it's cache, we need to do the chevy shuffle
2084
-        // grab the primary_registration object
2085
-        $primary_registration = $this->checkout->transaction->primary_registration();
2086
-        // at this point we'll consider a TXN to not have been failed
2087
-        $this->checkout->transaction->toggle_failed_transaction_status();
2088
-        // save the TXN ( which clears cached copy of primary_registration)
2089
-        $this->checkout->transaction->save();
2090
-        // grab TXN ID and save it to the primary_registration
2091
-        $primary_registration->set_transaction_id($this->checkout->transaction->ID());
2092
-        // save what we have so far
2093
-        $primary_registration->save();
2094
-        return true;
2095
-    }
2096
-
2097
-
2098
-    /**
2099
-     * _capture_primary_registration_data_from_billing_form
2100
-     *
2101
-     * @return bool
2102
-     * @throws EE_Error
2103
-     * @throws InvalidArgumentException
2104
-     * @throws ReflectionException
2105
-     * @throws InvalidDataTypeException
2106
-     * @throws InvalidInterfaceException
2107
-     */
2108
-    private function _capture_primary_registration_data_from_billing_form()
2109
-    {
2110
-        // convert billing form data into an attendee
2111
-        $this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2112
-        if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2113
-            EE_Error::add_error(
2114
-                sprintf(
2115
-                    esc_html__(
2116
-                        'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2117
-                        'event_espresso'
2118
-                    ),
2119
-                    '<br/>',
2120
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2121
-                ),
2122
-                __FILE__,
2123
-                __FUNCTION__,
2124
-                __LINE__
2125
-            );
2126
-            return false;
2127
-        }
2128
-        $primary_registration = $this->checkout->transaction->primary_registration();
2129
-        if (! $primary_registration instanceof EE_Registration) {
2130
-            EE_Error::add_error(
2131
-                sprintf(
2132
-                    esc_html__(
2133
-                        'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2134
-                        'event_espresso'
2135
-                    ),
2136
-                    '<br/>',
2137
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2138
-                ),
2139
-                __FILE__,
2140
-                __FUNCTION__,
2141
-                __LINE__
2142
-            );
2143
-            return false;
2144
-        }
2145
-        if (
2146
-            ! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2147
-              instanceof
2148
-              EE_Attendee
2149
-        ) {
2150
-            EE_Error::add_error(
2151
-                sprintf(
2152
-                    esc_html__(
2153
-                        'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2154
-                        'event_espresso'
2155
-                    ),
2156
-                    '<br/>',
2157
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2158
-                ),
2159
-                __FILE__,
2160
-                __FUNCTION__,
2161
-                __LINE__
2162
-            );
2163
-            return false;
2164
-        }
2165
-        /** @type EE_Registration_Processor $registration_processor */
2166
-        $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2167
-        // at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2168
-        $registration_processor->toggle_incomplete_registration_status_to_default($primary_registration);
2169
-        return true;
2170
-    }
2171
-
2172
-
2173
-    /**
2174
-     * _get_payment_method_for_selected_method_of_payment
2175
-     * retrieves a valid payment method
2176
-     *
2177
-     * @return EE_Payment_Method
2178
-     * @throws EE_Error
2179
-     * @throws InvalidArgumentException
2180
-     * @throws ReflectionException
2181
-     * @throws InvalidDataTypeException
2182
-     * @throws InvalidInterfaceException
2183
-     */
2184
-    private function _get_payment_method_for_selected_method_of_payment()
2185
-    {
2186
-        if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2187
-            $this->_redirect_because_event_sold_out();
2188
-            return null;
2189
-        }
2190
-        // get EE_Payment_Method object
2191
-        if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2192
-            $payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2193
-        } else {
2194
-            // load EEM_Payment_Method
2195
-            EE_Registry::instance()->load_model('Payment_Method');
2196
-            $EEM_Payment_Method = EEM_Payment_Method::instance();
2197
-            $payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2198
-        }
2199
-        // verify $payment_method
2200
-        if (! $payment_method instanceof EE_Payment_Method) {
2201
-            // not a payment
2202
-            EE_Error::add_error(
2203
-                sprintf(
2204
-                    esc_html__(
2205
-                        'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2206
-                        'event_espresso'
2207
-                    ),
2208
-                    '<br/>',
2209
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2210
-                ),
2211
-                __FILE__,
2212
-                __FUNCTION__,
2213
-                __LINE__
2214
-            );
2215
-            return null;
2216
-        }
2217
-        // and verify it has a valid Payment_Method Type object
2218
-        if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2219
-            // not a payment
2220
-            EE_Error::add_error(
2221
-                sprintf(
2222
-                    esc_html__(
2223
-                        'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2224
-                        'event_espresso'
2225
-                    ),
2226
-                    '<br/>',
2227
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2228
-                ),
2229
-                __FILE__,
2230
-                __FUNCTION__,
2231
-                __LINE__
2232
-            );
2233
-            return null;
2234
-        }
2235
-        return $payment_method;
2236
-    }
2237
-
2238
-
2239
-    /**
2240
-     *    _attempt_payment
2241
-     *
2242
-     * @access    private
2243
-     * @type    EE_Payment_Method $payment_method
2244
-     * @return mixed EE_Payment | boolean
2245
-     * @throws EE_Error
2246
-     * @throws InvalidArgumentException
2247
-     * @throws ReflectionException
2248
-     * @throws InvalidDataTypeException
2249
-     * @throws InvalidInterfaceException
2250
-     */
2251
-    private function _attempt_payment(EE_Payment_Method $payment_method)
2252
-    {
2253
-        $payment = null;
2254
-        $this->checkout->transaction->save();
2255
-        $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2256
-        if (! $payment_processor instanceof EE_Payment_Processor) {
2257
-            return false;
2258
-        }
2259
-        try {
2260
-            $payment_processor->set_revisit($this->checkout->revisit);
2261
-            // generate payment object
2262
-            $payment = $payment_processor->process_payment(
2263
-                $payment_method,
2264
-                $this->checkout->transaction,
2265
-                $this->checkout->amount_owing,
2266
-                $this->checkout->billing_form instanceof EE_Billing_Info_Form
2267
-                    ? $this->checkout->billing_form
2268
-                    : null,
2269
-                $this->_get_return_url($payment_method),
2270
-                'CART',
2271
-                $this->checkout->admin_request,
2272
-                true,
2273
-                $this->reg_step_url()
2274
-            );
2275
-        } catch (Exception $e) {
2276
-            $this->_handle_payment_processor_exception($e);
2277
-        }
2278
-        return $payment;
2279
-    }
2280
-
2281
-
2282
-    /**
2283
-     * _handle_payment_processor_exception
2284
-     *
2285
-     * @param Exception $e
2286
-     * @return void
2287
-     * @throws EE_Error
2288
-     * @throws InvalidArgumentException
2289
-     * @throws InvalidDataTypeException
2290
-     * @throws InvalidInterfaceException
2291
-     */
2292
-    protected function _handle_payment_processor_exception(Exception $e)
2293
-    {
2294
-        EE_Error::add_error(
2295
-            sprintf(
2296
-                esc_html__(
2297
-                    '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',
2298
-                    'event_espresso'
2299
-                ),
2300
-                '<br/>',
2301
-                EE_Registry::instance()->CFG->organization->get_pretty('email'),
2302
-                $e->getMessage(),
2303
-                $e->getFile(),
2304
-                $e->getLine()
2305
-            ),
2306
-            __FILE__,
2307
-            __FUNCTION__,
2308
-            __LINE__
2309
-        );
2310
-    }
2311
-
2312
-
2313
-    /**
2314
-     * _get_return_url
2315
-     *
2316
-     * @param EE_Payment_Method $payment_method
2317
-     * @return string
2318
-     * @throws EE_Error
2319
-     * @throws ReflectionException
2320
-     */
2321
-    protected function _get_return_url(EE_Payment_Method $payment_method)
2322
-    {
2323
-        $return_url = '';
2324
-        switch ($payment_method->type_obj()->payment_occurs()) {
2325
-            case EE_PMT_Base::offsite:
2326
-                $return_url = add_query_arg(
2327
-                    [
2328
-                        'action'                     => 'process_gateway_response',
2329
-                        'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2330
-                        'spco_txn'                   => $this->checkout->transaction->ID(),
2331
-                    ],
2332
-                    $this->reg_step_url()
2333
-                );
2334
-                break;
2335
-            case EE_PMT_Base::onsite:
2336
-            case EE_PMT_Base::offline:
2337
-                $return_url = $this->checkout->next_step->reg_step_url();
2338
-                break;
2339
-        }
2340
-        return $return_url;
2341
-    }
2342
-
2343
-
2344
-    /**
2345
-     * _validate_payment
2346
-     *
2347
-     * @param EE_Payment $payment
2348
-     * @return EE_Payment|FALSE
2349
-     * @throws EE_Error
2350
-     * @throws InvalidArgumentException
2351
-     * @throws InvalidDataTypeException
2352
-     * @throws InvalidInterfaceException
2353
-     */
2354
-    private function _validate_payment($payment = null)
2355
-    {
2356
-        if ($this->checkout->payment_method->is_off_line()) {
2357
-            return true;
2358
-        }
2359
-        // verify payment object
2360
-        if (! $payment instanceof EE_Payment) {
2361
-            // not a payment
2362
-            EE_Error::add_error(
2363
-                sprintf(
2364
-                    esc_html__(
2365
-                        'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2366
-                        'event_espresso'
2367
-                    ),
2368
-                    '<br/>',
2369
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2370
-                ),
2371
-                __FILE__,
2372
-                __FUNCTION__,
2373
-                __LINE__
2374
-            );
2375
-            return false;
2376
-        }
2377
-        return $payment;
2378
-    }
2379
-
2380
-
2381
-    /**
2382
-     * _post_payment_processing
2383
-     *
2384
-     * @param EE_Payment|bool $payment
2385
-     * @return bool
2386
-     * @throws EE_Error
2387
-     * @throws InvalidArgumentException
2388
-     * @throws InvalidDataTypeException
2389
-     * @throws InvalidInterfaceException
2390
-     * @throws ReflectionException
2391
-     */
2392
-    private function _post_payment_processing($payment = null)
2393
-    {
2394
-        // Off-Line payment?
2395
-        if ($payment === true) {
2396
-            // $this->_setup_redirect_for_next_step();
2397
-            return true;
2398
-            // On-Site payment?
2399
-        } elseif ($this->checkout->payment_method->is_on_site()) {
2400
-            if (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2401
-                // $this->_setup_redirect_for_next_step();
2402
-                $this->checkout->continue_reg = false;
2403
-            }
2404
-            // Off-Site payment?
2405
-        } elseif ($this->checkout->payment_method->is_off_site()) {
2406
-            // if a payment object was made and it specifies a redirect url, then we'll setup that redirect info
2407
-            if ($payment instanceof EE_Payment && $payment->redirect_url()) {
2408
-                do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2409
-                $this->checkout->redirect      = true;
2410
-                $this->checkout->redirect_form = $payment->redirect_form();
2411
-                $this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2412
-                // set JSON response
2413
-                $this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2414
-                // and lastly, let's bump the payment status to pending
2415
-                $payment->set_status(EEM_Payment::status_id_pending);
2416
-                $payment->save();
2417
-            } else {
2418
-                // we couldn't redirect the user. Let's tell them why.
2419
-                $error_message = sprintf(
2420
-                    esc_html__(
2421
-                        'It appears the Off Site Payment Method was not configured properly.%sPlease try again or contact %s for assistance.',
2422
-                        'event_espresso'
2423
-                    ),
2424
-                    '<br/>',
2425
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2426
-                );
2427
-                if ($payment instanceof EE_Payment && $payment->gateway_response()) {
2428
-                    $error_message = $error_message . '<br/>' . $payment->gateway_response();
2429
-                }
2430
-                $this->checkout->continue_reg = false;
2431
-                EE_Error::add_error(
2432
-                    $error_message,
2433
-                    __FILE__,
2434
-                    __FUNCTION__,
2435
-                    __LINE__
2436
-                );
2437
-            }
2438
-        } else {
2439
-            // ummm ya... not Off-Line, not On-Site, not off-Site ????
2440
-            $this->checkout->continue_reg = false;
2441
-            return false;
2442
-        }
2443
-        return $payment;
2444
-    }
2445
-
2446
-
2447
-    /**
2448
-     *    _process_payment_status
2449
-     *
2450
-     * @type    EE_Payment $payment
2451
-     * @param string       $payment_occurs
2452
-     * @return bool
2453
-     * @throws EE_Error
2454
-     * @throws InvalidArgumentException
2455
-     * @throws InvalidDataTypeException
2456
-     * @throws InvalidInterfaceException
2457
-     */
2458
-    private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2459
-    {
2460
-        // off-line payment? carry on
2461
-        if ($payment_occurs === EE_PMT_Base::offline) {
2462
-            return true;
2463
-        }
2464
-        // verify payment validity
2465
-        if ($payment instanceof EE_Payment) {
2466
-            do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2467
-            $msg = $payment->gateway_response();
2468
-            // check results
2469
-            switch ($payment->status()) {
2470
-                // good payment
2471
-                case EEM_Payment::status_id_approved:
2472
-                    EE_Error::add_success(
2473
-                        esc_html__('Your payment was processed successfully.', 'event_espresso'),
2474
-                        __FILE__,
2475
-                        __FUNCTION__,
2476
-                        __LINE__
2477
-                    );
2478
-                    return true;
2479
-                // slow payment
2480
-                case EEM_Payment::status_id_pending:
2481
-                    if (empty($msg)) {
2482
-                        $msg = esc_html__(
2483
-                            'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2484
-                            'event_espresso'
2485
-                        );
2486
-                    }
2487
-                    EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2488
-                    return true;
2489
-                // don't wanna payment
2490
-                case EEM_Payment::status_id_cancelled:
2491
-                    if (empty($msg)) {
2492
-                        $msg = _n(
2493
-                            'Payment cancelled. Please try again.',
2494
-                            'Payment cancelled. Please try again or select another method of payment.',
2495
-                            count($this->checkout->available_payment_methods),
2496
-                            'event_espresso'
2497
-                        );
2498
-                    }
2499
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2500
-                    return false;
2501
-                // not enough payment
2502
-                case EEM_Payment::status_id_declined:
2503
-                    if (empty($msg)) {
2504
-                        $msg = _n(
2505
-                            'We\'re sorry but your payment was declined. Please try again.',
2506
-                            'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2507
-                            count($this->checkout->available_payment_methods),
2508
-                            'event_espresso'
2509
-                        );
2510
-                    }
2511
-                    EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2512
-                    return false;
2513
-                // bad payment
2514
-                case EEM_Payment::status_id_failed:
2515
-                    if (! empty($msg)) {
2516
-                        EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2517
-                        return false;
2518
-                    }
2519
-                    // default to error below
2520
-                    break;
2521
-            }
2522
-        }
2523
-        // off-site payment gateway responses are too unreliable, so let's just assume that
2524
-        // the payment processing is just running slower than the registrant's request
2525
-        if ($payment_occurs === EE_PMT_Base::offsite) {
2526
-            return true;
2527
-        }
2528
-        EE_Error::add_error(
2529
-            sprintf(
2530
-                esc_html__(
2531
-                    'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2532
-                    'event_espresso'
2533
-                ),
2534
-                '<br/>',
2535
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
2536
-            ),
2537
-            __FILE__,
2538
-            __FUNCTION__,
2539
-            __LINE__
2540
-        );
2541
-        return false;
2542
-    }
2543
-
2544
-
2545
-
2546
-
2547
-
2548
-
2549
-    /********************************************************************************************************/
2550
-    /**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2551
-    /********************************************************************************************************/
2552
-    /**
2553
-     * process_gateway_response
2554
-     * this is the return point for Off-Site Payment Methods
2555
-     * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2556
-     * otherwise, it will load up the last payment made for the TXN.
2557
-     * If the payment retrieved looks good, it will then either:
2558
-     *    complete the current step and allow advancement to the next reg step
2559
-     *        or present the payment options again
2560
-     *
2561
-     * @return bool
2562
-     * @throws EE_Error
2563
-     * @throws InvalidArgumentException
2564
-     * @throws ReflectionException
2565
-     * @throws InvalidDataTypeException
2566
-     * @throws InvalidInterfaceException
2567
-     */
2568
-    public function process_gateway_response()
2569
-    {
2570
-        // how have they chosen to pay?
2571
-        $this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2572
-        // get EE_Payment_Method object
2573
-        if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2574
-            $this->checkout->continue_reg = false;
2575
-            return false;
2576
-        }
2577
-        if (! $this->checkout->payment_method->is_off_site()) {
2578
-            return false;
2579
-        }
2580
-        $this->_validate_offsite_return();
2581
-        // verify TXN
2582
-        if ($this->checkout->transaction instanceof EE_Transaction) {
2583
-            $gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2584
-            if (! $gateway instanceof EE_Offsite_Gateway) {
2585
-                $this->checkout->continue_reg = false;
2586
-                return false;
2587
-            }
2588
-            $payment = $this->_process_off_site_payment($gateway);
2589
-            $payment = $this->_process_cancelled_payments($payment);
2590
-            $payment = $this->_validate_payment($payment);
2591
-            // if payment was not declined by the payment gateway or cancelled by the registrant
2592
-            if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2593
-                // $this->_setup_redirect_for_next_step();
2594
-                // store that for later
2595
-                $this->checkout->payment = $payment;
2596
-                // mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2597
-                // because we will complete this step during the IPN processing then
2598
-                if (! $this->handle_IPN_in_this_request()) {
2599
-                    $this->set_completed();
2600
-                }
2601
-                return true;
2602
-            }
2603
-        }
2604
-        // DEBUG LOG
2605
-        // $this->checkout->log(
2606
-        //     __CLASS__,
2607
-        //     __FUNCTION__,
2608
-        //     __LINE__,
2609
-        //     array('payment' => $payment)
2610
-        // );
2611
-        $this->checkout->continue_reg = false;
2612
-        return false;
2613
-    }
2614
-
2615
-
2616
-    /**
2617
-     * _validate_return
2618
-     *
2619
-     * @return void
2620
-     * @throws EE_Error
2621
-     * @throws InvalidArgumentException
2622
-     * @throws InvalidDataTypeException
2623
-     * @throws InvalidInterfaceException
2624
-     * @throws ReflectionException
2625
-     */
2626
-    private function _validate_offsite_return()
2627
-    {
2628
-        $TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2629
-        if ($TXN_ID !== $this->checkout->transaction->ID()) {
2630
-            // Houston... we might have a problem
2631
-            $invalid_TXN = false;
2632
-            // first gather some info
2633
-            $valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2634
-            $primary_registrant = $valid_TXN instanceof EE_Transaction
2635
-                ? $valid_TXN->primary_registration()
2636
-                : null;
2637
-            // let's start by retrieving the cart for this TXN
2638
-            $cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2639
-            if ($cart instanceof EE_Cart) {
2640
-                // verify that the current cart has tickets
2641
-                $tickets = $cart->get_tickets();
2642
-                if (empty($tickets)) {
2643
-                    $invalid_TXN = true;
2644
-                }
2645
-            } else {
2646
-                $invalid_TXN = true;
2647
-            }
2648
-            $valid_TXN_SID = $primary_registrant instanceof EE_Registration
2649
-                ? $primary_registrant->session_ID()
2650
-                : null;
2651
-            // validate current Session ID and compare against valid TXN session ID
2652
-            if (
2653
-                $invalid_TXN // if this is already true, then skip other checks
2654
-                || EE_Session::instance()->id() === null
2655
-                || (
2656
-                    // WARNING !!!
2657
-                    // this could be PayPal sending back duplicate requests (ya they do that)
2658
-                    // or it **could** mean someone is simply registering AGAIN after having just done so
2659
-                    // so now we need to determine if this current TXN looks valid or not
2660
-                    // and whether this reg step has even been started ?
2661
-                    EE_Session::instance()->id() === $valid_TXN_SID
2662
-                    // really? you're half way through this reg step, but you never started it ?
2663
-                    && $this->checkout->transaction->reg_step_completed($this->slug()) === false
2664
-                )
2665
-            ) {
2666
-                $invalid_TXN = true;
2667
-            }
2668
-            if ($invalid_TXN) {
2669
-                // is the valid TXN completed ?
2670
-                if ($valid_TXN instanceof EE_Transaction) {
2671
-                    // has this step even been started ?
2672
-                    $reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2673
-                    if ($reg_step_completed !== false && $reg_step_completed !== true) {
2674
-                        // so it **looks** like this is a double request from PayPal
2675
-                        // so let's try to pick up where we left off
2676
-                        $this->checkout->transaction = $valid_TXN;
2677
-                        $this->checkout->refresh_all_entities(true);
2678
-                        return;
2679
-                    }
2680
-                }
2681
-                // you appear to be lost?
2682
-                $this->_redirect_wayward_request($primary_registrant);
2683
-            }
2684
-        }
2685
-    }
2686
-
2687
-
2688
-    /**
2689
-     * _redirect_wayward_request
2690
-     *
2691
-     * @param EE_Registration|null $primary_registrant
2692
-     * @return void
2693
-     * @throws EE_Error
2694
-     * @throws InvalidArgumentException
2695
-     * @throws InvalidDataTypeException
2696
-     * @throws InvalidInterfaceException
2697
-     * @throws ReflectionException
2698
-     */
2699
-    private function _redirect_wayward_request(EE_Registration $primary_registrant)
2700
-    {
2701
-        if (! $primary_registrant instanceof EE_Registration) {
2702
-            // try redirecting based on the current TXN
2703
-            $primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2704
-                ? $this->checkout->transaction->primary_registration()
2705
-                : null;
2706
-        }
2707
-        if (! $primary_registrant instanceof EE_Registration) {
2708
-            EE_Error::add_error(
2709
-                sprintf(
2710
-                    esc_html__(
2711
-                        '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.',
2712
-                        'event_espresso'
2713
-                    ),
2714
-                    '<br/>',
2715
-                    EE_Registry::instance()->CFG->organization->get_pretty('email')
2716
-                ),
2717
-                __FILE__,
2718
-                __FUNCTION__,
2719
-                __LINE__
2720
-            );
2721
-            return;
2722
-        }
2723
-        // make sure transaction is not locked
2724
-        $this->checkout->transaction->unlock();
2725
-        wp_safe_redirect(
2726
-            add_query_arg(
2727
-                [
2728
-                    'e_reg_url_link' => $primary_registrant->reg_url_link(),
2729
-                ],
2730
-                $this->checkout->thank_you_page_url
2731
-            )
2732
-        );
2733
-        exit();
2734
-    }
2735
-
2736
-
2737
-    /**
2738
-     * _process_off_site_payment
2739
-     *
2740
-     * @param EE_Offsite_Gateway $gateway
2741
-     * @return EE_Payment
2742
-     * @throws EE_Error
2743
-     * @throws InvalidArgumentException
2744
-     * @throws InvalidDataTypeException
2745
-     * @throws InvalidInterfaceException
2746
-     * @throws ReflectionException
2747
-     */
2748
-    private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2749
-    {
2750
-        try {
2751
-            $request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2752
-            $request_data = $request->requestParams();
2753
-            // if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2754
-            $this->set_handle_IPN_in_this_request(
2755
-                $gateway->handle_IPN_in_this_request($request_data, false)
2756
-            );
2757
-            if ($this->handle_IPN_in_this_request()) {
2758
-                // get payment details and process results
2759
-                /** @type EE_Payment_Processor $payment_processor */
2760
-                $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2761
-                $payment           = $payment_processor->process_ipn(
2762
-                    $request_data,
2763
-                    $this->checkout->transaction,
2764
-                    $this->checkout->payment_method,
2765
-                    true,
2766
-                    false
2767
-                );
2768
-                // $payment_source = 'process_ipn';
2769
-            } else {
2770
-                $payment = $this->checkout->transaction->last_payment();
2771
-                // $payment_source = 'last_payment';
2772
-            }
2773
-        } catch (Exception $e) {
2774
-            // let's just eat the exception and try to move on using any previously set payment info
2775
-            $payment = $this->checkout->transaction->last_payment();
2776
-            // $payment_source = 'last_payment after Exception';
2777
-            // but if we STILL don't have a payment object
2778
-            if (! $payment instanceof EE_Payment) {
2779
-                // then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2780
-                $this->_handle_payment_processor_exception($e);
2781
-            }
2782
-        }
2783
-        return $payment;
2784
-    }
2785
-
2786
-
2787
-    /**
2788
-     * _process_cancelled_payments
2789
-     * just makes sure that the payment status gets updated correctly
2790
-     * so tha tan error isn't generated during payment validation
2791
-     *
2792
-     * @param EE_Payment $payment
2793
-     * @return EE_Payment|null
2794
-     * @throws EE_Error
2795
-     */
2796
-    private function _process_cancelled_payments($payment = null)
2797
-    {
2798
-        if (
2799
-            $payment instanceof EE_Payment
2800
-            && $this->request->requestParamIsSet('ee_cancel_payment')
2801
-            && $payment->status() === EEM_Payment::status_id_failed
2802
-        ) {
2803
-            $payment->set_status(EEM_Payment::status_id_cancelled);
2804
-        }
2805
-        return $payment;
2806
-    }
2807
-
2808
-
2809
-    /**
2810
-     *    get_transaction_details_for_gateways
2811
-     *
2812
-     * @access    public
2813
-     * @return void
2814
-     * @throws EE_Error
2815
-     * @throws InvalidArgumentException
2816
-     * @throws ReflectionException
2817
-     * @throws InvalidDataTypeException
2818
-     * @throws InvalidInterfaceException
2819
-     */
2820
-    public function get_transaction_details_for_gateways()
2821
-    {
2822
-        $txn_details = [];
2823
-        // ya gotta make a choice man
2824
-        if (empty($this->checkout->selected_method_of_payment)) {
2825
-            $txn_details = [
2826
-                'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2827
-            ];
2828
-        }
2829
-        // get EE_Payment_Method object
2830
-        if (
2831
-            empty($txn_details)
2832
-            && ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2833
-        ) {
2834
-            $txn_details = [
2835
-                'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2836
-                'error'                      => esc_html__(
2837
-                    'A valid Payment Method could not be determined.',
2838
-                    'event_espresso'
2839
-                ),
2840
-            ];
2841
-        }
2842
-        if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2843
-            $return_url  = $this->_get_return_url($this->checkout->payment_method);
2844
-            $txn_details = [
2845
-                'TXN_ID'         => $this->checkout->transaction->ID(),
2846
-                'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2847
-                'TXN_total'      => $this->checkout->transaction->total(),
2848
-                'TXN_paid'       => $this->checkout->transaction->paid(),
2849
-                'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2850
-                'STS_ID'         => $this->checkout->transaction->status_ID(),
2851
-                'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2852
-                'payment_amount' => $this->checkout->amount_owing,
2853
-                'return_url'     => $return_url,
2854
-                'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2855
-                'notify_url'     => EE_Config::instance()->core->txn_page_url(
2856
-                    [
2857
-                        'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2858
-                        'ee_payment_method' => $this->checkout->payment_method->slug(),
2859
-                    ]
2860
-                ),
2861
-            ];
2862
-        }
2863
-        echo wp_json_encode($txn_details);
2864
-        exit();
2865
-    }
2866
-
2867
-
2868
-    /**
2869
-     *    __sleep
2870
-     * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2871
-     * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2872
-     * reg form, because if needed, it will be regenerated anyways
2873
-     *
2874
-     * @return array
2875
-     */
2876
-    public function __sleep()
2877
-    {
2878
-        // remove the reg form and the checkout
2879
-        return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2880
-    }
23
+	/**
24
+	 * @var EE_Line_Item_Display $Line_Item_Display
25
+	 */
26
+	protected $line_item_display;
27
+
28
+	/**
29
+	 * @var boolean $handle_IPN_in_this_request
30
+	 */
31
+	protected $handle_IPN_in_this_request = false;
32
+
33
+
34
+	/**
35
+	 *    set_hooks - for hooking into EE Core, other modules, etc
36
+	 *
37
+	 * @access    public
38
+	 * @return    void
39
+	 */
40
+	public static function set_hooks()
41
+	{
42
+		add_filter(
43
+			'FHEE__SPCO__EE_Line_Item_Filter_Collection',
44
+			['EE_SPCO_Reg_Step_Payment_Options', 'add_spco_line_item_filters']
45
+		);
46
+		add_action(
47
+			'wp_ajax_switch_spco_billing_form',
48
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
49
+		);
50
+		add_action(
51
+			'wp_ajax_nopriv_switch_spco_billing_form',
52
+			['EE_SPCO_Reg_Step_Payment_Options', 'switch_spco_billing_form']
53
+		);
54
+		add_action('wp_ajax_save_payer_details', ['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']);
55
+		add_action(
56
+			'wp_ajax_nopriv_save_payer_details',
57
+			['EE_SPCO_Reg_Step_Payment_Options', 'save_payer_details']
58
+		);
59
+		add_action(
60
+			'wp_ajax_get_transaction_details_for_gateways',
61
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
62
+		);
63
+		add_action(
64
+			'wp_ajax_nopriv_get_transaction_details_for_gateways',
65
+			['EE_SPCO_Reg_Step_Payment_Options', 'get_transaction_details']
66
+		);
67
+		add_filter(
68
+			'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
69
+			['EE_SPCO_Reg_Step_Payment_Options', 'bypass_recaptcha_for_load_payment_method'],
70
+			10,
71
+			1
72
+		);
73
+	}
74
+
75
+
76
+	/**
77
+	 *    ajax switch_spco_billing_form
78
+	 *
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
+	 *    ajax save_payer_details
88
+	 *
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
+	 *    ajax get_transaction_details
98
+	 *
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
+	 * bypass_recaptcha_for_load_payment_method
108
+	 *
109
+	 * @access public
110
+	 * @return array
111
+	 * @throws InvalidArgumentException
112
+	 * @throws InvalidDataTypeException
113
+	 * @throws InvalidInterfaceException
114
+	 */
115
+	public static function bypass_recaptcha_for_load_payment_method()
116
+	{
117
+		return [
118
+			'EESID'  => EE_Registry::instance()->SSN->id(),
119
+			'step'   => 'payment_options',
120
+			'action' => 'spco_billing_form',
121
+		];
122
+	}
123
+
124
+
125
+	/**
126
+	 *    class constructor
127
+	 *
128
+	 * @access    public
129
+	 * @param EE_Checkout $checkout
130
+	 */
131
+	public function __construct(EE_Checkout $checkout)
132
+	{
133
+		$this->request   = EED_Single_Page_Checkout::getRequest();
134
+		$this->_slug     = 'payment_options';
135
+		$this->_name     = esc_html__('Payment Options', 'event_espresso');
136
+		$this->_template = SPCO_REG_STEPS_PATH . $this->_slug . '/payment_options_main.template.php';
137
+		$this->checkout  = $checkout;
138
+		$this->_reset_success_message();
139
+		$this->set_instructions(
140
+			esc_html__(
141
+				'Please select a method of payment and provide any necessary billing information before proceeding.',
142
+				'event_espresso'
143
+			)
144
+		);
145
+	}
146
+
147
+
148
+	/**
149
+	 * @return null
150
+	 */
151
+	public function line_item_display()
152
+	{
153
+		return $this->line_item_display;
154
+	}
155
+
156
+
157
+	/**
158
+	 * @param null $line_item_display
159
+	 */
160
+	public function set_line_item_display($line_item_display)
161
+	{
162
+		$this->line_item_display = $line_item_display;
163
+	}
164
+
165
+
166
+	/**
167
+	 * @return boolean
168
+	 */
169
+	public function handle_IPN_in_this_request()
170
+	{
171
+		return $this->handle_IPN_in_this_request;
172
+	}
173
+
174
+
175
+	/**
176
+	 * @param boolean $handle_IPN_in_this_request
177
+	 */
178
+	public function set_handle_IPN_in_this_request($handle_IPN_in_this_request)
179
+	{
180
+		$this->handle_IPN_in_this_request = filter_var($handle_IPN_in_this_request, FILTER_VALIDATE_BOOLEAN);
181
+	}
182
+
183
+
184
+	/**
185
+	 * translate_js_strings
186
+	 *
187
+	 * @return void
188
+	 */
189
+	public function translate_js_strings()
190
+	{
191
+		EE_Registry::$i18n_js_strings['no_payment_method']      = esc_html__(
192
+			'Please select a method of payment in order to continue.',
193
+			'event_espresso'
194
+		);
195
+		EE_Registry::$i18n_js_strings['invalid_payment_method'] = esc_html__(
196
+			'A valid method of payment could not be determined. Please refresh the page and try again.',
197
+			'event_espresso'
198
+		);
199
+		EE_Registry::$i18n_js_strings['forwarding_to_offsite']  = esc_html__(
200
+			'Forwarding to Secure Payment Provider.',
201
+			'event_espresso'
202
+		);
203
+	}
204
+
205
+
206
+	/**
207
+	 * enqueue_styles_and_scripts
208
+	 *
209
+	 * @return void
210
+	 * @throws EE_Error
211
+	 * @throws InvalidArgumentException
212
+	 * @throws InvalidDataTypeException
213
+	 * @throws InvalidInterfaceException
214
+	 * @throws ReflectionException
215
+	 */
216
+	public function enqueue_styles_and_scripts()
217
+	{
218
+		$transaction = $this->checkout->transaction;
219
+		// if the transaction isn't set or nothing is owed on it, don't enqueue any JS
220
+		if (! $transaction instanceof EE_Transaction || EEH_Money::compare_floats($transaction->remaining(), 0)) {
221
+			return;
222
+		}
223
+		foreach (
224
+			EEM_Payment_Method::instance()->get_all_for_transaction(
225
+				$transaction,
226
+				EEM_Payment_Method::scope_cart
227
+			) as $payment_method
228
+		) {
229
+			$type_obj = $payment_method->type_obj();
230
+			if ($type_obj instanceof EE_PMT_Base) {
231
+				$billing_form = $type_obj->generate_new_billing_form($transaction);
232
+				if ($billing_form instanceof EE_Form_Section_Proper) {
233
+					$billing_form->enqueue_js();
234
+				}
235
+			}
236
+		}
237
+	}
238
+
239
+
240
+	/**
241
+	 * initialize_reg_step
242
+	 *
243
+	 * @return bool
244
+	 * @throws EE_Error
245
+	 * @throws InvalidArgumentException
246
+	 * @throws ReflectionException
247
+	 * @throws InvalidDataTypeException
248
+	 * @throws InvalidInterfaceException
249
+	 */
250
+	public function initialize_reg_step()
251
+	{
252
+		// TODO: if /when we implement donations, then this will need overriding
253
+		if (
254
+			// don't need payment options for:
255
+			// registrations made via the admin
256
+			// completed transactions
257
+			// overpaid transactions
258
+			// $ 0.00 transactions(no payment required)
259
+			! $this->checkout->payment_required()
260
+			// but do NOT remove if current action being called belongs to this reg step
261
+			&& ! is_callable([$this, $this->checkout->action])
262
+			&& ! $this->completed()
263
+		) {
264
+			// and if so, then we no longer need the Payment Options step
265
+			if ($this->is_current_step()) {
266
+				$this->checkout->generate_reg_form = false;
267
+			}
268
+			$this->checkout->remove_reg_step($this->_slug);
269
+			// DEBUG LOG
270
+			// $this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
271
+			return false;
272
+		}
273
+		// load EEM_Payment_Method
274
+		EE_Registry::instance()->load_model('Payment_Method');
275
+		// get all active payment methods
276
+		$this->checkout->available_payment_methods = EEM_Payment_Method::instance()->get_all_for_transaction(
277
+			$this->checkout->transaction,
278
+			EEM_Payment_Method::scope_cart
279
+		);
280
+		return true;
281
+	}
282
+
283
+
284
+	/**
285
+	 * @return EE_Form_Section_Proper
286
+	 * @throws EE_Error
287
+	 * @throws InvalidArgumentException
288
+	 * @throws ReflectionException
289
+	 * @throws EntityNotFoundException
290
+	 * @throws InvalidDataTypeException
291
+	 * @throws InvalidInterfaceException
292
+	 * @throws InvalidStatusException
293
+	 */
294
+	public function generate_reg_form()
295
+	{
296
+		// reset in case someone changes their mind
297
+		$this->_reset_selected_method_of_payment();
298
+		// set some defaults
299
+		$this->checkout->selected_method_of_payment = 'payments_closed';
300
+		$registrations_requiring_payment            = [];
301
+		$registrations_for_free_events              = [];
302
+		$registrations_requiring_pre_approval       = [];
303
+		$sold_out_events                            = [];
304
+		$insufficient_spaces_available              = [];
305
+		$no_payment_required                        = true;
306
+		// loop thru registrations to gather info
307
+		$registrations         = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
308
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
309
+			$registrations,
310
+			$this->checkout->revisit
311
+		);
312
+		foreach ($registrations as $REG_ID => $registration) {
313
+			/** @var $registration EE_Registration */
314
+			// has this registration lost it's space ?
315
+			if (isset($ejected_registrations[ $REG_ID ])) {
316
+				if ($registration->event()->is_sold_out() || $registration->event()->is_sold_out(true)) {
317
+					$sold_out_events[ $registration->event()->ID() ] = $registration->event();
318
+				} else {
319
+					$insufficient_spaces_available[ $registration->event()->ID() ] = $registration->event();
320
+				}
321
+				continue;
322
+			}
323
+			// event requires admin approval
324
+			if ($registration->status_ID() === EEM_Registration::status_id_not_approved) {
325
+				// add event to list of events with pre-approval reg status
326
+				$registrations_requiring_pre_approval[ $REG_ID ] = $registration;
327
+				do_action(
328
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_pre_approval',
329
+					$registration->event(),
330
+					$this
331
+				);
332
+				continue;
333
+			}
334
+			if (
335
+				$this->checkout->revisit
336
+				&& $registration->status_ID() !== EEM_Registration::status_id_approved
337
+				&& (
338
+					$registration->event()->is_sold_out()
339
+					|| $registration->event()->is_sold_out(true)
340
+				)
341
+			) {
342
+				// add event to list of events that are sold out
343
+				$sold_out_events[ $registration->event()->ID() ] = $registration->event();
344
+				do_action(
345
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__sold_out_event',
346
+					$registration->event(),
347
+					$this
348
+				);
349
+				continue;
350
+			}
351
+			// are they allowed to pay now and is there monies owing?
352
+			if ($registration->owes_monies_and_can_pay()) {
353
+				$registrations_requiring_payment[ $REG_ID ] = $registration;
354
+				do_action(
355
+					'AHEE__EE_SPCO_Reg_Step_Payment_Options__generate_reg_form__event_requires_payment',
356
+					$registration->event(),
357
+					$this
358
+				);
359
+			} elseif (
360
+				! $this->checkout->revisit
361
+					  && $registration->status_ID() !== EEM_Registration::status_id_not_approved
362
+					  && $registration->ticket()->is_free()
363
+			) {
364
+				$registrations_for_free_events[ $registration->ticket()->ID() ] = $registration;
365
+			}
366
+		}
367
+		$subsections = [];
368
+		// now decide which template to load
369
+		if (! empty($sold_out_events)) {
370
+			$subsections['sold_out_events'] = $this->_sold_out_events($sold_out_events);
371
+		}
372
+		if (! empty($insufficient_spaces_available)) {
373
+			$subsections['insufficient_space'] = $this->_insufficient_spaces_available(
374
+				$insufficient_spaces_available
375
+			);
376
+		}
377
+		if (! empty($registrations_requiring_pre_approval)) {
378
+			$subsections['registrations_requiring_pre_approval'] = $this->_registrations_requiring_pre_approval(
379
+				$registrations_requiring_pre_approval
380
+			);
381
+		}
382
+		if (! empty($registrations_for_free_events)) {
383
+			$subsections['no_payment_required'] = $this->_no_payment_required($registrations_for_free_events);
384
+		}
385
+		if (! empty($registrations_requiring_payment)) {
386
+			if ($this->checkout->amount_owing > 0) {
387
+				// autoload Line_Item_Display classes
388
+				EEH_Autoloader::register_line_item_filter_autoloaders();
389
+				$line_item_filter_processor = new EE_Line_Item_Filter_Processor(
390
+					apply_filters(
391
+						'FHEE__SPCO__EE_Line_Item_Filter_Collection',
392
+						new EE_Line_Item_Filter_Collection()
393
+					),
394
+					$this->checkout->cart->get_grand_total()
395
+				);
396
+				/** @var EE_Line_Item $filtered_line_item_tree */
397
+				$filtered_line_item_tree = $line_item_filter_processor->process();
398
+				EEH_Autoloader::register_line_item_display_autoloaders();
399
+				$this->set_line_item_display(new EE_Line_Item_Display('spco'));
400
+				$subsections['payment_options'] = $this->_display_payment_options(
401
+					$this->line_item_display->display_line_item(
402
+						$filtered_line_item_tree,
403
+						['registrations' => $registrations]
404
+					)
405
+				);
406
+				$this->checkout->amount_owing   = $filtered_line_item_tree->total();
407
+				$this->_apply_registration_payments_to_amount_owing($registrations);
408
+			}
409
+			$no_payment_required = false;
410
+		} else {
411
+			$this->_hide_reg_step_submit_button_if_revisit();
412
+		}
413
+		$this->_save_selected_method_of_payment();
414
+
415
+		$subsections['default_hidden_inputs'] = $this->reg_step_hidden_inputs();
416
+		$subsections['extra_hidden_inputs']   = $this->_extra_hidden_inputs($no_payment_required);
417
+
418
+		return new EE_Form_Section_Proper(
419
+			[
420
+				'name'            => $this->reg_form_name(),
421
+				'html_id'         => $this->reg_form_name(),
422
+				'subsections'     => $subsections,
423
+				'layout_strategy' => new EE_No_Layout(),
424
+			]
425
+		);
426
+	}
427
+
428
+
429
+	/**
430
+	 * add line item filters required for this reg step
431
+	 * these filters are applied via this line in EE_SPCO_Reg_Step_Payment_Options::set_hooks():
432
+	 *        add_filter( 'FHEE__SPCO__EE_Line_Item_Filter_Collection', array( 'EE_SPCO_Reg_Step_Payment_Options',
433
+	 *        'add_spco_line_item_filters' ) ); so any code that wants to use the same set of filters during the
434
+	 *        payment options reg step, can apply these filters via the following: apply_filters(
435
+	 *        'FHEE__SPCO__EE_Line_Item_Filter_Collection', new EE_Line_Item_Filter_Collection() ) or to an existing
436
+	 *        filter collection by passing that instead of instantiating a new collection
437
+	 *
438
+	 * @param EE_Line_Item_Filter_Collection $line_item_filter_collection
439
+	 * @return EE_Line_Item_Filter_Collection
440
+	 * @throws EE_Error
441
+	 * @throws InvalidArgumentException
442
+	 * @throws ReflectionException
443
+	 * @throws EntityNotFoundException
444
+	 * @throws InvalidDataTypeException
445
+	 * @throws InvalidInterfaceException
446
+	 * @throws InvalidStatusException
447
+	 */
448
+	public static function add_spco_line_item_filters(EE_Line_Item_Filter_Collection $line_item_filter_collection)
449
+	{
450
+		if (! EE_Registry::instance()->SSN instanceof EE_Session) {
451
+			return $line_item_filter_collection;
452
+		}
453
+		if (! EE_Registry::instance()->SSN->checkout() instanceof EE_Checkout) {
454
+			return $line_item_filter_collection;
455
+		}
456
+		if (! EE_Registry::instance()->SSN->checkout()->transaction instanceof EE_Transaction) {
457
+			return $line_item_filter_collection;
458
+		}
459
+		$line_item_filter_collection->add(
460
+			new EE_Billable_Line_Item_Filter(
461
+				EE_SPCO_Reg_Step_Payment_Options::remove_ejected_registrations(
462
+					EE_Registry::instance()->SSN->checkout()->transaction->registrations(
463
+						EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
464
+					)
465
+				)
466
+			)
467
+		);
468
+		$line_item_filter_collection->add(new EE_Non_Zero_Line_Item_Filter());
469
+		return $line_item_filter_collection;
470
+	}
471
+
472
+
473
+	/**
474
+	 * remove_ejected_registrations
475
+	 * if a registrant has lost their potential space at an event due to lack of payment,
476
+	 * then this method removes them from the list of registrations being paid for during this request
477
+	 *
478
+	 * @param EE_Registration[] $registrations
479
+	 * @return EE_Registration[]
480
+	 * @throws EE_Error
481
+	 * @throws InvalidArgumentException
482
+	 * @throws ReflectionException
483
+	 * @throws EntityNotFoundException
484
+	 * @throws InvalidDataTypeException
485
+	 * @throws InvalidInterfaceException
486
+	 * @throws InvalidStatusException
487
+	 */
488
+	public static function remove_ejected_registrations(array $registrations)
489
+	{
490
+		$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
491
+			$registrations,
492
+			EE_Registry::instance()->SSN->checkout()->revisit
493
+		);
494
+		foreach ($registrations as $REG_ID => $registration) {
495
+			// has this registration lost it's space ?
496
+			if (isset($ejected_registrations[ $REG_ID ])) {
497
+				unset($registrations[ $REG_ID ]);
498
+			}
499
+		}
500
+		return $registrations;
501
+	}
502
+
503
+
504
+	/**
505
+	 * find_registrations_that_lost_their_space
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, $revisit = false)
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() === EEM_Registration::status_id_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() !== EEM_Registration::status_id_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
+						EEM_Registration::status_id_wait_list
574
+					);
575
+				}
576
+			}
577
+		}
578
+		return $ejected_registrations;
579
+	}
580
+
581
+
582
+	/**
583
+	 * _hide_reg_step_submit_button
584
+	 * removes the html for the reg step submit button
585
+	 * by replacing it with an empty string via filter callback
586
+	 *
587
+	 * @return void
588
+	 */
589
+	protected function _adjust_registration_status_if_event_old_sold()
590
+	{
591
+	}
592
+
593
+
594
+	/**
595
+	 * _hide_reg_step_submit_button
596
+	 * removes the html for the reg step submit button
597
+	 * by replacing it with an empty string via filter callback
598
+	 *
599
+	 * @return void
600
+	 */
601
+	protected function _hide_reg_step_submit_button_if_revisit()
602
+	{
603
+		if ($this->checkout->revisit) {
604
+			add_filter('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', '__return_empty_string');
605
+		}
606
+	}
607
+
608
+
609
+	/**
610
+	 * sold_out_events
611
+	 * displays notices regarding events that have sold out since hte registrant first signed up
612
+	 *
613
+	 * @param EE_Event[] $sold_out_events_array
614
+	 * @return EE_Form_Section_Proper
615
+	 * @throws EE_Error
616
+	 */
617
+	private function _sold_out_events($sold_out_events_array = [])
618
+	{
619
+		// set some defaults
620
+		$this->checkout->selected_method_of_payment = 'events_sold_out';
621
+		$sold_out_events                            = '';
622
+		foreach ($sold_out_events_array as $sold_out_event) {
623
+			$sold_out_events .= EEH_HTML::li(
624
+				EEH_HTML::span(
625
+					'  ' . $sold_out_event->name(),
626
+					'',
627
+					'dashicons dashicons-marker ee-icon-size-16 pink-text'
628
+				)
629
+			);
630
+		}
631
+		return new EE_Form_Section_Proper(
632
+			[
633
+				'layout_strategy' => new EE_Template_Layout(
634
+					[
635
+						'layout_template_file' => SPCO_REG_STEPS_PATH
636
+												  . $this->_slug
637
+												  . '/sold_out_events.template.php',
638
+						'template_args'        => apply_filters(
639
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
640
+							[
641
+								'sold_out_events'     => $sold_out_events,
642
+								'sold_out_events_msg' => apply_filters(
643
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__sold_out_events_msg',
644
+									sprintf(
645
+										esc_html__(
646
+											'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',
647
+											'event_espresso'
648
+										),
649
+										'<strong>',
650
+										'</strong>',
651
+										'<br />'
652
+									)
653
+								),
654
+							]
655
+						),
656
+					]
657
+				),
658
+			]
659
+		);
660
+	}
661
+
662
+
663
+	/**
664
+	 * _insufficient_spaces_available
665
+	 * displays notices regarding events that do not have enough remaining spaces
666
+	 * to satisfy the current number of registrations looking to pay
667
+	 *
668
+	 * @param EE_Event[] $insufficient_spaces_events_array
669
+	 * @return EE_Form_Section_Proper
670
+	 * @throws EE_Error
671
+	 * @throws ReflectionException
672
+	 */
673
+	private function _insufficient_spaces_available($insufficient_spaces_events_array = [])
674
+	{
675
+		// set some defaults
676
+		$this->checkout->selected_method_of_payment = 'invoice';
677
+		$insufficient_space_events                  = '';
678
+		foreach ($insufficient_spaces_events_array as $event) {
679
+			if ($event instanceof EE_Event) {
680
+				$insufficient_space_events .= EEH_HTML::li(
681
+					EEH_HTML::span(' ' . $event->name(), '', 'dashicons dashicons-marker ee-icon-size-16 pink-text')
682
+				);
683
+			}
684
+		}
685
+		return new EE_Form_Section_Proper(
686
+			[
687
+				'subsections'     => [
688
+					'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
689
+					'extra_hidden_inputs'   => $this->_extra_hidden_inputs(),
690
+				],
691
+				'layout_strategy' => new EE_Template_Layout(
692
+					[
693
+						'layout_template_file' => SPCO_REG_STEPS_PATH
694
+												  . $this->_slug
695
+												  . '/sold_out_events.template.php',
696
+						'template_args'        => apply_filters(
697
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__template_args',
698
+							[
699
+								'sold_out_events'     => $insufficient_space_events,
700
+								'sold_out_events_msg' => apply_filters(
701
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___insufficient_spaces_available__insufficient_space_msg',
702
+									esc_html__(
703
+										'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.',
704
+										'event_espresso'
705
+									)
706
+								),
707
+							]
708
+						),
709
+					]
710
+				),
711
+			]
712
+		);
713
+	}
714
+
715
+
716
+	/**
717
+	 * registrations_requiring_pre_approval
718
+	 *
719
+	 * @param array $registrations_requiring_pre_approval
720
+	 * @return EE_Form_Section_Proper
721
+	 * @throws EE_Error
722
+	 * @throws EntityNotFoundException
723
+	 * @throws ReflectionException
724
+	 */
725
+	private function _registrations_requiring_pre_approval($registrations_requiring_pre_approval = [])
726
+	{
727
+		$events_requiring_pre_approval = [];
728
+		foreach ($registrations_requiring_pre_approval as $registration) {
729
+			if ($registration instanceof EE_Registration && $registration->event() instanceof EE_Event) {
730
+				$events_requiring_pre_approval[ $registration->event()->ID() ] = EEH_HTML::li(
731
+					EEH_HTML::span(
732
+						'',
733
+						'',
734
+						'dashicons dashicons-marker ee-icon-size-16 orange-text'
735
+					)
736
+					. EEH_HTML::span($registration->event()->name(), '', 'orange-text')
737
+				);
738
+			}
739
+		}
740
+		return new EE_Form_Section_Proper(
741
+			[
742
+				'layout_strategy' => new EE_Template_Layout(
743
+					[
744
+						'layout_template_file' => SPCO_REG_STEPS_PATH
745
+												  . $this->_slug
746
+												  . '/events_requiring_pre_approval.template.php', // layout_template
747
+						'template_args'        => apply_filters(
748
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___sold_out_events__template_args',
749
+							[
750
+								'events_requiring_pre_approval'     => implode('', $events_requiring_pre_approval),
751
+								'events_requiring_pre_approval_msg' => apply_filters(
752
+									'FHEE__EE_SPCO_Reg_Step_Payment_Options___events_requiring_pre_approval__events_requiring_pre_approval_msg',
753
+									esc_html__(
754
+										'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.',
755
+										'event_espresso'
756
+									)
757
+								),
758
+							]
759
+						),
760
+					]
761
+				),
762
+			]
763
+		);
764
+	}
765
+
766
+
767
+	/**
768
+	 * _no_payment_required
769
+	 *
770
+	 * @param EE_Event[] $registrations_for_free_events
771
+	 * @return EE_Form_Section_Proper
772
+	 * @throws EE_Error
773
+	 */
774
+	private function _no_payment_required($registrations_for_free_events = [])
775
+	{
776
+		// set some defaults
777
+		$this->checkout->selected_method_of_payment = 'no_payment_required';
778
+		// generate no_payment_required form
779
+		return new EE_Form_Section_Proper(
780
+			[
781
+				'layout_strategy' => new EE_Template_Layout(
782
+					[
783
+						'layout_template_file' => SPCO_REG_STEPS_PATH
784
+												  . $this->_slug
785
+												  . '/no_payment_required.template.php', // layout_template
786
+						'template_args'        => apply_filters(
787
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___no_payment_required__template_args',
788
+							[
789
+								'revisit'                       => $this->checkout->revisit,
790
+								'registrations'                 => [],
791
+								'ticket_count'                  => [],
792
+								'registrations_for_free_events' => $registrations_for_free_events,
793
+								'no_payment_required_msg'       => EEH_HTML::p(
794
+									esc_html__('This is a free event, so no billing will occur.', 'event_espresso')
795
+								),
796
+							]
797
+						),
798
+					]
799
+				),
800
+			]
801
+		);
802
+	}
803
+
804
+
805
+	/**
806
+	 * _display_payment_options
807
+	 *
808
+	 * @param string $transaction_details
809
+	 * @return EE_Form_Section_Proper
810
+	 * @throws EE_Error
811
+	 * @throws InvalidArgumentException
812
+	 * @throws InvalidDataTypeException
813
+	 * @throws InvalidInterfaceException
814
+	 */
815
+	private function _display_payment_options($transaction_details = '')
816
+	{
817
+		// has method_of_payment been set by no-js user?
818
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment();
819
+		// build payment options form
820
+		return apply_filters(
821
+			'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__payment_options_form',
822
+			new EE_Form_Section_Proper(
823
+				[
824
+					'subsections'     => [
825
+						'before_payment_options' => apply_filters(
826
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__before_payment_options',
827
+							new EE_Form_Section_Proper(
828
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
829
+							)
830
+						),
831
+						'payment_options'        => $this->_setup_payment_options(),
832
+						'after_payment_options'  => apply_filters(
833
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__after_payment_options',
834
+							new EE_Form_Section_Proper(
835
+								['layout_strategy' => new EE_Div_Per_Section_Layout()]
836
+							)
837
+						),
838
+					],
839
+					'layout_strategy' => new EE_Template_Layout(
840
+						[
841
+							'layout_template_file' => $this->_template,
842
+							'template_args'        => apply_filters(
843
+								'FHEE__EE_SPCO_Reg_Step_Payment_Options___display_payment_options__template_args',
844
+								[
845
+									'reg_count'                 => $this->line_item_display->total_items(),
846
+									'transaction_details'       => $transaction_details,
847
+									'available_payment_methods' => [],
848
+								]
849
+							),
850
+						]
851
+					),
852
+				]
853
+			)
854
+		);
855
+	}
856
+
857
+
858
+	/**
859
+	 * _extra_hidden_inputs
860
+	 *
861
+	 * @param bool $no_payment_required
862
+	 * @return EE_Form_Section_Proper
863
+	 * @throws EE_Error
864
+	 * @throws ReflectionException
865
+	 */
866
+	private function _extra_hidden_inputs($no_payment_required = true)
867
+	{
868
+		return new EE_Form_Section_Proper(
869
+			[
870
+				'html_id'         => 'ee-' . $this->slug() . '-extra-hidden-inputs',
871
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
872
+				'subsections'     => [
873
+					'spco_no_payment_required' => new EE_Hidden_Input(
874
+						[
875
+							'normalization_strategy' => new EE_Boolean_Normalization(),
876
+							'html_name'              => 'spco_no_payment_required',
877
+							'html_id'                => 'spco-no-payment-required-payment_options',
878
+							'default'                => $no_payment_required,
879
+						]
880
+					),
881
+					'spco_transaction_id'      => new EE_Fixed_Hidden_Input(
882
+						[
883
+							'normalization_strategy' => new EE_Int_Normalization(),
884
+							'html_name'              => 'spco_transaction_id',
885
+							'html_id'                => 'spco-transaction-id',
886
+							'default'                => $this->checkout->transaction->ID(),
887
+						]
888
+					),
889
+				],
890
+			]
891
+		);
892
+	}
893
+
894
+
895
+	/**
896
+	 *    _apply_registration_payments_to_amount_owing
897
+	 *
898
+	 * @param array $registrations
899
+	 * @throws EE_Error
900
+	 */
901
+	protected function _apply_registration_payments_to_amount_owing(array $registrations)
902
+	{
903
+		$payments = [];
904
+		foreach ($registrations as $registration) {
905
+			if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
906
+				$payments += $registration->registration_payments();
907
+			}
908
+		}
909
+		if (! empty($payments)) {
910
+			foreach ($payments as $payment) {
911
+				if ($payment instanceof EE_Registration_Payment) {
912
+					$this->checkout->amount_owing -= $payment->amount();
913
+				}
914
+			}
915
+		}
916
+	}
917
+
918
+
919
+	/**
920
+	 *    _reset_selected_method_of_payment
921
+	 *
922
+	 * @access    private
923
+	 * @param bool $force_reset
924
+	 * @return void
925
+	 * @throws InvalidArgumentException
926
+	 * @throws InvalidDataTypeException
927
+	 * @throws InvalidInterfaceException
928
+	 */
929
+	private function _reset_selected_method_of_payment($force_reset = false)
930
+	{
931
+		/** @var RequestInterface $request */
932
+		$request              = LoaderFactory::getLoader()->getShared(RequestInterface::class);
933
+		$reset_payment_method = $request->getRequestParam('reset_payment_method', $force_reset, 'bool');
934
+		if ($reset_payment_method) {
935
+			$this->checkout->selected_method_of_payment = null;
936
+			$this->checkout->payment_method             = null;
937
+			$this->checkout->billing_form               = null;
938
+			$this->_save_selected_method_of_payment();
939
+		}
940
+	}
941
+
942
+
943
+	/**
944
+	 * _save_selected_method_of_payment
945
+	 * stores the selected_method_of_payment in the session
946
+	 * so that it's available for all subsequent requests including AJAX
947
+	 *
948
+	 * @access        private
949
+	 * @param string $selected_method_of_payment
950
+	 * @return void
951
+	 * @throws InvalidArgumentException
952
+	 * @throws InvalidDataTypeException
953
+	 * @throws InvalidInterfaceException
954
+	 */
955
+	private function _save_selected_method_of_payment($selected_method_of_payment = '')
956
+	{
957
+		$selected_method_of_payment = ! empty($selected_method_of_payment)
958
+			? $selected_method_of_payment
959
+			: $this->checkout->selected_method_of_payment;
960
+		EE_Registry::instance()->SSN->set_session_data(
961
+			['selected_method_of_payment' => $selected_method_of_payment]
962
+		);
963
+	}
964
+
965
+
966
+	/**
967
+	 * _setup_payment_options
968
+	 *
969
+	 * @return EE_Form_Section_Proper
970
+	 * @throws EE_Error
971
+	 * @throws InvalidArgumentException
972
+	 * @throws InvalidDataTypeException
973
+	 * @throws InvalidInterfaceException
974
+	 */
975
+	public function _setup_payment_options()
976
+	{
977
+		// load payment method classes
978
+		$this->checkout->available_payment_methods = $this->_get_available_payment_methods();
979
+		if (empty($this->checkout->available_payment_methods)) {
980
+			EE_Error::add_error(
981
+				apply_filters(
982
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__error_message_no_payment_methods',
983
+					sprintf(
984
+						esc_html__(
985
+							'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.',
986
+							'event_espresso'
987
+						),
988
+						'<br>',
989
+						EE_Registry::instance()->CFG->organization->get_pretty('email')
990
+					)
991
+				),
992
+				__FILE__,
993
+				__FUNCTION__,
994
+				__LINE__
995
+			);
996
+		}
997
+		// switch up header depending on number of available payment methods
998
+		$payment_method_header     = count($this->checkout->available_payment_methods) > 1
999
+			? apply_filters(
1000
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1001
+				esc_html__('Please Select Your Method of Payment', 'event_espresso')
1002
+			)
1003
+			: apply_filters(
1004
+				'FHEE__registration_page_payment_options__method_of_payment_hdr',
1005
+				esc_html__('Method of Payment', 'event_espresso')
1006
+			);
1007
+		$available_payment_methods = [
1008
+			// display the "Payment Method" header
1009
+			'payment_method_header' => new EE_Form_Section_HTML(
1010
+				apply_filters(
1011
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options___setup_payment_options__payment_method_header',
1012
+					EEH_HTML::h4($payment_method_header, 'method-of-payment-hdr'),
1013
+					$payment_method_header
1014
+				)
1015
+			),
1016
+		];
1017
+		// the list of actual payment methods ( invoice, paypal, etc ) in a  ( slug => HTML )  format
1018
+		$available_payment_method_options = [];
1019
+		$default_payment_method_option    = [];
1020
+		// additional instructions to be displayed and hidden below payment methods (adding a clearing div to start)
1021
+		$payment_methods_billing_info = [
1022
+			new EE_Form_Section_HTML(
1023
+				EEH_HTML::div('<br />', '', '', 'clear:both;')
1024
+			),
1025
+		];
1026
+		// loop through payment methods
1027
+		foreach ($this->checkout->available_payment_methods as $payment_method) {
1028
+			if ($payment_method instanceof EE_Payment_Method) {
1029
+				$payment_method_button = EEH_HTML::img(
1030
+					$payment_method->button_url(),
1031
+					$payment_method->name(),
1032
+					'spco-payment-method-' . $payment_method->slug() . '-btn-img',
1033
+					'spco-payment-method-btn-img'
1034
+				);
1035
+				// check if any payment methods are set as default
1036
+				// if payment method is already selected OR nothing is selected and this payment method should be
1037
+				// open_by_default
1038
+				if (
1039
+					($this->checkout->selected_method_of_payment === $payment_method->slug())
1040
+					|| (! $this->checkout->selected_method_of_payment && $payment_method->open_by_default())
1041
+				) {
1042
+					$this->checkout->selected_method_of_payment = $payment_method->slug();
1043
+					$this->_save_selected_method_of_payment();
1044
+					$default_payment_method_option[ $payment_method->slug() ] = $payment_method_button;
1045
+				} else {
1046
+					$available_payment_method_options[ $payment_method->slug() ] = $payment_method_button;
1047
+				}
1048
+				$payment_methods_billing_info[ $payment_method->slug() . '-info' ] =
1049
+					$this->_payment_method_billing_info(
1050
+						$payment_method
1051
+					);
1052
+			}
1053
+		}
1054
+		// prepend available_payment_method_options with default_payment_method_option so that it appears first in list
1055
+		// of PMs
1056
+		$available_payment_method_options = $default_payment_method_option + $available_payment_method_options;
1057
+		// now generate the actual form  inputs
1058
+		$available_payment_methods['available_payment_methods'] = $this->_available_payment_method_inputs(
1059
+			$available_payment_method_options
1060
+		);
1061
+		$available_payment_methods                              += $payment_methods_billing_info;
1062
+		// build the available payment methods form
1063
+		return new EE_Form_Section_Proper(
1064
+			[
1065
+				'html_id'         => 'spco-available-methods-of-payment-dv',
1066
+				'subsections'     => $available_payment_methods,
1067
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1068
+			]
1069
+		);
1070
+	}
1071
+
1072
+
1073
+	/**
1074
+	 * _get_available_payment_methods
1075
+	 *
1076
+	 * @return EE_Payment_Method[]
1077
+	 * @throws EE_Error
1078
+	 * @throws InvalidArgumentException
1079
+	 * @throws InvalidDataTypeException
1080
+	 * @throws InvalidInterfaceException
1081
+	 */
1082
+	protected function _get_available_payment_methods()
1083
+	{
1084
+		if (! empty($this->checkout->available_payment_methods)) {
1085
+			return $this->checkout->available_payment_methods;
1086
+		}
1087
+		$available_payment_methods = [];
1088
+		$EEM_Payment_Method        = EEM_Payment_Method::instance();
1089
+		// get all active payment methods
1090
+		$payment_methods = $EEM_Payment_Method->get_all_for_transaction(
1091
+			$this->checkout->transaction,
1092
+			EEM_Payment_Method::scope_cart
1093
+		);
1094
+		foreach ($payment_methods as $payment_method) {
1095
+			if ($payment_method instanceof EE_Payment_Method) {
1096
+				$available_payment_methods[ $payment_method->slug() ] = $payment_method;
1097
+			}
1098
+		}
1099
+		return $available_payment_methods;
1100
+	}
1101
+
1102
+
1103
+	/**
1104
+	 *    _available_payment_method_inputs
1105
+	 *
1106
+	 * @access    private
1107
+	 * @param array $available_payment_method_options
1108
+	 * @return    EE_Form_Section_Proper
1109
+	 * @throws EE_Error
1110
+	 * @throws EE_Error
1111
+	 */
1112
+	private function _available_payment_method_inputs($available_payment_method_options = [])
1113
+	{
1114
+		// generate inputs
1115
+		return new EE_Form_Section_Proper(
1116
+			[
1117
+				'html_id'         => 'ee-available-payment-method-inputs',
1118
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1119
+				'subsections'     => [
1120
+					'' => new EE_Radio_Button_Input(
1121
+						$available_payment_method_options,
1122
+						[
1123
+							'html_name'          => 'selected_method_of_payment',
1124
+							'html_class'         => 'spco-payment-method',
1125
+							'default'            => $this->checkout->selected_method_of_payment,
1126
+							'label_size'         => 11,
1127
+							'enforce_label_size' => true,
1128
+						]
1129
+					),
1130
+				],
1131
+			]
1132
+		);
1133
+	}
1134
+
1135
+
1136
+	/**
1137
+	 *    _payment_method_billing_info
1138
+	 *
1139
+	 * @access    private
1140
+	 * @param EE_Payment_Method $payment_method
1141
+	 * @return EE_Form_Section_Proper
1142
+	 * @throws EE_Error
1143
+	 * @throws InvalidArgumentException
1144
+	 * @throws InvalidDataTypeException
1145
+	 * @throws InvalidInterfaceException
1146
+	 */
1147
+	private function _payment_method_billing_info(EE_Payment_Method $payment_method)
1148
+	{
1149
+		$currently_selected = $this->checkout->selected_method_of_payment === $payment_method->slug();
1150
+		// generate the billing form for payment method
1151
+		$billing_form                 = $currently_selected
1152
+			? $this->_get_billing_form_for_payment_method($payment_method)
1153
+			: new EE_Form_Section_HTML();
1154
+		$this->checkout->billing_form = $currently_selected
1155
+			? $billing_form
1156
+			: $this->checkout->billing_form;
1157
+		// it's all in the details
1158
+		$info_html = EEH_HTML::h3(
1159
+			esc_html__('Important information regarding your payment', 'event_espresso'),
1160
+			'',
1161
+			'spco-payment-method-hdr'
1162
+		);
1163
+		// add some info regarding the step, either from what's saved in the admin,
1164
+		// or a default string depending on whether the PM has a billing form or not
1165
+		if ($payment_method->description()) {
1166
+			$payment_method_info = $payment_method->description();
1167
+		} elseif ($billing_form instanceof EE_Billing_Info_Form) {
1168
+			$payment_method_info = sprintf(
1169
+				esc_html__(
1170
+					'Please provide the following billing information, then click the "%1$s" button below in order to proceed.',
1171
+					'event_espresso'
1172
+				),
1173
+				$this->submit_button_text()
1174
+			);
1175
+		} else {
1176
+			$payment_method_info = sprintf(
1177
+				esc_html__('Please click the "%1$s" button below in order to proceed.', 'event_espresso'),
1178
+				$this->submit_button_text()
1179
+			);
1180
+		}
1181
+		$info_html .= EEH_HTML::div(
1182
+			apply_filters(
1183
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___payment_method_billing_info__payment_method_info',
1184
+				$payment_method_info
1185
+			),
1186
+			'',
1187
+			'spco-payment-method-desc ee-attention'
1188
+		);
1189
+		return new EE_Form_Section_Proper(
1190
+			[
1191
+				'html_id'         => 'spco-payment-method-info-' . $payment_method->slug(),
1192
+				'html_class'      => 'spco-payment-method-info-dv',
1193
+				// only display the selected or default PM
1194
+				'html_style'      => $currently_selected ? '' : 'display:none;',
1195
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
1196
+				'subsections'     => [
1197
+					'info'         => new EE_Form_Section_HTML($info_html),
1198
+					'billing_form' => $currently_selected ? $billing_form : new EE_Form_Section_HTML(),
1199
+				],
1200
+			]
1201
+		);
1202
+	}
1203
+
1204
+
1205
+	/**
1206
+	 * get_billing_form_html_for_payment_method
1207
+	 *
1208
+	 * @return bool
1209
+	 * @throws EE_Error
1210
+	 * @throws InvalidArgumentException
1211
+	 * @throws ReflectionException
1212
+	 * @throws InvalidDataTypeException
1213
+	 * @throws InvalidInterfaceException
1214
+	 */
1215
+	public function get_billing_form_html_for_payment_method()
1216
+	{
1217
+		// how have they chosen to pay?
1218
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1219
+		$this->checkout->payment_method             = $this->_get_payment_method_for_selected_method_of_payment();
1220
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1221
+			return false;
1222
+		}
1223
+		if (
1224
+			apply_filters(
1225
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1226
+				false
1227
+			)
1228
+		) {
1229
+			EE_Error::add_success(
1230
+				apply_filters(
1231
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1232
+					sprintf(
1233
+						esc_html__(
1234
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1235
+							'event_espresso'
1236
+						),
1237
+						$this->checkout->payment_method->name()
1238
+					)
1239
+				)
1240
+			);
1241
+		}
1242
+		// now generate billing form for selected method of payment
1243
+		$payment_method_billing_form = $this->_get_billing_form_for_payment_method($this->checkout->payment_method);
1244
+		// fill form with attendee info if applicable
1245
+		if (
1246
+			$payment_method_billing_form instanceof EE_Billing_Attendee_Info_Form
1247
+			&& $this->checkout->transaction_has_primary_registrant()
1248
+		) {
1249
+			$payment_method_billing_form->populate_from_attendee(
1250
+				$this->checkout->transaction->primary_registration()->attendee()
1251
+			);
1252
+		}
1253
+		// and debug content
1254
+		if (
1255
+			$payment_method_billing_form instanceof EE_Billing_Info_Form
1256
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1257
+		) {
1258
+			$payment_method_billing_form =
1259
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1260
+					$payment_method_billing_form
1261
+				);
1262
+		}
1263
+		$billing_info = $payment_method_billing_form instanceof EE_Form_Section_Proper
1264
+			? $payment_method_billing_form->get_html()
1265
+			: '';
1266
+		$this->checkout->json_response->set_return_data(['payment_method_info' => $billing_info]);
1267
+		// localize validation rules for main form
1268
+		$this->checkout->current_step->reg_form->localize_validation_rules();
1269
+		$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1270
+		return true;
1271
+	}
1272
+
1273
+
1274
+	/**
1275
+	 * _get_billing_form_for_payment_method
1276
+	 *
1277
+	 * @param EE_Payment_Method $payment_method
1278
+	 * @return EE_Billing_Info_Form|EE_Billing_Attendee_Info_Form|EE_Form_Section_HTML
1279
+	 * @throws EE_Error
1280
+	 * @throws InvalidArgumentException
1281
+	 * @throws InvalidDataTypeException
1282
+	 * @throws InvalidInterfaceException
1283
+	 */
1284
+	private function _get_billing_form_for_payment_method(EE_Payment_Method $payment_method)
1285
+	{
1286
+		$billing_form = $payment_method->type_obj()->billing_form(
1287
+			$this->checkout->transaction,
1288
+			['amount_owing' => $this->checkout->amount_owing]
1289
+		);
1290
+		if ($billing_form instanceof EE_Billing_Info_Form) {
1291
+			if (
1292
+				apply_filters(
1293
+					'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1294
+					false
1295
+				)
1296
+				&& $this->request->requestParamIsSet('payment_method')
1297
+			) {
1298
+				EE_Error::add_success(
1299
+					apply_filters(
1300
+						'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1301
+						sprintf(
1302
+							esc_html__(
1303
+								'You have selected "%s" as your method of payment. Please note the important payment information below.',
1304
+								'event_espresso'
1305
+							),
1306
+							$payment_method->name()
1307
+						)
1308
+					)
1309
+				);
1310
+			}
1311
+			return apply_filters(
1312
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options___get_billing_form_for_payment_method__billing_form',
1313
+				$billing_form,
1314
+				$payment_method
1315
+			);
1316
+		}
1317
+		// no actual billing form, so return empty HTML form section
1318
+		return new EE_Form_Section_HTML();
1319
+	}
1320
+
1321
+
1322
+	/**
1323
+	 * _get_selected_method_of_payment
1324
+	 *
1325
+	 * @param boolean $required whether to throw an error if the "selected_method_of_payment"
1326
+	 *                          is not found in the incoming request
1327
+	 * @param string  $request_param
1328
+	 * @return NULL|string
1329
+	 * @throws EE_Error
1330
+	 * @throws InvalidArgumentException
1331
+	 * @throws InvalidDataTypeException
1332
+	 * @throws InvalidInterfaceException
1333
+	 */
1334
+	private function _get_selected_method_of_payment(
1335
+		$required = false,
1336
+		$request_param = 'selected_method_of_payment'
1337
+	) {
1338
+		// is selected_method_of_payment set in the request ?
1339
+		$selected_method_of_payment = $this->request->getRequestParam($request_param);
1340
+		if ($selected_method_of_payment) {
1341
+			// sanitize it
1342
+			$selected_method_of_payment = is_array($selected_method_of_payment)
1343
+				? array_shift($selected_method_of_payment)
1344
+				: $selected_method_of_payment;
1345
+			$selected_method_of_payment = sanitize_text_field($selected_method_of_payment);
1346
+			// store it in the session so that it's available for all subsequent requests including AJAX
1347
+			$this->_save_selected_method_of_payment($selected_method_of_payment);
1348
+		} else {
1349
+			// or is is set in the session ?
1350
+			$selected_method_of_payment = EE_Registry::instance()->SSN->get_session_data(
1351
+				'selected_method_of_payment'
1352
+			);
1353
+		}
1354
+		// do ya really really gotta have it?
1355
+		if (empty($selected_method_of_payment) && $required) {
1356
+			EE_Error::add_error(
1357
+				sprintf(
1358
+					esc_html__(
1359
+						'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.',
1360
+						'event_espresso'
1361
+					),
1362
+					'<br/>',
1363
+					'<br/>',
1364
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
1365
+				),
1366
+				__FILE__,
1367
+				__FUNCTION__,
1368
+				__LINE__
1369
+			);
1370
+			return null;
1371
+		}
1372
+		return $selected_method_of_payment;
1373
+	}
1374
+
1375
+
1376
+
1377
+
1378
+
1379
+
1380
+	/********************************************************************************************************/
1381
+	/***********************************  SWITCH PAYMENT METHOD  ************************************/
1382
+	/********************************************************************************************************/
1383
+	/**
1384
+	 * switch_payment_method
1385
+	 *
1386
+	 * @return bool
1387
+	 * @throws EE_Error
1388
+	 * @throws InvalidArgumentException
1389
+	 * @throws InvalidDataTypeException
1390
+	 * @throws InvalidInterfaceException
1391
+	 * @throws ReflectionException
1392
+	 */
1393
+	public function switch_payment_method()
1394
+	{
1395
+		if (! $this->_verify_payment_method_is_set()) {
1396
+			return false;
1397
+		}
1398
+		if (
1399
+			apply_filters(
1400
+				'FHEE__EE_SPCO_Reg_Step_Payment_Options__registration_checkout__selected_payment_method__display_success',
1401
+				false
1402
+			)
1403
+		) {
1404
+			EE_Error::add_success(
1405
+				apply_filters(
1406
+					'FHEE__Single_Page_Checkout__registration_checkout__selected_payment_method',
1407
+					sprintf(
1408
+						esc_html__(
1409
+							'You have selected "%s" as your method of payment. Please note the important payment information below.',
1410
+							'event_espresso'
1411
+						),
1412
+						$this->checkout->payment_method->name()
1413
+					)
1414
+				)
1415
+			);
1416
+		}
1417
+		// generate billing form for selected method of payment if it hasn't been done already
1418
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1419
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1420
+				$this->checkout->payment_method
1421
+			);
1422
+		}
1423
+		// fill form with attendee info if applicable
1424
+		if (
1425
+			apply_filters(
1426
+				'FHEE__populate_billing_form_fields_from_attendee',
1427
+				(
1428
+					$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
1429
+					&& $this->checkout->transaction_has_primary_registrant()
1430
+				),
1431
+				$this->checkout->billing_form,
1432
+				$this->checkout->transaction
1433
+			)
1434
+		) {
1435
+			$this->checkout->billing_form->populate_from_attendee(
1436
+				$this->checkout->transaction->primary_registration()->attendee()
1437
+			);
1438
+		}
1439
+		// and debug content
1440
+		if (
1441
+			$this->checkout->billing_form instanceof EE_Billing_Info_Form
1442
+			&& $this->checkout->payment_method->type_obj() instanceof EE_PMT_Base
1443
+		) {
1444
+			$this->checkout->billing_form =
1445
+				$this->checkout->payment_method->type_obj()->apply_billing_form_debug_settings(
1446
+					$this->checkout->billing_form
1447
+				);
1448
+		}
1449
+		// get html and validation rules for form
1450
+		if ($this->checkout->billing_form instanceof EE_Form_Section_Proper) {
1451
+			$this->checkout->json_response->set_return_data(
1452
+				['payment_method_info' => $this->checkout->billing_form->get_html()]
1453
+			);
1454
+			// localize validation rules for main form
1455
+			$this->checkout->billing_form->localize_validation_rules(true);
1456
+			$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
1457
+		} else {
1458
+			$this->checkout->json_response->set_return_data(['payment_method_info' => '']);
1459
+		}
1460
+		// prevents advancement to next step
1461
+		$this->checkout->continue_reg = false;
1462
+		return true;
1463
+	}
1464
+
1465
+
1466
+	/**
1467
+	 * _verify_payment_method_is_set
1468
+	 *
1469
+	 * @return bool
1470
+	 * @throws EE_Error
1471
+	 * @throws InvalidArgumentException
1472
+	 * @throws ReflectionException
1473
+	 * @throws InvalidDataTypeException
1474
+	 * @throws InvalidInterfaceException
1475
+	 */
1476
+	protected function _verify_payment_method_is_set()
1477
+	{
1478
+		// generate billing form for selected method of payment if it hasn't been done already
1479
+		if (empty($this->checkout->selected_method_of_payment)) {
1480
+			// how have they chosen to pay?
1481
+			$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
1482
+		} else {
1483
+			// choose your own adventure based on method_of_payment
1484
+			switch ($this->checkout->selected_method_of_payment) {
1485
+				case 'events_sold_out':
1486
+					EE_Error::add_attention(
1487
+						apply_filters(
1488
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__sold_out_events_msg',
1489
+							esc_html__(
1490
+								'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.',
1491
+								'event_espresso'
1492
+							)
1493
+						),
1494
+						__FILE__,
1495
+						__FUNCTION__,
1496
+						__LINE__
1497
+					);
1498
+					return false;
1499
+				case 'payments_closed':
1500
+					EE_Error::add_attention(
1501
+						apply_filters(
1502
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__payments_closed_msg',
1503
+							esc_html__(
1504
+								'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.',
1505
+								'event_espresso'
1506
+							)
1507
+						),
1508
+						__FILE__,
1509
+						__FUNCTION__,
1510
+						__LINE__
1511
+					);
1512
+					return false;
1513
+				case 'no_payment_required':
1514
+					EE_Error::add_attention(
1515
+						apply_filters(
1516
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___verify_payment_method_is_set__no_payment_required_msg',
1517
+							esc_html__(
1518
+								'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.',
1519
+								'event_espresso'
1520
+							)
1521
+						),
1522
+						__FILE__,
1523
+						__FUNCTION__,
1524
+						__LINE__
1525
+					);
1526
+					return false;
1527
+				default:
1528
+			}
1529
+		}
1530
+		// verify payment method
1531
+		if (! $this->checkout->payment_method instanceof EE_Payment_Method) {
1532
+			// get payment method for selected method of payment
1533
+			$this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment();
1534
+		}
1535
+		return $this->checkout->payment_method instanceof EE_Payment_Method;
1536
+	}
1537
+
1538
+
1539
+
1540
+	/********************************************************************************************************/
1541
+	/***************************************  SAVE PAYER DETAILS  ****************************************/
1542
+	/********************************************************************************************************/
1543
+	/**
1544
+	 * save_payer_details_via_ajax
1545
+	 *
1546
+	 * @return void
1547
+	 * @throws EE_Error
1548
+	 * @throws InvalidArgumentException
1549
+	 * @throws ReflectionException
1550
+	 * @throws RuntimeException
1551
+	 * @throws InvalidDataTypeException
1552
+	 * @throws InvalidInterfaceException
1553
+	 */
1554
+	public function save_payer_details_via_ajax()
1555
+	{
1556
+		if (! $this->_verify_payment_method_is_set()) {
1557
+			return;
1558
+		}
1559
+		// generate billing form for selected method of payment if it hasn't been done already
1560
+		if ($this->checkout->payment_method->type_obj()->has_billing_form()) {
1561
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1562
+				$this->checkout->payment_method
1563
+			);
1564
+		}
1565
+		// generate primary attendee from payer info if applicable
1566
+		if (! $this->checkout->transaction_has_primary_registrant()) {
1567
+			$attendee = $this->_create_attendee_from_request_data();
1568
+			if ($attendee instanceof EE_Attendee) {
1569
+				foreach ($this->checkout->transaction->registrations() as $registration) {
1570
+					if ($registration->is_primary_registrant()) {
1571
+						$this->checkout->primary_attendee_obj = $attendee;
1572
+						$registration->_add_relation_to($attendee, 'Attendee');
1573
+						$registration->set_attendee_id($attendee->ID());
1574
+						$registration->update_cache_after_object_save('Attendee', $attendee);
1575
+					}
1576
+				}
1577
+			}
1578
+		}
1579
+	}
1580
+
1581
+
1582
+	/**
1583
+	 * create_attendee_from_request_data
1584
+	 * uses info from alternate GET or POST data (such as AJAX) to create a new attendee
1585
+	 *
1586
+	 * @return EE_Attendee
1587
+	 * @throws EE_Error
1588
+	 * @throws InvalidArgumentException
1589
+	 * @throws ReflectionException
1590
+	 * @throws InvalidDataTypeException
1591
+	 * @throws InvalidInterfaceException
1592
+	 */
1593
+	protected function _create_attendee_from_request_data()
1594
+	{
1595
+		// get State ID
1596
+		$STA_ID = $this->request->getRequestParam('state');
1597
+		if (! empty($STA_ID)) {
1598
+			// can we get state object from name ?
1599
+			EE_Registry::instance()->load_model('State');
1600
+			$state  = EEM_State::instance()->get_col([['STA_name' => $STA_ID], 'limit' => 1], 'STA_ID');
1601
+			$STA_ID = is_array($state) && ! empty($state) ? reset($state) : $STA_ID;
1602
+		}
1603
+		// get Country ISO
1604
+		$CNT_ISO = $this->request->getRequestParam('country');
1605
+		if (! empty($CNT_ISO)) {
1606
+			// can we get country object from name ?
1607
+			EE_Registry::instance()->load_model('Country');
1608
+			$country = EEM_Country::instance()->get_col(
1609
+				[['CNT_name' => $CNT_ISO], 'limit' => 1],
1610
+				'CNT_ISO'
1611
+			);
1612
+			$CNT_ISO = is_array($country) && ! empty($country) ? reset($country) : $CNT_ISO;
1613
+		}
1614
+		// grab attendee data
1615
+		$attendee_data = [
1616
+			'ATT_fname'    => $this->request->getRequestParam('first_name'),
1617
+			'ATT_lname'    => $this->request->getRequestParam('last_name'),
1618
+			'ATT_email'    => $this->request->getRequestParam('email'),
1619
+			'ATT_address'  => $this->request->getRequestParam('address'),
1620
+			'ATT_address2' => $this->request->getRequestParam('address2'),
1621
+			'ATT_city'     => $this->request->getRequestParam('city'),
1622
+			'STA_ID'       => $STA_ID,
1623
+			'CNT_ISO'      => $CNT_ISO,
1624
+			'ATT_zip'      => $this->request->getRequestParam('zip'),
1625
+			'ATT_phone'    => $this->request->getRequestParam('phone'),
1626
+		];
1627
+		// validate the email address since it is the most important piece of info
1628
+		if (empty($attendee_data['ATT_email'])) {
1629
+			EE_Error::add_error(
1630
+				esc_html__('An invalid email address was submitted.', 'event_espresso'),
1631
+				__FILE__,
1632
+				__FUNCTION__,
1633
+				__LINE__
1634
+			);
1635
+		}
1636
+		// does this attendee already exist in the db ? we're searching using a combination of first name, last name,
1637
+		// AND email address
1638
+		if (
1639
+			! empty($attendee_data['ATT_fname'])
1640
+			&& ! empty($attendee_data['ATT_lname'])
1641
+			&& ! empty($attendee_data['ATT_email'])
1642
+		) {
1643
+			$existing_attendee = EEM_Attendee::instance()->find_existing_attendee(
1644
+				[
1645
+					'ATT_fname' => $attendee_data['ATT_fname'],
1646
+					'ATT_lname' => $attendee_data['ATT_lname'],
1647
+					'ATT_email' => $attendee_data['ATT_email'],
1648
+				]
1649
+			);
1650
+			if ($existing_attendee instanceof EE_Attendee) {
1651
+				return $existing_attendee;
1652
+			}
1653
+		}
1654
+		// no existing attendee? kk let's create a new one
1655
+		// kinda lame, but we need a first and last name to create an attendee, so use the email address if those
1656
+		// don't exist
1657
+		$attendee_data['ATT_fname'] = ! empty($attendee_data['ATT_fname'])
1658
+			? $attendee_data['ATT_fname']
1659
+			: $attendee_data['ATT_email'];
1660
+		$attendee_data['ATT_lname'] = ! empty($attendee_data['ATT_lname'])
1661
+			? $attendee_data['ATT_lname']
1662
+			: $attendee_data['ATT_email'];
1663
+		return EE_Attendee::new_instance($attendee_data);
1664
+	}
1665
+
1666
+
1667
+
1668
+	/********************************************************************************************************/
1669
+	/****************************************  PROCESS REG STEP  *****************************************/
1670
+	/********************************************************************************************************/
1671
+	/**
1672
+	 * process_reg_step
1673
+	 *
1674
+	 * @return bool
1675
+	 * @throws EE_Error
1676
+	 * @throws InvalidArgumentException
1677
+	 * @throws ReflectionException
1678
+	 * @throws EntityNotFoundException
1679
+	 * @throws InvalidDataTypeException
1680
+	 * @throws InvalidInterfaceException
1681
+	 * @throws InvalidStatusException
1682
+	 */
1683
+	public function process_reg_step()
1684
+	{
1685
+		// how have they chosen to pay?
1686
+		$this->checkout->selected_method_of_payment = $this->checkout->transaction->is_free()
1687
+			? 'no_payment_required'
1688
+			: $this->_get_selected_method_of_payment(true);
1689
+		// choose your own adventure based on method_of_payment
1690
+		switch ($this->checkout->selected_method_of_payment) {
1691
+			case 'events_sold_out':
1692
+				$this->checkout->redirect     = true;
1693
+				$this->checkout->redirect_url = $this->checkout->cancel_page_url;
1694
+				$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1695
+				// mark this reg step as completed
1696
+				$this->set_completed();
1697
+				return false;
1698
+
1699
+			case 'payments_closed':
1700
+				if (
1701
+					apply_filters(
1702
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__payments_closed__display_success',
1703
+						false
1704
+					)
1705
+				) {
1706
+					EE_Error::add_success(
1707
+						esc_html__('no payment required at this time.', 'event_espresso'),
1708
+						__FILE__,
1709
+						__FUNCTION__,
1710
+						__LINE__
1711
+					);
1712
+				}
1713
+				// mark this reg step as completed
1714
+				$this->set_completed();
1715
+				return true;
1716
+
1717
+			case 'no_payment_required':
1718
+				if (
1719
+					apply_filters(
1720
+						'FHEE__EE_SPCO_Reg_Step_Payment_Options__process_reg_step__no_payment_required__display_success',
1721
+						false
1722
+					)
1723
+				) {
1724
+					EE_Error::add_success(
1725
+						esc_html__('no payment required.', 'event_espresso'),
1726
+						__FILE__,
1727
+						__FUNCTION__,
1728
+						__LINE__
1729
+					);
1730
+				}
1731
+				// mark this reg step as completed
1732
+				$this->set_completed();
1733
+				return true;
1734
+
1735
+			default:
1736
+				$registrations         = EE_Registry::instance()->SSN->checkout()->transaction->registrations(
1737
+					EE_Registry::instance()->SSN->checkout()->reg_cache_where_params
1738
+				);
1739
+				$ejected_registrations = EE_SPCO_Reg_Step_Payment_Options::find_registrations_that_lost_their_space(
1740
+					$registrations,
1741
+					EE_Registry::instance()->SSN->checkout()->revisit
1742
+				);
1743
+				// calculate difference between the two arrays
1744
+				$registrations = array_diff($registrations, $ejected_registrations);
1745
+				if (empty($registrations)) {
1746
+					$this->_redirect_because_event_sold_out();
1747
+					return false;
1748
+				}
1749
+				$payment_successful = $this->_process_payment();
1750
+				if ($payment_successful) {
1751
+					$this->checkout->continue_reg = true;
1752
+					$this->_maybe_set_completed($this->checkout->payment_method);
1753
+				} else {
1754
+					$this->checkout->continue_reg = false;
1755
+				}
1756
+				return $payment_successful;
1757
+		}
1758
+	}
1759
+
1760
+
1761
+	/**
1762
+	 * _redirect_because_event_sold_out
1763
+	 *
1764
+	 * @return void
1765
+	 */
1766
+	protected function _redirect_because_event_sold_out()
1767
+	{
1768
+		$this->checkout->continue_reg = false;
1769
+		// set redirect URL
1770
+		$this->checkout->redirect_url = add_query_arg(
1771
+			['e_reg_url_link' => $this->checkout->reg_url_link],
1772
+			$this->checkout->current_step->reg_step_url()
1773
+		);
1774
+		$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1775
+	}
1776
+
1777
+
1778
+	/**
1779
+	 * _maybe_set_completed
1780
+	 *
1781
+	 * @param EE_Payment_Method $payment_method
1782
+	 * @return void
1783
+	 * @throws EE_Error
1784
+	 */
1785
+	protected function _maybe_set_completed(EE_Payment_Method $payment_method)
1786
+	{
1787
+		switch ($payment_method->type_obj()->payment_occurs()) {
1788
+			case EE_PMT_Base::offsite:
1789
+				break;
1790
+			case EE_PMT_Base::onsite:
1791
+			case EE_PMT_Base::offline:
1792
+				// mark this reg step as completed
1793
+				$this->set_completed();
1794
+				break;
1795
+		}
1796
+	}
1797
+
1798
+
1799
+	/**
1800
+	 *    update_reg_step
1801
+	 *    this is the final step after a user  revisits the site to retry a payment
1802
+	 *
1803
+	 * @return bool
1804
+	 * @throws EE_Error
1805
+	 * @throws InvalidArgumentException
1806
+	 * @throws ReflectionException
1807
+	 * @throws EntityNotFoundException
1808
+	 * @throws InvalidDataTypeException
1809
+	 * @throws InvalidInterfaceException
1810
+	 * @throws InvalidStatusException
1811
+	 */
1812
+	public function update_reg_step()
1813
+	{
1814
+		$success = true;
1815
+		// if payment required
1816
+		if ($this->checkout->transaction->total() > 0) {
1817
+			do_action(
1818
+				'AHEE__EE_Single_Page_Checkout__process_finalize_registration__before_gateway',
1819
+				$this->checkout->transaction
1820
+			);
1821
+			// attempt payment via payment method
1822
+			$success = $this->process_reg_step();
1823
+		}
1824
+		if ($success && ! $this->checkout->redirect) {
1825
+			$this->checkout->cart->get_grand_total()->save_this_and_descendants_to_txn(
1826
+				$this->checkout->transaction->ID()
1827
+			);
1828
+			// set return URL
1829
+			$this->checkout->redirect_url = add_query_arg(
1830
+				['e_reg_url_link' => $this->checkout->reg_url_link],
1831
+				$this->checkout->thank_you_page_url
1832
+			);
1833
+		}
1834
+		return $success;
1835
+	}
1836
+
1837
+
1838
+	/**
1839
+	 *    _process_payment
1840
+	 *
1841
+	 * @return bool
1842
+	 * @throws EE_Error
1843
+	 * @throws InvalidArgumentException
1844
+	 * @throws ReflectionException
1845
+	 * @throws RuntimeException
1846
+	 * @throws InvalidDataTypeException
1847
+	 * @throws InvalidInterfaceException
1848
+	 */
1849
+	private function _process_payment()
1850
+	{
1851
+		// basically confirm that the event hasn't sold out since they hit the page
1852
+		if (! $this->_last_second_ticket_verifications()) {
1853
+			return false;
1854
+		}
1855
+		// ya gotta make a choice man
1856
+		if (empty($this->checkout->selected_method_of_payment)) {
1857
+			$this->checkout->json_response->set_plz_select_method_of_payment(
1858
+				esc_html__('Please select a method of payment before proceeding.', 'event_espresso')
1859
+			);
1860
+			return false;
1861
+		}
1862
+		// get EE_Payment_Method object
1863
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
1864
+			return false;
1865
+		}
1866
+		// setup billing form
1867
+		if ($this->checkout->payment_method->is_on_site()) {
1868
+			$this->checkout->billing_form = $this->_get_billing_form_for_payment_method(
1869
+				$this->checkout->payment_method
1870
+			);
1871
+			// bad billing form ?
1872
+			if (! $this->_billing_form_is_valid()) {
1873
+				return false;
1874
+			}
1875
+		}
1876
+		// ensure primary registrant has been fully processed
1877
+		if (! $this->_setup_primary_registrant_prior_to_payment()) {
1878
+			return false;
1879
+		}
1880
+		// if session is close to expiring (under 10 minutes by default)
1881
+		if ((time() - EE_Registry::instance()->SSN->expiration()) < EE_Registry::instance()->SSN->extension()) {
1882
+			// add some time to session expiration so that payment can be completed
1883
+			EE_Registry::instance()->SSN->extend_expiration();
1884
+		}
1885
+		/** @type EE_Transaction_Processor $transaction_processor */
1886
+		// $transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
1887
+		// in case a registrant leaves to an Off-Site Gateway and never returns, we want to approve any registrations
1888
+		// for events with a default reg status of Approved
1889
+		// $transaction_processor->toggle_registration_statuses_for_default_approved_events(
1890
+		//      $this->checkout->transaction, $this->checkout->reg_cache_where_params
1891
+		// );
1892
+		// attempt payment
1893
+		$payment = $this->_attempt_payment($this->checkout->payment_method);
1894
+		// process results
1895
+		$payment = $this->_validate_payment($payment);
1896
+		$payment = $this->_post_payment_processing($payment);
1897
+		// verify payment
1898
+		if ($payment instanceof EE_Payment) {
1899
+			// store that for later
1900
+			$this->checkout->payment = $payment;
1901
+			// we can also consider the TXN to not have been failed, so temporarily upgrade it's status to abandoned
1902
+			$this->checkout->transaction->toggle_failed_transaction_status();
1903
+			$payment_status = $payment->status();
1904
+			return $payment_status === EEM_Payment::status_id_approved
1905
+				   || $payment_status === EEM_Payment::status_id_pending;
1906
+		}
1907
+		if ($payment === true) {
1908
+			// please note that offline payment methods will NOT make a payment,
1909
+			// but instead just mark themselves as the PMD_ID on the transaction, and return true
1910
+			$this->checkout->payment = $payment;
1911
+			return true;
1912
+		}
1913
+		// where's my money?
1914
+		return false;
1915
+	}
1916
+
1917
+
1918
+	/**
1919
+	 * _last_second_ticket_verifications
1920
+	 *
1921
+	 * @return bool
1922
+	 * @throws EE_Error
1923
+	 * @throws ReflectionException
1924
+	 */
1925
+	protected function _last_second_ticket_verifications()
1926
+	{
1927
+		// don't bother re-validating if not a return visit
1928
+		if (! $this->checkout->revisit) {
1929
+			return true;
1930
+		}
1931
+		$registrations = $this->checkout->transaction->registrations();
1932
+		if (empty($registrations)) {
1933
+			return false;
1934
+		}
1935
+		foreach ($registrations as $registration) {
1936
+			if ($registration instanceof EE_Registration && ! $registration->is_approved()) {
1937
+				$event = $registration->event_obj();
1938
+				if ($event instanceof EE_Event && $event->is_sold_out(true)) {
1939
+					EE_Error::add_error(
1940
+						apply_filters(
1941
+							'FHEE__EE_SPCO_Reg_Step_Payment_Options___last_second_ticket_verifications__sold_out_events_msg',
1942
+							sprintf(
1943
+								esc_html__(
1944
+									'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.',
1945
+									'event_espresso'
1946
+								),
1947
+								$event->name()
1948
+							)
1949
+						),
1950
+						__FILE__,
1951
+						__FUNCTION__,
1952
+						__LINE__
1953
+					);
1954
+					return false;
1955
+				}
1956
+			}
1957
+		}
1958
+		return true;
1959
+	}
1960
+
1961
+
1962
+	/**
1963
+	 * redirect_form
1964
+	 *
1965
+	 * @return bool
1966
+	 * @throws EE_Error
1967
+	 * @throws InvalidArgumentException
1968
+	 * @throws ReflectionException
1969
+	 * @throws InvalidDataTypeException
1970
+	 * @throws InvalidInterfaceException
1971
+	 */
1972
+	public function redirect_form()
1973
+	{
1974
+		$payment_method_billing_info = $this->_payment_method_billing_info(
1975
+			$this->_get_payment_method_for_selected_method_of_payment()
1976
+		);
1977
+		$html                        = $payment_method_billing_info->get_html();
1978
+		$html                        .= $this->checkout->redirect_form;
1979
+		/** @var ResponseInterface $response */
1980
+		$response = LoaderFactory::getLoader()->getShared(ResponseInterface::class);
1981
+		$response->addOutput($html);
1982
+		return true;
1983
+	}
1984
+
1985
+
1986
+	/**
1987
+	 * _billing_form_is_valid
1988
+	 *
1989
+	 * @return bool
1990
+	 * @throws EE_Error
1991
+	 */
1992
+	private function _billing_form_is_valid()
1993
+	{
1994
+		if (! $this->checkout->payment_method->type_obj()->has_billing_form()) {
1995
+			return true;
1996
+		}
1997
+		if ($this->checkout->billing_form instanceof EE_Billing_Info_Form) {
1998
+			if ($this->checkout->billing_form->was_submitted()) {
1999
+				$this->checkout->billing_form->receive_form_submission();
2000
+				if ($this->checkout->billing_form->is_valid()) {
2001
+					return true;
2002
+				}
2003
+				$validation_errors = $this->checkout->billing_form->get_validation_errors_accumulated();
2004
+				$error_strings     = [];
2005
+				foreach ($validation_errors as $validation_error) {
2006
+					if ($validation_error instanceof EE_Validation_Error) {
2007
+						$form_section = $validation_error->get_form_section();
2008
+						if ($form_section instanceof EE_Form_Input_Base) {
2009
+							$label = $form_section->html_label_text();
2010
+						} elseif ($form_section instanceof EE_Form_Section_Base) {
2011
+							$label = $form_section->name();
2012
+						} else {
2013
+							$label = esc_html__('Validation Error', 'event_espresso');
2014
+						}
2015
+						$error_strings[] = sprintf('%1$s: %2$s', $label, $validation_error->getMessage());
2016
+					}
2017
+				}
2018
+				EE_Error::add_error(
2019
+					sprintf(
2020
+						esc_html__(
2021
+							'One or more billing form inputs are invalid and require correction before proceeding. %1$s %2$s',
2022
+							'event_espresso'
2023
+						),
2024
+						'<br/>',
2025
+						implode('<br/>', $error_strings)
2026
+					),
2027
+					__FILE__,
2028
+					__FUNCTION__,
2029
+					__LINE__
2030
+				);
2031
+			} else {
2032
+				EE_Error::add_error(
2033
+					esc_html__(
2034
+						'The billing form was not submitted or something prevented it\'s submission.',
2035
+						'event_espresso'
2036
+					),
2037
+					__FILE__,
2038
+					__FUNCTION__,
2039
+					__LINE__
2040
+				);
2041
+			}
2042
+		} else {
2043
+			EE_Error::add_error(
2044
+				esc_html__(
2045
+					'The submitted billing form is invalid possibly due to a technical reason.',
2046
+					'event_espresso'
2047
+				),
2048
+				__FILE__,
2049
+				__FUNCTION__,
2050
+				__LINE__
2051
+			);
2052
+		}
2053
+		return false;
2054
+	}
2055
+
2056
+
2057
+	/**
2058
+	 * _setup_primary_registrant_prior_to_payment
2059
+	 * ensures that the primary registrant has a valid attendee object created with the critical details populated
2060
+	 * (first & last name & email) and that both the transaction object and primary registration object have been saved
2061
+	 * plz note that any other registrations will NOT be saved at this point (because they may not have any details
2062
+	 * yet)
2063
+	 *
2064
+	 * @return bool
2065
+	 * @throws EE_Error
2066
+	 * @throws InvalidArgumentException
2067
+	 * @throws ReflectionException
2068
+	 * @throws RuntimeException
2069
+	 * @throws InvalidDataTypeException
2070
+	 * @throws InvalidInterfaceException
2071
+	 */
2072
+	private function _setup_primary_registrant_prior_to_payment()
2073
+	{
2074
+		// check if transaction has a primary registrant and that it has a related Attendee object
2075
+		// if not, then we need to at least gather some primary registrant data before attempting payment
2076
+		if (
2077
+			$this->checkout->billing_form instanceof EE_Billing_Attendee_Info_Form
2078
+			&& ! $this->checkout->transaction_has_primary_registrant()
2079
+			&& ! $this->_capture_primary_registration_data_from_billing_form()
2080
+		) {
2081
+			return false;
2082
+		}
2083
+		// because saving an object clears it's cache, we need to do the chevy shuffle
2084
+		// grab the primary_registration object
2085
+		$primary_registration = $this->checkout->transaction->primary_registration();
2086
+		// at this point we'll consider a TXN to not have been failed
2087
+		$this->checkout->transaction->toggle_failed_transaction_status();
2088
+		// save the TXN ( which clears cached copy of primary_registration)
2089
+		$this->checkout->transaction->save();
2090
+		// grab TXN ID and save it to the primary_registration
2091
+		$primary_registration->set_transaction_id($this->checkout->transaction->ID());
2092
+		// save what we have so far
2093
+		$primary_registration->save();
2094
+		return true;
2095
+	}
2096
+
2097
+
2098
+	/**
2099
+	 * _capture_primary_registration_data_from_billing_form
2100
+	 *
2101
+	 * @return bool
2102
+	 * @throws EE_Error
2103
+	 * @throws InvalidArgumentException
2104
+	 * @throws ReflectionException
2105
+	 * @throws InvalidDataTypeException
2106
+	 * @throws InvalidInterfaceException
2107
+	 */
2108
+	private function _capture_primary_registration_data_from_billing_form()
2109
+	{
2110
+		// convert billing form data into an attendee
2111
+		$this->checkout->primary_attendee_obj = $this->checkout->billing_form->create_attendee_from_billing_form_data();
2112
+		if (! $this->checkout->primary_attendee_obj instanceof EE_Attendee) {
2113
+			EE_Error::add_error(
2114
+				sprintf(
2115
+					esc_html__(
2116
+						'The billing form details could not be used for attendee details due to a technical issue.%sPlease try again or contact %s for assistance.',
2117
+						'event_espresso'
2118
+					),
2119
+					'<br/>',
2120
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2121
+				),
2122
+				__FILE__,
2123
+				__FUNCTION__,
2124
+				__LINE__
2125
+			);
2126
+			return false;
2127
+		}
2128
+		$primary_registration = $this->checkout->transaction->primary_registration();
2129
+		if (! $primary_registration instanceof EE_Registration) {
2130
+			EE_Error::add_error(
2131
+				sprintf(
2132
+					esc_html__(
2133
+						'The primary registrant for this transaction could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2134
+						'event_espresso'
2135
+					),
2136
+					'<br/>',
2137
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2138
+				),
2139
+				__FILE__,
2140
+				__FUNCTION__,
2141
+				__LINE__
2142
+			);
2143
+			return false;
2144
+		}
2145
+		if (
2146
+			! $primary_registration->_add_relation_to($this->checkout->primary_attendee_obj, 'Attendee')
2147
+			  instanceof
2148
+			  EE_Attendee
2149
+		) {
2150
+			EE_Error::add_error(
2151
+				sprintf(
2152
+					esc_html__(
2153
+						'The primary registrant could not be associated with this transaction due to a technical issue.%sPlease try again or contact %s for assistance.',
2154
+						'event_espresso'
2155
+					),
2156
+					'<br/>',
2157
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2158
+				),
2159
+				__FILE__,
2160
+				__FUNCTION__,
2161
+				__LINE__
2162
+			);
2163
+			return false;
2164
+		}
2165
+		/** @type EE_Registration_Processor $registration_processor */
2166
+		$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
2167
+		// at this point, we should have enough details about the registrant to consider the registration NOT incomplete
2168
+		$registration_processor->toggle_incomplete_registration_status_to_default($primary_registration);
2169
+		return true;
2170
+	}
2171
+
2172
+
2173
+	/**
2174
+	 * _get_payment_method_for_selected_method_of_payment
2175
+	 * retrieves a valid payment method
2176
+	 *
2177
+	 * @return EE_Payment_Method
2178
+	 * @throws EE_Error
2179
+	 * @throws InvalidArgumentException
2180
+	 * @throws ReflectionException
2181
+	 * @throws InvalidDataTypeException
2182
+	 * @throws InvalidInterfaceException
2183
+	 */
2184
+	private function _get_payment_method_for_selected_method_of_payment()
2185
+	{
2186
+		if ($this->checkout->selected_method_of_payment === 'events_sold_out') {
2187
+			$this->_redirect_because_event_sold_out();
2188
+			return null;
2189
+		}
2190
+		// get EE_Payment_Method object
2191
+		if (isset($this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ])) {
2192
+			$payment_method = $this->checkout->available_payment_methods[ $this->checkout->selected_method_of_payment ];
2193
+		} else {
2194
+			// load EEM_Payment_Method
2195
+			EE_Registry::instance()->load_model('Payment_Method');
2196
+			$EEM_Payment_Method = EEM_Payment_Method::instance();
2197
+			$payment_method     = $EEM_Payment_Method->get_one_by_slug($this->checkout->selected_method_of_payment);
2198
+		}
2199
+		// verify $payment_method
2200
+		if (! $payment_method instanceof EE_Payment_Method) {
2201
+			// not a payment
2202
+			EE_Error::add_error(
2203
+				sprintf(
2204
+					esc_html__(
2205
+						'The selected method of payment could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2206
+						'event_espresso'
2207
+					),
2208
+					'<br/>',
2209
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2210
+				),
2211
+				__FILE__,
2212
+				__FUNCTION__,
2213
+				__LINE__
2214
+			);
2215
+			return null;
2216
+		}
2217
+		// and verify it has a valid Payment_Method Type object
2218
+		if (! $payment_method->type_obj() instanceof EE_PMT_Base) {
2219
+			// not a payment
2220
+			EE_Error::add_error(
2221
+				sprintf(
2222
+					esc_html__(
2223
+						'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
2224
+						'event_espresso'
2225
+					),
2226
+					'<br/>',
2227
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2228
+				),
2229
+				__FILE__,
2230
+				__FUNCTION__,
2231
+				__LINE__
2232
+			);
2233
+			return null;
2234
+		}
2235
+		return $payment_method;
2236
+	}
2237
+
2238
+
2239
+	/**
2240
+	 *    _attempt_payment
2241
+	 *
2242
+	 * @access    private
2243
+	 * @type    EE_Payment_Method $payment_method
2244
+	 * @return mixed EE_Payment | boolean
2245
+	 * @throws EE_Error
2246
+	 * @throws InvalidArgumentException
2247
+	 * @throws ReflectionException
2248
+	 * @throws InvalidDataTypeException
2249
+	 * @throws InvalidInterfaceException
2250
+	 */
2251
+	private function _attempt_payment(EE_Payment_Method $payment_method)
2252
+	{
2253
+		$payment = null;
2254
+		$this->checkout->transaction->save();
2255
+		$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2256
+		if (! $payment_processor instanceof EE_Payment_Processor) {
2257
+			return false;
2258
+		}
2259
+		try {
2260
+			$payment_processor->set_revisit($this->checkout->revisit);
2261
+			// generate payment object
2262
+			$payment = $payment_processor->process_payment(
2263
+				$payment_method,
2264
+				$this->checkout->transaction,
2265
+				$this->checkout->amount_owing,
2266
+				$this->checkout->billing_form instanceof EE_Billing_Info_Form
2267
+					? $this->checkout->billing_form
2268
+					: null,
2269
+				$this->_get_return_url($payment_method),
2270
+				'CART',
2271
+				$this->checkout->admin_request,
2272
+				true,
2273
+				$this->reg_step_url()
2274
+			);
2275
+		} catch (Exception $e) {
2276
+			$this->_handle_payment_processor_exception($e);
2277
+		}
2278
+		return $payment;
2279
+	}
2280
+
2281
+
2282
+	/**
2283
+	 * _handle_payment_processor_exception
2284
+	 *
2285
+	 * @param Exception $e
2286
+	 * @return void
2287
+	 * @throws EE_Error
2288
+	 * @throws InvalidArgumentException
2289
+	 * @throws InvalidDataTypeException
2290
+	 * @throws InvalidInterfaceException
2291
+	 */
2292
+	protected function _handle_payment_processor_exception(Exception $e)
2293
+	{
2294
+		EE_Error::add_error(
2295
+			sprintf(
2296
+				esc_html__(
2297
+					'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',
2298
+					'event_espresso'
2299
+				),
2300
+				'<br/>',
2301
+				EE_Registry::instance()->CFG->organization->get_pretty('email'),
2302
+				$e->getMessage(),
2303
+				$e->getFile(),
2304
+				$e->getLine()
2305
+			),
2306
+			__FILE__,
2307
+			__FUNCTION__,
2308
+			__LINE__
2309
+		);
2310
+	}
2311
+
2312
+
2313
+	/**
2314
+	 * _get_return_url
2315
+	 *
2316
+	 * @param EE_Payment_Method $payment_method
2317
+	 * @return string
2318
+	 * @throws EE_Error
2319
+	 * @throws ReflectionException
2320
+	 */
2321
+	protected function _get_return_url(EE_Payment_Method $payment_method)
2322
+	{
2323
+		$return_url = '';
2324
+		switch ($payment_method->type_obj()->payment_occurs()) {
2325
+			case EE_PMT_Base::offsite:
2326
+				$return_url = add_query_arg(
2327
+					[
2328
+						'action'                     => 'process_gateway_response',
2329
+						'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2330
+						'spco_txn'                   => $this->checkout->transaction->ID(),
2331
+					],
2332
+					$this->reg_step_url()
2333
+				);
2334
+				break;
2335
+			case EE_PMT_Base::onsite:
2336
+			case EE_PMT_Base::offline:
2337
+				$return_url = $this->checkout->next_step->reg_step_url();
2338
+				break;
2339
+		}
2340
+		return $return_url;
2341
+	}
2342
+
2343
+
2344
+	/**
2345
+	 * _validate_payment
2346
+	 *
2347
+	 * @param EE_Payment $payment
2348
+	 * @return EE_Payment|FALSE
2349
+	 * @throws EE_Error
2350
+	 * @throws InvalidArgumentException
2351
+	 * @throws InvalidDataTypeException
2352
+	 * @throws InvalidInterfaceException
2353
+	 */
2354
+	private function _validate_payment($payment = null)
2355
+	{
2356
+		if ($this->checkout->payment_method->is_off_line()) {
2357
+			return true;
2358
+		}
2359
+		// verify payment object
2360
+		if (! $payment instanceof EE_Payment) {
2361
+			// not a payment
2362
+			EE_Error::add_error(
2363
+				sprintf(
2364
+					esc_html__(
2365
+						'A valid payment was not generated due to a technical issue.%1$sPlease try again or contact %2$s for assistance.',
2366
+						'event_espresso'
2367
+					),
2368
+					'<br/>',
2369
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2370
+				),
2371
+				__FILE__,
2372
+				__FUNCTION__,
2373
+				__LINE__
2374
+			);
2375
+			return false;
2376
+		}
2377
+		return $payment;
2378
+	}
2379
+
2380
+
2381
+	/**
2382
+	 * _post_payment_processing
2383
+	 *
2384
+	 * @param EE_Payment|bool $payment
2385
+	 * @return bool
2386
+	 * @throws EE_Error
2387
+	 * @throws InvalidArgumentException
2388
+	 * @throws InvalidDataTypeException
2389
+	 * @throws InvalidInterfaceException
2390
+	 * @throws ReflectionException
2391
+	 */
2392
+	private function _post_payment_processing($payment = null)
2393
+	{
2394
+		// Off-Line payment?
2395
+		if ($payment === true) {
2396
+			// $this->_setup_redirect_for_next_step();
2397
+			return true;
2398
+			// On-Site payment?
2399
+		} elseif ($this->checkout->payment_method->is_on_site()) {
2400
+			if (! $this->_process_payment_status($payment, EE_PMT_Base::onsite)) {
2401
+				// $this->_setup_redirect_for_next_step();
2402
+				$this->checkout->continue_reg = false;
2403
+			}
2404
+			// Off-Site payment?
2405
+		} elseif ($this->checkout->payment_method->is_off_site()) {
2406
+			// if a payment object was made and it specifies a redirect url, then we'll setup that redirect info
2407
+			if ($payment instanceof EE_Payment && $payment->redirect_url()) {
2408
+				do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->redirect_url(), '$payment->redirect_url()');
2409
+				$this->checkout->redirect      = true;
2410
+				$this->checkout->redirect_form = $payment->redirect_form();
2411
+				$this->checkout->redirect_url  = $this->reg_step_url('redirect_form');
2412
+				// set JSON response
2413
+				$this->checkout->json_response->set_redirect_form($this->checkout->redirect_form);
2414
+				// and lastly, let's bump the payment status to pending
2415
+				$payment->set_status(EEM_Payment::status_id_pending);
2416
+				$payment->save();
2417
+			} else {
2418
+				// we couldn't redirect the user. Let's tell them why.
2419
+				$error_message = sprintf(
2420
+					esc_html__(
2421
+						'It appears the Off Site Payment Method was not configured properly.%sPlease try again or contact %s for assistance.',
2422
+						'event_espresso'
2423
+					),
2424
+					'<br/>',
2425
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2426
+				);
2427
+				if ($payment instanceof EE_Payment && $payment->gateway_response()) {
2428
+					$error_message = $error_message . '<br/>' . $payment->gateway_response();
2429
+				}
2430
+				$this->checkout->continue_reg = false;
2431
+				EE_Error::add_error(
2432
+					$error_message,
2433
+					__FILE__,
2434
+					__FUNCTION__,
2435
+					__LINE__
2436
+				);
2437
+			}
2438
+		} else {
2439
+			// ummm ya... not Off-Line, not On-Site, not off-Site ????
2440
+			$this->checkout->continue_reg = false;
2441
+			return false;
2442
+		}
2443
+		return $payment;
2444
+	}
2445
+
2446
+
2447
+	/**
2448
+	 *    _process_payment_status
2449
+	 *
2450
+	 * @type    EE_Payment $payment
2451
+	 * @param string       $payment_occurs
2452
+	 * @return bool
2453
+	 * @throws EE_Error
2454
+	 * @throws InvalidArgumentException
2455
+	 * @throws InvalidDataTypeException
2456
+	 * @throws InvalidInterfaceException
2457
+	 */
2458
+	private function _process_payment_status($payment, $payment_occurs = EE_PMT_Base::offline)
2459
+	{
2460
+		// off-line payment? carry on
2461
+		if ($payment_occurs === EE_PMT_Base::offline) {
2462
+			return true;
2463
+		}
2464
+		// verify payment validity
2465
+		if ($payment instanceof EE_Payment) {
2466
+			do_action('AHEE_log', __CLASS__, __FUNCTION__, $payment->status(), '$payment->status()');
2467
+			$msg = $payment->gateway_response();
2468
+			// check results
2469
+			switch ($payment->status()) {
2470
+				// good payment
2471
+				case EEM_Payment::status_id_approved:
2472
+					EE_Error::add_success(
2473
+						esc_html__('Your payment was processed successfully.', 'event_espresso'),
2474
+						__FILE__,
2475
+						__FUNCTION__,
2476
+						__LINE__
2477
+					);
2478
+					return true;
2479
+				// slow payment
2480
+				case EEM_Payment::status_id_pending:
2481
+					if (empty($msg)) {
2482
+						$msg = esc_html__(
2483
+							'Your payment appears to have been processed successfully, but the Instant Payment Notification has not yet been received. It should arrive shortly.',
2484
+							'event_espresso'
2485
+						);
2486
+					}
2487
+					EE_Error::add_success($msg, __FILE__, __FUNCTION__, __LINE__);
2488
+					return true;
2489
+				// don't wanna payment
2490
+				case EEM_Payment::status_id_cancelled:
2491
+					if (empty($msg)) {
2492
+						$msg = _n(
2493
+							'Payment cancelled. Please try again.',
2494
+							'Payment cancelled. Please try again or select another method of payment.',
2495
+							count($this->checkout->available_payment_methods),
2496
+							'event_espresso'
2497
+						);
2498
+					}
2499
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2500
+					return false;
2501
+				// not enough payment
2502
+				case EEM_Payment::status_id_declined:
2503
+					if (empty($msg)) {
2504
+						$msg = _n(
2505
+							'We\'re sorry but your payment was declined. Please try again.',
2506
+							'We\'re sorry but your payment was declined. Please try again or select another method of payment.',
2507
+							count($this->checkout->available_payment_methods),
2508
+							'event_espresso'
2509
+						);
2510
+					}
2511
+					EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
2512
+					return false;
2513
+				// bad payment
2514
+				case EEM_Payment::status_id_failed:
2515
+					if (! empty($msg)) {
2516
+						EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2517
+						return false;
2518
+					}
2519
+					// default to error below
2520
+					break;
2521
+			}
2522
+		}
2523
+		// off-site payment gateway responses are too unreliable, so let's just assume that
2524
+		// the payment processing is just running slower than the registrant's request
2525
+		if ($payment_occurs === EE_PMT_Base::offsite) {
2526
+			return true;
2527
+		}
2528
+		EE_Error::add_error(
2529
+			sprintf(
2530
+				esc_html__(
2531
+					'Your payment could not be processed successfully due to a technical issue.%sPlease try again or contact %s for assistance.',
2532
+					'event_espresso'
2533
+				),
2534
+				'<br/>',
2535
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
2536
+			),
2537
+			__FILE__,
2538
+			__FUNCTION__,
2539
+			__LINE__
2540
+		);
2541
+		return false;
2542
+	}
2543
+
2544
+
2545
+
2546
+
2547
+
2548
+
2549
+	/********************************************************************************************************/
2550
+	/**********************************  PROCESS GATEWAY RESPONSE  **********************************/
2551
+	/********************************************************************************************************/
2552
+	/**
2553
+	 * process_gateway_response
2554
+	 * this is the return point for Off-Site Payment Methods
2555
+	 * It will attempt to "handle the IPN" if it appears that this has not already occurred,
2556
+	 * otherwise, it will load up the last payment made for the TXN.
2557
+	 * If the payment retrieved looks good, it will then either:
2558
+	 *    complete the current step and allow advancement to the next reg step
2559
+	 *        or present the payment options again
2560
+	 *
2561
+	 * @return bool
2562
+	 * @throws EE_Error
2563
+	 * @throws InvalidArgumentException
2564
+	 * @throws ReflectionException
2565
+	 * @throws InvalidDataTypeException
2566
+	 * @throws InvalidInterfaceException
2567
+	 */
2568
+	public function process_gateway_response()
2569
+	{
2570
+		// how have they chosen to pay?
2571
+		$this->checkout->selected_method_of_payment = $this->_get_selected_method_of_payment(true);
2572
+		// get EE_Payment_Method object
2573
+		if (! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()) {
2574
+			$this->checkout->continue_reg = false;
2575
+			return false;
2576
+		}
2577
+		if (! $this->checkout->payment_method->is_off_site()) {
2578
+			return false;
2579
+		}
2580
+		$this->_validate_offsite_return();
2581
+		// verify TXN
2582
+		if ($this->checkout->transaction instanceof EE_Transaction) {
2583
+			$gateway = $this->checkout->payment_method->type_obj()->get_gateway();
2584
+			if (! $gateway instanceof EE_Offsite_Gateway) {
2585
+				$this->checkout->continue_reg = false;
2586
+				return false;
2587
+			}
2588
+			$payment = $this->_process_off_site_payment($gateway);
2589
+			$payment = $this->_process_cancelled_payments($payment);
2590
+			$payment = $this->_validate_payment($payment);
2591
+			// if payment was not declined by the payment gateway or cancelled by the registrant
2592
+			if ($this->_process_payment_status($payment, EE_PMT_Base::offsite)) {
2593
+				// $this->_setup_redirect_for_next_step();
2594
+				// store that for later
2595
+				$this->checkout->payment = $payment;
2596
+				// mark this reg step as completed, as long as gateway doesn't use a separate IPN request,
2597
+				// because we will complete this step during the IPN processing then
2598
+				if (! $this->handle_IPN_in_this_request()) {
2599
+					$this->set_completed();
2600
+				}
2601
+				return true;
2602
+			}
2603
+		}
2604
+		// DEBUG LOG
2605
+		// $this->checkout->log(
2606
+		//     __CLASS__,
2607
+		//     __FUNCTION__,
2608
+		//     __LINE__,
2609
+		//     array('payment' => $payment)
2610
+		// );
2611
+		$this->checkout->continue_reg = false;
2612
+		return false;
2613
+	}
2614
+
2615
+
2616
+	/**
2617
+	 * _validate_return
2618
+	 *
2619
+	 * @return void
2620
+	 * @throws EE_Error
2621
+	 * @throws InvalidArgumentException
2622
+	 * @throws InvalidDataTypeException
2623
+	 * @throws InvalidInterfaceException
2624
+	 * @throws ReflectionException
2625
+	 */
2626
+	private function _validate_offsite_return()
2627
+	{
2628
+		$TXN_ID = $this->request->getRequestParam('spco_txn', 0, 'int');
2629
+		if ($TXN_ID !== $this->checkout->transaction->ID()) {
2630
+			// Houston... we might have a problem
2631
+			$invalid_TXN = false;
2632
+			// first gather some info
2633
+			$valid_TXN          = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
2634
+			$primary_registrant = $valid_TXN instanceof EE_Transaction
2635
+				? $valid_TXN->primary_registration()
2636
+				: null;
2637
+			// let's start by retrieving the cart for this TXN
2638
+			$cart = $this->checkout->get_cart_for_transaction($this->checkout->transaction);
2639
+			if ($cart instanceof EE_Cart) {
2640
+				// verify that the current cart has tickets
2641
+				$tickets = $cart->get_tickets();
2642
+				if (empty($tickets)) {
2643
+					$invalid_TXN = true;
2644
+				}
2645
+			} else {
2646
+				$invalid_TXN = true;
2647
+			}
2648
+			$valid_TXN_SID = $primary_registrant instanceof EE_Registration
2649
+				? $primary_registrant->session_ID()
2650
+				: null;
2651
+			// validate current Session ID and compare against valid TXN session ID
2652
+			if (
2653
+				$invalid_TXN // if this is already true, then skip other checks
2654
+				|| EE_Session::instance()->id() === null
2655
+				|| (
2656
+					// WARNING !!!
2657
+					// this could be PayPal sending back duplicate requests (ya they do that)
2658
+					// or it **could** mean someone is simply registering AGAIN after having just done so
2659
+					// so now we need to determine if this current TXN looks valid or not
2660
+					// and whether this reg step has even been started ?
2661
+					EE_Session::instance()->id() === $valid_TXN_SID
2662
+					// really? you're half way through this reg step, but you never started it ?
2663
+					&& $this->checkout->transaction->reg_step_completed($this->slug()) === false
2664
+				)
2665
+			) {
2666
+				$invalid_TXN = true;
2667
+			}
2668
+			if ($invalid_TXN) {
2669
+				// is the valid TXN completed ?
2670
+				if ($valid_TXN instanceof EE_Transaction) {
2671
+					// has this step even been started ?
2672
+					$reg_step_completed = $valid_TXN->reg_step_completed($this->slug());
2673
+					if ($reg_step_completed !== false && $reg_step_completed !== true) {
2674
+						// so it **looks** like this is a double request from PayPal
2675
+						// so let's try to pick up where we left off
2676
+						$this->checkout->transaction = $valid_TXN;
2677
+						$this->checkout->refresh_all_entities(true);
2678
+						return;
2679
+					}
2680
+				}
2681
+				// you appear to be lost?
2682
+				$this->_redirect_wayward_request($primary_registrant);
2683
+			}
2684
+		}
2685
+	}
2686
+
2687
+
2688
+	/**
2689
+	 * _redirect_wayward_request
2690
+	 *
2691
+	 * @param EE_Registration|null $primary_registrant
2692
+	 * @return void
2693
+	 * @throws EE_Error
2694
+	 * @throws InvalidArgumentException
2695
+	 * @throws InvalidDataTypeException
2696
+	 * @throws InvalidInterfaceException
2697
+	 * @throws ReflectionException
2698
+	 */
2699
+	private function _redirect_wayward_request(EE_Registration $primary_registrant)
2700
+	{
2701
+		if (! $primary_registrant instanceof EE_Registration) {
2702
+			// try redirecting based on the current TXN
2703
+			$primary_registrant = $this->checkout->transaction instanceof EE_Transaction
2704
+				? $this->checkout->transaction->primary_registration()
2705
+				: null;
2706
+		}
2707
+		if (! $primary_registrant instanceof EE_Registration) {
2708
+			EE_Error::add_error(
2709
+				sprintf(
2710
+					esc_html__(
2711
+						'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.',
2712
+						'event_espresso'
2713
+					),
2714
+					'<br/>',
2715
+					EE_Registry::instance()->CFG->organization->get_pretty('email')
2716
+				),
2717
+				__FILE__,
2718
+				__FUNCTION__,
2719
+				__LINE__
2720
+			);
2721
+			return;
2722
+		}
2723
+		// make sure transaction is not locked
2724
+		$this->checkout->transaction->unlock();
2725
+		wp_safe_redirect(
2726
+			add_query_arg(
2727
+				[
2728
+					'e_reg_url_link' => $primary_registrant->reg_url_link(),
2729
+				],
2730
+				$this->checkout->thank_you_page_url
2731
+			)
2732
+		);
2733
+		exit();
2734
+	}
2735
+
2736
+
2737
+	/**
2738
+	 * _process_off_site_payment
2739
+	 *
2740
+	 * @param EE_Offsite_Gateway $gateway
2741
+	 * @return EE_Payment
2742
+	 * @throws EE_Error
2743
+	 * @throws InvalidArgumentException
2744
+	 * @throws InvalidDataTypeException
2745
+	 * @throws InvalidInterfaceException
2746
+	 * @throws ReflectionException
2747
+	 */
2748
+	private function _process_off_site_payment(EE_Offsite_Gateway $gateway)
2749
+	{
2750
+		try {
2751
+			$request      = LoaderFactory::getLoader()->getShared(RequestInterface::class);
2752
+			$request_data = $request->requestParams();
2753
+			// if gateway uses_separate_IPN_request, then we don't have to process the IPN manually
2754
+			$this->set_handle_IPN_in_this_request(
2755
+				$gateway->handle_IPN_in_this_request($request_data, false)
2756
+			);
2757
+			if ($this->handle_IPN_in_this_request()) {
2758
+				// get payment details and process results
2759
+				/** @type EE_Payment_Processor $payment_processor */
2760
+				$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
2761
+				$payment           = $payment_processor->process_ipn(
2762
+					$request_data,
2763
+					$this->checkout->transaction,
2764
+					$this->checkout->payment_method,
2765
+					true,
2766
+					false
2767
+				);
2768
+				// $payment_source = 'process_ipn';
2769
+			} else {
2770
+				$payment = $this->checkout->transaction->last_payment();
2771
+				// $payment_source = 'last_payment';
2772
+			}
2773
+		} catch (Exception $e) {
2774
+			// let's just eat the exception and try to move on using any previously set payment info
2775
+			$payment = $this->checkout->transaction->last_payment();
2776
+			// $payment_source = 'last_payment after Exception';
2777
+			// but if we STILL don't have a payment object
2778
+			if (! $payment instanceof EE_Payment) {
2779
+				// then we'll object ! ( not object like a thing... but object like what a lawyer says ! )
2780
+				$this->_handle_payment_processor_exception($e);
2781
+			}
2782
+		}
2783
+		return $payment;
2784
+	}
2785
+
2786
+
2787
+	/**
2788
+	 * _process_cancelled_payments
2789
+	 * just makes sure that the payment status gets updated correctly
2790
+	 * so tha tan error isn't generated during payment validation
2791
+	 *
2792
+	 * @param EE_Payment $payment
2793
+	 * @return EE_Payment|null
2794
+	 * @throws EE_Error
2795
+	 */
2796
+	private function _process_cancelled_payments($payment = null)
2797
+	{
2798
+		if (
2799
+			$payment instanceof EE_Payment
2800
+			&& $this->request->requestParamIsSet('ee_cancel_payment')
2801
+			&& $payment->status() === EEM_Payment::status_id_failed
2802
+		) {
2803
+			$payment->set_status(EEM_Payment::status_id_cancelled);
2804
+		}
2805
+		return $payment;
2806
+	}
2807
+
2808
+
2809
+	/**
2810
+	 *    get_transaction_details_for_gateways
2811
+	 *
2812
+	 * @access    public
2813
+	 * @return void
2814
+	 * @throws EE_Error
2815
+	 * @throws InvalidArgumentException
2816
+	 * @throws ReflectionException
2817
+	 * @throws InvalidDataTypeException
2818
+	 * @throws InvalidInterfaceException
2819
+	 */
2820
+	public function get_transaction_details_for_gateways()
2821
+	{
2822
+		$txn_details = [];
2823
+		// ya gotta make a choice man
2824
+		if (empty($this->checkout->selected_method_of_payment)) {
2825
+			$txn_details = [
2826
+				'error' => esc_html__('Please select a method of payment before proceeding.', 'event_espresso'),
2827
+			];
2828
+		}
2829
+		// get EE_Payment_Method object
2830
+		if (
2831
+			empty($txn_details)
2832
+			&& ! $this->checkout->payment_method = $this->_get_payment_method_for_selected_method_of_payment()
2833
+		) {
2834
+			$txn_details = [
2835
+				'selected_method_of_payment' => $this->checkout->selected_method_of_payment,
2836
+				'error'                      => esc_html__(
2837
+					'A valid Payment Method could not be determined.',
2838
+					'event_espresso'
2839
+				),
2840
+			];
2841
+		}
2842
+		if (empty($txn_details) && $this->checkout->transaction instanceof EE_Transaction) {
2843
+			$return_url  = $this->_get_return_url($this->checkout->payment_method);
2844
+			$txn_details = [
2845
+				'TXN_ID'         => $this->checkout->transaction->ID(),
2846
+				'TXN_timestamp'  => $this->checkout->transaction->datetime(),
2847
+				'TXN_total'      => $this->checkout->transaction->total(),
2848
+				'TXN_paid'       => $this->checkout->transaction->paid(),
2849
+				'TXN_reg_steps'  => $this->checkout->transaction->reg_steps(),
2850
+				'STS_ID'         => $this->checkout->transaction->status_ID(),
2851
+				'PMD_ID'         => $this->checkout->transaction->payment_method_ID(),
2852
+				'payment_amount' => $this->checkout->amount_owing,
2853
+				'return_url'     => $return_url,
2854
+				'cancel_url'     => add_query_arg(['ee_cancel_payment' => true], $return_url),
2855
+				'notify_url'     => EE_Config::instance()->core->txn_page_url(
2856
+					[
2857
+						'e_reg_url_link'    => $this->checkout->transaction->primary_registration()->reg_url_link(),
2858
+						'ee_payment_method' => $this->checkout->payment_method->slug(),
2859
+					]
2860
+				),
2861
+			];
2862
+		}
2863
+		echo wp_json_encode($txn_details);
2864
+		exit();
2865
+	}
2866
+
2867
+
2868
+	/**
2869
+	 *    __sleep
2870
+	 * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
2871
+	 * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
2872
+	 * reg form, because if needed, it will be regenerated anyways
2873
+	 *
2874
+	 * @return array
2875
+	 */
2876
+	public function __sleep()
2877
+	{
2878
+		// remove the reg form and the checkout
2879
+		return array_diff(array_keys(get_object_vars($this)), ['reg_form', 'checkout', 'line_item_display']);
2880
+	}
2881 2881
 }
Please login to merge, or discard this patch.
attendee_information/EE_SPCO_Reg_Step_Attendee_Information.class.php 1 patch
Indentation   +858 added lines, -858 removed lines patch added patch discarded remove patch
@@ -23,862 +23,862 @@
 block discarded – undo
23 23
 class EE_SPCO_Reg_Step_Attendee_Information extends EE_SPCO_Reg_Step
24 24
 {
25 25
 
26
-    /**
27
-     * @var RegForm
28
-     */
29
-    public $reg_form;
30
-
31
-    /**
32
-     * @var int
33
-     */
34
-    protected $reg_form_count = 0;
35
-
36
-
37
-    /**
38
-     *    class constructor
39
-     *
40
-     * @access    public
41
-     * @param EE_Checkout $checkout
42
-     */
43
-    public function __construct(EE_Checkout $checkout)
44
-    {
45
-        $this->_slug    = 'attendee_information';
46
-        $this->_name    = esc_html__('Attendee Information', 'event_espresso');
47
-        $this->checkout = $checkout;
48
-        $this->_reset_success_message();
49
-        $this->set_instructions(
50
-            esc_html__('Please answer the following registration questions before proceeding.', 'event_espresso')
51
-        );
52
-    }
53
-
54
-
55
-    public function translate_js_strings()
56
-    {
57
-        EE_Registry::$i18n_js_strings['required_field']            = esc_html__(
58
-            ' is a required question.',
59
-            'event_espresso'
60
-        );
61
-        EE_Registry::$i18n_js_strings['required_multi_field']      = esc_html__(
62
-            ' is a required question. Please enter a value for at least one of the options.',
63
-            'event_espresso'
64
-        );
65
-        EE_Registry::$i18n_js_strings['answer_required_questions'] = esc_html__(
66
-            'Please answer all required questions correctly before proceeding.',
67
-            'event_espresso'
68
-        );
69
-        EE_Registry::$i18n_js_strings['attendee_info_copied']      = sprintf(
70
-            esc_html_x(
71
-                'The attendee information was successfully copied.%sPlease ensure the rest of the registration form is completed before proceeding.',
72
-                'The attendee information was successfully copied.(line break)Please ensure the rest of the registration form is completed before proceeding.',
73
-                'event_espresso'
74
-            ),
75
-            '<br/>'
76
-        );
77
-        EE_Registry::$i18n_js_strings['attendee_info_copy_error']  = esc_html__(
78
-            'An unknown error occurred on the server while attempting to copy the attendee information. Please refresh the page and try again.',
79
-            'event_espresso'
80
-        );
81
-        EE_Registry::$i18n_js_strings['enter_valid_email']         = esc_html__(
82
-            'You must enter a valid email address.',
83
-            'event_espresso'
84
-        );
85
-        EE_Registry::$i18n_js_strings['valid_email_and_questions'] = esc_html__(
86
-            'You must enter a valid email address and answer all other required questions before you can proceed.',
87
-            'event_espresso'
88
-        );
89
-    }
90
-
91
-
92
-    public function enqueue_styles_and_scripts()
93
-    {
94
-    }
95
-
96
-
97
-    /**
98
-     * @return boolean
99
-     */
100
-    public function initialize_reg_step(): bool
101
-    {
102
-        return true;
103
-    }
104
-
105
-
106
-    /**
107
-     * @return RegForm
108
-     * @throws DomainException
109
-     * @throws InvalidArgumentException
110
-     * @throws EntityNotFoundException
111
-     * @throws InvalidDataTypeException
112
-     * @throws InvalidInterfaceException
113
-     */
114
-    public function generate_reg_form(): RegForm
115
-    {
116
-        $this->setLegacyFiltersForRegFormGeneration();
117
-        /** @var RegFormDependencyHandler $dependency_handler */
118
-        $dependency_handler = LoaderFactory::getShared(RegFormDependencyHandler::class);
119
-        $dependency_handler->registerDependencies();
120
-        // TODO detect if event has a reg form UUID and swap this out for form generated by new reg form builder
121
-        return LoaderFactory::getShared(RegForm::class, [$this]);
122
-    }
123
-
124
-
125
-    /**
126
-     * @since   $VID:$
127
-     */
128
-    private function setLegacyFiltersForRegFormGeneration()
129
-    {
130
-        add_filter(
131
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantForm__generateFormArgs__question_groups_query_parameters',
132
-            [$this, 'registrationQuestionGroupsQueryParameters'],
133
-            1,
134
-            2
135
-        );
136
-        add_filter(
137
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantForm__generateFormArgs__question_group_reg_form',
138
-            [$this, 'registrationQuestionGroupsRegForm'],
139
-            1,
140
-            3
141
-        );
142
-        add_filter(
143
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantForm__generateFormArgs__printCopyInfo',
144
-            [$this, 'registrationRegFormPrintCopyInfo'],
145
-            1,
146
-            2
147
-        );
148
-        add_filter(
149
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__related_questions_query_params',
150
-            [$this, 'registrationRegFormRelatedQuestionsQueryParams'],
151
-            1,
152
-            3
153
-        );
154
-        add_filter(
155
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__before_question_group_questions',
156
-            [$this, 'registrationRegFormBeforeQuestionGroupQuestions'],
157
-            1,
158
-            3
159
-        );
160
-        add_filter(
161
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__subsections_array',
162
-            [$this, 'registrationRegFormSubsections'],
163
-            1,
164
-            3
165
-        );
166
-        add_filter(
167
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__after_question_group_questions',
168
-            [$this, 'registrationRegFormAfterQuestionGroupQuestions'],
169
-            1,
170
-            3
171
-        );
172
-        add_filter(
173
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_AutoCopyAttendeeInfoForm__construct__template_args',
174
-            [$this, 'autoCopyAttendeeInfoTemplateArgs'],
175
-            1
176
-        );
177
-        add_filter(
178
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_CountryOptions__generateLegacyCountryOptions__country_options',
179
-            [$this, 'generateQuestionInputCountryOptions'],
180
-            1,
181
-            4
182
-        );
183
-        add_filter(
184
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_StateOptions__generateLegacyStateOptions__state_options',
185
-            [$this, 'generateQuestionInputStateOptions'],
186
-            1,
187
-            4
188
-        );
189
-    }
190
-
191
-
192
-    /**
193
-     * @param array           $query_params
194
-     * @param EE_Registration $registration
195
-     * @return mixed|void
196
-     * @since   $VID:$
197
-     */
198
-    public function registrationQuestionGroupsQueryParameters(
199
-        array $query_params,
200
-        EE_Registration $registration
201
-    ) {
202
-        return apply_filters(
203
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form__question_groups_query_parameters',
204
-            $query_params,
205
-            $registration,
206
-            $this
207
-        );
208
-    }
209
-
210
-
211
-    /**
212
-     * @param RegFormQuestionGroup $question_group_reg_form
213
-     * @param EE_Registration      $registration
214
-     * @param EE_Question_Group    $question_group
215
-     * @return mixed|void
216
-     * @since   $VID:$
217
-     */
218
-    public function registrationQuestionGroupsRegForm(
219
-        RegFormQuestionGroup $question_group_reg_form,
220
-        EE_Registration $registration,
221
-        EE_Question_Group $question_group
222
-    ) {
223
-        return apply_filters(
224
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__question_group_reg_form',
225
-            $question_group_reg_form,
226
-            $registration,
227
-            $question_group,
228
-            $this
229
-        );
230
-    }
231
-
232
-
233
-    /**
234
-     * @param int $print_copy_info
235
-     * @param int $attendee_nmbr
236
-     * @return mixed|void
237
-     * @since   $VID:$
238
-     */
239
-    public function registrationRegFormPrintCopyInfo(
240
-        int $print_copy_info,
241
-        int $attendee_nmbr
242
-    ) {
243
-        return apply_filters(
244
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form___printCopyInfo',
245
-            $print_copy_info,
246
-            $attendee_nmbr
247
-        );
248
-    }
249
-
250
-
251
-    /**
252
-     * @param array             $query_params
253
-     * @param EE_Question_Group $question_group
254
-     * @param EE_Registration   $registration
255
-     * @return mixed|void
256
-     * @since   $VID:$
257
-     */
258
-    public function registrationRegFormRelatedQuestionsQueryParams(
259
-        array $query_params,
260
-        EE_Question_Group $question_group,
261
-        EE_Registration $registration
262
-    ) {
263
-        return apply_filters(
264
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__related_questions_query_params',
265
-            $query_params,
266
-            $question_group,
267
-            $registration,
268
-            $this
269
-        );
270
-    }
271
-
272
-
273
-    /**
274
-     * @param                   $html
275
-     * @param EE_Registration   $registration
276
-     * @param EE_Question_Group $question_group
277
-     * @return mixed|void
278
-     * @since   $VID:$
279
-     */
280
-    public function registrationRegFormBeforeQuestionGroupQuestions(
281
-        $html,
282
-        EE_Registration $registration,
283
-        EE_Question_Group $question_group
284
-    ) {
285
-        return apply_filters(
286
-            'FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions',
287
-            $html,
288
-            $registration,
289
-            $question_group,
290
-            $this
291
-        );
292
-    }
293
-
294
-
295
-    /**
296
-     * @param array             $form_subsections
297
-     * @param EE_Registration   $registration
298
-     * @param EE_Question_Group $question_group
299
-     * @return mixed|void
300
-     * @since   $VID:$
301
-     */
302
-    public function registrationRegFormSubsections(
303
-        array $form_subsections,
304
-        EE_Registration $registration,
305
-        EE_Question_Group $question_group
306
-    ) {
307
-        return apply_filters(
308
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information__question_group_reg_form__subsections_array',
309
-            $form_subsections,
310
-            $registration,
311
-            $question_group,
312
-            $this
313
-        );
314
-    }
315
-
316
-
317
-    /**
318
-     * @param                   $html
319
-     * @param EE_Registration   $registration
320
-     * @param EE_Question_Group $question_group
321
-     * @return mixed|void
322
-     * @since   $VID:$
323
-     */
324
-    public function registrationRegFormAfterQuestionGroupQuestions(
325
-        $html,
326
-        EE_Registration $registration,
327
-        EE_Question_Group $question_group
328
-    ) {
329
-        return apply_filters(
330
-            'FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions',
331
-            $html,
332
-            $registration,
333
-            $question_group,
334
-            $this
335
-        );
336
-    }
337
-
338
-
339
-    /**
340
-     * @param array $template_args
341
-     * @return mixed|void
342
-     * @since   $VID:$
343
-     */
344
-    public function autoCopyAttendeeInfoTemplateArgs(array $template_args = [])
345
-    {
346
-        return apply_filters(
347
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information__auto_copy_attendee_info__template_args',
348
-            $template_args
349
-        );
350
-    }
351
-
352
-
353
-    /**
354
-     * @param array           $country_options
355
-     * @param EE_Registration $registration
356
-     * @param EE_Question     $question
357
-     * @param EE_Answer|null  $answer
358
-     * @return mixed|void
359
-     * @since   $VID:$
360
-     */
361
-    public function generateQuestionInputCountryOptions(
362
-        array $country_options,
363
-        EE_Registration $registration,
364
-        EE_Question $question,
365
-        ?EE_Answer $answer
366
-    ) {
367
-        return apply_filters(
368
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__country_options',
369
-            $country_options,
370
-            $this,
371
-            $registration,
372
-            $question,
373
-            $answer
374
-        );
375
-    }
376
-
377
-
378
-    /**
379
-     * @param array           $state_options
380
-     * @param EE_Registration $registration
381
-     * @param EE_Question     $question
382
-     * @param EE_Answer|null  $answer
383
-     * @return mixed|void
384
-     * @since   $VID:$
385
-     */
386
-    public function generateQuestionInputStateOptions(
387
-        array $state_options,
388
-        EE_Registration $registration,
389
-        EE_Question $question,
390
-        ?EE_Answer $answer
391
-    ) {
392
-        return apply_filters(
393
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__state_options',
394
-            $state_options,
395
-            $this,
396
-            $registration,
397
-            $question,
398
-            $answer
399
-        );
400
-    }
401
-
402
-
403
-    /**
404
-     * looking for hooks?
405
-     * this method has been replaced by:
406
-     * EventEspresso\core\domain\services\registration\form\v1\RegForm::getRegForm()
407
-     *
408
-     * @deprecated   $VID:$
409
-     */
410
-    private function _registrations_reg_form()
411
-    {
412
-    }
413
-
414
-
415
-    /**
416
-     * looking for hooks?
417
-     * this method has been replaced by:
418
-     * EventEspresso\core\domain\services\registration\form\v1\RegForm::additionalAttendeeRegInfoInput()
419
-     *
420
-     * @deprecated   $VID:$
421
-     */
422
-    private function _additional_attendee_reg_info_input()
423
-    {
424
-    }
425
-
426
-
427
-    /**
428
-     * looking for hooks?
429
-     * this method has been replaced by:
430
-     * EventEspresso\core\domain\services\registration\form\v1\RegForm::questionGroupRegForm()
431
-     *
432
-     * @deprecated   $VID:$
433
-     */
434
-    private function _question_group_reg_form()
435
-    {
436
-    }
437
-
438
-
439
-    /**
440
-     * looking for hooks?
441
-     * this method has been replaced by:
442
-     * EventEspresso\core\domain\services\registration\form\v1\RegForm::questionGroupHeader()
443
-     *
444
-     * @deprecated   $VID:$
445
-     */
446
-    private function _question_group_header()
447
-    {
448
-    }
449
-
450
-
451
-    /**
452
-     * looking for hooks?
453
-     * this method has been replaced by:
454
-     * EventEspresso\core\domain\services\registration\form\v1\CopyAttendeeInfoForm
455
-     *
456
-     * @deprecated   $VID:$
457
-     */
458
-    private function _copy_attendee_info_form()
459
-    {
460
-    }
461
-
462
-
463
-    /**
464
-     * looking for hooks?
465
-     * this method has been replaced by:
466
-     * EventEspresso\core\domain\services\registration\form\v1\AutoCopyAttendeeInfoForm
467
-     *
468
-     * @deprecated   $VID:$
469
-     */
470
-    private function _auto_copy_attendee_info()
471
-    {
472
-    }
473
-
474
-
475
-    /**
476
-     * looking for hooks?
477
-     * this method has been replaced by:
478
-     * EventEspresso\core\domain\services\registration\form\v1\CopyAttendeeInfoForm
479
-     *
480
-     * @deprecated   $VID:$
481
-     */
482
-    private function _copy_attendee_info_inputs()
483
-    {
484
-    }
485
-
486
-
487
-    /**
488
-     * looking for hooks?
489
-     * this method has been replaced by:
490
-     * EventEspresso\core\domain\services\registration\form\v1\RegForm::additionalPrimaryRegistrantInputs()
491
-     *
492
-     * @deprecated   $VID:$
493
-     */
494
-    private function _additional_primary_registrant_inputs()
495
-    {
496
-    }
497
-
498
-
499
-    /**
500
-     * looking for hooks?
501
-     * this method has been replaced by:
502
-     * EventEspresso\core\domain\services\registration\form\v1\RegFormQuestionFactory::create()
503
-     *
504
-     * @param EE_Registration $registration
505
-     * @param EE_Question     $question
506
-     * @return EE_Form_Input_Base
507
-     * @throws EE_Error
508
-     * @throws ReflectionException
509
-     * @deprecated   $VID:$
510
-     */
511
-    public function reg_form_question(EE_Registration $registration, EE_Question $question): EE_Form_Input_Base
512
-    {
513
-        /** @var RegFormQuestionFactory $reg_form_question_factory */
514
-        $reg_form_question_factory = LoaderFactory::getShared(RegFormQuestionFactory::class);
515
-        return $reg_form_question_factory->create($registration, $question);
516
-    }
517
-
518
-
519
-    /**
520
-     * looking for hooks?
521
-     * this method has been replaced by:
522
-     * EventEspresso\core\domain\services\registration\form\v1\RegForm::generateQuestionInput()
523
-     *
524
-     * @deprecated   $VID:$
525
-     */
526
-    private function _generate_question_input()
527
-    {
528
-    }
529
-
530
-
531
-    /**
532
-     * looking for hooks?
533
-     * this method has been replaced by:
534
-     * EventEspresso\core\domain\services\registration\form\v1\CountryOptions::forLegacyFormInput()
535
-     *
536
-     * @param array|null           $countries_list
537
-     * @param EE_Question|null     $question
538
-     * @param EE_Registration|null $registration
539
-     * @param EE_Answer|null       $answer
540
-     * @return array 2d keys are country IDs, values are their names
541
-     * @throws EE_Error
542
-     * @throws ReflectionException
543
-     * @deprecated   $VID:$
544
-     */
545
-    public function use_cached_countries_for_form_input(
546
-        array $countries_list = null,
547
-        EE_Question $question = null,
548
-        EE_Registration $registration = null,
549
-        EE_Answer $answer = null
550
-    ): array {
551
-        /** @var CountryOptions $country_options */
552
-        $country_options = LoaderFactory::getShared(CountryOptions::class, [$this->checkout->action]);
553
-        return $country_options->forLegacyFormInput($countries_list, $question, $registration, $answer);
554
-    }
555
-
556
-
557
-    /**
558
-     * looking for hooks?
559
-     * this method has been replaced by:
560
-     * EventEspresso\core\domain\services\registration\form\v1\StateOptions::forLegacyFormInput()
561
-     *
562
-     * @param array|null           $states_list
563
-     * @param EE_Question|null     $question
564
-     * @param EE_Registration|null $registration
565
-     * @param EE_Answer|null       $answer
566
-     * @return array 2d keys are state IDs, values are their names
567
-     * @throws EE_Error
568
-     * @throws ReflectionException
569
-     * @deprecated   $VID:$
570
-     */
571
-    public function use_cached_states_for_form_input(
572
-        array $states_list = null,
573
-        EE_Question $question = null,
574
-        EE_Registration $registration = null,
575
-        EE_Answer $answer = null
576
-    ): array {
577
-        /** @var StateOptions $state_options */
578
-        $state_options = LoaderFactory::getShared(StateOptions::class, [$this->checkout->action]);
579
-        return $state_options->forLegacyFormInput($states_list, $question, $registration, $answer);
580
-    }
581
-
582
-
583
-    /********************************************************************************************************/
584
-    /****************************************  PROCESS REG STEP  ****************************************/
585
-    /********************************************************************************************************/
586
-
587
-
588
-    /**
589
-     * @return bool
590
-     * @throws EE_Error
591
-     * @throws InvalidArgumentException
592
-     * @throws ReflectionException
593
-     * @throws RuntimeException
594
-     * @throws InvalidDataTypeException
595
-     * @throws InvalidInterfaceException
596
-     */
597
-    public function process_reg_step(): bool
598
-    {
599
-        $this->setLegacyFiltersForRegFormProcessing();
600
-        // grab validated data from form
601
-        $valid_data = $this->checkout->current_step->valid_data();
602
-        // if we don't have any $valid_data then something went TERRIBLY WRONG !!!
603
-        if (empty($valid_data)) {
604
-            return $this->inValidDataError();
605
-        }
606
-        if (! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
607
-            return $this->inValidTransactionError();
608
-        }
609
-        // get cached registrations
610
-        $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
611
-        // verify we got the goods
612
-        if (empty($registrations)) {
613
-            return $this->noRegistrationsError();
614
-        }
615
-        /** @var RegFormHandler $reg_form_handler */
616
-        $reg_form_handler = LoaderFactory::getNew(RegFormHandler::class, [$this->checkout]);
617
-        // extract attendee info from form data and save to model objects
618
-        if (! $reg_form_handler->processRegistrations($registrations, $valid_data)) {
619
-            // return immediately if the previous step exited early due to errors
620
-            return false;
621
-        }
622
-        // if first pass thru SPCO,
623
-        // then let's check processed registrations against the total number of tickets in the cart
624
-        $registrations_processed = $reg_form_handler->attendeeCount();
625
-        if (! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
626
-            return $this->registrationProcessingError($registrations_processed);
627
-        }
628
-        // mark this reg step as completed
629
-        $this->set_completed();
630
-        $this->_set_success_message(
631
-            esc_html__('The Attendee Information Step has been successfully completed.', 'event_espresso')
632
-        );
633
-        // do action in case a plugin wants to do something with the data submitted in step 1.
634
-        // passes EE_Single_Page_Checkout, and it's posted data
635
-        do_action('AHEE__EE_Single_Page_Checkout__process_attendee_information__end', $this, $valid_data);
636
-        return true;
637
-    }
638
-
639
-
640
-    /**
641
-     * @since   $VID:$
642
-     */
643
-    private function setLegacyFiltersForRegFormProcessing()
644
-    {
645
-        add_filter(
646
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormHandler__processRegistrations__bypass',
647
-            [$this, 'preRegistrationProcess'],
648
-            1,
649
-            5
650
-        );
651
-        add_filter(
652
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormHandler__processRegFormData__registrant_form_data',
653
-            [$this, 'validDataLineItem'],
654
-            1,
655
-            2
656
-        );
657
-        add_filter(
658
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormInputHandler__saveRegistrationFormInput',
659
-            [$this, 'saveRegistrationFormInput'],
660
-            1,
661
-            4
662
-        );
663
-        add_filter(
664
-            'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantData__ensureCriticalRegistrantDataIsSet',
665
-            [$this, 'mergeAddressDetailsWithCriticalAttendeeDetails'],
666
-            1
667
-        );
668
-    }
669
-
670
-
671
-    /**
672
-     * @param bool            $bypass
673
-     * @param int             $attendee_count
674
-     * @param EE_Registration $registration
675
-     * @param array           $registrations
676
-     * @param array           $reg_form_data
677
-     * @return mixed|void
678
-     * @since   $VID:$
679
-     */
680
-    public function preRegistrationProcess(
681
-        bool $bypass,
682
-        int $attendee_count,
683
-        EE_Registration $registration,
684
-        array $registrations,
685
-        array $reg_form_data
686
-    ) {
687
-        return apply_filters(
688
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___process_registrations__pre_registration_process',
689
-            $bypass,
690
-            $attendee_count,
691
-            $registration,
692
-            $registrations,
693
-            $reg_form_data,
694
-            $this
695
-        );
696
-    }
697
-
698
-
699
-    /**
700
-     * @param array           $reg_form_data
701
-     * @param EE_Registration $registration
702
-     * @return mixed|void
703
-     * @since   $VID:$
704
-     */
705
-    public function validDataLineItem(array $reg_form_data, EE_Registration $registration)
706
-    {
707
-        return apply_filters(
708
-            'FHEE__EE_Single_Page_Checkout__process_attendee_information__valid_data_line_item',
709
-            $reg_form_data,
710
-            $registration
711
-        );
712
-    }
713
-
714
-
715
-    /**
716
-     * @param bool            $save
717
-     * @param EE_Registration $registration
718
-     * @param                 $form_input
719
-     * @param                 $input_value
720
-     * @return mixed|void
721
-     * @since   $VID:$
722
-     */
723
-    public function saveRegistrationFormInput(bool $save, EE_Registration $registration, $form_input, $input_value)
724
-    {
725
-        return apply_filters(
726
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___save_registration_form_input',
727
-            $save,
728
-            $registration,
729
-            $form_input,
730
-            $input_value,
731
-            $this
732
-        );
733
-    }
734
-
735
-
736
-    /**
737
-     * @param bool $merge_data
738
-     * @return mixed|void
739
-     * @since   $VID:$
740
-     */
741
-    public function mergeAddressDetailsWithCriticalAttendeeDetails(bool $merge_data)
742
-    {
743
-        return apply_filters(
744
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information__merge_address_details_with_critical_attendee_details',
745
-            $merge_data
746
-        );
747
-    }
748
-
749
-
750
-    /**
751
-     * @return bool
752
-     * @since   $VID:$
753
-     */
754
-    private function inValidDataError(): bool
755
-    {
756
-        EE_Error::add_error(
757
-            esc_html__('No valid question responses were received.', 'event_espresso'),
758
-            __FILE__,
759
-            __FUNCTION__,
760
-            __LINE__
761
-        );
762
-        return false;
763
-    }
764
-
765
-
766
-    /**
767
-     * @return bool
768
-     * @since   $VID:$
769
-     */
770
-    private function inValidTransactionError(): bool
771
-    {
772
-        EE_Error::add_error(
773
-            esc_html__(
774
-                'A valid transaction could not be initiated for processing your registrations.',
775
-                'event_espresso'
776
-            ),
777
-            __FILE__,
778
-            __FUNCTION__,
779
-            __LINE__
780
-        );
781
-        return false;
782
-    }
783
-
784
-
785
-    /**
786
-     * @return bool
787
-     * @since   $VID:$
788
-     */
789
-    private function noRegistrationsError(): bool
790
-    {
791
-        // combine the old translated string with a new one, in order to not break translations
792
-        $error_message = esc_html__(
793
-            'Your form data could not be applied to any valid registrations.',
794
-            'event_espresso'
795
-        );
796
-        $error_message .= sprintf(
797
-            esc_html_x(
798
-                '%3$sThis can sometimes happen if too much time has been taken to complete the registration process.%3$sPlease return to the %1$sEvent List%2$s and reselect your tickets. If the problem continues, please contact the site administrator.',
799
-                '(line break)This can sometimes happen if too much time has been taken to complete the registration process.(line break)Please return to the (link)Event List(end link) and reselect your tickets. If the problem continues, please contact the site administrator.',
800
-                'event_espresso'
801
-            ),
802
-            '<a href="' . get_post_type_archive_link('espresso_events') . '" >',
803
-            '</a>',
804
-            '<br />'
805
-        );
806
-        EE_Error::add_error($error_message, __FILE__, __FUNCTION__, __LINE__);
807
-        return false;
808
-    }
809
-
810
-
811
-    /**
812
-     * @param int $registrations_processed
813
-     * @return bool
814
-     * @since   $VID:$
815
-     */
816
-    private function registrationProcessingError(int $registrations_processed): bool
817
-    {
818
-        // generate a correctly translated string for all possible singular/plural combinations
819
-        if ($this->checkout->total_ticket_count === 1 && $registrations_processed !== 1) {
820
-            $error_msg = sprintf(
821
-                esc_html_x(
822
-                    'There was %1$d ticket in the Event Queue, but %2$ds registrations were processed',
823
-                    'There was 1 ticket in the Event Queue, but 2 registrations were processed',
824
-                    'event_espresso'
825
-                ),
826
-                $this->checkout->total_ticket_count,
827
-                $registrations_processed
828
-            );
829
-        } elseif ($this->checkout->total_ticket_count !== 1 && $registrations_processed === 1) {
830
-            $error_msg = sprintf(
831
-                esc_html_x(
832
-                    'There was a total of %1$d tickets in the Event Queue, but only %2$ds registration was processed',
833
-                    'There was a total of 2 tickets in the Event Queue, but only 1 registration was processed',
834
-                    'event_espresso'
835
-                ),
836
-                $this->checkout->total_ticket_count,
837
-                $registrations_processed
838
-            );
839
-        } else {
840
-            $error_msg = sprintf(
841
-                esc_html__(
842
-                    'There was a total of 2 tickets in the Event Queue, but 2 registrations were processed',
843
-                    'event_espresso'
844
-                ),
845
-                $this->checkout->total_ticket_count,
846
-                $registrations_processed
847
-            );
848
-        }
849
-        EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
850
-        return false;
851
-    }
852
-
853
-
854
-    /**
855
-     *    update_reg_step
856
-     *    this is the final step after a user  revisits the site to edit their attendee information
857
-     *    this gets called AFTER the process_reg_step() method above
858
-     *
859
-     * @return bool
860
-     * @throws EE_Error
861
-     * @throws InvalidArgumentException
862
-     * @throws ReflectionException
863
-     * @throws RuntimeException
864
-     * @throws InvalidDataTypeException
865
-     * @throws InvalidInterfaceException
866
-     */
867
-    public function update_reg_step(): bool
868
-    {
869
-        // save everything
870
-        if ($this->process_reg_step()) {
871
-            $this->checkout->redirect     = true;
872
-            $this->checkout->redirect_url = add_query_arg(
873
-                [
874
-                    'e_reg_url_link' => $this->checkout->reg_url_link,
875
-                    'revisit'        => true,
876
-                ],
877
-                $this->checkout->thank_you_page_url
878
-            );
879
-            $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
880
-            return true;
881
-        }
882
-        return false;
883
-    }
26
+	/**
27
+	 * @var RegForm
28
+	 */
29
+	public $reg_form;
30
+
31
+	/**
32
+	 * @var int
33
+	 */
34
+	protected $reg_form_count = 0;
35
+
36
+
37
+	/**
38
+	 *    class constructor
39
+	 *
40
+	 * @access    public
41
+	 * @param EE_Checkout $checkout
42
+	 */
43
+	public function __construct(EE_Checkout $checkout)
44
+	{
45
+		$this->_slug    = 'attendee_information';
46
+		$this->_name    = esc_html__('Attendee Information', 'event_espresso');
47
+		$this->checkout = $checkout;
48
+		$this->_reset_success_message();
49
+		$this->set_instructions(
50
+			esc_html__('Please answer the following registration questions before proceeding.', 'event_espresso')
51
+		);
52
+	}
53
+
54
+
55
+	public function translate_js_strings()
56
+	{
57
+		EE_Registry::$i18n_js_strings['required_field']            = esc_html__(
58
+			' is a required question.',
59
+			'event_espresso'
60
+		);
61
+		EE_Registry::$i18n_js_strings['required_multi_field']      = esc_html__(
62
+			' is a required question. Please enter a value for at least one of the options.',
63
+			'event_espresso'
64
+		);
65
+		EE_Registry::$i18n_js_strings['answer_required_questions'] = esc_html__(
66
+			'Please answer all required questions correctly before proceeding.',
67
+			'event_espresso'
68
+		);
69
+		EE_Registry::$i18n_js_strings['attendee_info_copied']      = sprintf(
70
+			esc_html_x(
71
+				'The attendee information was successfully copied.%sPlease ensure the rest of the registration form is completed before proceeding.',
72
+				'The attendee information was successfully copied.(line break)Please ensure the rest of the registration form is completed before proceeding.',
73
+				'event_espresso'
74
+			),
75
+			'<br/>'
76
+		);
77
+		EE_Registry::$i18n_js_strings['attendee_info_copy_error']  = esc_html__(
78
+			'An unknown error occurred on the server while attempting to copy the attendee information. Please refresh the page and try again.',
79
+			'event_espresso'
80
+		);
81
+		EE_Registry::$i18n_js_strings['enter_valid_email']         = esc_html__(
82
+			'You must enter a valid email address.',
83
+			'event_espresso'
84
+		);
85
+		EE_Registry::$i18n_js_strings['valid_email_and_questions'] = esc_html__(
86
+			'You must enter a valid email address and answer all other required questions before you can proceed.',
87
+			'event_espresso'
88
+		);
89
+	}
90
+
91
+
92
+	public function enqueue_styles_and_scripts()
93
+	{
94
+	}
95
+
96
+
97
+	/**
98
+	 * @return boolean
99
+	 */
100
+	public function initialize_reg_step(): bool
101
+	{
102
+		return true;
103
+	}
104
+
105
+
106
+	/**
107
+	 * @return RegForm
108
+	 * @throws DomainException
109
+	 * @throws InvalidArgumentException
110
+	 * @throws EntityNotFoundException
111
+	 * @throws InvalidDataTypeException
112
+	 * @throws InvalidInterfaceException
113
+	 */
114
+	public function generate_reg_form(): RegForm
115
+	{
116
+		$this->setLegacyFiltersForRegFormGeneration();
117
+		/** @var RegFormDependencyHandler $dependency_handler */
118
+		$dependency_handler = LoaderFactory::getShared(RegFormDependencyHandler::class);
119
+		$dependency_handler->registerDependencies();
120
+		// TODO detect if event has a reg form UUID and swap this out for form generated by new reg form builder
121
+		return LoaderFactory::getShared(RegForm::class, [$this]);
122
+	}
123
+
124
+
125
+	/**
126
+	 * @since   $VID:$
127
+	 */
128
+	private function setLegacyFiltersForRegFormGeneration()
129
+	{
130
+		add_filter(
131
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantForm__generateFormArgs__question_groups_query_parameters',
132
+			[$this, 'registrationQuestionGroupsQueryParameters'],
133
+			1,
134
+			2
135
+		);
136
+		add_filter(
137
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantForm__generateFormArgs__question_group_reg_form',
138
+			[$this, 'registrationQuestionGroupsRegForm'],
139
+			1,
140
+			3
141
+		);
142
+		add_filter(
143
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantForm__generateFormArgs__printCopyInfo',
144
+			[$this, 'registrationRegFormPrintCopyInfo'],
145
+			1,
146
+			2
147
+		);
148
+		add_filter(
149
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__related_questions_query_params',
150
+			[$this, 'registrationRegFormRelatedQuestionsQueryParams'],
151
+			1,
152
+			3
153
+		);
154
+		add_filter(
155
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__before_question_group_questions',
156
+			[$this, 'registrationRegFormBeforeQuestionGroupQuestions'],
157
+			1,
158
+			3
159
+		);
160
+		add_filter(
161
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__subsections_array',
162
+			[$this, 'registrationRegFormSubsections'],
163
+			1,
164
+			3
165
+		);
166
+		add_filter(
167
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormQuestionGroup__generateFormArgs__after_question_group_questions',
168
+			[$this, 'registrationRegFormAfterQuestionGroupQuestions'],
169
+			1,
170
+			3
171
+		);
172
+		add_filter(
173
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_AutoCopyAttendeeInfoForm__construct__template_args',
174
+			[$this, 'autoCopyAttendeeInfoTemplateArgs'],
175
+			1
176
+		);
177
+		add_filter(
178
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_CountryOptions__generateLegacyCountryOptions__country_options',
179
+			[$this, 'generateQuestionInputCountryOptions'],
180
+			1,
181
+			4
182
+		);
183
+		add_filter(
184
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_StateOptions__generateLegacyStateOptions__state_options',
185
+			[$this, 'generateQuestionInputStateOptions'],
186
+			1,
187
+			4
188
+		);
189
+	}
190
+
191
+
192
+	/**
193
+	 * @param array           $query_params
194
+	 * @param EE_Registration $registration
195
+	 * @return mixed|void
196
+	 * @since   $VID:$
197
+	 */
198
+	public function registrationQuestionGroupsQueryParameters(
199
+		array $query_params,
200
+		EE_Registration $registration
201
+	) {
202
+		return apply_filters(
203
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form__question_groups_query_parameters',
204
+			$query_params,
205
+			$registration,
206
+			$this
207
+		);
208
+	}
209
+
210
+
211
+	/**
212
+	 * @param RegFormQuestionGroup $question_group_reg_form
213
+	 * @param EE_Registration      $registration
214
+	 * @param EE_Question_Group    $question_group
215
+	 * @return mixed|void
216
+	 * @since   $VID:$
217
+	 */
218
+	public function registrationQuestionGroupsRegForm(
219
+		RegFormQuestionGroup $question_group_reg_form,
220
+		EE_Registration $registration,
221
+		EE_Question_Group $question_group
222
+	) {
223
+		return apply_filters(
224
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__question_group_reg_form',
225
+			$question_group_reg_form,
226
+			$registration,
227
+			$question_group,
228
+			$this
229
+		);
230
+	}
231
+
232
+
233
+	/**
234
+	 * @param int $print_copy_info
235
+	 * @param int $attendee_nmbr
236
+	 * @return mixed|void
237
+	 * @since   $VID:$
238
+	 */
239
+	public function registrationRegFormPrintCopyInfo(
240
+		int $print_copy_info,
241
+		int $attendee_nmbr
242
+	) {
243
+		return apply_filters(
244
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form___printCopyInfo',
245
+			$print_copy_info,
246
+			$attendee_nmbr
247
+		);
248
+	}
249
+
250
+
251
+	/**
252
+	 * @param array             $query_params
253
+	 * @param EE_Question_Group $question_group
254
+	 * @param EE_Registration   $registration
255
+	 * @return mixed|void
256
+	 * @since   $VID:$
257
+	 */
258
+	public function registrationRegFormRelatedQuestionsQueryParams(
259
+		array $query_params,
260
+		EE_Question_Group $question_group,
261
+		EE_Registration $registration
262
+	) {
263
+		return apply_filters(
264
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__related_questions_query_params',
265
+			$query_params,
266
+			$question_group,
267
+			$registration,
268
+			$this
269
+		);
270
+	}
271
+
272
+
273
+	/**
274
+	 * @param                   $html
275
+	 * @param EE_Registration   $registration
276
+	 * @param EE_Question_Group $question_group
277
+	 * @return mixed|void
278
+	 * @since   $VID:$
279
+	 */
280
+	public function registrationRegFormBeforeQuestionGroupQuestions(
281
+		$html,
282
+		EE_Registration $registration,
283
+		EE_Question_Group $question_group
284
+	) {
285
+		return apply_filters(
286
+			'FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions',
287
+			$html,
288
+			$registration,
289
+			$question_group,
290
+			$this
291
+		);
292
+	}
293
+
294
+
295
+	/**
296
+	 * @param array             $form_subsections
297
+	 * @param EE_Registration   $registration
298
+	 * @param EE_Question_Group $question_group
299
+	 * @return mixed|void
300
+	 * @since   $VID:$
301
+	 */
302
+	public function registrationRegFormSubsections(
303
+		array $form_subsections,
304
+		EE_Registration $registration,
305
+		EE_Question_Group $question_group
306
+	) {
307
+		return apply_filters(
308
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information__question_group_reg_form__subsections_array',
309
+			$form_subsections,
310
+			$registration,
311
+			$question_group,
312
+			$this
313
+		);
314
+	}
315
+
316
+
317
+	/**
318
+	 * @param                   $html
319
+	 * @param EE_Registration   $registration
320
+	 * @param EE_Question_Group $question_group
321
+	 * @return mixed|void
322
+	 * @since   $VID:$
323
+	 */
324
+	public function registrationRegFormAfterQuestionGroupQuestions(
325
+		$html,
326
+		EE_Registration $registration,
327
+		EE_Question_Group $question_group
328
+	) {
329
+		return apply_filters(
330
+			'FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions',
331
+			$html,
332
+			$registration,
333
+			$question_group,
334
+			$this
335
+		);
336
+	}
337
+
338
+
339
+	/**
340
+	 * @param array $template_args
341
+	 * @return mixed|void
342
+	 * @since   $VID:$
343
+	 */
344
+	public function autoCopyAttendeeInfoTemplateArgs(array $template_args = [])
345
+	{
346
+		return apply_filters(
347
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information__auto_copy_attendee_info__template_args',
348
+			$template_args
349
+		);
350
+	}
351
+
352
+
353
+	/**
354
+	 * @param array           $country_options
355
+	 * @param EE_Registration $registration
356
+	 * @param EE_Question     $question
357
+	 * @param EE_Answer|null  $answer
358
+	 * @return mixed|void
359
+	 * @since   $VID:$
360
+	 */
361
+	public function generateQuestionInputCountryOptions(
362
+		array $country_options,
363
+		EE_Registration $registration,
364
+		EE_Question $question,
365
+		?EE_Answer $answer
366
+	) {
367
+		return apply_filters(
368
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__country_options',
369
+			$country_options,
370
+			$this,
371
+			$registration,
372
+			$question,
373
+			$answer
374
+		);
375
+	}
376
+
377
+
378
+	/**
379
+	 * @param array           $state_options
380
+	 * @param EE_Registration $registration
381
+	 * @param EE_Question     $question
382
+	 * @param EE_Answer|null  $answer
383
+	 * @return mixed|void
384
+	 * @since   $VID:$
385
+	 */
386
+	public function generateQuestionInputStateOptions(
387
+		array $state_options,
388
+		EE_Registration $registration,
389
+		EE_Question $question,
390
+		?EE_Answer $answer
391
+	) {
392
+		return apply_filters(
393
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__state_options',
394
+			$state_options,
395
+			$this,
396
+			$registration,
397
+			$question,
398
+			$answer
399
+		);
400
+	}
401
+
402
+
403
+	/**
404
+	 * looking for hooks?
405
+	 * this method has been replaced by:
406
+	 * EventEspresso\core\domain\services\registration\form\v1\RegForm::getRegForm()
407
+	 *
408
+	 * @deprecated   $VID:$
409
+	 */
410
+	private function _registrations_reg_form()
411
+	{
412
+	}
413
+
414
+
415
+	/**
416
+	 * looking for hooks?
417
+	 * this method has been replaced by:
418
+	 * EventEspresso\core\domain\services\registration\form\v1\RegForm::additionalAttendeeRegInfoInput()
419
+	 *
420
+	 * @deprecated   $VID:$
421
+	 */
422
+	private function _additional_attendee_reg_info_input()
423
+	{
424
+	}
425
+
426
+
427
+	/**
428
+	 * looking for hooks?
429
+	 * this method has been replaced by:
430
+	 * EventEspresso\core\domain\services\registration\form\v1\RegForm::questionGroupRegForm()
431
+	 *
432
+	 * @deprecated   $VID:$
433
+	 */
434
+	private function _question_group_reg_form()
435
+	{
436
+	}
437
+
438
+
439
+	/**
440
+	 * looking for hooks?
441
+	 * this method has been replaced by:
442
+	 * EventEspresso\core\domain\services\registration\form\v1\RegForm::questionGroupHeader()
443
+	 *
444
+	 * @deprecated   $VID:$
445
+	 */
446
+	private function _question_group_header()
447
+	{
448
+	}
449
+
450
+
451
+	/**
452
+	 * looking for hooks?
453
+	 * this method has been replaced by:
454
+	 * EventEspresso\core\domain\services\registration\form\v1\CopyAttendeeInfoForm
455
+	 *
456
+	 * @deprecated   $VID:$
457
+	 */
458
+	private function _copy_attendee_info_form()
459
+	{
460
+	}
461
+
462
+
463
+	/**
464
+	 * looking for hooks?
465
+	 * this method has been replaced by:
466
+	 * EventEspresso\core\domain\services\registration\form\v1\AutoCopyAttendeeInfoForm
467
+	 *
468
+	 * @deprecated   $VID:$
469
+	 */
470
+	private function _auto_copy_attendee_info()
471
+	{
472
+	}
473
+
474
+
475
+	/**
476
+	 * looking for hooks?
477
+	 * this method has been replaced by:
478
+	 * EventEspresso\core\domain\services\registration\form\v1\CopyAttendeeInfoForm
479
+	 *
480
+	 * @deprecated   $VID:$
481
+	 */
482
+	private function _copy_attendee_info_inputs()
483
+	{
484
+	}
485
+
486
+
487
+	/**
488
+	 * looking for hooks?
489
+	 * this method has been replaced by:
490
+	 * EventEspresso\core\domain\services\registration\form\v1\RegForm::additionalPrimaryRegistrantInputs()
491
+	 *
492
+	 * @deprecated   $VID:$
493
+	 */
494
+	private function _additional_primary_registrant_inputs()
495
+	{
496
+	}
497
+
498
+
499
+	/**
500
+	 * looking for hooks?
501
+	 * this method has been replaced by:
502
+	 * EventEspresso\core\domain\services\registration\form\v1\RegFormQuestionFactory::create()
503
+	 *
504
+	 * @param EE_Registration $registration
505
+	 * @param EE_Question     $question
506
+	 * @return EE_Form_Input_Base
507
+	 * @throws EE_Error
508
+	 * @throws ReflectionException
509
+	 * @deprecated   $VID:$
510
+	 */
511
+	public function reg_form_question(EE_Registration $registration, EE_Question $question): EE_Form_Input_Base
512
+	{
513
+		/** @var RegFormQuestionFactory $reg_form_question_factory */
514
+		$reg_form_question_factory = LoaderFactory::getShared(RegFormQuestionFactory::class);
515
+		return $reg_form_question_factory->create($registration, $question);
516
+	}
517
+
518
+
519
+	/**
520
+	 * looking for hooks?
521
+	 * this method has been replaced by:
522
+	 * EventEspresso\core\domain\services\registration\form\v1\RegForm::generateQuestionInput()
523
+	 *
524
+	 * @deprecated   $VID:$
525
+	 */
526
+	private function _generate_question_input()
527
+	{
528
+	}
529
+
530
+
531
+	/**
532
+	 * looking for hooks?
533
+	 * this method has been replaced by:
534
+	 * EventEspresso\core\domain\services\registration\form\v1\CountryOptions::forLegacyFormInput()
535
+	 *
536
+	 * @param array|null           $countries_list
537
+	 * @param EE_Question|null     $question
538
+	 * @param EE_Registration|null $registration
539
+	 * @param EE_Answer|null       $answer
540
+	 * @return array 2d keys are country IDs, values are their names
541
+	 * @throws EE_Error
542
+	 * @throws ReflectionException
543
+	 * @deprecated   $VID:$
544
+	 */
545
+	public function use_cached_countries_for_form_input(
546
+		array $countries_list = null,
547
+		EE_Question $question = null,
548
+		EE_Registration $registration = null,
549
+		EE_Answer $answer = null
550
+	): array {
551
+		/** @var CountryOptions $country_options */
552
+		$country_options = LoaderFactory::getShared(CountryOptions::class, [$this->checkout->action]);
553
+		return $country_options->forLegacyFormInput($countries_list, $question, $registration, $answer);
554
+	}
555
+
556
+
557
+	/**
558
+	 * looking for hooks?
559
+	 * this method has been replaced by:
560
+	 * EventEspresso\core\domain\services\registration\form\v1\StateOptions::forLegacyFormInput()
561
+	 *
562
+	 * @param array|null           $states_list
563
+	 * @param EE_Question|null     $question
564
+	 * @param EE_Registration|null $registration
565
+	 * @param EE_Answer|null       $answer
566
+	 * @return array 2d keys are state IDs, values are their names
567
+	 * @throws EE_Error
568
+	 * @throws ReflectionException
569
+	 * @deprecated   $VID:$
570
+	 */
571
+	public function use_cached_states_for_form_input(
572
+		array $states_list = null,
573
+		EE_Question $question = null,
574
+		EE_Registration $registration = null,
575
+		EE_Answer $answer = null
576
+	): array {
577
+		/** @var StateOptions $state_options */
578
+		$state_options = LoaderFactory::getShared(StateOptions::class, [$this->checkout->action]);
579
+		return $state_options->forLegacyFormInput($states_list, $question, $registration, $answer);
580
+	}
581
+
582
+
583
+	/********************************************************************************************************/
584
+	/****************************************  PROCESS REG STEP  ****************************************/
585
+	/********************************************************************************************************/
586
+
587
+
588
+	/**
589
+	 * @return bool
590
+	 * @throws EE_Error
591
+	 * @throws InvalidArgumentException
592
+	 * @throws ReflectionException
593
+	 * @throws RuntimeException
594
+	 * @throws InvalidDataTypeException
595
+	 * @throws InvalidInterfaceException
596
+	 */
597
+	public function process_reg_step(): bool
598
+	{
599
+		$this->setLegacyFiltersForRegFormProcessing();
600
+		// grab validated data from form
601
+		$valid_data = $this->checkout->current_step->valid_data();
602
+		// if we don't have any $valid_data then something went TERRIBLY WRONG !!!
603
+		if (empty($valid_data)) {
604
+			return $this->inValidDataError();
605
+		}
606
+		if (! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
607
+			return $this->inValidTransactionError();
608
+		}
609
+		// get cached registrations
610
+		$registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
611
+		// verify we got the goods
612
+		if (empty($registrations)) {
613
+			return $this->noRegistrationsError();
614
+		}
615
+		/** @var RegFormHandler $reg_form_handler */
616
+		$reg_form_handler = LoaderFactory::getNew(RegFormHandler::class, [$this->checkout]);
617
+		// extract attendee info from form data and save to model objects
618
+		if (! $reg_form_handler->processRegistrations($registrations, $valid_data)) {
619
+			// return immediately if the previous step exited early due to errors
620
+			return false;
621
+		}
622
+		// if first pass thru SPCO,
623
+		// then let's check processed registrations against the total number of tickets in the cart
624
+		$registrations_processed = $reg_form_handler->attendeeCount();
625
+		if (! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
626
+			return $this->registrationProcessingError($registrations_processed);
627
+		}
628
+		// mark this reg step as completed
629
+		$this->set_completed();
630
+		$this->_set_success_message(
631
+			esc_html__('The Attendee Information Step has been successfully completed.', 'event_espresso')
632
+		);
633
+		// do action in case a plugin wants to do something with the data submitted in step 1.
634
+		// passes EE_Single_Page_Checkout, and it's posted data
635
+		do_action('AHEE__EE_Single_Page_Checkout__process_attendee_information__end', $this, $valid_data);
636
+		return true;
637
+	}
638
+
639
+
640
+	/**
641
+	 * @since   $VID:$
642
+	 */
643
+	private function setLegacyFiltersForRegFormProcessing()
644
+	{
645
+		add_filter(
646
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormHandler__processRegistrations__bypass',
647
+			[$this, 'preRegistrationProcess'],
648
+			1,
649
+			5
650
+		);
651
+		add_filter(
652
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormHandler__processRegFormData__registrant_form_data',
653
+			[$this, 'validDataLineItem'],
654
+			1,
655
+			2
656
+		);
657
+		add_filter(
658
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegFormInputHandler__saveRegistrationFormInput',
659
+			[$this, 'saveRegistrationFormInput'],
660
+			1,
661
+			4
662
+		);
663
+		add_filter(
664
+			'FHEE__EventEspresso_core_domain_services_registration_form_v1_RegistrantData__ensureCriticalRegistrantDataIsSet',
665
+			[$this, 'mergeAddressDetailsWithCriticalAttendeeDetails'],
666
+			1
667
+		);
668
+	}
669
+
670
+
671
+	/**
672
+	 * @param bool            $bypass
673
+	 * @param int             $attendee_count
674
+	 * @param EE_Registration $registration
675
+	 * @param array           $registrations
676
+	 * @param array           $reg_form_data
677
+	 * @return mixed|void
678
+	 * @since   $VID:$
679
+	 */
680
+	public function preRegistrationProcess(
681
+		bool $bypass,
682
+		int $attendee_count,
683
+		EE_Registration $registration,
684
+		array $registrations,
685
+		array $reg_form_data
686
+	) {
687
+		return apply_filters(
688
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___process_registrations__pre_registration_process',
689
+			$bypass,
690
+			$attendee_count,
691
+			$registration,
692
+			$registrations,
693
+			$reg_form_data,
694
+			$this
695
+		);
696
+	}
697
+
698
+
699
+	/**
700
+	 * @param array           $reg_form_data
701
+	 * @param EE_Registration $registration
702
+	 * @return mixed|void
703
+	 * @since   $VID:$
704
+	 */
705
+	public function validDataLineItem(array $reg_form_data, EE_Registration $registration)
706
+	{
707
+		return apply_filters(
708
+			'FHEE__EE_Single_Page_Checkout__process_attendee_information__valid_data_line_item',
709
+			$reg_form_data,
710
+			$registration
711
+		);
712
+	}
713
+
714
+
715
+	/**
716
+	 * @param bool            $save
717
+	 * @param EE_Registration $registration
718
+	 * @param                 $form_input
719
+	 * @param                 $input_value
720
+	 * @return mixed|void
721
+	 * @since   $VID:$
722
+	 */
723
+	public function saveRegistrationFormInput(bool $save, EE_Registration $registration, $form_input, $input_value)
724
+	{
725
+		return apply_filters(
726
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___save_registration_form_input',
727
+			$save,
728
+			$registration,
729
+			$form_input,
730
+			$input_value,
731
+			$this
732
+		);
733
+	}
734
+
735
+
736
+	/**
737
+	 * @param bool $merge_data
738
+	 * @return mixed|void
739
+	 * @since   $VID:$
740
+	 */
741
+	public function mergeAddressDetailsWithCriticalAttendeeDetails(bool $merge_data)
742
+	{
743
+		return apply_filters(
744
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information__merge_address_details_with_critical_attendee_details',
745
+			$merge_data
746
+		);
747
+	}
748
+
749
+
750
+	/**
751
+	 * @return bool
752
+	 * @since   $VID:$
753
+	 */
754
+	private function inValidDataError(): bool
755
+	{
756
+		EE_Error::add_error(
757
+			esc_html__('No valid question responses were received.', 'event_espresso'),
758
+			__FILE__,
759
+			__FUNCTION__,
760
+			__LINE__
761
+		);
762
+		return false;
763
+	}
764
+
765
+
766
+	/**
767
+	 * @return bool
768
+	 * @since   $VID:$
769
+	 */
770
+	private function inValidTransactionError(): bool
771
+	{
772
+		EE_Error::add_error(
773
+			esc_html__(
774
+				'A valid transaction could not be initiated for processing your registrations.',
775
+				'event_espresso'
776
+			),
777
+			__FILE__,
778
+			__FUNCTION__,
779
+			__LINE__
780
+		);
781
+		return false;
782
+	}
783
+
784
+
785
+	/**
786
+	 * @return bool
787
+	 * @since   $VID:$
788
+	 */
789
+	private function noRegistrationsError(): bool
790
+	{
791
+		// combine the old translated string with a new one, in order to not break translations
792
+		$error_message = esc_html__(
793
+			'Your form data could not be applied to any valid registrations.',
794
+			'event_espresso'
795
+		);
796
+		$error_message .= sprintf(
797
+			esc_html_x(
798
+				'%3$sThis can sometimes happen if too much time has been taken to complete the registration process.%3$sPlease return to the %1$sEvent List%2$s and reselect your tickets. If the problem continues, please contact the site administrator.',
799
+				'(line break)This can sometimes happen if too much time has been taken to complete the registration process.(line break)Please return to the (link)Event List(end link) and reselect your tickets. If the problem continues, please contact the site administrator.',
800
+				'event_espresso'
801
+			),
802
+			'<a href="' . get_post_type_archive_link('espresso_events') . '" >',
803
+			'</a>',
804
+			'<br />'
805
+		);
806
+		EE_Error::add_error($error_message, __FILE__, __FUNCTION__, __LINE__);
807
+		return false;
808
+	}
809
+
810
+
811
+	/**
812
+	 * @param int $registrations_processed
813
+	 * @return bool
814
+	 * @since   $VID:$
815
+	 */
816
+	private function registrationProcessingError(int $registrations_processed): bool
817
+	{
818
+		// generate a correctly translated string for all possible singular/plural combinations
819
+		if ($this->checkout->total_ticket_count === 1 && $registrations_processed !== 1) {
820
+			$error_msg = sprintf(
821
+				esc_html_x(
822
+					'There was %1$d ticket in the Event Queue, but %2$ds registrations were processed',
823
+					'There was 1 ticket in the Event Queue, but 2 registrations were processed',
824
+					'event_espresso'
825
+				),
826
+				$this->checkout->total_ticket_count,
827
+				$registrations_processed
828
+			);
829
+		} elseif ($this->checkout->total_ticket_count !== 1 && $registrations_processed === 1) {
830
+			$error_msg = sprintf(
831
+				esc_html_x(
832
+					'There was a total of %1$d tickets in the Event Queue, but only %2$ds registration was processed',
833
+					'There was a total of 2 tickets in the Event Queue, but only 1 registration was processed',
834
+					'event_espresso'
835
+				),
836
+				$this->checkout->total_ticket_count,
837
+				$registrations_processed
838
+			);
839
+		} else {
840
+			$error_msg = sprintf(
841
+				esc_html__(
842
+					'There was a total of 2 tickets in the Event Queue, but 2 registrations were processed',
843
+					'event_espresso'
844
+				),
845
+				$this->checkout->total_ticket_count,
846
+				$registrations_processed
847
+			);
848
+		}
849
+		EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
850
+		return false;
851
+	}
852
+
853
+
854
+	/**
855
+	 *    update_reg_step
856
+	 *    this is the final step after a user  revisits the site to edit their attendee information
857
+	 *    this gets called AFTER the process_reg_step() method above
858
+	 *
859
+	 * @return bool
860
+	 * @throws EE_Error
861
+	 * @throws InvalidArgumentException
862
+	 * @throws ReflectionException
863
+	 * @throws RuntimeException
864
+	 * @throws InvalidDataTypeException
865
+	 * @throws InvalidInterfaceException
866
+	 */
867
+	public function update_reg_step(): bool
868
+	{
869
+		// save everything
870
+		if ($this->process_reg_step()) {
871
+			$this->checkout->redirect     = true;
872
+			$this->checkout->redirect_url = add_query_arg(
873
+				[
874
+					'e_reg_url_link' => $this->checkout->reg_url_link,
875
+					'revisit'        => true,
876
+				],
877
+				$this->checkout->thank_you_page_url
878
+			);
879
+			$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
880
+			return true;
881
+		}
882
+		return false;
883
+	}
884 884
 }
Please login to merge, or discard this patch.