幼敏悟过人,读书辄成诵。这篇文章主要讲述微信app支付android客户端以及.net服务端实现相关的知识,希望能为你提供帮助。
由于公司运营需要,需要在客户端(android/ios)增加微信以及支付宝支付,在调用微信app支付时遇到一些问题,也算是一些踩过的坑,记录下来
,希望能对.net开发者服务端网站更快的集成微信app支付。
【微信app支付android客户端以及.net服务端实现】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测试用例设计实践经验(转载)
- App开发定制前需要做哪些规划()
- 移动讲堂(如何避免App外包被坑())
- Android Studio中查看类的继承关系
- Appium环境搭建
- nginx配置https及Android客户端访问自签名证书
- Android---06---2中动画效果
- android 蓝牙低耗能(LBE)技术介绍
- Android app UI设计规范