微信支付宝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 服务器的工程结构

微信支付宝SDK接入服务器开发篇_第1张图片
微信支付宝SDK接入服务器开发篇_第2张图片

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();
                }
            }
        }

        ///  接收回调:处理网络消息 
        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 关闭连接的接口
        ///  关闭连接的socket 
        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;//请求支付的终端 IP
        public 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();
        }

        /// 
        /// 接受客户端的连接
        /// 
        private void AcceptClient()
        {
     
            //异步接收客户端的连接
            mListener.BeginAcceptSocket(new AsyncCallback(AcceptCallback), mListener);
            signal.WaitOne();
        }

        /// 
        /// 异步连接处理回调
        /// 
        /// 
        private void AcceptCallback(IAsyncResult result)
        {
     
            try
            {
     
                var listener = (TcpListener)result.AsyncState;
                Socket clienSocket = listener.EndAcceptSocket(result);
                //远程IP
                string 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
    {
     
        /// 获取订单需要支付的总金额
        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
    {
     
        ///  获取时间戳 
        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
        /// 
        /// 应用密钥
        /// 
        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==";

        /// 
        /// 支付宝公钥
        /// 
        string AliPay_Public_Key = @"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA37vnkTXPnA83r39o1TowQh8WChOgkbrR3ylQqWGm8eeFGn+wi1bhmNyO4MobIwgejgthVWTs/hXKr5YXUgTH3kEEZQpc+CP2YFroMyYKu5tl8my8Ki5LuPtnOnvEHNtMfXmm1TmqJzv5a+igjN3XY4x+CxKrK9hzzRplmpdoeJOtbQ1twYKk0wKRDitVY4ikbvKKy6mDaBh69pYfCpjAL9fDbewU7QjWKBg1YIFRlwOveUNSBdbflpiAr4DIS75xYrg8X/4i6u7p7M5Ma/tAV/Y0No3P++3roaiLaRzBkvmjQcL4akOlQ6klOCiKkcxYC/hEwNs1C+Gh8KuEKTFXXwIDAQAB";
        #endregion

        string 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;
        }

        ///  订单的集合 实际上 要持久化到数据库或者硬盘上 放在内存中会因为服务器重启而丢失已有的订单 
        Dictionary<string, AliPayOrderInfo> orderDic = new Dictionary<string, AliPayOrderInfo>();

        ///  阿里支付请求事件 
        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 AlipayTradeAppPayModel
            AliPayOrderInfo 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));

        }

        /// 
        /// 请求支付参数:https://docs.open.alipay.com/204/105465/
        /// 
        /// 客户端向安卓层(支付宝客户端SDK)请求的字符串
        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);//设置支付结果通知的地址

            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = client.SdkExecute(request);

            //(不用理这句注释)HttpUtility.HtmlEncode是为了输出到页面时防止被浏览器将关键参数html转义,实际打印到日志以及http传输不会有这个问题
            //通过Body获取到返回的参数
            return response.Body;
        }


        /// 监听阿里支付结果 https://docs.open.alipay.com/204/105301/ 
        public void ListenerAliPayResult()
        {
     
            //http监听器
            httpListener = new HttpListener();
            httpListener.Prefixes.Add(httpListenerUrl);
            httpListener.Start();
            //异步的方式处理请求
            httpListener.BeginGetContext(new AsyncCallback(CheckAliPayResult), null);
        }

        /// 解析结果以及校验
        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());
                }
            }
        }

        /// 
        /// 支付结果返回来的是字符串格式,而验证结果的API需要一个字典结构 so..提供这样的一个API
        /// 
        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 官方提供 固定的URL
        string 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

        ///  (服务器)微信组件的初始化 
        public void Init()
        {
     
            WeChatPayResultListener();
            //初始化的第一步:先启动监听支付结果的服务器
            //第二步:注册客户端发起的充值事件
            EventCenter.WeChatPay += WeChatPay;
        }

        ///  支付请求 
        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;//用户终端实际IP
            orderInfo.notify_url = notify_url;//支付结果通知地址
            orderInfo.trade_type = "APP";//交易类型

            //随机字符串 不长于32位 不能使用'.'符号拼接,如IP:127.0.0.1
            orderInfo.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++;
        }

        ///  请求客户端在微信支付时候所需的参数 
        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.expect100continue
                ServicePointManager.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_1
                return "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("");
            foreach (string dic1 in param1.Keys)
            {
     
                xmlStr.Append("<" + dic1 + ">" + dic[dic1] + " + dic1 + ">");
            }

            //追加到XML尾部
            xmlStr.Append("" + sign + "");
            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 = @"";
                        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";//创建的应用ID
    public string mch_id = "1468506502";//商户ID
    public string nonce_str = "1add1a30ac87aa2db72f57a2375d8fec";//随机字符串 不长于32public string body = "";//商品描述
    public string out_trade_no = "0078888899999988888";//订单号商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
    public int total_fee = 1000;//1000=1块钱
    public string spbill_create_ip = "";//用户终端实际IP
    public string notify_url = "";//139.196.112.69
    public string trade_type = "APP";
    public string scene_info = "大餐厅腾讯";//这里填写实际场景信息


    public string prepay_id;//预支付单号
    public string sign;//最终给客户端的签名

    public Agent clientAgent;//本次发起支付的客户端

    public string objID;//道具ID
    public int objCount;//道具数量

    public string transaction_id;//结果通知时返回的微信支付订单号
}

029 服务器部署

微信支付宝SDK接入服务器开发篇_第3张图片

030 对IP使用的详细说明

微信支付宝SDK接入服务器开发篇_第4张图片
微信支付宝SDK接入服务器开发篇_第5张图片
微信支付宝SDK接入服务器开发篇_第6张图片
微信支付宝SDK接入服务器开发篇_第7张图片

031 客户端支付宝功能的修正

微信支付宝SDK接入服务器开发篇_第8张图片

032 获取应用签名和测试所有功能

微信支付宝SDK接入服务器开发篇_第9张图片
微信支付宝SDK接入服务器开发篇_第10张图片
微信支付宝SDK接入服务器开发篇_第11张图片

你可能感兴趣的:(Unity微信支付宝SDK接入,网络通信,网络)