简体   繁体   中英

Stripe billing Subscription not charging the “subsequent charges” after initial successful Payment

I have followed this documentation to implement the Stripe Recurring Payments, which is working fine for both non-3d-Secure card and 3d-Secure enabled cards. For initial payments i get the 3d-Secure Payment authentication Modal from Stripe to authenticate payments using 3D-Secure Test cards.

The subscription is created along with invoices that Stripe generates for me. Everything's seems to be OK, BUT for 3D-Secure Enabled (Stripe Testing Cards) the First Payment goes through successfully but the subsequent payments which i have set interval of 1Day (for testing) are "Failed" by Stripe. From what i understand Stripe requires Customer's Authentication again to continue the Subscription.


In this they said

When 3D Secure is encountered, configure your billing settings to send a hosted link to your customer to complete the flow.

That's completely OK, But why they even require the authentication step again if the first payment is succeeded and the subscription is started? I have gone through all the related Docs but found nothing to prevent that.

What if Customer does not check his/her email to come back to my Application to authenticate the payments? OR

Is there anything wrong with the code? By that i mean that Stripe should have saved the Payment Method ie Card Number so it does not require the Customer Authentication on subsequent charges every time the new billing cycle start(monthly/biannually).

I have searched on internet and found and answer saying

You are using stripe.createToken which does not support 3ds authentication. You need to migrate your client side code to stripe.createPaymentMethod which does support it

Is that it? Any response is appreciated

My Payment.js

// set your stripe publishable key
var stripe = Stripe('pk_test_key');
var elements = stripe.elements();
var style = {
    base: {
        color: '#32325d',
        lineHeight: '18px',
        fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
        fontSmoothing: 'antialiased',
        fontSize: '16px',
        '::placeholder': {
            color: '#aab7c4'
        }
    },
    invalid: {
        color: '#fa755a',
        iconColor: '#fa755a'
    }
};

var cardNumber = elements.create('cardNumber', {
    style: style
});
cardNumber.mount('#cardNumber');
var cardExpiry = elements.create('cardExpiry', {
    style: style
});
cardExpiry.mount('#cardExpiry');
var cardCvc = elements.create('cardCvc', {
    style: style
});
cardCvc.mount('#cardCVC');

var cardholderName = $('#custName').val();
var amount = $('#amount').val();

var cardButton = document.getElementById('makePayment');
cardButton.addEventListener('click', function(ev) {
     // alert();
    ev.preventDefault();




    stripe.createToken(cardNumber).then(function(result) {

    if (result.error) {


    } else {
      //$body.addClass("loading");
      fetch('process.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ 
            token_id: result.token.id          
            })
      }).then(function(result) {


        // Handle server response (see Step 3)
        result.json().then(function(json) {
          handleServerResponse(json);
          //alert();
        })
      });
    }
  });
});
function handleServerResponse(response) {
            if (response.error) {
                // Show error from server on payment form
            } else if (response.requires_action) {
                // Use Stripe.js to handle required card action
                var action = response.next_action;
                if (action && action.type == 'redirect_to_url') {
                    window.location = action.redirect_to_url.url;
                }

                handleAction(response);

            } else {
                console.log("3D" + response);
            }
}

        function handleAction(response) {
           var paymentIntentSecret = response.payment_intent_client_secret;
            stripe.handleCardPayment(paymentIntentSecret).then(function(result) {
              if (result.error) {
                // Display error.message in your UI.
              } else {
                // The payment has succeeded. Display a success message.
                alert('Payment succeeded!');
              }
});

}

Process.php

<?php 

require_once('stripe-php/init.php');   
\Stripe\Stripe::setApiKey('sk_test_key');

$json_str = file_get_contents('php://input');

$json_obj = json_decode($json_str);
 //print_r($json_obj->token_id);die;

  //$intent = null;
try {
    if (isset($json_obj->token_id)) {
     //Create Customer   
    $customer = \Stripe\Customer::create([
    "email" => "test@gmail.com",
    "name" => "Haroon",  
    "source" => $json_obj->token_id,
]);
    //create product
    $product = \Stripe\Product::create([
    'name' => 'Water',
    'type' => 'service',
]);
    //create a plan
    $plan = \Stripe\Plan::create([
      'product' => $product->id,
      'nickname' => 'Water',
      'interval' => 'day',
      'currency' => 'eur',
      'amount' => 1200.00,
    ]);   

    //add subscription to stripe
    $subscription = \Stripe\Subscription::create([

        'customer' => $customer->id,
        'items' => [[
        "plan" => $plan->id],],
        'expand' => ['latest_invoice.payment_intent'],
]);    
}

      $intent = \Stripe\PaymentIntent::retrieve($subscription->latest_invoice->payment_intent->id);

      $subscription = \Stripe\Subscription::retrieve($subscription->id);
    //   $intent->confirm();
    // }
    generatePaymentResponse($intent,$subscription);
  }catch (\Stripe\Error\Base $e) {
    # Display error on client
    echo json_encode([
      'error' => $e->getMessage()
    ]);
  }  

   function generatePaymentResponse($intent,$subscription) {
    if ($intent->status == 'requires_action' && $subscription->status == 'incomplete' &&
        $intent->next_action->type == 'use_stripe_sdk' ) {
        # Tell the client to handle the action

        echo json_encode([
            'requires_action' => true,
            'payment_intent_client_secret' => $intent->client_secret
        ]);
    } else if ($intent->status == 'succeeded' && $subscription->status == 'active') {

        # The payment didn’t need any additional actions and completed!
        # Handle post-payment fulfillment
        echo json_encode([
            'success' => true
        ]);
    } else if ($intent->status == 'requires_payment_method' && $subscription->status == 'incomplete') {
        echo "Subscription failed";

    } else {
        # Invalid status
        http_response_code(500);
        echo json_encode(['error' => 'Invalid PaymentIntent status']);
    }
}

If you are doing this for already exist card then i have done this just before some day . Refer this document https://stripe.com/docs/payments/3d-secure .

This suggest that you need to create paymentIntent and need to confirm this payment intent . and also in 3ds there are test card 4242 4242 4242 4242 This card does not require any authentication for 3ds . Giving below link for test cards

https://stripe.com/docs/payments/3d-secure/web#three-ds-cards

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM