diff --git a/src/app/subscription/components/index.ts b/src/app/subscription/components/index.ts
index ddc3133b..19cb0abb 100644
--- a/src/app/subscription/components/index.ts
+++ b/src/app/subscription/components/index.ts
@@ -7,6 +7,7 @@ export * from "./subscription-banner";
export * from "./subscription-benefits-card";
export * from "./subscription-checkout-button";
export * from "./subscription-cta-button";
+export * from "./subscription-payment-method";
export * from "./subscription-plan-card";
export * from "./subscription-user-row";
export * from "./stripe-payment-dialog";
diff --git a/src/app/subscription/components/subscription-cta-button.tsx b/src/app/subscription/components/subscription-cta-button.tsx
index f59a4038..f138befd 100644
--- a/src/app/subscription/components/subscription-cta-button.tsx
+++ b/src/app/subscription/components/subscription-cta-button.tsx
@@ -1,13 +1,5 @@
"use client";
-/**
- * 订阅页主操作按钮
- *
- * 视觉规格(与设计稿对齐):
- * - 全宽,52px 高,--radius-xxxl 圆角
- * - 粉色渐变(#f96ADE → #f657A0)
- * - 白字 16px/600 + 粉色阴影
- * - :disabled 灰化
- */
+
import type { ButtonHTMLAttributes, ReactNode } from "react";
import styles from "./subscription-cta-button.module.css";
diff --git a/src/app/subscription/components/subscription-payment-method.module.css b/src/app/subscription/components/subscription-payment-method.module.css
new file mode 100644
index 00000000..91b73247
--- /dev/null
+++ b/src/app/subscription/components/subscription-payment-method.module.css
@@ -0,0 +1,80 @@
+.root {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: var(--spacing-sm);
+ padding: 0 var(--spacing-xs);
+}
+
+.title {
+ margin: 0;
+ font-family: var(--font-athelas), var(--font-system);
+ font-size: var(--font-size-lg);
+ font-weight: 700;
+ color: var(--color-auth-text-primary);
+ line-height: 1.2;
+}
+
+.caption {
+ color: var(--color-text-secondary);
+ font-size: var(--font-size-sm);
+ line-height: 1.2;
+ white-space: nowrap;
+}
+
+.options {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--spacing-sm);
+}
+
+.option {
+ display: flex;
+ min-height: 62px;
+ align-items: center;
+ justify-content: center;
+ padding: 10px 14px;
+ border: 1px solid rgba(246, 87, 160, 0.18);
+ border-radius: 18px;
+ background: #ffffff;
+ box-shadow: 0 5px 7px 0 rgba(247, 89, 168, 0.08);
+ color: #3c3b3b;
+ cursor: pointer;
+ font: inherit;
+ text-align: center;
+ transition: border-color 0.15s ease, box-shadow 0.15s ease,
+ transform 0.05s ease;
+}
+
+.option:focus-visible {
+ outline: 2px solid var(--color-accent);
+ outline-offset: 2px;
+}
+
+.option:not(:disabled):active {
+ transform: scale(0.98);
+}
+
+.option:disabled {
+ cursor: not-allowed;
+ opacity: 0.72;
+}
+
+.selected {
+ border-color: #f657a0;
+ border-width: 2px;
+ box-shadow: 0 6px 12px 0 rgba(247, 89, 168, 0.14);
+}
+
+.optionTitle {
+ color: #1e1e1e;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 1.1;
+}
diff --git a/src/app/subscription/components/subscription-payment-method.tsx b/src/app/subscription/components/subscription-payment-method.tsx
new file mode 100644
index 00000000..2a2d1b54
--- /dev/null
+++ b/src/app/subscription/components/subscription-payment-method.tsx
@@ -0,0 +1,66 @@
+"use client";
+
+import type { PayChannel } from "@/data/dto/payment";
+
+import styles from "./subscription-payment-method.module.css";
+
+const PAYMENT_METHODS: Array<{
+ channel: PayChannel;
+ title: string;
+}> = [
+ {
+ channel: "stripe",
+ title: "Stripe",
+ },
+ {
+ channel: "ezpay",
+ title: "Ezpay",
+ },
+];
+
+export interface SubscriptionPaymentMethodProps {
+ value: PayChannel;
+ disabled?: boolean;
+ onChange: (channel: PayChannel) => void;
+}
+
+export function SubscriptionPaymentMethod({
+ value,
+ disabled = false,
+ onChange,
+}: SubscriptionPaymentMethodProps) {
+ return (
+
+ Payment Method
+
+ Stripe by default
+