This should help you massively.
namespace Gateway
{
public class MerchantSellerIPNService : IMerchantSellerIPNService
{
/// <summary>
/// This is the method which is hit when using the URL in the PAY request to PayPal.
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public string ProcessIPN(Stream stream)
{
// Declare locally used variables.
byte[] requestArray = null;
string requestString = null;
string responseString = null;
StreamReader IPNReturnReader;
StreamWriter streamWriter;
MemoryStream responseStream = new MemoryStream();
HttpWebRequest payPalRequest;
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
// Get the URL to send the IPN received back to PayPal (use either of the two below depending on the environment.)
<add key="PAYPAL_IPN_URL" value="https://www.sandbox.paypal.com/cgi-bin/webscr" />
<add key="PAYPAL_IPN_URL" value="https://www.paypal.com/cgi-bin/webscr"/>
string IPNReturnURL = ConfigurationManager.AppSettings["PAYPAL_IPN_URL"];
// Read in the data provided from PayPal
StreamReader streamReader = new StreamReader(stream);
// Obtain the email address and pre-approval key passed to use via PayPal for later use.
string strPayPalMessage = streamReader.ReadToEnd();
// Initalize the POST web request we are going to send to PayPal to valid the IPN we received from them.
payPalRequest = (HttpWebRequest)WebRequest.Create(IPNReturnURL);
payPalRequest.Method = "POST";
payPalRequest.ContentType = "application/x-www-form-urlencoded";
// Create an array containing the IPN message PayPal sent to us.
requestArray = encoding.GetBytes(strPayPalMessage);
// Then add the necessary string to the back to use for verfication.
requestString = Encoding.ASCII.GetString(requestArray);
requestString += "&cmd=_notify-validate";
payPalRequest.ContentLength = requestString.Length;
// Now write the updated IPN message back to PayPal for verification.
streamWriter = new StreamWriter(payPalRequest.GetRequestStream(), System.Text.Encoding.ASCII);
streamWriter.Write(requestString);
streamWriter.Close();
// Read the response from PayPal and process it.
IPNReturnReader = new StreamReader(payPalRequest.GetResponse().GetResponseStream());
responseString = IPNReturnReader.ReadToEnd();
IPNReturnReader.Close();
if (responseString == "VERIFIED")
{
try
{
if (strPayPalMessage.Contains("payment_status=Completed"))
{
if (ProcessPaymentIPNMessage(strPayPalMessage))
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessPaymentIPNMessage - Able to create new payment Transaction Detail Record"), "DEBUG");
else
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessPaymentIPNMessage - Unable to create new payment Transaction Detail Record"), "DEBUG");
}
else if (strPayPalMessage.Contains("payment_status=Refunded"))
{
if (ProcessRefundIPNMessage(strPayPalMessage))
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessRefundIPNMessage - Able to create new refund Transaction Detail Record"), "DEBUG");
else
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessRefundIPNMessage - Unable to create new refund Transaction Detail Record"), "DEBUG");
}
else
{
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - ProcessIPN - Unknown message type"), "DEBUG");
}
}
catch (Exception ex)
{
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - ProcessIPN failed"), "DEBUG");
}
}
else if (responseString == "INVALID")
{
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - Invalid IPN Message Received: "), "DEBUG");
}
else
{
PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - Fatal IPN Message Received: "), "DEBUG");
}
return "MerchantSellerIPNService Completed";
}
/// <summary>
/// Method used to process the Payment IPN notification message and update the database as required.
/// </summary>
/// <returns></returns>
private bool ProcessPaymentIPNMessage(string PayPalIPNMessage)
{
// Firstly, we need to split the IPN message into sections based on the & sign.
string[] PayPalMessageElemetsArray = PayPalIPNMessage.Split('&');
// Now obtain the list of information (from the message) we require to make the TransactionDetail record.
string merchantTransactionId = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("txn_id=", StringComparison.Ordinal));
string feeAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_fee=", StringComparison.Ordinal));
string grossAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_gross=", StringComparison.Ordinal));
// TODO: REMOVE THIS ITS FOR DEBUGGING PURPOSES
string errorMessage2 = String.Format("ProcessPaymentIPNMessage - merchantTransactionId: {0}, feeAmount: {1}, grossAmount: {2}", merchantTransactionId, feeAmount, grossAmount);
PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage2), "DEBUG");
try
{
// We now need to remove the variable name and '=' from the elements so we only have the necessary information.
merchantTransactionId = merchantTransactionId.Replace("txn_id=", "");
feeAmount = feeAmount.Replace("mc_fee=", "");
grossAmount = grossAmount.Replace("mc_gross=", "");
// Now convert the values obtained from the IPN message and calculate the net amount.
decimal dFeeAmount = Convert.ToDecimal(feeAmount);
decimal dGrossAmount = Convert.ToDecimal(grossAmount);
decimal dNetAmount = Math.Round((dGrossAmount - dFeeAmount), 2);
try
{
// Finally create the new transaction fee record.
TransactionDetail transactionDetail = new TransactionDetail();
transactionDetail.MerchantTransactionId = merchantTransactionId;
transactionDetail.Gross = dGrossAmount;
transactionDetail.Fee = Decimal.Negate(dFeeAmount);
transactionDetail.Net = dNetAmount;
transactionDetail.TransactionType = (int)TransactionDetailTransactionType.InStorePayment;
transactionDetail.Save();
}
catch (Exception ex)
{
string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
return false;
}
return true;
}
catch (Exception ex)
{
string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
return false;
}
}
/// <summary>
/// Method used to process the Refund IPN notification message and update the database as required.
/// </summary>
/// <returns></returns>
private bool ProcessRefundIPNMessage(string PayPalIPNMessage)
{
// Firstly, we need to split the IPN message into sections based on the & sign.
string[] PayPalMessageElemetsArray = PayPalIPNMessage.Split('&');
// Now obtain the list of information (from the message) we require to make the TransactionDetail record.
string merchantTransactionId = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("txn_id=", StringComparison.Ordinal));
string parentTransactionId = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("parent_txn_id=", StringComparison.Ordinal));
string feeAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_fee=", StringComparison.Ordinal));
string grossAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_gross=", StringComparison.Ordinal));
try
{
// We now need to remove the variable name and '=' from the elements so we only have the necessary information.
merchantTransactionId = merchantTransactionId.Replace("txn_id=", "");
parentTransactionId = parentTransactionId.Replace("parent_txn_id=", "");
feeAmount = feeAmount.Replace("mc_fee=", "").Replace("-", "");
grossAmount = grossAmount.Replace("mc_gross=", "").Replace("-", "");
// Now convert the values obtained from the IPN message and calculate the net amount.
decimal dFeeAmount = Convert.ToDecimal(feeAmount);
decimal dGrossAmount = Convert.ToDecimal(grossAmount);
decimal dNetAmount = Math.Round((dGrossAmount - dFeeAmount), 2);
// Now create the new transaction fee record.
try
{
// Finally create the new transaction fee record.
TransactionDetail transactionDetail = new TransactionDetail();
transactionDetail.MerchantTransactionId = merchantTransactionId;
transactionDetail.Gross = dGrossAmount;
transactionDetail.Fee = Decimal.Negate(dFeeAmount);
transactionDetail.Net = dNetAmount;
transactionDetail.TransactionType = (int)TransactionDetailTransactionType.InStoreRefund;
transactionDetail.Save();
}
catch (Exception ex)
{
string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
return false;
}
// Finally update the PurchaseRefund record with the Parent Transaction Id (used as a backup incase the API IPN message for the payment wasn't received).
try
{
PurchaseRefund refund = PurchaseRefund.SingleOrDefault(x => x.RefundTransactionId == merchantTransactionId);
if (refund != null)
{
refund.ParentTransactionId = parentTransactionId;
refund.Save();
}
}
catch (Exception ex)
{
string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to update PurchaseRefund record (Transaction ID: {0}) with Parent Transaction Id: {1}", merchantTransactionId, parentTransactionId);
PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
return false;
}
// If all is succesful we can return true.
return true;
}
catch (Exception ex)
{
string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
return false;
}
}
}
}