refactor(subscription): tighten payment flow boundaries
This commit is contained in:
@@ -6,12 +6,17 @@
|
||||
*/
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { savePendingEzpayOrder } from "@/lib/payment/pending_payment_order";
|
||||
import {
|
||||
getPaymentUrl,
|
||||
getStripeClientSecret,
|
||||
isEzpayPayment,
|
||||
launchEzpayRedirect,
|
||||
} from "@/lib/payment/payment_launch";
|
||||
import {
|
||||
usePaymentDispatch,
|
||||
usePaymentState,
|
||||
} from "@/stores/payment/payment-context";
|
||||
import { AppEnvUtil, Logger, Result } from "@/utils";
|
||||
import { AppEnvUtil, Logger } from "@/utils";
|
||||
|
||||
import { StripePaymentDialog } from "./stripe-payment-dialog";
|
||||
import dialogStyles from "./stripe-payment-dialog.module.css";
|
||||
@@ -69,7 +74,7 @@ export function SubscriptionCheckoutButton({
|
||||
orderId: payment.currentOrderId,
|
||||
paymentUrl,
|
||||
subscriptionType,
|
||||
returnTo,
|
||||
returnTo: returnTo ?? undefined,
|
||||
onFailed: (errorMessage) =>
|
||||
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage }),
|
||||
});
|
||||
@@ -159,7 +164,7 @@ export function SubscriptionCheckoutButton({
|
||||
orderId: payment.currentOrderId,
|
||||
paymentUrl: ezpayPaymentUrl,
|
||||
subscriptionType,
|
||||
returnTo,
|
||||
returnTo: returnTo ?? undefined,
|
||||
onFailed: (errorMessage) => {
|
||||
setIsConfirmingEzpay(false);
|
||||
paymentDispatch({ type: "PaymentLaunchFailed", errorMessage });
|
||||
@@ -219,97 +224,6 @@ export function SubscriptionCheckoutButton({
|
||||
);
|
||||
}
|
||||
|
||||
function getPaymentUrl(payParams: Record<string, unknown>): string | null {
|
||||
const keys = [
|
||||
"cashierUrl",
|
||||
"cashier_url",
|
||||
"checkoutUrl",
|
||||
"checkout_url",
|
||||
"paymentUrl",
|
||||
"payment_url",
|
||||
"approvalUrl",
|
||||
"approval_url",
|
||||
"redirectUrl",
|
||||
"redirect_url",
|
||||
"url",
|
||||
];
|
||||
|
||||
for (const key of keys) {
|
||||
const value = payParams[key];
|
||||
if (typeof value === "string" && value.length > 0) return value;
|
||||
}
|
||||
|
||||
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" | "topup";
|
||||
returnTo?: "chat" | null;
|
||||
onFailed: (errorMessage: string) => void;
|
||||
}
|
||||
|
||||
async function launchEzpayRedirect({
|
||||
orderId,
|
||||
paymentUrl,
|
||||
subscriptionType,
|
||||
returnTo,
|
||||
onFailed,
|
||||
}: RedirectToEzpayInput): Promise<void> {
|
||||
log.debug("[subscription-checkout] launchEzpayRedirect START", {
|
||||
hasOrderId: Boolean(orderId),
|
||||
orderId,
|
||||
subscriptionType,
|
||||
paymentUrl,
|
||||
});
|
||||
|
||||
if (!orderId) {
|
||||
const errorMessage = "Missing order id before opening Ezpay.";
|
||||
log.error("[subscription-checkout] pending ezpay order save skipped", {
|
||||
subscriptionType,
|
||||
paymentUrl,
|
||||
errorMessage,
|
||||
});
|
||||
onFailed(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const saveResult = await savePendingEzpayOrder({
|
||||
orderId,
|
||||
subscriptionType,
|
||||
...(returnTo ? { returnTo } : {}),
|
||||
});
|
||||
if (Result.isErr(saveResult)) {
|
||||
const errorMessage =
|
||||
"Could not save payment order before opening Ezpay. Please try again.";
|
||||
log.error("[subscription-checkout] pending ezpay order save failed", {
|
||||
orderId,
|
||||
subscriptionType,
|
||||
error: saveResult.error,
|
||||
});
|
||||
onFailed(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("[subscription-checkout] pending ezpay order saved", {
|
||||
orderId,
|
||||
subscriptionType,
|
||||
});
|
||||
|
||||
log.debug("[subscription-checkout] launchEzpayRedirect NOW", {
|
||||
orderId,
|
||||
subscriptionType,
|
||||
paymentUrl,
|
||||
});
|
||||
window.location.href = paymentUrl;
|
||||
}
|
||||
|
||||
interface EzpayRedirectConfirmDialogProps {
|
||||
orderId: string;
|
||||
isConfirming: boolean;
|
||||
@@ -363,22 +277,3 @@ function EzpayRedirectConfirmDialog({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getStripeClientSecret(
|
||||
payParams: Record<string, unknown>,
|
||||
): string | null {
|
||||
const provider = payParams.provider;
|
||||
const clientSecret = payParams.clientSecret ?? payParams.client_secret;
|
||||
const isStripeProvider =
|
||||
typeof provider !== "string" || provider.toLowerCase() === "stripe";
|
||||
|
||||
if (
|
||||
isStripeProvider &&
|
||||
typeof clientSecret === "string" &&
|
||||
clientSecret.length > 0
|
||||
) {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user