遇到了一个需求,就是要生成客户的专属二维码,要求扫码后能关注订阅号,并且将每个扫码的人和这个二维码所属客户绑定,但是系统用的是商户号,商户号不能绑定订阅号,这就存在了怎么确认是同一个客户的问题。
生成带参数的二维码文档
基础帮助类
unionid 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
微信官方提供了unionid,在同一个账号主体下,unionid是一样的,前提是需要在开放平台绑定公众号或者小程序,在开放平台认证需要300块人民币。这样在返回用户信息的时候就会返回unionid,如果没有在开放平台绑定,返回的unionid为空。
认证有效期:一年,有效期最后三个月可申请年审即可续期 审核费用:中国大陆地区:300元,非中国大陆地区:120美元
查了下文档,得出一个方案:A是订阅号,B是商户号绑定的服务号,关注A后得到openid,然后获取unionid, 存入系统,之后在B系统登录注册后根据unionid判断是否是同一个人,之后openid绑定到系统账户,当然用unionid也可以,但是之前系统用的是openid,为了减少耦合,就保持原样。
创建二维码的底层方法
public class WxPlatformApi
{
/**
*
* 创建二维码ticket https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542
* @param string access_token 调用公众号接口令牌
* @param WxPaydata inputObj 提交给生成二维码API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
*/
public static WxPayData CreateQRCodeTicket(string access_token, WxPayData inputObj, int timeOut = 6)
{
if (string.IsNullOrWhiteSpace(access_token))
{
throw new Exception("缺少统一支付接口必填参数out_trade_no!");
}
string url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + access_token;
//检测必填参数
if (!inputObj.IsSet("action_name"))
{
throw new Exception("缺少创建二维码ticket接口必填参数action_name!");
}
else if (!inputObj.IsSet("action_info"))
{
throw new Exception("缺少创建二维码ticket接口必填参数action_info!");
}
string xml = inputObj.ToJson();
// Log.Debug("WxPayApi", "UnfiedOrder request : " + xml);
string response = HttpService.PostByJson(xml, url, false, timeOut);
//Log.Debug("WxPayApi", "UnfiedOrder response : " + response);
WxPayData result = new WxPayData();
result.FromJSON(response);
return result;
}
/**
*
* 获取用户基本信息(包括UnionID机制) https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839
* @param WxPaydata inputObj 提交给生成二维码API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
*/
public static WxPayData GetUserInfo(WxPayData inputObj, int timeOut = 6)
{
//检测必填参数
if (!inputObj.IsSet("access_token"))
{
throw new Exception("缺少获取用户基本信息(包括UnionID机制)接口必填参数access_token!");
}
else if (!inputObj.IsSet("openid"))
{
throw new Exception("缺少获取用户基本信息(包括UnionID机制)接口必填参数openid!");
}
string url = "https://api.weixin.qq.com/cgi-bin/user/info?" + inputObj.ToUrl();
string response = HttpService.Get(url);
WxPayData result = new WxPayData();
result.FromJSON(response);
return result;
}
}
二维码生成类
public class WCQRCode
{
/**
* 创建二维码ticket
* 页面直接显示为 https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
* @return 创建二维码ticket结果
* @失败时抛异常WxPayException
*/
public WxPayData CreateQRCodeTicket(string access_token, string action_name, int scene_id = 0, string scene_str = null)
{
if (action_name != "QR_SCENE" && action_name != "QR_STR_SCENE" && action_name != "QR_LIMIT_SCENE" && action_name != "QR_LIMIT_STR_SCENE")
{
throw new Exception("action_name参数异常!");
}
WxPayData data = new WxPayData();
//临时二维码需要时间
if (action_name == "QR_SCENE" || action_name == "QR_STR_SCENE")
{
//该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。
data.SetValue("expire_seconds", 172800);
}
//二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
data.SetValue("action_name", action_name);
//二维码详细信息
WxPayData actionInfo = new WxPayData();
WxPayData scene = new WxPayData();
if (scene_id > 0)
{
//场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000)
scene.SetValue("scene_id", scene_id);
actionInfo.SetValue("scene", scene.m_values);
}
else if (!string.IsNullOrWhiteSpace(scene_str))
{
//场景值ID(字符串形式的ID),字符串类型,长度限制为1到64
scene.SetValue("scene_str", scene_str);
actionInfo.SetValue("scene", scene.m_values);
}
data.SetValue("action_info", actionInfo.m_values);
WxPayData result = WxPlatformApi.CreateQRCodeTicket(access_token, data);
if (!result.IsSet("ticket") || !result.IsSet("url"))
{
//Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");
throw new Exception("CreateQRCodeTicket response error!");
}
return result;
}
/**
* 获取用户基本信息(包括UnionID机制)
* @return 创建二维码ticket结果
* @失败时抛异常WxPayException
*/
public WxPayData GetUserInfo(string access_token, string openid)
{
if (string.IsNullOrWhiteSpace(access_token) || string.IsNullOrWhiteSpace(openid))
{
throw new Exception("参数异常!");
}
WxPayData data = new WxPayData();
//调用接口凭证
data.SetValue("access_token", access_token);
data.SetValue("openid", openid);
WxPayData result = WxPlatformApi.GetUserInfo(data);
if (!result.IsSet("openid") || !result.IsSet("nickname"))
{
//Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");
throw new Exception("GetUserInfo response error!");
}
return result;
}
/// <summary>
/// 获取AccessToken
/// http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token
/// </summary>
/// <param name="grant_type"></param>
/// <param name="appid"></param>
/// <param name="secrect"></param>
/// <returns>access_toke</returns>
public string GetJSAPIAccessToken(string appid, string secret)
{
WxPayData data = new WxPayData();
data.SetValue("grant_type", "client_credential");
data.SetValue("appid", appid);
data.SetValue("secret", secret);
string url = "https://api.weixin.qq.com/cgi-bin/token?" + data.ToUrl();
//请求url以获取数据
string result = HttpService.Get(url);
JObject jd = JObject.Parse(result);
return (string)jd["access_token"];
}
}
获取二维码ticket接口
/// <summary>
/// 获取二维码ticket, 显示: https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
/// </summary>
public Hashtable CreateQRCodeTicket()
{
int scene_id = GetInt("scene_id");
string scene_str = GetStr("scene_str");
bool isForever = GetBool("isForever");
try
{
Authentication au = new Authentication();
string access_token = au.GetJSAPIAccessToken();
WCQRCode qrcode = new WCQRCode();
WxPayData result = new WxPayData();
if (isForever)
{
if (scene_id > 0)
{
result = qrcode.CreateQRCodeTicket(access_token, "QR_LIMIT_SCENE", scene_id, scene_str);
}
else if (!string.IsNullOrWhiteSpace(scene_str))
{
result = qrcode.CreateQRCodeTicket(access_token, "QR_LIMIT_STR_SCENE", scene_id, scene_str);
}
}
else
{
if (scene_id > 0)
{
result = qrcode.CreateQRCodeTicket(access_token, "QR_SCENE", scene_id, scene_str);
}
else if (!string.IsNullOrWhiteSpace(scene_str))
{
result = qrcode.CreateQRCodeTicket(access_token, "QR_STR_SCENE", scene_id, scene_str);
}
}
//获取H5调起JS API参数
return result.ToHashtable();
}
catch (Exception)
{
throw new Exception("获取二维码ticket失败!");
}
}
Authentication的GetJSAPIAccessToken方法,跟WCQRCode的GetJSAPIAccessToken是基本一样的,可以根据需求整理下,这边是原先有的,就不换方法了
/// <summary>
/// 获取AccessToken
/// http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token
/// </summary>
/// <param name="grant_type"></param>
/// <param name="appid"></param>
/// <param name="secrect"></param>
/// <returns>access_toke</returns>
public string GetJSAPIAccessToken()
{
WxPayData data = new WxPayData();
data.SetValue("grant_type", "client_credential");
data.SetValue("appid", Config.AppID);
data.SetValue("secret", Config.AppSecret);
string url = "https://api.weixin.qq.com/cgi-bin/token?" + data.ToUrl();
//请求url以获取数据
string result = HttpService.Get(url);
JObject jd = JObject.Parse(result);
return (string)jd["access_token"];
}
接收推送事件逻辑,具体C#公众平台(二)—— 接收事件推送之关注回调
case "event":
string eventType = data.GetValue("Event").ToString();
switch (eventType)
{
case "subscribe":
//用户未关注的情况下KEY值: qrscene_为前缀,后面为二维码的参数值
string eventKey = data.GetValue("EventKey").ToString();
if (!string.IsNullOrEmpty(eventKey))
{
//获取二维码的分销商ID
int CusID = CInt(eventKey.Substring(eventKey.IndexOf("qrscene_") + 8));
//记录OPENID
string openID = data.GetValue("FromUserName").ToString();
//根据OPENID获取UnionID
WCQRCode WCQRCode = new WCQRCode();
string access_token = string.Empty;
if (string.IsNullOrWhiteSpace(Config.SubscriptionAppID))
{
access_token = WCQRCode.GetJSAPIAccessToken(Config.AppID, Config.AppSecret);
}
else
{
access_token = WCQRCode.GetJSAPIAccessToken(Config.SubscriptionAppID, Config.SubscriptionAppSecret);
}
WxPayData userInfo = WCQRCode.GetUserInfo(access_token, openID);
string unionid = string.Empty;
//是否使用了微信企业模式,使用了就会有unionid
if (userInfo.IsSet("unionid"))
{
unionid = userInfo.GetValue("unionid").ToString();
}
//判断是否存在,记录到数据库
WeChatSubscribeDAO dao = new WeChatSubscribeDAO();
bool isExists = dao.Exists(openID);
if (!isExists)
{
//存入数据库
}
}
break;
需要注意的是这边的access_token是指操作公众号接口的access_token,而用户授权登录的时候的access_token是指获取用户信息的access_token,两个是不一样的,别弄混了。
还有要获取用户的unionid需要用户关注这个企业下面任意的公众号才可以获取到,否则为空。坑还是不少的,慢慢爬把。。。