├── .gitattributes ├── .gitignore ├── App.Common ├── App.Common.csproj ├── Extension │ └── Serialize.cs ├── Properties │ └── AssemblyInfo.cs ├── QRCode │ └── QRCodeHelper.cs ├── SharpZip │ └── SharpZip.cs ├── ValidateCode │ └── GraphicValidateCode.cs └── packages.config ├── App.Pay ├── AliPay │ ├── AliPay.cs │ └── AliPayConfig.cs ├── App.Pay.csproj ├── Log.cs ├── Properties │ └── AssemblyInfo.cs ├── WePay │ ├── AppPay │ │ ├── AppPayApi.cs │ │ ├── AppPayConfig.cs │ │ ├── AppPayData.cs │ │ ├── AppPayHttpService.cs │ │ └── AppPayNotify.cs │ ├── Config.cs │ ├── Exception.cs │ ├── SafeXmlDocument.cs │ ├── WeHelper.cs │ └── XcxPay │ │ ├── XcxPayApi.cs │ │ ├── XcxPayConfig.cs │ │ ├── XcxPayData.cs │ │ ├── XcxPayHttpService.cs │ │ └── XcxPayNotify.cs └── packages.config ├── App.WebTest ├── App.WebTest.csproj ├── App_Start │ ├── BundleConfig.cs │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ └── WebApiConfig.cs ├── ApplicationInsights.config ├── Content │ ├── Site.css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap-theme.min.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ └── bootstrap.min.css.map ├── Controllers │ ├── BaseController.cs │ ├── HomeController.cs │ ├── Pay │ │ ├── AliPayController.cs │ │ ├── WeAppPayController.cs │ │ └── WeXcxPayController.cs │ ├── QRCode │ │ ├── QRCodeApiController.cs │ │ └── QRCodeController.cs │ └── ValidateController.cs ├── Global.asax ├── Global.asax.cs ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-3.3.1.intellisense.js │ ├── jquery-3.3.1.js │ ├── jquery-3.3.1.min.js │ ├── jquery-3.3.1.min.map │ ├── jquery-3.3.1.slim.js │ ├── jquery-3.3.1.slim.min.js │ ├── jquery-3.3.1.slim.min.map │ ├── jquery.validate-vsdoc.js │ ├── jquery.validate.js │ ├── jquery.validate.min.js │ ├── jquery.validate.unobtrusive.js │ ├── jquery.validate.unobtrusive.min.js │ └── modernizr-2.8.3.js ├── Views │ ├── Home │ │ ├── About.cshtml │ │ ├── Contact.cshtml │ │ └── Index.cshtml │ ├── QRCode │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── Validate │ │ └── ValidateIndex.cshtml │ ├── Web.config │ └── _ViewStart.cshtml ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 └── packages.config ├── README.md └── Utility.sln /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /App.Common/App.Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {50777A16-B198-47C9-9510-5B2C50C7B48A} 8 | Library 9 | Properties 10 | App.Common 11 | App.Common 12 | v4.6.1 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\packages\ICSharpCode.SharpZipLib.dll.0.85.4.369\lib\net20\ICSharpCode.SharpZipLib.dll 36 | 37 | 38 | False 39 | ..\..\..\民政局培训\code2\全国\源码\Web\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll 40 | 41 | 42 | ..\packages\QRCoder.1.3.6\lib\net40\QRCoder.dll 43 | 44 | 45 | 46 | 47 | 48 | ..\packages\System.Drawing.Common.4.5.0\lib\net461\System.Drawing.Common.dll 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /App.Common/Extension/Serialize.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace App.Common.Extension 9 | { 10 | public static class Serialize 11 | { 12 | public static string ToJson(this object obj) 13 | { 14 | return JsonConvert.SerializeObject(obj); 15 | } 16 | 17 | public static T JsonTo(this string obj) 18 | { 19 | return (T)JsonConvert.DeserializeObject(obj, typeof(T)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /App.Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("App.Common")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("App.Common")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("50777a16-b198-47c9-9510-5b2c50c7b48a")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /App.Common/QRCode/QRCodeHelper.cs: -------------------------------------------------------------------------------- 1 | using QRCoder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Drawing; 5 | using System.Drawing.Imaging; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace App.Common.QRCode 12 | { 13 | /// 14 | /// 二维码帮助类 15 | /// 16 | public class QRCodeHelper 17 | { 18 | /// 19 | /// 生成二维码图片 20 | /// 21 | /// 22 | /// 23 | public Bitmap CreateQRimg(string str) 24 | { 25 | QRCodeGenerator qrGenerator = new QRCodeGenerator(); 26 | QRCodeData qrCodeData = qrGenerator.CreateQrCode(str, QRCodeGenerator.ECCLevel.Q); 27 | QRCoder.QRCode qrCode = new QRCoder.QRCode(qrCodeData); 28 | Bitmap bt = qrCode.GetGraphic(20); 29 | 30 | return bt; 31 | } 32 | 33 | /// 34 | /// 生成二维码图片流(二维码上含文字) 35 | /// 36 | /// 二维码中要传递的数据(地址) 37 | /// 二维码上显示的文字说明 38 | public byte[] GenerateQRCode(string str1, string str2) 39 | { 40 | using (Image codeImage = CreateQRimg(str1), strImage = ConvertStringToImage(str2)) 41 | { 42 | Image img = CombineImage(600, 600, codeImage, 60, 50, strImage, 0, 530); 43 | using (var stream = new MemoryStream()) 44 | { 45 | img.Save(stream, ImageFormat.Jpeg); 46 | //输出图片流 47 | return stream.ToArray(); 48 | } 49 | } 50 | } 51 | 52 | /// 53 | /// 生成二维码图片流(不含文字) 54 | /// 55 | /// 二维码中要传递的数据(地址) 56 | /// 57 | public byte[] GenerateQRCode(string str) 58 | { 59 | using (Image codeImage = CreateQRimg(str)) 60 | { 61 | using (var stream = new MemoryStream()) 62 | { 63 | codeImage.Save(stream, ImageFormat.Jpeg); 64 | 65 | return stream.ToArray(); 66 | } 67 | } 68 | } 69 | 70 | /// 71 | /// 生成文字图片 72 | /// 73 | /// 74 | /// 75 | public Image ConvertStringToImage(string str) 76 | { 77 | Bitmap image = new Bitmap(600, 40, PixelFormat.Format24bppRgb); 78 | 79 | Graphics g = Graphics.FromImage(image); 80 | 81 | try 82 | { 83 | Font font = new Font("SimHei", 14, FontStyle.Regular); 84 | 85 | g.Clear(Color.White); 86 | 87 | StringFormat format = new StringFormat(); 88 | format.Alignment = StringAlignment.Center; 89 | format.LineAlignment = StringAlignment.Center; 90 | 91 | Rectangle rectangle = new Rectangle(0, 0, 600, 40); 92 | 93 | g.DrawString(str, font, new SolidBrush(Color.Black), rectangle, format); 94 | 95 | return image; 96 | } 97 | catch (Exception e) 98 | { 99 | throw e; 100 | } 101 | finally 102 | { 103 | GC.Collect(); 104 | } 105 | } 106 | 107 | /// 108 | /// 在画板中合并二维码图片和文字图片 109 | /// 110 | /// 111 | /// 112 | /// 113 | /// 114 | /// 115 | /// 116 | /// 117 | /// 118 | /// 119 | public Image CombineImage(int width, int height, Image imgLeft, int imgLeft_left, int imgLeft_top, Image imgRight, int imgRight_left, int imgRight_top) 120 | { 121 | Bitmap image = new Bitmap(width, height, PixelFormat.Format24bppRgb); 122 | 123 | Graphics g = Graphics.FromImage(image); 124 | 125 | try 126 | { 127 | g.Clear(Color.White); 128 | g.DrawImage(imgLeft, imgLeft_left, imgLeft_top, 500, 500); 129 | g.DrawImage(imgRight, imgRight_left, imgRight_top, imgRight.Width, imgRight.Height); 130 | 131 | return image; 132 | } 133 | catch (Exception e) 134 | { 135 | throw e; 136 | } 137 | finally 138 | { 139 | g.Dispose(); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /App.Common/SharpZip/SharpZip.cs: -------------------------------------------------------------------------------- 1 | using ICSharpCode.SharpZipLib.Checksums; 2 | using ICSharpCode.SharpZipLib.Zip; 3 | using Microsoft.Win32; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace App.Common.SharpZip 13 | { 14 | 15 | public class SharpZip 16 | { 17 | public SharpZip() { } 18 | 19 | /// 20 | /// 压缩 21 | /// 22 | /// 压缩后的文件名(包含物理路径) 23 | /// 待压缩的文件夹(包含物理路径) 24 | public static void PackFiles(string filename, string directory, string password) 25 | { 26 | try 27 | { 28 | FastZip fz = new FastZip(); 29 | fz.CreateEmptyDirectories = true; 30 | if (!string.IsNullOrEmpty(password)) 31 | { 32 | fz.Password = password; 33 | } 34 | fz.CreateZip(filename, directory, true, ""); 35 | fz = null; 36 | } 37 | catch (Exception) 38 | { 39 | throw; 40 | } 41 | } 42 | 43 | /// 44 | /// 解压缩 45 | /// 46 | /// 待解压文件名(包含物理路径) 47 | /// 解压到哪个目录中(包含物理路径) 48 | public static bool UnpackFiles(string file, string dir) 49 | { 50 | try 51 | { 52 | if (!Directory.Exists(dir)) 53 | { 54 | Directory.CreateDirectory(dir); 55 | } 56 | ZipInputStream s = new ZipInputStream(File.OpenRead(file)); 57 | ZipEntry theEntry; 58 | while ((theEntry = s.GetNextEntry()) != null) 59 | { 60 | string directoryName = Path.GetDirectoryName(theEntry.Name); 61 | string fileName = Path.GetFileName(theEntry.Name); 62 | if (directoryName != String.Empty) 63 | { 64 | // Directory.CreateDirectory(dir + directoryName); 65 | } 66 | if (fileName != String.Empty) 67 | { 68 | FileStream streamWriter = File.Create(dir + fileName); 69 | int size = 2048; 70 | byte[] data = new byte[2048]; 71 | while (true) 72 | { 73 | size = s.Read(data, 0, data.Length); 74 | if (size > 0) 75 | { 76 | streamWriter.Write(data, 0, size); 77 | } 78 | else 79 | { 80 | break; 81 | } 82 | } 83 | streamWriter.Close(); 84 | } 85 | } 86 | s.Close(); 87 | return true; 88 | } 89 | catch (Exception) 90 | { 91 | throw; 92 | } 93 | } 94 | } 95 | 96 | public class ClassZip 97 | { 98 | #region 私有方法 99 | /// 100 | /// 递归压缩文件夹方法 101 | /// 102 | private static bool ZipFileDictory(string FolderToZip, ZipOutputStream s, string ParentFolderName) 103 | { 104 | bool res = true; 105 | string[] folders, filenames; 106 | ZipEntry entry = null; 107 | FileStream fs = null; 108 | Crc32 crc = new Crc32(); 109 | try 110 | { 111 | entry = new ZipEntry(Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip) + "/")); 112 | s.PutNextEntry(entry); 113 | s.Flush(); 114 | filenames = Directory.GetFiles(FolderToZip); 115 | foreach (string file in filenames) 116 | { 117 | fs = File.OpenRead(file); 118 | byte[] buffer = new byte[fs.Length]; 119 | fs.Read(buffer, 0, buffer.Length); 120 | entry = new ZipEntry(Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip) + "/" + Path.GetFileName(file))); 121 | entry.DateTime = DateTime.Now; 122 | entry.Size = fs.Length; 123 | fs.Close(); 124 | crc.Reset(); 125 | crc.Update(buffer); 126 | entry.Crc = crc.Value; 127 | s.PutNextEntry(entry); 128 | s.Write(buffer, 0, buffer.Length); 129 | } 130 | } 131 | catch 132 | { 133 | res = false; 134 | } 135 | finally 136 | { 137 | if (fs != null) 138 | { 139 | fs.Close(); 140 | fs = null; 141 | } 142 | if (entry != null) 143 | { 144 | entry = null; 145 | } 146 | GC.Collect(); 147 | GC.Collect(1); 148 | } 149 | folders = Directory.GetDirectories(FolderToZip); 150 | foreach (string folder in folders) 151 | { 152 | if (!ZipFileDictory(folder, s, Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip)))) 153 | { 154 | return false; 155 | } 156 | } 157 | return res; 158 | } 159 | 160 | /// 161 | /// 压缩目录 162 | /// 163 | /// 待压缩的文件夹,全路径格式 164 | /// 压缩后的文件名,全路径格式 165 | private static bool ZipFileDictory(string FolderToZip, string ZipedFile, int level) 166 | { 167 | bool res; 168 | if (!Directory.Exists(FolderToZip)) 169 | { 170 | return false; 171 | } 172 | ZipOutputStream s = new ZipOutputStream(File.Create(ZipedFile)); 173 | s.SetLevel(level); 174 | res = ZipFileDictory(FolderToZip, s, ""); 175 | s.Finish(); 176 | s.Close(); 177 | return res; 178 | } 179 | 180 | /// 181 | /// 压缩文件 182 | /// 183 | /// 要进行压缩的文件名 184 | /// 压缩后生成的压缩文件名 185 | private static bool ZipFile(string FileToZip, string ZipedFile, int level) 186 | { 187 | if (!File.Exists(FileToZip)) 188 | { 189 | throw new System.IO.FileNotFoundException("指定要压缩的文件: " + FileToZip + " 不存在!"); 190 | } 191 | FileStream ZipFile = null; 192 | ZipOutputStream ZipStream = null; 193 | ZipEntry ZipEntry = null; 194 | bool res = true; 195 | try 196 | { 197 | ZipFile = File.OpenRead(FileToZip); 198 | byte[] buffer = new byte[ZipFile.Length]; 199 | ZipFile.Read(buffer, 0, buffer.Length); 200 | ZipFile.Close(); 201 | 202 | ZipFile = File.Create(ZipedFile); 203 | ZipStream = new ZipOutputStream(ZipFile); 204 | ZipEntry = new ZipEntry(Path.GetFileName(FileToZip)); 205 | ZipStream.PutNextEntry(ZipEntry); 206 | ZipStream.SetLevel(level); 207 | 208 | ZipStream.Write(buffer, 0, buffer.Length); 209 | } 210 | catch 211 | { 212 | res = false; 213 | } 214 | finally 215 | { 216 | if (ZipEntry != null) 217 | { 218 | ZipEntry = null; 219 | } 220 | if (ZipStream != null) 221 | { 222 | ZipStream.Finish(); 223 | ZipStream.Close(); 224 | } 225 | if (ZipFile != null) 226 | { 227 | ZipFile.Close(); 228 | ZipFile = null; 229 | } 230 | GC.Collect(); 231 | GC.Collect(1); 232 | } 233 | return res; 234 | } 235 | #endregion 236 | 237 | /// 238 | /// 压缩 239 | /// 240 | /// 待压缩的文件目录 241 | /// 生成的目标文件 242 | /// 6 243 | public static bool Zip(String FileToZip, String ZipedFile, int level) 244 | { 245 | if (Directory.Exists(FileToZip)) 246 | { 247 | return ZipFileDictory(FileToZip, ZipedFile, level); 248 | } 249 | else if (File.Exists(FileToZip)) 250 | { 251 | return ZipFile(FileToZip, ZipedFile, level); 252 | } 253 | else 254 | { 255 | return false; 256 | } 257 | } 258 | 259 | /// 260 | /// 解压 261 | /// 262 | /// 待解压的文件 263 | /// 解压目标存放目录 264 | public static void UnZip(string FileToUpZip, string ZipedFolder) 265 | { 266 | if (!File.Exists(FileToUpZip)) 267 | { 268 | return; 269 | } 270 | if (!Directory.Exists(ZipedFolder)) 271 | { 272 | Directory.CreateDirectory(ZipedFolder); 273 | } 274 | ZipInputStream s = null; 275 | ZipEntry theEntry = null; 276 | string fileName; 277 | FileStream streamWriter = null; 278 | try 279 | { 280 | s = new ZipInputStream(File.OpenRead(FileToUpZip)); 281 | while ((theEntry = s.GetNextEntry()) != null) 282 | { 283 | if (theEntry.Name != String.Empty) 284 | { 285 | fileName = Path.Combine(ZipedFolder, theEntry.Name); 286 | if (fileName.EndsWith("/") || fileName.EndsWith("\\")) 287 | { 288 | Directory.CreateDirectory(fileName); 289 | continue; 290 | } 291 | streamWriter = File.Create(fileName); 292 | int size = 2048; 293 | byte[] data = new byte[2048]; 294 | while (true) 295 | { 296 | size = s.Read(data, 0, data.Length); 297 | if (size > 0) 298 | { 299 | streamWriter.Write(data, 0, size); 300 | } 301 | else 302 | { 303 | break; 304 | } 305 | } 306 | } 307 | } 308 | } 309 | finally 310 | { 311 | if (streamWriter != null) 312 | { 313 | streamWriter.Close(); 314 | streamWriter = null; 315 | } 316 | if (theEntry != null) 317 | { 318 | theEntry = null; 319 | } 320 | if (s != null) 321 | { 322 | s.Close(); 323 | s = null; 324 | } 325 | GC.Collect(); 326 | GC.Collect(1); 327 | 328 | } 329 | } 330 | } 331 | 332 | public class ZipHelper 333 | { 334 | #region 私有变量 335 | String the_rar; 336 | RegistryKey the_Reg; 337 | Object the_Obj; 338 | String the_Info; 339 | ProcessStartInfo the_StartInfo; 340 | Process the_Process; 341 | #endregion 342 | 343 | /// 344 | /// 压缩 345 | /// 346 | /// 要解压的文件名 347 | /// 要压缩的文件目录 348 | /// 初始目录 349 | public void EnZip(string zipname, string zippath, string dirpath) 350 | { 351 | try 352 | { 353 | the_Reg = Registry.ClassesRoot.OpenSubKey(@"Applications\WinRAR.exe\Shell\Open\Command"); 354 | the_Obj = the_Reg.GetValue(""); 355 | the_rar = the_Obj.ToString(); 356 | the_Reg.Close(); 357 | the_rar = the_rar.Substring(1, the_rar.Length - 7); 358 | the_Info = " a " + zipname + " " + zippath; 359 | the_StartInfo = new ProcessStartInfo(); 360 | the_StartInfo.FileName = the_rar; 361 | the_StartInfo.Arguments = the_Info; 362 | the_StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 363 | the_StartInfo.WorkingDirectory = dirpath; 364 | the_Process = new Process(); 365 | the_Process.StartInfo = the_StartInfo; 366 | the_Process.Start(); 367 | } 368 | catch (Exception ex) 369 | { 370 | throw new Exception(ex.Message); 371 | } 372 | } 373 | 374 | /// 375 | /// 解压缩 376 | /// 377 | /// 要解压的文件名 378 | /// 要解压的文件路径 379 | public void DeZip(string zipname, string zippath) 380 | { 381 | try 382 | { 383 | the_Reg = Registry.ClassesRoot.OpenSubKey(@"Applications\WinRar.exe\Shell\Open\Command"); 384 | the_Obj = the_Reg.GetValue(""); 385 | the_rar = the_Obj.ToString(); 386 | the_Reg.Close(); 387 | the_rar = the_rar.Substring(1, the_rar.Length - 7); 388 | the_Info = " X " + zipname + " " + zippath; 389 | the_StartInfo = new ProcessStartInfo(); 390 | the_StartInfo.FileName = the_rar; 391 | the_StartInfo.Arguments = the_Info; 392 | the_StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 393 | the_Process = new Process(); 394 | the_Process.StartInfo = the_StartInfo; 395 | the_Process.Start(); 396 | } 397 | catch (Exception ex) 398 | { 399 | throw new Exception(ex.Message); 400 | } 401 | } 402 | 403 | 404 | 405 | /// 406 | /// 压缩文件夹 407 | /// 408 | /// 源目录 409 | /// ZipOutputStream对象 410 | public static void Compress(string source, ZipOutputStream s) 411 | { 412 | string[] filenames = Directory.GetFileSystemEntries(source); 413 | foreach (string file in filenames) 414 | { 415 | if (Directory.Exists(file)) 416 | { 417 | // 递归压缩子文件夹 418 | Compress(file, s); 419 | } 420 | else 421 | { 422 | using (FileStream fs = File.OpenRead(file)) 423 | { 424 | byte[] buffer = new byte[4 * 1024]; 425 | // 此处去掉盘符,如D:\123\1.txt 去掉D: 426 | ZipEntry entry = new ZipEntry(Path.GetFileName(file)); 427 | entry.DateTime = DateTime.Now; 428 | s.PutNextEntry(entry); 429 | int sourceBytes; 430 | do 431 | { 432 | sourceBytes = fs.Read(buffer, 0, buffer.Length); 433 | s.Write(buffer, 0, sourceBytes); 434 | } while (sourceBytes > 0); 435 | } 436 | } 437 | } 438 | } 439 | 440 | /// 441 | /// 压缩文件(多个) 442 | /// 443 | /// 文件集合 444 | /// 文件名 445 | public static bool Compress(List list, string path, string password) 446 | { 447 | try 448 | { 449 | using (var s = new ZipOutputStream(File.Create(path))) 450 | { 451 | s.SetLevel(6); 452 | if (!string.IsNullOrWhiteSpace(password)) 453 | { 454 | s.Password = password.Trim(); 455 | } 456 | foreach (string file in list) 457 | { 458 | if (Directory.Exists(file)) 459 | { 460 | // 递归压缩子文件夹 461 | Compress(file, s); 462 | } 463 | else 464 | { 465 | using (FileStream fs = File.OpenRead(file)) 466 | { 467 | // 此处去掉盘符,如D:\123\1.txt 去掉D: 468 | ZipEntry entry = new ZipEntry(Path.GetFileName(file)); 469 | //entry.DateTime = DateTime.Now; 470 | //entry.Size = fs.Length; 471 | s.PutNextEntry(entry); 472 | 473 | byte[] data = new byte[1024 * 1024]; 474 | int size = fs.Read(data, 0, data.Length); 475 | while (size > 0) 476 | { 477 | s.Write(data, 0, size); 478 | size = fs.Read(data, 0, data.Length); 479 | } 480 | } 481 | } 482 | } 483 | 484 | } 485 | 486 | return true; 487 | } 488 | catch (Exception ex) 489 | { 490 | } 491 | return false; 492 | } 493 | 494 | /// 495 | /// 解压缩 496 | /// 497 | /// 压缩包完整路径地址 498 | /// 解压路径是哪里 499 | /// 500 | public static bool Decompress(string sourceFile, string targetPath, string password) 501 | { 502 | if (!File.Exists(sourceFile)) 503 | { 504 | throw new FileNotFoundException(string.Format("未能找到文件 '{0}' ", sourceFile)); 505 | } 506 | if (!Directory.Exists(targetPath)) 507 | { 508 | Directory.CreateDirectory(targetPath); 509 | } 510 | using (var s = new ZipInputStream(File.OpenRead(sourceFile))) 511 | { 512 | if (!string.IsNullOrWhiteSpace(password)) 513 | { 514 | s.Password = password.Trim(); 515 | } 516 | 517 | ZipEntry theEntry; 518 | while ((theEntry = s.GetNextEntry()) != null) 519 | { 520 | if (theEntry.IsDirectory) 521 | { 522 | continue; 523 | } 524 | string directorName = Path.Combine(targetPath, Path.GetDirectoryName(theEntry.Name)); 525 | string fileName = Path.Combine(directorName, Path.GetFileName(theEntry.Name)); 526 | if (!Directory.Exists(directorName)) 527 | { 528 | Directory.CreateDirectory(directorName); 529 | } 530 | if (!String.IsNullOrEmpty(fileName)) 531 | { 532 | using (FileStream streamWriter = File.Create(fileName)) 533 | { 534 | byte[] data = new byte[1024 * 1024]; 535 | int size = s.Read(data, 0, data.Length); 536 | while (size > 0) 537 | { 538 | streamWriter.Write(data, 0, size); 539 | size = s.Read(data, 0, data.Length); 540 | } 541 | } 542 | } 543 | } 544 | } 545 | return true; 546 | } 547 | } 548 | } 549 | -------------------------------------------------------------------------------- /App.Common/ValidateCode/GraphicValidateCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace App.Common.ValidateCode 11 | { 12 | public class GraphicValidateCode 13 | { 14 | 15 | public GraphicValidateCode() 16 | { 17 | 18 | } 19 | 20 | /// 21 | /// 验证码的最大长度 22 | /// 23 | public int MaxLength 24 | { 25 | get { return 10; } 26 | } 27 | 28 | /// 29 | /// 验证码的最小长度 30 | /// 31 | public int MinLength 32 | { 33 | get { return 1; } 34 | } 35 | 36 | private float _fontSize = 14; 37 | private int _imageHeight = 26; 38 | 39 | /// 40 | /// 生成验证码 41 | /// 42 | /// 验证码的长度 43 | /// 44 | public string GenerateCode(int length) 45 | { 46 | var codeStr = new StringBuilder(); 47 | var random = new Random(); 48 | for (var i = 0; i < length; i++) 49 | { 50 | string str; 51 | var isCharFlag = random.Next(1, length); 52 | 53 | if (isCharFlag > 1) 54 | { 55 | str = ((char)random.Next(65, 91)).ToString(); 56 | } 57 | else 58 | { 59 | str = random.Next(1, 10).ToString(); 60 | } 61 | codeStr.Append(str); 62 | } 63 | 64 | return codeStr.ToString(); 65 | } 66 | 67 | #region 生成校验码图片 68 | public Bitmap CreateImageCode(string code) 69 | { 70 | int fSize = 60; 71 | int padding = 2; 72 | int fWidth = fSize + padding; 73 | var colors = new[] 74 | { 75 | Color.Black, Color.Red, Color.DarkBlue, Color.Green, 76 | Color.Brown, Color.DarkCyan, Color.Purple, Color.Orange 77 | }; 78 | var fonts = new[] { "Arial", "Georgia" }; 79 | 80 | int imageWidth = (code.Length * fWidth) + 4 + padding * 2; 81 | int imageHeight = fSize * 2 + padding; 82 | 83 | Bitmap image = new Bitmap(imageWidth, imageHeight); 84 | 85 | Graphics g = Graphics.FromImage(image); 86 | 87 | g.Clear(Color.White); 88 | 89 | Random rand = new Random(); 90 | 91 | //给背景添加随机生成的噪点 92 | Pen pen = new Pen(Color.LightBlue, 0); 93 | int c = code.Length * 10; 94 | 95 | for (int i = 0; i < c; i++) 96 | { 97 | int x = rand.Next(image.Width); 98 | int y = rand.Next(image.Height); 99 | 100 | g.DrawRectangle(pen, x, y, 1, 1); 101 | } 102 | 103 | int left = 0, top = 0, top1 = 1, top2 = 1; 104 | 105 | int n1 = (imageHeight - fSize - padding * 2); 106 | int n2 = n1 / 4; 107 | top1 = n2; 108 | top2 = n2 * 2; 109 | 110 | Font f; 111 | Brush b; 112 | 113 | int cindex, findex; 114 | 115 | 116 | //随机字体和颜色的验证码字符 117 | for (int i = 0; i < code.Length; i++) 118 | { 119 | cindex = rand.Next(colors.Length - 1); 120 | findex = rand.Next(fonts.Length - 1); 121 | 122 | f = new System.Drawing.Font(fonts[findex], fSize, System.Drawing.FontStyle.Bold); 123 | b = new System.Drawing.SolidBrush(colors[cindex]); 124 | 125 | if (i % 2 == 1) 126 | { 127 | top = top2; 128 | } 129 | else 130 | { 131 | top = top1; 132 | } 133 | left = i * fWidth; 134 | 135 | g.DrawString(code.Substring(i, 1), f, b, left, top); 136 | } 137 | 138 | //画一个边框 边框颜色为Color.Gainsboro 139 | g.DrawRectangle(new Pen(Color.Gainsboro, 0), 0, 0, image.Width - 1, image.Height - 1); 140 | g.Dispose(); 141 | 142 | //产生波形(Add By 51aspx.com) 143 | image = TwistImage(image, true, 8, 5); 144 | 145 | return image; 146 | 147 | } 148 | 149 | /// 150 | /// 正弦曲线Wave扭曲图片(Edit By 51aspx.com) 151 | /// 152 | /// 图片路径 153 | /// 如果扭曲则选择为True 154 | /// 波形的幅度倍数,越大扭曲的程度越高,一般为3 155 | /// 波形的起始相位,取值区间[0-2*PI) 156 | /// 157 | private Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase) 158 | { 159 | const double pi2 = 6.283185307179586476925286766559; 160 | var destBmp = new Bitmap(srcBmp.Width, srcBmp.Height); 161 | 162 | // 将位图背景填充为白色 163 | var graph = Graphics.FromImage(destBmp); 164 | graph.FillRectangle(new SolidBrush(Color.White), 0, 0, destBmp.Width, destBmp.Height); 165 | graph.Dispose(); 166 | 167 | double dBaseAxisLen = bXDir ? (double)destBmp.Height : (double)destBmp.Width; 168 | 169 | for (int i = 0; i < destBmp.Width; i++) 170 | { 171 | for (int j = 0; j < destBmp.Height; j++) 172 | { 173 | double dx = 0; 174 | dx = bXDir ? (pi2 * (double)j) / dBaseAxisLen : (pi2 * (double)i) / dBaseAxisLen; 175 | dx += dPhase; 176 | double dy = Math.Sin(dx); 177 | 178 | // 取得当前点的颜色 179 | int nOldX = 0, nOldY = 0; 180 | nOldX = bXDir ? i + (int)(dy * dMultValue) : i; 181 | nOldY = bXDir ? j : j + (int)(dy * dMultValue); 182 | 183 | Color color = srcBmp.GetPixel(i, j); 184 | if (nOldX >= 0 && nOldX < destBmp.Width 185 | && nOldY >= 0 && nOldY < destBmp.Height) 186 | { 187 | destBmp.SetPixel(nOldX, nOldY, color); 188 | } 189 | } 190 | } 191 | return destBmp; 192 | } 193 | 194 | #endregion 195 | 196 | /// 197 | /// 创建验证码的图片 198 | /// 199 | /// 200 | public byte[] GenerateValidateGraphic(string validateCode) 201 | { 202 | using (var image = CreateImageCode(validateCode)) 203 | { 204 | using (var stream = new MemoryStream()) 205 | { 206 | image.Save(stream, ImageFormat.Jpeg); 207 | //输出图片流 208 | return stream.ToArray(); 209 | } 210 | } 211 | } 212 | } 213 | 214 | public class ValidateCodeValue 215 | { 216 | /// 217 | /// 校验码 218 | /// 219 | public string Code { get; set; } 220 | 221 | /// 222 | /// 校验失效次数 223 | /// 224 | public int Times { get; set; } = 5; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /App.Common/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /App.Pay/AliPay/AliPay.cs: -------------------------------------------------------------------------------- 1 | using Aop.Api; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace App.Pay.AliPay 9 | { 10 | public class AliPay 11 | { 12 | public static IAopClient GetAlipayClient() 13 | { 14 | string serviceUrl = AliPayConfig.serviceUrl; 15 | 16 | string appId = AliPayConfig.appId; 17 | 18 | string privateKey = AliPayConfig.privateKey; 19 | 20 | string publivKey = AliPayConfig.publicKey; 21 | 22 | string format = AliPayConfig.format; 23 | 24 | string version = AliPayConfig.version; 25 | 26 | string signType = AliPayConfig.signType; 27 | 28 | string charset = AliPayConfig.charset; 29 | 30 | bool keyFromFile = AliPayConfig.keyFromFile; 31 | 32 | 33 | IAopClient client = new DefaultAopClient(serviceUrl, appId, privateKey, format, version, signType, publivKey, charset, keyFromFile); ; 34 | 35 | return client; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /App.Pay/AliPay/AliPayConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web.Configuration; 7 | 8 | namespace App.Pay.AliPay 9 | { 10 | public class AliPayConfig 11 | { 12 | //支付宝网关地址 13 | public static string serviceUrl = WebConfigurationManager.AppSettings["aliServiceUrl"].ToString(); 14 | 15 | //应用ID 16 | public static string appId = WebConfigurationManager.AppSettings["aliAppId"].ToString(); 17 | 18 | //开发者私钥,由开发者自己生成 19 | public static string privateKey = WebConfigurationManager.AppSettings["aliPrivateKey"].ToString(); 20 | 21 | //支付宝的应用公钥 22 | public static string publicKey = WebConfigurationManager.AppSettings["aliPublicKey"].ToString(); 23 | 24 | //支付宝的支付公钥 25 | public static string payKey = WebConfigurationManager.AppSettings["aliPayKey"].ToString(); 26 | 27 | //服务器异步通知页面路径 28 | public static string notify_url = WebConfigurationManager.AppSettings["aliNotifyUrl"].ToString(); 29 | 30 | //页面跳转同步通知页面路径 31 | public static string return_url = WebConfigurationManager.AppSettings["aliReturnUrl"].ToString(); 32 | 33 | //参数返回格式,只支持json 34 | public static string format = WebConfigurationManager.AppSettings["aliFormat"].ToString(); 35 | 36 | // 调用的接口版本,固定为:1.0 37 | public static string version = WebConfigurationManager.AppSettings["aliVersion"].ToString(); 38 | 39 | // 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 40 | public static string signType = WebConfigurationManager.AppSettings["aliSignType"].ToString(); 41 | 42 | // 字符编码格式 目前支持utf-8 43 | public static string charset = WebConfigurationManager.AppSettings["aliCharset"].ToString(); 44 | 45 | // false 表示不从文件加载密钥 46 | public static bool keyFromFile = false; 47 | 48 | // 日志记录 49 | public static string LogPath = WebConfigurationManager.AppSettings["AliLog"].ToString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /App.Pay/App.Pay.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {97617EB9-9E25-4819-8729-E98AF6B3D297} 8 | Library 9 | Properties 10 | App.Pay 11 | App.Pay 12 | v4.6.1 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\packages\Alipay.AopSdk.2.0.0\lib\net35\AopSdk.dll 36 | 37 | 38 | ..\packages\LitJson.0.13.0\lib\net45\LitJSON.dll 39 | 40 | 41 | False 42 | ..\..\..\民政局培训\code2\全国\源码\Web\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll 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 | {50777A16-B198-47C9-9510-5B2C50C7B48A} 78 | App.Common 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /App.Pay/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Web; 8 | 9 | namespace App.Pay 10 | { 11 | public class Log 12 | { 13 | //在网站根目录下创建日志目录 14 | public string path; 15 | 16 | public Log(string path) 17 | { 18 | this.path = HttpContext.Current.Request.PhysicalApplicationPath + path; 19 | } 20 | /** 21 | * 向日志文件写入调试信息 22 | * @param className 类名 23 | * @param content 写入内容 24 | */ 25 | public void Debug(string className, string content) 26 | { 27 | WriteLog("DEBUG", className, content); 28 | } 29 | 30 | /** 31 | * 向日志文件写入运行时信息 32 | * @param className 类名 33 | * @param content 写入内容 34 | */ 35 | public void Info(string className, string content) 36 | { 37 | WriteLog("INFO", className, content); 38 | } 39 | 40 | /** 41 | * 向日志文件写入出错信息 42 | * @param className 类名 43 | * @param content 写入内容 44 | */ 45 | public void Error(string className, string content) 46 | { 47 | WriteLog("ERROR", className, content); 48 | } 49 | 50 | /** 51 | * 实际的写日志操作 52 | * @param type 日志记录类型 53 | * @param className 类名 54 | * @param content 写入内容 55 | */ 56 | protected void WriteLog(string type, string className, string content) 57 | { 58 | if (!Directory.Exists(path))//如果日志目录不存在就创建 59 | { 60 | Directory.CreateDirectory(path); 61 | } 62 | 63 | string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");//获取当前系统时间 64 | string filename = path + "/" + DateTime.Now.ToString("yyyy-MM-dd") + ".log";//用日期对日志文件命名 65 | 66 | //创建或打开日志文件,向日志文件末尾追加记录 67 | StreamWriter mySw = File.AppendText(filename); 68 | 69 | //向日志文件写入内容 70 | string write_content = time + " " + type + " " + className + ": " + content; 71 | mySw.WriteLine(write_content); 72 | 73 | //关闭日志文件 74 | mySw.Close(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /App.Pay/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("App.Pay")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("App.Pay")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("97617eb9-9e25-4819-8729-e98af6b3d297")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /App.Pay/WePay/AppPay/AppPayConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web.Configuration; 7 | 8 | namespace App.Pay.WePay.AppPay 9 | { 10 | public class AppPayConfig : WePayConfig 11 | { 12 | //=======【基本信息设置】===================================== 13 | /* 微信公众号信息配置 14 | * APPID:绑定支付的APPID(必须配置) 15 | * MCHID:商户号(必须配置) 16 | * KEY:商户支付密钥,参考开户邮件设置(必须配置) 17 | * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置) 18 | */ 19 | public static string APPID = WebConfigurationManager.AppSettings["appWxAPPID"].ToString(); 20 | public static string MCHID = WebConfigurationManager.AppSettings["appWxMCHID"].ToString(); 21 | public static string KEY = WebConfigurationManager.AppSettings["appWxKEY"].ToString(); 22 | public static string APPSECRET = WebConfigurationManager.AppSettings["appWxAppSecret"].ToString(); 23 | 24 | //=======【证书路径设置】===================================== 25 | /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要) 26 | */ 27 | public const string SSLCERT_PATH = "cert/apiclient_cert.p12"; 28 | public const string SSLCERT_PASSWORD = "1233410002"; 29 | 30 | 31 | 32 | //=======【支付结果通知url】===================================== 33 | /* 支付结果通知回调url,用于商户接收支付结果 34 | */ 35 | public static string NOTIFY_URL = WebConfigurationManager.AppSettings["WxAppNotifyUrl"].ToString();//ConfigurationManager.AppSettings["WxNotifyUrl"]; 36 | 37 | // log记录 38 | public static string LogPath = WebConfigurationManager.AppSettings["WeAppLog"].ToString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /App.Pay/WePay/AppPay/AppPayData.cs: -------------------------------------------------------------------------------- 1 | using LitJson; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml; 9 | 10 | namespace App.Pay.WePay.AppPay 11 | { 12 | /// 13 | /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构, 14 | /// 在调用接口之前先填充各个字段的值,然后进行接口通信, 15 | /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构, 16 | /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构 17 | /// 18 | public class AppPayData 19 | { 20 | private Log Log = new Log(AppPayConfig.LogPath); 21 | 22 | public AppPayData() 23 | { 24 | } 25 | 26 | //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序 27 | private SortedDictionary m_values = new SortedDictionary(); 28 | 29 | /** 30 | * 设置某个字段的值 31 | * @param key 字段名 32 | * @param value 字段值 33 | */ 34 | public void SetValue(string key, object value) 35 | { 36 | m_values[key] = value; 37 | } 38 | 39 | /** 40 | * 根据字段名获取某个字段的值 41 | * @param key 字段名 42 | * @return key对应的字段值 43 | */ 44 | public object GetValue(string key) 45 | { 46 | object o = null; 47 | m_values.TryGetValue(key, out o); 48 | return o; 49 | } 50 | 51 | /** 52 | * 判断某个字段是否已设置 53 | * @param key 字段名 54 | * @return 若字段key已被设置,则返回true,否则返回false 55 | */ 56 | public bool IsSet(string key) 57 | { 58 | object o = null; 59 | m_values.TryGetValue(key, out o); 60 | if (null != o) 61 | return true; 62 | else 63 | return false; 64 | } 65 | 66 | /** 67 | * @将Dictionary转成xml 68 | * @return 经转换得到的xml串 69 | * @throws WePayException 70 | **/ 71 | public string ToXml() 72 | { 73 | //数据为空时不能转化为xml格式 74 | if (0 == m_values.Count) 75 | { 76 | Log.Error(this.GetType().ToString(), "WxPayData数据为空!"); 77 | throw new WePayException("WxPayData数据为空!"); 78 | } 79 | 80 | string xml = ""; 81 | foreach (KeyValuePair pair in m_values) 82 | { 83 | //字段值不能为null,会影响后续流程 84 | if (pair.Value == null) 85 | { 86 | Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!"); 87 | throw new WePayException("WxPayData内部含有值为null的字段!"); 88 | } 89 | 90 | if (pair.Value.GetType() == typeof(int)) 91 | { 92 | xml += "<" + pair.Key + ">" + pair.Value + ""; 93 | } 94 | else if (pair.Value.GetType() == typeof(string)) 95 | { 96 | xml += "<" + pair.Key + ">" + ""; 97 | } 98 | else//除了string和int类型不能含有其他数据类型 99 | { 100 | Log.Error(this.GetType().ToString(), "WxPayData字段数据类型错误!"); 101 | throw new WePayException("WxPayData字段数据类型错误!"); 102 | } 103 | } 104 | xml += ""; 105 | return xml; 106 | } 107 | 108 | /** 109 | * @将xml转为WxPayData对象并返回对象内部的数据 110 | * @param string 待转换的xml串 111 | * @return 经转换得到的Dictionary 112 | * @throws WePayException 113 | */ 114 | public SortedDictionary FromXml(string xml) 115 | { 116 | if (string.IsNullOrEmpty(xml)) 117 | { 118 | Log.Error(this.GetType().ToString(), "将空的xml串转换为WxPayData不合法!"); 119 | throw new WePayException("将空的xml串转换为WxPayData不合法!"); 120 | } 121 | 122 | SafeXmlDocument xmlDoc = new SafeXmlDocument(); 123 | xmlDoc.LoadXml(xml); 124 | XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点 125 | XmlNodeList nodes = xmlNode.ChildNodes; 126 | foreach (XmlNode xn in nodes) 127 | { 128 | XmlElement xe = (XmlElement)xn; 129 | m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中 130 | } 131 | 132 | try 133 | { 134 | //2015-06-29 错误是没有签名 135 | if (m_values["return_code"] != "SUCCESS") 136 | { 137 | return m_values; 138 | } 139 | CheckSign();//验证签名,不通过会抛异常 140 | } 141 | catch (WePayException ex) 142 | { 143 | throw new WePayException(ex.Message); 144 | } 145 | 146 | return m_values; 147 | } 148 | 149 | /** 150 | * @Dictionary格式转化成url参数格式 151 | * @ return url格式串, 该串不包含sign字段值 152 | */ 153 | public string ToUrl() 154 | { 155 | string buff = ""; 156 | foreach (KeyValuePair pair in m_values) 157 | { 158 | if (pair.Value == null) 159 | { 160 | Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!"); 161 | throw new WePayException("WxPayData内部含有值为null的字段!"); 162 | } 163 | 164 | if (pair.Key != "sign" && pair.Value.ToString() != "") 165 | { 166 | buff += pair.Key + "=" + pair.Value + "&"; 167 | } 168 | } 169 | buff = buff.Trim('&'); 170 | return buff; 171 | } 172 | 173 | 174 | /** 175 | * @Dictionary格式化成Json 176 | * @return json串数据 177 | */ 178 | public string ToJson() 179 | { 180 | string jsonStr = JsonMapper.ToJson(m_values); 181 | return jsonStr; 182 | } 183 | 184 | /** 185 | * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串) 186 | */ 187 | public string ToPrintStr() 188 | { 189 | string str = ""; 190 | foreach (KeyValuePair pair in m_values) 191 | { 192 | if (pair.Value == null) 193 | { 194 | Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!"); 195 | throw new WePayException("WxPayData内部含有值为null的字段!"); 196 | } 197 | 198 | str += string.Format("{0}={1}
", pair.Key, pair.Value.ToString()); 199 | } 200 | Log.Debug(this.GetType().ToString(), "Print in Web Page : " + str); 201 | return str; 202 | } 203 | 204 | /** 205 | * @生成签名,详见签名生成算法 206 | * @return 签名, sign字段不参加签名 207 | */ 208 | public string MakeSign() 209 | { 210 | //转url格式 211 | string str = ToUrl(); 212 | //在string后加入API KEY 213 | str += "&key=" + AppPayConfig.KEY; 214 | //MD5加密 215 | var md5 = MD5.Create(); 216 | var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str)); 217 | var sb = new StringBuilder(); 218 | foreach (byte b in bs) 219 | { 220 | sb.Append(b.ToString("x2")); 221 | } 222 | //所有字符转为大写 223 | return sb.ToString().ToUpper(); 224 | } 225 | 226 | /** 227 | * 228 | * 检测签名是否正确 229 | * 正确返回true,错误抛异常 230 | */ 231 | public bool CheckSign() 232 | { 233 | //如果没有设置签名,则跳过检测 234 | if (!IsSet("sign")) 235 | { 236 | Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!"); 237 | throw new WePayException("WxPayData签名存在但不合法!"); 238 | } 239 | //如果设置了签名但是签名为空,则抛异常 240 | else if (GetValue("sign") == null || GetValue("sign").ToString() == "") 241 | { 242 | Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!"); 243 | throw new WePayException("WxPayData签名存在但不合法!"); 244 | } 245 | 246 | //获取接收到的签名 247 | string return_sign = GetValue("sign").ToString(); 248 | 249 | //在本地计算新的签名 250 | string cal_sign = MakeSign(); 251 | 252 | if (cal_sign == return_sign) 253 | { 254 | return true; 255 | } 256 | 257 | Log.Error(this.GetType().ToString(), "WxPayData签名验证错误!"); 258 | throw new WePayException("WxPayData签名验证错误!"); 259 | } 260 | 261 | /** 262 | * @获取Dictionary 263 | */ 264 | public SortedDictionary GetValues() 265 | { 266 | return m_values; 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /App.Pay/WePay/AppPay/AppPayHttpService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Security; 7 | using System.Security.Cryptography.X509Certificates; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Web; 11 | 12 | namespace App.Pay.WePay.AppPay 13 | { 14 | public class AppPayHttpService 15 | { 16 | private static Log Log = new Log(AppPayConfig.LogPath); 17 | 18 | public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 19 | { 20 | //直接确认,否则打不开 21 | return true; 22 | } 23 | 24 | public static string Post(string xml, string url, bool isUseCert, int timeout) 25 | { 26 | System.GC.Collect();//垃圾回收,回收没有正常关闭的http连接 27 | 28 | string result = "";//返回结果 29 | 30 | HttpWebRequest request = null; 31 | HttpWebResponse response = null; 32 | Stream reqStream = null; 33 | 34 | try 35 | { 36 | //设置最大连接数 37 | ServicePointManager.DefaultConnectionLimit = 200; 38 | //设置https验证方式 39 | if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 40 | { 41 | ServicePointManager.ServerCertificateValidationCallback = 42 | new RemoteCertificateValidationCallback(CheckValidationResult); 43 | } 44 | 45 | /*************************************************************** 46 | * 下面设置HttpWebRequest的相关属性 47 | * ************************************************************/ 48 | request = (HttpWebRequest)WebRequest.Create(url); 49 | 50 | request.Method = "POST"; 51 | request.Timeout = timeout * 1000; 52 | 53 | //设置代理服务器 54 | //WebProxy proxy = new WebProxy(); //定义一个网关对象 55 | //proxy.Address = new Uri(WxPayConfig.PROXY_URL); //网关服务器端口:端口 56 | //request.Proxy = proxy; 57 | 58 | //设置POST的数据类型和长度 59 | request.ContentType = "text/xml"; 60 | byte[] data = System.Text.Encoding.UTF8.GetBytes(xml); 61 | request.ContentLength = data.Length; 62 | 63 | //是否使用证书 64 | if (isUseCert) 65 | { 66 | string path = HttpContext.Current.Request.PhysicalApplicationPath; 67 | X509Certificate2 cert = new X509Certificate2(path + AppPayConfig.SSLCERT_PATH, AppPayConfig.SSLCERT_PASSWORD); 68 | request.ClientCertificates.Add(cert); 69 | //Log.Debug("WxPayApi", "PostXml used cert"); 70 | } 71 | 72 | //往服务器写入数据 73 | reqStream = request.GetRequestStream(); 74 | reqStream.Write(data, 0, data.Length); 75 | reqStream.Close(); 76 | 77 | //获取服务端返回 78 | response = (HttpWebResponse)request.GetResponse(); 79 | 80 | //获取服务端返回数据 81 | StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 82 | result = sr.ReadToEnd().Trim(); 83 | sr.Close(); 84 | } 85 | catch (System.Threading.ThreadAbortException e) 86 | { 87 | Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting."); 88 | Log.Error("Exception message: {0}", e.Message); 89 | System.Threading.Thread.ResetAbort(); 90 | } 91 | catch (WebException e) 92 | { 93 | Log.Error("HttpService", e.ToString()); 94 | if (e.Status == WebExceptionStatus.ProtocolError) 95 | { 96 | Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 97 | Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 98 | } 99 | throw new WePayException(e.ToString()); 100 | } 101 | catch (Exception e) 102 | { 103 | Log.Error("HttpService", e.ToString()); 104 | throw new WePayException(e.ToString()); 105 | } 106 | finally 107 | { 108 | //关闭连接和流 109 | if (response != null) 110 | { 111 | response.Close(); 112 | } 113 | if (request != null) 114 | { 115 | request.Abort(); 116 | } 117 | } 118 | return result; 119 | } 120 | 121 | /// 122 | /// 处理http GET请求,返回数据 123 | /// 124 | /// 请求的url地址 125 | /// http GET成功后返回的数据,失败抛WebException异常 126 | public static string Get(string url) 127 | { 128 | System.GC.Collect(); 129 | string result = ""; 130 | 131 | HttpWebRequest request = null; 132 | HttpWebResponse response = null; 133 | 134 | //请求url以获取数据 135 | try 136 | { 137 | //设置最大连接数 138 | ServicePointManager.DefaultConnectionLimit = 200; 139 | //设置https验证方式 140 | if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 141 | { 142 | ServicePointManager.ServerCertificateValidationCallback = 143 | new RemoteCertificateValidationCallback(CheckValidationResult); 144 | } 145 | 146 | /*************************************************************** 147 | * 下面设置HttpWebRequest的相关属性 148 | * ************************************************************/ 149 | request = (HttpWebRequest)WebRequest.Create(url); 150 | 151 | request.Method = "GET"; 152 | 153 | //设置代理 154 | //WebProxy proxy = new WebProxy(); 155 | //proxy.Address = new Uri(WxPayConfig.PROXY_URL); 156 | //request.Proxy = proxy; 157 | 158 | //获取服务器返回 159 | response = (HttpWebResponse)request.GetResponse(); 160 | 161 | //获取HTTP返回数据 162 | StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 163 | result = sr.ReadToEnd().Trim(); 164 | sr.Close(); 165 | } 166 | catch (System.Threading.ThreadAbortException e) 167 | { 168 | Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting."); 169 | Log.Error("Exception message: {0}", e.Message); 170 | System.Threading.Thread.ResetAbort(); 171 | } 172 | catch (WebException e) 173 | { 174 | Log.Error("HttpService", e.ToString()); 175 | if (e.Status == WebExceptionStatus.ProtocolError) 176 | { 177 | Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 178 | Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 179 | } 180 | throw new WePayException(e.ToString()); 181 | } 182 | catch (Exception e) 183 | { 184 | Log.Error("HttpService", e.ToString()); 185 | throw new WePayException(e.ToString()); 186 | } 187 | finally 188 | { 189 | //关闭连接和流 190 | if (response != null) 191 | { 192 | response.Close(); 193 | } 194 | if (request != null) 195 | { 196 | request.Abort(); 197 | } 198 | } 199 | return result; 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /App.Pay/WePay/AppPay/AppPayNotify.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | 8 | namespace App.Pay.WePay.AppPay 9 | { 10 | /// 11 | /// 回调处理基类 12 | /// 主要负责接收微信支付后台发送过来的数据,对数据进行签名验证 13 | /// 子类在此类基础上进行派生并重写自己的回调处理过程 14 | /// 15 | public class AppPayNotify 16 | { 17 | public HttpContext context { get; set; } 18 | public Log Log = new Log(AppPayConfig.LogPath); 19 | 20 | public AppPayNotify(HttpContext context) 21 | { 22 | this.context = context; 23 | } 24 | 25 | /// 26 | /// 接收从微信支付后台发送过来的数据并验证签名 27 | /// 28 | /// 微信支付后台返回的数据 29 | public AppPayData GetNotifyData() 30 | { 31 | //接收从微信后台POST过来的数据 32 | System.IO.Stream s = context.Request.InputStream; 33 | int count = 0; 34 | byte[] buffer = new byte[1024]; 35 | StringBuilder builder = new StringBuilder(); 36 | while ((count = s.Read(buffer, 0, 1024)) > 0) 37 | { 38 | builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 39 | } 40 | s.Flush(); 41 | s.Close(); 42 | s.Dispose(); 43 | 44 | //转换数据格式并验证签名 45 | AppPayData data = new AppPayData(); 46 | try 47 | { 48 | data.FromXml(builder.ToString()); 49 | } 50 | catch (WePayException ex) 51 | { 52 | //若签名错误,则立即返回结果给微信支付后台 53 | AppPayData res = new AppPayData(); 54 | res.SetValue("return_code", "FAIL"); 55 | res.SetValue("return_msg", ex.Message); 56 | Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml()); 57 | context.Response.Write(res.ToXml()); 58 | context.Response.End(); 59 | } 60 | 61 | Log.Info(this.GetType().ToString(), "Check sign success"); 62 | return data; 63 | } 64 | 65 | //派生类需要重写这个方法,进行不同的回调处理 66 | public virtual void ProcessNotify() 67 | { 68 | 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /App.Pay/WePay/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace App.Pay.WePay 8 | { 9 | /** 10 | * 配置账号信息 11 | */ 12 | public class WePayConfig 13 | { 14 | //=======【商户系统后台机器IP】===================================== 15 | /* 此参数可手动配置也可在程序中自动获取 16 | */ 17 | public const string IP = "8.8.8.8"; 18 | 19 | 20 | //=======【代理服务器设置】=================================== 21 | /* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置) 22 | */ 23 | public const string PROXY_URL = ""; 24 | 25 | //=======【上报信息配置】=================================== 26 | /* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报 27 | */ 28 | public const int REPORT_LEVENL = 1; 29 | 30 | //=======【日志级别】=================================== 31 | /* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息 32 | */ 33 | public const int LOG_LEVENL = 3; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /App.Pay/WePay/Exception.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace App.Pay.WePay 8 | { 9 | public class WePayException : Exception 10 | { 11 | public WePayException(string msg) : base(msg) 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /App.Pay/WePay/SafeXmlDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Xml; 7 | 8 | namespace App.Pay.WePay 9 | { 10 | public class SafeXmlDocument : XmlDocument 11 | { 12 | public SafeXmlDocument() 13 | { 14 | this.XmlResolver = null; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /App.Pay/WePay/WeHelper.cs: -------------------------------------------------------------------------------- 1 | using App.Common.Extension; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Configuration; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Security.Cryptography; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace App.Pay.WePay 13 | { 14 | public class WeHelper 15 | { 16 | // 小程序 17 | private static string _appid = ConfigurationManager.AppSettings["wxAPPID"]; 18 | // 小程序 19 | private static string _appSecret = ConfigurationManager.AppSettings["wxAppSecret"]; 20 | 21 | public static WxSession Code2Session(string code) 22 | { 23 | var url = $"https://api.weixin.qq.com/sns/jscode2session?appid={_appid}&secret={_appSecret}&js_code={code}&grant_type=authorization_code"; 24 | try 25 | { 26 | var request = WebRequest.Create(url); 27 | using (var response = request.GetResponse()) 28 | { 29 | using (var rs = response.GetResponseStream()) 30 | { 31 | using (var s = new System.IO.StreamReader(rs)) 32 | { 33 | return s.ReadToEnd().JsonTo(); 34 | } 35 | } 36 | } 37 | } 38 | catch (Exception) 39 | { 40 | return null; 41 | } 42 | } 43 | } 44 | 45 | public class WxSession 46 | { 47 | public string openid { get; set; } 48 | public string session_key { get; set; } 49 | public string errcode { get; set; } 50 | public string errMsg { get; set; } 51 | public string unionid { get; set; } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /App.Pay/WePay/XcxPay/XcxPayConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web.Configuration; 7 | 8 | namespace App.Pay.WePay.XcxPay 9 | { 10 | public class XcxPayConfig : WePayConfig 11 | { 12 | //=======【基本信息设置】===================================== 13 | /* 微信公众号信息配置 14 | * APPID:绑定支付的APPID(必须配置) 15 | * MCHID:商户号(必须配置) 16 | * KEY:商户支付密钥,参考开户邮件设置(必须配置) 17 | * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置) 18 | */ 19 | /// 小程序支付 20 | public static string APPID = WebConfigurationManager.AppSettings["XcxAppID"].ToString(); 21 | public static string MCHID = WebConfigurationManager.AppSettings["XcxMchID"].ToString(); 22 | public static string KEY = WebConfigurationManager.AppSettings["XcxKey"].ToString(); 23 | public static string APPSECRET = WebConfigurationManager.AppSettings["XcxAppSecret"].ToString(); 24 | 25 | //=======【证书路径设置】===================================== 26 | /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要) 27 | */ 28 | public const string SSLCERT_PATH = "cert/apiclient_cert.p12"; 29 | public const string SSLCERT_PASSWORD = "1233410002"; 30 | 31 | //=======【支付结果通知url】===================================== 32 | /* 支付结果通知回调url,用于商户接收支付结果 33 | */ 34 | public static string NOTIFY_URL = WebConfigurationManager.AppSettings["XcxNotifyUrl"].ToString(); 35 | 36 | // log记录 37 | public static string LogPath = WebConfigurationManager.AppSettings["XcxLog"].ToString(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /App.Pay/WePay/XcxPay/XcxPayData.cs: -------------------------------------------------------------------------------- 1 | using LitJson; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml; 9 | 10 | namespace App.Pay.WePay.XcxPay 11 | { 12 | /// 13 | /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构, 14 | /// 在调用接口之前先填充各个字段的值,然后进行接口通信, 15 | /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构, 16 | /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构 17 | /// 18 | public class XcxPayData 19 | { 20 | private Log Log = new Log(XcxPayConfig.LogPath); 21 | 22 | public XcxPayData() 23 | { 24 | } 25 | 26 | //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序 27 | private SortedDictionary m_values = new SortedDictionary(); 28 | 29 | /** 30 | * 设置某个字段的值 31 | * @param key 字段名 32 | * @param value 字段值 33 | */ 34 | public void SetValue(string key, object value) 35 | { 36 | m_values[key] = value; 37 | } 38 | 39 | /** 40 | * 根据字段名获取某个字段的值 41 | * @param key 字段名 42 | * @return key对应的字段值 43 | */ 44 | public object GetValue(string key) 45 | { 46 | object o = null; 47 | m_values.TryGetValue(key, out o); 48 | return o; 49 | } 50 | 51 | /** 52 | * 判断某个字段是否已设置 53 | * @param key 字段名 54 | * @return 若字段key已被设置,则返回true,否则返回false 55 | */ 56 | public bool IsSet(string key) 57 | { 58 | object o = null; 59 | m_values.TryGetValue(key, out o); 60 | if (null != o) 61 | return true; 62 | else 63 | return false; 64 | } 65 | 66 | /** 67 | * @将Dictionary转成xml 68 | * @return 经转换得到的xml串 69 | * @throws WePayException 70 | **/ 71 | public string ToXml() 72 | { 73 | //数据为空时不能转化为xml格式 74 | if (0 == m_values.Count) 75 | { 76 | Log.Error(this.GetType().ToString(), "WxPayData数据为空!"); 77 | throw new WePayException("WxPayData数据为空!"); 78 | } 79 | 80 | string xml = ""; 81 | foreach (KeyValuePair pair in m_values) 82 | { 83 | //字段值不能为null,会影响后续流程 84 | if (pair.Value == null) 85 | { 86 | Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!"); 87 | throw new WePayException("WxPayData内部含有值为null的字段!"); 88 | } 89 | 90 | if (pair.Value.GetType() == typeof(int)) 91 | { 92 | xml += "<" + pair.Key + ">" + pair.Value + ""; 93 | } 94 | else if (pair.Value.GetType() == typeof(string)) 95 | { 96 | xml += "<" + pair.Key + ">" + ""; 97 | } 98 | else//除了string和int类型不能含有其他数据类型 99 | { 100 | Log.Error(this.GetType().ToString(), "WxPayData字段数据类型错误!"); 101 | throw new WePayException("WxPayData字段数据类型错误!"); 102 | } 103 | } 104 | xml += ""; 105 | return xml; 106 | } 107 | 108 | /** 109 | * @将xml转为WxPayData对象并返回对象内部的数据 110 | * @param string 待转换的xml串 111 | * @return 经转换得到的Dictionary 112 | * @throws WePayException 113 | */ 114 | public SortedDictionary FromXml(string xml) 115 | { 116 | if (string.IsNullOrEmpty(xml)) 117 | { 118 | Log.Error(this.GetType().ToString(), "将空的xml串转换为WxPayData不合法!"); 119 | throw new WePayException("将空的xml串转换为WxPayData不合法!"); 120 | } 121 | 122 | SafeXmlDocument xmlDoc = new SafeXmlDocument(); 123 | xmlDoc.LoadXml(xml); 124 | XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点 125 | XmlNodeList nodes = xmlNode.ChildNodes; 126 | foreach (XmlNode xn in nodes) 127 | { 128 | XmlElement xe = (XmlElement)xn; 129 | m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中 130 | } 131 | 132 | try 133 | { 134 | //2015-06-29 错误是没有签名 135 | if (m_values["return_code"] != "SUCCESS") 136 | { 137 | return m_values; 138 | } 139 | CheckSign();//验证签名,不通过会抛异常 140 | } 141 | catch (WePayException ex) 142 | { 143 | throw new WePayException(ex.Message); 144 | } 145 | 146 | return m_values; 147 | } 148 | 149 | /** 150 | * @Dictionary格式转化成url参数格式 151 | * @ return url格式串, 该串不包含sign字段值 152 | */ 153 | public string ToUrl() 154 | { 155 | string buff = ""; 156 | foreach (KeyValuePair pair in m_values) 157 | { 158 | if (pair.Value == null) 159 | { 160 | Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!"); 161 | throw new WePayException("WxPayData内部含有值为null的字段!"); 162 | } 163 | 164 | if (pair.Key != "sign" && pair.Value.ToString() != "") 165 | { 166 | buff += pair.Key + "=" + pair.Value + "&"; 167 | } 168 | } 169 | buff = buff.Trim('&'); 170 | return buff; 171 | } 172 | 173 | 174 | /** 175 | * @Dictionary格式化成Json 176 | * @return json串数据 177 | */ 178 | public string ToJson() 179 | { 180 | string jsonStr = JsonMapper.ToJson(m_values); 181 | return jsonStr; 182 | } 183 | 184 | /** 185 | * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串) 186 | */ 187 | public string ToPrintStr() 188 | { 189 | string str = ""; 190 | foreach (KeyValuePair pair in m_values) 191 | { 192 | if (pair.Value == null) 193 | { 194 | Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!"); 195 | throw new WePayException("WxPayData内部含有值为null的字段!"); 196 | } 197 | 198 | str += string.Format("{0}={1}
", pair.Key, pair.Value.ToString()); 199 | } 200 | Log.Info(this.GetType().ToString(), "Print in Web Page : " + str); 201 | return str; 202 | } 203 | 204 | /** 205 | * @生成签名,详见签名生成算法 206 | * @return 签名, sign字段不参加签名 207 | */ 208 | public string MakeSign() 209 | { 210 | //转url格式 211 | string str = ToUrl(); 212 | //在string后加入API KEY 213 | str += "&key=" + XcxPayConfig.KEY; 214 | //MD5加密 215 | var md5 = MD5.Create(); 216 | var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str)); 217 | var sb = new StringBuilder(); 218 | foreach (byte b in bs) 219 | { 220 | sb.Append(b.ToString("x2")); 221 | } 222 | //所有字符转为大写 223 | return sb.ToString().ToUpper(); 224 | } 225 | 226 | /** 227 | * 228 | * 检测签名是否正确 229 | * 正确返回true,错误抛异常 230 | */ 231 | public bool CheckSign() 232 | { 233 | //如果没有设置签名,则跳过检测 234 | if (!IsSet("sign")) 235 | { 236 | Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!"); 237 | throw new WePayException("WxPayData签名存在但不合法!"); 238 | } 239 | //如果设置了签名但是签名为空,则抛异常 240 | else if (GetValue("sign") == null || GetValue("sign").ToString() == "") 241 | { 242 | Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!"); 243 | throw new WePayException("WxPayData签名存在但不合法!"); 244 | } 245 | 246 | //获取接收到的签名 247 | string return_sign = GetValue("sign").ToString(); 248 | 249 | //在本地计算新的签名 250 | string cal_sign = MakeSign(); 251 | 252 | if (cal_sign == return_sign) 253 | { 254 | return true; 255 | } 256 | 257 | Log.Error(this.GetType().ToString(), "WxPayData签名验证错误!"); 258 | throw new WePayException("WxPayData签名验证错误!"); 259 | } 260 | 261 | /** 262 | * @获取Dictionary 263 | */ 264 | public SortedDictionary GetValues() 265 | { 266 | return m_values; 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /App.Pay/WePay/XcxPay/XcxPayHttpService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Security; 7 | using System.Security.Cryptography.X509Certificates; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Web; 11 | 12 | namespace App.Pay.WePay.XcxPay 13 | { 14 | public class XcxPayHttpService 15 | { 16 | private static Log Log = new Log(XcxPayConfig.LogPath); 17 | 18 | public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 19 | { 20 | //直接确认,否则打不开 21 | return true; 22 | } 23 | 24 | public static string Post(string xml, string url, bool isUseCert, int timeout) 25 | { 26 | System.GC.Collect();//垃圾回收,回收没有正常关闭的http连接 27 | 28 | string result = "";//返回结果 29 | 30 | HttpWebRequest request = null; 31 | HttpWebResponse response = null; 32 | Stream reqStream = null; 33 | 34 | try 35 | { 36 | //设置最大连接数 37 | ServicePointManager.DefaultConnectionLimit = 200; 38 | //设置https验证方式 39 | if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 40 | { 41 | ServicePointManager.ServerCertificateValidationCallback = 42 | new RemoteCertificateValidationCallback(CheckValidationResult); 43 | } 44 | 45 | /*************************************************************** 46 | * 下面设置HttpWebRequest的相关属性 47 | * ************************************************************/ 48 | request = (HttpWebRequest)WebRequest.Create(url); 49 | 50 | request.Method = "POST"; 51 | request.Timeout = timeout * 1000; 52 | 53 | //设置代理服务器 54 | //WebProxy proxy = new WebProxy(); //定义一个网关对象 55 | //proxy.Address = new Uri(WxPayConfig.PROXY_URL); //网关服务器端口:端口 56 | //request.Proxy = proxy; 57 | 58 | //设置POST的数据类型和长度 59 | request.ContentType = "text/xml"; 60 | byte[] data = System.Text.Encoding.UTF8.GetBytes(xml); 61 | request.ContentLength = data.Length; 62 | 63 | //是否使用证书 64 | if (isUseCert) 65 | { 66 | string path = HttpContext.Current.Request.PhysicalApplicationPath; 67 | X509Certificate2 cert = new X509Certificate2(path + XcxPayConfig.SSLCERT_PATH, XcxPayConfig.SSLCERT_PASSWORD); 68 | request.ClientCertificates.Add(cert); 69 | Log.Info("XcxPayHttpService", "PostXml used cert"); 70 | } 71 | 72 | //往服务器写入数据 73 | reqStream = request.GetRequestStream(); 74 | reqStream.Write(data, 0, data.Length); 75 | reqStream.Close(); 76 | 77 | //获取服务端返回 78 | response = (HttpWebResponse)request.GetResponse(); 79 | 80 | //获取服务端返回数据 81 | StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 82 | result = sr.ReadToEnd().Trim(); 83 | sr.Close(); 84 | } 85 | catch (System.Threading.ThreadAbortException e) 86 | { 87 | Log.Error("XcxPayHttpService", "Thread - caught ThreadAbortException - resetting."); 88 | Log.Error("Exception message: {0}", e.Message); 89 | System.Threading.Thread.ResetAbort(); 90 | } 91 | catch (WebException e) 92 | { 93 | Log.Error("XcxPayHttpService", e.ToString()); 94 | if (e.Status == WebExceptionStatus.ProtocolError) 95 | { 96 | Log.Error("XcxPayHttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 97 | Log.Error("XcxPayHttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 98 | } 99 | throw new WePayException(e.ToString()); 100 | } 101 | catch (Exception e) 102 | { 103 | Log.Error("XcxPayHttpService", e.ToString()); 104 | throw new WePayException(e.ToString()); 105 | } 106 | finally 107 | { 108 | //关闭连接和流 109 | if (response != null) 110 | { 111 | response.Close(); 112 | } 113 | if (request != null) 114 | { 115 | request.Abort(); 116 | } 117 | } 118 | return result; 119 | } 120 | 121 | /// 122 | /// 处理http GET请求,返回数据 123 | /// 124 | /// 请求的url地址 125 | /// http GET成功后返回的数据,失败抛WebException异常 126 | public static string Get(string url) 127 | { 128 | System.GC.Collect(); 129 | string result = ""; 130 | 131 | HttpWebRequest request = null; 132 | HttpWebResponse response = null; 133 | 134 | //请求url以获取数据 135 | try 136 | { 137 | //设置最大连接数 138 | ServicePointManager.DefaultConnectionLimit = 200; 139 | //设置https验证方式 140 | if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 141 | { 142 | ServicePointManager.ServerCertificateValidationCallback = 143 | new RemoteCertificateValidationCallback(CheckValidationResult); 144 | } 145 | 146 | /*************************************************************** 147 | * 下面设置HttpWebRequest的相关属性 148 | * ************************************************************/ 149 | request = (HttpWebRequest)WebRequest.Create(url); 150 | 151 | request.Method = "GET"; 152 | 153 | //设置代理 154 | //WebProxy proxy = new WebProxy(); 155 | //proxy.Address = new Uri(WxPayConfig.PROXY_URL); 156 | //request.Proxy = proxy; 157 | 158 | //获取服务器返回 159 | response = (HttpWebResponse)request.GetResponse(); 160 | 161 | //获取HTTP返回数据 162 | StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 163 | result = sr.ReadToEnd().Trim(); 164 | sr.Close(); 165 | } 166 | catch (System.Threading.ThreadAbortException e) 167 | { 168 | Log.Error("XcxPayHttpService", "Thread - caught ThreadAbortException - resetting."); 169 | Log.Error("Exception message: {0}", e.Message); 170 | System.Threading.Thread.ResetAbort(); 171 | } 172 | catch (WebException e) 173 | { 174 | Log.Error("XcxPayHttpService", e.ToString()); 175 | if (e.Status == WebExceptionStatus.ProtocolError) 176 | { 177 | Log.Error("XcxPayHttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 178 | Log.Error("XcxPayHttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 179 | } 180 | throw new WePayException(e.ToString()); 181 | } 182 | catch (Exception e) 183 | { 184 | Log.Error("XcxPayHttpService", e.ToString()); 185 | throw new WePayException(e.ToString()); 186 | } 187 | finally 188 | { 189 | //关闭连接和流 190 | if (response != null) 191 | { 192 | response.Close(); 193 | } 194 | if (request != null) 195 | { 196 | request.Abort(); 197 | } 198 | } 199 | return result; 200 | } 201 | } 202 | } 203 | 204 | 205 | -------------------------------------------------------------------------------- /App.Pay/WePay/XcxPay/XcxPayNotify.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | 8 | namespace App.Pay.WePay.XcxPay 9 | { 10 | /// 11 | /// 回调处理基类 12 | /// 主要负责接收微信支付后台发送过来的数据,对数据进行签名验证 13 | /// 子类在此类基础上进行派生并重写自己的回调处理过程 14 | /// 15 | public class XcxPayNotify 16 | { 17 | public HttpContext context { get; set; } 18 | 19 | public Log Log = new Log(XcxPayConfig.LogPath); 20 | 21 | public XcxPayNotify(HttpContext context) 22 | { 23 | this.context = context; 24 | } 25 | 26 | /// 27 | /// 接收从微信支付后台发送过来的数据并验证签名 28 | /// 29 | /// 微信支付后台返回的数据 30 | public XcxPayData GetNotifyData() 31 | { 32 | //接收从微信后台POST过来的数据 33 | System.IO.Stream s = context.Request.InputStream; 34 | int count = 0; 35 | byte[] buffer = new byte[1024]; 36 | StringBuilder builder = new StringBuilder(); 37 | while ((count = s.Read(buffer, 0, 1024)) > 0) 38 | { 39 | builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 40 | } 41 | s.Flush(); 42 | s.Close(); 43 | s.Dispose(); 44 | 45 | //转换数据格式并验证签名 46 | XcxPayData data = new XcxPayData(); 47 | try 48 | { 49 | data.FromXml(builder.ToString()); 50 | } 51 | catch (WePayException ex) 52 | { 53 | //若签名错误,则立即返回结果给微信支付后台 54 | XcxPayData res = new XcxPayData(); 55 | res.SetValue("return_code", "FAIL"); 56 | res.SetValue("return_msg", ex.Message); 57 | Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml()); 58 | context.Response.Write(res.ToXml()); 59 | context.Response.End(); 60 | } 61 | 62 | Log.Info(this.GetType().ToString(), "Check sign success"); 63 | return data; 64 | } 65 | 66 | //派生类需要重写这个方法,进行不同的回调处理 67 | public virtual void ProcessNotify() 68 | { 69 | 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /App.Pay/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /App.WebTest/App.WebTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | 9 | 10 | 2.0 11 | {AC647A8A-15B3-47EA-970B-68C76AA6AEDE} 12 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 13 | Library 14 | Properties 15 | App.WebTest 16 | App.WebTest 17 | v4.6.1 18 | false 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | true 31 | full 32 | false 33 | bin\ 34 | DEBUG;TRACE 35 | prompt 36 | 4 37 | 38 | 39 | true 40 | pdbonly 41 | true 42 | bin\ 43 | TRACE 44 | prompt 45 | 4 46 | 47 | 48 | 49 | ..\packages\Alipay.AopSdk.2.0.0\lib\net35\AopSdk.dll 50 | 51 | 52 | 53 | 54 | 55 | 56 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 66 | 67 | 68 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | True 81 | ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll 82 | 83 | 84 | 85 | 86 | 87 | 88 | True 89 | ..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.Helpers.dll 90 | 91 | 92 | True 93 | ..\packages\Microsoft.AspNet.Mvc.5.2.4\lib\net45\System.Web.Mvc.dll 94 | 95 | 96 | ..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll 97 | 98 | 99 | True 100 | ..\packages\Microsoft.AspNet.Razor.3.2.4\lib\net45\System.Web.Razor.dll 101 | 102 | 103 | True 104 | ..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.dll 105 | 106 | 107 | True 108 | ..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.Deployment.dll 109 | 110 | 111 | True 112 | ..\packages\Microsoft.AspNet.WebPages.3.2.4\lib\net45\System.Web.WebPages.Razor.dll 113 | 114 | 115 | ..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll 116 | 117 | 118 | True 119 | ..\packages\WebGrease.1.6.0\lib\WebGrease.dll 120 | 121 | 122 | True 123 | ..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll 124 | 125 | 126 | 127 | 128 | ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll 129 | 130 | 131 | ..\packages\Microsoft.AspNet.TelemetryCorrelation.1.0.0\lib\net45\Microsoft.AspNet.TelemetryCorrelation.dll 132 | 133 | 134 | ..\packages\Microsoft.ApplicationInsights.2.5.1\lib\net46\Microsoft.ApplicationInsights.dll 135 | 136 | 137 | ..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.4.0\lib\net45\Microsoft.AI.Agent.Intercept.dll 138 | 139 | 140 | ..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.5.1\lib\net45\Microsoft.AI.DependencyCollector.dll 141 | 142 | 143 | ..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.5.1\lib\net45\Microsoft.AI.PerfCounterCollector.dll 144 | 145 | 146 | ..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.5.1\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll 147 | 148 | 149 | ..\packages\Microsoft.ApplicationInsights.WindowsServer.2.5.1\lib\net45\Microsoft.AI.WindowsServer.dll 150 | 151 | 152 | ..\packages\Microsoft.ApplicationInsights.Web.2.5.1\lib\net45\Microsoft.AI.Web.dll 153 | 154 | 155 | ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | Global.asax 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | PreserveNewest 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | Designer 203 | 204 | 205 | Web.config 206 | 207 | 208 | Web.config 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 | {50777A16-B198-47C9-9510-5B2C50C7B48A} 244 | App.Common 245 | 246 | 247 | {97617EB9-9E25-4819-8729-E98AF6B3D297} 248 | App.Pay 249 | 250 | 251 | 252 | 10.0 253 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | True 266 | True 267 | 44188 268 | / 269 | http://localhost:44188/ 270 | False 271 | False 272 | 273 | 274 | False 275 | 276 | 277 | 278 | 279 | 280 | 281 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 282 | 283 | 284 | 285 | 291 | -------------------------------------------------------------------------------- /App.WebTest/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace App.WebTest 5 | { 6 | public class BundleConfig 7 | { 8 | // 有关捆绑的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 15 | "~/Scripts/jquery.validate*")); 16 | 17 | // 使用要用于开发和学习的 Modernizr 的开发版本。然后,当你做好 18 | // 生产准备就绪,请使用 https://modernizr.com 上的生成工具仅选择所需的测试。 19 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 20 | "~/Scripts/modernizr-*")); 21 | 22 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 23 | "~/Scripts/bootstrap.js")); 24 | 25 | bundles.Add(new StyleBundle("~/Content/css").Include( 26 | "~/Content/bootstrap.css", 27 | "~/Content/site.css")); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /App.WebTest/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace App.WebTest 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /App.WebTest/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace App.WebTest 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | name: "Default", 18 | url: "{controller}/{action}/{id}", 19 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 | ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /App.WebTest/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | 6 | namespace App.WebTest 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | config.MapHttpAttributeRoutes(); 13 | 14 | config.Routes.MapHttpRoute( 15 | name: "DefaultApi", 16 | routeTemplate: "api/{controller}/{id}", 17 | defaults: new { id = RouteParameter.Optional } 18 | ); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /App.WebTest/ApplicationInsights.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | search|spider|crawl|Bot|Monitor|AlwaysOn 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | core.windows.net 31 | core.chinacloudapi.cn 32 | core.cloudapi.de 33 | core.usgovcloudapi.net 34 | localhost 35 | 127.0.0.1 36 | 37 | 38 | Microsoft.Azure.EventHubs 39 | Microsoft.Azure.ServiceBus 40 | 41 | 42 | 43 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 75 | Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler 76 | System.Web.StaticFileHandler 77 | System.Web.Handlers.AssemblyResourceLoader 78 | System.Web.Optimization.BundleHandler 79 | System.Web.Script.Services.ScriptHandlerFactory 80 | System.Web.Handlers.TraceHandler 81 | System.Web.Services.Discovery.DiscoveryRequestHandler 82 | System.Web.HttpDebugHandler 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 5 93 | Event 94 | 95 | 96 | 5 97 | Event 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /App.WebTest/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Override the default bootstrap behavior where horizontal description lists 13 | will truncate terms that are too long to fit in the left column 14 | */ 15 | .dl-horizontal dt { 16 | white-space: normal; 17 | } 18 | 19 | /* Set width on the form input elements since they're 100% wide by default */ 20 | input, 21 | select, 22 | textarea { 23 | max-width: 280px; 24 | } 25 | -------------------------------------------------------------------------------- /App.WebTest/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Resources; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | 9 | namespace App.WebTest.Controllers 10 | { 11 | public abstract class BaseController : Controller 12 | { 13 | protected BaseController() 14 | { 15 | } 16 | 17 | #region 提示信息 18 | 19 | private static ResourceManager _resourceManager; 20 | 21 | private ResourceManager ResourceManager 22 | { 23 | get 24 | { 25 | if (_resourceManager == null) 26 | { 27 | _resourceManager = new ResourceManager("ETL.CivilAffairsEdu.Web.App_LocalResources.Message", Assembly.GetExecutingAssembly()); 28 | } 29 | return _resourceManager; 30 | } 31 | } 32 | 33 | protected string GetMessage(string key) 34 | { 35 | return ResourceManager.GetString(key); 36 | } 37 | 38 | #endregion 39 | 40 | protected JsonResult Json(bool success, string status) 41 | { 42 | return Json(success, null, status, JsonRequestBehavior.DenyGet); 43 | } 44 | protected JsonResult Json(bool success, string status, JsonRequestBehavior behavior) 45 | { 46 | return Json(success, null, status, behavior); 47 | } 48 | 49 | protected JsonResult Json(bool success, object data, string status) 50 | { 51 | return Json(success, data, status, JsonRequestBehavior.AllowGet); 52 | } 53 | 54 | protected virtual JsonResult Json(bool success, object data, string status, JsonRequestBehavior behavior) 55 | { 56 | if (success && string.IsNullOrWhiteSpace(status)) 57 | { 58 | status = "OK"; 59 | } 60 | return Json(new { success, data, status, msg = GetMessage(status) }, JsonRequestBehavior.AllowGet); 61 | } 62 | 63 | protected JsonResult JsonMsg(bool success, string status, string msg) 64 | { 65 | if (success && string.IsNullOrWhiteSpace(status)) 66 | { 67 | status = "OK"; 68 | } 69 | return Json(new { success, status, msg }, JsonRequestBehavior.AllowGet); 70 | } 71 | 72 | } 73 | } -------------------------------------------------------------------------------- /App.WebTest/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace App.WebTest.Controllers 8 | { 9 | public class HomeController : Controller 10 | { 11 | public ActionResult Index() 12 | { 13 | return View(); 14 | } 15 | 16 | public ActionResult About() 17 | { 18 | ViewBag.Message = "Your application description page."; 19 | 20 | return View(); 21 | } 22 | 23 | public ActionResult Contact() 24 | { 25 | ViewBag.Message = "Your contact page."; 26 | 27 | return View(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /App.WebTest/Controllers/Pay/AliPayController.cs: -------------------------------------------------------------------------------- 1 | using Aop.Api; 2 | using Aop.Api.Domain; 3 | using Aop.Api.Request; 4 | using Aop.Api.Response; 5 | using Aop.Api.Util; 6 | using App.Common.Extension; 7 | using App.Pay.AliPay; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Collections.Specialized; 11 | using System.Linq; 12 | using System.Web; 13 | using System.Web.Mvc; 14 | 15 | namespace App.WebTest.Controllers 16 | { 17 | public class AliPayController : BaseController 18 | { 19 | /// 20 | /// 订单编号 21 | /// 22 | /// 23 | /// 24 | public ActionResult AliPay(string oidStr) 25 | { 26 | #region 验证订单有效 27 | 28 | if (string.IsNullOrEmpty(oidStr)) 29 | { 30 | return Json(false, "OrderError"); 31 | } 32 | 33 | int[] oIds = Serialize.JsonTo(oidStr); 34 | 35 | decimal payPrice = 0; 36 | 37 | ///订单验证,统计订单总金额 38 | 39 | #endregion 40 | 41 | #region 统一下单 42 | try 43 | { 44 | var notify_url = AliPayConfig.notify_url; 45 | var return_url = AliPayConfig.return_url; 46 | IAopClient client = Pay.AliPay.AliPay.GetAlipayClient(); 47 | AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); 48 | //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 49 | AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); 50 | model.Subject = "商品购买"; 51 | model.TotalAmount = payPrice.ToString("F2"); 52 | model.ProductCode = "QUICK_MSECURITY_PAY"; 53 | Random rd = new Random(); 54 | var payNum = DateTime.Now.ToString("yyyyMMddHHmmss") + rd.Next(0, 1000).ToString().PadLeft(3, '0'); 55 | model.OutTradeNo = payNum; 56 | model.TimeoutExpress = "30m"; 57 | request.SetBizModel(model); 58 | request.SetNotifyUrl(notify_url); 59 | //request.SetReturnUrl(return_url); 60 | //这里和普通的接口调用不同,使用的是sdkExecute 61 | AlipayTradeAppPayResponse response = client.SdkExecute(request); 62 | 63 | //统一下单 64 | //OrderBll.Value.UpdateOrderApp(oIds, payNum); 65 | 66 | return Json(true, new { response.Body }, "OK"); 67 | } 68 | catch (Exception ex) 69 | { 70 | return Json(new { Result = false, msg = "缺少参数" }); 71 | } 72 | #endregion 73 | } 74 | 75 | /// 76 | /// 页面跳转同步通知页面 77 | /// 78 | /// 79 | public ActionResult ReturnUrl() 80 | { 81 | Pay.Log Log = new Pay.Log(Pay.AliPay.AliPayConfig.LogPath); 82 | Log.Info("ReturnUrl", "支付页面同步回调"); 83 | //将同步通知中收到的所有参数都存放到map中 84 | IDictionary map = GetRequestGet(); 85 | if (map.Count > 0) //判断是否有带返回参数 86 | { 87 | try 88 | { 89 | //支付宝的公钥 90 | string alipayPublicKey = AliPayConfig.payKey; 91 | string signType = AliPayConfig.signType; 92 | string charset = AliPayConfig.charset; 93 | bool keyFromFile = false; 94 | // 获取支付宝GET过来反馈信息 95 | bool verify_result = AlipaySignature.RSACheckV1(map, alipayPublicKey, charset, signType, keyFromFile); 96 | if (verify_result) 97 | { 98 | // 验证成功 99 | return Json(new { Result = true, msg = "验证成功" }); 100 | } 101 | else 102 | { 103 | Log.Error("AliPayNotifyUrl", "支付验证失败"); 104 | return Json(new { Result = false, msg = "验证失败" }); 105 | } 106 | } 107 | catch (Exception e) 108 | { 109 | //throw new Exception(e.Message); 110 | return Json(new { Result = false, msg = "验证失败" }); 111 | Log.Error("AliPayNotifyUrl", "支付验证失败"); 112 | } 113 | } 114 | else 115 | { 116 | return Json(new { Result = false, msg = "无返回参数" }); 117 | } 118 | } 119 | 120 | /// 121 | /// 服务器异步通知页面 122 | /// 123 | public void AliPayNotifyUrl() 124 | { 125 | Pay.Log Log = new Pay.Log(AliPayConfig.LogPath); 126 | Log.Info("AliPayNotifyUrl", "支付页面异步回调"); 127 | IDictionary map = GetRequestPost(); 128 | 129 | if (map.Count > 0) 130 | { 131 | try 132 | { 133 | string alipayPublicKey = AliPayConfig.payKey; 134 | string signType = AliPayConfig.signType; 135 | string charset = AliPayConfig.charset; 136 | bool keyFromFile = false; 137 | 138 | bool verify_result = AlipaySignature.RSACheckV1(map, alipayPublicKey, charset, signType, keyFromFile); 139 | Log.Info("AliPayNotifyUrl验签", verify_result + ""); 140 | 141 | //验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后再response中返回success并继续商户自身业务处理,校验失败返回false 142 | if (verify_result) 143 | { 144 | //商户订单号 145 | string out_trade_no = map["out_trade_no"]; 146 | //支付宝交易号 147 | string trade_no = map["trade_no"]; 148 | //交易创建时间 149 | string gmt_create = map["gmt_create"]; 150 | //交易付款时间 151 | string gmt_payment = map["gmt_payment"]; 152 | //通知时间 153 | string notify_time = map["notify_time"]; 154 | //通知类型 trade_status_sync 155 | string notify_type = map["notify_type"]; 156 | //通知校验ID 157 | string notify_id = map["notify_id"]; 158 | //开发者的app_id 159 | string app_id = map["app_id"]; 160 | //卖家支付宝用户号 161 | string seller_id = map["seller_id"]; 162 | //买家支付宝用户号 163 | string buyer_id = map["buyer_id"]; 164 | //实收金额 165 | string receipt_amount = map["receipt_amount"]; 166 | //交易状态 167 | string return_code = map["trade_status"]; 168 | 169 | //交易状态TRADE_FINISHED的通知触发条件是商户签约的产品不支持退款功能的前提下,买家付款成功; 170 | //或者,商户签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限 171 | //状态TRADE_SUCCESS的通知触发条件是商户签约的产品支持退款功能的前提下,买家付款成功 172 | if (return_code == "TRADE_FINISHED" || return_code == "TRADE_SUCCESS") 173 | { 174 | string msg; 175 | 176 | Log.Error("AliPayNotifyUrl", receipt_amount + "==" + trade_no + "==" + return_code + "==" + out_trade_no + "==" + gmt_payment); 177 | 178 | //判断该笔订单是否在商户网站中已经做过处理 179 | ///支付回调的业务处理 180 | //bool res = OrderBll.Value.CompleteAliPay(receipt_amount, trade_no, return_code, out_trade_no, gmt_payment, out msg); 181 | bool res = true; 182 | 183 | if (res == false) 184 | { 185 | Response.Write("添加支付信息失败!"); 186 | } 187 | Log.Error("AliPayNotifyUrl", "支付成功"); 188 | Response.Write("success"); //请不要修改或删除 189 | } 190 | } 191 | else 192 | { 193 | //验证失败 194 | Log.Error("AliPayNotifyUrl", "支付验证失败"); 195 | Response.Write("验证失败!"); 196 | } 197 | } 198 | catch (Exception e) 199 | { 200 | Response.Write("添加支付信息失败!"); 201 | Log.Error("AliPayNotifyUrl", "添加支付信息失败"); 202 | } 203 | } 204 | else 205 | { 206 | //无返回参数 207 | Response.Write("无返回参数!"); 208 | Log.Error("AliPayNotifyUrl", "无返回参数"); 209 | } 210 | } 211 | //[AllowUser] 212 | //public ActionResult TestAliPay() 213 | //{ 214 | 215 | // var receipt_amount = "0.01"; 216 | // var trade_no = "20181226220013......."; 217 | // var return_code = "TRADE_SUCCESS"; 218 | // var out_trade_no = "20181226103124129"; 219 | // var gmt_payment = "2018-12-26 10:31:29"; 220 | 221 | // string msg = ""; 222 | // bool res = OrderBll.Value.CompleteAliPay(receipt_amount, trade_no, return_code, out_trade_no, gmt_payment, out msg); 223 | 224 | // return Json(res); 225 | //} 226 | 227 | /// 228 | /// 获取支付宝Get过来的通知消息,并以“参数名=参数值”的形式组成数组 229 | /// 230 | /// 231 | public IDictionary GetRequestGet() 232 | { 233 | Pay.Log Log = new Pay.Log(Pay.AliPay.AliPayConfig.LogPath); 234 | int i = 0; 235 | IDictionary sArry = new Dictionary(); 236 | NameValueCollection coll; 237 | coll = Request.QueryString; 238 | 239 | String[] requstItem = coll.AllKeys; 240 | 241 | for (i = 0; i < requstItem.Length; i++) 242 | { 243 | Log.Info("GetRequestGet", requstItem[i] + ":" + Request.QueryString[requstItem[i]]); 244 | sArry.Add(requstItem[i], Request.QueryString[requstItem[i]]); 245 | } 246 | 247 | return sArry; 248 | } 249 | 250 | /// 251 | /// 获取支付宝POST过来通知消息,并以“参数名=参数值”的形式组成数组 252 | /// 253 | /// request回来的信息组成的数组 254 | public IDictionary GetRequestPost() 255 | { 256 | Pay.Log Log = new Pay.Log(Pay.AliPay.AliPayConfig.LogPath); 257 | int i = 0; 258 | IDictionary sArray = new Dictionary(); 259 | NameValueCollection coll; 260 | 261 | //Load Form variables into NameValueCollection variable. 262 | coll = Request.Form; 263 | 264 | // Get names of all forms into a string array. 265 | String[] requestItem = coll.AllKeys; 266 | for (i = 0; i < requestItem.Length; i++) 267 | { 268 | Log.Info("GetRequestPost", requestItem[i] + ":" + Request.Form[requestItem[i]]); 269 | sArray.Add(requestItem[i], Request.Form[requestItem[i]]); 270 | } 271 | 272 | return sArray; 273 | } 274 | } 275 | } -------------------------------------------------------------------------------- /App.WebTest/Controllers/Pay/WeAppPayController.cs: -------------------------------------------------------------------------------- 1 | using App.Common.Extension; 2 | using App.Pay.WePay.AppPay; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Web; 7 | using System.Web.Configuration; 8 | using System.Web.Mvc; 9 | 10 | namespace App.WebTest.Controllers 11 | { 12 | public class WeAppPayController : BaseController 13 | { 14 | /// 15 | /// 微信app支付 16 | /// 17 | /// 订单编号 18 | /// 19 | public ActionResult WxPayApp(string oidStr) 20 | { 21 | // App调用只能传参 22 | int[] oIds = Serialize.JsonTo>(oidStr).ToArray(); 23 | 24 | #region 验证订单是否有效 25 | 26 | decimal payPrice = 0; 27 | string detail = ""; 28 | 29 | /// 验证订单是否有效,并统计订单总金额 30 | /// ... 31 | 32 | #endregion 33 | 34 | #region 统一下单 35 | try 36 | { 37 | //string userId = LoginUser.Id.ToString(); 38 | var address = WebConfigurationManager.AppSettings["WxAppNotifyUrl"].ToString(); 39 | AppPayData data = new AppPayData(); 40 | data.SetValue("body", "民政社工培训-课程购买"); 41 | //data.SetValue("attach", userId + "|" + String.Join(",", oIds).ToString()); 42 | data.SetValue("attach", String.Join(",", oIds).ToString()); 43 | Random rd = new Random(); 44 | var payNum = DateTime.Now.ToString("yyyyMMddHHmmss") + rd.Next(0, 1000).ToString().PadLeft(3, '0'); 45 | data.SetValue("out_trade_no", payNum); 46 | data.SetValue("detail", detail.Substring(0, detail.Length - 1)); 47 | data.SetValue("total_fee", Convert.ToInt32(payPrice * 100)); 48 | data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss")); 49 | data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss")); 50 | data.SetValue("notify_url", address); 51 | 52 | //注意,这里交易方式是APP 53 | data.SetValue("trade_type", "APP"); 54 | 55 | AppPayData result = AppPayApi.UnifiedOrder(data); 56 | var appid = ""; 57 | var partnerid = ""; 58 | var prepayid = ""; 59 | var package = ""; 60 | var nonceStr = ""; 61 | var timestamp = ""; 62 | var sign = ""; 63 | if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "") 64 | { 65 | return Json(false, "下单失败!"); 66 | } 67 | else 68 | { 69 | //统一下单 70 | /// 修改订单状态 71 | //OrderBll.Value.UpdateOrderApp(oIds, payNum); 72 | 73 | appid = result.GetValue("appid").ToString(); 74 | nonceStr = result.GetValue("nonce_str").ToString(); 75 | partnerid = result.GetValue("mch_id").ToString(); 76 | prepayid = result.GetValue("prepay_id").ToString(); 77 | package = "Sign=WXPay";// "prepay_id=" + result.GetValue("prepay_id").ToString(); 78 | } 79 | var signType = "MD5"; 80 | timestamp = ((DateTime.Now.Ticks - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).Ticks) / 10000).ToString(); 81 | AppPayData applet = new AppPayData(); 82 | applet.SetValue("appid", appid); 83 | applet.SetValue("noncestr", nonceStr); 84 | applet.SetValue("package", package); 85 | applet.SetValue("partnerid", partnerid); 86 | applet.SetValue("prepayid", prepayid); 87 | //applet.SetValue("signType", signType); 88 | applet.SetValue("timestamp", timestamp); 89 | sign = applet.MakeSign(); 90 | return Json(new { appid, partnerid, prepayid, package, nonceStr, timestamp, sign }); 91 | } 92 | catch (Exception ex) 93 | { 94 | return Json(false, "缺少参数"); 95 | } 96 | 97 | #endregion 98 | 99 | } 100 | 101 | /// 102 | /// 微信支付回调 103 | /// 104 | /// 105 | public string WxAppNotifyUrl() 106 | { 107 | Pay.Log Log = new Pay.Log(AppPayConfig.LogPath); 108 | Log.Info("WxAppNotifyUrl", "支付回调"); 109 | AppPayNotify notify = new AppPayNotify(System.Web.HttpContext.Current); 110 | AppPayData notifyData = notify.GetNotifyData(); 111 | 112 | //检查支付结果中transaction_id是否存在 113 | if (!notifyData.IsSet("transaction_id")) 114 | { 115 | //若transaction_id不存在,则立即返回结果给微信支付后台 116 | AppPayData res = new AppPayData(); 117 | res.SetValue("return_code", "FAIL"); 118 | res.SetValue("return_msg", "支付结果中微信订单号不存在"); 119 | Log.Error(this.GetType().ToString(), "The Pay result is error : " + res.ToXml()); 120 | Response.Write(res.ToXml()); 121 | Response.End(); 122 | } 123 | 124 | string transaction_id = notifyData.GetValue("transaction_id").ToString(); 125 | 126 | //查询订单,判断订单真实性 127 | if (!AppQueryOrder(transaction_id)) 128 | { 129 | //若订单查询失败,则立即返回结果给微信支付后台 130 | AppPayData res = new AppPayData(); 131 | res.SetValue("return_code", "FAIL"); 132 | res.SetValue("return_msg", "订单查询失败"); 133 | Log.Error(this.GetType().ToString(), "Order query failure : " + res.ToXml()); 134 | 135 | Response.Write(res.ToXml()); 136 | Response.End(); 137 | } 138 | //查询订单成功 139 | else 140 | { 141 | AppPayData res = new AppPayData(); 142 | res.SetValue("return_code", "SUCCESS"); 143 | res.SetValue("return_msg", "OK"); 144 | Log.Info(this.GetType().ToString(), "Order query success : " + res.ToXml()); 145 | Log.Info(this.GetType().ToString(), "Order query success,notifyData : " + notifyData.ToXml()); 146 | var returnCode = notifyData.GetValue("return_code").ToString(); 147 | var transactionNo = transaction_id;//微信订单号 148 | var outTradeNo = notifyData.GetValue("out_trade_no").ToString();//自定义订单号 149 | var attach = notifyData.GetValue("attach").ToString();//身份证 150 | var endTime = notifyData.GetValue("time_end").ToString();//交易结束时间 151 | //var body = notifyData.GetValue("body").ToString();//projectIdlist 152 | var totalFee = notifyData.GetValue("total_fee").ToString(); ;//支付金额 153 | 154 | int userId = Convert.ToInt32(attach.Split('|')[0]); 155 | string msg; 156 | try 157 | { 158 | ///修改数据库订单状态 159 | //var result = OrderBll.Value.CompleteWePay(userId, totalFee, transactionNo, returnCode, outTradeNo, attach, endTime, out msg); 160 | var result = true; 161 | 162 | Log.Info(this.GetType().ToString(), "CompleteWePay:" + result); 163 | } 164 | catch (Exception e) 165 | { 166 | Log.Error(this.GetType().ToString(), "CompleteWePay:" + e.ToString()); 167 | } 168 | 169 | Response.Write(res.ToXml()); 170 | Response.End(); 171 | } 172 | 173 | return ""; 174 | } 175 | 176 | /// 177 | /// 查询订单 178 | /// 179 | /// 180 | /// 181 | private bool AppQueryOrder(string transaction_id) 182 | { 183 | AppPayData req = new AppPayData(); 184 | req.SetValue("transaction_id", transaction_id); 185 | AppPayData res = AppPayApi.OrderQuery(req); 186 | if (res.GetValue("return_code").ToString() == "SUCCESS" && res.GetValue("result_code").ToString() == "SUCCESS") 187 | { 188 | return true; 189 | } 190 | else 191 | { 192 | return false; 193 | } 194 | } 195 | } 196 | } -------------------------------------------------------------------------------- /App.WebTest/Controllers/Pay/WeXcxPayController.cs: -------------------------------------------------------------------------------- 1 | using App.Pay.WePay; 2 | using App.Pay.WePay.XcxPay; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Web; 7 | using System.Web.Configuration; 8 | using System.Web.Mvc; 9 | 10 | namespace App.WebTest.Controllers 11 | { 12 | /// 13 | /// 微信小程序支付 14 | /// 15 | public class WeXcxPayController : BaseController 16 | { 17 | /// 18 | /// 小程序下单 19 | /// 20 | /// 订单Id 21 | /// 临时登录凭证 22 | /// 23 | public ActionResult WeXcxPay(int[] oIds, string code) 24 | { 25 | #region 验证订单是否有效,并合计价格 26 | 27 | //订单价格 28 | decimal payPrice = 0; 29 | 30 | //订单描述 31 | string detail = ""; 32 | 33 | //验证订单..... 34 | 35 | 36 | #endregion 37 | 38 | #region 统一下单 39 | 40 | try 41 | { 42 | //支付回调通知地址 43 | var address = WebConfigurationManager.AppSettings["WxXcxNotifyUrl"].ToString(); 44 | XcxPayData data = new XcxPayData(); 45 | data.SetValue("body", "商品购买"); 46 | 47 | //可以将用户Id和订单Id同时封装在attach中 48 | data.SetValue("attach", String.Join(",", oIds).ToString()); 49 | Random rd = new Random(); 50 | 51 | //外部商户订单号 52 | var payNum = DateTime.Now.ToString("yyyyMMddHHmmss") + rd.Next(0, 1000).ToString().PadLeft(3, '0'); 53 | data.SetValue("out_trade_no", payNum); 54 | data.SetValue("detail", detail.Substring(0, detail.Length - 1)); 55 | data.SetValue("total_fee", Convert.ToInt32(payPrice * 100)); 56 | data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss")); 57 | data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss")); 58 | data.SetValue("notify_url", address); 59 | //data.SetValue("goods_tag", "test"); 60 | data.SetValue("trade_type", "JSAPI"); 61 | data.SetValue("openid", WeHelper.Code2Session(code).openid); 62 | 63 | XcxPayData result = XcxPayApi.UnifiedOrder(data); 64 | var flag = true; 65 | var msg = ""; 66 | var nonceStr = ""; 67 | var appId = ""; 68 | var package = ""; 69 | var mch_id = ""; 70 | if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "") 71 | { 72 | flag = false; 73 | msg = "下单失败"; 74 | return Json(new { Result = false, Msg = "下单失败!" }); 75 | } 76 | else 77 | { 78 | //统一下单 79 | 80 | ///TO Do...... 81 | /// 修改订单状态 82 | 83 | nonceStr = result.GetValue("nonce_str").ToString(); 84 | appId = result.GetValue("appid").ToString(); 85 | mch_id = result.GetValue("mch_id").ToString(); 86 | package = "prepay_id=" + result.GetValue("prepay_id").ToString(); 87 | } 88 | var signType = "MD5"; 89 | var timeStamp = ((DateTime.Now.Ticks - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).Ticks) / 10000).ToString(); 90 | XcxPayData applet = new XcxPayData(); 91 | applet.SetValue("appId", appId); 92 | applet.SetValue("nonceStr", nonceStr); 93 | applet.SetValue("package", package); 94 | applet.SetValue("signType", signType); 95 | applet.SetValue("timeStamp", timeStamp); 96 | var appletSign = applet.MakeSign(); 97 | return Json(new { timeStamp, nonceStr, package, signType, paySign = appletSign, Result = flag, msg }); 98 | } 99 | catch (Exception ex) 100 | { 101 | return Json(new { Result = false, msg = "缺少参数" }); 102 | } 103 | #endregion 104 | } 105 | 106 | /// 107 | /// 微信小程序支付回调通知 108 | /// 109 | /// 110 | public void WeXcxNotifyUrl() 111 | { 112 | Pay.Log Log = new Pay.Log(XcxPayConfig.LogPath); 113 | Log.Info("WxXcxNotifyUrl", "支付回调"); 114 | XcxPayNotify notify = new XcxPayNotify(System.Web.HttpContext.Current); 115 | XcxPayData notifyData = notify.GetNotifyData(); 116 | 117 | //检查支付结果中transaction_id是否存在 118 | if (!notifyData.IsSet("transaction_id")) 119 | { 120 | //若transaction_id不存在,则立即返回结果给微信支付后台 121 | XcxPayData res = new XcxPayData(); 122 | res.SetValue("return_code", "FAIL"); 123 | res.SetValue("return_msg", "支付结果中微信订单号不存在"); 124 | Log.Error(this.GetType().ToString(), "The Pay result is error : " + res.ToXml()); 125 | Response.Write(res.ToXml()); 126 | Response.End(); 127 | } 128 | 129 | string transaction_id = notifyData.GetValue("transaction_id").ToString(); 130 | 131 | //查询订单,判断订单真实性 132 | if (!XcxQueryOrder(transaction_id)) 133 | { 134 | //若订单查询失败,则立即返回结果给微信支付后台 135 | XcxPayData res = new XcxPayData(); 136 | res.SetValue("return_code", "FAIL"); 137 | res.SetValue("return_msg", "订单查询失败"); 138 | Log.Error(this.GetType().ToString(), "Order query failure : " + res.ToXml()); 139 | 140 | Response.Write(res.ToXml()); 141 | Response.End(); 142 | } 143 | //查询订单成功 144 | else 145 | { 146 | XcxPayData res = new XcxPayData(); 147 | res.SetValue("return_code", "SUCCESS"); 148 | res.SetValue("return_msg", "OK"); 149 | Log.Info(this.GetType().ToString(), "Order query success : " + res.ToXml()); 150 | Log.Info(this.GetType().ToString(), "Order query success,notifyData : " + notifyData.ToXml()); 151 | var returnCode = notifyData.GetValue("return_code").ToString(); 152 | var transactionNo = transaction_id;//微信订单号 153 | var outTradeNo = notifyData.GetValue("out_trade_no").ToString();//自定义订单号 154 | var attach = notifyData.GetValue("attach").ToString();//身份证 155 | var endTime = notifyData.GetValue("time_end").ToString();//交易结束时间 156 | //var body = notifyData.GetValue("body").ToString();//projectIdlist 157 | var totalFee = notifyData.GetValue("total_fee").ToString(); ;//支付金额 158 | 159 | int userId = Convert.ToInt32(attach.Split('|')[0]); 160 | string msg; 161 | try 162 | { 163 | //var result = OrderBll.Value.CompleteWePay(userId, totalFee, transactionNo, returnCode, outTradeNo, attach, endTime, out msg); 164 | 165 | var result = true; 166 | 167 | Log.Info(this.GetType().ToString(), "CompleteWePay:" + result); 168 | } 169 | catch (Exception e) 170 | { 171 | Log.Error(this.GetType().ToString(), "CompleteWePay:" + e.ToString()); 172 | } 173 | 174 | Response.Write(res.ToXml()); 175 | Response.End(); 176 | } 177 | } 178 | 179 | /// 180 | /// 查询订单 181 | /// 182 | /// 微信交易订单号 183 | /// 184 | private bool XcxQueryOrder(string transaction_id) 185 | { 186 | XcxPayData req = new XcxPayData(); 187 | req.SetValue("transaction_id", transaction_id); 188 | XcxPayData res = XcxPayApi.OrderQuery(req); 189 | if (res.GetValue("return_code").ToString() == "SUCCESS" && res.GetValue("result_code").ToString() == "SUCCESS") 190 | { 191 | return true; 192 | } 193 | else 194 | { 195 | return false; 196 | } 197 | } 198 | } 199 | } -------------------------------------------------------------------------------- /App.WebTest/Controllers/QRCode/QRCodeApiController.cs: -------------------------------------------------------------------------------- 1 | using App.Common.QRCode; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Net.Http.Headers; 8 | using System.Web.Http; 9 | 10 | namespace App.WebTest.Controllers.QRCode 11 | { 12 | /// 13 | /// WebApi 14 | /// 15 | public class QRCodeApiController : ApiController 16 | { 17 | /// 18 | /// 获取二维码图片(base64格式) 19 | /// 20 | /// 21 | [HttpGet] 22 | public HttpResponseMessage GetQRCode() 23 | { 24 | var qrCode = new QRCodeHelper(); 25 | 26 | var url = "https://www.cnblogs.com/wenha"; 27 | var image = qrCode.GenerateQRCode(url, "博客园地址"); 28 | 29 | var resp = new HttpResponseMessage(HttpStatusCode.OK) 30 | { 31 | Content = new StringContent("data:image/jpeg;base64," + Convert.ToBase64String(image)) 32 | }; 33 | 34 | return resp; 35 | } 36 | 37 | /// 38 | /// 获取二维码图片(图片格式) 39 | /// 40 | /// 41 | [HttpGet] 42 | public HttpResponseMessage GetQRCodeImg() 43 | { 44 | 45 | var qrCode = new QRCodeHelper(); 46 | 47 | var url = "https://www.cnblogs.com/wenha"; 48 | var image = qrCode.GenerateQRCode(url, "博客园地址"); 49 | 50 | var resp = new HttpResponseMessage(HttpStatusCode.OK) 51 | { 52 | Content = new ByteArrayContent(image) 53 | }; 54 | resp.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpg"); 55 | return resp; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /App.WebTest/Controllers/QRCode/QRCodeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using App.Common.QRCode; 7 | 8 | namespace App.WebTest.Controllers.QRCode 9 | { 10 | /// 11 | /// Mvc 12 | /// 13 | public class QRCodeController : Controller 14 | { 15 | /// 16 | /// 显示二维码 17 | /// 18 | /// 19 | public ActionResult Index() 20 | { 21 | var qrCode = new QRCodeHelper(); 22 | var qrImg = qrCode.GenerateQRCode("https://www.cnblogs.com/wenha"); 23 | 24 | var base64 = "data:image/jpeg;base64," + Convert.ToBase64String(qrImg); 25 | 26 | ViewBag.CodeImg = base64; 27 | return View(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /App.WebTest/Controllers/ValidateController.cs: -------------------------------------------------------------------------------- 1 | using App.Common.ValidateCode; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Web; 6 | using System.Web.Mvc; 7 | 8 | namespace App.WebTest.Controllers 9 | { 10 | public class ValidateController : Controller 11 | { 12 | /// 13 | /// 获取图片验证码 14 | /// 15 | /// 16 | public ActionResult ValidateIndex() 17 | { 18 | var validateCode = new GraphicValidateCode(); 19 | var code = validateCode.GenerateCode(4); 20 | 21 | var image = validateCode.GenerateValidateGraphic(code); 22 | var base64 = "data:image/jpeg;base64," + Convert.ToBase64String(image); 23 | 24 | ViewBag.ValidateImg = base64; 25 | return View(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /App.WebTest/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="App.WebTest.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /App.WebTest/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Optimization; 7 | using System.Web.Routing; 8 | using System.Web.Http; 9 | using System.Web.Routing; 10 | 11 | namespace App.WebTest 12 | { 13 | public class MvcApplication : System.Web.HttpApplication 14 | { 15 | protected void Application_Start() 16 | { 17 | GlobalConfiguration.Configure(WebApiConfig.Register); 18 | 19 | AreaRegistration.RegisterAllAreas(); 20 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 21 | RouteConfig.RegisterRoutes(RouteTable.Routes); 22 | BundleConfig.RegisterBundles(BundleTable.Bundles); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /App.WebTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息是通过以下项进行控制的 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("App.WebTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("App.WebTest")] 13 | [assembly: AssemblyCopyright("版权所有(C) 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 将使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要 19 | // 从 COM 访问此程序集中的某个类型,请针对该类型将 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于 typelib 的 ID 23 | [assembly: Guid("82ec98fc-5826-45d6-a4f3-0d4436a8699c")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 内部版本号 30 | // 修订版本 31 | // 32 | // 你可以指定所有值,也可以让修订版本和内部版本号采用默认值, 33 | // 方法是按如下所示使用 "*": 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /App.WebTest/Scripts/jquery.validate.unobtrusive.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | 20 | /*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ 21 | /*global document: false, jQuery: false */ 22 | 23 | (function ($) { 24 | var $jQval = $.validator, 25 | adapters, 26 | data_validation = "unobtrusiveValidation"; 27 | 28 | function setValidationValues(options, ruleName, value) { 29 | options.rules[ruleName] = value; 30 | if (options.message) { 31 | options.messages[ruleName] = options.message; 32 | } 33 | } 34 | 35 | function splitAndTrim(value) { 36 | return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); 37 | } 38 | 39 | function escapeAttributeValue(value) { 40 | // As mentioned on http://api.jquery.com/category/selectors/ 41 | return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); 42 | } 43 | 44 | function getModelPrefix(fieldName) { 45 | return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); 46 | } 47 | 48 | function appendModelPrefix(value, prefix) { 49 | if (value.indexOf("*.") === 0) { 50 | value = value.replace("*.", prefix); 51 | } 52 | return value; 53 | } 54 | 55 | function onError(error, inputElement) { // 'this' is the form element 56 | var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), 57 | replaceAttrValue = container.attr("data-valmsg-replace"), 58 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; 59 | 60 | container.removeClass("field-validation-valid").addClass("field-validation-error"); 61 | error.data("unobtrusiveContainer", container); 62 | 63 | if (replace) { 64 | container.empty(); 65 | error.removeClass("input-validation-error").appendTo(container); 66 | } 67 | else { 68 | error.hide(); 69 | } 70 | } 71 | 72 | function onErrors(event, validator) { // 'this' is the form element 73 | var container = $(this).find("[data-valmsg-summary=true]"), 74 | list = container.find("ul"); 75 | 76 | if (list && list.length && validator.errorList.length) { 77 | list.empty(); 78 | container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); 79 | 80 | $.each(validator.errorList, function () { 81 | $("
  • ").html(this.message).appendTo(list); 82 | }); 83 | } 84 | } 85 | 86 | function onSuccess(error) { // 'this' is the form element 87 | var container = error.data("unobtrusiveContainer"), 88 | replaceAttrValue = container.attr("data-valmsg-replace"), 89 | replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; 90 | 91 | if (container) { 92 | container.addClass("field-validation-valid").removeClass("field-validation-error"); 93 | error.removeData("unobtrusiveContainer"); 94 | 95 | if (replace) { 96 | container.empty(); 97 | } 98 | } 99 | } 100 | 101 | function onReset(event) { // 'this' is the form element 102 | var $form = $(this), 103 | key = '__jquery_unobtrusive_validation_form_reset'; 104 | if ($form.data(key)) { 105 | return; 106 | } 107 | // Set a flag that indicates we're currently resetting the form. 108 | $form.data(key, true); 109 | try { 110 | $form.data("validator").resetForm(); 111 | } finally { 112 | $form.removeData(key); 113 | } 114 | 115 | $form.find(".validation-summary-errors") 116 | .addClass("validation-summary-valid") 117 | .removeClass("validation-summary-errors"); 118 | $form.find(".field-validation-error") 119 | .addClass("field-validation-valid") 120 | .removeClass("field-validation-error") 121 | .removeData("unobtrusiveContainer") 122 | .find(">*") // If we were using valmsg-replace, get the underlying error 123 | .removeData("unobtrusiveContainer"); 124 | } 125 | 126 | function validationInfo(form) { 127 | var $form = $(form), 128 | result = $form.data(data_validation), 129 | onResetProxy = $.proxy(onReset, form), 130 | defaultOptions = $jQval.unobtrusive.options || {}, 131 | execInContext = function (name, args) { 132 | var func = defaultOptions[name]; 133 | func && $.isFunction(func) && func.apply(form, args); 134 | } 135 | 136 | if (!result) { 137 | result = { 138 | options: { // options structure passed to jQuery Validate's validate() method 139 | errorClass: defaultOptions.errorClass || "input-validation-error", 140 | errorElement: defaultOptions.errorElement || "span", 141 | errorPlacement: function () { 142 | onError.apply(form, arguments); 143 | execInContext("errorPlacement", arguments); 144 | }, 145 | invalidHandler: function () { 146 | onErrors.apply(form, arguments); 147 | execInContext("invalidHandler", arguments); 148 | }, 149 | messages: {}, 150 | rules: {}, 151 | success: function () { 152 | onSuccess.apply(form, arguments); 153 | execInContext("success", arguments); 154 | } 155 | }, 156 | attachValidation: function () { 157 | $form 158 | .off("reset." + data_validation, onResetProxy) 159 | .on("reset." + data_validation, onResetProxy) 160 | .validate(this.options); 161 | }, 162 | validate: function () { // a validation function that is called by unobtrusive Ajax 163 | $form.validate(); 164 | return $form.valid(); 165 | } 166 | }; 167 | $form.data(data_validation, result); 168 | } 169 | 170 | return result; 171 | } 172 | 173 | $jQval.unobtrusive = { 174 | adapters: [], 175 | 176 | parseElement: function (element, skipAttach) { 177 | /// 178 | /// Parses a single HTML element for unobtrusive validation attributes. 179 | /// 180 | /// The HTML element to be parsed. 181 | /// [Optional] true to skip attaching the 182 | /// validation to the form. If parsing just this single element, you should specify true. 183 | /// If parsing several elements, you should specify false, and manually attach the validation 184 | /// to the form when you are finished. The default is false. 185 | var $element = $(element), 186 | form = $element.parents("form")[0], 187 | valInfo, rules, messages; 188 | 189 | if (!form) { // Cannot do client-side validation without a form 190 | return; 191 | } 192 | 193 | valInfo = validationInfo(form); 194 | valInfo.options.rules[element.name] = rules = {}; 195 | valInfo.options.messages[element.name] = messages = {}; 196 | 197 | $.each(this.adapters, function () { 198 | var prefix = "data-val-" + this.name, 199 | message = $element.attr(prefix), 200 | paramValues = {}; 201 | 202 | if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) 203 | prefix += "-"; 204 | 205 | $.each(this.params, function () { 206 | paramValues[this] = $element.attr(prefix + this); 207 | }); 208 | 209 | this.adapt({ 210 | element: element, 211 | form: form, 212 | message: message, 213 | params: paramValues, 214 | rules: rules, 215 | messages: messages 216 | }); 217 | } 218 | }); 219 | 220 | $.extend(rules, { "__dummy__": true }); 221 | 222 | if (!skipAttach) { 223 | valInfo.attachValidation(); 224 | } 225 | }, 226 | 227 | parse: function (selector) { 228 | /// 229 | /// Parses all the HTML elements in the specified selector. It looks for input elements decorated 230 | /// with the [data-val=true] attribute value and enables validation according to the data-val-* 231 | /// attribute values. 232 | /// 233 | /// Any valid jQuery selector. 234 | 235 | // $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one 236 | // element with data-val=true 237 | var $selector = $(selector), 238 | $forms = $selector.parents() 239 | .addBack() 240 | .filter("form") 241 | .add($selector.find("form")) 242 | .has("[data-val=true]"); 243 | 244 | $selector.find("[data-val=true]").each(function () { 245 | $jQval.unobtrusive.parseElement(this, true); 246 | }); 247 | 248 | $forms.each(function () { 249 | var info = validationInfo(this); 250 | if (info) { 251 | info.attachValidation(); 252 | } 253 | }); 254 | } 255 | }; 256 | 257 | adapters = $jQval.unobtrusive.adapters; 258 | 259 | adapters.add = function (adapterName, params, fn) { 260 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation. 261 | /// The name of the adapter to be added. This matches the name used 262 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 263 | /// [Optional] An array of parameter names (strings) that will 264 | /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and 265 | /// mmmm is the parameter name). 266 | /// The function to call, which adapts the values from the HTML 267 | /// attributes into jQuery Validate rules and/or messages. 268 | /// 269 | if (!fn) { // Called with no params, just a function 270 | fn = params; 271 | params = []; 272 | } 273 | this.push({ name: adapterName, params: params, adapt: fn }); 274 | return this; 275 | }; 276 | 277 | adapters.addBool = function (adapterName, ruleName) { 278 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 279 | /// the jQuery Validate validation rule has no parameter values. 280 | /// The name of the adapter to be added. This matches the name used 281 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 282 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 283 | /// of adapterName will be used instead. 284 | /// 285 | return this.add(adapterName, function (options) { 286 | setValidationValues(options, ruleName || adapterName, true); 287 | }); 288 | }; 289 | 290 | adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { 291 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 292 | /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and 293 | /// one for min-and-max). The HTML parameters are expected to be named -min and -max. 294 | /// The name of the adapter to be added. This matches the name used 295 | /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). 296 | /// The name of the jQuery Validate rule to be used when you only 297 | /// have a minimum value. 298 | /// The name of the jQuery Validate rule to be used when you only 299 | /// have a maximum value. 300 | /// The name of the jQuery Validate rule to be used when you 301 | /// have both a minimum and maximum value. 302 | /// [Optional] The name of the HTML attribute that 303 | /// contains the minimum value. The default is "min". 304 | /// [Optional] The name of the HTML attribute that 305 | /// contains the maximum value. The default is "max". 306 | /// 307 | return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { 308 | var min = options.params.min, 309 | max = options.params.max; 310 | 311 | if (min && max) { 312 | setValidationValues(options, minMaxRuleName, [min, max]); 313 | } 314 | else if (min) { 315 | setValidationValues(options, minRuleName, min); 316 | } 317 | else if (max) { 318 | setValidationValues(options, maxRuleName, max); 319 | } 320 | }); 321 | }; 322 | 323 | adapters.addSingleVal = function (adapterName, attribute, ruleName) { 324 | /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 325 | /// the jQuery Validate validation rule has a single value. 326 | /// The name of the adapter to be added. This matches the name used 327 | /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name). 328 | /// [Optional] The name of the HTML attribute that contains the value. 329 | /// The default is "val". 330 | /// [Optional] The name of the jQuery Validate rule. If not provided, the value 331 | /// of adapterName will be used instead. 332 | /// 333 | return this.add(adapterName, [attribute || "val"], function (options) { 334 | setValidationValues(options, ruleName || adapterName, options.params[attribute]); 335 | }); 336 | }; 337 | 338 | $jQval.addMethod("__dummy__", function (value, element, params) { 339 | return true; 340 | }); 341 | 342 | $jQval.addMethod("regex", function (value, element, params) { 343 | var match; 344 | if (this.optional(element)) { 345 | return true; 346 | } 347 | 348 | match = new RegExp(params).exec(value); 349 | return (match && (match.index === 0) && (match[0].length === value.length)); 350 | }); 351 | 352 | $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { 353 | var match; 354 | if (nonalphamin) { 355 | match = value.match(/\W/g); 356 | match = match && match.length >= nonalphamin; 357 | } 358 | return match; 359 | }); 360 | 361 | if ($jQval.methods.extension) { 362 | adapters.addSingleVal("accept", "mimtype"); 363 | adapters.addSingleVal("extension", "extension"); 364 | } else { 365 | // for backward compatibility, when the 'extension' validation method does not exist, such as with versions 366 | // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for 367 | // validating the extension, and ignore mime-type validations as they are not supported. 368 | adapters.addSingleVal("extension", "extension", "accept"); 369 | } 370 | 371 | adapters.addSingleVal("regex", "pattern"); 372 | adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); 373 | adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); 374 | adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); 375 | adapters.add("equalto", ["other"], function (options) { 376 | var prefix = getModelPrefix(options.element.name), 377 | other = options.params.other, 378 | fullOtherName = appendModelPrefix(other, prefix), 379 | element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; 380 | 381 | setValidationValues(options, "equalTo", element); 382 | }); 383 | adapters.add("required", function (options) { 384 | // jQuery Validate equates "required" with "mandatory" for checkbox elements 385 | if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { 386 | setValidationValues(options, "required", true); 387 | } 388 | }); 389 | adapters.add("remote", ["url", "type", "additionalfields"], function (options) { 390 | var value = { 391 | url: options.params.url, 392 | type: options.params.type || "GET", 393 | data: {} 394 | }, 395 | prefix = getModelPrefix(options.element.name); 396 | 397 | $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { 398 | var paramName = appendModelPrefix(fieldName, prefix); 399 | value.data[paramName] = function () { 400 | var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']"); 401 | // For checkboxes and radio buttons, only pick up values from checked fields. 402 | if (field.is(":checkbox")) { 403 | return field.filter(":checked").val() || field.filter(":hidden").val() || ''; 404 | } 405 | else if (field.is(":radio")) { 406 | return field.filter(":checked").val() || ''; 407 | } 408 | return field.val(); 409 | }; 410 | }); 411 | 412 | setValidationValues(options, "remote", value); 413 | }); 414 | adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { 415 | if (options.params.min) { 416 | setValidationValues(options, "minlength", options.params.min); 417 | } 418 | if (options.params.nonalphamin) { 419 | setValidationValues(options, "nonalphamin", options.params.nonalphamin); 420 | } 421 | if (options.params.regex) { 422 | setValidationValues(options, "regex", options.params.regex); 423 | } 424 | }); 425 | 426 | $(function () { 427 | $jQval.unobtrusive.parse(document); 428 | }); 429 | }(jQuery)); -------------------------------------------------------------------------------- /App.WebTest/Scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* NUGET: BEGIN LICENSE TEXT 16 | * 17 | * Microsoft grants you the right to use these script files for the sole 18 | * purpose of either: (i) interacting through your browser with the Microsoft 19 | * website or online service, subject to the applicable licensing or use 20 | * terms; or (ii) using the files as included with a Microsoft product subject 21 | * to that product's license terms. Microsoft reserves all other rights to the 22 | * files not expressly granted by Microsoft, whether by implication, estoppel 23 | * or otherwise. Insofar as a script file is dual licensed under GPL, 24 | * Microsoft neither took the code under GPL nor distributes it thereunder but 25 | * under the terms set out in this paragraph. All notices and licenses 26 | * below are for informational purposes only. 27 | * 28 | * NUGET: END LICENSE TEXT */ 29 | /* 30 | ** Unobtrusive validation support library for jQuery and jQuery Validate 31 | ** Copyright (C) Microsoft Corporation. All rights reserved. 32 | */ 33 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this),c="__jquery_unobtrusive_validation_form_reset";if(b.data(c))return;b.data(c,true);try{b.data("validator").resetForm()}finally{b.removeData(c)}b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){var d=a(b.form).find(":input").filter("[name='"+f(c)+"']");return d.is(":checkbox")?d.filter(":checked").val()||d.filter(":hidden").val()||"":d.is(":radio")?d.filter(":checked").val()||"":d.val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); -------------------------------------------------------------------------------- /App.WebTest/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "About"; 3 | } 4 |

    @ViewBag.Title.

    5 |

    @ViewBag.Message

    6 | 7 |

    Use this area to provide additional information.

    8 | -------------------------------------------------------------------------------- /App.WebTest/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Contact"; 3 | } 4 |

    @ViewBag.Title.

    5 |

    @ViewBag.Message

    6 | 7 |
    8 | One Microsoft Way
    9 | Redmond, WA 98052-6399
    10 | P: 11 | 425.555.0100 12 |
    13 | 14 |
    15 | Support: Support@example.com
    16 | Marketing: Marketing@example.com 17 |
    -------------------------------------------------------------------------------- /App.WebTest/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Home Page"; 3 | } 4 | 5 |
    6 |

    ASP.NET

    7 |

    ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.

    8 |

    Learn more »

    9 |
    10 | 11 |
    12 |
    13 |

    Getting started

    14 |

    15 | ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that 16 | enables a clean separation of concerns and gives you full control over markup 17 | for enjoyable, agile development. 18 |

    19 |

    Learn more »

    20 |
    21 |
    22 |

    Get more libraries

    23 |

    NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.

    24 |

    Learn more »

    25 |
    26 |
    27 |

    Web Hosting

    28 |

    You can easily find a web hosting company that offers the right mix of features and price for your applications.

    29 |

    Learn more »

    30 |
    31 |
    -------------------------------------------------------------------------------- /App.WebTest/Views/QRCode/Index.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewBag.Title = "Index"; 4 | } 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /App.WebTest/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 错误 7 | 8 | 9 |
    10 |

    错误。

    11 |

    处理你的请求时出错。

    12 |
    13 | 14 | 15 | -------------------------------------------------------------------------------- /App.WebTest/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | @ViewBag.Title - 我的 ASP.NET 应用程序 8 | @Styles.Render("~/Content/css") 9 | @Scripts.Render("~/bundles/modernizr") 10 | 11 | 12 | 31 |
    32 | @RenderBody() 33 |
    34 |
    35 |

    © @DateTime.Now.Year - 我的 ASP.NET 应用程序

    36 |
    37 |
    38 | 39 | @Scripts.Render("~/bundles/jquery") 40 | @Scripts.Render("~/bundles/bootstrap") 41 | @RenderSection("scripts", required: false) 42 | 43 | 44 | -------------------------------------------------------------------------------- /App.WebTest/Views/Validate/ValidateIndex.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewBag.Title = "ValidateIndex"; 4 | } 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /App.WebTest/Views/Web.config: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /App.WebTest/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /App.WebTest/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /App.WebTest/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /App.WebTest/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 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 | -------------------------------------------------------------------------------- /App.WebTest/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenha/Utility/1119ca4ef742dddfd3b267d5529fb2f74e2535db/App.WebTest/favicon.ico -------------------------------------------------------------------------------- /App.WebTest/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenha/Utility/1119ca4ef742dddfd3b267d5529fb2f74e2535db/App.WebTest/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /App.WebTest/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenha/Utility/1119ca4ef742dddfd3b267d5529fb2f74e2535db/App.WebTest/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /App.WebTest/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenha/Utility/1119ca4ef742dddfd3b267d5529fb2f74e2535db/App.WebTest/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /App.WebTest/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenha/Utility/1119ca4ef742dddfd3b267d5529fb2f74e2535db/App.WebTest/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /App.WebTest/packages.config: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .NET通用类库 2 | 该解决方法总结了.NET开发中常用的公共类库,这些公共类库不需要依赖现有项目,直接在实际解决方案中添加使用即可(个别类需要从NuGet包中添加相关DLL) 3 | 4 | ## .NET后台服务端实现常用支付 5 | * [.NET微信小程序支付后台实现](https://github.com/wenha/Utility/tree/master/App.Pay/WePay/XcxPay) 6 | * [.NET微信APP支付后台实现](https://github.com/wenha/Utility/tree/master/App.Pay/WePay/AppPay) 7 | * [.NET支付宝支付后台实现](https://github.com/wenha/Utility/tree/master/App.Pay/AliPay) 8 | 9 | ## .NET验证码生成 10 | * [.NET验证码生成](https://github.com/wenha/Utility/tree/master/App.Common/ValidateCode) 11 | 12 | ## .NET压缩与解压缩 13 | * [.NET压缩与解压缩](https://github.com/wenha/Utility/tree/master/App.Common/SharpZip) 14 | 15 | ## .NET二维码生成 16 | * [.NET二维码](https://github.com/wenha/Utility/tree/master/App.Common/QRCode) -------------------------------------------------------------------------------- /Utility.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App.Common", "App.Common\App.Common.csproj", "{50777A16-B198-47C9-9510-5B2C50C7B48A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App.WebTest", "App.WebTest\App.WebTest.csproj", "{AC647A8A-15B3-47EA-970B-68C76AA6AEDE}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App.Pay", "App.Pay\App.Pay.csproj", "{97617EB9-9E25-4819-8729-E98AF6B3D297}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {50777A16-B198-47C9-9510-5B2C50C7B48A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {50777A16-B198-47C9-9510-5B2C50C7B48A}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {50777A16-B198-47C9-9510-5B2C50C7B48A}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {50777A16-B198-47C9-9510-5B2C50C7B48A}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {AC647A8A-15B3-47EA-970B-68C76AA6AEDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {AC647A8A-15B3-47EA-970B-68C76AA6AEDE}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {AC647A8A-15B3-47EA-970B-68C76AA6AEDE}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {AC647A8A-15B3-47EA-970B-68C76AA6AEDE}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {97617EB9-9E25-4819-8729-E98AF6B3D297}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {97617EB9-9E25-4819-8729-E98AF6B3D297}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {97617EB9-9E25-4819-8729-E98AF6B3D297}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {97617EB9-9E25-4819-8729-E98AF6B3D297}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {25D489C7-A7CD-4A83-A7DA-D3F51DA4ADD5} 36 | EndGlobalSection 37 | EndGlobal 38 | --------------------------------------------------------------------------------