fix(checkout): take a repeated capture as a successful payment

The CMS answers a capture of an order already paid with this PayPal order
with { success: true, alreadyCaptured: true } instead of the order. Step 3
took that answer for a capture without authorisation and showed a payment
error. It now reloads the paid order (keeping the shown one if the reload
fails) and continues as after a first capture: confirmation, the
checkout-payment-completed event, and the redirect to the order's result
page. capturePayment is typed as Order | AlreadyCaptured, and the pure
isAlreadyCaptured (utils/captureResponse.ts) tells them apart.

Refs libretech/mp#71
This commit is contained in:
2026-10-09 02:32:38 +02:00
parent 8931afc670
commit 6f434c21ba
4 changed files with 45 additions and 5 deletions
+8 -3
View File
@@ -191,6 +191,7 @@
import { numberFormatter } from "~/utils/numberFormatter";
import { trackEvent } from "~/utils/trackEvent";
import { checkoutErrorMessageFor } from "~/utils/checkoutError";
import { isAlreadyCaptured } from "~/utils/captureResponse";
import type { Order } from "~/types";
const config = useRuntimeConfig();
@@ -312,10 +313,14 @@ async function initializePayPalButtons() {
onApprove: async (data) => {
try {
// Capture payment server-side (CMS captures via PayPal server SDK and updates order)
const capturedOrder = await shopApi.capturePayment(orderData.value.uuid, data.orderID!);
const captured = await shopApi.capturePayment(orderData.value.uuid, data.orderID!);
if (capturedOrder.paymentAuthorised) {
orderData.value = capturedOrder;
if (isAlreadyCaptured(captured)) {
// An earlier capture of this PayPal order went through: reload the paid order, keeping the shown one if that fails.
orderData.value = await shopApi.getOrder(orderData.value.uuid).catch(() => orderData.value);
await handleSuccessfulPayment();
} else if (captured.paymentAuthorised) {
orderData.value = captured;
await handleSuccessfulPayment();
} else {
console.error("Payment capture did not result in authorisation");