微信支付宝SDK接入服务器开发篇
文章目录
- 023 服务器的工程结构
- 024 封装支付事件
- EventCenter.cs
- 025 网络模块的封装
- Net/Agent.cs
- Net/NetComponent.cs
- 026 完成辅助类
- Tool/GettotalFeeTool.cs
- Tool/TimeTool.cs
- 027 支付宝服务器组件的实现
- AliPayComponent.cs
- 028 微信支付服务器
- WXPayComponent.cs
- 029 服务器部署
- 030 对IP使用的详细说明
- 031 客户端支付宝功能的修正
- 032 获取应用签名和测试所有功能
023 服务器的工程结构
024 封装支付事件
EventCenter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WAServer.Net;namespace WAServer
{class EventCenter{//声明委托原型public delegate void NetEvent(Agent agent, string[] msg);public static NetEvent WeChatPay;//微信支付事件public static NetEvent AliPay;//阿里支付事件//被调度 被注册的时候 才有具体的意义}
}
025 网络模块的封装
Net/Agent.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace WAServer.Net
{public class Agent{public Socket mSocket;public string remoteIP;private int closeReq = 0;private bool isClose = false;public bool IsClose{get { return isClose; }set { isClose = value; }}private byte[] mTemp = new byte[0x2000];//8192//构造函数 在new的时候 第一时间调用public Agent(Socket socket){Console.ForegroundColor = ConsoleColor.DarkCyan;Console.WriteLine("建立连接" + socket.RemoteEndPoint);this.mSocket = socket;this.remoteIP = socket.RemoteEndPoint.ToString().Split(':')[0];StartReceiving();}//开始接收客户端发送的消息public void StartReceiving(){if (mSocket != null && mSocket.Connected && !isClose){try{//异步接收消息的方式mSocket.BeginReceive(mTemp, 0, mTemp.Length, SocketFlags.None, OnReceive, mSocket);}catch (Exception exception){Console.WriteLine("(tcp)Agent接收消息异常", exception.ToString());Close();}}}/// <summary> 接收回调:处理网络消息 </summary>void OnReceive(IAsyncResult result){int length = 0;if (mSocket == null){Console.WriteLine("Tcp接收异常");Close();return;}try{length = mSocket.EndReceive(result);}catch (Exception exception){Console.WriteLine(exception.Message);Close();}if (length <= 0){Console.WriteLine("bytes <= 0 " + length);Close();}else{//从这里去进行解析消息 分发消息//byte[] message = new byte[length];if (length > 0){//解析消息 byte[] to string string message = Encoding.UTF8.GetString(mTemp, 0, length);//缓存客户端的IP和端口//client_ip = (mSocket.RemoteEndPoint as IPEndPoint).Address.ToString();//client_port = (mSocket.RemoteEndPoint as IPEndPoint).Port;//Console.WriteLine("接收到客户端消息:" + client_ip + ":" + message);//处理消息 格式为:协议类型+","+"参数1"+","+"参数n"+","+...if (message != ""){string[] msgList = message.Split(',');switch (msgList[0]){case "WeChatPay":EventCenter.WeChatPay(this, msgList);break;case "AliPay":EventCenter.AliPay(this, msgList);break;case "getProps":Console.ForegroundColor = ConsoleColor.DarkMagenta;Console.WriteLine("客户端已经收到了道具");break;default:break;}}}//持续接收来自客户端连接的消息}if (mSocket != null){try{mSocket.BeginReceive(mTemp, 0, mTemp.Length, SocketFlags.None, OnReceive, mSocket);}catch (Exception exception2){Console.WriteLine(exception2.Message);Close();}}}#region 发送接口public void SendBytes(byte[] bytes){try{Console.WriteLine("需要发送的字节是多少:" + bytes.Length);mSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, OnSend, null);}catch (Exception exception){Console.WriteLine(exception.Message);Close();}}private void OnSend(IAsyncResult result){int num = 0;try{num = mSocket.EndSend(result);Console.WriteLine("一共发送了:" + num + "多少字节");}catch (Exception exception){num = 0;Close();Console.WriteLine("tcp发送消息异常{0}", exception.Message);return;}//当socket不等于空 并且处于连接的时候if (mSocket != null && mSocket.Connected){}else{//否则需要把连接释放掉Close();}}#endregion#region 关闭连接的接口/// <summary> 关闭连接的socket </summary>public void Close(){if (Interlocked.Increment(ref closeReq) != 1){return;}if (isClose){return;}isClose = true;Console.WriteLine("(tcp)Agent CloseAgent");if (mSocket != null && mSocket.Connected){Console.WriteLine("CloseSocket");try{mSocket.Shutdown(SocketShutdown.Both);mSocket.Close();}catch (Exception exception){Console.WriteLine("socket Close Error {0}", exception.ToString());}}mSocket = null;if (mSocket != null){var ip = mSocket.RemoteEndPoint as IPEndPoint;Console.WriteLine("close client: {0}", ip.Address.ToString());}}#endregion}
}
Net/NetComponent.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace WAServer.Net
{class NetComponent{//第一 实现TCP服务器//第二 监听来自客户端的连接//第三 生成连接代理类public string client_ip;//请求支付的终端 IPpublic int client_port;//请求支付的终端 prot 端口TcpListener mListener;int mListenerPort = 7577;private int maxConnent = 500;//最大的连接数量Thread mThread;private ManualResetEvent signal = new ManualResetEvent(false);public void Init(){try{mListener = new TcpListener(IPAddress.Parse("10.186.42.84"), mListenerPort);mListener.Server.NoDelay = true;//一种优化阻塞的算法mListener.Start(maxConnent);//开始监听来自客户端的连接 (表示最多可以接受多少个连接)}catch (Exception exception){Console.WriteLine("Tcp启动失败{0}", exception.Message);}mThread = new Thread(new ThreadStart(this.AcceptClient));mThread.Start();}/// <summary>/// 接受客户端的连接/// </summary>private void AcceptClient(){//异步接收客户端的连接mListener.BeginAcceptSocket(new AsyncCallback(AcceptCallback), mListener);signal.WaitOne();}/// <summary>/// 异步连接处理回调/// </summary>/// <param name="result"></param>private void AcceptCallback(IAsyncResult result){try{var listener = (TcpListener)result.AsyncState;Socket clienSocket = listener.EndAcceptSocket(result);//远程IPstring remoteIP = clienSocket.RemoteEndPoint.ToString();//.Split(':')[0];Console.WriteLine("远程IP与端口:" + remoteIP);//这里要进行多开的检测 TODO//每一个连接就是一个Agent:每一个Agent处理网络消息的接收 发送 Agent ag = new Agent(clienSocket);listener.BeginAcceptSocket(new AsyncCallback(AcceptCallback), listener);}catch (Exception exp){Console.WriteLine("Tcp接收连接异常{0}", exp.ToString());}}}
}
026 完成辅助类
Tool/GettotalFeeTool.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WAServer.Tool
{class GettotalFeeTool{/// <summary>获取订单需要支付的总金额</summary>public static int WeChatGettotalFee(string objId, string objCount){//首先根据不同ID获取物品对应的单价int fee = int.Parse(objId) % 1000;//通过单价乘以数量得出物品总价int totalFee = fee * (int.Parse(objCount));return totalFee;}public static string AliGettotalFee(string objId, string objCount){//首先根据不同ID获取物品对应的单价/* int fee = float.Parse(objId) % 1000*/;float fee = 0.00f;switch (objId){case "1000":fee = 0.1f;//单价break;case "1001":fee = 1f;break;case "1002":fee = 2.123f;break;default:break;}float totalFee = fee * (int.Parse(objCount));//Convert.ToDecimal(totalFee.ToString()).ToString("0.00");return String.Format("{0:F}", totalFee);//默认为保留两位}}
}
Tool/TimeTool.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WAServer.Tool
{class TimeTool{/// <summary> 获取时间戳 </summary>public static int ConvertDateTimeInt(System.DateTime time){System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));Console.WriteLine("计算后的时间戳是:" + (int)(time - startTime).TotalSeconds);return (int)(time - startTime).TotalSeconds;}}
}
027 支付宝服务器组件的实现
AliPayComponent.cs
using Aop.Api;
using Aop.Api.Domain;
using Aop.Api.Request;
using Aop.Api.Response;
using Aop.Api.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using WAServer.Net;
using WAServer.Tool;
using System.Web;namespace WAServer.Component
{class AliPayComponent{#region APP_Private_Key and AliPay_Public_Key/// <summary>/// 应用密钥/// </summary>string APP_Private_Key = @"MIIEpAIBAAKCAQEAqx0Ebk9Io0ROSf6uYWSn2fJttz7PQP+HfrkDxaZX+MqlIRsnkWlBwmujw/U6XLJ1KtCs2o7f9bBwNMDJQZC+tktTDsE0e0NbcjqdL2xWld8hyoYuOIUZ8fSM5ama04MLf7B5F56Vu/95k7nYSwtGXWjrASyQu247+ga4KrEe1U5JsY9oS1QffqNLq2riBg6Cs0XdiO3g6geU3b//2/X/pZz7qzPKvflsEBdRbs1PqPare8l6uoRB+xKbgG1dUApmzwi2FDSe+PwuuMtqdDmGgyfofNcU691ButSbPHCPL8QVJZi+CimI4M7wBGQ2wE5Jp1NcO7KD35laDxDnQSvBHwIDAQABAoIBAEGRj/YZKXNupDVUg0vMv0kTzZkPV2nHwQr9KIXfdQxf0qD5/9KHq+wtRQa8/I0y0RUD+4iQgR9racO9MCGQrpO6D2yy+kJVkEAYV80pTZCGfTNW8XU1A7kkha0nra1pJMncPLqhSS1N+y9xYoF3I5J9trevdRJtbkwjsQSi9Ha1tZxFfmgBvsR+W2rD9ORpfqtrZVL/+DJFAiD8ZozRRXYh0+oAU1WXYNYy8LQDDN+nM+2VmDBdMuRUGiVyaSqnmcN4S8QCwYVWVmVO39dNSVWY11q7LJcKCYTZmLgQa01oY3oUbZ7n154k8oDcbPVemdqqGVyH7BgZX2LeRqUiNQECgYEA2gO8eRYV9WAAJcWqP0f+k/4i3m71FGPobsoIuNQr0iz5D2rQ7UlhgZ2Y72LqN2/Srx2g+KTnPigBnck3MHxh6wiBvzwTbF3pzD6SLHoNGRjNmPE+HNXlOaSxXWM6Qvf/KN2J6vua6P0DwkZehOi58TTR1ZRQi/51xWWYp7F2uYECgYEAyO1Phx93kZ273oSz2Igxj7YVhdH/8V9cvx5SiSAnsZ8Eeq7IsFFOE6z/ttxA3qQIhHgTeF66+4LUftjBD6F3M6m39C42SJKcv+C15u31vZ9WyLwqi5RbYDlq62ITBeRIveT0G/Xc24t3vrjinobWgHSq5IX0NDT1d/lNQVnvip8CgYEA10zTRz1RaCZrXuILFD10IxDZvJMVQxK7SxYIcQdPU1uIhvo04/EQ8yEBFH+50A+Fn9yByKuJlm+J0RoSf7aGOMcI4yNgByfjqQmt73CFGODOwZiUf4OYwUlsw04oDlS9Ts0h08awICEmIii+VUFDx/ois2qp9ObRxaRkkk8GcYECgYBoQFlHLtiHQWQ87HW0H9Y3Tq6UJIW741LoBv+kDn8J9gwI669NbKIqK1TyuA0gd9PDh9nyVpSF8zf2KNjjF1AWCjVcCK45sXiLRjibfVRH8ujAdoFMsslGgAQt5VEheXUUsjrGVyck8pRK7PsIbcXWGLKip64xeFj0yvF+uv9C2QKBgQCfjoNp+wygYJlQTo6YzhsnIvdzsqv3Nyq+kldBw4QXFWoK/P69Sh0aUZ+pwSdQ3HwzynSd3EotLlfZNRgNdjK1w9td1GosMoQYJu+LKoRSb0rmCdy1cKuyHCEIkrtOiVvumGE+nUSWrHRPopdRTQo7k3nvzWwQRKB8Pq9iK0aHZA==";/// <summary>/// 支付宝公钥/// </summary>string AliPay_Public_Key = @"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA37vnkTXPnA83r39o1TowQh8WChOgkbrR3ylQqWGm8eeFGn+wi1bhmNyO4MobIwgejgthVWTs/hXKr5YXUgTH3kEEZQpc+CP2YFroMyYKu5tl8my8Ki5LuPtnOnvEHNtMfXmm1TmqJzv5a+igjN3XY4x+CxKrK9hzzRplmpdoeJOtbQ1twYKk0wKRDitVY4ikbvKKy6mDaBh69pYfCpjAL9fDbewU7QjWKBg1YIFRlwOveUNSBdbflpiAr4DIS75xYrg8X/4i6u7p7M5Ma/tAV/Y0No3P++3roaiLaRzBkvmjQcL4akOlQ6klOCiKkcxYC/hEwNs1C+Gh8KuEKTFXXwIDAQAB";#endregionstring appid = "2017051607249364";//阿里 服务端SDK提供的对象 用于请求给客户端的支付参数IAopClient client;AlipayTradeAppPayRequest request;//告诉支付宝 有充值结果 就发送到这个链接来//http post url :订单状态发生变化(支付结果)通知的地址string aliPayResultListenerUrl = @"http://193.112.30.89:9090/";string httpListenerUrl = @"http://10.186.42.84:9090/";string aliServerURL = @"https://openapi.alipay.com/gateway.do";HttpListener httpListener;//初始化public void Init(){//1.启动监听支付结果的服务器ListenerAliPayResult();//2.监听用户充值请求EventCenter.AliPay += AliPay;}/// <summary> 订单的集合 实际上 要持久化到数据库或者硬盘上 放在内存中会因为服务器重启而丢失已有的订单 </summary>Dictionary<string, AliPayOrderInfo> orderDic = new Dictionary<string, AliPayOrderInfo>();/// <summary> 阿里支付请求事件 </summary>private void AliPay(Agent agent, string[] msg){//第一步:获取支付的价格string totalFee = GettotalFeeTool.AliGettotalFee(msg[1], msg[2]);Console.WriteLine("价格是:" + totalFee);//第二步:封装请求的参数模型//请求参数对照:https://docs.open.alipay.com/204/105465///AliPayOrderInfo AlipayTradeAppPayModelAliPayOrderInfo model = new AliPayOrderInfo(){Body = "活动时装(轻语)",//物品名称Subject = "超酷的英雄外表,仅限活动期间可以进行购买",//商品的标题/交易标题/订单标题/订单关键字等。TotalAmount = totalFee,//价格 单位为元ProductCode = "QUICK_MSECURITY_PAY",//官方提供的固定值OutTradeNo = TimeTool.ConvertDateTimeInt(DateTime.Now).ToString(),//唯一订单号TimeoutExpress = "30m",//付款超时时间clientAgent = agent,objID = msg[1],objCount = msg[2],};//缓存订单 用于结果的校验orderDic.Add(model.OutTradeNo, model);//第三步:向支付宝的服务器请求 可用于 客户端调起支付的 参数string aliRequestStr = GetAliPayParameter(model);Console.WriteLine("阿里支付的参数:" + aliRequestStr);//第四步:拼接格式 发送给客户端string toClientStr = "AliPay" + "," + aliRequestStr;agent.SendBytes(Encoding.UTF8.GetBytes(toClientStr));}/// <summary>/// 请求支付参数:https://docs.open.alipay.com/204/105465//// </summary>/// <returns>客户端向安卓层(支付宝客户端SDK)请求的字符串</returns>public string GetAliPayParameter(AlipayTradeAppPayModel alipaymode){if (client == null){client = new DefaultAopClient(aliServerURL, appid, APP_Private_Key, "JSON", "1.0", "RSA2", AliPay_Public_Key, "UTF-8", false);}//参数对照: https://docs.open.alipay.com/204/105465///用于请求的对象request = new AlipayTradeAppPayRequest();request.SetBizModel(alipaymode);//请求的数据模型request.SetNotifyUrl(aliPayResultListenerUrl);//设置支付结果通知的地址//这里和普通的接口调用不同,使用的是sdkExecuteAlipayTradeAppPayResponse response = client.SdkExecute(request);//(不用理这句注释)HttpUtility.HtmlEncode是为了输出到页面时防止被浏览器将关键参数html转义,实际打印到日志以及http传输不会有这个问题//通过Body获取到返回的参数return response.Body;}/// <summary>监听阿里支付结果 https://docs.open.alipay.com/204/105301/ </summary>public void ListenerAliPayResult(){//http监听器httpListener = new HttpListener();httpListener.Prefixes.Add(httpListenerUrl);httpListener.Start();//异步的方式处理请求httpListener.BeginGetContext(new AsyncCallback(CheckAliPayResult), null);}/// <summary>解析结果以及校验</summary>public void CheckAliPayResult(IAsyncResult ar){try{HttpListenerContext context = httpListener.EndGetContext(ar);HttpListenerRequest request = context.Request;//请求对象if (context != null){StreamReader body = new StreamReader(request.InputStream, Encoding.UTF8);//读取流,用来获取支付宝请求的数据string pay_notice = HttpUtility.UrlDecode(body.ReadToEnd(), Encoding.UTF8);//HttpUtility.UrlDecode:解码 url编码,将字符串格式为%的形式,解码就是将%转化为字符串信息Console.ForegroundColor = ConsoleColor.DarkCyan;Console.WriteLine("支付结果来了:" + pay_notice);Dictionary<string, string> aliPayResultDic = StringToDictionary(pay_notice);// 验签 API bool result = AlipaySignature.RSACheckV1(aliPayResultDic, AliPay_Public_Key, "UTF-8", "RSA2", false);//result支付结果if (result){AliPayOrderInfo souceDic = orderDic[aliPayResultDic["out_trade_no"]];//检验之前缓存的订单 是不是 跟本次支付宝发送过来的 有相同的if (souceDic.OutTradeNo.Equals(aliPayResultDic["out_trade_no"])){Console.WriteLine("存在相同的订单");if (souceDic.TotalAmount.Equals(aliPayResultDic["total_amount"])){//确定相同订单的金额Console.WriteLine("金额也是一致的:" + aliPayResultDic["total_amount"] + "元");}else{//去做其他的处理 可能是代码的逻辑的问题 也可能是用户进行恶意攻击 伪造充值 Console.WriteLine("金额不一致");}}else{//多数情况下 还是自己代码逻辑的问题 :需要依赖我们先存储好相应的订单与订单对应的实体模型Console.WriteLine("未存在的订单记录:" + aliPayResultDic["out_trade_no"]);}//验签(支付)成功 告诉客户端 加钻石,给数据库加记录,等。。。 //另外官方建议:最好将返回数据中的几个重要信息,与支付时候对应的参数去对比。 //返回的所有参数都在这里:StringToDictionary(pay_notice) //请求的参数 在:Get_equest_Str() //成功了就需要给支付宝回消息“success”//https://docs.open.alipay.com/204/105301/HttpListenerResponse response = context.Response;string responseString = "success";byte[] buffer = Encoding.UTF8.GetBytes(responseString);response.ContentLength64 = buffer.Length;Stream output = response.OutputStream;output.Write(buffer, 0, buffer.Length);//响应支付宝服务器本次的通知output.Close();response.Close();//给客户端发送道具// OutTradeNo(订单号),道具ID,道具数量string toClientMsg = "sendProps" + "," + souceDic.OutTradeNo + "," + souceDic.objID + "," + souceDic.objCount;byte[] sendByte = Encoding.UTF8.GetBytes(toClientMsg);souceDic.clientAgent.SendBytes(sendByte);}else{Console.WriteLine("验签失败");}Console.WriteLine("验签结果:" + (result == true ? "支付成功" : "支付失败"));if (aliPayResultDic.ContainsKey("trade_status")){switch (aliPayResultDic["trade_status"]){case "WAIT_BUYER_PAY":Console.WriteLine("交易状态:" + "交易创建,等待买家付款");break;case "TRADE_CLOSED":Console.WriteLine("交易状态:" + "未付款交易超时关闭,或支付完成后全额退款");break;case "TRADE_SUCCESS":Console.WriteLine("交易状态:" + "交易支付成功");break;case "TRADE_FINISHED":Console.WriteLine("交易结束,不可退款");break;default:break;}}}}catch (Exception e){Console.WriteLine(e.ToString());}if (httpListener.IsListening){try{httpListener.BeginGetContext(new AsyncCallback(CheckAliPayResult), null);}catch (Exception e){Console.WriteLine(e.ToString());}}}/// <summary>/// 支付结果返回来的是字符串格式,而验证结果的API需要一个字典结构 so..提供这样的一个API/// </summary>public Dictionary<string, string> StringToDictionary(string value){if (value.Length < 1){return null;}Dictionary<string, string> dic = new Dictionary<string, string>();//每个字段之间用"&"拼接string[] dicStrs = value.Split('&');foreach (string str in dicStrs){// Console.Write("183value--" + str); //每个字段的结构是通过"="拼接键值string[] strs = str.Split(new char[] { '=' }, 2);dic.Add(strs[0], strs[1]);}return dic;}}
}public class AliPayOrderInfo : AlipayTradeAppPayModel
{public Agent clientAgent;public string objID;public string objCount;
}
028 微信支付服务器
WXPayComponent.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web;
using System.Xml;
using WAServer.Net;
using WAServer.Tool;namespace WAServer.Component
{class WXPayComponent{//密钥 在商户后台自己配置的static string miyao = "jflkdwoakdlxvnawrfgwt11262357895";string appId = "wxc0d38c38f13506d4";string merchantId = "1495064892";//告诉微信服务器 有充值结果就通知到这个链接来string notify_url = @"http://193.112.30.89:7983";//请求支付参数的URL 官方提供 固定的URLstring getWeChatPayParameterURL = @"https://api.mch.weixin.qq.com/pay/unifiedorder";string packageValue = "Sign=WXPay";//扩展字段int orderNumber = 1;//充值的序号Dictionary<string, string> dic = new Dictionary<string, string>();//out_trade_no内部订单号 -> 订单信息Dictionary<string, OrderInfo> orderDic = new Dictionary<string, OrderInfo>();//启动时候绑定的 HttpListener httpListener; //http监听对象string Order_Url = @"http://10.186.42.84:7983/";//监听的url/// <summary> (服务器)微信组件的初始化 </summary>public void Init(){WeChatPayResultListener();//初始化的第一步:先启动监听支付结果的服务器//第二步:注册客户端发起的充值事件EventCenter.WeChatPay += WeChatPay;}/// <summary> 支付请求 </summary>private void WeChatPay(Agent agent, string[] msg){//请求预支付订单所需要的参数//参数的详细要求查看这里://https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1//现在的时间戳string nowTime = TimeTool.ConvertDateTimeInt(DateTime.Now).ToString();//随机数Random ran = new Random();int r_num = ran.Next(1000, 9999);OrderInfo orderInfo = new OrderInfo();orderInfo.appid = appId;orderInfo.mch_id = merchantId;orderInfo.body = "秦始皇的时装(活动限版)";//商品描述orderInfo.total_fee = GettotalFeeTool.WeChatGettotalFee(msg[1], msg[2]); //支付金额 单位:分orderInfo.spbill_create_ip = agent.remoteIP;//用户终端实际IPorderInfo.notify_url = notify_url;//支付结果通知地址orderInfo.trade_type = "APP";//交易类型//随机字符串 不长于32位 不能使用'.'符号拼接,如IP:127.0.0.1orderInfo.nonce_str = "nonceStr" + r_num + orderNumber + nowTime;//商户订单号 要求唯一性 orderInfo.out_trade_no = "wxpay" + r_num + orderNumber + nowTime;orderInfo.clientAgent = agent;orderInfo.objID = msg[1];orderInfo.objCount = int.Parse(msg[2]);//第一步:签名计算=>获取向微信服务器请求的参数Dictionary<string, string> dics = new Dictionary<string, string>();dics.Add("appid", orderInfo.appid);dics.Add("mch_id", orderInfo.mch_id);dics.Add("nonce_str", orderInfo.nonce_str);dics.Add("body", orderInfo.body);dics.Add("out_trade_no", orderInfo.out_trade_no);dics.Add("total_fee", orderInfo.total_fee.ToString());dics.Add("spbill_create_ip", orderInfo.spbill_create_ip);dics.Add("notify_url", orderInfo.notify_url);dics.Add("trade_type", orderInfo.trade_type);string tempXML = GetParamSrc(dics);//第二步:下单请求-获取响应的参数string result = GetWeChatPayParameter(tempXML);//第三步:将返回的参数再进行签名 并且按照我们跟客户端协定好的格式拼接string payList = "WeChatPay" + "," + PayOrder(result, orderInfo);//第四步:发送给客户端if (payList != "error"){Console.ForegroundColor = ConsoleColor.Blue;Console.WriteLine("将参数传递给客户端:" + payList);byte[] sendData = Encoding.UTF8.GetBytes(payList);agent.SendBytes(sendData);}dics.Clear();orderNumber++;}/// <summary> 请求客户端在微信支付时候所需的参数 </summary>private string GetWeChatPayParameter(string postData){//-----------------------------------第一步:创建Htt请求----------------------////向微信发起支付参数的请求HttpWebRequest request = null;if (getWeChatPayParameterURL.StartsWith("https", StringComparison.OrdinalIgnoreCase)){//创建WebRequest请求request = WebRequest.Create(getWeChatPayParameterURL) as HttpWebRequest;//设置用于验证服务器证书的回调ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);//设置HTTP版本request.ProtocolVersion = HttpVersion.Version11;// 这里设置了安全协议类型。ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;// SecurityProtocolType.Tls1.2; //false表示不建立持续性连接request.KeepAlive = false;//检查已吊销的证书ServicePointManager.CheckCertificateRevocationList = true;//URP最大的并发连接数量ServicePointManager.DefaultConnectionLimit = 100;//参考: https://msdn.microsoft.com/zh-cn/library/system.net.servicepoint.expect100continueServicePointManager.Expect100Continue = false;}//如果开头不是https 直接创建web请求else{request = (HttpWebRequest)WebRequest.Create(getWeChatPayParameterURL);}//web请求的一些属性设置request.Method = "POST";request.ContentType = "application/x-www-form-urlencoded";request.Referer = null;request.AllowAutoRedirect = true;request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";request.Accept = "*/*";//通过流的形式提交网络数据的请求 简单说就是往URL里提交数据byte[] data = Encoding.UTF8.GetBytes(postData);Stream newStream = request.GetRequestStream();//流要写入的数据和长度newStream.Write(data, 0, data.Length);newStream.Close();//-------------------------------第二步:获取请求响应的结果-----------------////获取网页响应结果HttpWebResponse response = (HttpWebResponse)request.GetResponse();Stream stream = response.GetResponseStream();string result = string.Empty;//接收到网络消息 就进一步加工处理using (StreamReader sr = new StreamReader(stream)){//从读取流中取出微信服务器返回的数据result = sr.ReadToEnd();}//-------------------------------第三步:根据返回的结果,计算客户端最终需要的参数-----------------////将返回的参数进一步计算出客户端本次支付需要的实际参数//并且根据协议格式 拼接客户端可以识别的网络消息return result;//读取微信返回的数据}/// <summary>/// 计算客户端调起微信支付所需要的参数/// </summary>/// <param name="str">微信服务器返回的数据</param>/// <returns>由参数加逗号拼接的字符串</returns>public string PayOrder(string str, OrderInfo orderinfo){//微信支付返回的是XML 需要进行解析XmlDocument doc = new XmlDocument();//防止xml被外部注入修改doc.XmlResolver = null;doc.LoadXml(str);XmlNode xml = doc.DocumentElement;//状态码:SUCCESS 成功 FAIL 失败if (xml["return_code"].InnerText == "FAIL")//获取预支付单号失败{Console.WriteLine("请求预支付单号异常:" + xml["return_msg"].InnerText);//实际上这里对错误 不应该直接处理 而是需要根据错误原因做相应的逻辑//如充值单号如果重复了 随机字符串如果重复了 就重新生成//但是 像这类异常 应该是在请求之前 就有相应的策略 而不应该等到这里来响应处理//错误码:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1return "error";}//请求参数:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12&index=2//解析得到以下的这些参数 再进行二次签名dic.Add("appid", xml["appid"].InnerText);dic.Add("partnerid", xml["mch_id"].InnerText);dic.Add("prepayid", xml["prepay_id"].InnerText);dic.Add("noncestr", xml["nonce_str"].InnerText);dic.Add("package", "Sign=WXPay");string timeStamp = TimeTool.ConvertDateTimeInt(DateTime.Now).ToString();dic.Add("timestamp", timeStamp);string sign = GetParamSrc(dic);//缓存订单信息 以便于在微信结果出来后 进行对比校验orderinfo.prepay_id = xml["prepay_id"].InnerText;orderinfo.sign = sign;orderDic.Add(orderinfo.out_trade_no, orderinfo);//将客户端所需要的参数进行返回string msg = xml["appid"].InnerText + "," + xml["mch_id"].InnerText + "," + xml["prepay_id"].InnerText + ","+ xml["nonce_str"].InnerText + "," + timeStamp + "," + packageValue + "," + sign;dic.Clear();//清空本次的数据return msg;}/// <summary> 设置用于验证服务器证书的回调 </summary>private bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors){return true; //总是接受 }/// <summary> 签名算法 </summary>public string GetParamSrc(Dictionary<string, string> dic){//-----------第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序-----////格式:键=值&键=值... 意义:获取sign参数StringBuilder str = new StringBuilder();//排序:升序var param1 = dic.OrderBy(x => x.Key).ToDictionary(x => x.Key, y => y.Value);//再从字典中 获取各个元素 拼接为XML的格式 键跟值之间 用"="连接起来foreach (string dic1 in param1.Keys){str.Append(dic1 + "=" + dic[dic1] + "&");}//-----------------第二步:拼接商户密钥 获取签名sign-----------------------------//str.Append("key=" + miyao);//把空字符串给移除替换掉 得到获取sign的字符串string getSignStr = str.ToString().Replace(" ", "");Console.WriteLine("第一次准备获取签名的字符串{0}", getSignStr);//从这里开始 是对str字符串进行MD5加密MD5 md5 = new MD5CryptoServiceProvider();byte[] bytValue, bytHash;bytValue = Encoding.UTF8.GetBytes(getSignStr);bytHash = md5.ComputeHash(bytValue);md5.Clear(); //释放掉MD5对象string tempStr = "";//按16进制的格式 将字节数组转化为等效字符串for (int i = 0; i < bytHash.Length; i++){tempStr += bytHash[i].ToString("X").PadLeft(2, '0');}//转化为大写 得到 sign签名参数string sign = tempStr.ToUpper();//------------------第三步 返回XML格式的字符串--------------------------//StringBuilder xmlStr = new StringBuilder();xmlStr.Append("<xml>");foreach (string dic1 in param1.Keys){xmlStr.Append("<" + dic1 + ">" + dic[dic1] + "</" + dic1 + ">");}//追加到XML尾部xmlStr.Append("<sign>" + sign + "</sign></xml>");Console.WriteLine("预支付请求参数:" + xmlStr.ToString().Replace(" ", ""));return xmlStr.ToString().Replace(" ", "");}/// <summary> 微信支付结果的监听 </summary>public void WeChatPayResultListener(){//HTP监听对象 监听微信给Order_Url地址发送的反馈if (httpListener == null){httpListener = new HttpListener();httpListener.Prefixes.Add(Order_Url);httpListener.Start();httpListener.BeginGetContext(new AsyncCallback(GetContextCallback), null);}}/// <summary>/// 异步处理请求/// </summary>/// <param name="ar"></param>private void GetContextCallback(IAsyncResult ar){try{HttpListenerContext context = httpListener.EndGetContext(ar);HttpListenerRequest request = context.Request;if (context != null){StreamReader body = new StreamReader(request.InputStream, Encoding.UTF8);//读取流,用来获取微信请求的数据string pay_notice = HttpUtility.UrlDecode(body.ReadToEnd(), Encoding.UTF8);//HttpUtility.UrlDecode:解码 //打印看看支付宝给我们发了什么Console.WriteLine("微信通知结果来了:" + pay_notice);//回应结果的对象HttpListenerResponse response = context.Response;//微信支付返回的是XML 需要进行解析XmlDocument doc = new XmlDocument();//防止xml被外部注入修改doc.XmlResolver = null;doc.LoadXml(pay_notice);XmlNode xml = doc.DocumentElement;//状态码:SUCCESS 成功 FAIL 失败if (xml["return_code"].InnerText == "SUCCESS"){//如果返回值是成功 就需要进一步检查 订单跟缓存 数据是否匹配OrderInfo checkData;if (orderDic.ContainsKey(xml["out_trade_no"].InnerText)){checkData = orderDic[xml["out_trade_no"].InnerText];}else{checkData = null;}if (checkData != null){//out_trade_no 商户订单号Console.WriteLine("支付成功:" + xml["return_code"].InnerText);if (xml["out_trade_no"].InnerText.Contains(checkData.out_trade_no)){//if (xml["sign"].InnerText.Contains(checkData.sign))//{//安全验证 单号对应的金额 if (int.Parse(xml["total_fee"].InnerText) == checkData.total_fee){Console.WriteLine("微信支付结果验证,单号金额一致");//消息协议 SendProps,会话ID/订单号,道具ID,数量...(道具实体)//向客户端发送道具string out_trade_no = xml["out_trade_no"].InnerText;string toClientMsg = "sendProps" + "," + out_trade_no + "," + checkData.objID + "," + checkData.objCount;byte[] sendByte = Encoding.UTF8.GetBytes(toClientMsg);checkData.clientAgent.SendBytes(sendByte);}else{Console.WriteLine("微信支付结果验证,单号金额不一致");//Todo 再进一步验证微信支付单号 ..... 如果一致 进一步排除原因 //最终如果确认是入侵的数据 再做相应的处理即可 }}else{Console.WriteLine("订单不匹配,商户订单号:{0},error:{1}", xml["out_trade_no"].InnerText, xml["err_code"].InnerText);}}//回应给微信服务器string responseStr = @"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";byte[] buffer = Encoding.UTF8.GetBytes(responseStr);response.ContentLength64 = buffer.Length;Stream output = response.OutputStream;output.Write(buffer, 0, buffer.Length);output.Close();response.Close();}else{Console.WriteLine("支付失败:" + xml["return_msg"].InnerText);//告诉客户端 支付失败以及具体的原因的}}}catch (Exception e){Console.WriteLine(e.ToString());}if (httpListener.IsListening){try{httpListener.BeginGetContext(new AsyncCallback(GetContextCallback), null);}catch (Exception e){Console.WriteLine(e.ToString());}}}}
}/// <summary>
/// 订单的数据模型: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
/// </summary>
public class OrderInfo
{public string appid = "wxc0d38c38f13506d4";//创建的应用IDpublic string mch_id = "1468506502";//商户IDpublic string nonce_str = "1add1a30ac87aa2db72f57a2375d8fec";//随机字符串 不长于32位public string body = "";//商品描述public string out_trade_no = "0078888899999988888";//订单号商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。public int total_fee = 1000;//1000分=1块钱public string spbill_create_ip = "";//用户终端实际IPpublic string notify_url = "";//139.196.112.69public string trade_type = "APP";public string scene_info = "大餐厅腾讯";//这里填写实际场景信息public string prepay_id;//预支付单号public string sign;//最终给客户端的签名public Agent clientAgent;//本次发起支付的客户端public string objID;//道具IDpublic int objCount;//道具数量public string transaction_id;//结果通知时返回的微信支付订单号
}
029 服务器部署
030 对IP使用的详细说明
031 客户端支付宝功能的修正
032 获取应用签名和测试所有功能
微信支付宝SDK接入服务器开发篇相关推荐
- Swift调用微信支付宝SDK(Swift4.0)
1.第一步在程序入口注册微信 (支付宝不需要) func application(_ application: UIApplication, didFinishLaunchingWithOption ...
- 微信商城小程序开发篇--前篇:商城列表项组件开发之一
最近在学习开发商城微信小程序,在开发商城的时候,我们可能在首页,商品列表页都会遇到商品项的展示,类似京东这种,但没这么复杂: 我将上面的这种商品展示暂且称之为竖向展示.我写过首页和商品列表页之后,发现 ...
- 微信支付宝合并为一个二维码支付的实现
背景说明 我们是一个位于三线城市的购物中心,虽然已经开业多年,但是受益于良好的招商和日常运营,客流量在同城同类型项目中一直比较稳定.在节假日出场车流高峰期的时候,由于人工收费的效率问题,会导致车辆积压 ...
- 微信小程序开发笔记 支付篇④——基于微信支付SDK实现Java后端接口使用
文章目录 一.前文 二.微信支付 Java SDK 三.示例 一.前文 微信小程序开发笔记--导读 微信支付-SDK与DEMO下载 先看README.md 二.微信支付 Java SDK 对微信支付开 ...
- 【微信小程序控制硬件⑥ 进阶篇】服务器如何集成七牛云存储SDK,把用户自定义设备图片存储在第三方服务器!
本博文由热爱分享热爱技术的半颗心脏原创,非官方人员.非组织名义编写,博文如有不对或侵犯您的权益,请及时留言,第一时间纠正! [微信小程序控制硬件①] 全网首发,借助 emq 消息服务器带你如何搭建微信 ...
- 支付宝支付 微信支付SDK接口不统一? 盘他!
开发过支付宝.微信支付的同学都知道,微信的支付 API 设计感觉是 Java 开发工程师写的,远不如支付宝 SDK 的接口设计用起来顺手.在这里,统一封装微信支付和支付宝支付的API,使两种支付方 ...
- iOS 支付宝支付 微信支付SDK接口不统一? 盘他!
开发过支付宝.微信支付的同学都知道,微信的支付 API 设计感觉是 Java 开发工程师写的,远不如支付宝 SDK 的接口设计用起来顺手.在这里,统一封装微信支付和支付宝支付的API,使两种支付方 ...
- 实战微信JS SDK开发:贺卡制作与播放(1)
前段时间忙于CanTK 2.0的开发,所以博客一直没有更新.CanTK 2.0主要增强了游戏和富媒体的开发,现在编码和测试基本完成了,等文档完成了再正式发布,里面有不少激动人心的功能,等发布时再一一细 ...
- 字节跳动小程序(抖音) uniapp PHP 支付宝SDK 开发支付功能
目录 前提条件 正文 代码流程: 详细代码 一.获取字节跳动订单 二.获取支付宝alipay_url参数 三.开始生成orderInfo 给前端吊起支付宝咯 总结哈 keke 前提条件 字节跳动: A ...
最新文章
- android 命令行创建模拟器,在命令行创建、删除和浏览AVD、使用android模拟器
- [Android]生成heap dump文件(.hprof)
- UA PHYS515A 电磁理论III 静磁学问题3 静磁学问题的边界条件与标量势方法的应用
- 160个Crackme010
- aix安装bff_AIX的yum安装
- java shiro登录实例_Shiro安全框架入门篇(登录验证实例详解与源码)
- BGP no-export
- 模板:后缀自动机(SAM)
- LeetCode MySQL 1412. 查找成绩处于中游的学生
- C++泛型算法的小总结
- 双机热备、集群及高可用性入门转载
- 慢日志之一:开启mysql慢查询日志并使用mysqldumpslow命令查看,分析诊断工具之四...
- Windows 11 配置使用 Edge 浏览器的 IE 兼容模式(永久)
- Linux7安装oracle11g报错 Error in invoking target 'agen
- 地震了,地震了!!!
- 一个FC游戏的下载站点
- ASP.NET搜索引擎
- 美国临床营养专家:冬季营养建议
- CPBL的选秀会有哪些规定和流程·棒球8号位
- 【整理分享】14张思维导图构建 Python 核心知识体系