獲取支付憑據
/// <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; }