ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

支付宝支付接入全流程:密钥配置与沙箱联调避坑指南

支付宝支付接入全流程:密钥配置与沙箱联调避坑指南 做后端开发的基本逃不掉接支付。尤其是支付宝项目一上量老板大概率会问“我们的订单什么时候能在线支付”。我在不同公司接过三次支付宝支付几乎每次都能在群里看到有人问密钥去哪生成为什么验签一直失败沙箱怎么开通这类问题翻来覆去。索性把我自己从零开通、配置、联调、上线的完整流程写下来重点放在最容易出错的几个环节账号选择、应用创建、密钥配置、回调验签、沙箱联调。不管你是第一次接支付还是之前只接过微信支付想补个支付宝照着做都能少走弯路。1. 开通前的准备账号类型和材料清单很多人第一步就走错了拿一个普通个人支付宝账号去开放平台登录结果创建应用后想签约线上支付产品直接被提示主体资质不符合。所以在动手之前先搞清楚自己手上应该用哪种账号。1.1 个人开发者账号和企业账号怎么选支付宝开放平台的账号体系分为个人开发者和企业开发者两类。简单说个人开发者账号用自己的身份证完成实名认证就能注册适合自学、练手、做沙箱测试。但个人身份能签约的线上支付类产品非常有限大部分真实交易场景电脑网站支付、手机网站支付、APP支付都要求主体是公司或个体工商户。企业开发者账号需要用营业执照、对公账户、法人身份证等材料完成企业认证。认证通过后才能申请签约真正的支付产品。如果你接的是公司项目别犹豫直接让公司用企业支付宝账号去注册。如果是自己接私活客户那边也必须有企业主体否则钱收不了。个人开发者想跑通支付流程靠沙箱环境完全够用这点后面第5部分会细说。我见过一个真实情况有同事用个人账号建好了应用代码全部写完最后发现签约不了只能换企业账号重新创建应用重新配密钥。所以开头这一下一定要想清楚。1.2 需要准备的材料清单企业开发者注册认证时建议提前备好以下材料营业执照照片或扫描件要求清晰、四角完整法人身份证正反面照片企业对公银行账号用于打款验证部分场景需要企业支付宝账号可以是公司邮箱注册的域名且已完成ICP备案签约线上支付产品时基本都会要求这里要强调域名备案不是可选项。支付宝签约时会校验网站域名的主体信息如果域名没备案或者备案主体和支付宝企业认证主体不一致签约会很麻烦。开发阶段可以先用沙箱不备案但上线前一定要把备案搞定。1.3 登录开放平台并完成开发者认证打开支付宝开放平台官网选择“登录”用企业支付宝账号扫码登录。登录后进入“控制台”如果系统提示你完善开发者信息或开发者认证就按指引补充资料。认证状态一般分“未认证”“认证中”“已认证”只有已认证的账号才能创建应用和签约产品。整个注册认证流程正常一两天能跑完企业打款验证通常当天到账。等不了的话可以先用沙箱环境提前把技术方案验证一遍等账号认证下来直接上正式配置。2. 创建应用与签约产品账号认证通过后接下来要创建应用。支付宝把不同类型的业务拆成了不同的应用类型比如“网页移动应用”“小程序应用”等。大多数电商网站、H5商城、App都走“网页移动应用”这一条线。2.1 在控制台创建应用登录开放平台控制台后找到“网页移动应用”点击“创建应用”。这里有几个字段要填应用名称建议直接写项目名比如“某某商城”方便后续在列表里认出来应用类型选“自研应用”说明这个应用是自己开发的应用图标有些版本需要上传随便放一张符合尺寸的图就行创建完成后系统会自动生成一个应用APPID这是一串纯数字后面代码里要用到。先把APPID存到本地备忘录别搞丢了。2.2 添加产品能力并签约应用创建成功后进入应用详情页找到“产品能力”或“添加能力”入口。在这里选择你需要的支付产品电脑网站支付对应PC端浏览器里的扫码/跳转支付手机网站支付对应手机浏览器里的H5支付APP支付对应手机App内拉起支付宝客户端支付JSAPI支付对应支付宝小程序、生活号等场景如果你的业务是PC官网手机H5那电脑网站支付和手机网站支付都要添加。添加后大概率进入“签约”流程系统会要求你提交网站信息、应用说明、截图等然后等待审核。审核周期不好说快的话几小时慢的话两三天。审核通过后产品状态会变成“已签约”。这里有个关键点签约通过前线上环境虽然也能发起支付请求但支付宝会返回“产品未开通”之类的错误沙箱环境不受影响。所以不要以为代码写完了就能直接收钱签约没通过一切都白搭。2.3 开发环境和生产环境的区分应用在未发布前一直处于“开发中”状态这时候可以随便调参数、改配置。正式上线前需要在应用详情页里提交发布审核审核通过后应用状态变成“已上线”。但这和产品签约是两件事应用发布审核主要校验应用信息的完整性产品签约决定你这个应用有没有权限调用某个支付产品两者都通过后才能算真正具备线上收款条件。3. 密钥配置全过程最容易被绕晕的一环支付宝支付里最容易出问题的就是密钥配置。我刚接手时在这上面卡了整整两天后来才搞明白整套逻辑。这里我尽量把它讲透。3.1 搞清楚公钥私钥的关系支付宝支付接口用的是RSA2非对称加密也就是SHA256WithRSA。系统里一共有三样东西应用私钥自己本地生成自己保存绝对不能泄露给别人应用公钥由应用私钥推导出来需要上传到支付宝开放平台支付宝公钥支付宝根据你上传的应用公钥生成的另一个公钥在开放平台页面上可以查到配置时需要填到自己的代码里生活化类比一下应用私钥是你家的钥匙只放在自己兜里应用公钥是你贴在门口的锁告诉支付宝“用这把锁验证我发来的消息”支付宝公钥是支付宝给你的锁你要用它验证支付宝发来的每一笔回调。很多人验签失败就是因为把“应用公钥”当成“支付宝公钥”填进了代码里。这两个东西长得很像都是BASE64字符串但作用完全不同填反了必然失败。3.2 生成密钥的两种方式生成RSA2密钥对通常有两种方式用支付宝官方密钥工具或者用OpenSSL命令行。我两个都用过说一下区别。如果是在Windows本机上快速操作用支付宝官方提供的“密钥生成工具”最省事。下载后打开选择“生成密钥”工具会直接生成应用公钥和应用私钥两个文本保存后就能用。工具的缺点是版本更新慢有些新电脑打开会有兼容问题。如果是在Linux服务器上操作或者想走自动化流程用OpenSSL更稳。一次性生成私钥和公钥# 生成2048位的RSA私钥 openssl genrsa -out app_private_key.pem 2048 # 从私钥中导出公钥 openssl rsa -in app_private_key.pem -pubout -out app_public_key.pem # 转成PKCS8格式Java后端一般需要这个格式 openssl pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem生成后用文本编辑器打开这几个文件就能看到类似下面的内容-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQ... -----END PRIVATE KEY-----配置到Java代码里时不需要带“BEGIN PRIVATE KEY”这行的标记和换行符可以把内容拼成一行。Python和Node的SDK则通常要求保留完整格式具体看SDK文档。3.3 在开放平台配置接口加签方式进入应用详情页找到“开发设置”里面有一项“接口加签方式”。点进去后选择“公钥模式”把上一步生成的“应用公钥”内容粘贴进去保存。保存成功后页面上会出现“支付宝公钥”字段点击查看复制完整内容。这一串就是要填到代码里的alipay_public_key。这里特别提醒加签方式还有另一种“公钥证书模式”需要上传CSR文件、应用公钥证书、支付宝公钥证书等适合对安全要求更高的企业。对于绝大多数中小项目公钥模式足够用而且流程简单。我第一次做支付时为了图新鲜选了证书模式结果被证书上传流程折磨得够呛。建议先跑通公钥模式上线稳定后再评估要不要换证书模式。3.4 配置到项目里的完整示例以Java项目为例配置类一般长这样public class AlipayConfig { // 应用APPID在开放平台应用详情页查看 public static final String APP_ID 2021000123456789; // 应用私钥自己生成的那串注意不是支付宝公钥 public static final String APP_PRIVATE_KEY MIIEvQIBADANBgkqhkiG9w0BAQEFAAS...; // 支付宝公钥从开放平台获取 public static final String ALIPAY_PUBLIC_KEY MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...; // 支付宝网关地址正式环境 public static final String GATEWAY_URL https://openapi.alipay.com/gateway.do; // 签名方式目前统一用RSA2 public static final String SIGN_TYPE RSA2; // 编码格式 public static final String CHARSET utf-8; // 参数格式固定json public static final String FORMAT json; }实际项目里不要把密钥硬编码到类里建议放到环境变量、配置中心或数据库配置表里避免提交到Git仓库后泄露。我有一次看到同事的代码仓库里直接带着生产环境的支付宝私钥吓得当场让他改掉。私钥一旦泄露别人就能伪造请求后果很严重。4. 服务端接入从下单到回调验签密钥配好以后真正写代码反而不难。支付宝的SDK封装得比较完善核心流程就是“创建客户端 - 构造请求 - 发起支付 - 处理回调”。4.1 引入SDK依赖Java项目用Maven的话在pom.xml里加dependency groupIdcom.alipay.sdk/groupId artifactIdalipay-sdk-java/artifactId version4.38.4.ALL/version /dependency如果是Python项目可以用支付宝官方维护的SDK也可以直接用第三方封装库Node.js同样有官方SDK。本文以Java为例其他语言逻辑完全一样。4.2 构建支付宝客户端AlipayClient alipayClient new DefaultAlipayClient( AlipayConfig.GATEWAY_URL, AlipayConfig.APP_ID, AlipayConfig.APP_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGN_TYPE );这一步就是把你前面配置的密钥都组装起来后面所有请求都用这个client发起。4.3 电脑网站支付下单示例电脑网站支付对应的接口是alipay.trade.page.pay。在Java SDK里代码大概如下AlipayTradePagePayRequest request new AlipayTradePagePayRequest(); // 异步回调地址支付宝服务器会往这个地址发送支付结果通知 request.setNotifyUrl(https://api.example.com/pay/notify); // 同步跳转地址支付完成后浏览器会跳转到这个页面 request.setReturnUrl(https://www.example.com/order/result); JSONObject bizContent new JSONObject(); // 商户订单号必须唯一 bizContent.put(out_trade_no, 202506071200001); // 订单总金额单位是元不是分 bizContent.put(total_amount, 0.01); // 订单标题 bizContent.put(subject, 测试商品); // 销售产品码固定值 bizContent.put(product_code, FAST_INSTANT_TRADE_PAY); request.setBizContent(bizContent.toString()); String form alipayClient.pageExecute(request).getBody(); // 把form输出到浏览器即可支付宝会渲染出一个自动提交的表单这里有两个细节第一total_amount单位是元。如果你的订单表里存的是分比如100分那这里要传“1.00”不能传“100”。我见过有人直接把数据库里的分字段传过去结果用户付了100倍的钱退款流程能跑半天。第二pageExecute返回的是字符串里面是一段HTML表单。后端拿到后直接把它输出到HTTP响应里浏览器会自动跳转到支付宝收银台。如果你发现输出到页面上是一堆HTML源码说明你没有设置Content-Type为text/html。手机网站支付H5用的接口是alipay.trade.wap.pay主体代码几乎一样区别是bizContent里的product_code要换成“QUICK_WAP_WAY”请求类换成AlipayTradeWapPayRequest。返回的也是一段form表单浏览器打开后会自动唤起支付宝App或进入手机网页收银台。4.4 异步通知和同步回跳的区别支付成功后会发生两件事浏览器从支付宝页面跳回你的return_url用户能看到“支付成功”页面支付宝服务器直接向你的notify_url发送一个POST请求通知后端支付结果很多新手只处理了return_url发现订单状态没更新找了一上午最后发现是notify_url没处理。记住return_url只是给用户看的不可作为订单状态更新的依据因为用户可能支付完直接关掉浏览器根本不跳回来。真正可靠的订单状态更新必须依赖异步通知。异步通知的处理逻辑可以拆成几步PostMapping(/pay/notify) public String handleNotify(HttpServletRequest request) { MapString, String params new HashMap(); MapString, String[] requestParams request.getParameterMap(); for (Map.EntryString, String[] entry : requestParams.entrySet()) { params.put(entry.getKey(), entry.getValue()[0]); } // 第一步验签确认是支付宝发来的请求 boolean signVerified AlipaySignature.rsaCheckV1( params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE); if (!signVerified) { // 验签失败直接返回失败支付宝会认为没送达继续重试 return failure; } // 第二步校验业务参数 String appId params.get(app_id); String outTradeNo params.get(out_trade_no); String totalAmount params.get(total_amount); String tradeStatus params.get(trade_status); if (!AlipayConfig.APP_ID.equals(appId)) { return failure; } // 第三步根据订单号查询本地订单核对金额是否一致 // 这一步很关键能防止回调被伪造到错误订单上 // 第四步判断交易状态 if (TRADE_SUCCESS.equals(tradeStatus) || TRADE_FINISHED.equals(tradeStatus)) { // 更新订单状态为已支付 // 注意幂等处理防止重复回调导致重复更新 } // 第五步返回success给支付宝 return success; }验签的作用是确认这个POST请求确实来自支付宝而不是攻击者伪造的。如果验签失败千万不能继续更新订单状态。支付宝收到非success的返回后会按一定策略重试通知所以处理逻辑必须保证幂等。这里还有个容易忽略的坑异步通知里不要把“TRADE_SUCCESS”直接当成“支付成功”的唯一判断。如果订单金额超过一定限制还会出现“WAIT_BUYER_PAY”等中间状态。最稳妥的做法是以notify里的trade_status为依据只要状态是TRADE_SUCCESS或TRADE_FINISHED并且金额、订单号核对无误就可以更新状态。4.5 金额计算建议涉及金额计算千万不要用double或float否则会出现0.10.2不等于0.3的经典问题。Java里用BigDecimal数据库里用DECIMAL(10,2)之类的字段。把分转成元再传给支付宝时可以这样BigDecimal amountInFen new BigDecimal(100); BigDecimal amountInYuan amountInFen.divide(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP);反过来收到支付宝回调里的total_amount时如果数据库存的是分就乘100取整。整个链路里保持单位一致是最省心的。5. 沙箱环境配置与联调支付宝开放平台提供了完整的沙箱环境专门用来在正式签约和上线前做测试。沙箱环境最大的价值是不需要企业资质、不需要签约审核、不需要真实资金就能把所有接口调通。5.1 申请沙箱应用登录开放平台控制台找到“沙箱”相关入口进入后系统一般会有一个默认的沙箱应用或者让你自己创建。创建后你会拿到沙箱环境专用的APPID沙箱买家账号形如xxxxxxalipaytest.com以及对应的登录密码沙箱环境的网关地址注意沙箱应用的APPID和正式环境的APPID是两回事千万别混用。沙箱网关一般是https://openapi-sandbox.dl.alipaydev.com/gateway.do而正式环境的网关是https://openapi.alipay.com/gateway.do这两个地址只要搞错一个请求就会失败。我的经验是在配置类里做两个环境切换用Spring的Profile或者环境变量去控制避免每次发布前都去改代码。5.2 沙箱密钥怎么配沙箱应用同样需要配置接口加签方式。你可以复用刚才生成的那套密钥对直接把同一个应用公钥上传到沙箱应用的“开发设置”里。然后从沙箱应用页面获取“沙箱支付宝公钥”。注意沙箱的支付宝公钥和正式环境的支付宝公钥不是同一串别一起复制到代码里。我建议在代码里把沙箱和正式环境的所有配置单独拆开比如沙箱一个配置文件正式一个配置文件启动时通过环境变量选择。5.3 完整联调流程沙箱环境准备好的话可以这样走一遍流程启动本地服务把下单接口的地址指到沙箱网关在浏览器访问下单接口确认能正确返回支付表单浏览器会自动跳到沙箱收银台用沙箱买家账号登录选择支付方式完成模拟支付观察服务端日志确认notify_url收到回调验签通过订单状态更新检查数据库订单状态是否正确金额是否正确这里有个现实问题沙箱环境要求notify_url必须是公网可访问的地址。如果你的服务跑在本地电脑上支付宝服务器是访问不到的。常用的做法有两种把服务部署到一台有公网IP的测试服务器上测试完再开本地环境本地开发时用内网穿透工具比如natapp、cpolar这类把本机端口映射成一个公网临时地址然后把notify_url填成这个临时地址用内网穿透工具联调时每次启动穿透服务地址会变记得同步改notify_url。还有一点穿透工具的免费域名有时会被运营商拦截如果回调一直收不到换个工具试试也是一种思路。5.4 沙箱环境常见异常我在沙箱测试中遇到不少奇奇怪怪的问题列几个典型的沙箱应用下单提示“应用未签约或权限不足”检查沙箱应用是否已经添加了你要测试的支付产品能力支付页面打不开或无法登录检查沙箱买家账号是否输入正确沙箱账密的登录入口和正式支付宝App是隔离的回调始终收不到先检查notify_url外网能否访问再检查服务端是否返回了“success”如果返回了其他内容支付宝会判定通知失败并持续重试沙箱网关返回“参数错误”大概率是网关地址写成了正式环境或者APPID填成了正式环境的6. 上线前检查清单与常见问题代码全部调通沙箱测试也过了接下来就是上线。上线不是把代码一部署就完事有几个点一定要检查不然很容易出现线上支付事故。6.1 上线前逐项检查清单检查项检查内容常见错误网关地址确认线上代码使用的是正式网关忘了把沙箱网关切回正式导致支付接口报错APPID确认是正式应用的APPID拿着沙箱APPID上线所有请求都失败支付宝公钥确认是正式环境的支付宝公钥复用沙箱公钥验签失败应用私钥确认私钥与正式应用公钥匹配沙箱和正式用同一套私钥但两边公钥不对应产品签约确认所需支付产品已签约未签约就上线用户支付时报“产品未开通”域名备案确认支付相关域名已完成ICP备案签约审核不通过或支付页面被浏览器拦截回调地址确认notify_url是线上可访问的HTTPS地址回调指向localhost或内网地址收不到通知服务器时间确保服务器时间准确误差不能太大时间偏差大签名验证可能失败订单幂等回调处理逻辑不依赖顺序重复通知不重复更新重复回调导致订单状态被覆盖日志记录记录下单、回调、验签、异常等关键日志出了问题无从排查6.2 常见问题速查问题现象可能原因解决方案下单报“签名错误”应用私钥和上传的应用公钥不匹配重新生成密钥对重新上传应用公钥下单报“应用未签约”对应支付产品没有签约或审核未通过去应用详情页查看产品签约状态回调验签失败代码里alipay_public_key填成了应用公钥换成从开放平台获取的支付宝公钥回调一直收不到notify_url填错、公网不可达、返回内容不是success用curl测试回调地址检查服务端返回内容支付金额不对total_amount单位传错确认单位是元从分转元用BigDecimalH5页面在浏览器里支付失败手机网站支付未签约或浏览器禁止唤起App确认产品已签约检查是否在微信/钉钉内置浏览器中访问6.3 实战避坑心得说几个我踩过比较深、比较有代表性的坑。第一个坑公钥私钥搞混。我早期为了省事直接把应用公钥当成支付宝公钥填到代码里结果所有回调验签全失败。排查了整整一天最后打印出来比对才发现是公钥填错了。后来我养成了一个习惯把应用私钥、应用公钥、支付宝公钥分别存成三个变量代码里注释写明来源从源头避免混淆。第二个坑沙箱和正式环境配置串了。有一次上线前急着发布配置文件的网关临时改回沙箱做最后一次联调结果忘了改回来。上线后所有支付请求全部打到沙箱用户付不了款紧急回滚才恢复。那次之后我把网关、APPID、支付宝公钥三个参数做成了部署时强制校验启动服务时检查当前环境变量一旦生产环境出现沙箱参数直接拒绝启动。第三个坑金额单位问题。我的订单表金额一直存的是分但没注意支付宝要求传元结果测试时下单金额填了个分单位的数字用户页面上显示金额直接放大了100倍。这种问题不是报错表面上一切正常但费用完全不对。处理办法就是统一用BigDecimal并且在下单代码和回调校验代码里都加上单位转换的注释。第四个坑异步通知的幂等处理。支付宝回调机制是“一定会到达但可能会重复”。如果回调处理里没有做幂等用户支付成功后订单状态在“已支付”和“待支付”之间反复横跳售后工单得爆炸。解决方法很简单更新订单状态前先查一遍当前状态如果已经是已支付直接返回success。7. 最后的经验总结先把沙箱跑通再想签约的事以上这套流程走完支付宝支付的接入基本就算落地了。我个人实际操作下来的最大体会是先别急着申请签约先把沙箱环境跑通把所有接口和回调逻辑验证完再回头去走签约审核流程。这样审核时间再长也不影响开发进度等签约一通过改几个配置参数就能直接上线。对于第一次做支付的同学我建议不要一上来就用证书模式从公钥模式起步把流程跑通理解清楚应用私钥、应用公钥、支付宝公钥这三者之间的关系再考虑要不要升级更严格的配置方式。整个过程中任何一次签名、验签问题都可以先回到密钥配置这一环排查大概率问题出在公钥和私钥的对应关系上。最后再分享一个小技巧无论开发还是测试阶段每次发起支付和收到回调都把完整的请求参数、响应内容打到日志里。这样出了问题把日志翻出来一看就知道是下单时参数写错了还是回调里金额对不上排查效率会高很多。支付不是不能出问题而是出了问题要有办法快速定位。日志就是你在支付调试中最靠得住的朋友。
返回列表