diff --git a/.changeset/onramp-success-guard.md b/.changeset/onramp-success-guard.md new file mode 100644 index 00000000000..51fd8da313c --- /dev/null +++ b/.changeset/onramp-success-guard.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Fixed Universal Bridge onramp checkout incorrectly reporting success when the onramp did not complete. diff --git a/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts b/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts index e0a8f024200..1f96be33dab 100644 --- a/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts +++ b/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts @@ -466,6 +466,7 @@ export function useStepExecutor( return { completed: true }; } else if (status === "FAILED") { setOnrampStatus("failed"); + throw new Error("Payment failed"); } return { completed: false }; @@ -517,12 +518,25 @@ export function useStepExecutor( } // Execute onramp first if configured and not already completed + let onrampCompleted = + preparedQuote.type !== "onramp" || onrampStatus === "completed"; if (preparedQuote.type === "onramp" && onrampStatus === "pending") { await executeOnramp( preparedQuote, completedStatusResults, abortController.signal, ); + onrampCompleted = true; + } + + // An onramp must complete before any follow-up transactions run or + // success is reported. + if (!onrampCompleted) { + throw new ApiError({ + code: "INTERNAL_SERVER_ERROR", + message: "Onramp did not complete", + statusCode: 500, + }); } if (flatTxs.length > 0) {