Validera kreditkortsnummer med Luhn's algoritm

Jag har under några år använt en funktion för att validera kreditkort på diverse projekt. Hittade den som exempel i någon bok som jag glömt namnet på.

Blev lite nyfiken på ursprunget och genom en sökning på Google, hittade jag exakt samma exempel i en artikel på The Code Project. Där finns även ytterligare användbar kod för hur man kan kontrollera vilken typ av kreditkort ett kortnummer hör till.

Man får även reda på att funktionen jag pratade om, använder Luhn's algoritm utvecklad av Hans Peter Luhn år 1954.

Implementeringen för att kontrollera kreditkortsnummer ser ut så här:

private static bool ValidateCardNumber(string cardNumber)
{
try
{
// Array to contain individual numbers
System.Collections.ArrayList CheckNumbers = new ArrayList();
// So, get length of card
int CardLength = cardNumber.Length;

// Double the value of alternate digits, starting with the second digit
// from the right, i.e. back to front.
// Loop through starting at the end
for (int i = CardLength - 2; i >= 0; i = i - 2)
{
// Now read the contents at each index, this
// can then be stored as an array of integers

// Double the number returned
CheckNumbers.Add(Int32.Parse(cardNumber[i].ToString()) * 2);
}

int CheckSum = 0; // Will hold the total sum of all checksum digits

// Second stage, add separate digits of all products
for (int iCount = 0; iCount <= CheckNumbers.Count - 1; iCount++)
{
int _count = 0; // will hold the sum of the digits

// determine if current number has more than one digit
if ((int)CheckNumbers[iCount] > 9)
{
int _numLength = ((int)CheckNumbers[iCount]).ToString().Length;
// add count to each digit
for (int x = 0; x < _numLength; x++)
{
_count = _count + Int32.Parse(
((int)CheckNumbers[iCount]).ToString()[x].ToString());
}
}
else
{
// single digit, just add it by itself
_count = (int)CheckNumbers[iCount];
}
CheckSum = CheckSum + _count; // add sum to the total sum
}
// Stage 3, add the unaffected digits
// Add all the digits that we didn't double still starting from the
// right but this time we'll start from the rightmost number with
// alternating digits
int OriginalSum = 0;
for (int y = CardLength - 1; y >= 0; y = y - 2)
{
OriginalSum = OriginalSum + Int32.Parse(cardNumber[y].ToString());
}

// Perform the final calculation, if the sum Mod 10 results in 0 then
// it's valid, otherwise its false.
return (((OriginalSum + CheckSum) % 10) == 0);
}
catch
{
return false;
}
}
By Jesper Lind

Integrera W3C Validation Service i dina projekt

När man tillhandahåller ett CMS-system åt en kund så är det nästan oundvikligt att kunderna kommer att skriva in ogiltig XHTML. Alla kan helt enkelt inte vara webbstandard-freaks och det är inget man kan kräva av någon.

Då skulle det vara perfekt om man kunde få en varning så fort ogiltig kod har publiserats, så man kan gå in och rätta till den.

Blitz Labs har gjort en sådan lösning och delar med sig av .NET-koden. Funktionen tar emot en url som ska kontrolleras. Skickar ett anrop till W3C Markup Validation Service. Tar emot resultatet genom deras SOAP-tjänst och transformerar det till RSS med hjälp av en XSLT-fil.

Prenumenera sedan på den RSS som skapats och få meddelande så fort någon ogiltig kod har publiserats.

By Jesper Lind

Debugga AJAX med Nikhil's Web Development Helper

Att programmera AJAX är roligt, men när något går fel är det inte lika kul längre. Den vanliga tracern i .NET går ju inte att köra samtidigt. Sökte lite efter tips hur man kunde felsöka lättare och hittade ScottGu's inlägg om Nikhil's Web Development Helper.

Det handlar om ett suveränt tillägg till Internet Explorer som är till stor hjälp när man felsöker Ajax-applikationer. Läs mer om det hos Nikhil och kolla även in dokumentationen (PDF).

By Jesper Lind

Lista över världens länder med .NET

Eftersom lokaliserings-funktionerna i ASP.NET är baserade på kulturer (MSDN:CultureInfo) kan det vara lite knepigt att generera en lista över alla världens länder.

Om man även vill ha dem i bokstavsordning får man se till att sortera dem själv. Jag la till alla länderna som nycklar i en SortedList, eftersom denna typ av datasamling automatiskt sorteras efter värdet i nycklarna.

Jag visar länderna och deras landskoder i en DropDownList som läggs till på .aspx-sidan.

<asp:DropDownList id="ddlCountry" DataValueField="CountryCode" DataTextField="CountryName" style="width:200px;" runat="server" />

I kodfilens Page_Load-event binder jag denna DropDown till en DataView.

void Page_Load(object Source, EventArgs E)
{
if((!Page.IsPostBack)
{
ddlCountry.DataSource = GetAllCountriesDataView();
ddlCountry.DataBind();
}
}

Funktionen för att skapa datakällan ser ut så här:

protected DataView GetAllCountriesDataView()
{
DataTable objTable = new DataTable("Countries");
objTable.Columns.Add("CountryName", typeof(string));
objTable.Columns.Add("CountryCode", typeof(string));

DataRow objDataRowTitle = objTable.NewRow();
objDataRowTitle["CountryCode"] = 0;
objDataRowTitle["CountryName"] = "Välj land";
objTable.Rows.InsertAt(objDataRowTitle, 0);

//Skapa en SortedList för att spara alla länderna med tillhörande landskoder
SortedList slAddedCountries = new SortedList();

//Skapa en array av CultureInfo från alla kulturer
//Vi använder SpecificCultures men NeutralCultures är ett annat alternativ
CultureInfo[] arrayAllCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);

try
{
foreach (CultureInfo ci in arrayAllCultures)
{

string country = new RegionInfo(ci.LCID).EnglishName;
string countryCode = ci.Name.Substring(3, 2);

if (!slAddedCountries.ContainsKey(country))
{
//Spara landets namn som nyckel och landskoden som värde
slAddedCountries.Add(country, countryCode);
}
}

//Lägg till länder som saknas
//Cypern
slAddedCountries.Add("Cyprus", "CY");

//Loopa igenom SortedList och vi får länderna i bokstavsordning
foreach (DictionaryEntry de in slAddedCountries)
{
Trace.Write("-"+de.Key);
DataRow objDataRow = objTable.NewRow();
objDataRow["CountryCode"] = de.Value;
objDataRow["CountryName"] = de.Key;
objTable.Rows.Add(objDataRow);
}
}
catch (Exception objException)
{
Trace.Warn("BindCultureSelector() Fel!", objException.Message);
}
finally
{

}

return objTable.DefaultView;
}

Här kommer ett del av den html-kod som skapas med de länder som ligger runt Sverige (i bokstavsordning alltså).

<option value="ZA">South Africa</option>
<option value="ES">Spain</option>
<option value="SE">Sweden</option>
<option value="CH">Switzerland</option>
<option value="-S">Syria</option>
<option value="TW">Taiwan</option>
<option value="TH">Thailand</option>
<option value="TT">Trinidad and Tobago</option>

Inte helt självklart alltså. Det går säkert att göra på något bättre sätt men detta är den bästa lösning jag kommit på. Annars kan man ju prova med en fristående databas över länder, t ex från GeoNames.

Uppdatering: Märkte att Cypern inte är med i ramverket av någon anledning. Konstigt eftersom landet blev självständigt 1960. Jag har lagt till en extra rad i koden som lägger till Cypern manuellt.

By Jesper Lind

Skapa xml från filnamn i en mapp

Vi har tidigare visat ett koncept på hur man kan göra ett bildspel i flash med bildnamn som laddas från en xml-fil. Det är ett bra sätt så att man ska slippa att gå in i flash-filen och ändra varje gång man vill byta ut bilderna.

Vi använder den rätt ofta i våra egna projekt och hittills har vi sparat meta-data för bilderna i en databas. Det har sina fördelar ifall man vill spara bildtexter eller liknande.

Men det kan bli rätt omständigt att först göra en editor för att hantera bilduppladdingen. Därför skrev jag ett litet kodstycke i C# som kollar efter bilder i en angiven mapp, och skapar en xml-fil baserat på filnamnen.

I exemplet används två variabler som är sökvägar till xml- och bildmappar. Dessa läses i vårat fall in från Web.Config

//Här finns våra xml-filer
public static string strXmlRootPath = ConfigurationManager.AppSettings["XmlRootPath"];

//Här lagrar vi bilderna
public static string strMediaRootPath = ConfigurationManager.AppSettings["MediaRootPath"];

Funktionerna som skapar xml-filen

private void CreateSlideshow()
{
string strXMLPath = strXmlRootPath + "slideshow.xml";

if(File.Exists(strXMLPath))
{
DateTime dtLastWriteTime = Convert.ToDateTime( ""+File.GetLastWriteTime(strXMLPath) );
//Skapar bara en ny fil en gång varje timme
if( DateTime.Compare(DateTime.Now,dtLastWriteTime.AddMinutes(60))==1 )
{
CreateSlideshowXml();
}
}
else
{
CreateSlideshowXml();
}
}

public void CreateSlideshowXml()
{
try
{
string strXMLPath = strXmlRootPath + "slideshow.xml";
FileStream objFileStream = new FileStream(strXMLPath, FileMode.Create);
XmlTextWriter objXmlWriter = new XmlTextWriter(objFileStream, System.Text.Encoding.GetEncoding("utf-8"));//iso-8859-1
objXmlWriter.Formatting = Formatting.Indented;
objXmlWriter.WriteStartDocument();
objXmlWriter.WriteStartElement("images");

//Skapa array av alla filer i mappen för slideshow-bilder
String[] strFiles = Directory.GetFiles(strMediaRootPath + "slides/", "*");
string strFileName = string.Empty;
string strFullPath = string.Empty;

foreach (string strFile in strFiles)
{
strFileName = Path.GetFileName(strFile);
strFullPath = Path.GetFullPath(strFile);

objXmlWriter.WriteStartElement("pic");
//Url till bild
objXmlWriter.WriteStartElement("image");
objXmlWriter.WriteString(strMediaRootUrl + "slides/" + strFileName);
objXmlWriter.WriteEndElement();//Slut image
//Länk
objXmlWriter.WriteStartElement("link");
//Denna gång lämnar vi -link- tomt. Här kan man spara en url om bilderna ska vara klickbara
objXmlWriter.WriteEndElement();//Slut link

objXmlWriter.WriteEndElement();//Slut pic
}


objXmlWriter.WriteEndElement();//Slut images
objXmlWriter.WriteEndDocument();
objXmlWriter.Close();
}
catch (Exception objException)
{
Trace.Warn("XML-fil för slideshow kunde ej sparas! Fel: ",objException.Message);
}
}

Och slutligen spottas xml-filen ut som följer den här strukturen:

<?xml version="1.0" encoding="utf-8"?>
<images>
<pic>
<image>http://localhost:1643/test-site/media/slides/IMG_4954.jpg</image>
<link />
</pic>
<pic>
<image>http://localhost:1643/test-site/media/slides/IMG_4955.jpg</image>
<link />
</pic>
<pic>
<image>http://localhost:1643/test-site/media/slides/IMG_4956.jpg</image>
<link />
</pic>
<pic>
<image>http://localhost:1643/test-site/media/slides/IMG_4960.jpg</image>
<link />
</pic>
<pic>
<image>http://localhost:1643/test-site/media/slides/IMG_4961.jpg</image>
<link />
</pic>
</images>

 

By Jesper Lind