#Bassil-overcharge
1 messages ยท Page 1 of 1 (latest)
Hello
we're here but what you shared, that sounds like possibly an integration issue. Happy to talk through it
Hi there yes please
Second time it happened today and I feel so embarrassed from my customer. He was charged 5,939.76AED when he should have only been charged AED 2,190.37
The meta data for the order is correct but amount charged is wrong
here is the payment ID
mind sharing the two PaymentIntent IDs?
pi_3KC1chBStawTvwlL0iCZkGBJ
This is for the most recent one, the first incident is the following:
pi_3KBvsABStawTvwlL2yZN9FPQ
digging
Ah
ok so both of these were PaymentIntents created by your code. And confirmed on the client-side
I thought this would be a "duplicate" charge issue, that your integration created the charge in quick succession
but that is NOT the case
these charges are ~6 hrs apart
so really, your code asked us to create two different charges, which were paid by two different cards.
Your metadata has tagged them as different customers too so I'm not clear on what the issue is
I found the culprit I think...
Okay here it is
I had a theory and checked my website access logs and confirmed the theory
When customers click Buy Now, it adds the product to their cart and takes them to the checkout page.
Now
They clicked back and viewed other products and then clicked buy now so now they have multiple items in their cart right? on the checkout page
They clicked remove on the products they dont want... but the payment intent was created and NOT updated since the update only happens when they click the pay now button
I need to a) update new amount when they click pay now and b) update payment intent when they remove an item from their cart
i think this may be the issue I will double check
Oh the issue was the customer was charged more than they should have
ah
so yeah the PaymentIntent not being updated to reduce the amount based on the reduced products
yeah that would be the issue here
going to try to do this method and let you know if it works ๐
boom found the issue
when they click Pay, it should update payment intent which it does due to the meta data
but the amount is only updated if they use code as per my code
if code_valid:
payment_intent = request.session['payment_intent'].strip()
stripe.api_key = STRIPE_SECRET_KEY
stripe.PaymentIntent.modify(
payment_intent,
amount=int(str(cart_subtotal_in_aed).replace('.', '')),
metadata={'code': code, 'code_valid': code_valid,
'percent_off': percent_off, 'discount': discount,
'cart_subtotal_in_aed_with_code': cart_subtotal_in_aed},
)
I need to update the payment amount regardless if they use a code or not
haha glad I found it, I felt so bad for my customers
I wanted to literally dig a hole and put my face in it
thank you so much for the brain storming session
๐
yeah tough situation to be in, one edge case causes charging an incorrect amount and unhappy customers
so I'm glad you found it quickly!
Thank you ๐ Do you think I should update payment intent when removing item from cart as a fail safe too?
Yay it worked ๐
Do you think I should update payment intent when removing item from cart as a fail safe too?
is that different from the case you fixed?
Well currently the payment intent only updates when they click the pay now stripe button
I basically make an ajax call to update the payment server side, and then go ahead with the charge
Ill just call the payment intent on cart removal too i guess
$.ajax({
type: "POST",
url: '{% url 'store:update_payment_intent' %}',
data: $('#payment-form').serialize(),
success: function (response) {
}
});
yeah depends, on the pay button is one canonical path where you update the PaymentIntent depending on the state of the cart
but doesn't hurt to do it before
hmm weird I was testing a transaction locally
AED 6,200.00
but it charged 620
Meta data seems to be correct:
cart_subtotal_in_aed_less_code
6200.0
My python code to update the amount is ```python
payment_intent = request.session['payment_intent'].strip()
stripe.api_key = STRIPE_SECRET_KEY
stripe.PaymentIntent.modify(
payment_intent,
amount=int(str(cart_subtotal_in_aed).replace('.', '')),
)
I suspect because python is formatting this number to 1 decimal place .0
maybe if it was .00 it would charge the correct amount, hmm what do you suggest, a way to format the total that stripe can read
because stripe doesn't allow decimals in the number we provide
Stripe just takes in amount in cents so to charge AED 6200.00, you pass amount: 620000
I would say treat amounts in cents, until you have to pass it under metadata and convert it to decimal there by dividing by 100
Oh i did ```python
cart_subtotal_in_aed = format(round(cart_subtotal_in_aed - discount, 2), '.2f')
what was the amount on the PaymentIntent you created
this part
is probably prone to math/rounding/significant place errors due to the conversions etc (I'm not a python expert)
my recommendation would be to treat the raw amount always in cents and only convert in other places when needed
cause what the PaymentIntent charges all depends on one field, the amount
field/param on the PaymentIntent
so you want that one field to be resilient to any conversions/miscalculation mistakes
and in your code where you change/modify the amount for display/metadata purposes, you do any math/conversion on it there but otherwise your system communicates to Stripe purely in an untouched cents value
hmm
so if the amount was 6200 how should i convert it to a format stripe accepts
or imagine it was 6200.52
6200.52 == 6200 dollars and 52 cents
Stripe accepts a value only in the lowest common denominator of the currency aka cents in this case
so you pass Stripe 620052
which is "6 hundred and twenty thousand and 52 cents"
Hmm okay but our currency is in AED, if i am getting a number with a decimal
jsut remove the decimal?
multiple by 100 in that case yes to remove the decimal and convert to an int
oh
so x 100 and convert to int
no need to do replace('.','')
6200.52 * 100 = 620,052 which in int form is 620052
excellent
My man out here saving companies from crashing haha thanks man
My math is correct I assume
basically that your raw amount number should be in int and that is what you should be basing everything off of
and then when you need to tell your system "hey this was $124.56 AED" you take the raw amount 12456 and divide it by 100 and display as a decimal or string
but otherwise you keep calculations to just cents so there are no rounding issues etc
My man out here saving companies from crashing haha thanks man
haha here to help
Does this look right
def calculate_order_amount(items):
# Adds cart items to payment intent
cart_items_list = ''
cart_subtotal_in_aed = 0.0
cart_cogs_in_aed = 0.0
for product_id, quantity in request.session['cart'].items():
cart_items_list += (product_id + ':' + quantity + ',')
product = TyreProducts.objects.get(pk=product_id)
cart_subtotal_in_aed += (product.price_in_aed * float(quantity))
cart_cogs_in_aed += (product.cogs_in_aed * float(quantity))
total = round(cart_subtotal_in_aed, 2) * 100
return int(str(total).replace('.', ''))
my suggestion was the opposite
that you keep the value always as an int and do all math/updates with the int aka "cent" value of the amounts
and then transform it by dividing by 100 when you need to pass into metadata
what I wanted to avoid was the thing like
cart_subtotal_in_aed += (product.price_in_aed * float(quantity))
like in my eyes it would be simpler to just treat everything as cents and ints the quantity * amount multiplication
e.g. 12456 * 5 qty
and then later down the line transform it by /100
Hmm I see so since the price of a item is a float by default from my database, I would simply remove the . and then convert to int
int(str(product.cogs_in_aed).replace('.',''))
that works though would it not work to int(product.cogs_in_aed * 100)
string conversion doesn't sound right, and the removal of decimal by char replace
Oh that makes sense, I was afraid we would still have a decimial after multiplying by 100
but I just checked, as long as there is only 2 decimal places, multiplying by 100 will remove the decimal correct/
but the int() gets rid of whatever is after the decimal I believe
so like ```python
int(round(product.cogs_in_aed,2) * 100)
Just in case somehow there is more than 2 decimal places in the number
Because for example 9283.383 * 100 is 928,338.3
and thus will cause an error, so by rounding off to 2 decimals before converting, should solve it?
and thus will cause an error
which part will cause error?roundthenintwon't (even justintprobably rounds as well)