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 <noreply@anthropic.com>
This commit is contained in:
parent
d0f0f86176
commit
bbfbbf1963
1 changed files with 53 additions and 39 deletions
|
|
@ -83,45 +83,6 @@ try {
|
||||||
WHERE o.ID = :orderID
|
WHERE o.ID = :orderID
|
||||||
", { orderID: orderID }, { datasource: "payfrit" });
|
", { 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;
|
deliveryFee = 0;
|
||||||
if (qOrder.recordCount > 0 && qOrder.OrderTypeID == 3) {
|
if (qOrder.recordCount > 0 && qOrder.OrderTypeID == 3) {
|
||||||
deliveryFee = val(qOrder.DeliveryFee);
|
deliveryFee = val(qOrder.DeliveryFee);
|
||||||
|
|
@ -169,6 +130,59 @@ try {
|
||||||
totalAmountCents = round(totalCustomerPays * 100);
|
totalAmountCents = round(totalCustomerPays * 100);
|
||||||
totalPlatformFeeCents = round((payfritCustomerFee + payfritBusinessFee) * 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)
|
// STRIPE CUSTOMER (for saving payment methods)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
Reference in a new issue