websitepanel/WebsitePanel/Sources/WebsitePanel.Gateways.AuthorizeNet/AuthorizeNetProvider.cs

324 lines
12 KiB
C#

// Copyright (c) 2012, Outercurve Foundation.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// - Neither the name of the Outercurve Foundation nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using System;
using System.Web;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
namespace WebsitePanel.Ecommerce.EnterpriseServer
{
/// <summary>
/// Payment provider for the Authorize.NET Gateway
/// </summary>
public class AuthorizeNetProvider : SystemPluginBase, IPaymentGatewayProvider
{
public const string DEMO_SERVICE_URL = "https://test.authorize.net/gateway/transact.dll";
public const string LIVE_SERVICE_URL = "https://secure.authorize.net/gateway/transact.dll";
public const char DELIMITER_CHAR = '|';
public const string API_VERSION = "3.1";
public const string TRANSACTION_TYPE = "AUTH_CAPTURE";
public const string PAYMENT_METHOD = "CC";
public const string MD5_INVALID_MSG = "MD5 hash value received is either incorrect or modified.";
public override string[] SecureSettings
{
get
{
return new string[] { AuthNetSettings.MD5_HASH, AuthNetSettings.TRANSACTION_KEY };
}
}
/// <summary>
/// Gets Authorize.Net account is demo
/// </summary>
public bool DemoAccount
{
get
{
return Convert.ToBoolean(PluginSettings[AuthNetSettings.DEMO_ACCOUNT]);
}
}
/// <summary>
/// Gets whether provider is running in live mode
/// </summary>
public bool LiveMode
{
get
{
return Convert.ToBoolean(PluginSettings[AuthNetSettings.LIVE_MODE]);
}
}
/// <summary>
/// Gets whether email confirmations enabled
/// </summary>
public bool SendConfirmation
{
get
{
return Convert.ToBoolean(PluginSettings[AuthNetSettings.SEND_CONFIRMATION]);
}
}
/// <summary>
/// Get MD5 hash value for the account
/// </summary>
public string MD5_Hash
{
get { return PluginSettings[AuthNetSettings.MD5_HASH]; }
}
/// <summary>
/// Gets transaction key for the account
/// </summary>
public string TransactionKey
{
get { return PluginSettings[AuthNetSettings.TRANSACTION_KEY]; }
}
/// <summary>
/// Gets Authorize.Net account username
/// </summary>
public string Username
{
get { return PluginSettings[AuthNetSettings.USERNAME]; }
}
/// <summary>
/// Gets merchant email to send confirmations by email
/// </summary>
public string MerchantEmail
{
get { return PluginSettings[AuthNetSettings.MERCHANT_EMAIL]; }
}
/// <summary>
/// Gets Authorize.Net service url
/// </summary>
public string ServiceUrl
{
get
{
if (DemoAccount)
return DEMO_SERVICE_URL;
return LIVE_SERVICE_URL;
}
}
#region IPaymentGatewayProvider Members
public TransactionResult SubmitPaymentTransaction(CheckoutDetails details)
{
//init result structure
TransactionResult ret = new TransactionResult();
//create request content
string data = GetRequestData(details);
// create webrequest instance
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(ServiceUrl);
webRequest.Method = "POST";
webRequest.ContentLength = data.Length;
webRequest.ContentType = "application/x-www-form-urlencoded";
// send service request
StreamWriter sw = null;
try
{
sw = new StreamWriter(webRequest.GetRequestStream());
sw.Write(data);
}
finally
{
if (sw != null)
sw.Close();
}
// read service response
AIMResponse aimResponse = null;
HttpWebResponse webResponse = null;
try
{
// get response
webResponse = (HttpWebResponse)webRequest.GetResponse();
// emit new response
aimResponse = new AIMResponse(webResponse.GetResponseStream(), DELIMITER_CHAR);
}
finally
{
webResponse.Close();
webRequest.Abort();
}
// copy raw service response
ret.RawResponse = aimResponse.RawResponse;
// read service response status
switch (aimResponse[AIMField.ResponseCode])
{
case "1": //This transaction has been approved.
case "4": //This transaction is being held for review.
// check MD5 signature
if (!CheckResponseSignature(Username, MD5_Hash, aimResponse[AIMField.TransactionId], details[CheckoutKeys.Amount],
aimResponse[AIMField.ResponseSignature]))
{
throw new Exception(MD5_INVALID_MSG);
}
//
ret.Succeed = true;
//
ret.TransactionId = aimResponse[AIMField.TransactionId];
// mark transaction as a completed
ret.TransactionStatus = TransactionStatus.Approved;
//
break;
case "2": // This transaction has been declined.
case "3": // There has been an error processing this transaction.
//
ret.StatusCode = String.Concat(AuthorizeNetKeys.ErrorPrefix, aimResponse[AIMField.ResponseCode],
aimResponse[AIMField.ResponseReasonCode]);
//
ret.Succeed = false;
//
ret.TransactionStatus = TransactionStatus.Declined;
//
break;
}
// return result
return ret;
}
#endregion
private bool CheckResponseSignature(string login, string md5hash,
string transactId, string amount, string signature)
{
//input string = "MD5 Hash Value"+"API Login ID"+"Trans ID"+"Amount"
byte[] data = Encoding.ASCII.GetBytes(md5hash + login + transactId + amount);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(data);
StringBuilder sb = new StringBuilder();
foreach (byte b in result)
{
sb.Append(b.ToString("X2"));
}
// validate response signature
return (sb.ToString() == signature);
}
private string GetRequestData(CheckoutDetails details)
{
StringBuilder sb = new StringBuilder();
AddRequestData(CheckoutKeys.CardNumber, AuthorizeNetKeys.CardNumber, sb, details);
AddRequestData(CheckoutKeys.VerificationCode, AuthorizeNetKeys.VerificationCode, sb, details);
// expire date
string expireDate = String.Concat(details[CheckoutKeys.ExpireMonth], details[CheckoutKeys.ExpireYear]);
AddRequestData(AuthorizeNetKeys.ExpirationDate, expireDate, sb);
AddRequestData(CheckoutKeys.Amount, AuthorizeNetKeys.Amount, sb, details);
//TODO: Add currency support
AddRequestData(CheckoutKeys.Currency, AuthorizeNetKeys.CurrencyCode, sb, details);
AddRequestData(CheckoutKeys.InvoiceNumber, AuthorizeNetKeys.InvoiceNumber, sb, details);
AddRequestData(CheckoutKeys.ContractNumber, AuthorizeNetKeys.TransDescription, sb, details);
AddRequestData(CheckoutKeys.FirstName, AuthorizeNetKeys.FirstName, sb, details);
AddRequestData(CheckoutKeys.LastName, AuthorizeNetKeys.LastName, sb, details);
// email is shipping
AddRequestData(CheckoutKeys.CustomerEmail, AuthorizeNetKeys.CustomerEmail, sb, details);
AddRequestData(CheckoutKeys.Address, AuthorizeNetKeys.Address, sb, details);
AddRequestData(CheckoutKeys.Zip, AuthorizeNetKeys.Zip, sb, details);
AddRequestData(CheckoutKeys.City, AuthorizeNetKeys.City, sb, details);
AddRequestData(CheckoutKeys.State, AuthorizeNetKeys.State, sb, details);
AddRequestData(CheckoutKeys.Country, AuthorizeNetKeys.Country, sb, details);
AddRequestData(CheckoutKeys.Phone, AuthorizeNetKeys.Phone, sb, details);
AddRequestData(CheckoutKeys.Fax, AuthorizeNetKeys.Fax, sb, details);
AddRequestData(CheckoutKeys.CustomerId, AuthorizeNetKeys.CustomerId, sb, details);
AddRequestData(CheckoutKeys.IPAddress, AuthorizeNetKeys.IPAddress, sb, details);
// shipping information
AddRequestData(CheckoutKeys.ShipToFirstName, AuthorizeNetKeys.ShipToFirstName, sb, details);
AddRequestData(CheckoutKeys.ShipToLastName, AuthorizeNetKeys.ShipToLastName, sb, details);
AddRequestData(CheckoutKeys.ShipToCompany, AuthorizeNetKeys.ShipToCompany, sb, details);
AddRequestData(CheckoutKeys.ShipToZip, AuthorizeNetKeys.ShipToZip, sb, details);
AddRequestData(CheckoutKeys.ShipToAddress, AuthorizeNetKeys.ShipToAddress, sb, details);
AddRequestData(CheckoutKeys.ShipToCity, AuthorizeNetKeys.ShipToCity, sb, details);
AddRequestData(CheckoutKeys.ShipToState, AuthorizeNetKeys.ShipToState, sb, details);
AddRequestData(CheckoutKeys.ShipToCountry, AuthorizeNetKeys.ShipToCountry, sb, details);
// service settings
// copy account username
AddRequestData(AuthorizeNetKeys.Account, Username, sb);
// copy response delimiter char
AddRequestData(AuthorizeNetKeys.DelimiterChar, DELIMITER_CHAR.ToString(), sb);
// copy transaction key
AddRequestData(AuthorizeNetKeys.TransactionKey, TransactionKey, sb);
// copy send confirmation flag & merchant email
if (SendConfirmation)
{
AddRequestData(AuthorizeNetKeys.MerchantEmail, MerchantEmail, sb);
AddRequestData(AuthorizeNetKeys.SendConfirmation, "TRUE", sb);
}
else
{
AddRequestData(AuthorizeNetKeys.SendConfirmation, "FALSE", sb);
}
// copy API version
AddRequestData(AuthorizeNetKeys.Version, API_VERSION, sb);
// copy demo mode flag
if (!LiveMode)
AddRequestData(AuthorizeNetKeys.DemoMode, "TRUE", sb);
// copy payment method
AddRequestData(AuthorizeNetKeys.PaymentMethod, PAYMENT_METHOD, sb);
// copy transaction type
AddRequestData(AuthorizeNetKeys.TransactType, TRANSACTION_TYPE, sb);
// copy delim data flag
AddRequestData(AuthorizeNetKeys.DelimData, "TRUE", sb);
// copy relay response flag
AddRequestData(AuthorizeNetKeys.RelayResponse, "FALSE", sb);
// return result
return sb.ToString();
}
private void AddRequestData(string key1, string key2, StringBuilder sb, CheckoutDetails details)
{
AddRequestData(key2, details[key1], sb);
}
private void AddRequestData(string key, StringBuilder sb, CheckoutDetails details)
{
AddRequestData(key, details[key], sb);
}
private void AddRequestData(string key, string value, StringBuilder sb)
{
if (!String.IsNullOrEmpty(key) && !String.IsNullOrEmpty(value))
{
if (sb.Length == 0)
sb.AppendFormat("{0}={1}", key, value);
else
sb.AppendFormat("&{0}={1}", key, value);
}
}
}
}