支付与积分

支付提供商集成、订阅管理与积分系统配置指南

概览

01MVP 内置了一套完整的支付和积分系统,支持国际和国内主流支付方式。核心逻辑封装在 packages/payment/(支付)和 packages/credits/(积分)两个包中。

  • 支持单次购买、订阅(周期性付费)和终身购买三种模式
  • 内置 webhook(支付平台在用户付款成功后主动通知你服务器的方式)处理,自动更新订单状态
  • 积分系统独立于支付方式,可用任意渠道充值后消费

支持的支付方式

支付提供商支付渠道适用场景
StripeCheckout Session、订阅、Customer Portal国际用户、信用卡/借记卡、订阅制产品
微信支付Native(扫码支付)、JSAPI(公众号/小程序内支付)中国大陆用户
支付宝PC 网页支付中国大陆用户
PayPalOrders API v2国际用户、PayPal 余额/银行卡
Waffo/PancakeSDK Checkout快速接入,适合东南亚市场
ZPAY / 易支付支付宝、微信支付、易支付回调国内一次性付款、数字商品自动发货、轻量会员售卖

所有提供商都实现了统一的 PaymentProvider 接口,页面从支付渠道注册表读取可用渠道。密钥放在环境变量里,启用状态、默认渠道和排序可以在管理员后台管理。

配置支付渠道

通过 PAYMENT_ENABLED_CHANNELS 限定开放的支付渠道;不设置时,系统会启用所有密钥已经配置完整的渠道。

# apps/01mvp-web/.env.local
PAYMENT_ENABLED_CHANNELS=zpay:alipay,zpay:wxpay

当前内置渠道包括 stripe:cardwaffo:defaultzpay:alipayzpay:wxpay。账单页和数字商品页都会只展示当前环境已配置、已启用、且支持对应业务的支付方式。后台路径是 /admin/payments

数字商品 V1 优先用于项目包、资料包、模板包和兑换码的一次性交付。详情见 数字商品

积分系统

积分系统独立于支付方式,适用于按次付费的场景(如 AI 生成次数、导出次数等)。

CreditService API

积分服务位于 packages/credits/,提供以下方法:

方法说明返回值
getBalance(userId)查询用户当前积分余额number
addCredits(params)增加积分(购买、奖励等)交易记录
consumeCredits(params)消耗积分(使用功能时扣费)ConsumeCreditsResult
hasEnoughCredits(userId, amount)检查余额是否充足boolean
getStatus(userId)获取余额及收支汇总{ balance, totalPurchased, totalConsumed }
getTransactions(userId, options)查询交易历史记录交易记录数组

交易类型

积分交易支持以下类型:purchase(购买)、consumption(消费)、refund(退款)、bonus(赠送)、adjustment(手动调整)。

使用示例

在 API 路由中使用积分:

import { CreditService } from "@01mvp/credits";
import { db } from "@/lib/database";

const creditService = new CreditService(db);

// 检查余额
const hasEnough = await creditService.hasEnoughCredits(userId, 10);
if (!hasEnough) {
  return Response.json({ error: "积分不足" }, { status: 402 });
}

// 扣除积分
const result = await creditService.consumeCredits({
  userId,
  amount: 10,
  description: "AI 图片生成",
  metadata: { feature: "image-generation" },
});

if (!result.success) {
  return Response.json({ error: result.error }, { status: 402 });
}

购买成功后添加积分:

// 在 Webhook 处理中
await creditService.addCredits({
  userId,
  amount: 100,
  type: "purchase",
  orderId: "order_xxx",
  description: "购买 100 积分套餐",
});

所有积分操作都在数据库事务中执行,保证余额计算的原子性和一致性。用户余额存储在 User.creditBalance 字段中。

订阅管理

订阅(Subscription)是一种周期性付费模式,用户按月或按年自动续费。

订阅生命周期

  1. 创建订阅 — 用户选择计划,跳转到支付页面完成首次付款
  2. 生效中(active) — 订阅有效,用户可正常使用付费功能
  3. 试用期(trialing) — 可选,设置试用天数,试用期内免费
  4. 续费 — 到期时自动扣款,invoice.payment_succeeded 事件触发
  5. 到期未续(past_due) — 续费失败,Stripe 会自动重试
  6. 取消 — 用户主动取消或到期不续,状态变为 canceled
  7. 过期(expired) — 当前付费周期结束,功能降级

订阅工具函数

@01mvp/payment 提供了一组数据库无关的工具函数:

函数说明
isSubscriptionValid(subscription)检查订阅是否有效(active 或 trialing 且未过期)
isLifetimeSubscription(subscription)检查是否为终身订阅
isSubscriptionExpired(subscription)检查订阅是否已过期
getSubscriptionDisplayStatus(subscription)获取可读的状态文案
checkSubscription(subscription)综合检查,返回 { hasSubscription, isLifetime, subscription }

路由保护

在页面或 API 路由中使用 createSubscriptionGuard 限制付费用户访问:

import { checkSubscription, createSubscriptionGuard } from "@01mvp/payment";

// 在 API 路由中
const subscription = await db.subscription.findFirst({
  where: { userId: user.id },
});
const guard = createSubscriptionGuard(subscription, {
  redirectUrl: "/pricing",
});
if (guard) {
  return NextResponse.redirect(new URL(guard.redirectUrl, request.url));
}

常见问题

各支付方式接入指南