Introduction
Here, I will show you how to accurately detect and display any errors generated by the PayPal .NET API. As any seasoned developer knows, you need to do all you can to make sure you know what's going on with your code, especially when dealing with user's credit cards. The examples use Visual Studio 2015, ASP.NET 4.6 and C# 6.
This tip assumes that you have downloaded the API from https://github.com/paypal/PayPal-NET-SDK and that you have a web application that's ready to use the API and that you have copied the relavent files to your web application's directory. This tip will NOT show you how to create a website and then integrate the API to the site. The relevant files (at least in my case) would include Common.cs, Configuration.cs, PayFlow,cs, RequestFLow.cs and RequestFlowItem.cs.
PayFlow Error Architecture
In the diagram, you'll see at the bottom that PayPal's messages are three levels down and they come in three flavors: General, Success and Error.
Adding the Properties
Open the RequestFlowItem.cs file and add a new readonly public
property that will tell you if there are any errors, I use “HasFlowItemErrors
” for the name. Add a getter accessor that will check the Messages
collection (a List
of RequestFlowItemMessage
objects) and using the “Any
” linq extension provides a lambda expression that interrogates the Type
property of every item and looks for a type that equals RequestFlowItemMessageType.Error
:
public bool HasFlowItemErrors
{
get
{
return Messages.Any(x => x.Type == RequestFlowItemMessageType.Error);
}
}
On the PayFLow.cs file, add a new readonly public
property that will tell you if there are any errors, I use “HasErrors
” for the name. Add a getter accessor that will check the Items
property (a List
of RequestFlowItem
objects) and using the Any linq extention provide a lambda expression that interrogates the HasFlowItemErrors
property of every item:
public bool HasErrors
{
get
{
return flow.Items.Any(x => x.HasFlowItemErrors);
}
}
As you can see, this property only has a getter, no need for a setter since the information we’re looking for is already contained within the framework.
Implementing the New Properties
On your web form’s code behind, add a click event handler for the button that submits a payment (if you don’t already have one). After your payment is processed, add a conditional statement that checks the new HasErrors
property of the PayFlow
object. If HasErrors
returns true
, then you need to iterate through the Items
collection and check the Messages
property of each one and check for an error on those.
To implement this check, I’ve created a page level function that does the first check using the ForEach
LINQ extension:
private void displayErrors(List<RequestFlowItem> items)
{
items.ForEach(x => getError(x));
}
As you can see, the lambda expression contains a call to a method named getError
which does the job of iterating through the Messages
property of each RequestFlowItem
:
private void getError(RequestFlowItem item)
{
item.Messages.ForEach(y => getErrorMarkup(y));
}
A third function named getErrorMarkup
takes in the RequestFlowItemMessage
object that is passed in and checks to see if its Type
property is equal to RequestFlowItemMessageType.Error
:
private void getErrorMarkup(RequestFlowItemMessage message)
{
if (message.Type == RequestFlowItemMessageType.Error)
{
ErrorMessages.Text = String.Format("<div>{0}</div>", message.Message);
}
}
If the message is an Error
, then it wraps the message inside a div
and adds it to the Text
property of a Literal control that I have placed on the page. To simplify the readability of my code, I went ahead and added a new boolean property to the RequestFlowItemMessage
class: IsError
. With this new property, I can replace the value equality check with a simple property value check, this makes for less clutter in my code and better readability:
public bool IsError
{
get
{
return (Type == RequestFlowItemMessageType.Error);
}
}
private void getErrorMarkup(RequestFlowItemMessage message)
{
if (message.IsError)
{
ErrorMessages.Text = String.Format("<div>{0}</div>", message.Message);
}
}
Finally, I replaced my String.Format
with the use of the new string
interpolation feature of CSharp 6 (https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6). So now I have:
ErrorMessages.Text = $"<div>{message.Message}</div>";
So, in the end, you should end up with something similar to this on your web forms code behind page:
protected void SubmitPayemnt_Click(object sender, EventArgs e)
{
payFlow.MakePayment();
if (payFlow.HasErrors)
{
displayErrors(payFlow.flow.Items);
return;
}
else
{
Messages.Text = "Payment posted successfuly!";
}
}
private void displayErrors(List<RequestFlowItem> items)
{
items.ForEach(x => getError(x));
}
private void getError(RequestFlowItem item)
{
item.Messages.ForEach(y => getErrorMarkup(y));
}
private void getErrorMarkup(RequestFlowItemMessage message)
{
if (message.IsError)
{
ErrorMessages.Text = $"<div>{message.Message}</div>";
}
}
I hope this tip will help anyone struggling with the PayPal's .NET API, as I work with the framework myself I will attempt to provide more tips/tricks or articles to help the fellow developer.