Skip to main content

WP, WC mail failed payment not sent. Payment gateway WC-Paypal, Yith-Stripe

How to test mail without wait ab 1 days, 2 days ?

The problem could be in payment gateway, not only WC.
https://docs.woocommerce.com/document/email-faq/
Standard Paypal troubleshoot: https://docs.woocommerce.com/document/paypal-standard/#section-20
Is  WC Paypal is default ?

Debug, not show error on site:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Log dump to wp-content/debug.log

There are many useful information about sending and receive email here. For example, addition information about Gmail free account limit up to 500 unique recipient in 24h period.
https://support.google.com/mail/answer/22839?hl=en

https://en-gb.wordpress.org/plugins/wp-mail-logging/

wp-content/plugins/post-smtp/Postman/Postman-Mail/
wp-mail-smtp

Suggested Dedicated SMTP Providers
Like Mailgun (plugin), Maildrill (plugin). Use this way we can test on Dev server instead of real Hosting like WPEngine. Dev server often lack of these Wordpress mail function (or server ?).
Certainly, this way introduce new bug, case that WPE itself mail function has problem. So be careful and clearly on this process.
There are many other issues that prevent we debug on staging/test server. For example, staging site is shared/used by many team (often two team, outsource + customer), so if we install some plugin, for send mail Mailgun, WP mail logging... or put some code debug to these server. Then it could be affect other test case, functions, generate many trash data to database...

Paypal standard
https://docs.woocommerce.com/document/paypal-standard/#section-20
WP mail
https://developer.wordpress.org/reference/functions/wp_mail/

https://www.flippercode.com/send-html-emails-using-wp-mail-wordpress/

https://stackoverflow.com/questions/39402755/simulate-stripe-subscription-renewal-failure

https://formidableforms.com/help-desk/failed-paypal-payment-email-notification-not-sent/

Update sub info, ie. end trial time or change default source (card):
We can not update trial end by Stripe Panel because of we are using error / invalid card for negative testing. When click save, error will occur.

curl https://api.stripe.com/v1/subscriptions/sub_D74qAwwXdb2jH6 \
   -u sk_test_uujpTqjYZx2eF4J66B5278H0: \
   -d tax_percent=10

Run update desc first to get current state:
Some thing like this
curl https://api.stripe.com/v1/customers/cus_D7RMbSXum154x3 \
   -u sk_test_uujpTqjYZx2eF4J66B5278H0 \
   -d description="Get detail customer"

From output result. Copy to editor then find the plan id (or subscription ID). Something like this:
product_50214_80c8d6bd8df23d7888bbd792f6d2a5f4
If there are many plan for current customer, it may be show many, show you have to find exactly subscription that currently work on.

Update trial_end: First we get current UNIX time, then add some amount to future time.
This unix time is in seconds from 1970 so 2 mins ~ + 120 in value.

$ date
Tue Jun 26 08:59:09 BST 2018
$ date -d 'Tue Jun 26 08:59:09 BST 2018' +"%s"
1529999949
date -d @1529999949
Tue Jun 26 08:59:09 BST 2018
We added ab 100s to current time, then set this value for payment plan trial_end.

        /**
         * Method triggered to send email
         *
         * @param int $subscription <<<<<<----------------- Bug here
         *
         * @return void
         * @since  1.0
         * @author Emanuela Castorina <emanuela.castorina@yithemes.com>
         */
        public function trigger( $subscription ) {

            $failed_attemps = $subscription->has_failed_attemps();     // :( :( :( call function on integer ?
            if( $failed_attemps['num_of_failed_attemps'] >= $failed_attemps['max_failed_attemps']){
             return;
            }

            $this->recipient = $subscription->get_billing_email();
         
            // Check if this email type is enabled, recipient is set
            if ( !$this->is_enabled() || !$this->get_recipient() ) {
                return;
            }

            $this->object = $subscription;

            $this->template_variables = array(
                'subscription'  => $this->object,
                'email_heading' => $this->get_heading(),
                'sent_to_admin' => false,
                'email'         => $this
            );
         
       


            $return = $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content_html(), $this->get_headers(), $this->get_attachments( ) );
        }

        /**
         * Get HTML content for the mail
         *
         * @return string HTML content of the mail
         * @since  1.0
         * @author Emanuela Castorina <emanuela.castorina@yithemes.com>
         */
        public function get_content_html() {
            ob_start();
            wc_get_template( $this->template_html, $this->template_variables, '', YITH_YWSBS_TEMPLATE_PATH.'/'  );     // reed + emails/
            return ob_get_clean();
        }

For trigger() function, after more investigation, we see that it not a big defect.
In the line: $this->object = $subscription; the data needed ($subscription) for email send is set and have correct value.
It is something magical since I do not have good experience in OOP.
The function call get_billing_email() is randomly return null, it can be caused by subscription data error.

 60 $failed_attemps = $subscription->has_failed_attemps();
 61 echo @$failed_attemps['max_failed_attempts']; 
 62 echo (!$failed_attemps['max_failed_attemps']) ? 'null max-attempt' : $failed_attemps['max_failed_attemps']. ' <- max attempt ';

Paypal negative testing
https://developer.paypal.com/docs/api/test-values/#negative-testing-steps
List PP test acc:
https://developer.paypal.com/developer/accounts

Get Paypal access token (sandbox).
https://stackoverflow.com/questions/8961544/how-do-i-get-identity-token-in-paypal-sandbox#
What ? PDT I have removed this value from staging.

How to get PP access token
https://www.paypal.com/vn/selfhelp/article/how-do-i-get-an-access-token-ts2128
For basic account, it require you have to activate (or upgrade ?) account to premier (?).

Find files modified within 24h
find . -mtime -1 -ls

Useful when not use version control when debugging new app.


if ( ! function_exists( 'ywsbs_get_max_failed_attemps_list' ) ) {

/**
* Return the list of max failed attempts for each compatible gateways
*
* @return array
*/

function ywsbs_get_max_failed_attemps_list() {
$arg = array(
'paypal'      => 3,
'yith-stripe' => 4
);

return apply_filters( 'ywsbs_max_failed_attemps_list', $arg );
}

}

if ( ! function_exists( 'ywsbs_get_num_of_days_between_attemps' ) ) {

/**
* Return the list of max failed attemps for each compatible gateways
*
* @return array
*/

function ywsbs_get_num_of_days_between_attemps() {
$arg = array(
'paypal'      => 5,
'yith-stripe' => 5
);

return apply_filters( 'ywsbs_get_num_of_days_between_attemps', $arg );
}

}

https://developer.paypal.com/docs/subscriptions/integrate/integrate-steps/#1-create-a-plan
https://www.sandbox.paypal.com/activity/payment/6F615230BA940920E

Paypal API credentials
https://developer.paypal.com/webapps/developer/docs/classic/api/apiCredentials/#create-an-api-signature
Show sandbox key
https://www.sandbox.paypal.com/businessprofile/mytools/apiaccess/firstparty/signature
https://developer.paypal.com/docs/api/overview/#make-your-first-call

PDT alternative for IPN
PDT need enable auto redirect in Pappal app (?)
https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNPDTAnAlternativetoIPN/#when-to-use-pdt

Doc
https://www.paypal.com/us/smarthelp/article/FAQ1211

Curl get Paypal API token, list billing plan, update plan ...
curl  https://api.sandbox.paypal.com/v1/oauth2/token \
   -H "Accept: application/json" \
   -H "Accept-Language: en_US" \
   -u "AVgziRvB-5OIMr33j6oMNF3LxlJQ63s3iXwwGIVX1P6BQsqqHFyxzEk61zXWC0nt2mPP80ltitTp****:EJIpT43Qhv9s3XFz8aU3npY-nUReZErQJ-cBx5dm1shVvkT3oXHDni1X7PVAkDrfKkgt8Vrvw4qo****"
   -d "grant_type=client_credentials"

Client_ID and Client_Secret get in Dashboard > App.
But my WooCommerce Paypal not use/set App. It look like this:
AVgziRvB-5OIMr33j6oMNF3LxlJQ63s3iXwwGIVX1P6BQsqqHFyxzEk61zXWC0nt2mPP80ltitTplWEf
EJIpT43Qhv9s3XFz8aU3npY-nUReZErQJ-cBx5dm1shVvkT3oXHDni1X7PVAkDrfKkgt8Vrvw4qoNi-E

It look longer than API username/password in merchant sandbox account:
mtischer53+rambo2_api1.gmail.com
Password:
TZ3REF9R8ZL*****
Signature:
AxBU5pnHF6qNArI7Nt5yNqy4EgGWAFKL6fIGO1GcedGlh8Oig5lM****

Cliest_ID, secret get from https://developer.paypal.com/developer/applications ...
Signature API get from sandbox.... (for test accont)

curl -v -X PATCH https://api.sandbox.paypal.com/v1/payments/billing-plans/I-74VW5D5L8SBT/ \
-H "Content-Type:application/json" \
-H "Authorization: Bearer A21AAHOEfPqgJSmXMRyQguYpSdeVk2QVdbpNc2qefM674qCDGvYzISTTNYvgRhRx4nKx2eRuqhop4-BqrH28Am25aqWibG1-Q" \
-d '[{
  "op": "replace",
  "path": "/",
  "value": {
    "state": "ACTIVE"
  }
}]'

Get billing plan
curl -v -X GET https://api.sandbox.paypal.com/v1/payments/billing-plans?page_size=3&status=ALL&page_size=2&page=1&total_required=yes \
-H "Content-Type: application/json" \
-H "Authorization: Bearer A21AAHOEfPqgJSmXMRyQguYpSdeVk2QVdbpNc2qefM674qCDGvYzISTTNYvgRhRx4nKx2eRuqhop4-BqrH28Am25aqWibG1-Q"

Run on Linux terminal, it has problem with -H option, like other previous issue with -v (--verbose) options. With -v I simple remove this option and run success but -H I can not remove it because of it is required.
So I use Postman to send:
URL: GET: https://api.sandbox.paypal.com/v1/payments/billing-plans
(Noted that I have remove all GET params, don't remember why these param make error.)
And in Header put data here:
Content-Type => application/json
Authorization (?} => Bearer <API-Token> ...

Result:
{"plans":[{"id":"P-62D334156T228444RL4AIVSI","state":"CREATED","name":"Plan with Regular and Trial Payment Definitions","description":"Plan with regular and trial payment definitions.","type":"FIXED","create_time":"2018-07-03T07:17:08.425Z","update_time":"2018-07-03T07:17:08.425Z","links":[{"href":"https://api.sandbox.paypal.com/v1/payments/billing-plans/P-62D334156T228444RL4AIVSI","rel":"self","method":"GET"}]}],"links":[{"href":"https://api.sandbox.paypal.com/v1/payments/billing-plans?page_size=10&page=0&start=1&status=CREATED","rel":"start","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/payments/billing-plans?page_size=10&page=0&status=CREATED","rel":"last","method":"GET"}]}

Here is the plan data that I have just created using Curl call API.
=> It seem that WooCommerce YITH Subscription billing is not a billing plan ? Since it's ID is subscr_id ?
I can not find any Plan ID yet, API call get list plan above return empty before. It only show 1 rocord after I manually create it.
So I can not use Negative testing flow of Stripe using trial time to trigger email send when end trial time.

Create a plan:
Creat a plan
curl -v -X POST https://api.sandbox.paypal.com/v1/payments/billing-plans/ \
-H "Content-Type:application/json" \
-H "Authorization: Bearer A21AAHOEfPqgJSmXMRyQguYpSdeVk2QVdbpNc2qefM674qCDGvYzISTTNYvgRhRx4nKx2eRuqhop4-BqrH28Am25aqWibG1-Q" \
-d '{
  "name": "Plan with Regular and Trial Payment Definitions",
  "description": "Plan with regular and trial payment definitions.",
  "type": "fixed",
  "payment_definitions": [
  {
    "name": "Regular payment definition",
    "type": "REGULAR",
    "frequency": "MONTH",
    "frequency_interval": "2",
    "amount":
    {
      "value": "100",
      "currency": "USD"
    },
    "cycles": "12",
    "charge_models": [
    {
      "type": "SHIPPING",
      "amount":
      {
        "value": "10",
        "currency": "USD"
      }
    },
    {
      "type": "TAX",
      "amount":
      {
        "value": "12",
        "currency": "USD"
      }
    }]
  },
  {
    "name": "Trial payment definition",
    "type": "trial",
    "frequency": "week",
    "frequency_interval": "5",
    "amount":
    {
      "value": "9.19",
      "currency": "USD"
    },
    "cycles": "2",
    "charge_models": [
    {
      "type": "SHIPPING",
      "amount":
      {
        "value": "1",
        "currency": "USD"
      }
    },
    {
      "type": "TAX",
      "amount":
      {
        "value": "2",
        "currency": "USD"
      }
    }]
  }],
  "merchant_preferences":
  {
    "setup_fee":
    {
      "value": "1",
      "currency": "USD"
    },
    "return_url": "https://example.com/return",
    "cancel_url": "https://example.com/cancel",
    "auto_bill_amount": "YES",
    "initial_fail_amount_action": "CONTINUE",
    "max_fail_attempts": "0"
  }
}'

About plan-id vs subscr-id, may be WC Yith sub do not require Paypal App, and/or my set up environmont not correct so subscr product from WC not sync/send to Paypal...

    It seem that event I use failed payment credit card linked to Paypal sandbox account, the renew order still in success status. But in WC subscr log, it show max_failed_attempt excess, and the subscr status change to suspended.



   Cool Github wiki page for WC: https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
After few month work with WP, I still feel unclear ab hook, filter, OOP, or mechanism that the data flow handler work.
For example the way that return query result in WC_Order_Query:
class WC_Order_Query extends WC_Object_Query {

/**
* Valid query vars for orders.
*
* @return array
*/
protected function get_default_query_vars() {
return array_merge(
parent::get_default_query_vars(),
array(
'status'               => array_keys( wc_get_order_statuses() ),
'type'                 => wc_get_order_types( 'view-orders' ),
'currency'             => '',
'version'              => '',
'prices_include_tax'   => '',
...
'cart_tax'             => '',
'total'                => '',
'total_tax'            => '',
'customer'             => '',
'customer_id'          => '',
'order_key'            => '',...
'payment_method'       => '',
'payment_method_title' => '',
'transaction_id'       => '',
'customer_ip_address'  => '',
'customer_user_agent'  => '',
'created_via'          => '',
'customer_note'        => '',
)
);
}

/**
* Get orders matching the current query vars.
*
* @return array|object of WC_Order objects
*/
public function get_orders() {
$args    = apply_filters( 'woocommerce_order_query_args', $this->get_query_vars() );
$results = WC_Data_Store::load( 'order' )->query( $args );
return apply_filters( 'woocommerce_order_query', $results, $args );
}
}

    Query get mixed subscr and orders:
//Subscr
SELECT   wp_posts.* FROM wp_posts  INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1  AND (    ( wp_postmeta.meta_key = 'user_id' AND wp_postmeta.meta_value = '6' ) ) AND wp_posts.post_type = 'ywsbs_subscription' AND ((wp_posts.post_status = 'publish')) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC;

// Orders
SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1  AND (
  (
    ( wp_postmeta.meta_key = '_customer_user' AND wp_postmeta.meta_value IN ('6') )
  )
) AND wp_posts.post_type IN ('shop_order', 'shop_order_refund') AND ((wp_posts.post_status = 'wc-pending' OR wp_posts.post_status = 'wc-processing' OR wp_posts.post_status = 'wc-on-hold' OR wp_posts.post_status = 'wc-completed' OR wp_posts.post_status = 'wc-cancelled' OR wp_posts.post_status = 'wc-refunded' OR wp_posts.post_status = 'wc-failed')) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 10, 10

// full post data
SELECT wp_posts.* FROM wp_posts  INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1  AND (
  (
    ( wp_postmeta.meta_key = '_customer_user' AND wp_postmeta.meta_value IN ('6') )
  )
) AND wp_posts.post_type IN ('shop_order', 'shop_order_refund') AND ((wp_posts.post_status = 'wc-pending' OR wp_posts.post_status = 'wc-processing' OR wp_posts.post_status = 'wc-on-hold' OR wp_posts.post_status = 'wc-completed' OR wp_posts.post_status = 'wc-cancelled' OR wp_posts.post_status = 'wc-refunded' OR wp_posts.post_status = 'wc-failed')) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 10, 10

I can mix two query to one. After that I have to update select columns, data for render both subscr and orders. Pagination, offset ... can be pass as variable.
Sub query to calculate another related order, subscr data seem to be not hard.
Most heavy query are in order get.

WC_Meta_Data to detect order related to a subscription.

Sort two ordered array by date-time:
// Compare [array] subscription date created and [object] order date created
function compare_subscr_order($subscr, $order) {
$sub_created = strtotime($subscr['post_date']);
$order_created = strtotime($order['post_date']);

return ( $order_created - $sub_created ) > 0 ? 1 : -1;
}

$subscr_and_orders = array_merge($subscr_arr, $pay_in_full_orders);
usort($subscr_and_orders, 'compare_subscr_order');
// usort($subscr_and_orders, function ($subscr, $order) {
// $sub_created = strtotime($subscr['post_date']);
// $order_created = strtotime($order['post_date']);
// print_r($sub_created);
// print_r($order_created);
// echo "<br\> \n";
// // die;

// return ($order_created - $sub_created > 0) ? 1 : -1;
// });

// print_r($subscr_and_orders);
// die;

    Testwp: #89 sub, Credit => After suspended, failed payment email still send. It can be stop (cancelled) when enable suspend/cancel subscr when failed payment 1 or some time ?
Interestingly the failed attempt on subscr dashboard not count, it still 1/4. So may be I have edited some line and forgot to restore back to original ?
   I have edit something on Yith-subscr but do not remember.
1133                         if ( $is_paypal_ec ) {
1134                                 $failed_attempts       = get_post_meta( $renew_order, 'failed_attemps', true ) ? get_post_meta( $renew_order, 'failed_attemps', true ) : 0;
1135                                 $next_payment_attempt = get_post_meta( $renew_order, 'next_payment_attempt', true );
1136                         } else {
1137                                 $failed_attempts       = get_post_meta( $order_id, 'failed_attemps', true ) ? get_post_meta( $order_id, 'failed_attemps', true ) : 0;
1138                                 $next_payment_attempt = get_post_meta( $order_id, 'next_payment_attempt', true );
1139                         }


1270                 /**
1271                  * Add failed attemp
1272                  *
1273                  * @param bool $attempts
1274                  * @param bool $latest_attemp if is the last attemp doesn't send email
1275                  *
1276                  * @since  1.1.3
1277                  * @author Emanuela Castorina <emanuela.castorina@yithemes.com>
1278                  */
1279                 public function register_failed_attemp( $attempts = false, $latest_attemp = false ) {
1280 
1281                         $order = wc_get_order( $this->order_id );
1282 
1283                         if ( false === $attempts ) {
1284                                 $failed_attemp = yit_get_prop( $order, 'failed_attemps' );
1285                                 $attempts      = intval( $failed_attemp ) + 1;
1286                         }
1287 
1288                         if ( ! $latest_attemp ) {
1289                                 YITH_WC_Activity()->add_activity( $this->id, 'failed-payment', 'success', $this->order_id, sprintf( __( 'Failed payment for order %d', 'yith-woocommerce-subscription' ), $this     ->order_id ) );
1290                                 yit_save_prop( $order, 'failed_attemps', $attempts, false, true );
1291                                 do_action( 'ywsbs_customer_subscription_payment_failed_mail', $this );
1292                         }
1293                 }

Addition WC PP doc:
https://docs.woocommerce.com/document/debug-subscriptions-paypal-ipn-issues/

Default behavior WC-Subscr (Free version) buy simple subscription.
Cancel ( Canceling here will not cancel from PayPal. Need to cancel by customer from PayPal dashboard under pre-approved payments )

IPN msg detail:

payment_cycle=Daily&txn_type=recurring_payment_suspended_due_to_max_failed_payment&last_name=Joe&next_payment_date=N/A&residence_country=US&initial_payment_amount=0.00&rp_invoice_id=WC-57982&currency_code=AUD&time_created=19:30:28 Jul 02, 2018 PDT&verify_sign=AOwIQopgbiAehe.uJ6zuI9Yn4zSEA4up3vsnWrx-ngfafN5pqvR-fiq-&period_type= Regular&payer_status=unverified&test_ipn=1&tax=0.00&payer_email=mtischer53+rambo@gmail.com&first_name=Rambo&receiver_email=mtischer53+rambo2@gmail.com&payer_id=GLRGTSHEHCZSG&product_type=1&payer_business_name=Rambo Joe's Test Store&shipping=0.00&amount_per_cycle=15.00&profile_status=Suspended&custom={"order_id":57982,"order_key":"wc_order_5b3adf96099b7"}&charset=windows-1252&notify_version=3.9&amount=15.00&outstanding_balance=15.00&recurring_payment_id=I-

Start Date 1 hour ago
Next payment date => empty (may be because of I set plan only 1 day) YYYY-MM-DD@HH:MM
So end date is tomorow:
Next Payment: YYYY-MM-DD@HH:MM

End Date: 2018-07-06@05

   Strange txn_type: recurring_payment_failed; No result on any plugin contain this type.
amount=15.00&initial_payment_amount=0.00&profile_status=Suspended&payer_id=GLRGTSHEHCZSG&product_type=1&ipn_track_id=694e29608d517&outstanding_balance=15.00&shipping=0.00&charset=windows-1252&period_type= Regular&currency_code=AUD&verify_sign=A98n6GS5SOpIIPySL9NVnTF09ihiAbBpgOVeJIq-OYgn1IjN0O7388C8&test_ipn=1&payment_cycle=Daily&txn_type=recurring_payment_failed&custom={"order_id":57982,"order_key":"wc_order_5b3adf96099b7"}&payer_status=unverified&first_name=Rambo&product_name=Introduction to Statistics - Payment Plan&amount_per_cycle=15.00&rp_invoice_id=WC-57982&last_name=Joe&payer_business_name=Rambo Joe's Test Store&time_created=19:30:28 Jul 02, 2018 PDT&resend=true&notify_version=3.8&recurring_payment_id=I-74VW5D5L8SBT&payer_email=mtischer53+rambo@gmail.com&receiver_email=mtischer53+rambo2@gmail.com&next_payment_date=N/A&tax=0.00&residence_country=US

https://stackoverflow.com/questions/16276622/negative-testing-on-paypal-sandbox-no-more-available

    SOAP/NVP (?) sandbox test API:
In Yith: yith-wc-pp-premium/includes/gateways/paypal/class.yith-wc-subscription-paypal.php  line 96
$this->api_endpoint  = ( $settings['testmode'] == 'yes' ) ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp';

YITH Stripe failed attempt not increase.
/**
* Add failed attemp
*
* @param bool $attempts
* @param bool $latest_attemp if is the last attemp doesn't send email
*
* @since  1.1.3
* @author Emanuela Castorina <emanuela.castorina@yithemes.com>
*/
public function register_failed_attemp( $attempts = false, $latest_attemp = false ) {

$order = wc_get_order( $this->order_id );

if ( false === $attempts ) {
$failed_attemp = yit_get_prop( $order, 'failed_attemps' );
$attempts      = $failed_attemp + 1;
}

if ( ! $latest_attemp ) {
YITH_WC_Activity()->add_activity( $this->id, 'failed-payment', 'success', $this->order_id, sprintf( __( 'Failed payment for order %d', 'yith-woocommerce-subscription' ), $this->order_id ) );
yit_save_prop( $order, 'failed_attemps', $attempts, false, true );
do_action( 'ywsbs_customer_subscription_payment_failed_mail', $this );
}
}

So failed_attempt only increase in case do not pass $attempts value (Paypal use this way).
Stripe pass value so it seem never increase and lead to failed email sent continuosly.
../yith-woocommerce-stripe-premium/includes/class-yith-stripe-webhook.php:415:                                  $subscription->register_failed_attemp( $invoice->attempt_count );

./includes/gateways/paypal/includes/class.ywsbs-paypal-ipn-handler.php:430:                                                                     $subscription->register_failed_attemp();
./includes/gateways/paypal/includes/class.ywsbs-paypal-ipn-handler.php:518:                                                             $subscription->register_failed_attemp();
./includes/gateways/paypal/includes/class.ywsbs-paypal-ipn-handler.php:586:                                                             $subscription->register_failed_attemp();

http://benfoster.io/blog/stripe-failed-payments-how-to
https://blog.statsbot.co/how-to-reduce-churn-rate-by-27-by-handling-stripe-failed-payments-e75892f8956c => business aspect


   Some other useful information ab Stripe: https://groups.google.com/a/lists.stripe.com/forum/#!topic/api-discuss/qzp5JrqgYI4
" Hey Chris,

You don't have to wait in that situation. You should open a separate Test account and set the Recurring settings [1] to mark the subscription as unpaid after the first failed attempt. Make sure it's not after a retry and really as soon as it fails. You then follow the usual flow to trigger a failure [2] and force the subscription to be unpaid immediately. This works with any plan not just weekly.

The idea is that you create a customer with the card "4000 0000 0000 0341" and then you create a subscription and pass `trial_end` set as a timestamp a few minutes in the future. This will create a trial period for a few minutes, then the subscription will renew automatically. This will create an invoice that will be attempted one or two hours later. It will fail and the subscription will move to Unpaid as needed. You can also force the invoice to be paid quicker either by clicking "Pay now" in the dashboard or using the Pay Invoice API [3].

Hope this helps!
Remi

[1] https://dashboard-admin.stripe.com/account/recurring
[2] https://support.stripe.com/questions/test-failed-invoice-payment
[3] https://stripe.com/docs/api#pay_invoice
"
https://groups.google.com/a/lists.stripe.com/forum/#!forum/api-discuss
Webhook cheatsheet:
https://www.masteringmodernpayments.com/stripe-webhook-event-cheatsheet

 mysql> select * from wp_postmeta where meta_key like 'failed_attemps';

    Why subscription daily do not stop send failed payment ?
It seem that Stripe will try maximum 3 day before it increase failed payment from1 to 2.
https://dashboard.stripe.com/account/recurring
So daily payment subscr seem never end send failed email ?
http://blog.stunning.co/quick-tip-stripe-attempts-to-pay-invoices-up-to-4-times-not-3/

Comments

  1. Hi! I am Thilde Carlsson, reachable all the day and even in nights to offer the most excellent solution to our clients associated with their Outlook Support Services. Just make a call at Is there a phone number for Outlook support? And make connection with me. With my whole experience and knowledge in this field, I will surely offer you the step-wise solution in an efficient manner.
    https://www.microsoft-supportnumber.com/microsft-outlook-support

    ReplyDelete

Post a Comment

Popular posts from this blog

Rand mm 10

https://stackoverflow.com/questions/2447791/define-vs-const Oh const vs define, many time I got unexpected interview question. As this one, I do not know much or try to study this. My work flow, and I believe of many programmer is that search topic only when we have task or job to tackle. We ignore many 'basic', 'fundamental' documents, RTFM is boring. So I think it is a trade off between the two way of study language. And I think there are a bridge or balanced way to extract both advantage of two method. There are some huge issue with programmer like me that prevent we master some technique that take only little time if doing properly. For example, some Red Hat certificate program, lesson, course that I have learned during Collage gave our exceptional useful when it cover almost all topic while working with Linux. I remember it called something like RHEL (RedHat Enterprise Linux) Certificate... I think there are many tons of documents, guide n books about Linux bu

Martin Fowler - Software Architecture - Making Architecture matter

  https://martinfowler.com/architecture/ One can appreciate the point of this presentation when one's sense of code smell is trained, functional and utilized. Those controlling the budget as well as developer leads should understand the design stamina hypothesis, so that the appropriate focus and priority is given to internal quality - otherwise pay a high price soon. Andrew Farrell 8 months ago I love that he was able to give an important lesson on the “How?” of software architecture at the very end: delegate decisions to those with the time to focus on them. Very nice and straight-forward talk about the value of software architecture For me, architecture is the distribution of complexity in a system. And also, how subsystems communicate with each other. A battle between craftmanship and the economics and economics always win... https://hackernoon.com/applying-clean-architecture-on-web-application-with-modular-pattern-7b11f1b89011 1. Independent of Frameworks 2. Testable 3. Indepe