Reuse existing PaymentIntent instead of blocking on retry

When user abandons checkout and retries, retrieve the existing
PaymentIntent from Stripe. If still usable (requires_payment_method,
requires_confirmation, requires_action), return its client_secret.
If already succeeded, block with clear error. If terminal/canceled,
clear and create new one.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-02-17 19:01:17 -08:00
parent 29327c4a13
commit d0f0f86176

View file

@ -83,14 +83,43 @@ try {
WHERE o.ID = :orderID WHERE o.ID = :orderID
", { orderID: orderID }, { datasource: "payfrit" }); ", { orderID: orderID }, { datasource: "payfrit" });
// Check if order already has a PaymentIntent (idempotency check) // Check if order already has a PaymentIntent - retrieve and reuse if still valid
if (qOrder.recordCount > 0 && len(trim(qOrder.StripePaymentIntentID ?: "")) > 0) { existingPiId = qOrder.StripePaymentIntentID ?: "";
response["OK"] = false; if (qOrder.recordCount > 0 && len(trim(existingPiId)) > 0) {
response["ERROR"] = "existing_payment_intent"; // Retrieve existing PaymentIntent from Stripe
response["MESSAGE"] = "Order already has a PaymentIntent. Use existing checkout session or retry payment."; piRetrieve = new http();
response["PAYMENT_INTENT_ID"] = qOrder.StripePaymentIntentID; piRetrieve.setMethod("GET");
writeOutput(serializeJSON(response)); piRetrieve.setUrl("https://api.stripe.com/v1/payment_intents/#existingPiId#");
abort; 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;