Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Bex 444 posts 555 karma points
    Feb 24, 2016 @ 12:20
    Bex
    0

    PayPal Updates With TeaCommerce 1.4.2.4

    Hi

    I've just been sent a link by a colleague saying that Paypal are rolling out some changes in June.

    We've got a few client sites running TeaCommerce 1.4.2.4 with PayPal.

    As this is an old version of TeaCommerce I am not quite sure what it means for us. Will this change break all these sites? If so, will there be a fix available or is it something we will need to do something about?

    Thanks for your help

    Becky

  • Anders Burla 2560 posts 8256 karma points
    Feb 24, 2016 @ 15:25
    Anders Burla
    0

    Hi Becky

    That should not be a problem as it has something to do with PayPal and HTTPS connection. Can't remember which encryption .NET uses as default when connecting to HTTPS, but I think you should be safe.

    Else the payment providers is open source:

    https://github.com/TeaCommerce/Payment-providers

    Kind regards

    Anders

  • Bex 444 posts 555 karma points
    Mar 09, 2016 @ 09:38
    Bex
    0

    For reference for others: when testing my site against the sandbox I get this error when they try and return the IPN request, so payments get taken but the site doesn't register them

    MESSAGE: Exception has been thrown by the target of an invocation. STACKTRACE: at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at TeaCommerce.Presentation.TeaCommerceBase.RequestModule.invokeMethod(restExtension myExtension, Object[] paras) INNEREXCEPTION: System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel. at System.Net.HttpWebRequest.GetRequestStream(TransportContext& context) at System.Net.HttpWebRequest.GetRequestStream() at TeaCommerce.Data.Payment.APaymentProvider.MakePostRequest(String url, String request, NetworkCredential credentials) at TeaCommerce.PaymentProviders.PayPal.ProcessCallback(Order order, HttpRequest request, Dictionary`2 settings) at TeaCommerce.Base.PaymentCallback(IPaymentProvider paymentProviderInstance, Int64 orderId) at TeaCommerce.Base.PaymentCallback(String alias, String encryptedOrderId)

    Hopefully I can work a fix but the TeaCommerce version is so old, I'm not sure if the code I'm using is in github.

  • Anders Burla 2560 posts 8256 karma points
    Mar 09, 2016 @ 10:35
    Anders Burla
    0

    The code for version 1 of Tea Commerce providers is here. BUT cant remember if this is compiled with the latest version 1.

    https://github.com/TeaCommerce/Payment-providers/tree/1.x

    Kind regards

    Anders

  • Bex 444 posts 555 karma points
    Mar 09, 2016 @ 10:38
    Bex
    0

    Thanks Anders! I will investigate.

  • Bex 444 posts 555 karma points
    Mar 16, 2016 @ 11:20
    Bex
    0

    Hi

    I am still investigating this and have found a possible fix :

    http://stackoverflow.com/questions/34939523/the-request-was-aborted-could-not-create-ssl-tls-secure-channel-sandbox-account

    I have used the version of the payment providers that you have given above to attempt to apply the fix. I am unsure as yet if it will work but I have added

    System.Net.ServicePointManager.SecurityProtocol= (SecurityProtocolType)3072; // SecurityProtocolType.Tls12

    in the process callback function as this appears to be the closest I can get to the place it seems to be required.

    When I apply the payment providers dll to a test site I get this error when trying to access the payment methods:

    Method 'GenerateForm' in type 'TeaCommerce.PaymentProviders.DIBS' from assembly 'TeaCommerce.PaymentProviders, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.

    I assume the reason for this is my teacommerce version does not match with the payment providers version?

    If this is the case I am wondering if you can help with my options?

    Is it possible to upgrade my teacommerce version, if only a little bit to match the payment providers? Assuming you know which versions match?

    Or is it possible for you to update the teacommerce 1.4.2.4 core code to include the tls change?

    or do I need to update the site entirely to run the latest version of teacommerce and what are the licence implications?

    We have numerous sites running older versions of teacommerce with numerous payment providers and although PayPal seems to be the only one applying this update the guidelines say it's going to need to be applied across the board at some point in the relatively near future so I'd rather work out what I am going to need to do now!

    Thanks for any help

  • Bex 444 posts 555 karma points
    Mar 18, 2016 @ 09:07
    Bex
    0

    Ok, got so caught up in the fact my teacommerce was so old I forgot I could actually create a properly custom payment provider.

    This I have now done and it is all working! :)

    I have added

     System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType) 3072;
    

    in the process callback.

  • Anders Burla 2560 posts 8256 karma points
    Mar 18, 2016 @ 10:54
    Anders Burla
    0

    Sweet you got it working!

  • Matt Taylor 873 posts 2086 karma points
    Jun 03, 2016 @ 10:57
    Matt Taylor
    0

    Hello Becky,

    I think you navigate to the url https://tlstest.paypal.com that is mentioned in the PayPal article in a browser on your hosting server it will tell you if your machine supports the required protocols.

    Matt

  • Matt Taylor 873 posts 2086 karma points
    Jun 03, 2016 @ 11:28
    Matt Taylor
    0

    I have a site running TC v1.4.2 and I need to make sure that it will continue to work after the PayPal updates.

    One of the things mentioned here is that all IPN postbacks will have to use HTTPS. Becky was HTTPS set up for your site? I'm assuming that it was.

    Anders can you confirm that sites running TC v1.4 will need to be running a custom PayPal provider that sets the security protocol as found by Becky in order to work?

    How about TC v2 or v3?

    Thanks,

    Matt

  • Anders Burla 2560 posts 8256 karma points
    Jun 07, 2016 @ 14:34
    Anders Burla
    0

    Hi Matt

    We have never used PayPal for one of our own clients so I don't know the exact process of have a site that works with PayPal. So I can confirm what is needed - BUT I know that the line of code Bex has written back in Mar 18, 2016 isn't in the payment provider code. So i dont know if this should be in the PayPal provider code as standard or if differs. Maybe you giys can help figure it out, and then make a pull request for the provider when you know it. Would be happy tp update the code if it doesn't work because of a PayPal update. But as we dont have a live or test account it is pretty hard to do any live debugging and testing :)

    Kind regards

    Anders

  • Bex 444 posts 555 karma points
    Jun 06, 2016 @ 08:07
    Bex
    0

    The only thing I changed was the tls setting on the post in the payment provider code. I didn't have to change anything else. I did a test to the IPN test page from page within the site and all was fine so I'm assuming that it will be fine.

  • Bex 444 posts 555 karma points
    Jun 07, 2016 @ 14:43
    Bex
    0

    If it helps. This is the code I have in the custom Paypal code for 1.4.2.4: It's exactly the same as the orginal but with that line I mentioned above.

        namespace TeaCommerce.PaymentProviders
    {
    
        public class PayPalNew : APaymentProvider
        {
    
            public override Dictionary<string, string> DefaultSettings
            {
                get
                {
                    if (defaultSettings == null)
                    {
                        defaultSettings = new Dictionary<string, string>();
                        defaultSettings["business"] = string.Empty;
                        defaultSettings["lc"] = "US";
                        defaultSettings["return"] = string.Empty;
                        defaultSettings["cancel_return"] = string.Empty;
                        defaultSettings["paymentaction"] = "authorization";
                        defaultSettings["USER"] = string.Empty;
                        defaultSettings["PWD"] = string.Empty;
                        defaultSettings["SIGNATURE"] = string.Empty;
                        defaultSettings["isSandbox"] = "0";
                        defaultSettings["productNumberPropertyAlias"] = "productNumber";
                        defaultSettings["productNamePropertyAlias"] = "productName";
                        defaultSettings["shippingMethodFormatString"] = "Shipping fee ({0})";
                        defaultSettings["paymentMethodFormatString"] = "Payment fee ({0})";
                    }
                    return defaultSettings;
                }
            }
    
            protected bool isSandbox;
    
            public override string FormPostUrl
            {
                get
                {
                    return !isSandbox
                               ? "https://www.paypal.com/cgi-bin/webscr"
                               : "https://www.sandbox.paypal.com/cgi-bin/webscr";
                }
            }
    
            protected string APIPostUrl
            {
                get { return !isSandbox ? "https://api-3t.paypal.com/nvp" : "https://api-3t.sandbox.paypal.com/nvp"; }
            }
    
    
            public override Dictionary<string, string> GenerateForm(Order order, string teaCommerceContinueUrl,
                                                                    string teaCommerceCancelUrl,
                                                                    string teaCommerceCallBackUrl,
                                                                    Dictionary<string, string> settings)
            {
                isSandbox = settings["isSandbox"] == "1";
                List<string> settingsToExclude =
                    new string[]
                        {
                            "USER", "PWD", "SIGNATURE", "isSandbox", "productNumberPropertyAlias", "productNamePropertyAlias",
                            "shippingMethodFormatString", "paymentMethodFormatString"
                        }.ToList();
                Dictionary<string, string> inputFields =
                    settings.Where(i => !settingsToExclude.Contains(i.Key)).ToDictionary(i => i.Key, i => i.Value);
    
                inputFields["cmd"] = "_cart";
                inputFields["upload"] = "1";
                inputFields["currency_code"] = order.CurrencyISOCode;
    
                inputFields["invoice"] = order.Name;
                inputFields["no_shipping"] = "1";
    
                inputFields["return"] = teaCommerceContinueUrl;
                inputFields["rm"] = "2";
                inputFields["cancel_return"] = teaCommerceCancelUrl;
                inputFields["notify_url"] = teaCommerceCallBackUrl;
    
                #region Order line information
    
                if (!string.IsNullOrEmpty(settings["productNamePropertyAlias"]))
                {
    
                    List<OrderLine> orderLines = order.OrderLines.ToList();
                    OrderLine orderLine;
                    int itemIndex = 1;
    
                    for (int i = 0; i < orderLines.Count; i++)
                    {
                        orderLine = orderLines[i];
                        OrderLineProperty productNameProp =
                            orderLine.Properties.SingleOrDefault(op => op.Alias.Equals(settings["productNamePropertyAlias"]));
                        OrderLineProperty productNumberProp =
                            orderLine.Properties.SingleOrDefault(
                                op => op.Alias.Equals(settings["productNumberPropertyAlias"]));
    
                        inputFields["item_name_" + itemIndex] = productNameProp != null
                                                                    ? productNameProp.Value
                                                                    : string.Empty;
                        if (productNumberProp != null)
                            inputFields["item_number_" + itemIndex] = productNumberProp.Value;
                        inputFields["amount_" + itemIndex] = orderLine.UnitPriceWithoutVAT.ToString("0.00",
                                                                                                    CultureInfo
                                                                                                        .InvariantCulture);
                        inputFields["tax_" + itemIndex] = orderLine.UnitVAT.ToString("0.00", CultureInfo.InvariantCulture);
                        inputFields["quantity_" + itemIndex] = orderLine.Quantity.ToString();
    
                        itemIndex++;
                    }
    
                    if (order.ShippingFee != 0)
                    {
                        inputFields["item_name_" + itemIndex] = string.Format(settings["shippingMethodFormatString"],
                                                                              order.ShippingMethod.Name);
                        inputFields["amount_" + itemIndex] = order.ShippingFeeWithoutVAT.ToString("0.00",
                                                                                                  CultureInfo
                                                                                                      .InvariantCulture);
                        inputFields["tax_" + itemIndex] = order.ShippingFeeVAT.ToString("0.00", CultureInfo.InvariantCulture);
                        itemIndex++;
                    }
    
                    if (order.PaymentFee != 0)
                    {
                        inputFields["item_name_" + itemIndex] = string.Format(settings["paymentMethodFormatString"],
                                                                              order.PaymentMethod.Name);
                        inputFields["amount_" + itemIndex] = order.PaymentFeeWithoutVAT.ToString("0.00",
                                                                                                 CultureInfo
                                                                                                     .InvariantCulture);
                        inputFields["tax_" + itemIndex] = order.PaymentFeeVAT.ToString("0.00", CultureInfo.InvariantCulture);
                    }
                }
    
                #endregion
    
                return inputFields;
            }
    
    
            public override string GetContinueUrl(Dictionary<string, string> settings)
            {
                return settings["return"];
            }
    
            public override string GetCancelUrl(Dictionary<string, string> settings)
            {
                return settings["cancel_return"];
            }
    
            public override CallbackInfo ProcessCallback(Order order, HttpRequest request,
                                                         Dictionary<string, string> settings)
            {
                //using ( StreamWriter writer = new StreamWriter( File.Create( HttpContext.Current.Server.MapPath( "~/PayPalTestCallback.txt" ) ) ) ) {
                //  writer.WriteLine( "FORM:" );
                //  foreach ( string k in request.Form.Keys ) {
                //    writer.WriteLine( k + " : " + request.Form[ k ] );
                //  }
                //  writer.Flush();
                //}
    
                string errorMessage = string.Empty;
                isSandbox = settings["isSandbox"] == "1";
    
                //Verify callback
                System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType) 3072; // SecurityProtocolType.Tls12
    
                string response = MakePostRequest(FormPostUrl,
                                                  Encoding.ASCII.GetString(request.BinaryRead(request.ContentLength)) +
                                                  "&cmd=_notify-validate");
    
                //using ( StreamWriter writer = new StreamWriter( File.Create( HttpContext.Current.Server.MapPath( "~/PayPalTestCallback2.txt" ) ) ) ) {
                //  writer.WriteLine( response );
                //  writer.Flush();
                //}
    
                if (response.Equals("VERIFIED"))
                {
    
                    string receiverId = request.Form["receiver_id"];
                    string receiverEmail = request.Form["receiver_email"];
                    string transaction = request.Form["txn_id"];
                    string transactionEntity = request.Form["transaction_entity"];
                    decimal amount = decimal.Parse(request.Form["mc_gross"], CultureInfo.InvariantCulture);
                    string state = request.Form["payment_status"];
                    string orderName = request.Form["invoice"];
    
                    string businessSetting = settings["business"];
    
                    //Check if the business email is the same in the callback
                    if (!string.IsNullOrEmpty(transaction) &&
                        ((!string.IsNullOrEmpty(receiverId) && businessSetting.Equals(receiverId)) ||
                         (!string.IsNullOrEmpty(receiverEmail) && businessSetting.Equals(receiverEmail))))
                    {
    
                        //Pending
                        if (state.Equals("Pending"))
                        {
    
                            if (request.Form["pending_reason"].Equals("authorization"))
                            {
                                if (request.Form["transaction_entity"].Equals("auth"))
                                {
                                    return new CallbackInfo(orderName, amount, transaction, PaymentStatus.Authorized, null,
                                                            null);
                                }
                            }
                            else if (request.Form["pending_reason"].Equals("multi_currency"))
                            {
                                return new CallbackInfo(orderName, amount, transaction, PaymentStatus.PendingExternalSystem,
                                                        null, null);
                            }
    
                            //Completed - auto capture
                        }
                        else if (state.Equals("Completed"))
                            return new CallbackInfo(orderName, amount, transaction, PaymentStatus.Captured, null, null);
    
                    }
                    else
                        errorMessage = "Tea Commerce - Paypal - Business isn't identical - settings: " + businessSetting +
                                       " - request-receiverId: " + receiverId + " - request-receiverEmail: " + receiverEmail;
                }
                else
                    errorMessage = "Tea Commerce - Paypal - Couldn't verify response - " + response;
    
                Log.Add(LogTypes.Error, -1, errorMessage);
                return new CallbackInfo(errorMessage);
            }
    
            public override APIInfo GetStatus(Order order, Dictionary<string, string> settings)
            {
                return InternalGetStatus(order.TransactionPaymentTransactionId, settings);
            }
    
            public override APIInfo CapturePayment(Order order, Dictionary<string, string> settings)
            {
                isSandbox = settings["isSandbox"] == "1";
    
                string errorMessage = string.Empty;
                Dictionary<string, string> inputFields = PrepareAPIPostRequest("DoCapture", settings);
    
                inputFields.Add("AUTHORIZATIONID", order.TransactionPaymentTransactionId);
                inputFields.Add("AMT", order.TotalPrice.ToString("0.00", CultureInfo.InvariantCulture));
                inputFields.Add("CURRENCYCODE", order.CurrencyISOCode);
                inputFields.Add("COMPLETETYPE", "Complete");
    
                Dictionary<string, string> responseKvp = GetApiResponseKvp(MakePostRequest(APIPostUrl, inputFields));
                if (responseKvp["ACK"].Equals("Success") || responseKvp["ACK"].Equals("SuccessWithWarning"))
                {
                    return InternalGetStatus(responseKvp["TRANSACTIONID"], settings);
                }
                else
                    errorMessage = "Tea Commerce - PayPal - " +
                                   string.Format(umbraco.ui.Text("teaCommerce", "paymentProvider_PayPal_error"),
                                                 responseKvp["L_ERRORCODE0"]);
    
                Log.Add(LogTypes.Error, -1, errorMessage);
                return new APIInfo(errorMessage);
            }
    
            public override APIInfo RefundPayment(Order order, Dictionary<string, string> settings)
            {
                isSandbox = settings["isSandbox"] == "1";
    
                string errorMessage = string.Empty;
                Dictionary<string, string> inputFields = PrepareAPIPostRequest("RefundTransaction", settings);
    
                inputFields.Add("TRANSACTIONID", order.TransactionPaymentTransactionId);
    
                Dictionary<string, string> responseKvp = GetApiResponseKvp(MakePostRequest(APIPostUrl, inputFields));
                if (responseKvp["ACK"].Equals("Success") || responseKvp["ACK"].Equals("SuccessWithWarning"))
                {
                    return InternalGetStatus(responseKvp["REFUNDTRANSACTIONID"], settings);
                }
                else
                    errorMessage = "Tea Commerce - PayPal - " +
                                   string.Format(umbraco.ui.Text("teaCommerce", "paymentProvider_PayPal_error"),
                                                 responseKvp["L_ERRORCODE0"]);
    
                Log.Add(LogTypes.Error, -1, errorMessage);
                return new APIInfo(errorMessage);
            }
    
            public override APIInfo CancelPayment(Order order, Dictionary<string, string> settings)
            {
                isSandbox = settings["isSandbox"] == "1";
    
                string errorMessage = string.Empty;
                Dictionary<string, string> inputFields = PrepareAPIPostRequest("DoVoid", settings);
    
                inputFields.Add("AUTHORIZATIONID", order.TransactionPaymentTransactionId);
    
                Dictionary<string, string> responseKvp = GetApiResponseKvp(MakePostRequest(APIPostUrl, inputFields));
                if (responseKvp["ACK"].Equals("Success") || responseKvp["ACK"].Equals("SuccessWithWarning"))
                {
                    return InternalGetStatus(responseKvp["AUTHORIZATIONID"], settings);
                }
                else
                    errorMessage = "Tea Commerce - PayPal - " +
                                   string.Format(umbraco.ui.Text("teaCommerce", "paymentProvider_PayPal_error"),
                                                 responseKvp["L_ERRORCODE0"]);
    
                Log.Add(LogTypes.Error, -1, errorMessage);
                return new APIInfo(errorMessage);
            }
    
            protected APIInfo InternalGetStatus(string transactionId, Dictionary<string, string> settings)
            {
                isSandbox = settings["isSandbox"] == "1";
    
                string errorMessage = string.Empty;
                Dictionary<string, string> inputFields = PrepareAPIPostRequest("GetTransactionDetails", settings);
    
                inputFields.Add("TRANSACTIONID", transactionId);
    
                Dictionary<string, string> responseKvp = GetApiResponseKvp(MakePostRequest(APIPostUrl, inputFields));
                if (responseKvp["ACK"].Equals("Success") || responseKvp["ACK"].Equals("SuccessWithWarning"))
                {
    
                    string paymentStatusResponse = responseKvp["PAYMENTSTATUS"];
    
                    //If the transaction is a refund
                    if (responseKvp.ContainsKey("TRANSACTIONTYPE") && responseKvp.ContainsKey("PARENTTRANSACTIONID") &&
                        responseKvp["TRANSACTIONTYPE"].Equals("sendmoney"))
                        return InternalGetStatus(responseKvp["PARENTTRANSACTIONID"], settings);
    
                    PaymentStatus paymentStatus = PaymentStatus.Initial;
                    if (paymentStatusResponse.Equals("Pending"))
                    {
                        if (responseKvp["PENDINGREASON"].Equals("authorization"))
                            paymentStatus = PaymentStatus.Authorized;
                        else
                            paymentStatus = PaymentStatus.PendingExternalSystem;
                    }
                    else if (paymentStatusResponse.Equals("Completed"))
                        paymentStatus = PaymentStatus.Captured;
                    else if (paymentStatusResponse.Equals("Voided"))
                        paymentStatus = PaymentStatus.Cancelled;
                    else if (paymentStatusResponse.Equals("Refunded"))
                        paymentStatus = PaymentStatus.Refunded;
    
                    return new APIInfo(transactionId, paymentStatus);
                }
                else
                    errorMessage = "Tea Commerce - PayPal - " +
                                   string.Format(umbraco.ui.Text("teaCommerce", "paymentProvider_PayPal_error"),
                                                 responseKvp["L_ERRORCODE0"]);
    
                Log.Add(LogTypes.Error, -1, errorMessage);
                return new APIInfo(errorMessage);
            }
    
            protected Dictionary<string, string> PrepareAPIPostRequest(string methodName,
                                                                       Dictionary<string, string> settings)
            {
                Dictionary<string, string> inputFields = new Dictionary<string, string>();
                inputFields.Add("USER", settings["USER"]);
                inputFields.Add("PWD", settings["PWD"]);
                inputFields.Add("SIGNATURE", settings["SIGNATURE"]);
                inputFields.Add("VERSION", "98.0");
                inputFields.Add("METHOD", methodName);
                return inputFields;
            }
    
            protected Dictionary<string, string> GetApiResponseKvp(string response)
            {
                HttpServerUtility server = HttpContext.Current.Server;
                return (from item in response.Split('&')
                        let kvp = item.Split('=')
                        select new
                            {
                                Key = kvp[0],
                                Value = server.UrlDecode(kvp[1])
                            }).ToDictionary(i => i.Key, i => i.Value);
            }
        }
    }
    
  • Matt Taylor 873 posts 2086 karma points
    Jun 09, 2016 @ 10:57
    Matt Taylor
    0

    I will surely look at the code but I assume that this is going to be a problem across all versions 1 to 3 if they use the same provider.

  • Anders Burla 2560 posts 8256 karma points
    Jun 09, 2016 @ 11:39
    Anders Burla
    0

    Yes - if there is a problem in version 3 it will most likely also be in v 1 + 2 as the provider code is almost the same all the way.

  • Matt Taylor 873 posts 2086 karma points
    Sep 27, 2016 @ 16:21
    Matt Taylor
    0

    I've submitted pull requests for v1, 2 & 3 of the TC payment providers to fix this problem.

    Bex please note that the fix you have made above is not quite complete. You need to add the same code to CapturePayment, RefundPayment, CancelPayment & InternalGetStatus.

    Kind regards,

    Matt

  • Bex 444 posts 555 karma points
    Sep 28, 2016 @ 08:04
    Bex
    0

    Hi Matt

    Thanks for the info. Didn't realize it needed adding in different places too, it all seems to be working here, but definitely good to know in case any problems crop up.

    Bex

  • Matt Taylor 873 posts 2086 karma points
    Sep 28, 2016 @ 12:32
    Matt Taylor
    0

    Yes, in the TC back-end UI there are buttons on the order tab that enable refunds, cancellations etc. They also communicate with PayPal using https so will also fail without the changes.

Please Sign in or register to post replies

Write your reply to:

Draft