PAYATHON 2026

Jelastic PAAS 应用如何集成订阅支付系统?

支付阿杰

结论

Jelastic PaaS 提供应用运行环境、容器编排、数据库和网络等基础能力,一般不会为部署在平台上的 Web 应用统一处理订阅收款。平台按资源向你计费,与应用向最终用户收取订阅费是两套不同的计费机制。

通常需要在应用中接入第三方支付服务商,通过 API 创建订阅和完成付款,再用 Webhook 同步续费、取消、支付失败等状态。至于能否直接安装支付组件,要看具体的 Jelastic 服务商及其 Marketplace,部署前应向服务商确认。

如何选择支付方案

可以通过以下渠道寻找合适的方案:

  • 支付服务商,例如 Stripe、PayPal、Adyen,以及目标市场常用的支付宝、微信支付等。
  • Merchant of Record 服务,例如 Paddle。这类服务可以在部分地区代为处理税务、账单和合规事务,但具体支持范围和费率需要另行核实。
  • 开源订阅计费系统,例如 Kill Bill,适合计费规则复杂、需要自行管理账单流程的项目。
  • Jelastic Marketplace,查看当前服务商是否提供支付网关、电子商务应用或相关插件。
  • 应用框架生态。Laravel、Django、Spring Boot、Node.js 等通常都有支付服务商的官方 SDK 或成熟的社区库。

选择方案时,除了确认能否收款,还要检查以下事项:

  • 是否支持订阅和自动续费;
  • 是否覆盖客户所在的国家或地区;
  • 支持哪些银行卡和本地支付方式;
  • 是否提供退款、试用期、优惠券和按量计费;
  • VAT、销售税、发票及争议处理由谁负责;
  • 是否支持 Webhook、幂等请求和测试环境;
  • 资金结算周期、手续费及货币转换成本。

推荐的集成结构

典型的订阅支付流程是:

  1. 在支付服务商后台创建商品和周期价格。
  2. 应用后端为当前用户创建支付会话。
  3. 浏览器跳转到服务商托管的结账页面。
  4. 支付服务商完成扣款后,通过 Webhook 通知应用。
  5. 应用验证 Webhook 签名并更新本地订阅状态。
  6. 用户访问付费功能时,应用根据本地权限记录进行鉴权。
  7. 发生续费失败、订阅取消或退款时,应用再根据 Webhook 收回相应权限。

不要只根据付款后的浏览器跳转开通服务。用户可能在付款后关闭页面,跳转参数也可能被伪造。订阅状态应以通过签名验证的 Webhook 事件为准。

Node.js 接入示例

下面用 Stripe Checkout 说明基本的接入方式。SDK 接口可能随版本变化,实际实现请以已安装版本的官方文档为准。

先通过环境变量配置密钥:

STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
STRIPE_PRICE_ID=price_xxx
APP_BASE_URL=https://app.example.com

创建订阅结账会话:

import express from "express";
import Stripe from "stripe";

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

app.post("/api/billing/checkout", express.json(), async (req, res) => {
  // 实际项目必须从已认证的会话中获取用户,不能直接信任请求体。
  const user = req.user;

  if (!user) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  const session = await stripe.checkout.sessions.create({
    mode: "subscription",
    line_items: [
      {
        price: process.env.STRIPE_PRICE_ID,
        quantity: 1
      }
    ],
    client_reference_id: String(user.id),
    customer_email: user.email,
    success_url:
      `${process.env.APP_BASE_URL}/billing/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.APP_BASE_URL}/billing/cancel`
  });

  res.json({ url: session.url });
});

处理 Webhook:

app.post(
  "/api/billing/webhook",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;

    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        req.headers["stripe-signature"],
        process.env.STRIPE_WEBHOOK_SECRET
      );
    } catch {
      return res.status(400).send("Invalid webhook signature");
    }

    switch (event.type) {
      case "checkout.session.completed": {
        const session = event.data.object;

        await saveSubscriptionReference({
          userId: session.client_reference_id,
          customerId: session.customer,
          subscriptionId: session.subscription
        });
        break;
      }

      case "customer.subscription.updated":
      case "customer.subscription.deleted": {
        const subscription = event.data.object;

        await updateSubscriptionStatus({
          subscriptionId: subscription.id,
          status: subscription.status,
          currentPeriodEnd: subscription.current_period_end
        });
        break;
      }

      case "invoice.payment_failed": {
        const invoice = event.data.object;

        await markPaymentFailed({
          customerId: invoice.customer,
          subscriptionId: invoice.subscription
        });
        break;
      }
    }

    res.sendStatus(200);
  }
);

saveSubscriptionReference、updateSubscriptionStatus 和 markPaymentFailed 都是应用自身的数据库操作,需要按照实际数据模型实现。

部署到 Jelastic 时的配置

应用代码与普通 Web 服务没有太大区别,但还需要处理以下配置:

  • 将 API 密钥和 Webhook Secret 存入环境变量或安全配置,不要提交到 Git 仓库。
  • 为应用配置稳定的 HTTPS 域名。
  • 在支付服务商后台登记可公开访问的 Webhook 地址,例如:
https://app.example.com/api/billing/webhook
  • 确认代理或负载均衡器不会修改 Webhook 请求体。
  • 如果运行了多个应用实例,Webhook 处理逻辑必须能够应对并发和重复投递。
  • 在数据库中保存支付平台的客户 ID、订阅 ID、状态和计费周期结束时间,不要保存完整的银行卡信息。

使用 Express 时要留意中间件的顺序。Webhook 签名验证通常需要原始请求体。如果在 Webhook 路由之前使用了全局 express.json() 解析请求,签名验证可能失败。

生产环境注意事项

Webhook 可能重复发送,也可能因网络问题乱序到达,因此处理逻辑必须具备幂等性。可以将事件 ID 保存到数据库,并为其建立唯一约束:

CREATE TABLE payment_events (
    event_id VARCHAR(255) PRIMARY KEY,
    event_type VARCHAR(100) NOT NULL,
    processed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

不要把支付平台返回的 active 状态直接视为永久访问权限。应用需要明确处理试用期、欠费宽限期、取消后可继续使用至当前周期结束、退款和争议付款等业务规则。

支付页面应优先使用服务商托管的 Checkout 或合规组件。自行采集和处理银行卡数据会大幅扩大 PCI DSS 的合规范围。上线前还要分别测试首次付款、自动续费、支付失败、取消订阅、退款和 Webhook 重试等流程。

Jelastic 可以承载订阅应用,但订阅支付通常需要在应用层实现。先根据目标市场、税务责任和计费复杂度选择支付服务商,再通过其 API 和 Webhook 完成接入。

备注:内容仅供参考。