[控场AI]
· 10 分钟阅读· 5,017 字

Python集成PayPal支付实战:一次性支付与订阅

Python集成PayPal支付实战:一次性支付与订阅

PayPal Python Server SDK 用两个端点实现一次性支付,订阅场景靠 webhook 驱动,核心逻辑需手写理解。

本文介绍 PayPal Python Server SDK 的实战用法,通过两个项目覆盖一次性支付和订阅场景。一次性支付后端只需创建订单和捕获订单两个端点,前端用 JS SDK 衔接支付流程,框架无关可迁移至 Django 或 FastAPI。订阅场景完全由后端驱动,先通过 REST API 创建产品和计划,用户授权后 PayPal 通过 webhook 异步通知激活、取消和续费事件。webhook 必须验证签名以防伪造。本地开发用 ngrok 暴露端点时需注意 URL 变化和端口映射。作者强调支付核心逻辑应手写并理解,UI 美化可交给 AI。

PayPal 的 Python Server SDK 把支付集成从过去的“劝退级”难度拉到了相当友好的水平。这个 crash course 用两个完整项目覆盖了一次性支付和订阅两种场景,背后是同一套核心逻辑:创建订单(或订阅)、用户确认、服务器捕获(或等待 webhook)。前者适合电商,后者适合 SaaS。框架选型几乎无关紧要——演示用 Flask,但 Django、FastAPI 甚至裸 requests 都能复用同样的代码。

为什么 PayPal Python SDK 值得重新关注

视频作者坦言自己几年前尝试集成 PayPal 时因为流程过于冗长而放弃,但现在的 Server SDK 已经大幅简化。SDK 是框架无关的,Flask 只是演示载体。视频中所有 Flask 相关代码(数据库、登录注册、模板渲染)都是直接复制粘贴的,真正手写并逐行解释的只有 PayPal 部分。

这释放了一个明确信号:支付逻辑本身的门槛已经降低,开发者的精力可以更多放在业务层面。作者在 GitHub 仓库中提供了完整代码,方便跳过框架样板代码直接学习支付部分。

项目一:一次性支付的商店实现

后端只需两个端点

整个 PayPal 后端集成浓缩为两个 API 端点。第一个是 /api/orders(POST),负责创建订单:调用 paypal.orders.create_order,传入 OrderRequest,其中 intent 设为 CheckoutPaymentIntent.CAPTURE(直接扣款,而非仅授权),purchase_units 中声明金额和币种。函数返回订单 ID 给前端。

第二个端点是 /api/orders/<order_id>/capture(POST),负责捕获订单。调用 paypal.orders.capture_order,传入订单 ID 和 prefer: return=representation 以获取完整订单对象。检查返回的 status 是否为 COMPLETED,如果是,就在本地数据库中标记订单已支付,并清空购物车。

result = paypal.orders.capture_order({
    "id": order_id,
    "prefer": "return=representation"
})
captured = result.body
if captured.status == "COMPLETED":
    # 更新数据库,标记已支付

作者特别强调:除了这两个函数,其余全部是 Flask 的样板代码。这意味着迁移到 Django 或 FastAPI 时,需要改写的只有路由定义和会话管理,PayPal 调用本身原封不动。

prefer: return=representation 是 PayPal REST API 中的一个 HTTP 头部指令,用于控制响应体的详细程度。默认情况下,捕获订单等写操作只返回最小化的确认信息(如订单 ID 和状态摘要)。加上该头部后,PayPal 会返回完整的订单对象,包括购买单元、金额、支付者信息和交易明细。这对于需要在本地数据库中记录完整交易快照的场景非常有用,可以避免捕获后再发起一次查询请求。在 Python SDK 中,该头部通过参数字典传入,SDK 会将其转换为对应的 HTTP 头。

前端用 JavaScript SDK 衔接支付流程

一次性支付场景需要前端配合,因为用户在 PayPal 弹窗中完成支付的动作发生在浏览器端。页面引入 PayPal JS SDK v6,通过 window.paypal.createInstance 初始化,传入从后端 config 注入的 client ID(注意:client ID 不敏感,secret 才敏感,因此可以暴露给前端)。

关键流程是:检查 findEligibleMethods 是否支持 PayPal,然后创建 createPayPalOneTimePaymentSession,在 onApprove 回调中向后端 capture 端点发起请求。用户点击按钮时,通过 addEventListener 触发创建订单的 POST 请求,拿到订单 ID 后启动支付会话。

PayPal支付前端集成流程示意

findEligibleMethods 是 PayPal JS SDK v6 提供的客户端能力检测方法。它接收一个包含支付类型(如 paypal、card)的对象,返回当前浏览器环境和用户账户状态下可用的支付方式列表。在创建支付会话之前调用此方法,可以避免在不支持的环境中展示无法使用的按钮,从而提升用户体验并减少错误。例如,某些地区或浏览器可能不支持特定的资金来源,提前检测能让开发者动态调整界面。该方法是异步的,通常与 createInstance 初始化配合使用。

项目二:SaaS 订阅与 Webhook 驱动

订阅场景不再依赖前端回调

与一次性支付不同,订阅场景完全由后端驱动。用户点击订阅后,后端调用 paypal.subscriptions.create_subscription,传入 CreateSubscriptionRequest,其中包含 plan_id 和 application_context(品牌名、成功/取消跳转 URL)。然后遍历返回的 links,找到 rel == "approve" 的链接并将用户重定向过去。

用户在 PayPal 完成订阅后,PayPal 会通过 webhook 通知服务器。这就是订阅场景的核心差异:不需要前端等待回调,服务器异步接收事件并做出反应。

先创建产品和计划

PayPal 需要知道订阅计划的存在。SDK 不覆盖产品创建,所以用 REST API 手动完成:先获取 access token(/v1/oauth2/token),再 POST 到 /v1/catalogs/products 创建产品,最后用 SDK 的 create_billing_plan 创建计划。视频中创建了 basic($9.99/月)和 pro($29.99/月)两个计划,并将 plan_id 写入 .env 文件。

计划定义中有意思的是 auto_bill_outstanding=True 和 payment_failure_threshold=3——自动扣款,允许三次失败后才触发取消事件。

Webhook 的安全验证不可省略

webhook 端点必须是公开的,这意味着任何人都可以向它发送请求。如果不验证来源,恶意用户可以伪造“订阅成功”事件来免费获取付费功能。视频中的做法是:调用 PayPal 的 /v1/notifications/verify-webhook-signature API,传入 transmission 相关字段和 webhook_id,只有返回 verification_status == "SUCCESS" 时才处理事件。

作者的原话是:“理论上你可以不做这个验证,但那样非常愚蠢。”

/v1/notifications/verify-webhook-signature 是 PayPal 提供的独立验证端点,用于确认 webhook 请求确实来自 PayPal 而非第三方伪造。验证时需要传入从请求头中获取的 transmission_id、transmission_time、cert_url、auth_algo、transmission_sig,以及原始请求体(webhook_event)和开发者在 PayPal 后台配置的 webhook_id。PayPal 会使用其证书链校验签名,并返回 verification_status 字段。只有返回 SUCCESS 时才应处理事件。该验证是同步 HTTP 调用,会增加少量延迟,但这是防止资金欺诈的必要代价。

事件驱动的状态管理

webhook 处理器根据 event_type 分派逻辑:

  • BILLING.SUBSCRIPTION.ACTIVATED:从 resource 中提取 plan_id,匹配到 basic 或 pro,更新用户的 tier 和 subscription_id
  • BILLING.SUBSCRIPTION.CANCELLED / EXPIRED:将用户降级为 free
  • PAYMENT.SALE.COMPLETED:续费成功,确保状态为 active

Webhook事件处理与验证逻辑

所有事件写入本地 webhook_events 表用于排查。作者演示了完整闭环:订阅 pro → 访问 premium 功能 → 在 dashboard 取消订阅 → webhook 收到取消事件 → 用户降级为 free → premium 页面重新被拦截。

BILLING.SUBSCRIPTION.ACTIVATED、BILLING.SUBSCRIPTION.CANCELLED 和 PAYMENT.SALE.COMPLETED 是 PayPal 订阅生命周期中的核心 webhook 事件类型。ACTIVATED 在用户完成订阅授权后触发,表示订阅正式生效;CANCELLED 在用户主动取消或系统因支付失败达到阈值后触发;EXPIRED 则在订阅周期自然结束时触发。PAYMENT.SALE.COMPLETED 每次成功扣款都会触发,可用于续费确认和收入记录。事件中的 resource 对象包含订阅 ID、计划 ID 和金额等关键字段,处理器应据此更新本地状态。注意事件可能重复投递,处理逻辑需保证幂等性。

本地开发与调试的实操细节

webhook 需要公网可达的 URL,视频使用 ngrok 将本地端口映射到公网。注意这里有两个容易踩的坑:第一,ngrok 免费版的 URL 每次重启都会变,需要同步更新 .env 中的 BASE_URL 和 PayPal 开发者后台的 webhook URL;第二,ngrok 要映射应用实际运行的端口(视频中是 5000),而不是默认的 80。

调试过程中作者犯了两个典型错误:把 paypal.orders 误写成 paypal.order,以及 request.post 应为 requests.post(混淆了 Flask 的 request 对象和 requests 库)。他还特别提到,PayPal 在收到错误响应时会自动重试 webhook,所以修好代码后事件会补发,不需要重新订阅。

支付逻辑值得手写,UI 可以交给 AI

视频末尾作者用 Codex 重新设计了两个项目的界面,效果明显提升。但他给出了明确的边界:支付相关的代码必须手写并理解,因为这是涉及资金流转的核心逻辑;界面美化、样式调整则可以放心交给 AI。这个取舍值得所有正在集成支付的开发者参考。

关键要点

  • PayPal Python Server SDK 把一次性支付简化为“创建订单 + 捕获订单”两个函数调用,框架无关
  • 订阅场景依赖 webhook 异步通知,必须验证请求签名以防止伪造事件
  • 订阅计划需要先通过 REST API 创建产品,再用 SDK 创建 billing plan
  • 前端 JS SDK 只在一次性支付场景中需要,订阅流程由后端重定向完成
  • 支付核心逻辑应手写并理解,UI 层可以用 AI 辅助加速
#PayPal Python集成#PayPal Server SDK#Python支付开发#PayPal订阅Webhook#Flask支付集成#PayPal一次性支付#SaaS订阅支付
分享:

相关推荐