PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.7.0.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.7.0.3
1.8.13 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 All 211 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.7.0.3, at includes/donations/class-charitable-donation-processor.php

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