Completed
Push — master ( b352f5...4ff6e2 )
by Roy
02:36
created

includes/legacy/class-wc-gateway-stripe.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * WC_Gateway_Stripe class.
8
 *
9
 * @extends WC_Payment_Gateway
10
 */
11
class WC_Gateway_Stripe extends WC_Payment_Gateway {
12
13
	/**
14
	 * Constructor
15
	 */
16
	public function __construct() {
17
		$this->id                   = 'stripe';
18
		$this->method_title         = __( 'Stripe', 'woocommerce-gateway-stripe' );
19
		$this->method_description   = __( 'Stripe works by adding credit card fields on the checkout and then sending the details to Stripe for verification.', 'woocommerce-gateway-stripe' );
20
		$this->has_fields           = true;
21
		$this->view_transaction_url = 'https://dashboard.stripe.com/payments/%s';
22
		$this->supports             = array(
23
			'subscriptions',
24
			'products',
25
			'refunds',
26
			'subscription_cancellation',
27
			'subscription_reactivation',
28
			'subscription_suspension',
29
			'subscription_amount_changes',
30
			'subscription_payment_method_change', // Subs 1.n compatibility
31
			'subscription_payment_method_change_customer',
32
			'subscription_payment_method_change_admin',
33
			'subscription_date_changes',
34
			'multiple_subscriptions',
35
			'pre-orders',
36
		);
37
38
		// Load the form fields
39
		$this->init_form_fields();
40
41
		// Load the settings.
42
		$this->init_settings();
43
44
		// Get setting values.
45
		$this->title                  = $this->get_option( 'title' );
46
		$this->description            = $this->get_option( 'description' );
47
		$this->enabled                = $this->get_option( 'enabled' );
48
		$this->testmode               = 'yes' === $this->get_option( 'testmode' );
49
		$this->capture                = 'yes' === $this->get_option( 'capture', 'yes' );
50
		$this->stripe_checkout        = 'yes' === $this->get_option( 'stripe_checkout' );
51
		$this->stripe_checkout_locale = $this->get_option( 'stripe_checkout_locale' );
52
		$this->stripe_checkout_image  = $this->get_option( 'stripe_checkout_image', '' );
53
		$this->saved_cards            = 'yes' === $this->get_option( 'saved_cards' );
54
		$this->secret_key             = $this->testmode ? $this->get_option( 'test_secret_key' ) : $this->get_option( 'secret_key' );
55
		$this->publishable_key        = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
56
		$this->bitcoin                = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' );
57
		$this->logging                = 'yes' === $this->get_option( 'logging' );
58
59
		if ( $this->stripe_checkout ) {
60
			$this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' );
61
		}
62
63
		if ( $this->testmode ) {
64
			$this->description .= ' ' . sprintf( __( 'TEST MODE ENABLED. In test mode, you can use the card number 4242424242424242 with any CVC and a valid expiration date or check the documentation "<a href="%s">Testing Stripe</a>" for more card numbers.', 'woocommerce-gateway-stripe' ), 'https://stripe.com/docs/testing' );
65
			$this->description  = trim( $this->description );
66
		}
67
68
		WC_Stripe_API::set_secret_key( $this->secret_key );
69
70
		// Hooks
71
		add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
72
		add_action( 'admin_notices', array( $this, 'admin_notices' ) );
73
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
74
	}
75
76
	/**
77
	 * get_icon function.
78
	 *
79
	 * @access public
80
	 * @return string
81
	 */
82 View Code Duplication
	public function get_icon() {
83
		$ext   = version_compare( WC()->version, '2.6', '>=' ) ? '.svg' : '.png';
84
		$style = version_compare( WC()->version, '2.6', '>=' ) ? 'style="margin-left: 0.3em"' : '';
85
86
		$icon  = '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/visa' . $ext ) . '" alt="Visa" width="32" ' . $style . ' />';
87
		$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/mastercard' . $ext ) . '" alt="Mastercard" width="32" ' . $style . ' />';
88
		$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/amex' . $ext ) . '" alt="Amex" width="32" ' . $style . ' />';
89
90
		if ( 'USD' === get_woocommerce_currency() ) {
91
			$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/discover' . $ext ) . '" alt="Discover" width="32" ' . $style . ' />';
92
			$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/jcb' . $ext ) . '" alt="JCB" width="32" ' . $style . ' />';
93
			$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/diners' . $ext ) . '" alt="Diners" width="32" ' . $style . ' />';
94
		}
95
96
		if ( $this->bitcoin ) {
97
			$icon .= '<img src="' . WC_HTTPS::force_https_url( plugins_url( '/assets/images/bitcoin' . $ext, WC_STRIPE_MAIN_FILE ) ) . '" alt="Bitcoin" width="32" ' . $style . ' />';
98
		}
99
100
		return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id );
101
	}
102
103
	/**
104
	 * Get Stripe amount to pay
105
	 * @return float
106
	 */
107 View Code Duplication
	public function get_stripe_amount( $total, $currency = '' ) {
108
		if ( ! $currency ) {
109
			$currency = get_woocommerce_currency();
110
		}
111
		switch ( strtoupper( $currency ) ) {
112
			// Zero decimal currencies
113
			case 'BIF' :
114
			case 'CLP' :
115
			case 'DJF' :
116
			case 'GNF' :
117
			case 'JPY' :
118
			case 'KMF' :
119
			case 'KRW' :
120
			case 'MGA' :
121
			case 'PYG' :
122
			case 'RWF' :
123
			case 'VND' :
124
			case 'VUV' :
125
			case 'XAF' :
126
			case 'XOF' :
127
			case 'XPF' :
128
				$total = absint( $total );
129
				break;
130
			default :
131
				$total = round( $total, 2 ) * 100; // In cents
132
				break;
133
		}
134
		return $total;
135
	}
136
137
	/**
138
	 * Check if SSL is enabled and notify the user
139
	 */
140
	public function admin_notices() {
141
		if ( $this->enabled == 'no' ) {
142
			return;
143
		}
144
145
		$addons = ( class_exists( 'WC_Subscriptions_Order' ) || class_exists( 'WC_Pre_Orders_Order' ) ) ? '_addons' : '';
146
147
		// Check required fields
148
		if ( ! $this->secret_key ) {
149
			echo '<div class="error"><p>' . sprintf( __( 'Stripe error: Please enter your secret key <a href="%s">here</a>', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout&section=wc_gateway_stripe' . $addons ) ) . '</p></div>';
150
			return;
151
152 View Code Duplication
		} elseif ( ! $this->publishable_key ) {
153
			echo '<div class="error"><p>' . sprintf( __( 'Stripe error: Please enter your publishable key <a href="%s">here</a>', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout&section=wc_gateway_stripe' . $addons ) ) . '</p></div>';
154
			return;
155
		}
156
157
		// Simple check for duplicate keys
158 View Code Duplication
		if ( $this->secret_key == $this->publishable_key ) {
159
			echo '<div class="error"><p>' . sprintf( __( 'Stripe error: Your secret and publishable keys match. Please check and re-enter.', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout&section=wc_gateway_stripe' . $addons ) ) . '</p></div>';
160
			return;
161
		}
162
163
		// Show message if enabled and FORCE SSL is disabled and WordpressHTTPS plugin is not detected
164 View Code Duplication
		if ( ( function_exists( 'wc_site_is_https' ) && ! wc_site_is_https() ) && ( 'no' === get_option( 'woocommerce_force_ssl_checkout' ) && ! class_exists( 'WordPressHTTPS' ) ) ) {
165
			echo '<div class="error"><p>' . sprintf( __( 'Stripe is enabled, but the <a href="%s">force SSL option</a> is disabled; your checkout may not be secure! Please enable SSL and ensure your server has a valid SSL certificate - Stripe will only work in test mode.', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout' ) ) . '</p></div>';
166
		}
167
	}
168
169
	/**
170
	 * Check if this gateway is enabled
171
	 */
172 View Code Duplication
	public function is_available() {
173
		if ( 'yes' === $this->enabled ) {
174
			if ( ! $this->testmode && is_checkout() && ! is_ssl() ) {
175
				return false;
176
			}
177
			if ( ! $this->secret_key || ! $this->publishable_key ) {
178
				return false;
179
			}
180
			return true;
181
		}
182
		return false;
183
	}
184
185
	/**
186
	 * Initialise Gateway Settings Form Fields
187
	 */
188
	public function init_form_fields() {
189
		$this->form_fields = include( untrailingslashit( plugin_dir_path( WC_STRIPE_MAIN_FILE ) ) . '/includes/settings-stripe.php' );
190
191
		wc_enqueue_js( "
192
			jQuery( function( $ ) {
193
				$( '#woocommerce_stripe_stripe_checkout' ).change(function(){
194
					if ( $( this ).is( ':checked' ) ) {
195
						$( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).show();
196
					} else {
197
						$( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).hide();
198
					}
199
				}).change();
200
			});
201
		" );
202
	}
203
204
	/**
205
	 * Payment form on checkout page
206
	 */
207
	public function payment_fields() {
208
		?>
209
		<fieldset class="stripe-legacy-payment-fields">
210
			<?php
211
				if ( $this->description ) {
212
					echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $this->description ) ) );
213
				}
214
				if ( $this->saved_cards && is_user_logged_in() ) {
215
					$stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
216
					?>
217
					<p class="form-row form-row-wide">
218
						<a class="<?php echo apply_filters( 'wc_stripe_manage_saved_cards_class', 'button' ); ?>" style="float:right;" href="<?php echo apply_filters( 'wc_stripe_manage_saved_cards_url', get_permalink( get_option( 'woocommerce_myaccount_page_id' ) ) ); ?>#saved-cards"><?php esc_html_e( 'Manage cards', 'woocommerce-gateway-stripe' ); ?></a>
219
						<?php
220
						if ( $cards = $stripe_customer->get_cards() ) {
221
							$default_card = $cards[0]->id;
222
							foreach ( (array) $cards as $card ) {
223
								if ( 'card' !== $card->object ) {
224
									continue;
225
								}
226
								?>
227
								<label for="stripe_card_<?php echo $card->id; ?>" class="brand-<?php echo esc_attr( strtolower( $card->brand ) ); ?>">
228
									<input type="radio" id="stripe_card_<?php echo $card->id; ?>" name="wc-stripe-payment-token" value="<?php echo $card->id; ?>" <?php checked( $default_card, $card->id ) ?> />
229
									<?php printf( __( '%s card ending in %s (Expires %s/%s)', 'woocommerce-gateway-stripe' ), $card->brand, $card->last4, $card->exp_month, $card->exp_year ); ?>
230
								</label>
231
								<?php
232
							}
233
						}
234
						?>
235
						<label for="new">
236
							<input type="radio" id="new" name="wc-stripe-payment-token" value="new" />
237
							<?php _e( 'Use a new credit card', 'woocommerce-gateway-stripe' ); ?>
238
						</label>
239
					</p>
240
					<?php
241
				}
242
243
				$user = wp_get_current_user();
244
245 View Code Duplication
				if ( $user ) {
246
					$user_email = get_user_meta( $user->ID, 'billing_email', true );
247
					$user_email = $user_email ? $user_email : $user->user_email;
248
				} else {
249
					$user_email = '';
250
				}
251
252
				$display = '';
253
254
				if ( $this->stripe_checkout || $this->saved_cards && ! empty( $cards ) ) {
255
					$display = 'style="display:none;"';
256
				}
257
258
				echo '<div ' . $display . ' id="stripe-payment-data"
259
					data-description=""
260
					data-email="' . esc_attr( $user_email ) . '"
261
					data-amount="' . esc_attr( $this->get_stripe_amount( WC()->cart->total ) ) . '"
262
					data-name="' . esc_attr( get_bloginfo( 'name', 'display' ) ) . '"
263
					data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
264
					data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
265
					data-bitcoin="' . esc_attr( $this->bitcoin ? 'true' : 'false' ) . '"
266
					data-locale="' . esc_attr( $this->stripe_checkout_locale ? $this->stripe_checkout_locale : 'en' ) . '">';
267
268
				if ( ! $this->stripe_checkout ) {
269
					$this->credit_card_form( array( 'fields_have_names' => false ) );
270
				}
271
272
				echo '</div>';
273
			?>
274
		</fieldset>
275
		<?php
276
	}
277
278
	/**
279
	 * payment_scripts function.
280
	 *
281
	 * Outputs scripts used for stripe payment
282
	 *
283
	 * @access public
284
	 */
285
	public function payment_scripts() {
286
		if ( $this->stripe_checkout ) {
287
			wp_enqueue_script( 'stripe', 'https://checkout.stripe.com/v2/checkout.js', '', '2.0', true );
288
			wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe_checkout.js', WC_STRIPE_MAIN_FILE ), array( 'stripe' ), WC_STRIPE_VERSION, true );
289 View Code Duplication
		} else {
290
			wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
291
			wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe.js', WC_STRIPE_MAIN_FILE ), array( 'jquery-payment', 'stripe' ), WC_STRIPE_VERSION, true );
292
		}
293
294
		$stripe_params = array(
295
			'key'                  => $this->publishable_key,
296
			'i18n_terms'           => __( 'Please accept the terms and conditions first', 'woocommerce-gateway-stripe' ),
297
			'i18n_required_fields' => __( 'Please fill in required checkout fields first', 'woocommerce-gateway-stripe' ),
298
		);
299
300
		// If we're on the pay page we need to pass stripe.js the address of the order.
301
		if ( is_checkout_pay_page() && isset( $_GET['order'] ) && isset( $_GET['order_id'] ) ) {
302
			$order_key = urldecode( $_GET['order'] );
303
			$order_id  = absint( $_GET['order_id'] );
304
			$order     = wc_get_order( $order_id );
305
306
			if ( $order->id === $order_id && $order->order_key === $order_key ) {
307
				$stripe_params['billing_first_name'] = $order->billing_first_name;
308
				$stripe_params['billing_last_name']  = $order->billing_last_name;
309
				$stripe_params['billing_address_1']  = $order->billing_address_1;
310
				$stripe_params['billing_address_2']  = $order->billing_address_2;
311
				$stripe_params['billing_state']      = $order->billing_state;
312
				$stripe_params['billing_city']       = $order->billing_city;
313
				$stripe_params['billing_postcode']   = $order->billing_postcode;
314
				$stripe_params['billing_country']    = $order->billing_country;
315
			}
316
		}
317
318
		wp_localize_script( 'woocommerce_stripe', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
319
	}
320
321
	/**
322
	 * Generate the request for the payment.
323
	 * @param  WC_Order $order
324
	 * @param  object $source
325
	 * @return array()
326
	 */
327 View Code Duplication
	protected function generate_payment_request( $order, $source ) {
328
		$post_data                = array();
329
		$post_data['currency']    = strtolower( $order->get_order_currency() ? $order->get_order_currency() : get_woocommerce_currency() );
330
		$post_data['amount']      = $this->get_stripe_amount( $order->get_total(), $post_data['currency'] );
331
		$post_data['description'] = sprintf( __( '%s - Order %s', 'woocommerce-gateway-stripe' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), $order->get_order_number() );
332
		$post_data['capture']     = $this->capture ? 'true' : 'false';
333
334
		if ( ! empty( $order->billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) {
335
			$post_data['receipt_email'] = $order->billing_email;
336
		}
337
338
		$post_data['expand[]']    = 'balance_transaction';
339
340
		if ( $source->customer ) {
341
			$post_data['customer'] = $source->customer;
342
		}
343
344
		if ( $source->source ) {
345
			$post_data['source'] = $source->source;
346
		}
347
348
		return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order, $source );
349
	}
350
351
	/**
352
	 * Get payment source. This can be a new token or existing card.
353
	 * @param  bool $force_customer Should we force customer creation?
354
	 * @return object
355
	 */
356
	protected function get_source( $user_id, $force_customer = false ) {
357
		$stripe_customer = new WC_Stripe_Customer( $user_id );
358
		$stripe_source   = false;
359
		$token_id        = false;
360
361
		// New CC info was entered and we have a new token to process
362
		if ( isset( $_POST['stripe_token'] ) ) {
363
			$stripe_token     = wc_clean( $_POST['stripe_token'] );
364
			$maybe_saved_card = isset( $_POST['wc-stripe-new-payment-method'] ) && ! empty( $_POST['wc-stripe-new-payment-method'] );
365
366
			// This is true if the user wants to store the card to their account.
367 View Code Duplication
			if ( ( $user_id && $this->saved_cards && $maybe_saved_card ) || $force_customer ) {
368
				$stripe_source = $stripe_customer->add_card( $stripe_token );
369
370
				if ( is_wp_error( $stripe_source ) ) {
371
					throw new Exception( $stripe_source->get_error_message() );
372
				}
373
374
			} else {
375
				// Not saving token, so don't define customer either.
376
				$stripe_source   = $stripe_token;
377
				$stripe_customer = false;
378
			}
379
		}
380
381
		// Use an existing token, and then process the payment
382
		elseif ( isset( $_POST['wc-stripe-payment-token'] ) && 'new' !== $_POST['wc-stripe-payment-token'] ) {
383
			$stripe_source = wc_clean( $_POST['wc-stripe-payment-token'] );
384
		}
385
386
		return (object) array(
387
			'token_id' => $token_id,
388
			'customer' => $stripe_customer ? $stripe_customer->get_id() : false,
389
			'source'   => $stripe_source,
390
		);
391
	}
392
393
	/**
394
	 * Get payment source from an order. This could be used in the future for
395
	 * a subscription as an example, therefore using the current user ID would
396
	 * not work - the customer won't be logged in :)
397
	 *
398
	 * Not using 2.6 tokens for this part since we need a customer AND a card
399
	 * token, and not just one.
400
	 *
401
	 * @param object $order
402
	 * @return object
403
	 */
404 View Code Duplication
	protected function get_order_source( $order = null ) {
405
		$stripe_customer = new WC_Stripe_Customer();
406
		$stripe_source   = false;
407
		$token_id        = false;
408
409
		if ( $order ) {
410
			if ( $meta_value = get_post_meta( $order->id, '_stripe_customer_id', true ) ) {
411
				$stripe_customer->set_id( $meta_value );
412
			}
413
			if ( $meta_value = get_post_meta( $order->id, '_stripe_card_id', true ) ) {
414
				$stripe_source = $meta_value;
415
			}
416
		}
417
418
		return (object) array(
419
			'token_id' => $token_id,
420
			'customer' => $stripe_customer ? $stripe_customer->get_id() : false,
421
			'source'   => $stripe_source,
422
		);
423
	}
424
425
	/**
426
	 * Process the payment
427
	 */
428
	public function process_payment( $order_id, $retry = true, $force_customer = false ) {
429
		try {
430
			$order  = wc_get_order( $order_id );
431
			$source = $this->get_source( get_current_user_id(), $force_customer );
432
433 View Code Duplication
			if ( empty( $source->source ) && empty( $source->customer ) ) {
434
				$error_msg = __( 'Please enter your card details to make a payment.', 'woocommerce-gateway-stripe' );
435
				$error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce-gateway-stripe' );
436
				throw new Exception( $error_msg );
437
			}
438
439
			// Store source to order meta
440
			$this->save_source( $order, $source );
441
442
			// Handle payment
443
			if ( $order->get_total() > 0 ) {
444
445 View Code Duplication
				if ( $order->get_total() * 100 < WC_Stripe::get_minimum_amount() ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
446
					throw new Exception( sprintf( __( 'Sorry, the minimum allowed order total is %1$s to use this payment method.', 'woocommerce-gateway-stripe' ), wc_price( WC_Stripe::get_minimum_amount() / 100 ) ) );
447
				}
448
449
				WC_Stripe::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
450
451
				// Make the request
452
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
453
454
				if ( is_wp_error( $response ) ) {
455
					// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
456
					if ( 'customer' === $response->get_error_code() && $retry ) {
457
						delete_user_meta( get_current_user_id(), '_stripe_customer_id' );
458
						return $this->process_payment( $order_id, false, $force_customer );
459
					}
460
					throw new Exception( $response->get_error_code() . ': ' . $response->get_error_message() );
461
				}
462
463
				// Process valid response
464
				$this->process_response( $response, $order );
465
			} else {
466
				$order->payment_complete();
467
			}
468
469
			// Remove cart
470
			WC()->cart->empty_cart();
471
472
			// Return thank you page redirect
473
			return array(
474
				'result'   => 'success',
475
				'redirect' => $this->get_return_url( $order )
476
			);
477
478
		} catch ( Exception $e ) {
479
			wc_add_notice( $e->getMessage(), 'error' );
480
			WC()->session->set( 'refresh_totals', true );
481
			WC_Stripe::log( sprintf( __( 'Error: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
482
			return;
483
		}
484
	}
485
486
	/**
487
	 * Save source to order.
488
	 */
489 View Code Duplication
	protected function save_source( $order, $source ) {
490
		// Store source in the order
491
		if ( $source->customer ) {
492
			update_post_meta( $order->id, '_stripe_customer_id', $source->customer );
493
		}
494
		if ( $source->source ) {
495
			update_post_meta( $order->id, '_stripe_card_id', $source->source->id );
496
		}
497
	}
498
499
	/**
500
	 * Store extra meta data for an order from a Stripe Response.
501
	 */
502
	public function process_response( $response, $order ) {
503
		WC_Stripe::log( "Processing response: " . print_r( $response, true ) );
504
505
		// Store charge data
506
		update_post_meta( $order->id, '_stripe_charge_id', $response->id );
507
		update_post_meta( $order->id, '_stripe_charge_captured', $response->captured ? 'yes' : 'no' );
508
509
		// Store other data such as fees
510
		if ( isset( $response->balance_transaction ) && isset( $response->balance_transaction->fee ) ) {
511
			$fee = number_format( $response->balance_transaction->fee / 100, 2, '.', '' );
512
			update_post_meta( $order->id, 'Stripe Fee', $fee );
513
			update_post_meta( $order->id, 'Net Revenue From Stripe', $order->get_total() - $fee );
514
		}
515
516
		if ( $response->captured ) {
517
			$order->payment_complete( $response->id );
518
			WC_Stripe::log( "Successful charge: $response->id" );
519 View Code Duplication
		} else {
520
			add_post_meta( $order->id, '_transaction_id', $response->id, true );
521
522
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
523
				$order->reduce_order_stock();
524
			}
525
526
			$order->update_status( 'on-hold', sprintf( __( 'Stripe charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization.', 'woocommerce-gateway-stripe' ), $response->id ) );
527
			WC_Stripe::log( "Successful auth: $response->id" );
528
		}
529
530
		return $response;
531
	}
532
533
	/**
534
	 * Add payment method via account screen.
535
	 * We don't store the token locally, but to the Stripe API.
536
	 * @since 3.0.0
537
	 */
538
	public function add_payment_method() {
539
		if ( empty( $_POST['stripe_token'] ) || ! is_user_logged_in() ) {
540
			wc_add_notice( __( 'There was a problem adding the card.', 'woocommerce-gateway-stripe' ), 'error' );
541
			return;
542
		}
543
544
		$stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
545
		$result          = $stripe_customer->add_card( wc_clean( $_POST['stripe_token'] ) );
546
547
		if ( is_wp_error( $result ) ) {
548
			throw new Exception( $result->get_error_message() );
549
		}
550
551
		return array(
552
			'result'   => 'success',
553
			'redirect' => wc_get_endpoint_url( 'payment-methods' ),
554
		);
555
	}
556
557
	/**
558
	 * Refund a charge
559
	 * @param  int $order_id
560
	 * @param  float $amount
561
	 * @return bool
562
	 */
563 View Code Duplication
	public function process_refund( $order_id, $amount = null, $reason = '' ) {
564
		$order = wc_get_order( $order_id );
565
566
		if ( ! $order || ! $order->get_transaction_id() ) {
567
			return false;
568
		}
569
570
		$body = array();
571
572
		if ( ! is_null( $amount ) ) {
573
			$body['amount']	= $this->get_stripe_amount( $amount );
574
		}
575
576
		if ( $reason ) {
577
			$body['metadata'] = array(
578
				'reason'	=> $reason,
579
			);
580
		}
581
582
		WC_Stripe::log( "Info: Beginning refund for order $order_id for the amount of {$amount}" );
583
584
		$response = WC_Stripe_API::request( $body, 'charges/' . $order->get_transaction_id() . '/refunds' );
585
586
		if ( is_wp_error( $response ) ) {
587
			WC_Stripe::log( "Error: " . $response->get_error_message() );
588
			return $response;
589
		} elseif ( ! empty( $response->id ) ) {
590
			$refund_message = sprintf( __( 'Refunded %s - Refund ID: %s - Reason: %s', 'woocommerce-gateway-stripe' ), wc_price( $response->amount / 100 ), $response->id, $reason );
591
			$order->add_order_note( $refund_message );
592
			WC_Stripe::log( "Success: " . html_entity_decode( strip_tags( $refund_message ) ) );
593
			return true;
594
		}
595
	}
596
}
597