随着多端小程序研发工具的日益普及,诸如uniapp、Taro、Flutter等跨平台解决方案使得开发者能够高效地构建同时适配多个主流小程序平台(如微信、支付宝、百度、字节跳动等)的应用。尽管各平台间存在一定的差异性,但在获取用户手机号码这一核心需求上,大体遵循相似的流程和规范。
在开发微信小程序时,获取用户手机号码同样需要前端与后端协同工作。
一、前置条件
1、微信开发者资质认证
- 确保您的小程序已通过微信平台的开发者资质认证,具备获取用户手机号的权限。
2、基础库版本要求
- 使用支持获取手机号功能的微信小程序基础库版本。
3、用户授权
- 用户必须主动同意授权小程序获取其手机号码。
每个小程序平台对于用户隐私保护和数据获取都有自己的规定和接口。在实施手机号获取功能时,首先要确保对目标平台的开发文档有深入了解,明确其获取手机号的接口调用方式、用户授权流程、数据传输格式等细节。例如,微信小程序使用getPhoneNumber
接口,支付宝小程序则可能使用my.getPhoneNumber
等类似方法。
在多端小程序研发工具中,通常会提供一套抽象层来适配不同平台的特性和API。利用这些抽象层,开发者可以编写一次代码,让其在各个平台上都能正确调用相应的获取手机号接口。
例如,uniapp提供了<button open-type="getPhoneNumber">
这样的组件和事件。
一、 基础库 2.21.2 版本新版 接口
从基础库2.21.2开始,对换取手机号getphonenumber信息的方式进行了安全升级。(旧方式目前可以继续使用,但建议开发者使用新方式,以增强小程序安全性)另外,新方式不再需要提前调用wx.login进行登录,通过动态令牌code就能换取用户手机号信息,能避免使用session_key不一致的问题。
1、前端实现步骤
1.1、配置按钮组件
在需要获取手机号的页面中,添加一个<button>
组件,设置其open-type
属性为getPhoneNumber
,并绑定@getphonenumber
事件处理回调:
<template>
<view>
<!-- 其他界面元素 -->
<button open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">授权并获取手机号</button>
</view>
</template>
1.2、处理@getphonenumber
事件
getPhoneNumber (e) {
console.log(e.detail.code) // 动态令牌
console.log(e.detail.errMsg) // 回调信息(成功失败都会返回)
console.log(e.detail.errno) // 错误码(失败时返回)
}
参数 | 类型 | 说明 |
---|---|---|
code | String | 可通过动态令牌换取用户手机号。使用方法详情 phonenumber.getPhoneNumber 接口 |
getPhoneNumber 返回的 code 与 wx.login 返回的 code 作用是不一样的,不能混用。每个code有效期为5分钟,且只能消费一次。
1.3、用户授权提示
无论使用哪个小程序平台,获取用户手机号都需要用户明确授权。在设计交互时,应确保授权提示清晰、易于理解,并尊重用户体验。通常,这包括在请求授权前展示说明文字、弹窗提示用户授权的目的和影响,以及在授权失败时提供适当的错误提示和重试机制。
可以在按钮附近添加提示文案,告知用户点击后将请求获取手机号授权。可以使用默认的授权弹窗提示,也可以自定义授权弹窗提示。
2、后端实现步骤
将 bindgetphonenumber 事件回调中的动态令牌code传到开发者后台,在服务器端调用微信后台提供的 phonenumber.getPhoneNumber 接口,消费code来换取用户手机号。后端创建一个API接口(如/api/getPhoneNumber
),用于接收前端发送的code
:
POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN
云调用
- 出入参和HTTPS调用相同,调用方式可查看云调用说明文档
- 接口方法为: openapi.phonenumber.getPhoneNumber
第三方调用
- 调用方式以及出入参和HTTPS相同,仅是调用的token不同
- 该接口所属的权限集id为:18
- 服务商获得其中之一权限集授权后,可通过使用authorizer_access_token代商家进行调用
请求参数
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
access_token | string | 是 | 接口调用凭证,该参数为 URL 参数,非 Body 参数。使用access_token或者authorizer_access_token |
code | string | 是 | 手机号获取凭证 |
openid | string | 否 |
const express = require('express');
const rp = require('request-promise-native');
const bodyParser = require('body-parser');
const app = express();
const appId = '你的小程序 appId';
const appSecret = '你的小程序 appSecret';
// 使用 body-parser 中间件解析请求体
app.use(bodyParser.json());
// 获取 Access Token
async function getAccessToken() {
try {
const options = {
uri: `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`,
json: true
};
const response = await rp(options);
return response.access_token;
} catch (error) {
console.error('获取 Access Token 失败:', error);
throw error;
}
}
// 创建 API 接口
app.post('/api/getPhoneNumber', async (req, res) => {
try {
const code = req.body.code;
if (!code) {
return res.status(400).send({ error: '缺少code' });
}
const accessToken = await getAccessToken();
const options = {
method: 'POST',
uri: `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${accessToken}`,
body: {
code: code
},
json: true
};
const response = await rp(options);
if (response.errcode) {
return res.status(500).send({ error: response.errmsg });
}
const phoneNumber = response.phone_info.phoneNumber;
res.send({ phoneNumber });
} catch (error) {
console.error('获取手机号失败:', error);
res.status(500).send({ error: '内部服务器错误' });
}
});
// 启动服务器
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
3. 更新用户信息
根据解密得到的手机号和openid
,更新或关联用户信息:
async function updateUserPhoneNumber(openid, phoneNumber) {
// 实现根据openid查找用户并更新其手机号的逻辑
}
二、 基础库 2.21.2 版本之前 接口
1、前端实现步骤
1.1、配置按钮组件
在需要获取手机号的页面中,添加一个<button>
组件,设置其open-type
属性为getPhoneNumber
,并绑定@getphonenumber
事件处理回调:
<template>
<view>
<!-- 其他界面元素 -->
<button open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">授权并获取手机号</button>
</view>
</template>
1.2、处理@getphonenumber
事件
在对应的Vue单文件组件(.vue
文件)中,编写onGetPhoneNumber
方法处理回调信息:
当用户授权之前,应该使得服务端session_key 和 当前session_key保持一致,不一致会导致手机号解密失败。
wx.login() 是微信小程序提供的一个 API,用于获取用户的登录凭证(code),之后可以通过这个凭证在后端服务器上换取 session_key 和 openid。
// 该方法应该在进入页面的时候,或者getPhonenumber之前调用
login(){
// 如果有session_id,可以检查一下是否过期
wx.checkSession()
// 过期就登录
wx.login({
success: res => {
if (res.code) {
// 成功获取 code,发送到后端
this.sendCodeToBackend(res.code);
} else {
console.error('获取用户登录态失败!' + res.errMsg);
}
}
});
}
确保服务端session_key 和 当前session_key保持一致,再处理@getphonenumber
事件。
<script>
export default {
methods: {
async onGetPhoneNumber(e) {
const { detail: { encryptedData, iv } } = e;
try {
// 发送encryptedData、iv到后端
const response = await this.$http.post('/api/getPhoneNumber', {
code,//为 wx.login 获取的登录凭证 code
encryptedData,
iv,
});
if (response.status === 200 && response.data.success) {
// 后端成功解密并返回手机号,此处假设响应数据结构为 { phone: '1234567890' }
const phoneNumber = response.data.phone;
this.savePhoneNumber(phoneNumber);
} else {
console.error('后端解密失败或返回异常:', response.data.message);
}
} catch (error) {
console.error('请求后端接口失败:', error);
}
},
savePhoneNumber(phoneNumber) {
// 在此处处理获得的手机号码,如保存到本地存储或更新用户状态
},
},
};
</script>
这里假设使用了axios
或类似的HTTP库封装在this.$http
中。实际项目中请替换为你的实际请求方式。
2、后端实现步骤
尽管前端调用方式可能因平台而异,但各平台返回的手机号数据通常都是加密的,需要后端通过特定的解密算法和平台提供的密钥(如session_key
)进行解密。因此,后端处理这部分逻辑时可以保持一致性,只需关注如何根据不同的平台标识正确地获取和使用密钥。解密后的手机号数据处理(如存储、验证、关联用户账户等)也是跨平台通用的。
2.1、接收前端数据
后端创建一个API接口(如/api/getPhoneNumber
),用于接收前端发送的code
、encryptedData
和iv
:
// 以Express为例
app.post('/api/getPhoneNumber', async (req, res) => {
const { code, encryptedData, iv } = req.body;
try {
// 使用code换取session_key和openid
const { session_key, openid } = await exchangeCodeForSessionKey(code);
// 使用session_key解密手机号
const phoneNumber = await decryptPhoneNumber(session_key, encryptedData, iv);
// 根据openid关联用户并保存手机号
await updateUserPhoneNumber(openid, phoneNumber);
res.json({ success: true, message: '手机号码获取成功' });
} catch (error) {
console.error('获取手机号码过程中出现错误:', error);
res.status(500).json({ success: false, message: '服务器内部错误' });
}
});
2.2、使用code
换取session_key
和openid
调用微信官方接口sns/jscode2session
,使用code
换取session_key
和openid
:
async function exchangeCodeForSessionKey(code) {
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${APP_ID}&secret=${APP_SECRET}&js_code=${code}&grant_type=authorization_code`;
const response = await fetch(url);
const data = await response.json();
if (!data.session_key || !data.openid) {
throw new Error('Failed to exchange code for session_key and openid');
}
return {
session_key: data.session_key,
openid: data.openid,
};
}
2.3、解密手机号
使用session_key
和接收到的encryptedData
、iv
解密手机号:
const WXBizDataCrypt = require('./WXBizDataCrypt'); // 引入微信官方提供的解密类
async function decryptPhoneNumber(session_key, encryptedData, iv) {
const pc = new WXBizDataCrypt(APP_ID, session_key);
const data = {};
const errCode = pc.decryptData(encryptedData, iv, data);
if (errCode !== 0) {
throw new Error(`Failed to decrypt phoneNumber: ${errCode}`);
}
return data.phoneNumber;
}
这里假设已经引入了微信官方提供的解密工具类WXBizDataCrypt
,并实现了相应的解密逻辑。
注意事项
- 安全传输:前后端通信应使用HTTPS确保数据安全。
- 合规使用:获取用户手机号后,严格遵守隐私政策,仅在用户同意范围内使用,并不得泄露给第三方。
- 错误处理:全面考虑各种可能的错误情况,如网络异常、解密失败、用户拒绝授权等,并提供合理的用户反馈。
在使用多端小程序研发工具如uniapp开发微信小程序获取用户手机号码时,需确保已在小程序后台配置相关权限作为前置条件。前端通过调用API获取加密手机号,后端则负责解密处理。整个流程需遵守微信小程序平台的隐私政策和规定,确保用户数据的安全与合规,为用户提供透明、可控的授权体验。
在实际开发中,请始终参考最新的官方文档和示例进行编程。