DEVELOPER

Back to Developer Blog

technical

PAX SDK Payments Part 2: Adding Payments to a .NET POS Using North’s PAX SI SDK

By Laura Olson | October 12th, 2024


Introduction

In Part 1 of this article, you learned how to build a simple Point of Sale (POS) app. Part 2 will explain how to add Semi-Integrated (SI) credit card payment functionality to it using the PAX SI SDK in C# with the .NET framework. This solution meets PCI requirements by securely sending transaction data from a physical payment device directly to the payment processor, EPX, without allowing any sensitive PCI data to enter your POS application or server environment.

Point of Sale PAX Payment Application

Before getting started, review Part 1 of this article, as well as the North Developer Integration Guide.

Build this App

Clone this code repository to quickly create your own app today!

How does the PAX SI SDK work?

PAX Semi-Integration Data Flow
  1. EPX’s EMV-certified and PCI-compliant application runs on the physical payment terminal.
  2. Your POS app initiates transaction requests to EPX’s application.
  3. The terminal displays a prompt for the customer to swipe, tap, or dip their card.
  4. The customer’s raw credit card information is securely read by EPX’s app.
  5. EPX’s app transmits the transaction request to EPX, where it is forwarded to the credit card network, then the customer’s bank.
  6. The request returns along the same route, but when it reaches the processor’s app running on the credit card terminal, it only forwards the non-sensitive data back to your POS application.
  7. This keeps the sensitive payment information outside of your app and the merchant’s environment, shifting most of the PCI responsibility away from your app and onto the payment provider. Learn more about how Semi-Integrated solutions work in this article.

There are two main payment components of the PAX Semi-Integrated solution:

  1. EPX’s BroadPOS application
  2. PAX’s POSLink API

EPX BroadPOS Application

BroadPOS is the processor-specific application that runs on the PAX terminal and communicates with the processor. Your POS app communicates with the EPX BroadPOS app using the POSLink API.

PAX POSLink API

PAX’s POSLink API enables interaction between your POS app and the processor’s BroadPOS application running on the payment terminal. The POS system is only responsible for initiating requests to the BroadPOS app using POSLink and handling the responses.

Transaction Data Flow

This tutorial will implement the following payment solution.

  1. A POS app written in .NET runs on a local computer, which acts as the server.
  2. A PAX terminal is connected to the same network as the computer.
  3. The merchant initiates a transaction request from the POS app, which sends a request using PAX’s POSLink API to the EPX BroadPOS app running on the PAX terminal. To learn more about each type of transaction request and their use cases, see the “SDK payment method definitions and examples” section of the North Developer Integration Guide.
  4. The EPX BroadPOS app uses the PAX terminal to collect the customer’s payment data.
  5. The EPX BroadPOS app builds and transmits the request to the EPX processor.
  6. EPX returns the full transaction response data to EPX BroadPOS.
  7. The EPX BroadPOS app forwards the non-sensitive response data to the POS app, again using PAX’s POSLink API.

Get in Touch

Talk to us about using the PAX SDK to accept in-person payments with your Point of Sale application.

Adding Payment Functionality to a POS

Starting with the POS application that was created in Part 1 of this tutorial, the payment methods will be called from both the Order Summaries and Order Details pages, and will be defined in a static class named CommonPayment.cs. Functionality to close transaction batches will be added to the Order Summaries page, and functionality to process individual payments will be added to the Order Details page.

Add a new class file named CommonPayment.cs to the project. For instructions on how to add a class file, see the “Creating the Class Files” section of Part 1 of this tutorial. CommonPayment.cs will be used to define a static class with all of the payment methods necessary to process payments from your POS.

Defining the Communication Settings

Add the following Using statements to the beginning of CommonPayment.cs and set the class type a to public static class.

using System;
using System.Windows.Forms;
using POSLink;

Within the class definition, add the following flag that will be used to indicate when payment methods are running, and prevent user action during that time, as it may disrupt payment processing.

public static bool isTransactionProcessing;

Next, add a method named commSettings that defines the settings used for communication between the POS and the payment terminal, and returns a CommSetting object. The specifications for PAX’s CommSetting class are documented in PAX’s POSLink API Guide.

For more information about these settings, see the “Connecting the terminal to an external POS” section of the North Developer PAX SI SDK Integration Guide.

public static CommSetting commSettings()
{
    //Instantiate a new CommSetting object named commSetting.
    CommSetting commSetting;
    commSetting = new CommSetting();

    //for more information about the following settings, see the “Connecting the terminal to an external POS” section of the North Developer PAX SI SDK Integration Guide.
    //the CommType specified below must be the same type that’s set on the physical payment terminal.
    commSetting.CommType = "HTTP";
    //follow the instructions in your terminal’s user manual to view the IP address from the terminal’s Wi-Fi (or Ethernet) settings.
    commSetting.DestIP = "insert terminal IP address";
    //The terminal’s default port number 10009 will be used as the Destination Port value in your POS requests.
    commSetting.DestPort = "insert terminal destination port";
    //communication timeout in milliseconds
    commSetting.TimeOut = "60000";
    //save the communication parameters to the commsetting.ini file located in the execute folder (bin/debug).
    //the file is written by the CommSetting class and read by the POSLink class.
    commSetting.saveFile();

    return commSetting;
}

Defining the Transaction Processing Method

Next, define a method named transaction that will process the payment transactions. The specifications for PAX’s PaymentRequest and PaymentResponse classes used in this method are documented in PAX’s POSLink API Guide.

The transaction method will be called when any of the payment buttons on the OrderDetails page are clicked. Depending on which button is clicked, specific arguments will be passed to the transaction method to perform the desired payment functionality. The transaction method accepts the following parameters:

  1. transType: This is an integer that tells the processor which type of transaction to perform, such as Authorization, Refund, etc. The transaction types and their corresponding integer values are listed in the PaymentRequest class definition of PAX’s POSLink API Guide.
    For more information about what each transaction type does and when to use them, see the “SDK payment method definitions and examples” section of the North Developer Integration Guide.

  2. labelMessage: This is a user-defined message that will be displayed on the UI after the payment process is complete, such as “Transaction authorized.” The purpose of this message is simply to let the user know the outcome of the transaction. If there is an error with the payment, the error message that was returned from the processor will be displayed instead of the labelMessage argument.

  3. authId: This parameter is optional under some conditions.

  • The transaction method requires the authId to perform subsequent actions on an existing authorization. An authorization is always the first payment function that’s performed, then subsequent actions may be performed on it by referencing its authId. The authId value is incremented by 1 with each new authorization.
  • For requests of the authorization type, authId is not accepted because a new auth is being requested and therefore no existing auth is referenced.
  1. amount: This parameter is optional under some conditions.
  • The transaction method requires the amount for all transaction types except voids. All transaction requests except voids must specify the dollar amount that the transaction request will act on.
  • Voids can only be performed on the full amount that was originally authorized, so void requests do not accept an amount.
  1. newStatus: This is a user-defined status that’s displayed for each record on the OrderSummaries page to indicate the last payment functionality that has been performed, such as “Voided”.
//define the static transaction method that returns an array of strings and accepts the parameters explained in the paragraph above.
//the return array includes two response values from the processor: the ResultCode, which is the payment status, and the HostCode, which is tokenized transaction data that can be securely stored and used to refer to the transaction later.
public static string[] transaction(int transType, string labelMessage, string authId = "0", string amount = "0", string newStatus = "")
        {
            //instantiate a new, empty PAX PosLink object named poslink.
            PosLink poslink;
            poslink = new PosLink();

            //instantiate a new, empty PAX PaymentRequest object named paymentReq. to view more information about the POSLink PaymentRequest class, see PAX's POSLink API Guide.
            PaymentRequest paymentReq;
            paymentReq = new PaymentRequest();

            //set the CommSetting property of the poslink object equal to the result of calling the commSettings method, defined above.
            poslink.CommSetting = commSettings();
            //tenderType 1 accepts card payments, but it can be set in the POS to dynamically accept cash, card, or other tender types. view the complete list of tender types in PAX's POSLink API Guide.
            paymentReq.TenderType = 1;
	          //set the transaction type equal to the transType argument passed in from the calling method, based on the payment button that was clicked.
            paymentReq.TransType = transType;
            //if the transaction type is authorization (transaction type 1) or refund (transaction type 3), don't set the OrigRefNum, which is the authId and is required for all other transaction types, since they must act on an existing auth.
            //for authorizations, a new auth is being created so there is no existing auth to reference.
            //refunds can only be performed on captures, not auths, so nothing is referenced in OrigRefNum for refunds. refunds use the transactionId which is set below as the ECRRefNum.
            if (paymentReq.TransType != 1 && paymentReq.TransType != 3)
            {
                paymentReq.OrigRefNum = authId;
            }
            //if the transaction is a void (transaction type 4), don't include the amount in the request. voids can only be processed for the full amount that was originally authorized, so void requests don't accept an amount value and will act on the full amount by default.
            if (paymentReq.TransType != 4)
            {
                paymentReq.Amount = amount.Trim('$');
            }
            //submit the transactionId as the ECR (Electronic Card Reader) Reference Number.
            paymentReq.ECRRefNum = FormProvider.OrderSummaries.transactionId;
            //set the PaymentRequest property of the poslink object equal to paymentReq.
            poslink.PaymentRequest = paymentReq;

            //before calling the PAX method that processes the transaction, set the isTransactionProcessing boolean to true.
            //this is used on the Order Details page to prevent any user action that might disrupt the transaction request/response from being processed.
            isTransactionProcessing = true;
            //call the POSLink API method ProcessTrans of the poslink object.
            poslink.ProcessTrans();
            //save the response to a PaymentResponse object named paymentRes.
            //to view more information about the POSLink PaymentResponse class, see PAX's POSLink API Guide.
            PaymentResponse paymentRes = poslink.PaymentResponse;
            //set the isTransactionProcessing boolean to false.
            isTransactionProcessing = false;

            //the ResultCode property of the PaymentResponse object will include a numeric value indicating the transaction's status.
            //if the ResultCode is 0, the transaction was successful, and the code below should be executed.
            if (paymentRes.ResultCode == "000000")
            {
                //set the labelText property on the Order Details page equal to labelMessage that was passed into this method to display the payment status message to the user based on the transaction type that was performed.
                FormProvider.OrderDetail.labelText = labelMessage;
                //if the transaction type isn't auth, update the order status to reflect the result of the payment function that was performed. the order status is displayed in the tables on the Order Summaries page.
                if (paymentReq.TransType != 1)
                {
                    FormProvider.OrderDetail.updateOrderStatus(newStatus);
                }
            }
            //if the ResultCode property of paymentRes is not 0, the transaction was not successful, and the code below should be executed.
            else
            {
                //display the error response that was received from the processor.
                FormProvider.OrderDetail.labelText = paymentRes.ResultTxt;
            }
            //return an array of strings, including the ResultCode, which is the payment status, and the HostCode, which is the token that can be used to refer to the transaction for various reasons including troubleshooting with the payment processor or performing subsequent actions on a transaction from a separate API.
            string[] result = { paymentRes.ResultCode, paymentRes.HostCode };

            return result;
        }

For more information about the values that can be returned in the ResultCode property, see the Transaction Response Codes page.

Defining the Batch Close Method

Below the transaction method, add a method named batch that will be used to submit batches of captured transactions to the payment processor. The Batch method will be called when the “Close Batch” button on the OrderSummaries page is clicked. The specifications for PAX’s BatchRequest and BatchResponse classes are documented in PAX’s POSLink API Guide.

public static string batch()
{
    //the code in this method follows the specifications in PAX's POSLink API Guide.

    //instantiate a new, empty PAX PosLink object named poslink.
    PosLink poslink;
    poslink = new PosLink();

    //instantiate a new, empty PAX BatchRequest object named batchReq.
    //to view more information about the POSLink BatchRequest class, see PAX's POSLink API Guide.
    BatchRequest batchReq;
    batchReq = new BatchRequest();

    //set the CommSetting property of the poslink object equal to the result of calling the commSettings method, defined above.
    poslink.CommSetting = commSettings();

    //transaction type 1 closes the current batch.
    batchReq.TransType = 1;
    poslink.BatchRequest = batchReq;

    //before calling the PAX method to close the batch, set the isTransactionProcessing boolean to true.
    //this is used in Order Details to prevent any user action that might disrupt the batch request/response from being processed.
    isTransactionProcessing = true;
    //call the POSLink API method ProcessTrans of the poslink object.
    poslink.ProcessTrans();
    //save the response to a BatchResponse object named batchRes.
    //to view more information about the POSLink BatchResponse class, see PAX's POSLink API Guide.
    BatchResponse batchRes = poslink.BatchResponse;
    //set the isTransactionProcessing boolean to false.
    isTransactionProcessing = false;

    //if the ResultCode property of batchRes is 0, the batch was successful, and the code below should be executed.
    if (batchRes.ResultCode == "000000")
    {
        MessageBox.Show("Your batch was submitted to the payment processor for settlement and funding.", "Batch Successful");
        //call the updateBatchStatus method to update the status of any Captured transactions to Batched.
        FormProvider.OrderSummaries.updateBatchStatus("Captured", "Batched");
    }
     //if the ResultCode property of batchRes is not 0, the batch was not successful, and the code below should be executed.
    else
    {
        //display the error response that was received from the processor.
        MessageBox.Show(batchRes.ResultTxt, "Batch Error");
    }

    return batchRes.ResultCode;

}

Calling the Payment Methods via the POS UI

In Form2.cs, for each of the payment button-click method definitions that were auto-generated in the “Setting Up the UI” section of Part 1 of this tutorial, add the corresponding code below. See the “SDK payment method definitions and examples” section of the North Developer PAX SI SDK Integration Guide for more details about each method and their use cases.

        //clicking the Sale button will perform an auth and immediately capture it.
        //see the “SDK payment method definitions and examples” section of the
        //North Developer PAX SI SDK Integration Guide for more details about these functions and their use cases.

        private void saleButton_Click(object sender, EventArgs e)
        {
            //clicking this button will perform an auth and immediately capture it.
            //see the “SDK payment method definitions and examples” section of the
            //North Developer PAX SI SDK Integration Guide for more details about these functions and their use cases.

            //after a new batch is created, transactionId and authId must have a positive int value, so they must be
            //incremented before the if condition is met and the Auth transaction method is called.
            FormProvider.OrderSummaries.authId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) + 1).ToString();
            FormProvider.OrderSummaries.transactionId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) + 1).ToString();
            //display the authId on the UI
            authIdValue.Text = FormProvider.OrderSummaries.authId;
            updateOrder();
            //call the transaction method of the CommonPayment class passing in 1 for the transaction type, which is used to Authorize a payment.
            string[] authResult = CommonPayment.transaction(1, "Transaction Authorized", "0", totalValue.Text);
            //if the result of calling the Auth transaction method is 0 (meaning the transaction was Approved), execute the code below
            if (authResult[0] == "000000")
            {
                //save the transaction token returned by the processor from the Auth method as an auth token.
                authToken = authResult[1];
                authAmtValue.Text = totalValue.Text;
                //call the transaction method of the CommonPayment class passing in 5 for the transaction type, which is used to Capture an Authorization.
                string[] transactionResult = CommonPayment.transaction(5, "Transaction Successful", authIdValue.Text, totalValue.Text, "Captured");
                //if the result of calling the Capture transaction method is 0, execute the code below
                if (transactionResult[0] == "000000")
                {
                    //save the transaction token returned by the processor from the Capture method as a transaction token.
                    transactionToken = transactionResult[1];
                    //increment the transaction ID by 1 and update the database, but note that the authId is not incremented here.
                    //for more information about tracking transactions and auths, see the "Transaction tracking" section of the North Developer PAX SI SDK Integration Guide.
                    FormProvider.OrderSummaries.transactionId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) + 1).ToString();
                }
                //if the result of calling the Capture transaction method is not 0, do nothing else.
            }
            //if the result of calling the Auth transaction method is not 0,
            //decrement the transaction ID by 1 so that it is reverted to the original value.
            else
            {
                FormProvider.OrderSummaries.transactionId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) - 1).ToString();
                FormProvider.OrderSummaries.authId = (Int32.Parse(FormProvider.OrderSummaries.authId) - 1).ToString();
            }
            //update the authId on the UI. if the auth isn't successful, the ID number will have been reverted.
            authIdValue.Text = FormProvider.OrderSummaries.authId;
            updateOrder();
            updateDb();
        }


        //clicking the Void button will release the auth hold on the customer’s funds if the auth hasn’t been captured.

        private void voidButton_Click(object sender, EventArgs e)
        {
            //transaction type 4 is used to Void a payment.
            string[] result = CommonPayment.transaction(4, "Transaction Voided", (string)FormProvider.OrderSummaries.selectedOrder[0]["AuthId"], "0", "Voided");
            //if the result of calling the Capture transaction method is 0, execute the code below, otherwise do nothing else.
            if (result[0] == "000000")
            {
                //increment the transactionId by 1
                FormProvider.OrderSummaries.transactionId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) + 1).ToString();
                authIdValue.Text = (string)FormProvider.OrderSummaries.selectedOrder[0]["AuthId"];
                updateOrder();
                updateDb();
            }
        }


        //clicking the Refund button will return the captured funds to the customer’s bank account.

        private void refundCPButton_Click(object sender, EventArgs e)
        {
            //transaction type 3 is used to Refund a payment.
            string[] result = CommonPayment.transaction(3, "Transaction Refunded", (string)FormProvider.OrderSummaries.selectedOrder[0]["AuthId"], totalValue.Text, "Refunded");
            //if the result of calling the Refund transaction method is 0, execute the code below, otherwise do nothing else.
            if (result[0] == "000000")
            {
                {
                    //increment the transactionId by 1
                    FormProvider.OrderSummaries.transactionId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) + 1).ToString();
                    authIdValue.Text = (string)FormProvider.OrderSummaries.selectedOrder[0]["AuthId"];
                    updateOrder();
                    updateDb();
                }
            }
        }


        //clicking the Authorize button will authorize a transaction without capturing it.

        private void authButton_Click_1(object sender, EventArgs e)
        {
            //after a new batch is created, transactionId and authId must have a positive int value, so they must be incremented before the if condition is met and the Auth transaction method is called.
            FormProvider.OrderSummaries.authId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) + 1).ToString();
            FormProvider.OrderSummaries.transactionId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) + 1).ToString();
            //display the authId on the UI
            authIdValue.Text = FormProvider.OrderSummaries.authId;
            updateOrder();
            //call the transaction method of the CommonPayment class passing in 1 for the transaction type, which is used to Authorize a payment.
            string[] authResult = CommonPayment.transaction(1, "Transaction Authorized", "0", totalValue.Text);
            //if the result of calling the Auth transaction method is not 0, decrement the transaction and auth IDs by 1 so that they are reverted to the original values.
            if (authResult[0] != "000000")
            {
                FormProvider.OrderSummaries.transactionId = (Int32.Parse(FormProvider.OrderSummaries.transactionId) - 1).ToString();
                FormProvider.OrderSummaries.authId = (Int32.Parse(FormProvider.OrderSummaries.authId) - 1).ToString();
            }
            //update the Auth Amount on the UI.
            authAmtValue.Text = totalValue.Text;
            //save the auth token returned by the processor.
            authToken = authResult[1];
            //update the AuthID on the UI. if the auth isn't successful, the ID number will have been reverted.
            authIdValue.Text = FormProvider.OrderSummaries.authId;
            updateOrder();
            updateDb();
        }


       //clicking the Add Tip button will update the total to include the tip amount

      private void tipButton_Click(object sender, EventArgs e)
        {
            updateOrder();
            //update the Auth Amount on the UI.
            authAmtValue.Text = totalValue.Text;
            updateDb();
            FormProvider.OrderSummaries.displayRecordSummary(orderNumValue.Text);
        }


        //clicking the Capture button will capture the auth

        private void captureButton_Click_1(object sender, EventArgs e)
        {
            //call the transaction method of the CommonPayment class passing in 5 for the transaction type, which is used to capture an auth.
            string[] transactionResult = CommonPayment.transaction(5, "Transaction Successful", (string)FormProvider.OrderSummaries.selectedOrder[0]["AuthId"],totalValue.Text, "Captured");
            //if the result of calling the Capture transaction method is 0, execute the code below, otherwise do nothing else.
            if (transactionResult[0] == "000000")
            {
                //increment the transactionId by 1
                authIdValue.Text = (string)FormProvider.OrderSummaries.selectedOrder[0]["AuthId"];
                //save the transaction token returned by the processor.
                transactionToken = transactionResult[1];
                updateOrder();
                updateDb();
            }
        }

        //clicking the Back button will save the changes that have been made on the Order Detail screen to the database

        private void backButton_Click(object sender, EventArgs e)
        {
            //update the record in the database with any changes made on the Order Detail screen
            updateDb();
            //if the value of the isTransactionProcessing boolean is true, do nothing
            if (CommonPayment.isTransactionProcessing)
            {
                MessageBox.Show("A transaction is processing.", "Warning");
            }
            else
            {
                //hide the Order Detail screen
                this.Hide();
                //call the updateOrders method from the existing object before loading
                //the Orders List screen so that the most current order data is displayed
                FormProvider.OrderSummaries.updateOrders();
                //reset the values of the UI message labels
                labelText = "";
                authIdValue.Text = "";
                authAmtValue.Text = "";
                //display the Orders List screen
                FormProvider.OrderSummaries.Show();
            }
        }

Calling the Batch Close Method

In Form1.cs, add the following code to the Batch Close button-click method:

        private void batchCloseButton_Click(object sender, EventArgs e)
        {
            //if the result of calling the Batch method of the CommonPayment class is 0 (meaning Approved), execute the code below.
            if (CommonPayment.batch() == "000000")
            {
                //for more information about tracking IDs, see the "Transaction tracking" section of the North Developer PAX SI SDK Integration Guide.

                //reset the transactionId and authId to 0.
                transactionId = "0";
                authId = "0";
                //increment batchId by 1.
                batchId = (Int32.Parse(batchId) + 1).ToString();
            }
        }

Finally, add the following method to update the order statuses after the batch has been submitted.

public void updateBatchStatus (string oldStatus, string newStatus)
{
    //use a linq query to select all records from the database where the status is equal to the oldStatus argument that was passed into this method.
    var query =
        from order in parseCsv().AsEnumerable()
        where order.Field<string>("Status") == oldStatus
        select Int32.Parse(order.Field<string>("Order"));

    //save all database records to an array of strings.
    string[] allRecords = File.ReadAllLines(dbPath());

    //for the records returned in the linq query, replace the status value with the newStatus argument that was passed into this method.
    foreach (int order in query)
    {
        string[] cellValues = allRecords[order].Split(',');
        cellValues[3] = newStatus;
        allRecords[order] = String.Join(",", cellValues);
    }

    //rewrite the file with the new data.
    File.WriteAllLines(dbPath(), allRecords);
    updateOrders();
}

Demonstrating the POS app

Follow the steps in the “Using the terminal” section of the North Developer PAX SI SDK Integration Guide to connect the POS to the terminal.

Click the Start button in Visual Studio Community to run the application. On the OrderSummaries page, click the Start New Order button.

Point of Sale Application
The OrderDetails page should open, displaying an empty order. Populate the textbox fields with data, and add items to the order.
Point of Sale Application Payment Functionality

Click the Sale button, and the payment terminal should prompt you to enter card data. Key in the following test card data to complete the test transaction:

Card #: 4111111111111111
Exp: 04/25
Zip: 11111
CVV: 123

The terminal should display an approval message, and the labelText field on the UI should display a status message.

Point of Sale Application Payment Response

Perform the same steps for each of the payment functions, paying attention to the requirements for some transaction types that are explained in the “SDK payment method definitions and examples” section of the North Developer PAX SI SDK Integration Guide. For example, if a transaction has been authorized and captured, it can be refunded, but it cannot be voided. Voids can only be performed on transactions that haven’t been captured yet. Conversely, if a transaction has not been captured, it can be voided but it cannot be refunded.

Conclusion

In Part 2 of this tutorial, you learned how to add credit card payments to the C#/.NET POS app that was built in Part 1 using the PAX Semi-Integrated SDK.

How To Get Started

North’s Sales Engineering team provides support to developers and business decision-makers to help select the best possible payment system. Contact us to learn more about how to connect your system to the North ecosystem.


Start your free Developer account and try it now.


©2025 North is a registered DBA of NorthAB, LLC. All rights reserved. North is a registered ISO of BMO Harris Bank N.A., Chicago, IL, Citizens Bank N.A., Providence, RI, The Bancorp Bank, Philadelphia, PA, FFB Bank, Fresno, CA, Wells Fargo Bank, N.A., Concord, CA, and PNC Bank, N.A.