PHP 怎么SaaS定价

wen PHP项目 2

本文目录导读:

PHP 怎么SaaS定价

  1. 第一层级:简易版(适合 MVP 或早期阶段)
  2. 第二层级:数据库驱动版(适合大多数常规 SaaS)
  3. 第三层级:高级用例(复杂计费、按量计费、多租户)
  4. 关键点:前端与支付网关集成
  5. 总结与最佳实践

在 PHP 中实现 SaaS 定价,核心不是用 PHP 写死价格表,而是设计一套灵活、可扩展的定价引擎,配合数据库、API 和前端交互。

根据你 SaaS 的复杂度,我将方案分为三个层级,你可以根据自己的需求选择。


第一层级:简易版(适合 MVP 或早期阶段)

如果套餐不多(比如只有 3 个固定套餐),直接在代码里写 matchswitch 即可,无需数据库。

优点:开发快,易读。 缺点:改价需改代码,无法做复杂优惠。

示例代码:config/plans.php

<?php
// 定义套餐(硬编码)
return [
    'free' => [
        'name' => '免费版',
        'price_monthly' => 0,
        'features' => ['10 个项目', '1GB 存储'],
        'limits' => ['projects' => 10, 'storage_mb' => 1024],
    ],
    'pro' => [
        'name' => '专业版',
        'price_monthly' => 29.00,
        'price_yearly' => 290.00, // 年付 10 个月价格
        'features' => ['无限项目', '100GB 存储', 'API 访问'],
        'limits' => ['projects' => PHP_INT_MAX, 'storage_mb' => 102400],
    ],
    'enterprise' => [
        'name' => '企业版',
        'price_monthly' => 99.00,
        'price_yearly' => 990.00,
        'features' => ['无限项目', '无限存储', '专属客服'],
        'limits' => ['projects' => PHP_INT_MAX, 'storage_mb' => PHP_INT_MAX],
    ],
];

在控制器中使用:

<?php
// 获取当前用户的套餐ID(例如从数据库 user 表读取)
$userPlanId = $user->plan_id; // 'free', 'pro', 'enterprise'
$plans = require 'config/plans.php';
// 用户请求升级到 'pro'
$requestedPlan = 'pro';
$price = ($billingCycle === 'yearly') 
    ? $plans[$requestedPlan]['price_yearly'] 
    : $plans[$requestedPlan]['price_monthly'];

第二层级:数据库驱动版(适合大多数常规 SaaS)

这是最推荐的方式,支持后台配置,适合产品迭代。

数据表设计:

-- 套餐主表
CREATE TABLE `plans` (
  `id` VARCHAR(50) PRIMARY KEY,        -- 代码标识: basic, pro
  `name` VARCHAR(100) NOT NULL,        -- 显示名称
  `description` TEXT,
  `is_active` BOOLEAN DEFAULT TRUE
);
-- 价格表(处理多币种、多周期)
CREATE TABLE `plan_prices` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `plan_id` VARCHAR(50) NOT NULL,
  `billing_cycle` ENUM('monthly', 'yearly', 'quarterly') NOT NULL,
  `currency` CHAR(3) DEFAULT 'USD',
  `amount` DECIMAL(10,2) NOT NULL,
  UNIQUE KEY `unique_plan_cycle` (`plan_id`, `billing_cycle`, `currency`)
);
-- 功能/限额表(用于后端鉴权)
CREATE TABLE `plan_features` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `plan_id` VARCHAR(50) NOT NULL,
  `feature_key` VARCHAR(50) NOT NULL, -- 'max_projects'
  `feature_value` INT/TEXT NOT NULL   -- 数量或 JSON
);

PHP 定价服务类:

<?php
class PricingService {
    public function __construct(private PDO $db) {}
    // 核心方法:获取套餐价格
    public function getPrice(string $planId, string $cycle, string $currency = 'USD'): float {
        $stmt = $this->db->prepare(
            "SELECT amount FROM plan_prices 
             WHERE plan_id = ? AND billing_cycle = ? AND currency = ?"
        );
        $stmt->execute([$planId, $cycle, $currency]);
        return (float) $stmt->fetchColumn();
    }
    // 获取所有可用套餐(用于前台展示)
    public function getActivePlans(): array {
        $plans = $this->db->query("SELECT * FROM plans WHERE is_active = TRUE")->fetchAll();
        foreach ($plans as &$plan) {
            // 获取价格列表
            $priceStmt = $this->db->prepare("SELECT * FROM plan_prices WHERE plan_id = ?");
            $priceStmt->execute([$plan['id']]);
            $plan['prices'] = $priceStmt->fetchAll();
        }
        return $plans;
    }
    // 鉴权:检查用户是否可以升级(例如处理按量计费超额)
    public function canAccessFeature(string $planId, string $featureKey): bool {
        // 逻辑:查询 plan_features 表
    }
}

第三层级:高级用例(复杂计费、按量计费、多租户)

如果你的 SaaS 涉及按 API 调用次数、存储 GB 数、GPU 时间等计量计费,PHP 中需要引入计量表配额校验

核心逻辑:

  1. 订阅合同表: 记录用户当前订阅的套餐和周期。
  2. 用量事件表: 记录每次 API 调用或资源消耗。
  3. 配额检查中间件: 在用户每次操作前,计算当前时段内用量总和,超过限制则返回 403。
<?php
// 中间件示例(伪代码)
function checkQuota(string $userId, string $resourceType, int $amount) {
    $max = getPlanLimit($userId, $resourceType); // 从缓存/数据库读取最大配额
    $used = getUsage($userId, $resourceType); // 计算这一周期内(比如本月)的用量总和
    // 当用量超过时,返回错误
    if ($used + $amount > $max) {
        throw new \Exception('资源配额不足,请升级套餐。');
    }
}

高级“按需付费”逻辑: 当用户在升级套餐时,要计算按比例计费(Proration)

<?php
// 计算升级费用
public function calculateUpgradeCost($currentPlan, $newPlan, $daysRemaining, $daysInPeriod) {
    $oldMonthlyCost = $currentPlan['monthly_price'];
    $newMonthlyCost = $newPlan['monthly_price'];
    // 已付的旧套餐剩余价值
    $credit = $oldMonthlyCost * ($daysRemaining / $daysInPeriod);
    // 新套餐总价
    $newCost = $newMonthlyCost;
    // 应付差额(需要向 Stripe 收费的金额)
    $amountDue = max(0, $newCost - $credit);
    return $amountDue;
}

关键点:前端与支付网关集成

定价策略的实现几乎总是需要结合支付网关(如 StripePaddle 或国内的海底捞、支付宝)。

  • 方案 A(推荐): 在 PHP 后端生成 Checkout Session,将套餐 ID(pro_yearly)发送给 Stripe,由 Stripe 计算金额。

    // Stripe 客户端直接收取固定金额,简单安全
    $session = \Stripe\Checkout\Session::create([
        'line_items' => [[
            'price' => 'price_123abc', // Stripe 后台定义的套餐价格ID
            'quantity' => 1,
        ]],
        'mode' => 'subscription',
        'metadata' => ['plan_id' => 'pro'],
    ]);
  • 方案 B: 由 PHP 计算好价格,展示给用户确认,再同步价格到支付网关。


总结与最佳实践

如果你要开始做,我建议:

  1. 不要硬编码:即使现在只有 3 个套餐,也请建两个数据表(plansplan_prices)。
  2. “限额”与“价格”分离:价格表是给财务看的,plan_features 是给后端代码看的(用于校验用户权限)。
  3. 缓存价格表:将 plansplan_prices 缓存到 Redis 或 Yac 中,避免每次请求都查询数据库。
  4. 时间敏感:价格修改时,考虑历史订单价格不变,所以要记录订单快照(下单时的套餐信息+价格)。

如果你需要针对具体的场景(如结合 Stripe 订阅、处理试用期),可以补充细节,我帮你写对应的代码。

抱歉,评论功能暂时关闭!