#smartbettorai_server-client-request-validation

1 messages ยท Page 1 of 1 (latest)

unreal muskBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1353741657627037778

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

sand solstice
#

Here is how I call update_payment_intent:

// Consolidated price calculation and payment intent update
useEffect(() => {
const newFinalPrice = appliedDiscount ? basePrice - appliedDiscount.amount : basePrice;
setFinalPrice(newFinalPrice);

const updatePaymentIntent = async () => {
  if (!stripe || !elements || !clientSecret) return;

  try {
    const response = await fetch('/api/update_payment_intent', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ 
        amount: isYearly ? .51 : newFinalPrice,
        clientSecret
      }),
    });
    
    if (!response.ok) throw new Error('Failed to update payment intent');
    await elements.fetchUpdates();
  } catch (error) {
    console.error('Error updating payment intent:', error);
    setPaymentError('Failed to update payment amount. Please try again.');
  }
};

updatePaymentIntent();

}, [appliedDiscount, stripe, elements, clientSecret, basePrice]);

#

Here is the update_payment_intent route:
import { NextResponse } from 'next/server';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-12-18.acacia',
});

export async function POST(request: Request) {
try {
const { amount, clientSecret } = await request.json();

if (!amount || !clientSecret) {
  return NextResponse.json(
    { error: 'Amount and clientSecret are required' },
    { status: 400 }
  );
}

// Extract the payment intent ID from the client secret
const paymentIntentId = clientSecret.split('_secret_')[0];

// Update the payment intent
const paymentIntent = await stripe.paymentIntents.update(paymentIntentId, {
  amount: Math.round(amount * 100), // Convert to cents
  capture_method: 'manual', // Maintain manual capture setting
});

return NextResponse.json({
  success: true,
  updatedAmount: paymentIntent.amount,
});

} catch (error) {
console.error('Error updating payment intent:', error);
return NextResponse.json(
{
error: error instanceof Error ? error.message : 'Failed to update payment intent',
details: error
},
{ status: 400 }
);
}
}

inner sleetBOT
weary tulip
#

Hi there ๐Ÿ‘‹ it sounds like you have an endpoint on your server that accepts an amount value from your client-side code, and is using that amount to update the Payment Intent you're using.

Please let me know if I'm parsing your situation incorrectly, but if that's the case you'll likely want to have validation logic on your update endpoint to ensure you're being passed an expected amount from your client-side code.

sand solstice
#

That is what is happening. The problem with simple validation is that it can be a 0 value. We offer a first month free to users so if 0 is valid then it defeats the point of any validation

#

Or even a 51 cent value which is what we use to validate cards

weary tulip
#

You'll need to find a way to make that validation more robust then, so it can handle determining whether the value being provided is valid and expected for the given scenario.

sand solstice
#

Why are users able to change this? shouldn't the client secret be secret and validate against this?

versed hamlet
#

Hey @sand solstice i was actually looking at the case you submitted and can explain the confusion here

#

shouldn't the client secret be secret and validate against this?
The payment intent client_secret does not have permission to update the amount of a payment, but that's not what is happening here. You're making a secret key API call to update the payment intent from your server, without validating the amount the client gives you.

#

The secret key can update any unconfirmed payment intent to any amount -- Stripe knows nothing about the amount you need to charge and accepts what you send using a secret key.

#

You should also not do this:

const paymentIntentId = clientSecret.split('secret')[0];
#

If you need the payment intent ID for tracking in the client, you should pass that explicitly as a separate value from the clientSecret

#

That structure of the client secret is not guaranteed to remain consistent, and we may change it at any time without notice. You should not assume you can get the payment intent ID this way.

#

Going back to the issue of amount and client_secret , if you tried to update a payment intent amount using that client secret the request would fail

sand solstice
#

So then why is the secret key not safeguarding against the user being able to make the request with a different amount?

versed hamlet
#

What do you mean?

#

The secret key is known only to your server, you, and you're allowed to make any amount update.

#

You server is passing the amount here, using a secret key. The secret key is the safeguard that this amount change is being requested by you.

#

Can you explain what you'd expect to happen instead?

sand solstice
#

Yes that makes sense. Just going back up to the top, users are able to change the amount and forge the request, I am wondering how that is possible if the secret key is only known to my server

versed hamlet
#

I infer they are forging the request to your server with a different amount than what your server expects your client to send.

#

Not forging the request to Stripe

#

So your back end/API may have vulnerability to malicious customers modifying amounts in ways you don't expect, but this is within your control to validate before making a request to the Stripe API with a secret key.

#

Hopefully that all makes sense! I'll follow up in the case you opened with a summary of the above.

unreal muskBOT
sand solstice
#

Ah so can I just add a secret key validation in the request as an argument check as well? I am still a bit confused on how a user forges the request. I am hoping I do not have to hardcode valid values because it seems like that is less secure than a new secret key?

#

I am thinking the solution is holding my stripe secret key still in a .env file and passing it in to the request? Assuming the user cannot access that key then

zenith bluff
#

Hello ๐Ÿ‘‹

My colleague had to go but they responded to your email with a thorough description of the problem. Have you reviewed this email?

sand solstice
#

Yes I did I just still have questions about validating the requests

zenith bluff
#

The requests are coming from your front-end to your server. You need to determine how to validate those requests. This isn't something specific to Stripe.

sand solstice
#

Can I not just use my stripe secret key?

zenith bluff
#

I'm sorry but this has nothing to do with Stripe. It's about someone spoofing requests from your front-end to your server. You need to determine how to validate those requests.

You have to make requests to Stripe's API with your Stripe Secret Key but that has nothing to do with the problem here.

sand solstice
#

Hmmm I think I am still a bit confused but will figure it out