Skip to main content

POST as form using C# to Pay Provider

The Remote Post Form class

I have used the code for the jigar site and added my own goodies, so all credit to him. Sorry about the flast formating but is the only way I could get a neatish post


using System;
using System.Collections.Generic;
using System.Web;

using System.Security.Cryptography;
/// <summary>
/// Remotely handle and Post form from http://www.jigar.net/articles/viewhtmlcontent78.aspx%20
/// </summary>
public class RemotePost
{
private System.Collections.Specialized.NameValueCollection Inputs
= new System.Collections.Specialized.NameValueCollection();
public string Url;
public string Method;
public string FormName;

public RemotePost(string pURL)
{
//
// TODO: Add constructor logic here
//
Url = pURL;
Method = "post";
FormName = "frmRemotePost";
}
 
public void Add(string name, string value)
{
Inputs.Add(name, value);
}

public void Post()
{
System.Web.HttpContext.Current.Response.Clear();

System.Web.HttpContext.Current.Response.Write("<html><head>");
System.Web.HttpContext.Current.Response.Write(string.Format("</head><body onload=\"document.{0}.submit()\">", FormName));
System.Web.HttpContext.Current.Response.Write(string.Format("<form name=\"{0}\" method=\"{1}\" action=\"{2}\" >",
FormName, Method, Url))
;
for (int i = 0; i < Inputs.Keys.Count; i++)
{
System.Web.HttpContext.Current.Response.Write(string.Format("<input name=\"{0}\" type=\"hidden\" value=\"{1}\">", Inputs.Keys[i], Inputs[Inputs.Keys[i]]));
}
System.Web.HttpContext.Current.Response.Write("</form>");
System.Web.HttpContext.Current.Response.Write("</body></html>");
System.Web.HttpContext.Current.Response.End();
}
/// <summary>
/// Calculates the md5 signautre using the items from start to pEnd (optional)
/// </summary>
/// <param name="pStart">From which field to start, 1 is first</param>
/// <param name="pEnd">(Optional) to what field</param>
/// <returns>MD5 string</returns>
public string CalcMD5Signature(int pStart)
{
if (Inputs.Keys.Count == 0)
return "";
else
return CalcMD5Signature(pStart, Inputs.Keys.Count);
}
public string CalcMD5Signature(int pStart, int pEnd)
{
if (Inputs.Keys.Count == 0)
return "";
else
{
// start calculating the standard string
string strKeys = String.Format("{0}={1}", Inputs.Keys[pStart], Inputs[Inputs.Keys[pStart]]);

if (pEnd > Inputs.Keys.Count) pEnd = Inputs.Keys.Count;

for (int i = pStart; i < pEnd; i++) {
if (Inputs[Inputs.Keys[i]].Length > 0) //only if it is not blank add
strKeys += String.Format("&{0}={1}", Inputs.Keys[i], Inputs[Inputs.Keys[i]]);
}
// now calc MD5
Byte[] bOriginal;
Byte[] bEncoded;
MD5 md5Signature = new System.Security.Cryptography.MD5CryptoServiceProvider();
string strHex;

// Now convert it to MD5
bOriginal = System.Text.ASCIIEncoding.Default.GetBytes(strKeys);
bEncoded = md5Signature.ComputeHash(bOriginal);

//Convert encoded bytes back to a 'readable' string
strHex = BitConverter.ToString(bEncoded); // converst string adds "-" betwen each byte
strHex = strHex.Replace("-", ""); //PayFast does not want the dashes the tostring adds

// this does not seem to work with PayFast for some reason
return strHex.ToLower(); // payfast needs the string in lower case
}

}
}

PayFast Class

This class is used by the PayNow button on your form, to send payment through
/// <summary>
/// class used to access and use the payfast services, used php sample as a guide
/// </summary>
public class PayFast
{

// Messages that PayFast may send
// Error
const string PF_ERR_AMOUNT_MISMATCH = "PF: Amount mismatch";
const string PF_ERR_BAD_SOURCE_IP = "PF: Bad source IP address";
const string PF_ERR_CONNECT_FAILED = "PF: Failed to connect to PayFast";
const string PF_ERR_BAD_ACCESS = "PF: Bad access of page";
const string PF_ERR_INVALID_SIGNATURE = "PF: Security signature mismatch";
const string PF_ERR_CURL_ERROR = "PF: An error occurred executing cURL";
const string PF_ERR_INVALID_DATA = "PF: The data received is invalid";
const string PF_ERR_UKNOWN = "PF: Unknown error occurred";
//General
const string PF_MSG_OK = "PF: Payment was successful";
const string PF_MSG_FAILED = "PF: Payment has failed";

/* Pay fast Constants - for get Value */
//-- Based on http://www.payfast.co.za/c/std/website-payments-variables
//-- Receiver Details --
public const int PF_GET_MERCHANT_ID = 1;
public const int PF_GET_MERCHANT_KEY = 2;
public const int PF_GET_RETURN_URL = 3;
public const int PF_GET_CANCEL_URL = 4;
public const int PF_GET_NOTIFY_URL = 5;

//-- Payer Details
public const int PF_GET_NAME_FIRST = 6;
public const int PF_GET_NAME_LAST = 7;
public const int PF_GET_EMAIL_ADDRESS = 8;

//-- Transaction Details
public const int PF_GET_M_PAYMENT_ID = 9;
public const int PF_GET_AMOUNT = 10;
public const int PF_GET_ITEM_NAME = 11;
public const int PF_GET_ITEM_DESCRIPTION = 12;

//-- Transaction Options
public const int PF_GET_EMAIL_CONFIRMATION = 13;
public const int PF_GET_CONFIRMATION_ADDRESS = 14;

//-- Security
public const int PF_GET_SIGNATURE = 15;

//-- Constants for values of above - strings used since they are passt in the input form
#if PAYFAST_TEST
const string PF_HOST = "https://sandbox.payfast.co.za/eng/process/"; //-- public means ew can use it to manuall control buttons
const string PF_USERNAME = "sbtm01@payfast.co.za";
const string PF_MERCHANT_ID = "10000100";
const string PF_MERCHANT_KEY = "46f0cd694581a";

// PDT Key: 0a1e2e10-03a7-4928-af8a-fbdfdfe31d43

#else
const string PF_HOST = "https://www.payfast.co.za/eng/process";
const string PF_USERNAME = "warrenm@Widget.co.za";
const string PF_MERCHANT_ID = "10000100"; //-- replace these with your
const string PF_MERCHANT_KEY = "46f0cd694581a "; //--replace these with yours

#endif

//-- constants that are used all the time regardless of mode
const string PF_RETURN_URL = "http://www.widget.co.za/Pages/forms/PaymentDone.aspx";
const string PF_CANCEL_URL = "http://www.widget.co.za/Pages/forms/PaymentCancelled.aspx";
const string PF_NOTIFY_URL = "http://www.widget.co.za/Pages/forms/PaymentNotify.aspx";

const string PF_EMAIL_CONFIRMATION = "1";
const string PF_CONFIRMATION_ADDRESS = "order@widget.co.za";

//-- Now class variables that are accessable to the outside world
public string PostResult = "";
public string PostURL = PF_HOST;

//-- Now class variables that are used internally
//-- class variables, set and used
//-- Payer Vairables
string NameFirst = ""; //-- Bob
string NameLast = ""; //-- Smith
string EmailAddress = "" ; //-value="bob.smith@example.com

//-- Transaction varaibles
string MPaymentId = ""; //-- Unique payment ID on the merchant's system
string Amount = ""; //-- The amount which the payer must pay
string ItemName = "WidgetOrder"; //--    The name of the item being charged for - 100 char    
string ItemDescription = "Widget Related Goods"; //-- The description of the item being charged for. - 255 char

//-- Security
string Signature = ""; //-- A security signature of the transmitted data taking the form of an MD5 hash of the submitted variables.

public PayFast()
    {
        //
        // any items we need to initialize go here
        //
    }

/// <summary>
/// Split a FullName into a first and last name, assuming that the first word is a first name
/// </summary>
/// <param name="pFullName">The full name string</param>
/// <param name="pFirstName">The first name string variable to be returned</param>
/// <param name="pLastName">The last name string variable to be returned</param>
public virtual void SplitFullName(string pFullName, out string pFirstName, out string pLastName)
{
int iSpace = pFullName.IndexOf(" ");

if(iSpace > 0)
{
//-- firsname lastnames
pFirstName = pFullName.Substring(0, iSpace).Trim();
pLastName = pFullName.Substring(iSpace + 1).Trim();
}
else
{
//-- did they enter last, first?
iSpace = pFullName.IndexOf(",");
if(iSpace > 0)
{
pLastName = pFullName.Substring(iSpace + 1).Trim();
pFirstName = pFullName.Substring(0, iSpace).Trim();
}
else
{
//-- looks like they sent only one name
pFirstName = pFullName.Trim();
pLastName = "";
}
}

}

/// <summary>
/// Set the payer details using FullName which is split for you, and email address
/// </summary>
/// <param name="pFullName">Full Name, i.e. first last</param>
/// <param name="pEmailAdress">email address of payer</param>
public virtual void SetPayerDetails (string pFullName, string pEmailAdress)
{

string strFirstName = "";
string strLastName = "";

//-- split the last name into first and last names
SplitFullName(pFullName, out strFirstName, out strLastName);

//-- now set the vars
SetPayerDetails(strFirstName, strLastName, pEmailAdress);
}

/// <summary>
/// Set the payer complete details with first name, last name and email address
/// </summary>
/// <param name="pNameFirst">Payers First Name</param>
/// <param name="pNameList">Payers Last Name</param>
/// <param name="pEmailAddress">Payers Email Address</param>
public virtual void SetPayerDetails (string pNameFirst, string pNameLast, string pEmailAddress)
{
NameFirst = pNameFirst;
NameLast = pNameLast;
EmailAddress = pEmailAddress;
}

/// <summary>
/// Set the transaciton details for the payfast transaction
/// </summary>
/// <param name="pPaymentId">Unique payment ID on the merchant's system</param>
/// <param name="pAmount">The amount which the payer must pay</param>
/// <param name="pItemBame">The name of the item being charged for - 100 char</param>
/// <param name="pItemDescription">The description of the item being charged for. - 255 char</param>
public virtual void SetTransactionDetails(string pPaymentId, string pAmount, string pItemName, string pItemDescription)
{
//-- set the local variables
MPaymentId = pPaymentId; //-- the order id can be used to cross reference so must be the same
Amount = pAmount.Replace(",",""); // strip any thousand seperator
ItemName = pItemName;
ItemDescription = pItemDescription;
}
/// <summary>
/// Can be called from the input form using in line script so returned string of the value requested, or
/// Directly from your own class, like it is in this class
/// </summary>
/// <param name="pGetType">PF_ item as per the items require on the form</param>
/// <returns>string of requested data</returns>
public virtual string GetPFItem(int pGetPFType)
{
switch (pGetPFType) {

case PF_GET_MERCHANT_ID:
return PF_MERCHANT_ID;

case PF_GET_MERCHANT_KEY:
return PF_MERCHANT_KEY;

case PF_GET_RETURN_URL:
return PF_RETURN_URL;

case PF_GET_CANCEL_URL:
return PF_CANCEL_URL;

case PF_GET_NOTIFY_URL:
return PF_NOTIFY_URL;

//-- Payer Details
case PF_GET_NAME_FIRST:
return NameFirst;

case PF_GET_NAME_LAST :
return NameLast;

case PF_GET_EMAIL_ADDRESS:
return EmailAddress;

//-- Transaction Details
case PF_GET_M_PAYMENT_ID:
return MPaymentId;

case PF_GET_AMOUNT:
return Amount;

case PF_GET_ITEM_NAME:
return ItemName;

case PF_GET_ITEM_DESCRIPTION:
return ItemDescription;

case PF_GET_EMAIL_CONFIRMATION:
return PF_EMAIL_CONFIRMATION;

case PF_GET_CONFIRMATION_ADDRESS:
return PF_CONFIRMATION_ADDRESS;

case PF_GET_SIGNATURE:
string sSignature; //name_first=John&name_last=Doe&email_address=.
// MD5 stuff
Byte[] bOriginal;
Byte[] bEncoded;
MD5 md5Signature = new System.Security.Cryptography.MD5CryptoServiceProvider();
string sHex;

sSignature = "name_first=" + NameFirst;
if (NameLast.Length > 0)
sSignature = sSignature + "&name_last=" + NameLast;

sSignature += "&email_address=" + EmailAddress
+ "&m_payment_id=" + MPaymentId
+ "&amount=" + Amount
+ "&item_name=" + ItemName
+ "&item_description=" + ItemDescription
+ "&email_confirmation=" + PF_EMAIL_CONFIRMATION
+ "&confirmation_address=" + PF_CONFIRMATION_ADDRESS;

sSignature = sSignature.ToLower();
// Now convert it to MD5
bOriginal = System.Text.ASCIIEncoding.Default.GetBytes(sSignature);
bEncoded = md5Signature.ComputeHash(bOriginal);

//Convert encoded bytes back to a 'readable' string
sHex = BitConverter.ToString(bEncoded); // converst string adds "-" betwen each byte
sHex = sHex.Replace("-", ""); //PayFast does not want the dashes the tostring adds

// crap thing does not work if I do not put blank
// return sHex.ToLower(); // payfast needs the string in lower case

return ""; //-- could not get the signature to work as doc says

default:
return "err-incorrect PF value passed";

} //-- end switch
}
/// <returns>true if success</returns>
public bool PostToRemote()
{ return PostToRemote("", "", "", "", ""); }
public bool PostToRemote(string pCustomStr1)
{ return PostToRemote(pCustomStr1, "","", "", ""); }
public bool PostToRemote(string pCustomStr1, string pCustomStr2)
{ return PostToRemote(pCustomStr1, pCustomStr2, "", "", ""); }
public bool PostToRemote(string pCustomStr1, string pCustomStr2, string pCustomStr3)
{ return PostToRemote(pCustomStr1, pCustomStr2, pCustomStr3,"",""); }
public bool PostToRemote(string pCustomStr1, string pCustomStr2, string pCustomStr3, string pCustomStr4)
{ return PostToRemote(pCustomStr1, pCustomStr2, pCustomStr3, pCustomStr4,""); }
public bool PostToRemote(string pCustomStr1, string pCustomStr2, string pCustomStr3, string pCustomStr4, string pCustomStr5)
{
try
{
RemotePost PayFastPost = new RemotePost(PF_HOST);

// Receiver Details
PayFastPost.Add("merchant_id",GetPFItem(PF_GET_MERCHANT_ID));
PayFastPost.Add("merchant_key",GetPFItem(PF_GET_MERCHANT_KEY));
PayFastPost.Add("return_url",GetPFItem(PF_GET_RETURN_URL));
PayFastPost.Add("cancel_url",GetPFItem(PF_GET_CANCEL_URL));
PayFastPost.Add("notify_url",GetPFItem(PF_GET_NOTIFY_URL));
// Payer Details
PayFastPost.Add("name_first",GetPFItem(PF_GET_NAME_FIRST));
PayFastPost.Add("name_last", GetPFItem(PF_GET_NAME_LAST));
PayFastPost.Add("email_address",GetPFItem(PF_GET_EMAIL_ADDRESS));
// Transaction Details
PayFastPost.Add("m_payment_id",GetPFItem(PF_GET_M_PAYMENT_ID));
PayFastPost.Add("amount", GetPFItem(PF_GET_AMOUNT));
PayFastPost.Add("item_name", GetPFItem(PF_GET_ITEM_NAME));
PayFastPost.Add("item_description", GetPFItem(PF_GET_ITEM_DESCRIPTION));
if (pCustomStr1 != "")
PayFastPost.Add("custom_str1", pCustomStr1);
if (pCustomStr2 != "")
PayFastPost.Add("custom_str2", pCustomStr2);
if (pCustomStr3 != "")
PayFastPost.Add("custom_str3", pCustomStr3);
if (pCustomStr4 != "")
PayFastPost.Add("custom_str5", pCustomStr3);
if (pCustomStr5 != "")
PayFastPost.Add("custom_str5", pCustomStr3);
// have not handled int
// Transaction Options
PayFastPost.Add("email_confirmation", GetPFItem(PF_GET_EMAIL_CONFIRMATION));
PayFastPost.Add("confirmation_address", GetPFItem(PF_GET_CONFIRMATION_ADDRESS));
//PayFastPost.Add("signature", PayFastPost.CalcMD5Signature(0)); // 5 should be the name, but this does not work?
PayFastPost.Add("signature", GetPFItem(PF_GET_SIGNATURE));
PayFastPost.Post();
PostResult = "form posted";
return true; //-- if we got here all is okay
}
catch (Exception ex)
{
PostResult = "err: " + ex.Source + ", " + ex.Message;
return false;
}
}
}

Usage

Here I post the data to PayFast, store a cookie and a session variable just in case one gets lost, I use them in the Pay%% files
The Quick cookie class simple saves or retrieves a cook with error handling and expiry


protected void btnPayNow_Click(object sender, EventArgs e)
{
PayFast myPayFast = new PayFast();


myPayFast.SetPayerDetails(ltrlFullName.Text, ltrlEmail.Text);
myPayFast.SetTransactionDetails(ltrlOrderID.Text, ltrlTotal.Text, ltrlItem.Text, ltrlOrderID.Text);

// set the session variable for use by the cancellation, done and processed URL's to be called
// by the PayFast server later
Session.Add("OrderID", ltrlOrderID.Text);
QuickCookie QuaffeeCookie = new QuickCookie();
QuaffeeCookie.SetCookie("LastOrderID", ltrlOrderID.Text, 50);
// at the moment the form is submitted no click handleing now
if (myPayFast.PostToRemote(ltrlOrderID.Text))
ltrlPayFastErr.Text = "Posted:" + myPayFast.PostResult; // transfer to the constructed URL
else
ltrlPayFastErr.Text = "Error posting: " + myPayFast.PostResult;
}
}


 
 Hope this helps good luck to all

Comments

Popular posts from this blog

Fixing winmail.dat problem - specifically in Quickbooks

For months we have had problems with attachments from Quickbooks. Having looked down many avenues I think we have found a fix or few: (See below for update)
Here are a few websites that help out:
Microsoft Outlook/Exchange MS-TNEF handling (aka "Winmail.dat", "Win.dat", or "Part 1.2" problem of unopenable email attachments)

http://news.office-watch.com/t/n.aspx?a=716
KB958012 : When you use Outlook 2007 to send an e-mail message, the recipient of the message sees an attachment that is called Winmail.dat
Essentially the summary is that there is a problem with Outlook trying to force Rich Text Format. so you need to turn that off. Mail users not receiving email in an Outlook derivative will have a problem. This format is called
So: Go into OutlookOn the "Tools" menu, click "Options", then click the "Mail Format" tab, and then the "Internet Format" button.Set "When sending Outlook Rich Text messages to Internet ...&…

SMTP servers of South Africa

SMTP Settings
Below is a list of SMTP sites in South Africa, using this and the ISP Map you can try and find which one works best for you.


Telkom smtp.saix.net (ADSL)
smtp.telkomsa.co.za (56k dial up)
smtp.telkomsa.net
Internet Solutions smtp.isdsl.net (ADSL)
smtp.dial-up.net (56k dial up on IS)
smtp.layerone.net (3g backbone) Vodacom smtp.vodacom.co.za
smtp.vodamail.co.za
MTN smtp.mtn.co.za
Cell C smtp.cellc.co.za (GPRS)
mail.cmobile.co.za (also used by Virgin)
ABSA mail.absa.co.za
iBurst smtp.wbs.co.za
smtp.iburst.co.za
@lantic smtp.lantic.net (ADSL,Dialup, ISDN)
Sentech smtp.sentech.co.za
MWEB smtp.mweb.net (ADSL) - this is to be retired End June 2012, use below instead
smtp.mweb.co.za (56k dial-up & ADSL & business)
iAfrica smtp.uunet.co.za
smtp.iafrica.com
Neotel smtp.neotel.co.za
TiscaliNOW MWeb
smtp.tiscali.co.za
NetactiveNOW MWeb
smtp.netactive.co.za
Global smtp.global.co.za

Hertzner Use your domain name in your SMTP eg. mail.craythorne.co.za (smtp …