/// <summary> /// Операция для получения токена /// </summary> /// <param name="amount">Стоимость</param> /// <param name="currencyCode">Валюта</param> /// <param name="action">Действие на сайте PayPal, которое пользователь должен будет сделать</param> /// <param name="cancelUrl">URL, куда перенаправят пользователя в случае ошибки или отказа от проведения платежа</param> /// <param name="returnUrl">URL, куда перенаправят пользователя в случае успешного выполнения платежа</param> /// <returns>Все параметры, которые пришли от PayPal</returns> public Dictionary<string, string> GetToken(Currency currencyCode, PayAction action, string cancelUrl, string returnUrl) { var errList = Validate(); if (errList.Count != 0) { throw new Exception("Can`t get PayPal token because an error has occurred: " + string.Join(",", errList.ToArray())); } if (string.IsNullOrWhiteSpace(cancelUrl)) { throw new Exception("Cancel url is not defined"); } if (string.IsNullOrWhiteSpace(returnUrl)) { throw new Exception("Successfull url is not defined"); } var retDict = new Dictionary<string, string>(); /* --- Получаем сумму для оплаты --- */ decimal amount = basket.Sum(t => Convert.ToDecimal(t.Amount)); /* --- !Получаем сумму для оплаты --- */ var client = new WebClient(); var requestStr = new StringBuilder(); requestStr.Append(string.Format("{0}/?USER={1}&PWD={2}&SIGNATURE={3}&METHOD=SetExpressCheckout&VERSION={4}&PAYMENTREQUEST_0_AMT={5}&PAYMENTREQUEST_0_CURRENCYCODE={6}&PAYMENTREQUEST_0_PAYMENTACTION={7}&cancelUrl={8}&returnUrl={9}", PayPalURL, User, Password, Signature, Version, amount.ToString("F").Replace(',','.'), currencyCode.ToString("F"), action.ToString("F"), cancelUrl, returnUrl)); requestStr.Append(currencyCode == Currency.RUB ? "&LOCALECODE=RU" : "&LOCALECODE=US"); for (int i = 0; i < basket.Count; i++) { requestStr.Append(string.Format("&L_PAYMENTREQUEST_0_NAME{0}={1}&L_PAYMENTREQUEST_0_DESC{0}={2}&L_PAYMENTREQUEST_0_AMT{0}={3}&L_PAYMENTREQUEST_0_QTY{0}={4}", i, basket[i].Name, basket[i].Description, basket[i].Amount.Replace(',', '.'), basket[i].Quantity)); } var sr = new StreamReader(client.OpenRead(requestStr.ToString())); string newLine; while ((newLine = sr.ReadLine()) != null) { foreach (var curPair in newLine.Split('&')) { var pairKeyVal = curPair.Split('='); retDict.Add(pairKeyVal[0], pairKeyVal[1]); } } TokenResponse = retDict; return retDict; }
/// <summary> /// Получаем детали о покупке /// </summary> /// <param name="token">Токен, пришедший через GET от PayPal</param> /// <returns>Детали покупки</returns> public Dictionary<string, string> GetCheckoutDetails(string token) { var errList = Validate(); if (errList.Count != 0) { throw new Exception("Can`t get PayPal token because an error has occurred: " + string.Join(",", errList.ToArray())); } if (string.IsNullOrWhiteSpace(token)) { throw new Exception("Token can`t be empty"); } var retDict = new Dictionary<string, string>(); var client = new WebClient(); var requestStr = string.Format("{0}/?USER={1}&PWD={2}&SIGNATURE={3}&METHOD=GetExpressCheckoutDetails&VERSION={4}&token={5}", PayPalURL, User, Password, Signature, Version, token); var sr = new StreamReader(client.OpenRead(requestStr)); string newLine; while ((newLine = sr.ReadLine()) != null) { foreach (var curPair in newLine.Split('&')) { var pairKeyVal = curPair.Split('='); retDict.Add(pairKeyVal[0], pairKeyVal[1]); } } TokenResponse = retDict; return retDict; }
/// <summary> /// Когда через IPN от сервера PayPal пришло сообщение, мы должны проверить его валидность этим методом /// </summary> /// <param name="postData">Словарь всех параметров, что пришли в посте через IPN</param> /// <param name="isTest">Режим работы PayPal</param> public static bool ValidateIPN(Dictionary<string, string> postData, bool isTest) { var postStr = new StringBuilder(); foreach(var entry in postData) { postStr.Append(string.Format("&{0}={1}", entry.Key, entry.Value)); } var client = new WebClient(); var requestStr = string.Format("{0}/?cmd=_notify-validate{1}", isTest?HARDCODED_SandboxPaymentURL : HARDCODED_PaymentURL, postStr); var sr = new StreamReader(client.OpenRead(requestStr)); var newLine = sr.ReadLine(); /*/* --- Debug --- #1# SiteUtils.SendMail(null, null, "*****@*****.**", "Антон Старостин", "Валидация запроса от PayPal", string.Format("<p>Request str: {0}</p><p>Response from PayPal: {1}</p>", requestStr, newLine), true); /* --- Debug --- #1#*/ return !string.IsNullOrWhiteSpace(newLine) && newLine == "VERIFIED"; // В случае подлинного сообщения, вернется VERIFIED, в ином случае - INVALID }
/// <summary> /// Завершает платеж и производит транзакцию /// </summary> /// <param name="token">Токен, выданный PayPal для операции. После операции перевода он должен прийти в GET параметре</param> /// <param name="payerId">Идентификатор плательщика, выданный PayPal после операции. Он должен прийти в GET параметре</param> /// <param name="amount">Стоимость. Должна совпадать с той стоимостью, что указана в токене</param> /// <returns></returns> public Dictionary<string, string> ConfirmPayment(string token, string payerId, decimal amount, Currency curr) { var errList = Validate(); if (errList.Count != 0) { throw new Exception("Can`t get PayPal token because an error has occurred: " + string.Join(",", errList.ToArray())); } if (string.IsNullOrWhiteSpace(payerId)) { throw new Exception("Payer ID can`t be empty"); } if (string.IsNullOrWhiteSpace(token)) { throw new Exception("Token can`t be empty"); } var retDict = new Dictionary<string, string>(); var client = new WebClient(); var requestStr = string.Format("{0}/?USER={1}&PWD={2}&SIGNATURE={3}&METHOD=DoExpressCheckoutPayment&VERSION={4}&token={5}&PAYERID={6}&PAYMENTREQUEST_0_AMT={7}&PAYMENTREQUEST_0_CURRENCYCODE={8}", PayPalURL, User, Password, Signature, Version, token, payerId, amount.ToString("F").Replace(',','.'), curr); if (!string.IsNullOrWhiteSpace(NotifyURL)) { requestStr += string.Format("&PAYMENTREQUEST_0_NOTIFYURL={0}", NotifyURL); } var sr = new StreamReader(client.OpenRead(requestStr)); string newLine; while ((newLine = sr.ReadLine()) != null) { foreach (var curPair in newLine.Split('&')) { var pairKeyVal = curPair.Split('='); retDict.Add(pairKeyVal[0], pairKeyVal[1]); } } TokenResponse = retDict; return retDict; }
/// <summary> /// Used to render the search result listing (virtual node) /// </summary> /// <param name="model"></param> /// <param name="term"> /// The search term /// </param> /// <param name="provider"> /// The search provider name (optional) /// </param> /// <param name="p"></param> /// <returns></returns> public ActionResult Search(RenderModel model, string term, string provider = null, int? p = null) { var tagPage = model.Content as ArticulateVirtualPage; if (tagPage == null) { throw new InvalidOperationException("The RenderModel.Content instance must be of type " + typeof(ArticulateVirtualPage)); } //create a blog model of the main page var rootPageModel = new ListModel(model.Content.Parent); if (term == null) { //nothing to search, just render the view var emptyList = new ListModel(tagPage, Enumerable.Empty<IPublishedContent>(), new PagerModel(rootPageModel.PageSize, 0, 0)); return View(PathHelper.GetThemeViewPath(emptyList, "List"), emptyList); } if (p != null && p.Value == 1) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } if (p == null || p.Value <= 0) { p = 1; } var splitSearch = term.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); //The fields to search on and their 'weight' (importance) var fields = new Dictionary<string, int> { {"markdown", 2}, {"richText", 2}, {"nodeName", 3}, {"tags", 1}, {"categories", 1}, {"umbracoUrlName", 3} }; //The multipliers for match types const int exactMatch = 5; const int termMatch = 2; var fieldQuery = new StringBuilder(); //build field query foreach (var field in fields) { //full exact match (which has a higher boost) fieldQuery.Append(string.Format("{0}:{1}^{2}", field.Key, "\"" + term + "\"", field.Value * exactMatch)); fieldQuery.Append(" "); //NOTE: Phrase match wildcard isn't really supported unless you use the Lucene // API like ComplexPhraseWildcardSomethingOrOther... //split match foreach (var s in splitSearch) { //match on each term, no wildcard, higher boost fieldQuery.Append(string.Format("{0}:{1}^{2}", field.Key, s, field.Value * termMatch)); fieldQuery.Append(" "); //match on each term, with wildcard fieldQuery.Append(string.Format("{0}:{1}*", field.Key, s)); fieldQuery.Append(" "); } } var criteria = provider == null ? ExamineManager.Instance.CreateSearchCriteria() : ExamineManager.Instance.SearchProviderCollection[provider].CreateSearchCriteria(); criteria.RawQuery(string.Format("+parentID:{0} +({1})", rootPageModel.BlogArchiveNode.Id, fieldQuery)); var searchProvider = provider == null ? ExamineManager.Instance.DefaultSearchProvider : ExamineManager.Instance.SearchProviderCollection[provider]; var searchResult = Umbraco.TypedSearch(criteria, searchProvider).ToArray(); //TODO: I wonder about the performance of this - when we end up with thousands of blog posts, // this will probably not be so efficient. I wonder if using an XPath lookup for batches of children // would work? The children count could be cached. I'd rather not put blog posts under 'month' nodes // just for the sake of performance. Hrm.... Examine possibly too. var totalPosts = searchResult.Count(); var pageSize = rootPageModel.PageSize; var totalPages = totalPosts == 0 ? 1 : Convert.ToInt32(Math.Ceiling((double)totalPosts / pageSize)); //Invalid page, redirect without pages if (totalPages < p) { return new RedirectToUmbracoPageResult(model.Content.Parent, UmbracoContext); } var pager = new PagerModel( pageSize, p.Value - 1, totalPages, totalPages > p ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p + 1) : null, p > 2 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p - 1) : p > 1 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term : null); var listModel = new ListModel(tagPage, searchResult, pager); return View(PathHelper.GetThemeViewPath(listModel, "List"), listModel); }