Ad

How To Properly Use Stripe Api For Saved Card On Client Side

- 1 answer

I am currently working with Stripe API. I was able to use one time payment properly, and save credit card correctly as well as process;

However, I was stuck on the 3D secure(2nd auth) for saved card on client side. I had search out the website with no success. And the official doc https://stripe.com/docs/payments/save-and-reuse#web-create-payment-intent-off-session seems with little information about using saved card on client side. Or probably I just don't understand it. Hopefully someone can guide me out with correct practice.

Below it's the critical part of codes, it currently works with one time payment and 2nd auth stripe pop out.

NOTE: I was able to create a charge based on the information from each individual card from $saved_cards in my server, however it would not trig the 3d secure hence it will always failed with cards that requires 2nd authentication

backend.php

    \Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));

    $intent = \Stripe\PaymentIntent::create([
        'amount' => $payment * 100,
        'currency' => 'usd',
    ]);

    if (!empty(Auth::user()->stripe_id)) { //all saved card info
        $saved_cards = \Stripe\PaymentMethod::all([
            'customer' => Auth::user()->stripe_id,
            'type' => 'card',
        ]);
    }

    return view('cart.preview', $items)->with('saved_cards', $saved_cards)->with('client_secret', $intent->client_secret);

Client.js

        // this is for one time payment
        var payment_method = {
            card: card,
            billing_details: {
                name: "name"
            }
        };

        stripe.confirmCardPayment( "{{ $client_secret }}", {

           payment_method: payment_method,  // <= I believe this is the place I can use the saved card?

        }).then(function (result) {
            if (result.error) {
                // Show error to your customer (e.g., insufficient funds)
                console.log(result.error.message);
            } else {
                // The payment has been processed!
                if (result.paymentIntent.status === 'succeeded') {
                    // Show a success message to your customer
                    // There's a risk of the customer closing the window before callback
                    // execution. Set up a webhook or plugin to listen for the
                    // payment_intent.succeeded event that handles any business critical
                    // post-payment actions.
                }
            }
        });

/*****************************************************************************/

Update for solution based on answer suggestion:

Disclaimer: this might be a bad practice since the secret switch happens on the frontend side. But you get the idea.

In order to use your payment_method id, you also have to attach your customer id, which happens in the backend side. So for my case I created another savedCard_intent in my backend and pass it to frontend for handle the saved card specifically.

    $savedCard_intent = \Stripe\PaymentIntent::create([
        'customer' => Auth::user()->stripe_id,
        'amount' => $payment * 100,
        'currency' => 'usd',
    ]);

pass it to frontend with('saved_secret' ,$savedCard_intent->client_secret);

Combine all of them together I have code like the following:

newBackend.php

\Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));

$new_intent = \Stripe\PaymentIntent::create([
    'amount' => $payment * 100,
    'currency' => 'usd',
]);

$savedCard_intent = \Stripe\PaymentIntent::create([
    'customer' => Auth::user()->stripe_id,
    'amount' => $payment * 100,
    'currency' => 'usd',
]);

if (!empty(Auth::user()->stripe_id)) { //all saved card info
    $saved_cards = \Stripe\PaymentMethod::all([
        'customer' => Auth::user()->stripe_id,
        'type' => 'card',
    ]);
}

return view('cart.preview', $items)->with('saved_cards', $saved_cards)->with('new_secret', $new_intent->client_secret)->with('saved_secret', $savedCard_intent->client_secret);

newClient.js

    // this is for one time payment
    var payment_method = {
        card: card,
        billing_details: {
            name: "name"
        }
    };

    var secret = (Some conditions here) ? "{{$new_secret}}" : "{{$saved_secret}}";

    stripe.confirmCardPayment( secret, {

       payment_method: (Some conditions here) ? payment_method : saved_payment_method.id,
    }).then(function (result) {
        if (result.error) {
            // Show error to your customer (e.g., insufficient funds)
            console.log(result.error.message);
        } else {
            // The payment has been processed!
            if (result.paymentIntent.status === 'succeeded') {
                // Show a success message to your customer
            }
        }
    });
Ad

Answer

You are correct. You would use the PaymentMethod's id (pm_******) like so:

stripe.confirmCardPayment( "{{ $client_secret }}", {
  payment_method: payment_method.id,
}). then( ... )

Documented here: https://stripe.com/docs/js/payment_intents/confirm_card_payment#stripe_confirm_card_payment-data-payment_method

You can also pass a PaymentMethod object if you're generating one on the client side, but that's not likely the approach you're looking for.

Ad
source: stackoverflow.com
Ad