2023年2月

微信语义理解接口提供从用户自然语言输入到结构化解析的技术实现,使用先进的自然语言处理技术给开发者提供一站式的语义解析方案。该平台覆盖多个垂直领域的语义场景,部分领域还可以支持取得最终的展示结果。开发者无需掌握语义理解及相关技术,只需根据自己的产品特点,选择相应的服务即可搭建一套智能语义服务。结合语音识别接口,通过微信语音识别得到用户的语音信息之后,经过语义分析理解,得到用户需求,及时回复用户。本文介绍如何实现对微信语义接口的封装处理,以及一些常用场景的调用。

1)微信语义理解接口

这个东西也就是把我们日常的话语(称之为自然语言)解析为对应的信息结构体,方便我们提取里面的相关信息进行搜索查询,并精确回应给对应的请求者的一个桥梁,其主要的功能就是解析我们所说的内容。

微信开放平台语义理解接口调用(http请求)简单方便,用户无需掌握语义理解及相关技术,只需根据自己的产品特点,选择相应的服务即可搭建一套智能语义服务。我们来看看微信语义理解接口的定义内容。

http请求方式: POST(请使用https协议)

https://api.weixin.qq.com/semantic/semproxy/search?access_token=YOUR_ACCESS_TOKEN

POST数据格式:JSON,
POST数据例子:

{

"query":"查一下明天从北京到上海的南航机票",

"city":"北京",

"category": "flight,hotel",

"appid":"wxaaaaaaaaaaaaaaaa",

"uid":"123456"

} 

参数说明

参数 是否必须 参数类型 说明
access_token String 根据appid和appsecret获取到的token
query String 输入文本串
category String 需要使用的服务类型,多个用“,”隔开,不能为空
latitude 见接口协议文档 Float 纬度坐标,与经度同时传入;与城市二选一传入
longitude 见接口协议文档 Float 经度坐标,与纬度同时传入;与城市二选一传入
city 见接口协议文档 String 城市名称,与经纬度二选一传入
region 见接口协议文档 String 区域名称,在城市存在的情况下可省;与经纬度二选一传入
appid String 公众号唯一标识,用于区分公众号开发者
uid String 用户唯一id(非开发者id),用户区分公众号下的不同用户(建议填入用户openid),如果为空,则无法使用上下文理解功能。appid和uid同时存在的情况下,才可以使用上下文理解功能。

注:单类别意图比较明确,识别的覆盖率比较大,所以如果只要使用特定某个类别,建议将category只设置为该类别。

返回说明 正常情况下,微信会返回下述JSON数据包:

{ 

“errcode”:
0,

“query”:”查一下明天从北京到上海的南航机票”,

“type”:”flight”,

“semantic”:{

“details”:{

“start_loc”:{

“type”:”LOC_CITY”,

“city”:”北京市”,

“city_simple”:”北京”,

“loc_ori”:”北京”

},

“end_loc”: {

“type”:”LOC_CITY”,

“city”:”上海市”,

“city_simple”:”上海”,

“loc_ori”:”上海”

},

“start_date”: {

“type”:”DT_ORI”,

“date”:”
2014-03-05”,

“date_ori”:”明天”

},

“airline”:”中国南方航空公司”

},

“intent”:”SEARCH”

}

返回参数说明

参数 是否必须 参数类型 说明
errcode Int 表示请求后的状态
query String 用户的输入字符串
type String 服务的全局类型id,详见协议文档中垂直服务协议定义
semantic Object 语义理解后的结构化标识,各服务不同
result Array 部分类别的结果
answer String 部分类别的结果html5展示,目前不支持
text String 特殊回复说明

上面就是微信官方给出的代码案例,以及一个《
语义理解接口协议文档
》,里面介绍了各个场景的语义结构信息,虽然这个文档好像好久都没怎么更新,不过总体内容还是稳定的,我们可以通过这个文档进行相关的类库设计工作。

2、语义理解接口的C#实现

根据《
语义理解接口协议文档
》文档,我们可以定义各种所需的语义结构类库,这些是我们开展语义接口的基础类。

例如我们定义基础的时间协议类,如下所示。

    /// <summary>
    ///时间相关协议datetime/// </summary>
    public classSemantic_DateTime
{
/// <summary> ///单时间的描述协议类型:“DT_SINGLE”。DT_SINGLE又细分为两个类别:DT_ORI和DT_INFER。DT_ORI是字面时间,比如:“上午九点”;///DT_INFER是推理时间,比如:“提前5分钟”。 时间段的描述协议类型:“DT_INTERVAL”///重复时间的描述协议类型:“DT_REPEAT” DT_ REPEAT又细分为两个类别:DT_RORI和DT_RINFER。DT_RORI是字面时间,比如:“每天上午九点”;DT_RINFER是推理时间,比如:“工作日除外”/// </summary> public string type { get; set; }/// <summary> ///24小时制,格式:HH:MM:SS,默认为00:00:00/// </summary> public string time { get; set; }/// <summary> ///Time的原始字符串/// </summary> public string time_ori { get; set; }
}
/// <summary> ///单时间的描述协议datetime/// </summary> public classSemantic_SingleDateTime : Semantic_DateTime
{
/// <summary> ///格式:YYYY-MM-DD,默认是当天时间/// </summary> public string date { get; set; }/// <summary> ///格式:YYYY-MM-DD 农历/// </summary> public string date_lunar { get; set; }/// <summary> ///date的原始字符串/// </summary> public string date_ori { get; set; }
}

当然时间还有很多类型的定义,都基本上按照文档所列的字段进行处理,上面的代码只是定义了常用的单时间的描述协议类型:“DT_SINGLE”。

除了时间协议,还有数字,地点位置等相关协议,如数字协议如下所示。

    public classSemantic_Number
{
/// <summary> ///大类型:“NUMBER” NUMBER又细分为如下类别:NUM_PRICE、NUM_PADIUS、NUM_DISCOUNT、NUM_SEASON、NUM_EPI、NUM_CHAPTER。/// </summary> public string type { get; set; }/// <summary> ///开始/// </summary> public string begin { get; set; }/// <summary> ///结束/// </summary> public string end { get; set; }
}

地点位置协议如下所示

    /// <summary>
    ///地点相关协议/// </summary>
    public classSemantic_Location
{
/// <summary> ///大类型:“LOC” LOC又细分为如下类别:LOC_COUNTRY、LOC_PROVINCE、LOC_CITY、LOC_TOWN、LOC_POI、NORMAL_POI。/// </summary> public string type { get; set; }/// <summary> ///国家/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string country { get; set; }/// <summary> ///省全称,例如:广东省/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string province { get; set; }/// <summary> ///省简称,例如:广东|粤/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string province_simple { get; set; }/// <summary> ///市全称,例如:北京市/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string city { get; set; }/// <summary> ///市简称,例如:北京/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string city_simple { get; set; }

..............

前面我们看到了,语音立即的POST数据格式是一个较为固定的格式内容,我们可以把它定义为一个类,方便数据处理。

POST数据格式:JSON,POST数据例子如下所示:

{

"query":"查一下明天从北京到上海的南航机票",

"city":"北京",

"category": "flight,hotel",

"appid":"wxaaaaaaaaaaaaaaaa",

"uid":"123456"

} 

那么我们可以定义它的类库如下所示。

    /// <summary>
    ///语义查询条件/// </summary>
    public classSemanticQueryJson
{
/// <summary> ///输入文本串///必填/// </summary> public string query { get; set; }/// <summary> ///需要使用的服务类别,多个用,隔开,不能为空///必填/// </summary> public string category { get; set; }/// <summary> ///城市名称,与经纬度二选一传入///见说明,选填/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string city { get; set; }/// <summary> ///App id,开发者的唯一标识,用于区分开放者,如果为空,则没法使用上下文理解功能。///非必填/// </summary> public string appid { get; set; }/// <summary> ///用户唯一id(并非开发者id),用于区分该开发者下不同用户,如果为空,则没法使用上下文理解功能。appid和uid同时存在的情况下,才可以使用上下文理解功能。///非必填/// </summary> public string uid { get; set; }

................
}

接着我们分析语义理解的接口返回值,它们基本上都是很有规律的内容,如下所示。

这样我们也就可以定义一个通用的类库对象,用来存储不同的返回内容了,如下代码所示。

    /// <summary>
    ///微信语义结果/// </summary>
    public class SemanticResultJson<T>: ErrorJsonResult
{
/// <summary> ///用户的输入字符串/// </summary> public string query { get; set; }/// <summary> ///服务的全局类别id/// </summary> public string type { get; set; }/// <summary> ///语义理解后的结构化标识,各服务不同/// </summary> public T semantic { get; set; }
}

而其中的T semantic就是另外一个结构体里面的内容,这个结构体总体也是固定的内容,我们继续定义一个如下的类。

    /// <summary>
    ///详细信息里面的对象/// </summary>
    /// <typeparam name="T"></typeparam>
    public class SemanticDetail<T>{/// <summary>
        ///详细信息/// </summary>
        public T details { get; set; }/// <summary>
        ///查询类型/// </summary>
        public string intent { get; set; }
}

有了这些类库的支持,我们可以封装语义理解接口的返回值了,这样它的接口定义和封装处理代码如下所示。

    /// <summary>
    ///语意理解接口///微信开放平台语义理解接口调用(http请求)简单方便,用户无需掌握语义理解及相关技术,只需根据自己的产品特点,选择相应的服务即可搭建一套智能语义服务。/// </summary>
    public classSemanticApi : ISemanticApi
{
/// <summary> ///发送语义理解请求/// </summary> /// <param name="accessToken">调用接口凭证</param> /// <param name="data">查询条件</param> public SemanticResultJson<SemanticDetail<T>> SearchSemantic<T>(stringaccessToken, SemanticQueryJson data)
{
var url = string.Format("https://api.weixin.qq.com/semantic/semproxy/search?access_token={0}", accessToken);string postData =data.ToJson();return JsonHelper<SemanticResultJson<SemanticDetail<T>>>.ConvertJson(url, postData);
}

由于微信语义结果是针对不同的服务协议,我们需要根据这些不同的服务协议,来定义属于这些信息结构,如在文档里,我们可以看到有很多不同类型的服务协议。

根据文档的详细字段说明,我们可以定义不同服务的对应类库。

例如对于旅游服务的语义理解,它们的协议类如下所示。

    /// <summary>
    ///旅游服务(travel)/// </summary>
    public classSemantic_Details_Travel
{
/// <summary> ///旅游目的地/// </summary> public Semantic_Location location { get; set; }/// <summary> ///景点名称/// </summary> public string spot { get; set; }/// <summary> ///旅游日期/// </summary> public Semantic_SingleDateTime datetime { get; set; }/// <summary> ///旅游类型词/// </summary> public string tag { get; set; }/// <summary> ///0默认,1自由行,2跟团游/// </summary> public int category { get; set; }
}

那么调用的旅游语义的案例代码如下所示

            var api = newSemanticApi();var json = newSemanticQueryJson
{
appid
=appId,
uid
=openId,
category
=SemanticCategory.travel.ToString(),
query
= "故宫门票多少钱",
city
= "北京市"};var travel = api.SearchSemantic<Semantic_Details_Travel>(token, json);
Console.WriteLine(travel.ToJson());

如果我们测试,上面的代码跑起来会返回一个旅游的协议对象,包括了相关的数据信息。

{"errcode" : 0,"query" : "故宫门票多少钱","semantic": {"details": {"answer" : "","context_info": {},"hit_str" : "故宫 门票 多少 钱","spot" : "故宫"},"intent" : "PRICE"},"type" : "travel"}

我们再来看一个例子,例如对于航班服务,我们定义它的语义理解协议如下所示。

    /// <summary>
    ///航班服务(flight)/// </summary>
    public classSemantic_Details_Flight
{
/// <summary> ///航班号/// </summary> public string flight_no { get; set; }/// <summary> ///出发地/// </summary> public Semantic_Location start_loc { get; set; }/// <summary> ///目的地/// </summary> public Semantic_Location end_loc { get; set; }/// <summary> ///出发日期/// </summary> public Semantic_SingleDateTime start_date { get; set; }/// <summary> ///返回日期/// </summary> public Semantic_SingleDateTime end_date { get; set; }/// <summary> ///航空公司/// </summary> public string airline { get; set; }/// <summary> ///座位级别(默认无限制):ECONOMY(经济舱)BIZ(商务舱)FIRST(头等舱)/// </summary> public string seat { get; set; }/// <summary> ///排序类型:0排序无要求(默认),1价格升序,2价格降序,3时间升序,4时间降序/// </summary> public int sort { get; set; }
}

那么调用获取语义理解内容的代码如下所示。

            json = newSemanticQueryJson
{
appid
=appId,
uid
=openId,
category
=SemanticCategory.flight.ToString(),
query
= "查一下明天从广州到上海的南航机票",
city
= "广州"};var flight = api.SearchSemantic<Semantic_Details_Flight>(token, json);
Console.WriteLine(flight.ToJson());

我们可以获取到的JSON数据如下所示

{"errcode" : 0,"query" : "查一下明天从广州到上海的南航机票","semantic": {"details": {"airline" : "中国南方航空公司","answer" : "已帮您预定2016-04-13,从广州市出发,前往上海市的航班。","context_info": {"isFinished" : "1","null_times" : "0"},"end_loc": {"city" : "上海市","city_simple" : "上海|沪|申","loc_ori" : "上海","modify_times" : "0","slot_content_type" : "2","type" : "LOC_CITY"},"hit_str" : "查 一下 明天 从 广州 到 上海 南航 机票","sort" : "1","start_date": {"date" : "2016-04-13","date_lunar" : "2016-03-07","date_ori" : "明天","modify_times" : "0","slot_content_type" : "2","type" : "DT_ORI","week" : "3"},"start_loc": {"city" : "广州市","city_simple" : "广州","loc_ori" : "广州","modify_times" : "0","province" : "广东省","province_simple" : "广东|粤","slot_content_type" : "2","type" : "LOC_CITY"}
},
"intent" : "SEARCH"},"type" : "flight"}

这样就是我们把我们常规的语义,分析成了机器可以识别的准确的数据结构了,我们可以根据不同的语义场合对它进行分析,然后给用户进行不同的响应就可以了,结合微信语音识别为文本内容,我们可以把它做得很强大,有的类似机器智能的味道了。

微信语义理解是一个好东西,不过在微信官网上没有看到进一步的案例,如果能够有一些与实际结合的例子,估计更能帮助我们理解和应用了。

在微信的应用上,微信支付是一个比较有用的部分,但也是比较复杂的技术要点,在微商大行其道的年代,自己的商店没有增加微信支付好像也说不过去,微信支付旨在为广大微信用户及商户提供更优质的支付服务,微信的支付和安全系统由腾讯财付通提供支持。本文主要介绍如何在微信公众号上实现微信支付的接入、微信支付API的封装,以及API的调用,实现我们一些常见的业务调用。

1、开通微信支付并配置

微信支付是需要微信公众号的认证基础,也就是只对认证的公众号开放,微信认证需要签署相关的资料,并且进行对账认证,一般会有电话联系确认相关的信息的。

在微信支付API开始使用前,我们一般需要在后台进行一定的配置,如我们需要配置公众号支付的授权目录,测试白名单等信息,以及扫码支持的回调处理地址(这个实现在后面再讲),如下所示。

在使用API之前,我们要知道微信一些关键的操作,如退款、撤销订单等操作是需要证书的,而且常规的支付操作,我们也需要商户号、商户支付秘钥等信息,这些证书和秘钥信息,是我们从微信支付的商户平台上获取的,我们微信支付开通并审核通过后,我们就可以登录商户平台进行相关的操作了。

首先我们需要在开发的电脑上安装证书。

然后需要设置API的秘钥

最后在【API安全】项目上下载证书供我们开发环境使用。

2、微信支付API的介绍

微信支付配置相关的参数,并获得证书、API秘钥、商户号等信息后,我们可以开始了解微信支付的API的具体使用了,我们需要先把API封装为C#的类库进行使用,这样才能在各种应用里面方便调用。

微信支付分为有多种方式,如扫码支付、公众号支付、JSAPI支付、APP支付等方面,不过核心的API都差不多,基本上都覆盖了下面截图的几个API,只是有部分的接口差异。

我们可以从其中扫码支付开始了解,这个是对二维码进行扫码支付的场景,分为了模式一和模式二两种方式。

扫码支付可分为两种模式,商户根据支付场景选择相应模式。

【模式一】:商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

【模式二】:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为
2
小时

,过期后无法支付。

根据扫码支付的API说明,我们可以分别对这些接口(如统一下单、查询订单、关闭订单、申请退款、查询退款、下载对账单等接口进行逐一封装,以方便我们的开发使用。

模式一和模式二,都需要使用到了统一下单的接口,然后生成相应的二维码给客户扫码支付使用。

那么我们先来看看统一下单的接口说明,以了解它的具体使用。

1)应用场景

除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。

2)接口链接

URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

3)是否需要证书

不需要

4)请求参数

请求参数看似很多,大概分为两部分,一部分是系统必须的固定参数,一部分是业务所需的参数。

系统必须的固定参数如下所示。

一部分是业务参数,业务参数如下所示,主要是记录订单的相关产品ID、说明、费用等等

微信支付接口的调用和公众号其他接口调用不太一样,这里全部是采用XML进行交换的,感觉没有JSON那么方便灵活,如下所示是统一下单的接口提交数据。

然后返回的数据也是XML的,如下面例子代码所示,而且其中的字段内容还不太确定,因此按官网的建议,使用字典集合来存储返回的数据对象。

3、微信支付APIC#封装和调用

根据上面的描述,我们大概了解了微信支付API 的大概说明,根据这些信息,我们可以对它进行C#代码的封装了,对于代码的封装,我们关键点在其中第一个,如果顺利封装好第一个接口,那么后面的根据通用的方式,就很容易继续处理这些接口了。

例如,我们可以定义好微信支付的API接口定义,如下所示。

    /// <summary>
    ///微信支付接口/// </summary>
    public interfaceITenPayApi
{
/// <summary> ///生成扫描支付模式一URL/// </summary> /// <param name="productId">商品ID</param> /// <returns></returns> string GetPrePayUrl(stringproductId);/// <summary> ///生成直接支付url,支付url有效期为2小时,模式二/// </summary> /// <param name="info">商品订单数据</param> /// <returns></returns> stringGetPayUrl(WxPayOrderData info);/// <summary> ///统一下单。(不需要证书,默认不需要)///除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,///返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。/// </summary> /// <param name="info">商品订单数据</param> WxPayData UnifiedOrder(WxPayOrderData info);

.............

其中的接口方法的输入参数我们定义一个实体类
WxPayOrderData
来存储一些业务参数,这些参数根据第二点的接口说明进行定义,代码如下所示

    /// <summary>
    ///统一下单的商品订单信息/// </summary>
    public classWxPayOrderData
{
/// <summary> ///商品ID, trade_type=NATIVE,此参数必传/// </summary> public string product_id { get; set; }/// <summary> ///商品或支付单简要描述/// </summary> public string body { get; set; }/// <summary> ///订单总金额,单位为分/// </summary> public int total_fee { get; set; }/// <summary> ///商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠/// </summary> public string goods_tag { get; set; }/// <summary> ///交易类型,默认为:NATIVE。///JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付/// </summary> public string trade_type { get; set; }/// <summary> ///商品名称明细列表/// </summary> public string detail { get; set; }/// <summary> ///附加数据///在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据/// </summary> public string attach { get; set; }/// <summary> ///用户标识///trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。/// </summary> public string openid { get; set; }publicWxPayOrderData()
{
this.trade_type = "NATIVE";
}
}

然后我们定义一个接口返回的类WxPayData,它用来存储返回的对象信息的,这个类在官网例子里面有说明,其里面内置一个排序过的字典对象进行存储数据,部分代码如下所示,我对它进行了相关的修改,以方便在构造函数里面初始化一些必备的参数(固定参数)。

    public classWxPayData
{
//采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序 private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();/// <summary> ///默认构造函数///如果initDefault为true,则自动填入字段(appid,mch_id,time_stamp,nonce_str,out_trade_no,)/// </summary> public WxPayData(bool initDefault = false)
{
if(initDefault)
{
Init();
}
}
/// <summary> ///对象初始化后,自动填入字段(appid,mch_id,time_stamp,nonce_str,out_trade_no,)/// </summary> public voidInit()
{
//初始化几个参数 this.SetValue("appid", WxPayConfig.APPID);//公众帐号id this.SetValue("mch_id", WxPayConfig.MCHID);//商户号 this.SetValue("nonce_str", GenerateNonceStr());//随机字符串 this.SetValue("out_trade_no", GenerateOutTradeNo(WxPayConfig.MCHID));//随机字符串 }

然后我们根据上面的数据定义,可以实现统一下单的函数内容,主要是把输入参数转换为我们需要的字典参数集合,如下代码所示。

        /// <summary>
        ///统一下单。(不需要证书,默认不需要)///除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,///返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。/// </summary>
        /// <param name="info">商品订单数据</param>
        publicWxPayData UnifiedOrder(WxPayOrderData info)
{
WxPayData data
= new WxPayData(true);
data.SetValue(
"product_id", info.product_id);//商品ID data.SetValue("openid", info.openid);//商品ID//其他信息 data.SetValue("body", info.body);//商品描述 data.SetValue("attach", info.attach);//附加数据 data.SetValue("total_fee", info.total_fee);//总金额 data.SetValue("goods_tag", info.goods_tag);//商品标记 data.SetValue("trade_type", info.trade_type);//交易类型//默认构建 data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间 data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间 ..............

最后的数据交换逻辑,我们通过对URL进行POST提交XML数据给它获取返回结果就可以了,如下所示。

            string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";return GetPostResult(data, url);

其中上面的函数的代码逻辑如下所示,主要是把返回的结果再还原为XML对象类WxPayData。

        /// <summary>
        ///通用的获取结果函数/// </summary>
        private WxPayData GetPostResult(WxPayData data, stringurl)
{
string xml =data.ToXml();string response = helper.GetHtml(url, xml, true);

WxPayData result
= newWxPayData();
result.FromXml(response);
returnresult;
}

对于扫码操作的模式二,直接生成一种二维码,不需要后台进行回调的,那么它的实现逻辑只需要对上面代码进行封装就可以了,如先构建二维码的函数代码如下所示。

        /// <summary>
        ///生成直接支付url,支付url有效期为2小时,模式二/// </summary>
        /// <param name="info">商品订单数据</param>
        /// <returns></returns>
        public stringGetPayUrl(WxPayOrderData info)
{
WxPayData result
= UnifiedOrder(info);//调用统一下单接口 return result.GetString("code_url");//获得统一下单接口返回的二维码链接 }

如在Winform界面里面,调用生成二维码的代码如下所示,主要逻辑就是构建好二维码,然后显示在界面上。

        private void btnGetPayUrl_Click(objectsender, EventArgs e)
{
//测试扫码模式二的生成二维码方式 WxPayOrderData data = newWxPayOrderData()
{
product_id
= "123456789",
body
= "测试支付-模式二",
attach
= "爱奇迪技术支持",
detail
= "测试扫码支付-模式二",
total_fee
= 1,
goods_tag
= "test1"};var url =api.GetPayUrl(data);var image =api.GenerateQRImage(url);this.imgGetPayUrl.Image =image;this.imgGetPayUrl.SizeMode =PictureBoxSizeMode.StretchImage;
}

另外对于模式一,它在前端传入一个简单的产品ID,生成二维码,当用户扫码的时候,微信后台会调用商户平台(我们服务器)的回调处理方法,这个回调方法会调用统一下单的API进行生成支付交易,过程有点复杂,我们来看看,我们的实现代码如下所示。

        /// <summary>
        ///生成扫描支付模式一URL/// </summary>
        /// <param name="productId">商品ID</param>
        /// <returns></returns>
        public string GetPrePayUrl(stringproductId)
{
WxPayData data
= new WxPayData(true);
data.SetValue(
"product_id", productId);//商品ID data.SetValue("time_stamp", data.GenerateTimeStamp());//随机字符串 data.SetValue("sign", data.MakeSign());//签名 string str = data.ToUrlParams();//转换为URL串 string url = "weixin://wxpay/bizpayurl?" +str;returnurl;
}

它的调用代码生成二维码操作如下所示。

        private void btnGetPrePayUrl_Click(objectsender, EventArgs e)
{
var productId = "12345678";var url =api.GetPrePayUrl(productId);var image =api.GenerateQRImage(url);this.imgGetPrePayUrl.Image =image;this.imgGetPayUrl.SizeMode =PictureBoxSizeMode.StretchImage;
}

我们在第一小节里面介绍了,需要在微信后台配置扫码的回调函数,如下所示。

这样我们还需要添加一个页面aspx、或者一般处理程序ashx的方式来实现扫码的回调过程。具体的逻辑也就是在这个页面里面获取到提交过来的参数,然后调用统一下单处理后,进行数据返回即可,代码逻辑如下所示。

4、在页面上进行扫码处理

前面的例子,我介绍了Winfrom的扫码例子,很多时候,我们的应用可能是基于Web的,那么它的实现是如何的呢,下面我继续介绍一下。

首先我们在自己的业务Web后台系统里面,添加两个页面,主要是用来生成二维码在页面上进行展示的,如下所示。

最终我们在NativePayPage.aspx页面上展示我们的二维码,方便用户进行扫码支付处理,页面的代码很简单,我们只需要在前端页面放置两个图片控件,图片内容通过MakeQRCode.aspx页面进行生成就可以了。

<!DOCTYPE html>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<headrunat="server">
    <metahttp-equiv="content-type"content="text/html;image/gif;charset=utf-8"/>
    <metaname="viewport"content="width=device-width, initial-scale=1" /> 
    <title>微信支付样例-扫码支付</title>
</head>
<body>
    <divstyle="margin-left: 10px;color:#00CD00;font-size:30px;font-weight: bolder;">扫码支付模式一</div><br/>
    <asp:ImageID="Image1"runat="server"style="width:200px;height:200px;"/>
    <br/><br/><br/>
    <divstyle="margin-left: 10px;color:#00CD00;font-size:30px;font-weight: bolder;">扫码支付模式二</div><br/>
    <asp:ImageID="Image2"runat="server"style="width:200px;height:200px;"/>
    
</body>
</html>

页面后台的代码就是绑定二维码的过程,代码如下所示,和Winform的代码类似操作。

        protected void Page_Load(objectsender, EventArgs e)
{
TenPayApi api
= newTenPayApi();var productId = "123456789";//生成扫码支付模式一url string url1 =api.GetPrePayUrl(productId);//生成扫码支付模式二url WxPayOrderData info = newWxPayOrderData()
{
product_id
= "123456789",
body
= "测试支付-模式二",
attach
= "爱奇迪技术支持",
detail
= "测试扫码支付-模式二",
total_fee
= 1,
goods_tag
= "test1"};string url2 =api.GetPayUrl(info);//将url生成二维码图片 Image1.ImageUrl = "MakeQRCode.aspx?data=" +HttpUtility.UrlEncode(url1);
Image2.ImageUrl
= "MakeQRCode.aspx?data=" +HttpUtility.UrlEncode(url2);
}

实现后的页面效果如下所示。

实现并预览效果,确定是我们所需的页面后,我们可以发布在公众号的菜单连接上进行测试使用了。

打开微信公众号-广州爱奇迪,我们可以看到对应的菜单发生改变,并且看到进入微信支付的菜单可以进行支付了。



以上就是微信支付的扫码过程的一个实现,微信支付还包括很多其他API接口,后面有机会可以继续进行介绍。微信支付的接口实现虽然相对其他微信接口比较复杂一些,但是我们一旦完成几个案例,后面的就相对比较容易的了,因为它的调用方式基本上比较一致,很类似。

如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(36)--微信卡劵管理的封装操作

C#开发微信门户及应用(35)--微信支付之企业付款封装操作

C#开发微信门户及应用(34)--微信裂变红包

C#开发微信门户及应用(33)--微信现金红包的封装及使用

C#开发微信门户及应用(32)--微信支付接入和API封装使用

C#开发微信门户及应用(31)--微信语义理解接口的实现和处理

C#开发微信门户及应用(30)--消息的群发处理和预览功能

C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现

C#开发微信门户及应用(27)-公众号模板消息管理

C#开发微信门户及应用(26)-公众号微信素材管理

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

C#开发微信门户及应用(24)-微信小店货架信息管理

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

C#开发微信门户及应用(22)-微信小店的开发和使用

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答


C#开发微信门户及应用(2)--微信消息的处理和应答


C#开发微信门户及应用(1)--开始使用微信接口

我在上篇随笔《
C#开发微信门户及应用(32)--微信支付接入和API封装使用
》介绍为微信支付的API封装及使用,其中介绍了如何配置好支付环境,并对扫码支付的两种方式如何在C#开发中使用进行了介绍,本随笔继续介绍微信支付的相关内容,介绍其中的微信现金红包和裂变红包的封装和使用。

在上篇随笔后,经过对整个微信框架的完善和重构,已经完成了对微信支付、企业付款、现金红包、代金券及各种卡劵进行了封装完成,并把其中微信支付及摇一摇红包部分等内容作为公众号和企业号通用的部分,这些支付相关的接口在公众号和企业号里面,都可以进行调用的,在经过一系列的优化整理后,把这些内容逐一进行介绍,希望大家喜欢支持。

1、现金红包的概念及使用

1)使用场景

微信支付现金红包向微信支付商户开发,具体能力如下:

◆ 商户调用接口时,通过指定发送对象以及发送金额的方式发放红包,这样的方式,允许商户灵活的应用于各种各样丰富的活动场景

◆ 领取到红包后,用户的资金直接进入微信零钱,避免繁复的领奖流程,带给用户微信支付原生的流畅体验

2)微信红包发送规则
发送频率规则

◆ 每分钟发送红包数量不得超过1800个;

◆ 同一个商户号,每分钟最多给同一个用户发送一个红包;

红包规则

◆ 单个红包金额介于[1.00元,200.00元]之间;

◆ 同一个红包只能发送给一个用户;(如果以上规则不满足您的需求,请发邮件至wxhongbao@tencent.com获取升级指引)

◆ 红包发放后72小时未被领取将进行退款

3) 微信红包接口调用流程

◆ 后台API调用:待进入联调过程时与开发进行详细沟通;

◆ 告知服务器:告知服务器接收微信红包的用户openID,告知服务器该用户获得的金额;

◆ 从商务号扣款:服务器获取信息后从对应的商务号扣取对应的金额;

◆ 调用失败:因不符合发送规则,商务号余额不足等原因造成调用失败,反馈至调用方;

◆ 发送成功:以微信红包公众账号发送对应红包至对应用户;

微信红包接口调用流程


2、 现金红包
API
接口的说明及C#的封装

用于企业向微信用户个人发现金红包,目前支持向指定微信用户的openid发放指定金额红包。

虽然可以通过微信的商户后台进行现金红包的发放,但我们也可以利用微信提供的接口API进行现金红包的发送。

接口调用请求说明

请求Url https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
是否需要证书 是(证书及使用说明详见
商户证书
请求方式 POST

请求参数

字段名 字段 必填 示例值 类型 说明
随机字符串 nonce_str 5K8264ILTKCH16CQ2502SI8ZNMTM67VS String(32) 随机字符串,不长于32位
签名 sign C380BEC2BFD727A4B6845133519F3AD6 String(32) 详见
签名生成算法
商户订单号 mch_billno 10000098201466666611234567890 String(28)

商户订单号(每个订单号必须唯一)

组成:mch_id+yyyymmdd+10位一天内不能重复的数字。

接口根据商户订单号支持重入,如出现超时可再调用。

商户号 mch_id 10000098 String(32) 微信支付分配的商户号
公众账号appid wxappid wx8888888888888888 String(32) 微信分配的公众账号ID(企业号corpid即为此appId)。接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。
商户名称 send_name 天虹百货 String(32) 红包发送者名称
用户openid re_openid oxTWIuGaIt6gTKsQRLau2M0yL16E String(32)

接受红包的用户

用户在wxappid下的openid

付款金额 total_amount 1000 int 付款金额,单位分
红包发放总人数 total_num 1 int

红包发放总人数

total_num=1

红包祝福语 wishing 感谢您参加猜灯谜活动,祝您元宵节快乐! String(128) 红包祝福语
Ip地址 client_ip 192.168.0.1 String(15) 调用接口的机器Ip地址
活动名称 act_name 猜灯谜抢红包活动 String(32) 活动名称
备注 remark 猜越多得越多,快来抢! String(256) 备注信息

数据示例:

<xml>
<xml> 
  <sign><![CDATA[E1EE61A91C8E90F299DE6AE075D60A2D]]></sign>  
  <mch_billno><![CDATA[0010010404201466666670000046545]]></mch_billno>  
  <mch_id><![CDATA[888]]></mch_id>  
  <wxappid><![CDATA[wxcbda96de0b165486]]></wxappid>  
  <send_name><![CDATA[send_name]]></send_name>  
  <re_openid><![CDATA[onqOjjmM1tad-3ROpncN-yUfa6uI]]></re_openid>  
  <total_amount><![CDATA[200]]></total_amount>  
  <total_num><![CDATA[1]]></total_num>  
  <wishing><![CDATA[恭喜发财]]></wishing>  
  <client_ip><![CDATA[127.0.0.1]]></client_ip>  
  <act_name><![CDATA[新年红包]]></act_name>  
  <remark><![CDATA[新年红包]]></remark>  
  <nonce_str><![CDATA[50780e0cca98c8c8e814883e5caa672e]]></nonce_str> 
</xml>

上面是接口及输入参数的说明,一般情况下,我们需要根据这些来决定如何实现C#代码的封装,首先我们来定义我们需要的接口和类,如下所示。

通过分析上面的接口说明,我们可以发现,其中接口有部分是固定的常规参数,也就是一般公众号或者企业号的身份信息,有部分是业务参数,因此我们把它们分别分离出来,这样有利于我们对接口的封装和使用,那些常规的参数我们通过公众号身份获取就可以了,业务信息,我们可以定义一个实体类来进行数据的存储交换即可。

对应上图的固定的常规参数,在接口说明中如下所示。

因此,这些信息我们从账号里面设置及获取即可,我们可以在管理后台对它们进行配置,然后在代码逻辑里面取出来使用即可。

根据上面的介绍,我们可以定义红包接口代码如下所示。

    /// <summary>
    ///微信红包(摇一摇红包)操作API/// </summary>
    public interfaceILotteryApi
{
/// <summary> ///用于企业向微信用户个人发现金红包。需要商户证书///目前支持向指定微信用户的openid发放指定金额红包。/// </summary> /// <returns></returns> SendRedPackResult SendRedPack(SendRedPackJson json);

其中的 SendRedPackJson 是我们变化的业务参数,我们定义了一个类来进行信息的承载,方便想接口传递信息。

    /// <summary>
    ///现金红包和裂变红包的基础信息/// </summary>
    public classBaseRedPackJson
{
/// <summary> ///接受红包的用户///用户openid/// </summary> public string re_openid { get; set; }/// <summary> ///付款金额,单位分/// </summary> public int total_amount { get; set; }/// <summary> ///红包发放总人数/// </summary> public int total_num { get; set; }/// <summary> ///红包祝福语/// </summary> public string wishing { get; set; }/// <summary> ///活动名称/// </summary> public string act_name { get; set; }/// <summary> ///备注信息/// </summary> public string remark { get; set; }
}
/// <summary> ///发送红包的数据信息/// </summary> public classSendRedPackJson :BaseRedPackJson
{
/// <summary> ///调用接口的机器Ip地址/// </summary> public string client_ip { get; set; }publicSendRedPackJson()
{
this.total_num = 1;//红包发放总人数 }
}

根据上面参数的定义,我们在现金红包的接口实现里面,具体代码如下所示,里面的逻辑内容,主要就是传入常规参数和业务参数两部分,然后调用接口的地址进行数据的提交(POST),获取返回结果并进行解析即可。

        /// <summary>
        ///用于企业向微信用户个人发现金红包。需要商户证书///目前支持向指定微信用户的openid发放指定金额红包。/// </summary>
        /// <returns></returns>
        publicSendRedPackResult SendRedPack(SendRedPackJson json)
{
CheckAccount();
//检查AccountInfo的对象属性值//加入常规的参数 WxPayData data = newWxPayData();
data.SetValue(
"wxappid", AccountInfo.UniteAppId);//公众账号appid data.SetValue("mch_id", AccountInfo.MchID);//商户号 data.SetValue("nonce_str", data.GenerateNonceStr());//随机字符串 data.SetValue("send_name", AccountInfo.Name);//红包发送者名称//商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。//接口根据商户订单号支持重入,如出现超时可再调用。 data.SetValue("mch_billno", data.GenerateOutTradeNo(AccountInfo.MchID));

data.SetValue(
"re_openid", json.re_openid);
data.SetValue(
"total_amount", json.total_amount);
data.SetValue(
"total_num", json.total_num);
data.SetValue(
"wishing", json.wishing);
data.SetValue(
"client_ip", json.client_ip);
data.SetValue(
"act_name", json.act_name);
data.SetValue(
"remark", json.remark);

data.SetValue(
"sign", data.MakeSign(AccountInfo.PayAPIKey));//最后生成签名 var url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";return Helper.GetPayResultWithCert<SendRedPackResult>(data, url, AccountInfo.CertPath, AccountInfo.CertPassword);
}

其中发送红包操作是需要证书的,因此需要添加对应的证书,证书是从微信的商户平台上进行下载的。

在商户后台的【API安全】项目上下载证书供我们开发环境使用。

下载证书后,在Windows环境,我们一般需要双击安装,输入所需的商户号作为密码即可。

在代码里面,我们可以使用证书类进行添加

            HttpHelper helper = newHttpHelper();

helper.ClientCertificates
= new X509CertificateCollection();
certPath = Path.Combine(System.Environment.CurrentDirectory, certPath);
helper.ClientCertificates.Add(new X509Certificate2(certPath, certPassword));
string response = helper.GetHtml(url, xml, true);

3、微信红包的使用结果

例如,我们在测试例子里面调用代码如下所示。

                //现金红包
                SendRedPackJson packJson = newSendRedPackJson()
{
act_name
= "恭喜发财",
client_ip
=NetworkUtil.GetIPAddress(),
remark
= "企业红包",
wishing
= "企业红包",
total_amount
= 100,
total_num
= 1,
re_openid
= tosendOpenId //发送给用户的OpenID };var result =hbApi.SendRedPack(packJson);var message = string.Format("企业发送红包:{0} {1}", result.Success ? "成功" : "失败", result.Message);
Console.WriteLine(message);
Console.WriteLine(result.ToJson());

其中的hbApi是上面接口的构造,如下代码所示。

 AccountInfo  accountInfo = newAccountInfo()
{
Name
= this.SendName,
AppID
= this.AppId,
AppSecret
= this.AppSecret,
MchID
= this.MchID,
PayAPIKey
= this.PayAPIKey,
CertPath
= this.CertPath,
CertPassword
= this.CertPassword,
PayNotifyUrl
= this.PayNotifyUrl
};
ILotteryApi hbApi
= new LotteryApi(accountInfo);

成功调用后,我们可以在公众号的对话里面看到红包的信息结果,如下是整个红包发送及拆开的过程。



如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(36)--微信卡劵管理的封装操作

C#开发微信门户及应用(35)--微信支付之企业付款封装操作

C#开发微信门户及应用(34)--微信裂变红包

C#开发微信门户及应用(33)--微信现金红包的封装及使用

C#开发微信门户及应用(32)--微信支付接入和API封装使用

C#开发微信门户及应用(31)--微信语义理解接口的实现和处理

C#开发微信门户及应用(30)--消息的群发处理和预览功能

C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现

C#开发微信门户及应用(27)-公众号模板消息管理

C#开发微信门户及应用(26)-公众号微信素材管理

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

C#开发微信门户及应用(24)-微信小店货架信息管理

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

C#开发微信门户及应用(22)-微信小店的开发和使用

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答


C#开发微信门户及应用(2)--微信消息的处理和应答


C#开发微信门户及应用(1)--开始使用微信接口

在上篇随笔《
C#开发微信门户及应用(33)--微信现金红包的封装及使用
》介绍了普通现金红包的封装和使用,这种红包只能单独一次发给一个人,用户获取了红包就完成了,如果我们让用户收到红包后,可以继续发送给多个用户,让他们获得固定或者随机金额的操作,这种称之为裂变红包。本篇随笔继续上面的主题,继续介绍其中裂变红包的C#代码封装和使用操作。

1、裂变红包介绍

领到企业裂变红包的用户,可以继续帮好友领红包,将企业红包以裂变形式散播给更多好友,赋予营销更多的趣味和愉悦!裂变红包不断强化企业品牌效应并形成裂变性传播,是品牌宣传的营销利器。企业只需要指定一组红包的个数和总金额,由微信支付计算出各红包金额,简单方便。

微信支付裂变红包向微信支付商户开发,具体能力如下:

1、商户调用接口时,通过指定发送金额以及指定一位发送对象的方式发放一组裂变红包

2、指定发送对象领取到红包后,资金直接进入微信零钱,带给用户微信支付原生的流畅体验

3、指定发送对象能够将组合中的剩余红包分享给好友,好友可继续领取,形成传播效应,放大企业品牌价值

裂变红包的总体处理过程及接口和普通的现金红包差别不太大,理解了现金红包,对裂变红包的封装和使用过程应该是很容易的事情。

裂变红包接口,用于企业向微信用户个人发裂变红包

目前支持向指定微信用户的openid发放指定金额裂变红包。(获取openid参见微信公众平台开发者文档:
网页授权获取用户基本信息

接口参数与用户领用实际效果对应关系如下:

接口调用请求说明

请求Url https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack
是否需要证书 是(证书及使用说明详见
商户证书
请求方式 POST

和前面介绍的现金红包一样,我们也可以把裂变红包的参数分为两个部分,一个是常规参数,一个是业务参数,如下所示。

2、裂变红包的C#代码封装

从上面的传递参数我们可以把它分两部分,当我们传入下面的参数后

<xml> 
   <sign><![CDATA[E1EE61A91C8E90F299DE6AE075D60A2D]]></sign>
   <mch_billno><![CDATA[0010010404201466666670000046545]]></mch_billno>
   <mch_id><![CDATA[1000888888]]></mch_id>
   <wxappid><![CDATA[wxcbda96de0b165486]]></wxappid> 
   <send_name><![CDATA[send_name]]></send_name> 
   <re_openid><![CDATA[onqOjjmM1tad-3ROpncN-yUfa6uI]]></re_openid> 
   <total_amount><![CDATA[600]]></total_amount> 
   <amt_type><![CDATA[ALL_RAND]]></amt_type> 
   <total_num><![CDATA[3]]></total_num> 
   <wishing><![CDATA[恭喜发财]]></wishing>
   <act_name><![CDATA[新年红包]]></act_name> 
   <remark><![CDATA[新年红包]]></remark> 
   <nonce_str><![CDATA[50780e0cca98c8c8e814883e5caa672e]]></nonce_str> 
</xml>

上面的传入参数提交给地址:
https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack
后,成功后会返回下面的XML。

<xml> 
   <return_code><![CDATA[SUCCESS]]></return_code> 
   <return_msg><![CDATA[发放成功.]]></return_msg> 
   <result_code><![CDATA[SUCCESS]]></result_code> 
   <err_code><![CDATA[0]]></err_code> 
   <err_code_des><![CDATA[发放成功.]]></err_code_des> 
   <mch_billno><![CDATA[0010010404201466666670000046545]]></mch_billno> 
   <mch_id>10010404</mch_id> 
   <wxappid><![CDATA[wx6fa7e3bab7e15415]]></wxappid> 
   <re_openid><![CDATA[onqOjjmM1tad-3ROpncN-yUfa6uI]]></re_openid> 
   <total_amount>3</total_amount> 
   <send_time><![CDATA[20150227091010]]></send_time>
   <send_listid><![CDATA[1000000000201502270093647546]]></send_listid>
</xml> 

如果失败的时候,那么返回的XML就少了很多信息,如下所示。

<xml>
   <return_code><![CDATA[FAIL]]></return_code>
   <return_msg><![CDATA[系统繁忙,请稍后再试.]]></return_msg>
   <result_code><![CDATA[FAIL]]></result_code>
   <err_code><![CDATA[268458547]]></err_code>
   <err_code_des><![CDATA[系统繁忙,请稍后再试.]]></err_code_des>
   <mch_billno><![CDATA[0010010404201466666670000046542]]></mch_billno>
   <mch_id>10010404</mch_id>
   <wxappid><![CDATA[wx6fa7e3bab7e15415]]></wxappid>
   <re_openid><![CDATA[onqOjjmM1tad-3ROpncN-yUfa6uI]]></re_openid>
   <total_amount>3</total_amount>
</xml>

根据这些规则,我们对裂变红包的接口和实现代码进行了封装,以便更好的使用。

例如我们根据普通现金红包和裂变红包的实体类相似的特点,我们设计了几个类来存储信息,传入参数的对象关系如下图所示。

同样,对于返回的发送红包结果,由于需要考虑在错误的时候的信息返回和成功的信息返回,我们设计了返回结果类的关系如下所示。

根据上面的设计思路,我们设计的类代码如下所示。

    /// <summary>
    ///发送裂变红包的数据信息/// </summary>
    public classSendGroupRedPackJson : BaseRedPackJson
{
/// <summary> ///红包金额设置方式///ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额/// </summary> public string amt_type { get; set; }publicSendGroupRedPackJson()
{
this.amt_type = "ALL_RAND";
}
}

结果对象类的代码如下所示。

    /// <summary>
    ///发送红包的返回结果/// </summary>
    public classSendRedPackResult : PayResult
{
/// <summary> ///商户订单号/// </summary> public string mch_billno { get; set; }/// <summary> ///商户appid,接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。/// </summary> public string wxappid { get; set; }/// <summary> ///接受收红包的用户///用户在wxappid下的openid/// </summary> public string re_openid { get; set; }/// <summary> ///付款金额,单位分/// </summary> public int total_amount { get; set; }/// <summary> ///红包发送时间/// </summary> public string send_time { get; set; }/// <summary> ///红包订单的微信单号/// </summary> public string send_listid { get; set; }
}

这样,有了这些对象的代码,我们根据接口的说明,依旧遵循上篇随笔介绍的接口设计方式,实现裂变红包的代码处理。

    /// <summary>
    ///微信红包、摇一摇红包的操作API接口/// </summary>
    public interfaceILotteryApi
{
/// <summary> ///用于企业向微信用户个人发现金红包。需要商户证书///目前支持向指定微信用户的openid发放指定金额红包。/// </summary> /// <returns></returns> SendRedPackResult SendRedPack(SendRedPackJson json);/// <summary> ///用于企业向微信用户个人发裂变红包。需要商户证书///目前支持向指定微信用户的openid发放指定金额裂变红包。/// </summary> /// <returns></returns> SendRedPackResult SendGroupRedPack(SendGroupRedPackJson json);

.............

然后实现它们的接口代码如下所示。

    /// <summary>
    ///微信红包管理类/// </summary>
    public classLotteryApi : ILotteryApi
{
#region 裂变红包说明 //微信支付裂变红包向微信支付商户开发,具体能力如下://1、商户调用接口时,通过指定发送金额以及指定一位发送对象的方式发放一组裂变红包//2、指定发送对象领取到红包后,资金直接进入微信零钱,带给用户微信支付原生的流畅体验//3、指定发送对象能够将组合中的剩余红包分享给好友,好友可继续领取,形成传播效应,放大企业品牌价值 #endregion /// <summary> ///用于企业向微信用户个人发裂变红包。需要商户证书///目前支持向指定微信用户的openid发放指定金额裂变红包。/// </summary> /// <returns></returns> publicSendRedPackResult SendGroupRedPack(SendGroupRedPackJson json)
{
CheckAccount();
//检查AccountInfo的对象属性值 WxPayData data= newWxPayData();
data.SetValue(
"wxappid", AccountInfo.UniteAppId);//公众账号appid data.SetValue("mch_id", AccountInfo.MchID);//商户号 data.SetValue("nonce_str", data.GenerateNonceStr());//随机字符串 data.SetValue("send_name", AccountInfo.Name);//红包发送者名称//商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。//接口根据商户订单号支持重入,如出现超时可再调用。 data.SetValue("mch_billno", data.GenerateOutTradeNo(AccountInfo.MchID));

data.SetValue(
"re_openid", json.re_openid);//接收红包的种子用户(首个用户) data.SetValue("total_amount", json.total_amount);//红包发放总金额,即一组红包金额总和,包括分享者的红包和裂变的红包,单位分 data.SetValue("total_num", json.total_num);//红包发放总人数,即总共有多少人可以领到该组红包(包括分享者) data.SetValue("wishing", json.wishing);//红包祝福语 data.SetValue("act_name", json.act_name);
data.SetValue(
"remark", json.remark);
data.SetValue(
"amt_type", json.amt_type);

data.SetValue(
"sign", data.MakeSign(AccountInfo.PayAPIKey));//最后生成签名 var url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack";return Helper.GetPayResultWithCert<SendRedPackResult>(data, url, AccountInfo.CertPath, AccountInfo.CertPassword);
}

...............

3、裂变红包接口的调用及效果展示

上面我们对裂变红包进行了接口的封装,如果我们需要发送裂变红包的时候,直接调用上面的接口即可实现红包发送的操作了。

例如调用接口的代码如下所示。

                //裂变红包
                SendGroupRedPackJson groupJson = newSendGroupRedPackJson()
{
act_name
= "恭喜发财",
remark
= "企业红包",
wishing
= "企业红包",
total_amount
= 600,
total_num
= 4,
re_openid
= tosendOpenId, //发送给用户的OpenID };var groupResult =hbApi.SendGroupRedPack(groupJson);
message
= string.Format("企业发送裂变红包:{0} {1}", groupResult.Success ? "成功" : "失败", groupResult.Message);
Console.WriteLine(message);
Console.WriteLine(groupResult.ToJson());

其中hbApi的对象初始化代码如下所示

ILotteryApi hbApi = new LotteryApi(accountInfo);

最后我们可以在微信上看到发过来的裂变红包。



由于发送红包的金额都需要大于1块,那么如果我们发送的金额大一些,那么每个人拆到的红包金额是不等的,如下图所示。

以上就是关于现金红包和裂变红包的接口封装和使用过程,希望对你使用微信开发有所帮助,感谢支持。

如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(36)--微信卡劵管理的封装操作

C#开发微信门户及应用(35)--微信支付之企业付款封装操作

C#开发微信门户及应用(34)--微信裂变红包

C#开发微信门户及应用(33)--微信现金红包的封装及使用

C#开发微信门户及应用(32)--微信支付接入和API封装使用

C#开发微信门户及应用(31)--微信语义理解接口的实现和处理

C#开发微信门户及应用(30)--消息的群发处理和预览功能

C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现

C#开发微信门户及应用(27)-公众号模板消息管理

C#开发微信门户及应用(26)-公众号微信素材管理

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

C#开发微信门户及应用(24)-微信小店货架信息管理

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

C#开发微信门户及应用(22)-微信小店的开发和使用

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答


C#开发微信门户及应用(2)--微信消息的处理和应答


C#开发微信门户及应用(1)--开始使用微信接口

在前面几篇随笔,都是介绍微信支付及红包相关的内容,其实支付部分的内容还有很多,例如企业付款、公众号支付或刷卡支付、摇一摇红包、代金券等方面的内容,这些都是微信接口支持的内容,本篇继续微信支付这一主题,继续介绍微信支付中的企业付款的操作,实现对企业付款API的接口C#代码的封装,以及测试效果。

1、企业付款的介绍

所谓企业付款指的是,在功能开放后诸如保险行业的客户理赔、退保、商品退款、发放征集活动奖金、抽奖互动等操作都可以通过企业付款完成。而此前,微信支付只能提供客户向企业单向付款。

商户如果需要给用户付款,可以直接将钱打入用户的“微信零钱”中,微信支付将做零钱入账消息通知,零钱收支明细会展示相应记录。针对无零钱账户的历史客户端版本,资金将进入用户的红包账户,微信支付无消息通知用户,企业可选择自行触达用户。

通过认证的企业号可以开通微信支付功能。通过认证的企业号可以管理平台的“服务中心”的“微信支付”入口里进行微信支付功能申请。而开通微信支付功能后,企业号将拥有两项功能:收款和付款。如果是用户号对企业号付款,款项将会进入企业号所关联的商户号中。同时,企业号可以通过微信红包或微信转账的形式对用户号付款。

企业付款,提供企业向用户付款的功能,支持企业通过API接口付款,或通过微信支付商户平台网页功能操作付款。

涉及资金操作的功能,安全性要求较高,需要操作员安装证书(商户平台-账户设置-密码安全-操作证书);通过API或网页操作,付款至目标用户(企业可根据APPID+OpenID锁定目标用户)。针对已实名认证的用户,微信支付可提供校验真实姓名一致性的可选功能。

企业付款提示:

◆ 给同一个实名用户付款,单笔单日限额2W/2W

◆ 给同一个非实名用户付款,单笔单日限额2000/2000

◆ 一个商户同一日付款总额限额100W

◆仅支持商户号已绑定的APPID;

◆针对付款的目标用户,已微信支付实名认证的用户可提供校验真实姓名的功能,未实名认证的用户无法校验,企业可根据自身业务的安全级别选择验证类型;

◆付款金额必须小于或等于商户当前可用余额的金额;

◆ 已付款的记录,企业可通过企业付款查询查看相应数据。

2、企业付款API

企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。
比如目前的保险行业向客户退保、给付、理赔。

企业付款将使用商户的可用余额,需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”进行操作。

接口地址

接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers

是否需要证书

请求需要双向证书。 详见
证书使用

请求参数

提交数据示例:

<xml>
<mch_appid>wxe062425f740c30d8</mch_appid>
<mchid>10000098</mchid>
<nonce_str>3PG2J4ILTKCH16CQ2502SI8ZNMTM67VS</nonce_str>
<partner_trade_no>100000982014120919616</partner_trade_no>
<openid>ohO4Gt7wVPxIT1A9GjFaMYMiZY1s</openid>
<check_name>OPTION_CHECK</check_name>
<re_user_name>张三</re_user_name>
<amount>100</amount>
<desc>节日快乐!</desc>
<spbill_create_ip>10.2.3.10</spbill_create_ip>
<sign>C97BDBACF37622775366F38B629F45E3</sign>
</xml>

成功返回的数据示例:

<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[]]></return_msg>
<mch_appid><![CDATA[wxec38b8ff840bd989]]></mch_appid>
<mchid><![CDATA[10013274]]></mchid>
<device_info><![CDATA[]]></device_info>
<nonce_str><![CDATA[lxuDzMnRjpcXzxLx0q]]></nonce_str>
<result_code><![CDATA[SUCCESS]]></result_code>
<partner_trade_no><![CDATA[10013574201505191526582441]]></partner_trade_no>
<payment_no><![CDATA[1000018301201505190181489473]]></payment_no>
<payment_time><![CDATA[2015-05-19 15:26:59]]></payment_time>
</xml>

根据上面的接口描述,以及输入,返回参数,我们可以构建对应的C#代码的接口

根据上面的接口定义,我们可以定义接口信息如下所示

    /// <summary>
    ///微信支付接口/// </summary>
    public interfaceITenPayApi
{
/// <summary> ///企业付款(请求需要双向证书)///企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,///针对部分有开发能力的商户,提供通过API完成企业付款的功能。 比如目前的保险行业向客户退保、给付、理赔。///企业付款将使用商户的可用余额,需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”进行操作。https://pay.weixin.qq.com/ ///注意:与商户微信支付收款资金并非同一账户,需要单独充值。/// </summary> /// <param name="json">企业支付数据</param> /// <returns></returns> CorpPayResult CorpPay(CorpPayJson json);

..............

其中上面的CorpPayJson 和CorpPayResult 分别是传入参数和获取的结果内容,两者的对象信息根据参数进行定义即可。

    /// <summary>
    ///企业付款的数据信息/// </summary>
    public classCorpPayJson
{
publicCorpPayJson()
{
this.check_name = "FORCE_CHECK";
}
/// <summary> ///微信支付分配的终端设备号/// </summary> public string device_info { get; set; }/// <summary> ///用户openid/// </summary> public string openid { get; set; }/// <summary> ///校验用户姓名选项,可以使用 PayCheckName枚举对象获取名称///NO_CHECK:不校验真实姓名///FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)///OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)/// </summary> public string check_name { get; set; }/// <summary> ///收款用户真实姓名。///如果check_name设置为FORCE_CHECK或OPTION_CHECK,则必填用户真实姓名/// </summary> public string re_user_name { get; set; }/// <summary> ///企业付款金额,单位为分/// </summary> public int amount { get; set; }/// <summary> ///企业付款操作说明信息。必填。/// </summary> public string desc { get; set; }/// <summary> ///调用接口的机器Ip地址/// </summary> public string spbill_create_ip { get; set; }
}
    /// <summary>
    ///企业付款操作的返回结果/// </summary>
    public classCorpPayResult : PayResult
{
/// <summary> ///微信分配的公众账号ID(企业号corpid即为此appId)/// </summary> public string mch_appid { get; set; }/// <summary> ///微信支付分配的终端设备号/// </summary> public string device_info { get; set; }/// <summary> ///商户使用查询API填写的单号的原路返回./// </summary> public string partner_trade_no { get; set; }/// <summary> ///企业付款成功,返回的微信订单号/// </summary> public string payment_no { get; set; }/// <summary> ///企业付款成功时间/// </summary> public string payment_time { get; set; }
}

企业付款的API实现和前面两种红包的处理方式 差不多,一个是传入常规的账号信息,一个是传入业务参数,然后提交获取结果即可,具体代码如下所示。

        /// <summary>
        ///企业付款(请求需要双向证书)///企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,///针对部分有开发能力的商户,提供通过API完成企业付款的功能。 比如目前的保险行业向客户退保、给付、理赔。///企业付款将使用商户的可用余额,需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”进行操作。https://pay.weixin.qq.com/ 
        ///注意:与商户微信支付收款资金并非同一账户,需要单独充值。/// </summary>
        /// <param name="json">企业支付数据</param>
        /// <returns></returns>
        publicCorpPayResult CorpPay(CorpPayJson json)
{
CheckAccount();
//检查AccountInfo的对象属性值 WxPayData data= newWxPayData();
data.SetValue(
"mch_appid", AccountInfo.UniteAppId);//公众账号appid, 注意是mch_appid,而非wxappid data.SetValue("mchid", AccountInfo.MchID);//商户号, 注意是mchid而非mch_id data.SetValue("nonce_str", data.GenerateNonceStr());//随机字符串 data.SetValue("spbill_create_ip", NetworkUtil.GetIPAddress());//终端ip data.SetValue("partner_trade_no", data.GenerateOutTradeNo(AccountInfo.MchID));//随机字符串 data.SetValue("device_info", json.device_info);//终端ip data.SetValue("openid", json.openid);
data.SetValue(
"check_name", json.check_name);
data.SetValue(
"re_user_name", json.re_user_name);
data.SetValue(
"amount", json.amount);
data.SetValue(
"desc", json.desc);

data.SetValue(
"sign", data.MakeSign(AccountInfo.PayAPIKey));//最后生成签名 var url = string.Format("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers");return Helper.GetPayResultWithCert<CorpPayResult>(data, url, AccountInfo.CertPath, AccountInfo.CertPassword);
}

接口定义及实现好后,我们可以在项目中对API进行调用测试,具体代码如下所示

CorpPayJson json = newCorpPayJson()
{
amount
= 100,
check_name
=PayCheckName.FORCE_CHECK.ToString(),
desc
= "测试退款",
openid
= this.openId,
device_info
= "",
re_user_name
= "伍华聪",
spbill_create_ip
=NetworkUtil.GetIPAddress()
};
var result =api.CorpPay(json);var message = string.Format("企业直接付款:{0} {1}", result.Success ? "成功" : "失败", result.Message);
Console.WriteLine(message);
Console.WriteLine(result.ToJson());

付款操作成功后,我们可以看到这个钱是直接到用户钱包里面去的,而且我们也可以在商户后台进行记录的查看,可以看到对应的记录。

如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(36)--微信卡劵管理的封装操作

C#开发微信门户及应用(35)--微信支付之企业付款封装操作

C#开发微信门户及应用(34)--微信裂变红包

C#开发微信门户及应用(33)--微信现金红包的封装及使用

C#开发微信门户及应用(32)--微信支付接入和API封装使用

C#开发微信门户及应用(31)--微信语义理解接口的实现和处理

C#开发微信门户及应用(30)--消息的群发处理和预览功能

C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现

C#开发微信门户及应用(27)-公众号模板消息管理

C#开发微信门户及应用(26)-公众号微信素材管理

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

C#开发微信门户及应用(24)-微信小店货架信息管理

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

C#开发微信门户及应用(22)-微信小店的开发和使用

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答


C#开发微信门户及应用(2)--微信消息的处理和应答


C#开发微信门户及应用(1)--开始使用微信接口