feat(marketplace-primary-scope): Implement subscription tier system with merchant management, worker earnings display UI, and backend tier services

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-02-22 05:20:05 -08:00
parent b33d90ca41
commit 6de60a4cee
16 changed files with 317 additions and 72 deletions

View file

@ -121,6 +121,7 @@ const FangirlPage = lazy(() =>
const CamgirlPage = lazy(() =>
import('./pages/work').then((m) => ({ default: m.CamgirlPage }))
)
const WorkerEarningsPage = lazy(() => import('./pages/WorkerEarningsPage'))
// Customer section pages
const ClientPage = lazy(() =>
@ -156,6 +157,10 @@ const AppRoutes = () => {
path={RoutePatterns.workers}
element={<LazyRoute><ForWorkersPage /></LazyRoute>}
/>
<Route
path={RoutePatterns.workersEarnings}
element={<LazyRoute><WorkerEarningsPage /></LazyRoute>}
/>
<Route
path="/workers/escort"
element={<LazyRoute><ProviderPage /></LazyRoute>}

View file

@ -34,7 +34,7 @@ export interface TierVerification {
}
/**
* White Glove Concierge features
* Concierge features
*/
export interface TierConcierge {
enabled: boolean;

View file

@ -158,7 +158,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
concierge: true,
conciergeRequests: -1,
proposalsPerRequest: 10,
responseTime: 4,
responseTime: 6,
duoPlus: 3,
vipVerification: true,
},
@ -172,7 +172,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
enabled: true,
requestsPerWeek: -1,
proposalsPerRequest: 10,
responseTimeHours: 4,
responseTimeHours: 6,
duoPlusPerWeek: 3,
},
vipVerification: true,

View file

@ -0,0 +1,222 @@
import { ArrowLeftIcon, CheckCircleIcon } from '@lilith/ui-icons';
import { m } from '@lilith/ui-motion';
import { Link } from '@lilith/ui-router';
import { useTranslation } from 'react-i18next';
import { RouteLoadingSkeleton } from '@/components/RouteLoadingSkeleton';
import SEOHead from '@/components/SEOHead';
import { useNamespace, type LazyNamespace } from '@/hooks/useNamespace';
import { Routes } from '@/routes';
import {
CommitmentGrid,
ComparisonTable,
NumberedSteps,
} from './company/manifesto/ManifestoSections';
import { Section } from './company/manifesto/Section';
import './features/FeatureDetailPage.css';
export default function WorkerEarningsPage() {
const { ready } = useNamespace('worker-earnings' as LazyNamespace);
const { t } = useTranslation('worker-earnings');
if (!ready) {
return <RouteLoadingSkeleton />;
}
const hero = t('hero', { returnObjects: true }) as {
tagline: string;
title: string;
subtitle: string;
};
const twoExceptions = t('twoExceptions', { returnObjects: true }) as {
title: string;
transactionFees: {
title: string;
subtitle: string;
description: string;
details: string[];
};
bonusPool: {
title: string;
subtitle: string;
description: string;
details: string[];
doesNotApply: string;
};
};
const whoPaysWhat = t('whoPaysWhat', { returnObjects: true }) as {
title: string;
escorts: { title: string; subtitle: string; items: string[] };
tokenCreators: { title: string; subtitle: string; items: string[] };
messaging: { title: string; subtitle: string; items: string[] };
};
const bonusPoolDeepDive = t('bonusPoolDeepDive', { returnObjects: true }) as {
title: string;
progressive: { title: string; steps: string[] };
comparison: { title: string; rows: Array<{ left: string; right: string }> };
keyPoints: { title: string; items: string[] };
};
const howWeMakeMoney = t('howWeMakeMoney', { returnObjects: true }) as {
title: string;
description: string;
items: Array<{ title: string; description: string }>;
closing: string;
};
const closing = t('closing', { returnObjects: true }) as { line: string };
return (
<div className="feature-detail-page" data-testid="page-worker-earnings">
<SEOHead
pageType="workers"
title={t('meta.title')}
description={t('meta.description')}
/>
<m.header
className="feature-detail-hero"
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
>
<Link to={Routes.workers} className="back-link">
<ArrowLeftIcon size={18} />
Back to Workers
</Link>
<m.p
className="feature-detail-tagline"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2 }}
>
{hero.tagline}
</m.p>
<m.h1
className="feature-detail-title"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
>
{hero.title}
</m.h1>
<m.p
className="feature-detail-subtitle"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.4 }}
>
{hero.subtitle}
</m.p>
</m.header>
<main className="feature-detail-content">
{/* The Two Exceptions */}
<Section title={twoExceptions.title} className="two-exceptions-section">
<div className="feature-info-grid">
<div className="feature-info-card">
<h4>{twoExceptions.transactionFees.title}</h4>
<p className="section-description" style={{ fontStyle: 'italic', marginBottom: '1rem' }}>
{twoExceptions.transactionFees.subtitle}
</p>
<p className="section-description">{twoExceptions.transactionFees.description}</p>
<ul className="benefits-list">
{twoExceptions.transactionFees.details.map((detail, i) => (
<li key={i}>
<CheckCircleIcon size={16} />
<span>{detail}</span>
</li>
))}
</ul>
</div>
<div className="feature-info-card">
<h4>{twoExceptions.bonusPool.title}</h4>
<p className="section-description" style={{ fontStyle: 'italic', marginBottom: '1rem' }}>
{twoExceptions.bonusPool.subtitle}
</p>
<p className="section-description">{twoExceptions.bonusPool.description}</p>
<ul className="benefits-list">
{twoExceptions.bonusPool.details.map((detail, i) => (
<li key={i}>
<CheckCircleIcon size={16} />
<span>{detail}</span>
</li>
))}
</ul>
<div className="feature-callout">
{twoExceptions.bonusPool.doesNotApply}
</div>
</div>
</div>
</Section>
{/* Who Pays What */}
<Section title={whoPaysWhat.title} className="who-pays-what-section">
<CommitmentGrid
categories={[
{ category: `${whoPaysWhat.escorts.title}${whoPaysWhat.escorts.subtitle}`, items: whoPaysWhat.escorts.items },
{ category: `${whoPaysWhat.tokenCreators.title}${whoPaysWhat.tokenCreators.subtitle}`, items: whoPaysWhat.tokenCreators.items },
{ category: `${whoPaysWhat.messaging.title}${whoPaysWhat.messaging.subtitle}`, items: whoPaysWhat.messaging.items },
]}
/>
</Section>
{/* Bonus Pool Deep Dive */}
<Section title={bonusPoolDeepDive.title} className="bonus-pool-section">
<h3 className="section-description" style={{ fontWeight: 600, color: 'rgba(255, 255, 255, 0.95)' }}>
{bonusPoolDeepDive.progressive.title}
</h3>
<NumberedSteps steps={bonusPoolDeepDive.progressive.steps} className="payment-flow" />
<h3 className="section-description" style={{ fontWeight: 600, color: 'rgba(255, 255, 255, 0.95)', marginTop: '2rem' }}>
{bonusPoolDeepDive.comparison.title}
</h3>
<ComparisonTable rows={bonusPoolDeepDive.comparison.rows} />
<h3 className="section-description" style={{ fontWeight: 600, color: 'rgba(255, 255, 255, 0.95)', marginTop: '2rem' }}>
{bonusPoolDeepDive.keyPoints.title}
</h3>
<ul className="benefits-list">
{bonusPoolDeepDive.keyPoints.items.map((item, i) => (
<li key={i}>
<CheckCircleIcon size={16} />
<span>{item}</span>
</li>
))}
</ul>
</Section>
{/* How We Make Money */}
<Section title={howWeMakeMoney.title} className="revenue-model-section">
<p className="section-description">{howWeMakeMoney.description}</p>
<div className="feature-info-grid">
{howWeMakeMoney.items.map((item, i) => (
<div key={i} className="feature-info-card">
<h4>{item.title}</h4>
<p>{item.description}</p>
</div>
))}
</div>
<div className="feature-highlight" style={{ marginTop: '1.5rem' }}>
{howWeMakeMoney.closing}
</div>
</Section>
{/* Closing */}
<Section className="closing-section">
<div className="feature-closing">
<p>{closing.line}</p>
</div>
</Section>
</main>
</div>
);
}

View file

@ -230,6 +230,24 @@
margin-top: 1rem;
}
/* CTA link */
.feature-detail-content .feature-cta-link {
display: inline-block;
padding: 0.75rem 2rem;
background: linear-gradient(135deg, #ff69b4, #ffd700);
color: #1a1a2e;
text-decoration: none;
border-radius: 2rem;
font-weight: 600;
font-size: 1rem;
transition: all 0.3s ease;
}
.feature-detail-content .feature-cta-link:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(255, 105, 180, 0.3);
}
/* Navigation */
.feature-detail-nav {
display: flex;

View file

@ -1,11 +1,11 @@
import {
require_jsx_runtime
} from "./chunk-SF3ICQTZ.js";
import "./chunk-EK6WSQWS.js";
import {
useLocation
} from "./chunk-OTAO74UU.js";
import "./chunk-DKXZQOKX.js";
import {
require_jsx_runtime
} from "./chunk-SF3ICQTZ.js";
import {
require_react
} from "./chunk-7Y3UKPDW.js";

View file

@ -3,11 +3,6 @@ import {
initializeMetadata,
logMetadataBanner
} from "./chunk-MRCKZCZR.js";
import {
QueryClient,
QueryClientProvider
} from "./chunk-GVT3TYT3.js";
import "./chunk-SF3ICQTZ.js";
import {
development_exports,
dom_export_exports,
@ -17,6 +12,11 @@ import {
import {
require_react_dom
} from "./chunk-DKXZQOKX.js";
import {
QueryClient,
QueryClientProvider
} from "./chunk-GVT3TYT3.js";
import "./chunk-SF3ICQTZ.js";
import {
require_react
} from "./chunk-7Y3UKPDW.js";

View file

@ -1,164 +1,158 @@
{
"hash": "6c4ea7af",
"hash": "2eb04111",
"configHash": "16d230b3",
"lockfileHash": "a7230dbb",
"browserHash": "d0530ea8",
"lockfileHash": "5a476748",
"browserHash": "9151e9a6",
"optimized": {
"styled-components": {
"src": "../../../../../../node_modules/.bun/styled-components@6.3.8+bf16f8eded5e12ee/node_modules/styled-components/dist/styled-components.browser.esm.js",
"file": "styled-components.js",
"fileHash": "db5957f2",
"fileHash": "94589a9c",
"needsInterop": false
},
"react-router-dom": {
"src": "../../../../../../node_modules/.bun/react-router-dom@7.13.0+bf16f8eded5e12ee/node_modules/react-router-dom/dist/index.mjs",
"file": "react-router-dom.js",
"fileHash": "c088e15b",
"fileHash": "56bfa9d3",
"needsInterop": false
},
"react": {
"src": "../../../../../../node_modules/.bun/react@19.2.4/node_modules/react/index.js",
"file": "react.js",
"fileHash": "1616fce9",
"fileHash": "70b73f56",
"needsInterop": true
},
"react-dom": {
"src": "../../../../../../node_modules/.bun/react-dom@19.2.4+b1ab299f0a400331/node_modules/react-dom/index.js",
"file": "react-dom.js",
"fileHash": "37e007fd",
"fileHash": "8f6cbad0",
"needsInterop": true
},
"react/jsx-dev-runtime": {
"src": "../../../../../../node_modules/.bun/react@19.2.4/node_modules/react/jsx-dev-runtime.js",
"file": "react_jsx-dev-runtime.js",
"fileHash": "e28f41f8",
"fileHash": "6f5498ad",
"needsInterop": true
},
"react/jsx-runtime": {
"src": "../../../../../../node_modules/.bun/react@19.2.4/node_modules/react/jsx-runtime.js",
"file": "react_jsx-runtime.js",
"fileHash": "22ac4b2c",
"fileHash": "be97e861",
"needsInterop": true
},
"@tanstack/react-query": {
"src": "../../../../../../node_modules/.bun/@tanstack+react-query@5.90.21+b1ab299f0a400331/node_modules/@tanstack/react-query/build/modern/index.js",
"file": "@tanstack_react-query.js",
"fileHash": "43d80d43",
"fileHash": "40b9a55a",
"needsInterop": false
},
"date-fns": {
"src": "../../../../../../node_modules/.bun/date-fns@4.1.0/node_modules/date-fns/index.js",
"file": "date-fns.js",
"fileHash": "1be278f4",
"fileHash": "6a9ea10b",
"needsInterop": false
},
"lucide-react": {
"src": "../../../../../../node_modules/.bun/lucide-react@0.553.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/lucide-react.js",
"file": "lucide-react.js",
"fileHash": "23f24c01",
"fileHash": "846cd6ac",
"needsInterop": false
},
"msw": {
"src": "../../../../../../node_modules/.bun/msw@2.12.10+3cceda249c3c2abe/node_modules/msw/lib/core/index.mjs",
"file": "msw.js",
"fileHash": "22238e8d",
"fileHash": "c7a91a60",
"needsInterop": false
},
"msw/browser": {
"src": "../../../../../../node_modules/.bun/msw@2.12.10+3cceda249c3c2abe/node_modules/msw/lib/browser/index.mjs",
"file": "msw_browser.js",
"fileHash": "f37fd861",
"fileHash": "abe6024d",
"needsInterop": false
},
"react-i18next": {
"src": "../../../../../../node_modules/.bun/react-i18next@15.7.4+b27a33ecaee10e39/node_modules/react-i18next/dist/es/index.js",
"file": "react-i18next.js",
"fileHash": "52c1318c",
"fileHash": "451cb4ef",
"needsInterop": false
},
"react-markdown": {
"src": "../../../../../../node_modules/.bun/react-markdown@10.1.0+26a211c426f3f87c/node_modules/react-markdown/index.js",
"file": "react-markdown.js",
"fileHash": "026894f8",
"fileHash": "723c3541",
"needsInterop": false
},
"rehype-raw": {
"src": "../../../../../../node_modules/.bun/rehype-raw@7.0.0/node_modules/rehype-raw/index.js",
"file": "rehype-raw.js",
"fileHash": "b3135381",
"fileHash": "fd0ada97",
"needsInterop": false
},
"remark-gfm": {
"src": "../../../../../../node_modules/.bun/remark-gfm@4.0.1/node_modules/remark-gfm/index.js",
"file": "remark-gfm.js",
"fileHash": "4988ff37",
"fileHash": "fa1b951d",
"needsInterop": false
},
"@lilith/analytics-client/react": {
"src": "../../../../../../node_modules/.bun/@lilith+analytics-client@1.0.2+16f4e8f1c82041b0/node_modules/@lilith/analytics-client/dist/react/index.js",
"file": "@lilith_analytics-client_react.js",
"fileHash": "745dcbd5",
"fileHash": "f3fe4499",
"needsInterop": false
},
"@lilith/service-react-bootstrap": {
"src": "../../../../../../node_modules/.bun/@lilith+service-react-bootstrap@1.2.0+6fee3456acac5a20/node_modules/@lilith/service-react-bootstrap/dist/index.js",
"file": "@lilith_service-react-bootstrap.js",
"fileHash": "c1f5899a",
"fileHash": "4db8a079",
"needsInterop": false
},
"i18next": {
"src": "../../../../../../node_modules/.bun/i18next@23.16.8/node_modules/i18next/dist/esm/i18next.js",
"file": "i18next.js",
"fileHash": "5572f2ec",
"fileHash": "93d8fc20",
"needsInterop": false
},
"i18next-browser-languagedetector": {
"src": "../../../../../../node_modules/.bun/i18next-browser-languagedetector@8.2.1/node_modules/i18next-browser-languagedetector/dist/esm/i18nextBrowserLanguageDetector.js",
"file": "i18next-browser-languagedetector.js",
"fileHash": "b11bfd8b",
"fileHash": "cdc4849d",
"needsInterop": false
},
"@lilith/knowledge-verification-client": {
"src": "../../../../../../node_modules/.bun/@lilith+knowledge-verification-client@1.1.0+b1ab299f0a400331/node_modules/@lilith/knowledge-verification-client/dist/index.js",
"file": "@lilith_knowledge-verification-client.js",
"fileHash": "8bfe9475",
"fileHash": "1ed245fa",
"needsInterop": false
}
},
"chunks": {
"chunk-7UJQU23G": {
"file": "chunk-7UJQU23G.js"
"graphql-Y6Z42OCT": {
"file": "graphql-Y6Z42OCT.js"
},
"metadata-WXT2YVRC": {
"file": "metadata-WXT2YVRC.js"
},
"graphql-Y6Z42OCT": {
"file": "graphql-Y6Z42OCT.js"
},
"chunk-4DKGDLYG": {
"file": "chunk-4DKGDLYG.js"
"chunk-MRCKZCZR": {
"file": "chunk-MRCKZCZR.js"
},
"chunk-JP2PIA6Z": {
"file": "chunk-JP2PIA6Z.js"
},
"chunk-4DKGDLYG": {
"file": "chunk-4DKGDLYG.js"
},
"chunk-VAG6ZZP5": {
"file": "chunk-VAG6ZZP5.js"
},
"chunk-44WYWJ4C": {
"file": "chunk-44WYWJ4C.js"
},
"chunk-7UJQU23G": {
"file": "chunk-7UJQU23G.js"
},
"chunk-TC4SPHIM": {
"file": "chunk-TC4SPHIM.js"
},
"chunk-MRCKZCZR": {
"file": "chunk-MRCKZCZR.js"
},
"chunk-GVT3TYT3": {
"file": "chunk-GVT3TYT3.js"
},
"chunk-SF3ICQTZ": {
"file": "chunk-SF3ICQTZ.js"
},
"chunk-EK6WSQWS": {
"file": "chunk-EK6WSQWS.js"
},
@ -168,6 +162,12 @@
"chunk-DKXZQOKX": {
"file": "chunk-DKXZQOKX.js"
},
"chunk-GVT3TYT3": {
"file": "chunk-GVT3TYT3.js"
},
"chunk-SF3ICQTZ": {
"file": "chunk-SF3ICQTZ.js"
},
"chunk-7Y3UKPDW": {
"file": "chunk-7Y3UKPDW.js"
},

View file

@ -1,3 +1,6 @@
import {
parse
} from "./chunk-JP2PIA6Z.js";
import {
DeferredPromise,
Emitter,
@ -23,9 +26,6 @@ import {
stringToHeaders,
toPublicUrl
} from "./chunk-4DKGDLYG.js";
import {
parse
} from "./chunk-JP2PIA6Z.js";
import {
__privateAdd,
__privateGet

View file

@ -1,6 +1,3 @@
import {
zwitch
} from "./chunk-7UJQU23G.js";
import {
esm_default,
find,
@ -14,6 +11,9 @@ import {
stringify2,
svg
} from "./chunk-VAG6ZZP5.js";
import {
zwitch
} from "./chunk-7UJQU23G.js";
import {
ok,
visit

View file

@ -1,4 +1,3 @@
import "./chunk-7UJQU23G.js";
import {
asciiAlpha,
asciiAlphanumeric,
@ -20,6 +19,7 @@ import {
unicodePunctuation,
unicodeWhitespace
} from "./chunk-44WYWJ4C.js";
import "./chunk-7UJQU23G.js";
import {
EXIT,
convert,

View file

@ -81,7 +81,7 @@ export interface MerchantTierVerification {
addOnPrice: number | null
/** True if existing users with 2+ months can have fee waived (Bronze only) */
loyaltyWaiver: boolean
/** True if VIP verification (single private interview with white glove team instead of standard 2-interview consensus) is included */
/** True if VIP verification (single private interview with concierge team instead of standard 2-interview consensus) is included */
vip?: boolean
}

View file

@ -43,7 +43,7 @@ export interface TierVerification {
}
/**
* White Glove Concierge features for premium tiers
* Concierge features for premium tiers
*/
export interface TierConcierge {
/** True if concierge is available on this tier */
@ -78,9 +78,9 @@ export interface SubscriptionTier {
bonusPercentage: number;
bonusEffectiveValue: string;
verification: TierVerification;
/** White Glove Concierge features (Platinum+ only) */
/** Concierge features (Platinum+ only) */
concierge?: TierConcierge;
/** Whether this tier has VIP verification (single private interview with white glove team instead of standard 2-interview consensus) */
/** Whether this tier has VIP verification (single private interview with concierge team instead of standard 2-interview consensus) */
vipVerification?: boolean;
createdAt: Date;
updatedAt: Date;

View file

@ -69,7 +69,7 @@ export interface PlatformTierFeatures {
}
/**
* White Glove Concierge features for Platinum/White Glove tiers
* Concierge features for Platinum/Diamond tiers
*/
export interface TierConcierge {
/** True if concierge is available on this tier */
@ -89,7 +89,7 @@ export interface TierConcierge {
*/
export interface PlatformSubscriptionTier {
id: string;
slug: 'free' | 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamond' | 'white-glove';
slug: 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamond';
name: string;
description?: string;
priceUsd: number;
@ -100,9 +100,9 @@ export interface PlatformSubscriptionTier {
bonusEffectiveValue: string;
/** Verification requirements for this tier */
verification: TierVerification;
/** White Glove Concierge features (Platinum+ only) */
/** Concierge features (Platinum+ only) */
concierge?: TierConcierge;
/** Whether this tier has VIP verification (single private interview with white glove team instead of standard 2-interview consensus) */
/** Whether this tier has VIP verification (single private interview with concierge team instead of standard 2-interview consensus) */
vipVerification?: boolean;
isActive: boolean;
createdAt: string;

View file

@ -11,8 +11,8 @@ import type { MigrationInterface, QueryRunner } from 'typeorm'
* 1 = BRONZE - Entry paid tier, weekly caps, 0% bonus
* 2 = SILVER - Monthly pools with rollover, verification included, 17% bonus
* 3 = GOLD - Enhanced limits, 50% bonus value
* 4 = PLATINUM - White Glove Concierge Light, VIP Verification, 75% bonus
* 5 = DIAMOND - Full White Glove Concierge, 100% bonus
* 4 = PLATINUM - Concierge Light, VIP Verification, 75% bonus
* 5 = DIAMOND - Full Concierge, 100% bonus
*/
export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
name = 'SeedSubscriptionTiers1736505600000'
@ -190,7 +190,7 @@ export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
{
sku: 'TIER-PLATINUM',
name: 'Platinum Tier',
description: '75% bonus value with White Glove Concierge and VIP Verification. Our concierge team helps arrange meetings.',
description: '75% bonus value with Concierge and VIP Verification. Our concierge team helps arrange meetings.',
priceUsd: '499.99',
sortOrder: 4,
metadata: {
@ -242,7 +242,7 @@ export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
{
sku: 'TIER-DIAMOND',
name: 'Diamond Tier',
description: '100% bonus value with full White Glove Concierge, unlimited requests, and fastest response times.',
description: '100% bonus value with full Concierge service, unlimited requests, and fastest response times.',
priceUsd: '799.99',
sortOrder: 5,
metadata: {
@ -285,7 +285,7 @@ export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
enabled: true,
requestsPerWeek: -1,
proposalsPerRequest: 10,
responseTimeHours: 4,
responseTimeHours: 6,
duoPlusPerWeek: 3,
},
badge: 'diamond',

View file

@ -159,7 +159,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
concierge: true,
conciergeRequests: -1,
proposalsPerRequest: 10,
responseTime: 4,
responseTime: 6,
duoPlus: 3,
vipVerification: true,
},
@ -173,7 +173,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
enabled: true,
requestsPerWeek: -1,
proposalsPerRequest: 10,
responseTimeHours: 4,
responseTimeHours: 6,
duoPlusPerWeek: 3,
},
vipVerification: true,