zoukankan      html  css  js  c++  java
  • 公众号微信支付

    1、概要

         公众号是以微信用户的一个联系人形式存在的,支付是微信服务号的核心一环。

         本篇主要介绍微信支付这一功能,避免大家再跳微信支付的坑。

    1.1 关于Magicodes.WeChat.SDK

          MAGICODES.WECHAT.SDK为心莱团队封装的轻量级微信SDK,现已全部开源,开源库地址为:https://github.com/xin-lai/Magicodes.WeChat.SDK

          更多介绍,请关注后续博客。

    2、微信公众号支付

         用户已有商城网址,用户通过微信消息、微信扫描二维码、微信自定义菜单等操作在微信内打开网页时,可以调用微信支付完成下单购买流程。

    2.1    支付流程

      2.1.1    微信网页支付流程

          

    备注:实际流程其实非常简单

      ① 用户支付之前,程序生成订单并调用统一下单API()

      ② 微信系统返回预付单信息

      ③ 根据信息生成JSAPI页面调用的支付参数并签名,jssdk调用

      ④ 用户支付,jsapi向微信系统发送请求

      ⑤ 微信系统返回支付结果给用户,同时异步发送结果给程序后台(程序没有收到通知,可以调用查询接口)

      ⑥ 支付完成,用户界面根据结果做相关页面跳转或提示处理,程序后台根据通知做订单状态变更等逻辑处理。

    2.1.2    刷卡支付

        后续更新

    2.1.3    扫码支付

         后续更新

    2.1.4    app支付

         后续更新 

    2.2    注意事项

    2.3    开发实践 

       2.3.1    开发配置

       1、设置测试目录

    在微信公众平台设置。支付测试状态下,设置测试目录,测试人的微信号添加到白名单,发起支付的页面目录必须与设置的精确匹配。并将支付链接发到对应的公众号会话窗口中才能正常发起支付测试。注意正式目录一定不能与测试目录设置成一样,否则支付会出错。

           

    友情提示:如果是使用测试目录的地址,一定要记得把个人测试微信号添加到白名单。

       2、设置正式支付目录

    根据图中栏目顺序进入修改栏目,勾选JSAPI网页支付开通该权限,并配置好支付授权目录,该目录必须是发起支付的页面的精确目录,子目录下无法正常调用支付。具体界面如图:

          

    友情提示:注意红色框框里面的说明,一不小心会很容易进坑的。

       2.3.2    开发程序

                直接看代码吧

    微信支付业务类

      1 /// <summary>
      2 /// 微信支付接口,官方API:https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course2_tmpl&lang=zh_CN&token=25857919#4
      3 
      4     /// </summary>
      5     public class TenPayV3 : PayBase
      6 
      7     {
      8         public UnifiedorderResult Unifiedorder(UnifiedorderRequest model)
      9 
     10         {
     11 
     12             var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
     13 
     14  
     15 
     16  
     17 
     18             UnifiedorderResult result = null;
     19 
     20  
     21 
     22             model.AppId = WeiChatConfig.AppId;
     23 
     24             model.MchId = PayConfig.MchId;
     25 
     26             if (model.NotifyUrl == null)
     27 
     28                 model.NotifyUrl = PayConfig.Notify;
     29 
     30             Dictionary<string, string> dictionary = PayUtil.GetAuthors<UnifiedorderRequest>(model);
     31 
     32             model.Sign = PayUtil.CreateMd5Sign(dictionary, PayConfig.TenPayKey);//生成Sign
     33 
     34             Dictionary<string, string> dict = PayUtil.GetAuthors<UnifiedorderRequest>(model);
     35 
     36             result = PostXML<UnifiedorderResult>(url, model);
     37 
     38             return result;
     39 
     40         }      
     41 
     42  
     43 
     44         /// <summary>
     45 
     46         /// 订单查询接口
     47 
     48         /// </summary>
     49 
     50         /// <param name="data"></param>
     51 
     52         /// <returns></returns>
     53 
     54         public static string OrderQuery(string data)
     55 
     56         {
     57 
     58             var urlFormat = "https://api.mch.weixin.qq.com/pay/orderquery";
     59 
     60  
     61 
     62             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
     63 
     64             using (MemoryStream ms = new MemoryStream())
     65 
     66             {
     67 
     68                 ms.Write(formDataBytes, 0, formDataBytes.Length);
     69 
     70                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
     71 
     72                 return RequestUtility.HttpPost(urlFormat, null, ms);
     73 
     74             }
     75 
     76         }
     77 
     78  
     79 
     80         /// <summary>
     81 
     82         /// 关闭订单接口
     83 
     84         /// </summary>
     85 
     86         /// <param name="data">关闭订单需要post的xml数据</param>
     87 
     88         /// <returns></returns>
     89 
     90         public static string CloseOrder(string data)
     91 
     92         {
     93 
     94             var urlFormat = "https://api.mch.weixin.qq.com/pay/closeorder";
     95 
     96  
     97 
     98             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
     99 
    100             using (MemoryStream ms = new MemoryStream())
    101 
    102             {
    103 
    104                 ms.Write(formDataBytes, 0, formDataBytes.Length);
    105 
    106                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
    107 
    108                 return RequestUtility.HttpPost(urlFormat, null, ms);
    109 
    110             }
    111 
    112         }
    113 
    114  
    115 
    116       
    117 
    118  
    119 
    120         /// <summary>
    121 
    122         /// 退款查询接口
    123 
    124         /// </summary>
    125 
    126         /// <param name="data"></param>
    127 
    128         /// <returns></returns>
    129 
    130         public static string RefundQuery(string data)
    131 
    132         {
    133 
    134             var urlFormat = "https://api.mch.weixin.qq.com/pay/refundquery";
    135 
    136  
    137 
    138             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
    139 
    140             using (MemoryStream ms = new MemoryStream())
    141 
    142             {
    143 
    144                 ms.Write(formDataBytes, 0, formDataBytes.Length);
    145 
    146                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
    147 
    148                 return RequestUtility.HttpPost(urlFormat, null, ms);
    149 
    150             }
    151 
    152         }
    153 
    154  
    155 
    156         /// <summary>
    157 
    158         /// 对账单接口
    159 
    160         /// </summary>
    161 
    162         /// <param name="data"></param>
    163 
    164         /// <returns></returns>
    165 
    166         public static string DownloadBill(string data)
    167 
    168         {
    169 
    170             var urlFormat = "https://api.mch.weixin.qq.com/pay/downloadbill";
    171 
    172  
    173 
    174             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
    175 
    176             using (MemoryStream ms = new MemoryStream())
    177 
    178             {
    179 
    180                 ms.Write(formDataBytes, 0, formDataBytes.Length);
    181 
    182                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
    183 
    184                 return RequestUtility.HttpPost(urlFormat, null, ms);
    185 
    186             }
    187 
    188         }
    189 
    190  
    191 
    192         /// <summary>
    193 
    194         /// 短链接转换接口
    195 
    196         /// </summary>
    197 
    198         /// <param name="data"></param>
    199 
    200         /// <returns></returns>
    201 
    202         public static string ShortUrl(string data)
    203 
    204         {
    205 
    206             var urlFormat = "https://api.mch.weixin.qq.com/tools/shorturl";
    207 
    208  
    209 
    210             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
    211 
    212             using (MemoryStream ms = new MemoryStream())
    213 
    214             {
    215 
    216                 ms.Write(formDataBytes, 0, formDataBytes.Length);
    217 
    218                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
    219 
    220                 return RequestUtility.HttpPost(urlFormat, null, ms);
    221 
    222             }
    223 
    224         }
    225 
    226         /// <summary>
    227 
    228         ///
    229 
    230         /// </summary>
    231 
    232         /// <param name="page"></param>
    233 
    234         /// <returns></returns>
    235 
    236         public NotifyResult Notify(Stream inputStream)
    237 
    238         {
    239 
    240             NotifyResult result = null;
    241 
    242             string data = PayUtil.PostInput(inputStream);
    243 
    244             result = XmlHelper.DeserializeObject<NotifyResult>(data);
    245 
    246             return result;
    247 
    248         }
    249 
    250         /// <summary>
    251 
    252         /// 通知并返回处理XML
    253 
    254         /// </summary>
    255 
    256         /// <param name="inputStream">输入流</param>
    257 
    258         /// <param name="successAction">成功处理逻辑回调函数</param>
    259 
    260         /// <param name="failAction">失败处理逻辑回调函数</param>
    261 
    262         /// <param name="successMsg">成功返回消息</param>
    263 
    264         /// <param name="errorMsg">失败返回消息</param>
    265 
    266         /// <param name="isSync">是否异步执行相关处理逻辑</param>
    267 
    268         /// <returns></returns>
    269 
    270         public string NotifyAndReurnResult(Stream inputStream, Action<NotifyResult> successAction, Action<NotifyResult> failAction, string successMsg = "OK", string errorMsg = "FAIL", bool isSync = true)
    271 
    272         {
    273 
    274             var result = Notify(inputStream);
    275 
    276             var request = new NotifyRequest();
    277 
    278             request.ReturnCode = "FAIL";
    279 
    280             if (result.IsSuccess())
    281 
    282             {
    283 
    284                 if (isSync)
    285 
    286                     Task.Run(() => successAction(result));
    287 
    288                 else
    289 
    290                     successAction.Invoke(result);
    291 
    292                 //交易成功
    293 
    294                 request.ReturnCode = "SUCCESS";
    295 
    296                 request.ReturnMsg = successMsg;
    297 
    298                 return XmlHelper.SerializeObject(request);
    299 
    300             }
    301 
    302             else
    303 
    304             {
    305 
    306                 if (isSync)
    307 
    308                     Task.Run(() => failAction(result));
    309 
    310                 else
    311 
    312                     failAction.Invoke(result);
    313 
    314                 request.ReturnMsg = errorMsg;
    315 
    316                 return XmlHelper.SerializeObject(request);
    317 
    318             }
    319 
    320         }
    321 
    322 }
    323 
    324 }
    View Code

    把返回参数和请求参数,序列化成对象,方便我们在编写我们本身逻辑的时候调用

      1  [XmlRoot("xml")]
      2     [Serializable()]
      3     public class Result : PayResult
      4     {
      5         /// <summary>
      6         /// 微信分配的公众账号ID
      7         /// </summary>
      8         [XmlElement("appid")]
      9         public string AppId { get; set; }
     10         /// <summary>
     11         /// 微信支付分配的商户号
     12         /// </summary>
     13         [XmlElement("mch_id")]
     14         public string Mch_Id { get; set; }
     15         /// <summary>
     16         /// 微信支付分配的终端设备号
     17         /// </summary>
     18         [XmlElement("device_info")]
     19         public string Device_Info { get; set; }
     20         /// <summary>
     21         /// 随机字符串,不长于32 位
     22         /// </summary>
     23         [XmlElement("nonce_str")]
     24         public string NonceStr { get; set; }
     25         /// <summary>
     26         /// 签名
     27         /// </summary>
     28         [XmlElement("sign")]
     29         public string Sign { get; set; }
     30         /// <summary>
     31         /// SUCCESS/FAIL
     32         /// </summary>
     33         [XmlElement("result_code")]
     34         public string ResultCode { get; set; }
     35         [XmlElement("err_code")]
     36         public string ErrCode { get; set; }
     37         [XmlElement("err_code_des")]
     38         public string ErrCodeDes { get; set; }
     39     }
     40 
     41     [XmlRoot("xml")]
     42     [Serializable()]
     43     public class UnifiedorderResult : Result
     44     {
     45         /// <summary>
     46         /// 交易类型:JSAPI、NATIVE、APP
     47         /// </summary>
     48         [XmlElement("trade_type")]
     49         public string TradeType { get; set; }
     50         /// <summary>
     51         /// 微信生成的预支付ID,用于后续接口调用中使用
     52         /// </summary>
     53         [XmlElement("prepay_id")]
     54         public string PrepayId { get; set; }
     55         /// <summary>
     56         /// trade_type为NATIVE时有返回,此参数可直接生成二维码展示出来进行扫码支付
     57         /// </summary>
     58         [XmlElement("code_url")]
     59         public string CodeUrl { get; set; }
     60     }
     61 
     62 
     63     [XmlRoot("xml")]
     64     [Serializable()]
     65     public class UnifiedorderRequest
     66     {
     67         /// <summary>
     68         /// OpenId
     69         /// </summary>
     70         [XmlElement("openid")]
     71         public string OpenId { get; set; }
     72         /// <summary>
     73         /// 【不用填写】微信开放平台审核通过的应用APPID
     74         /// </summary>
     75         [XmlElement("appid")]
     76         public string AppId { get; set; }
     77         /// <summary>
     78         /// 【不用填写】微信支付分配的商户号
     79         /// </summary>
     80         [XmlElement("mch_id")]
     81         public string MchId { get; set; }
     82 
     83         /// <summary>
     84         /// 终端设备号(门店号或收银设备ID),默认请传"WEB"
     85         /// </summary>
     86         [XmlElement("device_info")]
     87         public string DeviceInfo { get; set; }
     88         /// <summary>
     89         /// 随机字符串,不长于32位
     90         /// </summary>
     91         [XmlElement("nonce_str")]
     92         public string NonceStr { get; set; }
     93         /// <summary>
     94         /// 【不用填写】签名
     95         /// </summary>
     96         [XmlElement("sign")]
     97         public string Sign { get; set; }
     98         /// <summary>
     99         /// 商品描述交易字段格式根据不同的应用场景按照以下格式: APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
    100         /// </summary>
    101         [XmlElement("body")]
    102         public string Body { get; set; }
    103         /// <summary>
    104         /// 商品名称明细列表
    105         /// </summary>
    106         [XmlElement("detail")]
    107         public string Detail { get; set; }
    108         /// <summary>
    109         /// 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    110         /// </summary>
    111         [XmlElement("attach")]
    112         public string Attach { get; set; }
    113         /// <summary>
    114         /// 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
    115         /// </summary>
    116         [XmlElement("out_trade_no")]
    117         public string OutTradeNo { get; set; }
    118         /// <summary>
    119         /// 符合ISO 4217标准的三位字母代码,默认人民币:CNY
    120         /// </summary>
    121         [XmlElement("fee_type")]
    122         public string FeeType { get; set; }
    123         /// <summary>
    124         /// 订单总金额,单位为分
    125         /// </summary>
    126         [XmlElement("total_fee")]
    127         public string TotalFee { get; set; }
    128         /// <summary>
    129         /// 用户端实际ip
    130         /// </summary>
    131         [XmlElement("spbill_create_ip")]
    132         public string SpbillCreateIp { get; set; }
    133         /// <summary>
    134         /// 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。
    135         /// </summary>
    136         [XmlElement("time_start")]
    137         public string TimeStart { get; set; }
    138         /// <summary>
    139         /// 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010
    140         /// </summary>
    141         [XmlElement("time_expire")]
    142         public string TimeExpire { get; set; }
    143         /// <summary>
    144         /// 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    145         /// </summary>
    146         [XmlElement("goods_tag")]
    147         public string GoodsTag { get; set; }
    148         /// <summary>
    149         /// 【不用填写】接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数
    150         /// </summary>
    151         [XmlElement("notify_url")]
    152         public string NotifyUrl { get; set; }
    153         /// <summary>
    154         /// 支付类型(JSAPI,NATIVE,APP)公众号内支付填JSAPI
    155         /// </summary>
    156         [XmlElement("trade_type")]
    157         public string TradeType { get; set; }
    158         /// <summary>
    159         /// no_credit--指定不能使用信用卡支付
    160         /// </summary>
    161         [XmlElement("limit_pay")]
    162         public string LimitPay { get; set; }
    163     }
    164     [XmlRoot("xml")]
    165     [Serializable()]
    166     public class NotifyResult : PayResult
    167     {
    168         /// <summary>
    169         /// 微信分配的公众账号ID(企业号corpid即为此appId)
    170         /// </summary>
    171         [XmlElement("appid")]
    172         public string AppId { get; set; }
    173         /// <summary>
    174         /// 微信支付分配的商户号
    175         /// </summary>
    176         [XmlElement("mch_id")]
    177         public string MchId { get; set; }
    178         /// <summary>
    179         /// 微信支付分配的终端设备号
    180         /// </summary>
    181         [XmlElement("device_info")]
    182         public string DeviceInfo { get; set; }
    183         /// <summary>
    184         /// 随机字符串,不长于32位
    185         /// </summary>
    186         [XmlElement("nonce_str")]
    187         public string NonceStr { get; set; }
    188         /// <summary>
    189         /// 签名
    190         /// </summary>
    191         [XmlElement("sign")]
    192         public string Sign { get; set; }
    193         /// <summary>
    194         /// 业务结果,SUCCESS/FAIL
    195         /// </summary>
    196         [XmlElement("result_code")]
    197         public string ResultCode { get; set; }
    198         /// <summary>
    199         /// 错误返回的信息描述
    200         /// </summary>
    201         [XmlElement("err_code")]
    202         public string ErrCode { get; set; }
    203         /// <summary>
    204         /// 错误返回的信息描述
    205         /// </summary>
    206         [XmlElement("err_code_des")]
    207         public string ErrCodeDes { get; set; }
    208         /// <summary>
    209         /// 用户在商户appid下的唯一标识
    210         /// </summary>
    211         [XmlElement("openid")]
    212         public string OpenId { get; set; }
    213         /// <summary>
    214         /// 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    215         /// </summary>
    216         [XmlElement("is_subscribe")]
    217         public string IsSubscribe { get; set; }
    218         /// <summary>
    219         /// 交易类型,JSAPI、NATIVE、APP
    220         /// </summary>
    221         [XmlElement("trade_type")]
    222         public string TradeType { get; set; }
    223         /// <summary>
    224         /// 银行类型,采用字符串类型的银行标识,银行类型见银行列表
    225         /// </summary>
    226         [XmlElement("bank_type")]
    227         public string BankType { get; set; }
    228         /// <summary>
    229         /// 订单总金额,单位为分
    230         /// </summary>
    231         [XmlElement("total_fee")]
    232         public string TotalFee { get; set; }
    233         /// <summary>
    234         /// 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额
    235         /// </summary>
    236         [XmlElement("settlement_total_fee")]
    237         public string SettlementTotalFee { get; set; }
    238         /// <summary>
    239         /// 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    240         /// </summary>
    241         [XmlElement("fee_type")]
    242         public string FeeType { get; set; }
    243         /// <summary>
    244         /// 货币类型现金支付金额订单现金支付金额,详见支付金额
    245         /// </summary>
    246         [XmlElement("cash_fee")]
    247         public string CashFee { get; set; }
    248         /// <summary>
    249         /// 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    250         /// </summary>
    251         [XmlElement("cash_fee_type")]
    252         public string CashFeeType { get; set; }
    253         /// <summary>
    254         /// 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额]
    255         /// </summary>
    256         [XmlElement("coupon_fee")]
    257         public string CouponFee { get; set; }
    258         /// <summary>
    259         /// 代金券使用数量
    260         /// </summary>
    261         [XmlElement("coupon_count")]
    262         public string CouponCount { get; set; }
    263         /// <summary>
    264         /// CASH--充值代金券         NO_CASH---非充值代金券        订单使用代金券时有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_$0
    265         /// </summary>
    266         [XmlElement("coupon_type_$n")]
    267         public string CouponTypeN { get; set; }
    268         /// <summary>
    269         /// 代金券ID,$n为下标,从0开始编号
    270         /// </summary>
    271         [XmlElement("coupon_id_$n")]
    272         public string CouponIdN { get; set; }
    273         /// <summary>
    274         /// 单个代金券支付金额,$n为下标,从0开始编号
    275         /// </summary>
    276         [XmlElement("coupon_fee_$n")]
    277         public string CouponFeeN { get; set; }
    278         /// <summary>
    279         /// 微信支付订单号
    280         /// </summary>
    281         [XmlElement("transaction_id")]
    282         public string TransactionId { get; set; }
    283         /// <summary>
    284         /// 商户系统的订单号,与请求一致
    285         /// </summary>
    286         [XmlElement("out_trade_no")]
    287         public string OutTradeNo { get; set; }
    288         /// <summary>
    289         /// 商家数据包,原样返回
    290         /// </summary>
    291         [XmlElement("attach")]
    292         public string Attach { get; set; }
    293         /// <summary>
    294         /// 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    295         /// </summary>
    296         [XmlElement("time_end")]
    297         public string TimeEnd { get; set; }
    298     }
    299     [XmlRoot("xml")]
    300     [Serializable()]
    301     public class NotifyRequest
    302     {
    303         /// <summary>
    304         /// SUCCESS/FAIL SUCCESS表示商户接收通知成功并校验成功
    305         /// </summary>
    306         [XmlElement("return_code")]
    307         public string ReturnCode { get; set; }
    308         /// <summary>
    309         /// 返回信息,如非空,为错误原因:签名失败 参数格式校验错误
    310         /// </summary>
    311         [XmlElement("return_msg")]
    312         public string ReturnMsg { get; set; }
    313     }
    View Code 

    这些地方是获取微信支付的配置信息,appid,machid,密钥等信息。由于我们项目本身涉及到了多租户功能,所以Key是租户Id,这里通过Key获取不能租户的配置信息,如果不要实现多租户,默认不填写就OK,然后我们也可以通过把配置信息封装成一个对象写死在这里。具体源码可以去http:githup.com/xin-lai下载。全部功能已开源。

     1 /// <summary>
     2 
     3         /// POST提交请求,返回ApiResult对象
     4 
     5         /// </summary>
     6 
     7         /// <typeparam name="T">ApiResult对象</typeparam>
     8 
     9         /// <param name="url">请求地址</param>
    10 
    11         /// <param name="obj">提交的数据对象</param>
    12 
    13         /// <returns>ApiResult对象</returns>
    14 
    15         protected T PostXML<T>(string url, object obj, Func<string, string> serializeStrFunc = null) where T : PayResult
    16 
    17         {
    18 
    19             var wr = new WeChatApiWebRequestHelper();
    20 
    21             string resultStr = null;
    22 
    23             var result = wr.HttpPost<T>(url, obj, out resultStr, serializeStrFunc, inputDataType: WebRequestDataTypes.XML, outDataType: WebRequestDataTypes.XML);
    24 
    25             if (result != null)
    26 
    27             {
    28 
    29                 result.DetailResult = resultStr;
    30 
    31             }
    32 
    33             return result;
    34 
    35         }
    36 
    37         /// <summary>
    38 
    39         /// POST提交请求,带证书,返回ApiResult对象
    40 
    41         /// </summary>
    42 
    43         /// <typeparam name="T">ApiResult对象</typeparam>
    44 
    45         /// <param name="url">请求地址</param>
    46 
    47         /// <param name="obj">提交的数据对象</param>
    48 
    49         /// <returns>ApiResult对象</returns>
    50 
    51         protected T PostXML<T>(string url, object obj, X509Certificate2 cer, Func<string, string> serializeStrFunc = null) where T : PayResult
    52 
    53         {
    54 
    55             var wr = new WeChatApiWebRequestHelper();
    56 
    57             string resultStr = null;
    58 
    59             var result = wr.HttpPost<T>(url, obj,cer, out resultStr, serializeStrFunc, inputDataType: WebRequestDataTypes.XML, outDataType: WebRequestDataTypes.XML);
    60 
    61             if (result != null)
    62 
    63             {
    64 
    65                 result.DetailResult = resultStr;
    66 
    67             }
    68 
    69             return result;
    70 
    71         }
    72 
    73     }
    View Code

    PayUtiy类,封装了一些公共方法

      1 public static class PayUtil
      2 
      3     {
      4 
      5         /// <summary>
      6 
      7         /// 随机生成Noncestr
      8 
      9         /// </summary>
     10 
     11         /// <returns></returns>
     12 
     13         public static string GetNoncestr()
     14 
     15         {
     16 
     17             Random random = new Random();
     18 
     19             return MD5UtilHelper.GetMD5(random.Next(1000).ToString(), "GBK");
     20 
     21         }
     22 
     23         /// <summary>
     24 
     25         /// 根据当前系统时间加随机序列来生成订单号
     26 
     27         /// </summary>
     28 
     29         /// <returns>订单号</returns>
     30 
     31         public static string GenerateOutTradeNo()
     32 
     33         {
     34 
     35             var ran = new Random();
     36 
     37             return string.Format("{0}{1}", UnixStamp(), ran.Next(999));
     38 
     39         }
     40 
     41         /// <summary>
     42 
     43         /// 获取时间戳
     44 
     45         /// </summary>
     46 
     47         /// <returns></returns>
     48 
     49         public static string GetTimestamp()
     50 
     51         {
     52 
     53             TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
     54 
     55             return Convert.ToInt64(ts.TotalSeconds).ToString();
     56 
     57         }
     58 
     59  
     60 
     61         /// <summary>
     62 
     63         /// 对字符串进行URL编码
     64 
     65         /// </summary>
     66 
     67         /// <param name="instr"></param>
     68 
     69         /// <param name="charset"></param>
     70 
     71         /// <returns></returns>
     72 
     73         public static string UrlEncode(string instr, string charset)
     74 
     75         {
     76 
     77             //return instr;
     78 
     79             if (instr == null || instr.Trim() == "")
     80 
     81                 return "";
     82 
     83             else
     84 
     85             {
     86 
     87                 string res;
     88 
     89  
     90 
     91                 try
     92 
     93                 {
     94 
     95                     res = System.Web.HttpUtility.UrlEncode(instr, Encoding.GetEncoding(charset));
     96 
     97  
     98 
     99                 }
    100 
    101                 catch (Exception ex)
    102 
    103                 {
    104 
    105                     res = System.Web.HttpUtility.UrlEncode(instr, Encoding.GetEncoding("GB2312"));
    106 
    107                 }
    108 
    109  
    110 
    111  
    112 
    113                 return res;
    114 
    115             }
    116 
    117         }
    118 
    119  
    120 
    121         /// <summary>
    122 
    123         /// 对字符串进行URL解码
    124 
    125         /// </summary>
    126 
    127         /// <param name="instr"></param>
    128 
    129         /// <param name="charset"></param>
    130 
    131         /// <returns></returns>
    132 
    133         public static string UrlDecode(string instr, string charset)
    134 
    135         {
    136 
    137             if (instr == null || instr.Trim() == "")
    138 
    139                 return "";
    140 
    141             else
    142 
    143             {
    144 
    145                 string res;
    146 
    147  
    148 
    149                 try
    150 
    151                 {
    152 
    153                     res = System.Web.HttpUtility.UrlDecode(instr, Encoding.GetEncoding(charset));
    154 
    155  
    156 
    157                 }
    158 
    159                 catch (Exception ex)
    160 
    161                 {
    162 
    163                     res = System.Web.HttpUtility.UrlDecode(instr, Encoding.GetEncoding("GB2312"));
    164 
    165                 }
    166 
    167  
    168 
    169  
    170 
    171                 return res;
    172 
    173  
    174 
    175             }
    176 
    177         }
    178 
    179  
    180 
    181  
    182 
    183         /// <summary>
    184 
    185         /// 取时间戳生成随即数,替换交易单号中的后10位流水号
    186 
    187         /// </summary>
    188 
    189         /// <returns></returns>
    190 
    191         public static UInt32 UnixStamp()
    192 
    193         {
    194 
    195             TimeSpan ts = DateTime.Now - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
    196 
    197             return Convert.ToUInt32(ts.TotalSeconds);
    198 
    199         }
    200 
    201         /// <summary>
    202 
    203         /// 取随机数
    204 
    205         /// </summary>
    206 
    207         /// <param name="length"></param>
    208 
    209         /// <returns></returns>
    210 
    211         public static string BuildRandomStr(int length)
    212 
    213         {
    214 
    215             Random rand = new Random();
    216 
    217  
    218 
    219             int num = rand.Next();
    220 
    221  
    222 
    223             string str = num.ToString();
    224 
    225  
    226 
    227             if (str.Length > length)
    228 
    229             {
    230 
    231                 str = str.Substring(0, length);
    232 
    233             }
    234 
    235             else if (str.Length < length)
    236 
    237             {
    238 
    239                 int n = length - str.Length;
    240 
    241                 while (n > 0)
    242 
    243                 {
    244 
    245                     str.Insert(0, "0");
    246 
    247                     n--;
    248 
    249                 }
    250 
    251             }
    252 
    253  
    254 
    255             return str;
    256 
    257         }
    258 
    259         /// <summary>
    260 
    261         /// 循环获取一个实体类每个字段的XmlAttribute属性的值
    262 
    263         /// </summary>
    264 
    265         /// <typeparam name="T"></typeparam>
    266 
    267         /// <returns></returns>
    268 
    269         public static Dictionary<string, string> GetAuthors<T>(T model)
    270 
    271         {
    272 
    273             Dictionary<string, string> _dict = new Dictionary<string, string>();
    274 
    275  
    276 
    277             Type type = model.GetType(); //获取类型
    278 
    279  
    280 
    281             PropertyInfo[] props = typeof(T).GetProperties();
    282 
    283             foreach (PropertyInfo prop in props)
    284 
    285             {
    286 
    287                 object[] attrs = prop.GetCustomAttributes(true);
    288 
    289                 foreach (object attr in attrs)
    290 
    291                 {
    292 
    293                     XmlElementAttribute authAttr = attr as XmlElementAttribute;
    294 
    295                     if (authAttr != null)
    296 
    297                     {
    298 
    299                         string auth = authAttr.ElementName;
    300 
    301  
    302 
    303                         PropertyInfo property = type.GetProperty(prop.Name);
    304 
    305                         string value = (string)property.GetValue(model, null); //获取属性值
    306 
    307  
    308 
    309                         _dict.Add(auth, value);
    310 
    311                     }
    312 
    313                 }
    314 
    315             }
    316 
    317             return _dict;
    318 
    319         }
    320 
    321  
    322 
    323         /// <summary>
    324 
    325         /// 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名
    326 
    327         /// </summary>
    328 
    329         /// <param name="key">参数名</param>
    330 
    331         /// <param name="value">参数值</param>
    332 
    333         /// key和value通常用于填充最后一组参数
    334 
    335         /// <returns></returns>
    336 
    337         public static string CreateMd5Sign(Dictionary<string, string> dict, string value)
    338 
    339         {
    340 
    341             ArrayList akeys = new ArrayList();
    342 
    343             foreach (var x in dict)
    344 
    345             {
    346 
    347                 if ("sign".CompareTo(x.Key) == 0)
    348 
    349                     continue;
    350 
    351                 akeys.Add(x.Key);
    352 
    353             }
    354 
    355             StringBuilder sb = new StringBuilder();
    356 
    357             akeys.Sort();
    358 
    359  
    360 
    361             foreach (string k in akeys)
    362 
    363             {
    364 
    365                 string v = (string)dict[k];
    366 
    367                 if (null != v && "".CompareTo(v) != 0
    368 
    369                     && "sign".CompareTo(k) != 0 && "key".CompareTo(k) != 0)
    370 
    371                 {
    372 
    373                     sb.Append(k + "=" + v + "&");
    374 
    375                 }
    376 
    377             }
    378 
    379             sb.Append("key=" + value);
    380 
    381             var md5 = MD5.Create();
    382 
    383             var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()));
    384 
    385             var sbuilder = new StringBuilder();
    386 
    387             foreach (byte b in bs)
    388 
    389             {
    390 
    391                 sbuilder.Append(b.ToString("x2"));
    392 
    393             }
    394 
    395             //所有字符转为大写
    396 
    397             return sbuilder.ToString().ToUpper();
    398 
    399         }
    400 
    401         /// <summary>
    402 
    403         /// 接收post数据
    404 
    405         /// </summary>
    406 
    407         /// <param name="context"></param>
    408 
    409         /// <returns></returns>
    410 
    411         public static string PostInput(Stream stream)
    412 
    413         {
    414 
    415             int count = 0;
    416 
    417             byte[] buffer = new byte[1024];
    418 
    419             StringBuilder builder = new StringBuilder();
    420 
    421             while ((count = stream.Read(buffer, 0, 1024)) > 0)
    422 
    423             {
    424 
    425                 builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    426 
    427             }
    428 
    429             return builder.ToString();
    430 
    431         }
    432 
    433     }
    434 
    435 PayResult类,请求参数基类
    436 
    437 [XmlRoot("xml")]
    438 
    439     [Serializable()]
    440 
    441     public class PayResult
    442 
    443     {
    444 
    445         public virtual bool IsSuccess()
    446 
    447         {
    448 
    449             return this.ReturnCode == "SUCCESS";
    450 
    451         }
    452 
    453         /// <summary>
    454 
    455         /// 返回状态码
    456 
    457         /// SUCCESS/FAIL,此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
    458 
    459         /// </summary>
    460 
    461         [XmlElement("return_code")]
    462 
    463         public string ReturnCode { get; set; }
    464 
    465  
    466 
    467         /// <summary>
    468 
    469         /// 返回信息,返回信息,如非空,为错误原因,签名失败,参数格式校验错误
    470 
    471         /// </summary>
    472 
    473         [XmlElement("return_msg")]
    474 
    475         public string Message { get; set; }
    476 
    477  
    478 
    479         /// <summary>
    480 
    481         /// 详细内容
    482 
    483         /// </summary>
    484 
    485         [XmlIgnore]
    486 
    487         public string DetailResult { get; set; }
    488 
    489     }
    View Code

    支付完成之后,异步回掉的处理

     1 /// <summary>
     2 
     3         /// 微信支付回调地址
     4 
     5         /// </summary>
     6 
     7         /// <param name="tenantId"></param>
     8 
     9         /// <returns></returns>
    10 
    11         [Route("PayNotify/{tenantId}")]
    12 
    13         [AllowAnonymous]
    14 
    15         public ActionResult PayNotify(int tenantId)
    16 
    17         {
    18 
    19             Action<NotifyResult> successAction = (result) =>
    20 
    21             {
    22 
    23                 using (var context = new AppDbContext())
    24 
    25                 {
    26 
    27                     var order = context.Order_Infos.FirstOrDefault(o => o.Code == result.OutTradeNo);
    28 
    29                     if (null != order)
    30 
    31                     {
    32 
    33                         //修改订单状态
    34 
    35                         order.State = EnumOrderStatus.Overhang;
    36 
    37                         order.ThirdPayType = EnumThirdPayType.WX;
    38 
    39                         order.PaymentOn = DateTime.Now;
    40 
    41                         order.UpdateTime = DateTime.Now;
    42 
    43                         context.SaveChanges();
    44 
    45  
    46 
    47  
    48 
    49                     }
    50 
    51                     else
    52 
    53                     {
    54 
    55                         logger.Log(LoggerLevels.Debug, "Order information does not exist!");
    56 
    57                     }
    58 
    59                 }
    60 
    61                 //此处编写成功处理逻辑
    62 
    63                 logger.Log(LoggerLevels.Debug, "Success: JSON:" + JsonConvert.SerializeObject(result));
    64 
    65             };
    66 
    67             Action<NotifyResult> failAction = (result) =>
    68 
    69             {
    70 
    71                 //此处编写失败处理逻辑
    72 
    73                 logger.Log(LoggerLevels.Debug, "Fail: JSON:" + JsonConvert.SerializeObject(result));
    74 
    75             };
    76 
    77             return Content(WeiChatApisContext.Current.TenPayV3Api.NotifyAndReurnResult(Request.InputStream, successAction, failAction));
    78 
    79         }
    View Code

    当前是mvc项目,所以我建了一个webapi控制器,路由地址是http://xx.com/api/ PayNotify/{tenantId},tenantId是租户id,非必填,但是如果没有,要记得把路由里面的参数也去掉。这个地址就是本文前面配置的回调地址,一定要配置正确

    然后是调用微信的统一下单的方法

     1 /// <summary>
     2 
     3         /// 微信支付(统一下单)
     4 
     5         /// </summary>
     6 
     7         /// <param name="id"></param>
     8 
     9         /// <returns></returns>
    10 
    11         [HttpGet]
    12 
    13         [Route("Pay/{id}")]
    14 
    15         public IHttpActionResult WechatPay(Guid id)
    16 
    17         {
    18 
    19             try
    20 
    21             {
    22 
    23                 //查询订单
    24 
    25                 var order = db.Order_Infos.SingleOrDefault(o => o.Id == id && o.OpenId == WeiChatApplicationContext.Current.WeiChatUser.OpenId);
    26 
    27                 if (null == order)
    28 
    29                     return BadRequest("订单信息不存在");
    30 
    31                 #region 统一下单
    32 
    33                 var model = new UnifiedorderRequest();
    34 
    35                 model.OpenId = WeiChatApplicationContext.Current.WeiChatUser.OpenId;
    36 
    37                 model.SpbillCreateIp = "8.8.8.8";
    38 
    39                 model.OutTradeNo = order.Code;
    40 
    41                 model.TotalFee = Convert.ToInt32((order.TotalPrice + order.Shipping) * 100).ToString();
    42 
    43                 model.NonceStr = PayUtil.GetNoncestr();
    44 
    45                 model.TradeType = "JSAPI";
    46 
    47                 model.Body = "购买商品";
    48 
    49                 model.DeviceInfo = "WEB";
    50 
    51                 var result = WeiChatApisContext.Current.TenPayV3Api.Unifiedorder(model);
    52 
    53  
    54 
    55                 Dictionary<string, string> _dict = new Dictionary<string, string>();
    56 
    57                 _dict.Add("appId", result.AppId);
    58 
    59                 _dict.Add("timeStamp", PayUtil.GetTimestamp());
    60 
    61                 _dict.Add("nonceStr", result.NonceStr);
    62 
    63                 _dict.Add("package", "prepay_id=" + result.PrepayId);
    64 
    65                 _dict.Add("signType", "MD5");
    66 
    67                 _dict.Add("paySign", PayUtil.CreateMd5Sign(_dict, WeiChatConfigManager.Current.GetPayConfig().TenPayKey));
    68 
    69                 #endregion
    70 
    71                 return Ok(_dict);
    72 
    73             }
    74 
    75             catch (Exception ex)
    76 
    77             {
    78 
    79                 log.Log(LoggerLevels.Error, "WechatPay:" + ex.Message);
    80 
    81             }
    82 
    83             return BadRequest("操作失败,请联系管理员!");
    84 
    85         }
    View Code

    这也是一个webapi或mvc控制器,给前台页面调用,作用是通过微信的统一下单方法获取微信的预付单,然后再生成一个供给jssdk调用的对象。

    页面上的jssdk调用方法

     1 function onBridgeReady(data) {
     2 
     3             WeixinJSBridge.invoke('getBrandWCPayRequest', data, function (res) {
     4 
     5                 is_suc = true;
     6 
     7                 if (res.err_msg == "get_brand_wcpay_request:ok") { //支付成功后
     8 
     9                     location.href = '@Url.TenantAction("PaySuccess", "Personal")';
    10 
    11                 } else {
    12 
    13  
    14 
    15                 }
    16 
    17             });
    18 
    19         }
    20 
    21         function CallPay(data) {
    22 
    23             if (typeof WeixinJSBridge == "undefined") {
    24 
    25                 if (document.addEventListener) {
    26 
    27                     document.addEventListener('WeixinJSBridgeReady', onBridgeReady(data), false);
    28 
    29                 } else if (document.attachEvent) {
    30 
    31                     document.attachEvent('WeixinJSBridgeReady', onBridgeReady(data));
    32 
    33                     document.attachEvent('onWeixinJSBridgeReady', onBridgeReady(data));
    34 
    35                 }
    36 
    37             } else {
    38 
    39                 onBridgeReady(data);
    40 
    41             }
    42 
    43         }
    44 
    45  
    46 
    47 this.toPay = function () {
    48 
    49                 wc.restApi.get({
    50 
    51                     isBlockUI: false,
    52 
    53                     url: '/api/MyOrder/Pay/' + _orderid,
    54 
    55                     success: function (result) {
    56 
    57                         CallPay(ko.toJS(result));
    58 
    59                     }
    60 
    61                 });
    62 
    63             }
    View Code

    注意这里,这里先是通过ajax请求到我们前面所写的微信统一下单那个action,然后获取到返回的那个对象

    关于ko.toJS(),这是knockout里面的方法,作用是把result处理成json对象。

    好了,到此微信支付已经处理完了,详细代码请移步http://github.com/xin-lai下载最新源码。

  • 相关阅读:
    HTML表单
    CSS等高布局的6种方式
    HTML用户反馈表单
    HTML美化修饰<A>
    sql查询语句 --------一个表中的字段对应另外一个表中的两个字段的查询语句
    jq 表格添加删除行
    js 静止f1到f12 和屏蔽鼠标右键
    手机自适应页面的meta标签
    tp3.2 的验证码的使用
    php多线程抓取网页
  • 原文地址:https://www.cnblogs.com/smileyearn/p/5958330.html
Copyright © 2011-2022 走看看