FastAPI全栈项目中集成Stripe与PayPal支付方案
环境准备与依赖安装
在开始之前,请确认你的开发环境满足以下要求:
- 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等工具将本地服务暴露至公网,测试支付回调。