PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.18.0
GiveWP – Donation Plugin and Fundraising Platform v4.18.0
4.18.0 4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 All 257 releases
give / includes / gateways / stripe / includes / class-give-stripe-gateway.php

class-give-stripe-gateway.php in GiveWP – Donation Plugin and Fundraising Platform 4.18.0, at includes/gateways/stripe/includes/class-give-stripe-gateway.php

723 lines 18.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Give - Stripe Core Gateway
4 *
5 * @since 2.5.0
6 *
7 * @package Give
8 * @subpackage Stripe Core
9 * @copyright Copyright (c) 2019, GiveWP
10 * @license https://opensource.org/licenses/gpl-license GNU Public License
11 */
12
13 // Exit, if accessed directly.
14 use Give\PaymentGateways\Stripe\ApplicationFee;
15 use Give\ValueObjects\Money;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * Check for class Give_Stripe_Gateway exists.
23 *
24 * @since 2.5.0
25 */
26 if ( ! class_exists( 'Give_Stripe_Gateway' ) ) {
27
28 class Give_Stripe_Gateway {
29
30 /**
31 * Default Gateway ID.
32 *
33 * @since 2.5.0
34 * @access public
35 *
36 * @var string
37 */
38 public $id;
39
40 /**
41 * Set Latest Stripe Version.
42 *
43 * @since 2.5.0
44 * @access public
45 *
46 * @var string
47 */
48 public $api_version = '2019-05-16';
49
50 /**
51 * Payment Intent.
52 *
53 * @since 2.5.0
54 * @access public
55 *
56 * @var \Stripe\PaymentIntent
57 */
58 public $payment_intent;
59
60 /**
61 * Stripe Payment Method Object.
62 *
63 * @since 2.5.0
64 * @access public
65 *
66 * @var Give_Stripe_Payment_Method
67 */
68 public $payment_method;
69
70 /**
71 * Error Messages.
72 *
73 * @since 2.7.0
74 * @access public
75 *
76 * @var array $errorMessages List of error messages.
77 */
78 public $errorMessages = [];
79
80 /**
81 * Give_Stripe_Gateway constructor.
82 *
83 * @since 2.5.0
84 * @access public
85 *
86 * @return bool|void
87 */
88 public function __construct() {
89
90 // Set API Version.
91 $this->set_api_version();
92
93 // Call Payment Intent Class to utilize.
94 $this->payment_intent = new Give_Stripe_Payment_Intent();
95 $this->payment_method = new Give_Stripe_Payment_Method();
96
97 add_action( "give_gateway_{$this->id}", [ $this, 'process_payment' ] );
98
99 }
100
101 /**
102 * This function is used to determine whether to show the payment fields or not.
103 *
104 * @since 2.7.0
105 * @access public
106 *
107 * @return bool
108 */
109 public function canShowFields() {
110
111 $status = true;
112 $isConfigured = Give\Helpers\Gateways\Stripe::isAccountConfigured();
113 $isTestMode = give_is_test_mode();
114 $isSslActive = is_ssl();
115
116 if ( ! $isConfigured && ! $isSslActive && ! $isTestMode ) {
117 // Account not configured, No SSL scenario.
118 Give_Notices::print_frontend_notice(
119 sprintf(
120 '<strong>%1$s</strong> %2$s',
121 esc_html__( 'Notice:', 'give' ),
122 $this->errorMessages['accountNotConfiguredNoSsl']
123 )
124 );
125 $status = false;
126
127 } elseif ( ! $isConfigured ) {
128 // Account not configured scenario.
129 Give_Notices::print_frontend_notice(
130 sprintf(
131 '<strong>%1$s</strong> %2$s',
132 esc_html__( 'Notice:', 'give' ),
133 $this->errorMessages['accountNotConfigured']
134 )
135 );
136 $status = false;
137
138 } elseif ( ! $isTestMode && ! $isSslActive ) {
139 // Account configured, No SSL scenario.
140 Give_Notices::print_frontend_notice(
141 sprintf(
142 '<strong>%1$s</strong> %2$s',
143 esc_html__( 'Notice:', 'give' ),
144 $this->errorMessages['accountConfiguredNoSsl']
145 )
146 );
147 $status = false;
148 }
149
150 return $status;
151 }
152
153 /**
154 * This function will help to set the latest Stripe API version.
155 *
156 * @since 2.5.0
157 * @access public
158 *
159 * @return void
160 */
161 public function set_api_version() {
162
163 // Set Application Info.
164 give_stripe_set_app_info();
165
166 try {
167
168 // Set API Version to latest.
169 \Stripe\Stripe::setApiVersion( $this->api_version );
170
171 } catch ( \Stripe\Error\Base $e ) {
172
173 // Log Error.
174 $this->log_error( $e );
175
176 } catch ( Exception $e ) {
177
178 // Something went wrong outside of Stripe.
179 give_record_gateway_error(
180 __( 'Stripe Error', 'give' ),
181 sprintf(
182 /* translators: %s Exception Message Body */
183 __( 'Unable to set Stripe API Version. Details: %s', 'give' ),
184 $e->getMessage()
185 )
186 );
187 give_set_error( 'stripe_error', __( 'An error occurred while processing the donation. Please try again.', 'give' ) );
188
189 // Send donor back to checkout page on error.
190 $this->send_back_to_checkout();
191 }
192 }
193
194 /**
195 * Send back to checkout based on the gateway id.
196 *
197 * @since 2.5.0
198 * @access public
199 *
200 * @return void
201 */
202 public function send_back_to_checkout() {
203 give_send_back_to_checkout( '?payment-mode=' . $this->id );
204 }
205
206 /**
207 * This function will be used to fetch token details from token id.
208 *
209 * @param string $id Stripe Token ID.
210 * @param array $args Additional arguments.
211 *
212 * @since 2.5.0
213 * @access public
214 *
215 * @return \Stripe\Token
216 */
217 public function get_token_details( $id, $args = [] ) {
218
219 // Set Application Info.
220 give_stripe_set_app_info();
221
222 try {
223
224 $args = wp_parse_args( $args, give_stripe_get_connected_account_options() );
225
226 // Retrieve Token Object.
227 return \Stripe\Token::retrieve( $id, $args );
228
229 } catch ( \Stripe\Error\Base $e ) {
230 $this->log_error( $e );
231 } catch ( Exception $e ) {
232
233 // Something went wrong outside of Stripe.
234 give_record_gateway_error(
235 __( 'Stripe Token Error', 'give' ),
236 sprintf(
237 /* translators: %s Exception Message Body */
238 __( 'Unable to retrieve token. Details: %s', 'give' ),
239 $e->getMessage()
240 )
241 );
242 give_set_error( 'stripe_error', __( 'An error occurred while processing the donation. Please try again.', 'give' ) );
243
244 // Send donor back to checkout page on error.
245 $this->send_back_to_checkout();
246 }
247 }
248
249 /**
250 * This function will be used to fetch source details from source id.
251 *
252 * @param string $id Stripe Source ID.
253 *
254 * @since 2.5.0
255 * @access public
256 *
257 * @return \Stripe\Source
258 */
259 public function get_source_details( $id ) {
260
261 // Set Application Info.
262 give_stripe_set_app_info();
263
264 try {
265
266 // Retrieve Source Object.
267 return \Stripe\Source::retrieve( $id, give_stripe_get_connected_account_options() );
268
269 } catch ( \Stripe\Error\Base $e ) {
270 $this->log_error( $e );
271 } catch ( Exception $e ) {
272
273 // Something went wrong outside of Stripe.
274 give_record_gateway_error(
275 __( 'Stripe Source Error', 'give' ),
276 sprintf(
277 /* translators: %s Exception Message Body */
278 __( 'Unable to retrieve source. Details: %s', 'give' ),
279 $e->getMessage()
280 )
281 );
282 give_set_error( 'stripe_error', __( 'An error occurred while processing the donation. Please try again.', 'give' ) );
283
284 // Send donor back to checkout page on error.
285 $this->send_back_to_checkout();
286 }
287 }
288
289 /**
290 * This function will prepare source based on the parameters provided.
291 *
292 * @param array $args List of arguments \Stripe\Source::create() supports.
293 *
294 * @since 2.5.0
295 * @access public
296 *
297 * @return \Stripe\Source
298 */
299 public function prepare_source( $args ) {
300
301 // Set Application Info.
302 give_stripe_set_app_info();
303
304 try {
305
306 // Create Source Object.
307 return \Stripe\Source::create( $args, give_stripe_get_connected_account_options() );
308
309 } catch ( \Stripe\Error\Base $e ) {
310 $this->log_error( $e );
311 } catch ( Exception $e ) {
312
313 // Something went wrong outside of Stripe.
314 give_record_gateway_error(
315 __( 'Stripe Error', 'give' ),
316 sprintf(
317 /* translators: %s Exception Message Body */
318 __( 'Unable to create source. Details: %s', 'give' ),
319 $e->getMessage()
320 )
321 );
322 give_set_error( 'stripe_error', __( 'An error occurred while processing the donation. Please try again.', 'give' ) );
323
324 // Send donor back to checkout page on error.
325 $this->send_back_to_checkout();
326 }
327 }
328
329 /**
330 * Get Customer's card.
331 *
332 * @param \Stripe\Customer $stripe_customer Stripe Customer Object.
333 * @param string $id Source or Token ID.
334 *
335 * @since 2.5.0
336 *
337 * @return \Stripe\Source|bool
338 */
339 public function get_customer_card( $stripe_customer, $id ) {
340
341 $card_exists = false;
342 $all_sources = $stripe_customer->sources->all();
343 $card = $this->get_source_details( $id );
344 $source_list = wp_list_pluck( $all_sources->data, 'id' );
345
346 // Check whether the source is already attached to customer or not.
347 if ( in_array( $id, $source_list, true ) ) {
348 $card_exists = true;
349 }
350
351 // Create the card if none found above.
352 if ( ! $card_exists ) {
353 try {
354
355 // Attach Source to existing Customer.
356 $card = $stripe_customer->sources->create(
357 [
358 'source' => $id,
359 ]
360 );
361
362 } catch ( \Stripe\Error\Base $e ) {
363
364 // Log Error.
365 $this->log_error( $e );
366
367 } catch ( Exception $e ) {
368
369 give_record_gateway_error(
370 __( 'Stripe Card Error', 'give' ),
371 sprintf(
372 /* translators: %s Exception Error Message */
373 __( 'The Stripe Gateway returned an error while processing a donation. Details: %s', 'give' ),
374 $e->getMessage()
375 )
376 );
377
378 // Send donor back to checkout page on error.
379 $this->send_back_to_checkout();
380 }
381 }
382
383 // Return Card Details, if exists.
384 if ( ! empty( $card->id ) ) {
385 return $card;
386 } else {
387
388 give_set_error( 'stripe_error', __( 'An error occurred while processing the donation. Please try again.', 'give' ) );
389 give_record_gateway_error( __( 'Stripe Error', 'give' ), __( 'An error occurred retrieving or creating the ', 'give' ) );
390
391 // Send donor back to checkout page on error.
392 $this->send_back_to_checkout();
393
394 return false;
395 }
396 }
397
398 /**
399 * Save Stripe Customer ID.
400 *
401 * @param string $stripe_customer_id Customer ID.
402 * @param int $payment_id Payment ID.
403 *
404 * @since 2.5.0
405 */
406 public function save_stripe_customer_id( $stripe_customer_id, $payment_id ) {
407
408 // Update customer meta.
409 if ( class_exists( 'Give_DB_Donor_Meta' ) ) {
410
411 $donor_id = give_get_payment_donor_id( $payment_id );
412
413 // Get the Give donor.
414 $donor = new Give_Donor( $donor_id );
415
416 // Update donor meta.
417 $donor->update_meta( give_stripe_get_customer_key(), $stripe_customer_id );
418
419 } elseif ( is_user_logged_in() ) {
420
421 // Support saving to legacy method of user method.
422 update_user_meta( get_current_user_id(), give_stripe_get_customer_key(), $stripe_customer_id );
423
424 }
425
426 }
427
428 /**
429 * Log a Stripe Error.
430 *
431 * Logs in the Give db the error and also displays the error message to the donor.
432 *
433 * @param \Stripe\Error\Base|\Stripe\Error\Card $exception Exception.
434 *
435 * @since 2.5.0
436 *
437 * @return bool
438 */
439 public function log_error( $exception ) {
440
441 $log_message = __( 'The Stripe payment gateway returned an error while processing the donation.', 'give' ) . '<br><br>';
442 $exception_message = $exception->getMessage();
443
444 // Bad Request of some sort.
445 if ( ! empty( $exception_message ) ) {
446 $log_message .= sprintf(
447 /* translators: %s Exception Message */
448 __( 'Message: %s', 'give' ),
449 $exception_message
450 ) . '<br><br>';
451
452 $trace_string = $exception->getTraceAsString();
453
454 if ( ! empty( $trace_string ) ) {
455 $log_message .= sprintf(
456 /* translators: %s Trace String */
457 __( 'Code: %s', 'give' ),
458 $trace_string
459 );
460 }
461
462 give_set_error( 'stripe_request_error', $exception_message );
463 } else {
464 give_set_error( 'stripe_request_error', __( 'The Stripe API request was invalid, please try again.', 'give' ) );
465 }
466
467 // Log it with DB.
468 give_record_gateway_error( __( 'Stripe Error', 'give' ), $log_message );
469
470 // Send donor back to checkout page on error.
471 $this->send_back_to_checkout();
472
473 return false;
474
475 }
476
477 /**
478 * Format currency for Stripe.
479 *
480 * @see https://support.stripe.com/questions/which-zero-decimal-currencies-does-stripe-support
481 *
482 * @param float $amount Donation amount.
483 *
484 * @return mixed
485 */
486 public function format_amount( $amount ) {
487 return Money::of( $amount, give_get_currency() )->getMinorAmount();
488 }
489
490 /**
491 * Verify Payment.
492 *
493 * @param int $payment_id Payment ID.
494 * @param string $stripe_customer_id Customer ID.
495 * @param \Stripe\Charge $charge Stripe Charge Object.
496 */
497 public function verify_payment( $payment_id, $stripe_customer_id, $charge ) {
498
499 // Sanity checks: verify all vars exist.
500 if ( $payment_id && ( ! empty( $stripe_customer_id ) || ! empty( $charge ) ) ) {
501
502 /**
503 * This action hook is used to perform some additional steps to verify the payment.
504 *
505 * @param int $payment_id Payment ID.
506 * @param string $stripe_customer_id Customer ID.
507 * @param \Stripe\Charge $charge Stripe Charge Object.
508 *
509 * @since 2.5.0
510 */
511 do_action( 'give_stripe_verify_payment', $payment_id, $stripe_customer_id, $charge );
512
513 // @TODO use Stripe's API here to retrieve the invoice then confirm it has been paid.
514 // Regular payment, publish it.
515 give_update_payment_status( $payment_id, 'publish' );
516
517 // Save Stripe customer id.
518 $this->save_stripe_customer_id( $stripe_customer_id, $payment_id );
519
520 // Send them to success page.
521 give_send_to_success_page();
522
523 } else {
524
525 give_set_error( 'payment_not_recorded', __( 'Your donation could not be recorded, please contact the site administrator.', 'give' ) );
526
527 // If errors are present, send the user back to the purchase page so they can be corrected.
528 $this->send_back_to_checkout();
529
530 } // End if().
531 }
532
533 /**
534 * This function will prepare metadata to send to Stripe.
535 *
536 * @param int $donation_id Donation ID.
537 * @param array $donation_data Donation Data.
538 *
539 * @since 2.5.0
540 * @access public
541 *
542 * @return array
543 */
544 public function prepare_metadata( $donation_id, $donation_data = [] ) {
545 return give_stripe_prepare_metadata( $donation_id, $donation_data );
546 }
547
548 /**
549 * This function will help to charge with Stripe.
550 *
551 * @param int $donation_id Donation ID with pending status.
552 * @param array $charge_args List of charge arguments.
553 *
554 * @since 2.5.0
555 * @access public
556 *
557 * @return \Stripe\Charge
558 */
559 public function create_charge( $donation_id, $charge_args ) {
560
561 // Set App Info to Stripe.
562 give_stripe_set_app_info();
563
564 try {
565
566 $charge_args = apply_filters( "give_{$this->id}_create_charge_args", $charge_args );
567
568 // Charge application fee, only if the Stripe premium add-on is not active.
569 if ( ApplicationFee::canAddfee() ) {
570 // Set Application Fee Amount.
571 $charge_args['application_fee_amount'] = give_stripe_get_application_fee_amount( $charge_args['amount'] );
572 }
573
574 $charge = \Stripe\Charge::create(
575 $charge_args,
576 give_stripe_get_connected_account_options()
577 );
578
579 // Add note for the charge.
580 // Save Stripe's charge ID to the transaction.
581 if ( ! empty( $charge ) ) {
582 give_insert_payment_note( $donation_id, 'Stripe Charge ID: ' . $charge->id );
583 give_set_payment_transaction_id( $donation_id, $charge->id );
584 }
585
586 return $charge;
587
588 } catch ( \Stripe\Error\Base $e ) {
589 Give_Stripe_Logger::log_error( $e, $this->id );
590 } catch ( Exception $e ) {
591
592 give_record_gateway_error(
593 __( 'Stripe Charge Error', 'give' ),
594 sprintf(
595 /* translators: %s Exception Error Message */
596 __( 'Unable to create a successful charge. Details: %s', 'give' ),
597 $e
598 )
599 );
600 give_set_error( 'stripe_charge_error', __( 'Error processing donation with Stripe. Please try again.', 'give' ) );
601 return false;
602 } // End try().
603 }
604
605 /**
606 * Create Source for Stripe 3D Secure Payments.
607 *
608 * @param int $donation_id Donation ID.
609 * @param int $source_id Source ID/Object.
610 *
611 * @since 1.6
612 * @access public
613 *
614 * @return bool|\Stripe\Source
615 */
616 public function create_3d_secure_source( $donation_id, $source_id ) {
617 $form_id = give_get_payment_form_id( $donation_id );
618 $customer_id = give_get_payment_meta( $donation_id, '_give_stripe_customer_id', true );
619 $donation_amount = give_donation_amount( $donation_id );
620
621 // Prepare basic source args.
622 $source_args = [
623 'amount' => $this->format_amount( $donation_amount ),
624 'currency' => give_get_currency( $form_id ),
625 'type' => 'three_d_secure',
626 'three_d_secure' => [
627 'card' => $source_id,
628 ],
629 'statement_descriptor' => give_stripe_get_statement_descriptor(),
630 'redirect' => [
631 'return_url' => esc_url_raw( add_query_arg(
632 [
633 'give-listener' => 'stripe_three_d_secure',
634 'donation_id' => $donation_id,
635 ],
636 give_get_success_page_uri()
637 ) ),
638 ],
639 ];
640
641 $source = $this->prepare_source( $source_args );
642
643 // Add donation note for 3D secure source ID.
644 if ( ! empty( $source->id ) ) {
645 give_insert_payment_note( $donation_id, 'Stripe 3D Secure Source ID: ' . $source->id );
646 }
647
648 // Save 3D secure source id to donation.
649 give_update_payment_meta( $donation_id, '_give_stripe_3dsecure_source_id', $source->id );
650
651 return $source;
652 }
653
654 /**
655 * Is 3D secure payment required?
656 *
657 * @param \Stripe\Source $source_object Stripe Source Object.
658 *
659 * @since 1.6
660 * @access public
661 *
662 * @return bool
663 */
664 public function is_3d_secure_required( $source_object ) {
665
666 $is_3d_secure_enabled = give_is_setting_enabled( give_get_option( 'stripe_enable_three_d_secure_payments', '' ) );
667
668 if ( $is_3d_secure_enabled ) {
669 return apply_filters(
670 'give_stripe_3d_secure_required',
671 (
672 ! empty( $source_object->type ) &&
673 'card' === $source_object->type &&
674 'required' === $source_object->card->three_d_secure
675 ),
676 $source_object
677 );
678 }
679
680 return false;
681 }
682
683 /**
684 * Process One Time Charge.
685 *
686 * @param array $donation_data List of donation data.
687 * @param string $stripe_customer_id Customer ID.
688 *
689 * @return bool|\Stripe\Charge
690 */
691 public function process_charge( $donation_data, $stripe_customer_id ) {
692
693 $form_id = ! empty( $donation_data['post_data']['give-form-id'] ) ? intval( $donation_data['post_data']['give-form-id'] ) : 0;
694 $donation_id = ! empty( $donation_data['donation_id'] ) ? intval( $donation_data['donation_id'] ) : 0;
695 $source_id = ! empty( $donation_data['source_id'] ) ? $donation_data['source_id'] : 0;
696 $description = ! empty( $donation_data['description'] ) ? $donation_data['description'] : false;
697
698 // Process the charge.
699 $amount = $this->format_amount( $donation_data['price'] );
700
701 $charge_args = [
702 'amount' => $amount,
703 'currency' => give_get_currency( $form_id ),
704 'customer' => $stripe_customer_id,
705 'description' => html_entity_decode( $description, ENT_COMPAT, 'UTF-8' ),
706 'statement_descriptor' => give_stripe_get_statement_descriptor( $donation_data ),
707 'metadata' => $this->prepare_metadata( $donation_id ),
708 'source' => $source_id,
709 ];
710
711 // Create charge with general gateway fn.
712 $charge = $this->create_charge( $donation_id, $charge_args );
713
714 // Return charge if set.
715 if ( isset( $charge ) ) {
716 return $charge;
717 } else {
718 return false;
719 }
720 }
721 }
722 }
723