文章目录

  • 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接入服务器开发篇相关推荐

  1. Swift调用微信支付宝SDK(Swift4.0)

    1.第一步在程序入口注册微信  (支付宝不需要) func application(_ application: UIApplication, didFinishLaunchingWithOption ...

  2. 微信商城小程序开发篇--前篇:商城列表项组件开发之一

    最近在学习开发商城微信小程序,在开发商城的时候,我们可能在首页,商品列表页都会遇到商品项的展示,类似京东这种,但没这么复杂: 我将上面的这种商品展示暂且称之为竖向展示.我写过首页和商品列表页之后,发现 ...

  3. 微信支付宝合并为一个二维码支付的实现

    背景说明 我们是一个位于三线城市的购物中心,虽然已经开业多年,但是受益于良好的招商和日常运营,客流量在同城同类型项目中一直比较稳定.在节假日出场车流高峰期的时候,由于人工收费的效率问题,会导致车辆积压 ...

  4. 微信小程序开发笔记 支付篇④——基于微信支付SDK实现Java后端接口使用

    文章目录 一.前文 二.微信支付 Java SDK 三.示例 一.前文 微信小程序开发笔记--导读 微信支付-SDK与DEMO下载 先看README.md 二.微信支付 Java SDK 对微信支付开 ...

  5. 【微信小程序控制硬件⑥ 进阶篇】服务器如何集成七牛云存储SDK,把用户自定义设备图片存储在第三方服务器!

    本博文由热爱分享热爱技术的半颗心脏原创,非官方人员.非组织名义编写,博文如有不对或侵犯您的权益,请及时留言,第一时间纠正! [微信小程序控制硬件①] 全网首发,借助 emq 消息服务器带你如何搭建微信 ...

  6. 支付宝支付 微信支付SDK接口不统一? 盘他!

      开发过支付宝.微信支付的同学都知道,微信的支付 API 设计感觉是 Java 开发工程师写的,远不如支付宝 SDK 的接口设计用起来顺手.在这里,统一封装微信支付和支付宝支付的API,使两种支付方 ...

  7. iOS 支付宝支付 微信支付SDK接口不统一? 盘他!

      开发过支付宝.微信支付的同学都知道,微信的支付 API 设计感觉是 Java 开发工程师写的,远不如支付宝 SDK 的接口设计用起来顺手.在这里,统一封装微信支付和支付宝支付的API,使两种支付方 ...

  8. 实战微信JS SDK开发:贺卡制作与播放(1)

    前段时间忙于CanTK 2.0的开发,所以博客一直没有更新.CanTK 2.0主要增强了游戏和富媒体的开发,现在编码和测试基本完成了,等文档完成了再正式发布,里面有不少激动人心的功能,等发布时再一一细 ...

  9. 字节跳动小程序(抖音) uniapp PHP 支付宝SDK 开发支付功能

    目录 前提条件 正文 代码流程: 详细代码 一.获取字节跳动订单 二.获取支付宝alipay_url参数 三.开始生成orderInfo 给前端吊起支付宝咯 总结哈 keke 前提条件 字节跳动: A ...

最新文章

  1. android 命令行创建模拟器,在命令行创建、删除和浏览AVD、使用android模拟器
  2. [Android]生成heap dump文件(.hprof)
  3. UA PHYS515A 电磁理论III 静磁学问题3 静磁学问题的边界条件与标量势方法的应用
  4. 160个Crackme010
  5. aix安装bff_AIX的yum安装
  6. java shiro登录实例_Shiro安全框架入门篇(登录验证实例详解与源码)
  7. BGP no-export
  8. 模板:后缀自动机(SAM)
  9. LeetCode MySQL 1412. 查找成绩处于中游的学生
  10. C++泛型算法的小总结
  11. 双机热备、集群及高可用性入门转载
  12. 慢日志之一:开启mysql慢查询日志并使用mysqldumpslow命令查看,分析诊断工具之四...
  13. Windows 11 配置使用 Edge 浏览器的 IE 兼容模式(永久)
  14. Linux7安装oracle11g报错 Error in invoking target 'agen
  15. 地震了,地震了!!!
  16. 一个FC游戏的下载站点
  17. ASP.NET搜索引擎
  18. 美国临床营养专家:冬季营养建议
  19. CPBL的选秀会有哪些规定和流程·棒球8号位
  20. 【整理分享】14张思维导图构建 Python 核心知识体系

热门文章

  1. 喜欢看的小说的可以收藏下
  2. IMX6ULL---SD卡制作与烧录步骤(重要)
  3. ETH2.0 要来了,要不要参与质押?
  4. 软件缺陷度量中用EXCEL制作柏拉图的方法
  5. 单招计算机面试技巧和注意事项,单招面试有哪些技巧和注意事项
  6. 平流式初沉池贮砂斗计算_污水的物理处理
  7. 提取游戏《Limbus Company》(边狱公司)内素材
  8. 高级软件工程--平时作业汇总
  9. 《使用「Markdown」编辑器的那些天 |CSDN编辑器测评》
  10. 2010上海世博会吉祥物--海宝昨晚揭晓