微信app支付android客户端以及.net服务端实现

Posted 沙滩裤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信app支付android客户端以及.net服务端实现相关的知识,希望对你有一定的参考价值。

由于公司运营需要,需要在客户端(android/ios)增加微信以及支付宝支付,在调用微信app支付时遇到一些问题,也算是一些踩过的坑,记录下来

,希望能对.net开发者服务端网站更快的集成微信app支付。

1.开发所需资料:微信开放平台应用的appid以及appsecert,商户平台的商户号以及api安全里面里面设置的key,详见 微信支付账户相关信息;

2.微信开发者平台完善应用平台的相关信息,android应用签名必须用打包签名过的发布版本apk(这一步很重用),包名必须一致,可以用微信提供的签名工具获得,签名工具下载地址https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk

以下是交互时序图,统一下单API、支付结果通知API和查询订单API等都涉及签名过程,调用都必须在商户服务器端完成(来源微信支付开发文档):

商户系统和微信支付支付系统主要交互:

步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。商户后台收到订单时需要调用微信的同意下单接口,生成预支付单;

C#大概代码如下(相关代码参考自JeffreySu/WeiXinMPSDK ):

private WechatPayResult generatePayResult(string mchappid,string mchid
            ,string body,string orderno,int total,string ip,string notify,string mchkey,string nonce)
        {
            DateTime start = DateTime.Now,end= DateTime.Now.AddMinutes(15);
            var xmlDataInfo = new TenPayV3UnifiedorderRequestData(mchappid
               ,mchid, body, orderno, total
               ,ip,notify, TenPayV3Type.APP
               , null, mchkey, nonce, null,start,end);
            var result = TenPayV3.Unifiedorder(xmlDataInfo);//调用统一订单接口
            return new WechatPayResult
            {
                appid=mchappid,
                body=body,
                CreatedOn=DateTime.Now,
                mch_id=mchid,
                prepay_id=result.prepay_id,
                spbill_create_ip=ip,
                nonce_str=nonce,
                timeStamp= TenPayV3Util.GetTimestamp(),
                out_trade_no=orderno,
                time_start=start,
                time_expire=end,
                total_fee=total,
                trade_type=result.trade_type
            };
        }
View Code
  public class RequestHandler
    {

        public RequestHandler()
        {
            Parameters = new Hashtable();
        }


        public RequestHandler(HttpContext httpContext)
        {
            Parameters = new Hashtable();

            this.HttpContext = httpContext ?? HttpContext.Current;

        }
        /// <summary>
        /// 密钥
        /// </summary>
        private string Key;

        protected HttpContext HttpContext;

        /// <summary>
        /// 请求的参数
        /// </summary>
        protected Hashtable Parameters;

        /// <summary>
        /// debug信息
        /// </summary>
        private string DebugInfo;

        /// <summary>
        /// 初始化函数
        /// </summary>
        public virtual void Init()
        {
        }
        /// <summary>
        /// 获取debug信息
        /// </summary>
        /// <returns></returns>
        public String GetDebugInfo()
        {
            return DebugInfo;
        }
        /// <summary>
        /// 获取密钥
        /// </summary>
        /// <returns></returns>
        public string GetKey()
        {
            return Key;
        }
        /// <summary>
        /// 设置密钥
        /// </summary>
        /// <param name="key"></param>
        public void SetKey(string key)
        {
            this.Key = key;
        }

        /// <summary>
        /// 设置参数值
        /// </summary>
        /// <param name="parameter"></param>
        /// <param name="parameterValue"></param>
        public void SetParameter(string parameter, string parameterValue)
        {
            if (parameter != null && parameter != "")
            {
                if (Parameters.Contains(parameter))
                {
                    Parameters.Remove(parameter);
                }

                Parameters.Add(parameter, parameterValue);
            }
        }


        /// <summary>
        /// 当参数不为null或空字符串时,设置参数值
        /// </summary>
        /// <param name="parameter"></param>
        /// <param name="parameterValue"></param>
        public void SetParameterWhenNotNull(string parameter, string parameterValue)
        {
            if (!string.IsNullOrEmpty(parameterValue))
            {
                SetParameter(parameter, parameterValue);
            }
        }

        /// <summary>
        /// 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名
        /// </summary>
        /// <param name="key">参数名</param>
        /// <param name="value">参数值</param>
        /// key和value通常用于填充最后一组参数
        /// <returns></returns>
        public virtual string CreateMd5Sign(string key, string value)
        {
            StringBuilder sb = new StringBuilder();

            ArrayList akeys = new ArrayList(Parameters.Keys);
            akeys.Sort();

            foreach (string k in akeys)
            {
                string v = (string)Parameters[k];
                if (null != v && "".CompareTo(v) != 0
                    && "sign".CompareTo(k) != 0 
                    //&& "sign_type".CompareTo(k) != 0
                    && "key".CompareTo(k) != 0)
                {
                    sb.Append(k + "=" + v + "&");
                }
            }

            sb.Append(key + "=" + value);
            string sign = EncryptHelper.GetMD5(sb.ToString(), GetCharset()).ToUpper();
            //string sign = MD5UtilHelper.GetMD5(sb.ToString(), GetCharset()).ToUpper();
            return sign;
        }
        public virtual string CreateMd5Sign()
        {
            StringBuilder sb = new StringBuilder();

            ArrayList akeys = new ArrayList(Parameters.Keys);
            akeys.Sort();

            foreach (string k in akeys)
            {
                string v = (string)Parameters[k];
                if (null != v && "".CompareTo(v) != 0
                    && "sign".CompareTo(k) != 0
                    //&& "sign_type".CompareTo(k) != 0
                    && "key".CompareTo(k) != 0)
                {
                    sb.Append(k + "=" + v + "&");
                }
            }
            string sign = EncryptHelper.GetMD5(sb.ToString().Substring(0,sb.Length-1), GetCharset()).ToUpper();
            return sign;
        }
        /// <summary>
        /// 输出XML
        /// </summary>
        /// <returns></returns>
        public string ParseXML()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<xml>");
            foreach (string k in Parameters.Keys)
            {
                string v = (string)Parameters[k];
                if (v != null && Regex.IsMatch(v, @"^[0-9.]$"))
                {

                    sb.Append("<" + k + ">" + v + "</" + k + ">");
                }
                else
                {
                    sb.Append("<" + k + "><![CDATA[" + v + "]]></" + k + ">");
                }

            }
            sb.Append("</xml>");
            return sb.ToString();
        }



        /// <summary>
        /// 设置debug信息
        /// </summary>
        /// <param name="debugInfo"></param>
        public void SetDebugInfo(String debugInfo)
        {
            this.DebugInfo = debugInfo;
        }

        public Hashtable GetAllParameters()
        {
            return this.Parameters;
        }

        protected virtual string GetCharset()
        {
            if (this.HttpContext == null)
            {
                return Encoding.UTF8.BodyName;
            }

            return this.HttpContext.Request.ContentEncoding.BodyName;
        }
    }
View Code
 /// <summary>
    /// 微信支付提交的XML Data数据[统一下单]
    /// </summary>
    public class TenPayV3UnifiedorderRequestData
    {
        /// <summary>
        /// 公众账号ID
        /// </summary>
        public string AppId { get; set; }
        /// <summary>
        /// 商户号
        /// </summary>
        public string MchId { get; set; }
        /// <summary>
        /// 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB",String(32)如:013467007045764
        /// </summary>
        public string DeviceInfo { get; set; }
        /// <summary>
        /// 随机字符串
        /// </summary>
        public string NonceStr { get; set; }
        /// <summary>
        /// 签名类型,默认为MD5,支持HMAC-SHA256和MD5。(使用默认)
        /// </summary>
        public string SignType { get; set; }
        /// <summary>
        /// 商品信息
        /// </summary>
        public string Body { get; set; }
        /// <summary>
        /// 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。
        ///cost_price Int 可选 32 订单原价,商户侧一张小票订单可能被分多次支付,订单原价用于记录整张小票的支付金额。当订单原价与支付金额不相等则被判定为拆单,无法享/受/优/惠。
        /// receipt_id String 可选 32 商家小票ID
        ///goods_detail 服务商必填[]:
        ///└ goods_id String 必填 32 商品的编号
        ///└ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号
        ///└ goods_name String 可选 256 商品名称 
        ///└ quantity Int 必填  32 商品数量
        ///└ price Int 必填 32 商品单价,如果商户有优惠,需传输商户优惠后的单价
        ///注意:单品总金额应&lt;=订单总金额total_fee,否则会无法享受优惠。
        /// String(6000)
        /// </summary>
        public string Detail { get; set; }
        /// <summary>
        /// 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。String(127),如:深圳分店
        /// </summary>
        public string Attach { get; set; }
        /// <summary>
        /// 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型。String(16),如:CNY
        /// </summary>
        public string FeeType { get; set; }
        /// <summary>
        /// 商家订单号
        /// </summary>
        public string OutTradeNo { get; set; }
        /// <summary>
        /// 商品金额,以分为单位(money * 100).ToString()
        /// </summary>
        public int TotalFee { get; set; }
        /// <summary>
        /// 用户的公网ip,不是商户服务器IP
        /// </summary>
        public string SpbillCreateIP { get; set; }
        /// <summary>
        /// 订单生成时间,最终生成格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则。
        /// 如果为空,则默认为当前服务器时间
        /// </summary>
        public string TimeStart { get; set; }
        /// <summary>
        /// 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
        /// 注意:最短失效时间间隔必须大于5分钟。
        /// 留空则不设置失效时间
        /// </summary>
        public string TimeExpire { get; set; }
        /// <summary>
        /// 商品标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠。String(32),如:WXG
        /// </summary>
        public string GoodsTag { get; set; }
        /// <summary>
        /// 接收财付通通知的URL
        /// </summary>
        public string NotifyUrl { get; set; }
        /// <summary>
        /// 交易类型
        /// </summary>
        public TenPayV3Type TradeType { get; set; }
        /// <summary>
        /// trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
        /// String(32),如:12235413214070356458058
        /// </summary>
        public string ProductId { get; set; }
        /// <summary>
        /// 上传此参数no_credit--可限制用户不能使用信用卡支付
        /// </summary>
        public string LimitPay { get; set; }
        /// <summary>
        /// 用户的openId
        /// </summary>
        public string OpenId { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public string Key { get; set; }

        public readonly RequestHandler PackageRequestHandler;
        public readonly string Sign;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="mchId"></param>
        /// <param name="body"></param>
        /// <param name="outTradeNo"></param>
        /// <param name="totalFee">单位:分</param>
        /// <param name="spbillCreateIp"></param>
        /// <param name="notifyUrl"></param>
        /// <param name="tradeType"></param>
        /// <param name="openid"></param>
        /// <param name="key"></param>
        /// <param name="nonceStr"></param>
        /// <param name="deviceInfo">自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB",String(32)如:013467007045764</param>
        /// <param name="timeStart">订单生成时间,如果为空,则默认为当前服务器时间</param>
        /// <param name="timeExpire">订单失效时间,留空则不设置失效时间</param>
        /// <param name="detail">商品详细列表</param>
        /// <param name="attach">附加数据</param>
        /// <param name="feeType">符合ISO 4217标准的三位字母代码,默认人民币:CNY</param>
        /// <param name="goodsTag">商品标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠。String(32),如:WXG</param>
        /// <param name="productId">trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。String(32),如:12235413214070356458058</param>
        /// <param name="limitPay">是否限制用户不能使用信用卡支付</param>
        public TenPayV3UnifiedorderRequestData(string appId, string mchId, string body, string outTradeNo, int totalFee, string spbillCreateIp,
            string notifyUrl, TenPayV3Type tradeType, string openid, string key, string nonceStr,
            string deviceInfo = null, DateTime? timeStart = null, DateTime? timeExpire = null,
           string detail = null, string attach = null, string feeType = "CNY", string goodsTag = null, string productId = null, bool limitPay = false)
        {
            AppId = appId;
            MchId = mchId;
            DeviceInfo = deviceInfo;
            NonceStr = nonceStr;
            SignType = "MD5";
            Body = body ?? "";
            Detail = detail;
            Attach = attach;
            OutTradeNo = outTradeNo;
            FeeType = feeType;
            TotalFee = totalFee;
            SpbillCreateIP = spbillCreateIp;
            TimeStart = (timeStart ?? DateTime.Now).ToString("yyyyMMddHHmmss");
            TimeExpire = timeExpire.HasValue ? timeExpire.Value.ToString("yyyyMMddHHmmss") : null;
            GoodsTag = goodsTag;
            NotifyUrl = notifyUrl;
            TradeType = tradeType;
            ProductId = productId;
            LimitPay = limitPay ? "no_credit" : null;
            OpenId = openid;
            Key = key;

            #region 设置RequestHandler

            //创建支付应答对象
            PackageRequestHandler = new RequestHandler(null);
            //初始化
            PackageRequestHandler.Init();

            //设置package订单参数
            //以下设置顺序按照官方文档排序,方便维护:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
            PackageRequestHandler.SetParameter("appid", this.AppId);                       //公众账号ID
            PackageRequestHandler.SetParameter("mch_id", this.MchId);                      //商户号
            PackageRequestHandler.SetParameterWhenNotNull("device_info", this.DeviceInfo); //自定义参数
            PackageRequestHandler.SetParameter("nonce_str", this.NonceStr);                //随机字符串
            PackageRequestHandler.SetParameterWhenNotNull("sign_type", this.SignType);     //签名类型,默认为MD5
            PackageRequestHandler.SetParameter("body", this.Body);                         //商品信息
            PackageRequestHandler.SetParameterWhenNotNull("detail", this.Detail);          //商品详细列表
            PackageRequestHandler.SetParameterWhenNotNull("attach", this.Attach);          //附加数据
            PackageRequestHandler.SetParameter("out_trade_no", this.OutTradeNo);           //商家订单号
            PackageRequestHandler.SetParameterWhenNotNull("fee_type", this.FeeType);       //符合ISO 4217标准的三位字母代码,默认人民币:CNY
            PackageRequestHandler.SetParameter("total_fee", this.TotalFee.ToString());  //商品金额,以分为单位(money * 100).ToString()
            PackageRequestHandler.SetParameter("spbill_create_ip", this.SpbillCreateIP);   //用户的公网ip,不是商户服务器IP
            PackageRequestHandler.SetParameterWhenNotNull("time_start", this.TimeStart);   //订单生成时间
            PackageRequestHandler.SetParameterWhenNotNull("time_expire", this.TimeExpire);  //订单失效时间
            PackageRequestHandler.SetParameterWhenNotNull("goods_tag", this.GoodsTag);     //商品标记
            PackageRequestHandler.SetParameter("notify_url", this.NotifyUrl);              //接收财付通通知的URL
            PackageRequestHandler.SetParameter("trade_type", this.TradeType.ToString());   //交易类型
            PackageRequestHandler.SetParameterWhenNotNull("product_id", this.ProductId);   //trade_type=NATIVE时(即扫码支付),此参数必传。
            PackageRequestHandler.SetParameterWhenNotNull("limit_pay", this.LimitPay);     //上传此参数no_credit--可限制用户不能使用信用卡支付
            PackageRequestHandler.SetParameter("openid", this.OpenId);          //以上是关于微信app支付android客户端以及.net服务端实现的主要内容,如果未能解决你的问题,请参考以下文章

.net 微信APP支付接口的开发流程以及坑(转)

微信app支付(android端+java后台)

Android 三方使用之微信登录,微信分享,微信分享图片,支付

golang微信支付服务端

uniapp开启微信支付支付宝支付 + Android Studio离线打包APK文件 (前端部分)

Android 支付宝以及微信支付快速接入流程