0
votes

I am struggling for a long period of time to implement Stripe in my Laravel app, but I've encountered a lot of problems. I did somehow implemented the logic but I cannot or I don't know how I can send the current order total to create the paymentIntent, all of the payments are stored with a default amount of 500, set up in the Controller. I have to mention that after the success response from stripe, the current order should be stored in the database with user shipping details which are found in the first form and all the products ordered, stored in session. Let me show you for a better understanding.

This is the view (revieworder.blade.php) where I have a 2 forms, one with the user's shipping details, and the Stripe payment form, and a list of cart products from the session:

<ul class="list-group mb-3">
    <?php $total = 0 ?>
    @if(session('cart'))
        @foreach(session('cart') as $id => $details)
        <?php $total += $details['price'] * $details['quantity'] ?>
            <li class="list-group-item d-flex justify-content-between lh-condensed">
                <img src="../img/{{ $details['image'] }}" alt="{{ $details['name'] }}" width="60" height="60">
                <div>
                    <h6 class="my-0">{{ $details['name'] }}</h6>
                    <small class="text-muted">{{ __('Quantity') }}: {{ $details['quantity'] }}</small><br>
                    <small class="text-muted">{{ __('Unit price') }}: {{ $details['price'] }} RON</small>
                </div>
                <span class="text-muted">{{ $details['price'] * $details['quantity'] }} RON</span>
            </li>
        @endforeach
    @endif
    <li class="list-group-item d-flex justify-content-between">
        <span>Total (RON)</span>
        <strong id="total">{{ $total.' RON' }}</strong>
    </li>
</ul>


<form id="payment-form">
     @csrf
     <div id="card-element"><!--Stripe.js injects the Card Element--></div>
     <button id="submit" class="submit-id">
          <div class="spinner hidden" id="spinner"></div>
          <span id="button-text">Pay now</span>
      </button>
      <p id="card-error" role="alert"></p>
      <p class="result-message hidden">
      </p>
</form>

<script>
//Stripe script
var stripe = Stripe("pk_test_XXX");

// The items the customer wants to buy
var purchase = {
  items: [{id: "prod"}]   //sessions cart
};
console.log(purchase);

var elements = stripe.elements();

    var style = {
      base: { //some styling },
      invalid: {
        fontFamily: 'Arial, sans-serif',
        color: "#fa755a"
      }
    };

    var card = elements.create("card", { style: style });
    // Stripe injects an iframe into the DOM
    card.mount("#card-element");

    card.on("change", function (event) {
      // Disable the Pay button if there are no card details in the Element
      document.querySelector("button").disabled = event.empty;
      document.querySelector("#card-error").textContent = event.error ? event.error.message : "";
    });
// Disable the button until we have Stripe set up on the page
document.getElementsByClassName("submit-id").disabled = true;

$('#payment-form').submit(function(){
    fetch("{{ url(app()->getLocale().'/revieworder') }}", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        'X-CSRF-TOKEN': "{{ csrf_token() }}"
    },
    body: JSON.stringify(purchase)
    })
    .then(function(data) {
        $('#payment-form').submit(function(event) {
        event.preventDefault();
        // Complete payment when the submit button is clicked
        payWithCard(stripe, card, data.clientSecret);
        });
    });

    // Calls stripe.confirmCardPayment
    var payWithCard = function(stripe, card, clientSecret) {
    loading(true);
    stripe
        .confirmCardPayment(clientSecret, {
        payment_method: {
            card: card
        }
        })
        .then(function(result) {
        if (result.error) {
            // Show error to your customer
            showError(result.error.message);
        } else {
            // The payment succeeded!
            // The order should be stored in the database
            orderComplete(result.paymentIntent.id);
        }
        });
    };

    // Shows a success message when the payment is complete
    var orderComplete = function(paymentIntentId) {
    loading(false);
    document
        .querySelector(".result-message a")
        .setAttribute(
        "href",
        "https://dashboard.stripe.com/test/payments/" + paymentIntentId
        );
    document.querySelector(".result-message").classList.remove("hidden");
    document.getElementsByClassName("submit-id").disabled = true;
    };

    // Show the customer the error from Stripe if their card fails to charge
    var showError = function(errorMsgText) {
    loading(false);
    var errorMsg = document.querySelector("#card-error");
    errorMsg.textContent = errorMsgText;
    setTimeout(function() {
        errorMsg.textContent = "";
    }, 4000);
    };

    // Show a spinner on payment submission
    var loading = function(isLoading) {
    if (isLoading) {
        // Disable the button and show a spinner
        document.getElementsByClassName("submit-id").disabled = true;
        document.querySelector("#spinner").classList.remove("hidden");
        document.querySelector("#button-text").classList.add("hidden");
    } else {
        document.getElementsByClassName("submit-id").disabled = false;
        document.querySelector("#spinner").classList.add("hidden");
        document.querySelector("#button-text").classList.remove("hidden");
    }
    };
});

</script>

And then this is the controller which handles the secret client key and the paymentIntent (CheckoutController.php):

public function create(){
        \Stripe\Stripe::setApiKey('sk_test_XXX');  

        header('Content-Type: application/json');
        try {
            $json_str = file_get_contents('php://input');
            $json_obj = json_decode($json_str);
            
            $paymentIntent = \Stripe\PaymentIntent::create([
                'amount' => "500",
                'currency' => 'ron',
            ]);
            $output = [
                'clientSecret' => $paymentIntent->client_secret,
            ];
            echo json_encode($output);
        } catch (Error $e) {
            http_response_code(500);
            echo json_encode(['error' => $e->getMessage()]);
        }
    }

So, one of the problems is that whenever I get to pay for an order, no matter what credit card number I pass, it always succeeds, and that is a major bug. The second one, I need to pass the total of the current order, otherwise all orders will get a total amount of 500, the default value. I tried to pass the session cart items to the fetch but it didn't work. Even though I cannot send all the items from the cart to the intent, at least the total price should correspond.

If I wasn't clear enough with anything, please let me know. I would really appreciate any piece of advice. Thank you !

**Edit:Logs

1
if you are in a test environment for stripe you have two credit cards numbers: first is always successfull and the other is always failing. Read the documentation to see what are these numbers for you.Lelio Faieta
No matter which one I am inserting, it always succeeds, even if it is a random numbertheospace
then you are not listening to the response from stripe. you always receive a response from them: inside the json you have the feedback (successfull or not)Lelio Faieta
@LelioFaieta fetch function is only sending the amount and the currency, no information about the card, so all the response body received as a response has status OK. As far as I know, fetch is responsible for the payment. I followed all the steps from the docs, no idea what could be wrongtheospace

1 Answers

1
votes

You can update the PaymentIntent amount via the API (reference) before you confirm it (either on the server or client side. If you're encountering errors sending this, please share the details of that error.

You need to say more about the "it always succeeds" and exactly what request you are making. You did not show how you are using Stripe.js. You should use the test cards that Stripe provides to test success and failure cases. If you are getting different results than specified you will need to provide more detail about what exactly you're doing and what you expect the result to be.