在线支付系列四PayPal——一位海外买家的安全支付之旅Emma 为什么不敢在陌生网站输入卡号伦敦的设计师 Emma 在 Instagram 上刷到了一个小众手工包品牌。她点进链接一个设计简洁的独立站标价 $89 的手工皮包看起来很不错。她心动了但手指悬在「Buy Now」按钮上方犹豫了三秒。这个网站她从没听说过。页面底部没有什么知名品牌的 logoAbout Us 写得语焉不详。如果她输入了信用卡号万一是诈骗网站怎么办被盗刷了怎么办然后她看到了支付选项里的PayPal 按钮。“PayPal 我信得过。就算有问题PayPal 会帮我追回来。”她点击 PayPal 按钮跳转到 PayPal 登录页面输入邮箱和密码确认支付。全程没有在这个陌生网站上输入任何银行卡信息。这就是 PayPal 存在的核心价值在商户和买家之间建立信任层。对 Emma 来说PayPal 是安全感对商户来说PayPal 意味着能转化那些不敢在陌生网站输卡号的用户。这篇文章就从 Emma 的这笔 $89 说起。一、PayPal 的商业逻辑在讲技术之前先理解 PayPal 在支付生态中的位置。1.1 PayPal 不是信用卡也不是银行PayPal 的角色有点像支付宝——它是一个数字钱包。用户在 PayPal 绑定信用卡或银行账户购物时只需要登录 PayPal 确认不需要向商户暴露任何银行信息。关键数据4.31 亿活跃账户主要在北美和欧洲覆盖 200 个国家/地区支持 25 种货币注册门槛极低——有个邮箱就能开始收款1.2 PayPal 的费用结构但安全感和低门槛是有代价的。PayPal 是四大支付渠道中费率最高的Emma 这笔 $89 的交易手续费$89 × 3.49% $0.49 $3.60商户到账$85.40如果 Emma 后来申请了退款呢PayPal不退原交易手续费——商户白亏 $3.59这是和 Stripe 最大的区别。Stripe 退款时会退手续费PayPal 不会。如果 Emma 发起了争议呢PayPal 收取$15 标准争议费高争议量账户争议率过高升至$30/笔如果交易未通过 PayPal 账户而直接走信用卡网络退单费为$20/笔一句话总结 PayPal 的成本特点费率最高、退款最贵不退手续费但门槛最低、买家信任度最高。适合需要转化谨慎型海外用户的场景。二、PayPal 的技术架构PayPal 的支付流程和 Stripe 不同。Stripe 的核心交互在前端Stripe.js而 PayPal 是一种前端触发 后端完成的模式。2.1 整体流程让我们跟着 Emma 的 $89 走一遍Emma 的浏览器 商户后端 PayPal 服务器 │ │ │ │ ① 点击 PayPal 按钮 │ │ │──────────────────────→│ │ │ │ ② 创建 Order │ │ │─────────────────────→│ │ │ ③ 返回 Order ID │ │ │←─────────────────────│ │ ④ 返回 Order ID │ │ │←──────────────────────│ │ │ │ │ │ ⑤ PayPal SDK 弹出登录窗口 │ │─────────────────────────────────────────────→│ │ ⑥ Emma 登录 PayPal 确认支付 │ │─────────────────────────────────────────────→│ │ │ │ │ ⑦ PayPal 返回 APPROVED │ │←─────────────────────────────────────────────│ │ │ │ │ ⑧ 告诉后端去 Capture │ │ │──────────────────────→│ │ │ │ ⑨ Capture Order │ │ │─────────────────────→│ │ │ ⑩ 返回 COMPLETED │ │ │←─────────────────────│ │ │ ⑪ 更新订单状态 │注意第 ⑨ 步和 Stripe 不同PayPal 的扣款需要后端主动调用 Capture。用户在 PayPal 弹窗中确认后钱还没有真正扣——你需要再调一次 API 来捕获这笔款。这其实就是信用卡的授权-捕获分离思想——PayPal 也借鉴了这个设计。2.2 认证方式OAuth 2.0PayPal API 使用OAuth 2.0认证。每次调用 API 前你需要用 Client ID 和 Secret 换取一个 Access Token。这和支付宝/微信的签名机制完全不同。支付宝/微信是每个请求都签名PayPal 是先拿 token 再带着 token 请求。三、动手PayPal 对接全流程3.1 接入准备第一步访问 developer.paypal.com 登录 ▼ 第二步创建 AppMy Apps Credentials → Create App ▼ 第三步获取 Client ID 和 Secret ▼ 第四步创建沙盒测试账号一个买家账号 一个商家账号 ▼ 第五步配置 Webhook Endpoint密钥用途安全级别Client ID标识应用前端 SDK 后端 OAuth可公开Secret后端 API 认证 绝密Webhook ID验证 Webhook 来源 绝密3.2 OAuth 2.0先拿 Token每次调用 PayPal API 之前你都需要先获取 Access Token。Token 有效期通常是几小时过期后重新获取即可。importbase64,requests,os PAYPAL_CLIENT_IDos.getenv(PAYPAL_CLIENT_ID)PAYPAL_SECRETos.getenv(PAYPAL_SECRET)PAYPAL_BASEhttps://api-m.sandbox.paypal.com# 沙盒环境defget_access_token():用 Client ID Secret 换取 Access Tokenauthbase64.b64encode(f{PAYPAL_CLIENT_ID}:{PAYPAL_SECRET}.encode()).decode()resprequests.post(f{PAYPAL_BASE}/v1/oauth2/token,headers{Authorization:fBasic{auth},Content-Type:application/x-www-form-urlencoded,},data{grant_type:client_credentials})returnresp.json()[access_token]和支付宝/微信的区别支付宝和微信是每个请求都用私钥签名PayPal 是先用凭证换 token然后带着 token 请求。两种方式都安全但 OAuth 的方式更符合 REST API 的风格。3.3 前端Smart Payment ButtonsPayPal 提供了一套官方的前端 SDK——Smart Payment Buttons。它会根据用户所在地区自动显示最合适的 PayPal 按钮样式。!-- 引入 PayPal SDK --scriptsrchttps://www.paypal.com/sdk/js?client-idYOUR_CLIENT_IDcurrencyUSD/script!-- PayPal 按钮容器 --dividpaypal-button-container/divpaypal.Buttons({// ① 用户点击按钮后调用你的后端创建订单createOrder:async(){constresawaitfetch(/api/pay/paypal/create-order,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify({amount:89.00,currency:USD,description:Handmade Leather Bag,order_id:ORD_20260404002,})});constdataawaitres.json();returndata.paypal_order_id;// 返回给 PayPal SDK},// ② Emma 在 PayPal 弹窗中确认后调用后端去 CaptureonApprove:async(data){constresawaitfetch(/api/pay/paypal/capture-order,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify({paypal_order_id:data.orderID})});constresultawaitres.json();if(result.statusCOMPLETED){alert(支付成功);}},// ③ 出错了onError:(err){console.error(PayPal Error:,err);alert(支付出现问题请重试);},// ④ Emma 在 PayPal 弹窗中取消了onCancel:(){alert(支付已取消);},}).render(#paypal-button-container);3.4 后端创建订单 捕获fromfastapiimportFastAPI,Request,HTTPExceptionimportrequestsashttp_requestsimportos appFastAPI()PAYPAL_BASEos.getenv(PAYPAL_BASE_URL,https://api-m.sandbox.paypal.com)app.post(/api/pay/paypal/create-order)asyncdefcreate_order(request:Request):Emma 点击 PayPal 按钮后后端创建一个 PayPal 订单bodyawaitrequest.json()tokenget_access_token()order_data{intent:CAPTURE,# 意图直接捕获不是先授权再捕获purchase_units:[{reference_id:body[order_id],# 你的订单号description:body.get(description,),amount:{currency_code:body.get(currency,USD),value:body[amount],# 金额——注意是元字符串不是分}}],application_context:{brand_name:Your Store,user_action:PAY_NOW,}}resphttp_requests.post(f{PAYPAL_BASE}/v2/checkout/orders,headers{Authorization:fBearer{token},Content-Type:application/json,},jsonorder_data)resultresp.json()ifresp.status_code201:# 保存你的订单号和 PayPal 订单号的映射关系awaitsave_order_mapping(body[order_id],result[id])return{paypal_order_id:result[id],status:result[status],# 此时应该是 CREATED}raiseHTTPException(400,detailresult)app.post(/api/pay/paypal/capture-order)asyncdefcapture_order(request:Request):Emma 在 PayPal 弹窗中确认后后端来捕获这笔款bodyawaitrequest.json()tokenget_access_token()resphttp_requests.post(f{PAYPAL_BASE}/v2/checkout/orders/f{body[paypal_order_id]}/capture,headers{Authorization:fBearer{token},Content-Type:application/json,},)resultresp.json()ifresult[status]COMPLETED:# 提取支付详情captureresult[purchase_units][0][payments][captures][0]awaitupdate_order_status(order_idresult[purchase_units][0][reference_id],statusPAID,transaction_idcapture[id],amountcapture[amount][value],currencycapture[amount][currency_code],)return{status:result[status]}注意金额单位PayPal 和支付宝一样用元字符串不是分。89.00而不是8900。这一点和 Stripe/微信相反。3.5 Webhook 验签PayPal 的 Webhook 验签方式比较独特——它不是你自己算签名比对而是调用 PayPal 的 API 来验证。app.post(/api/pay/paypal/webhook)asyncdefpaypal_webhook(request:Request):PayPal Webhook 处理bodyawaitrequest.json()headersdict(request.headers)# 1. 验签——调用 PayPal 官方 API 验证tokenget_access_token()verify_payload{auth_algo:headers.get(paypal-auth-algo),cert_url:headers.get(paypal-cert-url),transmission_id:headers.get(paypal-transmission-id),transmission_sig:headers.get(paypal-transmission-sig),transmission_time:headers.get(paypal-transmission-time),webhook_id:os.getenv(PAYPAL_WEBHOOK_ID),webhook_event:body,}verify_resphttp_requests.post(f{PAYPAL_BASE}/v1/notifications/verify-webhook-signature,headers{Authorization:fBearer{token},Content-Type:application/json,},jsonverify_payload)ifverify_resp.json().get(verification_status)!SUCCESS:raiseHTTPException(400,Invalid webhook signature)# 2. 业务处理event_typebody[event_type]resourcebody[resource]ifevent_typePAYMENT.CAPTURE.COMPLETED:# 幂等检查 更新订单状态awaithandle_capture_completed(resource)elifevent_typePAYMENT.CAPTURE.REFUNDED:awaithandle_refund(resource)elifevent_typeCUSTOMER.DISPUTE.CREATED:# 收到争议通知需要及时处理awaithandle_dispute_created(resource)return{status:ok}四大渠道的验签方式对比渠道验签方式复杂度支付宝用支付宝公钥证书验证 RSA 签名★★★☆微信支付用平台证书验 RSA 签名 AES-GCM 解密★★★★Stripe用 Webhook Secret 算 HMAC-SHA256 比对★★☆☆PayPal调用 PayPal API 验证把签名信息转发给 PayPal★★★☆四、PayPal 的争议保护Emma 的安全网Emma 之所以敢在陌生网站用 PayPal 付款核心原因是 PayPal 的买家保护计划Buyer Protection。4.1 买家保护怎么运作如果 Emma 收到的手工包和描述不符比如图片是真皮收到的是人造革或者根本没收到包裹她可以在 PayPal 发起争议。Emma 发起争议 → PayPal 冻结 $89 → 商户有 20 天响应 │ ├── 商户和 Emma 协商解决 → 结案 │ └── 协商失败 → PayPal 介入裁决 │ ├── 判商户赢 → 解冻 $89退还给商户 └── 判 Emma 赢 → $89 退给 Emma商户承担损失 $15 争议费4.2 对商户的影响作为商户PayPal 的争议保护是一把双刃剑好处它让更多谨慎型用户愿意下单。没有 PayPalEmma 可能直接关掉页面——你连成交的机会都没有。风险PayPal 的裁决总体上倾向于保护买家。如果你没有充分的发货证明和交易记录很可能败诉。建议每笔订单都保存发货证明和物流追踪号使用 PayPal 的卖家保护功能——寄送到 PayPal 交易详情中的地址收到争议后尽快响应20 天内不要拖争议率控制在 1% 以下否则 PayPal 会采取限制措施五、PayPal 的特殊之处对接过支付宝、微信、Stripe 之后PayPal 有一些独特的特点值得单独说明。5.1 退款政策手续费不退这是 PayPal 和其他三个渠道最大的区别。当你退款时渠道退款后原交易手续费支付宝✅ 退还微信支付✅ 退还Stripe✅ 退还PayPal❌ 不退还也就是说如果 Emma 退了那笔 $89 的订单商户不仅拿不到钱还要亏 $3.59 的手续费。如果你的退货率很高这笔成本会非常可观。5.2 PayPal 支持部分退款好消息是 PayPal 支持部分退款最多 10 次。如果 Emma 对包的颜色不满意但不想退货你可以退 $20 表示歉意而不是全额退款。5.3 汇率加价如果你的结算币种和用户的支付币种不同PayPal 会自动做货币转换——但它会在中间汇率上加 3~4% 的差价。这是跨境使用 PayPal 时一个经常被忽略的隐性成本。5.4 退款时限PayPal 的退款时限是180 天6 个月。相比之下支付宝是 3 个月微信是 1 年Stripe 没有限制。六、沙盒测试PayPal 的沙盒环境和生产环境完全隔离。你需要在 developer.paypal.com 创建测试账号。6.1 测试账号PayPal 沙盒会为你自动创建两个测试账号Business 账号模拟商户用来收款Personal 账号模拟买家就像 Emma用来付款6.2 测试卡号如果你需要测试用信用卡支付而不是 PayPal 余额的场景卡组织卡号用途Visa4032039317984658正常支付Mastercard5425233430109903正常支付Visa4687380000000002触发拒绝6.3 沙盒 vs 生产切换到生产环境只需要做两件事把 API 地址从api-m.sandbox.paypal.com改成api-m.paypal.com把沙盒的 Client ID / Secret 换成生产的七、什么时候该选 PayPal经过这篇文章的学习让我们总结一下 PayPal 的适用场景。强烈推荐接入 PayPal 的场景你的目标用户在北美或欧洲你是一个新品牌/小品牌用户对你的网站缺乏信任你卖的是数字商品或服务PayPal 的即时交付验证机制成熟你需要最快速度上线收款注册即用不需要企业审核可以不接 PayPal 的场景你只做中国市场用户几乎不用 PayPal你的退款率很高手续费不退的成本太大你已经有了知名品牌背书用户信任度足够高你的客单价很低固定费用 $0.49 的比例太高本系列文章导读篇目标题你将学到第 1 篇概览篇——一笔订单触发的支付之旅四方模型、支付生命周期、市场格局、成本分析、选型决策第 2 篇支付宝 微信支付——一杯咖啡的扫码之旅签名机制、回调处理、AES-GCM 解密、完整对接代码第 3 篇Stripe 信用卡——一件跨境商品的卡支付之旅Payment Intents、3D Secure、PCI DSS、前后端代码第 4 篇PayPal——一位海外买家的安全支付之旅本文OAuth 认证、Smart Buttons、争议保护、Webhook第 5 篇统一支付网关——当四条河流汇入一片海三层架构、策略模式、幂等设计、对账补偿参考来源PayPal Developer DocumentationPayPal Orders V2 APIPayPal WebhooksPayPal Buyer ProtectionPayPal Standard Transaction FeesPayPal Seller Protection欢迎关注公众号coft获取更多深度技术文章。下一篇也是最后一篇——当你同时接了支付宝、微信、Stripe、PayPal 之后四套签名机制、四种回调格式、四个退款接口……维护成本开始指数增长。怎么办答案是搭一个统一支付网关。