简介PHP微信支付V3完整实例是一套面向PHP开发者的微信支付V3接口集成方案覆盖统一下单、前端调起支付、异步回调、订单查询与退款等核心流程适用于商城网站、小程序后端等需要快速接入微信支付的场景。压缩包共16个文件同时包含PHP和ASP脚本涉及支付API、签名与证书处理另有配置说明文档、pem证书、Access数据库.mdb、前端JS库及演示图片整体仅61KB目录简单清晰便于快速定位和使用。截至当前已有4621人学习下载在开发者中有较高参考价值。除可运行的demo外资料还介绍了V3版API签名机制、证书获取与存储、沙箱环境测试方法以及支付失败和退款时的异常处理思路并针对SQL注入、敏感信息传输等安全与合规问题给出注意点能帮助开发者避开常见接入坑点快速完成微信支付V3的上线显著缩短支付功能的开发周期。1. 微信支付 v3 对 PHP 开发者意味着什么从签名方式到落地姿势做 PHP 支付开发绕不开微信支付。如果你接手过老项目大概率被 v2 的 MD5 签名、原生 RSA 密钥、XML 报文和那套「证书文件要放服务器上」的体系折磨过。微信支付 v3 的出现把这些历史包袱几乎全部推倒重来报文改成 JSON、签名换成 SHA256-RSA2048、密钥体系拆成商户 API 证书和平台证书两套还引入了微信支付平台证书的下载与自动更新机制。对 PHP 开发者来说v3 最直观的感受是「终于不用再拼 XML 了」但代价是——你得先搞懂一套比 v2 严格得多的鉴权流程。这篇东西写给谁第一种是刚拿到一个 v3 接入任务、连 APIv3 密钥和商户证书都分不清的新手第二种是已经跑通了下单、但回调验签一直报「签名错误」的熟手。我假定你用的是 PHP 7.4 以上版本有 Composer 环境并且已经拿到了商户号 mchid、商户 API 证书和 APIv3 密钥这三样东西。如果你连这些是从哪来的都没概念建议先找一遍微信商户平台的「账户中心 - API 安全」页面再往下读。接下来我会按自己实际接支付时的顺序走一遍先搭好基础配置再写下单和回调把退款做为进阶单独收尾。整个方案不依赖任何第三方支付 SDK只靠 Guzzle 和 PHP 内置的 OpenSSL 扩展就能跑。2. 准备 APIv3 的密钥与证书先搞清楚四把钥匙分别锁什么2.1 四把密钥的分工和存放姿势v3 体系里一共有四类密钥/证书很多坑都是因为把它们的用途搞混。商户 API 证书apiclient_cert.pem、apiclient_key.pem是你向微信证明「我是这个商户」的身份凭证请求下单、退款这类写操作必须用它做客户端签名微信支付平台证书wechatpay_platform_cert.pem是微信用来证明「响应和回调确实来自微信」的公钥证书验签和解密回调都得靠它APIv3 密钥apiv3_key是一个 32 位字符串用来对回调报文里的敏感字段做 AES-256-GCM 解密最后商户号 mchid 本身不算钥匙但所有请求的签名串里都要带上它。存放位置值得单独说一句。生产环境里这几样东西不要放在 Web 根目录下更不要提交进 Git 仓库。常见的做法是放到项目根目录外的 storage/secure 目录或者直接使用环境变量加文件路径的组合。我一般会在 .env 里放 APIv3 密钥和商户号证书文件路径也通过环境变量传入这样部署到多台服务器时不需要改代码。# .env 配置示例不要在 .env 文件里写注释以外的中文 WECHAT_MCHID1900001234 WECHAT_APIV3_KEYyour_32_char_apiv3_key_here WECHAT_CERT_DIR/www/secure/wechat WECHAT_APPIDwx1234567890abcdef提示APIv3 密钥要求 32 个字符必须是 ASCII 可见字符。如果你在商户平台重置过密钥旧的、曾经用过的密钥会立即失效记得同步更新所有服务器上的配置。2.2 用 Guzzle 构建带证书的 HTTP 客户端整个 v3 请求流程里所有 HTTP 调用几乎都围绕同一个动作用商户私钥生成 Authorization 头。所以先把这一步封装好后续所有接口都是用它加个签名头再发 JSON。先创建项目并安装依赖composer require guzzlehttp/guzzle然后写一个 WechatPayV3Client 类把签名逻辑放在 buildAuthorizationHeader 方法里?php // src/WechatPayV3Client.php use GuzzleHttp\Client; use GuzzleHttp\Psr7\Request; class WechatPayV3Client { private string $mchid; private string $appId; private string $apiv3Key; private string $merchantPrivateKey; // 商户私钥内容 private string $merchantCertSerialNo; // 商户证书序列号 private Client $httpClient; public function __construct(array $config) { $this-mchid $config[mchid]; $this-appId $config[appid]; $this-apiv3Key $config[apiv3_key]; // 读取商户私钥文件内容 $this-merchantPrivateKey file_get_contents($config[merchant_private_key_path]); $this-merchantCertSerialNo $this-getCertSerialNo($config[merchant_cert_path]); $this-httpClient new Client([base_uri https://api.mch.weixin.qq.com]); } private function getCertSerialNo(string $certPath): string { $cert file_get_contents($certPath); $parsed openssl_x509_parse($cert); return $parsed[serialNumberHex] ?? $parsed[serialNumber]; } private function buildAuthorizationHeader(string $method, string $url, string $body): string { $timestamp time(); $nonce bin2hex(random_bytes(16)); $message $method . \n . $url . \n . $timestamp . \n . $nonce . \n . $body . \n; openssl_sign($message, $signature, $this-merchantPrivateKey, OPENSSL_ALGO_SHA256); return sprintf( WECHATPAY2-SHA256-RSA2048 mchid%s,nonce_str%s,timestamp%d,serial_no%s,signature%s, $this-mchid, $nonce, $timestamp, $this-merchantCertSerialNo, base64_encode($signature) ); } }这段代码里最关键的是拼签名串的格式HTTP 方法、请求路径不带域名、时间戳、随机串、请求体五个部分用 \n 连接最后再补一个 \n。很多人验签不过就是因为请求体空字符串时没保留最后的换行或者 URL 里带了 query string 却把 query 也拼进了待签名串。微信要求的规范是 URL 只包含 path 部分比如 /v3/pay/transactions/jsapi不要带 query 参数。serial_no 是从商户证书里解析出来的序列号注意 PHP 的 openssl_x509_parse 返回的 serialNumber 可能是十进制字符串需要确认是否需要转成十六进制。我在代码里先尝试读 serialNumberHex 字段这是 OpenSSL 扩展在较新版本里会提供的能避免进制转换这种化学。2.3 把下单请求封装成一个可复用的方法基础客户端有了现在补一个「统一下单」方法。v3 的 JSAPI 下单接口路径是 POST /v3/pay/transactions/jsapi比 v2 少了一堆 XML 包裹直接丢 JSON。这里有个容易踩的点接口路径必须与签名时用的路径完全一致多一个斜杠或少一个斜杠微信端验签就会失败。// 在 WechatPayV3Client 类中继续追加方法 public function createJsapiOrder(array $orderData): array { $url /v3/pay/transactions/jsapi; $body json_encode($orderData, JSON_UNESCAPED_UNICODE); $authHeader $this-buildAuthorizationHeader(POST, $url, $body); $response $this-httpClient-post($url, [ headers [ Authorization $authHeader, Content-Type application/json, Accept application/json, ], body $body, ]); $result json_decode($response-getBody()-getContents(), true); return $result; // 包含 prepay_id } // 实际调用示例 $client new WechatPayV3Client([ mchid getenv(WECHAT_MCHID), appid getenv(WECHAT_APPID), apiv3_key getenv(WECHAT_APIV3_KEY), merchant_private_key_path /www/secure/wechat/apiclient_key.pem, merchant_cert_path /www/secure/wechat/apiclient_cert.pem, ]); $order [ appid getenv(WECHAT_APPID), mchid getenv(WECHAT_MCHID), description 测试商品, out_trade_no ORDER_ . date(YmdHis) . rand(1000, 9999), notify_url https://yourdomain.com/api/wechat/notify, amount [total 100, currency CNY], // 金额单位是分 payer [openid 用户openid], ]; $result $client-createJsapiOrder($order); $prepayId $result[prepay_id] ?? null;下单参数里最容易出问题的是金额单位。v3 里 total 是整数、单位是分如果你从数据库取的是元忘了乘以 100就会出现「订单金额与回调金额不一致」的提示。description 字段是商品描述建议把商品名和订单号都拼进去后面对账会省很多心。out_trade_no 必须是商户号下唯一的字符串我习惯用日期加随机数生成但如果你想在回调里方便地关联业务订单推荐直接把数据库订单 ID 放进去。2.4 返回给前端的 JSAPI 调起参数二次签名的坑拿到 prepay_id 还不算完前端 wx.chooseWXMessage 之类的 JSAPI 调起需要四个参数appId、timeStamp、nonceStr、package值为 prepay_idxxx和一个签名 paySign。这个 paySign 是「商户私钥对这四个字段拼出来的字符串做 SHA256 签名」签名串格式是appIdxxxtimeStampxxxnonceStrxxxpackageprepay_idxxx注意这里的签名串没有 \n 分隔也不是 JSON而是 URL query 格式。和请求头的签名逻辑类似但拼法不同。很多人在这一步翻车拿请求签名的方式去签 paySign结果前端一直报 invalid signature。// 生成前端调起支付所需的参数 function buildJsapiPayParams(string $appId, string $prepayId, string $privateKeyPath): array { $timestamp (string) time(); $nonceStr bin2hex(random_bytes(16)); $package prepay_id . $prepayId; $signStr appId{$appId}timeStamp{$timestamp}nonceStr{$nonceStr}package{$package}; openssl_sign($signStr, $signature, file_get_contents($privateKeyPath), OPENSSL_ALGO_SHA256); return [ appId $appId, timeStamp $timestamp, nonceStr $nonceStr, package $package, signType RSA, paySign base64_encode($signature), ]; }这组参数直接返回给前端前端拿到后调用wx.requestPayment就能拉起收银台。有个关于 signType 的容易混淆的点v3 的 paySign 对应的 signType 是 RSA不是 SHA256-RSA2048后者的名称只用于 HTTP 请求头的 Authorization。如果你把请求头的签名类型名抄到这里前端 SDK 不认。3. 跑通支付回调与验签一套必须亲手实现的「信任链」3.1 为什么回调必须验 sign 而不是直接解密报文微信支付服务器把用户的支付结果推送到你配置的 notify_url推送的 HTTP 请求头里带了一个 Wechatpay-Signature 和一个 Wechatpay-Timestamp。报文体是加密过的使用 AES-256-GCM 算法加密了 resource 字段。很多新手第一反应是「先解密再处理业务」。这个顺序是错的。正确的顺序是先用微信支付平台证书验证报文的签名确认这笔通知确实来自微信支付再用 APIv3 密钥解密 resource拿到明文订单数据。如果跳过验签直接解密等于放弃了身份认证——任何能拿到你 notify_url 的人都可以伪造请求体触发你的业务逻辑。这个流程和登录鉴权很像先验证 JWT 的签名再读取 payload而不是先解码再看内容。这里的「先验签后解密」是整个回调安全的根基。3.2 回调验签代码用平台证书做 RSA 验签验签需要两个输入一是请求头里的 Wechatpay-Signature 和 Wechatpay-Timestamp、Wechatpay-Nonce二是原始报文 body。把这三段拼起来时间戳、随机串、报文主体用 \n 连接和请求签名是同一个拼法。// 回调处理器入口 public function handleNotify(string $rawBody, array $headers, string $platformCertPath): bool { $timestamp $headers[Wechatpay-Timestamp][0] ?? ; $nonce $headers[Wechatpay-Nonce][0] ?? ; $signature $headers[Wechatpay-Signature][0] ?? ; // 计算待验签文本 $message $timestamp . \n . $nonce . \n . $rawBody . \n; // 读取微信支付平台证书公钥 $platformCert file_get_contents($platformCertPath); $pubKey openssl_pkey_get_public($platformCert); // 用平台证书验签 $verifyResult openssl_verify($message, base64_decode($signature), $pubKey, OPENSSL_ALGO_SHA256); if ($verifyResult ! 1) { // 记录日志验签失败可能是平台证书过期或请求被篡改 return false; } // 验签通过后继续解密 resource return $this-decryptResource($rawBody); }验签失败时要看的具体原因九成以上是平台证书不对。微信支付平台证书有效期为五年但可以在商户平台主动更换换证时旧证书可能短暂过期。还有一种情况是你从接口下载平台证书时没有直接下载而是用了某个 SDK 的缓存换证后缓存没更新。后面我会专门讲平台证书的自动更新这里先记住一点验签用的证书必须和微信当前签名使用的证书序列号匹配。响应是 JSON 时验签逻辑和这里完全一样。3.3 用 APIv3 密钥解密 resourceAES-256-GCM 的 PHP 实现验签通过后报文体里的 resource 字段长这样{ id: ev-xxx, resource: { original_type: transaction, algorithm: AEAD_AES_256_GCM, ciphertext: base64编码的密文, associated_data: transaction, nonce: 随机串 } }解密参数全部在 resource 里算法是 AEAD_AES_256_GCM。PHP 的 openssl_decrypt 支持 aes-256-gcm但参数名容易搞混。这里列出完整的解密代码并解释每个参数来源// 解密回调里的 resource private function decryptResource(string $rawBody): array { $body json_decode($rawBody, true); $resource $body[resource] ?? []; $ciphertext base64_decode($resource[ciphertext]); $nonce $resource[nonce]; $associatedData $resource[associated_data] ?? ; $apiv3Key $this-apiv3Key; // PHP 的 openssl_decrypt 要求 key 是裸字符串不能是 base64 或 hex 格式 $plaintext openssl_decrypt( $ciphertext, aes-256-gcm, $apiv3Key, OPENSSL_RAW_DATA, $nonce, $tag, $associatedData ); if ($plaintext false) { // 记录日志并返回空数组 return []; } return json_decode($plaintext, true); }这里有一个很不起眼但影响结果的细节openssl_decrypt 的 $tag 参数按引用传入GCM 模式下必须提供。如果你用 PHP 7.1 以下版本openssl_decrypt 还不支持 GCM生产环境务必保证 PHP 版本不低于 7.2建议 7.4 以上。另一个坑是 APIv3 密钥不要进行 base64_decode它本身就是原始 32 字节字符串你在 .env 里配置时按原样写就行不要脑补什么编码转换。解密成功后拿到的明文数据包括 out_trade_no、transaction_id、trade_state、amount 等字段。此时你才应该更新业务订单状态。3.4 回调幂等与应答规范别让微信重复通知打爆你的订单状态微信支付回调有一个重试机制通知失败或你的应答超时微信会按一定间隔持续推送最多重试若干次。如果你的业务代码里没有做幂等控制用户支付成功后订单状态会被重复更新极端情况下会在退款逻辑里引发重复退款。我见过最典型的翻车现场业务代码里把「支付成功」写成无条件更新回调第一次进来把订单标记为已支付第二次重试时又把相同字段更新一遍看起来没毛病。但如果你在回调里顺便发了短信、推送了邮件或者调用了第三方发票接口这些动作会被重复执行。解决方案也很直接在处理前先查一次订单表如果已经是 paid 状态直接返回成功应答不再处理业务。正确应答格式是返回 HTTP 200 和 JSON 串成功如果业务处理失败返回非 200 且不要包含 success 的字符串微信会继续重试。// 回调成功应答 public function ackNotify(): void { header(Content-Type: application/json); echo json_encode([code SUCCESS, message 成功]); exit; } // 回调失败应答 public function failNotify(string $message): void { header(Content-Type: application/json); http_response_code(500); echo json_encode([code FAIL, message $message]); exit; }应答体的 code 和 message 字段微信端并不解析它只看 HTTP 状态码。但为了排查时方便建议还是按规范返回 SUCCESS/FAIL 语义这样日志里能直接看到业务成功还是失败。4. v3 接入避坑手册签名错误、金额不一致、证书过期的血泪记录4.1 用户态签名错误提示「signature 错误」的排查路径这类报错信息在微信支付里非常常见前台 JSAPI 拉起支付时返回「签名错误」或者后端日志里出现「验签失败」。我踩过三次三次的原因各不相同。第一次是把请求体做了一次 json_encode 后就拿去拼签名串但发送时因为 Guzzle 内部把中文转成了 Unicode 转义序列导致签名时用的 body 和实际发送的 body 不一致——这个好解决把 json_encode 结果存进变量签名和发送都用同一个变量。第二次是 Web 服务器对请求路径做了 rewrite实际到达 PHP 的 URL 是 /index.php/v3/pay/transactions/jsapi而签名用的是 /v3/pay/transactions/jsapi斜杠前的路径多了 index.php 导致签名验证不过。第三次比较玄学是因为 .env 文件的 APIv3 密钥尾部多了一个空格解密回调一直失败。排查这类问题我一般按三步走先在代码里把 $url 和 $body 打出来和自己拼的签名串原文做对比确认与实际发送一致再用微信官方提供的签名校验工具商户平台有在线工具或者用微信支付 SDK 里的验签脚本验证 Authorization 头里的 signature 能否通过最后确认服务器时间和真实时间误差在一分钟内签名时间戳超过五分钟会直接被拒。4.2 回调解密出来的金额是 nullJSON 解析与数据类型踩坑有段时间我碰到一个诡异现象支付成功、回调解密成功日志里打印的明文都有数据但金额字段是 null。后来检查代码发现我之前在 json_decode 时只传了一个参数没有加 JSON_BIGINT_AS_STRING。金额 total 是一个以分为单位的整数正常情况下不会超过 PHP_INT_MAX但微信在个别场景下会返回科学计数法表示的浮点数导致强转 float 后精度丢失、最终变成 null。// 正确做法第二个参数加 JSON_BIGINT_AS_STRING保证大整数不被转成浮点数 $data json_decode($plaintext, true, 512, JSON_BIGINT_AS_STRING);这个参数很关键尤其当你的订单金额大于 900 万亿不可能或者是测试环境下微信返回了一些「特殊值」时无论如何加上它不会损失任何信息还能避免一些奇怪的类型转换问题。如果你还发现回调里金额频繁对不上优先确认自己下单时是不是把「元」当成「分」传给接口了。4.3 平台证书过期或更换后的连锁反应及自动更新方案微信支付平台证书由微信统一管理你无法手动更换但微信会在证书临近过期时生成新证书并通过接口通知你。如果你长期不更新到了过期日当天所有回调验签都会失败一切支付业务停摆。最稳妥的办法是主动实现「证书自动更新」用一个定时任务cron每天请求一次 GET /v3/certificates 接口获取当前生效的平台证书列表比对序列号后把新的证书存到本地。这个接口的响应也是加密的需要用 APIv3 密钥解密证书内容。听起来绕但逻辑简单——用自己的商户证书请求这个接口得到的新平台证书列表逐个解密后覆盖本地文件。// 获取平台证书列表并更新本地缓存 public function refreshPlatformCertificates(): bool { $url /v3/certificates; $authHeader $this-buildAuthorizationHeader(GET, $url, ); $response $this-httpClient-get($url, [ headers [Authorization $authHeader, Accept application/json], ]); $result json_decode($response-getBody()-getContents(), true); foreach ($result[data] ?? [] as $certItem) { // 解密 certificate 字段拿到 PEM 内容 $decrypted $this-decryptResourceItem($certItem[encrypt_certificate]); file_put_contents($this-platformCertPath, $decrypted); } return true; }定时任务频率不用太高每天跑一次就够了。换证窗口期微信会同时返回新旧两张证书解密后应全部保存但文件只能存一张的话优先保存序列号较大的那张。这个细节容易被忽略我当时的教训是「只存了第一张返回的证书结果微信切到第二张时全线验签失败」。4.4 回调成功但前端没收到支付结果同步通知与异步回调的时序错乱第 4 个高频问题用户付完钱数据库状态已更新但前端页面还停在「待支付」。这不是代码逻辑错误是时序问题。微信的支付回调不是实时触发的从支付成功到回调到达你的服务器中间可能有几秒甚至几十秒的延迟。前端应该以 wx.requestPayment 的 success 回调为准来跳转页面而不是等后端异步回调。后端回调只用来更新状态和触发后续业务不负责前端页面的结果展示。如果你在支付成功页需要展示余额、发放积分等实时数据不要在回调里做这些而是前端付款成功后主动请求后端查询订单状态。典型做法是下单时记录 out_trade_no前端支付成功后调一个查询接口后端先查数据库数据库没有支付记录时再调微信的查询订单接口兜底。4.5 测试环境回调不进来内网穿透与 HTTPS 证书的硬性要求微信支付要求 notify_url 必须是公网可访问的 HTTPS 地址且证书链完整。很多人在本地开发时直接用内网穿透工具映射一个临时域名但往往忘了这个域名没有合法 HTTPS 证书导致微信回调永远打不进来。解决方法是给穿透域名配上正规证书——我用过的方法是本地跑 Caddy 自动申请证书然后把 Caddy 反向代理到 PHP-FPM 端口。如果你连这一步都嫌麻烦就考虑一下用微信支付提供的「模拟支付」工具在商户平台后台直接构造支付成功的通知内容发送到你的回调地址这样可以在不真实付款的情况下测试回调处理逻辑。5. 退款与平台证书轮换把 v3 的最后一公里走完5.1 退款接口从下单到退款的签名一致性退款和下单一样要用 POST /v3/refund/domestic/refunds 接口参数是 original out_trade_no 或 transaction_id、退款单号 out_refund_no、退款金额 refund、订单总金额 amount 和退款原因。这里有一个容易忽略的点退款接口的金额参数是一个嵌套结构不是平铺的 total 字段。// 退款请求示例 $refundData [ out_trade_no $orderNo, out_refund_no REFUND_ . date(YmdHis) . rand(1000, 9999), reason 用户申请退款, notify_url https://yourdomain.com/api/wechat/refund_notify, amount [ refund 100, // 退款金额单位分 total 100, // 原订单金额单位分 currency CNY, ], ]; $result $client-createRefund($refundData);注意 amount.refund 必须小于等于 amount.total。如果用户用了优惠券、实际支付金额小于订单金额退款额也必须按「实际支付金额」来否则微信会拒绝。退款回调的验签和解密逻辑与支付回调完全一致只是 resource.original_type 不同。你可以在同一个回调入口里用 original_type 区分支付通知和退款通知。5.2 一个手工校验签名的小工具函数开发调试阶段不能每次都等微信回调来验证验签代码对不对。我习惯写一个本地命令行脚本手动指定请求头和报文内容单独执行验签并打印结果。这样排查问题时不需要改业务代码直接复用现有函数。// bin/verify-sign.php 命令行调试脚本 require __DIR__ . /../vendor/autoload.php; // 从命令行读参数--bodyxxx 文件路径 --timestampxxx --noncexxx --signaturexxx $opts getopt(, [body:, timestamp:, nonce:, signature:, cert:]); $body file_get_contents($opts[body]); $message $opts[timestamp] . \n . $opts[nonce] . \n . $body . \n; $pubKey openssl_pkey_get_public(file_get_contents($opts[cert])); $result openssl_verify($message, base64_decode($opts[signature]), $pubKey, OPENSSL_ALGO_SHA256); echo $result 1 ? 验签成功\n : 验签失败请检查签名串拼法和证书匹配性\n;这个脚本我用了很久。每次微信回调出问题时把收到的原始请求体、请求头保存下来跑一遍脚本就能区分是「微信端签名使用的证书和我本地的不一致」还是「我拼签名串的格式有误」。如果你的日志系统里存了完整请求头这个脚本可以直接接入线上排障流程比一个一个比对效率高得多。5.3 平台证书自动轮换的实践建议平台证书的自动更新不要只在过期前才做建议在每次初始化支付客户端时检查一次本地证书文件的最后修改时间超过二十四小时就主动调用刷新接口。这样即使定时任务故障也不会长时间使用过期证书。我在实现时还会把证书序列号打印到日志里方便和微信返回的 Wechatpay-Serial 头做对比。以后排查验签问题时第一件事就是「看序列号是否一致」这一步能挡掉一半以上的签名类报错。5.4 给订单状态机设计一个「后悔药」退款状态与订单状态解耦最后分享一个开发习惯不要把退款状态直接写在订单表里而是单独建一张 refund 表记录每一次退款请求及其回调状态。这样做的好处是当用户发起部分退款、又申请全额退款时订单状态不受影响每一笔退款都能独立追踪。我遇到过一个生产事故因为退款状态和订单状态耦合用户退了一次款后订单状态变成「已退款」再次申请退款时系统判断订单已退款、拒绝处理。把退款独立成表后这类问题从设计层面被消除了。支付开发没有太多玄学大部分报错都能从签名串拼法、证书序列号、金额单位这三个维度找到答案。如果你照着这套流程跑通了第一笔真实支付后续接退款、企业转账、商家转账到零钱时会发现整个 v3 的接口风格高度统一先拼签名再发请求最后验签解密回调。把最开始的客户端封装做好后续每个新接口只是在加一个方法而已。希望这篇整理能帮你少走我当年走过的弯路。本文还有配套的精品资源点击获取
