feat(payment): share payment method selector

This commit is contained in:
2026-07-17 18:27:24 +08:00
parent 66445a9972
commit b22db6d147
13 changed files with 386 additions and 173 deletions
+38 -3
View File
@@ -6,6 +6,8 @@ import { ArrowLeft, Heart, Sparkles } from "lucide-react";
import { CharacterAvatar } from "@/app/_components";
import { MobileShell } from "@/app/_components/core";
import { PaymentMethodSelector } from "@/app/_components/payment/payment-method-selector";
import { usePaymentMethodSelection } from "@/app/_hooks/use-payment-method-selection";
import { usePaymentPlanAnalytics } from "@/app/_hooks/use-payment-plan-analytics";
import { usePaymentRouteFlow } from "@/app/_hooks/use-payment-route-flow";
import type { PayChannel } from "@/data/schemas/payment";
@@ -13,6 +15,7 @@ import {
behaviorAnalytics,
type PaymentAnalyticsContext,
} from "@/lib/analytics";
import { getPaymentMethodConfig } from "@/lib/payment/payment_method";
import {
buildTipCoffeePath,
DEFAULT_TIP_COFFEE_TYPE,
@@ -26,6 +29,7 @@ import {
useActiveCharacterRoutes,
} from "@/providers/character-provider";
import { useAuthState } from "@/stores/auth/auth-context";
import { useUserState } from "@/stores/user/user-context";
import { TipCheckoutButton } from "./tip-checkout-button";
import {
@@ -59,6 +63,11 @@ export function TipScreen({
const character = useActiveCharacter();
const characterRoutes = useActiveCharacterRoutes();
const authState = useAuthState();
const userState = useUserState();
const paymentMethodConfig = getPaymentMethodConfig({
countryCode: userState.currentUser?.countryCode,
requestedPayChannel: initialPayChannel,
});
const [selectedCoffeeType, setSelectedCoffeeType] =
useState<TipCoffeeType>(coffeeType);
const coffeeOption = getTipCoffeeOption(selectedCoffeeType);
@@ -66,11 +75,9 @@ export function TipScreen({
selectedCoffeeType,
characterRoutes.tip,
);
const resolvedInitialPayChannel =
initialPayChannel ?? navigator.getDefaultPayChannel();
const { payment, paymentDispatch } = usePaymentRouteFlow({
catalog: "tip",
initialPayChannel: resolvedInitialPayChannel,
initialPayChannel: paymentMethodConfig.initialPayChannel,
paymentType: "tip",
shouldResumePendingOrder,
});
@@ -120,6 +127,21 @@ export function TipScreen({
payment.status !== "ready" || payment.isLoadingPlans || isPaymentBusy;
const isAuthLoading = !authState.hasInitialized || authState.isLoading;
const handlePaymentMethodChange = (payChannel: PayChannel) => {
paymentDispatch({
type: "PaymentPayChannelChanged",
payChannel,
});
};
usePaymentMethodSelection({
config: paymentMethodConfig,
currentPayChannel: payment.payChannel,
isPaymentBusy,
requestedPayChannel: initialPayChannel,
onChange: handlePaymentMethodChange,
});
useEffect(() => {
if (payment.isLoadingPlans || payment.isCreatingOrder || payment.isPollingOrder) {
return;
@@ -270,6 +292,19 @@ export function TipScreen({
/>
</section>
{paymentMethodConfig.canChoosePaymentMethod ? (
<section className={styles.paymentMethodSlot}>
<PaymentMethodSelector
value={payment.payChannel}
defaultChannel={paymentMethodConfig.initialPayChannel}
disabled={isPaymentBusy}
caption="GCash by default in the Philippines"
analyticsKey="tip.payment_method"
onChange={handlePaymentMethodChange}
/>
</section>
) : null}
{showMissingPlan ? (
<p className={styles.statusMessage} role="alert">
Coffee tip is not available yet. Please try again later.