WooCommerce Stripe Payment Gateway - Version 4.0.6

Version Description

  • 2018-02-20 =
  • Fix - A WC 2.6 backwards compat issue with function from WC 3.0.
  • Fix - Subs renewal sometimes failed due to parameters being different.
  • Fix - Stripe accepts only NO for Norwegian language on Stripe Checkout.
  • Fix - Refund fees may not accurately reflect net fees. Props @rvola.
  • Fix - Undefined SERVERNAME property in some cases.
  • Fix - Potential issue when a charge parameter changes due to initial failed request causing retries to fail.
  • Fix - When 3DS is not required, failed payments was not change order status to failed.
  • Fix - Potential duplicate order processes on WC side when webhook and redirect has a race condition.
  • Remove - Checkout validation and let WC handle it.
  • Update - Stripe API version to 2018-02-06.
  • Add - Webhooks for review open/closed for Radar.
  • Add - Hook wc_stripe_refund_request for refund request arguments.
  • Add - Hook wc_stripe_validate_modal_checkout to enable 3rd party checkout validation.
  • Add - Hook wc_stripe_validate_modal_checkout_action to enable 3rd party checkout validation.

See changelog for all versions.

=

Download this release

Release Info

Developer royho
Plugin Icon 128x128 WooCommerce Stripe Payment Gateway
Version 4.0.6
Comparing to
See all releases

Code changes from version 4.0.5 to 4.0.6

assets/js/stripe.js CHANGED
@@ -6,8 +6,8 @@ jQuery( function( $ ) {
6
  var stripe = Stripe( wc_stripe_params.key );
7
 
8
  if ( 'yes' === wc_stripe_params.use_elements ) {
9
- var stripe_elements_options = wc_stripe_params.elements_options.length ? wc_stripe_params.elements_options : {};
10
- var elements = stripe.elements( stripe_elements_options ),
11
  stripe_card,
12
  stripe_exp,
13
  stripe_cvc;
@@ -29,12 +29,114 @@ jQuery( function( $ ) {
29
  .replace( '%%endpoint%%', 'wc_stripe_' + endpoint );
30
  },
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  /**
33
  * Initialize event handlers and UI state.
34
  */
35
  init: function() {
36
  // Initialize tokenization script if on change payment method page and pay for order page.
37
- if ( 'yes' === wc_stripe_params.is_change_payment_page ) {
38
  $( document.body ).trigger( 'wc-credit-card-form-init' );
39
  }
40
 
@@ -57,7 +159,7 @@ jQuery( function( $ ) {
57
  this.form = $( 'form#order_review' );
58
  }
59
 
60
- $( 'form#order_review' )
61
  .on(
62
  'submit',
63
  this.onSubmit
@@ -68,16 +170,9 @@ jQuery( function( $ ) {
68
  this.form = $( 'form#add_payment_method' );
69
  }
70
 
71
- $( 'form#add_payment_method' )
72
- .on(
73
- 'submit',
74
- this.onSubmit
75
- );
76
-
77
  $( 'form.woocommerce-checkout' )
78
  .on(
79
  'change',
80
- '#stripe-bank-country',
81
  this.reset
82
  );
83
 
@@ -91,101 +186,8 @@ jQuery( function( $ ) {
91
  this.reset
92
  );
93
 
94
- var elementStyles = {
95
- base: {
96
- iconColor: '#666EE8',
97
- color: '#31325F',
98
- fontSize: '15px',
99
- '::placeholder': {
100
- color: '#CFD7E0',
101
- }
102
- }
103
- };
104
-
105
- var elementClasses = {
106
- focus: 'focused',
107
- empty: 'empty',
108
- invalid: 'invalid',
109
- };
110
-
111
- if ( 'yes' === wc_stripe_params.use_elements && $( '#stripe-card-element' ).length ) {
112
- elementStyles = wc_stripe_params.elements_styling ? wc_stripe_params.elements_styling : elementStyles;
113
- elementClasses = wc_stripe_params.elements_classes ? wc_stripe_params.elements_classes : elementClasses;
114
-
115
- if ( 'yes' === wc_stripe_params.inline_cc_form ) {
116
- stripe_card = elements.create( 'card', { style: elementStyles, hidePostalCode: true } );
117
-
118
- stripe_card.addEventListener( 'change', function( event ) {
119
- wc_stripe_form.onCCFormChange();
120
-
121
- if ( event.error ) {
122
- $( document.body ).trigger( 'stripeError', event );
123
- }
124
- } );
125
- } else {
126
- stripe_card = elements.create( 'cardNumber', { style: elementStyles, classes: elementClasses } );
127
- stripe_exp = elements.create( 'cardExpiry', { style: elementStyles, classes: elementClasses } );
128
- stripe_cvc = elements.create( 'cardCvc', { style: elementStyles, classes: elementClasses } );
129
-
130
- stripe_card.addEventListener( 'change', function( event ) {
131
- wc_stripe_form.onCCFormChange();
132
-
133
- if ( event.error ) {
134
- $( document.body ).trigger( 'stripeError', event );
135
- }
136
- } );
137
-
138
- stripe_exp.addEventListener( 'change', function( event ) {
139
- wc_stripe_form.onCCFormChange();
140
-
141
- if ( event.error ) {
142
- $( document.body ).trigger( 'stripeError', event );
143
- }
144
- } );
145
-
146
- stripe_cvc.addEventListener( 'change', function( event ) {
147
- wc_stripe_form.onCCFormChange();
148
-
149
- if ( event.error ) {
150
- $( document.body ).trigger( 'stripeError', event );
151
- }
152
- } );
153
- }
154
-
155
- /**
156
- * Only in checkout page we need to delay the mounting of the
157
- * card as some AJAX process needs to happen before we do.
158
- */
159
- if ( wc_stripe_params.is_checkout ) {
160
- $( document.body ).on( 'updated_checkout', function() {
161
- // Don't mount elements a second time.
162
- if ( stripe_card ) {
163
- if ( 'yes' === wc_stripe_params.inline_cc_form ) {
164
- stripe_card.unmount( '#stripe-card-element' );
165
- } else {
166
- stripe_card.unmount( '#stripe-card-element' );
167
- stripe_exp.unmount( '#stripe-exp-element' );
168
- stripe_cvc.unmount( '#stripe-cvc-element' );
169
- }
170
- }
171
-
172
- if ( 'yes' === wc_stripe_params.inline_cc_form ) {
173
- stripe_card.mount( '#stripe-card-element' );
174
- } else {
175
- stripe_card.mount( '#stripe-card-element' );
176
- stripe_exp.mount( '#stripe-exp-element' );
177
- stripe_cvc.mount( '#stripe-cvc-element' );
178
- }
179
- });
180
- } else if ( $( 'form#add_payment_method' ).length || $( 'form#order_review' ).length ) {
181
- if ( 'yes' === wc_stripe_params.inline_cc_form ) {
182
- stripe_card.mount( '#stripe-card-element' );
183
- } else {
184
- stripe_card.mount( '#stripe-card-element' );
185
- stripe_exp.mount( '#stripe-exp-element' );
186
- stripe_cvc.mount( '#stripe-cvc-element' );
187
- }
188
- }
189
  }
190
  },
191
 
@@ -272,15 +274,7 @@ jQuery( function( $ ) {
272
  },
273
 
274
  block: function() {
275
- if ( wc_stripe_form.isMobile() ) {
276
- $.blockUI({
277
- message: null,
278
- overlayCSS: {
279
- background: '#fff',
280
- opacity: 0.6
281
- }
282
- });
283
- } else {
284
  wc_stripe_form.form.block({
285
  message: null,
286
  overlayCSS: {
@@ -292,11 +286,7 @@ jQuery( function( $ ) {
292
  },
293
 
294
  unblock: function() {
295
- if ( wc_stripe_form.isMobile() ) {
296
- $.unblockUI();
297
- } else {
298
- wc_stripe_form.form.unblock();
299
- }
300
  },
301
 
302
  getSelectedPaymentElement: function() {
@@ -357,45 +347,6 @@ jQuery( function( $ ) {
357
  wc_stripe_form.unblock();
358
  },
359
 
360
- onError: function( e, result ) {
361
- var message = result.error.message,
362
- errorContainer = wc_stripe_form.getSelectedPaymentElement().parents( 'li' ).eq(0).find( '.stripe-source-errors' );
363
-
364
- /*
365
- * Customers do not need to know the specifics of the below type of errors
366
- * therefore return a generic localizable error message.
367
- */
368
- if (
369
- 'invalid_request_error' === result.error.type ||
370
- 'api_connection_error' === result.error.type ||
371
- 'api_error' === result.error.type ||
372
- 'authentication_error' === result.error.type ||
373
- 'rate_limit_error' === result.error.type
374
- ) {
375
- message = wc_stripe_params.invalid_request_error;
376
- }
377
-
378
- if ( 'card_error' === result.error.type && wc_stripe_params.hasOwnProperty( result.error.code ) ) {
379
- message = wc_stripe_params[ result.error.code ];
380
- }
381
-
382
- if ( 'validation_error' === result.error.type && wc_stripe_params.hasOwnProperty( result.error.code ) ) {
383
- message = wc_stripe_params[ result.error.code ];
384
- }
385
-
386
- wc_stripe_form.reset();
387
- $( '.woocommerce-NoticeGroup-checkout' ).remove();
388
- console.log( result.error.message ); // Leave for troubleshooting.
389
- $( errorContainer ).html( '<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li>' + message + '</li></ul>' );
390
-
391
- if ( $( '.wc-stripe-error' ).length ) {
392
- $( 'html, body' ).animate({
393
- scrollTop: ( $( '.wc-stripe-error' ).offset().top - 200 )
394
- }, 200 );
395
- }
396
- wc_stripe_form.unblock();
397
- },
398
-
399
  getOwnerDetails: function() {
400
  var first_name = $( '#billing_first_name' ).length ? $( '#billing_first_name' ).val() : wc_stripe_params.billing_first_name,
401
  last_name = $( '#billing_last_name' ).length ? $( '#billing_last_name' ).val() : wc_stripe_params.billing_last_name,
@@ -630,10 +581,10 @@ jQuery( function( $ ) {
630
  // Stripe Checkout.
631
  if ( 'yes' === wc_stripe_params.is_stripe_checkout && wc_stripe_form.isStripeModalNeeded() && wc_stripe_form.isStripeCardChosen() ) {
632
  // Since in mobile actions cannot be deferred, no dynamic validation applied.
633
- if ( wc_stripe_form.isMobile() ) {
634
  wc_stripe_form.openModal();
635
  } else {
636
- wc_stripe_form.validateCheckout( 'modal' );
637
  }
638
 
639
  return false;
@@ -680,10 +631,6 @@ jQuery( function( $ ) {
680
  this.onSubmit
681
  );
682
 
683
- if ( wc_stripe_form.isMobile() ) {
684
- wc_stripe_form.unblock();
685
- }
686
-
687
  wc_stripe_form.form.submit();
688
  }
689
 
@@ -694,10 +641,6 @@ jQuery( function( $ ) {
694
  this.onSubmit
695
  );
696
 
697
- if ( wc_stripe_form.isMobile() ) {
698
- wc_stripe_form.unblock();
699
- }
700
-
701
  return true;
702
  }
703
 
@@ -708,20 +651,11 @@ jQuery( function( $ ) {
708
  this.onSubmit
709
  );
710
 
711
- if ( wc_stripe_form.isMobile() ) {
712
- wc_stripe_form.unblock();
713
- }
714
-
715
  wc_stripe_form.form.submit();
716
  }
717
  }
718
 
719
- // We don't need to run validate on non checkout pages.
720
- if ( wc_stripe_params.is_checkout ) {
721
- wc_stripe_form.validateCheckout();
722
- } else {
723
- wc_stripe_form.createSource();
724
- }
725
 
726
  // Prevent form submitting
727
  return false;
@@ -753,7 +687,7 @@ jQuery( function( $ ) {
753
  },
754
 
755
  reset: function() {
756
- $( '.wc-stripe-error, .stripe-source, .stripe_token, .stripe-checkout-object' ).remove();
757
 
758
  // Stripe Checkout.
759
  if ( 'yes' === wc_stripe_params.is_stripe_checkout ) {
@@ -762,56 +696,70 @@ jQuery( function( $ ) {
762
  },
763
 
764
  getRequiredFields: function() {
765
- return wc_stripe_form.form.find( '.form-row.validate-required > input, .form-row.validate-required > select, .form-row.validate-required > textarea' );
766
  },
767
 
768
- validateCheckout: function( type ) {
769
- if ( typeof type === 'undefined' ) {
770
- type = '';
771
- }
772
-
773
  var data = {
774
  'nonce': wc_stripe_params.stripe_nonce,
775
  'required_fields': wc_stripe_form.getRequiredFields().serialize(),
776
- 'all_fields': wc_stripe_form.form.serialize(),
777
- 'source_type': wc_stripe_form.getSelectedPaymentElement().val(),
778
- 'is_add_payment_page': wc_stripe_params.is_add_payment_method_page
779
  };
780
 
781
- $.ajax({
782
  type: 'POST',
783
  url: wc_stripe_form.getAjaxURL( 'validate_checkout' ),
784
  data: data,
785
  dataType: 'json',
786
  success: function( result ) {
787
  if ( 'success' === result ) {
788
- // Stripe Checkout.
789
- if ( 'modal' === type ) {
790
- wc_stripe_form.openModal();
791
- } else {
792
- if ( wc_stripe_form.isSepaChosen() ) {
793
- // Check if SEPA owner is filled before proceed.
794
- if ( '' === $( '#stripe-sepa-owner' ).val() ) {
795
- $( document.body ).trigger( 'stripeError', { error: { message: wc_stripe_params.no_sepa_owner_msg } } );
796
- return false;
797
- }
798
-
799
- // Check if SEPA IBAN is filled before proceed.
800
- if ( '' === $( '#stripe-sepa-iban' ).val() ) {
801
- $( document.body ).trigger( 'stripeError', { error: { message: wc_stripe_params.no_sepa_iban_msg } } );
802
- return false;
803
- }
804
- }
805
-
806
- wc_stripe_form.createSource();
807
- }
808
  } else if ( result.messages ) {
809
  wc_stripe_form.resetModal();
810
  wc_stripe_form.reset();
811
  wc_stripe_form.submitError( result.messages );
812
  }
813
  }
814
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
815
  },
816
 
817
  submitError: function( error_message ) {
6
  var stripe = Stripe( wc_stripe_params.key );
7
 
8
  if ( 'yes' === wc_stripe_params.use_elements ) {
9
+ var stripe_elements_options = wc_stripe_params.elements_options.length ? wc_stripe_params.elements_options : {},
10
+ elements = stripe.elements( stripe_elements_options ),
11
  stripe_card,
12
  stripe_exp,
13
  stripe_cvc;
29
  .replace( '%%endpoint%%', 'wc_stripe_' + endpoint );
30
  },
31
 
32
+ unmountElements: function() {
33
+ if ( 'yes' === wc_stripe_params.inline_cc_form ) {
34
+ stripe_card.unmount( '#stripe-card-element' );
35
+ } else {
36
+ stripe_card.unmount( '#stripe-card-element' );
37
+ stripe_exp.unmount( '#stripe-exp-element' );
38
+ stripe_cvc.unmount( '#stripe-cvc-element' );
39
+ }
40
+ },
41
+
42
+ mountElements: function() {
43
+ if ( ! $( '#stripe-card-element' ).length ) {
44
+ return;
45
+ }
46
+ if ( 'yes' === wc_stripe_params.inline_cc_form ) {
47
+ stripe_card.mount( '#stripe-card-element' );
48
+ } else {
49
+ stripe_card.mount( '#stripe-card-element' );
50
+ stripe_exp.mount( '#stripe-exp-element' );
51
+ stripe_cvc.mount( '#stripe-cvc-element' );
52
+ }
53
+ },
54
+
55
+ createElements: function() {
56
+ var elementStyles = {
57
+ base: {
58
+ iconColor: '#666EE8',
59
+ color: '#31325F',
60
+ fontSize: '15px',
61
+ '::placeholder': {
62
+ color: '#CFD7E0',
63
+ }
64
+ }
65
+ };
66
+
67
+ var elementClasses = {
68
+ focus: 'focused',
69
+ empty: 'empty',
70
+ invalid: 'invalid',
71
+ };
72
+
73
+ elementStyles = wc_stripe_params.elements_styling ? wc_stripe_params.elements_styling : elementStyles;
74
+ elementClasses = wc_stripe_params.elements_classes ? wc_stripe_params.elements_classes : elementClasses;
75
+
76
+ if ( 'yes' === wc_stripe_params.inline_cc_form ) {
77
+ stripe_card = elements.create( 'card', { style: elementStyles, hidePostalCode: true } );
78
+
79
+ stripe_card.addEventListener( 'change', function( event ) {
80
+ wc_stripe_form.onCCFormChange();
81
+
82
+ if ( event.error ) {
83
+ $( document.body ).trigger( 'stripeError', event );
84
+ }
85
+ } );
86
+ } else {
87
+ stripe_card = elements.create( 'cardNumber', { style: elementStyles, classes: elementClasses } );
88
+ stripe_exp = elements.create( 'cardExpiry', { style: elementStyles, classes: elementClasses } );
89
+ stripe_cvc = elements.create( 'cardCvc', { style: elementStyles, classes: elementClasses } );
90
+
91
+ stripe_card.addEventListener( 'change', function( event ) {
92
+ wc_stripe_form.onCCFormChange();
93
+
94
+ if ( event.error ) {
95
+ $( document.body ).trigger( 'stripeError', event );
96
+ }
97
+ } );
98
+
99
+ stripe_exp.addEventListener( 'change', function( event ) {
100
+ wc_stripe_form.onCCFormChange();
101
+
102
+ if ( event.error ) {
103
+ $( document.body ).trigger( 'stripeError', event );
104
+ }
105
+ } );
106
+
107
+ stripe_cvc.addEventListener( 'change', function( event ) {
108
+ wc_stripe_form.onCCFormChange();
109
+
110
+ if ( event.error ) {
111
+ $( document.body ).trigger( 'stripeError', event );
112
+ }
113
+ } );
114
+ }
115
+
116
+ /**
117
+ * Only in checkout page we need to delay the mounting of the
118
+ * card as some AJAX process needs to happen before we do.
119
+ */
120
+ if ( 'yes' === wc_stripe_params.is_checkout ) {
121
+ $( document.body ).on( 'updated_checkout', function() {
122
+ // Don't mount elements a second time.
123
+ if ( stripe_card ) {
124
+ wc_stripe_form.unmountElements();
125
+ }
126
+
127
+ wc_stripe_form.mountElements();
128
+ } );
129
+ } else if ( $( 'form#add_payment_method' ).length || $( 'form#order_review' ).length ) {
130
+ wc_stripe_form.mountElements();
131
+ }
132
+ },
133
+
134
  /**
135
  * Initialize event handlers and UI state.
136
  */
137
  init: function() {
138
  // Initialize tokenization script if on change payment method page and pay for order page.
139
+ if ( 'yes' === wc_stripe_params.is_change_payment_page || 'yes' === wc_stripe_params.is_pay_for_order_page ) {
140
  $( document.body ).trigger( 'wc-credit-card-form-init' );
141
  }
142
 
159
  this.form = $( 'form#order_review' );
160
  }
161
 
162
+ $( 'form#order_review, form#add_payment_method' )
163
  .on(
164
  'submit',
165
  this.onSubmit
170
  this.form = $( 'form#add_payment_method' );
171
  }
172
 
 
 
 
 
 
 
173
  $( 'form.woocommerce-checkout' )
174
  .on(
175
  'change',
 
176
  this.reset
177
  );
178
 
186
  this.reset
187
  );
188
 
189
+ if ( 'yes' === wc_stripe_params.use_elements ) {
190
+ wc_stripe_form.createElements();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  }
192
  },
193
 
274
  },
275
 
276
  block: function() {
277
+ if ( ! wc_stripe_form.isMobile() ) {
 
 
 
 
 
 
 
 
278
  wc_stripe_form.form.block({
279
  message: null,
280
  overlayCSS: {
286
  },
287
 
288
  unblock: function() {
289
+ wc_stripe_form.form.unblock();
 
 
 
 
290
  },
291
 
292
  getSelectedPaymentElement: function() {
347
  wc_stripe_form.unblock();
348
  },
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  getOwnerDetails: function() {
351
  var first_name = $( '#billing_first_name' ).length ? $( '#billing_first_name' ).val() : wc_stripe_params.billing_first_name,
352
  last_name = $( '#billing_last_name' ).length ? $( '#billing_last_name' ).val() : wc_stripe_params.billing_last_name,
581
  // Stripe Checkout.
582
  if ( 'yes' === wc_stripe_params.is_stripe_checkout && wc_stripe_form.isStripeModalNeeded() && wc_stripe_form.isStripeCardChosen() ) {
583
  // Since in mobile actions cannot be deferred, no dynamic validation applied.
584
+ if ( wc_stripe_form.isMobile() || 'no' === wc_stripe_params.validate_modal_checkout ) {
585
  wc_stripe_form.openModal();
586
  } else {
587
+ wc_stripe_form.validateCheckout();
588
  }
589
 
590
  return false;
631
  this.onSubmit
632
  );
633
 
 
 
 
 
634
  wc_stripe_form.form.submit();
635
  }
636
 
641
  this.onSubmit
642
  );
643
 
 
 
 
 
644
  return true;
645
  }
646
 
651
  this.onSubmit
652
  );
653
 
 
 
 
 
654
  wc_stripe_form.form.submit();
655
  }
656
  }
657
 
658
+ wc_stripe_form.createSource();
 
 
 
 
 
659
 
660
  // Prevent form submitting
661
  return false;
687
  },
688
 
689
  reset: function() {
690
+ $( '.wc-stripe-error, .stripe-source, .stripe_token' ).remove();
691
 
692
  // Stripe Checkout.
693
  if ( 'yes' === wc_stripe_params.is_stripe_checkout ) {
696
  },
697
 
698
  getRequiredFields: function() {
699
+ return wc_stripe_form.form.find( '.form-row.validate-required > input:visible, .form-row.validate-required > select:visible, .form-row.validate-required > textarea:visible' );
700
  },
701
 
702
+ validateCheckout: function() {
 
 
 
 
703
  var data = {
704
  'nonce': wc_stripe_params.stripe_nonce,
705
  'required_fields': wc_stripe_form.getRequiredFields().serialize(),
706
+ 'all_fields': wc_stripe_form.form.serialize()
 
 
707
  };
708
 
709
+ $.ajax( {
710
  type: 'POST',
711
  url: wc_stripe_form.getAjaxURL( 'validate_checkout' ),
712
  data: data,
713
  dataType: 'json',
714
  success: function( result ) {
715
  if ( 'success' === result ) {
716
+ wc_stripe_form.openModal();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
717
  } else if ( result.messages ) {
718
  wc_stripe_form.resetModal();
719
  wc_stripe_form.reset();
720
  wc_stripe_form.submitError( result.messages );
721
  }
722
  }
723
+ } );
724
+ },
725
+
726
+ onError: function( e, result ) {
727
+ var message = result.error.message,
728
+ errorContainer = wc_stripe_form.getSelectedPaymentElement().parents( 'li' ).eq(0).find( '.stripe-source-errors' );
729
+
730
+ /*
731
+ * Customers do not need to know the specifics of the below type of errors
732
+ * therefore return a generic localizable error message.
733
+ */
734
+ if (
735
+ 'invalid_request_error' === result.error.type ||
736
+ 'api_connection_error' === result.error.type ||
737
+ 'api_error' === result.error.type ||
738
+ 'authentication_error' === result.error.type ||
739
+ 'rate_limit_error' === result.error.type
740
+ ) {
741
+ message = wc_stripe_params.invalid_request_error;
742
+ }
743
+
744
+ if ( 'card_error' === result.error.type && wc_stripe_params.hasOwnProperty( result.error.code ) ) {
745
+ message = wc_stripe_params[ result.error.code ];
746
+ }
747
+
748
+ if ( 'validation_error' === result.error.type && wc_stripe_params.hasOwnProperty( result.error.code ) ) {
749
+ message = wc_stripe_params[ result.error.code ];
750
+ }
751
+
752
+ wc_stripe_form.reset();
753
+ $( '.woocommerce-NoticeGroup-checkout' ).remove();
754
+ console.log( result.error.message ); // Leave for troubleshooting.
755
+ $( errorContainer ).html( '<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li>' + message + '</li></ul>' );
756
+
757
+ if ( $( '.wc-stripe-error' ).length ) {
758
+ $( 'html, body' ).animate({
759
+ scrollTop: ( $( '.wc-stripe-error' ).offset().top - 200 )
760
+ }, 200 );
761
+ }
762
+ wc_stripe_form.unblock();
763
  },
764
 
765
  submitError: function( error_message ) {
assets/js/stripe.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(function(e){"use strict";var r=Stripe(wc_stripe_params.key);if("yes"===wc_stripe_params.use_elements)var t,i,o,s=wc_stripe_params.elements_options.length?wc_stripe_params.elements_options:{},a=r.elements(s);var n={getAjaxURL:function(e){return wc_stripe_params.ajaxurl.toString().replace("%%endpoint%%","wc_stripe_"+e)},init:function(){"yes"===wc_stripe_params.is_change_payment_page&&e(document.body).trigger("wc-credit-card-form-init"),this.stripe_checkout_submit=!1,e("form.woocommerce-checkout").length&&(this.form=e("form.woocommerce-checkout")),e("form.woocommerce-checkout").on("checkout_place_order_stripe checkout_place_order_stripe_bancontact checkout_place_order_stripe_sofort checkout_place_order_stripe_giropay checkout_place_order_stripe_ideal checkout_place_order_stripe_alipay checkout_place_order_stripe_sepa checkout_place_order_stripe_bitcoin",this.onSubmit),e("form#order_review").length&&(this.form=e("form#order_review")),e("form#order_review").on("submit",this.onSubmit),e("form#add_payment_method").length&&(this.form=e("form#add_payment_method")),e("form#add_payment_method").on("submit",this.onSubmit),e("form.woocommerce-checkout").on("change","#stripe-bank-country",this.reset),e(document).on("stripeError",this.onError).on("checkout_error",this.reset);var r={base:{iconColor:"#666EE8",color:"#31325F",fontSize:"15px","::placeholder":{color:"#CFD7E0"}}},s={focus:"focused",empty:"empty",invalid:"invalid"};"yes"===wc_stripe_params.use_elements&&e("#stripe-card-element").length&&(r=wc_stripe_params.elements_styling?wc_stripe_params.elements_styling:r,s=wc_stripe_params.elements_classes?wc_stripe_params.elements_classes:s,"yes"===wc_stripe_params.inline_cc_form?(t=a.create("card",{style:r,hidePostalCode:!0})).addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)}):(t=a.create("cardNumber",{style:r,classes:s}),i=a.create("cardExpiry",{style:r,classes:s}),o=a.create("cardCvc",{style:r,classes:s}),t.addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)}),i.addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)}),o.addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)})),wc_stripe_params.is_checkout?e(document.body).on("updated_checkout",function(){t&&("yes"===wc_stripe_params.inline_cc_form?t.unmount("#stripe-card-element"):(t.unmount("#stripe-card-element"),i.unmount("#stripe-exp-element"),o.unmount("#stripe-cvc-element"))),"yes"===wc_stripe_params.inline_cc_form?t.mount("#stripe-card-element"):(t.mount("#stripe-card-element"),i.mount("#stripe-exp-element"),o.mount("#stripe-cvc-element"))}):(e("form#add_payment_method").length||e("form#order_review").length)&&("yes"===wc_stripe_params.inline_cc_form?t.mount("#stripe-card-element"):(t.mount("#stripe-card-element"),i.mount("#stripe-exp-element"),o.mount("#stripe-cvc-element"))))},isStripeChosen:function(){return e("#payment_method_stripe, #payment_method_stripe_bancontact, #payment_method_stripe_sofort, #payment_method_stripe_giropay, #payment_method_stripe_ideal, #payment_method_stripe_alipay, #payment_method_stripe_sepa, #payment_method_stripe_bitcoin").is(":checked")||e("#payment_method_stripe").is(":checked")&&"new"===e('input[name="wc-stripe-payment-token"]:checked').val()||e("#payment_method_stripe_sepa").is(":checked")&&"new"===e('input[name="wc-stripe-payment-token"]:checked').val()},isStripeSaveCardChosen:function(){return e("#payment_method_stripe").is(":checked")&&e('input[name="wc-stripe-payment-token"]').is(":checked")&&"new"!==e('input[name="wc-stripe-payment-token"]:checked').val()||e("#payment_method_stripe_sepa").is(":checked")&&e('input[name="wc-stripe_sepa-payment-token"]').is(":checked")&&"new"!==e('input[name="wc-stripe_sepa-payment-token"]:checked').val()},isStripeCardChosen:function(){return e("#payment_method_stripe").is(":checked")},isBancontactChosen:function(){return e("#payment_method_stripe_bancontact").is(":checked")},isGiropayChosen:function(){return e("#payment_method_stripe_giropay").is(":checked")},isIdealChosen:function(){return e("#payment_method_stripe_ideal").is(":checked")},isSofortChosen:function(){return e("#payment_method_stripe_sofort").is(":checked")},isAlipayChosen:function(){return e("#payment_method_stripe_alipay").is(":checked")},isSepaChosen:function(){return e("#payment_method_stripe_sepa").is(":checked")},isBitcoinChosen:function(){return e("#payment_method_stripe_bitcoin").is(":checked")},isP24Chosen:function(){return e("#payment_method_stripe_p24").is(":checked")},hasSource:function(){return 0<e("input.stripe-source").length},hasToken:function(){return 0<e("input.stripe_token").length},isMobile:function(){return!!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},isStripeModalNeeded:function(e){var r=n.form.find("input.stripe_token");return(!n.stripe_submit||!r)&&!!n.isStripeChosen()},block:function(){n.isMobile()?e.blockUI({message:null,overlayCSS:{background:"#fff",opacity:.6}}):n.form.block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},unblock:function(){n.isMobile()?e.unblockUI():n.form.unblock()},getSelectedPaymentElement:function(){return e('.payment_methods input[name="payment_method"]:checked')},openModal:function(){var t=n.form,i=e("#stripe-payment-data");n.reset();StripeCheckout.open({key:wc_stripe_params.key,billingAddress:"yes"===wc_stripe_params.stripe_checkout_require_billing_address,amount:i.data("amount"),name:i.data("name"),description:i.data("description"),currency:i.data("currency"),image:i.data("image"),bitcoin:i.data("bitcoin"),locale:i.data("locale"),email:e("#billing_email").val()||i.data("email"),panelLabel:i.data("panel-label"),allowRememberMe:i.data("allow-remember-me"),token:function(e){if(t.find("input.stripe_source").remove(),"token"===e.object)r.createSource({type:"card",token:e.id}).then(n.sourceResponse);else if("source"===e.object){var i={source:e};n.sourceResponse(i)}},closed:n.onClose()})},resetModal:function(){n.reset(),n.stripe_checkout_submit=!1},onClose:function(){n.unblock()},onError:function(r,t){var i=t.error.message,o=n.getSelectedPaymentElement().parents("li").eq(0).find(".stripe-source-errors");"invalid_request_error"!==t.error.type&&"api_connection_error"!==t.error.type&&"api_error"!==t.error.type&&"authentication_error"!==t.error.type&&"rate_limit_error"!==t.error.type||(i=wc_stripe_params.invalid_request_error),"card_error"===t.error.type&&wc_stripe_params.hasOwnProperty(t.error.code)&&(i=wc_stripe_params[t.error.code]),"validation_error"===t.error.type&&wc_stripe_params.hasOwnProperty(t.error.code)&&(i=wc_stripe_params[t.error.code]),n.reset(),e(".woocommerce-NoticeGroup-checkout").remove(),console.log(t.error.message),e(o).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li>'+i+"</li></ul>"),e(".wc-stripe-error").length&&e("html, body").animate({scrollTop:e(".wc-stripe-error").offset().top-200},200),n.unblock()},getOwnerDetails:function(){var r=e("#billing_first_name").length?e("#billing_first_name").val():wc_stripe_params.billing_first_name,t=e("#billing_last_name").length?e("#billing_last_name").val():wc_stripe_params.billing_last_name,i={owner:{name:"",address:{},email:"",phone:""}};return i.owner.name=r,r&&t&&(i.owner.name=r+" "+t),i.owner.email=e("#billing_email").val(),i.owner.phone=e("#billing_phone").val(),void 0!==i.owner.phone&&0>=i.owner.phone.length&&delete i.owner.phone,void 0!==i.owner.email&&0>=i.owner.email.length&&delete i.owner.email,void 0!==i.owner.name&&0>=i.owner.name.length&&delete i.owner.name,e("#billing_address_1").length>0?(i.owner.address.line1=e("#billing_address_1").val(),i.owner.address.line2=e("#billing_address_2").val(),i.owner.address.state=e("#billing_state").val(),i.owner.address.city=e("#billing_city").val(),i.owner.address.postal_code=e("#billing_postcode").val(),i.owner.address.country=e("#billing_country").val()):wc_stripe_params.billing_address_1&&(i.owner.address.line1=wc_stripe_params.billing_address_1,i.owner.address.line2=wc_stripe_params.billing_address_2,i.owner.address.state=wc_stripe_params.billing_state,i.owner.address.city=wc_stripe_params.billing_city,i.owner.address.postal_code=wc_stripe_params.billing_postcode,i.owner.address.country=wc_stripe_params.billing_country),i},createSource:function(){var i=n.getOwnerDetails(),o="card";if(n.isBancontactChosen()&&(o="bancontact"),n.isSepaChosen()&&(o="sepa_debit"),n.isIdealChosen()&&(o="ideal"),n.isSofortChosen()&&(o="sofort"),n.isBitcoinChosen()&&(o="bitcoin"),n.isGiropayChosen()&&(o="giropay"),n.isAlipayChosen()&&(o="alipay"),"card"===o)r.createSource(t,i).then(n.sourceResponse);else{switch(o){case"bancontact":case"giropay":case"ideal":case"sofort":case"alipay":i.amount=e("#stripe-"+o+"-payment-data").data("amount"),i.currency=e("#stripe-"+o+"-payment-data").data("currency"),i.redirect={return_url:wc_stripe_params.return_url},wc_stripe_params.statement_descriptor&&(i.statement_descriptor=wc_stripe_params.statement_descriptor)}switch(o){case"sepa_debit":var s=e("#stripe-payment-data"),a=e("#billing_email").length?e("#billing_email").val():s.data("email");i.currency=e("#stripe-"+o+"-payment-data").data("currency"),i.owner.name=e("#stripe-sepa-owner").val(),i.owner.email=a,i.sepa_debit={iban:e("#stripe-sepa-iban").val()},i.mandate={notification_method:wc_stripe_params.sepa_mandate_notification};break;case"ideal":i.ideal={bank:e("#stripe-ideal-bank").val()};break;case"bitcoin":case"alipay":i.currency=e("#stripe-"+o+"-payment-data").data("currency"),i.amount=e("#stripe-"+o+"-payment-data").data("amount");break;case"sofort":i.sofort={country:e("#billing_country").val()}}i.type=o,r.createSource(i).then(n.sourceResponse)}},sourceResponse:function(r){r.error?e(document.body).trigger("stripeError",r):"no"===wc_stripe_params.allow_prepaid_card&&"card"===r.source.type&&"prepaid"===r.source.card.funding?(r.error={message:wc_stripe_params.no_prepaid_card_msg},wc_stripe_params.is_stripe_checkout?n.submitError('<ul class="woocommerce-error"><li>'+wc_stripe_params.no_prepaid_card_msg+"</li></ul>"):e(document.body).trigger("stripeError",r)):n.processStripeResponse(r.source)},processStripeResponse:function(r){n.reset(),n.form.append("<input type='hidden' class='stripe-source' name='stripe_source' value='"+r.id+"'/>"),e("form#add_payment_method").length&&e(n.form).off("submit",n.form.onSubmit),n.form.submit()},createToken:function(){var r=e("#stripe-card-number").val(),t=e("#stripe-card-cvc").val(),i=e("#stripe-card-expiry").payment("cardExpiryVal"),o=e("#billing_first_name").length?e("#billing_first_name").val():wc_stripe_params.billing_first_name,s=e("#billing_last_name").length?e("#billing_last_name").val():wc_stripe_params.billing_last_name,a={number:r,cvc:t,exp_month:parseInt(i.month,10)||0,exp_year:parseInt(i.year,10)||0};o&&s&&(a.name=o+" "+s),e("#billing_address_1").length>0?(a.address_line1=e("#billing_address_1").val(),a.address_line2=e("#billing_address_2").val(),a.address_state=e("#billing_state").val(),a.address_city=e("#billing_city").val(),a.address_zip=e("#billing_postcode").val(),a.address_country=e("#billing_country").val()):wc_stripe_params.billing_address_1&&(a.address_line1=wc_stripe_params.billing_address_1,a.address_line2=wc_stripe_params.billing_address_2,a.address_state=wc_stripe_params.billing_state,a.address_city=wc_stripe_params.billing_city,a.address_zip=wc_stripe_params.billing_postcode,a.address_country=wc_stripe_params.billing_country),Stripe.setPublishableKey(wc_stripe_params.key),Stripe.createToken(a,n.onStripeTokenResponse)},onStripeTokenResponse:function(r,t){if(t.error)e(document).trigger("stripeError",t);else{if("no"===wc_stripe_params.allow_prepaid_card&&"prepaid"===t.card.funding)return t.error={message:wc_stripe_params.no_prepaid_card_msg},e(document).trigger("stripeError",{response:t}),!1;var i=t.id;n.form.append("<input type='hidden' class='stripe_token' name='stripe_token' value='"+i+"'/>"),e("form#add_payment_method").length&&e(n.form).off("submit",n.form.onSubmit),n.form.submit()}},onSubmit:function(r){if(n.isStripeChosen()&&!n.isStripeSaveCardChosen()&&!n.hasSource()&&!n.hasToken()){if(r.preventDefault(),"yes"===wc_stripe_params.is_stripe_checkout&&n.isStripeModalNeeded()&&n.isStripeCardChosen())return n.isMobile()?n.openModal():n.validateCheckout("modal"),!1;if(n.block(),n.isStripeCardChosen()&&"no"===wc_stripe_params.use_elements)return n.createToken(),!1;if(n.isSepaChosen()){if(""===e("#stripe-sepa-owner").val())return e(document.body).trigger("stripeError",{error:{message:wc_stripe_params.no_sepa_owner_msg}}),!1;if(""===e("#stripe-sepa-iban").val())return e(document.body).trigger("stripeError",{error:{message:wc_stripe_params.no_sepa_iban_msg}}),!1}if(n.isBancontactChosen()||n.isGiropayChosen()||n.isIdealChosen()||n.isAlipayChosen()||n.isSofortChosen()||n.isP24Chosen()){if(e("form#order_review").length&&(e("form#order_review").off("submit",this.onSubmit),n.isMobile()&&n.unblock(),n.form.submit()),e("form.woocommerce-checkout").length)return e("form.woocommerce-checkout").off("submit",this.onSubmit),n.isMobile()&&n.unblock(),!0;e("form#add_payment_method").length&&(e("form#add_payment_method").off("submit",this.onSubmit),n.isMobile()&&n.unblock(),n.form.submit())}return wc_stripe_params.is_checkout?n.validateCheckout():n.createSource(),!1}if(e("form#add_payment_method").length)return r.preventDefault(),"yes"===wc_stripe_params.is_stripe_checkout&&n.isStripeModalNeeded()&&n.isStripeCardChosen()?(n.openModal(),!1):(n.block(),n.isStripeCardChosen()&&"no"===wc_stripe_params.use_elements?(n.createToken(),!1):(n.createSource(),!1))},onCCFormChange:function(){n.reset()},reset:function(){e(".wc-stripe-error, .stripe-source, .stripe_token, .stripe-checkout-object").remove(),"yes"===wc_stripe_params.is_stripe_checkout&&(n.stripe_submit=!1)},getRequiredFields:function(){return n.form.find(".form-row.validate-required > input, .form-row.validate-required > select, .form-row.validate-required > textarea")},validateCheckout:function(r){void 0===r&&(r="");var t={nonce:wc_stripe_params.stripe_nonce,required_fields:n.getRequiredFields().serialize(),all_fields:n.form.serialize(),source_type:n.getSelectedPaymentElement().val(),is_add_payment_page:wc_stripe_params.is_add_payment_method_page};e.ajax({type:"POST",url:n.getAjaxURL("validate_checkout"),data:t,dataType:"json",success:function(t){if("success"===t)if("modal"===r)n.openModal();else{if(n.isSepaChosen()){if(""===e("#stripe-sepa-owner").val())return e(document.body).trigger("stripeError",{error:{message:wc_stripe_params.no_sepa_owner_msg}}),!1;if(""===e("#stripe-sepa-iban").val())return e(document.body).trigger("stripeError",{error:{message:wc_stripe_params.no_sepa_iban_msg}}),!1}n.createSource()}else t.messages&&(n.resetModal(),n.reset(),n.submitError(t.messages))}})},submitError:function(r){e(".woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message").remove(),n.form.prepend('<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">'+r+"</div>"),n.form.removeClass("processing").unblock(),n.form.find(".input-text, select, input:checkbox").blur();var t="";e("#add_payment_method").length&&(t=e("#add_payment_method")),e("#order_review").length&&(t=e("#order_review")),e("form.checkout").length&&(t=e("form.checkout")),t.length&&e("html, body").animate({scrollTop:t.offset().top-100},500),e(document.body).trigger("checkout_error"),n.unblock()}};n.init()});
1
+ jQuery(function(e){"use strict";var r=Stripe(wc_stripe_params.key);if("yes"===wc_stripe_params.use_elements)var t,i,o,s=wc_stripe_params.elements_options.length?wc_stripe_params.elements_options:{},a=r.elements(s);var n={getAjaxURL:function(e){return wc_stripe_params.ajaxurl.toString().replace("%%endpoint%%","wc_stripe_"+e)},unmountElements:function(){"yes"===wc_stripe_params.inline_cc_form?t.unmount("#stripe-card-element"):(t.unmount("#stripe-card-element"),i.unmount("#stripe-exp-element"),o.unmount("#stripe-cvc-element"))},mountElements:function(){e("#stripe-card-element").length&&("yes"===wc_stripe_params.inline_cc_form?t.mount("#stripe-card-element"):(t.mount("#stripe-card-element"),i.mount("#stripe-exp-element"),o.mount("#stripe-cvc-element")))},createElements:function(){var r={base:{iconColor:"#666EE8",color:"#31325F",fontSize:"15px","::placeholder":{color:"#CFD7E0"}}},s={focus:"focused",empty:"empty",invalid:"invalid"};r=wc_stripe_params.elements_styling?wc_stripe_params.elements_styling:r,s=wc_stripe_params.elements_classes?wc_stripe_params.elements_classes:s,"yes"===wc_stripe_params.inline_cc_form?(t=a.create("card",{style:r,hidePostalCode:!0})).addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)}):(t=a.create("cardNumber",{style:r,classes:s}),i=a.create("cardExpiry",{style:r,classes:s}),o=a.create("cardCvc",{style:r,classes:s}),t.addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)}),i.addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)}),o.addEventListener("change",function(r){n.onCCFormChange(),r.error&&e(document.body).trigger("stripeError",r)})),"yes"===wc_stripe_params.is_checkout?e(document.body).on("updated_checkout",function(){t&&n.unmountElements(),n.mountElements()}):(e("form#add_payment_method").length||e("form#order_review").length)&&n.mountElements()},init:function(){"yes"!==wc_stripe_params.is_change_payment_page&&"yes"!==wc_stripe_params.is_pay_for_order_page||e(document.body).trigger("wc-credit-card-form-init"),this.stripe_checkout_submit=!1,e("form.woocommerce-checkout").length&&(this.form=e("form.woocommerce-checkout")),e("form.woocommerce-checkout").on("checkout_place_order_stripe checkout_place_order_stripe_bancontact checkout_place_order_stripe_sofort checkout_place_order_stripe_giropay checkout_place_order_stripe_ideal checkout_place_order_stripe_alipay checkout_place_order_stripe_sepa checkout_place_order_stripe_bitcoin",this.onSubmit),e("form#order_review").length&&(this.form=e("form#order_review")),e("form#order_review, form#add_payment_method").on("submit",this.onSubmit),e("form#add_payment_method").length&&(this.form=e("form#add_payment_method")),e("form.woocommerce-checkout").on("change",this.reset),e(document).on("stripeError",this.onError).on("checkout_error",this.reset),"yes"===wc_stripe_params.use_elements&&n.createElements()},isStripeChosen:function(){return e("#payment_method_stripe, #payment_method_stripe_bancontact, #payment_method_stripe_sofort, #payment_method_stripe_giropay, #payment_method_stripe_ideal, #payment_method_stripe_alipay, #payment_method_stripe_sepa, #payment_method_stripe_bitcoin").is(":checked")||e("#payment_method_stripe").is(":checked")&&"new"===e('input[name="wc-stripe-payment-token"]:checked').val()||e("#payment_method_stripe_sepa").is(":checked")&&"new"===e('input[name="wc-stripe-payment-token"]:checked').val()},isStripeSaveCardChosen:function(){return e("#payment_method_stripe").is(":checked")&&e('input[name="wc-stripe-payment-token"]').is(":checked")&&"new"!==e('input[name="wc-stripe-payment-token"]:checked').val()||e("#payment_method_stripe_sepa").is(":checked")&&e('input[name="wc-stripe_sepa-payment-token"]').is(":checked")&&"new"!==e('input[name="wc-stripe_sepa-payment-token"]:checked').val()},isStripeCardChosen:function(){return e("#payment_method_stripe").is(":checked")},isBancontactChosen:function(){return e("#payment_method_stripe_bancontact").is(":checked")},isGiropayChosen:function(){return e("#payment_method_stripe_giropay").is(":checked")},isIdealChosen:function(){return e("#payment_method_stripe_ideal").is(":checked")},isSofortChosen:function(){return e("#payment_method_stripe_sofort").is(":checked")},isAlipayChosen:function(){return e("#payment_method_stripe_alipay").is(":checked")},isSepaChosen:function(){return e("#payment_method_stripe_sepa").is(":checked")},isBitcoinChosen:function(){return e("#payment_method_stripe_bitcoin").is(":checked")},isP24Chosen:function(){return e("#payment_method_stripe_p24").is(":checked")},hasSource:function(){return 0<e("input.stripe-source").length},hasToken:function(){return 0<e("input.stripe_token").length},isMobile:function(){return!!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},isStripeModalNeeded:function(e){var r=n.form.find("input.stripe_token");return(!n.stripe_submit||!r)&&!!n.isStripeChosen()},block:function(){n.isMobile()||n.form.block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},unblock:function(){n.form.unblock()},getSelectedPaymentElement:function(){return e('.payment_methods input[name="payment_method"]:checked')},openModal:function(){var t=n.form,i=e("#stripe-payment-data");n.reset();StripeCheckout.open({key:wc_stripe_params.key,billingAddress:"yes"===wc_stripe_params.stripe_checkout_require_billing_address,amount:i.data("amount"),name:i.data("name"),description:i.data("description"),currency:i.data("currency"),image:i.data("image"),bitcoin:i.data("bitcoin"),locale:i.data("locale"),email:e("#billing_email").val()||i.data("email"),panelLabel:i.data("panel-label"),allowRememberMe:i.data("allow-remember-me"),token:function(e){if(t.find("input.stripe_source").remove(),"token"===e.object)r.createSource({type:"card",token:e.id}).then(n.sourceResponse);else if("source"===e.object){var i={source:e};n.sourceResponse(i)}},closed:n.onClose()})},resetModal:function(){n.reset(),n.stripe_checkout_submit=!1},onClose:function(){n.unblock()},getOwnerDetails:function(){var r=e("#billing_first_name").length?e("#billing_first_name").val():wc_stripe_params.billing_first_name,t=e("#billing_last_name").length?e("#billing_last_name").val():wc_stripe_params.billing_last_name,i={owner:{name:"",address:{},email:"",phone:""}};return i.owner.name=r,r&&t&&(i.owner.name=r+" "+t),i.owner.email=e("#billing_email").val(),i.owner.phone=e("#billing_phone").val(),void 0!==i.owner.phone&&0>=i.owner.phone.length&&delete i.owner.phone,void 0!==i.owner.email&&0>=i.owner.email.length&&delete i.owner.email,void 0!==i.owner.name&&0>=i.owner.name.length&&delete i.owner.name,e("#billing_address_1").length>0?(i.owner.address.line1=e("#billing_address_1").val(),i.owner.address.line2=e("#billing_address_2").val(),i.owner.address.state=e("#billing_state").val(),i.owner.address.city=e("#billing_city").val(),i.owner.address.postal_code=e("#billing_postcode").val(),i.owner.address.country=e("#billing_country").val()):wc_stripe_params.billing_address_1&&(i.owner.address.line1=wc_stripe_params.billing_address_1,i.owner.address.line2=wc_stripe_params.billing_address_2,i.owner.address.state=wc_stripe_params.billing_state,i.owner.address.city=wc_stripe_params.billing_city,i.owner.address.postal_code=wc_stripe_params.billing_postcode,i.owner.address.country=wc_stripe_params.billing_country),i},createSource:function(){var i=n.getOwnerDetails(),o="card";if(n.isBancontactChosen()&&(o="bancontact"),n.isSepaChosen()&&(o="sepa_debit"),n.isIdealChosen()&&(o="ideal"),n.isSofortChosen()&&(o="sofort"),n.isBitcoinChosen()&&(o="bitcoin"),n.isGiropayChosen()&&(o="giropay"),n.isAlipayChosen()&&(o="alipay"),"card"===o)r.createSource(t,i).then(n.sourceResponse);else{switch(o){case"bancontact":case"giropay":case"ideal":case"sofort":case"alipay":i.amount=e("#stripe-"+o+"-payment-data").data("amount"),i.currency=e("#stripe-"+o+"-payment-data").data("currency"),i.redirect={return_url:wc_stripe_params.return_url},wc_stripe_params.statement_descriptor&&(i.statement_descriptor=wc_stripe_params.statement_descriptor)}switch(o){case"sepa_debit":var s=e("#stripe-payment-data"),a=e("#billing_email").length?e("#billing_email").val():s.data("email");i.currency=e("#stripe-"+o+"-payment-data").data("currency"),i.owner.name=e("#stripe-sepa-owner").val(),i.owner.email=a,i.sepa_debit={iban:e("#stripe-sepa-iban").val()},i.mandate={notification_method:wc_stripe_params.sepa_mandate_notification};break;case"ideal":i.ideal={bank:e("#stripe-ideal-bank").val()};break;case"bitcoin":case"alipay":i.currency=e("#stripe-"+o+"-payment-data").data("currency"),i.amount=e("#stripe-"+o+"-payment-data").data("amount");break;case"sofort":i.sofort={country:e("#billing_country").val()}}i.type=o,r.createSource(i).then(n.sourceResponse)}},sourceResponse:function(r){r.error?e(document.body).trigger("stripeError",r):"no"===wc_stripe_params.allow_prepaid_card&&"card"===r.source.type&&"prepaid"===r.source.card.funding?(r.error={message:wc_stripe_params.no_prepaid_card_msg},wc_stripe_params.is_stripe_checkout?n.submitError('<ul class="woocommerce-error"><li>'+wc_stripe_params.no_prepaid_card_msg+"</li></ul>"):e(document.body).trigger("stripeError",r)):n.processStripeResponse(r.source)},processStripeResponse:function(r){n.reset(),n.form.append("<input type='hidden' class='stripe-source' name='stripe_source' value='"+r.id+"'/>"),e("form#add_payment_method").length&&e(n.form).off("submit",n.form.onSubmit),n.form.submit()},createToken:function(){var r=e("#stripe-card-number").val(),t=e("#stripe-card-cvc").val(),i=e("#stripe-card-expiry").payment("cardExpiryVal"),o=e("#billing_first_name").length?e("#billing_first_name").val():wc_stripe_params.billing_first_name,s=e("#billing_last_name").length?e("#billing_last_name").val():wc_stripe_params.billing_last_name,a={number:r,cvc:t,exp_month:parseInt(i.month,10)||0,exp_year:parseInt(i.year,10)||0};o&&s&&(a.name=o+" "+s),e("#billing_address_1").length>0?(a.address_line1=e("#billing_address_1").val(),a.address_line2=e("#billing_address_2").val(),a.address_state=e("#billing_state").val(),a.address_city=e("#billing_city").val(),a.address_zip=e("#billing_postcode").val(),a.address_country=e("#billing_country").val()):wc_stripe_params.billing_address_1&&(a.address_line1=wc_stripe_params.billing_address_1,a.address_line2=wc_stripe_params.billing_address_2,a.address_state=wc_stripe_params.billing_state,a.address_city=wc_stripe_params.billing_city,a.address_zip=wc_stripe_params.billing_postcode,a.address_country=wc_stripe_params.billing_country),Stripe.setPublishableKey(wc_stripe_params.key),Stripe.createToken(a,n.onStripeTokenResponse)},onStripeTokenResponse:function(r,t){if(t.error)e(document).trigger("stripeError",t);else{if("no"===wc_stripe_params.allow_prepaid_card&&"prepaid"===t.card.funding)return t.error={message:wc_stripe_params.no_prepaid_card_msg},e(document).trigger("stripeError",{response:t}),!1;var i=t.id;n.form.append("<input type='hidden' class='stripe_token' name='stripe_token' value='"+i+"'/>"),e("form#add_payment_method").length&&e(n.form).off("submit",n.form.onSubmit),n.form.submit()}},onSubmit:function(r){if(n.isStripeChosen()&&!n.isStripeSaveCardChosen()&&!n.hasSource()&&!n.hasToken()){if(r.preventDefault(),"yes"===wc_stripe_params.is_stripe_checkout&&n.isStripeModalNeeded()&&n.isStripeCardChosen())return n.isMobile()||"no"===wc_stripe_params.validate_modal_checkout?n.openModal():n.validateCheckout(),!1;if(n.block(),n.isStripeCardChosen()&&"no"===wc_stripe_params.use_elements)return n.createToken(),!1;if(n.isSepaChosen()){if(""===e("#stripe-sepa-owner").val())return e(document.body).trigger("stripeError",{error:{message:wc_stripe_params.no_sepa_owner_msg}}),!1;if(""===e("#stripe-sepa-iban").val())return e(document.body).trigger("stripeError",{error:{message:wc_stripe_params.no_sepa_iban_msg}}),!1}if(n.isBancontactChosen()||n.isGiropayChosen()||n.isIdealChosen()||n.isAlipayChosen()||n.isSofortChosen()||n.isP24Chosen()){if(e("form#order_review").length&&(e("form#order_review").off("submit",this.onSubmit),n.form.submit()),e("form.woocommerce-checkout").length)return e("form.woocommerce-checkout").off("submit",this.onSubmit),!0;e("form#add_payment_method").length&&(e("form#add_payment_method").off("submit",this.onSubmit),n.form.submit())}return n.createSource(),!1}if(e("form#add_payment_method").length)return r.preventDefault(),"yes"===wc_stripe_params.is_stripe_checkout&&n.isStripeModalNeeded()&&n.isStripeCardChosen()?(n.openModal(),!1):(n.block(),n.isStripeCardChosen()&&"no"===wc_stripe_params.use_elements?(n.createToken(),!1):(n.createSource(),!1))},onCCFormChange:function(){n.reset()},reset:function(){e(".wc-stripe-error, .stripe-source, .stripe_token").remove(),"yes"===wc_stripe_params.is_stripe_checkout&&(n.stripe_submit=!1)},getRequiredFields:function(){return n.form.find(".form-row.validate-required > input:visible, .form-row.validate-required > select:visible, .form-row.validate-required > textarea:visible")},validateCheckout:function(){var r={nonce:wc_stripe_params.stripe_nonce,required_fields:n.getRequiredFields().serialize(),all_fields:n.form.serialize()};e.ajax({type:"POST",url:n.getAjaxURL("validate_checkout"),data:r,dataType:"json",success:function(e){"success"===e?n.openModal():e.messages&&(n.resetModal(),n.reset(),n.submitError(e.messages))}})},onError:function(r,t){var i=t.error.message,o=n.getSelectedPaymentElement().parents("li").eq(0).find(".stripe-source-errors");"invalid_request_error"!==t.error.type&&"api_connection_error"!==t.error.type&&"api_error"!==t.error.type&&"authentication_error"!==t.error.type&&"rate_limit_error"!==t.error.type||(i=wc_stripe_params.invalid_request_error),"card_error"===t.error.type&&wc_stripe_params.hasOwnProperty(t.error.code)&&(i=wc_stripe_params[t.error.code]),"validation_error"===t.error.type&&wc_stripe_params.hasOwnProperty(t.error.code)&&(i=wc_stripe_params[t.error.code]),n.reset(),e(".woocommerce-NoticeGroup-checkout").remove(),console.log(t.error.message),e(o).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li>'+i+"</li></ul>"),e(".wc-stripe-error").length&&e("html, body").animate({scrollTop:e(".wc-stripe-error").offset().top-200},200),n.unblock()},submitError:function(r){e(".woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message").remove(),n.form.prepend('<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">'+r+"</div>"),n.form.removeClass("processing").unblock(),n.form.find(".input-text, select, input:checkbox").blur();var t="";e("#add_payment_method").length&&(t=e("#add_payment_method")),e("#order_review").length&&(t=e("#order_review")),e("form.checkout").length&&(t=e("form.checkout")),t.length&&e("html, body").animate({scrollTop:t.offset().top-100},500),e(document.body).trigger("checkout_error"),n.unblock()}};n.init()});
changelog.txt CHANGED
@@ -1,5 +1,21 @@
1
  *** Changelog ***
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  = 4.0.5 - 2018-02-02 =
4
  * Fix - Illegal offset error on settings when non is defined or saved.
5
  * Fix - Wrong ID used for dispute webhook handler.
1
  *** Changelog ***
2
 
3
+ = 4.0.6 - 2018-02-20 =
4
+ * Fix - A WC 2.6 backwards compat issue with function from WC 3.0.
5
+ * Fix - Subs renewal sometimes failed due to parameters being different.
6
+ * Fix - Stripe accepts only NO for Norwegian language on Stripe Checkout.
7
+ * Fix - Refund fees may not accurately reflect net fees. Props @rvola.
8
+ * Fix - Undefined SERVERNAME property in some cases.
9
+ * Fix - Potential issue when a charge parameter changes due to initial failed request causing retries to fail.
10
+ * Fix - When 3DS is not required, failed payments was not change order status to failed.
11
+ * Fix - Potential duplicate order processes on WC side when webhook and redirect has a race condition.
12
+ * Remove - Checkout validation and let WC handle it.
13
+ * Update - Stripe API version to 2018-02-06.
14
+ * Add - Webhooks for review open/closed for Radar.
15
+ * Add - Hook `wc_stripe_refund_request` for refund request arguments.
16
+ * Add - Hook `wc_stripe_validate_modal_checkout` to enable 3rd party checkout validation.
17
+ * Add - Hook `wc_stripe_validate_modal_checkout_action`.
18
+
19
  = 4.0.5 - 2018-02-02 =
20
  * Fix - Illegal offset error on settings when non is defined or saved.
21
  * Fix - Wrong ID used for dispute webhook handler.
includes/abstracts/abstract-wc-stripe-payment-gateway.php CHANGED
@@ -25,7 +25,9 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
25
  return (
26
  'invalid_request_error' === $error->type ||
27
  'idempotency_error' === $error->type ||
28
- 'rate_limit_error' === $error->type
 
 
29
  );
30
  }
31
 
@@ -180,16 +182,25 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
180
  return esc_url_raw( add_query_arg( array( 'utm_nooverride' => '1' ), $this->get_return_url() ) );
181
  }
182
 
 
 
 
 
 
 
 
 
 
183
  /**
184
  * Generate the request for the payment.
185
  *
186
  * @since 3.1.0
187
  * @version 4.0.0
188
  * @param WC_Order $order
189
- * @param object $source
190
  * @return array()
191
  */
192
- public function generate_payment_request( $order, $source ) {
193
  $settings = get_option( 'woocommerce_stripe_settings', array() );
194
  $statement_descriptor = ! empty( $settings['statement_descriptor'] ) ? str_replace( "'", '', $settings['statement_descriptor'] ) : '';
195
  $capture = ! empty( $settings['capture'] ) && 'yes' === $settings['capture'] ? true : false;
@@ -229,14 +240,21 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
229
  'order_id' => WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id(),
230
  );
231
 
232
- $post_data['metadata'] = apply_filters( 'wc_stripe_payment_metadata', $metadata, $order, $source );
 
 
 
 
 
233
 
234
- if ( $source->customer ) {
235
- $post_data['customer'] = $source->customer;
 
 
236
  }
237
 
238
- if ( $source->source ) {
239
- $post_data['source'] = $source->source;
240
  }
241
 
242
  /**
@@ -247,7 +265,7 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
247
  * @param WC_Order $order
248
  * @param object $source
249
  */
250
- return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order, $source );
251
  }
252
 
253
  /**
@@ -260,10 +278,10 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
260
 
261
  $captured = ( isset( $response->captured ) && $response->captured ) ? 'yes' : 'no';
262
 
263
- // Store charge data
264
  WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_stripe_charge_captured', $captured ) : $order->update_meta_data( '_stripe_charge_captured', $captured );
265
 
266
- // Store other data such as fees
267
  if ( isset( $response->balance_transaction ) && isset( $response->balance_transaction->fee ) ) {
268
  // Fees and Net needs to both come from Stripe to be accurate as the returned
269
  // values are in the local currency of the Stripe account, not from WC.
@@ -280,7 +298,9 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
280
  * take care of the status changes.
281
  */
282
  if ( 'pending' === $response->status ) {
283
- if ( ! wc_string_to_bool( get_post_meta( $order_id, '_order_stock_reduced', true ) ) ) {
 
 
284
  WC_Stripe_Helper::is_pre_30() ? $order->reduce_order_stock() : wc_reduce_stock_levels( $order_id );
285
  }
286
 
@@ -371,18 +391,17 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
371
  }
372
 
373
  /**
374
- * Create source object by source id.
375
  *
376
  * @since 4.0.3
 
377
  */
378
- public function get_source_object() {
379
- $source = ! empty( $_POST['stripe_source'] ) ? wc_clean( $_POST['stripe_source'] ) : '';
380
-
381
- if ( empty( $source ) ) {
382
  return '';
383
  }
384
 
385
- $source_object = WC_Stripe_API::retrieve( 'sources/' . $source );
386
 
387
  if ( ! empty( $source_object->error ) ) {
388
  throw new WC_Stripe_Exception( print_r( $source_object, true ), $source_object->error->message );
@@ -417,6 +436,17 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
417
  return ( $source_object && 'three_d_secure' === $source_object->type );
418
  }
419
 
 
 
 
 
 
 
 
 
 
 
 
420
  /**
421
  * Creates the 3DS source for charge.
422
  *
@@ -452,24 +482,25 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
452
  *
453
  * @since 3.1.0
454
  * @version 4.0.0
455
- * @param object $source_object
456
  * @param string $user_id
457
  * @param bool $force_save_source Should we force save payment source.
458
  *
459
  * @throws Exception When card was not added or for and invalid card.
460
  * @return object
461
  */
462
- public function prepare_source( $source_object = '', $user_id, $force_save_source = false ) {
463
  $customer = new WC_Stripe_Customer( $user_id );
464
  $set_customer = true;
465
  $force_save_source = apply_filters( 'wc_stripe_force_save_source', $force_save_source, $customer );
 
466
  $source_id = '';
467
  $wc_token_id = false;
468
  $payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : 'stripe';
469
 
470
  // New CC info was entered and we have a new source to process.
471
- if ( ! empty( $source_object ) ) {
472
- $source_id = $source_object->id;
 
473
 
474
  // This checks to see if customer opted to save the payment method to file.
475
  $maybe_saved_card = isset( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] ) && ! empty( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] );
@@ -487,7 +518,7 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
487
  }
488
  }
489
  } elseif ( isset( $_POST[ 'wc-' . $payment_method . '-payment-token' ] ) && 'new' !== $_POST[ 'wc-' . $payment_method . '-payment-token' ] ) {
490
- // Use an existing token, and then process the payment
491
  $wc_token_id = wc_clean( $_POST[ 'wc-' . $payment_method . '-payment-token' ] );
492
  $wc_token = WC_Payment_Tokens::get( $wc_token_id );
493
 
@@ -520,10 +551,15 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
520
  $customer_id = $customer->get_id() ? $customer->get_id() : false;
521
  }
522
 
 
 
 
 
523
  return (object) array(
524
- 'token_id' => $wc_token_id,
525
- 'customer' => $customer_id,
526
- 'source' => $source_id,
 
527
  );
528
  }
529
 
@@ -624,7 +660,7 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
624
  * e.g usage would be after a refund.
625
  *
626
  * @since 4.0.0
627
- * @version 4.0.0
628
  * @param object $order The order object
629
  * @param int $balance_transaction_id
630
  */
@@ -637,8 +673,16 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
637
  if ( isset( $balance_transaction ) && isset( $balance_transaction->fee ) ) {
638
  // Fees and Net needs to both come from Stripe to be accurate as the returned
639
  // values are in the local currency of the Stripe account, not from WC.
640
- $fee = ! empty( $balance_transaction->fee ) ? WC_Stripe_Helper::format_balance_fee( $balance_transaction, 'fee' ) : 0;
641
- $net = ! empty( $balance_transaction->net ) ? WC_Stripe_Helper::format_balance_fee( $balance_transaction, 'net' ) : 0;
 
 
 
 
 
 
 
 
642
 
643
  WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, self::META_NAME_FEE, $fee ) : $order->update_meta_data( self::META_NAME_FEE, $fee );
644
  WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, self::META_NAME_NET, $net ) : $order->update_meta_data( self::META_NAME_NET, $net );
@@ -697,6 +741,8 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
697
 
698
  WC_Stripe_Logger::log( "Info: Beginning refund for order {$order->get_transaction_id()} for the amount of {$amount}" );
699
 
 
 
700
  $response = WC_Stripe_API::request( $request, 'refunds' );
701
 
702
  if ( ! empty( $response->error ) ) {
@@ -776,4 +822,58 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
776
  'redirect' => wc_get_endpoint_url( 'payment-methods' ),
777
  );
778
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
779
  }
25
  return (
26
  'invalid_request_error' === $error->type ||
27
  'idempotency_error' === $error->type ||
28
+ 'rate_limit_error' === $error->type ||
29
+ 'api_connection_error' === $error->type ||
30
+ 'api_error' === $error->type
31
  );
32
  }
33
 
182
  return esc_url_raw( add_query_arg( array( 'utm_nooverride' => '1' ), $this->get_return_url() ) );
183
  }
184
 
185
+ /**
186
+ * Is $order_id a subscription?
187
+ * @param int $order_id
188
+ * @return boolean
189
+ */
190
+ public function has_subscription( $order_id ) {
191
+ return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
192
+ }
193
+
194
  /**
195
  * Generate the request for the payment.
196
  *
197
  * @since 3.1.0
198
  * @version 4.0.0
199
  * @param WC_Order $order
200
+ * @param object $prepared_source
201
  * @return array()
202
  */
203
+ public function generate_payment_request( $order, $prepared_source ) {
204
  $settings = get_option( 'woocommerce_stripe_settings', array() );
205
  $statement_descriptor = ! empty( $settings['statement_descriptor'] ) ? str_replace( "'", '', $settings['statement_descriptor'] ) : '';
206
  $capture = ! empty( $settings['capture'] ) && 'yes' === $settings['capture'] ? true : false;
240
  'order_id' => WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id(),
241
  );
242
 
243
+ if ( $this->has_subscription( WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id() ) ) {
244
+ $metadata += array(
245
+ 'payment_type' => 'recurring',
246
+ 'site_url' => esc_url( get_site_url() ),
247
+ );
248
+ }
249
 
250
+ $post_data['metadata'] = apply_filters( 'wc_stripe_payment_metadata', $metadata, $order, $prepared_source );
251
+
252
+ if ( $prepared_source->customer ) {
253
+ $post_data['customer'] = $prepared_source->customer;
254
  }
255
 
256
+ if ( $prepared_source->source ) {
257
+ $post_data['source'] = $prepared_source->source;
258
  }
259
 
260
  /**
265
  * @param WC_Order $order
266
  * @param object $source
267
  */
268
+ return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order, $prepared_source );
269
  }
270
 
271
  /**
278
 
279
  $captured = ( isset( $response->captured ) && $response->captured ) ? 'yes' : 'no';
280
 
281
+ // Store charge data.
282
  WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_stripe_charge_captured', $captured ) : $order->update_meta_data( '_stripe_charge_captured', $captured );
283
 
284
+ // Store other data such as fees.
285
  if ( isset( $response->balance_transaction ) && isset( $response->balance_transaction->fee ) ) {
286
  // Fees and Net needs to both come from Stripe to be accurate as the returned
287
  // values are in the local currency of the Stripe account, not from WC.
298
  * take care of the status changes.
299
  */
300
  if ( 'pending' === $response->status ) {
301
+ $order_stock_reduced = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_order_stock_reduced', true ) : $order->get_meta( '_order_stock_reduced', true );
302
+
303
+ if ( ! $order_stock_reduced ) {
304
  WC_Stripe_Helper::is_pre_30() ? $order->reduce_order_stock() : wc_reduce_stock_levels( $order_id );
305
  }
306
 
391
  }
392
 
393
  /**
394
+ * Get source object by source id.
395
  *
396
  * @since 4.0.3
397
+ * @param string $source_id The source ID to get source object for.
398
  */
399
+ public function get_source_object( $source_id = '' ) {
400
+ if ( empty( $source_id ) ) {
 
 
401
  return '';
402
  }
403
 
404
+ $source_object = WC_Stripe_API::retrieve( 'sources/' . $source_id );
405
 
406
  if ( ! empty( $source_object->error ) ) {
407
  throw new WC_Stripe_Exception( print_r( $source_object, true ), $source_object->error->message );
436
  return ( $source_object && 'three_d_secure' === $source_object->type );
437
  }
438
 
439
+ /**
440
+ * Checks if card is a prepaid card.
441
+ *
442
+ * @since 4.0.6
443
+ * @param object $source_object
444
+ * @return bool
445
+ */
446
+ public function is_prepaid_card( $source_object ) {
447
+ return ( $source_object && 'token' === $source_object->object && 'prepaid' === $source_object->card->funding );
448
+ }
449
+
450
  /**
451
  * Creates the 3DS source for charge.
452
  *
482
  *
483
  * @since 3.1.0
484
  * @version 4.0.0
 
485
  * @param string $user_id
486
  * @param bool $force_save_source Should we force save payment source.
487
  *
488
  * @throws Exception When card was not added or for and invalid card.
489
  * @return object
490
  */
491
+ public function prepare_source( $user_id, $force_save_source = false ) {
492
  $customer = new WC_Stripe_Customer( $user_id );
493
  $set_customer = true;
494
  $force_save_source = apply_filters( 'wc_stripe_force_save_source', $force_save_source, $customer );
495
+ $source_object = '';
496
  $source_id = '';
497
  $wc_token_id = false;
498
  $payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : 'stripe';
499
 
500
  // New CC info was entered and we have a new source to process.
501
+ if ( ! empty( $_POST['stripe_source'] ) ) {
502
+ $source_object = self::get_source_object( wc_clean( $_POST['stripe_source'] ) );
503
+ $source_id = $source_object->id;
504
 
505
  // This checks to see if customer opted to save the payment method to file.
506
  $maybe_saved_card = isset( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] ) && ! empty( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] );
518
  }
519
  }
520
  } elseif ( isset( $_POST[ 'wc-' . $payment_method . '-payment-token' ] ) && 'new' !== $_POST[ 'wc-' . $payment_method . '-payment-token' ] ) {
521
+ // Use an existing token, and then process the payment.
522
  $wc_token_id = wc_clean( $_POST[ 'wc-' . $payment_method . '-payment-token' ] );
523
  $wc_token = WC_Payment_Tokens::get( $wc_token_id );
524
 
551
  $customer_id = $customer->get_id() ? $customer->get_id() : false;
552
  }
553
 
554
+ if ( empty( $source_object ) ) {
555
+ $source_object = self::get_source_object( $source_id );
556
+ }
557
+
558
  return (object) array(
559
+ 'token_id' => $wc_token_id,
560
+ 'customer' => $customer_id,
561
+ 'source' => $source_id,
562
+ 'source_object' => $source_object,
563
  );
564
  }
565
 
660
  * e.g usage would be after a refund.
661
  *
662
  * @since 4.0.0
663
+ * @version 4.0.6
664
  * @param object $order The order object
665
  * @param int $balance_transaction_id
666
  */
673
  if ( isset( $balance_transaction ) && isset( $balance_transaction->fee ) ) {
674
  // Fees and Net needs to both come from Stripe to be accurate as the returned
675
  // values are in the local currency of the Stripe account, not from WC.
676
+ $fee_refund = ! empty( $balance_transaction->fee ) ? WC_Stripe_Helper::format_balance_fee( $balance_transaction, 'fee' ) : 0;
677
+ $net_refund = ! empty( $balance_transaction->net ) ? WC_Stripe_Helper::format_balance_fee( $balance_transaction, 'net' ) : 0;
678
+
679
+ // Current data fee & net.
680
+ $fee_current = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, self::META_NAME_FEE, true ) : $order->get_meta( self::META_NAME_FEE, true );
681
+ $net_current = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, self::META_NAME_NET, true ) : $order->get_meta( self::META_NAME_NET, true );
682
+
683
+ // Calculation.
684
+ $fee = (float) $fee_current + (float) $fee_refund;
685
+ $net = (float) $net_current + (float) $net_refund;
686
 
687
  WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, self::META_NAME_FEE, $fee ) : $order->update_meta_data( self::META_NAME_FEE, $fee );
688
  WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, self::META_NAME_NET, $net ) : $order->update_meta_data( self::META_NAME_NET, $net );
741
 
742
  WC_Stripe_Logger::log( "Info: Beginning refund for order {$order->get_transaction_id()} for the amount of {$amount}" );
743
 
744
+ $request = apply_filters( 'wc_stripe_refund_request', $request, $order );
745
+
746
  $response = WC_Stripe_API::request( $request, 'refunds' );
747
 
748
  if ( ! empty( $response->error ) ) {
822
  'redirect' => wc_get_endpoint_url( 'payment-methods' ),
823
  );
824
  }
825
+
826
+ /**
827
+ * Gets the locale with normalization that only Stripe accepts.
828
+ *
829
+ * @since 4.0.6
830
+ * @return string $locale
831
+ */
832
+ public function get_locale() {
833
+ $locale = get_locale();
834
+
835
+ /*
836
+ * Stripe expects Norwegian to only be passed NO.
837
+ * But WP has different dialects.
838
+ */
839
+ if ( 'NO' === substr( $locale, 3, 2 ) ) {
840
+ $locale = 'no';
841
+ } else {
842
+ $locale = substr( get_locale(), 0, 2 );
843
+ }
844
+
845
+ return $locale;
846
+ }
847
+
848
+ /**
849
+ * Change the idempotency key so charge can
850
+ * process order as a different transaction.
851
+ *
852
+ * @since 4.0.6
853
+ * @param string $idempotency_key
854
+ * @param array $request
855
+ */
856
+ public function change_idempotency_key( $idempotency_key, $request ) {
857
+ $customer = ! empty( $request['customer'] ) ? $request['customer'] : '';
858
+ $source = ! empty( $request['source'] ) ? $request['source'] : $customer;
859
+ $count = $this->retry_interval;
860
+
861
+ return $request['metadata']['order_id'] . '-' . $count . '-' . $source;
862
+ }
863
+
864
+ /**
865
+ * Checks if request is the original to prevent double processing
866
+ * on WC side. The original-request header and request-id header
867
+ * needs to be the same to mean its the original request.
868
+ *
869
+ * @since 4.0.6
870
+ * @param array $headers
871
+ */
872
+ public function is_original_request( $headers ) {
873
+ if ( $headers['original-request'] === $headers['request-id'] ) {
874
+ return true;
875
+ }
876
+
877
+ return false;
878
+ }
879
  }
includes/class-wc-gateway-stripe.php CHANGED
@@ -9,6 +9,11 @@ if ( ! defined( 'ABSPATH' ) ) {
9
  * @extends WC_Payment_Gateway
10
  */
11
  class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
 
 
 
 
 
12
  public $retry_interval;
13
 
14
  /**
@@ -81,13 +86,6 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
81
  */
82
  public $payment_request;
83
 
84
- /**
85
- * Apple Pay Domain Set.
86
- *
87
- * @var bool
88
- */
89
- public $apple_pay_domain_set;
90
-
91
  /**
92
  * Is test mode active?
93
  *
@@ -95,13 +93,6 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
95
  */
96
  public $testmode;
97
 
98
- /**
99
- * Stores Apple Pay domain verification issues.
100
- *
101
- * @var string
102
- */
103
- public $apple_pay_verify_notice;
104
-
105
  /**
106
  * Inline CC form styling
107
  *
@@ -113,7 +104,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
113
  * Constructor
114
  */
115
  public function __construct() {
116
- $this->retry_interval = 2;
117
  $this->id = 'stripe';
118
  $this->method_title = __( 'Stripe', 'woocommerce-gateway-stripe' );
119
  /* translators: 1) link to Stripe register page 2) link to Stripe api keys page */
@@ -159,8 +150,6 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
159
  $this->publishable_key = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
160
  $this->bitcoin = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' );
161
  $this->payment_request = 'yes' === $this->get_option( 'payment_request', 'yes' );
162
- $this->apple_pay_domain_set = 'yes' === $this->get_option( 'apple_pay_domain_set', 'no' );
163
- $this->apple_pay_verify_notice = '';
164
 
165
  if ( $this->stripe_checkout ) {
166
  $this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' );
@@ -168,15 +157,26 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
168
 
169
  WC_Stripe_API::set_secret_key( $this->secret_key );
170
 
171
- $this->init_apple_pay();
172
-
173
  // Hooks.
174
  add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
175
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
176
- add_action( 'admin_notices', array( $this, 'admin_notices' ) );
177
  add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
178
  }
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  /**
181
  * Checks if gateway should be available to use.
182
  *
@@ -219,149 +219,6 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
219
  return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
220
  }
221
 
222
- /**
223
- * Initializes Apple Pay process on settings page.
224
- *
225
- * @since 3.1.0
226
- * @version 3.1.0
227
- */
228
- public function init_apple_pay() {
229
- if (
230
- is_admin() &&
231
- isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] &&
232
- isset( $_GET['tab'] ) && 'checkout' === $_GET['tab'] &&
233
- isset( $_GET['section'] ) && 'stripe' === $_GET['section'] &&
234
- $this->payment_request
235
- ) {
236
- $this->process_apple_pay_verification();
237
- }
238
- }
239
-
240
- /**
241
- * Registers the domain with Stripe/Apple Pay
242
- *
243
- * @since 3.1.0
244
- * @version 3.1.0
245
- * @param string $secret_key
246
- */
247
- private function register_apple_pay_domain( $secret_key = '' ) {
248
- if ( empty( $secret_key ) ) {
249
- throw new Exception( __( 'Unable to verify domain - missing secret key.', 'woocommerce-gateway-stripe' ) );
250
- }
251
-
252
- $endpoint = 'https://api.stripe.com/v1/apple_pay/domains';
253
-
254
- $data = array(
255
- 'domain_name' => $_SERVER['HTTP_HOST'],
256
- );
257
-
258
- $headers = array(
259
- 'User-Agent' => 'WooCommerce Stripe Apple Pay',
260
- 'Authorization' => 'Bearer ' . $secret_key,
261
- );
262
-
263
- $response = wp_remote_post( $endpoint, array(
264
- 'headers' => $headers,
265
- 'body' => http_build_query( $data ),
266
- ) );
267
-
268
- if ( is_wp_error( $response ) ) {
269
- /* translators: error message */
270
- throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
271
- }
272
-
273
- if ( 200 !== $response['response']['code'] ) {
274
- $parsed_response = json_decode( $response['body'] );
275
-
276
- $this->apple_pay_verify_notice = $parsed_response->error->message;
277
-
278
- /* translators: error message */
279
- throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $parsed_response->error->message ) );
280
- }
281
- }
282
-
283
- /**
284
- * Processes the Apple Pay domain verification.
285
- *
286
- * @since 3.1.0
287
- * @version 3.1.0
288
- */
289
- public function process_apple_pay_verification() {
290
- $gateway_settings = get_option( 'woocommerce_stripe_settings', array() );
291
-
292
- try {
293
- $path = untrailingslashit( $_SERVER['DOCUMENT_ROOT'] );
294
- $dir = '.well-known';
295
- $file = 'apple-developer-merchantid-domain-association';
296
- $fullpath = $path . '/' . $dir . '/' . $file;
297
-
298
- if ( ! empty( $gateway_settings['apple_pay_domain_set'] ) && 'yes' === $gateway_settings['apple_pay_domain_set'] && file_exists( $fullpath ) ) {
299
- return;
300
- }
301
-
302
- if ( ! file_exists( $path . '/' . $dir ) ) {
303
- if ( ! @mkdir( $path . '/' . $dir, 0755 ) ) {
304
- throw new Exception( __( 'Unable to create domain association folder to domain root.', 'woocommerce-gateway-stripe' ) );
305
- }
306
- }
307
-
308
- if ( ! file_exists( $fullpath ) ) {
309
- if ( ! @copy( WC_STRIPE_PLUGIN_PATH . '/' . $file, $fullpath ) ) {
310
- throw new Exception( __( 'Unable to copy domain association file to domain root.', 'woocommerce-gateway-stripe' ) );
311
- }
312
- }
313
-
314
- // At this point then the domain association folder and file should be available.
315
- // Proceed to verify/and or verify again.
316
- $this->register_apple_pay_domain( $this->secret_key );
317
-
318
- // No errors to this point, verification success!
319
- $gateway_settings['apple_pay_domain_set'] = 'yes';
320
- $this->apple_pay_domain_set = true;
321
-
322
- update_option( 'woocommerce_stripe_settings', $gateway_settings );
323
-
324
- WC_Stripe_Logger::log( 'Your domain has been verified with Apple Pay!' );
325
-
326
- } catch ( Exception $e ) {
327
- $gateway_settings['apple_pay_domain_set'] = 'no';
328
-
329
- update_option( 'woocommerce_stripe_settings', $gateway_settings );
330
-
331
- WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
332
- }
333
- }
334
-
335
- /**
336
- * Check if SSL is enabled and notify the user
337
- */
338
- public function admin_notices() {
339
- if ( 'no' === $this->enabled ) {
340
- return;
341
- }
342
-
343
- if ( $this->payment_request && ! empty( $this->apple_pay_verify_notice ) ) {
344
- $allowed_html = array(
345
- 'a' => array(
346
- 'href' => array(),
347
- 'title' => array(),
348
- ),
349
- );
350
-
351
- echo '<div class="error stripe-apple-pay-message"><p>' . wp_kses( make_clickable( $this->apple_pay_verify_notice ), $allowed_html ) . '</p></div>';
352
- }
353
-
354
- /**
355
- * Apple pay is enabled by default and domain verification initializes
356
- * when setting screen is displayed. So if domain verification is not set,
357
- * something went wrong so lets notify user.
358
- */
359
- if ( ! empty( $this->secret_key ) && $this->payment_request && ! $this->apple_pay_domain_set ) {
360
- /* translators: 1) HTML anchor open tag 2) HTML anchor closing tag */
361
- echo '<div class="error stripe-apple-pay-message"><p>' . sprintf( __( 'Apple Pay domain verification failed. Please check the %1$slog%2$s to see the issue. (Logging must be enabled to see recorded logs)', 'woocommerce-gateway-stripe' ), '<a href="' . admin_url( 'admin.php?page=wc-status&tab=logs' ) . '">', '</a>' ) . '</p></div>';
362
- }
363
- }
364
-
365
  /**
366
  * Initialise Gateway Settings Form Fields
367
  */
@@ -407,7 +264,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
407
  data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
408
  data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
409
  data-bitcoin="' . esc_attr( ( $this->bitcoin && $this->capture ) ? 'true' : 'false' ) . '"
410
- data-locale="' . esc_attr( apply_filters( 'wc_stripe_checkout_locale', substr( get_locale(), 0, 2 ) ) ) . '"
411
  data-three-d-secure="' . esc_attr( $this->three_d_secure ? 'true' : 'false' ) . '"
412
  data-allow-remember-me="' . esc_attr( $this->saved_cards ? 'true' : 'false' ) . '">';
413
 
@@ -429,6 +286,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
429
  if ( apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ) {
430
  $this->elements_form();
431
  } else {
 
432
  $this->form();
433
  echo '<div class="stripe-source-errors" role="alert"></div>';
434
  }
@@ -523,6 +381,23 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
523
  return;
524
  }
525
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
527
 
528
  wp_register_style( 'stripe_paymentfonts', plugins_url( 'assets/css/stripe-paymentfonts.css', WC_STRIPE_MAIN_FILE ), array(), '1.2.5' );
@@ -560,7 +435,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
560
  $stripe_params['allow_prepaid_card'] = apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no';
561
  $stripe_params['inline_cc_form'] = $this->inline_cc_form ? 'yes' : 'no';
562
  $stripe_params['stripe_checkout_require_billing_address'] = apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'yes' : 'no';
563
- $stripe_params['is_checkout'] = ( is_checkout() && empty( $_GET['pay_for_order'] ) );
564
  $stripe_params['return_url'] = $this->get_stripe_return_url();
565
  $stripe_params['ajaxurl'] = WC_AJAX::get_endpoint( '%%endpoint%%' );
566
  $stripe_params['stripe_nonce'] = wp_create_nonce( '_wc_stripe_nonce' );
@@ -568,8 +443,9 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
568
  $stripe_params['use_elements'] = apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ? 'yes' : 'no';
569
  $stripe_params['elements_options'] = apply_filters( 'wc_stripe_elements_options', array() );
570
  $stripe_params['is_stripe_checkout'] = $this->stripe_checkout ? 'yes' : 'no';
571
- $stripe_params['is_change_payment_page'] = ( isset( $_GET['pay_for_order'] ) || isset( $_GET['change_payment_method'] ) ) ? 'yes' : 'no';
572
- $stripe_params['is_add_payment_method_page'] = is_add_payment_method_page() ? 'yes' : 'no';
 
573
  $stripe_params['elements_styling'] = apply_filters( 'wc_stripe_elements_styling', false );
574
  $stripe_params['elements_classes'] = apply_filters( 'wc_stripe_elements_classes', false );
575
 
@@ -613,15 +489,13 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
613
  $new_stripe_customer->create_customer();
614
  }
615
 
616
- $source_object = $this->get_source_object();
617
- $prepared_source = $this->prepare_source( $source_object, get_current_user_id(), $force_save_source );
618
 
619
  // Check if we don't allow prepaid credit cards.
620
- if ( ! apply_filters( 'wc_stripe_allow_prepaid_card', true ) ) {
621
- if ( $source_object && 'token' === $source_object->object && 'prepaid' === $source_object->card->funding ) {
622
- $localized_message = __( 'Sorry, we\'re not accepting prepaid cards at this time. Your credit card has not been charge. Please try with alternative payment method.', 'woocommerce-gateway-stripe' );
623
- throw new WC_Stripe_Exception( print_r( $source_object, true ), $localized_message );
624
- }
625
  }
626
 
627
  if ( empty( $prepared_source->source ) ) {
@@ -674,20 +548,36 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
674
 
675
  WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
676
 
 
 
 
 
 
 
 
677
  // Make the request.
678
  $response = WC_Stripe_API::request( $this->generate_payment_request( $order, $prepared_source ) );
679
 
680
  if ( ! empty( $response->error ) ) {
681
- // If it is an API error such connection or server, let's retry.
682
- if ( 'api_connection_error' === $response->error->type || 'api_error' === $response->error->type ) {
683
- if ( $retry ) {
684
- sleep( 5 );
685
- return $this->process_payment( $order_id, false, $force_save_source );
686
  } else {
687
- $localized_message = 'API connection error and retries exhausted.';
688
- $order->add_order_note( $localized_message );
689
- throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
690
  }
 
 
 
 
 
 
 
 
 
691
  }
692
 
693
  // We want to retry.
@@ -702,6 +592,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
702
  sleep( $this->retry_interval );
703
 
704
  $this->retry_interval++;
 
705
  return $this->process_payment( $order_id, true, $force_save_source );
706
  } else {
707
  $localized_message = __( 'On going requests error and retries exhausted.', 'woocommerce-gateway-stripe' );
@@ -710,27 +601,6 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
710
  }
711
  }
712
 
713
- // Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
714
- if ( preg_match( '/No such customer/i', $response->error->message ) && $retry ) {
715
- if ( WC_Stripe_Helper::is_pre_30() ) {
716
- delete_user_meta( $order->customer_user, '_stripe_customer_id' );
717
- delete_post_meta( $order_id, '_stripe_customer_id' );
718
- } else {
719
- delete_user_meta( $order->get_customer_id(), '_stripe_customer_id' );
720
- $order->delete_meta_data( '_stripe_customer_id' );
721
- $order->save();
722
- }
723
-
724
- return $this->process_payment( $order_id, false, $force_save_source );
725
- } elseif ( preg_match( '/No such token/i', $response->error->message ) && $prepared_source->token_id ) {
726
- // Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
727
- $wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
728
- $wc_token->delete();
729
- $localized_message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
730
- $order->add_order_note( $localized_message );
731
- throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
732
- }
733
-
734
  $localized_messages = WC_Stripe_Helper::get_localized_messages();
735
 
736
  if ( 'card_error' === $response->error->type ) {
@@ -767,6 +637,9 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
767
 
768
  do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
769
 
 
 
 
770
  if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
771
  $this->send_failed_order_email( $order_id );
772
  }
9
  * @extends WC_Payment_Gateway
10
  */
11
  class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
12
+ /**
13
+ * The delay between retries.
14
+ *
15
+ * @var int
16
+ */
17
  public $retry_interval;
18
 
19
  /**
86
  */
87
  public $payment_request;
88
 
 
 
 
 
 
 
 
89
  /**
90
  * Is test mode active?
91
  *
93
  */
94
  public $testmode;
95
 
 
 
 
 
 
 
 
96
  /**
97
  * Inline CC form styling
98
  *
104
  * Constructor
105
  */
106
  public function __construct() {
107
+ $this->retry_interval = 1;
108
  $this->id = 'stripe';
109
  $this->method_title = __( 'Stripe', 'woocommerce-gateway-stripe' );
110
  /* translators: 1) link to Stripe register page 2) link to Stripe api keys page */
150
  $this->publishable_key = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
151
  $this->bitcoin = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' );
152
  $this->payment_request = 'yes' === $this->get_option( 'payment_request', 'yes' );
 
 
153
 
154
  if ( $this->stripe_checkout ) {
155
  $this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' );
157
 
158
  WC_Stripe_API::set_secret_key( $this->secret_key );
159
 
 
 
160
  // Hooks.
161
  add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
162
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
 
163
  add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
164
  }
165
 
166
+ /**
167
+ * Checks if keys are set.
168
+ *
169
+ * @since 4.0.6
170
+ * @return bool
171
+ */
172
+ public function are_keys_set() {
173
+ if ( empty( $this->secret_key ) || empty( $this->publishable_key ) ) {
174
+ return false;
175
+ }
176
+
177
+ return true;
178
+ }
179
+
180
  /**
181
  * Checks if gateway should be available to use.
182
  *
219
  return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
220
  }
221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  /**
223
  * Initialise Gateway Settings Form Fields
224
  */
264
  data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
265
  data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
266
  data-bitcoin="' . esc_attr( ( $this->bitcoin && $this->capture ) ? 'true' : 'false' ) . '"
267
+ data-locale="' . esc_attr( apply_filters( 'wc_stripe_checkout_locale', $this->get_locale() ) ) . '"
268
  data-three-d-secure="' . esc_attr( $this->three_d_secure ? 'true' : 'false' ) . '"
269
  data-allow-remember-me="' . esc_attr( $this->saved_cards ? 'true' : 'false' ) . '">';
270
 
286
  if ( apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ) {
287
  $this->elements_form();
288
  } else {
289
+ WC_Stripe_Logger::log( 'DEPRECATED! Since version 4.0. Stripe Elements is used. This legacy credit card form will be removed by version 5.0!' );
290
  $this->form();
291
  echo '<div class="stripe-source-errors" role="alert"></div>';
292
  }
381
  return;
382
  }
383
 
384
+ // If Stripe is not enabled bail.
385
+ if ( 'no' === $this->enabled ) {
386
+ return;
387
+ }
388
+
389
+ // If keys are not set bail.
390
+ if ( ! $this->are_keys_set() ) {
391
+ WC_Stripe_Logger::log( 'Keys are not set correctly.' );
392
+ return;
393
+ }
394
+
395
+ // If no SSL bail.
396
+ if ( ! $this->testmode && ! is_ssl() ) {
397
+ WC_Stripe_Logger::log( 'Stripe requires SSL.' );
398
+ return;
399
+ }
400
+
401
  $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
402
 
403
  wp_register_style( 'stripe_paymentfonts', plugins_url( 'assets/css/stripe-paymentfonts.css', WC_STRIPE_MAIN_FILE ), array(), '1.2.5' );
435
  $stripe_params['allow_prepaid_card'] = apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no';
436
  $stripe_params['inline_cc_form'] = $this->inline_cc_form ? 'yes' : 'no';
437
  $stripe_params['stripe_checkout_require_billing_address'] = apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'yes' : 'no';
438
+ $stripe_params['is_checkout'] = ( is_checkout() && empty( $_GET['pay_for_order'] ) ) ? 'yes' : 'no';
439
  $stripe_params['return_url'] = $this->get_stripe_return_url();
440
  $stripe_params['ajaxurl'] = WC_AJAX::get_endpoint( '%%endpoint%%' );
441
  $stripe_params['stripe_nonce'] = wp_create_nonce( '_wc_stripe_nonce' );
443
  $stripe_params['use_elements'] = apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ? 'yes' : 'no';
444
  $stripe_params['elements_options'] = apply_filters( 'wc_stripe_elements_options', array() );
445
  $stripe_params['is_stripe_checkout'] = $this->stripe_checkout ? 'yes' : 'no';
446
+ $stripe_params['is_change_payment_page'] = isset( $_GET['change_payment_method'] ) ? 'yes' : 'no';
447
+ $stripe_params['is_pay_for_order_page'] = isset( $_GET['pay_for_order'] ) ? 'yes' : 'no';
448
+ $stripe_params['validate_modal_checkout'] = apply_filters( 'wc_stripe_validate_modal_checkout', false ) ? 'yes' : 'no';
449
  $stripe_params['elements_styling'] = apply_filters( 'wc_stripe_elements_styling', false );
450
  $stripe_params['elements_classes'] = apply_filters( 'wc_stripe_elements_classes', false );
451
 
489
  $new_stripe_customer->create_customer();
490
  }
491
 
492
+ $prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
493
+ $source_object = $prepared_source->source_object;
494
 
495
  // Check if we don't allow prepaid credit cards.
496
+ if ( ! apply_filters( 'wc_stripe_allow_prepaid_card', true ) && $this->is_prepaid_card( $source_object ) ) {
497
+ $localized_message = __( 'Sorry, we\'re not accepting prepaid cards at this time. Your credit card has not been charge. Please try with alternative payment method.', 'woocommerce-gateway-stripe' );
498
+ throw new WC_Stripe_Exception( print_r( $source_object, true ), $localized_message );
 
 
499
  }
500
 
501
  if ( empty( $prepared_source->source ) ) {
548
 
549
  WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
550
 
551
+ /* If we're doing a retry and source is chargeable, we need to pass
552
+ * a different idempotency key and retry for success.
553
+ */
554
+ if ( 1 < $this->retry_interval && ! empty( $source_object ) && 'chargeable' === $source_object->status ) {
555
+ add_filter( 'wc_stripe_idempotency_key', array( $this, 'change_idempotency_key' ), 10, 2 );
556
+ }
557
+
558
  // Make the request.
559
  $response = WC_Stripe_API::request( $this->generate_payment_request( $order, $prepared_source ) );
560
 
561
  if ( ! empty( $response->error ) ) {
562
+ // Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
563
+ if ( preg_match( '/No such customer/i', $response->error->message ) && $retry ) {
564
+ if ( WC_Stripe_Helper::is_pre_30() ) {
565
+ delete_user_meta( $order->customer_user, '_stripe_customer_id' );
566
+ delete_post_meta( $order_id, '_stripe_customer_id' );
567
  } else {
568
+ delete_user_meta( $order->get_customer_id(), '_stripe_customer_id' );
569
+ $order->delete_meta_data( '_stripe_customer_id' );
570
+ $order->save();
571
  }
572
+
573
+ return $this->process_payment( $order_id, false, $force_save_source );
574
+ } elseif ( preg_match( '/No such token/i', $response->error->message ) && $prepared_source->token_id ) {
575
+ // Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
576
+ $wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
577
+ $wc_token->delete();
578
+ $localized_message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
579
+ $order->add_order_note( $localized_message );
580
+ throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
581
  }
582
 
583
  // We want to retry.
592
  sleep( $this->retry_interval );
593
 
594
  $this->retry_interval++;
595
+
596
  return $this->process_payment( $order_id, true, $force_save_source );
597
  } else {
598
  $localized_message = __( 'On going requests error and retries exhausted.', 'woocommerce-gateway-stripe' );
601
  }
602
  }
603
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
604
  $localized_messages = WC_Stripe_Helper::get_localized_messages();
605
 
606
  if ( 'card_error' === $response->error->type ) {
637
 
638
  do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
639
 
640
+ /* translators: error message */
641
+ $order->update_status( 'failed' );
642
+
643
  if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
644
  $this->send_failed_order_email( $order_id );
645
  }
includes/class-wc-stripe-api.php CHANGED
@@ -14,7 +14,7 @@ class WC_Stripe_API {
14
  * Stripe API Endpoint
15
  */
16
  const ENDPOINT = 'https://api.stripe.com/v1/';
17
- const STRIPE_API_VERSION = '2018-01-23';
18
 
19
  /**
20
  * Secret API Key.
@@ -90,12 +90,13 @@ class WC_Stripe_API {
90
  * Send the request to Stripe's API
91
  *
92
  * @since 3.1.0
93
- * @version 4.0.0
94
  * @param array $request
95
  * @param string $api
 
96
  * @return array|WP_Error
97
  */
98
- public static function request( $request, $api = 'charges', $method = 'POST' ) {
99
  WC_Stripe_Logger::log( "{$api} request: " . print_r( $request, true ) );
100
 
101
  $headers = self::get_headers();
@@ -104,7 +105,7 @@ class WC_Stripe_API {
104
  $customer = ! empty( $request['customer'] ) ? $request['customer'] : '';
105
  $source = ! empty( $request['source'] ) ? $request['source'] : $customer;
106
 
107
- $headers['Idempotency-Key'] = $request['metadata']['order_id'] . '-' . $source;
108
  }
109
 
110
  $response = wp_safe_remote_post(
@@ -122,6 +123,10 @@ class WC_Stripe_API {
122
  throw new WC_Stripe_Exception( print_r( $response, true ), __( 'There was a problem connecting to the Stripe API endpoint.', 'woocommerce-gateway-stripe' ) );
123
  }
124
 
 
 
 
 
125
  return json_decode( $response['body'] );
126
  }
127
 
14
  * Stripe API Endpoint
15
  */
16
  const ENDPOINT = 'https://api.stripe.com/v1/';
17
+ const STRIPE_API_VERSION = '2018-02-06';
18
 
19
  /**
20
  * Secret API Key.
90
  * Send the request to Stripe's API
91
  *
92
  * @since 3.1.0
93
+ * @version 4.0.6
94
  * @param array $request
95
  * @param string $api
96
+ * @param bool $with_headers To get the response with headers.
97
  * @return array|WP_Error
98
  */
99
+ public static function request( $request, $api = 'charges', $method = 'POST', $with_headers = false ) {
100
  WC_Stripe_Logger::log( "{$api} request: " . print_r( $request, true ) );
101
 
102
  $headers = self::get_headers();
105
  $customer = ! empty( $request['customer'] ) ? $request['customer'] : '';
106
  $source = ! empty( $request['source'] ) ? $request['source'] : $customer;
107
 
108
+ $headers['Idempotency-Key'] = apply_filters( 'wc_stripe_idempotency_key', $request['metadata']['order_id'] . '-' . $source, $request );
109
  }
110
 
111
  $response = wp_safe_remote_post(
123
  throw new WC_Stripe_Exception( print_r( $response, true ), __( 'There was a problem connecting to the Stripe API endpoint.', 'woocommerce-gateway-stripe' ) );
124
  }
125
 
126
+ if ( $with_headers ) {
127
+ return array( 'headers' => wp_remote_retrieve_headers( $response ), 'body' => json_decode( $response['body'] ) );
128
+ }
129
+
130
  return json_decode( $response['body'] );
131
  }
132
 
includes/class-wc-stripe-apple-pay-registration.php ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Stripe Apple Pay Registration Class.
4
+ *
5
+ * @since 4.0.6
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+
12
+ class WC_Stripe_Apple_Pay_Registration {
13
+ /**
14
+ * Enabled.
15
+ *
16
+ * @var
17
+ */
18
+ public $stripe_settings;
19
+
20
+ /**
21
+ * Main Stripe Enabled.
22
+ *
23
+ * @var bool
24
+ */
25
+ public $stripe_enabled;
26
+
27
+ /**
28
+ * Do we accept Payment Request?
29
+ *
30
+ * @var bool
31
+ */
32
+ public $payment_request;
33
+
34
+ /**
35
+ * Apple Pay Domain Set.
36
+ *
37
+ * @var bool
38
+ */
39
+ public $apple_pay_domain_set;
40
+
41
+ /**
42
+ * Testmode.
43
+ *
44
+ * @var bool
45
+ */
46
+ public $testmode;
47
+
48
+ /**
49
+ * Secret Key.
50
+ *
51
+ * @var string
52
+ */
53
+ public $secret_key;
54
+
55
+ /**
56