Incorrect price in 3D Secure on Stripe using Laravel Cashier

Viewed 312

I created a website with a subscription. I don't know why i don't have the good price in my 3D Secure confirmation. The confirmation display: 0$ and when you pay. You are charged successfully of 20$. Here is my controller :

public function create_checkout_session(Request $request)
{
    $this->validate($request, [
        'plan' => 'required'
    ]);

    if($request->plan !== config('app.football_price_id')) // Football
    {
        return redirect()->route('homepage');
    }

    $data = [
        'intent' => auth()->user()->createSetupIntent()
    ];

    return view('subscription.payment')->with($data);
}

Here is the view

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Payer mon abonnement</div>
                <div class="card-body">
                    <div class="text-center">
                        <img src="{{ asset('/img/secure-payment.jpg') }}" style="height: 100px;">
                    </div>
                    <form id="payment-form" action="{{ route('subscription_payments_store') }}" method="post" class="mt-4">
                        @csrf
                        <input type="hidden" name="plan" id="plan" value="{{ request('plan') }}">
                        <div class="form-group">
                            <label for="">Nom complet</label>
                            <input type="text" name="name" id="card-holder-name" class="form-control" value="" placeholder="Nom complet sur la carte">
                        </div>
                        <div class="form-group">
                            <label for="">Informations de votre carte bancaires</label>
                            <div id="card-element"></div>
                        </div>

                        <button type="submit" class="btn btn-primary w-100" id="card-button" data-secret="{{ $intent->client_secret }}">Payer</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

<script src="https://js.stripe.com/v3/"></script>
<script>
    const stripe = Stripe('{{ config('cashier.key') }}')

    const elements = stripe.elements()
    const cardElement = elements.create('card', {
        hidePostalCode: true
    })

    cardElement.mount('#card-element')

    const form = document.getElementById('payment-form')
    const cardBtn = document.getElementById('card-button')
    const cardHolderName = document.getElementById('card-holder-name')

    form.addEventListener('submit', async (e) => {
        e.preventDefault()

        cardBtn.disabled = true
        const { setupIntent, error } = await stripe.confirmCardSetup(
            cardBtn.dataset.secret, {
                payment_method: {
                    card: cardElement,
                    billing_details: {
                        name: cardHolderName.value
                    }
                }
            }
        )

        if(error) {
            cardBtn.disable = false
        } else {
            let token = document.createElement('input')

            token.setAttribute('type', 'hidden')
            token.setAttribute('name', 'token')
            token.setAttribute('value', setupIntent.payment_method)

            form.appendChild(token)
            form.submit();
        }
    })
</script>
@endsection

If someone have an idea about the problem.

config('app.football_price_id') is equal to the stripe product ID

Here is a screenshot of the 3D Secure: http://image.noelshack.com/fichiers/2021/11/6/1616246407-capture.png

Thank's to taking time to read this and maybe help me

2 Answers

Did you accidentally configured trial in your subscription? It would help to show your Product, Price and Subscription objects. I don't see anything out of the ordinary in your code.

Edit: I see that you're using stripe.confirmCardSetup. This is wrong and you should be using stripe.confirmCardPayment instead. The former is used to set up new card for future reuse.

When you create a SetupIntent, Stripe first save the card. To do so, they make a first call to the bank, with an amount of 0. So the amount you want to collect is not used at this step, but correctly used in the next steps.

If you don't need to re-use the card, just use a PaymentIntent instead of a SetupIntent, and everything should work fine.

Related