From bbfbbf1963a16431251a248ada6623a780726e0a Mon Sep 17 00:00:00 2001 From: John Mizerek Date: Tue, 17 Feb 2026 19:06:01 -0800 Subject: [PATCH] Update PaymentIntent amount if cart changed on retry Moved fee calculation before PI check so we can compare amounts. If existing PaymentIntent has different amount than current cart, update it via Stripe API before returning. Co-Authored-By: Claude Opus 4.5 --- api/stripe/createPaymentIntent.cfm | 92 +++++++++++++++++------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/api/stripe/createPaymentIntent.cfm b/api/stripe/createPaymentIntent.cfm index 3be2d44..4fc9ccb 100644 --- a/api/stripe/createPaymentIntent.cfm +++ b/api/stripe/createPaymentIntent.cfm @@ -83,45 +83,6 @@ try { WHERE o.ID = :orderID ", { orderID: orderID }, { datasource: "payfrit" }); - // Check if order already has a PaymentIntent - retrieve and reuse if still valid - existingPiId = qOrder.StripePaymentIntentID ?: ""; - if (qOrder.recordCount > 0 && len(trim(existingPiId)) > 0) { - // Retrieve existing PaymentIntent from Stripe - piRetrieve = new http(); - piRetrieve.setMethod("GET"); - piRetrieve.setUrl("https://api.stripe.com/v1/payment_intents/#existingPiId#"); - piRetrieve.setUsername(stripeSecretKey); - piRetrieve.setPassword(""); - piResult = piRetrieve.send().getPrefix(); - existingPi = deserializeJSON(piResult.fileContent); - - if (!structKeyExists(existingPi, "error")) { - piStatus = existingPi.status ?: ""; - // Reusable states: can still complete payment - if (listFindNoCase("requires_payment_method,requires_confirmation,requires_action", piStatus)) { - // Return existing PaymentIntent - user can retry with same one - response["OK"] = true; - response["CLIENT_SECRET"] = existingPi.client_secret; - response["PAYMENT_INTENT_ID"] = existingPi.id; - response["PUBLISHABLE_KEY"] = application.stripePublishableKey ?: "pk_test_sPBNzSyJ9HcEPJGC7dSo8NqN"; - response["REUSED"] = true; - writeOutput(serializeJSON(response)); - abort; - } else if (piStatus == "succeeded") { - // Already paid - don't create another - response["OK"] = false; - response["ERROR"] = "already_paid"; - response["MESSAGE"] = "This order has already been paid."; - writeOutput(serializeJSON(response)); - abort; - } - // Other terminal states (canceled, etc.) - clear and create new - } - // PaymentIntent not found or terminal - clear it from order - queryExecute("UPDATE Orders SET StripePaymentIntentID = NULL WHERE ID = :orderID", - { orderID: orderID }, { datasource: "payfrit" }); - } - deliveryFee = 0; if (qOrder.recordCount > 0 && qOrder.OrderTypeID == 3) { deliveryFee = val(qOrder.DeliveryFee); @@ -169,6 +130,59 @@ try { totalAmountCents = round(totalCustomerPays * 100); totalPlatformFeeCents = round((payfritCustomerFee + payfritBusinessFee) * 100); + // ============================================================ + // CHECK FOR EXISTING PAYMENTINTENT - REUSE OR UPDATE IF VALID + // ============================================================ + existingPiId = qOrder.StripePaymentIntentID ?: ""; + if (qOrder.recordCount > 0 && len(trim(existingPiId)) > 0) { + // Retrieve existing PaymentIntent from Stripe + piRetrieve = new http(); + piRetrieve.setMethod("GET"); + piRetrieve.setUrl("https://api.stripe.com/v1/payment_intents/#existingPiId#"); + piRetrieve.setUsername(stripeSecretKey); + piRetrieve.setPassword(""); + piResult = piRetrieve.send().getPrefix(); + existingPi = deserializeJSON(piResult.fileContent); + + if (!structKeyExists(existingPi, "error")) { + piStatus = existingPi.status ?: ""; + // Reusable states: can still complete payment + if (listFindNoCase("requires_payment_method,requires_confirmation,requires_action", piStatus)) { + // Check if amount changed (cart modified) + if (existingPi.amount != totalAmountCents) { + // Update the PaymentIntent with new amount + piUpdate = new http(); + piUpdate.setMethod("POST"); + piUpdate.setUrl("https://api.stripe.com/v1/payment_intents/#existingPiId#"); + piUpdate.setUsername(stripeSecretKey); + piUpdate.setPassword(""); + piUpdate.addParam(type="formfield", name="amount", value=totalAmountCents); + piUpdateResult = piUpdate.send().getPrefix(); + existingPi = deserializeJSON(piUpdateResult.fileContent); + } + // Return existing PaymentIntent + response["OK"] = true; + response["CLIENT_SECRET"] = existingPi.client_secret; + response["PAYMENT_INTENT_ID"] = existingPi.id; + response["PUBLISHABLE_KEY"] = application.stripePublishableKey ?: "pk_test_sPBNzSyJ9HcEPJGC7dSo8NqN"; + response["REUSED"] = true; + writeOutput(serializeJSON(response)); + abort; + } else if (piStatus == "succeeded") { + // Already paid - don't create another + response["OK"] = false; + response["ERROR"] = "already_paid"; + response["MESSAGE"] = "This order has already been paid."; + writeOutput(serializeJSON(response)); + abort; + } + // Other terminal states (canceled, etc.) - clear and create new + } + // PaymentIntent not found or terminal - clear it from order + queryExecute("UPDATE Orders SET StripePaymentIntentID = NULL WHERE ID = :orderID", + { orderID: orderID }, { datasource: "payfrit" }); + } + // ============================================================ // STRIPE CUSTOMER (for saving payment methods) // ============================================================