使用 Stripe 和数字门禁政策构建仅限成员的酒吧网站

我长期以来一直被地下酒吧文化所吸引。我的意思是,谁不会呢?这不仅仅是关于精酿鸡尾酒和昏暗的灯光,而是背后的心理学。有一种特别之处在于其排他性——天鹅绒栅栏、秘密口令,以及那种令人兴奋的感觉,让你觉得自己是精英圈子里的内部人士。所以,去年当一家酒吧老板找到我创建这种体验的数字版本时,我立即同意了。构建一个数字地下酒吧不仅仅是关于草率的在线支付系统或受密码保护的入口。这是关于创造可以媲美在昏暗小巷中推开隐藏门的刺激冒险的体验。

以下是我学到的关于创建在线地下酒吧体验的一切——从轮换秘密代码的魔力,到设置那些诱人的 Stripe 订阅层级,甚至是让人们想要在门外排队的申请流程。

使用 Stripe 和数字门禁政策构建仅限成员的酒吧网站

目录

为什么地下酒吧模式在网络上有效

排他性不是什么技巧——它深深植根于我们的心理。稀缺性会增加欲望。自禁酒令时代以来,地下酒吧一直在玩这种心理游戏。而且,哇,数字说明了一切:从 2023 年到 2025 年,城市中基于会员制的地下酒吧增加了 25%。为什么?因为专业人士渴望在典型酒吧里根本无法获得的精选社交体验。

你不能忽视数字化的重要性。到 2025 年,高达 70% 的新地下酒吧概念已经融入了数字角度——具有秘密进入代码的网站、会员应用程序,甚至是 NFT 风格的数字钥匙。现实世界和数字体验就像舞伴一样,彼此增强对方的步伐。你在 Instagram 上找到它们,撞上一个神秘的链接,破解一个谜题来访问申请页面,获得审核,在 Stripe 上支付,每周五收到一个轮换的秘密代码——然后砰,你就通过那扇隐秘的隐藏门进入了。

这不仅仅是建立一个网站。这是在创造一种体验。

真实世界的地下酒吧会员模式

在构建任何东西之前,我必须看看那里实际上有什么在发挥作用。以下是一些最迷人的地下酒吧通过他们的会员制度所取得的成就:

场所 入场方式 层级和定价 值得注意的福利
Casa De Lobo (泽西城) 邀请 + OpenTable 预订 Red Seal (免费,仅限邀请);Green Seal ($1,000/年) 10 位客人的优先预订、18% 合作伙伴折扣、VIP 活动、私人储物柜
Red Phone Booth 拨入物理红色电话亭的秘密代码 可选增强会员资格 每晚代码轮换,增加物理交互层
No Soliciting 创始人每月申请审核 单一层级 (基于申请) 超级排他性;耐心成为入场券
Founder's Room (The Paramount) 会员注册 个人和企业层级 VIP 见面会、私人入口、礼宾服务、F&B 折扣
Gainesville Speakeasy 直接购买 $799 入场费 + $299/年续期 季度活动租赁、$100 饮品额度、编号杯子、每次访问 3 位客人、多地点访问

有几件事真的很吸引眼球。大约 60% 繁荣的地下酒吧要求要么推荐,要么申请——他们不会让任何拿着信用卡的人都能进来。这个社交证明循环——成员招入新成员——引发了 Casa De Lobo 的 2 倍会员增长,主要由名人和会员推荐驱动。

定价甜蜜点?入场费在 $500-$1,000 之间,年费 $200-$400。保留率徘徊在 65% 左右,这要归功于客人津贴等福利,这也将推荐人数增加了约 20%。

使用 Stripe 和数字门禁政策构建仅限成员的酒吧网站 - 架构

设计数字门禁政策

这里是大多数人搞砸的地方——他们把门禁政策当作一个基本的登录页面。新手错误。你的入口应该感觉像是一个有趣的启示,而不仅仅是另一种形式。

作为隐藏门的登陆页面

你的公开页面?保持低调。也许是一张照片,也许是一个地址。Red Phone Booth 的网站基本上是一个雪茄酒廊店面的数字版本。No Soliciting?他们真的告诉你要"耐心等待"。

这个前层的设计技巧:

  • 采用深色和大气的风格——黑色、红色、提示金色。
  • 最少的文本。让氛围吸引他们。
  • 没有刺眼的"注册"按钮。这条路应该感觉是巧合的,而不是被强迫的。
  • 隐藏入口:一个看起来不像它的可点击对象、一个序列或一个在悬停时出现的代码输入。

代码输入机制

这是一个开发人员的游乐场,提供对通过狭槽低声说密码的数字致敬。

// components/DoorEntry.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';

export function DoorEntry() {
  const [code, setCode] = useState('');
  const [error, setError] = useState(false);
  const [shaking, setShaking] = useState(false);
  const router = useRouter();

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    const res = await fetch('/api/verify-code', {
      method: 'POST',
      body: JSON.stringify({ code }),
    });
    
    if (res.ok) {
      // Trigger a "door opening" animation before redirect
      document.body.classList.add('door-reveal');
      setTimeout(() => router.push('/inside'), 1500);
    } else {
      setShaking(true);
      setError(true);
      setTimeout(() => setShaking(false), 500);
    }
  }

  return (
    <form onSubmit={handleSubmit} className={shaking ? 'animate-shake' : ''}>
      <input
        type="text"
        value={code}
        onChange={(e) => setCode(e.target.value.toUpperCase())}
        placeholder="Enter tonight's word"
        className="bg-transparent border-b border-amber-600 text-amber-100
                   text-center text-2xl tracking-widest font-mono
                   focus:outline-none focus:border-amber-400"
        maxLength={12}
        autoComplete="off"
      />
      {error && <p className="text-red-400 text-sm mt-2">Wrong door.</p>}
    </form>
  );
}

想象一下——"Wrong door" 信号一个错误更具有趣味性,而不是"Invalid code"。每个小细节都应该点头回到主题。

构建会员层级

我已经查阅了大量竞争对手数据来建议这些将易用性与独占辉煌相结合的层级:

层级 入场 年费 目标 关键福利
The Knock (仅限邀请) $0 $0 社交证明层 基本访问、2 张客人通行证/月、每周轮换代码
The Regular $199 入场费 $299/年 核心收入 无限访问、4 位客人/月、$50 季度饮品额度、仅限成员的活动
The Proprietor $999 入场费 $799/年 高价值 无限客人、私人储物柜、礼宾预订、所有活动优先权、15% F&B 折扣、私人房间访问
Founding Member $2,500 (一次性,限于 50 人) $0 永远 启动资金 + 传播者 Proprietor 中的所有内容 + 终身访问、墙上刻名、每年 2 场创始人活动

那个免费的、仅限邀请的层级至关重要。这是你如何播下推荐引擎的种子。付费会员可以邀请有限数量的免费层级人士,在不稀释品牌的情况下推动漏斗。

Founding Member 层级?绝妙的发布战术。这是一个不错的前期收入(如果你卖出所有 50 个名额则为 $125,000)并培养一个核心的狂热粉丝群体,他们在财务和情感上都很有吸引力。

层级的数据库模式

CREATE TABLE members (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT UNIQUE NOT NULL,
  full_name TEXT NOT NULL,
  tier TEXT NOT NULL CHECK (tier IN ('knock', 'regular', 'proprietor', 'founding')),
  stripe_customer_id TEXT,
  stripe_subscription_id TEXT,
  invited_by UUID REFERENCES members(id),
  guest_passes_remaining INT DEFAULT 2,
  application_status TEXT DEFAULT 'pending' 
    CHECK (application_status IN ('pending', 'approved', 'rejected', 'waitlisted')),
  approved_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  metadata JSONB DEFAULT '{}'
);

CREATE TABLE guest_passes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  member_id UUID REFERENCES members(id) NOT NULL,
  code TEXT UNIQUE NOT NULL,
  used_at TIMESTAMPTZ,
  guest_name TEXT,
  valid_until TIMESTAMPTZ NOT NULL
);

Supabase 以其行级安全 (RLS) 政策取胜,这确保成员只能访问他们自己的数据,管理员可以监督一切,API 保持安全,所有这些都不需要额外的开发工作。

Stripe 集成用于循环会员

Stripe 是这里的首选。我已经构建了足够多的订阅系统,知道陷阱。这是一种永远不会让我失望的方式。

设置产品和价格

首先在 Stripe 的仪表板中设置这些产品:

  • The Regular:$299 年费 + $199 一次性设置
  • The Proprietor:$799 年费 + $999 一次性设置
  • Founding Member:单个 $2,500 (一次性)

具有混合行项目的结账会话

// app/api/checkout/route.ts
import Stripe from 'stripe';
import { NextRequest, NextResponse } from 'next/server';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

const TIER_CONFIG = {
  regular: {
    lineItems: [
      { price: 'price_regular_initiation', quantity: 1 }, // one-time
      { price: 'price_regular_annual', quantity: 1 },      // recurring
    ],
    mode: 'subscription' as const,
  },
  proprietor: {
    lineItems: [
      { price: 'price_proprietor_initiation', quantity: 1 },
      { price: 'price_proprietor_annual', quantity: 1 },
    ],
    mode: 'subscription' as const,
  },
  founding: {
    lineItems: [
      { price: 'price_founding_lifetime', quantity: 1 },
    ],
    mode: 'payment' as const,
  },
};

export async function POST(req: NextRequest) {
  const { tier, memberId } = await req.json();
  const config = TIER_CONFIG[tier as keyof typeof TIER_CONFIG];
  
  if (!config) {
    return NextResponse.json({ error: 'Invalid tier' }, { status: 400 });
  }

  const session = await stripe.checkout.sessions.create({
    mode: config.mode,
    payment_method_types: ['card'],
    line_items: config.lineItems,
    success_url: `${process.env.NEXT_PUBLIC_URL}/welcome?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/membership`,
    metadata: { memberId, tier },
    // Include a manual age check since bars typically mean...well, age restrictions
    custom_fields: [
      {
        key: 'dob',
        label: { type: 'custom', custom: 'Date of Birth (must be 21+)' },
        type: 'text',
      },
    ],
  });

  return NextResponse.json({ url: session.url });
}

层级激活的 Webhook 处理程序

大多数指南遗漏的地方:处理超过成功交易的内容——考虑失败的续期、取消、订阅调整。

// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import Stripe from 'stripe';
import { createClient } from '@supabase/supabase-js';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_KEY!
);

export async function POST(req: Request) {
  const body = await req.text();
  const headersList = await headers();
  const sig = headersList.get('stripe-signature')!;

  const event = stripe.webhooks.constructEvent(
    body,
    sig,
    process.env.STRIPE_WEBHOOK_SECRET!
  );

  switch (event.type) {
    case 'checkout.session.completed': {
      const session = event.data.object as Stripe.Checkout.Session;
      const { memberId, tier } = session.metadata!;
      
      await supabase.from('members').update({
        tier,
        stripe_customer_id: session.customer as string,
        stripe_subscription_id: session.subscription as string,
        application_status: 'approved',
        approved_at: new Date().toISOString(),
      }).eq('id', memberId);
      
      // Time for that welcome email with the inaugural secret code
      await fetch(process.env.RESEND_WEBHOOK_URL!, {
        method: 'POST',
        body: JSON.stringify({ memberId, tier, event: 'welcome' }),
      });
      break;
    }
    
    case 'invoice.payment_failed': {
      const invoice = event.data.object as Stripe.Invoice;
      // Use Stripe's Smart Retries to handle beginning dunning and, if necessary, timely downgrading to 'knock' tier
      break;
    }
    
    case 'customer.subscription.deleted': {
      const sub = event.data.object as Stripe.Subscription;
      await supabase.from('members').update({
        tier: 'knock',
        stripe_subscription_id: null,
      }).eq('stripe_subscription_id', sub.id);
      break;
    }
  }

  return new Response('OK', { status: 200 });
}

记住费用:Stripe 每笔交易收取约 2.9% + $0.30。在 $299 的年度会员上?那大约是 $9。在 $2,500 的创始成员上?你要向他们交出约 $73。不是什么破坏交易的事情,但考虑到这一点是好的。

构建技术栈

根据我们的 Next.js 开发项目 的经验,这是我推荐的工具集:

工具 为什么
框架 Next.js 15 (App Router) 无服务器组件与路由灵活性和出色开发者体验的最佳结合
数据库 Supabase (Postgres + Auth + RLS) 内置身份验证、实时订阅、这个东西也有行级安全!
支付 Stripe Billing + Checkout 订阅行业巨头,客户门户轻而易举地设置
电子邮件 Resend 自动化秘密代码、欢迎漏斗、续期通知而不会失步
CMS (用于活动/内容) Sanity 或 Payload CMS 事件和菜单管理供酒保或经理无需代码
托管 Vercel 边界函数、API 路由、动态代码轮换、实时更新——它能让事情变得更简单吗?
日程安排 Cal.com 或自定义 私人预订、RSVP、简单的日程安排选项

对于优先考虑内容的酒吧,如活动列表或不断变化的菜单,无头 CMS 确保非开发人员可以平稳地更新内容。如果卓越的性能和 SEO 对于仪表板式体验更重要,也许考虑 Astro 用于公开面向内容,同时为安全成员门户保留 Next.js。

秘密代码系统

这是它变得有趣的地方:设置一个工作来轮换秘密代码并将其发送给成员。

// app/api/cron/rotate-code/route.ts
import { createClient } from '@supabase/supabase-js';
import { Resend } from 'resend';

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_KEY!
);
const resend = new Resend(process.env.RESEND_API_KEY!);

// Secret-hued words fitting the speakeasy culture
const WORDS = [
  'PROHIBITION', 'BATHTUB', 'BOOTLEG', 'MOONSHINE', 'GIGGLEWATER',
  'SPEAKEASY', 'HOOCH', 'BLINDPIG', 'ROTGUT', 'FIREWATER',
  'COFFIN_VARNISH', 'JAZZJUICE', 'NEEDLEBEER', 'PANTHER_SWEAT',
];

export async function GET(req: Request) {
  // Authentication is crucial for our cron secret checks
  const authHeader = req.headers.get('authorization');
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response('Unauthorized', { status: 401 });
  }

  const todaysCode = WORDS[Math.floor(Math.random() * WORDS.length)] 
    + '-' + Math.floor(Math.random() * 99).toString().padStart(2, '0');

  // Log the current code
  await supabase.from('active_codes').upsert({
    id: 'current',
    code: todaysCode,
    valid_from: new Date().toISOString(),
    valid_until: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
  });

  // Fetch all approved members
  const { data: members } = await supabase
    .from('members')
    .select('email, full_name')
    .eq('application_status', 'approved');

  // Fire off batch emails using Resend API
  if (members?.length) {
    await resend.batch.send(
      members.map((m) => ({
        from: 'The Door <door@yourspeakeasy.com>',
        to: m.email,
        subject: 'Tonight\'s Word',
        html: `<p style="font-family: monospace; font-size: 24px; 
                text-align: center; color: #d4a574;">${todaysCode}</p>`,
      }))
    );
  }

  return new Response(`Code rotated: ${todaysCode}`, { status: 200 });
}

将其安排为 Vercel Cron Job 在每天下午 4 点运行(晚间的完美时机):

// vercel.json
{
  "crons": [{
    "path": "/api/cron/rotate-code",
    "schedule": "0 16 * * *"
  }]
}

会员仪表板和独家功能

既然他们已经进入,你就得在他们的仪表板上提供持续的价值。以下是你想展示的内容:

  • 今晚的代码:前置和中心,简单地复制以与批准的客人分享
  • 客人通行证生成器:成员为每个客人邀请旋转单一用途 QR 代码
  • 活动日历:RSVP 控制人们秘密地(或不那么秘密地)不能等待的东西
  • 饮品额度状态:想要实时更新吗?好用!
  • 升级选项:诱惑他们尚未拥有的层级亮点
  • 预留功能:预订优先级空间,如私人房间
  • 独占网络:这个选择加入目录对于那些专业联系的场所可能是巨大的

Stripe 的客户门户解决了更改和支付的细节,让你少了一件要解决的事情。

申请和审核流程

这是你销售它不仅仅是一家酒吧的地方。从 No Soliciting 和 Casa De Lobo 的智慧中汲取灵感:

  1. 发现:我们有希望的人发现那个隐藏的申请按钮(通过推荐、复活节彩蛋或一个在某些时髦地方栖息的二维码)
  2. 申请提交:这是他们分享的——关于自己的基础知识、一个可选的(高度加权的)推荐代码和一个小论文。
  3. 等待:一个神奇的月度审核流程。滞后是有目的的。自动回复设定期望:"应用程序在每个月的第 1 天进行审核。"
  4. 管理员的转身:通过 Retool 或一个简单的管理面板,决策者可以批准、拒绝或列出潜在成员。
  5. 庆祝时间!启动一个 Stripe 结账链接,然后一堆欢迎笔记和那个诱人的第一个代码。

推迟批准实际上增加了感知价值。No Soliciting 的"耐心等待"规则?这是一个天才笔触。

定价策略和收入基准

让我们将一些现实数字投入组合中:

指标 保守 激进
创始成员 (一次性) 30 × $2,500 = $75,000 50 × $2,500 = $125,000
Proprietor 层级 (第 1 年) 25 × ($999 + $799) = $44,950 50 × $1,798 = $89,900
Regular 层级 (第 1 年) 100 × ($199 + $299) = $49,800 200 × $498 = $99,600
Knock (免费) 层级 200 × $0 = $0 (但驱动推荐) 400 × $0 = $0
第 1 年总额 $169,750 $314,500
第 2 年循环 (65% 保留) ~$73,000 ~$146,000

Stripe 费用将承担约 3-4%。基础设施成本 (Vercel 托管、Supabase、Resend) 可能每月运行 $100-$200。考虑到这些数字会员资格,利润率看起来金灿灿的。

对于 2026 年的预测,动态定价获得了势头——根据需求调整入场费、大幅提高收入潜力。怎么样一些 AI 集成审核工具?预测趋势显示他们将增长约 35%。

部署和发布路线图

计划在大约一个月内推出 MVP?这是一个简单的时间表:

第 1 周:设置舞台。为登陆、申请和仪表板在 Figma 中设计。启动你的 Next.js 和 Supabase 设置,以及 Stripe 产品。钉住那种外观——美学在这里非常重要。

第 2 周:至关重要的基础工作。集成 NextAuth.js 或 Clerk 进行身份验证、构建 Stripe 流、修复这些 webhook 并设置你的管理批准界面。

第 3 周:深入你的热情。开发代码轮换、门打开动画、会员仪表板、客人通行证系统和通过 Resend 的电子邮件模板。

第 4 周:密封所有内容。测试整个流程(你希望从申请到入场都平稳航行)并使用 Vercel 部署。获取 10-20 个测试人员的小组来使用测试版邀请来压力测试所有内容。

需要一个合作伙伴来构建这样的东西吗?联系我们。这正在我们的胡同里,而且通过 透明定价,你将在开始之前知道成本和范围。

常见问题

构建仅限成员的地下酒吧网站需要多少钱? 一个 Next.js 和 Supabase 自定义交易,搭配 Stripe?根据深度,想象 $15,000-$40,000。一个更流线型的 Webflow + Memberstack + Stripe 解决方案可能运行 $5,000-$10,000。持续的基础设施通常保持在 $200 一个月以下。记住:便宜的外观会打败目标——设计是你这里的皇后棋子。

你能为此使用 Squarespace 或 Wix 吗? 是的,但你会很快撞上限制。他们只是不能平方轮换代码或微妙的层级模型。集合多个外部工具变得昂贵和复杂。对于基础邀请网站来说很完美但自定义是我们完整地下酒吧故事的关键。

处理酒吧会员资格的年龄验证? 查看 Stripe Identity 进行身份证检查。为了减少摩擦,在结账时包含一个出生日期字段,进行服务器检查。如果一些成员需要 ID 上传?不要跳过当地酒类法律——每个地点都有其古怪的特定。

秘密代码分发的最佳方式? 电子邮件是基本通道——去 Resend 或 SendGrid 的那个。通过 Twilio 发送代码片段以增加交付风格。通过 PWA 考虑推送通知以提高参与度。加密聊天,如 Telegram 或 Signal,增加秘密触感。经常代码轮换?这一切都是关于保持动态。

防止成员公开分享秘密代码? 虽然完全无法停止,但让努力变得毫无意义。将代码验证与活动会话绑定。追踪代码输入、提醒可疑活动。每个成员的唯一代码会减少泄漏,创建一条追踪踪迹。

需要申请还是允许开放注册? 应用第一种方法重新定义价值并提高保留(年度 65% 而不是 40%)。甜蜜点?使用推荐打开免费层级;保留层级需要推荐和申请。

什么 Stripe 功能是必不可少的? 强制:Stripe Checkout 用于支付、Billing 用于循环、Webhooks 用于数据库同步和自服务的客户门户。在高端:Stripe Identity 用于身份证检查、Stripe Tax 用于销售税计算。仅 Stripe 的 Smart Retries 就可以自动保存多达 10% 的逾期订阅。

你能集成预留系统吗? 是的!嵌入现有服务(如 Resy)、使用 Cal.com 作为平衡的方法或设计深入的 Supabase 预订系统——授予完整的灵活性。基于层级的策略蓬勃发展——让更高的层级更早地抓住预订。

物理地下酒吧如何将网站与真实的门集成? 二维码验证位于顶部。成员在门口分享他们的仪表板二维码,通过工作人员平板电脑验证。NFC 会员卡(每张 $3-5)是另一条路线。Red Phone Booth 的物理拨号创建仍然保持着优点标准——获取代码,在电话亭上物理地执行它。