一、 需求场景
(1) 业务方,要求给用户发送的短信内含有可以拉起我们的小程序指定位置的链接;
【XXX】尊敬的客户,您好,由于您XX,请微信XX小程序-微信授权登录-个人中心去XX,如已操作请忽略,[链接链接]
(2) 功能调研后,确定方案后端提供小程序 Url Scheme;前端提供H5页面渲染,拉起小程序
微信开发文档:获取scheme码 | 微信开放文档
二、后端代码
(1)先获取接口凭证,getAccessToken
获取接口调用凭据 | 微信开放文档
(2)在获取Url Scheme,generateScheme
获取scheme码 | 微信开放文档
(3)整体工具类代码如下:
package service
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"XX/lib"
"strings"
"time"
)
type AccessTokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}
//GetWxAccessToken 获取微信AccessToken
func GetWxAccessToken() (string, error) {
client := &http.Client{}
redis := lib.GetRedisInstance().Connect(1)
//凭证的有效时间(秒)
rKey := "XXX-AccessToken"
result, rErr := redis.Get(rKey).Result() //key, value, time秒
if rErr != nil {
WxAppId := "微信小程序的APPID"
WxAppSecret := "微信小程序的AppSecret"
url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%v&secret=%v", WxAppId, WxAppSecret)
fmt.Println("GetWxAccessToken url >>> " + url)
resp, err := client.Get(url)
defer func() {
if resp != nil && resp.Body != nil {
fmt.Println("-----GetWxAccessToken Close----")
resp.Body.Close()
}
}()
if err != nil {
fmt.Println("GetWxAccessToken err >>>>>" + err.Error())
return "", err
}
respB := AccessTokenResponse{}
dec := json.NewDecoder(resp.Body)
err = dec.Decode(&respB)
if err != nil {
return "", errors.New("GetWxAccessToken JSON decode failed: " + err.Error())
}
timeOutInt := respB.ExpiresIn
fmt.Println("-----GetWxAccessToken 凭证的有效时间(秒) ----", timeOutInt)
redis.Set(rKey, respB.AccessToken, time.Duration(timeOutInt)*time.Second)
return respB.AccessToken, nil
}
return result, nil
}
//GetWxScheme 获取微信小程序跳转链接
func GetWxScheme() string {
accessToken, tErr := GetWxAccessToken()
if tErr != nil {
fmt.Println("GetWxScheme accessToken err >>>>", tErr.Error())
}
url := fmt.Sprintf("https://api.weixin.qq.com/wxa/generatescheme?access_token=%s", accessToken)
comMap := make(map[string]interface{})
//指定小程序打开位置
comMap["jump_wxa"] = map[string]interface{}{
"path": "/pages/user",
}
reqBody, _ := json.Marshal(comMap)
resp, err := http.Post(url,
"application/x-www-form-urlencoded",
strings.NewReader(string(reqBody)))
if err != nil {
fmt.Println("GetWxScheme 接口返回失败 err >>>>", err.Error())
return ""
}
defer func() {
if resp != nil && resp.Body != nil {
fmt.Println("-----GetWxScheme Close----")
resp.Body.Close()
}
}()
body, err := ioutil.ReadAll(resp.Body)
resData := make(map[string]interface{})
err = json.Unmarshal(body, &resData)
if err != nil {
fmt.Println("GetWxScheme 格式化 resp.Body err >>>>", err.Error())
return ""
}
fmt.Println("GetWxScheme 接口返回值 resData >>>>", resData)
if val, ok := resData["openlink"]; ok {
openlink := val.(string)
vals := strings.Split(openlink, "=")
if len(vals) == 0 {
fmt.Println("GetWxScheme 格式化 resp.Body openlink err >>>>", openlink)
return ""
}
fmt.Println("GetWxScheme 格式化 resp.Body openlink[1] >>>>", vals[1])
return vals[1]
}
return ""
}
若获取Scheme的时候出现48001,api unauthorized rid;说明在获取Token时候使用的appId和密钥不是小程序的;
现在使用正确的AppID和密钥,重新获取Token后,拿到了openLink
注意:openLink 字符串前段固定不变,让前端写死了,后端只返回等号后的变量,所以在代码中,我做了切割
三、前端H5页面代码+短信模板
(1)H5的链接地址:https://XXX.com/h5/test.html?AH4fEW61ZKu,?号后面{wxScheme}是变量,短信模板中配置好变量的占位符即可;
(2)短信模板:
【XXX】尊敬的客户,您好,由于您XX,请微信XX小程序-微信授权登录-个人中心去XX,如已操作请忽略,https://XXX.com/h5/test.html?{wxScheme}
(3) 短信内,点击链接,拉起手机默认浏览器后,出现“跳转小程序”,ok;页面是前端同事写的测试页面;功能串通完成!
<!DOCTYPE html>
<html lang="en">
<head>
<title>test</title>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body id="body">
<a id="linkB" href="weixin://dl/business/?t=PDZAVBL1Rxc" target="_blank">跳转小程序</a>
</body>
<script>
window.onload = function () {
var url = location.search; // 获取url中?后面的字符串
if (url.indexOf("?") != -1) {
var str = url.substr(1);
console.log('str', str)
if (str) {
var pa = 'weixin://dl/business/?t=' + str
document.getElementById('linkB').setAttribute('href', pa)
}
}
}
// 获取当前链接的参数
function LGetQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
</script>
</html>