# 疑难解答

在微信公众平台开发的道路上，遍布着各种大大小小的坑，有的人掉坑里，几经折腾又爬出来了，然后拍拍屁股走人。然而坑还在那里，还会继续有后来人掉进去……

这，是我们不愿看到的。

所以在这里，我们将陆续将微信开发中可能遇到的各种疑难问题进行汇总，并给出对应的解决办法。一般情况下，这些问题都可以对号入座，轻松地解决。但也不排除特殊情况，这时候你遇到的问题与文中某一个症状一致，但文中所给的解决方案并不凑效，这种情况下就需要发挥你自己的智慧，去……折腾了……

我们期待这一版块为各位的开发带来便利，同时也希望各位本着开源、分享的精神对其进行补充和完善，将各种坑一一填小、填平，让微信开发变得不那么痛苦，甚至，变成一件快乐的事……

## 时区不对

使用命令 `date` 可以在服务器上查看当前时间，如果发现时区不对则需要修改时区：[Setting The Correct Timezone In CentOS And Ubuntu Servers With NTP](https://www.liberiangeek.net/2013/02/setting-the-correct-timezone-in-centos-and-ubuntu-servers-with-ntp/)

## curl: (60) SSL certificate problem: unable to get local issuer certificate

这是 SSL 证书问题所致，在使用 SDK 调用微信支付等相关的操作时可能会遇到报 “SSL certificate problem: unable to get local issuer certificate” 的错误。

微信公众平台提供的文档中建议对部分较敏感的操作接口使用 https 协议进行访问，例如微信支付和红包等接口中涉及到操作商户资金的一些操作。
wechat SDK 遵循了官方建议，所以在调用这些接口时，除了按照官方文档设置操作证书文件外，还需要保证服务器正确安装了 CA 证书。

1. 下载 CA 证书

你可以从 http://curl.haxx.se/ca/cacert.pem 下载 或者 使用[微信官方提供的证书](https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=4_3)中的 CA 证书 `rootca.pem` 也是同样的效果。

2. 在 `php.ini` 中配置 CA 证书

只需要将上面下载好的 CA 证书放置到您的服务器上某个位置，然后修改 `php.ini` 的 `curl.cainfo` 为该路径（**绝对路径！**），重启 `php-fpm` 服务即可。

```ini
curl.cainfo = /path/to/downloaded/cacert.pem
```

> 注意证书文件**路径为绝对路径**！以自己实际情况为准。

其它修改 HTTP 类源文件的方式是不允许的。

## cURL error 56: SSLRead() return error -9806

目前在 OSX 下，发现使用 HomeBrew 装的 PHP 7.0 有这个问题，解决方案是重新 brew 安装 PHP：

```shell
$ brew install homebrew/php/php70 --with-homebrew-openssl --with-homebrew-curl --without-snmp -vvv
```

验证：

```shell
$ php -i | grep 'OpenSSL support'

OpenSSL support => enabled
OpenSSL support => enabled
```

## 支付失败！当前页面的 URL 未注册

这是由于微信支付授权目录未正确配置引起的。此时开发者应该登录微信公众平台，进入**【微信支付】->【开发设置】**进行设置。

1. 公众号可添加 3 个支付授权目录，满足不同应用使用同一个公众号进行支付的业务需求。

2. 正确的**【支付授权目录】**应以 `http://` 或 `https://` 开头，并以正斜杠 `/` 结尾，授权目录所包含的域名**必须经过 ICP 备案**。

3. 支付授权目录需**细化至二级或三级目录**。

4. 所有**实际调起微信支付请求的页面都必须要所配置的支付授权目录之下**。

5. 在开发过程中，也可以使用测试授权目录进行开发测试，此时还**应该将参与测试的个人微信号添加到测试白名单中**，否则将出现对应的错误提示……

> \*页面**、**目录**、**URL **以及**域名\*\*等几个基本概念，并对自己所使用的框架的路由机制有一个大致了解。这样你才会知道自己正在配置的参数是个啥玩意儿，有什么卵用…… :smile:

## redirect_url 参数错误

这是由于程序使用了**网页授权**而公众号没有正确配置**【网页授权域名】**所致。此时你需要登录[微信公众平台](https://mp.weixin.qq.com/)，在【开发】->【接口权限】页面找到**网页授权获取用户基本信息**进行配置并保存。

1. 网页授权域名应该为通过 ICP 备案的有效域名，否则保存时无法通过安全监测。

2. 网页授权域名即程序完成授权获得授权 code 后跳转到的页面的域名，一般情况下为你的业务域名。

3. 网页授权域名配置成功后会立即生效。

4. 公众号的网页授权域名只可配置一个，请合理规划你的业务，否则你会发现……授权域名不够用哈。

## [JSAPI] config: invalid url domain

在使用 JS-SDK 进行开发时，每个页面都需要调用 wx.config() 方法配置 JSPAI 参数。如果没有正确配置 **JSAPI 安全域名**并且开启了调试模式，此时就报此错误。遇到这个问题时，开发者需要登录微信公众平台，进入【公众号设置】->【功能设置】页面，将项目所使用的域名添加至 **【JSAPI 安全域名】**列表中。

1. 一个公众号同时最多可绑定**三个**安全域名，并且这些域名必须为通过 **ICP 备案**的**一级或一级以上**的有效域名。

2. JSAPI 安全域名每个月**限修改三次**，修改任何一个都算，所以，请谨慎操作。

3. 如果需要使用 JSAPI 调起支付功能，则支付目录必须也在所配置的**安全域名之下**，并且需要将支付目录添加至**支付授权目录**。

## token 验证失败、向公众号发送消息无任何反应

相信对接公众号一般是微信开发者进行开发过程中最先进行的工作，而在这看似简单的配置操作中，也可能会掉坑里。
最常见的两种情况就如下：

1. 确认你 “**启用**” 了开发模式， token 验证通过不代表启用，保存后也不代表启用。看到红色 “**停用**” 才真正的是启用了。

2. 配置好 URL(服务器地址)以及 Token(令牌)后，点击保存时提示**token 验证失败**，出现这种情况的原因有多种，其中之一便是网络不稳定，所以**可尝试多次保存**，若始终无法通过再排查其它可能因素。

3. 配置保存成功之后，向公众号发送消息无任何反应，自己的消息处理程序也没有被调用的记录（无对应日志）。这种情况下如果你尝试**反复停用和启用服务器配置**，可能突然间惊奇地了现，问题莫名其妙的解决了。

4. 使用在线调试工具的消息接口，http://mp.weixin.qq.com/debug/， 只要返回绿色的“**请求成功**”，就代表你的代码没有问题，请**重复上面第 3 项**再测试。

5. **如果你在用什么本地开发工具，或者什么 ngrok 代理到本机这样的开发方式，那么失败就很正常了，微信服务器到你机器的网络延迟太大（还是用服务器开发吧）。**

> 器 TOKEN 验证原理（官方文档有说明）并谨记服务器验证时使用 GET 方式访问，而公众平台向你的服务器发送消息/数据则使用 POST 方式，所以服务器验证成功之后，在某些启用了 CSRF 验证的框架里，接收消息时可能还会遇到 CSRF 相关的问题，请根据自己项目实际情况进行排查。
> 另外有的朋友的 Laravel 里使用了 laravel-debugbar，这个组件的原理是在页面输出时在后面添加 HTML 来实现的，所以它会改变我们返回给微信的内容，此时要么卸载，要么禁用掉它。

## Maximum function nesting level of '100' reached, aborting!

在使用了 Xdebug 的环境下可能出现这个问题。这是由于 Xdebug 限制函数嵌套的最大层级数（默认为 100），当嵌套次数达到该值便会触发 Xdebug 跳出嵌套并报此错误。

为避免这个问题，**可以将 Xdebug 的 max_nesting_level 参数适当设置大一些**，通常设置为 200 就可以了（当然可根据自己实际情况设置为更大的值）。

如下，修改 php.ini 配置文件后，重启 Apache 或 php-fpm 服务即可。

```ini
xdebug.max_nesting_level=200
```

## 扫码支付 获取商户订单信息超时或商户返回 httpcode 非 200!

1.确定签名正确,使用 SDK 基本上不会出什么问题 2.微信调用扫码支付回调链接,使用 POST 方式,确定服务器回调方法是否取消 csrf 验证

## Request access_token fail:{"errcode":61023,"errmsg":"refresh_token is invalid hint: [zDNUIA07582974]"}！

在用户授权时会获得该 authorizer_refresh_token 刷新令牌，而当缓存或数据库存储的该 authorizer_refresh_token 刷新令牌丢失后，可能会出现该问题，微信文档中说明

1.接口调用凭据刷新令牌（在授权的公众号具备 API 权限时，才有此返回值），刷新令牌主要用于第三方平台获取和刷新已授权用户的 access_token，只会在授权时刻提供，请妥善保存。

2.一旦丢失，只能让用户重新授权，才能再次拿到新的刷新令牌(https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=)。

3.为避免该问题，请将存储该刷新令牌的缓存有效期设置为 0(永久存储)，并尽量不要去将该缓存或数据库清空。

如下：以 redis 为例。

```php
'expire'     => 0,
```

## 替换 Handler，让 easywechat 支持协程

在 Swoft、IMI 等基于 Swoole 的协程框架中使用 easywechat 时，不免会有一个问题，就是 guzzle 客户端内置的 handler 不支持协程的问题。
这里，提供一个办法主动替换容器内的 guzzle_handler.

```php
$app = Factory::miniProgram($config);
$app['guzzle_handler'] = CoroutineHandler::class;
```

鉴于有些同学找不到可用的 `CoroutineHandler`，这里提供几个，供大家使用。

- hyperf/guzzle
- yurunsoft/guzzle-swoole
- mix/guzzle-hook
