feat(payment): restore ezpay return orders
This commit is contained in:
@@ -6,30 +6,45 @@
|
||||
*/
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { PendingPaymentOrderStorage } from "@/data/storage";
|
||||
import {
|
||||
usePaymentDispatch,
|
||||
usePaymentState,
|
||||
} from "@/stores/payment/payment-context";
|
||||
import { AppEnvUtil } from "@/utils";
|
||||
|
||||
import { StripePaymentDialog } from "./stripe-payment-dialog";
|
||||
import { SubscriptionCtaButton } from "./subscription-cta-button";
|
||||
import styles from "./subscription-cta-button.module.css";
|
||||
|
||||
const EZPAY_DEVELOPMENT_REDIRECT_DELAY_MS = 3000;
|
||||
|
||||
export interface SubscriptionCheckoutButtonProps {
|
||||
/** 是否可用(未选套餐 / 未勾选协议 → false) */
|
||||
disabled?: boolean;
|
||||
subscriptionType: "vip" | "voice";
|
||||
}
|
||||
|
||||
export function SubscriptionCheckoutButton({
|
||||
disabled = false,
|
||||
subscriptionType,
|
||||
}: SubscriptionCheckoutButtonProps) {
|
||||
const payment = usePaymentState();
|
||||
const paymentDispatch = usePaymentDispatch();
|
||||
const launchedNonceRef = useRef(0);
|
||||
const ezpayRedirectTimeoutRef = useRef<number | null>(null);
|
||||
const [hiddenStripeClientSecret, setHiddenStripeClientSecret] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (ezpayRedirectTimeoutRef.current !== null) {
|
||||
window.clearTimeout(ezpayRedirectTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!payment.payParams || payment.launchNonce <= launchedNonceRef.current) {
|
||||
return;
|
||||
@@ -43,6 +58,34 @@ export function SubscriptionCheckoutButton({
|
||||
|
||||
const paymentUrl = getPaymentUrl(payment.payParams);
|
||||
if (paymentUrl) {
|
||||
const isEzpay = isEzpayPayment(payment.payParams);
|
||||
|
||||
if (AppEnvUtil.isDevelopment() && isEzpay) {
|
||||
if (ezpayRedirectTimeoutRef.current !== null) {
|
||||
window.clearTimeout(ezpayRedirectTimeoutRef.current);
|
||||
}
|
||||
|
||||
void redirectToEzpay({
|
||||
orderId: payment.currentOrderId,
|
||||
paymentUrl,
|
||||
subscriptionType,
|
||||
delayMs: EZPAY_DEVELOPMENT_REDIRECT_DELAY_MS,
|
||||
setTimeoutId: (timeoutId) => {
|
||||
ezpayRedirectTimeoutRef.current = timeoutId;
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEzpay) {
|
||||
void redirectToEzpay({
|
||||
orderId: payment.currentOrderId,
|
||||
paymentUrl,
|
||||
subscriptionType,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = paymentUrl;
|
||||
return;
|
||||
}
|
||||
@@ -52,7 +95,13 @@ export function SubscriptionCheckoutButton({
|
||||
errorMessage:
|
||||
"Payment parameters did not include a supported URL or Stripe client secret.",
|
||||
});
|
||||
}, [payment.launchNonce, payment.payParams, paymentDispatch]);
|
||||
}, [
|
||||
payment.currentOrderId,
|
||||
payment.launchNonce,
|
||||
payment.payParams,
|
||||
paymentDispatch,
|
||||
subscriptionType,
|
||||
]);
|
||||
|
||||
const isLoading =
|
||||
payment.isCreatingOrder ||
|
||||
@@ -70,6 +119,10 @@ export function SubscriptionCheckoutButton({
|
||||
|
||||
const handleClick = () => {
|
||||
if (disabled || isLoading) return;
|
||||
if (ezpayRedirectTimeoutRef.current !== null) {
|
||||
window.clearTimeout(ezpayRedirectTimeoutRef.current);
|
||||
ezpayRedirectTimeoutRef.current = null;
|
||||
}
|
||||
setHiddenStripeClientSecret(null);
|
||||
paymentDispatch({ type: "PaymentCreateOrderSubmitted" });
|
||||
};
|
||||
@@ -155,6 +208,46 @@ function getPaymentUrl(payParams: Record<string, unknown>): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function isEzpayPayment(payParams: Record<string, unknown>): boolean {
|
||||
const provider = payParams.provider;
|
||||
return typeof provider === "string" && provider.toLowerCase() === "ezpay";
|
||||
}
|
||||
|
||||
interface RedirectToEzpayInput {
|
||||
orderId: string | null;
|
||||
paymentUrl: string;
|
||||
subscriptionType: "vip" | "voice";
|
||||
delayMs?: number;
|
||||
setTimeoutId?: (timeoutId: number) => void;
|
||||
}
|
||||
|
||||
async function redirectToEzpay({
|
||||
orderId,
|
||||
paymentUrl,
|
||||
subscriptionType,
|
||||
delayMs = 0,
|
||||
setTimeoutId,
|
||||
}: RedirectToEzpayInput): Promise<void> {
|
||||
if (orderId) {
|
||||
await PendingPaymentOrderStorage.setPendingOrder({
|
||||
orderId,
|
||||
payChannel: "ezpay",
|
||||
subscriptionType,
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
if (delayMs > 0) {
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
window.location.href = paymentUrl;
|
||||
}, delayMs);
|
||||
setTimeoutId?.(timeoutId);
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = paymentUrl;
|
||||
}
|
||||
|
||||
function getStripeClientSecret(
|
||||
payParams: Record<string, unknown>,
|
||||
): string | null {
|
||||
|
||||
Reference in New Issue
Block a user