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
+21 -43
View File
@@ -1,8 +1,10 @@
"use client";
import { useEffect, useMemo, useRef } from "react";
import { useEffect, useMemo } from "react";
import { BackButton } from "@/app/_components";
import { Checkbox, 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 { AppConstants } from "@/core/app_constants";
import type { PayChannel } from "@/data/schemas/payment";
@@ -13,22 +15,20 @@ import {
getDefaultPaymentAnalyticsContext,
type PaymentAnalyticsContext,
} from "@/lib/analytics";
import { getPaymentMethodConfig } from "@/lib/payment/payment_method";
import { useUserState } from "@/stores/user/user-context";
import {
SubscriptionCheckoutButton,
SubscriptionCoinsOfferSection,
SubscriptionPaymentMethod,
SubscriptionPaymentSuccessDialog,
SubscriptionVipOfferSection,
} from "./components";
import styles from "./components/subscription-screen.module.css";
import {
findSelectedSubscriptionPlan,
canChooseSubscriptionPayChannel,
getFirstRechargeOfferView,
getDefaultSubscriptionPlanId,
resolveSubscriptionPayChannel,
toCoinsOfferPlanViews,
toVipOfferPlanViews,
type SubscriptionType,
@@ -56,13 +56,10 @@ export function SubscriptionScreen({
}: SubscriptionScreenProps) {
const userState = useUserState();
const countryCode = userState.currentUser?.countryCode;
const canChoosePaymentMethod =
canChooseSubscriptionPayChannel(countryCode);
const resolvedInitialPayChannel = resolveSubscriptionPayChannel({
const paymentMethodConfig = getPaymentMethodConfig({
countryCode,
requestedPayChannel: initialPayChannel,
});
const phDefaultPayChannelAppliedRef = useRef(false);
const {
payment,
paymentDispatch,
@@ -74,7 +71,7 @@ export function SubscriptionScreen({
shouldResumePendingOrder,
returnTo,
characterSlug,
initialPayChannel: resolvedInitialPayChannel,
initialPayChannel: paymentMethodConfig.initialPayChannel,
});
const canSubscribeVip = subscriptionType === "vip";
const analyticsContext =
@@ -134,35 +131,20 @@ export function SubscriptionScreen({
paymentDispatch({ type: "PaymentPlanSelected", planId });
};
useEffect(() => {
if (isPaymentBusy) return;
if (!canChoosePaymentMethod) {
if (payment.payChannel === "stripe") return;
paymentDispatch({
type: "PaymentPayChannelChanged",
payChannel: "stripe",
});
return;
}
if (initialPayChannel !== null) return;
if (phDefaultPayChannelAppliedRef.current) return;
phDefaultPayChannelAppliedRef.current = true;
if (payment.payChannel === resolvedInitialPayChannel) return;
const handlePaymentMethodChange = (payChannel: PayChannel) => {
paymentDispatch({
type: "PaymentPayChannelChanged",
payChannel: resolvedInitialPayChannel,
payChannel,
});
}, [
canChoosePaymentMethod,
initialPayChannel,
};
usePaymentMethodSelection({
config: paymentMethodConfig,
currentPayChannel: payment.payChannel,
isPaymentBusy,
payment.payChannel,
paymentDispatch,
resolvedInitialPayChannel,
]);
requestedPayChannel: initialPayChannel,
onChange: handlePaymentMethodChange,
});
useEffect(() => {
const defaultPlanId = getDefaultSubscriptionPlanId({
@@ -242,19 +224,15 @@ export function SubscriptionScreen({
/>
</div>
{canChoosePaymentMethod ? (
{paymentMethodConfig.canChoosePaymentMethod ? (
<section className={styles.paymentMethodSlot}>
<SubscriptionPaymentMethod
<PaymentMethodSelector
value={payment.payChannel}
defaultChannel={resolvedInitialPayChannel}
defaultChannel={paymentMethodConfig.initialPayChannel}
disabled={isPaymentBusy}
caption="GCash by default in the Philippines"
onChange={(payChannel) => {
paymentDispatch({
type: "PaymentPayChannelChanged",
payChannel,
});
}}
analyticsKey="subscription.payment_method"
onChange={handlePaymentMethodChange}
/>
</section>
) : null}