我是靠谱客的博主 轻松大碗,这篇文章主要介绍微信支付宝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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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(); } /// <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); //远程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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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"; #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; } /// <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 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)); } /// <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);//设置支付结果通知的地址 //这里和普通的接口调用不同,使用的是sdkExecute AlipayTradeAppPayResponse 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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
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 /// <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;//用户终端实际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++; } /// <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.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("<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";//创建的应用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 服务器部署

在这里插入图片描述

030 对IP使用的详细说明

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最后

以上就是轻松大碗最近收集整理的关于微信支付宝SDK接入服务器开发篇的全部内容,更多相关微信支付宝SDK接入服务器开发篇内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(113)

评论列表共有 0 条评论

立即
投稿
返回
顶部