使用微信小程序和公众号开发,其中最重要的就是可以使用微信账号一键授权登录,免去复杂的注册步骤,然后快速体验应用的功能,下面先来看看微信提供的文档。
为了便于用户便捷使用App、网站、移动端网页、小程序的服务,微信提供不同的技术方案,便于开发者在不同终端平台的服务中接入微信登录。 通过这个教程,开发者可以了解平台提供的针对各终端平台的微信登录能力,并可以根据实际使用场景合理选择接入方式。
以下为几类型微信登录的功能说明:
类型 | 授权域/接口 | 用户侧使用流程 | 接入流程 |
---|---|---|---|
App | 接入微信SDK,并调用snsapi_userinfo | (1)在App内选择使用微信登录 (2)拉起微信客户端,打开用户授权页,完成登录授权 | (1)注册微信开放平台(open.weixin.qq.com)帐号,并完成开发者资质认证 (2)申请【App移动应用】并审核通过后可以使用,查看开发文档 |
网站应用 | snsapi_login | (1)用户使用微信“扫一扫”,在PC端扫码(2)客户端打开授权页,完成登录授权 | (1)注册微信开放平台(open.weixin.qq.com)帐号,并完成开发者资质认证(2)申请【网站应用】并审核通过后可以使用,查看开发文档 |
微信客户端内H5 | 使用公众号的登录能力: snsapi_base snsapi_userinfo | snsapi_base:静默授权 snsapi_userinfo: (1)用户在H5内点击登录,唤起授权弹窗 (2)用户侧完成登录授权 | (1)注册微信公众号,选择“服务号”类型,并完成微信认证(2)在公众号管理后台设置回调域名(3)接入微信登录能力,查看开发文档 |
小程序 | wx.login wx.getUserInfo | wx.login:静默授权,开发者可获取openid wx.getUserInfo: (1)用户在小程序内点击组件,唤起登录窗口(2)用户侧完成登录授权 | (1)注册小程序 (2)接入微信登录功能,查看开发文档,查看登录流程设计指引 |
我的服务同时有App、官网、公众号、小程序,那我怎么打通用户数据?
对于多平台的服务,若开发者希望能识别用户身份,例如:希望用户在小程序内也能查看到在App内购买的商品订单,则可以通过平台提供的UnionID机制来实现用户身份识别。
另外需要注意的是,非微信环境的H5手机站(浏览器直接打开),也是需要使用开放平台的网站应用。
登录
小程序的登录
小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
登录流程时序
注:图片来源【小程序开发文档】
这图的意思是先要在小程序端通过wx.login获取到code,然后再通过http请求发送code到开发者服务器,然后再由开发者服务器通过http请求发送相关参数到微信接口服务器获取到openid. 开发者服务器获取到openid之后,便可以做相关操作,比如入库注册会员,然后再返回相关的数据状态给小程序端,一般是生成一个token返回给小程序端,小程序端拿到token,缓存起来,在请求相关接口的时候在http的header里带上token。
新增getUserProfile接口
需要注意的是,腾讯的规则一直在变,新增了getUserProfile接口。若开发者需要获取用户的个人信息(头像、昵称、性别与地区),可以通过wx.getUserProfile接口进行获取。详细请查看官方的文档:小程序登录、用户信息相关接口调整说明
公众号登录
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。 微信公众号网页开发的文档还是比较详细的,所以本篇就不作过多赘述,详细可以查看公众号开发文档微信网页开发:网页授权
注意:登录那里要静默登录的功能,前端只传code,后端通过code生成openId,然后去查询该用户有没有注册,有注册就走登录流程,没有注册就返回空。
Openid
用户在公众号内的身份标识,不同公众号拥有不同的openid。商户后台系统通过登录授权、支付通知、查询订单等API可获取到用户的openid。主要用途是判断同一个用户,对用户发送客服消息、模板消息等。企业号用户需要使用企业号userid转openid接口将企业成员的userid转换成openid。
支付
在了解微信支付之前,我们先要了解几个有关微信的支付相关名词解释
微信公众平台是微信公众账号申请入口和管理后台。商户可以在公众平台提交基本资料、业务资料、财务资料申请开通微信支付功能。
微信开放平台是商户APP接入微信支付开放接口的申请入口,通过此平台可申请微信APP支付。
微信商户平台是微信支付相关的商户功能集合,包括参数配置、支付数据查询与统计、在线退款、代金券或立减优惠运营等功能。
我们web前端涉及的微信支付一般是微信小程序或微信公众号,那么先要在微信小程序或者微信公众号管理后台申请开通微信支付功能。在开通微信支付之前你先要在微信商户平台上注册一个微信商户,然后在微信商户平台里绑定相关的微信小程序和微信公众号,然后再回到微信小程序或微信公众号管理后台进行确认绑定,这一过程就叫所谓开通微信支付功能。
业务流程图
重点步骤说明:
步骤3 用户下单发起支付,商户可通过JSAPI下单创建支付订单。
步骤8 商户可在微信浏览器内通过JSAPI调起支付API调起微信支付,发起支付请求。
步骤15 用户支付成功后,商户可接收到微信支付支付结果通知支付结果通知API。
步骤20 商户在没有接收到微信支付结果通知的情况下需要主动调用查询订单API查询支付结果。
小程序支付
小程序支付是指在商户既有的小程序内通过对接微信支付API,实现用户在小程序内完成交易的场景。
请求示例
wx.requestPayment
(
{
"timeStamp": "1414561699",
"nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
"package": "prepay_id=wx201410272009395522657a690389285100",
"signType": "RSA",
"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==",
"success":function(res){},
"fail":function(res){},
"complete":function(res){}
}
)
请求示例 uniapp
// #ifdef MP
uni.requestPayment({
"timeStamp": res.data.timeStamp,
"nonceStr": res.data.nonceStr,
"package": res.data.packageValue,
"signType": res.data.signType,
"paySign": res.data.paySign,
"success": function(res) {
callbackQueryOrder(orderId).then(res => {
if (res.status === 'OK') {
_this.$store.dispatch('saveCurrPayOrder', {...currPayOrder, status: 'success'})
_this.$util.Tips({
title: '支付成功',
icon: 'success'
}, {
tab: 4,
url: toPages
})
}
}).catch(err => console.log(err))
},
"fail": function(res) {
_this.$store.dispatch('saveCurrPayOrder', {...currPayOrder, status: 'fail'})
_this.$util.Tips({
title: '取消支付'
}, {
tab: 4,
url: toPages
});
},
"complete": function(res) {
uni.hideLoading();
//关闭当前页面跳转至订单状态
if (res.errMsg == 'requestPayment:cancel') return that.$util.Tips({
title: '取消支付'
}, {
tab: 4,
url: goPages
});
}
})
// #endif
公众号支付(JSAPI网页支付)
JSAPI网页支付,即日常所说的公众号支付,可在微信公众号、朋友圈、聊天会话中点击页面链接,或者用微信“扫一扫”扫描页面地址二维码在微信中打开商户HTML5页面,在页面内下单完成支付。
通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的前端JS方法调起公众号支付。
注意:
- 请确保实际支付时的请求目录与后台配置的目录一致(现在已经支持配置根目录,配置后有一定的生效时间,一般5分钟内生效),否则将无法成功唤起微信支付。配置方式详见支付授权目录。
- WeixinJSBridge内置对象在其他浏览器中无效。
- JSSDK的wx.chooseWXPay唤起支付,唤起格式内容参考公众号开发指南。
- JSSDK的JSSDK使用步骤说明链接地址,内容说明信息。
- 唤起支付返回的res对象驼峰式是JSSDK返回的格式,而err_msg是WeixinJSBridge返回的格式,如商户使用不同的方式,此处响应的字符大小写、格式需注意。
发起一个微信支付请求
wx.chooseWXPay({
timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: '', // 支付签名随机串,不长于 32 位
package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: '', // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致
paySign: '', // 支付签名
success: function (res) {
// 支付成功后的回调函数
}
});
详细可查看微信支付V3的开发文档 :pay.weixin.qq.com/wiki/doc/ap…
地址
小程序获取地址
wx.chooseAddress(Object object)
公众号获取地址
uniapp代码示例
// 获取微信地址
getWxAddress() {
let that = this;
// #ifdef MP-WEIXIN
uni.authorize({
scope: 'scope.address',
success: function(res) {
uni.chooseAddress({
success: function(res) {
console.log(res);
const item = {
province: res.provinceName,
city: res.cityName,
region: res.countyName,
detailAddress: res.detailInfo,
default: false,
name: res.userName,
phoneNumber: res.telNumber,
id: '',
}
that.editAddress(item)
},
fail: function(res) {
if (res.errMsg == 'chooseAddress:cancel') return that.$util
.Tips({
title: '取消选择'
});
},
})
},
fail: function(res) {
uni.showModal({
title: '您已拒绝导入微信地址权限',
content: '是否进入权限管理,调整授权?',
success(res) {
if (res.confirm) {
uni.openSetting({
success: function(res) {}
});
} else if (res.cancel) {
return that.$util.Tips({
title: '已取消!'
});
}
}
})
},
})
// #endif
// #ifdef H5
// return
this.$wechat.openAddress().then(res=>{
const item = {
province: res.provinceName,
city: res.cityName,
region: res.countyName,
detailAddress: res.detailInfo,
default: false,
name: res.userName,
phoneNumber: res.telNumber,
id: '',
}
that.editAddress(item)
}).catch(err=>{
console.log('失败',err);
})
// #endif
}
定位
小程序获取定位
详细可以查看我之前写的文章:关于微信小程序根据定位实现打卡功能的工作总结
公众号获取定位
uni-app 有现成的接口,可以直接获取经纬度
// 获取经纬度
export function getLocation() {
return new Promise((resolve, reject) => {
uni.getLocation({
type: 'gcj02',
success: function(res) {
uni.setStorageSync('user_latitude', res.latitude)
uni.setStorageSync('user_longitude', res.longitude)
resolve(res)
},
fail: function(err) {
uni.showToast({
title: '访问位置被拒绝'
})
reject(err)
}
})
})
}
公众号定位转换为地址
因为H5端编译至浏览器后会存在跨域问题,所以我们需要先解决跨域问题,现在根目录中找到manifest.json文件,然后选择最后一项源码视图,找到最后H5配置,将腾讯接口进行跨域代理(使用HBuilder X内部浏览器不会出现跨域问题)
开发环境解决跨域问题(通过proxy)
"h5" : {
"devServer" : {
"https" : false,
"port" : 80,
"disableHostCheck" : true,
"proxy" : { // 可代理多个
"/TencentGet" : {
"target" : "https://apis.map.qq.com/ws/geocoder/v1/", // 腾讯地图逆地址解析
"changeOrigin" : true,
"secure" : false,
"pathRewrite" : {
"^/TencentGet" : ""
}
}
}
},
}
生产环境解决跨域问题(nginx 配置proxy_pass)
location /TencentGet {
proxy_pass https://apis.map.qq.com/ws/geocoder/v1/;
}
抽成公共方法,通过经纬度获取城市信息 ި
// 通过经纬度获取地址信息
export function getLocationCity(latitude, longitude) {
// #ifdef H5
// 存在跨域需要在manifest中代理一下
let baseUrl = `/TencentGet/?coord_type=5&get_poi=0&output=json&location=${latitude},${longitude}&key=${key}`
return new Promise((resolve, reject) => {
uni.request({
url: baseUrl,
method: 'GET'
}).then(res => {
uni.setStorageSync('city_address', res[1].data.result)
uni.setStorageSync('city', res[1].data.result.address_component.city)
resolve(res[1].data.result)
}).catch(err => {
})
})
// #endif
// 除H5之外
// #ifndef H5
return new Promise((resolve, reject) => {
var that = this
var map = new TencentMap({
key: key
})
map.reverseGeocoder({
location: {
latitude: latitude,
longitude: longitude
},
success: function(res) {
// console.log('腾讯获取城市名', res)
uni.setStorageSync('city_address', res.result)
uni.setStorageSync('city', res.result.address_component.city)
resolve(res.result)
},
fail: function(err) {
reject(err)
}
})
})
// #endif
}
电话号码
小程序获取电话号码
详细可查看:获取手机号
公众号获取电话号码
公众号获取不了电话号码
二维码
小程序码可以通过微信官方提供的api生成
小程序码也可以通过开发管理中的开发设置扫普通链接二维码打开小程序
小程序页面调起摄像头扫描二维码
// #ifdef MP
wx.scanCode({
onlyFromCamera: true, // 只允许从相机扫码
scanType: ['qrCode'], // 只允许扫二维码
success(res) {
console.log('res', res)
_this.handlerScanQRCodeResult(res.result)
}
})
// #endif
公众号H5页面调起摄像头扫描二维码
// #ifdef H5
_this.$wechat.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ["qrCode", "barCode"] // 可以指定扫二维码还是一维码,默认二者都有
}).then(res => {
console.log('res', res)
const result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
_this.handlerScanQRCodeResult(result)
}).catch(err => console.log('扫码出错', err));
// #endif
扫普通链接二维码打开小程序
为了方便小程序开发者更便捷地推广小程序,兼容线下已有的二维码,微信公众平台开放扫描普通链接二维码跳转小程序能力。
二维码跳转规则
注意:从2017年5月开始,微信客户端支持二维码规则根据“子路径匹配”。如原有二维码链接为 http://www.qq.com/a/123456
,其中12345为业务参数,则可配置规则 http://www.qq.com/a/
实现扫码打开小程序。
微信客户端扫码将按以下匹配规则控制跳转:
- 二维码链接的协议、域名与已配置的二维码规则一致。
- 二维码链接属于后台配置的二维码规则的子路径。(如需支持子路径匹配,请确认后台配置的二维码规则以
/
结尾) - 如果二维码规则包含参数,链接
?
后为参数部分,参数要求前缀匹配。
常见匹配错误类型:
后台已配置的二维码规则 | 线下二维码完整链接 | 错误原因 |
---|---|---|
www.qq.com/a/b | www.qq.com/a/b | 协议不一致 |
www.qq.com/a/b | www.weixin.qq.com/a/b | 域名不一 |
www.qq.com/a/b?id=123 | www.qq.com/a/b?id=132 | 参数不满足前缀匹配 |
www.qq.com/a/b | www.qq.com/a/bc | 不属于子路径 |
www.qq.com/a/b | www.qq.com/a/b/123 | 规则没有以/ 结尾,不支持子路径匹配 |
www.qq.com/a/(已发布) www.qq.com/a/b(未发布) | www.qq.com/a/b | 命中未发布规则不会跳转小程序 |
二维码内容获取
在小程序后台配置二维码跳转小程序规则之后即可使用微信(6.5.6及其以上客户端版本)扫码打开小程序。
二维码链接内容会以参数 q
的形式带给页面,在onLoad事件中提取 q
参数并自行 decodeURIComponent
一次(对于小游戏可使用 wx.getEnterOptionsSync 接口获取),即可获取原二维码的完整内容。同时会附加一个参数 scancode_time(UNIX 时间戳,单位秒),表示用户扫码的时间。
Page({
onLoad(query) {
const q = decodeURIComponent(query.q) // 获取到二维码原始链接内容
const scancode_time = parseInt(query.scancode_time) // 获取用户扫码时间 UNIX 时间戳
}
})
直接使用微信扫一扫处理小程序扫码结果
/**
* 获取url参数 支持中文
* @param string param 扫码携带参数
*
*/
getUrlParams: function(params) {
let url = params.substring(params.indexOf("?")); //获取url中"?"符后的字串
let theRequest = new Object();
if (url.indexOf("?") != -1) {
let str = url.substr(1);
let strs = str.split("&");
for (let i = 0; i < strs.length; i++) {
theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
}
}
return theRequest;
}
处理扫码结果
/**
* 处理扫码结果
*/
handlerScanQRCodeResult(result) {
if (result.includes(appConfig.PUBLISHURL) || result.includes(appConfig.PUBLISHURL_MINI)) {
const params = this.$util.getUrlParams(result)
if (params.pickCode) {
this.$util.Tips('/pages/users/order/verificationGoodsList?pickCode=' + params.pickCode)
} else {
this.$util.Tips({
title: "不是核销码"
})
}
} else {
console.log('不是核销码')
this.$util.Tips({
title: "不是核销码"
})
}
},
注意事项
注意小程序码的配置要发布了才能进行测试,而且测试的参数要写死在配置里
生成的二维码可以通过开发工具进行测试