diff --git a/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/GroupMessage/GroupMessageApi.cs b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/GroupMessage/GroupMessageApi.cs
new file mode 100644
index 0000000000..ee94446a97
--- /dev/null
+++ b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/GroupMessage/GroupMessageApi.cs
@@ -0,0 +1,941 @@
+/*----------------------------------------------------------------
+ Copyright (C) 2016 Senparc
+
+ 文件名:GroupMessageAPI.cs
+ 文件功能描述:高级群发接口
+
+
+ 创建标识:Senparc - 20150211
+
+ 修改标识:Senparc - 20150303
+ 修改描述:整理接口
+
+ 修改标识:Senparc - 20150312
+ 修改描述:开放代理请求超时时间
+
+ 修改标识:Senparc - 20160718
+ 修改描述:增加其接口的异步方法
+----------------------------------------------------------------*/
+
+/*
+ API地址:http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
+*/
+
+using System;
+using System.Threading.Tasks;
+using Senparc.Weixin.Entities;
+using Senparc.Weixin.MP.AdvancedAPIs.GroupMessage;
+using Senparc.Weixin.MP.CommonAPIs;
+using Senparc.Weixin.HttpUtility;
+
+namespace Senparc.Weixin.MP.AdvancedAPIs
+{
+ ///
+ /// 高级群发接口
+ ///
+ public static class GroupMessageApi
+ {
+
+ #region 同步请求
+
+
+
+ ///
+ /// 根据分组进行群发【订阅号与服务号认证后均可用】
+ ///
+ /// 请注意:
+ /// 1、该接口暂时仅提供给已微信认证的服务号
+ /// 2、虽然开发者使用高级群发接口的每日调用限制为100次,但是用户每月只能接收4条,请小心测试
+ /// 3、无论在公众平台网站上,还是使用接口群发,用户每月只能接收4条群发消息,多于4条的群发将对该用户发送失败。
+ /// 4、群发视频时需要先调用GetVideoMediaIdResult接口获取专用的MediaId然后进行群发
+ ///
+ ///
+ ///
+ /// 群发到的分组的group_id,参加用户管理中用户分组接口,若is_to_all值为true,可不填写group_id
+ /// 群发媒体文件时传入mediaId,群发文本消息时传入content,群发卡券时传入cardId
+ ///
+ /// 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户,选择false可根据group_id发送给指定群组的用户
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static SendResult SendGroupMessageByGroupId(string accessTokenOrAppId, string groupId, string value, GroupMessageType type, bool isToAll = false, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={0}";
+
+ BaseGroupMessageDataByGroupId baseData = null;
+ switch (type)
+ {
+ case GroupMessageType.image:
+ baseData = new GroupMessageByGroupId_ImageData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ image = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "image"
+ };
+ break;
+ case GroupMessageType.voice:
+ baseData = new GroupMessageByGroupId_VoiceData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ voice = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "voice"
+ };
+ break;
+ case GroupMessageType.mpnews:
+ baseData = new GroupMessageByGroupId_MpNewsData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ mpnews = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpnews"
+ };
+ break;
+ case GroupMessageType.video:
+ baseData = new GroupMessageByGroupId_MpVideoData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ mpvideo = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpvideo"
+ };
+ break;
+ case GroupMessageType.wxcard:
+ baseData = new GroupMessageByGroupId_WxCardData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ wxcard = new GroupMessageByGroupId_WxCard()
+ {
+ card_id = value
+ },
+ msgtype = "wxcard"
+ };
+ break;
+ case GroupMessageType.text:
+ baseData = new GroupMessageByGroupId_TextData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ text = new GroupMessageByGroupId_Content()
+ {
+ content = value
+ },
+ msgtype = "text"
+ };
+ break;
+ default:
+ throw new Exception("参数错误。");
+ break;
+ }
+
+ return CommonJsonSend.Send(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 根据OpenId进行群发
+ ///
+ ///
+ /// 群发媒体文件时传入mediaId,群发文本消息时传入content,群发卡券时传入cardId
+ ///
+ /// openId字符串数组
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static SendResult SendGroupMessageByOpenId(string accessTokenOrAppId, GroupMessageType type, string value, int timeOut = Config.TIME_OUT, params string[] openIds)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token={0}";
+
+ BaseGroupMessageDataByOpenId baseData = null;
+ switch (type)
+ {
+ case GroupMessageType.image:
+ baseData = new GroupMessageByOpenId_ImageData()
+ {
+ touser = openIds,
+ image = new GroupMessageByOpenId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "image"
+ };
+ break;
+ case GroupMessageType.voice:
+ baseData = new GroupMessageByOpenId_VoiceData()
+ {
+ touser = openIds,
+ voice = new GroupMessageByOpenId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "voice"
+ };
+ break;
+ case GroupMessageType.mpnews:
+ baseData = new GroupMessageByOpenId_MpNewsData()
+ {
+ touser = openIds,
+ mpnews = new GroupMessageByOpenId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpnews"
+ };
+ break;
+ case GroupMessageType.wxcard:
+ baseData = new GroupMessageByOpenId_WxCardData()
+ {
+ touser = openIds,
+ wxcard = new GroupMessageByOpenId_WxCard()
+ {
+ card_id = value
+ },
+ msgtype = "wxcard"
+ };
+ break;
+ case GroupMessageType.video:
+ throw new Exception("发送视频信息请使用SendVideoGroupMessageByOpenId方法。");
+ break;
+ case GroupMessageType.text:
+ baseData = new GroupMessageByOpenId_TextData()
+ {
+ touser = openIds,
+ text = new GroupMessageByOpenId_Content()
+ {
+ content = value
+ },
+ msgtype = "text"
+ };
+ break;
+ default:
+ throw new Exception("参数错误。");
+ break;
+ }
+ return CommonJsonSend.Send(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 根据OpenID列表群发视频消息【订阅号不可用,服务号认证后可用】
+ /// 注意:群发视频时需要先调用GetVideoMediaIdResult接口获取专用的MediaId然后进行群发
+ ///
+ ///
+ ///
+ ///
+ /// openId字符串数组
+ ///
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static SendResult SendVideoGroupMessageByOpenId(string accessTokenOrAppId, string title, string description, string mediaId, int timeOut = Config.TIME_OUT, params string[] openIds)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token={0}";
+
+ BaseGroupMessageDataByOpenId baseData = new GroupMessageByOpenId_MpVideoData()
+ {
+ touser = openIds,
+ video = new GroupMessageByOpenId_Video()
+ {
+ title = title,
+ description = description,
+ media_id = mediaId
+ },
+ msgtype = "mpvideo"
+ };
+
+ return CommonJsonSend.Send(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 删除群发消息
+ ///
+ ///
+ /// 发送出去的消息ID
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static WxJsonResult DeleteSendMessage(string accessTokenOrAppId, string msgId, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ //官方API地址为https://api.weixin.qq.com//cgi-bin/message/mass/delete?access_token={0},应该是多了一个/
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token={0}";
+
+ var data = new
+ {
+ msg_id = msgId
+ };
+ return CommonJsonSend.Send(accessToken, urlFormat, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 预览接口【订阅号与服务号认证后均可用】
+ /// 注意:openId与wxName两者任选其一,同时传入以wxName优先
+ ///
+ ///
+ /// 群发媒体消息时为media_id,群发文本信息为content
+ ///
+ /// 接收消息用户对应该公众号的openid
+ /// 接收消息用户的微信号
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static SendResult SendGroupMessagePreview(string accessTokenOrAppId, GroupMessageType type, string value, string openId, string wxName = null, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token={0}";
+
+ BaseGroupMessageDataPreview baseData = null;
+ switch (type)
+ {
+ case GroupMessageType.image:
+ baseData = new GroupMessagePreview_ImageData()
+ {
+ touser = openId,
+ towxname = wxName,
+ image = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "image"
+ };
+ break;
+ case GroupMessageType.voice:
+ baseData = new GroupMessagePreview_VoiceData()
+ {
+ touser = openId,
+ towxname = wxName,
+ voice = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "voice"
+ };
+ break;
+ case GroupMessageType.mpnews:
+ baseData = new GroupMessagePreview_MpNewsData()
+ {
+ touser = openId,
+ towxname = wxName,
+ mpnews = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpnews"
+ };
+ break;
+ case GroupMessageType.video:
+ baseData = new GroupMessagePreview_MpVideoData()
+ {
+ touser = openId,
+ towxname = wxName,
+ mpvideo = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpvideo"
+ };
+ break;
+ case GroupMessageType.text:
+ baseData = new GroupMessagePreview_TextData()
+ {
+ touser = openId,
+ towxname = wxName,
+ text = new GroupMessagePreview_Content()
+ {
+ content = value
+ },
+ msgtype = "text"
+ };
+ break;
+ case GroupMessageType.wxcard:
+ throw new Exception("发送卡券息请使用WxCardGroupMessagePreview方法。");
+ break;
+ default:
+ throw new Exception("参数错误。");
+ break;
+ }
+ return CommonJsonSend.Send(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 预览卡券接口
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static SendResult WxCardGroupMessagePreview(string accessTokenOrAppId, string cardId, string code,
+ string openId, string wxName, string timestamp, string signature, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token={0}";
+
+ BaseGroupMessageDataPreview baseData = new GroupMessagePreview_WxCardData()
+ {
+ touser = openId,
+ towxname = wxName,
+ wxcard = new GroupMessagePreview_WxCard()
+ {
+ card_id = cardId,
+ card_ext = string.Format("\"code\":\"{0}\",\"openid\":\"{1}\",\"timestamp\":\"{2}\",\"signature\":\"{3}\"", code, openId, timestamp, signature)
+ },
+ msgtype = "wxcard"
+ };
+
+ return CommonJsonSend.Send(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 查询群发消息发送状态【订阅号与服务号认证后均可用】
+ ///
+ ///
+ /// 群发消息后返回的消息id
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static GetSendResult GetGroupMessageResult(string accessTokenOrAppId, string msgId, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token={0}";
+
+ var data = new
+ {
+ msg_id = msgId
+ };
+
+ return CommonJsonSend.Send(accessToken, urlFormat, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 获取视频群发用的MediaId
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static VideoMediaIdResult GetVideoMediaIdResult(string accessTokenOrAppId, string mediaId, string title,
+ string description, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ string url = string.Format("https://file.api.weixin.qq.com/cgi-bin/media/uploadvideo?access_token={0}", accessToken.AsUrlData());
+
+ var data = new
+ {
+ media_id = mediaId,
+ title = title,
+ description = description
+ };
+
+ return CommonJsonSend.Send(null, url, data, CommonJsonSendType.POST, timeOut, true);
+
+ }, accessTokenOrAppId);
+ }
+ #endregion
+
+ #region 异步请求
+
+ ///
+ /// 【异步方法】根据分组进行群发【订阅号与服务号认证后均可用】
+ ///
+ /// 请注意:
+ /// 1、该接口暂时仅提供给已微信认证的服务号
+ /// 2、虽然开发者使用高级群发接口的每日调用限制为100次,但是用户每月只能接收4条,请小心测试
+ /// 3、无论在公众平台网站上,还是使用接口群发,用户每月只能接收4条群发消息,多于4条的群发将对该用户发送失败。
+ /// 4、群发视频时需要先调用GetVideoMediaIdResult接口获取专用的MediaId然后进行群发
+ ///
+ ///
+ ///
+ /// 群发到的分组的group_id,参加用户管理中用户分组接口,若is_to_all值为true,可不填写group_id
+ /// 群发媒体文件时传入mediaId,群发文本消息时传入content,群发卡券时传入cardId
+ ///
+ /// 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户,选择false可根据group_id发送给指定群组的用户
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task SendGroupMessageByGroupIdAsync(string accessTokenOrAppId, string groupId, string value, GroupMessageType type, bool isToAll = false, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={0}";
+
+ BaseGroupMessageDataByGroupId baseData = null;
+ switch (type)
+ {
+ case GroupMessageType.image:
+ baseData = new GroupMessageByGroupId_ImageData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ image = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "image"
+ };
+ break;
+ case GroupMessageType.voice:
+ baseData = new GroupMessageByGroupId_VoiceData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ voice = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "voice"
+ };
+ break;
+ case GroupMessageType.mpnews:
+ baseData = new GroupMessageByGroupId_MpNewsData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ mpnews = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpnews"
+ };
+ break;
+ case GroupMessageType.video:
+ baseData = new GroupMessageByGroupId_MpVideoData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ mpvideo = new GroupMessageByGroupId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpvideo"
+ };
+ break;
+ case GroupMessageType.wxcard:
+ baseData = new GroupMessageByGroupId_WxCardData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ wxcard = new GroupMessageByGroupId_WxCard()
+ {
+ card_id = value
+ },
+ msgtype = "wxcard"
+ };
+ break;
+ case GroupMessageType.text:
+ baseData = new GroupMessageByGroupId_TextData()
+ {
+ filter = new GroupMessageByGroupId_GroupId()
+ {
+ group_id = groupId,
+ is_to_all = isToAll
+ },
+ text = new GroupMessageByGroupId_Content()
+ {
+ content = value
+ },
+ msgtype = "text"
+ };
+ break;
+ default:
+ throw new Exception("参数错误。");
+ break;
+ }
+
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】根据OpenId进行群发
+ ///
+ ///
+ /// 群发媒体文件时传入mediaId,群发文本消息时传入content,群发卡券时传入cardId
+ ///
+ /// openId字符串数组
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task SendGroupMessageByOpenIdAsync(string accessTokenOrAppId, GroupMessageType type, string value, int timeOut = Config.TIME_OUT, params string[] openIds)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token={0}";
+
+ BaseGroupMessageDataByOpenId baseData = null;
+ switch (type)
+ {
+ case GroupMessageType.image:
+ baseData = new GroupMessageByOpenId_ImageData()
+ {
+ touser = openIds,
+ image = new GroupMessageByOpenId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "image"
+ };
+ break;
+ case GroupMessageType.voice:
+ baseData = new GroupMessageByOpenId_VoiceData()
+ {
+ touser = openIds,
+ voice = new GroupMessageByOpenId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "voice"
+ };
+ break;
+ case GroupMessageType.mpnews:
+ baseData = new GroupMessageByOpenId_MpNewsData()
+ {
+ touser = openIds,
+ mpnews = new GroupMessageByOpenId_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpnews"
+ };
+ break;
+ case GroupMessageType.wxcard:
+ baseData = new GroupMessageByOpenId_WxCardData()
+ {
+ touser = openIds,
+ wxcard = new GroupMessageByOpenId_WxCard()
+ {
+ card_id = value
+ },
+ msgtype = "wxcard"
+ };
+ break;
+ case GroupMessageType.video:
+ throw new Exception("发送视频信息请使用SendVideoGroupMessageByOpenId方法。");
+ break;
+ case GroupMessageType.text:
+ baseData = new GroupMessageByOpenId_TextData()
+ {
+ touser = openIds,
+ text = new GroupMessageByOpenId_Content()
+ {
+ content = value
+ },
+ msgtype = "text"
+ };
+ break;
+ default:
+ throw new Exception("参数错误。");
+ break;
+ }
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】根据OpenID列表群发视频消息【订阅号不可用,服务号认证后可用】
+ /// 注意:群发视频时需要先调用GetVideoMediaIdResult接口获取专用的MediaId然后进行群发
+ ///
+ ///
+ ///
+ ///
+ /// openId字符串数组
+ ///
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task SendVideoGroupMessageByOpenIdAsync(string accessTokenOrAppId, string title, string description, string mediaId, int timeOut = Config.TIME_OUT, params string[] openIds)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token={0}";
+
+ BaseGroupMessageDataByOpenId baseData = new GroupMessageByOpenId_MpVideoData()
+ {
+ touser = openIds,
+ video = new GroupMessageByOpenId_Video()
+ {
+ title = title,
+ description = description,
+ media_id = mediaId
+ },
+ msgtype = "mpvideo"
+ };
+
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】删除群发消息
+ ///
+ ///
+ /// 发送出去的消息ID
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task DeleteSendMessageAsync(string accessTokenOrAppId, string msgId, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ //官方API地址为https://api.weixin.qq.com//cgi-bin/message/mass/delete?access_token={0},应该是多了一个/
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/delete?access_token={0}";
+
+ var data = new
+ {
+ msg_id = msgId
+ };
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】预览接口【订阅号与服务号认证后均可用】
+ /// 注意:openId与wxName两者任选其一,同时传入以wxName优先
+ ///
+ ///
+ /// 群发媒体消息时为media_id,群发文本信息为content
+ ///
+ /// 接收消息用户对应该公众号的openid
+ /// 接收消息用户的微信号
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task SendGroupMessagePreviewAsync(string accessTokenOrAppId, GroupMessageType type, string value, string openId, string wxName = null, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token={0}";
+
+ BaseGroupMessageDataPreview baseData = null;
+ switch (type)
+ {
+ case GroupMessageType.image:
+ baseData = new GroupMessagePreview_ImageData()
+ {
+ touser = openId,
+ towxname = wxName,
+ image = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "image"
+ };
+ break;
+ case GroupMessageType.voice:
+ baseData = new GroupMessagePreview_VoiceData()
+ {
+ touser = openId,
+ towxname = wxName,
+ voice = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "voice"
+ };
+ break;
+ case GroupMessageType.mpnews:
+ baseData = new GroupMessagePreview_MpNewsData()
+ {
+ touser = openId,
+ towxname = wxName,
+ mpnews = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpnews"
+ };
+ break;
+ case GroupMessageType.video:
+ baseData = new GroupMessagePreview_MpVideoData()
+ {
+ touser = openId,
+ towxname = wxName,
+ mpvideo = new GroupMessagePreview_MediaId()
+ {
+ media_id = value
+ },
+ msgtype = "mpvideo"
+ };
+ break;
+ case GroupMessageType.text:
+ baseData = new GroupMessagePreview_TextData()
+ {
+ touser = openId,
+ towxname = wxName,
+ text = new GroupMessagePreview_Content()
+ {
+ content = value
+ },
+ msgtype = "text"
+ };
+ break;
+ case GroupMessageType.wxcard:
+ throw new Exception("发送卡券息请使用WxCardGroupMessagePreview方法。");
+ break;
+ default:
+ throw new Exception("参数错误。");
+ break;
+ }
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】预览卡券接口
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task WxCardGroupMessagePreviewAsync(string accessTokenOrAppId, string cardId, string code,
+ string openId, string wxName, string timestamp, string signature, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token={0}";
+
+ BaseGroupMessageDataPreview baseData = new GroupMessagePreview_WxCardData()
+ {
+ touser = openId,
+ towxname = wxName,
+ wxcard = new GroupMessagePreview_WxCard()
+ {
+ card_id = cardId,
+ card_ext = string.Format("\"code\":\"{0}\",\"openid\":\"{1}\",\"timestamp\":\"{2}\",\"signature\":\"{3}\"", code, openId, timestamp, signature)
+ },
+ msgtype = "wxcard"
+ };
+
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, baseData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】查询群发消息发送状态【订阅号与服务号认证后均可用】
+ ///
+ ///
+ /// 群发消息后返回的消息id
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task GetGroupMessageResultAsync(string accessTokenOrAppId, string msgId, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/mass/get?access_token={0}";
+
+ var data = new
+ {
+ msg_id = msgId
+ };
+
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】获取视频群发用的MediaId
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task GetVideoMediaIdResultAsync(string accessTokenOrAppId, string mediaId, string title,
+ string description, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ string url = string.Format("https://file.api.weixin.qq.com/cgi-bin/media/uploadvideo?access_token={0}", accessToken.AsUrlData());
+
+ var data = new
+ {
+ media_id = mediaId,
+ title = title,
+ description = description
+ };
+
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(null, url, data, CommonJsonSendType.POST, timeOut, true);
+
+ }, accessTokenOrAppId);
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/TemplateMessage/TemplateApi.cs b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/TemplateMessage/TemplateApi.cs
new file mode 100644
index 0000000000..08aaf3a284
--- /dev/null
+++ b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/TemplateMessage/TemplateApi.cs
@@ -0,0 +1,304 @@
+/*----------------------------------------------------------------
+ Copyright (C) 2016 Senparc
+
+ 文件名:TemplateAPI.cs
+ 文件功能描述:模板消息接口
+
+
+ 创建标识:Senparc - 20150211
+
+ 修改标识:Senparc - 20150303
+ 修改描述:整理接口
+
+ 修改标识:Senparc - 20150312
+ 修改描述:开放代理请求超时时间
+
+ 修改标识:Senparc - 20160719
+ 修改描述:增加其接口的异步方法
+
+ 修改标识:Senparc - 20160808
+ 修改描述:去掉SendTemplateMessage,SendTemplateMessageAsync中的topcolor参数
+----------------------------------------------------------------*/
+
+/*
+ API:http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html
+ */
+
+using System;
+using System.Threading.Tasks;
+using Senparc.Weixin.Entities;
+using Senparc.Weixin.MP.AdvancedAPIs.TemplateMessage;
+using Senparc.Weixin.MP.CommonAPIs;
+
+namespace Senparc.Weixin.MP.AdvancedAPIs
+{
+ ///
+ /// 模板消息接口
+ ///
+ public static class TemplateApi
+ {
+ #region 同步请求
+
+ ///
+ /// 模板消息接口
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static SendTemplateMessageResult SendTemplateMessage(string accessTokenOrAppId, string openId, string templateId, string url, object data, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";
+ var msgData = new TempleteModel()
+ {
+ touser = openId,
+ template_id = templateId,
+ // topcolor = topcolor,
+ url = url,
+ data = data
+ };
+ return CommonJsonSend.Send(accessToken, urlFormat, msgData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 设置所属行业
+ ///
+ ///
+ /// 公众号模板消息所属行业编号
+ /// 公众号模板消息所属行业编号
+ ///
+ ///
+
+ public static WxJsonResult SetIndustry(string accessTokenOrAppId, IndustryCode industry_id1, IndustryCode industry_id2, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token={0}";
+ var msgData = new
+ {
+ industry_id1 = ((int)industry_id1).ToString(),
+ industry_id2 = ((int)industry_id2).ToString()
+ };
+ return CommonJsonSend.Send(accessToken, urlFormat, msgData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 获取设置的行业信息
+ ///
+ ///
+ ///
+ ///
+
+ public static GetIndustryJsonResult GetIndustry(string accessTokenOrAppId, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/get_industry?access_token={0}";
+ return CommonJsonSend.Send(accessToken, urlFormat, null, CommonJsonSendType.GET, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ ///
+ /// 获得模板ID
+ ///
+ ///
+ /// 模板库中模板的编号,有“TM**”和“OPENTMTM**”等形式
+ ///
+ ///
+ public static AddtemplateJsonResult Addtemplate(string accessTokenOrAppId, string template_id_short, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token={0}";
+ var msgData = new
+ {
+ template_id_short = template_id_short
+
+ };
+ return CommonJsonSend.Send(accessToken, urlFormat, msgData, CommonJsonSendType.POST, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ ///
+ /// 获取模板列表
+ ///
+ ///
+ ///
+ ///
+
+ public static GetPrivateTemplateJsonResult GetPrivateTemplate(string accessTokenOrAppId, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token={0}";
+ return CommonJsonSend.Send(accessToken, urlFormat, null, CommonJsonSendType.GET, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ ///
+ /// 删除模板
+ ///
+ ///
+ /// 公众帐号下模板消息ID
+ ///
+ ///
+ public static WxJsonResult DelPrivateTemplate(string accessTokenOrAppId, string template_id, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token={0}";
+ var msgData = new
+ {
+ template_id = template_id
+ };
+
+ return CommonJsonSend.Send(accessToken, urlFormat, msgData, CommonJsonSendType.POST, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ #endregion
+
+ #region 异步请求
+ ///
+ /// 【异步方法】模板消息接口
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task SendTemplateMessageAsync(string accessTokenOrAppId, string openId, string templateId, string url, object data, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";
+ var msgData = new TempleteModel()
+ {
+ touser = openId,
+ template_id = templateId,
+ // topcolor = topcolor,
+ url = url,
+ data = data
+ };
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, msgData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】设置所属行业
+ ///
+ ///
+ /// 公众号模板消息所属行业编号
+ /// 公众号模板消息所属行业编号
+ ///
+ ///
+
+ public static async Task SetIndustryAsync(string accessTokenOrAppId, IndustryCode industry_id1, IndustryCode industry_id2, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token={0}";
+ var msgData = new
+ {
+ industry_id1 = ((int)industry_id1).ToString(),
+ industry_id2 = ((int)industry_id2).ToString()
+ };
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, msgData, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】获取设置的行业信息
+ ///
+ ///
+ ///
+ ///
+
+ public static async Task GetIndustryAsync(string accessTokenOrAppId, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/get_industry?access_token={0}";
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, null, CommonJsonSendType.GET, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ ///
+ /// 【异步方法】获得模板ID
+ ///
+ ///
+ /// 模板库中模板的编号,有“TM**”和“OPENTMTM**”等形式
+ ///
+ ///
+ public static async Task AddtemplateAsync(string accessTokenOrAppId, string template_id_short, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token={0}";
+ var msgData = new
+ {
+ template_id_short = template_id_short
+
+ };
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, msgData, CommonJsonSendType.POST, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ ///
+ ///【异步办法】 获取模板列表
+ ///
+ ///
+ ///
+ ///
+
+ public static async Task GetPrivateTemplateAsync(string accessTokenOrAppId, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token={0}";
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, null, CommonJsonSendType.GET, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ ///
+ /// 【异步方法】删除模板
+ ///
+ ///
+ /// 公众帐号下模板消息ID
+ ///
+ ///
+ public static async Task DelPrivateTemplateAsync(string accessTokenOrAppId, string template_id, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync(accessToken =>
+ {
+ const string urlFormat = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token={0}";
+ var msgData = new
+ {
+ template_id = template_id
+ };
+
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, urlFormat, msgData, CommonJsonSendType.POST, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ #endregion
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/User/UserApi.cs b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/User/UserApi.cs
new file mode 100644
index 0000000000..340508b29a
--- /dev/null
+++ b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/AdvancedAPIs/User/UserApi.cs
@@ -0,0 +1,217 @@
+/*----------------------------------------------------------------
+ Copyright (C) 2016 Senparc
+
+ 文件名:UserAPI.cs
+ 文件功能描述:用户接口
+
+
+ 创建标识:Senparc - 20150211
+
+ 修改标识:Senparc - 20150303
+ 修改描述:整理接口
+
+ 修改标识:jsionr - 20150322
+ 修改描述:添加修改关注者备注信息接口
+
+ 修改标识:Senparc - 20150325
+ 修改描述:修改关注者备注信息开放代理请求超时时间
+
+ 修改标识:Senparc - 20160719
+ 修改描述:增加其接口的异步方法
+
+----------------------------------------------------------------*/
+
+/*
+ 接口详见:http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF
+ */
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Senparc.Weixin.Entities;
+using Senparc.Weixin.MP.AdvancedAPIs.User;
+using Senparc.Weixin.MP.CommonAPIs;
+using Senparc.Weixin.HttpUtility;
+
+namespace Senparc.Weixin.MP.AdvancedAPIs
+{
+ ///
+ /// 用户接口
+ ///
+ public static class UserApi
+ {
+ #region 同步请求
+
+ ///
+ /// 获取用户信息
+ ///
+ /// 调用接口凭证
+ /// 普通用户的标识,对当前公众号唯一
+ /// 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
+ ///
+ public static UserInfoJson Info(string accessTokenOrAppId, string openId, Language lang = Language.zh_CN)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang={2}",
+ accessToken.AsUrlData(), openId.AsUrlData(), lang.ToString("g").AsUrlData());
+ return HttpUtility.Get.GetJson(url);
+
+ //错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
+ //{"errcode":40013,"errmsg":"invalid appid"}
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 获取关注者OpenId信息
+ ///
+ ///
+ ///
+ ///
+ public static OpenIdResultJson Get(string accessTokenOrAppId, string nextOpenId)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/get?access_token={0}", accessToken.AsUrlData());
+ if (!string.IsNullOrEmpty(nextOpenId))
+ {
+ url += "&next_openid=" + nextOpenId;
+ }
+ return HttpUtility.Get.GetJson(url);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 修改关注者备注信息
+ ///
+ /// 调用接口凭证
+ /// 普通用户的标识,对当前公众号唯一
+ /// 新的备注名,长度必须小于30字符
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static WxJsonResult UpdateRemark(string accessTokenOrAppId, string openId, string remark, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token={0}", accessToken.AsUrlData());
+ var data = new
+ {
+ openid = openId,
+ remark = remark
+ };
+ return CommonJsonSend.Send(accessToken, url, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 批量获取用户基本信息
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static BatchGetUserInfoJsonResult BatchGetUserInfo(string accessTokenOrAppId, List userList, int timeOut = Config.TIME_OUT)
+ {
+ return ApiHandlerWapper.TryCommonApi(accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token={0}", accessToken.AsUrlData());
+ var data = new
+ {
+ user_list = userList,
+ };
+ return CommonJsonSend.Send(accessToken, url, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ #endregion
+
+ #region 异步请求
+ ///
+ /// 【异步方法】获取用户信息
+ ///
+ /// 调用接口凭证
+ /// 普通用户的标识,对当前公众号唯一
+ /// 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
+ ///
+ public static async Task InfoAsync(string accessTokenOrAppId, string openId, Language lang = Language.zh_CN)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang={2}",
+ accessToken.AsUrlData(), openId.AsUrlData(), lang.ToString("g").AsUrlData());
+ return HttpUtility.Get.GetJsonAsync(url);
+
+ //错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
+ //{"errcode":40013,"errmsg":"invalid appid"}
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】获取关注者OpenId信息
+ ///
+ ///
+ ///
+ ///
+ public static async Task GetAsync(string accessTokenOrAppId, string nextOpenId)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/get?access_token={0}", accessToken.AsUrlData());
+ if (!string.IsNullOrEmpty(nextOpenId))
+ {
+ url += "&next_openid=" + nextOpenId;
+ }
+ return HttpUtility.Get.GetJsonAsync(url);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】修改关注者备注信息
+ ///
+ /// 调用接口凭证
+ /// 普通用户的标识,对当前公众号唯一
+ /// 新的备注名,长度必须小于30字符
+ /// 代理请求超时时间(毫秒)
+ ///
+ public static async Task UpdateRemarkAsync(string accessTokenOrAppId, string openId, string remark, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token={0}", accessToken.AsUrlData());
+ var data = new
+ {
+ openid = openId,
+ remark = remark
+ };
+ return Senparc.Weixin .CommonAPIs .CommonJsonSend.SendAsync(accessToken, url, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+
+ ///
+ /// 【异步方法】批量获取用户基本信息
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task BatchGetUserInfoAsync(string accessTokenOrAppId, List userList, int timeOut = Config.TIME_OUT)
+ {
+ return await ApiHandlerWapper.TryCommonApiAsync( accessToken =>
+ {
+ string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token={0}", accessToken.AsUrlData());
+ var data = new
+ {
+ user_list = userList,
+ };
+ return Senparc.Weixin.CommonAPIs.CommonJsonSend.SendAsync(accessToken, url, data, timeOut: timeOut);
+
+ }, accessTokenOrAppId);
+ }
+ #endregion
+ }
+}
diff --git a/src/Senparc.Weixin.MP/Senparc.Weixin.MP/TenPayLib/TenPayHttpClient.cs b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/TenPayLib/TenPayHttpClient.cs
new file mode 100644
index 0000000000..3719b9d2c3
--- /dev/null
+++ b/src/Senparc.Weixin.MP/Senparc.Weixin.MP/TenPayLib/TenPayHttpClient.cs
@@ -0,0 +1,292 @@
+/*----------------------------------------------------------------
+ Copyright (C) 2016 Senparc
+
+ 文件名:TenPayHttpClient.cs
+ 文件功能描述:微信支付http、https通信类
+
+
+ 创建标识:Senparc - 20150211
+
+ 修改标识:Senparc - 20150303
+ 修改描述:整理接口
+----------------------------------------------------------------*/
+
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Text.RegularExpressions;
+
+/**
+ * http、https通信类
+ * ============================================================================
+ * api说明:
+ * setReqContent($reqContent),设置请求内容,无论post和get,都用get方式提供
+ * getResContent(), 获取应答内容
+ * setMethod($method),设置请求方法,post或者get
+ * getErrInfo(),获取错误信息
+ * setCertInfo($certFile, $certPasswd, $certType="PEM"),设置证书,双向https时需要使用
+ * setCaInfo($caFile), 设置CA,格式未pem,不设置则不检查
+ * setTimeOut($timeOut), 设置超时时间,单位秒
+ * getResponseCode(), 取返回的http状态码
+ * call(),真正调用接口
+ *
+ * ============================================================================
+ *
+ */
+
+namespace Senparc.Weixin.MP.TenPayLib
+{
+ public class TenPayHttpClient
+ {
+ ///
+ /// 请求内容,无论post和get,都用get方式提供
+ ///
+ private string ReqContent;
+
+ ///
+ /// 应答内容
+ ///
+ private string ResContent;
+
+ ///
+ /// 请求方法
+ ///
+ private string Method;
+
+ ///
+ /// 错误信息
+ ///
+ private string ErrInfo;
+
+ ///
+ /// 证书文件
+ ///
+
+ private string CertFile;
+
+ ///
+ /// 证书密码
+ ///
+ private string CertPasswd;
+
+ ///
+ /// ca证书文件
+ ///
+ private string CaFile;
+
+ ///
+ /// 超时时间,以秒为单位
+ ///
+ private int TimeOut;
+
+ ///
+ /// http应答编码
+ ///
+
+ private int ResponseCode;
+
+ ///
+ /// 字符编码
+ ///
+ private string Charset;
+
+ public TenPayHttpClient()
+ {
+ this.CaFile = "";
+ this.CertFile = "";
+ this.CertPasswd = "";
+
+ this.ReqContent = "";
+ this.ResContent = "";
+ this.Method = "POST";
+ this.ErrInfo = "";
+ this.TimeOut = 1 * 60;//5分钟
+
+ this.ResponseCode = 0;
+ this.Charset = "gb2312";
+
+ }
+
+ ///
+ /// 设置请求内容
+ ///
+ ///
+ public void SetReqContent(string reqContent)
+ {
+ this.ReqContent = reqContent;
+ }
+
+ ///
+ /// 获取结果内容
+ ///
+ ///
+ public string GetResContent()
+ {
+ return this.ResContent;
+ }
+
+ ///
+ /// 设置请求方法post或者get
+ ///
+ ///
+
+ public void SetMethod(string method)
+ {
+ this.Method = method;
+ }
+
+ ///
+ /// 获取错误信息
+ ///
+ ///
+ public string GetErrInfo()
+ {
+ return this.ErrInfo;
+ }
+
+ ///
+ /// 设置证书信息
+ ///
+ ///
+ ///
+ public void SetCertInfo(string certFile, string certPasswd)
+ {
+ this.CertFile = certFile;
+ this.CertPasswd = certPasswd;
+ }
+
+ ///
+ /// 设置ca
+ ///
+ ///
+ public void SetCaInfo(string caFile)
+ {
+ this.CaFile = caFile;
+ }
+
+ ///
+ /// 设置超时时间,以秒为单位
+ ///
+ ///
+ public void SetTimeOut(int timeOut)
+ {
+ this.TimeOut = timeOut;
+ }
+
+
+ ///
+ /// 获取http状态码
+ ///
+ ///
+ public int GetResponseCode()
+ {
+ return this.ResponseCode;
+ }
+
+ public void SetCharset(string charset)
+ {
+ this.Charset = charset;
+ }
+
+ ///
+ /// 验证服务器证书
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
+ {
+ return true;
+ }
+
+ ///
+ /// 执行http调用
+ ///
+ ///
+ public bool Call()
+ {
+ StreamReader sr = null;
+ HttpWebResponse wr = null;
+
+ HttpWebRequest hp = null;
+ try
+ {
+ string postData = null;
+ if (this.Method.ToUpper() == "POST")
+ {
+ string[] sArray = Regex.Split(this.ReqContent, "\\?");
+
+ hp = (HttpWebRequest)WebRequest.Create(sArray[0]);
+
+ if (sArray.Length >= 2)
+ {
+ postData = sArray[1];
+ }
+
+ }
+ else
+ {
+ hp = (HttpWebRequest)WebRequest.Create(this.ReqContent);
+ }
+
+
+ ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
+ if (this.CertFile != "")
+ {
+ hp.ClientCertificates.Add(new X509Certificate2(this.CertFile, this.CertPasswd));
+ }
+ hp.Timeout = this.TimeOut * 1000;
+
+ Encoding encoding = Encoding.GetEncoding(this.Charset);
+ if (postData != null)
+ {
+ byte[] data = encoding.GetBytes(postData);
+
+ hp.Method = "POST";
+
+ hp.ContentType = "application/x-www-form-urlencoded";
+
+ hp.ContentLength = data.Length;
+
+ Stream ws = hp.GetRequestStream();
+
+ // 发送数据
+
+ ws.Write(data, 0, data.Length);
+ ws.Close();
+
+
+ }
+
+
+ wr = (HttpWebResponse)hp.GetResponse();
+ sr = new StreamReader(wr.GetResponseStream(), encoding);
+
+
+
+ this.ResContent = sr.ReadToEnd();
+ sr.Close();
+ wr.Close();
+ }
+ catch (Exception exp)
+ {
+ this.ErrInfo += exp.Message;
+ if (wr != null)
+ {
+ this.ResponseCode = Convert.ToInt32(wr.StatusCode);
+ }
+
+ return false;
+ }
+
+ this.ResponseCode = Convert.ToInt32(wr.StatusCode);
+
+ return true;
+ }
+ }
+}