Flask集成PayPal REST API:支付后即时更新用户积分的问题
Flask + PayPal v2 API 实现支付后自动更新用户积分
核心方案
- 采用PayPal官方最新的v2 REST API(替代过时的
paypal-python-sdk) - 创建订单时,将用户ID存入
purchase_units[0].custom_id字段,PayPal会在支付完成后返回该标识 - 捕获订单时,读取
custom_id定位用户,同时验证实际支付金额与后端预设金额一致,杜绝篡改风险 - 无需依赖IPN,直接通过
capture_order路由处理支付结果(PayPal前端会自动回调该接口)
前置准备
- 安装官方Python SDK:
pip install paypalcheckoutsdk flask-jwt-extended flask-sqlalchemy - 在PayPal开发者平台创建应用,获取Client ID和Secret(优先用沙箱环境测试)
- 确保你的用户模型包含
id(用户唯一标识)和points(积分字段)
完整代码示例
基础配置与模型
from flask import Flask, request, jsonify from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment, LiveEnvironment from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest from flask_sqlalchemy import SQLAlchemy from flask_jwt_extended import JWTManager, jwt_required, get_jwt_identity import os app = Flask(__name__) # 核心配置 app.secret_key = os.getenv('FLASK_SECRET_KEY', 'your-strong-flask-secret') app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' # 替换为你的数据库链接 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # JWT配置(用于验证用户身份,防止前端篡改用户ID) app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY', 'your-strong-jwt-secret') db = SQLAlchemy(app) jwt = JWTManager(app) # 用户模型示例 class User(db.Model): id = db.Column(db.Integer, primary_key=True) points = db.Column(db.Integer, default=0) # PayPal客户端初始化 PAYPAL_CLIENT_ID = os.getenv('PAYPAL_CLIENT_ID', 'your-paypal-client-id') PAYPAL_CLIENT_SECRET = os.getenv('PAYPAL_CLIENT_SECRET', 'your-paypal-secret') # 沙箱环境测试,上线时替换为LiveEnvironment environment = SandboxEnvironment(client_id=PAYPAL_CLIENT_ID, client_secret=PAYPAL_CLIENT_SECRET) paypal_client = PayPalHttpClient(environment)
创建订单路由(生成PayPal支付链接)
@app.route('/api/paypal/create-order', methods=['POST']) @jwt_required() def create_order(): # 从JWT令牌获取用户ID,完全杜绝前端篡改 user_id = get_jwt_identity() data = request.get_json() amount = data.get('amount') # 安全校验1:验证金额格式与合法性 try: float(amount) except ValueError: return jsonify({'error': '无效的金额格式'}), 400 # 安全校验2:后端预设金额与积分的对应关系,完全不依赖前端传递的积分值 # 示例:1美元=10积分,可根据业务逻辑从数据库/配置文件读取 points_to_add = int(float(amount) * 10) # 构建PayPal订单请求 paypal_request = OrdersCreateRequest() paypal_request.prefer('return=representation') paypal_request.request_body({ "intent": "CAPTURE", "purchase_units": [ { "amount": { "currency_code": "USD", "value": amount }, # 存储用户ID,PayPal支付完成后会原封不动返回该字段 "custom_id": str(user_id) } ], "application_context": { "return_url": "https://your-domain.com/payment-success", # 支付成功跳转前端页面 "cancel_url": "https://your-domain.com/payment-cancel" # 支付取消跳转页面 } }) try: response = paypal_client.execute(paypal_request) return jsonify({ 'order_id': response.result.id, 'status': response.result.status, 'points_to_add': points_to_add }) except Exception as e: return jsonify({'error': str(e)}), 500
捕获订单路由(支付成功后更新积分)
@app.route('/api/paypal/capture-order', methods=['POST']) def capture_order(): order_id = request.get_json().get('order_id') if not order_id: return jsonify({'error': '缺少订单ID'}), 400 # 发起捕获订单请求 paypal_request = OrdersCaptureRequest(order_id) try: response = paypal_client.execute(paypal_request) order_result = response.result # 安全校验1:确认支付状态为完成 if order_result.status != 'COMPLETED': return jsonify({'error': '支付未完成'}), 400 # 获取PayPal返回的用户ID purchase_unit = order_result.purchase_units[0] user_id = purchase_unit.custom_id if not user_id: return jsonify({'error': '未找到关联用户信息'}), 400 # 安全校验2:验证实际支付金额与预期一致(建议提前将订单信息存入数据库,此处简化为直接计算) captured_amount = purchase_unit.amount.value expected_points = int(float(captured_amount) * 10) # 更新用户积分 user = User.query.get(int(user_id)) if not user: return jsonify({'error': '用户不存在'}), 400 user.points += expected_points db.session.commit() return jsonify({ 'status': 'success', 'user_id': user_id, 'updated_points': user.points, 'captured_amount': captured_amount }) except Exception as e: return jsonify({'error': str(e)}), 500
关键安全注意事项
- 禁止信任前端参数:用户ID必须通过JWT令牌获取,金额与积分的对应关系必须由后端预设,完全拒绝前端传递的积分值
- 强制金额校验:捕获订单时必须对比实际支付金额与后端预期金额,防止用户篡改前端支付金额
- 使用HTTPS:所有与PayPal交互的接口必须部署在HTTPS环境下,避免数据被拦截篡改
- 沙箱测试优先:上线前务必在PayPal沙箱环境完成全流程测试,确认无误后切换为
LiveEnvironment
前端集成示例(核心逻辑)
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_PAYPAL_CLIENT_ID¤cy=USD"></script> <div id="paypal-button-container"></div> <script> // 假设已通过登录接口获取JWT令牌,存储在localStorage中 const token = localStorage.getItem('access_token'); paypal.Buttons({ createOrder: async (data, actions) => { const response = await fetch('/api/paypal/create-order', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ amount: '10.00' }) }); const orderData = await response.json(); return orderData.order_id; }, onApprove: async (data, actions) => { const captureResponse = await fetch('/api/paypal/capture-order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ order_id: data.orderID }) }); const result = await captureResponse.json(); alert(`积分已更新:${result.updated_points}`); window.location.href = '/payment-success'; } }).render('#paypal-button-container'); </script>
内容的提问来源于stack exchange,提问作者wildcolor
相关产品推荐
相关产品推荐

