Passed
Push — master ( ed9391...1afebe )
by Brian
05:14
created

GetPaid_Checkout::post_process_submission()   B

Complexity

Conditions 7
Paths 40

Size

Total Lines 39
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 19
nc 40
nop 3
dl 0
loc 39
rs 8.8333
c 2
b 0
f 0
1
<?php
2
/**
3
 * Contains the Main Checkout Class.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * Main Checkout Class.
11
 *
12
 */
13
class GetPaid_Checkout {
14
15
	/**
16
	 * @var GetPaid_Payment_Form_Submission
17
	 */
18
	protected $payment_form_submission;
19
20
	/**
21
	 * Class constructor.
22
	 *
23
	 * @param GetPaid_Payment_Form_Submission $submission
24
	 */
25
	public function __construct( $submission ) {
26
		$this->payment_form_submission = $submission;
27
	}
28
29
	/**
30
	 * Processes the checkout.
31
	 *
32
	 */
33
	public function process_checkout() {
34
35
		// Validate the submission.
36
		$this->validate_submission();
37
38
		// Prepare the invoice.
39
		$items      = $this->get_submission_items();
40
		$invoice    = $this->get_submission_invoice();
41
		$invoice    = $this->process_submission_invoice( $invoice, $items );
42
		$prepared   = $this->prepare_submission_data_for_saving();
43
44
		$this->prepare_billing_info( $invoice );
45
46
		$shipping   = $this->prepare_shipping_info( $invoice );
47
48
		// Save the invoice.
49
		$invoice->set_is_viewed( true );
50
		$invoice->recalculate_total();
51
        $invoice->save();
52
53
		do_action( 'getpaid_checkout_invoice_updated', $invoice );
54
55
		// Send to the gateway.
56
		$this->post_process_submission( $invoice, $prepared, $shipping );
57
	}
58
59
	/**
60
	 * Validates the submission.
61
	 *
62
	 */
63
	protected function validate_submission() {
64
65
		$submission = $this->payment_form_submission;
66
		$data       = $submission->get_data();
67
68
		// Do we have an error?
69
        if ( ! empty( $submission->last_error ) ) {
70
			wp_send_json_error( $submission->last_error );
71
        }
72
73
		// We need a billing email.
74
        if ( ! $submission->has_billing_email() ) {
75
            wp_send_json_error( __( 'Provide a valid billing email.', 'invoicing' ) );
76
		}
77
78
		// Non-recurring gateways should not be allowed to process recurring invoices.
79
		if ( $submission->should_collect_payment_details() && $submission->has_recurring && ! wpinv_gateway_support_subscription( $data['wpi-gateway'] ) ) {
80
			wp_send_json_error( __( 'The selected payment gateway does not support subscription payments.', 'invoicing' ) );
81
		}
82
83
		// Ensure the gateway is active.
84
		if ( $submission->should_collect_payment_details() && ! wpinv_is_gateway_active( $data['wpi-gateway'] ) ) {
85
			wp_send_json_error( __( 'The selected payment gateway is not active', 'invoicing' ) );
86
		}
87
88
		// Clear any existing errors.
89
		wpinv_clear_errors();
90
91
		// Allow themes and plugins to hook to errors
92
		do_action( 'getpaid_checkout_error_checks', $submission );
93
94
		// Do we have any errors?
95
        if ( wpinv_get_errors() ) {
96
            wp_send_json_error( getpaid_get_errors_html() );
97
		}
98
99
	}
100
101
	/**
102
	 * Retrieves submission items.
103
	 *
104
	 * @return GetPaid_Form_Item[]
105
	 */
106
	protected function get_submission_items() {
107
108
		$items = $this->payment_form_submission->get_items();
109
110
        // Ensure that we have items.
111
        if ( empty( $items ) && ! $this->payment_form_submission->has_fees() ) {
112
            wp_send_json_error( __( 'Please provide at least one item or amount.', 'invoicing' ) );
113
		}
114
115
		return $items;
116
	}
117
118
	/**
119
	 * Retrieves submission invoice.
120
	 *
121
	 * @return WPInv_Invoice
122
	 */
123
	protected function get_submission_invoice() {
124
		$submission = $this->payment_form_submission;
125
126
		if ( ! $submission->has_invoice() ) {
127
			$invoice = new WPInv_Invoice();
128
			$invoice->set_created_via( 'payment_form' );
129
			return $invoice;
130
        }
131
132
		$invoice = $submission->get_invoice();
133
134
		// Make sure that it is neither paid or refunded.
135
		if ( $invoice->is_paid() || $invoice->is_refunded() ) {
136
			wp_send_json_error( __( 'This invoice has already been paid for.', 'invoicing' ) );
137
		}
138
139
		return $invoice;
140
	}
141
142
	/**
143
	 * Processes the submission invoice.
144
	 *
145
	 * @param WPInv_Invoice $invoice
146
	 * @param GetPaid_Form_Item[] $items
147
	 * @return WPInv_Invoice
148
	 */
149
	protected function process_submission_invoice( $invoice, $items ) {
150
151
		$submission = $this->payment_form_submission;
152
153
		// Set-up the invoice details.
154
		$invoice->set_email( sanitize_email( $submission->get_billing_email() ) );
155
		$invoice->set_user_id( $this->get_submission_customer() );
156
		$invoice->set_payment_form( absint( $submission->get_payment_form()->get_id() ) );
157
        $invoice->set_items( $items );
158
        $invoice->set_fees( $submission->get_fees() );
159
        $invoice->set_taxes( $submission->get_taxes() );
160
		$invoice->set_discounts( $submission->get_discounts() );
161
		$invoice->set_gateway( $submission->get_field( 'wpi-gateway' ) );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $submission->get_field('wpi-gateway') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
162
		$invoice->set_currency( $submission->get_currency() );
163
164
		if ( $submission->has_shipping() ) {
165
			$invoice->set_shipping( $submission->get_shipping() );
166
		}
167
168
		$address_confirmed = $submission->get_field( 'confirm-address' );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $address_confirmed is correct as $submission->get_field('confirm-address') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
169
		$invoice->set_address_confirmed( ! empty( $address_confirmed ) );
170
171
		if ( $submission->has_discount_code() ) {
172
            $invoice->set_discount_code( $submission->get_discount_code() );
173
		}
174
175
		getpaid_maybe_add_default_address( $invoice );
176
		return $invoice;
177
	}
178
179
	/**
180
	 * Retrieves the submission's customer.
181
	 *
182
	 * @return int The customer id.
183
	 */
184
	protected function get_submission_customer() {
185
		$submission = $this->payment_form_submission;
186
187
		// If this is an existing invoice...
188
		if ( $submission->has_invoice() ) {
189
			return $submission->get_invoice()->get_user_id();
190
		}
191
192
		// (Maybe) create the user.
193
        $user = get_current_user_id();
194
195
        if ( empty( $user ) ) {
196
            $user = get_user_by( 'email', $submission->get_billing_email() );
197
        }
198
199
        if ( empty( $user ) ) {
200
			$name = array( $submission->get_field( 'wpinv_first_name', 'billing' ), $submission->get_field( 'wpinv_last_name', 'billing' ) );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $submission->get_field('..._last_name', 'billing') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the usage of $submission->get_field('...first_name', 'billing') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
201
			$name = implode( '', array_filter( $name ) );
202
            $user = wpinv_create_user( $submission->get_billing_email(), $name );
203
204
			// (Maybe) send new user notification.
205
			$should_send_notification = wpinv_get_option( 'disable_new_user_emails' );
206
			if ( ! empty( $user ) && is_numeric( $user ) && apply_filters( 'getpaid_send_new_user_notification', empty( $should_send_notification ), $user ) ) {
0 ignored issues
show
introduced by
The condition is_numeric($user) is always false.
Loading history...
207
				wp_send_new_user_notifications( $user, 'user' );
208
			}
209
		}
210
211
        if ( is_wp_error( $user ) ) {
212
            wp_send_json_error( $user->get_error_message() );
213
        }
214
215
        if ( is_numeric( $user ) ) {
216
            return $user;
217
		}
218
219
		return $user->ID;
220
221
	}
222
223
	/**
224
     * Prepares submission data for saving to the database.
225
     *
226
	 * @return array
227
     */
228
    public function prepare_submission_data_for_saving() {
229
230
		$submission = $this->payment_form_submission;
231
232
		// Prepared submission details.
233
        $prepared = array(
234
			'all'  => array(),
235
			'meta' => array(),
236
		);
237
238
        // Raw submission details.
239
		$data     = $submission->get_data();
240
241
		// Loop through the submitted details.
242
        foreach ( $submission->get_payment_form()->get_elements() as $field ) {
243
244
			// Skip premade fields.
245
            if ( ! empty( $field['premade'] ) ) {
246
                continue;
247
            }
248
249
			// Ensure address is provided.
250
			if ( 'address' === $field['type'] ) {
251
                $address_type = isset( $field['address_type'] ) && 'shipping' === $field['address_type'] ? 'shipping' : 'billing';
252
253
				foreach ( $field['fields'] as $address_field ) {
254
255
					if ( ! empty( $address_field['visible'] ) && ! empty( $address_field['required'] ) && '' === trim( $_POST[ $address_type ][ $address_field['name'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
256
						wp_send_json_error( __( 'Please fill all required fields.', 'invoicing' ) );
257
					}
258
			}
259
		}
260
261
            // If it is required and not set, abort.
262
            if ( ! $submission->is_required_field_set( $field ) ) {
263
                wp_send_json_error( __( 'Please fill all required fields.', 'invoicing' ) );
264
            }
265
266
            // Handle misc fields.
267
            if ( isset( $data[ $field['id'] ] ) ) {
268
269
				// Uploads.
270
				if ( 'file_upload' === $field['type'] ) {
271
					$max_file_num = empty( $field['max_file_num'] ) ? 1 : absint( $field['max_file_num'] );
272
273
					if ( count( $data[ $field['id'] ] ) > $max_file_num ) {
274
						wp_send_json_error( __( 'Maximum number of allowed files exceeded.', 'invoicing' ) );
275
					}
276
277
					$value = array();
278
279
					foreach ( $data[ $field['id'] ] as $url => $name ) {
280
						$value[] = sprintf(
281
							'<a href="%s" target="_blank">%s</a>',
282
							esc_url_raw( $url ),
283
							esc_html( $name )
284
						);
285
					}
286
287
					$value = implode( ' | ', $value );
288
289
				} elseif ( 'checkbox' === $field['type'] ) {
290
					$value = ! empty( $data[ $field['id'] ] ) ? __( 'Yes', 'invoicing' ) : __( 'No', 'invoicing' );
291
				} else {
292
					$value = wp_kses_post( $data[ $field['id'] ] );
293
				}
294
295
                $label = $field['id'];
296
297
                if ( isset( $field['label'] ) ) {
298
                    $label = $field['label'];
299
                }
300
301
				if ( ! empty( $field['add_meta'] ) ) {
302
					$prepared['meta'][ wpinv_clean( $label ) ] = wp_kses_post_deep( $value );
303
				}
304
				$prepared['all'][ wpinv_clean( $label ) ] = wp_kses_post_deep( $value );
305
306
            }
307
		}
308
309
		return $prepared;
310
311
	}
312
313
	/**
314
     * Retrieves address details.
315
     *
316
	 * @return array
317
	 * @param WPInv_Invoice $invoice
318
	 * @param string $type
319
     */
320
    public function prepare_address_details( $invoice, $type = 'billing' ) {
321
322
		$data     = $this->payment_form_submission->get_data();
323
		$type     = sanitize_key( $type );
324
		$address  = array();
325
		$prepared = array();
326
327
		if ( ! empty( $data[ $type ] ) ) {
328
			$address = $data[ $type ];
329
		}
330
331
		// Clean address details.
332
		foreach ( $address as $key => $value ) {
333
			$key             = sanitize_key( $key );
334
			$key             = str_replace( 'wpinv_', '', $key );
335
			$value           = wpinv_clean( $value );
336
			$prepared[ $key ] = apply_filters( "getpaid_checkout_{$type}_address_$key", $value, $this->payment_form_submission, $invoice );
337
		}
338
339
		// Filter address details.
340
		$prepared = apply_filters( "getpaid_checkout_{$type}_address", $prepared, $this->payment_form_submission, $invoice );
341
342
		// Remove non-whitelisted values.
343
		return array_filter( $prepared, 'getpaid_is_address_field_whitelisted', ARRAY_FILTER_USE_KEY );
344
345
	}
346
347
	/**
348
     * Prepares the billing details.
349
     *
350
	 * @return array
351
	 * @param WPInv_Invoice $invoice
352
     */
353
    protected function prepare_billing_info( &$invoice ) {
354
355
		$billing_address = $this->prepare_address_details( $invoice, 'billing' );
356
357
		// Update the invoice with the billing details.
358
		$invoice->set_props( $billing_address );
359
360
	}
361
362
	/**
363
     * Prepares the shipping details.
364
     *
365
	 * @return array
366
	 * @param WPInv_Invoice $invoice
367
     */
368
    protected function prepare_shipping_info( $invoice ) {
369
370
		$data = $this->payment_form_submission->get_data();
371
372
		if ( empty( $data['same-shipping-address'] ) ) {
373
			return $this->prepare_address_details( $invoice, 'shipping' );
374
		}
375
376
		return $this->prepare_address_details( $invoice, 'billing' );
377
378
	}
379
380
	/**
381
	 * Confirms the submission is valid and send users to the gateway.
382
	 *
383
	 * @param WPInv_Invoice $invoice
384
	 * @param array $prepared_payment_form_data
385
	 * @param array $shipping
386
	 */
387
	protected function post_process_submission( $invoice, $prepared_payment_form_data, $shipping ) {
388
389
		// Ensure the invoice exists.
390
        if ( ! $invoice->exists() ) {
391
            wp_send_json_error( __( 'An error occured while saving your invoice. Please try again.', 'invoicing' ) );
392
        }
393
394
		// Save payment form data.
395
		$prepared_payment_form_data = apply_filters( 'getpaid_prepared_payment_form_data', $prepared_payment_form_data, $invoice );
396
        delete_post_meta( $invoice->get_id(), 'payment_form_data' );
397
		delete_post_meta( $invoice->get_id(), 'additional_meta_data' );
398
		if ( ! empty( $prepared_payment_form_data ) ) {
399
400
			if ( ! empty( $prepared_payment_form_data['all'] ) ) {
401
				update_post_meta( $invoice->get_id(), 'payment_form_data', $prepared_payment_form_data['all'] );
402
			}
403
404
			if ( ! empty( $prepared_payment_form_data['meta'] ) ) {
405
				update_post_meta( $invoice->get_id(), 'additional_meta_data', $prepared_payment_form_data['meta'] );
406
			}
407
		}
408
409
		// Save payment form data.
410
		$shipping = apply_filters( 'getpaid_checkout_shipping_details', $shipping, $this->payment_form_submission );
411
        if ( ! empty( $shipping ) ) {
412
            update_post_meta( $invoice->get_id(), 'shipping_address', $shipping );
413
		}
414
415
		// Backwards compatibility.
416
        add_filter( 'wp_redirect', array( $this, 'send_redirect_response' ) );
417
418
		try {
419
			$this->process_payment( $invoice );
420
		} catch ( Exception $e ) {
421
			wpinv_set_error( 'payment_error', $e->getMessage() );
422
		}
423
424
        // If we are here, there was an error.
425
		wpinv_send_back_to_checkout( $invoice );
426
427
	}
428
429
	/**
430
	 * Processes the actual payment.
431
	 *
432
	 * @param WPInv_Invoice $invoice
433
	 */
434
	protected function process_payment( $invoice ) {
435
436
		// Clear any checkout errors.
437
		wpinv_clear_errors();
438
439
		// No need to send free invoices to the gateway.
440
		if ( $invoice->is_free() ) {
441
			$this->process_free_payment( $invoice );
442
		}
443
444
		$submission = $this->payment_form_submission;
445
446
		// Fires before sending to the gateway.
447
		do_action( 'getpaid_checkout_before_gateway', $invoice, $submission );
448
449
		// Allow the sumission data to be modified before it is sent to the gateway.
450
		$submission_data    = $submission->get_data();
451
		$submission_gateway = apply_filters( 'getpaid_gateway_submission_gateway', $invoice->get_gateway(), $submission, $invoice );
452
		$submission_data    = apply_filters( 'getpaid_gateway_submission_data', $submission_data, $submission, $invoice );
453
454
		// Validate the currency.
455
		if ( ! apply_filters( "getpaid_gateway_{$submission_gateway}_is_valid_for_currency", true, $invoice->get_currency() ) ) {
456
			wpinv_set_error( 'invalid_currency' );
457
		}
458
459
		// Check to see if we have any errors.
460
		if ( wpinv_get_errors() ) {
461
			wpinv_send_back_to_checkout( $invoice );
462
		}
463
464
		// Send info to the gateway for payment processing
465
		do_action( "getpaid_gateway_$submission_gateway", $invoice, $submission_data, $submission );
466
467
		// Backwards compatibility.
468
		wpinv_send_to_gateway( $submission_gateway, $invoice );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_send_to_gateway() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

468
		/** @scrutinizer ignore-deprecated */ wpinv_send_to_gateway( $submission_gateway, $invoice );
Loading history...
469
470
	}
471
472
	/**
473
	 * Marks the invoice as paid in case the checkout is free.
474
	 *
475
	 * @param WPInv_Invoice $invoice
476
	 */
477
	protected function process_free_payment( $invoice ) {
478
479
		$invoice->set_gateway( 'none' );
480
		$invoice->add_note( __( "This is a free invoice and won't be sent to the payment gateway", 'invoicing' ), false, false, true );
481
		$invoice->mark_paid();
482
		wpinv_send_to_success_page( array( 'invoice_key' => $invoice->get_key() ) );
483
484
	}
485
486
	/**
487
     * Sends a redrect response to payment details.
488
     *
489
     */
490
    public function send_redirect_response( $url ) {
491
        $url = rawurlencode( $url );
492
        wp_send_json_success( $url );
493
    }
494
495
}
496