开发随笔-关于支付

一篇老外的文章,感兴趣的可以研究下,里面有源代码的,呵呵,别说您英文看不懂了.

http://www.west-wind.com/presentations/aspnetecommerce/aspnetecommerce.asp

 

Integrating Electronic Payment Processing into ASP.NET Webapplications

 

by Rick Strahl

www.west-wind.com

 

 

Last Update: 3/15/2006

 

Electronic Payment processing continues tobe a common theme for many Web based applications and while the process isgetting easier with the market maturing, there's still a maze of choices andoptions in setting up your payment solutions and get them integrated intoexisting applications.

 

In this article, I'll give a high leveloverview of the electronic payment processing by looking at the various playersinvolved in the payment processing operation and some suggestions and optionsyou have on getting hooked up with the right provider. I'll talk about how theprocess works, what it takes to get started and what it's going to cost.Finally I'll take you through an application that integrates payment processingand show a class you can use to integrate a few of the common Gateway servicesinto existing applications.

An overview of Electronic Payment Processing

The process of taking electronic paymentsfrom customers and getting the money deposited into your own accounts is awinding one, with several levels of providers being part of the transactionprocess. Figure 1 shows an overview of the process and the entities involved.

Figure1: The 10,000 foot view of Credit Card Processingusing one of the big Gateway providers.

 

YourWeb Application

The process starts with your Webapplication. I'll be approaching this topic from the perspective of

a small business that integrates paymentprocessing into an existing Shopping Cart solution, so the Web application isresponsible for collecting the customer's contact and credit card information.In order to process a Credit Card you'll need to capture some basic informationwhich includes:

 

  • Name
  • Address
  • Email Address
  • Phone Number
  • Order Amount
  • Credit Card Number
  • Expiration Date
  • Card Verification Code (optional)

 

GatewayProcessor

Once you have collected this information,you'll need to send the information collected to a Gateway provider that canprocess the credit card on your behalf. Gateway providers provide as the namesuggests the Internet Gateway API that your application can communicate with.The APIs are generally HTTP or TCP/IP based and provide a relatively simpleinterface that your application can communicate with.

 

A few common Gateway providers areAuthorize.NET, Verisign (PayFlow Pro), LinkPoint. There are many other Gatewayservice providers but they all perform the same basic functionality forproviding a standard interface for your application to process a credit card.

 

In theory Gateway providers are optional.You could in theory talk directly to the underlying processing networks, but inorder to gain access to these networks you need to be certified and you need tobe able to communicate with the various different processing networks thathandle the actual payment processing. So unless you are a bank or large vendorthat deals with a high volume of transaction this is not going to be an option.For most of us a Gateway provider is not optional.

 

FrontEnd Network

The various gateway providers communicatewith the front end Credit Card Processing networks that handle the actualpayment processing by passing inbound transactions to the issuing bank. You canthink of the front end network sort of as the gateway for the gatewayproviders. There a number of different providers that service this front endprocessing network with names such as PaymentTec, FirstData, Nova, GlobalPayment. The Gateway Providers interface with each of these networks that aresupported. The front end network in turn interfaces with each of the issuingbanks.

 

The issuing banks then are ultimatelyresponsible for authorizing the credit card transaction by comparing theinbound data and reserving funds (doing an 'AUTH' only at this time). The bankactually doesn't do a whole lot of checking of the data – it merely checks tosee if there are funds available and passes back an authorization code as wellas the customer information available to the bank back to the front endnetwork.

 

Backto the Gateway and the Web Application

The front end passes the result back to theGateway processor which now can examine the data returned for the bank. If theresult was not approved the transaction is declined immediately. If funds wereapproved additional checks are performed for fraud detection. The gatewayperforms things like AVS (Address Verification Services) validation against thecontact information provided and checks the CVS code (the 3 or 4 digit code onthe back or front of the card) if provided, which if not matching and enabledon the gateway can cause the transaction to be Declined. Not that both AVS andCVS verification is user definable at the gateway level, typically by the storeowner, so how AVS and CVS are handled may vary.

 

If AVS and CVS validation succeeded theorder is approved and the Gateway sends back an confirmation message to theclient application. Most Gateways support 3 different main response codes whichare APPROVED, DECLINED, ERROR (or FAILED) along with additional information.For example Authorize.NET returns a comma delimited string that includes theauthorization code, the input data of address and order amounts. The exactinformation returned from the providers can vary per Gateway.

 

The key pieces of information that allproviders return are:

 

  • Response Code
  • Message (a semi descriptive message of failure)
  • Authorization Code (on successful)
  • Transaction ID (Gateway Transaction Id you can map to your     orders)

 

Most gateways also echo back theinformation that you sent to the gateway to ensure that if data was sent fromone application to another that you can verify the transaction amounts.

 

Settlingtransactions

Note that at this point the transaction haseither been approved or declined. If approved the transaction is onlyAuthorized with the issuing bank, which means that funds are put on hold onlyfor the moment. Funds are not actually transferred and taken out of the user'saccount until the transaction batch is settled.

 

Gateways collect approved transactions intobatches which are processed at regular intervals that you can specify as partof the Gateway interface. When batches are settles the Gateway communicateswith the back end networks to cause the funds to be put on hold, but removedfrom the customer's account. At this point the money is being moved into themerchant bank account and held in escrow for a period of time – IOW, it's notimmediately transferred into your checking account.

 

MerchantBank – the 'Escrow' Account

The Merchant bank acts as the intermediatorbetween your bank account the processing networks that retrieve the funds. Thefunds retrieved are held – typically for 2 days and then deposited into yourbank account. The merchant bank also substracts the merchant percentage fromeach transactions, assigning the per transaction charges for the credit cardprocessing.

 

The merchant provider also acts as anintermediary between you and the issuing banks should the be a dispute forpayments. So if there's a fraudulent transaction or a charge back you'll hearfrom the Merchant bank that provides you with the details and to whom you haveto send any documentation. A good merchant bank can also deflect frivolouscharge back claims before they ever reach you.

 

AMEX,Discover, Diners, JCB – special cases

These card providers actually act as boththe Issuing bank as well as the merchant provider – they essentially bypass theMerchant Service Provider when it comes for handling the authorized funds.Rather than holding funds with the Merchant Service Provider, AMEX etc. depositdirectly into your merchant account after holding funds for a short period.

 

Lest you think that this is lesscomplicated, realize that all of the independent providers tend to chargeconsiderably higher merchant percentages. Also, even though the merchantservice provider isn't used for escrow, billing or dispute resolution cases,these providers still need to use the processing networks and gain access tothem through the Merchant Service provider. So AMEX, Discover, Diner etc. allhave their per transaction charges billed through the Merchant statement. Evenif you were only to take AMEX you still need a merchant provider.

 

Finally at the end of this long journey themoney from your customer – minus the merchant percentage will end up in yourbank account.

 

Paying the man

As you might expect, most of the players inFigure 1 want to get paid in some way, so fees are involved at various levels.If you're starting out from scratch your first mission is to find a merchantprovider.

 

MerchantService Provider

The key to getting started usually is amerchant account which you can get from a variety of sources. The merchantaccount is where most of the recurring fees occur and this is where you want tomake sure you're getting a good deal. Typical small business merchant ratesrange from 2.2-2.5% for Visa/MasterCard and 3.5-4% for AMEX, Discover etc. Inaddition you'll typically pay some sort of monthly statement fee, plus a pertransaction charge (from 0.15 - 0.40 per transaction) for every charge andcredit that is made. The Transaction charge is usually done as a minimum feewith a set number of transactions included. On my account it's a $25 minimumwith 80 or so free transactions for example.

 

Also keep in mind that although Merchantproviders quote rates like 2.2 percent, the reality is, especially for businesstransactions, that you rarely get these rates processed. Business cards,International cards, or cards with rewards points bump the the rate in mostcases into the mid 3% range. Checking my most recent statement for example, Isee that my average transaction for MasterCard and Visa was 3.40% even thoughmy discount rate is 2.25%. The base rate is important though becausepercentages are piled ontop of it.

 

GatewayService

Gateway Services provide the Internetfacing interface for credit card processing. Fees here vary significantly withsome of the high end, super high volume providers being fairly expensive andthe mainstream providers being relatively cheap or even free in a package.

 

Rates for Gateways are paid for monthlygateway fee, which can range from free to $15 (Authorize.NET official rate) tothe more expensive Verisign PayFlow Pro which charges $60 for monthly fees. Inaddition to the gateway fee the gateways also charge small per transaction feesfor each transaction run against the gateway fail or not. These are typically 5cents or so, but they can add up. And remember you're paying for transactionswhether they go through or not and for the closing batches at the end of theday for each network.

 

Howdo I get my Merchant Account and Gateway?

There are a number of different ways tofind providers. The easiest way today is probably to find a merchant accountreseller that sells packages of both Gateway Service and Merchant ServiceProvider, since this is a one stop solution. In this scenario you end up signing up in one place, filling out one setof paperwork as opposed to separately signing up with a merchant banks andgateway service.

 

Resellers can be found on the Internet anda good way to start if you have no idea where to look is by going to theGateway providers and check several of their preferred services.

 
I'm very happy with the service and rates I get with a reseller called MerchantPlus (using Synergy forMerchant Bank and Authorize.NET forGateway) which is the current setup I'm running. But don't take my word for it– be sure to do your research and check and compare rates and fees carefully.

 

Other options include checking with yourexisting bank which may provide merchant services, although it's probably withrates that are higher than what you can find yourself. But it never hurts toask as pricing can vary. You can also check out large resellers like CostCowhich have merchant programs that include Internet packages that arereasonable.

 

The Merchant provider you choose to somedegree will determine which processing network is used such as Nova,PaymentTech, FirstData etc.  Most Gatewayproviders

 

Picking a Gateway Provider

These days when you sign up with a Merchantprovider you likely get a choice of gateway that you can use with it. If you're building your ownapplication, you will have to interface with these gateways. Later on in thisarticle I'll present a class that handles a number of common gateways using acommon interface.

 

Let's review some of the common Gatewayproviders:

 

Authorize.NET

Authorize.NET Gateway is super easy tointegrate into existing applications. It uses a plain HTTP POST interface tocommunicate with the server, so there's no setup and configuration – you merelyneed an HTTP client that can post the data. Authorize.NET returns data overHTTP in a simple comma delimited format that's easy to access within just aboutany application.

 

Authorize.NET is fast and stable – I'vebeen using them for 4 years now without any problems during that entire time.Response time's somewhere between 2-5 seconds per transaction which is plentyfast.

 

Price: $7.95 a month, 0.05 per transaction(first 250 free), $175 cancellation fee

(prices are through MerchantPlus reseller)

 

VerisignPayFlow Pro

Versigns PayFlowPro uses a COM or C++ basedAPI that works with custom client side certificate. Installation is a littlemore involved in that you need to install the certificate on a specificlocation on the Web Server. Versign has a COM based API and has published a.NET front end for the COM API (basically a COM Interop assembly).

I've not used Verisign other than in testmodes, but I work with several customers who use it and are happy withperformance and stability. Verisign claims high stability and guarantees uptime(not sure if I buy that though).


Signing up with PayFlowPro can be done entirely on the Verisign site – they canhook you up with the gateway and merchant account. Many merchants also allowyou to use PayFlowPro with their accounts. It's one of the more popular low tomidrange solutions.

 

Direct Price: $60 a month, 0.10 pertransaction(1000 free), $249 setup

http://www.verisign.com/products-services/payment-processing/index.html

(note: there are resellers that can getthis pricing down a little)

 

LinkPointAPI

LinkPoint tends to be a Gateway used formany high risk sites. The most common match is with CardServices account whichalso are frequently of the high risk type. Linkpoint is a frequent choicerequired if you get a merchant account through a local bank when you let slipthat you are doing an Internet business. LinkPoint/CardServices will tend totake on just about any customer, but the rates tend to be higher and the amountof declined transaction due to fraud checks can be much higher on this networkthan any other.

 

LinkPoint provides both a COM and .NET APIsand I've used both of them. Both basically work on the same message part modelwhere you set various objects (Order,Billing,Options etc.) and fill each withthe order data.

 

The .NET API is using some proprietary SLLdriver you have to download and install in a specific location. For both API'syou also must install a certificate on your server. The API itself passesmessages via proto-XML – the documents passed are not actually parsable XML,but an XML fragment. Everything about this API made me cringe and think – whodesigned this???

 

Once hooked up and installed though LinkPointworks well and fast, but as mentioned watch for higher numbers of declinedorders.

 

I would avoid LinkPoint if you have achoice. As mentioned many traditional banks resell LinkPoint and CardServicesand if you go that route you may not have a choice.

 

Manymore providers

There are many more providers, but theabove are common ones that I've worked with. A few others include AccessPoint,BluePay, PaymentNet, iTransact to name a few of the smaller ones. There arealso providers that cater to the high end for the mega online stores withmillions of dollars in revenue a month. Cybersource is one of the providers inthat end of the market and these providers provide special merchant deals thatcater to these higher end customers.

 

In the old days some of these high endproviders were the only ones able to handle the high volume vendors werethrowing at them, but times have changed and technology has improved. All thegateways I've worked with now return transaction results in under 5 secondstypically and generally don't balk at high volume. The main difference is thatlarge vendors have enough dollar volume going through the gateways and merchantproviders to work special deals for better rates.

 

For anything but the high end of e-Commerceapplications and of the solution mentioned here should work just fine.

 

Web Site Integration

OK, so you've picked your merchant providerand gateway – the next step is to integrate the payment processing into yourWeb Application. In order to do this the first step is to create the basiccredit card processing routines that can interface your code with the Gatewayof your choice. The focus of the following discussion is describe how to createa somewhat generic credit card processing class hierarchy that can work withvarious providers, and then describe how to use these classes in a custom WebStore application.

Implementing a generic set of Credit Card Classes

Assuming the rest of your site is in placethe first thing you need to figure out is how to actually process the creditcards you receive. This means you'll need to write the code that interfaceswith the Gateway you selected. You can go download the providers API referenceand start figuring out how to use the particular gateway interface fromscratch, which with most providers is not terribly difficult but even thoughthe process is often fairly straight forward, integration tends to be timeconsuming due to the testing and often to interacting with Gateway support toget all the information (account ids, passwords, sometimes certificates,switching accounts from test to live mode etc.) you need in a to startprocessing.

 

I believe it's a good idea to use a moregeneric framework that is more flexible and allows to potentially switch to adifferent provider in the future. Over the years, I've gone through 6 differentmerchant providers and with each switch I ended up on a new kind of gateway.Rather than writing application level code to integrate with each of thegateways, I long ago created a generic Credit Card processing class thatprovides a base interface for credit card processing. The base class providesthe core properties that every credit card gateway requires as well as fewhelper methods that validate field input, deal with formatting properties suchas card numbers and card expiration dates, deal with MOD10 validation,switching into test mode, logging and so on. The provider specificimplementation then handles the actual credit card processing with the Gateway.

 

The result is a hierarchy of credit cardprocessing classes which are provided as part of this article. The classesinclude support for the following gateway services:

 

  • Authorize.NET
  • Verisign PayFlowPro
  • LinkPoint
  • AccessPoint
  • BluePay

These are providers I have worked with overthe years and while this list is not extensive, it contains some of the mostpopular Gateways and it's relatively straight forward to add other providers usingthe same processing logic.

 

This class hierarchy starts out with anabstract base class called ccProcessing. The ccProcessing class provides a coreproperty interface that provides the common interface that application codewrites to. So regardless of whether you're using Authorize.NET or BluePay,you're always setting the MerchantID and MerchantPassword properties even thoughthe providers may call these different things (for example, BluePay calls itthe AccountId and SecretKey).

 

Here's the interface for the abstractccProcessing class:

 

                                                                                                                                                                               
 

Member

 
 

Description

 
 

        AvsCodeToString

 
 

Returns a string  for a single AVS code value Supported codes: ANSUWXYZER_
  public string AvsCodeToString( string AvsCode );

 
 

        Mod10Check

 
 

Determines  whether given string passes standard Mod10 check.
  public static bool Mod10Check( string StringToValidate );

 
 

        ValidateCard

 
 

Base ValidateCard  method that provides the core CreditCard checking. Should always be called at  the beginning of the subclassed overridden method.
  public virtual bool ValidateCard();

 
 

        Address

 
 

Billing Street  Address.

 
 

        AuthorizationCode

 
 

Authorization  Code returned for Approved transactions from the gateway

 
 

        AvsResultCode

 
 

The AVS Result  code from the gateway if available

 
 

        CertificatePath

 
 

Used for  Linkpoint only. Specifies the path to the certificate file

 
 

        City

 
 

Billing City

 
 

        Comment

 
 

Order Comment.  Usually this comment shows up on the CC bill.

 
 

        Company

 
 

Billing Company

 
 

        Country

 
 

Two letter  Country Id - US, DE, CH etc.

 
 

        CreditCardExpiration

 
 

Full expiration  date in the format 01/2003

 
 

        CreditCardExpirationMonth

 
 

Credit Card  Expiration Month as a string (2 digits ie. 08)

 
 

        CreditCardExpirationYear

 
 

Credit Card  Expiration Year as a 4 or 2 digit string

 
 

        CreditCardNumber

 
 

The credit card  number. Number can contain spaces and other markup characters which are stripped  for processing later.

 
 

        Email

 
 

Email address

 
 

        Error

 
 

Error flag set  after a call to ValidateCard if an error occurs.

 
 

        ErrorMessage

 
 

Error message if  error flag is set or negative result is returned. Generally this value will  contain the value of this.ValidatedResult for processor failures or more  general API/HTTP failure messages.

 
 

        Firstname

 
 

First Name of  customer's name on the card. Can be used in lieu of Name property. If  Firstname and Lastname are used they get combined into a name IF Name is  blank.

 
 

        Http

 
 

Reference to an  wwHttp object. You can preseed this object after instantiation to allow  setting custom HTTP settings prior to calling ValidateCard().

 
 

        HttpLink

 
 

The link to hit  on the server. Depending on the interface this can be a URL or domainname or  domainname:Port combination.

 
 

        Lastname

 
 

Last Name of  customer's name on the card. Can be used in lieu of Name property. If  Firstname and Lastname are used they get combined into a name IF Name is  blank.

 
 

        LogFile

 
 

Optional path to  the log file used to write out request results. If this filename is blank no  logging occurs. The filename specified here needs to be a fully qualified  operating system path and the application has to be able to write to this  path.

 
 

        MerchantId

 
 

The merchant Id  or store name or other mechanism used to identify your account.

 
 

        MerchantPassword

 
 

The merchant  password for your merchant account. Not used in most cases.

 
 

        Name

 
 

First name and  last name on the card

 
 

        OrderAmount

 
 

The amount of the  order.

 
 

        OrderId

 
 

The Order Id as a  string. This is mainly for reference but should be unique.

 
 

        Phone

 
 

Billing Phone  Number

 
 

        ProcessType

 
 

Determines what  type of transaction is being processed (Sale,  Credit, PreAuth)

 
 

        RawProcessorRequest

 
 

This is a string  that contains the format that's sent to the processor. Not used with all  providers, but this property can be used for debugging and seeing what  exactly gets sent to the server.

 
 

        RawProcessorResult

 
 

The raw response  from the Credit Card Processor Server.

 
 

        ReferingUrl

 
 

Referring Url  used with certain providers

 
 

        SecurityCode

 
 

The 3 or 4 letter  digit that is on the back of the card

 
 

        State

 
 

Billing State (2 letter code or empty for foreign)

 
 

        TaxAmount

 
 

The amount of Tax  for this transaction

 
 

        Timeout

 
 

Timeout in  seconds for the connection against the remote processor

 
 

        TransactionId

 
 

The Transaction  ID returned from the server. Use to match transactions against the gateway  for reporting.

 
 

        UseMod10Check

 
 

Determines  whether a Mod10Check is performed before sending the credit card to the  processor. Turn this off for testing so you can at least get to the provider.

 
 

        UseTestTransaction

 
 

Optional flag  that determines whether to send a test transaction Not supported for  AccessPoint

 
 

        ValidatedMessage

 
 

The parsed error  message from the server if result is not APPROVED This message generally is a  string regarding the failure like 'Invalid Card' 'AVS Error' etc. This info  may or may not be appropriate for your customers to see - that's up to you.

 
 

        ValidatedResult

 
 

The parsed short  form response. APPROVED, DECLINED, FAILED, FRAUD

 
 

        Zip

 
 

Postal or Zip  code

 

 

As you can see the main portion of thisclass provides properties for most of the possible values that you need to describea credit card transaction. There's billing information for the customer, theCredit Card information including the Card Number, Card Expiration and CVSSecurityCode as well as the order amount.

 

There are processing specific propertiessuch as the HttpLink (used with HTTP based providers like Authorize.NET,BluePay and AccessPoint). There's MerchantId and MerchantPassword thatidentifies the merchange.


Then there are Response values such as the ValidatedResult (APPROVED, DECLINED,FAILED, FRAUD), ValidatedMessage which returns a descriptive message returnedfrom the provider, RawProcessorResult, which returns the raw data returned ifavailable. There's also AuthorizationCode and TransactionId which return thesevalues from transactions.

 

The base class also supports logging ofevery request to a log file. and you can easily Test mode on and off (for thoseproviders that support it through the gateway API) for a transaction.

 

There are also a couple of helper methodsthat can do Mod10 checking and convert a single AVS code to a string value. AVScodes are returned for failed transactions and you can use the conversion topotentially provide more information to your customers.

 

The worker method that does all the work isValidateCard(). Since this class is abstract the default ValidateCard() methoddoesn't do anything useful (it does have logic though so make sure to call thebase method in your implementation) – this method needs to be overridden by thespecific processor implementation classes.

Customizations do the real work

Logic says there should be a common GatewayAPI for all providers, but the reality is that most of the APIs work verydifferently. Even those that are operationally similar – those that use pureHTTP POST interfaces like Authorize.NET, AccessPoint and BluePay – usecompletely different POST variables and formats. Essentially to implement acustom provider you have to follow their rules.

 

The ccProcessing class provides merely somecore functionality – the real work of processing a credit card is done throughthe specific implementation methods of the specialized classes that use eachgateway's specific processing logic to get the card processing done.

 

While the base class interface of thevarious processing classes is identical, most of the classes require slightlydifferent start up configuration. This means if you plan on supporting multiplecredit card providers in a single application (such as a more generic shoppingcart) there's a little bit of conditional code required for each of theproviders.

 

The following C# code is an example of how allof the supported credit card providers are integrated into my Invoice businessobject, which gives you a pretty good idea how the class can be used at theApplication level:

 

///

/// Processes creditcards for the provider set in App.Configuration.CCMerchant

/// Works with theWebStoreConfig settings for setting to configure the provider

/// settings.

///

/// The Invoice is notsaved at this point.

/// Make sure to callSave() after this operation.

///

public boolProcessCreditCard()

{

    bool Result = false;

 

    wws_invoiceRow Inv = this.Entity;

    wws_customersRow Cust = this.Customer.Entity;

 

    ccProcessing CC = null;

    ccProcessors CCType = App.Configuration.CCProcessor;

 

    try

    {

        if (CCType == ccProcessors.AccessPoint)

        {

            CC = new ccAccessPoint();

        }

        else if (CCType == ccProcessors.AuthorizeNet)

        {

            CC = new ccAuthorizeNet();

            CC.MerchantPassword = App.Configuration.CCMerchantPassword;

        }

        else if (CCType == ccProcessors.PayFlowPro)

        {

            CC = new ccPayFlowPro();

            CC.MerchantPassword = App.Configuration.CCMerchantPassword;

        }

        else if (CCType == ccProcessors.LinkPoint)

        {

            CC = new ccLinkPoint();

            CC.MerchantPassword = App.Configuration.CCMerchantPassword;

            CC.CertificatePath = App.Configuration.CCCertificatePath;  

            // ie. "d:\app\MyCert.pem"

        }

        else if (CCType == ccProcessors.PayPalWebPaymentsPro)

        {

            CC = new ccPayPalWebPaymentsPro();

            CC.MerchantPassword = App.Configuration.CCMerchantPassword;

            ((ccPayPalWebPaymentsPro)CC).PrivateKeyPassword= "";

        }

        else if (CCType == ccProcessors.BluePay)

        {

            CC = new ccBluePay();

            CC.MerchantId = App.Configuration.CCMerchantId;

            CC.MerchantPassword = App.Configuration.CCMerchantPassword;

        }

 

        CC.MerchantId = App.Configuration.CCMerchantId;

 

        //CC.UseTestTransaction = true;

 

        // *** Tell whether we do SALE or Pre-Auth

        CC.ProcessType = App.Configuration.CCProcessType;

 

        // *** Disable this for testing toget provider response

        CC.UseMod10Check = true;

 

        CC.Timeout = App.Configuration.CCConnectionTimeout;  // In Seconds

        CC.HttpLink = App.Configuration.CCHostUrl;     

 

        CC.LogFile = App.Configuration.CCLogFile;

        CC.ReferingUrl = App.Configuration.CCReferingOrderUrl;

 

        // *** Name can be provided as asingle string or as firstname and lastname

        //CC.Name =Cust.Firstname.TrimEnd() + " " + Cust.Lastname.TrimEnd();

        CC.Firstname = Cust.Firstname.TrimEnd();

        CC.Lastname = Cust.Lastname.TrimEnd();

 

        CC.Company = Cust.Company.TrimEnd();

        CC.Address = Cust.Address.TrimEnd();

        CC.State = Cust.State.TrimEnd();

        CC.City = Cust.City.TrimEnd();

        CC.Zip = Cust.Zip.TrimEnd();

        CC.Country = Cust.Countryid.TrimEnd();  // 2 Character Country ID

        CC.Phone = Cust.Phone.TrimEnd();

        CC.Email = Cust.Email.TrimEnd();

 

        CC.OrderAmount = Inv.Invtotal;

        CC.TaxAmount = Inv.Tax;                 // Optional

        CC.CreditCardNumber = Inv.Cc.TrimEnd();

        CC.CreditCardExpiration =Inv.Ccexp.TrimEnd();

 

        CC.SecurityCode =Inv.Ccsecurity.TrimEnd();

 

        // *** Make this Unique

        CC.OrderId = Inv.Invno.TrimEnd() + "_" + DateTime.Now.ToString();

        CC.Comment = App.Configuration.CompanyName + "Order # " +

                                                    Inv.Invno.TrimEnd();

        // *** Result returned as true orfalse

        Result = CC.ValidateCard();

 

        // *** Pick up the Validatedresult values from the class

        Inv.Ccresult = CC.ValidatedResult;

        if (!Result)

        {

            this.ErrorMessage = CC.ValidatedMessage;

            Inv.Ccerror = this.ErrorMessage;

        }

 

        // *** Always write out the rawresponse

        if (string.NullOrEmpty(CC.RawProcessorResult))

            Inv.Ccresultx =CC.ValidatedMessage;

        else

            Inv.Ccresultx =CC.RawProcessorResult;

    }

    catch (Exception ex)

    {

        this.SetError(ex.Message);

        Inv.Ccresult = "FAILED";

        Inv.Ccresultx = "Processing Error:" + ex.Message;

        Inv.Ccerror = "Processing Error: " +ex.Message;

        return false;

    }

 

    return Result;

}

 

This class method is designed to work withmultiple credit card providers that are supported by the store. Using thisroutine as a front end I can use basically any of the credit card providersthat are supported by the ccProcessing subclasses simply by setting a switch inthe web.config file (or via an admin interface).

 

The code starts out instantiating each ofthe individual classes and setting a few provider specific properties on it.You notice that some require a certificate, others require only a merchant id,others require password and so on.

 

The code that follows then essentiallyassigns the application specific data to the processing object – in this casethe data comes from Invoice and Customer objects and – for the systemconfiguration settings – from an App.Configuration object which providesapplication configuration settings of various kinds through the .config file.

 

The call to ValidateCard() is made whichgoes out and uses the provider specific code to process the credit card. Themethod returns true or false. If true the order was approved otherwise it wasdeclined or failed.

 

The code here captures the ValidatedResult(APPROVED, DECLINED, FAILED or FRAUD) as well as the ValidatedMessage and theRawProcessorResult which in turn are stored in the invoice object. Storing theRawProcessor result may seem like overkill but it provides a good record incase there are problem in the future such as a chargeback or fraudulenttransaction. I highly recommend storing the raw response data if available bythe provider.

 

The business object method then returns theresult value true or false depending on whether the operation worked.

 

Notice that this code is completelyprovider agnostic and keeps all the messy domain knowledge about the creditcard processing out of the business object code. The code is relatively shortand clean and easy to read and modify and generic enough to work with variousgateways at the flick of a switch. Sweet.

Gateway Provider Implementations

So now let's take a look at the individualprovider implementations. As you might have guessed each one of the providerclasses essentially implements the ValidateCard() method and maybe a few of thestock property settings in the constructor. For example, the various HTTPgateway classes override the HttpLink property to point at the default gatewayUrl so that you don't have to set it. Some providers like PayFlow also add acouple of additional properties such as explicit proxy settings that arepublished by their API.

 

Let's take a look at the various classesand what it takes to set up each of the gateway APIs.

Authorize.NET

Authorize.NET has become the most popularGateway provider and having written implementation code for various gateways Ican see why. Authorize.NET is easy to work with and doesn't require anyexternal configuration. Even if you were to start from scratch building aninterface you could probably do it in very short order. Authorize.NET usesplain HTTP POST operations against its gateway server.

 

The ccAuthorizeNet class provides CreditCard processing against the Authorize.Net Gateway. It uses plain POSToperations against the Web Server so other than providing URL and logininformation there are no further setup requirements.

 

What you need:

  • Authorize.NET     Gateway URL
  • Authorize.NET     Merchant Login ID and Password
  • Referring     URL
  • No further     installation required - uses plain POST interface to server gateway

Here is the ccAuthorizeNet implementation:

 

///

/// This class providesCredit Card processing against

/// the Authorize.NetGateway.

///

public class ccAuthorizeNet : ccProcessing

{

 

    public ccAuthorizeNet()

    {

       this.HttpLink  = "https://secure.authorize.net/gateway/transact.dll";

    }

 

    ///

    /// Validates the actual card against Authorize.Net Gatewayusing the HTTP

    /// interface.

    /// Class ccAuthorizeNet

    ///

    ///

    /// Boolean

    public override bool ValidateCard()

    {

       if (!base.ValidateCard())

           return false;

 

       if (this.Http== null)

       {

           this.Http = new wwHttp();

           this.Http.Timeout = this.Timeout;

       }

 

       string CardNo = Regex.Replace(this.CreditCardNumber,@"[-/._#]","");

 

       this.Http.AddPostKey("x_version","3.1");

       this.Http.AddPostKey("x_method","CC");

       this.Http.AddPostKey("x_delim_data","True");

       this.Http.AddPostKey("x_password",this.MerchantPassword);

       this.Http.AddPostKey("x_login",this.MerchantId);

 

       if (this.UseTestTransaction)

           this.Http.AddPostKey("x_test_request","True");

      

 

       if (this.OrderAmount>= 0)

       {

           if (this.ProcessType == ccProcessTypes.Sale)

              this.Http.AddPostKey("x_type","AUTH_CAPTURE");

           else if (this.ProcessType == ccProcessTypes.PreAuth)

              this.Http.AddPostKey("x_type","AUTH_ONLY");

           else if (this.ProcessType == ccProcessTypes.Credit)

              this.Http.AddPostKey("x_type","CREDIT");

 

           this.Http.AddPostKey("x_amount",this.OrderAmount.ToString(

                                    CultureInfo.InvariantCulture.NumberFormat));

       }

       else

       {

           this.Http.AddPostKey("x_type","CREDIT");

           this.Http.AddPostKey("x_amount",((int) (-1 * this.OrderAmount)).ToString(CultureInfo.InvariantCulture.NumberFormat));

       }

 

       if (this.TaxAmount> 0.00M)

           this.Http.AddPostKey("x_tax",

this.TaxAmount.ToString(CultureInfo.InvariantCulture.NumberFormat));

 

 

       this.Http.AddPostKey("x_card_num",CardNo);

       this.Http.AddPostKey("x_exp_date",this.CreditCardExpirationMonth.ToString()+ "-" +

                                         this.CreditCardExpirationYear.ToString());

 

       if (this.SecurityCode.Trim()!= "")

           this.Http.AddPostKey("x_card_code",this.SecurityCode.Trim());

 

      

       this.Http.AddPostKey("x_first_name",this.Firstname);

       this.Http.AddPostKey("x_last_name",this.Lastname);

       this.Http.AddPostKey("x_company",this.Company);

       this.Http.AddPostKey("x_address",this.Address);

       this.Http.AddPostKey("x_city",this.City);

       this.Http.AddPostKey("x_state",this.State);

       this.Http.AddPostKey("x_zip",this.Zip);

       this.Http.AddPostKey("x_country",this.Country);

      

       if (this.Phone.Trim()!= "")

           this.Http.AddPostKey("x_phone",this.Phone);

 

       this.Http.AddPostKey("x_email",this.Email);

 

       this.Http.AddPostKey("x_invoice_num",this.OrderId);

       this.Http.AddPostKey("x_description",this.Comment);

 

       this.Http.CreateWebRequestObject(this.HttpLink);

       this.Http.WebRequest.Referer=this.ReferringUrl;

 

       this.RawProcessorResult= this.Http.GetUrl(this.HttpLink);

 

       if (this.Http.Error)

       {

           this.ValidatedResult = "FAILED";

           this.ValidatedMessage = this.Http.ErrorMessage;

           this.SetError(this.Http.ErrorMessage);

           this.LogTransaction();

           return false;

       }

 

       string[] Result = this.RawProcessorResult.Split(new char[1]  {','} );

       if (Result == null)

       {

           this.ValidatedResult = "FAILED";

           this.ValidatedMessage = "Invalidresponse received from Merchant Server";

           this.SetError(this.ValidatedMessage);

           this.LogTransaction();

           return false;

       }

 

       // *** REMEMBER: Result Codes are in 0 based array!

      this.TransactionId = Result[6];

         this.AvsResultCode = Result[5];

 

       if (Result[0] == "3" )  // Error - Processorcommunications usually

       {

            // *** Consider an invalid Card aDECLINE

            // *** so we can send back to thecustomer for display

            if (Result[3].IndexOf("credit cardnumber is invalid") > -1)

                this.ValidatedResult = "DECLINED";

            else

                this.ValidatedResult = "FAILED";   

 

           this.ValidatedMessage = Result[3];

           this.SetError(this.ValidatedMessage);

       }

        if (Result[0] == "2" ||Result[0] == "4")  // Declined

        {

           this.ValidatedResult = "DECLINED";

            this.ValidatedMessage = Result[3];

            if (this.ValidatedMessage == "")

                this.ValidatedMessage = this.RawProcessorResult;

            this.SetError(this.ValidatedMessage);

        }

        else

        {

            this.ValidatedResult = "APPROVED";

 

            // *** Make the RawProcessorResultmore readable for client application

            this.ValidatedMessage = Result[3];

            this.AuthorizationCode = Result[4];

            this.SetError(null);

        }

 

       this.LogTransaction();

 

       return !this.Error;

    }

   

}

 

Note, Authorize.NET's gateway API hasbecome so popular that various smaller providers now offer Authorize.NETcompatible gateways. For example, MerchantPlus offers its Navigate gateway thatuses the same Authorize.NET POST interface. Other smaller gateway providers arestarting to do the same.

Verisign PayFlow Pro

Verisign uses a custom API that is based onan API that is available in COM versions and a .NET version that wrappers theCOM API with an interop assembly.

 

The ccPayFlow class processes credit cardsagainst Verisign PayFlowPro gateway service. This service is set up to workwith the PayFlow Pro COM object which must be installed and configured properlyon the machine to process credit cards. To avoid use of the Interop assembly,the class uses Late Binding with Reflection to invoke the COM API which ismerely a single method call.

 

To configure PayFlow Pro follow thesesteps: Download the PayFlow Pro SDK Version 3.0 from the Verisign Web Site. Youcan download a free trial from here:

http://www.verisign.com/products/payflow/trial.html


To install the SDK unzip the file into a directory. The following instructionsare basic instructions for installing the PayFlow SDK itself - if you run intoproblems with this please check the SDK documentation.

The implementation used with the Web Storeuses the COM implementation of PayFlow so you will need to register the COMcomponent. To do so:

  • Find the     Win32\Libs directory in the SDK
  • Copy the     pfPro.dll file into the Windows\System32 directory
  • Find the     Win32\COM in the SDK
  • Run     PFProCOMSetup.exe in the COM\ dir to install and register the Payflow Pro     COM client
  • Find and     make a note of the Win32\Certs directory
  • Create an     Environment Variable called PFPRO_CERT_PATH and point it at this path
    •  
    • To      do this open Control Panel | System
    •  
    • Go      to the Advanced Tab
    •  
    • Click      on Environment Variables
    •  
    • Select      System Variables
    •  
    • Create      a new variable PFPRO_CERT_PATH
    •  
    • Set      the value to the path of the Cert directory
           
           
  • For     ASP.Net operation the PFPRO_CERT_PATH environment variable is not     available to the NETWORK SERVICE/ASPNET account by default and you have to     manually copy the CERTS directory into the operating directory of the Web     Server (or rather the IIS worker process). This directory is typically:

/System32/inetsrv

Copy the SDK's entire Cert directory intothis path so you have:

/System32/inetsrv/cert/

with the content of the certificate file.

Ifyou've performed all these steps the PayFlow SDK should be up and running.

Inaddition to the steps above you'll also need PayFlow UserId and Password whichshould be assigned to MerchantId and MerchantPassword respectively.

Here'sthe implementation of the PayFlowPro class:

public class ccPayFlowPro : ccProcessing

{

    public int HostPort

    {

        get { return _HostPort; }

        set { _HostPort = value; }

    }

    private int _HostPort = 443;

 

 

    public string ProxyAddress

    {

        get { return _ProxyAddress; }

        set { _ProxyAddress = value; }

    }

    private string _ProxyAddress = "";

 

   

    public int ProxyPort

    {

        get { return _ProxyPort; }

        set {_ProxyPort = value; }

    }

    private int _ProxyPort = 0;

 

 

    public string ProxyUsername

    {

        get { return _ProxyUsername; }

        set { _ProxyUsername = value; }

    }

    private string _ProxyUsername = "";

 

    public string ProxyPassword

    {

        get { return _ProxyPassword; }

        set { _ProxyPassword = value; }

    }

    private string _ProxyPassword = "";

 

    ///

    /// Sign up partner ID. Required only if you signed up through

    /// a third party.

    ///

    public string SignupPartner

    {

        get { return _SignupPartner; }

        set { _SignupPartner = value; }

    }

    private string _SignupPartner = "Verisign";

 

    ///

    /// Overridden consistency with API names.

    /// maps to MerchantId

    ///

    public string UserId

    {

        get { return _UserId; }

        set { _UserId = value; }

    }

    private string _UserId = "";

 

    ///

    /// Internal string value used to hold the values sent to theserver

    ///

    string Parameters = "";

 

    ///

    /// Validates the credit card. Supported transactions includeonly Sale orCredit.

    /// Credits should have a negative sales amount.

    ///

    ///

    public override bool ValidateCard()

    {

       if (!base.ValidateCard())

           return false;

      

       this.Error= false;

       this.ErrorMessage= "";

 

       // *** Counter that holds our parameter string to send

       this.Parameters= "";

 

       // *** Saleand Credit Supported

       decimal TOrderAmount = this.OrderAmount;

       if (this.OrderAmount< 0.00M)

       {

           this.Parameters = "TRXTYPE=C";

           TOrderAmount = this.OrderAmount * -1.0M;

       }

       else

           this.Parameters = "TRXTYPE=S";

 

       string CardNo = Regex.Replace(this.CreditCardNumber,@"[-/._#]","");

      

       string TUserId = this.UserId;

 

       if (TUserId == "")

           TUserId = this.MerchantId;

 

       string ExpDate = string.Format("{0:##}",this.CreditCardExpirationMonth) ;

       ExpDate +=  this.CreditCardExpirationYear;

 

       this.Parameters+= "&TENDER=C" +

                         "&ACCT=" +CardNo +

                       "&VENDOR=" + this.MerchantId +

                       "&USER=" + TUserId +

                       "&PWD=" + this.MerchantPassword +

                       "&PARTNER=" + this.SignupPartner +

                       "&AMT=" + TOrderAmount.ToString(CultureInfo.InvariantCulture.NumberFormat) +

                       "&EXPDATE=" + ExpDate +

                       "&STREET=" + this.Address +

                       "&CITY=" + this.City +

                       "&STATE=" + this.State +

                       "&ZIP=" + this.Zip +

                       "&EMAIL=" + this.Email +

                       "&COMMENT1=" + this.Comment;

 

      

       if (this.TaxAmount> 0.00M)

            this.Parameters += "&TAXAMT=" + this.TaxAmount.ToString(CultureInfo.InvariantCulture.NumberFormat);

 

       if (this.SecurityCode!= "")

           this.Parameters += "&CVV2=" + this.SecurityCode;

 

 

        // ***  Save our raw input string for debugging

        this.RawProcessorRequest = this.Parameters;

 

       // *** connects to Verisign COM object to handletransaction

       System.Type typPayFlow = System.Type.GetTypeFromProgID( "PFProCOMControl.PFProCOMControl.1" );

       object PayFlow = Activator.CreateInstance( typPayFlow );

      

       this.RawProcessorResult= "";

 

       try

       {

            // *** Use Reflection and Latebinding to call COM object

            // *** to avoid creating Interopassembly

           int context = (int) wwUtils.CallMethod(PayFlow,"CreateContext",this.HttpLink,this.HostPort,this.Timeout,

                               this.ProxyAddress,this.ProxyPort,this.ProxyUsername,this.ProxyPassword);

          

            this.RawProcessorResult = (string) wwUtils.CallMethod(PayFlow,"SubmitTransaction",context,Parameters,Parameters.Length);

 

           wwUtils.CallMethod(PayFlow, "DestroyContext",context);

       }

       catch(Exception ex)

       {

           this.ValidatedResult = "FAILED";

           this.ValidatedMessage = ex.Message;

           this.LogTransaction();

           return false;

       }

 

       string ResultValue = wwHttpUtils.GetUrlEncodedKey(this.RawProcessorResult,"RESULT");

 

        this.TransactionId = wwHttpUtils.GetUrlEncodedKey(this.RawProcessorResult, "PNREF");

 

 

       // *** 0 means success

       if (ResultValue == "0")

       {

           this.ValidatedResult = "APPROVED";

            this.AuthorizationCode= wwHttpUtils.GetUrlEncodedKey(this.RawProcessorResult, "AUTHCODE");

            this.LogTransaction();

           return true;

       }

           // *** Empty response means wehave an unknown failure

       else if (ResultValue == "")

       {

           this.ValidatedMessage = "UnknownError";

           this.ValidatedResult = "FAILED";

       }

           // *** Negative number meanscommunication failure

       else if (ResultValue.StartsWith("-") )

       {

           this.ValidatedResult = "FAILED";

           this.ValidatedMessage = wwHttpUtils.GetUrlEncodedKey(this.RawProcessorResult,"RESPMSG");

          

       }

           // *** POSITIVE number means wehave a DECLINE

       else

       {

           this.ValidatedResult = "DECLINED";

             this.ValidatedMessage= wwHttpUtils.GetUrlEncodedKey(this.RawProcessorResult,"RESPMSG");

       }

 

       this.Error= true;

       this.LogTransaction();

       return false;

    }

 

}

Notethat this class implements a few additional provider specific properties suchas the proxy settings and VerisignPartner.

LinkPoint API

The LinkPoint API is another custom APIthat provides a .NET implementation as well as a COM implementation. The .NETimplementation is a bit clunky to say the least as it requires you to manuallydownload and install a custom SSL driver that LinkPoint uses for securecommunications. I guess stock .NET SSL is not good enough for these guys. The need for this is likely related to their use of a customcertificate on the client.

 

The process of using LinkPoint involves:

  • Signing up     with LinkPoint and getting a test account
  • Downloading     and installing the .NET API
  • Using the     ccLinkPoint class from within .NET
  • Enabling     the class by setting a #define EnableLinkPoint

Getting and Installingthe LinkPoint API


The LinkPoint API provides a variety of components for different languages anddevelopment environments. The API basically handles setting up transactions andsending the transactions via HTTPS to the LinkPoint Gateway server. To workwith the API you only the appropriate component plus a little code to drive thecomponent which is provided by the wwLinkPoint class.

 

You can download the LinkPoint API from:
https://www.linkpoint.com/viewcart/

 

You'll want to use the LinkPoint API .NET(C# or VB) download for Windows. This download includes a couple of DLLs(linkpointTransaction.dll and lpssl.dll) which you should copy in your bin orexecutable directory so your application can find it.

 

OpenSSL is a separate, required download
For .NET you will also need to download the OpenSSL DLLs which Linkpoint uses.You can go to http://www.linkpoint.com/support/ and follow the links from thereto download and drop libeay.dll and ssleay.dll into your System32 directory.

 

Usingthe wwLinkPoint Class
Once the LinkPoint API is installed you'll need the following information fromyour confirmation email(s):

  • The path     to the filename to which you copied your Certificate (a .PEM file)
  • Your Store     Number
  • Your Store     Password
  • The domain     name and port number of the live or test server

Enablingthe ccLinkPoint class
Because the LinkPoint API has a dependency on the external LinkPoint assembliesthe ccLinkPoint class by default disables the class. At the top of theccLinkPoint class there's a #define EnableLinkPoint directive, which iscommented out by default. When commented the body of the class is included andany attempt to instantiate the class fails with an exception.

Toenable the class uncomment the #define EnableLinkPoint directive. Then copy theLinkpointTransaction.dll and lpssl.dll into the bin/executable directory ofyour application and add a reference in your project toLinkPointTransaction.dll.

Althougha factory pattern might have isolated this interface more cleanly it would havealso required a separate assembly. Since LinkPoint is a specialty install andyou will need to copy the LinkPoint DLLs explicitly anyway so we opted for thissimpler more integrated approach.

SpecialProperties you need to set

CC = new ccLinkPoint();
CC.MerchantPassword = App.Configuration.CCMerchantId;   // "123412314"
CC.CertificatePath = App.Configuration.CCCertificatePath;     // "d:\app\MyCert.pem" 
 
// *** HTTP Link can be server name plus port
CC.HTTPLink = "staging.linkpt.net:1229";
// CC.Port = 1229;    // or specify port separately

Here'sthe implementation of the ccLinkPoint class:

#defineEnableLinkPoint 

 

using System;

using System.Reflection;

usingSystem.Text.RegularExpressions;

usingSystem.Globalization;

 

usingWestwind.InternetTools;

usingWestwind.Tools;

 

#ifEnableLinkPoint

usingLinkPointTransaction;

#endif

 

namespaceWestwind.WebStore

{

///

/// The ccLinkPoint classprovides an ccProcessing interface to the

/// LinkPoint 6.0interface.

///

public class ccLinkPoint : ccProcessing

{

    ///

    /// The Port used by the LinkPoint API to communicate with theserver.

    /// This port will be provided to you by LinkPoint. Note thatyou

    /// can also provide the port as part of the HTTPLinkdomainname.

    ///

    public int HostPort = 1129;

 

    public ccLinkPoint()

    {

       this.HttpLink= "secure.linkpt.net:1129";

    }

 

    #if EnableLinkPoint

 

    ///

    /// Validates the credit card. Supported transactions include Sale or Credit.

    /// Credits should have a negative sales amount.

    ///

    ///

    public override bool ValidateCard()

    {

       if (!base.ValidateCard())

           return false;

      

       this.Error= false;

       this.ErrorMessage= "";

 

       // *** Parse the HttpLink for domain + port number

       int At = this.HttpLink.IndexOf(":");

       if (At > 0)

       {

           string t = this.HttpLink.Substring(At+1);

           this.HostPort = int.Parse(t );

           this.HttpLink = this.HttpLink.Substring(0,At);

       }

 

       // create order

       LPOrderPart order =LPOrderFactory.createOrderPart("order");

 

       // create a part we will use to build the order

       LPOrderPart op =LPOrderFactory.createOrderPart();

          

       // Figure out what type of order we are processing

       if (this.OrderAmount< 0 )

       {

           op.put("ordertype","CREDIT");

           this.OrderAmount = this.OrderAmount* -1;

       }

       else if (this.ProcessType== ccProcessTypes.Credit )

           op.put("ordertype","CREDIT");

       else if (this.ProcessType== ccProcessTypes.Sale )

           op.put("ordertype","SALE");

       else if (this.ProcessType== ccProcessTypes.PreAuth )

           op.put("ordertype","PREAUTH");

                 

       // add 'orderoptions to order

       order.addPart("orderoptions", op );

 

       // Build 'merchantinfo'

       op.clear();

       op.put("configfile",this.MerchantId);

 

       // add 'merchantinfo to order

       order.addPart("merchantinfo", op );

 

       // Build 'billing'

       // Required for AVS. If not provided,

       // transactions will downgrade.

       op.clear();

       op.put("name",this.Name);

       op.put("address1",this.Address);

       op.put("city",this.City);

       op.put("state",this.State);

       op.put("zip",this.Zip);

       op.put("country",this.Country);

       op.put("email",this.Email);

       op.put("phone",this.Phone);

      

       int  AddrNum = 0;

       try

       {

           string T = this.Address.Substring( 0 , this.Address.IndexOf(" ")).Trim();

           AddrNum = int.Parse(T);

       }

       catch { ; }

      

       if (AddrNum != 0) 

       {

           op.put("addrnum",  AddrNum.ToString( System.Globalization.CultureInfo.InvariantCulture.NumberFormat) );

       }

       order.addPart("billing", op );

 

       // Build 'creditcard'

       op.clear();

       op.put("cardnumber",this.CreditCardNumber );

       op.put("cardexpmonth",this.CreditCardExpirationMonth);

       op.put("cardexpyear",this.CreditCardExpirationYear);

 

       order.addPart("creditcard", op );

 

       // Build 'payment'

       op.clear();

     op.put("chargetotal",this.OrderAmount.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat) );

 

       order.addPart("payment", op ); 

 

       // *** Trasnaction Details

       op.clear();

       if (this.OrderId!= null&& this.OrderId != "")

           op.put("oid",this.OrderId );

 

       order.addPart("transactiondetails",op);

 

       if (this.Comment!= "" )

       {

           // *** Notes

           op.clear();

           op.put("comments",this.Comment );

           order.addPart("notes",op);

       }

 

       // create transaction object   

       LinkPointTxn LPTxn = newLinkPointTxn();

 

       // get outgoing XML from the 'order' object

       this.RawProcessorRequest= order.toXML();

 

       // Call LPTxn

       try

       {

           this.RawProcessorResult = LPTxn.send(this.CertificatePath,this.HttpLink,this.HostPort, this.RawProcessorRequest);

       }

       catch(Exception ex)

       {

           if (this.RawProcessorResult != "")

           {

              string Msg = wwUtils.ExtractString(this.RawProcessorResult,"","") ;

              if (Msg == "")

                  this.SetError(ex.Message);

              else

                  this.SetError(Msg);

           }

           else

           {  

              this.SetError(ex.Message);

           }

 

           this.ValidatedMessage = this.ErrorMessage;

           this.ValidatedResult = "FAILED";

           return false;

       }

      

       this.ValidatedResult= wwUtils.ExtractString(this.RawProcessorResult,"","");

       if (this.ValidatedResult== "")

           this.ValidatedResult = "FAILED";

       this.ValidatedMessage= wwUtils.ExtractString(this.RawProcessorResult,"","");

 

      

       if (this.ValidatedResult== "APPROVED")

       {

           this.SetError(null);

       }

       else

       {

           this.SetError( wwUtils.ExtractString(this.RawProcessorResult,"","") );

           this.ValidatedMessage = this.ErrorMessage;

       }

      

       this.LogTransaction();

 

       return !this.Error;

    }

    #else

    public override bool ValidateCard()

    {

       throw new Exception("ccLinkPoint Class is not enabled.Set the EnableLinkPoint #define in the source file.");

    }

    #endif

}

 

Other providers

In addition to these three providers listedabove there are also classes for AccessPoint and BluePay which are included inthe download that goes with this article. The provided help file also providesthe configuration details.

A few comments about these classes

Please realize that these classes providebasic sale functionality. Most of the classes support SALE, AUTH and CREDIT transactions. If youneed to do things like recurring billing or debit card payments you'll have toextend the functionality of these classes.

 

However, I think that this base set of classesprovides an excellent starting point for creating additional providers and/orextending functionality beyond this base set.

Sample Application

I've provided a small sample applicationthat lets you test this class. You only need to configure the provider specificsettings in the sample and you then should be able to test the specificprovider that you have the appropriate credentials for. The sample applicationis included in the downloads for this article.

Testing your Gateways

All of the Gateway providers provideTestModes that allow you to test processing without charging real transactions.Some of the gateways require a special URL, while others have a flag value thatlets operation switch into Test mode. The ccProcessing classes automaticallyswitches gateways that use flags (Authorize.NET, BluePay,AccessPoint,PayFlowPro).

 

Once you've tested the gateways in testmode I recommend that you run a few test transactions through with live creditcards. The reason for this is that test transactions tend to give you genericmessages and usually don't provide all the information that live requestsreturn. For example, AVS codes and detailed error messages are usually notavailable through test modes.

Most APIs also support some special creditcard numbers that are intended to run against the live gateway and to producespecific errors. For example, Authorize.NET lets you specify a special cardnumber and set a dollar amount that matches the result error code you want tohave returned. Other gateways provide similar functionality. Check theprovider's gateway documentation.

What about PayPal?

PayPal is actually another alternativepayment processing solution. PayPal supports both PayPal Basic and Pro. Basicis a remote site payment integration solution that requires you to navigate toPayPal's servers to validate your payment.

 

PayPal is a great choice if you are juststarting out because it is easy to sign up without any startup costs and starttaking money almost immediately. Depending on what price you're selling thingsat, PayPal can be a bit pricier than merchant accounts, but it's great solutionfor getting online quick and with paperwork and up front money outlay or anysort of commitment.

 

I've written an extensive article aboutPayPal Basic ASP.NET integration here:

 

http://www.west-wind.com/presentations/PayPalIntegration/PayPalIntegration.asp

 

PayPal recently also started offeringPayPal Payments Pro which also provides a merchant type API. I currently havenot completed integrating PayPal Payments Pro into this class hierarchy,although I started the process. There's some starter code in place, but it isnot complete and has not been tested yet. At the time I had problems with thePayPal certificate not working and decided there were more important thingsthan PayPals funky API . Left for another day.

 

PayPal Pro offers flat rate merchantpercentages and no transaction fees so in some situations it can be a cheapersolution. However, there's been a lot of complaining about PayPal's support andin dealing with chargeback resolution. If you are thinking about PayPalPayments Pro be sure to do some research online to see what problems exist. Atthe time of this writing PayPal Payments Pro just got released a couple ofweeks ago. Time will tell.

Hooking up Credit Card Processing in ASP.NET

Phew, we're finally ready to put all ofthis to work and stick it into an application. I use a standard shopping cartas an example here, because it's likely the most common scenario and can beeasily adapted to any kind of payment situation.

 

Let's take a quick walk through a typicalWeb store from shopping cart through checkout and describe the process, andthen go through the code to make it happen afterwards. Figure 2 through 6 showsthe process. If you want to check this out in real time, you can visit www.west-wind.com/webstoresandbox/and access the store from there. This is a sandbox site so you can put in bogusdata and play around.

 

It all starts with your Shopping Cartapplication. Generally you'll have some sort of shopping basket that collectsall the items and provides running totals of the order. Here's what this lookslike on my Web site:

 

Figure2: The starting point for an order is usually ashopping cart that collects and subtotals items.    

 

Customers will tend to browse your site,pick items and eventually are ready to check out. At that point there needs tobe some way to check out or in this case Place Order. In a live site, this isusually also the point where the application transitions from regular http: toa secure https: connection.

 

The next couple of steps collect customerand order information. In my store I broke out this information into 2 distinctforms – one for the customer information and one for the order specificinformation. But you can obviously put this all onto one form (a little toobusy for my taste) or split it into additional forms.

Figure 3: Taking customer information maps to the credit cardbilling information. Note this page should run under SSL to protect thecustomers personal information over the wire.

 

The order form collects customerinformation which will ultimately maps to the credit card billing information.It's important that you collect credit card BILLING information because thisinformation is used in Address Verification of the customer. If you haveseparate shipping address information you should present that separately. Notethe form shows a checkbox to expand shipping information that lets the customerenter the shipping address if it's different from the billing address (and inthis case only if a physical shipment is requested).

 

I've found that it's important to make thatvery clear on your forms - customers often forget to use their billing addressespecially on business credit cards. We'll have a chance to catch this later aspart of processing when Address Verification fails, but it's much easier andcheaper to catch it at the point of data entry. Make sure you make it clearthat Billing address info is what you need to capture.


Figure4: The final order form should let you capturecredit card information and handle the validation process. Errors should alsodisplay back on this page.

 

ReviewOrder Information

Next we need to collect the billinginformation from the customer. It's important that the customer can see theorder information on this page so he or she can review his order beforesubmitting payment. At minimum this form should display an order total, but Irecommend you show the order in all its detail to make absolutely sure thecustomer knows what he's getting. I know I've gone into online stores more thanonce only to purchase the wrong thing or ordering 2 of something when Iintended to only get one. 

Anything you can do make the orderssubmitted correct at the point of entry, you should do as it takes a lot moreeffort to fix it after the order has been placed. Not only does it take awayfrom the automated process you're striving to achieve with an online site, italso costs money if you have to issue credits and/or resubmit orders.

 

CVSCode

The information captured here is the CreditCard Number, Expiration date and Security Code. The security code is optionalwith most providers but I highly recommend that you require users to provide itas it provides at least a little additional protection against fraud. Using thecode also lowers your merchant percentage slightly with some merchantproviders. It's a good idea to require it. If you do make sure you provideinformation on where to find it on the card. In Figure 4 clicking on the ? iconbrings up more information and if you go to the live site you can use the textand images I use there.

 

Pre-Validationof Credit Card Information

The credit card gateways will validate thecredit card number and expiration date and it may seem to tempting to skipvalidation and let the gateway deal with it. But keep in mind that every accessto the gateway and the front end network costs a little money in transactioncharges, so it's a good idea to do as much up front checking as possible.

 

Credit Card numbers can be validated usingsimple Mod10 validation. The form above includes some client side JavaScriptthat checks the card number for Mod10 compliance and notifies the user if

 

A JavaScript validation routine (that Iused with some slight modification) in my store can be found here:

 

http://javascript.internet.com/forms/credit-card-number-validation.html?CreditCard=4111111111111112

 

In the application above the Mod10validation is hooked to the onblur ofthe credit card textbox and it displays a little tooltip underneath the textboxas a visual hint to the customer. The server side ccProcessing class alsoincludes Mod10 validation and you can utilize that code on the server if youdon't want to deal with client side script validation.

 

The form also validates all user input onthe server. The Invoice business object has a Validate() method that canvalidate the user input and can check for the credit card date being valid andthe various informational field being filled in. If an error of any sort occursthe form is spit back to the user with an error message (as shown above) BEFOREany credit card processing is done. In other words, the credit card processingis the last step of the invoice processing and only if everything else is OK doesthe card get sent off to the Gateway for processing.

 

Note that in ASP.NET the request posts backto the same page. And while the processing for the credit card validation isgoing on you want to make sure that the customer is not clicking on the submitbutton more than once. The form actually replaces the display content of thepage as soon as the Place Order button is clicked and you'll see a display likethis:

 

Figure5: A 'please wait' interface replaces the form assoon as the place order button is clicked in order to avoid having customersclick more than once to submit the form.

 

This HTML panel is merely a client sideinitially hidden