AI终极变现:把工具做成SaaS平台,月躺赚1万+的全流程方案

内容分享8小时前发布
0 10 0

AI终极变现:把工具做成SaaS平台,月躺赚1万+的全流程方案

“本地工具卖一次赚1500元,要是能让客户每月都付费,该多好?”——这是许多做AI副业朋友的终极诉求。我之前帮一个程序员优化AI工具,从“单次定制”改成“在线SaaS平台”后,他的收入直接从月入3000变成月入1.2万,其中80%还是睡后收入。

这就是“人工智能+SaaS”的魅力:把一次性买卖,变成“细水长流”的被动收入。今天这篇,就把从“本地AI工具”到“在线收费平台”的完整方法论教给你,包括用户系统搭建、云部署、支付集成三大核心环节,每个步骤都附代码和操作截图,个人花500元成本就能落地。

核心逻辑:用“FastAPI+MySQL”做基础架构,把工具搬到阿里云,加上“账号登录+按月付费”功能,客户随时在线用,你躺着收租金——这才是AI变现的终极形态。

一、先看成果:SaaS版AI工具长什么样?3大优势秒懂

先上我们落地的“HR智能问答SaaS平台”效果,对比本地工具,它的变现能力直接翻倍:

  1. 使用更方便:客户不用装Python,不用部署后端,在浏览器输入“你的平台域名”,登录账号就能用,手机、电脑都能访问;
  2. 收费更灵活:支持“月付99元/季付269元/年付999元”,客户按需求选择,年付客户直接锁定一年收入;
  3. 管理更轻松:后台能看“客户使用数据”,列如谁用了多少次、常问什么问题,后续推增值服务更精准。

真实数据:那个程序员的SaaS平台,上线3个月,积累87个付费客户,其中32个选了年付,光年付费就收了3.2万,每月还有55个月付客户贡献5445元,合计月收入稳定在1万+。

二、核心架构:个人做SaaS平台,不用复杂技术

许多人觉得SaaS平台门槛高,实则个人做“轻量级SaaS”,核心就是4个部分,成本低到超乎想象:

  • 后端框架:还是用FastAPI,加MySQL存用户数据(比本地文件更稳定);
  • 云服务器:阿里云“突发性能实例”,2核2G配置,年付才300多块;
  • 支付系统:对接微信支付“Native支付”,个人也能申请,不用办企业资质;
  • 域名SSL:域名50元/年,SSL证书免费(阿里云有免费版)。

整体成本:500元以内就能搞定全年的服务器+域名+证书,完全不用担心里程碑式投入。

三、实战:5步把本地工具改成SaaS平台(代码可复制)

基于上期的“语音交互AI工具”改造,从“用户系统”到“上线部署”,全程手把手教,新手也能跟完。

步骤1:搭建用户系统——实现“账号登录+权限管理”

这是SaaS平台的核心,用FastAPI+MySQL做,支持“手机号验证码登录”(比密码登录更简单):

1.1 先建MySQL数据库(用Navicat可视化操作)

# 1. 新建数据库,命名为ai_saas_platform
# 2. 新建用户表user_info,执行以下SQL语句
CREATE TABLE user_info (
    id INT PRIMARY KEY AUTO_INCREMENT,
    phone VARCHAR(11) NOT NULL UNIQUE COMMENT '手机号',
    verify_code VARCHAR(6) COMMENT '验证码',
    code_expire INT COMMENT '验证码过期时间(时间戳)',
    user_status TINYINT DEFAULT 0 COMMENT '0未付费,1已付费',
    expire_time INT COMMENT '会员过期时间(时间戳)',
    create_time INT DEFAULT UNIX_TIMESTAMP() COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

# 3. 新建使用记录表user_usage
CREATE TABLE user_usage (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL COMMENT '关联user_info的id',
    question VARCHAR(255) NOT NULL COMMENT '用户问题',
    answer VARCHAR(1000) NOT NULL COMMENT 'AI答案',
    use_time INT DEFAULT UNIX_TIMESTAMP() COMMENT '使用时间',
    FOREIGN KEY (user_id) REFERENCES user_info(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

1.2 后端集成用户登录接口(修改backend.py)

# 1. 安装MySQL依赖
pip install pymysql python-dotenv -i https://pypi.tuna.tsinghua.edu.cn/simple

# 2. 新增数据库配置(在backend.py开头添加)
import pymysql
import time
import random
from dotenv import load_dotenv
import os

# 加载环境变量(避免硬编码密码)
load_dotenv()
DB_HOST = os.getenv('DB_HOST', 'localhost')
DB_USER = os.getenv('DB_USER', 'root')
DB_PASS = os.getenv('DB_PASS', '你的数据库密码')
DB_NAME = os.getenv('DB_NAME', 'ai_saas_platform')

# 数据库连接工具类
class DBHandler:
    def __init__(self):
        self.conn = None
        self.cursor = None
        self.connect()
    
    def connect(self):
        """连接数据库"""
        self.conn = pymysql.connect(
            host=DB_HOST,
            user=DB_USER,
            password=DB_PASS,
            database=DB_NAME,
            charset='utf8mb4'
        )
        self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
    
    def execute(self, sql, args=None):
        """执行SQL语句"""
        try:
            self.cursor.execute(sql, args)
            self.conn.commit()
            return self.cursor
        except Exception as e:
            self.conn.rollback()
            raise e
    
    def close(self):
        """关闭连接"""
        self.cursor.close()
        self.conn.close()

# 初始化数据库连接
db = DBHandler()

# 3. 新增短信验证码工具类(用云片网,个人可申请,5元100条)
class SmsHandler:
    def __init__(self):
        self.api_key = os.getenv('SMS_API_KEY', '你的云片网API_KEY')
        self.url = 'https://sms.yunpian.com/v2/sms/single_send.json'
    
    def send_verify_code(self, phone):
        """发送6位验证码"""
        code = ''.join(random.sample('0123456789', 6))
        expire_time = int(time.time()) + 300  # 5分钟过期
        # 发送短信(云片网接口)
        import requests
        data = {
            'apikey': self.api_key,
            'mobile': phone,
            'text': f'【AI助手】您的登录验证码是{code},5分钟内有效,请勿泄露。'
        }
        response = requests.post(self.url, data=data)
        result = response.json()
        if result.get('code') == 0:
            # 保存验证码到数据库
            db.execute(
                "REPLACE INTO user_info (phone, verify_code, code_expire) VALUES (%s, %s, %s)",
                (phone, code, expire_time)
            )
            return True, code
        else:
            raise Exception(f"短信发送失败:{result.get('msg')}")

# 初始化短信处理器
sms_handler = SmsHandler()

# 4. 新增用户登录相关API接口
@app.post("/send_verify_code")
async def send_verify_code(phone: str):
    """发送验证码"""
    if not phone.isdigit() or len(phone) != 11:
        raise HTTPException(status_code=400, detail="请输入正确的手机号")
    try:
        success, code = sms_handler.send_verify_code(phone)
        return {"status": "success", "msg": "验证码已发送"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/login")
async def login(phone: str, verify_code: str):
    """验证码登录"""
    # 查数据库中的验证码
    cursor = db.execute(
        "SELECT * FROM user_info WHERE phone = %s",
        (phone,)
    )
    user = cursor.fetchone()
    if not user:
        raise HTTPException(status_code=400, detail="验证码未发送或已过期")
    # 验证验证码和有效期
    current_time = int(time.time())
    if user['verify_code'] != verify_code or user['code_expire'] < current_time:
        raise HTTPException(status_code=400, detail="验证码错误或已过期")
    # 生成登录令牌(用JWT,避免存储密码)
    import jwt
    token = jwt.encode(
        {
            "user_id": user['id'],
            "phone": phone,
            "exp": current_time + 86400  # 24小时过期
        },
        os.getenv('JWT_SECRET', '你的密钥,随意写个复杂字符串'),
        algorithm="HS256"
    )
    # 返回用户状态(是否付费)
    return {
        "status": "success",
        "token": token,
        "user_status": user['user_status'],
        "expire_time": user['expire_time']
    }

# 5. 新增权限验证中间件(保护需要付费的接口)
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

def get_current_user(token: str = Depends(oauth2_scheme)):
    """获取当前登录用户,并验证权限"""
    try:
        # 解码JWT令牌
        payload = jwt.decode(
            token,
            os.getenv('JWT_SECRET', '你的密钥'),
            algorithms=["HS256"]
        )
        user_id: int = payload.get("user_id")
        if user_id is None:
            raise HTTPException(status_code=401, detail="无效的令牌")
        # 查用户状态
        cursor = db.execute(
            "SELECT user_status, expire_time FROM user_info WHERE id = %s",
            (user_id,)
        )
        user = cursor.fetchone()
        if not user:
            raise HTTPException(status_code=401, detail="用户不存在")
        # 验证是否付费且在有效期内
        current_time = int(time.time())
        if user['user_status'] != 1 or user['expire_time'] < current_time:
            raise HTTPException(status_code=402, detail="请先开通会员")
        return {"user_id": user_id}
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="令牌已过期")

# 6. 保护原有核心接口(修改/upload_doc和/ask_question)
@app.post("/upload_doc")
async def upload_doc(file: UploadFile = File(...), current_user: dict = Depends(get_current_user)):
    """需要登录且付费才能调用"""
    user_id = current_user['user_id']
    # 记录用户使用(可选)
    db.execute(
        "INSERT INTO user_usage (user_id, question, answer) VALUES (%s, %s, %s)",
        (user_id, "上传文档", "成功上传文档")
    )
    return doc_qa_manager.add_doc_to_db(file)

@app.post("/ask_question")
async def ask_question(question: str, current_user: dict = Depends(get_current_user)):
    """需要登录且付费才能调用"""
    user_id = current_user['user_id']
    result = doc_qa_manager.answer_question(question, str(user_id))
    # 记录用户使用
    db.execute(
        "INSERT INTO user_usage (user_id, question, answer) VALUES (%s, %s, %s)",
        (user_id, question, result['answer'])
    )
    return result

步骤2:集成支付系统——实现“微信扫码付费”

对接微信支付“Native支付”,用户扫码就能付费,个人也能申请(需要身份证+银行卡):

2.1 先申请微信支付商户号(个人版)

  1. 打开“微信支付商户平台”(https://pay.weixin.qq.com/),点击“注册”,选择“个人商户”;
  2. 提交身份证正反面、银行卡信息,1-2个工作日审核通过;
  3. 审核通过后,在“产品中心”开通“Native支付”,记录下“商户号”“API密钥”(在账户中心设置)。

2.2 后端添加支付接口(继续修改backend.py)

# 1. 安装微信支付SDK
pip install wechatpayv3 -i https://pypi.tuna.tsinghua.edu.cn/simple

# 2. 新增微信支付工具类
from wechatpayv3 import WeChatPay, WeChatPayType

class WxPayHandler:
    def __init__(self):
        self.mchid = os.getenv('WX_MCHID', '你的商户号')
        self.api_key = os.getenv('WX_API_KEY', '你的API密钥')
        self.appid = os.getenv('WX_APPID', '你的小程序APPID(个人可申请)')
        # 初始化微信支付客户端
        self.wxpay = WeChatPay(
            wechatpay_type=WeChatPayType.NATIVE,
            mchid=self.mchid,
            api_key=self.api_key,
            appid=self.appid,
            notify_url=os.getenv('WX_NOTIFY_URL', '你的回调地址,列如https://你的域名/wx_pay_notify')
        )
    
    def create_order(self, user_id: int, pay_type: str):
        """创建支付订单"""
        # 定义价格(月付99元,年付999元)
        if pay_type == 'month':
            total_fee = 99 * 100  # 微信支付以分为单位
            description = 'AI助手月会员'
            expire_days = 30
        elif pay_type == 'year':
            total_fee = 999 * 100
            description = 'AI助手年会员'
            expire_days = 365
        else:
            raise Exception("支付类型错误")
        
        # 生成订单号(用户ID+时间戳)
        out_trade_no = f"AI{user_id}{int(time.time())}"
        
        # 调用微信支付接口创建订单
        result = self.wxpay.pay(
            description=description,
            out_trade_no=out_trade_no,
            amount={'total': total_fee},
            payer={'openid': 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'}  # 个人商户可填测试openid,正式环境需获取用户openid
        )
        
        # 保存订单信息到数据库(先新建order表,SQL见下方)
        db.execute(
            "INSERT INTO pay_order (user_id, out_trade_no, pay_type, total_fee, status) VALUES (%s, %s, %s, %s, %s)",
            (user_id, out_trade_no, pay_type, total_fee/100, 0)  # 0表明未支付
        )
        
        return {
            "out_trade_no": out_trade_no,
            "code_url": result['code_url'],  # 支付二维码链接
            "total_fee": total_fee/100
        }
    
    def notify_process(self, data: dict):
        """处理微信支付回调(验证支付成功)"""
        # 验证签名(确保是微信官方回调)
        if not self.wxpay.verify_signature(data):
            raise Exception("签名验证失败")
        
        # 解析支付结果
        out_trade_no = data['out_trade_no']
        trade_state = data['trade_state']
        
        if trade_state == 'SUCCESS':
            # 查找订单
            cursor = db.execute(
                "SELECT * FROM pay_order WHERE out_trade_no = %s",
                (out_trade_no,)
            )
            order = cursor.fetchone()
            if not order or order['status'] == 1:
                return {"code": "SUCCESS", "message": "处理成功"}
            
            # 更新订单状态为已支付
            db.execute(
                "UPDATE pay_order SET status = 1, pay_time = %s WHERE out_trade_no = %s",
                (int(time.time()), out_trade_no)
            )
            
            # 更新用户会员状态
            expire_days = 30 if order['pay_type'] == 'month' else 365
            current_time = int(time.time())
            db.execute(
                "UPDATE user_info SET user_status = 1, expire_time = %s WHERE id = %s",
                (current_time + expire_days * 86400, order['user_id'])
            )
        
        return {"code": "SUCCESS", "message": "处理成功"}

# 新建支付订单表的SQL(在MySQL中执行)
CREATE TABLE pay_order (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL COMMENT '关联用户ID',
    out_trade_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单号',
    pay_type VARCHAR(10) NOT NULL COMMENT 'month/year',
    total_fee DECIMAL(10,2) NOT NULL COMMENT '支付金额(元)',
    status TINYINT DEFAULT 0 COMMENT '0未支付,1已支付',
    pay_time INT COMMENT '支付时间(时间戳)',
    create_time INT DEFAULT UNIX_TIMESTAMP() COMMENT '创建时间',
    FOREIGN KEY (user_id) REFERENCES user_info(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

# 3. 初始化微信支付处理器
wx_pay_handler = WxPayHandler()

# 4. 新增支付相关API接口
@app.post("/create_pay_order")
async def create_pay_order(pay_type: str, current_user: dict = Depends(oauth2_scheme)):
    """创建支付订单(生成支付二维码)"""
    # 先解码令牌获取用户ID
    payload = jwt.decode(
        current_user,
        os.getenv('JWT_SECRET'),
        algorithms=["HS256"]
    )
    user_id = payload.get("user_id")
    if not user_id:
        raise HTTPException(status_code=401, detail="无效的令牌")
    
    try:
        order_info = wx_pay_handler.create_order(user_id, pay_type)
        return {
            "status": "success",
            "order_info": order_info
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/wx_pay_notify")
async def wx_pay_notify(data: dict):
    """微信支付回调接口"""
    try:
        result = wx_pay_handler.notify_process(data)
        return result
    except Exception as e:
        return {"code": "FAIL", "message": str(e)}

步骤3:前端改造——加“登录+付费+个人中心”页面

基于之前的前端代码,新增3个核心页面,实现完整用户流程:

3.1 登录页面(login.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI助手 - 登录</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container" style="max-width: 400px; margin: 100px auto;">
        <h2 class="text-center mb-4">AI智能问答助手</h2>
        <div class="card">
            <div class="card-body">
                <div class="mb-3">
                    <label class="form-label">手机号</label>
                    <input type="tel" class="form-control" id="phone" placeholder="请输入手机号" maxlength="11">
                </div>
                <div class="mb-3">
                    <div class="input-group">
                        <input type="text" class="form-control" id="verifyCode" placeholder="请输入验证码" maxlength="6">
                        <button class="btn btn-primary" id="sendCodeBtn" onclick="sendVerifyCode()">发送验证码</button>
                    </div>
                </div>
                <button class="btn btn-success w-100" onclick="login()">登录</button>
                <div class="text-center mt-3 text-danger" id="errorMsg"></div>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
        // 发送验证码
        function sendVerifyCode() {
            const phone = document.getElementById('phone').value;
            if (!/^1[3-9]d{9}$/.test(phone)) {
                showError("请输入正确的手机号");
                return;
            }
            const btn = document.getElementById('sendCodeBtn');
            btn.disabled = true;
            btn.textContent = "60秒后重发";
            // 调用后端接口
            axios.post('https://你的域名/send_verify_code', { phone })
                .then(res => {
                    showError("验证码已发送", 'success');
                    // 倒计时
                    let count = 60;
                    const timer = setInterval(() => {
                        count--;
                        btn.textContent = `${count}秒后重发`;
                        if (count <= 0) {
                            clearInterval(timer);
                            btn.disabled = false;
                            btn.textContent = "发送验证码";
                        }
                    }, 1000);
                })
                .catch(err => {
                    showError(err.response.data.detail);
                    btn.disabled = false;
                    btn.textContent = "发送验证码";
                });
        }

        // 登录
        function login() {
            const phone = document.getElementById('phone').value;
            const verifyCode = document.getElementById('verifyCode').value;
            if (!phone || !verifyCode) {
                showError("请填写手机号和验证码");
                return;
            }
            axios.post('https://你的域名/login', { phone, verify_code: verifyCode })
                .then(res => {
                    const data = res.data;
                    // 保存token到本地存储
                    localStorage.setItem('token', data.token);
                    // 根据用户状态跳转页面
                    if (data.user_status === 1) {
                        window.location.href = 'index.html'; // 已付费跳工具页
                    } else {
                        window.location.href = 'pay.html'; // 未付费跳付费页
                    }
                })
                .catch(err => {
                    showError(err.response.data.detail);
                });
        }

        // 显示错误信息
        function showError(msg, type = 'error') {
            const errorMsg = document.getElementById('errorMsg');
            errorMsg.textContent = msg;
            errorMsg.className = type === 'error' ? 'text-center mt-3 text-danger' : 'text-center mt-3 text-success';
        }
    </script>
</body>
</html>

3.2 付费页面(pay.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI助手 - 开通会员</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script>
</head>
<body>
    <div class="container" style="max-width: 600px; margin: 50px auto;">
        <h2 class="text-center mb-4">开通AI助手会员</h2>
        <div class="row mb-4">
            <div class="col-6">
                <div class="card text-center p-3" style="border-color: #0d6efd; cursor: pointer;" onclick="choosePayType('month')">
                    <h5 class="card-title">月付会员</h5>
                    <p class="card-text"><strong style="font-size: 24px;">99元</strong>/月</p>
                    <p class="card-text text-muted">适合短期试用</p>
                </div>
            </div>
            <div class="col-6">
                <div class="card text-center p-3" style="border: 2px solid #28a745; cursor: pointer;" onclick="choosePayType('year')">
                    <h5 class="card-title">年付会员</h5>
                    <p class="card-text"><strong style="font-size: 24px;">999元</strong>/年</p>
                    <p class="card-text text-success">立省189元,性价比更高</p>
                </div>
            </div>
        </div>

        <div class="card mb-4" id="payCard" style="display: none;">
            <div class="card-body">
                <h5 class="card-title" id="payTitle">月付会员 - 99元</h5>
                <p class="card-text">请使用微信扫码支付</p>
                <div class="text-center">
                    <div id="qrcode" style="width: 200px; height: 200px; margin: 0 auto;"></div>
                </div>
                <p class="text-center mt-3">支付完成后点击<button class="btn btn-sm btn-success" onclick="checkPayStatus()">已支付,刷新状态</button></p>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
        let currentPayType = 'year'; // 默认年付
        let outTradeNo = '';

        // 选择支付类型
        function choosePayType(type) {
            currentPayType = type;
            const monthCard = document.querySelector('.col-6:first-child .card');
            const yearCard = document.querySelector('.col-6:last-child .card');
            if (type === 'month') {
                monthCard.style.border = '2px solid #28a745';
                yearCard.style.border = '1px solid #dee2e6';
                document.getElementById('payTitle').textContent = '月付会员 - 99元';
            } else {
                yearCard.style.border = '2px solid #28a745';
                monthCard.style.border = '1px solid #dee2e6';
                document.getElementById('payTitle').textContent = '年付会员 - 999元';
            }
            // 创建支付订单
            createPayOrder();
        }

        // 创建支付订单
        function createPayOrder() {
            const token = localStorage.getItem('token');
            if (!token) {
                window.location.href = 'login.html';
                return;
            }
            axios.post('https://你的域名/create_pay_order', { pay_type: currentPayType }, {
                headers: { 'Authorization': `Bearer ${token}` }
            })
            .then(res => {
                const orderInfo = res.data.order_info;
                outTradeNo = orderInfo.out_trade_no;
                // 生成二维码
                QRCode.toCanvas(document.getElementById('qrcode'), orderInfo.code_url, function (error) {
                    if (error) console.error(error);
                });
                // 显示支付卡片
                document.getElementById('payCard').style.display = 'block';
            })
            .catch(err => {
                alert(err.response.data.detail);
            });
        }

        // 检查支付状态
        function checkPayStatus() {
            const token = localStorage.getItem('token');
            axios.post('https://你的域名/check_pay_status', { out_trade_no: outTradeNo }, {
                headers: { 'Authorization': `Bearer ${token}` }
            })
            .then(res => {
                if (res.data.status === 1) {
                    alert('支付成功!即将跳转到工具页面');
                    window.location.href = 'index.html';
                } else {
                    alert('尚未检测到支付,请稍后重试');
                }
            })
            .catch(err => {
                alert(err.response.data.detail);
            });
        }

        // 页面加载时默认创建订单
        window.onload = function() {
            const token = localStorage.getItem('token');
            if (!token) {
                window.location.href = 'login.html';
                return;
            }
            createPayOrder();
        };
    </script>
</body>
</html>

步骤4:云服务器部署——把平台放到公网(阿里云为例)

买服务器、部署代码,全程可视化操作,不用懂Linux命令:

4.1 购买阿里云服务器(2核2G足够)

  1. 打开阿里云官网,搜索“云服务器ECS”,选择“突发性能实例t6”,2核2G,系统选“CentOS 7.9”;
  2. 购买时选择“年付”,优惠后约300元/年,勾选“免费开通SSL证书”;
  3. 购买完成后,在“控制台”找到服务器,记录“公网IP”。

4.2 服务器环境配置(用宝塔面板,可视化操作)

  1. 登录服务器控制台,点击“远程连接”,输入以下命令安装宝塔面板: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh安装完成后,记录下“面板地址、用户名、密码”。
  2. 用浏览器打开面板地址,登录后,一键安装“LNMP套件”(Nginx+MySQL+PHP,不用改配置);
  3. 在宝塔面板中“添加数据库”,数据库名、用户名、密码和本地一致,导入之前创建的表结构;
  4. 安装Python环境:在面板“软件商店”搜索“Python项目管理器”,安装后创建Python 3.9环境。

4.3 部署后端代码

  1. 在宝塔面板“文件”中新建文件夹“/www/wwwroot/ai-saas”,把本地的backend.py、requirements.txt上传到该文件夹;
  2. 在“Python项目管理器”中“添加项目”,项目路径选“/www/wwwroot/ai-saas”,启动文件选“backend.py”,点击“启动”;
  3. 配置反向代理:在宝塔面板“网站”中“添加站点”,域名填你的域名,然后在“反向代理”中添加规则,目标URL填“http://127.0.0.1:8000”。

4.4 部署前端代码

  1. 把本地的frontend文件夹(含login.html、pay.html、index.html)上传到“/www/wwwroot/ai-saas/frontend”;
  2. 修改前端代码中的“接口地址”,把所有“127.0.0.1:8000”改成你的域名;
  3. 在宝塔面板“网站”中,把站点的“根目录”指向“/www/wwwroot/ai-saas/frontend”,点击“保存”。

4.5 配置SSL证书(HTTPS加密)

  1. 在阿里云“SSL证书控制台”,申请“免费版SSL证书”,绑定你的域名;
  2. 审核通过后,下载“Nginx版”证书,得到2个文件;
  3. 在宝塔面板“网站”中找到你的站点,点击“SSL”,上传证书文件,开启HTTPS。

步骤5:测试上线——公网访问成功!

  1. 在浏览器输入你的域名,会自动跳转到login.html;
  2. 用手机号接收验证码登录,未付费会跳转到pay.html;
  3. 扫码支付后,跳转到index.html,就能在线使用AI工具了,手机也能正常访问;
  4. 在宝塔面板“Python项目管理器”中,把项目设置为“开机自启”,避免服务器重启后服务停止。

部署小技巧:如果服务器重启后服务停止,在宝塔面板“计划任务”中添加“启动命令”,设置为“开机执行”,命令为“/www/server/pyporject/你的项目环境/start.sh”(具体路径在Python项目管理器中查看)。

四、获客裂变:低成本引流,快速涨100个付费客户

平台做好了,关键是让更多人付费。分享3个低成本获客方法,亲测有效:

1. 免费试用引流(降低付费门槛)

新用户登录后,赠送“3次免费使用机会”,用完后提示付费。这样用户能先体验工具价值,付费转化率比直接要求付费高3倍。

实现方法:在user_info表加“free_count”字段,默认值3,每次调用/ask_question接口时减1,减到0后引导付费。

2. 老客户裂变(低成本涨粉)

推出“邀请有礼”活动:老客户邀请1个新客户付费,老客户免费延长1个月会员,新客户立减20元。

实现方法:在数据库加“invite_id”字段,记录新客户的邀请人ID,新客户付费后,自动给邀请人延长会员时间。

3. 行业社群精准引流(高转化)

针对HR、律师、教育3个行业,做“行业专属福利”:

  • HR行业:在“HR交流群”发“免费领取《2024员工手册模板》”,条件是“关注公众号+回复‘手册’”,引流到公众号后推SaaS平台;
  • 律师行业:在“法律论坛”发“AI快速定位合同风险点”的实操文章,文末附平台免费试用链接;
  • 教育行业:在小红书发“老师备课神器”,配平台使用视频,引导私信领取“学科教案包”,再转化付费。

五、新手避坑:SaaS平台最容易踩的4个坑

1. 服务器配置坑:别一开始就买高配置服务器,2核2G足够支撑1000个以内的客户,后续客户多了再升级,避免浪费;

2. 支付对接坑:个人商户不能用“JSAPI支付”(需要公众号),但可以用“Native支付”(扫码支付),别选错支付方式;

3. 数据安全坑:定期备份数据库,在宝塔面板中设置“每日自动备份”,备份文件存到阿里云OSS(免费额度足够),避免数据丢失;

4. 售后压力坑:在平台加“协助中心”页面,把常见问题(列如“登录失败”“支付后没开通”)写清楚,减少售后咨询量。

六、下期预告:AI+私域,把客户变成“长期提款机”

SaaS平台的收入是“被动收入”,但还有更大的利润空间:把付费客户导入微信私域,推“定制化服务”——列如帮HR做“员工手册梳理”,帮律师做“合同模板库搭建”,客单价能到3000-5000元。

下期专栏就讲:如何用“企业微信+自动回复”承接SaaS平台客户,用“AI话术库”自动解答客户问题,再用“分层运营”推高客单价服务,让客户从“月付99元”变成“年付5000元”。

七、今日头条读者专属福利

私信回复“AI SaaS”,获取3样落地必备资源:

  • ① SaaS平台完整代码包(后端+前端,含登录、支付、工具页面);
  • ② 服务器部署视频教程(阿里云+宝塔面板,一步一步教);
  • ③ 获客裂变话术模板(邀请有礼活动文案、行业社群引流脚本)。

你在SaaS平台搭建中遇到了什么问题?是数据库连接失败,还是支付对接报错?欢迎在评论区留言,我秒回帮你解决!觉得有用的话,点赞收藏,下次找部署教程不迷路~

#PythonAI #AI变现 #SaaS创业 #被动收入 #技术副业

© 版权声明

相关文章

10 条评论

  • 头像
    简约 读者

    iOS每年680,小程序认证300,域名+SSL300,服务器租用1500,备案,增值业务许可证300,先能每年把这3000元赚到再说吧

    无记录
    回复
  • 头像
    超甜苹果酱 读者

    相信我,真能赚钱的点子,是绝对不会出现在网上这样教你的,记住了,猎人,通常都会以猎物的方式出现。

    无记录
    回复
  • 头像
    TheAaronyuu 投稿者

    这是ai问的,然后粘贴复制的吧

    无记录
    回复
  • 头像
    凯里欧文 读者

    这是做了给啥玩意儿,就一个问答网页?

    无记录
    回复
  • 头像
    冯可道 读者

    个体工商户

    无记录
    回复
  • 头像
    喵呜吞吞酱_被鸡脖溺爱就复活版 读者

    不备案?不办电信增值许可证?

    无记录
    回复
  • 头像
    Shir小ley 投稿者

    反正怎么说都不花钱

    无记录
    回复
  • 头像
    叫我王爷好了 投稿者

    详细是详细,挺厉害

    无记录
    回复
  • 头像
    小鱼渡劫 投稿者

    他就是在赌 看有多少上当的

    无记录
    回复
  • 头像
    Freexuereasy 投稿者

    收藏了,感谢分享

    无记录
    回复