PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.6.31
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.6.31
1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 1.7.0.9 1.8.0 All 210 releases
charitable / includes / donations / class-charitable-donation-processor.php

class-charitable-donation-processor.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.6.31, at includes/donations/class-charitable-donation-processor.php

941 lines 26.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The class that is responsible for responding to donation events.
4 *
5 * @version 1.0.0
6 * @package Charitable/Classes/Charitable_Donation_Processor
7 * @author Eric Daams
8 * @copyright Copyright (c) 2020, Studio 164a
9 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
10 */
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( ! class_exists( 'Charitable_Donation_Processor' ) ) :
18
19 /**
20 * Charitable Donation Processor.
21 *
22 * @since 1.0.0
23 */
24 class Charitable_Donation_Processor {
25
26 /**
27 * The single instance of this class.
28 *
29 * @var Charitable_Donation_Processor|null
30 */
31 private static $instance = null;
32
33 /**
34 * The campaign receiving a donation.
35 *
36 * @var Charitable_Campaign|false
37 */
38 protected $campaign;
39
40 /**
41 * The donation data.
42 *
43 * @var mixed[]
44 */
45 protected $donation_data;
46
47 /**
48 * The campaign donations array.
49 *
50 * @var array
51 */
52 protected $campaign_donations_data;
53
54 /**
55 * The donor ID for the current donation.
56 *
57 * @var int
58 */
59 protected $donor_id;
60
61 /**
62 * The donoration ID for the current donation.
63 *
64 * @var int
65 */
66 protected $donation_id;
67
68 /**
69 * Create class object. A protected constructor, so this is used in a singleton context.
70 *
71 * @since 1.0.0
72 */
73 protected function __construct() {
74 $this->campaign = charitable_get_current_campaign();
75 }
76
77 /**
78 * Returns and/or create the single instance of this class.
79 *
80 * @since 1.0.0
81 *
82 * @return Charitable_Donation_Processor
83 */
84 public static function get_instance() {
85 if ( is_null( self::$instance ) ) {
86 self::$instance = new self();
87 }
88
89 return self::$instance;
90 }
91
92 /**
93 * Destroy the Charitable_Donation_Processor instance.
94 *
95 * This is primarily useful for testing purposes, as it allows you to
96 * create multiple donations in a single request.
97 *
98 * @since 1.3.0
99 *
100 * @return void
101 */
102 public static function destroy() {
103 self::$instance = null;
104 }
105
106 /**
107 * Return the current campaign.
108 *
109 * @since 1.0.0
110 *
111 * @return Charitable_Campaign|false False if no campaign is set. Campaign object otherwise.
112 */
113 public function get_campaign() {
114 return $this->campaign;
115 }
116
117 /**
118 * Return the donation ID.
119 *
120 * @since 1.4.0
121 *
122 * @return int
123 */
124 public function get_donation_id() {
125 return $this->donation_id;
126 }
127
128 /**
129 * Executed when a user first clicks the Donate button on a campaign.
130 *
131 * @since 1.0.0
132 *
133 * @return void
134 */
135 public static function add_donation_to_session() {
136 $processor = self::get_instance();
137
138 if ( ! $processor->get_campaign() ) {
139 return;
140 }
141
142 $nonce = $_POST['charitable-donate-now'];
143
144 if ( ! wp_verify_nonce( $nonce, 'charitable-donate' ) ) {
145
146 /* For backwards compatibility, we also check this nonce. */
147 if ( ! wp_verify_nonce( $nonce, 'charitable-donate-' ) ) {
148 return;
149 }
150 }
151
152 /* Save the donation in the session */
153 charitable_get_session()->add_donation( $processor->get_campaign()->ID, 0 );
154
155 $donations_url = charitable_get_permalink( 'campaign_donation_page', array( 'campaign_id' => $processor->get_campaign()->ID ) );
156
157 wp_redirect( $donations_url );
158
159 die();
160 }
161
162 /**
163 * Process the donation.
164 *
165 * This is used by all donation form submissions, AJAX or not.
166 *
167 * @since 1.3.0
168 *
169 * @return mixed
170 */
171 public function process_donation() {
172 $processor = self::get_instance();
173 $campaign = $processor->get_campaign();
174
175 if ( ! $campaign ) {
176 return;
177 }
178
179 /* Validate the form submission and retrieve the values. */
180 $form = $campaign->get_donation_form();
181
182 /**
183 * Do something before any donation form processing takes place.
184 *
185 * @since 1.0.0
186 *
187 * @param Charitable_Donation_Processor $processor The Donation Processor object.
188 * @param Charitable_Donation_Form $form The Donation Form object.
189 */
190 do_action( 'charitable_before_process_donation_form', $processor, $form );
191
192 if ( ! $form->validate_submission() ) {
193 return false;
194 }
195
196 $values = $form->get_donation_values();
197 $gateway = $values['gateway'];
198
199 /**
200 * Validate the gateway values.
201 *
202 * @since 1.0.0
203 *
204 * @param boolean $valid Whether the submission passes validation.
205 * @param string $gateway The gateway ID.
206 * @param array $values The values submitted by the user.
207 */
208 if ( ! apply_filters( 'charitable_validate_donation_form_submission_gateway', true, $gateway, $values ) ) {
209 return false;
210 }
211
212 $this->donation_id = $processor->save_donation( $values );
213
214 /**
215 * Set a transient to allow plugins to act on this donation on the next page load.
216 */
217 set_transient( 'charitable_donation_' . charitable_get_session()->get_session_id(), $this );
218
219 /**
220 * We check whether the gateway is compatible with version 1.3, since Charitable 1.3
221 * change the hook into a filter (instead of an action).
222 */
223 if ( $this->gateway_is_130_compatible( $gateway ) ) {
224
225 /**
226 * Pass donation processing to the payment gateway and return the results.
227 *
228 * Clients should return one of three results:
229 *
230 * - TRUE : If the donation was processed successfully and the user should
231 * be redirected to the donation receipt.
232 * - FALSE : If the donation could not be processed successfully and the user
233 * should be redirected back to the donation form.
234 * - ARRAY : If the user needs to be redirected somewhere other than the donation
235 * receipt or donation form. Array should contain two values:
236 * `redirect` : The url to be redirected to.
237 * `safe` : Whether to use wp_safe_redirect. If unset, will default to true.
238 *
239 * @since 1.3.0
240 *
241 * @param mixed $result The result of gateway processing.
242 * @param int $donation_id The donation ID.
243 * @param Charitable_Donation_Processor $processor The Donation Processor object.
244 * @return mixed
245 */
246 return apply_filters( 'charitable_process_donation_' . $gateway, true, $this->donation_id, $processor );
247
248 } else {
249 /**
250 * Send donation processing to the payment gateway.
251 *
252 * This is a fallback for payment gateways that have not been updated to the filter method above.
253 *
254 * @since 1.0.0
255 *
256 * @param int $donation_id The donation ID.
257 * @param Charitable_Donation_Processor $processor The Donation Processor object.
258 */
259 do_action( 'charitable_process_donation_' . $gateway, $this->donation_id, $processor );
260
261 /**
262 * If we get this far, forward the user through to the receipt page.
263 */
264 wp_safe_redirect( charitable_get_permalink( 'donation_receipt_page', array( 'donation_id' => $this->donation_id ) ) );
265
266 die();
267
268 }//end if
269 }
270
271 /**
272 * Save a donation submitted through a page reload (i.e. not AJAX).
273 *
274 * @since 1.0.0
275 *
276 * @return void
277 */
278 public static function process_donation_form_submission() {
279 $processor = self::get_instance();
280 $result = $processor->process_donation();
281
282 $processor->redirect_after_gateway_processing( $result );
283 }
284
285 /**
286 * Add a donation with AJAX.
287 *
288 * @since 1.3.0
289 *
290 * @return void
291 */
292 public static function ajax_process_donation_form_submission() {
293 if ( ! isset( $_POST['campaign_id'] ) ) {
294 wp_send_json_error( new WP_Error( 'missing_campaign_id', __( 'Campaign ID was not found. Unable to create donation.', 'charitable' ) ) );
295 }
296
297 $processor = self::get_instance();
298
299 $result = $processor->process_donation();
300 $response = is_array( $result ) ? $result : array();
301
302 if ( false !== $result ) {
303 $response['success'] = true;
304 $response['redirect_to'] = $processor->get_redirection_after_gateway_processing( $result );
305 } else {
306 $errors = charitable_get_notices()->get_errors();
307
308 if ( empty( $errors ) ) {
309 $errors = array( __( 'Unable to process donation.', 'charitable' ) );
310 }
311
312 $response['success'] = false;
313 $response['errors'] = $errors;
314 $response['donation_id'] = (int) $processor->get_donation_id();
315 }
316
317 wp_send_json( $response );
318
319 exit();
320 }
321
322 /**
323 * Save a donation.
324 *
325 * @since 1.0.0
326 *
327 * @return void
328 */
329 public static function make_donation_streamlined() {
330 $processor = self::get_instance();
331 $campaign = $processor->get_campaign();
332
333 if ( ! $campaign ) {
334 return;
335 }
336
337 /* Validate the form submission and retrieve the values. */
338 $form = new Charitable_Donation_Amount_Form( $campaign );
339
340 /**
341 * @hook charitable_before_process_donation_amount_form
342 */
343 do_action( 'charitable_before_process_donation_amount_form', $processor, $form );
344
345 /**
346 * Form validated, so add the donation to session.
347 */
348 if ( $form->validate_submission() ) {
349
350 $submitted = $form->get_donation_values();
351 $period = array_key_exists( 'donation_period', $submitted ) ? $submitted['donation_period'] : 'once';
352
353 charitable_get_session()->add_donation( $submitted['campaign_id'], $submitted['amount'], $period );
354
355 /**
356 * @hook charitable_after_process_donation_amount_form
357 */
358 do_action( 'charitable_after_process_donation_amount_form', $processor, $submitted );
359 }
360
361 $redirect_url = charitable_get_permalink( 'campaign_donation_page', array( 'campaign_id' => $campaign->ID ) );
362
363 if ( 'same_page' == charitable_get_option( 'donation_form_display', 'separate_page' ) ) {
364 $redirect_url .= '#charitable-donation-form';
365 }
366
367 wp_safe_redirect( $redirect_url );
368 die();
369 }
370
371 /**
372 * Inserts a new donation.
373 *
374 * This method is designed to be completely form agnostic.
375 *
376 * We use this when integrating third-party systems like Easy Digital Downloads and
377 * WooCommerce.
378 *
379 * @since 1.0.0
380 *
381 * @param mixed[] $values Submitted donation values.
382 * @return int $donation_id Returns 0 in case of failure. Positive donation ID otherwise.
383 */
384 public function save_donation( array $values ) {
385 /**
386 * Filter the donation values to be saved.
387 *
388 * @since 1.0.0
389 *
390 * @param array $values The donation values to be saved.
391 */
392 $this->donation_data = apply_filters( 'charitable_donation_values', $values );
393
394 if ( ! $this->get_campaign_donations_data() ) {
395 charitable_get_deprecated()->doing_it_wrong(
396 __METHOD__,
397 __( 'A donation cannot be inserted without an array of campaigns being donated to.', 'charitable' ),
398 '1.0.0'
399 );
400 return 0;
401 }
402
403 if ( ! $this->is_valid_user_data() ) {
404 charitable_get_deprecated()->doing_it_wrong(
405 __METHOD__,
406 __( 'A donation cannot be inserted without valid user data.', 'charitable' ),
407 '1.0.0'
408 );
409 return 0;
410 }
411
412 /**
413 * Do something right before the donation is inserted into the database.
414 *
415 * @since 1.0.0
416 *
417 * @param Charitable_Donation_Processor $this The Processor instance.
418 */
419 do_action( 'charitable_before_save_donation', $this );
420
421 $donation_id = wp_insert_post( $this->parse_donation_data() );
422
423 $this->set_donation_key();
424
425 if ( is_wp_error( $donation_id ) ) {
426 charitable_get_notices()->add_errors_from_wp_error( $donation_id );
427 return 0;
428 }
429
430 if ( 0 == $donation_id ) {
431 charitable_get_notices()->add_error( __( 'We were unable to save the donation. Please try again.', 'charitable' ) );
432 return 0;
433 }
434
435 $this->save_campaign_donations( $donation_id );
436
437 $this->save_donation_meta( $donation_id );
438
439 $this->save_donor_contact_consent();
440
441 $log_note = array_key_exists( 'log_note', $values ) ? $values['log_note'] : '';
442
443 if ( empty( $log_note ) ) {
444 $log_note = isset( $values['ID'] ) && 0 !== $values['ID'] ? __( 'Payment attempted.', 'charitable' ) : __( 'Donation created.', 'charitable' );
445 }
446
447 $this->update_donation_log( $donation_id, $log_note );
448
449 /**
450 * Update the user session if we're on the public site or in an AJAX request.
451 *
452 * 1. Write the donation key to the session.
453 * 2. Write the campaign donation amounts to the session. This is required in
454 * case the donor gets sent back to the donation form (cancellation, failure).
455 */
456 if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
457
458 $session = charitable_get_session();
459
460 /* Required for the donation receipt to load for logged out donors. */
461 $session->add_donation_key( $this->get_donation_data_value( 'donation_key' ) );
462
463 /* Required in case the donor is redirected back to the donation form. */
464 foreach ( $this->get_campaign_donations_data() as $campaign ) {
465 $session->add_donation( $campaign['campaign_id'], $campaign['amount'], $this->get_donation_data_value( 'donation_key', 'once' ) );
466 }
467 }
468
469 /**
470 * Do something after the donation has been saved in the database.
471 *
472 * @since 1.0.0
473 *
474 * @param int $donation_id The donation ID.
475 * @param Charitable_Donation_Processor $this The Processor instance.
476 */
477 do_action( 'charitable_after_save_donation', $donation_id, $this );
478
479 return $donation_id;
480 }
481
482 /**
483 * Inserts the campaign donations into the campaign_donations table.
484 *
485 * @since 1.0.0
486 *
487 * @param int $donation_id The donation ID.
488 * @return int The number of donations inserted.
489 */
490 public function save_campaign_donations( $donation_id ) {
491 $donor_id = $this->get_donor_id();
492 $campaigns = $this->get_campaign_donations_data();
493
494 if ( false !== $this->get_donation_data_value( 'ID', false ) ) {
495 $records = charitable_get_table( 'campaign_donations' )->get_donation_records( $donation_id );
496 $campaign_donations = wp_list_pluck( $records, 'campaign_id', 'campaign_donation_id' );
497 } else {
498 $campaign_donations = array();
499 }
500
501 foreach ( $campaigns as $campaign ) {
502
503 if ( array_key_exists( 'campaign_donation_id', $campaign ) ) {
504 $campaign_donation_id = $campaign['campaign_donation_id'];
505 } else {
506 $campaign_donation_id = array_search( $campaign['campaign_id'], $campaign_donations );
507 }
508
509 /* Avoid adding duplicate campaign donations when re-submitting a campaign. */
510 if ( false !== $campaign_donation_id ) {
511
512 /* Cast the existing campaign_donation record to an array and sanitize the amount. */
513 $campaign_donation = (array) $records[ $campaign_donation_id ];
514 $campaign_donation['amount'] = charitable_sanitize_amount( $campaign_donation['amount'], true );
515
516 /* Get our updated campaign donation values. */
517 $args = charitable_array_subset( $campaign, array( 'campaign_id', 'campaign_name', 'amount' ) );
518 $args['donor_id'] = $donor_id;
519
520 /* If any values have changed, update the record. */
521 if ( count( array_diff( $args, $campaign_donation ) ) ) {
522 charitable_get_table( 'campaign_donations' )->update( $campaign_donation_id, $args, 'campaign_donation_id' );
523 }
524 } else {
525
526 /* Add a new donation */
527 $campaign['donor_id'] = $donor_id;
528 $campaign['donation_id'] = $donation_id;
529 $campaign_donation_id = charitable_get_table( 'campaign_donations' )->insert( $campaign );
530
531 if ( 0 == $campaign_donation_id ) {
532 return 0;
533 }
534 }
535 }//end foreach
536
537 return count( $campaigns );
538 }
539
540 /**
541 * Save the meta for the donation.
542 *
543 * @since 1.0.0
544 *
545 * @param int $donation_id The donation ID.
546 * @return void
547 */
548 public function save_donation_meta( $donation_id ) {
549 $meta = array(
550 'donation_gateway' => $this->get_donation_data_value( 'gateway' ),
551 'donor' => $this->get_donation_data_value( 'user' ),
552 'test_mode' => charitable_get_option( 'test_mode', 0 ),
553 'donation_key' => $this->get_donation_data_value( 'donation_key' ),
554 );
555
556 if ( $this->get_donation_data_value( 'meta' ) ) {
557 $meta = array_merge( $meta, $this->get_donation_data_value( 'meta' ) );
558 }
559
560 /**
561 * Filter the donation meta to be saved.
562 *
563 * @since 1.0.0
564 *
565 * @param array $meta The meta to be saved, in a key=>value array.
566 * @param int $donation_id The donation ID.
567 * @param Charitable_Donation_Processor $processor This instance of `Charitable_Donation_Processor`.
568 */
569 $meta = apply_filters( 'charitable_donation_meta', $meta, $donation_id, $this );
570
571 foreach ( $meta as $meta_key => $value ) {
572 /**
573 * Sanitize a particular meta value.
574 *
575 * @since 1.0.0
576 *
577 * @param mixed $value The value.
578 * @param string $meta_key The meta key.
579 */
580 $value = apply_filters( 'charitable_sanitize_donation_meta', $value, $meta_key );
581 update_post_meta( $donation_id, $meta_key, $value );
582 }
583 }
584
585 /**
586 * Save whether the donor gave consent to be contacted.
587 *
588 * @since 1.6.0
589 *
590 * @return void
591 */
592 public function save_donor_contact_consent() {
593 /* Contact consent requires he database upgrade to have been completed. */
594 if ( ! charitable_is_contact_consent_activated() ) {
595 return;
596 }
597
598 $donor_id = $this->get_donor_id();
599 $meta = $this->get_donation_data_value( 'meta', array() );
600 $contact_consent = array_key_exists( 'contact_consent', $meta ) ? (bool) $meta['contact_consent'] : false;
601
602 charitable_get_table( 'donors' )->update(
603 $donor_id,
604 array(
605 'contact_consent' => $contact_consent,
606 )
607 );
608 }
609
610 /**
611 * Add a message to the donation log.
612 *
613 * @since 1.0.0
614 *
615 * @param int $donation_id The donation ID.
616 * @param string $message The message to add to the log.
617 * @return void
618 */
619 public function update_donation_log( $donation_id, $message ) {
620 $donation = charitable_get_donation( $donation_id );
621 if ( $donation ) {
622 $donation->log()->add( $message );
623 }
624 }
625
626 /**
627 * Returns the submitted donation data.
628 *
629 * @since 1.0.0
630 *
631 * @return mixed[]
632 */
633 public function get_donation_data() {
634 return $this->donation_data;
635 }
636
637 /**
638 * Return the submitted value for a particular key.
639 *
640 * @since 1.0.0
641 *
642 * @param string $key The key to search for.
643 * @param mixed $default Fallback value to return if the data is not set.
644 * @return mixed
645 */
646 public function get_donation_data_value( $key, $default = false ) {
647 $data = $this->get_donation_data();
648 return isset( $data[ $key ] ) ? $data[ $key ] : $default;
649 }
650
651 /**
652 * Set a value for a particular key.
653 *
654 * @since 1.4.0
655 *
656 * @param string $key The key to set.
657 * @param mixed $value The value to be set.
658 * @return void
659 */
660 public function set_donation_data_value( $key, $value ) {
661 $this->donation_data[ $key ] = $value;
662 }
663
664 /**
665 * Returns the campaign donations array, or false if the data is invalid.
666 *
667 * @since 1.0.0
668 *
669 * @return array[]|false
670 */
671 public function get_campaign_donations_data() {
672 if ( ! isset( $this->campaign_donations_data ) ) {
673
674 if ( ! is_array( $this->get_donation_data_value( 'campaigns' ) ) ) {
675 return false;
676 }
677
678 $this->campaign_donations_data = array();
679
680 foreach ( $this->get_donation_data_value( 'campaigns' ) as $campaign ) {
681
682 /* If the amount or campaign_id are missing, this donation won't work. */
683 if ( ! isset( $campaign['campaign_id'] ) || ! isset( $campaign['amount'] ) ) {
684 return false;
685 }
686
687 if ( ! isset( $campaign['campaign_name'] ) ) {
688 $campaign['campaign_name'] = get_post_field( 'post_title', $campaign['campaign_id'], 'raw' );
689 }
690
691 $this->campaign_donations_data[] = $campaign;
692 }
693 }//end if
694
695 return $this->campaign_donations_data;
696 }
697
698 /**
699 * Returns the donor_id for the current donation.
700 *
701 * @since 1.0.0
702 *
703 * @return int
704 */
705 public function get_donor_id() {
706 if ( ! isset( $this->donor_id ) ) {
707 $this->donor_id = $this->get_donation_data_value( 'donor_id', 0 );
708
709 if ( $this->donor_id ) {
710 return $this->donor_id;
711 }
712
713 $user_data = $this->get_donation_data_value( 'user', array() );
714
715 if ( array_key_exists( 'email', $user_data ) ) {
716 $this->donor_id = charitable_get_table( 'donors' )->get_donor_id_by_email( $user_data['email'] );
717 }
718
719 /* If we still do not have a donor ID, it means that this is a first-time donor */
720 if ( 0 == $this->donor_id ) {
721 $user = new Charitable_User( $this->get_donation_data_value( 'user_id', get_current_user_id() ) );
722 $this->donor_id = $user->add_donor( $user_data );
723 }
724 }//end if
725
726 return $this->donor_id;
727 }
728
729 /**
730 * Returns the name of the donor.
731 *
732 * @since 1.0.0
733 * @since 1.6.14 Changed access to public. Previously protected.
734 *
735 * @return string
736 */
737 public function get_donor_name() {
738 $user = new WP_User( $this->get_donation_data_value( 'user_id', 0 ) );
739 $user_data = $this->get_donation_data_value( 'user' );
740 $first_name = isset( $user_data['first_name'] ) ? $user_data['first_name'] : $user->get( 'first_name' );
741 $last_name = isset( $user_data['last_name'] ) ? $user_data['last_name'] : $user->get( 'last_name' );
742 return trim( sprintf( '%s %s', $first_name, $last_name ) );
743 }
744
745 /**
746 * Returns a comma separated list of the campaigns that are being donated to.
747 *
748 * @since 1.0.0
749 * @since 1.6.14 Changed access to public. Previously protected.
750 *
751 * @return string
752 */
753 public function get_campaign_names() {
754 $campaigns = wp_list_pluck( $this->get_campaign_donations_data(), 'campaign_name' );
755 return implode( ', ', $campaigns );
756 }
757
758 /**
759 * Returns the donation status. Defaults to charitable-pending.
760 *
761 * @since 1.0.0
762 *
763 * @return string
764 */
765 protected function get_donation_status() {
766 $status = $this->get_donation_data_value( 'status', 'charitable-pending' );
767
768 if ( ! charitable_is_valid_donation_status( $status ) ) {
769 $status = 'charitable-pending';
770 }
771
772 return $status;
773 }
774
775 /**
776 * Redirect the user after the gateway has processed the donation.
777 *
778 * @uses Charitable_Donation_Processor::get_redirection_after_gateway_processing()
779 *
780 * @since 1.3.0
781 *
782 * @param mixed $gateway_processing The result of the gateway processing.
783 * @return void
784 */
785 private function redirect_after_gateway_processing( $gateway_processing ) {
786 $redirect_url = $this->get_redirection_after_gateway_processing( $gateway_processing );
787
788 /* If the gateway processing failed, add the error notices to the session. */
789 if ( false == $gateway_processing ) {
790
791 /* Log the failed payment. */
792 $this->update_donation_log(
793 $this->donation_id,
794 sprintf(
795 /* translators: %s: error message */
796 __( 'Payment failed with errors: %s', 'charitable' ),
797 PHP_EOL . implode( PHP_EOL, charitable_get_notices()->get_errors() )
798 )
799 );
800
801 charitable_get_session()->add_notices();
802 }
803
804 /* Set the redirect status to use. */
805 $status = isset( $gateway_processing['status'] ) ? $gateway_processing['status'] : 302;
806
807 /**
808 * If the gateway processing returned an array with a directive to NOT
809 * use wp_safe_redirect, use wp_redirect instead.
810 */
811 if ( isset( $gateway_processing['safe'] ) && false == $gateway_processing['safe'] ) {
812 wp_redirect( $redirect_url, $status );
813 die();
814 }
815
816 wp_safe_redirect( $redirect_url, $status );
817
818 die();
819 }
820
821 /**
822 * Return the URL that the donor should be redirected to.
823 *
824 * @since 1.3.0
825 *
826 * @param mixed $gateway_processing The result of the gateway processing.
827 * @return string
828 */
829 private function get_redirection_after_gateway_processing( $gateway_processing ) {
830 if ( false == $gateway_processing ) {
831
832 $redirect_url = esc_url( add_query_arg( array( 'donation_id' => $this->donation_id ), wp_get_referer() ) );
833
834 } elseif ( is_array( $gateway_processing ) && isset( $gateway_processing['redirect'] ) ) {
835
836 $redirect_url = $gateway_processing['redirect'];
837
838 } else {
839
840 /* Fall back to returning the donation receipt URL. */
841 $redirect_url = charitable_get_permalink( 'donation_receipt_page', array( 'donation_id' => $this->donation_id ) );
842
843 }
844
845 return $redirect_url;
846 }
847
848 /**
849 * Validate user data passed to insert.
850 *
851 * @since 1.0.0
852 *
853 * @return boolean
854 */
855 protected function is_valid_user_data() {
856 if ( charitable_permit_donor_without_email() ) {
857 return true;
858 }
859
860 $ret = $this->get_donation_data_value( 'user_id' ) || $this->get_donation_data_value( 'donor_id' );
861
862 if ( ! $ret ) {
863 $user = $this->get_donation_data_value( 'user', array() );
864 $ret = array_key_exists( 'email', $user ) && strlen( $user['email'] );
865 }
866
867 return $ret;
868 }
869
870 /**
871 * Parse the donation data, based on the passed $values array.
872 *
873 * @since 1.0.0
874 *
875 * @return array
876 */
877 protected function parse_donation_data() {
878 $core_values = array(
879 'ID' => $this->get_donation_data_value( 'ID', 0 ),
880 'post_type' => Charitable::DONATION_POST_TYPE,
881 'post_author' => $this->get_donation_data_value( 'user_id', get_current_user_id() ),
882 'post_status' => $this->get_donation_status(),
883 'post_content' => $this->get_donation_data_value( 'note', '' ),
884 'post_parent' => $this->get_donation_data_value( 'donation_plan', 0 ),
885 'post_date_gmt' => $this->get_donation_data_value( 'date_gmt', current_time( 'mysql', true ) ),
886 'post_title' => sprintf( '%s &ndash; %s', $this->get_donor_name(), $this->get_campaign_names() ),
887 );
888
889 $core_values['post_date'] = get_date_from_gmt( $core_values['post_date_gmt'] );
890
891 return apply_filters( 'charitable_donation_values_core', $core_values, $this );
892 }
893
894 /**
895 * Set a unique key for the donation.
896 *
897 * @since 1.0.0
898 *
899 * @return void
900 */
901 protected function set_donation_key() {
902 if ( array_key_exists( 'donation_key', $this->donation_data ) ) {
903 return;
904 }
905
906 $this->donation_data['donation_key'] = strtolower( md5( uniqid() ) );
907 }
908
909 /**
910 * Checks whether the given gateway has been updated for compatibility with 1.3.
911 *
912 * @since 1.3.0
913 *
914 * @param string $gateway The gateway for the donation.
915 * @return boolean
916 */
917 private function gateway_is_130_compatible( $gateway ) {
918 return Charitable_Gateways::get_instance()->get_gateway_object( $gateway )->supports( '1.3.0' );
919 }
920
921 /**
922 * Return the IPN url.
923 *
924 * IPNs in Charitable are structured in this way: charitable-listener=gateway
925 *
926 * @deprecated
927 *
928 * @since 1.0.0
929 *
930 * @param string $gateway The gateway for the donation.
931 * @return string
932 */
933 public function get_ipn_url( $gateway ) {
934 charitable_get_deprecated()->deprecated_function( __METHOD__, '1.4.0', 'charitable_get_ipn_url()' );
935
936 return charitable_get_ipn_url( $gateway );
937 }
938 }
939
940 endif;
941