3
votes

I am using the PaymentIntent API to integrate Stripe payments using stripe-php SDK and Stripe.js V3. Following This guide https://stripe.com/docs/payments/payment-intents/migration#saving-cards-checkout. I am getting what successful payments in my Stripe Dashboard done with test cards which do not require 3d-secure. But The Stripe's new SCA 3d secure Popup(according to their docs.) is not popping up, Which leads payments done with 3dsecure ENABLED cards to "Incomplete Payments" Tab in stripe Dashboard.

I have examine the code thoroughly multiple times and tested. I have noticed that my code skips(somtimes) OR throws an error "Unexpected end of JSON input" in the "Fetch Part" on the client side code.. which leads the 3d-secure cards payments to be incomplete.The JavaScript Fetch function is not fetching the "payment_method_id" from the specified file(url).

My Payment.js File:

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();

$(document).ready(function () {
    $("#paymentForm").submit(function (event) {
        //event.preventDefault();

        stripe.createPaymentMethod('card', cardNumber, {
            billing_details: {name: cardholderName.value}
        }).then(function (result) {
            console.log(result);

            if (result.error) {
                var errorElement = document.getElementById('card-error');
                errorElement.textContent = result.error.massage;

            } else {
                 stripeTokenHandler(result);

                fetch('example.com/stripe/index.php', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        payment_method_id: result.paymentMethod.id
                    })
                }).then(function (result) {
                    // Handle server response (see Step 3)
                    result.json().then(function (result) {
                        handleServerResponse(result);
                    })
                });
            }
        });   
        //return false;
    });
 function stripeTokenHandler(result) {
            var payForm = $("#paymentForm");
            var paymentMethodID = result.paymentMethod.id;
            //set the token into the form hidden input to make payment
            payForm.append("<input type='hidden' name='payment_method_id' value='" + paymentMethodID + "' />");
            //  payForm.submit();   
             payForm.submit();
        }
}

My Index.php File

header('Content-Type: application/json');
if(isset($_POST['submit'])){

    //include Stripe PHP library
    require_once('stripe-php/init.php');    
   //set Stripe Secret Key
\Stripe\Stripe::setApiKey('sk_test_key');

    //add customer to stripe
    $customer = \Stripe\Customer::create(array(
        'email' => $custEmail,
    ));    

function generatePaymentResponse($intent) {
    if ($intent->status == 'requires_action' &&
        $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') {        
# The payment didn’t need any additional actions and completed!
        # Handle post-payment fulfillment
        echo json_encode([
            'success' => true
        ]);
    } else {
        # Invalid status
        http_response_code(500);
        echo json_encode(['error' => 'Invalid PaymentIntent status']);
    }
}
# retrieve json from POST body
  $json_str = file_get_contents('php://input');
  $json1 = json_encode($_POST);
  $json_obj = json_decode($json1);

  $intent = null;

try {
    if (isset($json_obj->payment_method_id)) {
    $intent = \Stripe\PaymentIntent::create([
    'payment_method' => $json_obj->payment_method_id,
    'customer' => $customer->id,
    'amount' => 1099,
    'currency' => 'gbp',
    'confirmation_method' => 'manual',
    'confirm' => true,

]);
}
    generatePaymentResponse($intent);

  }catch (\Stripe\Error\Base $e) {
    # Display error on client
    echo json_encode([
      'error' => $e->getMessage()
    ]);
  }


}
?>

As it can be seen my stripeTokenHandler is appending the payment_method.id into HTML form and the process goes on. but the "fetch" section of JS code should get payment_method_id to generate "Response" and to proceed for "next_action" if the payment status is "requires_action".

1
What are the contents of the JSON when you are seeing this error? A stack trace would help, including what line your code is throwing the error on.taintedzodiac
@taintedzodiac inside stripe.createPaymentmethod() i get the paymentMethod and which is sent to the html form using stripeTokenHandler Function. The problem is below that code. "Fetch" is supposed to fetch the payment_method_id from the process.php file. But it gives me an error at its Promise .then() says "Unexpected end of JSON input".. which what i think means its not getting anything in response. Screenshot: imgur.com/gdD2fB0Haroon Khan
OK, and what is the actual response that your PHP backend is returning when the frontend gets the unexpected end of JSON? Any logs in your server that show any errors, etc.?taintedzodiac
I switched to a different approach and made few changes into my current integration.. which solved all problems and i am getting successful payments on 3d-Secure cards. Stripe's 3dsecure authentication pop-up is also doing its work.Haroon Khan

1 Answers

4
votes

So, In order to achieve what i wanted to, what i did was

- removed stripeTokenHandler()

Because i was using it in my previous charges API integration and thought it will work with the new PaymentIntent. And i guess many people misunderstood or misguided buy bunch of different methods stripe has in its docs.I saw a lot of questions on internet people complaining that stripe's "poorly managed" Documentation has confused them.

- Learned Fetch Api.

As a newbie i didnt know what it was for.

- removed isset post submit from my php code

reason: payment.js was unable to POST the paymentMethod.id to the server using Fetch Api and get the response body back from the server to proceed the code further.

I MUST say that Stripe needs to improve its Docs about this SCA ready PaymentIntent thing.