refactor(payment): share launch flow
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { shouldShowEzpayConfirmation } from "../use-payment-launch-flow";
|
||||||
|
|
||||||
|
describe("shouldShowEzpayConfirmation", () => {
|
||||||
|
it("shows confirmation for Ezpay in non-production builds", () => {
|
||||||
|
expect(
|
||||||
|
shouldShowEzpayConfirmation({
|
||||||
|
currentOrderId: "order-1",
|
||||||
|
isProduction: false,
|
||||||
|
payParams: { provider: "ezpay" },
|
||||||
|
paymentUrl: "https://pay.example/checkout",
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show confirmation in production", () => {
|
||||||
|
expect(
|
||||||
|
shouldShowEzpayConfirmation({
|
||||||
|
currentOrderId: "order-1",
|
||||||
|
isProduction: true,
|
||||||
|
payParams: { provider: "ezpay" },
|
||||||
|
paymentUrl: "https://pay.example/checkout",
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show confirmation without an order id", () => {
|
||||||
|
expect(
|
||||||
|
shouldShowEzpayConfirmation({
|
||||||
|
currentOrderId: null,
|
||||||
|
isProduction: false,
|
||||||
|
payParams: { provider: "ezpay" },
|
||||||
|
paymentUrl: "https://pay.example/checkout",
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show confirmation for non-Ezpay providers", () => {
|
||||||
|
expect(
|
||||||
|
shouldShowEzpayConfirmation({
|
||||||
|
currentOrderId: "order-1",
|
||||||
|
isProduction: false,
|
||||||
|
payParams: { provider: "stripe" },
|
||||||
|
paymentUrl: "https://pay.example/checkout",
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { type Dispatch, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getPaymentUrl,
|
||||||
|
getStripeClientSecret,
|
||||||
|
isEzpayPayment,
|
||||||
|
launchEzpayRedirect,
|
||||||
|
} from "@/lib/payment/payment_launch";
|
||||||
|
import type {
|
||||||
|
PendingPaymentReturnTo,
|
||||||
|
PendingPaymentSubscriptionType,
|
||||||
|
} from "@/lib/payment/pending_payment_order";
|
||||||
|
import type {
|
||||||
|
PaymentContextState,
|
||||||
|
} from "@/stores/payment/payment-context";
|
||||||
|
import type { PaymentEvent } from "@/stores/payment/payment-events";
|
||||||
|
import { AppEnvUtil, Logger } from "@/utils";
|
||||||
|
|
||||||
|
const UNSUPPORTED_PAYMENT_PARAMS_MESSAGE =
|
||||||
|
"Payment parameters did not include a supported URL or Stripe client secret.";
|
||||||
|
|
||||||
|
export interface ShouldShowEzpayConfirmationInput {
|
||||||
|
currentOrderId: string | null;
|
||||||
|
isProduction: boolean;
|
||||||
|
payParams: Record<string, unknown> | null;
|
||||||
|
paymentUrl: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UsePaymentLaunchFlowInput {
|
||||||
|
log: Logger;
|
||||||
|
logScope: string;
|
||||||
|
payment: PaymentContextState;
|
||||||
|
paymentDispatch: Dispatch<PaymentEvent>;
|
||||||
|
returnTo?: PendingPaymentReturnTo;
|
||||||
|
subscriptionType: PendingPaymentSubscriptionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaymentLaunchFlow {
|
||||||
|
ezpayPaymentUrl: string | null;
|
||||||
|
handleEzpayCancel: () => void;
|
||||||
|
handleEzpayConfirm: () => void;
|
||||||
|
handleStripeClose: () => void;
|
||||||
|
handleStripeConfirmed: () => void;
|
||||||
|
isConfirmingEzpay: boolean;
|
||||||
|
resetLaunchState: () => void;
|
||||||
|
shouldShowEzpayConfirmDialog: boolean;
|
||||||
|
shouldShowStripeDialog: boolean;
|
||||||
|
stripeClientSecret: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldShowEzpayConfirmation({
|
||||||
|
currentOrderId,
|
||||||
|
isProduction,
|
||||||
|
payParams,
|
||||||
|
paymentUrl,
|
||||||
|
}: ShouldShowEzpayConfirmationInput): boolean {
|
||||||
|
return Boolean(
|
||||||
|
!isProduction &&
|
||||||
|
payParams &&
|
||||||
|
isEzpayPayment(payParams) &&
|
||||||
|
paymentUrl &&
|
||||||
|
currentOrderId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePaymentLaunchFlow({
|
||||||
|
log,
|
||||||
|
logScope,
|
||||||
|
payment,
|
||||||
|
paymentDispatch,
|
||||||
|
returnTo,
|
||||||
|
subscriptionType,
|
||||||
|
}: UsePaymentLaunchFlowInput): PaymentLaunchFlow {
|
||||||
|
const launchedNonceRef = useRef(0);
|
||||||
|
const [hiddenStripeClientSecret, setHiddenStripeClientSecret] = useState<
|
||||||
|
string | null
|
||||||
|
>(null);
|
||||||
|
const [isConfirmingEzpay, setIsConfirmingEzpay] = useState(false);
|
||||||
|
const stripeClientSecret = payment.payParams
|
||||||
|
? getStripeClientSecret(payment.payParams)
|
||||||
|
: null;
|
||||||
|
const ezpayPaymentUrl = payment.payParams
|
||||||
|
? getPaymentUrl(payment.payParams)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!payment.payParams || payment.launchNonce <= launchedNonceRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
launchedNonceRef.current = payment.launchNonce;
|
||||||
|
const clientSecret = getStripeClientSecret(payment.payParams);
|
||||||
|
if (clientSecret) return;
|
||||||
|
|
||||||
|
const paymentUrl = getPaymentUrl(payment.payParams);
|
||||||
|
if (paymentUrl) {
|
||||||
|
const isEzpay = isEzpayPayment(payment.payParams);
|
||||||
|
|
||||||
|
if (!AppEnvUtil.isProduction() && isEzpay) {
|
||||||
|
log.debug(`[${logScope}] ezpay confirmation required`, {
|
||||||
|
orderId: payment.currentOrderId,
|
||||||
|
paymentUrl,
|
||||||
|
subscriptionType,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEzpay) {
|
||||||
|
void launchEzpayRedirect({
|
||||||
|
orderId: payment.currentOrderId,
|
||||||
|
paymentUrl,
|
||||||
|
subscriptionType,
|
||||||
|
...(returnTo ? { returnTo } : {}),
|
||||||
|
onFailed: (errorMessage) =>
|
||||||
|
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage }),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = paymentUrl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentDispatch({
|
||||||
|
type: "PaymentLaunchFailed",
|
||||||
|
errorMessage: UNSUPPORTED_PAYMENT_PARAMS_MESSAGE,
|
||||||
|
});
|
||||||
|
}, [
|
||||||
|
log,
|
||||||
|
logScope,
|
||||||
|
payment.currentOrderId,
|
||||||
|
payment.launchNonce,
|
||||||
|
payment.payParams,
|
||||||
|
paymentDispatch,
|
||||||
|
returnTo,
|
||||||
|
subscriptionType,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const shouldShowStripeDialog =
|
||||||
|
stripeClientSecret !== null &&
|
||||||
|
stripeClientSecret !== hiddenStripeClientSecret &&
|
||||||
|
!payment.isPaid;
|
||||||
|
const shouldShowEzpayConfirmDialog = shouldShowEzpayConfirmation({
|
||||||
|
currentOrderId: payment.currentOrderId,
|
||||||
|
isProduction: AppEnvUtil.isProduction(),
|
||||||
|
payParams: payment.payParams,
|
||||||
|
paymentUrl: ezpayPaymentUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
function resetLaunchState(): void {
|
||||||
|
setIsConfirmingEzpay(false);
|
||||||
|
setHiddenStripeClientSecret(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStripeClose(): void {
|
||||||
|
if (stripeClientSecret) {
|
||||||
|
setHiddenStripeClientSecret(stripeClientSecret);
|
||||||
|
}
|
||||||
|
if (payment.orderStatus !== "paid") {
|
||||||
|
paymentDispatch({ type: "PaymentReset" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStripeConfirmed(): void {
|
||||||
|
if (stripeClientSecret) {
|
||||||
|
setHiddenStripeClientSecret(stripeClientSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEzpayConfirm(): void {
|
||||||
|
if (!payment.currentOrderId || !ezpayPaymentUrl) return;
|
||||||
|
setIsConfirmingEzpay(true);
|
||||||
|
void launchEzpayRedirect({
|
||||||
|
orderId: payment.currentOrderId,
|
||||||
|
paymentUrl: ezpayPaymentUrl,
|
||||||
|
subscriptionType,
|
||||||
|
...(returnTo ? { returnTo } : {}),
|
||||||
|
onFailed: (errorMessage) => {
|
||||||
|
setIsConfirmingEzpay(false);
|
||||||
|
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEzpayCancel(): void {
|
||||||
|
log.debug(`[${logScope}] ezpay confirmation cancelled`, {
|
||||||
|
orderId: payment.currentOrderId,
|
||||||
|
subscriptionType,
|
||||||
|
});
|
||||||
|
setIsConfirmingEzpay(false);
|
||||||
|
paymentDispatch({ type: "PaymentReset" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ezpayPaymentUrl,
|
||||||
|
handleEzpayCancel,
|
||||||
|
handleEzpayConfirm,
|
||||||
|
handleStripeClose,
|
||||||
|
handleStripeConfirmed,
|
||||||
|
isConfirmingEzpay,
|
||||||
|
resetLaunchState,
|
||||||
|
shouldShowEzpayConfirmDialog,
|
||||||
|
shouldShowStripeDialog,
|
||||||
|
stripeClientSecret,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,19 +4,12 @@
|
|||||||
*
|
*
|
||||||
* 包装 `SubscriptionCtaButton` —— 通过 payment 状态机创建后端订单并拉起支付。
|
* 包装 `SubscriptionCtaButton` —— 通过 payment 状态机创建后端订单并拉起支付。
|
||||||
*/
|
*/
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { usePaymentLaunchFlow } from "@/app/_hooks/use-payment-launch-flow";
|
||||||
|
|
||||||
import {
|
|
||||||
getPaymentUrl,
|
|
||||||
getStripeClientSecret,
|
|
||||||
isEzpayPayment,
|
|
||||||
launchEzpayRedirect,
|
|
||||||
} from "@/lib/payment/payment_launch";
|
|
||||||
import {
|
import {
|
||||||
usePaymentDispatch,
|
usePaymentDispatch,
|
||||||
usePaymentState,
|
usePaymentState,
|
||||||
} from "@/stores/payment/payment-context";
|
} from "@/stores/payment/payment-context";
|
||||||
import { AppEnvUtil, Logger } from "@/utils";
|
import { Logger } from "@/utils";
|
||||||
|
|
||||||
import { StripePaymentDialog } from "./stripe-payment-dialog";
|
import { StripePaymentDialog } from "./stripe-payment-dialog";
|
||||||
import dialogStyles from "./stripe-payment-dialog.module.css";
|
import dialogStyles from "./stripe-payment-dialog.module.css";
|
||||||
@@ -39,65 +32,14 @@ export function SubscriptionCheckoutButton({
|
|||||||
}: SubscriptionCheckoutButtonProps) {
|
}: SubscriptionCheckoutButtonProps) {
|
||||||
const payment = usePaymentState();
|
const payment = usePaymentState();
|
||||||
const paymentDispatch = usePaymentDispatch();
|
const paymentDispatch = usePaymentDispatch();
|
||||||
const launchedNonceRef = useRef(0);
|
const paymentLaunch = usePaymentLaunchFlow({
|
||||||
const [hiddenStripeClientSecret, setHiddenStripeClientSecret] = useState<
|
log,
|
||||||
string | null
|
logScope: "subscription-checkout",
|
||||||
>(null);
|
payment,
|
||||||
const [isConfirmingEzpay, setIsConfirmingEzpay] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!payment.payParams || payment.launchNonce <= launchedNonceRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
launchedNonceRef.current = payment.launchNonce;
|
|
||||||
const clientSecret = getStripeClientSecret(payment.payParams);
|
|
||||||
if (clientSecret) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const paymentUrl = getPaymentUrl(payment.payParams);
|
|
||||||
if (paymentUrl) {
|
|
||||||
const isEzpay = isEzpayPayment(payment.payParams);
|
|
||||||
|
|
||||||
if (!AppEnvUtil.isProduction() && isEzpay) {
|
|
||||||
log.debug("[subscription-checkout] ezpay confirmation required", {
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
paymentUrl,
|
|
||||||
subscriptionType,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEzpay) {
|
|
||||||
void launchEzpayRedirect({
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
paymentUrl,
|
|
||||||
subscriptionType,
|
|
||||||
returnTo: returnTo ?? undefined,
|
|
||||||
onFailed: (errorMessage) =>
|
|
||||||
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage }),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.href = paymentUrl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentDispatch({
|
|
||||||
type: "PaymentLaunchFailed",
|
|
||||||
errorMessage:
|
|
||||||
"Payment parameters did not include a supported URL or Stripe client secret.",
|
|
||||||
});
|
|
||||||
}, [
|
|
||||||
payment.currentOrderId,
|
|
||||||
payment.launchNonce,
|
|
||||||
payment.payParams,
|
|
||||||
paymentDispatch,
|
paymentDispatch,
|
||||||
returnTo,
|
returnTo: returnTo ?? undefined,
|
||||||
subscriptionType,
|
subscriptionType,
|
||||||
]);
|
});
|
||||||
|
|
||||||
const isLoading =
|
const isLoading =
|
||||||
payment.isCreatingOrder ||
|
payment.isCreatingOrder ||
|
||||||
@@ -118,69 +60,10 @@ export function SubscriptionCheckoutButton({
|
|||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (disabled || isLoading) return;
|
if (disabled || isLoading) return;
|
||||||
setIsConfirmingEzpay(false);
|
paymentLaunch.resetLaunchState();
|
||||||
setHiddenStripeClientSecret(null);
|
|
||||||
paymentDispatch({ type: "PaymentCreateOrderSubmitted" });
|
paymentDispatch({ type: "PaymentCreateOrderSubmitted" });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStripeClose = () => {
|
|
||||||
if (stripeClientSecret) {
|
|
||||||
setHiddenStripeClientSecret(stripeClientSecret);
|
|
||||||
}
|
|
||||||
if (payment.orderStatus !== "paid") {
|
|
||||||
paymentDispatch({ type: "PaymentReset" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStripeConfirmed = () => {
|
|
||||||
if (stripeClientSecret) {
|
|
||||||
setHiddenStripeClientSecret(stripeClientSecret);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const stripeClientSecret = payment.payParams
|
|
||||||
? getStripeClientSecret(payment.payParams)
|
|
||||||
: null;
|
|
||||||
const shouldShowStripeDialog =
|
|
||||||
stripeClientSecret !== null &&
|
|
||||||
stripeClientSecret !== hiddenStripeClientSecret &&
|
|
||||||
!payment.isPaid;
|
|
||||||
const ezpayPaymentUrl = payment.payParams
|
|
||||||
? getPaymentUrl(payment.payParams)
|
|
||||||
: null;
|
|
||||||
const shouldShowEzpayConfirmDialog =
|
|
||||||
!AppEnvUtil.isProduction() &&
|
|
||||||
payment.payParams &&
|
|
||||||
isEzpayPayment(payment.payParams) &&
|
|
||||||
ezpayPaymentUrl &&
|
|
||||||
payment.currentOrderId
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
|
|
||||||
const handleEzpayConfirm = () => {
|
|
||||||
if (!payment.currentOrderId || !ezpayPaymentUrl) return;
|
|
||||||
setIsConfirmingEzpay(true);
|
|
||||||
void launchEzpayRedirect({
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
paymentUrl: ezpayPaymentUrl,
|
|
||||||
subscriptionType,
|
|
||||||
returnTo: returnTo ?? undefined,
|
|
||||||
onFailed: (errorMessage) => {
|
|
||||||
setIsConfirmingEzpay(false);
|
|
||||||
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEzpayCancel = () => {
|
|
||||||
log.debug("[subscription-checkout] ezpay confirmation cancelled", {
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
subscriptionType,
|
|
||||||
});
|
|
||||||
setIsConfirmingEzpay(false);
|
|
||||||
paymentDispatch({ type: "PaymentReset" });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SubscriptionCtaButton
|
<SubscriptionCtaButton
|
||||||
@@ -205,19 +88,19 @@ export function SubscriptionCheckoutButton({
|
|||||||
{payment.errorMessage}
|
{payment.errorMessage}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{shouldShowEzpayConfirmDialog ? (
|
{paymentLaunch.shouldShowEzpayConfirmDialog ? (
|
||||||
<EzpayRedirectConfirmDialog
|
<EzpayRedirectConfirmDialog
|
||||||
orderId={payment.currentOrderId ?? ""}
|
orderId={payment.currentOrderId ?? ""}
|
||||||
isConfirming={isConfirmingEzpay}
|
isConfirming={paymentLaunch.isConfirmingEzpay}
|
||||||
onCancel={handleEzpayCancel}
|
onCancel={paymentLaunch.handleEzpayCancel}
|
||||||
onConfirm={handleEzpayConfirm}
|
onConfirm={paymentLaunch.handleEzpayConfirm}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{shouldShowStripeDialog ? (
|
{paymentLaunch.shouldShowStripeDialog && paymentLaunch.stripeClientSecret ? (
|
||||||
<StripePaymentDialog
|
<StripePaymentDialog
|
||||||
clientSecret={stripeClientSecret}
|
clientSecret={paymentLaunch.stripeClientSecret}
|
||||||
onClose={handleStripeClose}
|
onClose={paymentLaunch.handleStripeClose}
|
||||||
onConfirmed={handleStripeConfirmed}
|
onConfirmed={paymentLaunch.handleStripeConfirmed}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,19 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { usePaymentLaunchFlow } from "@/app/_hooks/use-payment-launch-flow";
|
||||||
|
|
||||||
import {
|
|
||||||
getPaymentUrl,
|
|
||||||
getStripeClientSecret,
|
|
||||||
isEzpayPayment,
|
|
||||||
launchEzpayRedirect,
|
|
||||||
} from "@/lib/payment/payment_launch";
|
|
||||||
import { ROUTES } from "@/router/routes";
|
import { ROUTES } from "@/router/routes";
|
||||||
import {
|
import {
|
||||||
usePaymentDispatch,
|
usePaymentDispatch,
|
||||||
usePaymentState,
|
usePaymentState,
|
||||||
} from "@/stores/payment/payment-context";
|
} from "@/stores/payment/payment-context";
|
||||||
import { AppEnvUtil, Logger } from "@/utils";
|
import { Logger } from "@/utils";
|
||||||
|
|
||||||
import { StripePaymentDialog } from "../subscription/components/stripe-payment-dialog";
|
import { StripePaymentDialog } from "../subscription/components/stripe-payment-dialog";
|
||||||
import dialogStyles from "../subscription/components/stripe-payment-dialog.module.css";
|
import dialogStyles from "../subscription/components/stripe-payment-dialog.module.css";
|
||||||
@@ -34,80 +27,16 @@ export function TipCheckoutButton({
|
|||||||
}: TipCheckoutButtonProps) {
|
}: TipCheckoutButtonProps) {
|
||||||
const payment = usePaymentState();
|
const payment = usePaymentState();
|
||||||
const paymentDispatch = usePaymentDispatch();
|
const paymentDispatch = usePaymentDispatch();
|
||||||
const launchedNonceRef = useRef(0);
|
const paymentLaunch = usePaymentLaunchFlow({
|
||||||
const [hiddenStripeClientSecret, setHiddenStripeClientSecret] = useState<
|
log,
|
||||||
string | null
|
logScope: "tip-checkout",
|
||||||
>(null);
|
payment,
|
||||||
const [isConfirmingEzpay, setIsConfirmingEzpay] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!payment.payParams || payment.launchNonce <= launchedNonceRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
launchedNonceRef.current = payment.launchNonce;
|
|
||||||
const clientSecret = getStripeClientSecret(payment.payParams);
|
|
||||||
if (clientSecret) return;
|
|
||||||
|
|
||||||
const paymentUrl = getPaymentUrl(payment.payParams);
|
|
||||||
if (paymentUrl) {
|
|
||||||
const isEzpay = isEzpayPayment(payment.payParams);
|
|
||||||
|
|
||||||
if (!AppEnvUtil.isProduction() && isEzpay) {
|
|
||||||
log.debug("[tip-checkout] ezpay confirmation required", {
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
paymentUrl,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEzpay) {
|
|
||||||
void launchEzpayRedirect({
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
paymentUrl,
|
|
||||||
subscriptionType: "tip",
|
|
||||||
onFailed: (errorMessage) =>
|
|
||||||
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage }),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.href = paymentUrl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentDispatch({
|
|
||||||
type: "PaymentLaunchFailed",
|
|
||||||
errorMessage:
|
|
||||||
"Payment parameters did not include a supported URL or Stripe client secret.",
|
|
||||||
});
|
|
||||||
}, [
|
|
||||||
payment.currentOrderId,
|
|
||||||
payment.launchNonce,
|
|
||||||
payment.payParams,
|
|
||||||
paymentDispatch,
|
paymentDispatch,
|
||||||
]);
|
subscriptionType: "tip",
|
||||||
|
});
|
||||||
|
|
||||||
const isLoading =
|
const isLoading =
|
||||||
isAuthLoading || payment.isCreatingOrder || payment.isPollingOrder;
|
isAuthLoading || payment.isCreatingOrder || payment.isPollingOrder;
|
||||||
const stripeClientSecret = payment.payParams
|
|
||||||
? getStripeClientSecret(payment.payParams)
|
|
||||||
: null;
|
|
||||||
const shouldShowStripeDialog =
|
|
||||||
stripeClientSecret !== null &&
|
|
||||||
stripeClientSecret !== hiddenStripeClientSecret &&
|
|
||||||
!payment.isPaid;
|
|
||||||
const ezpayPaymentUrl = payment.payParams
|
|
||||||
? getPaymentUrl(payment.payParams)
|
|
||||||
: null;
|
|
||||||
const shouldShowEzpayConfirmDialog =
|
|
||||||
!AppEnvUtil.isProduction() &&
|
|
||||||
payment.payParams &&
|
|
||||||
isEzpayPayment(payment.payParams) &&
|
|
||||||
ezpayPaymentUrl &&
|
|
||||||
payment.currentOrderId
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
const label = payment.isPollingOrder
|
const label = payment.isPollingOrder
|
||||||
? "Processing payment..."
|
? "Processing payment..."
|
||||||
: payment.isCreatingOrder
|
: payment.isCreatingOrder
|
||||||
@@ -116,43 +45,6 @@ export function TipCheckoutButton({
|
|||||||
? "Thanks for the coffee"
|
? "Thanks for the coffee"
|
||||||
: "Order and Buy";
|
: "Order and Buy";
|
||||||
|
|
||||||
const handleStripeClose = () => {
|
|
||||||
if (stripeClientSecret) {
|
|
||||||
setHiddenStripeClientSecret(stripeClientSecret);
|
|
||||||
}
|
|
||||||
if (payment.orderStatus !== "paid") {
|
|
||||||
paymentDispatch({ type: "PaymentReset" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStripeConfirmed = () => {
|
|
||||||
if (stripeClientSecret) {
|
|
||||||
setHiddenStripeClientSecret(stripeClientSecret);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEzpayConfirm = () => {
|
|
||||||
if (!payment.currentOrderId || !ezpayPaymentUrl) return;
|
|
||||||
setIsConfirmingEzpay(true);
|
|
||||||
void launchEzpayRedirect({
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
paymentUrl: ezpayPaymentUrl,
|
|
||||||
subscriptionType: "tip",
|
|
||||||
onFailed: (errorMessage) => {
|
|
||||||
setIsConfirmingEzpay(false);
|
|
||||||
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEzpayCancel = () => {
|
|
||||||
log.debug("[tip-checkout] ezpay confirmation cancelled", {
|
|
||||||
orderId: payment.currentOrderId,
|
|
||||||
});
|
|
||||||
setIsConfirmingEzpay(false);
|
|
||||||
paymentDispatch({ type: "PaymentReset" });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
@@ -168,20 +60,20 @@ export function TipCheckoutButton({
|
|||||||
{payment.errorMessage}
|
{payment.errorMessage}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{shouldShowEzpayConfirmDialog ? (
|
{paymentLaunch.shouldShowEzpayConfirmDialog ? (
|
||||||
<EzpayRedirectConfirmDialog
|
<EzpayRedirectConfirmDialog
|
||||||
orderId={payment.currentOrderId ?? ""}
|
orderId={payment.currentOrderId ?? ""}
|
||||||
isConfirming={isConfirmingEzpay}
|
isConfirming={paymentLaunch.isConfirmingEzpay}
|
||||||
onCancel={handleEzpayCancel}
|
onCancel={paymentLaunch.handleEzpayCancel}
|
||||||
onConfirm={handleEzpayConfirm}
|
onConfirm={paymentLaunch.handleEzpayConfirm}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{shouldShowStripeDialog ? (
|
{paymentLaunch.shouldShowStripeDialog && paymentLaunch.stripeClientSecret ? (
|
||||||
<StripePaymentDialog
|
<StripePaymentDialog
|
||||||
clientSecret={stripeClientSecret}
|
clientSecret={paymentLaunch.stripeClientSecret}
|
||||||
returnPath={ROUTES.tip}
|
returnPath={ROUTES.tip}
|
||||||
onClose={handleStripeClose}
|
onClose={paymentLaunch.handleStripeClose}
|
||||||
onConfirmed={handleStripeConfirmed}
|
onConfirmed={paymentLaunch.handleStripeConfirmed}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user