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_CC { |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Should we capture Credit cards |
15
|
|
|
* |
16
|
|
|
* @var bool |
17
|
|
|
*/ |
18
|
|
|
public $capture; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Checkout enabled |
22
|
|
|
* |
23
|
|
|
* @var bool |
24
|
|
|
*/ |
25
|
|
|
public $stripe_checkout; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Checkout Locale |
29
|
|
|
* |
30
|
|
|
* @var string |
31
|
|
|
*/ |
32
|
|
|
public $stripe_checkout_locale; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Credit card image |
36
|
|
|
* |
37
|
|
|
* @var string |
38
|
|
|
*/ |
39
|
|
|
public $stripe_checkout_image; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Should we store the users credit cards? |
43
|
|
|
* |
44
|
|
|
* @var bool |
45
|
|
|
*/ |
46
|
|
|
public $saved_cards; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* API access secret key |
50
|
|
|
* |
51
|
|
|
* @var string |
52
|
|
|
*/ |
53
|
|
|
public $secret_key; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Api access publishable key |
57
|
|
|
* |
58
|
|
|
* @var string |
59
|
|
|
*/ |
60
|
|
|
public $publishable_key; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Do we accept bitcoin? |
64
|
|
|
* |
65
|
|
|
* @var bool |
66
|
|
|
*/ |
67
|
|
|
public $bitcoin; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Is test mode active? |
71
|
|
|
* |
72
|
|
|
* @var bool |
73
|
|
|
*/ |
74
|
|
|
public $testmode; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Logging enabled? |
78
|
|
|
* |
79
|
|
|
* @var bool |
80
|
|
|
*/ |
81
|
|
|
public $logging; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Constructor |
85
|
|
|
*/ |
86
|
|
View Code Duplication |
public function __construct() { |
|
|
|
|
87
|
|
|
$this->id = 'stripe'; |
88
|
|
|
$this->method_title = __( 'Stripe', 'woocommerce-gateway-stripe' ); |
89
|
|
|
$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' ); |
90
|
|
|
$this->has_fields = true; |
91
|
|
|
$this->view_transaction_url = 'https://dashboard.stripe.com/payments/%s'; |
92
|
|
|
$this->supports = array( |
93
|
|
|
'subscriptions', |
94
|
|
|
'products', |
95
|
|
|
'refunds', |
96
|
|
|
'subscription_cancellation', |
97
|
|
|
'subscription_reactivation', |
98
|
|
|
'subscription_suspension', |
99
|
|
|
'subscription_amount_changes', |
100
|
|
|
'subscription_payment_method_change', // Subs 1.n compatibility. |
101
|
|
|
'subscription_payment_method_change_customer', |
102
|
|
|
'subscription_payment_method_change_admin', |
103
|
|
|
'subscription_date_changes', |
104
|
|
|
'multiple_subscriptions', |
105
|
|
|
'pre-orders', |
106
|
|
|
'tokenization', |
107
|
|
|
'add_payment_method' |
108
|
|
|
); |
109
|
|
|
|
110
|
|
|
// Load the form fields. |
111
|
|
|
$this->init_form_fields(); |
112
|
|
|
|
113
|
|
|
// Load the settings. |
114
|
|
|
$this->init_settings(); |
115
|
|
|
|
116
|
|
|
// Get setting values. |
117
|
|
|
$this->title = $this->get_option( 'title' ); |
118
|
|
|
$this->description = $this->get_option( 'description' ); |
119
|
|
|
$this->enabled = $this->get_option( 'enabled' ); |
120
|
|
|
$this->testmode = 'yes' === $this->get_option( 'testmode' ); |
121
|
|
|
$this->capture = 'yes' === $this->get_option( 'capture', 'yes' ); |
122
|
|
|
$this->stripe_checkout = 'yes' === $this->get_option( 'stripe_checkout' ); |
123
|
|
|
$this->stripe_checkout_locale = $this->get_option( 'stripe_checkout_locale' ); |
124
|
|
|
$this->stripe_checkout_image = $this->get_option( 'stripe_checkout_image', '' ); |
125
|
|
|
$this->saved_cards = 'yes' === $this->get_option( 'saved_cards' ); |
126
|
|
|
$this->secret_key = $this->testmode ? $this->get_option( 'test_secret_key' ) : $this->get_option( 'secret_key' ); |
127
|
|
|
$this->publishable_key = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' ); |
128
|
|
|
$this->bitcoin = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' ); |
129
|
|
|
$this->logging = 'yes' === $this->get_option( 'logging' ); |
130
|
|
|
|
131
|
|
|
if ( $this->stripe_checkout ) { |
132
|
|
|
$this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' ); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
if ( $this->testmode ) { |
136
|
|
|
$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' ); |
137
|
|
|
$this->description = trim( $this->description ); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
WC_Stripe_API::set_secret_key( $this->secret_key ); |
141
|
|
|
|
142
|
|
|
// Hooks. |
143
|
|
|
add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) ); |
144
|
|
|
add_action( 'admin_notices', array( $this, 'admin_notices' ) ); |
145
|
|
|
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) ); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Get_icon function. |
150
|
|
|
* |
151
|
|
|
* @access public |
152
|
|
|
* @return string |
153
|
|
|
*/ |
154
|
|
|
public function get_icon() { |
155
|
|
|
$ext = version_compare( WC()->version, '2.6', '>=' ) ? '.svg' : '.png'; |
156
|
|
|
$style = version_compare( WC()->version, '2.6', '>=' ) ? 'style="margin-left: 0.3em"' : ''; |
157
|
|
|
|
158
|
|
|
$icon = '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/visa' . $ext ) . '" alt="Visa" width="32" ' . $style . ' />'; |
159
|
|
|
$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/mastercard' . $ext ) . '" alt="Mastercard" width="32" ' . $style . ' />'; |
160
|
|
|
$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/amex' . $ext ) . '" alt="Amex" width="32" ' . $style . ' />'; |
161
|
|
|
|
162
|
|
View Code Duplication |
if ( 'USD' === get_woocommerce_currency() ) { |
|
|
|
|
163
|
|
|
$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/discover' . $ext ) . '" alt="Discover" width="32" ' . $style . ' />'; |
164
|
|
|
$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/jcb' . $ext ) . '" alt="JCB" width="32" ' . $style . ' />'; |
165
|
|
|
$icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/diners' . $ext ) . '" alt="Diners" width="32" ' . $style . ' />'; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
if ( 'yes' === $this->bitcoin && 'yes' === $this->stripe_checkout ) { |
169
|
|
|
$icon .= '<img src="' . WC_HTTPS::force_https_url( plugins_url( '/assets/images/bitcoin' . $ext, WC_STRIPE_MAIN_FILE ) ) . '" alt="Bitcoin" width="32" ' . $style . ' />'; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id ); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Get Stripe amount to pay |
177
|
|
|
* |
178
|
|
|
* @param float $total Amount due. |
179
|
|
|
* @param string $currency Accepted currency. |
180
|
|
|
* |
181
|
|
|
* @return float|int |
182
|
|
|
*/ |
183
|
|
View Code Duplication |
public function get_stripe_amount( $total, $currency = '' ) { |
|
|
|
|
184
|
|
|
if ( ! $currency ) { |
185
|
|
|
$currency = get_woocommerce_currency(); |
186
|
|
|
} |
187
|
|
|
switch ( strtoupper( $currency ) ) { |
188
|
|
|
// Zero decimal currencies. |
189
|
|
|
case 'BIF' : |
190
|
|
|
case 'CLP' : |
191
|
|
|
case 'DJF' : |
192
|
|
|
case 'GNF' : |
193
|
|
|
case 'JPY' : |
194
|
|
|
case 'KMF' : |
195
|
|
|
case 'KRW' : |
196
|
|
|
case 'MGA' : |
197
|
|
|
case 'PYG' : |
198
|
|
|
case 'RWF' : |
199
|
|
|
case 'VND' : |
200
|
|
|
case 'VUV' : |
201
|
|
|
case 'XAF' : |
202
|
|
|
case 'XOF' : |
203
|
|
|
case 'XPF' : |
204
|
|
|
$total = absint( $total ); |
205
|
|
|
break; |
206
|
|
|
default : |
207
|
|
|
$total = round( $total, 2 ) * 100; // In cents. |
208
|
|
|
break; |
209
|
|
|
} |
210
|
|
|
return $total; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Check if SSL is enabled and notify the user |
215
|
|
|
*/ |
216
|
|
|
public function admin_notices() { |
217
|
|
|
if ( 'no' === $this->enabled ) { |
218
|
|
|
return; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
// Show message if enabled and FORCE SSL is disabled and WordpressHTTPS plugin is not detected. |
222
|
|
View Code Duplication |
if ( ( function_exists( 'wc_site_is_https' ) && ! wc_site_is_https() ) && ( 'no' === get_option( 'woocommerce_force_ssl_checkout' ) && ! class_exists( 'WordPressHTTPS' ) ) ) { |
|
|
|
|
223
|
|
|
echo '<div class="error stripe-ssl-message"><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>'; |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Check if this gateway is enabled |
229
|
|
|
*/ |
230
|
|
View Code Duplication |
public function is_available() { |
|
|
|
|
231
|
|
|
if ( 'yes' === $this->enabled ) { |
232
|
|
|
if ( ! $this->testmode && is_checkout() && ! is_ssl() ) { |
233
|
|
|
return false; |
234
|
|
|
} |
235
|
|
|
if ( ! $this->secret_key || ! $this->publishable_key ) { |
236
|
|
|
return false; |
237
|
|
|
} |
238
|
|
|
return true; |
239
|
|
|
} |
240
|
|
|
return false; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Initialise Gateway Settings Form Fields |
245
|
|
|
*/ |
246
|
|
|
public function init_form_fields() { |
247
|
|
|
$this->form_fields = include( 'settings-stripe.php' ); |
248
|
|
|
|
249
|
|
|
wc_enqueue_js( " |
250
|
|
|
jQuery( function( $ ) { |
251
|
|
|
$( '#woocommerce_stripe_stripe_checkout' ).change(function(){ |
252
|
|
|
if ( $( this ).is( ':checked' ) ) { |
253
|
|
|
$( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).show(); |
254
|
|
|
} else { |
255
|
|
|
$( '#woocommerce_stripe_stripe_checkout_locale, #woocommerce_stripe_stripe_bitcoin, #woocommerce_stripe_stripe_checkout_image' ).closest( 'tr' ).hide(); |
256
|
|
|
} |
257
|
|
|
}).change(); |
258
|
|
|
|
259
|
|
|
$( '#woocommerce_stripe_secret_key, #woocommerce_stripe_publishable_key' ).change(function(){ |
260
|
|
|
var value = $( this ).val(); |
261
|
|
|
|
262
|
|
|
if ( value.indexOf( '_test_' ) >= 0 ) { |
263
|
|
|
$( this ).css( 'border-color', 'red' ).after( '<span class=\"description stripe-error-description\" style=\"color:red; display:block;\">" . __( 'This is not a valid live key. Live keys start with "sk_live_" and "pk_live_".', 'woocommerce-gateway-stripe' ) . "</span>' ); |
264
|
|
|
} else { |
265
|
|
|
$( this ).css( 'border-color', '' ); |
266
|
|
|
$( '.stripe-error-description', $( this ).parent() ).remove(); |
267
|
|
|
} |
268
|
|
|
}).change(); |
269
|
|
|
|
270
|
|
|
$( '#woocommerce_stripe_test_secret_key, #woocommerce_stripe_test_publishable_key' ).change(function(){ |
271
|
|
|
var value = $( this ).val(); |
272
|
|
|
|
273
|
|
|
if ( value.indexOf( '_live_' ) >= 0 ) { |
274
|
|
|
$( this ).css( 'border-color', 'red' ).after( '<span class=\"description stripe-error-description\" style=\"color:red; display:block;\">" . __( 'This is not a valid test key. Test keys start with "sk_test_" and "pk_test_".', 'woocommerce-gateway-stripe' ) . "</span>' ); |
275
|
|
|
} else { |
276
|
|
|
$( this ).css( 'border-color', '' ); |
277
|
|
|
$( '.stripe-error-description', $( this ).parent() ).remove(); |
278
|
|
|
} |
279
|
|
|
}).change(); |
280
|
|
|
}); |
281
|
|
|
" ); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Payment form on checkout page |
286
|
|
|
*/ |
287
|
|
|
public function payment_fields() { |
288
|
|
|
$user = wp_get_current_user(); |
289
|
|
|
$display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards; |
290
|
|
|
|
291
|
|
View Code Duplication |
if ( $user->ID ) { |
|
|
|
|
292
|
|
|
$user_email = get_user_meta( $user->ID, 'billing_email', true ); |
293
|
|
|
$user_email = $user_email ? $user_email : $user->user_email; |
294
|
|
|
} else { |
295
|
|
|
$user_email = ''; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
if ( is_add_payment_method_page() ) { |
299
|
|
|
$pay_button_text = __( 'Add Card', 'woocommerce-gateway-stripe' ); |
300
|
|
|
} else { |
301
|
|
|
$pay_button_text = ''; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
echo '<div |
305
|
|
|
id="stripe-payment-data" |
306
|
|
|
data-panel-label="' . esc_attr( $pay_button_text ) . '" |
307
|
|
|
data-description="" |
308
|
|
|
data-email="' . esc_attr( $user_email ) . '" |
309
|
|
|
data-amount="' . esc_attr( $this->get_stripe_amount( WC()->cart->total ) ) . '" |
310
|
|
|
data-name="' . esc_attr( sprintf( __( '%s', 'woocommerce-gateway-stripe' ), get_bloginfo( 'name', 'display' ) ) ) . '" |
311
|
|
|
data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '" |
312
|
|
|
data-image="' . esc_attr( $this->stripe_checkout_image ) . '" |
313
|
|
|
data-bitcoin="' . esc_attr( $this->bitcoin ? 'true' : 'false' ) . '" |
314
|
|
|
data-locale="' . esc_attr( $this->stripe_checkout_locale ? $this->stripe_checkout_locale : 'en' ) . '">'; |
315
|
|
|
|
316
|
|
|
if ( $this->description ) { |
317
|
|
|
echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $this->description ) ) ); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
if ( $display_tokenization ) { |
321
|
|
|
$this->tokenization_script(); |
322
|
|
|
$this->saved_payment_methods(); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
if ( ! $this->stripe_checkout ) { |
326
|
|
|
$this->form(); |
327
|
|
|
|
328
|
|
|
if ( $display_tokenization ) { |
329
|
|
|
$this->save_payment_method_checkbox(); |
330
|
|
|
} |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
echo '</div>'; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Localize Stripe messages based on code |
338
|
|
|
* |
339
|
|
|
* @since 3.0.6 |
340
|
|
|
* @version 3.0.6 |
341
|
|
|
* @return array |
342
|
|
|
*/ |
343
|
|
|
public function get_localized_messages() { |
344
|
|
|
return apply_filters( 'wc_stripe_localized_messages', array( |
345
|
|
|
'invalid_number' => __( 'The card number is not a valid credit card number.', 'woocommerce-gateway-stripe' ), |
346
|
|
|
'invalid_expiry_month' => __( 'The card\'s expiration month is invalid.', 'woocommerce-gateway-stripe' ), |
347
|
|
|
'invalid_expiry_year' => __( 'The card\'s expiration year is invalid.', 'woocommerce-gateway-stripe' ), |
348
|
|
|
'invalid_cvc' => __( 'The card\'s security code is invalid.', 'woocommerce-gateway-stripe' ), |
349
|
|
|
'incorrect_number' => __( 'The card number is incorrect.', 'woocommerce-gateway-stripe' ), |
350
|
|
|
'expired_card' => __( 'The card has expired.', 'woocommerce-gateway-stripe' ), |
351
|
|
|
'incorrect_cvc' => __( 'The card\'s security code is incorrect.', 'woocommerce-gateway-stripe' ), |
352
|
|
|
'incorrect_zip' => __( 'The card\'s zip code failed validation.', 'woocommerce-gateway-stripe' ), |
353
|
|
|
'card_declined' => __( 'The card was declined.', 'woocommerce-gateway-stripe' ), |
354
|
|
|
'missing' => __( 'There is no card on a customer that is being charged.', 'woocommerce-gateway-stripe' ), |
355
|
|
|
'processing_error' => __( 'An error occurred while processing the card.', 'woocommerce-gateway-stripe' ), |
356
|
|
|
'invalid_request_error' => __( 'Could not find payment information.', 'woocommerce-gateway-stripe' ), |
357
|
|
|
) ); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* payment_scripts function. |
362
|
|
|
* |
363
|
|
|
* Outputs scripts used for stripe payment |
364
|
|
|
* |
365
|
|
|
* @access public |
366
|
|
|
*/ |
367
|
|
|
public function payment_scripts() { |
368
|
|
|
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; |
369
|
|
|
|
370
|
|
|
if ( $this->stripe_checkout ) { |
371
|
|
|
wp_enqueue_script( 'stripe', 'https://checkout.stripe.com/v2/checkout.js', '', '2.0', true ); |
372
|
|
|
wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe_checkout' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'stripe' ), WC_STRIPE_VERSION, true ); |
373
|
|
View Code Duplication |
} else { |
|
|
|
|
374
|
|
|
wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true ); |
375
|
|
|
wp_enqueue_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'jquery-payment', 'stripe' ), WC_STRIPE_VERSION, true ); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
$stripe_params = array( |
379
|
|
|
'key' => $this->publishable_key, |
380
|
|
|
'i18n_terms' => __( 'Please accept the terms and conditions first', 'woocommerce-gateway-stripe' ), |
381
|
|
|
'i18n_required_fields' => __( 'Please fill in required checkout fields first', 'woocommerce-gateway-stripe' ), |
382
|
|
|
); |
383
|
|
|
|
384
|
|
|
// If we're on the pay page we need to pass stripe.js the address of the order. |
385
|
|
View Code Duplication |
if ( is_checkout_pay_page() && isset( $_GET['order'] ) && isset( $_GET['order_id'] ) ) { |
|
|
|
|
386
|
|
|
$order_key = urldecode( $_GET['order'] ); |
387
|
|
|
$order_id = absint( $_GET['order_id'] ); |
388
|
|
|
$order = wc_get_order( $order_id ); |
389
|
|
|
|
390
|
|
|
if ( $order->id === $order_id && $order->order_key === $order_key ) { |
391
|
|
|
$stripe_params['billing_first_name'] = $order->billing_first_name; |
392
|
|
|
$stripe_params['billing_last_name'] = $order->billing_last_name; |
393
|
|
|
$stripe_params['billing_address_1'] = $order->billing_address_1; |
394
|
|
|
$stripe_params['billing_address_2'] = $order->billing_address_2; |
395
|
|
|
$stripe_params['billing_state'] = $order->billing_state; |
396
|
|
|
$stripe_params['billing_city'] = $order->billing_city; |
397
|
|
|
$stripe_params['billing_postcode'] = $order->billing_postcode; |
398
|
|
|
$stripe_params['billing_country'] = $order->billing_country; |
399
|
|
|
} |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
$stripe_params['no_prepaid_card_msg'] = __( 'Sorry, we\'re not accepting prepaid cards at this time.', 'woocommerce-gateway-stripe' ); |
403
|
|
|
$stripe_params['allow_prepaid_card'] = apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no'; |
404
|
|
|
$stripe_params['stripe_checkout_require_billing_address'] = apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'yes' : 'no'; |
405
|
|
|
|
406
|
|
|
// merge localized messages to be use in JS |
407
|
|
|
$stripe_params = array_merge( $stripe_params, $this->get_localized_messages() ); |
408
|
|
|
|
409
|
|
|
wp_localize_script( 'woocommerce_stripe', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) ); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Generate the request for the payment. |
414
|
|
|
* @param WC_Order $order |
415
|
|
|
* @param object $source |
416
|
|
|
* @return array() |
|
|
|
|
417
|
|
|
*/ |
418
|
|
View Code Duplication |
protected function generate_payment_request( $order, $source ) { |
|
|
|
|
419
|
|
|
$post_data = array(); |
420
|
|
|
$post_data['currency'] = strtolower( $order->get_order_currency() ? $order->get_order_currency() : get_woocommerce_currency() ); |
421
|
|
|
$post_data['amount'] = $this->get_stripe_amount( $order->get_total(), $post_data['currency'] ); |
422
|
|
|
$post_data['description'] = sprintf( __( '%s - Order %s', 'woocommerce-gateway-stripe' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), $order->get_order_number() ); |
423
|
|
|
$post_data['capture'] = $this->capture ? 'true' : 'false'; |
424
|
|
|
|
425
|
|
|
if ( ! empty( $order->billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) { |
426
|
|
|
$post_data['receipt_email'] = $order->billing_email; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
$post_data['expand[]'] = 'balance_transaction'; |
430
|
|
|
|
431
|
|
|
if ( $source->customer ) { |
432
|
|
|
$post_data['customer'] = $source->customer; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
if ( $source->source ) { |
436
|
|
|
$post_data['source'] = $source->source; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* Filter the return value of the WC_Payment_Gateway_CC::generate_payment_request. |
441
|
|
|
* |
442
|
|
|
* @since 3.1.0 |
443
|
|
|
* @param array $post_data |
444
|
|
|
* @param WC_Order $order |
445
|
|
|
* @param object $source |
446
|
|
|
*/ |
447
|
|
|
return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order, $source ); |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Get payment source. This can be a new token or existing card. |
452
|
|
|
* |
453
|
|
|
* @param string $user_id |
454
|
|
|
* @param bool $force_customer Should we force customer creation. |
455
|
|
|
* |
456
|
|
|
* @throws Exception When card was not added or for and invalid card. |
457
|
|
|
* @return object |
458
|
|
|
*/ |
459
|
|
|
protected function get_source( $user_id, $force_customer = false ) { |
460
|
|
|
$stripe_customer = new WC_Stripe_Customer( $user_id ); |
461
|
|
|
$stripe_source = false; |
462
|
|
|
$token_id = false; |
463
|
|
|
|
464
|
|
|
// New CC info was entered and we have a new token to process |
465
|
|
|
if ( isset( $_POST['stripe_token'] ) ) { |
466
|
|
|
$stripe_token = wc_clean( $_POST['stripe_token'] ); |
467
|
|
|
$maybe_saved_card = isset( $_POST['wc-stripe-new-payment-method'] ) && ! empty( $_POST['wc-stripe-new-payment-method'] ); |
468
|
|
|
|
469
|
|
|
// This is true if the user wants to store the card to their account. |
470
|
|
View Code Duplication |
if ( ( $user_id && $this->saved_cards && $maybe_saved_card ) || $force_customer ) { |
|
|
|
|
471
|
|
|
$stripe_source = $stripe_customer->add_card( $stripe_token ); |
472
|
|
|
|
473
|
|
|
if ( is_wp_error( $stripe_source ) ) { |
474
|
|
|
throw new Exception( $stripe_source->get_error_message() ); |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
} else { |
478
|
|
|
// Not saving token, so don't define customer either. |
479
|
|
|
$stripe_source = $stripe_token; |
480
|
|
|
$stripe_customer = false; |
481
|
|
|
} |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
// Use an existing token, and then process the payment |
485
|
|
|
elseif ( isset( $_POST['wc-stripe-payment-token'] ) && 'new' !== $_POST['wc-stripe-payment-token'] ) { |
486
|
|
|
$token_id = wc_clean( $_POST['wc-stripe-payment-token'] ); |
487
|
|
|
$token = WC_Payment_Tokens::get( $token_id ); |
488
|
|
|
|
489
|
|
|
if ( ! $token || $token->get_user_id() !== get_current_user_id() ) { |
490
|
|
|
WC()->session->set( 'refresh_totals', true ); |
491
|
|
|
throw new Exception( __( 'Invalid payment method. Please input a new card number.', 'woocommerce-gateway-stripe' ) ); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
$stripe_source = $token->get_token(); |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
return (object) array( |
498
|
|
|
'token_id' => $token_id, |
499
|
|
|
'customer' => $stripe_customer ? $stripe_customer->get_id() : false, |
500
|
|
|
'source' => $stripe_source, |
501
|
|
|
); |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* Get payment source from an order. This could be used in the future for |
506
|
|
|
* a subscription as an example, therefore using the current user ID would |
507
|
|
|
* not work - the customer won't be logged in :) |
508
|
|
|
* |
509
|
|
|
* Not using 2.6 tokens for this part since we need a customer AND a card |
510
|
|
|
* token, and not just one. |
511
|
|
|
* |
512
|
|
|
* @param object $order |
513
|
|
|
* @return object |
514
|
|
|
*/ |
515
|
|
View Code Duplication |
protected function get_order_source( $order = null ) { |
|
|
|
|
516
|
|
|
$stripe_customer = new WC_Stripe_Customer(); |
517
|
|
|
$stripe_source = false; |
518
|
|
|
$token_id = false; |
519
|
|
|
|
520
|
|
|
if ( $order ) { |
521
|
|
|
if ( $meta_value = get_post_meta( $order->id, '_stripe_customer_id', true ) ) { |
522
|
|
|
$stripe_customer->set_id( $meta_value ); |
523
|
|
|
} |
524
|
|
|
if ( $meta_value = get_post_meta( $order->id, '_stripe_card_id', true ) ) { |
525
|
|
|
$stripe_source = $meta_value; |
526
|
|
|
} |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
return (object) array( |
530
|
|
|
'token_id' => $token_id, |
531
|
|
|
'customer' => $stripe_customer ? $stripe_customer->get_id() : false, |
532
|
|
|
'source' => $stripe_source, |
533
|
|
|
); |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
/** |
537
|
|
|
* Process the payment |
538
|
|
|
* |
539
|
|
|
* @param int $order_id Reference. |
540
|
|
|
* @param bool $retry Should we retry on fail. |
541
|
|
|
* @param bool $force_customer Force user creation. |
542
|
|
|
* |
543
|
|
|
* @throws Exception If payment will not be accepted. |
544
|
|
|
* |
545
|
|
|
* @return array|void |
546
|
|
|
*/ |
547
|
|
|
public function process_payment( $order_id, $retry = true, $force_customer = false ) { |
548
|
|
|
try { |
549
|
|
|
$order = wc_get_order( $order_id ); |
550
|
|
|
$source = $this->get_source( get_current_user_id(), $force_customer ); |
551
|
|
|
|
552
|
|
View Code Duplication |
if ( empty( $source->source ) && empty( $source->customer ) ) { |
|
|
|
|
553
|
|
|
$error_msg = __( 'Please enter your card details to make a payment.', 'woocommerce-gateway-stripe' ); |
554
|
|
|
$error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce-gateway-stripe' ); |
555
|
|
|
throw new Exception( $error_msg ); |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
// Store source to order meta. |
559
|
|
|
$this->save_source( $order, $source ); |
560
|
|
|
|
561
|
|
|
// Handle payment. |
562
|
|
|
if ( $order->get_total() > 0 ) { |
563
|
|
|
|
564
|
|
|
if ( $order->get_total() * 100 < 50 ) { |
565
|
|
|
throw new Exception( __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce-gateway-stripe' ) ); |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
WC_Stripe::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" ); |
569
|
|
|
|
570
|
|
|
// Make the request. |
571
|
|
|
$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) ); |
572
|
|
|
|
573
|
|
|
if ( is_wp_error( $response ) ) { |
574
|
|
|
// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without. |
575
|
|
|
if ( 'customer' === $response->get_error_code() && $retry ) { |
576
|
|
|
delete_user_meta( get_current_user_id(), '_stripe_customer_id' ); |
577
|
|
|
return $this->process_payment( $order_id, false, $force_customer ); |
578
|
|
|
// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message. |
579
|
|
|
} elseif ( 'source' === $response->get_error_code() && $source->token_id ) { |
580
|
|
|
$token = WC_Payment_Tokens::get( $source->token_id ); |
581
|
|
|
$token->delete(); |
582
|
|
|
throw new Exception( __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' ) ); |
583
|
|
|
} |
584
|
|
|
$localized_messages = $this->get_localized_messages(); |
585
|
|
|
|
586
|
|
|
throw new Exception( ( isset( $localized_messages[ $response->get_error_code() ] ) ? $localized_messages[ $response->get_error_code() ] : $response->get_error_message() ) ); |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
// Process valid response. |
590
|
|
|
$this->process_response( $response, $order ); |
591
|
|
|
} else { |
592
|
|
|
$order->payment_complete(); |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
// Remove cart. |
596
|
|
|
WC()->cart->empty_cart(); |
597
|
|
|
|
598
|
|
|
// Return thank you page redirect. |
599
|
|
|
return array( |
600
|
|
|
'result' => 'success', |
601
|
|
|
'redirect' => $this->get_return_url( $order ) |
602
|
|
|
); |
603
|
|
|
|
604
|
|
|
} catch ( Exception $e ) { |
605
|
|
|
wc_add_notice( $e->getMessage(), 'error' ); |
606
|
|
|
WC()->session->set( 'refresh_totals', true ); |
607
|
|
|
WC_Stripe::log( sprintf( __( 'Error: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) ); |
608
|
|
|
|
609
|
|
|
if ( $order->has_status( array( 'pending', 'failed' ) ) ) { |
610
|
|
|
$this->send_failed_order_email( $order_id ); |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
return; |
614
|
|
|
} |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
/** |
618
|
|
|
* Save source to order. |
619
|
|
|
* |
620
|
|
|
* @param WC_Order $order For to which the source applies. |
621
|
|
|
* @param stdClass $source Source information. |
622
|
|
|
*/ |
623
|
|
View Code Duplication |
protected function save_source( $order, $source ) { |
|
|
|
|
624
|
|
|
// Store source in the order. |
625
|
|
|
if ( $source->customer ) { |
626
|
|
|
update_post_meta( $order->id, '_stripe_customer_id', $source->customer ); |
627
|
|
|
} |
628
|
|
|
if ( $source->source ) { |
629
|
|
|
update_post_meta( $order->id, '_stripe_card_id', $source->source ); |
630
|
|
|
} |
631
|
|
|
} |
632
|
|
|
|
633
|
|
|
/** |
634
|
|
|
* Store extra meta data for an order from a Stripe Response. |
635
|
|
|
*/ |
636
|
|
|
public function process_response( $response, $order ) { |
637
|
|
|
WC_Stripe::log( "Processing response: " . print_r( $response, true ) ); |
638
|
|
|
|
639
|
|
|
// Store charge data |
640
|
|
|
update_post_meta( $order->id, '_stripe_charge_id', $response->id ); |
641
|
|
|
update_post_meta( $order->id, '_stripe_charge_captured', $response->captured ? 'yes' : 'no' ); |
642
|
|
|
|
643
|
|
|
// Store other data such as fees |
644
|
|
View Code Duplication |
if ( isset( $response->balance_transaction ) && isset( $response->balance_transaction->fee ) ) { |
|
|
|
|
645
|
|
|
$fee = number_format( $response->balance_transaction->fee / 100, 2, '.', '' ); |
646
|
|
|
update_post_meta( $order->id, 'Stripe Fee', $fee ); |
647
|
|
|
update_post_meta( $order->id, 'Net Revenue From Stripe', $order->get_total() - $fee ); |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
if ( $response->captured ) { |
651
|
|
|
$order->payment_complete( $response->id ); |
652
|
|
|
|
653
|
|
|
$message = sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $response->id ); |
654
|
|
|
$order->add_order_note( $message ); |
655
|
|
|
WC_Stripe::log( 'Success: ' . $message ); |
656
|
|
|
|
657
|
|
View Code Duplication |
} else { |
|
|
|
|
658
|
|
|
add_post_meta( $order->id, '_transaction_id', $response->id, true ); |
659
|
|
|
|
660
|
|
|
if ( $order->has_status( array( 'pending', 'failed' ) ) ) { |
661
|
|
|
$order->reduce_order_stock(); |
662
|
|
|
} |
663
|
|
|
|
664
|
|
|
$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 ) ); |
665
|
|
|
WC_Stripe::log( "Successful auth: $response->id" ); |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
return $response; |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
/** |
672
|
|
|
* Add payment method via account screen. |
673
|
|
|
* We don't store the token locally, but to the Stripe API. |
674
|
|
|
* @since 3.0.0 |
675
|
|
|
*/ |
676
|
|
|
public function add_payment_method() { |
677
|
|
|
if ( empty( $_POST['stripe_token'] ) || ! is_user_logged_in() ) { |
678
|
|
|
wc_add_notice( __( 'There was a problem adding the card.', 'woocommerce-gateway-stripe' ), 'error' ); |
679
|
|
|
return; |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
$stripe_customer = new WC_Stripe_Customer( get_current_user_id() ); |
683
|
|
|
$card = $stripe_customer->add_card( wc_clean( $_POST['stripe_token'] ) ); |
684
|
|
|
|
685
|
|
|
if ( is_wp_error( $card ) ) { |
686
|
|
|
$localized_messages = $this->get_localized_messages(); |
687
|
|
|
$error_msg = __( 'There was a problem adding the card.', 'woocommerce-gateway-stripe' ); |
688
|
|
|
|
689
|
|
|
// loop through the errors to find matching localized message |
690
|
|
|
foreach ( $card->errors as $error => $msg ) { |
691
|
|
|
if ( isset( $localized_messages[ $error ] ) ) { |
692
|
|
|
$error_msg = $localized_messages[ $error ]; |
693
|
|
|
} |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
wc_add_notice( $error_msg, 'error' ); |
697
|
|
|
return; |
698
|
|
|
} |
699
|
|
|
|
700
|
|
|
return array( |
701
|
|
|
'result' => 'success', |
702
|
|
|
'redirect' => wc_get_endpoint_url( 'payment-methods' ), |
703
|
|
|
); |
704
|
|
|
} |
705
|
|
|
|
706
|
|
|
/** |
707
|
|
|
* Refund a charge |
708
|
|
|
* @param int $order_id |
709
|
|
|
* @param float $amount |
710
|
|
|
* @return bool |
711
|
|
|
*/ |
712
|
|
View Code Duplication |
public function process_refund( $order_id, $amount = null, $reason = '' ) { |
|
|
|
|
713
|
|
|
$order = wc_get_order( $order_id ); |
714
|
|
|
|
715
|
|
|
if ( ! $order || ! $order->get_transaction_id() ) { |
716
|
|
|
return false; |
717
|
|
|
} |
718
|
|
|
|
719
|
|
|
$body = array(); |
720
|
|
|
|
721
|
|
|
if ( ! is_null( $amount ) ) { |
722
|
|
|
$body['amount'] = $this->get_stripe_amount( $amount ); |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
if ( $reason ) { |
726
|
|
|
$body['metadata'] = array( |
727
|
|
|
'reason' => $reason, |
728
|
|
|
); |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
WC_Stripe::log( "Info: Beginning refund for order $order_id for the amount of {$amount}" ); |
732
|
|
|
|
733
|
|
|
$response = WC_Stripe_API::request( $body, 'charges/' . $order->get_transaction_id() . '/refunds' ); |
734
|
|
|
|
735
|
|
|
if ( is_wp_error( $response ) ) { |
736
|
|
|
WC_Stripe::log( "Error: " . $response->get_error_message() ); |
737
|
|
|
return $response; |
738
|
|
|
} elseif ( ! empty( $response->id ) ) { |
739
|
|
|
$refund_message = sprintf( __( 'Refunded %s - Refund ID: %s - Reason: %s', 'woocommerce-gateway-stripe' ), wc_price( $response->amount / 100 ), $response->id, $reason ); |
740
|
|
|
$order->add_order_note( $refund_message ); |
741
|
|
|
WC_Stripe::log( "Success: " . html_entity_decode( strip_tags( $refund_message ) ) ); |
742
|
|
|
return true; |
743
|
|
|
} |
744
|
|
|
} |
745
|
|
|
|
746
|
|
|
/** |
747
|
|
|
* Sends the failed order email to admin |
748
|
|
|
* |
749
|
|
|
* @version 3.1.0 |
750
|
|
|
* @since 3.1.0 |
751
|
|
|
* @param int $order_id |
752
|
|
|
* @return null |
753
|
|
|
*/ |
754
|
|
|
public function send_failed_order_email( $order_id ) { |
755
|
|
|
$emails = WC()->mailer()->get_emails(); |
756
|
|
|
if ( ! empty( $emails ) && ! empty( $order_id ) ) { |
757
|
|
|
$emails['WC_Email_Failed_Order']->trigger( $order_id ); |
758
|
|
|
} |
759
|
|
|
} |
760
|
|
|
} |
761
|
|
|
|
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.