javascriptjavastripe-payments

Stripe payment api sending an OPTIONS request instead of a POST


This is where the payment fails, the options request.


const appearance = {
    theme: 'night',
};

const stripe = Stripe('pk_test_51PccgFRo1v56iAOxEVEimciGkyyLOcjVE98T3rqOHLuvx28QNxI8sfvYN0zwSka8vLRIIIEdkOHorjb5OJUdwhIx00VRs64Seu');

// Set up Stripe Elements
const elements = stripe.elements({appearance: {theme: 'night'}});

const card = elements.create('card' );
card.mount('#payment-element');

document.querySelector("#payment-form").addEventListener("submit", async (event) => {
    event.preventDefault();

    document.getElementById('submit').disabled = true;

    const name = document.querySelector("#name").value;
    const email = document.querySelector("#email").value;

    try {
        // Create the customer by sending data to the server
        const customerResponse = await fetch("http://localhost:4242/create-customer", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({ name, email }),
        });

        console.log(customerResponse);

        // Ensure the response is JSON
        if (!customerResponse.ok) {
            throw new Error("Failed to create customer.");
        }

        const customerData = await customerResponse.json();
        console.log("Customer created:", customerData);

        // Create a token for the card details
        const { token, error } = await stripe.createToken(card);

        if (error) {
            console.error('Error creating token:', error);
            alert('Failed to tokenize card details. Please try again.');
            document.getElementById('submit').disabled = false;
            return;
        }

        // Proceed to initialize the payment process
        await initializePayment(customerData.customerId, token.id);

    } catch (error) {
        document.querySelector("#error-message").textContent = error.message || "Failed to create customer.";
        console.error("Error:", error);
        document.getElementById('submit').disabled = false;
    }
});

async function initializePayment(customerId, tokenId) {
    try {
        // Create the checkout session
        const sessionResponse = await fetch("http://localhost:4242/create-checkout-session", {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                customer: customerId, // Pass the customer ID to backend
                token: tokenId,       // Pass the tokenized card ID to backend
                priceId: 'price_1PfUDSRo1v56iAOxxLORAlv9'
            })
        });

        if (!sessionResponse.ok) {
            const errorData = await sessionResponse.json();
            throw new Error(errorData.message || `Failed to create checkout session.`);
        }

        const sessionData = await sessionResponse.json();
        window.location.href = sessionData.url; // Redirect to the Stripe Checkout or success page

    } catch (error) {
        document.querySelector("#error-message").textContent = error.message || "Failed to process payment.";
        console.error('Error:', error);
    } finally {
        document.getElementById('submit').disabled = false;
    }
}


public class Server {
    private static final Gson gson = new Gson();

    public static void main(String[] args) {
        port(4242);

        options("/*", (request, response) -> {
            String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
            if (accessControlRequestHeaders != null) {
                response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
            }

            String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
            if (accessControlRequestMethod != null) {
                response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
            }

            return "OK";
        });

        before((request, response) -> {
            response.header("Access-Control-Allow-Origin", "*");  // Allow all origins
            response.header("Access-Control-Allow-Headers", "*");
            response.header("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
            response.header("Access-Control-Allow-Credentials", "true");
        });



        // Set Stripe API keys directly in the code
        Stripe.apiKey = "sk_test_yHMt2Vkexample";

        // Domain URL
        String domainUrl = "http://localhost:4242";

        get("/config", (request, response) -> {
            response.type("application/json");

            Map<String, Object> responseData = new HashMap<>();
            responseData.put("publishableKey", "pk_test_51PccgFRo1v56iAOxEVEimciGkyyLOcjVE98T3rqOHLuvx28QNxI8sfvYN0zwSka8vLRIIIEdkOHorjb5OJUdwhIx00VRs64Seu");
            responseData.put("proPrice", "price_1PfUDSRo1v56iAOxxLORAlv9");

            return gson.toJson(responseData);
        });

        // Fetch the Checkout Session to display the JSON result on the success page
        get("/checkout-session", (request, response) -> {
            response.type("application/json");

            String sessionId = request.queryParams("sessionId");
            Session session = Session.retrieve(sessionId);

            return gson.toJson(session);
        });

        post("/create-customer", (request, response) -> {
            response.type("application/json");
            Map<String, Object> requestData = gson.fromJson(request.body(), Map.class);
            String email = (String) requestData.get("email");
            String name = (String) requestData.get("name");

            try {
                CustomerCreateParams params = CustomerCreateParams.builder()
                        .setEmail(email)
                        .setName(name)
                        .build();
                Customer customer = Customer.create(params);

                Map<String, Object> responseData = new HashMap<>();
                responseData.put("customerId", customer.getId());
                responseData.put("email", customer.getEmail());
                responseData.put("name", customer.getName());
                return gson.toJson(responseData);
            } catch (StripeException e) {
                response.status(400);
                Map<String, Object> errorData = new HashMap<>();
                errorData.put("error", e.getMessage());
                return gson.toJson(errorData);
            }
        });

        post("/create-checkout-session", (request, response) -> {
            response.type("application/json");

            // Parse request body as JSON
            Map<String, Object> requestData = gson.fromJson(request.body(), Map.class);

            // Retrieve the priceId from the JSON payload
            String priceId = (String) requestData.get("priceId");

            // Create session parameters
            SessionCreateParams params = new SessionCreateParams.Builder()
                    .setSuccessUrl(domainUrl + "/success.html?session_id={CHECKOUT_SESSION_ID}")
                    .setCancelUrl(domainUrl + "/canceled.html")
                    .setMode(SessionCreateParams.Mode.SUBSCRIPTION)
                    .addLineItem(new SessionCreateParams.LineItem.Builder()
                            .setQuantity(1L)
                            .setPrice(priceId)
                            .build()
                    )
                    .build();

            try {
                Session session = Session.create(params);
                response.status(303); // HTTP 303 See Other
                response.redirect(session.getUrl());
                return ""; // Returning an empty string to indicate no body content
            } catch (Exception e) {
                Map<String, Object> messageData = new HashMap<>();
                messageData.put("message", e.getMessage());
                Map<String, Object> responseData = new HashMap<>();
                responseData.put("error", messageData);
                response.status(400);
                return gson.toJson(responseData);
            }
        });


        post("/customer-portal", (request, response) -> {
            String sessionId = request.queryParams("sessionId");

            // Retrieve the Checkout Session to get the customer ID
            Session checkoutSession;
            try {
                checkoutSession = Session.retrieve(sessionId);
            } catch (Exception e) {
                response.status(400);
                return gson.toJson(Collections.singletonMap("error", "Failed to retrieve session: " + e.getMessage()));
            }

            String customer = checkoutSession.getCustomer();

            // Create a Billing Portal session
            com.stripe.param.billingportal.SessionCreateParams params =
                    new com.stripe.param.billingportal.SessionCreateParams.Builder()
                            .setReturnUrl(domainUrl + "/account") // Redirect to your account page after managing billing
                            .setCustomer(customer)
                            .build();
            com.stripe.model.billingportal.Session portalSession;
            try {
                portalSession = com.stripe.model.billingportal.Session.create(params);
            } catch (Exception e) {
                response.status(400);
                return gson.toJson(Collections.singletonMap("error", "Failed to create billing portal session: " + e.getMessage()));
            }

            // Redirect to the billing portal
            response.redirect(portalSession.getUrl(), 303);
            return "";
        });


        post("/webhook", (request, response) -> {
            String payload = request.body();
            String sigHeader = request.headers("Stripe-Signature");
            String endpointSecret = "your_webhook_secret"; // Replace with your actual webhook secret

            Event event;
            try {
                event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
            } catch (SignatureVerificationException e) {
                response.status(400);
                return gson.toJson(Collections.singletonMap("error", "Invalid signature"));
            }

            // Handle the event
            switch (event.getType()) {
                case "checkout.session.completed":
                    // Handle successful payment
                    System.out.println("Checkout Session completed: " + event.getDataObjectDeserializer().getObject().toString());
                    response.status(200);
                    break;
                case "invoice.payment_succeeded":
                    // Handle successful payment for invoices
                    System.out.println("Invoice payment succeeded: " + event.getDataObjectDeserializer().getObject().toString());
                    response.status(200);
                    break;
                case "invoice.payment_failed":
                    // Handle failed payment for invoices
                    System.out.println("Invoice payment failed: " + event.getDataObjectDeserializer().getObject().toString());
                    response.status(200);
                    break;
                case "customer.subscription.created":
                    // Handle subscription creation
                    System.out.println("Subscription created: " + event.getDataObjectDeserializer().getObject().toString());
                    response.status(200);
                    break;
                case "customer.subscription.deleted":
                    // Handle subscription cancellation
                    System.out.println("Subscription canceled: " + event.getDataObjectDeserializer().getObject().toString());
                    response.status(200);
                    break;
                default:
                    response.status(200);
                    System.out.println("Unhandled event type: " + event.getType());
                    break;
            }

            return "";
        });
    }
}

So this error is caused when initializepayment function executes, but stripe does not accept OPTIONS request due to AWS constraint, based on my javascript is there anything I can do to prevent this from happening because I have my app running on PORT 8443. I can see the customer creation happen on stripe, but it fails to mak the payment.


Solution

  • Your backend code tries to immediately redirect to the Stripe Checkout URL :

    response.status(303); // HTTP 303 See Other
    response.redirect(session.getUrl()); 
    

    But your frontend code is expecting instead to get JSON from your backend and extract a URL from that and redirect the browser itself in JS:

    const sessionData = await sessionResponse.json();
    window.location.href = sessionData.url; // Redirect to the Stripe Checkout or success page
    

    Redirecting with a HTTP 3xx from a server will not work in a fetch/XHR context, which is why your frontend code correctly is doing the redirect, but you haven't changed the backend code to actually work with it(the backend code you probably copied from a Stripe guide where that code would be hit by a <form action="create-checkout-session">, not a fetch/XHR).

    To fix this you should change the backend code to return the information the frontend is expecting.

    post("/create-checkout-session", (request, response) -> {
        ...
            Session session = Session.create(params);
            Map<String, String> responseData = new HashMap<>();
            responseData.put("url", session.getUrl());
            return gson.toJson(responseData);
       ...
    }});
    

    Your code has other issues out of scope like using CardElement as well as Checkout on the same page which is a bit strange.