当前位置:首页 > 技术 > 正文内容

FastAPI全栈项目中集成Stripe与PayPal支付方案

访客 技术 2026年6月4日 1

环境准备与依赖安装

在开始之前,请确认你的开发环境满足以下要求:

  • Python 3.8 或更高版本
  • 已配置FastAPI与PostgreSQL的项目(可通过克隆 full-stack-fastapi-postgresql 快速搭建)
  • 注册Stripe与PayPal开发者账号(免费)

安装必要的支付SDK包:

pip install stripe paypalrestsdk python-dotenv

项目结构与配置

在现有API架构上添加支付模块,推荐以下目录布局:

backend/
└── app/
    ├── api/
    │   └── routes/
    │       └── payments.py  # 新增支付路由
    ├── core/
    │   └── config.py        # 集成支付配置
    └── crud/
        └── payment.py       # 支付业务逻辑

config.py 中添加支付相关的密钥配置:

STRIPE_API_KEY = settings.STRIPE_API_KEY
STRIPE_WEBHOOK_SECRET = settings.STRIPE_WEBHOOK_SECRET
PAYPAL_CLIENT_ID = settings.PAYPAL_CLIENT_ID
PAYPAL_CLIENT_SECRET = settings.PAYPAL_CLIENT_SECRET

支付路由与接口定义

创建 backend/app/api/routes/payments.py,定义支付端点:

from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session
from app.api.deps import SessionDep, CurrentUser
from app.models import PaymentCreate, PaymentResponse, Message
from app.crud.payment import create_payment_intent, process_payment_callback

router = APIRouter(prefix="/payments", tags=["payments"])

@router.post("/create-intent", response_model=PaymentResponse)
def create_payment(
    *,
    session: SessionDep,
    current_user: CurrentUser,
    payment_in: PaymentCreate
):
    """创建支付意向,支持Stripe或PayPal"""
    if payment_in.platform not in ["stripe", "paypal"]:
        raise HTTPException(status_code=400, detail="不支持的支付平台")
    return create_payment_intent(session, current_user, payment_in)

@router.post("/webhook/{platform}", response_model=Message)
def payment_webhook(platform: str, session: SessionDep):
    """处理支付平台回调"""
    return process_payment_callback(session, platform)

支付核心逻辑实现

backend/app/crud/payment.py 中实现支付处理:

import stripe
import paypalrestsdk
from sqlmodel import Session
from app.core.config import settings
from app.models import User, PaymentCreate, PaymentResponse

# 初始化支付SDK
stripe.api_key = settings.STRIPE_API_KEY
paypalrestsdk.configure({
    "mode": "sandbox",
    "client_id": settings.PAYPAL_CLIENT_ID,
    "client_secret": settings.PAYPAL_CLIENT_SECRET
})

def create_payment_intent(session: Session, user: User, payment_in: PaymentCreate) -> PaymentResponse:
    """生成支付意向"""
    if payment_in.platform == "stripe":
        intent = stripe.PaymentIntent.create(
            amount=int(payment_in.amount * 100),  # Stripe以分为单位
            currency=payment_in.currency,
            metadata={"user_id": str(user.id)}
        )
        return PaymentResponse(
            client_secret=intent.client_secret,
            payment_id=intent.id
        )
    elif payment_in.platform == "paypal":
        payment = paypalrestsdk.Payment({
            "intent": "sale",
            "payer": {"payment_method": "paypal"},
            "transactions": [{
                "amount": {
                    "total": str(payment_in.amount),
                    "currency": payment_in.currency
                }
            }],
            "redirect_urls": {
                "return_url": f"{settings.SERVER_HOST}/payments/success",
                "cancel_url": f"{settings.SERVER_HOST}/payments/cancel"
            }
        })
        if payment.create():
            return PaymentResponse(
                payment_id=payment.id,
                approval_url=[link.href for link in payment.links if link.rel == "approval_url"][0]
            )
        raise HTTPException(status_code=400, detail=payment.error)

前端支付组件(React示例)

在前端项目中创建支付表单组件(以React为例):

import { useState } from 'react';
import { usePayment } from '../../hooks/usePayment';
import { Button } from '../ui/button';
import { Card } from '../ui/card';
import { Input } from '../ui/input';
import { Select } from '../ui/select';

export function PaymentForm() {
  const [amount, setAmount] = useState('');
  const [platform, setPlatform] = useState('stripe');
  const { createPaymentIntent, isLoading } = usePayment();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const result = await createPaymentIntent({
      amount: parseFloat(amount),
      currency: 'usd',
      platform
    });
    if (platform === 'stripe') {
      // 集成Stripe Elements完成支付
    } else {
      // 跳转到PayPal支付页面
      window.location.href = result.approval_url;
    }
  };

  return (
    <Card className="p-6">
      <h3 className="text-xl font-semibold mb-4">发起支付</h3>
      <form onSubmit={handleSubmit} className="space-y-4">
        <div>
          <label className="block mb-2">金额(美元)</label>
          <Input type="number" value={amount} onChange={(e) => setAmount(e.target.value)} required />
        </div>
        <div>
          <label className="block mb-2">支付方式</label>
          <Select value={platform} onValueChange={setPlatform}>
            <option value="stripe">Stripe</option>
            <option value="paypal">PayPal</option>
          </Select>
        </div>
        <Button type="submit" disabled={isLoading}>
          {isLoading ? '处理中...' : '确认支付'}
        </Button>
      </form>
    </Card>
  );
}

调试与测试指南

  • 使用沙盒环境:Stripe和PayPal均提供测试模式,利用测试卡号模拟支付。
  • 日志追踪:在 config.py 中配置日志输出,跟踪支付请求和响应。
  • Webhook测试:借助ngrok等工具将本地服务暴露至公网,测试支付回调。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。