Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Odd issue when processing a payment #92

Closed
MLyons10 opened this issue Apr 21, 2015 · 42 comments
Closed

Odd issue when processing a payment #92

MLyons10 opened this issue Apr 21, 2015 · 42 comments
Labels

Comments

@MLyons10
Copy link

I very well may be doing something wrong here, but I have noticed that the credit card processing is working flawlessly when run from within the debugger (VS), but when I run the application locally (With all dependencies carried over to the program folder), the payment doesn't process...

I realize this isn't much to go on here, and I'm willing to do whatever I need to in order to get this working, but has anyone else seen an issue like this? I have no idea where the issue could lie if it works perfectly fine within the debugger... There shouldn't be any difference in code execution at all...

I appreciate any insight or help anyone can provide here. I've been trying to figure this out for some time...

Thanks,

@jziaja
Copy link
Contributor

jziaja commented Apr 21, 2015

@MLyons10 just curious, but is this happening on sandbox or the live service? Since yesterday, we've been seeing a huge uptick in 500 errors related to overused credit card numbers on sandbox.

(cc PayPal-node-SDK Issue #86)

@MLyons10
Copy link
Author

This is actually Live. After finding that the code worked in the sandbox, I moved to Live and that's when I found the issue...

It's really very strange, as I can process the card without any issue ever while in the dev environment. Run the program locally however and it fails... I have tried catching the error, but nothing seems to catch it.

@jziaja
Copy link
Contributor

jziaja commented Apr 21, 2015

That is strange... Are you getting an HTTP error (e.g. 400, 500) or is a different exception being thrown? Have you tried using an HTTP monitoring tool (I use Fiddler) to view the request & response?

@MLyons10
Copy link
Author

Thank you for your help. I haven't been able to catch an error, as it just doesn't succeed for some reason. I will try Fiddler and will report back for you. I really appreciate the help with this.

I also just downloaded the current version, and realized that the dll's that I am using are different. The .zip includes:

Newtonsoft.Json.dll
PayPal.dll

The version that I was using included:

Newtonsoft.Json.dll
PayPalCoreSDK.dll
RestApiSDK.dll

Is this change correct and I have to move my solution to using these new dll's?

I duplicated my project and tried these other resources and am working to revise the code to match the needs of these new dll's. The example that I found processes a PayPal payment. Is there an example for processing a credit card payment? It looks like this has changed from the code that I had with the other dll's...

@jziaja
Copy link
Contributor

jziaja commented Apr 21, 2015

Yes, the previous DLLs you were using are the older, deprecated version of this SDK. The newer version just requires the two you downloaded.

You can view this sample to see how to make a credit card payment using the latest version of this SDK. Let me know if you have any trouble getting the newer library to work with your project.

@jziaja
Copy link
Contributor

jziaja commented Apr 21, 2015

Also, since you were using a pre-1.0 version of the SDK, you'll want to check out this wiki page on the breaking pages introduced with the 1.0 release. Mainly, some of the namespaces were changed to avoid namespace conflicts with the older Core SDK (which has since been integrated into this SDK).

@MLyons10
Copy link
Author

Thank you for your help. I got the code reworked today and will test it thoroughly and report back by the end of the week. Hopefully this will have resolved the issue I was experiencing above, but I will definitely let you know for certain.

Will I need to change the Catches that I had previously from:

catch (PayPal.PayPalException ex)

to:

catch (PayPal.PaymentsException ex)?

Also, I know that there are two different ID's that a transaction gets. Which one is recommended that I store? I just wanted to ask for clarification, as I honestly wasn't sure.

Thanks Again,

@jziaja
Copy link
Contributor

jziaja commented Apr 21, 2015

Will I need to change the Catches that I had previously from:

catch (PayPal.PayPalException ex)

to:

catch (PayPal.PaymentsException ex)?

You can, but it's not absolutely necessary. Catching PayPal.PayPalException will allow you to catch both. You could also use two catch statements if you'd like to make your exception handling more explicit:

try
{
    // ...
}
catch (PayPal.PaymentsException ex)
{
    // The API likely returned an error.  Check ex.Details for more information.
}
catch (PayPal.PayPalException ex)
{
    // Something else went wrong.
}

Also, I know that there are two different ID's that a transaction gets. Which one is recommended that I store? I just wanted to ask for clarification, as I honestly wasn't sure.

I'm guessing the two IDs you're referring to are payment ID and the transaction ID, correct? You're welcome to store either one, as each should allow you to access the other. The payment resource will return a list of all associated transaction resources via its related_resources property. The transaction resource ID should always include a HATEOAS link to its parent_payment resource; however, the SDKs aren't currently setup to quickly retrieve the resource using this link. We do plan to eventually support this, though. The benefit with storing the transaction ID is that's the ID that will appear in your PayPal account when you look at your transaction history.

So it's up to you on which you'd rather store on your end. :)

@MLyons10
Copy link
Author

Thanks for the help with this.

I was able to test a card number today using the new API and the necessary changes and I'm seeing the same thing. Running the program directly the credit card processing just hangs, it never processes, and it never throws an error... If I run it through VS in the debugger in order to determine what's going on and trouble shoot, it works perfectly... The card gets processed, the application reports this accurately, nothing hangs... It's really rather strange and appears as if the issue is actually occurring when the call is being made to process the card...?

As some additional information, I built this as a Class that will process the credit card, so from my application, I am calling this class and handling the processing there. This then reports back with the Pass or Fail information and allows me to view the error itself or the Transaction ID and associated information. Originally, I thought the issue was that I was attempting to parse the response too soon, but I have even addressed the potential of that being an issue with a timer that checks to make sure there is a response before the response is parsed... When run from VS, there is a response. When run directly, there is never any response...?

Thank you again for your help with this. I am really eager to get this working.

@jziaja
Copy link
Contributor

jziaja commented Apr 23, 2015

@MLyons10 would you mind sharing the code you're calling? Also, just to clarify, this is only happening when running against the live service in Release mode? Running against the live service in Debug mode works?

@MLyons10
Copy link
Author

Sure. Please let me know your thoughts. And correct, it works when debugging, but not when released or the executable is run directly.

    public static List<string> ProcessWithPayPal(string _CCNumber, string _CCType, string _CCCVV, string _CCExpiration, string _FName, string _LName, string _Address1, string _Address2, string _City, string _State, string _Zip, string _Amount)
    {
        //Sandbox
        //string PaypalClientID = "REMOVED";
        //string PaypalSecret = "REMOVED";

        //Live
        string PaypalClientID = "REMOVED";
        string PaypalSecret = "REMOVED";

        string PaypalAccessToken = "";

        List<string> ResultList = new List<string>();

        string CCNumber = _CCNumber;
        string CCType = _CCType.ToLower();
        string CCCVV = _CCCVV;
        string CCExpiration = _CCExpiration;
        string CCExpMonth = "";
        string CCExpYear = "";
        string FName = _FName;
        string LName = _LName;
        string Address1 = _Address1;
        string Address2 = _Address2;
        string City = _City;
        string State = _State;
        string Zip = _Zip;
        string AmountString = _Amount.Replace("$", "").Replace(" ", "");
        decimal AmountDecimal = Convert.ToDecimal(AmountString);

        //Handle dashes and spaces in the card number
        CCNumber = CCNumber.Replace("-", "").Replace(" ", ""); ;

        //Split Expiration
        int ExpMonth = 0;
        int ExpYear = 0;
        string[] expiration = CCExpiration.Split('/');
        foreach (string word in expiration)
        {
            if (CCExpMonth == "")
            {
                CCExpMonth = word.ToString();
            }
            else
            {
                CCExpYear = word.ToString();
            }
        }
        try
        {
            ExpMonth = Convert.ToInt32(CCExpMonth);

            ExpYear = Convert.ToInt32(CCExpYear);
        }
        catch
        {

        }

        //Format State
        if (State.Count() != 2)
        {
            State = CAddress.GetStateShort(State);
        }

        //Process Payment
        string ResultString = "";

        try
        {
            PaypalAccessToken = GetPayPalAccessToken(PaypalClientID, PaypalSecret);
        }
        catch
        {

        }
        finally
        {
            try
            {
                var apiContext = new APIContext(PaypalAccessToken);

                // A transaction defines the contract of a payment - what is the payment for and who is fulfilling it. 
                var transaction = new Transaction()
                {
                    amount = new Amount()
                    {
                        currency = "USD",
                        total = AmountString,
                        details = new Details()
                        {
                            shipping = "0",
                            subtotal = AmountString,
                            tax = "0"
                        }
                    },
                    description = "",
                    item_list = new ItemList()
                    {
                        items = new List<Item>()
                        {
                            new Item()
                            {
                                name = "Disaster Blaster Services",
                                currency = "USD",
                                price = AmountString,
                                quantity = "1"
                            }
                        },
                        shipping_address = new ShippingAddress
                        {
                            city = City,
                            country_code = "US",
                            line1 = Address1,
                            postal_code = Zip,
                            state = State,
                            recipient_name = FName + " " + LName
                        }
                    },
                    invoice_number = ""
                };

                // A resource representing a Payer that funds a payment.
                var payer = new Payer()
                {
                    payment_method = "credit_card",
                    funding_instruments = new List<FundingInstrument>()
                    {
                        new FundingInstrument()
                        {
                            credit_card = new CreditCard()
                            {
                                billing_address = new Address()
                                {
                                    city = City,
                                    country_code = "US",
                                    line1 = Address1,
                                    postal_code = Zip,
                                    state = State
                                },
                                cvv2 = CCCVV,
                                expire_month = ExpMonth,
                                expire_year = ExpYear,
                                first_name = FName,
                                last_name = LName,
                                number = CCNumber,
                                type = CCType
                            }
                        }
                    },
                    payer_info = new PayerInfo
                    {
                        email = "test@email.com"
                    }
                };

                // A Payment resource; create one using the above types and intent as `sale` or `authorize`
                var payment = new Payment()
                {
                    intent = "sale",
                    payer = payer,
                    transactions = new List<Transaction>() { transaction }
                };

                // Create a payment using a valid APIContext
                var createdPayment = payment.Create(apiContext);

                var transactionID = createdPayment.transactions[0].related_resources[0].sale.id;
                var payID = createdPayment.transactions[0].related_resources[0].sale.parent_payment;

                ResultString += transactionID.ToString();

                ResultList.Add("Success");
                ResultList.Add(transactionID.ToString());
                ResultList.Add(payID.ToString());
            }
            catch (PayPal.PayPalException ex)
            {
                string response = ((PayPal.ConnectionException)ex.InnerException).Response;
                //string ErrorCode = ((PayPal.Exception.ConnectionException)ex.InnerException).Message;
                //jsonObject json = new jsonObject

                ResultList.Add("Failed");
                ResultList.Add(response);

                // response should contain the full JSON error response. You can deserialize the string into a generic JSON object to get the error details.
            }
            catch (Exception e)
            {
                //ResultString = "RUNTIME EXCEPTION" + e;
                ResultList.Add("Failed");
                ResultList.Add(e.ToString());
            }
        }

        return ResultList;
    }

    private static string GetPayPalAccessToken(string _ClientID, string _Secret)
    {
        string PaypalClientID = _ClientID;
        string PaypalSecret = _Secret;

        string AccessToken = "";

        try
        {
            OAuthTokenCredential tokenCredential = new OAuthTokenCredential(PaypalClientID, PaypalSecret);
            AccessToken = tokenCredential.GetAccessToken();
        }
        catch
        {

        }

        return AccessToken;
    }

@MLyons10
Copy link
Author

Those are both classes used for processing the payments through Paypal and getting the access token... If you would like me to post anything else, please let me know.

Thanks,

@jziaja
Copy link
Contributor

jziaja commented Apr 23, 2015

Hmm, I'm not seeing anything that immediately jumps out at me. Is the hang happening on the var createdPayment = payment.Create(apiContext); call?

@MLyons10
Copy link
Author

Hi jziaja,

I believe so, but with me being unable to test this in the debugger, it's incredibly difficult to pin down. I don't know how better I can determine where this error is occurring...

I want to try to build logging into the class so that I can get a .txt file or something detailing each line that completes, so I will try to do that over the weekend and will report back.

But I don't understand what would be causing this given the code looks correct...

If you think of anything else, please let me know as I'm willing to try anything at this point.

@jziaja
Copy link
Contributor

jziaja commented Apr 28, 2015

Out of curiosity (and something I just noticed when looking back over your code), is there a reason you're running the payment code inside the finally block for when you try and get an access token? That part of the code is setup so that even if the method getting the access token throws an exception, it'll still try and make the payment call. I don't think that would cause a hang, but may be something worth refactoring.

@MLyons10
Copy link
Author

I will try that. I thought that I needed to use a try / catch / finally in order to ensure that the access token was returned before running the payment...

If that is not the case, I can try that and report back on that as well.

Thanks,

@jziaja
Copy link
Contributor

jziaja commented Apr 28, 2015

The finally block just ensures the specified code is executed after the code in the try block completes, regardless of whether an exception was thrown or not. The most common use-case is disposing of unmanaged resources or closing handles that might've been opened.

If an exception is thrown when attempting to get an access token, or if the access token is an empty string (which is how your GetPayPalAccessToken() method is setup), then you'll want to exit immediately with an appropriate error message. This would indicate that an error occurred when attempting to authenticate with the API, and any API operations thereafter would likely fail with a 401 Unauthorized error response.

@MLyons10
Copy link
Author

MLyons10 commented May 1, 2015

Alright, thanks. I will try that this weekend and will report back.

Thanks,

@MLyons10
Copy link
Author

MLyons10 commented May 4, 2015

I tried removing the code from the Finally and including all of the code in the same try / catch block and get an error that the AccessToken cannot be null...

My initial thinking was that the code will attempt to continue to run if all in the same block, so it could attempt to run the transaction before it has returned the AccessToken... That was why I originally had the card processing in the Finally...

This is what the code looks like now.

        //Live
        string PaypalClientID = "REMOVED";
        string PaypalSecret = "REMOVED";

        string PaypalAccessToken = "";

        List<string> ResultList = new List<string>();

        string CCNumber = _CCNumber;
        string CCType = _CCType.ToLower();
        string CCCVV = _CCCVV;
        string CCExpiration = _CCExpiration;
        string CCExpMonth = "";
        string CCExpYear = "";
        string FName = _FName;
        string LName = _LName;
        string Address1 = _Address1;
        string Address2 = _Address2;
        string City = _City;
        string State = _State;
        string Zip = _Zip;
        string AmountString = _Amount.Replace("$", "").Replace(" ", "");
        decimal AmountDecimal = Convert.ToDecimal(AmountString);

        //Handle dashes and spaces in the card number
        CCNumber = CCNumber.Replace("-", "").Replace(" ", ""); ;

        //Split Expiration
        int ExpMonth = 0;
        int ExpYear = 0;
        string[] expiration = CCExpiration.Split('/');
        foreach (string word in expiration)
        {
            if (CCExpMonth == "")
            {
                CCExpMonth = word.ToString();
            }
            else
            {
                CCExpYear = word.ToString();
            }
        }
        try
        {
            ExpMonth = Convert.ToInt32(CCExpMonth);

            ExpYear = Convert.ToInt32(CCExpYear);
        }
        catch
        {

        }

        //Format State
        if (State.Count() != 2)
        {
            State = CAddress.GetStateShort(State);
        }

        //Process Payment
        string ResultString = "";

        try
        {
            PaypalAccessToken = GetPayPalAccessToken(PaypalClientID, PaypalSecret);

            var apiContext = new APIContext(PaypalAccessToken);

            //var apiContext = Configuration.GetAPIContext();

            // A transaction defines the contract of a payment - what is the payment for and who is fulfilling it. 
            var transaction = new Transaction()
            {
                amount = new Amount()
                {
                    currency = "USD",
                    total = AmountString,
                    details = new Details()
                    {
                        shipping = "0",
                        subtotal = AmountString,
                        tax = "0"
                    }
                },
                description = "",
                item_list = new ItemList()
                {
                    items = new List<Item>()
                        {
                            new Item()
                            {
                                name = "Disaster Blaster Services",
                                currency = "USD",
                                price = AmountString,
                                quantity = "1"
                            }
                        },
                    shipping_address = new ShippingAddress
                    {
                        city = City,
                        country_code = "US",
                        line1 = Address1,
                        postal_code = Zip,
                        state = State,
                        recipient_name = FName + " " + LName
                    }
                },
                invoice_number = ""
            };

            // A resource representing a Payer that funds a payment.
            var payer = new Payer()
            {
                payment_method = "credit_card",
                funding_instruments = new List<FundingInstrument>()
                    {
                        new FundingInstrument()
                        {
                            credit_card = new CreditCard()
                            {
                                billing_address = new Address()
                                {
                                    city = City,
                                    country_code = "US",
                                    line1 = Address1,
                                    postal_code = Zip,
                                    state = State
                                },
                                cvv2 = CCCVV,
                                expire_month = ExpMonth,
                                expire_year = ExpYear,
                                first_name = FName,
                                last_name = LName,
                                number = CCNumber,
                                type = CCType
                            }
                        }
                    },
                payer_info = new PayerInfo
                {
                    email = "test@email.com"
                }
            };

            // A Payment resource; create one using the above types and intent as `sale` or `authorize`
            var payment = new Payment()
            {
                intent = "sale",
                payer = payer,
                transactions = new List<Transaction>() { transaction }
            };

            // Create a payment using a valid APIContext
            var createdPayment = payment.Create(apiContext);

            //var saleId = payment.transactions.Select(x => x.related_resources).Select(resource => resource.First().sale.id).First();
            //var saleId = payment.transactions[0].related_resources[0].sale.id;
            var transactionID = createdPayment.transactions[0].related_resources[0].sale.id;
            var payID = createdPayment.transactions[0].related_resources[0].sale.parent_payment;

            ResultString += transactionID.ToString();
            //Console.WriteLine(p.ConvertToJson());

            ResultList.Add("Success");
            ResultList.Add(transactionID.ToString());
            ResultList.Add(payID.ToString());
        }

        catch (PayPal.PayPalException ex)
        {
            string response = ((PayPal.ConnectionException)ex.InnerException).Response;
            //string ErrorCode = ((PayPal.Exception.ConnectionException)ex.InnerException).Message;
            //jsonObject json = new jsonObject

            ResultList.Add("Failed");
            ResultList.Add(response);

            // response should contain the full JSON error response. You can deserialize the string into a generic JSON object to get the error details.
        }
        catch (Exception e)
        {
            //ResultString = "RUNTIME EXCEPTION" + e;
            ResultList.Add("Failed");
            ResultList.Add(e.ToString());
        }

        return ResultList;
    }

    //// Create the configuration map that contains mode and other optional configuration details.
    //public static Dictionary<string, string> GetConfig()
    //{
    //    return ConfigManager.Instance.GetProperties();
    //}

    private static string GetPayPalAccessToken(string _ClientID, string _Secret)
    {
        string PaypalClientID = _ClientID;
        string PaypalSecret = _Secret;

        string AccessToken = "";

        try
        {
            OAuthTokenCredential tokenCredential = new OAuthTokenCredential(PaypalClientID, PaypalSecret);
            AccessToken = tokenCredential.GetAccessToken();

            //AccessToken = new OAuthTokenCredential(PaypalClientID, PaypalSecret).GetAccessToken();

            //string tokenCredential = new OAuthTokenCredential(PaypalClientID, PaypalSecret).GetAccessToken();
            //AccessToken = tokenCredential;
        }
        catch
        {

        }

        return AccessToken;

@jziaja
Copy link
Contributor

jziaja commented May 4, 2015

When you create an APIContext object using an access token, the constructor for APIContext is designed to do a quick empty/null check on the provided token. The way you have your code setup, GetPayPalAccessToken() will return an empty string if an error is encountered while attempting to get the access token. At this point, you would want the code to stop because any further SDK calls will result in errors due to using an invalid access token.

With that said, can you share the exception details for the exception being caught in your GetPayPalAccessToken() method? If you change it to the following you'll be able to dive into the error details if you put a break point in each of the catch statements:

private static string GetPayPalAccessToken(string _ClientID, string _Secret)
{
    string PaypalClientID = _ClientID;
    string PaypalSecret = _Secret;

    string AccessToken = "";

    try
    {
        OAuthTokenCredential tokenCredential = new OAuthTokenCredential(PaypalClientID, PaypalSecret);
        AccessToken = tokenCredential.GetAccessToken();
    }
    catch (PayPal.IdentityException ex)
    {
        // ex.Details provides quick access to the API error information.
    }
    catch (PayPal.HttpException ex)
    {
        // Some other error occurred when attempting to send the request to PayPal.
        // ex.Response contains the full response from the API which should highlight the error encountered.
    }
    catch (PayPal.PayPalException ex)
    {
        // A general exception was thrown from the SDK.
    }
    catch (System.Exception ex)
    {
        // Some other general exception occurred.
    }

    return AccessToken;
}

@MLyons10
Copy link
Author

Thanks jziaja, I appreciate your help.

I added these catches and can confirm that no errors are being caught. When processing a payment now I get the following error:

System.ArgumentNullException: Value cannot be null.
Parameter name: AccessToken cannot be null
at PayPal.Api.APIContext..ctor(String token)

It appears that it is now trying to process the payment before the AccessToken has been returned?

When running the application in the debuger, the payment is able to process because it runs just a bit slower and has the AccessToken returned prior to processing the payment...

This is why I initially had the code in a try / catch / finally. However at that time there was of course another issue that I'm still unsure of the cause of.

@jziaja
Copy link
Contributor

jziaja commented May 14, 2015

Are you saying that the line AccessToken = tokenCredential.GetAccessToken(); returns an empty string in your GetPayPalAccessToken() call? Also, would you mind posting your current code?

@jziaja
Copy link
Contributor

jziaja commented May 22, 2015

@MLyons10 just checking in - are you still encountering this issue?

@MLyons10
Copy link
Author

I'm sorry, I haven't had a chance to get the code posted until now.

To answer your questions, I am still having this issue, and it does appear to be tied presently to the fact that the AccessToken is returning an empty string. When I had this in a try / catch / finally, the payment processing code would not run until after the accesstoken had already been returned, so that was solving this issue, however then the payment was failing somewhere else.

Here is my current code:

    public static List<string> ProcessWithPayPal(string _CCNumber, string _CCType, string _CCCVV, string _CCExpiration, string _FName, string _LName, string _Address1, string _Address2, string _City, string _State, string _Zip, string _Amount)
    {
        //https://developer.paypal.com
        //Sandbox
        //app.config - <add name="mode" value="sandbox"/>
        //string PaypalClientID = "REMOVED";
        //string PaypalSecret = "REMOVED";

        //Live
        //app.config - <add name="mode" value="live"/>
        string PaypalClientID = "REMOVED";
        string PaypalSecret = "REMOVED";

        string PaypalAccessToken = "";

        List<string> ResultList = new List<string>();

        string CCNumber = _CCNumber;
        string CCType = _CCType.ToLower();
        string CCCVV = _CCCVV;
        string CCExpiration = _CCExpiration;
        string CCExpMonth = "";
        string CCExpYear = "";
        string FName = _FName;
        string LName = _LName;
        string Address1 = _Address1;
        string Address2 = _Address2;
        string City = _City;
        string State = _State;
        string Zip = _Zip;
        string AmountString = _Amount.Replace("$", "").Replace(" ", "");
        decimal AmountDecimal = Convert.ToDecimal(AmountString);

        //Handle dashes and spaces in the card number
        CCNumber = CCNumber.Replace("-", "").Replace(" ", ""); ;

        //Split Expiration
        int ExpMonth = 0;
        int ExpYear = 0;
        string[] expiration = CCExpiration.Split('/');
        foreach (string word in expiration)
        {
            if (CCExpMonth == "")
            {
                CCExpMonth = word.ToString();
            }
            else
            {
                CCExpYear = word.ToString();
            }
        }
        try
        {
            ExpMonth = Convert.ToInt32(CCExpMonth);

            ExpYear = Convert.ToInt32(CCExpYear);
        }
        catch
        {

        }

        //Format State
        if (State.Count() != 2)
        {
            State = CAddress.GetStateShort(State);
        }

        //Process Payment
        string ResultString = "";

        try
        {
            PaypalAccessToken = GetPayPalAccessToken(PaypalClientID, PaypalSecret);

            var apiContext = new APIContext(PaypalAccessToken);

            //var apiContext = Configuration.GetAPIContext();

            // A transaction defines the contract of a payment - what is the payment for and who is fulfilling it. 
            var transaction = new Transaction()
            {
                amount = new Amount()
                {
                    currency = "USD",
                    total = AmountString,
                    details = new Details()
                    {
                        shipping = "0",
                        subtotal = AmountString,
                        tax = "0"
                    }
                },
                description = "",
                item_list = new ItemList()
                {
                    items = new List<Item>()
                        {
                            new Item()
                            {
                                name = "Disaster Blaster Services",
                                currency = "USD",
                                price = AmountString,
                                quantity = "1"
                            }
                        },
                    shipping_address = new ShippingAddress
                    {
                        city = City,
                        country_code = "US",
                        line1 = Address1,
                        postal_code = Zip,
                        state = State,
                        recipient_name = FName + " " + LName
                    }
                },
                invoice_number = ""
            };

            // A resource representing a Payer that funds a payment.
            var payer = new Payer()
            {
                payment_method = "credit_card",
                funding_instruments = new List<FundingInstrument>()
                    {
                        new FundingInstrument()
                        {
                            credit_card = new CreditCard()
                            {
                                billing_address = new Address()
                                {
                                    city = City,
                                    country_code = "US",
                                    line1 = Address1,
                                    postal_code = Zip,
                                    state = State
                                },
                                cvv2 = CCCVV,
                                expire_month = ExpMonth,
                                expire_year = ExpYear,
                                first_name = FName,
                                last_name = LName,
                                number = CCNumber,
                                type = CCType
                            }
                        }
                    },
                payer_info = new PayerInfo
                {
                    email = "test@email.com"
                }
            };

            // A Payment resource; create one using the above types and intent as `sale` or `authorize`
            var payment = new Payment()
            {
                intent = "sale",
                payer = payer,
                transactions = new List<Transaction>() { transaction }
            };

            // Create a payment using a valid APIContext
            var createdPayment = payment.Create(apiContext);

            //var saleId = payment.transactions.Select(x => x.related_resources).Select(resource => resource.First().sale.id).First();
            //var saleId = payment.transactions[0].related_resources[0].sale.id;
            var transactionID = createdPayment.transactions[0].related_resources[0].sale.id;
            var payID = createdPayment.transactions[0].related_resources[0].sale.parent_payment;

            ResultString += transactionID.ToString();
            //Console.WriteLine(p.ConvertToJson());

            ResultList.Add("Success");
            ResultList.Add(transactionID.ToString());
            ResultList.Add(payID.ToString());
        }

        catch (PayPal.PayPalException ex)
        {
            string response = ((PayPal.ConnectionException)ex.InnerException).Response;
            //string ErrorCode = ((PayPal.Exception.ConnectionException)ex.InnerException).Message;
            //jsonObject json = new jsonObject

            ResultList.Add("Failed");
            ResultList.Add(response);

            // response should contain the full JSON error response. You can deserialize the string into a generic JSON object to get the error details.
        }
        catch (Exception ex)
        {
            //ResultString = "RUNTIME EXCEPTION" + e;
            ResultList.Add("Failed");
            ResultList.Add(ex.ToString());
        }

        return ResultList;
    }

    private static string GetPayPalAccessToken(string _ClientID, string _Secret)
    {
        string PaypalClientID = _ClientID;
        string PaypalSecret = _Secret;

        string AccessToken = "";

        try
        {
            OAuthTokenCredential tokenCredential = new OAuthTokenCredential(PaypalClientID, PaypalSecret);
            AccessToken = tokenCredential.GetAccessToken();
        }
        //catch
        //{

        //}
        catch (PayPal.IdentityException ex)
        {
            // ex.Details provides quick access to the API error information.
            string ErrorString = ex.ToString();
        }
        catch (PayPal.HttpException ex)
        {
            // Some other error occurred when attempting to send the request to PayPal.
            // ex.Response contains the full response from the API which should highlight the error encountered.
            string ErrorString = ex.ToString();
        }
        catch (PayPal.PayPalException ex)
        {
            // A general exception was thrown from the SDK.
            string ErrorString = ex.ToString();
        }
        catch (System.Exception ex)
        {
            // Some other general exception occurred.
            string ErrorString = ex.ToString();
        }

        return AccessToken;
    }

@jziaja
Copy link
Contributor

jziaja commented May 26, 2015

When the AccessToken is returned as an empty string when calling your GetPayPalAccessToken() method, are you catching an exception inside that method?

Also, have you tried enabling logging with your application? If you do, then whatever exception the SDK might be encountering will be logged for quick reference.

@MLyons10
Copy link
Author

I am catching an exception, however it doesn't fire an exception, as when running in the debuger the AccessToken method has adequate time to populate.

This is not an issue of the AccessToken not being returned, it has to do with the fact that it is not returned fast enough. That is why structuring this code in a try / catch / finally as I had previously had it worked. I would argue that that is how a call should be structured in order to ensure that the AccessToken has been given adequate time to return a value prior to the card processing being done.

I will look into enabling logging, however the current issue, which is easily fixed with a try / catch / finally will only be replaced with the original issue, which appears to be somewhere in the SDK, as the issue is occuring after leaving my code...

There has to be something that is affecting the credit card processing only when code execution isn't delayed by the debugger. I have confirmed that all necessary values are properly being passed to the Class and structured properly in the call (Otherwise it would not work when being debugged). It is only when this information is passed to the SDK for payment processing that it fails, and then only when this is not delayed by the debugger.

As you can see above, there are no issues with my code. I am unable to see anything that I can fix or change on my end to resolve the initial issue at all.

@jziaja
Copy link
Contributor

jziaja commented May 29, 2015

This is not an issue of the AccessToken not being returned, it has to do with the fact that it is not returned fast enough.

Can you explain this a bit more? Even if there is a small delay in getting the AccessToken, the code as you have it should wait until it's received before proceeding. Are you getting a timeout of some sort? Are multiple threads attempting to call this static method?

Another option for your application - have you considered just getting the AccessToken once and using it until it has expired? You shouldn't need to request a new token for every request if you're processing a bunch of requests one after the other.

@MLyons10
Copy link
Author

MLyons10 commented Jun 4, 2015

I'm working on some changes at the moment to try to see if I can get more information about this error. As it is a hang somewhere after submitting the payment, I'm not sure what I can really get here however...

I did want to clarify a little bit the process I am using. Here is a detailed step by step through my code structure.

Button Click:
Collects the values needed to process the card.
Starts a BackgroundWorker to process the card and report progress.
BackgroundWorker:
Sends data to a Class that processes the card using the code posted above and handles response.
Class:
This class handles formatting of the data and card processing. Handles errors and returns the result and transaction id, etc.

Now, assuming I handle the AccessToken appropriately and it has a value before the payment gets processed, this all works perfectly up until the payment is processed by the API.

Also, I am not processing a bunch of requests at a time, there may be 2 or 3 on a busy day. It really wouldn't make sense to attempt to reuse an AccessToken that would expire before I needed it again anyway. They are larger transactions, ranging from a few hundred to a few thousand...

@jziaja
Copy link
Contributor

jziaja commented Jun 5, 2015

Thanks for the clarification, @MLyons10!

Does this process employ a single BackgroundWorker per payment transaction or are you reusing the same worker for each transaction? Would it be possible for you to provide a simplified code sample showing how you have it setup so I can replicate it on my end? The symptoms make it sound like a race condition of some sort, which usually happens when you have multiple threads that may be accessing shared data. I'd like to determine if the potential race condition is due either to your code strategy or due to something in the SDK.

This SDK should be thread-safe; and if there's a specific part that's not, then I'll get that fixed. :)

@MLyons10
Copy link
Author

MLyons10 commented Jun 8, 2015

Certainly. I will put that together now. How would you like me to get that to you?

Thanks,

@jziaja
Copy link
Contributor

jziaja commented Jun 8, 2015

A Dropbox (or similar cloud storage) public link would be great! 👍

@MLyons10
Copy link
Author

MLyons10 commented Jun 9, 2015

Hi jziaja,

I put together that sample and it is exhibiting the same behavior. I built in some logging to log the catches when not being run in the debugger (Not sure why this didn't occur to me prior).

Now, keep in mind that this issue is different from the issue I was experiencing prior, but I am logging an error that:

A general exception was thrown from the SDK.
PayPal.ConfigException: Cannot parse *.Config file. Ensure you have configured the 'paypal' section correctly.
   at PayPal.Api.ConfigManager..ctor()
   at PayPal.Api.ConfigManager.get_Instance()
   at PayPal.Api.OAuthTokenCredential..ctor(String clientId, String clientSecret, Dictionary`2 config)
   at ProjectDesk1.CPayments.GetPayPalAccessToken(String _ClientID, String _Secret)

Now, I find this odd that it does still work when being run from within Visual Studio, but looking at my app.config file, I don't see anything that is wrong. But before I sent you over the demo project, I wanted to run this past you to see if anything jumps out at you here. That way I will know that I'm at least sending you something useful...

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <!-- PayPal SDK -->
  <configSections>
    <section name="paypal" type="PayPal.SDKConfigHandler, PayPal" />
  </configSections>
  <!-- PayPal account settings-->
  <paypal>
    <accounts>
      <account apiUsername="v43-restpaypal_api1.paypal.com" apiPassword="xxxx"/>
    </accounts>
    <settings>
      <add name="mode" value="live"/>
    </settings>
  </paypal>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

The site wouldn't let me post the XML, so I removed the first < from each line to allow it to post...

Thanks,

@jziaja
Copy link
Contributor

jziaja commented Jun 9, 2015

I updated your comment with markdown tags for posting code and doing the syntax highlighting (here's a handy guide on how to do it).

Which version of the SDK are you using? A bug was recently fixed in version 1.4.4 related to loading the config if you also have .NET 4.6 RC installed.

@MLyons10
Copy link
Author

MLyons10 commented Jun 9, 2015

Thank you for your help. I just updated to 1.4.4, however am still getting that error. Looking at the Config, I don't honestly see an issue...

I did just try again, and from within Visual Studio, the credit card processes perfectly without any issues. It didn't even log an error in the error log...

Here is a link to the ziped sample project: (removed)

I really appreciate your help and hope this can be figured out. It is incredibly frustrating and I really don't know where else to look. I did try on other machines and it doesn't work on those either...

Thanks Again,

@jziaja
Copy link
Contributor

jziaja commented Jun 9, 2015

@MLyons10, I'm removing the link you posted for the project because I noticed you're using your live account credentials in the sample. I'll be testing this against sandbox.

One more question - is your project for a web or desktop application? The sample project you provided is a WinForms application, which is fine for demonstration purposes; however, this SDK is meant to be used in a server environment where full trust can be established with the system. If your production system is considered to be a fully trusted environment, then disregard that question. :)

@MLyons10
Copy link
Author

MLyons10 commented Jun 9, 2015

Oh, I'm sorry, I meant to remove that... Thank you for removing that link.

I am using this in a desktop application. Is that not supported? Do I need to do something differently? I do need to have PayPal Payment Processing in this application.

@jziaja
Copy link
Contributor

jziaja commented Jun 9, 2015

If this is for a personal application, then that should be fine. If this is for an application that others will be running on other systems (e.g. POS systems), then that is not supported. The reason is that on a desktop environment (and mobile devices falls under the same umbrella), your account details used by the SDK are not considered to be secure - even if you embed the account credentials in your application.

@jziaja
Copy link
Contributor

jziaja commented Jun 9, 2015

@MLyons10, I was able to get your project working, but it required a couple changes:

  1. ProcessCCTimer_Tick() needs to check the size of the CCList array before you start indexing into it. When I attempted to use sandbox credentials and left the mode set to live, I correctly got a 401 error response. However, this method was still attempting to extract missing data from the list which caused it to go into an infinite exception loop.
  2. In the event you are unable to get the access token, the code is still setup to try and continue. Continuing will result in an exception being thrown when you try and create the APIContext object, which isn't terrible, but it unnecessarily masks information that might've revealed why you failed to get an access token in the first place. For me, this information was useful because I was initially trying to use sandbox credentials with the live service.

Also, using the ProcessCCTimer_Tick() method to determine the success of the operation seems dangerous. The timer tick is more useful in simply displaying ongoing status information (like a "Processing..." message with the ellipses moving so you know it's still working and not hung). The logic of determining the success would be better suited for the ProcessCCBW_RunWorkerCompleted method.

Something else to consider: What happens if ProcessCCBW_DoWork() completes before ProcessCCTimer_Tick() has had a chance to evaluate the result? This is a potential race condition in your application and could result in it not processing a completed transaction when you click the "Process Card" button.

Do you mind trying out this version of your project and see if it works correctly for you? It includes the above fixes along with some other refactoring (I also moved the account credentials to the app.config file). I was able to use it to complete a number of test credit card transactions. Also, I hard-coded the test credit card details so I could quickly run it and test it out.

@MLyons10
Copy link
Author

I'm sorry about the delay. I will take a look at your post tomorrow. Thank you very much for your help. :)

@jziaja
Copy link
Contributor

jziaja commented Jun 29, 2015

Closing this issue. Feel free to reopen it if you still feel like there's a problem with the SDK. Thanks!

@jziaja jziaja closed this as completed Jun 29, 2015
@KushTiwari
Copy link

KushTiwari commented Sep 13, 2016

protected void Button1_Click(object sender, EventArgs e)
{
try
{
ReceiverList receiverList = new ReceiverList();
receiverList.receiver = new List();

Receiver secondaryReceiver = new Receiver((decimal?)2.00);
secondaryReceiver.email = "info-buyer@indorendezvous.com";
secondaryReceiver.primary = false;
secondaryReceiver.paymentType = "GOODS";
receiverList.receiver.Add(secondaryReceiver);

Receiver primaryReceiver = new Receiver((decimal?)10.00);
primaryReceiver.email = "info.indorendezvous-facilitator_api1.gmail.com";
primaryReceiver.primary = true;
primaryReceiver.paymentType = "GOODS";
primaryReceiver.invoiceId = "123456789";
receiverList.receiver.Add(primaryReceiver);

RequestEnvelope requestEnvelope = new RequestEnvelope("en_US");
string actionType = "Pay";
string returnUrl = "https://devtools-paypal.com/guide/ap_chained_payment/dotnet?success=true";
string cancelUrl = "https://devtools-paypal.com/guide/ap_chained_payment/dotnet?cancel=true";
string currencyCode = "USD";
PayRequest payRequest = new PayRequest(requestEnvelope, actionType, cancelUrl, currencyCode, receiverList, returnUrl);

payRequest.ipnNotificationUrl = "http://replaceIpnUrl.com";

payRequest.feesPayer = "PRIMARYRECEIVER";
payRequest.trackingId = "123456789";
Dictionary<string, string> paypalConfig = new Dictionary<string, string>();
paypalConfig.Add("account1.apiUsername", "info.indorendezvous-facilitator_api1.gmail.com");
paypalConfig.Add("account1.apiPassword", "NZ5W43354BB6XHXZ");
paypalConfig.Add("account1.apiSignature", "AFcWxV21C7fd0v3bYYYRCpSSRl31A-MKJfZ2Iq8yWmVYC5bPl1zACFhO");
paypalConfig.Add("account1.applicationId", "Braintree-1472270699532");
paypalConfig.Add("IPNEndpoint", "https://www.paypal.com/cgi-bin/webscr");
paypalConfig.Add("url", "https://www.paypal.com/webscr&cmd=_express-checkout&token=EC%2d4RX1920730957200V");
paypalConfig.Add("endpoint", "https://api-3t.paypal.com/2.0/");
paypalConfig.Add("mode", "sandbox");
PayPal.AdaptivePayments.AdaptivePaymentsService service = new PayPal.AdaptivePayments.AdaptivePaymentsService(paypalConfig);
PayResponse response = service.Pay(payRequest);

string redirectUrl = null;

if (!response.responseEnvelope.ack.ToString().Trim().ToUpper().Equals(AckCode.FAILURE.ToString()) && !response.responseEnvelope.ack.ToString().Trim().ToUpper().Equals(AckCode.FAILUREWITHWARNING.ToString()))
{
redirectUrl = "https://www.sandbox.paypal.com/webscr?cmd=_ap-payment&paykey=" + response.payKey;
}
}
catch (PayPal.PayPalException ex)
{
Response.Write(ex.Message);
}
}

when i am using this code I am getting this error

An exception of type 'PayPal.Exception.HttpException' occurred in PayPalCoreSDK.dll but was not handled in user code

Additional information: The remote server returned an error: (404) Not Found.

@randstraw
Copy link
Contributor

@KushTiwari that appears to be an Adaptive Payment SDK integration, can you please add your issue here: https://github.com/paypal/adaptivepayments-sdk-dotnet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants