新版本获取手机:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
从基础库 2.21.2 开始,对获取手机号的接口进行了安全升级,以下是新版本接口使用指南。(旧版本接口目前可以继续使用,但建议开发者使用新版本接口,以增强小程序安全性)
因为需要用户主动触发才能发起获取手机号接口,所以该功能不由 API 来调用,需用 button 组件的点击来触发。另外,新版本接口不再需要提前调用wx.login
进行登录。
注意:
- 目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)。需谨慎使用,若用户举报较多或被发现在不必要场景下使用,微信有权永久回收该小程序的该接口权限;
- 在使用该接口时,用户可使用微信绑定手机号进行授权,也添加非微信绑定手机号进行授权。若开发者仅通过手机号作为业务关联凭证,在重点场景可适当增加短信验证逻辑。
使用方法
step1:获取code
需要将 button 组件 open-type
的值设置为 getPhoneNumber
,当用户点击并同意之后,可以通过 bindgetphonenumber
事件回调获取到动态令牌code
,然后把code
传到开发者后台,并在开发者后台调用微信后台提供的 phonenumber.getPhoneNumber 接口,消费code
来换取用户手机号。每个code
有效期为5分钟,且只能消费一次。
注:getPhoneNumber
返回的 code
与 wx.login
返回的 code
作用是不一样的,不能混用。
代码示例
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
Page({
getPhoneNumber (e) {
console.log(e.detail.code)
}
})
返回参数说明
参数 | 类型 | 说明 | 最低版本 |
code | String | 动态令牌。可通过动态令牌换取用户手机号。使用方法详情 phonenumber.getPhoneNumber 接口 |
step2: 在服务器端 根据code换手机号:
https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
接口说明
接口英文名
getPhoneNumber
功能描述
该接口用于将 code 换取用户手机号。 说明,每个 code 只能使用一次,code的有效期为5min。
调用方式
HTTPS 调用
POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN
第三方调用
- 调用方式以及出入参和 HTTPS 相同,仅是调用的 token 不同
- 该接口所属的权限集 id 为:18
- 服务商获得其中之一权限集授权后,可通过使用authorizer_access_token代商家进行调用
请求参数
属性 | 类型 | 必填 | 说明 |
access_token | string | 是 | 接口调用凭证,该参数为 URL 参数,非 Body 参数。使用access_token或者authorizer_access_token |
code | string | 是 | 手机号获取凭证 |
返回参数
| 属性 | 类型 | 说明 |
| errcode | number | 错误码 |
| errmsg | string | 错误信息 |
| phone_info | object | 用户手机号信息 |
|
| 属性 | 类型 | 说明 |
| phoneNumber | string | 用户绑定的手机号(国外手机号会有区号) |
| purePhoneNumber | string | 没有区号的手机号 |
| countryCode | string | 区号 |
| watermark | object | 数据水印 |
| 属性类型说明 timestampnumber用户获取手机号操作的时间戳 appidstring小程序appid |
调用示例
示例说明: HTTPS请求示例
请求数据示例
{
"code": "e31968a7f94cc5ee25fafc2aef2773f0bb8c3937b22520eb8ee345274d00c144"
}
返回数据示例
{
"errcode":0,
"errmsg":"ok",
"phone_info": {
"phoneNumber":"xxxxxx",
"purePhoneNumber": "xxxxxx",
"countryCode": 86,
"watermark": {
"timestamp": 1637744274,
"appid": "xxxx"
}
}
}
错误码
错误码 | 错误码取值 | 解决方案 |
-1 | system error | 系统繁忙,此时请开发者稍候再试 |
40029 | code 无效 | js_code无效 |
【小程序】老版获取手机号:【基础库 2.21.2之前】
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/deprecatedGetPhoneNumber.html
以下是旧版本接口使用指南,建议开发者使用新版本接口,以增强小程序安全性。详情新版接口使用指南。
获取微信用户绑定的手机号,需先调用wx.login接口。
因为需要用户主动触发才能发起获取手机号接口,所以该功能不由 API 来调用,需用 button 组件的点击来触发。
注意:目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)。需谨慎使用,若用户举报较多或被发现在不必要场景下使用,微信有权永久回收该小程序的该接口权限。
使用方法
step1: 获取加密后的手机号
需要将 button 组件 open-type
的值设置为 getPhoneNumber
,当用户点击并同意之后,可以通过 bindgetphonenumber
事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key
以及 app_id
进行解密获取手机号。
注意事项
在回调中调用 wx.login 登录,可能会刷新登录态。
此时服务器使用 code 换取的 sessionKey 不是加密时使用的 sessionKey,导致解密失败。建议开发者提前进行 login
;或者在回调中先使用 checkSession
进行登录态检查,避免 login
刷新登录态。
代码示例
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
Page({
getPhoneNumber (e) {
console.log(e.detail.errMsg)
console.log(e.detail.iv)
console.log(e.detail.encryptedData)
}
})
返回参数说明
参数 | 类型 | 说明 | 最低版本 |
encryptedData | String | 包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法 | |
iv | String | 加密算法的初始向量,详细见加密数据解密算法 | |
cloudID | string | 敏感数据对应的云 ID,开通云开发的小程序才会返回,可通过云调用直接获取开放数据,详细见云调用直接获取开放数据 | 基础库 2.8.0 |
step2:将前端获取的数据解密
解密算法如下:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html#%E5%8A%A0%E5%AF%86%E6%95%B0%E6%8D%AE%E8%A7%A3%E5%AF%86%E7%AE%97%E6%B3%95
1、对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
2、对称解密的目标密文为 Base64_Decode(encryptedData)。
3、对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
4、对称解密算法初始向量 为Base64_Decode(iv),其中 iv 由数据接口返回。
public static String decrypt(String sessionKey, String encryptedData, String ivStr) {
try {
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(Base64.decodeBase64(ivStr)));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(2, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params);
return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("AES解密失败!", e);
}
}
public static String decryptAnotherWay(String sessionKey, String encryptedData, String ivStr) {
byte[] keyBytes = Base64.decodeBase64(sessionKey.getBytes(UTF_8));
int base = 16;
if (keyBytes.length % base != 0) {
int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte)0);
System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
keyBytes = temp;
}
Security.addProvider(new BouncyCastleProvider());
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(2, key, new IvParameterSpec(Base64.decodeBase64(ivStr.getBytes(StandardCharsets.UTF_8))));
return new String(cipher.doFinal(Base64.decoedeBase64(encryptedData.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("AES解密失败!", e);
}
}
获取得到安全的开放数据。
json 结构如下:
{
"phoneNumber": "13580006666",
"purePhoneNumber": "13580006666",
"countryCode": "86",
"watermark":
{
"appid":"APPID",
"timestamp": TIMESTAMP
}
}
参数 | 类型 | 说明 |
phoneNumber | String | 用户绑定的手机号(国外手机号会有区号) |
purePhoneNumber | String | 没有区号的手机号 |
countryCode | String | 区号 |