概述
文章目录
- 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;//请求支付的终端 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
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";
#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
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";//随机字符串 不长于32位
public 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接入服务器开发篇所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复