WooCommerce Stripe Payment Gateway - Version 4.3.0

Version Description

2019-10-17 = * Add - For WooCommerce Subscriptions optimize the payment flow for subsequent subscription payments when authentication may be required by using the setup_future_usage parameter for the first subscription payment * Add - Allow customer to authenticate payment even if they are not charged right away for WooCommerce Subscriptions and Pre-Orders, for example for a WooCommerce Subscription that has a free trial * Add - When an off-session payment requires authentication, create a link for customers to come back to the store to authenticate the payment * Add - Send an email to WooCommerce Subscription and Pre-Orders customers who need to authenticate a payment that was automatically tried on their behalf * Add - When an off-session payment requires authentication, send an email to the admin * Add - Admin notice about SCA-readiness * Fix - Avoid idempotency key errors for Pre-Orders * Fix - Use unique anchor for link about checkout styling changes

Download this release

Release Info

Developer woothemes
Plugin Icon 128x128 WooCommerce Stripe Payment Gateway
Version 4.3.0
Comparing to
See all releases

Code changes from version 4.2.5 to 4.3.0

assets/js/stripe.js CHANGED
@@ -709,30 +709,32 @@ jQuery( function( $ ) {
709
  },
710
 
711
  /**
712
- * Handles changes in the hash in order to show a modal for PaymentIntent confirmations.
713
  *
714
  * Listens for `hashchange` events and checks for a hash in the following format:
715
  * #confirm-pi-<intentClientSecret>:<successRedirectURL>
716
  *
717
  * If such a hash appears, the partials will be used to call `stripe.handleCardPayment`
718
- * in order to allow customers to confirm an 3DS/SCA authorization.
 
719
  *
720
  * Those redirects/hashes are generated in `WC_Gateway_Stripe::process_payment`.
721
  */
722
  onHashChange: function() {
723
- var partials = window.location.hash.match( /^#?confirm-pi-([^:]+):(.+)$/ );
724
 
725
- if ( ! partials || 3 > partials.length ) {
726
  return;
727
  }
728
 
729
- var intentClientSecret = partials[1];
730
- var redirectURL = decodeURIComponent( partials[2] );
 
731
 
732
  // Cleanup the URL
733
  window.location.hash = '';
734
 
735
- wc_stripe_form.openIntentModal( intentClientSecret, redirectURL );
736
  },
737
 
738
  maybeConfirmIntent: function() {
@@ -743,7 +745,7 @@ jQuery( function( $ ) {
743
  var intentSecret = $( '#stripe-intent-id' ).val();
744
  var returnURL = $( '#stripe-intent-return' ).val();
745
 
746
- wc_stripe_form.openIntentModal( intentSecret, returnURL, true );
747
  },
748
 
749
  /**
@@ -753,15 +755,18 @@ jQuery( function( $ ) {
753
  * @param {string} redirectURL The URL to ping on fail or redirect to on success.
754
  * @param {boolean} alwaysRedirect If set to true, an immediate redirect will happen no matter the result.
755
  * If not, an error will be displayed on failure.
 
 
756
  */
757
- openIntentModal: function( intentClientSecret, redirectURL, alwaysRedirect ) {
758
- stripe.handleCardPayment( intentClientSecret )
759
  .then( function( response ) {
760
  if ( response.error ) {
761
  throw response.error;
762
  }
763
 
764
- if ( 'requires_capture' !== response.paymentIntent.status && 'succeeded' !== response.paymentIntent.status ) {
 
765
  return;
766
  }
767
 
709
  },
710
 
711
  /**
712
+ * Handles changes in the hash in order to show a modal for PaymentIntent/SetupIntent confirmations.
713
  *
714
  * Listens for `hashchange` events and checks for a hash in the following format:
715
  * #confirm-pi-<intentClientSecret>:<successRedirectURL>
716
  *
717
  * If such a hash appears, the partials will be used to call `stripe.handleCardPayment`
718
+ * in order to allow customers to confirm an 3DS/SCA authorization, or stripe.handleCardSetup if
719
+ * what needs to be confirmed is a SetupIntent.
720
  *
721
  * Those redirects/hashes are generated in `WC_Gateway_Stripe::process_payment`.
722
  */
723
  onHashChange: function() {
724
+ var partials = window.location.hash.match( /^#?confirm-(pi|si)-([^:]+):(.+)$/ );
725
 
726
+ if ( ! partials || 4 > partials.length ) {
727
  return;
728
  }
729
 
730
+ var type = partials[1];
731
+ var intentClientSecret = partials[2];
732
+ var redirectURL = decodeURIComponent( partials[3] );
733
 
734
  // Cleanup the URL
735
  window.location.hash = '';
736
 
737
+ wc_stripe_form.openIntentModal( intentClientSecret, redirectURL, false, 'si' === type );
738
  },
739
 
740
  maybeConfirmIntent: function() {
745
  var intentSecret = $( '#stripe-intent-id' ).val();
746
  var returnURL = $( '#stripe-intent-return' ).val();
747
 
748
+ wc_stripe_form.openIntentModal( intentSecret, returnURL, true, false );
749
  },
750
 
751
  /**
755
  * @param {string} redirectURL The URL to ping on fail or redirect to on success.
756
  * @param {boolean} alwaysRedirect If set to true, an immediate redirect will happen no matter the result.
757
  * If not, an error will be displayed on failure.
758
+ * @param {boolean} isSetupIntent If set to true, ameans that the flow is handling a Setup Intent.
759
+ * If false, it's a Payment Intent.
760
  */
761
+ openIntentModal: function( intentClientSecret, redirectURL, alwaysRedirect, isSetupIntent ) {
762
+ stripe[ isSetupIntent ? 'handleCardSetup' : 'handleCardPayment' ]( intentClientSecret )
763
  .then( function( response ) {
764
  if ( response.error ) {
765
  throw response.error;
766
  }
767
 
768
+ var intent = response[ isSetupIntent ? 'setupIntent' : 'paymentIntent' ];
769
+ if ( 'requires_capture' !== intent.status && 'succeeded' !== intent.status ) {
770
  return;
771
  }
772
 
assets/js/stripe.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(function(c){"use strict";try{var n=Stripe(wc_stripe_params.key)}catch(e){return void console.log(e)}var t,o,i,e=Object.keys(wc_stripe_params.elements_options).length?wc_stripe_params.elements_options:{},r=Object.keys(wc_stripe_params.sepa_elements_options).length?wc_stripe_params.sepa_elements_options:{},s=n.elements(e),a=s.create("iban",r),m={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"),o.unmount("#stripe-exp-element"),i.unmount("#stripe-cvc-element"))},mountElements:function(){if(c("#stripe-card-element").length){if("yes"===wc_stripe_params.inline_cc_form)return t.mount("#stripe-card-element");t.mount("#stripe-card-element"),o.mount("#stripe-exp-element"),i.mount("#stripe-cvc-element")}},createElements:function(){var e={base:{iconColor:"#666EE8",color:"#31325F",fontSize:"15px","::placeholder":{color:"#CFD7E0"}}},r={focus:"focused",empty:"empty",invalid:"invalid"};e=wc_stripe_params.elements_styling?wc_stripe_params.elements_styling:e,r=wc_stripe_params.elements_classes?wc_stripe_params.elements_classes:r,"yes"===wc_stripe_params.inline_cc_form?(t=s.create("card",{style:e,hidePostalCode:!0})).addEventListener("change",function(e){m.onCCFormChange(),e.error&&c(document.body).trigger("stripeError",e)}):(t=s.create("cardNumber",{style:e,classes:r}),o=s.create("cardExpiry",{style:e,classes:r}),i=s.create("cardCvc",{style:e,classes:r}),t.addEventListener("change",function(e){m.onCCFormChange(),m.updateCardBrand(e.brand),e.error&&c(document.body).trigger("stripeError",e)}),o.addEventListener("change",function(e){m.onCCFormChange(),e.error&&c(document.body).trigger("stripeError",e)}),i.addEventListener("change",function(e){m.onCCFormChange(),e.error&&c(document.body).trigger("stripeError",e)})),"yes"===wc_stripe_params.is_checkout?c(document.body).on("updated_checkout",function(){t&&m.unmountElements(),m.mountElements(),c("#stripe-iban-element").length&&a.mount("#stripe-iban-element")}):(c("form#add_payment_method").length||c("form#order_review").length)&&(m.mountElements(),c("#stripe-iban-element").length&&a.mount("#stripe-iban-element"))},updateCardBrand:function(e){var r={visa:"stripe-visa-brand",mastercard:"stripe-mastercard-brand",amex:"stripe-amex-brand",discover:"stripe-discover-brand",diners:"stripe-diners-brand",jcb:"stripe-jcb-brand",unknown:"stripe-credit-card-brand"},t=c(".stripe-card-brand"),n="stripe-credit-card-brand";e in r&&(n=r[e]),c.each(r,function(e,r){t.removeClass(r)}),t.addClass(n)},init:function(){"yes"!==wc_stripe_params.is_change_payment_page&&"yes"!==wc_stripe_params.is_pay_for_order_page||c(document.body).trigger("wc-credit-card-form-init"),c("form.woocommerce-checkout").length&&(this.form=c("form.woocommerce-checkout")),c("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",this.onSubmit),c("form#order_review").length&&(this.form=c("form#order_review")),c("form#order_review, form#add_payment_method").on("submit",this.onSubmit),c("form#add_payment_method").length&&(this.form=c("form#add_payment_method")),c("form.woocommerce-checkout").on("change",this.reset),c(document).on("stripeError",this.onError).on("checkout_error",this.reset),a.on("change",this.onSepaError),m.createElements(),window.addEventListener("hashchange",m.onHashChange),m.maybeConfirmIntent()},isStripeChosen:function(){return c("#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_eps, #payment_method_stripe_multibanco").is(":checked")||c("#payment_method_stripe").is(":checked")&&"new"===c('input[name="wc-stripe-payment-token"]:checked').val()||c("#payment_method_stripe_sepa").is(":checked")&&"new"===c('input[name="wc-stripe-payment-token"]:checked').val()},isStripeSaveCardChosen:function(){return c("#payment_method_stripe").is(":checked")&&c('input[name="wc-stripe-payment-token"]').is(":checked")&&"new"!==c('input[name="wc-stripe-payment-token"]:checked').val()||c("#payment_method_stripe_sepa").is(":checked")&&c('input[name="wc-stripe_sepa-payment-token"]').is(":checked")&&"new"!==c('input[name="wc-stripe_sepa-payment-token"]:checked').val()},isStripeCardChosen:function(){return c("#payment_method_stripe").is(":checked")},isBancontactChosen:function(){return c("#payment_method_stripe_bancontact").is(":checked")},isGiropayChosen:function(){return c("#payment_method_stripe_giropay").is(":checked")},isIdealChosen:function(){return c("#payment_method_stripe_ideal").is(":checked")},isSofortChosen:function(){return c("#payment_method_stripe_sofort").is(":checked")},isAlipayChosen:function(){return c("#payment_method_stripe_alipay").is(":checked")},isSepaChosen:function(){return c("#payment_method_stripe_sepa").is(":checked")},isP24Chosen:function(){return c("#payment_method_stripe_p24").is(":checked")},isEpsChosen:function(){return c("#payment_method_stripe_eps").is(":checked")},isMultibancoChosen:function(){return c("#payment_method_stripe_multibanco").is(":checked")},hasSource:function(){return 0<c("input.stripe-source").length},isMobile:function(){return!!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},block:function(){m.isMobile()||m.form.block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},unblock:function(){m.form&&m.form.unblock()},getSelectedPaymentElement:function(){return c('.payment_methods input[name="payment_method"]:checked')},getOwnerDetails:function(){var e=c("#billing_first_name").length?c("#billing_first_name").val():wc_stripe_params.billing_first_name,r=c("#billing_last_name").length?c("#billing_last_name").val():wc_stripe_params.billing_last_name,t={name:"",address:{},email:"",phone:""};return t.name=e,t.name=e&&r?e+" "+r:c("#stripe-payment-data").data("full-name"),t.email=c("#billing_email").val(),t.phone=c("#billing_phone").val(),(void 0===t.phone||t.phone.length<=0)&&delete t.phone,(void 0===t.email||t.email.length<=0)&&(c("#stripe-payment-data").data("email").length?t.email=c("#stripe-payment-data").data("email"):delete t.email),(void 0===t.name||t.name.length<=0)&&delete t.name,t.address.line1=c("#billing_address_1").val()||wc_stripe_params.billing_address_1,t.address.line2=c("#billing_address_2").val()||wc_stripe_params.billing_address_2,t.address.state=c("#billing_state").val()||wc_stripe_params.billing_state,t.address.city=c("#billing_city").val()||wc_stripe_params.billing_city,t.address.postal_code=c("#billing_postcode").val()||wc_stripe_params.billing_postcode,t.address.country=c("#billing_country").val()||wc_stripe_params.billing_country,{owner:t}},createSource:function(){var e=m.getOwnerDetails();return m.isSepaChosen()?(e.currency=c("#stripe-sepa_debit-payment-data").data("currency"),e.mandate={notification_method:wc_stripe_params.sepa_mandate_notification},e.type="sepa_debit",n.createSource(a,e).then(m.sourceResponse)):n.createSource(t,e).then(m.sourceResponse)},sourceResponse:function(e){if(e.error)return c(document.body).trigger("stripeError",e);m.reset(),m.form.append(c('<input type="hidden" />').addClass("stripe-source").attr("name","stripe_source").val(e.source.id)),c("form#add_payment_method").length&&c(m.form).off("submit",m.form.onSubmit),m.form.submit()},onSubmit:function(){return!m.isStripeChosen()||(!(!m.isStripeSaveCardChosen()&&!m.hasSource())||(!!(m.isBancontactChosen()||m.isGiropayChosen()||m.isIdealChosen()||m.isAlipayChosen()||m.isSofortChosen()||m.isP24Chosen()||m.isEpsChosen()||m.isMultibancoChosen())||(m.block(),m.createSource(),!1)))},onCCFormChange:function(){m.reset()},reset:function(){c(".wc-stripe-error, .stripe-source").remove()},onSepaError:function(e){var r=m.getSelectedPaymentElement().parents("li").eq(0).find(".stripe-source-errors");if(!e.error)return c(r).html("");console.log(e.error.message),c(r).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>'),c(r).find("li").text(e.error.message)},onError:function(e,r){var t,n=r.error.message,o=m.getSelectedPaymentElement().closest("li"),i=o.find(".woocommerce-SavedPaymentMethods-tokenInput");if(i.length){var s=i.filter(":checked");t=s.closest(".woocommerce-SavedPaymentMethods-new").length?c("#wc-stripe-cc-form .stripe-source-errors"):s.closest("li").find(".stripe-source-errors")}else t=o.find(".stripe-source-errors");if(m.isSepaChosen()&&"invalid_owner_name"===r.error.code&&wc_stripe_params.hasOwnProperty(r.error.code)){var a='<ul class="woocommerce-error"><li /></ul>';return a.find("li").text(wc_stripe_params[r.error.code]),m.submitError(a)}"email_invalid"===r.error.code?n=wc_stripe_params.email_invalid:"invalid_request_error"!==r.error.type&&"api_connection_error"!==r.error.type&&"api_error"!==r.error.type&&"authentication_error"!==r.error.type&&"rate_limit_error"!==r.error.type||(n=wc_stripe_params.invalid_request_error),"card_error"===r.error.type&&wc_stripe_params.hasOwnProperty(r.error.code)&&(n=wc_stripe_params[r.error.code]),"validation_error"===r.error.type&&wc_stripe_params.hasOwnProperty(r.error.code)&&(n=wc_stripe_params[r.error.code]),m.reset(),c(".woocommerce-NoticeGroup-checkout").remove(),console.log(r.error.message),c(t).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>'),c(t).find("li").text(n),c(".wc-stripe-error").length&&c("html, body").animate({scrollTop:c(".wc-stripe-error").offset().top-200},200),m.unblock(),c.unblockUI()},submitError:function(e){c(".woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message").remove(),m.form.prepend('<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">'+e+"</div>"),m.form.removeClass("processing").unblock(),m.form.find(".input-text, select, input:checkbox").blur();var r="";c("#add_payment_method").length&&(r=c("#add_payment_method")),c("#order_review").length&&(r=c("#order_review")),c("form.checkout").length&&(r=c("form.checkout")),r.length&&c("html, body").animate({scrollTop:r.offset().top-100},500),c(document.body).trigger("checkout_error"),m.unblock()},onHashChange:function(){var e=window.location.hash.match(/^#?confirm-pi-([^:]+):(.+)$/);if(e&&!(e.length<3)){var r=e[1],t=decodeURIComponent(e[2]);window.location.hash="",m.openIntentModal(r,t)}},maybeConfirmIntent:function(){if(c("#stripe-intent-id").length&&c("#stripe-intent-return").length){var e=c("#stripe-intent-id").val(),r=c("#stripe-intent-return").val();m.openIntentModal(e,r,!0)}},openIntentModal:function(e,r,t){n.handleCardPayment(e).then(function(e){if(e.error)throw e.error;"requires_capture"!==e.paymentIntent.status&&"succeeded"!==e.paymentIntent.status||(window.location=r)}).catch(function(e){if(t)return window.location=r;c(document.body).trigger("stripeError",{error:e}),m.form&&m.form.removeClass("processing"),c.get(r+"&is_ajax")})}};m.init()});
1
+ jQuery(function(c){"use strict";try{var o=Stripe(wc_stripe_params.key)}catch(e){return void console.log(e)}var t,n,i,e=Object.keys(wc_stripe_params.elements_options).length?wc_stripe_params.elements_options:{},r=Object.keys(wc_stripe_params.sepa_elements_options).length?wc_stripe_params.sepa_elements_options:{},s=o.elements(e),a=s.create("iban",r),m={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"),n.unmount("#stripe-exp-element"),i.unmount("#stripe-cvc-element"))},mountElements:function(){if(c("#stripe-card-element").length){if("yes"===wc_stripe_params.inline_cc_form)return t.mount("#stripe-card-element");t.mount("#stripe-card-element"),n.mount("#stripe-exp-element"),i.mount("#stripe-cvc-element")}},createElements:function(){var e={base:{iconColor:"#666EE8",color:"#31325F",fontSize:"15px","::placeholder":{color:"#CFD7E0"}}},r={focus:"focused",empty:"empty",invalid:"invalid"};e=wc_stripe_params.elements_styling?wc_stripe_params.elements_styling:e,r=wc_stripe_params.elements_classes?wc_stripe_params.elements_classes:r,"yes"===wc_stripe_params.inline_cc_form?(t=s.create("card",{style:e,hidePostalCode:!0})).addEventListener("change",function(e){m.onCCFormChange(),e.error&&c(document.body).trigger("stripeError",e)}):(t=s.create("cardNumber",{style:e,classes:r}),n=s.create("cardExpiry",{style:e,classes:r}),i=s.create("cardCvc",{style:e,classes:r}),t.addEventListener("change",function(e){m.onCCFormChange(),m.updateCardBrand(e.brand),e.error&&c(document.body).trigger("stripeError",e)}),n.addEventListener("change",function(e){m.onCCFormChange(),e.error&&c(document.body).trigger("stripeError",e)}),i.addEventListener("change",function(e){m.onCCFormChange(),e.error&&c(document.body).trigger("stripeError",e)})),"yes"===wc_stripe_params.is_checkout?c(document.body).on("updated_checkout",function(){t&&m.unmountElements(),m.mountElements(),c("#stripe-iban-element").length&&a.mount("#stripe-iban-element")}):(c("form#add_payment_method").length||c("form#order_review").length)&&(m.mountElements(),c("#stripe-iban-element").length&&a.mount("#stripe-iban-element"))},updateCardBrand:function(e){var r={visa:"stripe-visa-brand",mastercard:"stripe-mastercard-brand",amex:"stripe-amex-brand",discover:"stripe-discover-brand",diners:"stripe-diners-brand",jcb:"stripe-jcb-brand",unknown:"stripe-credit-card-brand"},t=c(".stripe-card-brand"),n="stripe-credit-card-brand";e in r&&(n=r[e]),c.each(r,function(e,r){t.removeClass(r)}),t.addClass(n)},init:function(){"yes"!==wc_stripe_params.is_change_payment_page&&"yes"!==wc_stripe_params.is_pay_for_order_page||c(document.body).trigger("wc-credit-card-form-init"),c("form.woocommerce-checkout").length&&(this.form=c("form.woocommerce-checkout")),c("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",this.onSubmit),c("form#order_review").length&&(this.form=c("form#order_review")),c("form#order_review, form#add_payment_method").on("submit",this.onSubmit),c("form#add_payment_method").length&&(this.form=c("form#add_payment_method")),c("form.woocommerce-checkout").on("change",this.reset),c(document).on("stripeError",this.onError).on("checkout_error",this.reset),a.on("change",this.onSepaError),m.createElements(),window.addEventListener("hashchange",m.onHashChange),m.maybeConfirmIntent()},isStripeChosen:function(){return c("#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_eps, #payment_method_stripe_multibanco").is(":checked")||c("#payment_method_stripe").is(":checked")&&"new"===c('input[name="wc-stripe-payment-token"]:checked').val()||c("#payment_method_stripe_sepa").is(":checked")&&"new"===c('input[name="wc-stripe-payment-token"]:checked').val()},isStripeSaveCardChosen:function(){return c("#payment_method_stripe").is(":checked")&&c('input[name="wc-stripe-payment-token"]').is(":checked")&&"new"!==c('input[name="wc-stripe-payment-token"]:checked').val()||c("#payment_method_stripe_sepa").is(":checked")&&c('input[name="wc-stripe_sepa-payment-token"]').is(":checked")&&"new"!==c('input[name="wc-stripe_sepa-payment-token"]:checked').val()},isStripeCardChosen:function(){return c("#payment_method_stripe").is(":checked")},isBancontactChosen:function(){return c("#payment_method_stripe_bancontact").is(":checked")},isGiropayChosen:function(){return c("#payment_method_stripe_giropay").is(":checked")},isIdealChosen:function(){return c("#payment_method_stripe_ideal").is(":checked")},isSofortChosen:function(){return c("#payment_method_stripe_sofort").is(":checked")},isAlipayChosen:function(){return c("#payment_method_stripe_alipay").is(":checked")},isSepaChosen:function(){return c("#payment_method_stripe_sepa").is(":checked")},isP24Chosen:function(){return c("#payment_method_stripe_p24").is(":checked")},isEpsChosen:function(){return c("#payment_method_stripe_eps").is(":checked")},isMultibancoChosen:function(){return c("#payment_method_stripe_multibanco").is(":checked")},hasSource:function(){return 0<c("input.stripe-source").length},isMobile:function(){return!!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},block:function(){m.isMobile()||m.form.block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},unblock:function(){m.form&&m.form.unblock()},getSelectedPaymentElement:function(){return c('.payment_methods input[name="payment_method"]:checked')},getOwnerDetails:function(){var e=c("#billing_first_name").length?c("#billing_first_name").val():wc_stripe_params.billing_first_name,r=c("#billing_last_name").length?c("#billing_last_name").val():wc_stripe_params.billing_last_name,t={name:"",address:{},email:"",phone:""};return t.name=e,t.name=e&&r?e+" "+r:c("#stripe-payment-data").data("full-name"),t.email=c("#billing_email").val(),t.phone=c("#billing_phone").val(),(void 0===t.phone||t.phone.length<=0)&&delete t.phone,(void 0===t.email||t.email.length<=0)&&(c("#stripe-payment-data").data("email").length?t.email=c("#stripe-payment-data").data("email"):delete t.email),(void 0===t.name||t.name.length<=0)&&delete t.name,t.address.line1=c("#billing_address_1").val()||wc_stripe_params.billing_address_1,t.address.line2=c("#billing_address_2").val()||wc_stripe_params.billing_address_2,t.address.state=c("#billing_state").val()||wc_stripe_params.billing_state,t.address.city=c("#billing_city").val()||wc_stripe_params.billing_city,t.address.postal_code=c("#billing_postcode").val()||wc_stripe_params.billing_postcode,t.address.country=c("#billing_country").val()||wc_stripe_params.billing_country,{owner:t}},createSource:function(){var e=m.getOwnerDetails();return m.isSepaChosen()?(e.currency=c("#stripe-sepa_debit-payment-data").data("currency"),e.mandate={notification_method:wc_stripe_params.sepa_mandate_notification},e.type="sepa_debit",o.createSource(a,e).then(m.sourceResponse)):o.createSource(t,e).then(m.sourceResponse)},sourceResponse:function(e){if(e.error)return c(document.body).trigger("stripeError",e);m.reset(),m.form.append(c('<input type="hidden" />').addClass("stripe-source").attr("name","stripe_source").val(e.source.id)),c("form#add_payment_method").length&&c(m.form).off("submit",m.form.onSubmit),m.form.submit()},onSubmit:function(){return!m.isStripeChosen()||(!(!m.isStripeSaveCardChosen()&&!m.hasSource())||(!!(m.isBancontactChosen()||m.isGiropayChosen()||m.isIdealChosen()||m.isAlipayChosen()||m.isSofortChosen()||m.isP24Chosen()||m.isEpsChosen()||m.isMultibancoChosen())||(m.block(),m.createSource(),!1)))},onCCFormChange:function(){m.reset()},reset:function(){c(".wc-stripe-error, .stripe-source").remove()},onSepaError:function(e){var r=m.getSelectedPaymentElement().parents("li").eq(0).find(".stripe-source-errors");if(!e.error)return c(r).html("");console.log(e.error.message),c(r).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>'),c(r).find("li").text(e.error.message)},onError:function(e,r){var t,n=r.error.message,o=m.getSelectedPaymentElement().closest("li"),i=o.find(".woocommerce-SavedPaymentMethods-tokenInput");if(i.length){var s=i.filter(":checked");t=s.closest(".woocommerce-SavedPaymentMethods-new").length?c("#wc-stripe-cc-form .stripe-source-errors"):s.closest("li").find(".stripe-source-errors")}else t=o.find(".stripe-source-errors");if(m.isSepaChosen()&&"invalid_owner_name"===r.error.code&&wc_stripe_params.hasOwnProperty(r.error.code)){var a='<ul class="woocommerce-error"><li /></ul>';return a.find("li").text(wc_stripe_params[r.error.code]),m.submitError(a)}"email_invalid"===r.error.code?n=wc_stripe_params.email_invalid:"invalid_request_error"!==r.error.type&&"api_connection_error"!==r.error.type&&"api_error"!==r.error.type&&"authentication_error"!==r.error.type&&"rate_limit_error"!==r.error.type||(n=wc_stripe_params.invalid_request_error),"card_error"===r.error.type&&wc_stripe_params.hasOwnProperty(r.error.code)&&(n=wc_stripe_params[r.error.code]),"validation_error"===r.error.type&&wc_stripe_params.hasOwnProperty(r.error.code)&&(n=wc_stripe_params[r.error.code]),m.reset(),c(".woocommerce-NoticeGroup-checkout").remove(),console.log(r.error.message),c(t).html('<ul class="woocommerce_error woocommerce-error wc-stripe-error"><li /></ul>'),c(t).find("li").text(n),c(".wc-stripe-error").length&&c("html, body").animate({scrollTop:c(".wc-stripe-error").offset().top-200},200),m.unblock(),c.unblockUI()},submitError:function(e){c(".woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message").remove(),m.form.prepend('<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">'+e+"</div>"),m.form.removeClass("processing").unblock(),m.form.find(".input-text, select, input:checkbox").blur();var r="";c("#add_payment_method").length&&(r=c("#add_payment_method")),c("#order_review").length&&(r=c("#order_review")),c("form.checkout").length&&(r=c("form.checkout")),r.length&&c("html, body").animate({scrollTop:r.offset().top-100},500),c(document.body).trigger("checkout_error"),m.unblock()},onHashChange:function(){var e=window.location.hash.match(/^#?confirm-(pi|si)-([^:]+):(.+)$/);if(e&&!(e.length<4)){var r=e[1],t=e[2],n=decodeURIComponent(e[3]);window.location.hash="",m.openIntentModal(t,n,!1,"si"===r)}},maybeConfirmIntent:function(){if(c("#stripe-intent-id").length&&c("#stripe-intent-return").length){var e=c("#stripe-intent-id").val(),r=c("#stripe-intent-return").val();m.openIntentModal(e,r,!0,!1)}},openIntentModal:function(e,t,r,n){o[n?"handleCardSetup":"handleCardPayment"](e).then(function(e){if(e.error)throw e.error;var r=e[n?"setupIntent":"paymentIntent"];"requires_capture"!==r.status&&"succeeded"!==r.status||(window.location=t)}).catch(function(e){if(r)return window.location=t;c(document.body).trigger("stripeError",{error:e}),m.form&&m.form.removeClass("processing"),c.get(t+"&is_ajax")})}};m.init()});
changelog.txt CHANGED
@@ -1,5 +1,15 @@
1
  *** Changelog ***
2
 
 
 
 
 
 
 
 
 
 
 
3
  = 4.2.5 - 2019-10-02 =
4
  * Fix - WooCommerce Subscriptions that use only the Stripe customer ID can again be renewed
5
 
1
  *** Changelog ***
2
 
3
+ = 4.3.0 2019-10-17 =
4
+ * Add - For WooCommerce Subscriptions optimize the payment flow for subsequent subscription payments when authentication may be required by using the `setup_future_usage` parameter for the first subscription payment
5
+ * Add - Allow customer to authenticate payment even if they are not charged right away for WooCommerce Subscriptions and Pre-Orders, for example for a WooCommerce Subscription that has a free trial
6
+ * Add - When an off-session payment requires authentication, create a link for customers to come back to the store to authenticate the payment
7
+ * Add - Send an email to WooCommerce Subscription and Pre-Orders customers who need to authenticate a payment that was automatically tried on their behalf
8
+ * Add - When an off-session payment requires authentication, send an email to the admin
9
+ * Add - Admin notice about SCA-readiness
10
+ * Fix - Avoid idempotency key errors for Pre-Orders
11
+ * Fix - Use unique anchor for link about checkout styling changes
12
+
13
  = 4.2.5 - 2019-10-02 =
14
  * Fix - WooCommerce Subscriptions that use only the Stripe customer ID can again be renewed
15
 
includes/abstracts/abstract-wc-stripe-payment-gateway.php CHANGED
@@ -72,21 +72,6 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
72
  );
73
  }
74
 
75
- /**
76
- * Checks to see if error is of invalid request
77
- * error and source is already consumed.
78
- *
79
- * @since 4.1.0
80
- * @param array $error
81
- */
82
- public function is_source_already_consumed_error( $error ) {
83
- return (
84
- $error &&
85
- 'invalid_request_error' === $error->type &&
86
- preg_match( '/The reusable source you provided is consumed because it was previously charged without being attached to a customer or was detached from a customer. To charge a reusable source multiple time you must attach it to a customer first./i', $error->message )
87
- );
88
- }
89
-
90
  /**
91
  * Checks to see if error is of invalid request
92
  * error and it is no such customer.
@@ -548,7 +533,11 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
548
  * @return bool
549
  */
550
  public function is_prepaid_card( $source_object ) {
551
- return ( $source_object && 'token' === $source_object->object && 'prepaid' === $source_object->card->funding );
 
 
 
 
552
  }
553
 
554
  /**
@@ -589,7 +578,6 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
589
  */
590
  public function prepare_source( $user_id, $force_save_source = false ) {
591
  $customer = new WC_Stripe_Customer( $user_id );
592
- $set_customer = true;
593
  $force_save_source = apply_filters( 'wc_stripe_force_save_source', $force_save_source, $customer );
594
  $source_object = '';
595
  $source_id = '';
@@ -644,16 +632,15 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
644
  throw new WC_Stripe_Exception( print_r( $response, true ), $response->error->message );
645
  }
646
  } else {
647
- $set_customer = false;
648
  $source_id = $stripe_token;
649
  $is_token = true;
650
  }
651
  }
652
 
653
- if ( ! $set_customer ) {
654
- $customer_id = false;
655
- } else {
656
- $customer_id = $customer->get_id() ? $customer->get_id() : false;
657
  }
658
 
659
  if ( empty( $source_object ) && ! $is_token ) {
@@ -1021,13 +1008,13 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
1021
  }
1022
 
1023
  /**
1024
- * Create a new PaymentIntent.
1025
  *
1026
  * @param WC_Order $order The order that is being paid for.
1027
  * @param object $prepared_source The source that is used for the payment.
1028
- * @return object An intent or an error.
1029
  */
1030
- public function create_intent( $order, $prepared_source ) {
1031
  // The request for a charge contains metadata for the intent.
1032
  $full_request = $this->generate_payment_request( $order, $prepared_source );
1033
 
@@ -1037,7 +1024,6 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
1037
  'currency' => strtolower( WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->get_order_currency() : $order->get_currency() ),
1038
  'description' => $full_request['description'],
1039
  'metadata' => $full_request['metadata'],
1040
- 'statement_descriptor' => $full_request['statement_descriptor'],
1041
  'capture_method' => ( 'true' === $full_request['capture'] ) ? 'automatic' : 'manual',
1042
  'payment_method_types' => array(
1043
  'card',
@@ -1048,6 +1034,31 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
1048
  $request['customer'] = $prepared_source->customer;
1049
  }
1050
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1051
  // Create an intent that awaits an action.
1052
  $intent = WC_Stripe_API::request( $request, 'payment_intents' );
1053
  if ( ! empty( $intent->error ) ) {
@@ -1167,11 +1178,22 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
1167
  $intent_id = $order->get_meta( '_stripe_intent_id' );
1168
  }
1169
 
1170
- if ( ! $intent_id ) {
1171
- return false;
 
 
 
 
 
 
 
 
 
 
 
1172
  }
1173
 
1174
- return WC_Stripe_API::request( array(), "payment_intents/$intent_id", 'GET' );
1175
  }
1176
 
1177
  /**
@@ -1208,4 +1230,106 @@ abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC {
1208
  $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
1209
  delete_transient( 'wc_stripe_processing_intent_' . $order_id );
1210
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1211
  }
72
  );
73
  }
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  /**
76
  * Checks to see if error is of invalid request
77
  * error and it is no such customer.
533
  * @return bool
534
  */
535
  public function is_prepaid_card( $source_object ) {
536
+ return (
537
+ $source_object
538
+ && ( 'token' === $source_object->object || 'source' === $source_object->object )
539
+ && 'prepaid' === $source_object->card->funding
540
+ );
541
  }
542
 
543
  /**
578
  */
579
  public function prepare_source( $user_id, $force_save_source = false ) {
580
  $customer = new WC_Stripe_Customer( $user_id );
 
581
  $force_save_source = apply_filters( 'wc_stripe_force_save_source', $force_save_source, $customer );
582
  $source_object = '';
583
  $source_id = '';
632
  throw new WC_Stripe_Exception( print_r( $response, true ), $response->error->message );
633
  }
634
  } else {
 
635
  $source_id = $stripe_token;
636
  $is_token = true;
637
  }
638
  }
639
 
640
+ $customer_id = $customer->get_id();
641
+ if ( ! $customer_id ) {
642
+ $customer->set_id( $customer->create_customer() );
643
+ $customer_id = $customer->get_id();
644
  }
645
 
646
  if ( empty( $source_object ) && ! $is_token ) {
1008
  }
1009
 
1010
  /**
1011
+ * Generates the request when creating a new payment intent.
1012
  *
1013
  * @param WC_Order $order The order that is being paid for.
1014
  * @param object $prepared_source The source that is used for the payment.
1015
+ * @return array The arguments for the request.
1016
  */
1017
+ public function generate_create_intent_request( $order, $prepared_source ) {
1018
  // The request for a charge contains metadata for the intent.
1019
  $full_request = $this->generate_payment_request( $order, $prepared_source );
1020
 
1024
  'currency' => strtolower( WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->get_order_currency() : $order->get_currency() ),
1025
  'description' => $full_request['description'],
1026
  'metadata' => $full_request['metadata'],
 
1027
  'capture_method' => ( 'true' === $full_request['capture'] ) ? 'automatic' : 'manual',
1028
  'payment_method_types' => array(
1029
  'card',
1034
  $request['customer'] = $prepared_source->customer;
1035
  }
1036
 
1037
+ if ( isset( $full_request['statement_descriptor'] ) ) {
1038
+ $request['statement_descriptor'] = $full_request['statement_descriptor'];
1039
+ }
1040
+
1041
+ /**
1042
+ * Filter the return value of the WC_Payment_Gateway_CC::generate_create_intent_request.
1043
+ *
1044
+ * @since 3.1.0
1045
+ * @param array $request
1046
+ * @param WC_Order $order
1047
+ * @param object $source
1048
+ */
1049
+ return apply_filters( 'wc_stripe_generate_create_intent_request', $request, $order, $prepared_source );
1050
+ }
1051
+
1052
+ /**
1053
+ * Create a new PaymentIntent.
1054
+ *
1055
+ * @param WC_Order $order The order that is being paid for.
1056
+ * @param object $prepared_source The source that is used for the payment.
1057
+ * @return object An intent or an error.
1058
+ */
1059
+ public function create_intent( $order, $prepared_source ) {
1060
+ $request = $this->generate_create_intent_request( $order, $prepared_source );
1061
+
1062
  // Create an intent that awaits an action.
1063
  $intent = WC_Stripe_API::request( $request, 'payment_intents' );
1064
  if ( ! empty( $intent->error ) ) {
1178
  $intent_id = $order->get_meta( '_stripe_intent_id' );
1179
  }
1180
 
1181
+ if ( $intent_id ) {
1182
+ return WC_Stripe_API::request( array(), "payment_intents/$intent_id", 'GET' );
1183
+ }
1184
+
1185
+ // The order doesn't have a payment intent, but it may have a setup intent.
1186
+ if ( WC_Stripe_Helper::is_wc_lt( '3.0' ) ) {
1187
+ $intent_id = get_post_meta( $order_id, '_stripe_setup_intent', true );
1188
+ } else {
1189
+ $intent_id = $order->get_meta( '_stripe_setup_intent' );
1190
+ }
1191
+
1192
+ if ( $intent_id ) {
1193
+ return WC_Stripe_API::request( array(), "setup_intents/$intent_id", 'GET' );
1194
  }
1195
 
1196
+ return false;
1197
  }
1198
 
1199
  /**
1230
  $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
1231
  delete_transient( 'wc_stripe_processing_intent_' . $order_id );
1232
  }
1233
+
1234
+ /**
1235
+ * Given a response from Stripe, check if it's a card error where authentication is required
1236
+ * to complete the payment.
1237
+ *
1238
+ * @param object $response The response from Stripe.
1239
+ * @return boolean Whether or not it's a 'authentication_required' error
1240
+ */
1241
+ public function is_authentication_required_for_payment( $response ) {
1242
+ return ( ! empty( $response->error ) && 'authentication_required' === $response->error->code )
1243
+ || ( ! empty( $response->last_payment_error ) && 'authentication_required' === $response->last_payment_error->code );
1244
+ }
1245
+
1246
+ /**
1247
+ * Creates a SetupIntent for future payments, and saves it to the order.
1248
+ *
1249
+ * @param WC_Order $order The ID of the (free/pre- order).
1250
+ * @param object $prepared_source The source, entered/chosen by the customer.
1251
+ * @return string The client secret of the intent, used for confirmation in JS.
1252
+ */
1253
+ public function setup_intent( $order, $prepared_source ) {
1254
+ $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
1255
+ $setup_intent = WC_Stripe_API::request( array(
1256
+ 'payment_method' => $prepared_source->source,
1257
+ 'customer' => $prepared_source->customer,
1258
+ 'confirm' => 'true',
1259
+ ), 'setup_intents' );
1260
+
1261
+ if ( is_wp_error( $setup_intent ) ) {
1262
+ WC_Stripe_Logger::log( "Unable to create SetupIntent for Order #$order_id: " . print_r( $setup_intent, true ) );
1263
+ } elseif ( 'requires_action' === $setup_intent->status ) {
1264
+ if ( WC_Stripe_Helper::is_wc_lt( '3.0' ) ) {
1265
+ update_post_meta( $order_id, '_stripe_setup_intent', $setup_intent->id );
1266
+ } else {
1267
+ $order->update_meta_data( '_stripe_setup_intent', $setup_intent->id );
1268
+ $order->save();
1269
+ }
1270
+
1271
+ return $setup_intent->client_secret;
1272
+ }
1273
+ }
1274
+
1275
+ /**
1276
+ * Create and confirm a new PaymentIntent.
1277
+ *
1278
+ * @param WC_Order $order The order that is being paid for.
1279
+ * @param object $prepared_source The source that is used for the payment.
1280
+ * @param float $amount The amount to charge. If not specified, it will be read from the order.
1281
+ * @return object An intent or an error.
1282
+ */
1283
+ public function create_and_confirm_intent_for_off_session( $order, $prepared_source, $amount = NULL ) {
1284
+ // The request for a charge contains metadata for the intent.
1285
+ $full_request = $this->generate_payment_request( $order, $prepared_source );
1286
+
1287
+ $request = array(
1288
+ 'amount' => $amount ? WC_Stripe_Helper::get_stripe_amount( $amount, $full_request['currency'] ) : $full_request['amount'],
1289
+ 'currency' => $full_request['currency'],
1290
+ 'description' => $full_request['description'],
1291
+ 'metadata' => $full_request['metadata'],
1292
+ 'payment_method_types' => array(
1293
+ 'card',
1294
+ ),
1295
+ 'off_session' => 'true',
1296
+ 'confirm' => 'true',
1297
+ 'confirmation_method' => 'automatic',
1298
+ );
1299
+
1300
+ if ( isset( $full_request['statement_descriptor'] ) ) {
1301
+ $request['statement_descriptor'] = $full_request['statement_descriptor'];
1302
+ }
1303
+
1304
+ if ( isset( $full_request['customer'] ) ) {
1305
+ $request['customer'] = $full_request['customer'];
1306
+ }
1307
+
1308
+ if ( isset( $full_request['source'] ) ) {
1309
+ $request['source'] = $full_request['source'];
1310
+ }
1311
+
1312
+ $intent = WC_Stripe_API::request( $request, 'payment_intents' );
1313
+ $is_authentication_required = $this->is_authentication_required_for_payment( $intent );
1314
+
1315
+ if ( ! empty( $intent->error ) && ! $is_authentication_required ) {
1316
+ return $intent;
1317
+ }
1318
+
1319
+ $intent_id = ( ! empty( $intent->error )
1320
+ ? $intent->error->payment_intent->id
1321
+ : $intent->id
1322
+ );
1323
+ $payment_intent = ( ! empty( $intent->error )
1324
+ ? $intent->error->payment_intent
1325
+ : $intent
1326
+ );
1327
+ $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
1328
+ WC_Stripe_Logger::log( "Stripe PaymentIntent $intent_id initiated for order $order_id" );
1329
+
1330
+ // Save the intent ID to the order.
1331
+ $this->save_intent_to_order( $order, $payment_intent );
1332
+
1333
+ return $intent;
1334
+ }
1335
  }
includes/admin/class-wc-stripe-admin-notices.php CHANGED
@@ -23,6 +23,7 @@ class WC_Stripe_Admin_Notices {
23
  public function __construct() {
24
  add_action( 'admin_notices', array( $this, 'admin_notices' ) );
25
  add_action( 'wp_loaded', array( $this, 'hide_notices' ) );
 
26
  }
27
 
28
  /**
@@ -106,6 +107,7 @@ class WC_Stripe_Admin_Notices {
106
  $show_phpver_notice = get_option( 'wc_stripe_show_phpver_notice' );
107
  $show_wcver_notice = get_option( 'wc_stripe_show_wcver_notice' );
108
  $show_curl_notice = get_option( 'wc_stripe_show_curl_notice' );
 
109
  $options = get_option( 'woocommerce_stripe_settings' );
110
  $testmode = ( isset( $options['testmode'] ) && 'yes' === $options['testmode'] ) ? true : false;
111
  $test_pub_key = isset( $options['test_publishable_key'] ) ? $options['test_publishable_key'] : '';
@@ -126,9 +128,9 @@ class WC_Stripe_Admin_Notices {
126
 
127
  if ( empty( $show_style_notice ) ) {
128
  /* translators: 1) int version 2) int version */
129
- $message = __( 'WooCommerce Stripe - We recently made changes to Stripe that may impact the appearance of your checkout. If your checkout has changed unexpectedly, please follow these <a href="https://docs.woocommerce.com/document/stripe/#section-45" target="_blank">instructions</a> to fix.', 'woocommerce-gateway-stripe' );
130
 
131
- $this->add_admin_notice( 'style', 'error', $message, true );
132
 
133
  return;
134
  }
@@ -199,6 +201,10 @@ class WC_Stripe_Admin_Notices {
199
  $this->add_admin_notice( 'ssl', 'notice notice-warning', sprintf( __( 'Stripe is enabled, but a SSL certificate is not detected. Your checkout may not be secure! Please ensure your server has a valid <a href="%1$s" target="_blank">SSL certificate</a>', 'woocommerce-gateway-stripe' ), 'https://en.wikipedia.org/wiki/Transport_Layer_Security' ), true );
200
  }
201
  }
 
 
 
 
202
  }
203
  }
204
 
@@ -292,6 +298,9 @@ class WC_Stripe_Admin_Notices {
292
  case 'SOFORT':
293
  update_option( 'wc_stripe_show_sofort_notice', 'no' );
294
  break;
 
 
 
295
  }
296
  }
297
  }
@@ -310,6 +319,25 @@ class WC_Stripe_Admin_Notices {
310
 
311
  return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug );
312
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  }
314
 
315
  new WC_Stripe_Admin_Notices();
23
  public function __construct() {
24
  add_action( 'admin_notices', array( $this, 'admin_notices' ) );
25
  add_action( 'wp_loaded', array( $this, 'hide_notices' ) );
26
+ add_action( 'woocommerce_stripe_updated', array( $this, 'stripe_updated' ) );
27
  }
28
 
29
  /**
107
  $show_phpver_notice = get_option( 'wc_stripe_show_phpver_notice' );
108
  $show_wcver_notice = get_option( 'wc_stripe_show_wcver_notice' );
109
  $show_curl_notice = get_option( 'wc_stripe_show_curl_notice' );
110
+ $show_sca_notice = get_option( 'wc_stripe_show_sca_notice' );
111
  $options = get_option( 'woocommerce_stripe_settings' );
112
  $testmode = ( isset( $options['testmode'] ) && 'yes' === $options['testmode'] ) ? true : false;
113
  $test_pub_key = isset( $options['test_publishable_key'] ) ? $options['test_publishable_key'] : '';
128
 
129
  if ( empty( $show_style_notice ) ) {
130
  /* translators: 1) int version 2) int version */
131
+ $message = __( 'WooCommerce Stripe - We recently made changes to Stripe that may impact the appearance of your checkout. If your checkout has changed unexpectedly, please follow these <a href="https://docs.woocommerce.com/document/stripe/#styling" target="_blank">instructions</a> to fix.', 'woocommerce-gateway-stripe' );
132
 
133
+ $this->add_admin_notice( 'style', 'notice notice-warning', $message, true );
134
 
135
  return;
136
  }
201
  $this->add_admin_notice( 'ssl', 'notice notice-warning', sprintf( __( 'Stripe is enabled, but a SSL certificate is not detected. Your checkout may not be secure! Please ensure your server has a valid <a href="%1$s" target="_blank">SSL certificate</a>', 'woocommerce-gateway-stripe' ), 'https://en.wikipedia.org/wiki/Transport_Layer_Security' ), true );
202
  }
203
  }
204
+
205
+ if ( empty( $show_sca_notice ) ) {
206
+ $this->add_admin_notice( 'sca', 'notice notice-success', sprintf( __( 'Stripe is now ready for Strong Customer Authentication (SCA) and 3D Secure 2! <a href="%1$s" target="_blank">Read about SCA</a>', 'woocommerce-gateway-stripe' ), 'https://woocommerce.com/posts/introducing-strong-customer-authentication-sca/' ), true );
207
+ }
208
  }
209
  }
210
 
298
  case 'SOFORT':
299
  update_option( 'wc_stripe_show_sofort_notice', 'no' );
300
  break;
301
+ case 'sca':
302
+ update_option( 'wc_stripe_show_sca_notice', 'no' );
303
+ break;
304
  }
305
  }
306
  }
319
 
320
  return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug );
321
  }
322
+
323
+ /**
324
+ * Saves options in order to hide notices based on the gateway's version.
325
+ *
326
+ * @since 4.3.0
327
+ */
328
+ public function stripe_updated() {
329
+ $previous_version = get_option( 'wc_stripe_version' );
330
+
331
+ // Only show the style notice if the plugin was installed and older than 4.1.4.
332
+ if ( empty( $previous_version ) || version_compare( $previous_version, '4.1.4', 'ge' ) ) {
333
+ update_option( 'wc_stripe_show_style_notice', 'no' );
334
+ }
335
+
336
+ // Only show the SCA notice on pre-4.3.0 installs.
337
+ if ( empty( $previous_version ) || version_compare( $previous_version, '4.3.0', 'ge' ) ) {
338
+ update_option( 'wc_stripe_show_sca_notice', 'no' );
339
+ }
340
+ }
341
  }
342
 
343
  new WC_Stripe_Admin_Notices();
includes/class-wc-gateway-stripe.php CHANGED
@@ -139,6 +139,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
139
  add_action( 'woocommerce_account_view-order_endpoint', array( $this, 'check_intent_status_on_order_page' ), 1 );
140
  add_filter( 'woocommerce_payment_successful_result', array( $this, 'modify_successful_payment_result' ), 99999, 2 );
141
  add_action( 'set_logged_in_cookie', array( $this, 'set_cookie_on_current_request' ) );
 
142
 
143
  if ( WC_Stripe_Helper::is_pre_orders_exists() ) {
144
  $this->pre_orders = new WC_Stripe_Pre_Orders_Compat();
@@ -248,29 +249,17 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
248
  }
249
 
250
  if ( is_add_payment_method_page() ) {
251
- $pay_button_text = __( 'Add Card', 'woocommerce-gateway-stripe' );
252
- $total = '';
253
  $firstname = $user->user_firstname;
254
  $lastname = $user->user_lastname;
255
-
256
- } elseif ( function_exists( 'wcs_order_contains_subscription' ) && isset( $_GET['change_payment_method'] ) ) { // wpcs: csrf ok.
257
- $pay_button_text = __( 'Change Payment Method', 'woocommerce-gateway-stripe' );
258
- $total = '';
259
- } else {
260
- $pay_button_text = '';
261
  }
262
 
263
  ob_start();
264
 
265
  echo '<div
266
  id="stripe-payment-data"
267
- data-panel-label="' . esc_attr( $pay_button_text ) . '"
268
  data-email="' . esc_attr( $user_email ) . '"
269
- data-amount="' . esc_attr( WC_Stripe_Helper::get_stripe_amount( $total ) ) . '"
270
- data-name="' . esc_attr( $this->statement_descriptor ) . '"
271
  data-full-name="' . esc_attr( $firstname . ' ' . $lastname ) . '"
272
  data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
273
- data-allow-remember-me="' . esc_attr( apply_filters( 'wc_stripe_allow_remember_me', true ) ? 'true' : 'false' ) . '"
274
  >';
275
 
276
  if ( $this->testmode ) {
@@ -383,7 +372,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
383
  * @version 4.0.0
384
  */
385
  public function payment_scripts() {
386
- if ( ! is_product() && ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) { // wpcs: csrf ok.
387
  return;
388
  }
389
 
@@ -476,23 +465,6 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
476
  wp_enqueue_script( 'woocommerce_stripe' );
477
  }
478
 
479
- /**
480
- * Creates a new WC_Stripe_Customer if the visitor chooses to.
481
- *
482
- * @since 4.2.0
483
- * @param WC_Order $order The order that is being created.
484
- */
485
- public function maybe_create_customer( $order ) {
486
- // This comes from the create account checkbox in the checkout page.
487
- if ( empty( $_POST['createaccount'] ) ) { // wpcs: csrf ok.
488
- return;
489
- }
490
-
491
- $new_customer_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->customer_user : $order->get_customer_id();
492
- $new_stripe_customer = new WC_Stripe_Customer( $new_customer_id );
493
- $new_stripe_customer->create_customer();
494
- }
495
-
496
  /**
497
  * Checks if a source object represents a prepaid credit card and
498
  * throws an exception if it is one, but that is not allowed.
@@ -554,20 +526,33 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
554
  * Completes an order without a positive value.
555
  *
556
  * @since 4.2.0
557
- * @param WC_Order $order The order to complete.
558
- * @return array Redirection data for `process_payment`.
 
 
559
  */
560
- public function complete_free_order( $order ) {
561
- $order->payment_complete();
 
 
 
 
 
 
 
 
 
 
 
 
562
 
563
  // Remove cart.
564
  WC()->cart->empty_cart();
565
 
 
 
566
  // Return thank you page redirect.
567
- return array(
568
- 'result' => 'success',
569
- 'redirect' => $this->get_return_url( $order ),
570
- );
571
  }
572
 
573
  /**
@@ -593,8 +578,6 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
593
  return $this->pre_orders->process_pre_order( $order_id );
594
  }
595
 
596
- $this->maybe_create_customer( $order );
597
-
598
  $prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
599
 
600
  $this->maybe_disallow_prepaid_card( $prepared_source );
@@ -602,7 +585,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
602
  $this->save_source_to_order( $order, $prepared_source );
603
 
604
  if ( 0 >= $order->get_total() ) {
605
- return $this->complete_free_order( $order );
606
  }
607
 
608
  // This will throw exception if not valid.
@@ -611,6 +594,10 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
611
  WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
612
 
613
  $intent = $this->get_intent_from_order( $order );
 
 
 
 
614
  if ( $intent ) {
615
  $intent = $this->update_existing_intent( $intent, $order, $prepared_source );
616
  } else {
@@ -658,9 +645,9 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
658
  */
659
 
660
  return array(
661
- 'result' => 'success',
662
- 'redirect' => $this->get_return_url( $order ),
663
- 'intent_secret' => $intent->client_secret,
664
  );
665
  }
666
  }
@@ -670,7 +657,9 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
670
  $this->process_response( $response, $order );
671
 
672
  // Remove cart.
673
- WC()->cart->empty_cart();
 
 
674
 
675
  // Unlock the order.
676
  $this->unlock_order_payment( $order );
@@ -836,9 +825,16 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
836
  return $gateways;
837
  }
838
 
 
 
 
 
 
 
 
839
  add_filter( 'woocommerce_checkout_show_terms', '__return_false' );
840
  add_filter( 'woocommerce_pay_order_button_html', '__return_false' );
841
- add_filter( 'woocommerce_available_payment_gateways', array( $this, '__return_empty_array' ) );
842
  add_filter( 'woocommerce_no_available_payment_methods_message', array( $this, 'change_no_available_methods_message' ) );
843
  add_action( 'woocommerce_pay_order_after_submit', array( $this, 'render_payment_intent_inputs' ) );
844
 
@@ -856,14 +852,52 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
856
  return wpautop( __( "Almost there!\n\nYour order has already been created, the only thing that still needs to be done is for you to authorize the payment with your bank.", 'woocommerce-gateway-stripe' ) );
857
  }
858
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
859
  /**
860
  * Renders hidden inputs on the "Pay for Order" page in order to let Stripe handle PaymentIntents.
861
  *
 
 
 
862
  * @since 4.2
863
  */
864
- public function render_payment_intent_inputs() {
865
- $order = wc_get_order( absint( get_query_var( 'order-pay' ) ) );
866
- $intent = $this->get_intent_from_order( $order );
 
 
 
 
867
 
868
  $verification_url = add_query_arg(
869
  array(
@@ -875,7 +909,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
875
  WC_AJAX::get_endpoint( 'wc_stripe_verify_intent' )
876
  );
877
 
878
- echo '<input type="hidden" id="stripe-intent-id" value="' . esc_attr( $intent->client_secret ) . '" />';
879
  echo '<input type="hidden" id="stripe-intent-return" value="' . esc_attr( $verification_url ) . '" />';
880
  }
881
 
@@ -917,7 +951,7 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
917
  /**
918
  * Attached to `woocommerce_payment_successful_result` with a late priority,
919
  * this method will combine the "naturally" generated redirect URL from
920
- * WooCommerce and a payment intent secret into a hash, which contains both
921
  * the secret, and a proper URL, which will confirm whether the intent succeeded.
922
  *
923
  * @since 4.2.0
@@ -926,8 +960,8 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
926
  * @return array
927
  */
928
  public function modify_successful_payment_result( $result, $order_id ) {
929
- // Only redirects with intents need to be modified.
930
- if ( ! isset( $result['intent_secret'] ) ) {
931
  return $result;
932
  }
933
 
@@ -941,8 +975,11 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
941
  WC_AJAX::get_endpoint( 'wc_stripe_verify_intent' )
942
  );
943
 
944
- // Combine into a hash.
945
- $redirect = sprintf( '#confirm-pi-%s:%s', $result['intent_secret'], rawurlencode( $verification_url ) );
 
 
 
946
 
947
  return array(
948
  'result' => 'success',
@@ -990,7 +1027,14 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
990
  return;
991
  }
992
 
993
- if ( 'succeeded' === $intent->status || 'requires_capture' === $intent->status ) {
 
 
 
 
 
 
 
994
  // Proceed with the payment completion.
995
  $this->process_response( end( $intent->charges->data ), $order );
996
  } else if ( 'requires_payment_method' === $intent->status ) {
@@ -1015,12 +1059,26 @@ class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
1015
  }
1016
 
1017
  // Load the right message and update the status.
1018
- $status_message = ( $intent->last_payment_error )
1019
  /* translators: 1) The error message that was received from Stripe. */
1020
  ? sprintf( __( 'Stripe SCA authentication failed. Reason: %s', 'woocommerce-gateway-stripe' ), $intent->last_payment_error->message )
1021
  : __( 'Stripe SCA authentication failed.', 'woocommerce-gateway-stripe' );
1022
  $order->update_status( 'failed', $status_message );
 
1023
 
1024
- $this->send_failed_order_email( $order->get_id() );
 
 
 
 
 
 
 
 
 
 
 
 
 
1025
  }
1026
  }
139
  add_action( 'woocommerce_account_view-order_endpoint', array( $this, 'check_intent_status_on_order_page' ), 1 );
140
  add_filter( 'woocommerce_payment_successful_result', array( $this, 'modify_successful_payment_result' ), 99999, 2 );
141
  add_action( 'set_logged_in_cookie', array( $this, 'set_cookie_on_current_request' ) );
142
+ add_filter( 'woocommerce_get_checkout_payment_url', array( $this, 'get_checkout_payment_url' ), 10, 2 );
143
 
144
  if ( WC_Stripe_Helper::is_pre_orders_exists() ) {
145
  $this->pre_orders = new WC_Stripe_Pre_Orders_Compat();
249
  }
250
 
251
  if ( is_add_payment_method_page() ) {
 
 
252
  $firstname = $user->user_firstname;
253
  $lastname = $user->user_lastname;
 
 
 
 
 
 
254
  }
255
 
256
  ob_start();
257
 
258
  echo '<div
259
  id="stripe-payment-data"
 
260
  data-email="' . esc_attr( $user_email ) . '"
 
 
261
  data-full-name="' . esc_attr( $firstname . ' ' . $lastname ) . '"
262
  data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
 
263
  >';
264
 
265
  if ( $this->testmode ) {
372
  * @version 4.0.0
373
  */
374
  public function payment_scripts() {
375
+ if ( ! is_product() && ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) || ( is_order_received_page() ) ) { // wpcs: csrf ok.
376
  return;
377
  }
378
 
465
  wp_enqueue_script( 'woocommerce_stripe' );
466
  }
467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
  /**
469
  * Checks if a source object represents a prepaid credit card and
470
  * throws an exception if it is one, but that is not allowed.
526
  * Completes an order without a positive value.
527
  *
528
  * @since 4.2.0
529
+ * @param WC_Order $order The order to complete.
530
+ * @param WC_Order $prepared_source Payment source and customer data.
531
+ * @param boolean $force_save_source Whether the payment source must be saved, like when dealing with a Subscription setup.
532
+ * @return array Redirection data for `process_payment`.
533
  */
534
+ public function complete_free_order( $order, $prepared_source, $force_save_source ) {
535
+ $response = array(
536
+ 'result' => 'success',
537
+ 'redirect' => $this->get_return_url( $order ),
538
+ );
539
+
540
+ if ( $force_save_source ) {
541
+ $intent_secret = $this->setup_intent( $order, $prepared_source );
542
+
543
+ if ( ! empty( $intent_secret ) ) {
544
+ $response['setup_intent_secret'] = $intent_secret;
545
+ return $response;
546
+ }
547
+ }
548
 
549
  // Remove cart.
550
  WC()->cart->empty_cart();
551
 
552
+ $order->payment_complete();
553
+
554
  // Return thank you page redirect.
555
+ return $response;
 
 
 
556
  }
557
 
558
  /**
578
  return $this->pre_orders->process_pre_order( $order_id );
579
  }
580
 
 
 
581
  $prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
582
 
583
  $this->maybe_disallow_prepaid_card( $prepared_source );
585
  $this->save_source_to_order( $order, $prepared_source );
586
 
587
  if ( 0 >= $order->get_total() ) {
588
+ return $this->complete_free_order( $order, $prepared_source, $force_save_source );
589
  }
590
 
591
  // This will throw exception if not valid.
594
  WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
595
 
596
  $intent = $this->get_intent_from_order( $order );
597
+ if ( isset( $intent->object ) && 'setup_intent' === $intent->object ) {
598
+ $intent = false; // This function can only deal with *payment* intents
599
+ }
600
+
601
  if ( $intent ) {
602
  $intent = $this->update_existing_intent( $intent, $order, $prepared_source );
603
  } else {
645
  */
646
 
647
  return array(
648
+ 'result' => 'success',
649
+ 'redirect' => $this->get_return_url( $order ),
650
+ 'payment_intent_secret' => $intent->client_secret,
651
  );
652
  }
653
  }
657
  $this->process_response( $response, $order );
658
 
659
  // Remove cart.
660
+ if ( isset( WC()->cart ) ) {
661
+ WC()->cart->empty_cart();
662
+ }
663
 
664
  // Unlock the order.
665
  $this->unlock_order_payment( $order );
825
  return $gateways;
826
  }
827
 
828
+ try {
829
+ $this->prepare_intent_for_order_pay_page();
830
+ } catch ( WC_Stripe_Exception $e ) {
831
+ // Just show the full order pay page if there was a problem preparing the Payment Intent
832
+ return $gateways;
833
+ }
834
+
835
  add_filter( 'woocommerce_checkout_show_terms', '__return_false' );
836
  add_filter( 'woocommerce_pay_order_button_html', '__return_false' );
837
+ add_filter( 'woocommerce_available_payment_gateways', '__return_empty_array' );
838
  add_filter( 'woocommerce_no_available_payment_methods_message', array( $this, 'change_no_available_methods_message' ) );
839
  add_action( 'woocommerce_pay_order_after_submit', array( $this, 'render_payment_intent_inputs' ) );
840
 
852
  return wpautop( __( "Almost there!\n\nYour order has already been created, the only thing that still needs to be done is for you to authorize the payment with your bank.", 'woocommerce-gateway-stripe' ) );
853
  }
854
 
855
+ /**
856
+ * Prepares the Payment Intent for it to be completed in the "Pay for Order" page.
857
+ *
858
+ * @param WC_Order|null $order Order object, or null to get the order from the "order-pay" URL parameter
859
+ *
860
+ * @throws WC_Stripe_Exception
861
+ * @since 4.3
862
+ */
863
+ public function prepare_intent_for_order_pay_page( $order = null ) {
864
+ if ( ! isset( $order ) || empty( $order ) ) {
865
+ $order = wc_get_order( absint( get_query_var( 'order-pay' ) ) );
866
+ }
867
+ $intent = $this->get_intent_from_order( $order );
868
+
869
+ if ( ! $intent ) {
870
+ throw new WC_Stripe_Exception( 'Payment Intent not found', __( 'Payment Intent not found for order #' . $order->get_id(), 'woocommerce-gateway-stripe' ) );
871
+ }
872
+
873
+ if ( 'requires_payment_method' === $intent->status && isset( $intent->last_payment_error )
874
+ && 'authentication_required' === $intent->last_payment_error->code ) {
875
+ $intent = WC_Stripe_API::request( array(
876
+ 'payment_method' => $intent->last_payment_error->source->id,
877
+ ), 'payment_intents/' . $intent->id . '/confirm' );
878
+ if ( isset( $intent->error ) ) {
879
+ throw new WC_Stripe_Exception( print_r( $intent, true ), $intent->error->message );
880
+ }
881
+ }
882
+
883
+ $this->order_pay_intent = $intent;
884
+ }
885
+
886
  /**
887
  * Renders hidden inputs on the "Pay for Order" page in order to let Stripe handle PaymentIntents.
888
  *
889
+ * @param WC_Order|null $order Order object, or null to get the order from the "order-pay" URL parameter
890
+ *
891
+ * @throws WC_Stripe_Exception
892
  * @since 4.2
893
  */
894
+ public function render_payment_intent_inputs( $order = null ) {
895
+ if ( ! isset( $order ) || empty( $order ) ) {
896
+ $order = wc_get_order( absint( get_query_var( 'order-pay' ) ) );
897
+ }
898
+ if ( ! isset( $this->order_pay_intent ) ) {
899
+ $this->prepare_intent_for_order_pay_page( $order );
900
+ }
901
 
902
  $verification_url = add_query_arg(
903
  array(
909
  WC_AJAX::get_endpoint( 'wc_stripe_verify_intent' )
910
  );
911
 
912
+ echo '<input type="hidden" id="stripe-intent-id" value="' . esc_attr( $this->order_pay_intent->client_secret ) . '" />';
913
  echo '<input type="hidden" id="stripe-intent-return" value="' . esc_attr( $verification_url ) . '" />';
914
  }
915
 
951
  /**
952
  * Attached to `woocommerce_payment_successful_result` with a late priority,
953
  * this method will combine the "naturally" generated redirect URL from
954
+ * WooCommerce and a payment/setup intent secret into a hash, which contains both
955
  * the secret, and a proper URL, which will confirm whether the intent succeeded.
956
  *
957
  * @since 4.2.0
960
  * @return array
961
  */
962
  public function modify_successful_payment_result( $result, $order_id ) {
963
+ if ( ! isset( $result['payment_intent_secret'] ) && ! isset( $result['setup_intent_secret'] ) ) {
964
+ // Only redirects with intents need to be modified.
965
  return $result;
966
  }
967
 
975
  WC_AJAX::get_endpoint( 'wc_stripe_verify_intent' )
976
  );
977
 
978
+ if ( isset( $result['payment_intent_secret'] ) ) {
979
+ $redirect = sprintf( '#confirm-pi-%s:%s', $result['payment_intent_secret'], rawurlencode( $verification_url ) );
980
+ } else if ( isset( $result['setup_intent_secret'] ) ) {
981
+ $redirect = sprintf( '#confirm-si-%s:%s', $result['setup_intent_secret'], rawurlencode( $verification_url ) );
982
+ }
983
 
984
  return array(
985
  'result' => 'success',
1027
  return;
1028
  }
1029
 
1030
+ if ( 'setup_intent' === $intent->object && 'succeeded' === $intent->status ) {
1031
+ WC()->cart->empty_cart();
1032
+ if ( WC_Stripe_Helper::is_pre_orders_exists() && WC_Pre_Orders_Order::order_contains_pre_order( $order ) ) {
1033
+ WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
1034
+ } else {
1035
+ $order->payment_complete();
1036
+ }
1037
+ } else if ( 'succeeded' === $intent->status || 'requires_capture' === $intent->status ) {
1038
  // Proceed with the payment completion.
1039
  $this->process_response( end( $intent->charges->data ), $order );
1040
  } else if ( 'requires_payment_method' === $intent->status ) {
1059
  }
1060
 
1061
  // Load the right message and update the status.
1062
+ $status_message = isset( $intent->last_payment_error )
1063
  /* translators: 1) The error message that was received from Stripe. */
1064
  ? sprintf( __( 'Stripe SCA authentication failed. Reason: %s', 'woocommerce-gateway-stripe' ), $intent->last_payment_error->message )
1065
  : __( 'Stripe SCA authentication failed.', 'woocommerce-gateway-stripe' );
1066
  $order->update_status( 'failed', $status_message );
1067
+ }
1068
 
1069
+ /**
1070
+ * Preserves the "wc-stripe-confirmation" URL parameter so the user can complete the SCA authentication after logging in.
1071
+ *
1072
+ * @param string $pay_url Current computed checkout URL for the given order.
1073
+ * @param WC_Order $order Order object.
1074
+ *
1075
+ * @return string Checkout URL for the given order.
1076
+ */
1077
+ public function get_checkout_payment_url( $pay_url, $order ) {
1078
+ global $wp;
1079
+ if ( isset( $_GET['wc-stripe-confirmation'] ) && isset( $wp->query_vars['order-pay'] ) && $wp->query_vars['order-pay'] == $order->get_id() ) {
1080
+ $pay_url = add_query_arg( 'wc-stripe-confirmation', 1, $pay_url );
1081
+ }
1082
+ return $pay_url;
1083
  }
1084
  }
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 = '2019-02-19';
18
 
19
  /**
20
  * Secret API Key.
14
  * Stripe API Endpoint
15
  */
16
  const ENDPOINT = 'https://api.stripe.com/v1/';
17
+ const STRIPE_API_VERSION = '2019-09-09';
18
 
19
  /**
20
  * Secret API Key.
includes/class-wc-stripe-customer.php CHANGED
@@ -116,16 +116,23 @@ class WC_Stripe_Customer {
116
  $billing_last_name = get_user_meta( $user->ID, 'last_name', true );
117
  }
118
 
119
- $description = __( 'Name', 'woocommerce-gateway-stripe' ) . ': ' . $billing_first_name . ' ' . $billing_last_name . ' ' . __( 'Username', 'woocommerce-gateway-stripe' ) . ': ' . $user->user_login;
 
120
 
121
  $defaults = array(
122
  'email' => $user->user_email,
123
  'description' => $description,
124
  );
125
  } else {
 
 
 
 
 
 
126
  $defaults = array(
127
  'email' => $billing_email,
128
- 'description' => '',
129
  );
130
  }
131
 
@@ -171,10 +178,9 @@ class WC_Stripe_Customer {
171
  /**
172
  * Add a source for this stripe customer.
173
  * @param string $source_id
174
- * @param bool $retry
175
  * @return WP_Error|int
176
  */
177
- public function add_source( $source_id, $retry = true ) {
178
  if ( ! $this->get_id() ) {
179
  $this->set_id( $this->create_customer() );
180
  }
@@ -195,7 +201,7 @@ class WC_Stripe_Customer {
195
  if ( $this->is_no_such_customer_error( $response->error ) ) {
196
  delete_user_meta( $this->get_user_id(), '_stripe_customer_id' );
197
  $this->create_customer();
198
- return $this->add_source( $source_id, false );
199
  } else {
200
  return $response;
201
  }
116
  $billing_last_name = get_user_meta( $user->ID, 'last_name', true );
117
  }
118
 
119
+ // translators: %1$s First name, %2$s Second name, %3$s Username.
120
+ $description = sprintf( __( 'Name: %1$s %2$s, Username: %s', 'woocommerce-gateway-stripe' ), $billing_first_name, $billing_last_name, $user->user_login );
121
 
122
  $defaults = array(
123
  'email' => $user->user_email,
124
  'description' => $description,
125
  );
126
  } else {
127
+ $billing_first_name = isset( $_POST['billing_first_name'] ) ? filter_var( wp_unslash( $_POST['billing_first_name'] ), FILTER_SANITIZE_STRING ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
128
+ $billing_last_name = isset( $_POST['billing_last_name'] ) ? filter_var( wp_unslash( $_POST['billing_last_name'] ), FILTER_SANITIZE_STRING ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
129
+
130
+ // translators: %1$s First name, %2$s Second name.
131
+ $description = sprintf( __( 'Name: %1$s %2$s, Guest', 'woocommerce-gateway-stripe' ), $billing_first_name, $billing_last_name );
132
+
133
  $defaults = array(
134
  'email' => $billing_email,
135
+ 'description' => $description,
136
  );
137
  }
138
 
178
  /**
179
  * Add a source for this stripe customer.
180
  * @param string $source_id
 
181
  * @return WP_Error|int
182
  */
183
+ public function add_source( $source_id ) {
184
  if ( ! $this->get_id() ) {
185
  $this->set_id( $this->create_customer() );
186
  }
201
  if ( $this->is_no_such_customer_error( $response->error ) ) {
202
  delete_user_meta( $this->get_user_id(), '_stripe_customer_id' );
203
  $this->create_customer();
204
+ return $this->add_source( $source_id );
205
  } else {
206
  return $response;
207
  }
includes/class-wc-stripe-helper.php CHANGED
@@ -453,6 +453,25 @@ class WC_Stripe_Helper {
453
  return false;
454
  }
455
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  /**
457
  * Sanitize statement descriptor text.
458
  *
453
  return false;
454
  }
455
 
456
+ /**
457
+ * Gets the order by Stripe SetupIntent ID.
458
+ *
459
+ * @since 4.3
460
+ * @param string $intent_id The ID of the intent.
461
+ * @return WC_Order|bool Either an order or false when not found.
462
+ */
463
+ public static function get_order_by_setup_intent_id( $intent_id ) {
464
+ global $wpdb;
465
+
466
+ $order_id = $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT ID FROM $wpdb->posts as posts LEFT JOIN $wpdb->postmeta as meta ON posts.ID = meta.post_id WHERE meta.meta_value = %s AND meta.meta_key = %s", $intent_id, '_stripe_setup_intent' ) );
467
+
468
+ if ( ! empty( $order_id ) ) {
469
+ return wc_get_order( $order_id );
470
+ }
471
+
472
+ return false;
473
+ }
474
+
475
  /**
476
  * Sanitize statement descriptor text.
477
  *
includes/class-wc-stripe-intent-controller.php CHANGED
@@ -92,7 +92,7 @@ class WC_Stripe_Intent_Controller {
92
  wc_add_notice( esc_html( $message ), 'error' );
93
 
94
  $redirect_url = $woocommerce->cart->is_empty()
95
- ? get_permalink( woocommerce_get_page_id( 'shop' ) )
96
  : wc_get_checkout_url();
97
 
98
  $this->handle_error( $e, $redirect_url );
92
  wc_add_notice( esc_html( $message ), 'error' );
93
 
94
  $redirect_url = $woocommerce->cart->is_empty()
95
+ ? get_permalink( WC_Stripe_Helper::is_wc_lt( '3.0' ) ? woocommerce_get_page_id( 'shop' ) : wc_get_page_id( 'shop' ) )
96
  : wc_get_checkout_url();
97
 
98
  $this->handle_error( $e, $redirect_url );
includes/class-wc-stripe-webhook-handler.php CHANGED
@@ -81,7 +81,6 @@ class WC_Stripe_Webhook_Handler extends WC_Stripe_Payment_Gateway {
81
  *
82
  * @since 4.0.0
83
  * @version 4.0.0
84
- * @todo Implement proper webhook signature validation. Ref https://stripe.com/docs/webhooks#signatures
85
  * @param string $request_headers The request headers from Stripe.
86
  * @param string $request_body The request body from Stripe.
87
  * @return bool
@@ -693,6 +692,43 @@ class WC_Stripe_Webhook_Handler extends WC_Stripe_Payment_Gateway {
693
  $this->unlock_order_payment( $order );
694
  }
695
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
696
  /**
697
  * Processes the incoming webhook.
698
  *
@@ -744,6 +780,11 @@ class WC_Stripe_Webhook_Handler extends WC_Stripe_Payment_Gateway {
744
  case 'payment_intent.payment_failed':
745
  case 'payment_intent.amount_capturable_updated':
746
  $this->process_payment_intent_success( $notification );
 
 
 
 
 
747
 
748
  }
749
  }
81
  *
82
  * @since 4.0.0
83
  * @version 4.0.0
 
84
  * @param string $request_headers The request headers from Stripe.
85
  * @param string $request_body The request body from Stripe.
86
  * @return bool
692
  $this->unlock_order_payment( $order );
693
  }
694
 
695
+ public function process_setup_intent( $notification ) {
696
+ $intent = $notification->data->object;
697
+ $order = WC_Stripe_Helper::get_order_by_setup_intent_id( $intent->id );
698
+
699
+ if ( ! $order ) {
700
+ WC_Stripe_Logger::log( 'Could not find order via setup intent ID: ' . $intent->id );
701
+ return;
702
+ }
703
+
704
+ if ( 'pending' !== $order->get_status() && 'failed' !== $order->get_status() ) {
705
+ return;
706
+ }
707
+
708
+ if ( $this->lock_order_payment( $order, $intent ) ) {
709
+ return;
710
+ }
711
+
712
+ $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
713
+ if ( 'setup_intent.succeeded' === $notification->type ) {
714
+ WC_Stripe_Logger::log( "Stripe SetupIntent $intent->id succeeded for order $order_id" );
715
+ if ( WC_Stripe_Helper::is_pre_orders_exists() && WC_Pre_Orders_Order::order_contains_pre_order( $order ) ) {
716
+ WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
717
+ } else {
718
+ $order->payment_complete();
719
+ }
720
+ } else {
721
+ $error_message = $intent->last_setup_error ? $intent->last_setup_error->message : "";
722
+
723
+ /* translators: 1) The error message that was received from Stripe. */
724
+ $order->update_status( 'failed', sprintf( __( 'Stripe SCA authentication failed. Reason: %s', 'woocommerce-gateway-stripe' ), $error_message ) );
725
+
726
+ $this->send_failed_order_email( $order_id );
727
+ }
728
+
729
+ $this->unlock_order_payment( $order );
730
+ }
731
+
732
  /**
733
  * Processes the incoming webhook.
734
  *
780
  case 'payment_intent.payment_failed':
781
  case 'payment_intent.amount_capturable_updated':
782
  $this->process_payment_intent_success( $notification );
783
+ break;
784
+
785
+ case 'setup_intent.succeeded':
786
+ case 'setup_intent.setup_failed':
787
+ $this->process_setup_intent( $notification );
788
 
789
  }
790
  }
includes/compat/class-wc-stripe-email-failed-authentication-retry.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin email about payment retry failed due to authentication
4
+ *
5
+ * Email sent to admins when an attempt to automatically process a subscription renewal payment has failed
6
+ * with the `authentication_needed` error, and a retry rule has been applied to retry the payment in the future.
7
+ *
8
+ * @version 4.3.0
9
+ * @package WooCommerce_Stripe/Classes/WC_Stripe_Email_Failed_Authentication_Retry
10
+ * @extends WC_Email_Failed_Order
11
+ */
12
+
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * An email sent to the admin when payment fails to go through due to authentication_required error.
19
+ *
20
+ * @since 4.3.0
21
+ */
22
+ class WC_Stripe_Email_Failed_Authentication_Retry extends WC_Email_Failed_Order {
23
+
24
+ /**
25
+ * Constructor
26
+ */
27
+ public function __construct() {
28
+ $this->id = 'failed_authentication_requested';
29
+ $this->title = __( 'Payment Authentication Requested Email', 'woocommerce-gateway-stripe' );
30
+ $this->description = __( 'Payment authentication requested emails are sent to chosen recipient(s) when an attempt to automatically process a subscription renewal payment fails because the transaction requires an SCA verification, the customer is requested to authenticate the payment, and a retry rule has been applied to notify the customer again within a certain time period.', 'woocommerce-gateway-stripe' );
31
+
32
+ $this->heading = __( 'Automatic renewal payment failed due to authentication required', 'woocommerce-gateway-stripe' );
33
+ $this->subject = __( '[{site_title}] Automatic payment failed for {order_number}. Customer asked to authenticate payment and will be notified again {retry_time}', 'woocommerce-gateway-stripe' );
34
+
35
+ $this->template_html = 'emails/failed-renewal-authentication-requested.php';
36
+ $this->template_plain = 'emails/plain/failed-renewal-authentication-requested.php';
37
+ $this->template_base = plugin_dir_path( WC_STRIPE_MAIN_FILE ) . 'templates/';
38
+
39
+ $this->recipient = $this->get_option( 'recipient', get_option( 'admin_email' ) );
40
+
41
+ // We want all the parent's methods, with none of its properties, so call its parent's constructor, rather than my parent constructor.
42
+ WC_Email::__construct();
43
+ }
44
+
45
+ /**
46
+ * Get the default e-mail subject.
47
+ *
48
+ * @return string
49
+ */
50
+ public function get_default_subject() {
51
+ return $this->subject;
52
+ }
53
+
54
+ /**
55
+ * Get the default e-mail heading.
56
+ *
57
+ * @return string
58
+ */
59
+ public function get_default_heading() {
60
+ return $this->heading;
61
+ }
62
+
63
+ /**
64
+ * Trigger.
65
+ *
66
+ * @param int $order_id The order ID.
67
+ * @param WC_Order|null $order Order object.
68
+ */
69
+ public function trigger( $order_id, $order = null ) {
70
+ $this->object = $order;
71
+
72
+ $this->find['retry-time'] = '{retry_time}';
73
+ if ( class_exists( 'WCS_Retry_Manager' ) && function_exists( 'wcs_get_human_time_diff' ) ) {
74
+ $this->retry = WCS_Retry_Manager::store()->get_last_retry_for_order( wcs_get_objects_property( $order, 'id' ) );
75
+ $this->replace['retry-time'] = wcs_get_human_time_diff( $this->retry->get_time() );
76
+ } else {
77
+ WC_Stripe_Logger::log( 'WCS_Retry_Manager class or does not exist. Not able to send admnin email about customer notification for authentication required for renewal payment.' );
78
+ return;
79
+ }
80
+
81
+ $this->find['order-number'] = '{order_number}';
82
+ $this->replace['order-number'] = $this->object->get_order_number();
83
+
84
+ if ( ! $this->is_enabled() || ! $this->get_recipient() ) {
85
+ return;
86
+ }
87
+
88
+ $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
89
+ }
90
+
91
+ /**
92
+ * Get content html.
93
+ *
94
+ * @return string
95
+ */
96
+ public function get_content_html() {
97
+ return wc_get_template_html(
98
+ $this->template_html,
99
+ array(
100
+ 'order' => $this->object,
101
+ 'retry' => $this->retry,
102
+ 'email_heading' => $this->get_heading(),
103
+ 'sent_to_admin' => true,
104
+ 'plain_text' => false,
105
+ 'email' => $this,
106
+ ),
107
+ '',
108
+ $this->template_base
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Get content plain.
114
+ *
115
+ * @return string
116
+ */
117
+ public function get_content_plain() {
118
+ return wc_get_template_html(
119
+ $this->template_plain,
120
+ array(
121
+ 'order' => $this->object,
122
+ 'retry' => $this->retry,
123
+ 'email_heading' => $this->get_heading(),
124
+ 'sent_to_admin' => true,
125
+ 'plain_text' => true,
126
+ 'email' => $this,
127
+ ),
128
+ '',
129
+ $this->template_base
130
+ );
131
+ }
132
+ }
includes/compat/class-wc-stripe-email-failed-authentication.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly.
4
+ }
5
+
6
+ /**
7
+ * Base for Failed Renewal/Pre-Order Authentication Notifications.
8
+ *
9
+ * @extends WC_Email
10
+ */
11
+ abstract class WC_Stripe_Email_Failed_Authentication extends WC_Email {
12
+ /**
13
+ * An instance of the email, which would normally be sent after a failed payment.
14
+ *
15
+ * @var WC_Email
16
+ */
17
+ public $original_email;
18
+
19
+ /**
20
+ * Generates the HTML for the email while keeping the `template_base` in mind.
21
+ *
22
+ * @return string
23
+ */
24
+ public function get_content_html() {
25
+ ob_start();
26
+ wc_get_template(
27
+ $this->template_html,
28
+ array(
29
+ 'order' => $this->object,
30
+ 'email_heading' => $this->get_heading(),
31
+ 'sent_to_admin' => false,
32
+ 'plain_text' => false,
33
+ 'authorization_url' => $this->get_authorization_url( $this->object ),
34
+ 'email' => $this,
35
+ ),
36
+ '',
37
+ $this->template_base
38
+ );
39
+ return ob_get_clean();
40
+ }
41
+
42
+ /**
43
+ * Generates the plain text for the email while keeping the `template_base` in mind.
44
+ *
45
+ * @return string
46
+ */
47
+ public function get_content_plain() {
48
+ ob_start();
49
+ wc_get_template(
50
+ $this->template_plain,
51
+ array(
52
+ 'order' => $this->object,
53
+ 'email_heading' => $this->get_heading(),
54
+ 'sent_to_admin' => false,
55
+ 'plain_text' => true,
56
+ 'authorization_url' => $this->get_authorization_url( $this->object ),
57
+ 'email' => $this,
58
+ ),
59
+ '',
60
+ $this->template_base
61
+ );
62
+ return ob_get_clean();
63
+ }
64
+
65
+ /**
66
+ * Generates the URL, which will be used to authenticate the payment.
67
+ *
68
+ * @param WC_Order $order The order whose payment needs authentication.
69
+ * @return string
70
+ */
71
+ public function get_authorization_url( $order ) {
72
+ return add_query_arg( 'wc-stripe-confirmation', 1, $order->get_checkout_payment_url( false ) );
73
+ }
74
+
75
+ /**
76
+ * Uses specific fields from `WC_Email_Customer_Invoice` for this email.
77
+ */
78
+ public function init_form_fields() {
79
+ parent::init_form_fields();
80
+ $base_fields = $this->form_fields;
81
+
82
+ $this->form_fields = array(
83
+ 'enabled' => array(
84
+ 'title' => _x( 'Enable/Disable', 'an email notification', 'woocommerce-gateway-stripe' ),
85
+ 'type' => 'checkbox',
86
+ 'label' => __( 'Enable this email notification', 'woocommerce-gateway-stripe' ),
87
+ 'default' => 'yes',
88
+ ),
89
+
90
+ 'subject' => $base_fields['subject'],
91
+ 'heading' => $base_fields['heading'],
92
+ 'email_type' => $base_fields['email_type'],
93
+ );
94
+ }
95
+
96
+ /**
97
+ * Triggers the email.
98
+ *
99
+ * @param WC_Order $order The renewal order whose payment failed.
100
+ */
101
+ public function trigger( $order ) {
102
+ if ( ! $this->is_enabled() ) {
103
+ return;
104
+ }
105
+
106
+ $this->object = $order;
107
+
108
+ if ( method_exists( $order, 'get_billing_email' ) ) {
109
+ $this->recipient = $order->get_billing_email();
110
+ } else {
111
+ $this->recipient = $order->billing_email;
112
+ }
113
+
114
+ $this->find['order_date'] = '{order_date}';
115
+ if ( function_exists( 'wc_format_datetime' ) ) { // WC 3.0+
116
+ $this->replace['order_date'] = wc_format_datetime( $order->get_date_created() );
117
+ } else { // WC < 3.0
118
+ $this->replace['order_date'] = $order->date_created->date_i18n( wc_date_format() );
119
+ }
120
+
121
+ $this->find['order_number'] = '{order_number}';
122
+ $this->replace['order_number'] = $order->get_order_number();
123
+
124
+ $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
125
+ }
126
+ }
includes/compat/class-wc-stripe-email-failed-preorder-authentication.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly.
4
+ }
5
+
6
+ /**
7
+ * Failed Renewal/Pre-Order Authentication Notification
8
+ *
9
+ * @extends WC_Stripe_Email_Failed_Authentication
10
+ */
11
+ class WC_Stripe_Email_Failed_Preorder_Authentication extends WC_Stripe_Email_Failed_Authentication {
12
+ /**
13
+ * Holds the message, which is entered by admins when sending the email.
14
+ *
15
+ * @var string
16
+ */
17
+ protected $custom_message;
18
+
19
+ /**
20
+ * Constructor.
21
+ *
22
+ * @param WC_Email[] $email_classes All existing instances of WooCommerce emails.
23
+ */
24
+ public function __construct( $email_classes = array() ) {
25
+ $this->id = 'failed_preorder_sca_authentication';
26
+ $this->title = __( 'Pre-order Payment Action Needed', 'woocommerce-gateway-stripe' );
27
+ $this->description = __( 'This is an order notification sent to the customer once a pre-order is complete, but additional payment steps are required.', 'woocommerce-gateway-stripe' );
28
+ $this->customer_email = true;
29
+
30
+ $this->template_html = 'emails/failed-preorder-authentication.php';
31
+ $this->template_plain = 'emails/plain/failed-preorder-authentication.php';
32
+ $this->template_base = plugin_dir_path( WC_STRIPE_MAIN_FILE ) . 'templates/';
33
+
34
+ // Use the "authentication required" hook to add the correct, later hook.
35
+ add_action( 'wc_gateway_stripe_process_payment_authentication_required', array( $this, 'trigger' ) );
36
+
37
+ if ( isset( $email_classes['WC_Pre_Orders_Email_Pre_Order_Available'] ) ) {
38
+ $this->original_email = $email_classes['WC_Pre_Orders_Email_Pre_Order_Available'];
39
+ }
40
+
41
+ // We want all the parent's methods, with none of its properties, so call its parent's constructor, rather than my parent constructor.
42
+ parent::__construct();
43
+ }
44
+
45
+ /**
46
+ * When autnentication is required, this adds another action to `wc_pre_orders_pre_order_completed`
47
+ * in order to send the authentication required email when the custom pre-orders message is available.
48
+ *
49
+ * @param WC_Order $order The order whose payment is failing.
50
+ */
51
+ public function trigger( $order ) {
52
+ if ( class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Order::order_contains_pre_order( $order->get_id() ) ) {
53
+ if ( isset( $this->original_email ) ) {
54
+ remove_action( 'wc_pre_order_status_completed_notification', array( $this->original_email, 'trigger' ), 10, 2 );
55
+ }
56
+
57
+ add_action( 'wc_pre_orders_pre_order_completed', array( $this, 'send_email' ), 10, 2 );
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Triggers the email while also disconnecting the original Pre-Orders email.
63
+ *
64
+ * @param WC_Order $order The order that is being paid.
65
+ * @param string $message The message, which should be added to the email.
66
+ */
67
+ public function send_email( $order, $message ) {
68
+ $this->custom_message = $message;
69
+
70
+ parent::trigger( $order );
71
+
72
+ // Restore the action of the original email for other bulk actions.
73
+ if ( isset( $this->original_email ) ) {
74
+ add_action( 'wc_pre_order_status_completed_notification', array( $this->original_email, 'trigger' ), 10, 2 );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Returns the default subject of the email (modifyable in settings).
80
+ *
81
+ * @return string
82
+ */
83
+ public function get_default_subject() {
84
+ return __( 'Payment authorization needed for pre-order {order_number}', 'woocommerce-gateway-stripe' );
85
+ }
86
+
87
+ /**
88
+ * Returns the default heading of the email (modifyable in settings).
89
+ *
90
+ * @return string
91
+ */
92
+ public function get_default_heading() {
93
+ return __( 'Payment authorization needed for pre-order {order_number}', 'woocommerce-gateway-stripe' );
94
+ }
95
+
96
+ /**
97
+ * Returns the custom message, entered by the admin.
98
+ *
99
+ * @return string
100
+ */
101
+ public function get_custom_message() {
102
+ return $this->custom_message;
103
+ }
104
+ }
includes/compat/class-wc-stripe-email-failed-renewal-authentication.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly.
4
+ }
5
+
6
+ /**
7
+ * Failed Renewal/Pre-Order Authentication Notification
8
+ *
9
+ * @extends WC_Email_Customer_Invoice
10
+ */
11
+ class WC_Stripe_Email_Failed_Renewal_Authentication extends WC_Stripe_Email_Failed_Authentication {
12
+ /**
13
+ * Constructor.
14
+ *
15
+ * @param WC_Email[] $email_classes All existing instances of WooCommerce emails.
16
+ */
17
+ public function __construct( $email_classes = array() ) {
18
+ $this->id = 'failed_renewal_authentication';
19
+ $this->title = __( 'Failed Subscription Renewal SCA Authentication', 'woocommerce-gateway-stripe' );
20
+ $this->description = __( 'Sent to a customer when a renewal fails because the transaction requires an SCA verification. The email contains renewal order information and payment links.', 'woocommerce-gateway-stripe' );
21
+ $this->customer_email = true;
22
+
23
+ $this->template_html = 'emails/failed-renewal-authentication.php';
24
+ $this->template_plain = 'emails/plain/failed-renewal-authentication.php';
25
+ $this->template_base = plugin_dir_path( WC_STRIPE_MAIN_FILE ) . 'templates/';
26
+
27
+ // Triggers the email at the correct hook.
28
+ add_action( 'wc_gateway_stripe_process_payment_authentication_required', array( $this, 'trigger' ) );
29
+
30
+ if ( isset( $email_classes['WCS_Email_Customer_Renewal_Invoice'] ) ) {
31
+ $this->original_email = $email_classes['WCS_Email_Customer_Renewal_Invoice'];
32
+ }
33
+
34
+ // We want all the parent's methods, with none of its properties, so call its parent's constructor, rather than my parent constructor.
35
+ parent::__construct();
36
+ }
37
+
38
+ /**
39
+ * Triggers the email while also disconnecting the original Subscriptions email.
40
+ *
41
+ * @param WC_Order $order The order that is being paid.
42
+ */
43
+ public function trigger( $order ) {
44
+ if ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order->get_id() ) || wcs_is_subscription( $order->get_id() ) || wcs_order_contains_renewal( $order->get_id() ) ) ) {
45
+ parent::trigger( $order );
46
+
47
+ // Prevent the renewal email from WooCommerce Subscriptions from being sent.
48
+ if ( isset( $this->original_email ) ) {
49
+ remove_action( 'woocommerce_generated_manual_renewal_order_renewal_notification', array( $this->original_email, 'trigger' ) );
50
+ remove_action( 'woocommerce_order_status_failed_renewal_notification', array( $this->original_email, 'trigger' ) );
51
+ }
52
+
53
+ // Prevent the retry email from WooCommerce Subscriptions from being sent.
54
+ add_filter( 'wcs_get_retry_rule_raw', array( $this, 'prevent_retry_notification_email' ), 100, 3 );
55
+
56
+ // Send email to store owner indicating communication is happening with the customer to request authentication.
57
+ add_filter( 'wcs_get_retry_rule_raw', array( $this, 'set_store_owner_custom_email' ), 100, 3 );
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Returns the default subject of the email (modifyable in settings).
63
+ *
64
+ * @return string
65
+ */
66
+ public function get_default_subject() {
67
+ return __( 'Payment authorization needed for renewal of {site_title} order {order_number}', 'woocommerce-gateway-stripe' );
68
+ }
69
+
70
+ /**
71
+ * Returns the default heading of the email (modifyable in settings).
72
+ *
73
+ * @return string
74
+ */
75
+ public function get_default_heading() {
76
+ return __( 'Payment authorization needed for renewal of order {order_number}', 'woocommerce-gateway-stripe' );
77
+ }
78
+
79
+ /**
80
+ * Prevent all customer-facing retry notifications from being sent after this email.
81
+ *
82
+ * @param array $rule_array The raw details about the retry rule.
83
+ * @param int $retry_number The number of the retry.
84
+ * @param int $order_id The ID of the order that needs payment.
85
+ * @return array
86
+ */
87
+ public function prevent_retry_notification_email( $rule_array, $retry_number, $order_id ) {
88
+ if ( wcs_get_objects_property( $this->object, 'id' ) === $order_id ) {
89
+ $rule_array['email_template_customer'] = '';
90
+ }
91
+
92
+ return $rule_array;
93
+ }
94
+
95
+ /**
96
+ * Send store owner a different email when the retry is related to an authentication required error.
97
+ *
98
+ * @param array $rule_array The raw details about the retry rule.
99
+ * @param int $retry_number The number of the retry.
100
+ * @param int $order_id The ID of the order that needs payment.
101
+ * @return array
102
+ */
103
+ public function set_store_owner_custom_email( $rule_array, $retry_number, $order_id ) {
104
+ if (
105
+ wcs_get_objects_property( $this->object, 'id' ) === $order_id &&
106
+ '' !== $rule_array['email_template_admin'] // Only send our email if a retry admin email was already going to be sent.
107
+ ) {
108
+ $rule_array['email_template_admin'] = 'WC_Stripe_Email_Failed_Authentication_Retry';
109
+ }
110
+
111
+ return $rule_array;
112
+ }
113
+ }
includes/compat/class-wc-stripe-pre-orders-compat.php CHANGED
@@ -28,19 +28,15 @@ class WC_Stripe_Pre_Orders_Compat extends WC_Stripe_Payment_Gateway {
28
  * @param object $order
29
  */
30
  public function remove_order_source_before_retry( $order ) {
31
- $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
32
- delete_post_meta( $order_id, '_stripe_source_id' );
33
- // For BW compat will remove in the future.
34
- delete_post_meta( $order_id, '_stripe_card_id' );
35
- }
36
-
37
- /**
38
- * Remove order meta
39
- * @param object $order
40
- */
41
- public function remove_order_customer_before_retry( $order ) {
42
- $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
43
- delete_post_meta( $order_id, '_stripe_customer_id' );
44
  }
45
 
46
  /**
@@ -61,19 +57,29 @@ class WC_Stripe_Pre_Orders_Compat extends WC_Stripe_Payment_Gateway {
61
  throw new WC_Stripe_Exception( __( 'Unable to store payment details. Please try again.', 'woocommerce-gateway-stripe' ) );
62
  }
63
 
 
 
 
 
 
 
64
  $this->save_source_to_order( $order, $prepared_source );
65
 
66
- // Remove cart
 
 
 
 
 
 
 
67
  WC()->cart->empty_cart();
68
 
69
  // Is pre ordered!
70
  WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
71
 
72
  // Return thank you page redirect
73
- return array(
74
- 'result' => 'success',
75
- 'redirect' => $this->get_return_url( $order ),
76
- );
77
  } catch ( WC_Stripe_Exception $e ) {
78
  wc_add_notice( $e->getLocalizedMessage(), 'error' );
79
  WC_Stripe_Logger::log( 'Pre Orders Error: ' . $e->getMessage() );
@@ -87,37 +93,49 @@ class WC_Stripe_Pre_Orders_Compat extends WC_Stripe_Payment_Gateway {
87
 
88
  /**
89
  * Process a pre-order payment when the pre-order is released.
 
90
  * @param WC_Order $order
 
 
91
  * @return void
92
  */
93
- public function process_pre_order_release_payment( $order ) {
94
  try {
95
- // Define some callbacks if the first attempt fails.
96
- $retry_callbacks = array(
97
- 'remove_order_source_before_retry',
98
- 'remove_order_customer_before_retry',
99
- );
100
 
101
- while ( 1 ) {
102
- $source = $this->prepare_order_source( $order );
103
- $response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
104
-
105
- if ( ! empty( $response->error ) ) {
106
- if ( 0 === sizeof( $retry_callbacks ) ) {
107
- throw new Exception( $response->error->message );
108
- } else {
109
- $retry_callback = array_shift( $retry_callbacks );
110
- call_user_func( array( $this, $retry_callback ), $order );
111
- }
112
- } else {
113
- // Successful
114
- $this->process_response( $response, $order );
115
- break;
116
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
  } catch ( Exception $e ) {
 
119
  /* translators: error message */
120
- $order_note = sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $e->getMessage() );
121
 
122
  // Mark order as failed if not already set,
123
  // otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times
28
  * @param object $order
29
  */
30
  public function remove_order_source_before_retry( $order ) {
31
+ if ( WC_Stripe_Helper::is_wc_lt( '3.0' ) ) {
32
+ delete_post_meta( $order->id, '_stripe_source_id' );
33
+ // For BW compat will remove in the future.
34
+ delete_post_meta( $order->id, '_stripe_card_id' );
35
+ } else {
36
+ $order->delete_meta_data( '_stripe_source_id' );
37
+ $order->delete_meta_data( '_stripe_card_id' );
38
+ $order->save();
39
+ }
 
 
 
 
40
  }
41
 
42
  /**
57
  throw new WC_Stripe_Exception( __( 'Unable to store payment details. Please try again.', 'woocommerce-gateway-stripe' ) );
58
  }
59
 
60
+ // Setup the response early to allow later modifications.
61
+ $response = array(
62
+ 'result' => 'success',
63
+ 'redirect' => $this->get_return_url( $order ),
64
+ );
65
+
66
  $this->save_source_to_order( $order, $prepared_source );
67
 
68
+ // Try setting up a payment intent.
69
+ $intent_secret = $this->setup_intent( $order, $prepared_source );
70
+ if ( ! empty( $intent_secret ) ) {
71
+ $response['setup_intent_secret'] = $intent_secret;
72
+ return $response;
73
+ }
74
+
75
+ // Remove cart.
76
  WC()->cart->empty_cart();
77
 
78
  // Is pre ordered!
79
  WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
80
 
81
  // Return thank you page redirect
82
+ return $response;
 
 
 
83
  } catch ( WC_Stripe_Exception $e ) {
84
  wc_add_notice( $e->getLocalizedMessage(), 'error' );
85
  WC_Stripe_Logger::log( 'Pre Orders Error: ' . $e->getMessage() );
93
 
94
  /**
95
  * Process a pre-order payment when the pre-order is released.
96
+ *
97
  * @param WC_Order $order
98
+ * @param bool $retry
99
+ *
100
  * @return void
101
  */
102
+ public function process_pre_order_release_payment( $order, $retry = true ) {
103
  try {
104
+ $source = $this->prepare_order_source( $order );
105
+ $response = $this->create_and_confirm_intent_for_off_session( $order, $source );
106
+
107
+ $is_authentication_required = $this->is_authentication_required_for_payment( $response );
 
108
 
109
+ if ( ! empty( $response->error ) && ! $is_authentication_required ) {
110
+ if ( ! $retry ) {
111
+ throw new Exception( $response->error->message );
 
 
 
 
 
 
 
 
 
 
 
 
112
  }
113
+ $this->remove_order_source_before_retry( $order );
114
+ $this->process_pre_order_release_payment( $order, false );
115
+ } else if ( $is_authentication_required ) {
116
+ $charge = end( $response->error->payment_intent->charges->data );
117
+ $id = $charge->id;
118
+ $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
119
+
120
+ WC_Stripe_Helper::is_wc_lt( '3.0' ) ? update_post_meta( $order_id, '_transaction_id', $id ) : $order->set_transaction_id( $id );
121
+ $order->update_status( 'failed', sprintf( __( 'Stripe charge awaiting authentication by user: %s.', 'woocommerce-gateway-stripe' ), $id ) );
122
+ if ( is_callable( array( $order, 'save' ) ) ) {
123
+ $order->save();
124
+ }
125
+
126
+ WC_Emails::instance();
127
+
128
+ do_action( 'wc_gateway_stripe_process_payment_authentication_required', $order );
129
+
130
+ throw new WC_Stripe_Exception( print_r( $response, true ), $response->error->message );
131
+ } else {
132
+ // Successful
133
+ $this->process_response( end( $response->charges->data ), $order );
134
  }
135
  } catch ( Exception $e ) {
136
+ $error_message = is_callable( array( $e, 'getLocalizedMessage' ) ) ? $e->getLocalizedMessage() : $e->getMessage();
137
  /* translators: error message */
138
+ $order_note = sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $error_message );
139
 
140
  // Mark order as failed if not already set,
141
  // otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times
includes/compat/class-wc-stripe-subs-compat.php CHANGED
@@ -30,6 +30,15 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
30
  add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 );
31
  add_filter( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 );
32
  add_filter( 'wc_stripe_display_save_payment_method_checkbox', array( $this, 'maybe_hide_save_checkbox' ) );
 
 
 
 
 
 
 
 
 
33
  }
34
  }
35
 
@@ -124,19 +133,9 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
124
  try {
125
  $subscription = wc_get_order( $order_id );
126
  $prepared_source = $this->prepare_source( get_current_user_id(), true );
127
- $source_object = $prepared_source->source_object;
128
-
129
- // Check if we don't allow prepaid credit cards.
130
- if ( ! apply_filters( 'wc_stripe_allow_prepaid_card', true ) && $this->is_prepaid_card( $source_object ) ) {
131
- $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' );
132
- throw new WC_Stripe_Exception( print_r( $source_object, true ), $localized_message );
133
- }
134
-
135
- if ( empty( $prepared_source->source ) ) {
136
- $localized_message = __( 'Payment processing failed. Please retry.', 'woocommerce-gateway-stripe' );
137
- throw new WC_Stripe_Exception( print_r( $prepared_source, true ), $localized_message );
138
- }
139
 
 
 
140
  $this->save_source_to_order( $subscription, $prepared_source );
141
 
142
  do_action( 'wc_stripe_change_subs_payment_method_success', $prepared_source->source, $prepared_source );
@@ -169,6 +168,28 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
169
  }
170
  }
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  /**
173
  * Scheduled_subscription_payment function.
174
  *
@@ -199,6 +220,11 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
199
 
200
  $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $renewal_order->id : $renewal_order->get_id();
201
 
 
 
 
 
 
202
  // Get source from order
203
  $prepared_source = $this->prepare_order_source( $renewal_order );
204
  $source_object = $prepared_source->source_object;
@@ -221,9 +247,13 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
221
  $prepared_source->source = '';
222
  }
223
 
224
- $response = $this->create_and_confirm_intent_for_renewal( $amount, $renewal_order, $prepared_source );
 
 
225
 
226
- if ( ! empty( $response->error ) ) {
 
 
227
  // We want to retry.
228
  if ( $this->is_retryable_error( $response->error ) ) {
229
  if ( $retry ) {
@@ -257,10 +287,29 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
257
  throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
258
  }
259
 
260
- do_action( 'wc_gateway_stripe_process_payment', $response, $renewal_order );
 
 
 
 
 
 
261
 
 
 
 
262
 
263
- $this->process_response( end( $response->charges->data ), $renewal_order );
 
 
 
 
 
 
 
 
 
 
264
  } catch ( WC_Stripe_Exception $e ) {
265
  WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
266
 
@@ -271,57 +320,6 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
271
  }
272
  }
273
 
274
- /**
275
- * Create and confirm a new PaymentIntent.
276
- *
277
- * @param float $amount The amount to charge.
278
- * @param WC_Order $order The order that is being paid for.
279
- * @param object $prepared_source The source that is used for the payment.
280
- * @return object An intent or an error.
281
- */
282
- public function create_and_confirm_intent_for_renewal( $amount, $order, $prepared_source ) {
283
- // The request for a charge contains metadata for the intent.
284
- $full_request = $this->generate_payment_request( $order, $prepared_source );
285
-
286
- $request = array(
287
- 'amount' => WC_Stripe_Helper::get_stripe_amount( $amount, $full_request['currency'] ),
288
- 'currency' => $full_request['currency'],
289
- 'description' => $full_request['description'],
290
- 'metadata' => $full_request['metadata'],
291
- 'payment_method_types' => array(
292
- 'card',
293
- ),
294
- 'off_session' => 'true',
295
- 'confirm' => 'true',
296
- 'confirmation_method' => 'automatic',
297
- );
298
-
299
- if ( isset( $full_request['statement_descriptor'] ) ) {
300
- $request['statement_descriptor'] = $full_request['statement_descriptor'];
301
- }
302
-
303
- if ( isset( $full_request['customer'] ) ) {
304
- $request['customer'] = $full_request['customer'];
305
- }
306
-
307
- if ( isset( $full_request['source'] ) ) {
308
- $request['source'] = $full_request['source'];
309
- }
310
-
311
- $intent = WC_Stripe_API::request( $request, 'payment_intents' );
312
- if ( ! empty( $intent->error ) ) {
313
- return $intent;
314
- }
315
-
316
- $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
317
- WC_Stripe_Logger::log( "Stripe PaymentIntent $intent->id initiated for order $order_id" );
318
-
319
- // Save the intent ID to the order.
320
- $this->save_intent_to_order( $order, $intent );
321
-
322
- return $intent;
323
- }
324
-
325
  /**
326
  * Updates other subscription sources.
327
  *
@@ -551,4 +549,62 @@ class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
551
 
552
  return $payment_method_to_display;
553
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  }
30
  add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 );
31
  add_filter( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 );
32
  add_filter( 'wc_stripe_display_save_payment_method_checkbox', array( $this, 'maybe_hide_save_checkbox' ) );
33
+
34
+ /*
35
+ * WC subscriptions hooks into the "template_redirect" hook with priority 100.
36
+ * If the screen is "Pay for order" and the order is a subscription renewal, it redirects to the plain checkout.
37
+ * See: https://github.com/woocommerce/woocommerce-subscriptions/blob/99a75687e109b64cbc07af6e5518458a6305f366/includes/class-wcs-cart-renewal.php#L165
38
+ * If we are in the "You just need to authorize SCA" flow, we don't want that redirection to happen.
39
+ */
40
+ add_action( 'template_redirect', array( $this, 'remove_order_pay_var' ), 99 );
41
+ add_action( 'template_redirect', array( $this, 'restore_order_pay_var' ), 101 );
42
  }
43
  }
44
 
133
  try {
134
  $subscription = wc_get_order( $order_id );
135
  $prepared_source = $this->prepare_source( get_current_user_id(), true );
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ $this->maybe_disallow_prepaid_card( $prepared_source );
138
+ $this->check_source( $prepared_source );
139
  $this->save_source_to_order( $subscription, $prepared_source );
140
 
141
  do_action( 'wc_stripe_change_subs_payment_method_success', $prepared_source->source, $prepared_source );
168
  }
169
  }
170
 
171
+ /**
172
+ * Overloads WC_Stripe_Payment_Gateway::generate_create_intent_request() in order to
173
+ * include additional flags, used when setting payment intents up for off-session usage.
174
+ *
175
+ * @param WC_Order $order The order that is being paid for.
176
+ * @param object $prepared_source The source that is used for the payment.
177
+ * @return array The arguments for the request.
178
+ */
179
+ public function generate_create_intent_request( $order, $prepared_source ) {
180
+ $request = parent::generate_create_intent_request( $order, $prepared_source );
181
+
182
+ // Non-subscription orders do not need any additional parameters.
183
+ if ( ! $this->has_subscription( $order ) ) {
184
+ return $request;
185
+ }
186
+
187
+ // Let Stripe know that the payment should be prepared for future usage.
188
+ $request['setup_future_usage'] = 'off_session';
189
+
190
+ return $request;
191
+ }
192
+
193
  /**
194
  * Scheduled_subscription_payment function.
195
  *
220
 
221
  $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $renewal_order->id : $renewal_order->get_id();
222
 
223
+ // Check for an existing intent, which is associated with the order.
224
+ if ( $this->has_authentication_already_failed( $renewal_order ) ) {
225
+ return;
226
+ }
227
+
228
  // Get source from order
229
  $prepared_source = $this->prepare_order_source( $renewal_order );
230
  $source_object = $prepared_source->source_object;
247
  $prepared_source->source = '';
248
  }
249
 
250
+ $response = $this->create_and_confirm_intent_for_off_session( $renewal_order, $prepared_source, $amount );
251
+
252
+ $is_authentication_required = $this->is_authentication_required_for_payment( $response );
253
 
254
+ // It's only a failed payment if it's an error and it's not of the type 'authentication_required'.
255
+ // If it's 'authentication_required', then we should email the user and ask them to authenticate.
256
+ if ( ! empty( $response->error ) && ! $is_authentication_required ) {
257
  // We want to retry.
258
  if ( $this->is_retryable_error( $response->error ) ) {
259
  if ( $retry ) {
287
  throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
288
  }
289
 
290
+ // Either the charge was successfully captured, or it requires further authentication.
291
+
292
+ if ( $is_authentication_required ) {
293
+ do_action( 'wc_gateway_stripe_process_payment_authentication_required', $renewal_order, $response );
294
+
295
+ $error_message = __( 'This transaction requires authentication.', 'woocommerce-gateway-stripe' );
296
+ $renewal_order->add_order_note( $error_message );
297
 
298
+ $charge = end( $response->error->payment_intent->charges->data );
299
+ $id = $charge->id;
300
+ $order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $renewal_order->id : $renewal_order->get_id();
301
 
302
+ WC_Stripe_Helper::is_wc_lt( '3.0' ) ? update_post_meta( $order_id, '_transaction_id', $id ) : $renewal_order->set_transaction_id( $id );
303
+ $renewal_order->update_status( 'failed', sprintf( __( 'Stripe charge awaiting authentication by user: %s.', 'woocommerce-gateway-stripe' ), $id ) );
304
+ if ( is_callable( array( $renewal_order, 'save' ) ) ) {
305
+ $renewal_order->save();
306
+ }
307
+ } else {
308
+ // The charge was successfully captured
309
+ do_action( 'wc_gateway_stripe_process_payment', $response, $renewal_order );
310
+
311
+ $this->process_response( end( $response->charges->data ), $renewal_order );
312
+ }
313
  } catch ( WC_Stripe_Exception $e ) {
314
  WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
315
 
320
  }
321
  }
322
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  /**
324
  * Updates other subscription sources.
325
  *
549
 
550
  return $payment_method_to_display;
551
  }
552
+
553
+ /**
554
+ * If this is the "Pass the SCA challenge" flow, remove a variable that is checked by WC Subscriptions
555
+ * so WC Subscriptions doesn't redirect to the checkout
556
+ */
557
+ public function remove_order_pay_var() {
558
+ global $wp;
559
+ if ( isset( $_GET['wc-stripe-confirmation'] ) ) {
560
+ $this->order_pay_var = $wp->query_vars['order-pay'];
561
+ $wp->query_vars['order-pay'] = null;
562
+ }
563
+ }
564
+
565
+ /**
566
+ * Restore the variable that was removed in remove_order_pay_var()
567
+ */
568
+ public function restore_order_pay_var() {
569
+ global $wp;
570
+ if ( isset( $this->order_pay_var ) ) {
571
+ $wp->query_vars['order-pay'] = $this->order_pay_var;
572
+ }
573
+ }
574
+
575
+ /**
576
+ * Checks if a renewal already failed because a manual authentication is required.
577
+ *
578
+ * @param WC_Order $renewal_order The renewal order.
579
+ * @return boolean
580
+ */
581
+ public function has_authentication_already_failed( $renewal_order ) {
582
+ $existing_intent = $this->get_intent_from_order( $renewal_order );
583
+
584
+ if (
585
+ ! $existing_intent
586
+ || 'requires_payment_method' !== $existing_intent->status
587
+ || empty( $existing_intent->last_payment_error )
588
+ || 'authentication_required' !== $existing_intent->last_payment_error->code
589
+ ) {
590
+ return false;
591
+ }
592
+
593
+ // Make sure all emails are instantiated.
594
+ WC_Emails::instance();
595
+
596
+ /**
597
+ * A payment attempt failed because SCA authentication is required.
598
+ *
599
+ * @param WC_Order $renewal_order The order that is being renewed.
600
+ */
601
+ do_action( 'wc_gateway_stripe_process_payment_authentication_required', $renewal_order );
602
+
603
+ // Fail the payment attempt (order would be currently pending because of retry rules).
604
+ $charge = end( $existing_intent->charges->data );
605
+ $charge_id = $charge->id;
606
+ $renewal_order->update_status( 'failed', sprintf( __( 'Stripe charge awaiting authentication by user: %s.', 'woocommerce-gateway-stripe' ), $charge_id ) );
607
+
608
+ return true;
609
+ }
610
  }
languages/woocommerce-gateway-stripe.pot CHANGED
@@ -2,10 +2,9 @@
2
  # This file is distributed under the same license as the WooCommerce Stripe Gateway package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: WooCommerce Stripe Gateway 4.2.4\n"
6
- "Report-Msgid-Bugs-To: "
7
- "https://wordpress.org/support/plugin/woocommerce-gateway-stripe\n"
8
- "POT-Creation-Date: 2019-10-02 17:24:17+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
@@ -28,72 +27,71 @@ msgstr ""
28
  msgid "Save payment information to my account for future purchases."
29
  msgstr ""
30
 
31
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:245
32
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:216
33
- #: includes/compat/class-wc-stripe-subs-compat.php:197
34
  #. translators: 1) dollar amount
35
  #. translators: minimum amount
36
  msgid "Sorry, the minimum allowed order total is %1$s to use this payment method."
37
  msgstr ""
38
 
39
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:341
40
  #. translators: 1) blog name 2) order number
41
  msgid "%1$s - Order %2$s"
42
  msgstr ""
43
 
44
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:368
45
  msgid "customer_name"
46
  msgstr ""
47
 
48
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:369
49
  msgid "customer_email"
50
  msgstr ""
51
 
52
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:432
53
  #. translators: transaction id
54
  msgid "Stripe charge awaiting payment: %s."
55
  msgstr ""
56
 
57
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:439
58
  #: includes/class-wc-stripe-order-handler.php:296
59
- #: includes/class-wc-stripe-webhook-handler.php:348
60
- #: includes/class-wc-stripe-webhook-handler.php:400
61
  #. translators: transaction id
62
  msgid "Stripe charge complete (Charge ID: %s)"
63
  msgstr ""
64
 
65
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:444
66
- #: includes/class-wc-gateway-stripe.php:523
67
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:173
68
- #: includes/compat/class-wc-stripe-subs-compat.php:136
69
  msgid "Payment processing failed. Please retry."
70
  msgstr ""
71
 
72
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:456
73
  #. translators: transaction id
74
  msgid ""
75
  "Stripe charge authorized (Charge ID: %s). Process order to take payment, or "
76
  "cancel to remove the pre-authorization."
77
  msgstr ""
78
 
79
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:627
80
  msgid "Invalid payment method. Please input a new card number."
81
  msgstr ""
82
 
83
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:908
84
  #. translators: 1) dollar amount 2) transaction id 3) refund message
85
  msgid "Refunded %1$s - Refund ID: %2$s - Reason: %3$s"
86
  msgstr ""
87
 
88
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:908
89
  msgid "Pre-Authorization Released"
90
  msgstr ""
91
 
92
- #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:926
93
  msgid "There was a problem adding the payment method."
94
  msgstr ""
95
 
96
- #: includes/admin/class-wc-stripe-admin-notices.php:122
97
  #. translators: 1) A URL that explains Stripe Radar.
98
  msgid ""
99
  "WooCommerce Stripe - We see that you had the \"Require 3D secure when "
@@ -102,42 +100,42 @@ msgid ""
102
  "href=\"%s\" target=\"_blank\">here</a>."
103
  msgstr ""
104
 
105
- #: includes/admin/class-wc-stripe-admin-notices.php:129
106
  #. translators: 1) int version 2) int version
107
  msgid ""
108
  "WooCommerce Stripe - We recently made changes to Stripe that may impact the "
109
  "appearance of your checkout. If your checkout has changed unexpectedly, "
110
  "please follow these <a "
111
- "href=\"https://docs.woocommerce.com/document/stripe/#section-45\" "
112
  "target=\"_blank\">instructions</a> to fix."
113
  msgstr ""
114
 
115
- #: includes/admin/class-wc-stripe-admin-notices.php:139
116
  #. translators: 1) int version 2) int version
117
  msgid ""
118
  "WooCommerce Stripe - The minimum PHP version required for this plugin is "
119
  "%1$s. You are running %2$s."
120
  msgstr ""
121
 
122
- #: includes/admin/class-wc-stripe-admin-notices.php:150
123
  #. translators: 1) int version 2) int version
124
  msgid ""
125
  "WooCommerce Stripe - The minimum WooCommerce version required for this "
126
  "plugin is %1$s. You are running %2$s."
127
  msgstr ""
128
 
129
- #: includes/admin/class-wc-stripe-admin-notices.php:160
130
  msgid "WooCommerce Stripe - cURL is not installed."
131
  msgstr ""
132
 
133
- #: includes/admin/class-wc-stripe-admin-notices.php:170
134
  #. translators: 1) link
135
  msgid ""
136
  "Stripe is almost ready. To get started, <a href=\"%s\">set your Stripe "
137
  "account keys</a>."
138
  msgstr ""
139
 
140
- #: includes/admin/class-wc-stripe-admin-notices.php:181
141
  #. translators: 1) link
142
  msgid ""
143
  "Stripe is in test mode however your test keys may not be valid. Test keys "
@@ -145,7 +143,7 @@ msgid ""
145
  "<a href=\"%s\">set your Stripe account keys</a>."
146
  msgstr ""
147
 
148
- #: includes/admin/class-wc-stripe-admin-notices.php:190
149
  #. translators: 1) link
150
  msgid ""
151
  "Stripe is in live mode however your test keys may not be valid. Live keys "
@@ -153,7 +151,7 @@ msgid ""
153
  "<a href=\"%s\">set your Stripe account keys</a>."
154
  msgstr ""
155
 
156
- #: includes/admin/class-wc-stripe-admin-notices.php:199
157
  #. translators: 1) link
158
  msgid ""
159
  "Stripe is enabled, but a SSL certificate is not detected. Your checkout may "
@@ -161,16 +159,22 @@ msgid ""
161
  "target=\"_blank\">SSL certificate</a>"
162
  msgstr ""
163
 
164
- #: includes/admin/class-wc-stripe-admin-notices.php:223
 
 
 
 
 
 
165
  #. translators: %1$s Payment method, %2$s List of supported currencies
166
  msgid "%1$s is enabled - it requires store currency to be set to %2$s"
167
  msgstr ""
168
 
169
- #: includes/admin/class-wc-stripe-admin-notices.php:237
170
  msgid "Action failed. Please refresh the page and retry."
171
  msgstr ""
172
 
173
- #: includes/admin/class-wc-stripe-admin-notices.php:241
174
  msgid "Cheatin&#8217; huh?"
175
  msgstr ""
176
 
@@ -206,8 +210,8 @@ msgstr ""
206
 
207
  #: includes/admin/class-wc-stripe-privacy.php:41
208
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:463
209
- #: includes/compat/class-wc-stripe-subs-compat.php:529
210
- #: includes/compat/class-wc-stripe-subs-compat.php:544
211
  msgid "N/A"
212
  msgstr ""
213
 
@@ -761,22 +765,14 @@ msgid ""
761
  "your Stripe account keys</a>."
762
  msgstr ""
763
 
764
- #: includes/class-wc-gateway-stripe.php:190
765
  #. translators: 1) Opening anchor tag 2) closing anchor tag
766
  msgid ""
767
  "If your billing address has been changed for saved payment methods, be sure "
768
  "to remove any %1$ssaved payment methods%2$s on file and re-add them."
769
  msgstr ""
770
 
771
- #: includes/class-wc-gateway-stripe.php:251
772
- msgid "Add Card"
773
- msgstr ""
774
-
775
- #: includes/class-wc-gateway-stripe.php:257
776
- msgid "Change Payment Method"
777
- msgstr ""
778
-
779
- #: includes/class-wc-gateway-stripe.php:278
780
  #. translators: link to Stripe testing page
781
  msgid ""
782
  "TEST MODE ENABLED. In test mode, you can use the card number "
@@ -785,84 +781,84 @@ msgid ""
785
  "card numbers."
786
  msgstr ""
787
 
788
- #: includes/class-wc-gateway-stripe.php:317
789
  msgid "Credit or debit card"
790
  msgstr ""
791
 
792
- #: includes/class-wc-gateway-stripe.php:325
793
  msgid "Card Number"
794
  msgstr ""
795
 
796
- #: includes/class-wc-gateway-stripe.php:336
797
  msgid "Expiry Date"
798
  msgstr ""
799
 
800
- #: includes/class-wc-gateway-stripe.php:344
801
  msgid "Card Code (CVC)"
802
  msgstr ""
803
 
804
- #: includes/class-wc-gateway-stripe.php:419
805
  msgid "Please accept the terms and conditions first"
806
  msgstr ""
807
 
808
- #: includes/class-wc-gateway-stripe.php:420
809
  msgid "Please fill in required checkout fields first"
810
  msgstr ""
811
 
812
- #: includes/class-wc-gateway-stripe.php:449
813
- #: includes/class-wc-gateway-stripe.php:510
814
  msgid ""
815
  "Sorry, we're not accepting prepaid cards at this time. Your credit card has "
816
  "not been charged. Please try with alternative payment method."
817
  msgstr ""
818
 
819
- #: includes/class-wc-gateway-stripe.php:450
820
  msgid "Please enter your IBAN account name."
821
  msgstr ""
822
 
823
- #: includes/class-wc-gateway-stripe.php:451
824
  msgid "Please enter your IBAN account number."
825
  msgstr ""
826
 
827
- #: includes/class-wc-gateway-stripe.php:452
828
  msgid "We couldn't initiate the payment. Please try again."
829
  msgstr ""
830
 
831
- #: includes/class-wc-gateway-stripe.php:463
832
  msgid "Billing First Name and Last Name are required."
833
  msgstr ""
834
 
835
- #: includes/class-wc-gateway-stripe.php:725
836
  #. translators: error message
837
  msgid "This represents the fee Stripe collects for the transaction."
838
  msgstr ""
839
 
840
- #: includes/class-wc-gateway-stripe.php:726
841
  msgid "Stripe Fee:"
842
  msgstr ""
843
 
844
- #: includes/class-wc-gateway-stripe.php:762
845
  msgid ""
846
  "This represents the net total that will be credited to your Stripe bank "
847
  "account. This may be in the currency that is set in your Stripe account."
848
  msgstr ""
849
 
850
- #: includes/class-wc-gateway-stripe.php:763
851
  msgid "Stripe Payout:"
852
  msgstr ""
853
 
854
- #: includes/class-wc-gateway-stripe.php:810
855
  #: includes/class-wc-stripe-order-handler.php:162
856
- #: includes/class-wc-stripe-webhook-handler.php:239
857
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:263
858
- #: includes/compat/class-wc-stripe-subs-compat.php:241
859
  #: includes/payment-methods/class-wc-gateway-stripe-sepa.php:373
860
  msgid ""
861
  "Sorry, we are unable to process your payment at this time. Please retry "
862
  "later."
863
  msgstr ""
864
 
865
- #: includes/class-wc-gateway-stripe.php:856
866
  msgid ""
867
  "Almost there!\n"
868
  "\n"
@@ -870,13 +866,14 @@ msgid ""
870
  "done is for you to authorize the payment with your bank."
871
  msgstr ""
872
 
873
- #: includes/class-wc-gateway-stripe.php:1020
874
- #: includes/class-wc-stripe-webhook-handler.php:686
 
875
  #. translators: 1) The error message that was received from Stripe.
876
  msgid "Stripe SCA authentication failed. Reason: %s"
877
  msgstr ""
878
 
879
- #: includes/class-wc-gateway-stripe.php:1021
880
  msgid "Stripe SCA authentication failed."
881
  msgstr ""
882
 
@@ -909,15 +906,17 @@ msgid ""
909
  "the issue. (Logging must be enabled to see recorded logs)"
910
  msgstr ""
911
 
912
- #: includes/class-wc-stripe-customer.php:119
913
- msgid "Name"
 
914
  msgstr ""
915
 
916
- #: includes/class-wc-stripe-customer.php:119
917
- msgid "Username"
 
918
  msgstr ""
919
 
920
- #: includes/class-wc-stripe-customer.php:203
921
  msgid "Unable to add payment source."
922
  msgstr ""
923
 
@@ -1008,7 +1007,7 @@ msgid "Payment verification error: %s"
1008
  msgstr ""
1009
 
1010
  #: includes/class-wc-stripe-order-handler.php:144
1011
- #: includes/class-wc-stripe-webhook-handler.php:220
1012
  #: includes/payment-methods/class-wc-gateway-stripe-sepa.php:353
1013
  msgid "This card is no longer available and has been removed."
1014
  msgstr ""
@@ -1035,7 +1034,7 @@ msgstr ""
1035
  msgid "SEPA IBAN ending in %s"
1036
  msgstr ""
1037
 
1038
- #: includes/class-wc-stripe-webhook-handler.php:297
1039
  #. translators: 1) The URL to the order.
1040
  msgid ""
1041
  "A dispute was created for this order. Response is needed. Please go to your "
@@ -1043,34 +1042,34 @@ msgid ""
1043
  "Dashboard</a> to review this dispute."
1044
  msgstr ""
1045
 
1046
- #: includes/class-wc-stripe-webhook-handler.php:343
1047
  #. translators: partial captured amount
1048
  msgid "This charge was partially captured via Stripe Dashboard in the amount of: %s"
1049
  msgstr ""
1050
 
1051
- #: includes/class-wc-stripe-webhook-handler.php:429
1052
  msgid "This payment failed to clear."
1053
  msgstr ""
1054
 
1055
- #: includes/class-wc-stripe-webhook-handler.php:462
1056
  msgid "This payment has cancelled."
1057
  msgstr ""
1058
 
1059
- #: includes/class-wc-stripe-webhook-handler.php:497
1060
  msgid "Refunded via Stripe Dashboard"
1061
  msgstr ""
1062
 
1063
- #: includes/class-wc-stripe-webhook-handler.php:497
1064
- #: includes/class-wc-stripe-webhook-handler.php:525
1065
  msgid "Pre-Authorization Released via Stripe Dashboard"
1066
  msgstr ""
1067
 
1068
- #: includes/class-wc-stripe-webhook-handler.php:525
1069
  #. translators: 1) dollar amount 2) transaction id 3) refund message
1070
  msgid "Refunded %1$s - Refund ID: %2$s - %3$s"
1071
  msgstr ""
1072
 
1073
- #: includes/class-wc-stripe-webhook-handler.php:556
1074
  #. translators: 1) The URL to the order. 2) The reason type.
1075
  msgid ""
1076
  "A review has been opened for this order. Action is needed. Please go to "
@@ -1078,47 +1077,116 @@ msgid ""
1078
  "Dashboard</a> to review the issue. Reason: (%2$s)"
1079
  msgstr ""
1080
 
1081
- #: includes/class-wc-stripe-webhook-handler.php:589
1082
  #. translators: 1) The reason type.
1083
  msgid "The opened review for this order is now closed. Reason: (%s)"
1084
  msgstr ""
1085
 
1086
- #: includes/compat/class-wc-stripe-pre-orders-compat.php:61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1087
  msgid "Unable to store payment details. Please try again."
1088
  msgstr ""
1089
 
1090
- #: includes/compat/class-wc-stripe-pre-orders-compat.php:120
 
 
 
 
 
 
1091
  #. translators: error message
1092
  msgid "Stripe Transaction Failed (%s)"
1093
  msgstr ""
1094
 
1095
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:88
1096
- #: includes/compat/class-wc-stripe-subs-compat.php:88
1097
  msgid ""
1098
  "Update the Payment Method used for all of my active subscriptions "
1099
  "(optional)."
1100
  msgstr ""
1101
 
1102
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:226
1103
- #: includes/compat/class-wc-stripe-subs-compat.php:207
1104
  msgid "Customer not found"
1105
  msgstr ""
1106
 
1107
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:386
1108
- #: includes/compat/class-wc-stripe-subs-compat.php:452
1109
  #. translators: error message
1110
  msgid "A \"Stripe Customer ID\" value is required."
1111
  msgstr ""
1112
 
1113
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:388
1114
- #: includes/compat/class-wc-stripe-subs-compat.php:454
1115
  msgid ""
1116
  "Invalid customer ID. A valid \"Stripe Customer ID\" must begin with "
1117
  "\"cus_\"."
1118
  msgstr ""
1119
 
1120
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:397
1121
- #: includes/compat/class-wc-stripe-subs-compat.php:463
1122
  msgid ""
1123
  "Invalid source ID. A valid source \"Stripe Source ID\" must begin with "
1124
  "\"src_\" or \"card_\"."
@@ -1129,19 +1197,17 @@ msgstr ""
1129
  msgid "Via SEPA Direct Debit ending in %1$s"
1130
  msgstr ""
1131
 
1132
- #: includes/compat/class-wc-stripe-subs-compat.php:131
1133
- msgid ""
1134
- "Sorry, we're not accepting prepaid cards at this time. Your credit card has "
1135
- "not been charge. Please try with alternative payment method."
1136
  msgstr ""
1137
 
1138
- #: includes/compat/class-wc-stripe-subs-compat.php:544
1139
  #. translators: 1) card brand 2) last 4 digits
1140
  msgid "Via %1$s card ending in %2$s"
1141
  msgstr ""
1142
 
1143
  #: includes/payment-methods/class-wc-gateway-stripe-alipay.php:60
1144
- #: woocommerce-gateway-stripe.php:253
1145
  msgid "Stripe Alipay"
1146
  msgstr ""
1147
 
@@ -1170,27 +1236,27 @@ msgid "Add Payment"
1170
  msgstr ""
1171
 
1172
  #: includes/payment-methods/class-wc-gateway-stripe-bancontact.php:60
1173
- #: woocommerce-gateway-stripe.php:247
1174
  msgid "Stripe Bancontact"
1175
  msgstr ""
1176
 
1177
  #: includes/payment-methods/class-wc-gateway-stripe-eps.php:60
1178
- #: woocommerce-gateway-stripe.php:250
1179
  msgid "Stripe EPS"
1180
  msgstr ""
1181
 
1182
  #: includes/payment-methods/class-wc-gateway-stripe-giropay.php:60
1183
- #: woocommerce-gateway-stripe.php:249
1184
  msgid "Stripe Giropay"
1185
  msgstr ""
1186
 
1187
  #: includes/payment-methods/class-wc-gateway-stripe-ideal.php:60
1188
- #: woocommerce-gateway-stripe.php:251
1189
  msgid "Stripe iDeal"
1190
  msgstr ""
1191
 
1192
  #: includes/payment-methods/class-wc-gateway-stripe-multibanco.php:60
1193
- #: woocommerce-gateway-stripe.php:255
1194
  msgid "Stripe Multibanco"
1195
  msgstr ""
1196
 
@@ -1219,12 +1285,12 @@ msgid "Awaiting Multibanco payment"
1219
  msgstr ""
1220
 
1221
  #: includes/payment-methods/class-wc-gateway-stripe-p24.php:60
1222
- #: woocommerce-gateway-stripe.php:252
1223
  msgid "Stripe P24"
1224
  msgstr ""
1225
 
1226
  #: includes/payment-methods/class-wc-gateway-stripe-sepa.php:75
1227
- #: woocommerce-gateway-stripe.php:254
1228
  msgid "Stripe SEPA Direct Debit"
1229
  msgstr ""
1230
 
@@ -1251,7 +1317,7 @@ msgid ""
1251
  msgstr ""
1252
 
1253
  #: includes/payment-methods/class-wc-gateway-stripe-sofort.php:60
1254
- #: woocommerce-gateway-stripe.php:248
1255
  msgid "Stripe SOFORT"
1256
  msgstr ""
1257
 
@@ -1309,6 +1375,23 @@ msgstr ""
1309
  msgid "Discount"
1310
  msgstr ""
1311
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1312
  #: woocommerce-gateway-stripe.php:32
1313
  #. translators: 1. URL link.
1314
  msgid ""
@@ -1316,15 +1399,15 @@ msgid ""
1316
  "here."
1317
  msgstr ""
1318
 
1319
- #: woocommerce-gateway-stripe.php:194
1320
  msgid "Settings"
1321
  msgstr ""
1322
 
1323
- #: woocommerce-gateway-stripe.php:195
1324
  msgid "Docs"
1325
  msgstr ""
1326
 
1327
- #: woocommerce-gateway-stripe.php:196
1328
  msgid "Support"
1329
  msgstr ""
1330
 
@@ -1346,4 +1429,51 @@ msgstr ""
1346
 
1347
  #. Author URI of the plugin/theme
1348
  msgid "https://woocommerce.com/"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1349
  msgstr ""
2
  # This file is distributed under the same license as the WooCommerce Stripe Gateway package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: WooCommerce Stripe Gateway 4.3.0\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/stripe-git\n"
7
+ "POT-Creation-Date: 2019-10-17 20:55:54+00:00\n"
 
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
27
  msgid "Save payment information to my account for future purchases."
28
  msgstr ""
29
 
30
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:230
31
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:216
32
+ #: includes/compat/class-wc-stripe-subs-compat.php:218
33
  #. translators: 1) dollar amount
34
  #. translators: minimum amount
35
  msgid "Sorry, the minimum allowed order total is %1$s to use this payment method."
36
  msgstr ""
37
 
38
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:326
39
  #. translators: 1) blog name 2) order number
40
  msgid "%1$s - Order %2$s"
41
  msgstr ""
42
 
43
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:353
44
  msgid "customer_name"
45
  msgstr ""
46
 
47
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:354
48
  msgid "customer_email"
49
  msgstr ""
50
 
51
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:417
52
  #. translators: transaction id
53
  msgid "Stripe charge awaiting payment: %s."
54
  msgstr ""
55
 
56
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:424
57
  #: includes/class-wc-stripe-order-handler.php:296
58
+ #: includes/class-wc-stripe-webhook-handler.php:347
59
+ #: includes/class-wc-stripe-webhook-handler.php:399
60
  #. translators: transaction id
61
  msgid "Stripe charge complete (Charge ID: %s)"
62
  msgstr ""
63
 
64
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:429
65
+ #: includes/class-wc-gateway-stripe.php:495
66
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:173
 
67
  msgid "Payment processing failed. Please retry."
68
  msgstr ""
69
 
70
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:441
71
  #. translators: transaction id
72
  msgid ""
73
  "Stripe charge authorized (Charge ID: %s). Process order to take payment, or "
74
  "cancel to remove the pre-authorization."
75
  msgstr ""
76
 
77
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:615
78
  msgid "Invalid payment method. Please input a new card number."
79
  msgstr ""
80
 
81
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:895
82
  #. translators: 1) dollar amount 2) transaction id 3) refund message
83
  msgid "Refunded %1$s - Refund ID: %2$s - Reason: %3$s"
84
  msgstr ""
85
 
86
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:895
87
  msgid "Pre-Authorization Released"
88
  msgstr ""
89
 
90
+ #: includes/abstracts/abstract-wc-stripe-payment-gateway.php:913
91
  msgid "There was a problem adding the payment method."
92
  msgstr ""
93
 
94
+ #: includes/admin/class-wc-stripe-admin-notices.php:124
95
  #. translators: 1) A URL that explains Stripe Radar.
96
  msgid ""
97
  "WooCommerce Stripe - We see that you had the \"Require 3D secure when "
100
  "href=\"%s\" target=\"_blank\">here</a>."
101
  msgstr ""
102
 
103
+ #: includes/admin/class-wc-stripe-admin-notices.php:131
104
  #. translators: 1) int version 2) int version
105
  msgid ""
106
  "WooCommerce Stripe - We recently made changes to Stripe that may impact the "
107
  "appearance of your checkout. If your checkout has changed unexpectedly, "
108
  "please follow these <a "
109
+ "href=\"https://docs.woocommerce.com/document/stripe/#styling\" "
110
  "target=\"_blank\">instructions</a> to fix."
111
  msgstr ""
112
 
113
+ #: includes/admin/class-wc-stripe-admin-notices.php:141
114
  #. translators: 1) int version 2) int version
115
  msgid ""
116
  "WooCommerce Stripe - The minimum PHP version required for this plugin is "
117
  "%1$s. You are running %2$s."
118
  msgstr ""
119
 
120
+ #: includes/admin/class-wc-stripe-admin-notices.php:152
121
  #. translators: 1) int version 2) int version
122
  msgid ""
123
  "WooCommerce Stripe - The minimum WooCommerce version required for this "
124
  "plugin is %1$s. You are running %2$s."
125
  msgstr ""
126
 
127
+ #: includes/admin/class-wc-stripe-admin-notices.php:162
128
  msgid "WooCommerce Stripe - cURL is not installed."
129
  msgstr ""
130
 
131
+ #: includes/admin/class-wc-stripe-admin-notices.php:172
132
  #. translators: 1) link
133
  msgid ""
134
  "Stripe is almost ready. To get started, <a href=\"%s\">set your Stripe "
135
  "account keys</a>."
136
  msgstr ""
137
 
138
+ #: includes/admin/class-wc-stripe-admin-notices.php:183
139
  #. translators: 1) link
140
  msgid ""
141
  "Stripe is in test mode however your test keys may not be valid. Test keys "
143
  "<a href=\"%s\">set your Stripe account keys</a>."
144
  msgstr ""
145
 
146
+ #: includes/admin/class-wc-stripe-admin-notices.php:192
147
  #. translators: 1) link
148
  msgid ""
149
  "Stripe is in live mode however your test keys may not be valid. Live keys "
151
  "<a href=\"%s\">set your Stripe account keys</a>."
152
  msgstr ""
153
 
154
+ #: includes/admin/class-wc-stripe-admin-notices.php:201
155
  #. translators: 1) link
156
  msgid ""
157
  "Stripe is enabled, but a SSL certificate is not detected. Your checkout may "
159
  "target=\"_blank\">SSL certificate</a>"
160
  msgstr ""
161
 
162
+ #: includes/admin/class-wc-stripe-admin-notices.php:206
163
+ msgid ""
164
+ "Stripe is now ready for Strong Customer Authentication (SCA) and 3D Secure "
165
+ "2! <a href=\"%1$s\" target=\"_blank\">Read about SCA</a>"
166
+ msgstr ""
167
+
168
+ #: includes/admin/class-wc-stripe-admin-notices.php:229
169
  #. translators: %1$s Payment method, %2$s List of supported currencies
170
  msgid "%1$s is enabled - it requires store currency to be set to %2$s"
171
  msgstr ""
172
 
173
+ #: includes/admin/class-wc-stripe-admin-notices.php:243
174
  msgid "Action failed. Please refresh the page and retry."
175
  msgstr ""
176
 
177
+ #: includes/admin/class-wc-stripe-admin-notices.php:247
178
  msgid "Cheatin&#8217; huh?"
179
  msgstr ""
180
 
210
 
211
  #: includes/admin/class-wc-stripe-privacy.php:41
212
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:463
213
+ #: includes/compat/class-wc-stripe-subs-compat.php:527
214
+ #: includes/compat/class-wc-stripe-subs-compat.php:542
215
  msgid "N/A"
216
  msgstr ""
217
 
765
  "your Stripe account keys</a>."
766
  msgstr ""
767
 
768
+ #: includes/class-wc-gateway-stripe.php:191
769
  #. translators: 1) Opening anchor tag 2) closing anchor tag
770
  msgid ""
771
  "If your billing address has been changed for saved payment methods, be sure "
772
  "to remove any %1$ssaved payment methods%2$s on file and re-add them."
773
  msgstr ""
774
 
775
+ #: includes/class-wc-gateway-stripe.php:267
 
 
 
 
 
 
 
 
776
  #. translators: link to Stripe testing page
777
  msgid ""
778
  "TEST MODE ENABLED. In test mode, you can use the card number "
781
  "card numbers."
782
  msgstr ""
783
 
784
+ #: includes/class-wc-gateway-stripe.php:306
785
  msgid "Credit or debit card"
786
  msgstr ""
787
 
788
+ #: includes/class-wc-gateway-stripe.php:314
789
  msgid "Card Number"
790
  msgstr ""
791
 
792
+ #: includes/class-wc-gateway-stripe.php:325
793
  msgid "Expiry Date"
794
  msgstr ""
795
 
796
+ #: includes/class-wc-gateway-stripe.php:333
797
  msgid "Card Code (CVC)"
798
  msgstr ""
799
 
800
+ #: includes/class-wc-gateway-stripe.php:408
801
  msgid "Please accept the terms and conditions first"
802
  msgstr ""
803
 
804
+ #: includes/class-wc-gateway-stripe.php:409
805
  msgid "Please fill in required checkout fields first"
806
  msgstr ""
807
 
808
+ #: includes/class-wc-gateway-stripe.php:438
809
+ #: includes/class-wc-gateway-stripe.php:482
810
  msgid ""
811
  "Sorry, we're not accepting prepaid cards at this time. Your credit card has "
812
  "not been charged. Please try with alternative payment method."
813
  msgstr ""
814
 
815
+ #: includes/class-wc-gateway-stripe.php:439
816
  msgid "Please enter your IBAN account name."
817
  msgstr ""
818
 
819
+ #: includes/class-wc-gateway-stripe.php:440
820
  msgid "Please enter your IBAN account number."
821
  msgstr ""
822
 
823
+ #: includes/class-wc-gateway-stripe.php:441
824
  msgid "We couldn't initiate the payment. Please try again."
825
  msgstr ""
826
 
827
+ #: includes/class-wc-gateway-stripe.php:452
828
  msgid "Billing First Name and Last Name are required."
829
  msgstr ""
830
 
831
+ #: includes/class-wc-gateway-stripe.php:716
832
  #. translators: error message
833
  msgid "This represents the fee Stripe collects for the transaction."
834
  msgstr ""
835
 
836
+ #: includes/class-wc-gateway-stripe.php:717
837
  msgid "Stripe Fee:"
838
  msgstr ""
839
 
840
+ #: includes/class-wc-gateway-stripe.php:753
841
  msgid ""
842
  "This represents the net total that will be credited to your Stripe bank "
843
  "account. This may be in the currency that is set in your Stripe account."
844
  msgstr ""
845
 
846
+ #: includes/class-wc-gateway-stripe.php:754
847
  msgid "Stripe Payout:"
848
  msgstr ""
849
 
850
+ #: includes/class-wc-gateway-stripe.php:801
851
  #: includes/class-wc-stripe-order-handler.php:162
852
+ #: includes/class-wc-stripe-webhook-handler.php:238
853
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:263
854
+ #: includes/compat/class-wc-stripe-subs-compat.php:271
855
  #: includes/payment-methods/class-wc-gateway-stripe-sepa.php:373
856
  msgid ""
857
  "Sorry, we are unable to process your payment at this time. Please retry "
858
  "later."
859
  msgstr ""
860
 
861
+ #: includes/class-wc-gateway-stripe.php:854
862
  msgid ""
863
  "Almost there!\n"
864
  "\n"
866
  "done is for you to authorize the payment with your bank."
867
  msgstr ""
868
 
869
+ #: includes/class-wc-gateway-stripe.php:1066
870
+ #: includes/class-wc-stripe-webhook-handler.php:685
871
+ #: includes/class-wc-stripe-webhook-handler.php:724
872
  #. translators: 1) The error message that was received from Stripe.
873
  msgid "Stripe SCA authentication failed. Reason: %s"
874
  msgstr ""
875
 
876
+ #: includes/class-wc-gateway-stripe.php:1067
877
  msgid "Stripe SCA authentication failed."
878
  msgstr ""
879
 
906
  "the issue. (Logging must be enabled to see recorded logs)"
907
  msgstr ""
908
 
909
+ #: includes/class-wc-stripe-customer.php:120
910
+ #. translators: %1$s First name, %2$s Second name, %3$s Username.
911
+ msgid "Name: %1$s %2$s, Username: %s"
912
  msgstr ""
913
 
914
+ #: includes/class-wc-stripe-customer.php:131
915
+ #. translators: %1$s First name, %2$s Second name.
916
+ msgid "Name: %1$s %2$s, Guest"
917
  msgstr ""
918
 
919
+ #: includes/class-wc-stripe-customer.php:209
920
  msgid "Unable to add payment source."
921
  msgstr ""
922
 
1007
  msgstr ""
1008
 
1009
  #: includes/class-wc-stripe-order-handler.php:144
1010
+ #: includes/class-wc-stripe-webhook-handler.php:219
1011
  #: includes/payment-methods/class-wc-gateway-stripe-sepa.php:353
1012
  msgid "This card is no longer available and has been removed."
1013
  msgstr ""
1034
  msgid "SEPA IBAN ending in %s"
1035
  msgstr ""
1036
 
1037
+ #: includes/class-wc-stripe-webhook-handler.php:296
1038
  #. translators: 1) The URL to the order.
1039
  msgid ""
1040
  "A dispute was created for this order. Response is needed. Please go to your "
1042
  "Dashboard</a> to review this dispute."
1043
  msgstr ""
1044
 
1045
+ #: includes/class-wc-stripe-webhook-handler.php:342
1046
  #. translators: partial captured amount
1047
  msgid "This charge was partially captured via Stripe Dashboard in the amount of: %s"
1048
  msgstr ""
1049
 
1050
+ #: includes/class-wc-stripe-webhook-handler.php:428
1051
  msgid "This payment failed to clear."
1052
  msgstr ""
1053
 
1054
+ #: includes/class-wc-stripe-webhook-handler.php:461
1055
  msgid "This payment has cancelled."
1056
  msgstr ""
1057
 
1058
+ #: includes/class-wc-stripe-webhook-handler.php:496
1059
  msgid "Refunded via Stripe Dashboard"
1060
  msgstr ""
1061
 
1062
+ #: includes/class-wc-stripe-webhook-handler.php:496
1063
+ #: includes/class-wc-stripe-webhook-handler.php:524
1064
  msgid "Pre-Authorization Released via Stripe Dashboard"
1065
  msgstr ""
1066
 
1067
+ #: includes/class-wc-stripe-webhook-handler.php:524
1068
  #. translators: 1) dollar amount 2) transaction id 3) refund message
1069
  msgid "Refunded %1$s - Refund ID: %2$s - %3$s"
1070
  msgstr ""
1071
 
1072
+ #: includes/class-wc-stripe-webhook-handler.php:555
1073
  #. translators: 1) The URL to the order. 2) The reason type.
1074
  msgid ""
1075
  "A review has been opened for this order. Action is needed. Please go to "
1077
  "Dashboard</a> to review the issue. Reason: (%2$s)"
1078
  msgstr ""
1079
 
1080
+ #: includes/class-wc-stripe-webhook-handler.php:588
1081
  #. translators: 1) The reason type.
1082
  msgid "The opened review for this order is now closed. Reason: (%s)"
1083
  msgstr ""
1084
 
1085
+ #: includes/compat/class-wc-stripe-email-failed-authentication-retry.php:29
1086
+ msgid "Payment Authentication Requested Email"
1087
+ msgstr ""
1088
+
1089
+ #: includes/compat/class-wc-stripe-email-failed-authentication-retry.php:30
1090
+ msgid ""
1091
+ "Payment authentication requested emails are sent to chosen recipient(s) "
1092
+ "when an attempt to automatically process a subscription renewal payment "
1093
+ "fails because the transaction requires an SCA verification, the customer is "
1094
+ "requested to authenticate the payment, and a retry rule has been applied to "
1095
+ "notify the customer again within a certain time period."
1096
+ msgstr ""
1097
+
1098
+ #: includes/compat/class-wc-stripe-email-failed-authentication-retry.php:32
1099
+ msgid "Automatic renewal payment failed due to authentication required"
1100
+ msgstr ""
1101
+
1102
+ #: includes/compat/class-wc-stripe-email-failed-authentication-retry.php:33
1103
+ msgid ""
1104
+ "[{site_title}] Automatic payment failed for {order_number}. Customer asked "
1105
+ "to authenticate payment and will be notified again {retry_time}"
1106
+ msgstr ""
1107
+
1108
+ #: includes/compat/class-wc-stripe-email-failed-authentication.php:86
1109
+ msgid "Enable this email notification"
1110
+ msgstr ""
1111
+
1112
+ #: includes/compat/class-wc-stripe-email-failed-preorder-authentication.php:26
1113
+ msgid "Pre-order Payment Action Needed"
1114
+ msgstr ""
1115
+
1116
+ #: includes/compat/class-wc-stripe-email-failed-preorder-authentication.php:27
1117
+ msgid ""
1118
+ "This is an order notification sent to the customer once a pre-order is "
1119
+ "complete, but additional payment steps are required."
1120
+ msgstr ""
1121
+
1122
+ #: includes/compat/class-wc-stripe-email-failed-preorder-authentication.php:84
1123
+ #: includes/compat/class-wc-stripe-email-failed-preorder-authentication.php:93
1124
+ msgid "Payment authorization needed for pre-order {order_number}"
1125
+ msgstr ""
1126
+
1127
+ #: includes/compat/class-wc-stripe-email-failed-renewal-authentication.php:19
1128
+ msgid "Failed Subscription Renewal SCA Authentication"
1129
+ msgstr ""
1130
+
1131
+ #: includes/compat/class-wc-stripe-email-failed-renewal-authentication.php:20
1132
+ msgid ""
1133
+ "Sent to a customer when a renewal fails because the transaction requires an "
1134
+ "SCA verification. The email contains renewal order information and payment "
1135
+ "links."
1136
+ msgstr ""
1137
+
1138
+ #: includes/compat/class-wc-stripe-email-failed-renewal-authentication.php:67
1139
+ msgid ""
1140
+ "Payment authorization needed for renewal of {site_title} order "
1141
+ "{order_number}"
1142
+ msgstr ""
1143
+
1144
+ #: includes/compat/class-wc-stripe-email-failed-renewal-authentication.php:76
1145
+ msgid "Payment authorization needed for renewal of order {order_number}"
1146
+ msgstr ""
1147
+
1148
+ #: includes/compat/class-wc-stripe-pre-orders-compat.php:57
1149
  msgid "Unable to store payment details. Please try again."
1150
  msgstr ""
1151
 
1152
+ #: includes/compat/class-wc-stripe-pre-orders-compat.php:121
1153
+ #: includes/compat/class-wc-stripe-subs-compat.php:303
1154
+ #: includes/compat/class-wc-stripe-subs-compat.php:606
1155
+ msgid "Stripe charge awaiting authentication by user: %s."
1156
+ msgstr ""
1157
+
1158
+ #: includes/compat/class-wc-stripe-pre-orders-compat.php:138
1159
  #. translators: error message
1160
  msgid "Stripe Transaction Failed (%s)"
1161
  msgstr ""
1162
 
1163
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:88
1164
+ #: includes/compat/class-wc-stripe-subs-compat.php:97
1165
  msgid ""
1166
  "Update the Payment Method used for all of my active subscriptions "
1167
  "(optional)."
1168
  msgstr ""
1169
 
1170
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:226
1171
+ #: includes/compat/class-wc-stripe-subs-compat.php:233
1172
  msgid "Customer not found"
1173
  msgstr ""
1174
 
1175
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:386
1176
+ #: includes/compat/class-wc-stripe-subs-compat.php:450
1177
  #. translators: error message
1178
  msgid "A \"Stripe Customer ID\" value is required."
1179
  msgstr ""
1180
 
1181
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:388
1182
+ #: includes/compat/class-wc-stripe-subs-compat.php:452
1183
  msgid ""
1184
  "Invalid customer ID. A valid \"Stripe Customer ID\" must begin with "
1185
  "\"cus_\"."
1186
  msgstr ""
1187
 
1188
  #: includes/compat/class-wc-stripe-sepa-subs-compat.php:397
1189
+ #: includes/compat/class-wc-stripe-subs-compat.php:461
1190
  msgid ""
1191
  "Invalid source ID. A valid source \"Stripe Source ID\" must begin with "
1192
  "\"src_\" or \"card_\"."
1197
  msgid "Via SEPA Direct Debit ending in %1$s"
1198
  msgstr ""
1199
 
1200
+ #: includes/compat/class-wc-stripe-subs-compat.php:295
1201
+ msgid "This transaction requires authentication."
 
 
1202
  msgstr ""
1203
 
1204
+ #: includes/compat/class-wc-stripe-subs-compat.php:542
1205
  #. translators: 1) card brand 2) last 4 digits
1206
  msgid "Via %1$s card ending in %2$s"
1207
  msgstr ""
1208
 
1209
  #: includes/payment-methods/class-wc-gateway-stripe-alipay.php:60
1210
+ #: woocommerce-gateway-stripe.php:256
1211
  msgid "Stripe Alipay"
1212
  msgstr ""
1213
 
1236
  msgstr ""
1237
 
1238
  #: includes/payment-methods/class-wc-gateway-stripe-bancontact.php:60
1239
+ #: woocommerce-gateway-stripe.php:250
1240
  msgid "Stripe Bancontact"
1241
  msgstr ""
1242
 
1243
  #: includes/payment-methods/class-wc-gateway-stripe-eps.php:60
1244
+ #: woocommerce-gateway-stripe.php:253
1245
  msgid "Stripe EPS"
1246
  msgstr ""
1247
 
1248
  #: includes/payment-methods/class-wc-gateway-stripe-giropay.php:60
1249
+ #: woocommerce-gateway-stripe.php:252
1250
  msgid "Stripe Giropay"
1251
  msgstr ""
1252
 
1253
  #: includes/payment-methods/class-wc-gateway-stripe-ideal.php:60
1254
+ #: woocommerce-gateway-stripe.php:254
1255
  msgid "Stripe iDeal"
1256
  msgstr ""
1257
 
1258
  #: includes/payment-methods/class-wc-gateway-stripe-multibanco.php:60
1259
+ #: woocommerce-gateway-stripe.php:258
1260
  msgid "Stripe Multibanco"
1261
  msgstr ""
1262
 
1285
  msgstr ""
1286
 
1287
  #: includes/payment-methods/class-wc-gateway-stripe-p24.php:60
1288
+ #: woocommerce-gateway-stripe.php:255
1289
  msgid "Stripe P24"
1290
  msgstr ""
1291
 
1292
  #: includes/payment-methods/class-wc-gateway-stripe-sepa.php:75
1293
+ #: woocommerce-gateway-stripe.php:257
1294
  msgid "Stripe SEPA Direct Debit"
1295
  msgstr ""
1296
 
1317
  msgstr ""
1318
 
1319
  #: includes/payment-methods/class-wc-gateway-stripe-sofort.php:60
1320
+ #: woocommerce-gateway-stripe.php:251
1321
  msgid "Stripe SOFORT"
1322
  msgstr ""
1323
 
1375
  msgid "Discount"
1376
  msgstr ""
1377
 
1378
+ #: templates/emails/failed-preorder-authentication.php:20
1379
+ msgid "Authorize the payment now &raquo;"
1380
+ msgstr ""
1381
+
1382
+ #: templates/emails/failed-preorder-authentication.php:56
1383
+ msgid "Thanks for shopping with us."
1384
+ msgstr ""
1385
+
1386
+ #: templates/emails/failed-renewal-authentication-requested.php:35
1387
+ #: templates/emails/plain/failed-renewal-authentication-requested.php:26
1388
+ msgid "The renewal order is as follows:"
1389
+ msgstr ""
1390
+
1391
+ #: templates/emails/failed-renewal-authentication.php:12
1392
+ msgid "Authorize the payment &raquo;"
1393
+ msgstr ""
1394
+
1395
  #: woocommerce-gateway-stripe.php:32
1396
  #. translators: 1. URL link.
1397
  msgid ""
1399
  "here."
1400
  msgstr ""
1401
 
1402
+ #: woocommerce-gateway-stripe.php:197
1403
  msgid "Settings"
1404
  msgstr ""
1405
 
1406
+ #: woocommerce-gateway-stripe.php:198
1407
  msgid "Docs"
1408
  msgstr ""
1409
 
1410
+ #: woocommerce-gateway-stripe.php:199
1411
  msgid "Support"
1412
  msgstr ""
1413
 
1429
 
1430
  #. Author URI of the plugin/theme
1431
  msgid "https://woocommerce.com/"
1432
+ msgstr ""
1433
+
1434
+ #: includes/compat/class-wc-stripe-email-failed-authentication.php:84
1435
+ msgctxt "an email notification"
1436
+ msgid "Enable/Disable"
1437
+ msgstr ""
1438
+
1439
+ #: templates/emails/failed-preorder-authentication.php:19
1440
+ #. translators: %s is a link to the payment re-authentication URL.
1441
+ msgctxt "In failed SCA authentication for a pre-order."
1442
+ msgid ""
1443
+ "Your pre-order is now available, but payment cannot be completed "
1444
+ "automatically. %s"
1445
+ msgstr ""
1446
+
1447
+ #: templates/emails/failed-renewal-authentication-requested.php:23
1448
+ #: templates/emails/plain/failed-renewal-authentication-requested.php:17
1449
+ #. translators: %1$s: an order number, %2$s: the customer's full name, %3$s:
1450
+ #. lowercase human time diff in the form returned by wcs_get_human_time_diff(),
1451
+ #. e.g. 'in 12 hours'.
1452
+ msgctxt "In admin renewal failed email"
1453
+ msgid ""
1454
+ "The automatic recurring payment for order %1$s from %2$s has failed. The "
1455
+ "customer was sent an email requesting authentication of payment. If the "
1456
+ "customer does not authenticate the payment, they will be requested by email "
1457
+ "again %3$s."
1458
+ msgstr ""
1459
+
1460
+ #: templates/emails/failed-renewal-authentication.php:12
1461
+ #: templates/emails/plain/failed-renewal-authentication.php:9
1462
+ #. translators: %1$s: name of the blog, %2$s: link to payment re-authentication
1463
+ #. URL, note: no full stop due to url at the end
1464
+ #. translators: %1$s: name of the blog, %2$s: link to checkout payment url,
1465
+ #. note: no full stop due to url at the end
1466
+ msgctxt "In failed renewal authentication email"
1467
+ msgid ""
1468
+ "The automatic payment to renew your subscription with %1$s has failed. To "
1469
+ "reactivate the subscription, please login and authorize the renewal from "
1470
+ "your account page: %2$s"
1471
+ msgstr ""
1472
+
1473
+ #: templates/emails/plain/failed-preorder-authentication.php:10
1474
+ #. translators: %s is a link to the payment re-authentication URL.
1475
+ msgctxt "woocommerce-gateway-stripe"
1476
+ msgid ""
1477
+ "Your pre-order is now available, but payment cannot be completed "
1478
+ "automatically. Please complete the payment now: %s"
1479
  msgstr ""
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: automattic, royho, akeda, mattyza, bor0, woothemes
3
  Tags: credit card, stripe, apple pay, payment request, google pay, sepa, sofort, bancontact, alipay, giropay, ideal, p24, woocommerce, automattic
4
  Requires at least: 4.4
5
- Tested up to: 5.2.2
6
  Requires PHP: 5.6
7
- Stable tag: 4.2.5
8
  License: GPLv3
9
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
10
  Attributions: thorsten-stripe
@@ -113,20 +113,19 @@ If you get stuck, you can ask for help in the Plugin Forum.
113
 
114
  == Changelog ==
115
 
 
 
 
 
 
 
 
 
 
 
116
  = 4.2.5 - 2019-10-02 =
117
  * Fix - WooCommerce Subscriptions that use only the Stripe customer ID can again be renewed
118
 
119
- = 4.2.4 - 2019-09-18 =
120
- * Fix - Unclear error message when email address not completely filled in.
121
- * Fix - Add payment request button compatibility with variable subscriptions
122
- * Tweak - Do not show payment request button for shippable trial subscription products
123
- * Fix - Do not copy the payment intent id when creating a subscription renewal
124
- * Fix - Return early from check intent status if no order - props strayobject
125
- * Fix - Extend webhook time window from 1 to 5 minutes to match Stripe client
126
- * Update - WooCommerce 3.7 compatibility
127
- * Update - Apple Pay Domain association file
128
- * Update - Grandfather pre-SCA subscription renewals for SCA
129
-
130
  [See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/master/changelog.txt).
131
 
132
  == Upgrade Notice ==
2
  Contributors: automattic, royho, akeda, mattyza, bor0, woothemes
3
  Tags: credit card, stripe, apple pay, payment request, google pay, sepa, sofort, bancontact, alipay, giropay, ideal, p24, woocommerce, automattic
4
  Requires at least: 4.4
5
+ Tested up to: 5.2.4
6
  Requires PHP: 5.6
7
+ Stable tag: 4.3.0
8
  License: GPLv3
9
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
10
  Attributions: thorsten-stripe
113
 
114
  == Changelog ==
115
 
116
+ = 4.3.0 2019-10-17 =
117
+ * Add - For WooCommerce Subscriptions optimize the payment flow for subsequent subscription payments when authentication may be required by using the `setup_future_usage` parameter for the first subscription payment
118
+ * Add - Allow customer to authenticate payment even if they are not charged right away for WooCommerce Subscriptions and Pre-Orders, for example for a WooCommerce Subscription that has a free trial
119
+ * Add - When an off-session payment requires authentication, create a link for customers to come back to the store to authenticate the payment
120
+ * Add - Send an email to WooCommerce Subscription and Pre-Orders customers who need to authenticate a payment that was automatically tried on their behalf
121
+ * Add - When an off-session payment requires authentication, send an email to the admin
122
+ * Add - Admin notice about SCA-readiness
123
+ * Fix - Avoid idempotency key errors for Pre-Orders
124
+ * Fix - Use unique anchor for link about checkout styling changes
125
+
126
  = 4.2.5 - 2019-10-02 =
127
  * Fix - WooCommerce Subscriptions that use only the Stripe customer ID can again be renewed
128
 
 
 
 
 
 
 
 
 
 
 
 
129
  [See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/master/changelog.txt).
130
 
131
  == Upgrade Notice ==
templates/emails/failed-preorder-authentication.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly
4
+ }
5
+ ?>
6
+
7
+ <?php do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
8
+
9
+ <?php
10
+ $pre_wc_30 = version_compare( WC_VERSION, '3.0', '<' );
11
+ $billing_email = $pre_wc_30 ? $order->billing_email : $order->get_billing_email();
12
+ $billing_phone = $pre_wc_30 ? $order->billing_phone : $order->get_billing_phone();
13
+
14
+ ?>
15
+ <p><?php
16
+ echo wp_kses(
17
+ sprintf(
18
+ // translators: %s is a link to the payment re-authentication URL.
19
+ _x( 'Your pre-order is now available, but payment cannot be completed automatically. %s', 'In failed SCA authentication for a pre-order.', 'woocommerce-gateway-stripe' ),
20
+ '<a href="' . esc_url( $authorization_url ) . '">' . esc_html__( 'Authorize the payment now &raquo;', 'woocommerce-gateway-stripe' ) . '</a>'
21
+ ),
22
+ array( 'a' => array( 'href' => true ) )
23
+ );
24
+ ?></p>
25
+
26
+ <?php if ( $email->get_custom_message() ) : ?>
27
+ <blockquote><?php echo wpautop( wptexturize( $email->get_custom_message() ) ); ?></blockquote>
28
+ <?php endif; ?>
29
+
30
+ <?php
31
+ do_action( 'woocommerce_email_before_order_table', $order, false, $plain_text, $email );
32
+
33
+ /*
34
+ * @hooked WC_Emails::order_details() Shows the order details table.
35
+ * @hooked WC_Structured_Data::generate_order_data() Generates structured data.
36
+ * @hooked WC_Structured_Data::output_structured_data() Outputs structured data.
37
+ * @since 2.5.0
38
+ */
39
+ do_action( 'woocommerce_email_order_details', $order, $sent_to_admin, $plain_text, $email );
40
+
41
+ /*
42
+ * @hooked WC_Emails::order_meta() Shows order meta data.
43
+ */
44
+ do_action( 'woocommerce_email_order_meta', $order, $sent_to_admin, $plain_text, $email );
45
+
46
+ do_action( 'woocommerce_email_after_order_table', $order, false, $plain_text, $email );
47
+
48
+ /*
49
+ * @hooked WC_Emails::customer_details() Shows customer details
50
+ * @hooked WC_Emails::email_address() Shows email address
51
+ */
52
+ do_action( 'woocommerce_email_customer_details', $order, $sent_to_admin, $plain_text, $email );
53
+
54
+ ?>
55
+ <p>
56
+ <?php esc_html_e( 'Thanks for shopping with us.', 'woocommerce-gateway-stripe' ); ?>
57
+ </p>
58
+ <?php
59
+
60
+ /*
61
+ * @hooked WC_Emails::email_footer() Output the email footer
62
+ */
63
+ do_action( 'woocommerce_email_footer', $email );
templates/emails/failed-renewal-authentication-requested.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin email about payment retry failed due to authentication
4
+ *
5
+ * @package WooCommerce_Stripe/Templates/Emails
6
+ * @version 4.3.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Output the email header.
15
+ */
16
+ do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
17
+
18
+ <p>
19
+ <?php
20
+ echo esc_html(
21
+ sprintf(
22
+ // translators: %1$s: an order number, %2$s: the customer's full name, %3$s: lowercase human time diff in the form returned by wcs_get_human_time_diff(), e.g. 'in 12 hours'.
23
+ _x(
24
+ 'The automatic recurring payment for order %1$s from %2$s has failed. The customer was sent an email requesting authentication of payment. If the customer does not authenticate the payment, they will be requested by email again %3$s.',
25
+ 'In admin renewal failed email',
26
+ 'woocommerce-gateway-stripe'
27
+ ),
28
+ $order->get_order_number(),
29
+ $order->get_formatted_billing_full_name(),
30
+ wcs_get_human_time_diff( $retry->get_time() )
31
+ )
32
+ );
33
+ ?>
34
+ </p>
35
+ <p><?php esc_html_e( 'The renewal order is as follows:', 'woocommerce-gateway-stripe' ); ?></p>
36
+
37
+ <?php
38
+
39
+ /**
40
+ * Shows the order details table.
41
+ */
42
+ do_action( 'woocommerce_email_order_details', $order, $sent_to_admin, $plain_text, $email );
43
+
44
+ /**
45
+ * Shows order meta data.
46
+ */
47
+ do_action( 'woocommerce_email_order_meta', $order, $sent_to_admin, $plain_text, $email );
48
+
49
+ /**
50
+ * Shows customer details, and email address.
51
+ */
52
+ do_action( 'woocommerce_email_customer_details', $order, $sent_to_admin, $plain_text, $email );
53
+
54
+ /**
55
+ * Output the email footer.
56
+ */
57
+ do_action( 'woocommerce_email_footer', $email );
templates/emails/failed-renewal-authentication.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly
4
+ }
5
+ ?>
6
+
7
+ <?php do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
8
+
9
+ <p>
10
+ <?php
11
+ // translators: %1$s: name of the blog, %2$s: link to payment re-authentication URL, note: no full stop due to url at the end
12
+ echo wp_kses( sprintf( _x( 'The automatic payment to renew your subscription with %1$s has failed. To reactivate the subscription, please login and authorize the renewal from your account page: %2$s', 'In failed renewal authentication email', 'woocommerce-gateway-stripe' ), esc_html( get_bloginfo( 'name' ) ), '<a href="' . esc_url( $authorization_url ) . '">' . esc_html__( 'Authorize the payment &raquo;', 'woocommerce-gateway-stripe' ) . '</a>' ), array( 'a' => array( 'href' => true ) ) ); ?>
13
+ </p>
14
+
15
+ <?php do_action( 'woocommerce_subscriptions_email_order_details', $order, $sent_to_admin, $plain_text, $email ); ?>
16
+
17
+ <?php do_action( 'woocommerce_email_footer', $email ); ?>
templates/emails/plain/failed-preorder-authentication.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly
4
+ }
5
+
6
+ echo $email_heading . "\n\n";
7
+
8
+ printf(
9
+ // translators: %s is a link to the payment re-authentication URL.
10
+ _x( 'Your pre-order is now available, but payment cannot be completed automatically. Please complete the payment now: %s', 'woocommerce-gateway-stripe' ),
11
+ $authorization_url
12
+ );
13
+
14
+ if ( $email->get_custom_message() ) :
15
+
16
+ echo "----------\n\n";
17
+ echo wptexturize( $email->get_custom_message() ) . "\n\n";
18
+ echo "----------\n\n";
19
+
20
+ endif;
21
+
22
+
23
+ echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
24
+
25
+ do_action( 'woocommerce_subscriptions_email_order_details', $order, $sent_to_admin, $plain_text, $email );
26
+
27
+ echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
28
+
29
+ echo apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) );
templates/emails/plain/failed-renewal-authentication-requested.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin email about payment retry failed due to authentication
4
+ *
5
+ * @package WooCommerce_Stripe/Templates/Emails
6
+ * @version 4.3.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ echo '= ' . $email_heading . " =\n\n";
14
+
15
+ printf(
16
+ // translators: %1$s: an order number, %2$s: the customer's full name, %3$s: lowercase human time diff in the form returned by wcs_get_human_time_diff(), e.g. 'in 12 hours'.
17
+ _x(
18
+ 'The automatic recurring payment for order %1$s from %2$s has failed. The customer was sent an email requesting authentication of payment. If the customer does not authenticate the payment, they will be requested by email again %3$s.',
19
+ 'In admin renewal failed email',
20
+ 'woocommerce-gateway-stripe'
21
+ ),
22
+ $order->get_order_number(),
23
+ $order->get_formatted_billing_full_name(),
24
+ wcs_get_human_time_diff( $retry->get_time() )
25
+ ) . "\n\n";
26
+ printf( __( 'The renewal order is as follows:', 'woocommerce-gateway-stripe' ) ) . "\n\n";
27
+
28
+ echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
29
+
30
+ /**
31
+ * Shows the order details table.
32
+ */
33
+ do_action( 'woocommerce_email_order_details', $order, $sent_to_admin, $plain_text, $email );
34
+
35
+ echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
36
+
37
+ /**
38
+ * Shows order meta data.
39
+ */
40
+ do_action( 'woocommerce_email_order_meta', $order, $sent_to_admin, $plain_text, $email );
41
+
42
+ /**
43
+ * Shows customer details, and email address.
44
+ */
45
+ do_action( 'woocommerce_email_customer_details', $order, $sent_to_admin, $plain_text, $email );
46
+
47
+ echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
48
+
49
+ echo apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) );
templates/emails/plain/failed-renewal-authentication.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit; // Exit if accessed directly
4
+ }
5
+
6
+ echo $email_heading . "\n\n";
7
+
8
+ // translators: %1$s: name of the blog, %2$s: link to checkout payment url, note: no full stop due to url at the end
9
+ printf( esc_html_x( 'The automatic payment to renew your subscription with %1$s has failed. To reactivate the subscription, please login and authorize the renewal from your account page: %2$s', 'In failed renewal authentication email', 'woocommerce-gateway-stripe' ), esc_html( get_bloginfo( 'name' ) ), esc_attr( $authorization_url ) );
10
+
11
+ echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
12
+
13
+ do_action( 'woocommerce_subscriptions_email_order_details', $order, $sent_to_admin, $plain_text, $email );
14
+
15
+ echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
16
+
17
+ echo apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) );
woocommerce-gateway-stripe.php CHANGED
@@ -5,11 +5,11 @@
5
  * Description: Take credit card payments on your store using Stripe.
6
  * Author: WooCommerce
7
  * Author URI: https://woocommerce.com/
8
- * Version: 4.2.5
9
  * Requires at least: 4.4
10
- * Tested up to: 5.2.2
11
  * WC requires at least: 2.6
12
- * WC tested up to: 3.7
13
  * Text Domain: woocommerce-gateway-stripe
14
  * Domain Path: /languages
15
  *
@@ -46,7 +46,7 @@ function woocommerce_gateway_stripe_init() {
46
  /**
47
  * Required minimums and constants
48
  */
49
- define( 'WC_STRIPE_VERSION', '4.2.5' );
50
  define( 'WC_STRIPE_MIN_PHP_VER', '5.6.0' );
51
  define( 'WC_STRIPE_MIN_WC_VER', '2.6.0' );
52
  define( 'WC_STRIPE_MAIN_FILE', __FILE__ );
@@ -145,6 +145,9 @@ function woocommerce_gateway_stripe_init() {
145
  add_filter( 'woocommerce_payment_gateways', array( $this, 'add_gateways' ) );
146
  add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
147
 
 
 
 
148
  if ( version_compare( WC_VERSION, '3.4', '<' ) ) {
149
  add_filter( 'woocommerce_get_sections_checkout', array( $this, 'filter_gateway_order_admin' ) );
150
  }
@@ -256,6 +259,26 @@ function woocommerce_gateway_stripe_init() {
256
 
257
  return $sections;
258
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  }
260
 
261
  WC_Stripe::get_instance();
5
  * Description: Take credit card payments on your store using Stripe.
6
  * Author: WooCommerce
7
  * Author URI: https://woocommerce.com/
8
+ * Version: 4.3.0
9
  * Requires at least: 4.4
10
+ * Tested up to: 5.2.4
11
  * WC requires at least: 2.6
12
+ * WC tested up to: 3.7.1
13
  * Text Domain: woocommerce-gateway-stripe
14
  * Domain Path: /languages
15
  *
46
  /**
47
  * Required minimums and constants
48
  */
49
+ define( 'WC_STRIPE_VERSION', '4.3.0' );
50
  define( 'WC_STRIPE_MIN_PHP_VER', '5.6.0' );
51
  define( 'WC_STRIPE_MIN_WC_VER', '2.6.0' );
52
  define( 'WC_STRIPE_MAIN_FILE', __FILE__ );
145
  add_filter( 'woocommerce_payment_gateways', array( $this, 'add_gateways' ) );
146
  add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
147
 
148
+ // Modify emails emails.
149
+ add_filter( 'woocommerce_email_classes', array( $this, 'add_emails' ), 20 );
150
+
151
  if ( version_compare( WC_VERSION, '3.4', '<' ) ) {
152
  add_filter( 'woocommerce_get_sections_checkout', array( $this, 'filter_gateway_order_admin' ) );
153
  }
259
 
260
  return $sections;
261
  }
262
+
263
+ /**
264
+ * Adds the failed SCA auth email to WooCommerce.
265
+ *
266
+ * @param WC_Email[] $email_classes All existing emails.
267
+ * @return WC_Email[]
268
+ */
269
+ public function add_emails( $email_classes ) {
270
+ require_once WC_STRIPE_PLUGIN_PATH . '/includes/compat/class-wc-stripe-email-failed-authentication.php';
271
+ require_once WC_STRIPE_PLUGIN_PATH . '/includes/compat/class-wc-stripe-email-failed-renewal-authentication.php';
272
+ require_once WC_STRIPE_PLUGIN_PATH . '/includes/compat/class-wc-stripe-email-failed-preorder-authentication.php';
273
+ require_once WC_STRIPE_PLUGIN_PATH . '/includes/compat/class-wc-stripe-email-failed-authentication-retry.php';
274
+
275
+ // Add all emails, generated by the gateway.
276
+ $email_classes['WC_Stripe_Email_Failed_Renewal_Authentication'] = new WC_Stripe_Email_Failed_Renewal_Authentication( $email_classes );
277
+ $email_classes['WC_Stripe_Email_Failed_Preorder_Authentication'] = new WC_Stripe_Email_Failed_Preorder_Authentication( $email_classes );
278
+ $email_classes['WC_Stripe_Email_Failed_Authentication_Retry'] = new WC_Stripe_Email_Failed_Authentication_Retry( $email_classes );
279
+
280
+ return $email_classes;
281
+ }
282
  }
283
 
284
  WC_Stripe::get_instance();