获取支付凭据
/// <summary> /// 获取支付凭据 /// </summary> /// <param name="model"></param> /// <returns></returns> [HttpPost] [IsLoginAuthorizeAttribute] public MessagesDataCode<string> GetPaymentCredential([FromBody] AlipayCharge model) { MessagesDataCode<string> result = new MessagesDataCode<string>(false, "无效的参数", -300); if (model != null) { try { Pingpp.Pingpp.SetApiKey("sk_live_v5KiDKS0Wz1Cnv5Oa19ebrTS"); //sk_test_zvHePSeH0SuHLqP4SGaPKWDS string appId = "app_4WzfvTm9CGi1eLO0"; string ip = HttpContext.Current.Request.UserHostAddress; logger.Error("支付获取到的Ip地址为:" + ip); string OrderNum = DateTime.Now.ToString("yyyyMMddHHmmss") + new Random().Next(1, 1000); //唯一订单号 int UserId = ADT.Cache.Cache.CAC.Instance.GetUseridBySession(); //用户id var chParams = new Dictionary<string, object> { // 推荐使用 8-20 位,要求数字或字母,不允许其他字符 {"order_no",OrderNum }, // 订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100) {"amount",1}, // 支付使用的第三方支付渠道取值,请参考: https://www.pingxx.com/api#支付渠道属性值 {"channel", model.channel}, // 3 位 ISO 货币代码,人民币为 cny 。 {"currency", "cny"}, // 商品标题,该参数最长为 32 个 Unicode 字符,银联全渠道( upacp / upacp_wap )限制在 32 个字节。 {"subject", model.subject}, // 商品描述信息,该参数最长为 128 个 Unicode 字符, yeepay_wap 对于该参数长度限制为 100 个 Unicode 字符。 {"body", model.body}, // 发起支付请求客户端的 IP 地址,格式为 IPV4,如: 127.0.0.1 {"client_ip",ip}, {"app", new Dictionary<string, string> {{"id", appId}}}, // 特定渠道发起交易时需要的额外参数,以及部分渠道支付成功返回的额外参数,详细参考 https://www.pingxx.com/api#支付渠道-extra-参数说明 {"extra", new Dictionary<string,object>{ // 可选,开放平台返回的包含账户信息的 token(授权令牌,商户在一定时间内对支付宝某些服务的访问权限)。通过授权登录后获取的 alipay_open_id ,作为该参数的 value ,登录授权账户即会为支付账户,32 位字符串。 //{"extern_token", ""}, // 可选,是否发起实名校验,T 代表发起实名校验;F 代表不发起实名校验。 {"rn_check",'T'} }}, // 可选:订单失效时间,用 Unix 时间戳表示。时间范围在订单创建后的 1 分钟到 15 天,默认为 1 天,创建时间以 Ping++ 服务器时间为准。 微信对该参数的有效值限制为 2 小时内;银联对该参数的有效值限制为 1 小时内。 {"time_expire", CreateBase64.timeExpire()}, // 可选:订单附加说明,最多 255 个 Unicode 字符。 {"description",model.description} }; var ch = Charge.Create(chParams); var test = JsonConvert.SerializeObject(ch); result.Data = test; result.Success = true; result.Code = 0; result.Msg = "成功获取支付凭据"; //在这里生成订单 PayLogBean PayModel = new PayLogBean(); PayModel.PayNo = OrderNum; PayModel.PayTime = DateTime.Now; PayModel.Type = model.channel; PayModel.Valid = false; PayModel.UserId = UserId; PayModel.ChangeNum = model.ChangeNum; PayModel.OrderType = model.OrderType; if (!new PayLog().Add(PayModel)) { logger.Error("创建订单失败,该用户Id为" + UserId); } } catch (Exception ex) { logger.Error("创建支付凭据对象发生异常:" + ex); } } return result; }
验证签名
public static class RSACryptoServiceProviderExtension { private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public static bool VerifySignedHash(string strDataToVerify, string strSignedData, string strPublicKeyFilePath) { byte[] signedData = Convert.FromBase64String(strSignedData); //加密过的签名 UTF8Encoding ByteConverter = new UTF8Encoding(); byte[] dataToVerify = ByteConverter.GetBytes(strDataToVerify);//源数据,使用相同的算法进行消息摘要 ,和解密后的签名比较 try { string sPublicKeyPem = File.ReadAllText(strPublicKeyFilePath); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider { PersistKeyInCsp = false }; rsa.LoadPublicKeyPEM(sPublicKeyPem); if (rsa.VerifyData(dataToVerify, "SHA256", signedData)) { return true; } } catch (CryptographicException e) { logger.Error(e.Message); } return false; } #region Methods /// <summary>Extension method which initializes an RSACryptoServiceProvider from a DER public key blob.</summary> public static void LoadPublicKeyDER(this RSACryptoServiceProvider provider, byte[] DERData) { byte[] RSAData = RSACryptoServiceProviderExtension.GetRSAFromDER(DERData); byte[] publicKeyBlob = RSACryptoServiceProviderExtension.GetPublicKeyBlobFromRSA(RSAData); provider.ImportCspBlob(publicKeyBlob); } /// <summary>Extension method which initializes an RSACryptoServiceProvider from a DER private key blob.</summary> public static void LoadPrivateKeyDER(this RSACryptoServiceProvider provider, byte[] DERData) { byte[] privateKeyBlob = RSACryptoServiceProviderExtension.GetPrivateKeyDER(DERData); provider.ImportCspBlob(privateKeyBlob); } /// <summary>Extension method which initializes an RSACryptoServiceProvider from a PEM public key string.</summary> public static void LoadPublicKeyPEM(this RSACryptoServiceProvider provider, string sPEM) { byte[] DERData = RSACryptoServiceProviderExtension.GetDERFromPEM(sPEM); RSACryptoServiceProviderExtension.LoadPublicKeyDER(provider, DERData); } /// <summary>Extension method which initializes an RSACryptoServiceProvider from a PEM private key string.</summary> public static void LoadPrivateKeyPEM(this RSACryptoServiceProvider provider, string sPEM) { byte[] DERData = RSACryptoServiceProviderExtension.GetDERFromPEM(sPEM); RSACryptoServiceProviderExtension.LoadPrivateKeyDER(provider, DERData); } /// <summary>Returns a public key blob from an RSA public key.</summary> internal static byte[] GetPublicKeyBlobFromRSA(byte[] RSAData) { byte[] data = null; UInt32 dwCertPublicKeyBlobSize = 0; if (RSACryptoServiceProviderExtension.CryptDecodeObject(CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr((int)CRYPT_OUTPUT_TYPES.RSA_CSP_PUBLICKEYBLOB), RSAData, (UInt32)RSAData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwCertPublicKeyBlobSize)) { data = new byte[dwCertPublicKeyBlobSize]; if (!RSACryptoServiceProviderExtension.CryptDecodeObject(CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr((int)CRYPT_OUTPUT_TYPES.RSA_CSP_PUBLICKEYBLOB), RSAData, (UInt32)RSAData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwCertPublicKeyBlobSize)) throw new Win32Exception(Marshal.GetLastWin32Error()); } else throw new Win32Exception(Marshal.GetLastWin32Error()); return data; } /// <summary>Converts DER binary format to a CAPI CRYPT_PRIVATE_KEY_INFO structure.</summary> internal static byte[] GetPrivateKeyDER(byte[] DERData) { byte[] data = null; UInt32 dwRSAPrivateKeyBlobSize = 0; IntPtr pRSAPrivateKeyBlob = IntPtr.Zero; if (RSACryptoServiceProviderExtension.CryptDecodeObject(CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr((int)CRYPT_OUTPUT_TYPES.PKCS_RSA_PRIVATE_KEY), DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwRSAPrivateKeyBlobSize)) { data = new byte[dwRSAPrivateKeyBlobSize]; if (!RSACryptoServiceProviderExtension.CryptDecodeObject(CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr((int)CRYPT_OUTPUT_TYPES.PKCS_RSA_PRIVATE_KEY), DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwRSAPrivateKeyBlobSize)) throw new Win32Exception(Marshal.GetLastWin32Error()); } else throw new Win32Exception(Marshal.GetLastWin32Error()); return data; } /// <summary>Converts DER binary format to a CAPI CERT_PUBLIC_KEY_INFO structure containing an RSA key.</summary> internal static byte[] GetRSAFromDER(byte[] DERData) { byte[] data = null; byte[] publicKey = null; CERT_PUBLIC_KEY_INFO info; UInt32 dwCertPublicKeyInfoSize = 0; IntPtr pCertPublicKeyInfo = IntPtr.Zero; if (RSACryptoServiceProviderExtension.CryptDecodeObject(CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr((int)CRYPT_OUTPUT_TYPES.X509_PUBLIC_KEY_INFO), DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwCertPublicKeyInfoSize)) { data = new byte[dwCertPublicKeyInfoSize]; if (RSACryptoServiceProviderExtension.CryptDecodeObject(CRYPT_ENCODING_FLAGS.X509_ASN_ENCODING | CRYPT_ENCODING_FLAGS.PKCS_7_ASN_ENCODING, new IntPtr((int)CRYPT_OUTPUT_TYPES.X509_PUBLIC_KEY_INFO), DERData, (UInt32)DERData.Length, CRYPT_DECODE_FLAGS.NONE, data, ref dwCertPublicKeyInfoSize)) { GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { info = (CERT_PUBLIC_KEY_INFO)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(CERT_PUBLIC_KEY_INFO)); publicKey = new byte[info.PublicKey.cbData]; Marshal.Copy(info.PublicKey.pbData, publicKey, 0, publicKey.Length); } finally { handle.Free(); } } else throw new Win32Exception(Marshal.GetLastWin32Error()); } else throw new Win32Exception(Marshal.GetLastWin32Error()); return publicKey; } /// <summary>Extracts the binary data from a PEM file.</summary> internal static byte[] GetDERFromPEM(string sPEM) { UInt32 dwSkip, dwFlags; UInt32 dwBinarySize = 0; if (!RSACryptoServiceProviderExtension.CryptStringToBinary(sPEM, (UInt32)sPEM.Length, CRYPT_STRING_FLAGS.CRYPT_STRING_BASE64HEADER, null, ref dwBinarySize, out dwSkip, out dwFlags)) throw new Win32Exception(Marshal.GetLastWin32Error()); byte[] decodedData = new byte[dwBinarySize]; if (!RSACryptoServiceProviderExtension.CryptStringToBinary(sPEM, (UInt32)sPEM.Length, CRYPT_STRING_FLAGS.CRYPT_STRING_BASE64HEADER, decodedData, ref dwBinarySize, out dwSkip, out dwFlags)) throw new Win32Exception(Marshal.GetLastWin32Error()); return decodedData; } #endregion Methods #region P/Invoke Constants /// <summary>Enumeration derived from Crypto API.</summary> internal enum CRYPT_ACQUIRE_CONTEXT_FLAGS : uint { CRYPT_NEWKEYSET = 0x8, CRYPT_DELETEKEYSET = 0x10, CRYPT_MACHINE_KEYSET = 0x20, CRYPT_SILENT = 0x40, CRYPT_DEFAULT_CONTAINER_OPTIONAL = 0x80, CRYPT_VERIFYCONTEXT = 0xF0000000 } /// <summary>Enumeration derived from Crypto API.</summary> internal enum CRYPT_PROVIDER_TYPE : uint { PROV_RSA_FULL = 1 } /// <summary>Enumeration derived from Crypto API.</summary> internal enum CRYPT_DECODE_FLAGS : uint { NONE = 0, CRYPT_DECODE_ALLOC_FLAG = 0x8000 } /// <summary>Enumeration derived from Crypto API.</summary> internal enum CRYPT_ENCODING_FLAGS : uint { PKCS_7_ASN_ENCODING = 0x00010000, X509_ASN_ENCODING = 0x00000001, } /// <summary>Enumeration derived from Crypto API.</summary> internal enum CRYPT_OUTPUT_TYPES : int { X509_PUBLIC_KEY_INFO = 8, RSA_CSP_PUBLICKEYBLOB = 19, PKCS_RSA_PRIVATE_KEY = 43, PKCS_PRIVATE_KEY_INFO = 44 } /// <summary>Enumeration derived from Crypto API.</summary> internal enum CRYPT_STRING_FLAGS : uint { CRYPT_STRING_BASE64HEADER = 0, CRYPT_STRING_BASE64 = 1, CRYPT_STRING_BINARY = 2, CRYPT_STRING_BASE64REQUESTHEADER = 3, CRYPT_STRING_HEX = 4, CRYPT_STRING_HEXASCII = 5, CRYPT_STRING_BASE64_ANY = 6, CRYPT_STRING_ANY = 7, CRYPT_STRING_HEX_ANY = 8, CRYPT_STRING_BASE64X509CRLHEADER = 9, CRYPT_STRING_HEXADDR = 10, CRYPT_STRING_HEXASCIIADDR = 11, CRYPT_STRING_HEXRAW = 12, CRYPT_STRING_NOCRLF = 0x40000000, CRYPT_STRING_NOCR = 0x80000000 } #endregion P/Invoke Constants #region P/Invoke Structures /// <summary>Structure from Crypto API.</summary> [StructLayout(LayoutKind.Sequential)] internal struct CRYPT_OBJID_BLOB { internal UInt32 cbData; internal IntPtr pbData; } /// <summary>Structure from Crypto API.</summary> [StructLayout(LayoutKind.Sequential)] internal struct CRYPT_ALGORITHM_IDENTIFIER { internal IntPtr pszObjId; internal CRYPT_OBJID_BLOB Parameters; } /// <summary>Structure from Crypto API.</summary> [StructLayout(LayoutKind.Sequential)] struct CRYPT_BIT_BLOB { internal UInt32 cbData; internal IntPtr pbData; internal UInt32 cUnusedBits; } /// <summary>Structure from Crypto API.</summary> [StructLayout(LayoutKind.Sequential)] struct CERT_PUBLIC_KEY_INFO { internal CRYPT_ALGORITHM_IDENTIFIER Algorithm; internal CRYPT_BIT_BLOB PublicKey; } #endregion P/Invoke Structures #region P/Invoke Functions /// <summary>Function for Crypto API.</summary> [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CryptDestroyKey(IntPtr hKey); /// <summary>Function for Crypto API.</summary> [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CryptImportKey(IntPtr hProv, byte[] pbKeyData, UInt32 dwDataLen, IntPtr hPubKey, UInt32 dwFlags, ref IntPtr hKey); /// <summary>Function for Crypto API.</summary> [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CryptReleaseContext(IntPtr hProv, Int32 dwFlags); /// <summary>Function for Crypto API.</summary> [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CryptAcquireContext(ref IntPtr hProv, string pszContainer, string pszProvider, CRYPT_PROVIDER_TYPE dwProvType, CRYPT_ACQUIRE_CONTEXT_FLAGS dwFlags); /// <summary>Function from Crypto API.</summary> [DllImport("crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CryptStringToBinary(string sPEM, UInt32 sPEMLength, CRYPT_STRING_FLAGS dwFlags, [Out] byte[] pbBinary, ref UInt32 pcbBinary, out UInt32 pdwSkip, out UInt32 pdwFlags); /// <summary>Function from Crypto API.</summary> [DllImport("crypt32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CryptDecodeObjectEx(CRYPT_ENCODING_FLAGS dwCertEncodingType, IntPtr lpszStructType, byte[] pbEncoded, UInt32 cbEncoded, CRYPT_DECODE_FLAGS dwFlags, IntPtr pDecodePara, ref byte[] pvStructInfo, ref UInt32 pcbStructInfo); /// <summary>Function from Crypto API.</summary> [DllImport("crypt32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CryptDecodeObject(CRYPT_ENCODING_FLAGS dwCertEncodingType, IntPtr lpszStructType, byte[] pbEncoded, UInt32 cbEncoded, CRYPT_DECODE_FLAGS flags, [In, Out] byte[] pvStructInfo, ref UInt32 cbStructInfo); #endregion P/Invoke Functions }
接受支付结果(异步)
public HttpResponseMessage Index() { MessagesCode result = new MessagesCode(false, "无效的参数", -300); try { if (HttpContext.Current.Request.RequestType.ToUpper().Equals("POST")) { //获取 post 的 event对象 var inputData = ReadStream(HttpContext.Current.Request.InputStream); //获取 header 中的签名 var sig = HttpContext.Current.Request.Headers.Get("x-pingplusplus-signature"); //公钥路径(请检查你的公钥 .pem 文件存放路径) var path = HttpContext.Current.Server.MapPath("/Lib/public_key.pem"); //验证签名 if (RSACryptoServiceProviderExtension.VerifySignedHash(inputData, sig, path)) { var jObject = JObject.Parse(inputData); var type = jObject.SelectToken("type"); var eventType = jObject.SelectToken("object"); if (eventType.ToString() == "event")//验证接收到的是否为 Event 对象。 { if (type.ToString() == "charge.succeeded") { //在这里做支付成功的逻辑处理 1.订单状态改变 2.支付目的改变 3.这里要限制一下更新的次数,只有当订单是未支付时在进行下面的操作 var data = jObject.SelectToken("data"); var credentialObject = data["object"];//凭据对象 var PayNo = credentialObject["order_no"];//订单号 logger.Error("接受到支付成功的事件"); bool IsOk = false; PayLogBean model = new PayLog().GetModelById(PayNo.ToString()); if (model != null) { if (model.Valid) { IsOk = true; } else { using (TransactionScope ts = new TransactionScope()) { bool OneTrans = false; bool TwoTrans = false; OneTrans = new PayLog().UpdateValid(PayNo.ToString(), true); UserBean userModel = new User().GetModelById((int)model.UserId); if (userModel != null) { if (model.OrderType ==1) { if (Convert.IsDBNull(userModel.Ranketime)|| userModel.RankId==1)//一定不是会员 { userModel.Rankbtime = DateTime.Now; userModel.Ranketime = DateTime.Now.AddMonths((int)model.ChangeNum); userModel.RankId = 2; } else {//代表历史上是有会员的记录的 //判断当前是否为会员 if (userModel.RankId == 2 && userModel.Ranketime >= DateTime.Now) { userModel.Ranketime = userModel.Ranketime.AddMonths((int)model.ChangeNum); } else { userModel.RankId = 2; userModel.Ranketime = DateTime.Now.AddMonths((int)model.ChangeNum); userModel.Ranketime = DateTime.Now; } } //更新用户信息 TwoTrans = new User().UpdateMemRoleByRMB(userModel); } else { userModel.Currency = userModel.Currency + model.ChangeNum; TwoTrans = new User().UpdateCurrency(model.UserId, userModel.Currency); } } if (OneTrans&&TwoTrans) { IsOk = true; ts.Complete(); } } } } if (IsOk) { //在这里写日志 if (model.OrderType == 1) //购买会员 { //在这里加一个消费日志 //需要拿这个有效时间去获得人民币的价格是多少 List<ProductBean> productList = new Product().GetAllList().Where(p => p.ValidTime == model.ChangeNum).ToList(); if (productList.Count > 0) { CreateBase64.CreateReclog(7, "人民币购买会员", Convert.ToInt64(productList[0].ProdutPrice), (int)model.UserId); } } else {//购买虚拟币 CreateBase64.CreateReclog(2, "充虚拟币", model.ChangeNum, (int)model.UserId); } return Request.CreateResponse(HttpStatusCode.OK, "接受成功"); } } } } } } catch (Exception ex) { logger.Error("接受ping++的支付订单消息发生异常:" + ex); } return Request.CreateResponse(HttpStatusCode.InternalServerError, "接受失败"); } private static string ReadStream(Stream stream) { using (var reader = new StreamReader(stream, Encoding.UTF8)) { return reader.ReadToEnd(); } }
根据订单号获取支付状态
/// <summary> /// 根据订单号获取支付状态 /// </summary> /// <param name="order_no"></param> /// <returns></returns> [HttpGet] public MessagesDataCode<bool> GetOrderStatus(string order_no) { MessagesDataCode<bool> result = new MessagesDataCode<bool>(false, "无效的参数", -300); try { PayLogBean model = new PayLog().GetModelById(order_no); if (model != null) { result.Data = model.Valid; result.Code = 0; result.Msg = "成功获取到订单状态"; result.Success = true; } } catch (Exception ex) { logger.Error("获取订单状态发生异常:"+ex); } return result; }