/// <summary> /// Gets API credentials /// </summary> /// <returns></returns> public static CustomSecurityHeaderType GetPaypalApiCredentials(PayPalApiSettingsBase settings) { CustomSecurityHeaderType customSecurityHeaderType = new CustomSecurityHeaderType(); customSecurityHeaderType.Credentials = new UserIdPasswordType(); customSecurityHeaderType.Credentials.Username = settings.ApiAccountName; customSecurityHeaderType.Credentials.Password = settings.ApiAccountPassword; customSecurityHeaderType.Credentials.Signature = settings.Signature; customSecurityHeaderType.Credentials.Subject = ""; return customSecurityHeaderType; }
public PayPalResponse GetPayment(PayPalApiSettingsBase settings, PayPalSessionData session) { var result = CallApi("GET", "/v1/payments/payment/" + session.PaymentId, session.AccessToken, settings, null); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } return result; }
/// <remarks>return 503 (HttpStatusCode.ServiceUnavailable) to ask paypal to resend it at later time again</remarks> public HttpStatusCode ProcessWebhook( PayPalApiSettingsBase settings, NameValueCollection headers, string rawJson, string providerSystemName) { if (rawJson.IsEmpty()) return HttpStatusCode.OK; dynamic json = JObject.Parse(rawJson); var eventType = (string)json.event_type; //foreach (var key in headers.AllKeys)"{0}: {1}".FormatInvariant(key, headers[key]).Dump(); //string data = JsonConvert.SerializeObject(json, Formatting.Indented);data.Dump(); // validating against PayPal SDK failing using sandbox, so better we do not use it: //var apiContext = new global::PayPal.Api.APIContext //{ // AccessToken = "I do not have one here", // Config = new Dictionary<string, string> // { // { "mode", settings.UseSandbox ? "sandbox" : "live" }, // { "clientId", settings.ClientId }, // { "clientSecret", settings.Secret }, // { "webhook.id", setting.WebhookId }, // } //}; //var result = global::PayPal.Api.WebhookEvent.ValidateReceivedEvent(apiContext, headers, rawJson, webhookId); //} var paymentId = (string)json.resource.parent_payment; if (paymentId.IsEmpty()) { LogError(null, T("Plugins.SmartStore.PayPal.FoundOrderForPayment", 0, "".NaIfEmpty()), JsonConvert.SerializeObject(json, Formatting.Indented), isWarning: true); return HttpStatusCode.OK; } var orders = _orderRepository.Value.Table .Where(x => x.PaymentMethodSystemName == providerSystemName && x.AuthorizationTransactionCode == paymentId) .ToList(); if (orders.Count != 1) { LogError(null, T("Plugins.SmartStore.PayPal.FoundOrderForPayment", orders.Count, paymentId), JsonConvert.SerializeObject(json, Formatting.Indented), isWarning: true); return HttpStatusCode.OK; } var order = orders.First(); var store = _services.StoreService.GetStoreById(order.StoreId); var total = decimal.Zero; var currency = (string)json.resource.amount.currency; var primaryCurrency = store.PrimaryStoreCurrency.CurrencyCode; if (!primaryCurrency.IsCaseInsensitiveEqual(currency)) { LogError(null, T("Plugins.SmartStore.PayPal.CurrencyNotEqual", currency.NaIfEmpty(), primaryCurrency), JsonConvert.SerializeObject(json, Formatting.Indented), isWarning: true); return HttpStatusCode.OK; } eventType = eventType.Substring(eventType.LastIndexOf('.') + 1); var newPaymentStatus = GetPaymentStatus(eventType, "authorization", order.PaymentStatus); var isValidTotal = decimal.TryParse((string)json.resource.amount.total, NumberStyles.Currency, CultureInfo.InvariantCulture, out total); if (newPaymentStatus == PaymentStatus.Refunded && (Math.Abs(order.OrderTotal) - Math.Abs(total)) > decimal.Zero) { newPaymentStatus = PaymentStatus.PartiallyRefunded; } switch (newPaymentStatus) { case PaymentStatus.Pending: break; case PaymentStatus.Authorized: if (_orderProcessingService.CanMarkOrderAsAuthorized(order)) _orderProcessingService.MarkAsAuthorized(order); break; case PaymentStatus.Paid: if (_orderProcessingService.CanMarkOrderAsPaid(order)) _orderProcessingService.MarkOrderAsPaid(order); break; case PaymentStatus.Refunded: if (_orderProcessingService.CanRefundOffline(order)) _orderProcessingService.RefundOffline(order); break; case PaymentStatus.PartiallyRefunded: if (_orderProcessingService.CanPartiallyRefundOffline(order, Math.Abs(total))) _orderProcessingService.PartiallyRefundOffline(order, Math.Abs(total)); break; case PaymentStatus.Voided: if (_orderProcessingService.CanVoidOffline(order)) _orderProcessingService.VoidOffline(order); break; } AddOrderNote(settings, order, (string)ToInfoString(json), true); return HttpStatusCode.OK; }
public PayPalResponse EnsureAccessToken(PayPalSessionData session, PayPalApiSettingsBase settings) { if (session.AccessToken.IsEmpty() || DateTime.UtcNow >= session.TokenExpiration) { var result = CallApi("POST", "/v1/oauth2/token", null, settings, "grant_type=client_credentials"); if (result.Success) { session.AccessToken = (string)result.Json.access_token; var expireSeconds = ((string)result.Json.expires_in).ToInt(5 * 60); session.TokenExpiration = DateTime.UtcNow.AddSeconds(expireSeconds); } else { return result; } } return new PayPalResponse { Success = session.AccessToken.HasValue() }; }
public PayPalResponse ExecutePayment(PayPalApiSettingsBase settings, PayPalSessionData session) { var data = new Dictionary<string, object>(); data.Add("payer_id", session.PayerId); var result = CallApi("POST", "/v1/payments/payment/{0}/execute".FormatInvariant(session.PaymentId), session.AccessToken, settings, JsonConvert.SerializeObject(data)); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; //Logger.InsertLog(LogLevel.Information, "PayPal PLUS", JsonConvert.SerializeObject(data, Formatting.Indented) + "\r\n\r\n" + result.Json.ToString()); } return result; }
public PayPalResponse DeleteCheckoutExperience(PayPalApiSettingsBase settings, PayPalSessionData session) { var result = CallApi("DELETE", "/v1/payment-experience/web-profiles/" + settings.ExperienceProfileId, session.AccessToken, settings, null); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } return result; }
public PayPalResponse DeleteWebhook(PayPalApiSettingsBase settings, PayPalSessionData session) { var result = CallApi("DELETE", "/v1/notifications/webhooks/" + settings.WebhookId, session.AccessToken, settings, null); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } return result; }
public PayPalResponse CreatePayment( PayPalApiSettingsBase settings, PayPalSessionData session, List<OrganizedShoppingCartItem> cart, string providerSystemName, string returnUrl, string cancelUrl) { var store = _services.StoreContext.CurrentStore; var customer = _services.WorkContext.CurrentCustomer; var language = _services.WorkContext.WorkingLanguage; var currencyCode = store.PrimaryStoreCurrency.CurrencyCode; var dateOfBirth = customer.GetAttribute<DateTime?>(SystemCustomerAttributeNames.DateOfBirth); Discount orderAppliedDiscount; List<AppliedGiftCard> appliedGiftCards; int redeemedRewardPoints = 0; decimal redeemedRewardPointsAmount; decimal orderDiscountInclTax; decimal totalOrderItems = decimal.Zero; var includingTax = (_services.WorkContext.GetTaxDisplayTypeFor(customer, store.Id) == TaxDisplayType.IncludingTax); var shipping = (_orderTotalCalculationService.GetShoppingCartShippingTotal(cart) ?? decimal.Zero); var paymentFee = _paymentService.GetAdditionalHandlingFee(cart, providerSystemName); var total = (_orderTotalCalculationService.GetShoppingCartTotal(cart, out orderDiscountInclTax, out orderAppliedDiscount, out appliedGiftCards, out redeemedRewardPoints, out redeemedRewardPointsAmount) ?? decimal.Zero); var data = new Dictionary<string, object>(); var redirectUrls = new Dictionary<string, object>(); var payer = new Dictionary<string, object>(); var payerInfo = new Dictionary<string, object>(); var transaction = new Dictionary<string, object>(); var amount = new Dictionary<string, object>(); var amountDetails = new Dictionary<string, object>(); var items = new List<Dictionary<string, object>>(); var itemList = new Dictionary<string, object>(); // "PayPal PLUS only supports transaction type “Sale” (instant settlement)" if (providerSystemName == PayPalPlusProvider.SystemName) data.Add("intent", "sale"); else data.Add("intent", settings.TransactMode == TransactMode.AuthorizeAndCapture ? "sale" : "authorize"); if (settings.ExperienceProfileId.HasValue()) data.Add("experience_profile_id", settings.ExperienceProfileId); // redirect urls if (returnUrl.HasValue()) redirectUrls.Add("return_url", returnUrl); if (cancelUrl.HasValue()) redirectUrls.Add("cancel_url", cancelUrl); if (redirectUrls.Any()) data.Add("redirect_urls", redirectUrls); // payer, payer_info if (dateOfBirth.HasValue) { payerInfo.Add("birth_date", dateOfBirth.Value.ToString("yyyy-MM-dd")); } if (customer.BillingAddress != null) { payerInfo.Add("billing_address", CreateAddress(customer.BillingAddress, false)); } payer.Add("payment_method", "paypal"); payer.Add("payer_info", payerInfo); data.Add("payer", payer); // line items foreach (var item in cart) { decimal unitPriceTaxRate = decimal.Zero; decimal unitPrice = _priceCalculationService.GetUnitPrice(item, true); decimal productPrice = _taxService.GetProductPrice(item.Item.Product, unitPrice, includingTax, customer, out unitPriceTaxRate); var line = new Dictionary<string, object>(); line.Add("quantity", item.Item.Quantity); line.Add("name", item.Item.Product.GetLocalized(x => x.Name, language.Id, true, false).Truncate(127)); line.Add("price", productPrice.FormatInvariant()); line.Add("currency", currencyCode); line.Add("sku", item.Item.Product.Sku.Truncate(50)); items.Add(line); totalOrderItems += (productPrice * item.Item.Quantity); } var itemsPlusMisc = (totalOrderItems + shipping + paymentFee); if (total != itemsPlusMisc) { var line = new Dictionary<string, object>(); line.Add("quantity", "1"); line.Add("name", T("Plugins.SmartStore.PayPal.Other").Text.Truncate(127)); line.Add("price", (total - itemsPlusMisc).FormatInvariant()); line.Add("currency", currencyCode); items.Add(line); totalOrderItems += (total - itemsPlusMisc); } itemList.Add("items", items); if (customer.ShippingAddress != null) { itemList.Add("shipping_address", CreateAddress(customer.ShippingAddress, true)); } // transactions amountDetails.Add("shipping", shipping.FormatInvariant()); amountDetails.Add("subtotal", totalOrderItems.FormatInvariant()); if (!includingTax) { // "To avoid rounding errors we recommend not submitting tax amounts on line item basis. // Calculated tax amounts for the entire shopping basket may be submitted in the amount objects. // In this case the item amounts will be treated as amounts excluding tax. // In a B2C scenario, where taxes are included, no taxes should be submitted to PayPal." SortedDictionary<decimal, decimal> taxRates = null; var taxTotal = _orderTotalCalculationService.GetTaxTotal(cart, out taxRates); amountDetails.Add("tax", taxTotal.FormatInvariant()); } if (paymentFee != decimal.Zero) { amountDetails.Add("handling_fee", paymentFee.FormatInvariant()); } amount.Add("total", total.FormatInvariant()); amount.Add("currency", currencyCode); amount.Add("details", amountDetails); transaction.Add("amount", amount); transaction.Add("item_list", itemList); transaction.Add("invoice_number", session.OrderGuid.ToString()); data.Add("transactions", new List<Dictionary<string, object>> { transaction }); var result = CallApi("POST", "/v1/payments/payment", session.AccessToken, settings, JsonConvert.SerializeObject(data)); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } //Logger.InsertLog(LogLevel.Information, "PayPal PLUS", JsonConvert.SerializeObject(data, Formatting.Indented) + "\r\n\r\n" + (result.Json != null ? result.Json.ToString() : "")); return result; }
public PayPalResponse CreateWebhook(PayPalApiSettingsBase settings, PayPalSessionData session, string url) { var data = new Dictionary<string, object>(); var events = new List<Dictionary<string, object>>(); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.AUTHORIZATION.VOIDED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.CAPTURE.COMPLETED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.CAPTURE.DENIED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.CAPTURE.PENDING" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.CAPTURE.REFUNDED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.CAPTURE.REVERSED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.SALE.COMPLETED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.SALE.DENIED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.SALE.PENDING" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.SALE.REFUNDED" } }); events.Add(new Dictionary<string, object> { { "name", "PAYMENT.SALE.REVERSED" } }); data.Add("url", url); data.Add("event_types", events); var result = CallApi("POST", "/v1/notifications/webhooks", session.AccessToken, settings, JsonConvert.SerializeObject(data)); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } return result; }
public PayPalResponse Capture(PayPalApiSettingsBase settings, PayPalSessionData session, CapturePaymentRequest request) { var data = new Dictionary<string, object>(); //var isAuthorize = request.Order.AuthorizationTransactionCode.IsCaseInsensitiveEqual("authorize"); var path = "/v1/payments/authorization/{0}/capture".FormatInvariant(request.Order.AuthorizationTransactionId); var store = _services.StoreService.GetStoreById(request.Order.StoreId); var amount = new Dictionary<string, object>(); amount.Add("total", request.Order.OrderTotal.FormatInvariant()); amount.Add("currency", store.PrimaryStoreCurrency.CurrencyCode); data.Add("amount", amount); var result = CallApi("POST", path, session.AccessToken, settings, JsonConvert.SerializeObject(data)); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } return result; }
public PayPalResponse CallApi(string method, string path, string accessToken, PayPalApiSettingsBase settings, string data) { var isJson = (data.HasValue() && data.StartsWith("{")); var encoding = (isJson ? Encoding.UTF8 : Encoding.ASCII); var result = new PayPalResponse(); HttpWebResponse webResponse = null; var url = GetApiUrl(settings.UseSandbox) + path.EnsureStartsWith("/"); if (method.IsCaseInsensitiveEqual("GET") && data.HasValue()) url = url.EnsureEndsWith("?") + data; if (settings.SecurityProtocol.HasValue) ServicePointManager.SecurityProtocol = settings.SecurityProtocol.Value; var request = (HttpWebRequest)WebRequest.Create(url); request.Method = method; request.Accept = "application/json"; request.ContentType = (isJson ? "application/json" : "application/x-www-form-urlencoded"); try { if (HttpContext.Current != null && HttpContext.Current.Request != null) request.UserAgent = HttpContext.Current.Request.UserAgent; else request.UserAgent = Plugin.SystemName; } catch { } if (path.EmptyNull().EndsWith("/token")) { // see https://github.com/paypal/sdk-core-dotnet/blob/master/Source/SDK/OAuthTokenCredential.cs byte[] credentials = Encoding.UTF8.GetBytes("{0}:{1}".FormatInvariant(settings.ClientId, settings.Secret)); request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(credentials)); } else { request.Headers["Authorization"] = "Bearer " + accessToken.EmptyNull(); } if (data.HasValue() && (method.IsCaseInsensitiveEqual("POST") || method.IsCaseInsensitiveEqual("PUT") || method.IsCaseInsensitiveEqual("PATCH"))) { byte[] bytes = encoding.GetBytes(data); request.ContentLength = bytes.Length; using (var stream = request.GetRequestStream()) { stream.Write(bytes, 0, bytes.Length); } } else { request.ContentLength = 0; } try { webResponse = request.GetResponse() as HttpWebResponse; result.Success = ((int)webResponse.StatusCode < 400); } catch (WebException wexc) { result.Success = false; result.ErrorMessage = wexc.ToString(); webResponse = wexc.Response as HttpWebResponse; } catch (Exception exception) { result.Success = false; result.ErrorMessage = exception.ToString(); LogError(exception); } try { if (webResponse != null) { using (var reader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8)) { var rawResponse = reader.ReadToEnd(); if (rawResponse.HasValue()) { if (webResponse.ContentType.IsCaseInsensitiveEqual("application/json")) { if (rawResponse.StartsWith("[")) result.Json = JArray.Parse(rawResponse); else result.Json = JObject.Parse(rawResponse); if (result.Json != null) { if (!result.Success) { var name = (string)result.Json.name; var message = (string)result.Json.message; if (name.IsEmpty()) name = (string)result.Json.error; if (message.IsEmpty()) message = (string)result.Json.error_description; result.ErrorMessage = "{0} ({1}).".FormatInvariant(message.NaIfEmpty(), name.NaIfEmpty()); } } } else if (!result.Success) { result.ErrorMessage = rawResponse; } } } if (!result.Success) { if (result.ErrorMessage.IsEmpty()) result.ErrorMessage = webResponse.StatusDescription; LogError(null, result.ErrorMessage, result.Json == null ? null : result.Json.ToString(), false); } } } catch (Exception exception) { LogError(exception); } finally { if (webResponse != null) { webResponse.Close(); webResponse.Dispose(); } } return result; }
public PayPalResponse Void(PayPalApiSettingsBase settings, PayPalSessionData session, VoidPaymentRequest request) { var path = "/v1/payments/authorization/{0}/void".FormatInvariant(request.Order.AuthorizationTransactionId); var result = CallApi("POST", path, session.AccessToken, settings, null); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } return result; }
public PayPalResponse UpsertCheckoutExperience(PayPalApiSettingsBase settings, PayPalSessionData session, Store store) { PayPalResponse result; var name = store.Name; var logo = _pictureService.Value.GetPictureById(store.LogoPictureId); var path = "/v1/payment-experience/web-profiles"; var data = new Dictionary<string, object>(); var presentation = new Dictionary<string, object>(); var inpuFields = new Dictionary<string, object>(); // find existing profile id, only one profile per profile name possible if (settings.ExperienceProfileId.IsEmpty()) { result = CallApi("GET", path, session.AccessToken, settings, null); if (result.Success && result.Json != null) { foreach (var profile in result.Json) { var profileName = (string)profile.name; if (profileName.IsCaseInsensitiveEqual(name)) { settings.ExperienceProfileId = (string)profile.id; break; } } } } presentation.Add("brand_name", name); presentation.Add("locale_code", _services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToUpper()); if (logo != null) presentation.Add("logo_image", _pictureService.Value.GetPictureUrl(logo, showDefaultPicture: false, storeLocation: store.Url)); inpuFields.Add("allow_note", false); inpuFields.Add("no_shipping", 0); inpuFields.Add("address_override", 1); data.Add("name", name); data.Add("presentation", presentation); data.Add("input_fields", inpuFields); if (settings.ExperienceProfileId.HasValue()) path = string.Concat(path, "/", HttpUtility.UrlPathEncode(settings.ExperienceProfileId)); result = CallApi(settings.ExperienceProfileId.HasValue() ? "PUT" : "POST", path, session.AccessToken, settings, JsonConvert.SerializeObject(data)); if (result.Success) { if (result.Json != null) result.Id = (string)result.Json.id; else result.Id = settings.ExperienceProfileId; } return result; }
public PayPalResponse Refund(PayPalApiSettingsBase settings, PayPalSessionData session, RefundPaymentRequest request) { var data = new Dictionary<string, object>(); var store = _services.StoreService.GetStoreById(request.Order.StoreId); var isSale = request.Order.AuthorizationTransactionResult.Contains("(sale)"); var path = "/v1/payments/{0}/{1}/refund".FormatInvariant(isSale ? "sale" : "capture", request.Order.CaptureTransactionId); var amount = new Dictionary<string, object>(); amount.Add("total", request.AmountToRefund.FormatInvariant()); amount.Add("currency", store.PrimaryStoreCurrency.CurrencyCode); data.Add("amount", amount); var result = CallApi("POST", path, session.AccessToken, settings, data.Any() ? JsonConvert.SerializeObject(data) : null); if (result.Success && result.Json != null) { result.Id = (string)result.Json.id; } //Logger.InsertLog(LogLevel.Information, "PayPal Refund", JsonConvert.SerializeObject(data, Formatting.Indented) + "\r\n\r\n" + (result.Json != null ? result.Json.ToString() : "")); return result; }