开发随笔-关于支付

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

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.
开发随笔-关于支付_第1张图片

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

开发随笔-关于支付_第2张图片

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:

 

/// <summary>

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

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

/// settings.

/// </summary>

/// <remarks>The Invoice is notsaved at this point.

/// Make sure to callSave() after this operation.</remarks>

/// <returns></returns>

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:

 

/// <summary>

/// This class providesCredit Card processing against

/// the Authorize.NetGateway.

/// </summary>

public class ccAuthorizeNet : ccProcessing

{

 

    public ccAuthorizeNet()

    {

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

    }

 

    /// <summary>

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

    /// interface.

    /// <seealso>Class ccAuthorizeNet</seealso>

    /// </summary>

    /// <param name=""></param>

    /// <returns>Boolean</returns>

    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:

<Windows Path>/System32/inetsrv

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

<WindowsPath>/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 = "";

 

    /// <summary>

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

    /// a third party.

    /// </summary>

    public string SignupPartner

    {

        get { return _SignupPartner; }

        set { _SignupPartner = value; }

    }

    private string _SignupPartner = "Verisign";

 

    /// <summary>

    /// Overridden consistency with API names.

    /// maps to MerchantId

    /// </summary>

    public string UserId

    {

        get { return _UserId; }

        set { _UserId = value; }

    }

    private string _UserId = "";

 

    /// <summary>

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

    /// </summary>

    string Parameters = "";

 

    /// <summary>

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

    /// Credits should have a negative sales amount.

    /// </summary>

    /// <returns></returns>

    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<g>. 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

{

/// <summary>

/// The ccLinkPoint classprovides an ccProcessing interface to the

/// LinkPoint 6.0interface.

/// </summary>

public class ccLinkPoint : ccProcessing

{

    /// <summary>

    /// 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.

    /// </summary>

    public int HostPort = 1129;

 

    public ccLinkPoint()

    {

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

    }

 

    #if EnableLinkPoint

 

    /// <summary>

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

    /// Credits should have a negative sales amount.

    /// </summary>

    /// <returns></returns>

    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,"<r_error>","</r_error>") ;

              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,"<r_approved>","</r_approved>");

       if (this.ValidatedResult== "")

           this.ValidatedResult = "FAILED";

       this.ValidatedMessage= wwUtils.ExtractString(this.RawProcessorResult,"<r_message>","</r_message>");

 

      

       if (this.ValidatedResult== "APPROVED")

       {

           this.SetError(null);

       }

       else

       {

           this.SetError( wwUtils.ExtractString(this.RawProcessorResult,"<r_error>","</r_error>") );

           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 <g>. 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 <iframe> tag, that is made visible by the button click,while the main page content – wrapped in a <div> is hidden. The logic ishooked up to the form's onsubmit:

 

<formid="frmOrderForm"onsubmit="ShowWaitDisplay()"runat="server">

 

and then implemented with thissimple JavaScript function:

 

functionShowWaitDisplay()

{

    document.getElementById("MainForm").style.display="none";

    IFrame = document.getElementById("WaitFrame");

    IFrame.src = "waitform.htm"

    IFrame.style.display = "inline";

    window.scrollTo(0,0);

    return true;     

}

 

The IFRAME loads a static HTMLpage that can be designed separately of the order form itself, so it's easy tomaintain. If you need a dynamic message you can also use an ASPX page and pass datato it via the querystring or if more complicated via the Session object.

 

When the server side processing is done thepage returns back to itself, since the page is using a standard page POSTBACK.If an error occurred it displays the credit card error message. This value isretrievd indirectly ccProcessing.cValidatedMessage and presented as yet anothererror message similar to the ones shown in Figure 4.

 

If the credit card processing went throughthe invoice is saved and written to disk and the form then redirects to anorder confirmation page. which is shown in Figure 6.

 

Figure6: The order confirmation page summarizes the orderand lets the user know what to expect and where to go if there should be aproblem. This is especially important if you're using some form of electronicfulfillment and the fulfillment (usually email) fails.

 

The confirmation serves a number ofimportant functions. It obviously confirms to the customer what's she's justpaid for, but more importantly it communicates to them what happens next. Theypaid for the product so now you need to fulfills the order. If you are a vendorthat fulfills orders electronically you can notify the customer that aconfirmation will be sent or that items will be shipped etc. along with acontent address that can be used for them to contact support.

 

 

The confirmation form in this applicationis also serves as the notification portion of the application, so an orderconfirmation is sent with the same info displayed on this form, and if itemscan be confirmed immediately or accessed via a download link a confirmationnotice is sent off immediately as well.

 

Alright, now we've seen how the orderprocess works, let's take a look behind the scenes and see how the credit cardprocessing is hooked into this.

How it works

In this store application the UI page thathandles the credit card processing is OrderForm.aspx which is shown in Figure4. Up until this point data has been internally collected in the applicationand stored in a database (or temporary table) along with some identificationthat identifies the customer and the invoice.

 

The core processing occurs in thebtnSubmit_Click when the Place order button is clicked.

 

/// <summary>

/// Saves the invoice ifall goes well!

/// </summary>

/// <paramname="sender"></param>

/// <paramname="e"></param>

protected voidbtnSubmit_Click(object sender, System.EventArgs e)

{

    // *** Unbind the form and display any errors relatedto the binding

    this.UnbindData();

 

    // *** This check is somewhat application specific

    if (this.txtHeardFrom.Text.StartsWith("***"))

    {

        this.AddBindingError("Pleaseenter where you heard about us.",this.txtHeardFrom.ID);

    }

 

    invRow = this.Invoice.Entity;

 

    // *** Override custom field handling

    invRow.Ccexp = Request.Form["txtCCMonth"] + "/" +Request.Form["txtCCYear"];

    invRow.Shipdisks =Invoice.ShipInfo.ShipToCustomer;

 

    if(Invoice.ShipInfo.ShippingMethod != "--")

        invRow.Shipby =Invoice.ShipInfo.ShippingMethod;

 

    // *** Add the DevTool Used

    if (this.DevToolUsed!= "" && !this.DevToolUsed.StartsWith("***"))

        this.Invoice.SetProperty("DevToolUsed", this.DevToolUsed);

 

    // *** Load up the Shipping Address captured previously

    object Temp = Session["ShippingAddressPk"];

    if (Temp != null)

    {

        busShippingAddressShipAddress = WebStoreFactory.GetbusShippingAddress();

        if (ShipAddress.Load((int)Temp))

            this.Invoice.UpdateShippingAddress(ShipAddress);

    }

 

    // The promo code wasn't databound to the invoice - doit now

    invRow.Ordercode = this.Invoice.ShipInfo.PromoCode;

 

    // *** Must recalc and update the invoice and display

    this.ShowLineItems();

 

    // *** If Invoice doesn't validate show error and exit

    if (!this.Invoice.Validate()|| this.bindingErrors.Count > 0 )

    {

        this.AddValidationErrorsToBindingErrors(this.Invoice.ValidationErrors);

        this.ErrorDisplay.ShowError(this.bindingErrors.ToHtml(),"Please correct the following:");

        return;

    }

 

 

    if (this.bindingErrors.Count> 0)

    {

        this.ErrorDisplay.ShowError(this.bindingErrors.ToHtml());

        return;

    }

 

    // *** Handle PayPalProcessing seperately from ProcessCard() since it requires

    // *** passing off to another page on the PayPal Site.

    // *** This request will return to this page Cancel orSuccess querystring

    if (App.Configuration.CCProcessingType!= CCProcessingTypes.None &&

        this.txtCCType.Text == "PP" &&

        !this.PayPalReturnRequest)

    {

        // *** We have to Save Invoice sothat IPN confirm can see it!

        // *** We'll mark it as anUNPROCESSED order.

        this.invRow.Ccresult = "UNPROCESSED";

        if (!Invoice.Save(true))

        {

            this.ErrorDisplay.ShowError(this.Invoice.ErrorMessage,

                                        "An error occurredsaving the invoice:");

            return;

        }

 

        // *** Now redirect to PayPal

        this.HandlePayPalRedirection();

    }

 

    // *** We're processing Credit Cards

    // *** the method decides whether CC processingactually takes place

    if (!this.ProcessCreditCard())

    {

        string Error =Invoice.ErrorMessage;

        int lnAt = Error.IndexOf(";");

        if (lnAt > 0)

            Error = Error.Substring(0, lnAt);

 

        // *** If we have a FAILEDtransaction pass it through unprocessed - we handle it offline

 

        // *** Otherwise echo error back.

        if (invRow.Ccresult != "FAILED")

        {

            this.ErrorDisplay.ShowError(Error, "CreditCard Processing failed: ");

            return;

        }

    }

   

    // *** Let's make sure the Temp LineItems are updated

    // *** Since we may have changed the Promo Code

    Invoice.LineItems.SaveLineItems(false);

 

    // *** Save the invoice by copynig the TemporaryLineItems to the real lineitems

    if (!this.Invoice.Save(true))

    {

        this.ErrorDisplay.ShowError(this.Invoice.ErrorMessage,"An error occurred saving the invoice:");

        return;

    }

 

    // *** Confirm the order items

    if (invRow.Ccresult.Trim() == "APPROVED"&& this.Invoice.CanAutoConfirm())

        this.Invoice.SendEmailConfirmation();

 

    // *** Clear out theInvoice Session PK so this invoice is 'history'  

    Session.Remove("ShoppingCartItems");

    Session.Remove("ShoppingCartSubTotal");

    Session.Remove("ShippingAddressPk");

 

    // *** Clear out expired Temporary lineitems

    busTLineItem LineItems = WebStoreFactory.GetbusTLineItem();

    LineItems.ClearExpiredLineItems(7200); // 2 hours

 

    // *** Show the confirmation page - don't transfer sothey can refresh without error

    Response.Redirect("Confirmation.aspx");

}

     
The code starts off with unbinding the data on the form back into the businessobjects that are loaded on the form. The form's Page_Load() sets up an emptyInvoice and Customer object and loads the available data (Invoice Pk, CustomerPk and lineitems) into it. The unbinding the takes the values from the currentform and updates the appropriate fields (if you're interested in how thiscontrol based two-way databinding works gohere).

 

Once the data's been bound from thecontrols into the business object, it gets validated. First a few explict pagelevel field checks are done followed by the Invoice object's validation(Invoice.Validate()). If either of these fail, BindingErrors are added and thepage is redisplayed with the errors displayed at the top of the form.

 

If all that goes well, the next step is thepayment processing. This form handles both PayPal and Credit Card Processing.For more detail on the PayPal integration in this same mechanism visit thispage. The Credit card processing essentially goes out to an internalroutine that calls the business object's ValidateCreditCard() method I showedearlier. This method returns true or false, and if false the error message isdisplayed and the form redisplayed. If successful the order is saved(Invoice.Save()) and written to disk. Finally the application redirects to theconfirmation page which then displays the final order information and sendsemail confirmations.

 

The internal credit card processing isoffloaded to a custom method:

 

boolProcessCreditCard()

{

    // *** PayPal shouldn't be procesed

    if (App.Configuration.CCProcessingType==  CCProcessingTypes.None||

        this.invRow.Cctype == "PP")

        return true;

 

    // *** See if we're On to do online processing

    // *** This logic checks to see whether items  are shippable without manual validation

    // *** If all are then the order is processed online.Otherwise it's simply submitted

    // *** and processed offline later.

    if (!App.Configuration.ccProcessCardsOnline)

        return true;

 

    // *** Check to see if this order can be processedwithout

    // *** manual confirmation (such as upgrades etc.)

    // *** YOU MAY WANT TO REMOVE THIS LINE IF YOU ALWAYSPROCESS CARDS

    if (!Invoice.CanAutoConfirm())

        return true;

 

    // *** Process the actual card for this invoice

    if (!Invoice.ProcessCreditCard())

    {

        // *** Let the Page handle theerror

        // *** Invoice.ErrorMessage hasparsed error info

        return false;

    }

 

    return true;

}

 

The core logic in this method decides howcredit cards are to be processed, which uses global configuration settings inthe App.Configuration object which maps to web.config settings (for more infosee Buildinga better Application Configuration Class). It's a good idea to not hardcodeany settings related to credit card processing into the application, so usingconfiguration settings.

 

Here's the Invoice.ProcessCreditCard()method again in context. As you can see this method also uses configurationsettings for any of the credit card processing parameters, such as merchant idand password, timeouts and so on. The rest of the properties are set simplyfrom the Invoice object's data members:

 

/// <summary>

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

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

/// settings.

/// </summary>

/// <remarks>The Invoice is notsaved at this point.

/// Make sure to callSave() after this operation.</remarks>

/// <returns></returns>

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;   //"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 = CC.ValidateCard();

 

        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;

}

 

The business object takes care of all thedetails of dealing with the credit card processing. When the processing iscomplete, the Invoice's ccResult,ccResultx and ccError properties are set.ccResultx is the raw response and is stored for future reference should therebe any problems with the transaction.

 

When this method returns – true or false –the client code can simply check these 3 properties to determine whether itworked and get an error message to display back on the page on failure.

 

Ok, so that completes the CC Processingcode. This example, is a live application so there are a few applicationspecific details I've shown, but I hope these have given you a realistic viewof how you can integrate the basic credit card processing classes I describedhere.  Of course you don't have to gothrough this abstraction if you don't want to – you can simply call theappropriate class directly in your ASPX if you choose, but I've found thatabstraction of credit card processing code is a good idea in case you need toswitch providers later, or more importantly if you need that same credit cardprocessing in another application.

 

For example, the abstraction also allows meto reuse this credit card processing code in an offline application of the WebStore. Orders can be downloaded from the Web Site via Web Service to a WinFormsapplications that keeps the order and customer data local. The same Invoice andccProcessing classes are used in the WinForms application. And in bothapplications I now only have to fill the invoice properties and callProcessCreditCard() and check the return value and 3 result values – it becomesreally easy to reuse the credit card processing logic in the Invoice object.Even without the business object, the various ccProcessing classes can also beused directly in WinForms applications.

 

Figure7: Reusability is good: An offline application ofthe store can use the same exact credit card processing routines from thebusiness object and/or ccProcessing classes.

Online or Offline Card Processing

The offline application actually raises aninteresting point. Should you process cards online or should you process themafter the original order entry is complete. Naturally we all want to takecredit cards and charge them right away and get the customers money <g>.But there are situations where you might NOT want to charge the customer rightaway.

 

For example, on my Web Site I sellprimarily software which can be downloaded immediately. For those items youdefinitely want to do online processing. Or do you? <g> For a while I hadmajor problems with orders from the far east being fraudulent. I now have afilter built into my business object that checks the country where the order isplaced from and if it's not within a list of countries allowed the order is notimmediately processed and instead completed as unprocessed.

 

I can then later download the order,manually review it in hopes of catching potential fraud. It's often easy totell potential fraud when email addresses look wrong or addresses sound phoney.At that point I can contact the customer and double check.

 

Another scenario in my business isupgrades. I don't keep invoice information online beyond a couple of days – sothe online site does not actually have all the order history for customers.Even if I did, customer companies often change and so verifying upgrades is notsomething that I have been able to completely automate. So, things likeupgrades are flagged in my store's inventory as nonSelfConfirming items. Whenthere's a nonSelfConfirming item on the order, the order is not sent to creditcard processing immediately.

 

All of this happens inside of the Invoicebusiness object with this method:

 

public override boolCanAutoConfirm()

{

    InvoiceEntity Invoice = this.Entity;

 

    // *** If we have a special order code we need tomanually

    // *** process this order as we have to apply adiscount

    // *** and verify the order

    string OrderCode = Invoice.Ordercode;

    if (OrderCode != null && OrderCode != "" ||OrderCode == "TEST")

        return false;

 

    // *** Don't process cards online without a securitycode (PayPal is an exception)

    if (Invoice.Ccsecurity.Trim() == ""&& Invoice.Cctype != "PP")

        return false;

 

    // *** Due to potential fraud don't process foreigncards

    // *** except from common countries - others will

    // *** require offline processing

    string CountryCode = (string) this.Customer.DataRow["CountryId"];

   

    if(CountryCode != "US" && CountryCode != "CA"&& CountryCode != "DE" &&

        CountryCode != "GB"&& CountryCode != "AU" && CountryCode !="AT"&&

        CountryCode != "CH"&& CountryCode != "FR")

        return false;

 

    return base.CanAutoConfirm();

}

 

The above is what I call conditionaloffline processing.

 

Note that 'offline' processing doesn't haveto be done in an offline application necessarily either. You can have a Webinterface to this as well. For example, I have an invoice form that I canperform these same operations on the live Web site.

 

Offline processing gives you a chance toverify orders. If you're dealing in large ticket items, or you are in abusiness where the fraud potential is high, offline processing might be a goodchoice. Unless you sell electronic delivery goods or access type services whereimmediate notification is required, there's not much inconvenience to thecustomer by not immediately processing the card.

 

The downside to offline processing is thatyou can't immediately get the results from the credit card processing and echoany errors back to the customer. So if an invalid card was entered, or morelikely, an invalid address was used, you're not going to catch it until you runthe card at a later point. If the card fails you'll have to contact thecustomer to get her to resubmit the order or otherwise clear up the error. Thisis obviously a lot more work than having the customer fix it immediately asthey are placing the order.

AUTH vs. SALE Transactions        

Another way that you can 'buy a littletime' is by using an AUTH as opposed to a SALEor AUTH CAPTURE transaction. In the samples here I've shown SALE transactions, which immediately chargethe customers credit card (well at the end of the day or whenever your batchesclose anyway). But you can also do just an AUTH which reserves funds, butdoesn't charge the customer until you go back and do an AUTH CAPTURE. AUTHoperation works by returning you an Authorization code that is returned andreserves funds on the customer's credit card. The money is held for some periodof time. To capture the actual money you do another credit card transaction anduse AUTH CAPTURE passing in the Authorization code (or transaction id – dependson gateway) from the original AUTH procedure.

 

Using AUTH is required if the vendor is notshipping product to the customer within 48 hours.

 

Support for later AUTH CAPTURE requests arenot supported in the ccProcessing classes except for Authorize.NET. For allothers you can run the initial AUTH but the code for later capturing is notavailable. It should be easy to add if you need it though.

Security Considerations

It's pretty obvious that there are securityconsiderations when you are dealing with customer's credit card data. It'simportant that the date the customer submits on the Web site is safe whiletraveling over the wire and that the data captured from the customer stays safewhen store with you as the vendor.

SSL Encryption

Your customers will expect your site to beSSL enabled if they are to trust you with their credit card information. SSLensures that data is encrypted as it travels over the wire and this protectsyou from potential network sniffing. To hook up SSL you need to get a publicSSL certificate from a certificate authority or reseller of certificates. Basecertificates have gotten a lot cheaper in recent years and you can getcertificates from many providers and hosting companies for under $100. I useDirectNic for all my certificates and domain registrations, but there are plentyof other providers out there that are in that same range. If you use an ISPcheck with the providers they use as they often get volume deals that may becheaper than your own shopping.

Displaying Credit Card Information

The next issue is to make sure that youNEVER display full credit card numbers in your Web application back to thecustomer beyond the initial point of entry. The idea is if for whatever reasonyour application is compromised or a user account is compromised you are not inany way displaying the card information back to the unauthorized person.

 

This means, if after the order has beenplaced you display order information back to the customer, like say on an orderstatus page, make sure you display the credit card number with blacked out numbersexcept a few of the numbers to let the customer identify the card used. Theofficial rules from the Credit Card companies are that only the last fourdigits can be displayed.

 

Here's a routine I use to handle thedisplay consistently as part of the business object that normally displays thevalue:

 

public stringGetHiddenCC()

{

    InvoiceEntity Inv = this.Entity;

 

    string CC = Inv.Cc.TrimEnd();

 

    if (Inv.Cctype == "PP"&& CC == "")

        return "<ahref='http://www.paypal.com/'>paid with PayPal</a>";

 

    if (CC.Length == 0)

        return "";

    if (CC.Length < 10)

        return "**** **** **** ****";

 

    string lcFirst = CC.Substring(0, 2);

    string lcLast = CC.Substring(CC.Length - 4, 4);

    //return "**** **** **** " + lcLast;

 

    return lcFirst + "** **** **** " + lcLast;

}

 

So inside of a page I might display thevalue as an expression like this:


<%= this.Invoice.GetHiddenCC() %>

 

Notice I cheat a little in displaying thefirst two digits, so I have an idea what type of card I'm dealing with. I thinkthis makes it easier for anyone to identify a card at a glance. But be theofficial rules are only to display the last 4 digits.

Storing Credit Cards

Storing credit cards is also an importantsecurity consideration. The threat is always that your Web server (or evenstandalone machine) might be compromised in some way and credit cards might getout. It's highly unlikely that this will happen, but there's always thepotential.

 

If at all possible you should not hold onto credit card data of your customers. This is not always possible especiallyin e-Commerce applications that expect frequent repeat customers and want toavoid having to re-enter credit cards over and over again.

 

If possible though, it's a good idea toclear credit card data for orders once the order has been processed. Shouldthere be any sort of trouble with the transaction and you need to issue arefund or the customer returns you can just ask for the card again. This is oneway to remove any possibility that large amounts of credit card data can becompromised and it limits your liability greatly.

 

If you absolutely must store credit carddata it should be stored in encrypted form.

 

There are various compliance standards(specifically CSIP and PCI) that vendors are supposed to follow that describespecific rules of how networks need to be secured and data stored. These rulesare fairly complex and require very expensive certification. However, thesestandards are so strict and expensive to get verified for that it's nearlyimpossible for smaller businesses to comply. While smaller vendors are notlikely to be pushed to comply, not complying essentially releases the creditcard company of any liability should there be fraud or a security breach. Inother words you are fully responsible for the full extent of the damage(realistically you are anyway – I'm only echoing back the rough concepts ofthese certifications).

 

Conflict Resolution: Chargebacks, Fraud

While we're at it – as vendor you usuallyget the short end of the stick if there is any problem with a transaction. If acustomer calls the merchant provider and requests his money back this is knownas a ChargeBack. If a customer complains and files a chargeback request,chances are greatly in favor of the customer to get his money back.

 

You can and should always fight achargeback and your best weapon are complete records of the transaction andmore importantly the fulfillment of the order. Make sure you keep gooddocumentation on all transactions. Keep all transaction data, have goodshipping records if you ship physical product including signature receipts.This gives you the best chance of fighting frivolous chargebacks.

 

Some merchant banks are also moreaggressive about standing up for their vendors than others. My previousmerchant provider pretty much never let chargebacks get reversed even though Isent in fairly complete documentation. My current merchant (Synergy) on theother hand has much fewer chargebacks coming back to me – presumably becausethey are being resolved at the merchant level. And chargebacks that I havereceived have had a much higher reversal rate after documentation was sent in.The reality is that most chargeback requests are created by customers who can'tfigure out what they actually bought. Often contacting the customers andverifying what they bought can get them to reverse chargeback.

 

So the merchant matters in thesesituations. Unfortunately, there aren't good ways to find out before signingwhich type you end up with, unless you spend some time online search variousmerchant forums and paying close attention to which providers are getting goodfeedback from vendors.

Summary

We've covered a lot of ground in this longarticle and I hope you find this information useful. If you're starting outwith payment processing this article has most of the information you need shortof a specific provider list. I obviously have my preferences and I've mentionedthem here. However, I recommend that you spend a little time comparing rates,and checking out various merchant forums to see which merchant providers offerthe best rates, choices and customer service.

 

It's time to get busy and start making somemoney online…

 

你可能感兴趣的:(String,validation,application,processing,interface,transactions)