ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

agents24 仓库 paypal-integration Skill 实战指南:Express Checkout、IPN、订阅与退款全流程实现

2026/9/11 10:44:39 拓冰建站 浏览量
agents24 仓库 paypal-integration Skill 实战指南:Express Checkout、IPN、订阅与退款全流程实现 agents24 仓库 paypal-integration Skill 实战指南Express Checkout、IPN、订阅与退款全流程实现【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents本指南以agents24/agents仓库中 paypal-integration Skill 及其详细模式文档为主体系统讲解 PayPal 支付集成的六大核心场景OAuth 鉴权与 Express Checkout 服务端订单、IPN 异步通知验证与处理、订阅计费Billing Plans/Subscriptions、退款工作流、统一错误处理以及沙箱测试。读完本文你将掌握一套可直接复制运行的服务端 PayPal 集成代码骨架并理解 webhook 安全、幂等处理与 sandbox/live 环境切换等生产级细节。一、Skill 定位何时使用 paypal-integration该 Skill 位于仓库 plugins/payment-processing 插件目录下属于 Payment Processing支付处理插件家族的四个 Skill 之一其余为 stripe-integration、pci-compliance、billing-automation。依据 SKILL.md 中的 frontmatter 声明该 Skill 的激活场景为将 PayPal 作为支付选项接入实现 Express Checkout 快速结账流程用 PayPal 搭建周期性订阅计费recurring billing处理退款与支付争议disputes处理 PayPal webhook即 IPN 异步通知支持国际支付实现 PayPal Subscriptions 订阅产品在仓库的文档体系里Skill 采用「渐进式披露」Progressive Disclosure三层架构Frontmatter 元数据名称与激活条件始终加载核心指导在 SKILL.md 激活时加载而references/details.md属于按需加载的第三层资源存放完整的模式与可运行示例即本文主体内容的来源。1.1 三种支付产品产品用途PayPal Checkout一次性支付、Express Checkout 体验、支持访客与 PayPal 账户支付PayPal Subscriptions周期性计费、订阅计划、自动续费PayPal Payouts向多个收款人批量打款适用于市场与平台支付1.2 两种集成方式客户端集成JavaScript SDK使用 Smart Payment Buttons 托管支付流程后端代码最少服务端集成REST API对支付流程拥有完全控制权可定制结账 UI支持高级功能。details.md中的示例全部采用服务端 REST API路线通过requests直接调用 PayPal 官方 HTTP 接口不依赖第三方 SDK 封装便于理解底层请求结构。二、Express Checkout 服务端实现从 OAuth 到订单捕获2.1 PayPalClient 基类环境切换与 OAuth 访问令牌details.md给出的核心类是PayPalClient它同时承担环境路由与令牌管理两个职责import requests import json class PayPalClient: def __init__(self, client_id, client_secret, modesandbox): self.client_id client_id self.client_secret client_secret self.base_url https://api-m.sandbox.paypal.com if mode sandbox else https://api-m.paypal.com self.access_token self.get_access_token() def get_access_token(self): Get OAuth access token. url f{self.base_url}/v1/oauth2/token headers {Accept: application/json, Accept-Language: en_US} response requests.post( url, headersheaders, data{grant_type: client_credentials}, auth(self.client_id, self.client_secret) ) return response.json()[access_token]关键点拆解mode参数sandbox路由到https://api-m.sandbox.paypal.com其余值路由到生产环境https://api-m.paypal.com。这是隔离测试与线上流量的核心开关OAuth2 客户端凭证模式向/v1/oauth2/token发起POST携带grant_typeclient_credentials并以(client_id, client_secret)作为 HTTP Basic Authrequests.post的auth参数会自动编码。返回 JSON 中的access_token即为后续所有请求的Bearer凭证仓库中 payment-integration 智能体 强调「测试凭证必须不能在线上生效」因此在多环境部署时应把mode、CLIENT_ID、CLIENT_SECRET全部纳入环境变量管理避免测试卡在线上站点被接受而触发 PCI 违规。2.2 创建订单v2/checkout/ordersdef create_order(self, amount, currencyUSD): Create a PayPal order. url f{self.base_url}/v2/checkout/orders headers { Content-Type: application/json, Authorization: fBearer {self.access_token} } payload { intent: CAPTURE, purchase_units: [{ amount: { currency_code: currency, value: str(amount) } }] } response requests.post(url, headersheaders, jsonpayload) return response.json()要点说明订单创建接口是 PayPalOrders v2API请求体至少包含intentCAPTURE表示创建后直接捕获与purchase_units采购单元含金额金额value必须转为字符串str(amount)这是 PayPal API 对金额字段的类型约束避免浮点精度问题创建成功后返回的 JSON 中links数组内rel approve的链接即为用户批准支付页见下方订阅部分对同一模式的复用这也被 SKILL.md 的测试示例所验证next((link[href] for link in order[links] if link[rel] approve), None)。2.3 捕获订单与查询订单详情def capture_order(self, order_id): Capture payment for an order. url f{self.base_url}/v2/checkout/orders/{order_id}/capture headers { Content-Type: application/json, Authorization: fBearer {self.access_token} } response requests.post(url, headersheaders) return response.json() def get_order_details(self, order_id): Get order details. url f{self.base_url}/v2/checkout/orders/{order_id} headers { Authorization: fBearer {self.access_token} } response requests.get(url, headersheaders) return response.json()生产建议捕获动作必须在服务端完成。客户端Smart Buttons 的onApprove回调只负责把orderID回传后端由后端调用capture_order并向 PayPal 再次确认订单状态。这正对应 payment-integration.md 中「服务端验证从提供商 API 重新拉取支付状态永远不要只信任 webhook 负载或客户端响应」的安全要求。2.4 客户端入口Smart Buttons 快速开始完整的客户端-服务端链路可参考 SKILL.md 的 Quick Start前端通过 PayPal JS SDK 渲染按钮createOrder中声明purchase_unitsonApprove中调用actions.order.capture()成功后把orderID发往后端/api/paypal/capture进行服务端捕获与校验// Frontend - PayPal Smart Buttons div idpaypal-button-container/div script srchttps://www.paypal.com/sdk/js?client-idYOUR_CLIENT_IDcurrencyUSD/script script paypal.Buttons({ createOrder: function(data, actions) { return actions.order.create({ purchase_units: [{ amount: { value: 25.00 } }] }); }, onApprove: function(data, actions) { return actions.order.capture().then(function(details) { // Payment successful console.log(Transaction completed by details.payer.name.given_name); // Send to backend for verification fetch(/api/paypal/capture, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({orderID: data.orderID}) }); }); } }).render(#paypal-button-container); /script三、IPNInstant Payment Notification处理验证与业务分发IPN 是 PayPal 的异步通知机制支付状态变化时PayPal 向商户配置的端点推送表单数据。details.md给出一个完整的 Flask 端点示例其核心分为「回验」与「分发」两段。3.1 端点与消息分发from flask import Flask, request import requests from urllib.parse import parse_qs app Flask(__name__) app.route(/ipn, methods[POST]) def handle_ipn(): Handle PayPal IPN notifications. # Get IPN message ipn_data request.form.to_dict() # Verify IPN with PayPal if not verify_ipn(ipn_data): return IPN verification failed, 400 # Process IPN based on transaction type payment_status ipn_data.get(payment_status) txn_type ipn_data.get(txn_type) if payment_status Completed: handle_payment_completed(ipn_data) elif payment_status Refunded: handle_refund(ipn_data) elif payment_status Reversed: handle_chargeback(ipn_data) return IPN processed, 200分发逻辑依据payment_status字段路由到三个处理器Completed支付完成、Refunded退款、Reversed退单/撤销。注意txn_type字段同样被读取可用于更细粒度的事件识别。3.2 回验机制VERIFIED / INVALIDdef verify_ipn(ipn_data): Verify IPN message authenticity. # Add cmd parameter verify_data ipn_data.copy() verify_data[cmd] _notify-validate # Send back to PayPal for verification paypal_url https://ipnpb.sandbox.paypal.com/cgi-bin/webscr # or production URL response requests.post(paypal_url, dataverify_data) return response.text VERIFIEDIPN 安全模型的核心是回环验证商户把收到的完整 IPN 数据原样加cmd_notify-validate后回传给 PayPal 的 IPN 端点PayPal 返回VERIFIED才视为可信。生产环境应将 URL 替换为https://ipnpb.paypal.com/cgi-bin/webscr。这一点与仓库智能体 payment-integration.md 强调的 webhook 安全要求完全一致签名验证必须使用官方机制验证通知真实性绝不处理未验证的 webhook原始 Body 保留验证前不得修改请求体JSON 中间件会破坏校验幂等处理把事件 ID 存入数据库处理前检查去重——webhook 失败会重试提供商不保证单次投递快速响应应在执行数据库写入等昂贵操作之前返回2xx文档中的示例先返回200处理器内部完成业务超时触发重试会导致重复处理。3.3 三个业务处理器def handle_payment_completed(ipn_data): Process completed payment. txn_id ipn_data.get(txn_id) payer_email ipn_data.get(payer_email) mc_gross ipn_data.get(mc_gross) item_name ipn_data.get(item_name) # Check if already processed (prevent duplicates) if is_transaction_processed(txn_id): return # Update database # Send confirmation email # Fulfill order print(fPayment completed: {txn_id}, Amount: ${mc_gross}) def handle_refund(ipn_data): Handle refund. parent_txn_id ipn_data.get(parent_txn_id) mc_gross ipn_data.get(mc_gross) # Process refund in your system print(fRefund processed: {parent_txn_id}, Amount: ${mc_gross}) def handle_chargeback(ipn_data): Handle payment reversal/chargeback. txn_id ipn_data.get(txn_id) reason_code ipn_data.get(reason_code) # Handle chargeback print(fChargeback: {txn_id}, Reason: {reason_code})字段语义说明txn_id本次交易 ID退款场景下是退款交易 IDparent_txn_id退款对应的原始交易 ID退款处理应以它为键关联原订单mc_gross交易总额含费用reason_code退单原因码用于风控分析。handle_payment_completed中的is_transaction_processed(txn_id)幂等检查不可省略——这正是仓库智能体所列「Out-of-order webhooks breaking Lambda functions (no idempotency) → production failures」这一真实故障案例的防御措施。四、订阅与周期性计费Billing Plans 与 Subscriptions4.1 创建订阅计划v1/billing/plansdef create_subscription_plan(name, amount, intervalMONTH): Create a subscription plan. client PayPalClient(CLIENT_ID, CLIENT_SECRET) url f{client.base_url}/v1/billing/plans headers { Content-Type: application/json, Authorization: fBearer {client.access_token} } payload { product_id: PRODUCT_ID, # Create product first name: name, billing_cycles: [{ frequency: { interval_unit: interval, interval_count: 1 }, tenure_type: REGULAR, sequence: 1, total_cycles: 0, # Infinite pricing_scheme: { fixed_price: { value: str(amount), currency_code: USD } } }], payment_preferences: { auto_bill_outstanding: True, setup_fee: { value: 0, currency_code: USD }, setup_fee_failure_action: CONTINUE, payment_failure_threshold: 3 } } response requests.post(url, headersheaders, jsonpayload) return response.json()参数解析参数取值/默认含义product_id需预先创建PayPal 要求先创建 Product产品再在计划中引用其 IDfrequency.interval_unitMONTH/YEAR/WEEK/DAY计费周期单位frequency.interval_count整数每个计费周期的单位数量tenure_typeREGULAR常规/TRIAL试用计费期类型total_cycles0表示无限期该 tenure 的总周期数auto_bill_outstandingTrue是否自动补收欠款setup_fee金额对象一次性设置费0表示免设置费setup_fee_failure_actionCONTINUE设置费收取失败后的动作payment_failure_threshold3支付连续失败多少次后暂停订阅与下方 dunning 思路呼应4.2 为客户创建订阅并获取批准链接def create_subscription(plan_id, subscriber_email): Create a subscription for a customer. client PayPalClient(CLIENT_ID, CLIENT_SECRET) url f{client.base_url}/v1/billing/subscriptions headers { Content-Type: application/json, Authorization: fBearer {client.access_token} } payload { plan_id: plan_id, subscriber: { email_address: subscriber_email }, application_context: { return_url: https://yourdomain.com/subscription/success, cancel_url: https://yourdomain.com/subscription/cancel } } response requests.post(url, headersheaders, jsonpayload) subscription response.json() # Get approval URL for link in subscription.get(links, []): if link[rel] approve: return { subscription_id: subscription[id], approval_url: link[href] }模式要点订阅创建后同样返回一个links数组其中rel approve的href是订阅批准页。后端应将用户重定向到该 URL用户批准后PayPal 回调return_url携带subscription_id等参数此时订阅才正式激活。return_url与cancel_url需要替换为业务方真实域名。4.3 订阅生命周期与自动化计费订阅本身不产生代码但它与仓库中另一 Skill billing-automation 的订阅生命周期管理紧密配合。billing-automation 定义的典型状态机为trial → active → past_due → canceled → paused → resumed其BillingEngine.process_billing_cycle展示了完整的周期处理流程判断是否到账期 → 生成发票 → 尝试扣款 → 成功则标记已付并推进账期失败则标记past_due并进入 dunning催缴流程。而 PayPal 侧的payment_failure_threshold: 3与auto_bill_outstanding: True正是把「自动重试 失败上限」下沉到支付服务商侧的配置化实现二者互为补充。五、退款工作流部分退款与全额退款def create_refund(capture_id, amountNone, noteNone): Create a refund for a captured payment. client PayPalClient(CLIENT_ID, CLIENT_SECRET) url f{client.base_url}/v2/payments/captures/{capture_id}/refund headers { Content-Type: application/json, Authorization: fBearer {client.access_token} } payload {} if amount: payload[amount] { value: str(amount), currency_code: USD } if note: payload[note_to_payer] note response requests.post(url, headersheaders, jsonpayload) return response.json() def get_refund_details(refund_id): Get refund details. client PayPalClient(CLIENT_ID, CLIENT_SECRET) url f{client.base_url}/v2/payments/refunds/{refund_id} headers { Authorization: fBearer {client.access_token} } response requests.get(url, headersheaders) return response.json()退款以capture_id捕获交易 ID为操作对象调用/v2/payments/captures/{capture_id}/refundamount与note_to_payer均为可选参数不传amount即全额退款传amount即部分退款若只做全款退款payload可以保持为空对象{}PayPal 默认退还全部捕获金额退款完成后可用get_refund_details按refund_id查询退款明细用于对账与审计。退款通常由两类场景触发商户主动发起本节的create_refund路径以及支付服务商主动回调上一节 IPN 的Refunded状态。两者都需要在业务系统中记录refund_id与parent_txn_id的关联关系保证幂等、防止重复退款。六、统一错误处理PayPalError 封装class PayPalError(Exception): Custom PayPal error. pass def handle_paypal_api_call(api_function): Wrapper for PayPal API calls with error handling. try: result api_function() return result except requests.exceptions.RequestException as e: # Network error raise PayPalError(fNetwork error: {str(e)}) except Exception as e: # Other errors raise PayPalError(fPayPal API error: {str(e)}) # Usage try: order handle_paypal_api_call(lambda: client.create_order(25.00)) except PayPalError as e: # Handle error appropriately log_error(e)该封装的价值在于错误归一化无论底层是网络异常requests.exceptions.RequestException如超时、连接失败、DNS 解析错误还是 PayPal 返回的业务错误统一包装为自定义PayPalError业务层只需捕获一种异常类型即可统一处理记录日志、重试、通知用户。这与仓库智能体 payment-integration.md 中「Payment integration code with error handling」「实现所有支付操作的幂等性」「处理所有边界情况支付失败、争议、退款」的输出要求一致。七、沙箱测试与上线迁移SKILL.md 的 Testing 章节给出了完整的沙箱验证路径# Use sandbox credentials SANDBOX_CLIENT_ID ... SANDBOX_SECRET ... # Test accounts # Create test buyer and seller accounts at developer.paypal.com def test_payment_flow(): Test complete payment flow. client PayPalClient(SANDBOX_CLIENT_ID, SANDBOX_SECRET, modesandbox) # Create order order client.create_order(10.00) assert id in order # Get approval URL approval_url next((link[href] for link in order[links] if link[rel] approve), None) assert approval_url is not None # After approval (manual step with test account) # Capture order # captured client.capture_order(order[id]) # assert captured[status] COMPLETED测试与上线要点沙箱凭证在 developer.paypal.com 创建沙箱应用获取SANDBOX_CLIENT_ID/SANDBOX_SECRET同时创建测试买家和卖家账户全链路验证订单创建assert id in order→ 批准链接存在assert approval_url is not None→ 用测试买家账户手动完成批准 → 服务端捕获captured[status] COMPLETED环境隔离PayPalClient的mode参数即切换开关生产环境必须使用modelive、生产 API 域https://api-m.paypal.com与真实凭证。参照 payment-integration.md 的要求测试凭证必须确保在线上站点失效防止测试卡被线上接受。八、生产级 Checklist 汇总结合details.md的模式与仓库配套文档落地 PayPal 集成时建议逐项核对OAuth 令牌管理access_token有有效期长生命周期应用中应按官方建议缓存并在过期前刷新当前示例为每次实例化时获取生产应升级为带过期时间的缓存金额处理value一律str()化金额计算使用定点数避免浮点误差服务端捕获客户端只回传orderID捕获与状态校验必须发生在服务端IPN 回验所有通知先回传cmd_notify-validate验证VERIFIED才处理生产端点替换为ipnpb.paypal.com幂等去重以txn_id/parent_txn_id为键落库去重webhook 重试与重复投递不会造成重复发货/重复退款订阅失败策略payment_failure_threshold与服务端 dunning 流程联动避免长期欠费错误归一化所有 PayPal 调用经handle_paypal_api_call包装业务层只捕获PayPalError环境隔离sandbox/live 的凭证、域名、回调地址全部环境变量化测试与生产严格分离。参考与延伸阅读Skill 入口与激活条件plugins/payment-processing/skills/paypal-integration/SKILL.md本文主体详细模式与完整代码plugins/payment-processing/skills/paypal-integration/references/details.md支付集成智能体安全要求与常见故障plugins/payment-processing/agents/payment-integration.md相关 SkillStripe 集成 stripe-integration、PCI DSS 合规 pci-compliance、订阅生命周期与催缴 billing-automationSkill 体系与渐进式披露说明docs/agent-skills.md插件安装方式/plugin install payment-processing详见 docs/plugins.md【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考