<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christoph Bünte &nbsp;&raquo; Software Entwicklung Berlin</title>
	<atom:link href="http://www.christophbuente.de/tag/web-entwicklung/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.christophbuente.de</link>
	<description>Software Entwicklung</description>
	<lastBuildDate>Tue, 07 Dec 2010 11:30:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.5</generator>
		<item>
		<title>Ruby on Rails auf Mac OS X &#8211; 5 Minuten Kurzanleitung</title>
		<link>http://www.christophbuente.de/2007-11-25-ruby-on-rails-auf-mac-os-x-5-minuten-kurzanleitung/</link>
		<comments>http://www.christophbuente.de/2007-11-25-ruby-on-rails-auf-mac-os-x-5-minuten-kurzanleitung/#comments</comments>
		<pubDate>Sun, 25 Nov 2007 13:33:25 +0000</pubDate>
		<dc:creator>Christoph Bünte</dc:creator>
				<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[anleitung]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[kurzanleitung]]></category>
		<category><![CDATA[locomotive]]></category>
		<category><![CDATA[mac os x]]></category>
		<category><![CDATA[mac user]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[software development]]></category>
		<category><![CDATA[Software Entwicklung]]></category>
		<category><![CDATA[tutorium]]></category>
		<category><![CDATA[web development]]></category>
		<category><![CDATA[Web Entwicklung]]></category>

		<guid isPermaLink="false">http://www.christophbuente.de/2007-11-25-ruby-on-rails-auf-mac-os-x-5-minuten-kurzanleitung/</guid>
		<description><![CDATA[Ruby on Rails isst in aller Munde, wenn es um Webentwicklung geht. Ich werde in Zukunft eine Reihe von Artikeln veröffentlichen, die etwas tiefer in die Materie eindringen. Dazu ist es aber nötig, dass jeder Leser weiss, wie man Ruby on Rails auf seinem Rechner zum laufen bekommt und ein Projekt erzeugt. Aus diesem Grund [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://rubyonrails.com/" title="Webentwicklung, die nicht weh tut">Ruby on Rails</a> isst in aller Munde, wenn es um Webentwicklung geht. Ich werde in Zukunft eine Reihe von Artikeln veröffentlichen, die etwas tiefer in die Materie eindringen. Dazu ist es aber nötig, dass jeder Leser weiss, wie man Ruby on Rails auf seinem Rechner zum laufen bekommt und ein Projekt erzeugt. Aus diesem Grund hier eine 5 Minuten Kurzanleitung. Als Mac User werde speziell auf das Betriebsystem <a href="http://www.apple.com/de/macosx/">Mac OS X</a> eingehen.</p>
<p><span id="more-22"></span></p>
<p>Um mit Rails auf dem Mac zu beginnen, gibt es zwei Möglichkeiten:</p>
<ol>
<li> 		<a href="http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx">Kompilieren und installieren aller benötigten Komponenten</a></li>
<li> 		Benutzen einer Applikation, die schon alles mitbringt.</li>
</ol>
<p>Getreu dem Motto dieses Artikels, empfehle ich den schnellen, einfachen Weg <img src='http://www.christophbuente.de/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  <a href="http://locomotive.raaum.org/" title="Ryan Rauum">Ryan Rauum</a> stellt auf seiner Website eine Cocoa Anwendung zur Verfügung, die bereits alle Komponenten &#8211; bis auf die Datenbank &#8211; in einer Anwendung bündelt. Die Installation ist so einfach, wie bei jeder anderen Anwendung.</p>
<ul>
<li> 		<a href="http://locomotive.raaum.org" title="Locomotive herunterladen">Herunterladen</a></li>
<li> 		DMG Datei doppelt anklicken</li>
<li> 		Locomotive Symbol in das Programmverzeichnis ziehen</li>
</ul>
<p>Als Datenbank empfehle ich für die ersten Schritte <a href="http://www.mysql.com">MySQL</a>. Über den Produktiveinsatz von MySQL mag man streiten, aber um einfach mal anzufangen reicht MySQL völlig aus. Die Dateien für sehr viele Betriebsysteme stehen auf der <a href="http://dev.mysql.com/downloads/mysql/5.1.html">download Seite</a> bereit. Die Installation besteht eigentlich nur noch aus dem entpacken des tar.gz Archivs und starten der darin enthaltenen .pkg Datei. Eine <a href="http://dev.mysql.com/doc/refman/5.1/de/mac-os-x-installation.html">Anleitung</a> hat MySQL auch online gestellt.</p>
<p>Nachdem die Datenbank nun läuft ist es Zeit Locomotive zu starten. In der Applikation kann nun ein beliebiges Projekt erstellt werden (Apfel + N). Dazu gibt man den Namen und Pfad an, in dem das neue Projekt erstellt werden soll. Wer glücklicher Besitzer einer <a href="http://macromates.com/">Textmate</a> Lizens ist, klickt rechts auf das erstellt Projekt im Locomotive Fenster und wählt: &#8220;Edit in Textmate&#8221;. Es geht aber auch jeder andere Editor, um die wenigen Schritte zum laufenden Projekt zu gehen.</p>
<p>Die Datei <code>config/database.yml</code> im Projektverzeichnis enthält die Angaben, um Rails mit der Datenbank kommunizieren zu lassen. Hier sind in der bei der Verwendung von MySQL lediglich die Datenbanknamen für development, test und production anzugeben. Bei Bedarf kann hier auch der Benutzer, Passwort und Host bzw. Socket angepasst werden. Der schnellste Weg zu den drei Datenbanken ist mit Hilfe des Terminals:</p>
<pre lang="bash">mysql -uUser -pPasswort</pre>
<p>In der geöffneten mysql shell sind die drei create Anweisungen nötig</p>
<pre lang="sql">
create database projektname_development;
create database projektname_test;
create database projektname_production;</pre>
<p>So, das war es eigentlich schon. Nachdem mit Hilfe von Locomotive der Server für das Projekt gestartet wurde, sollte man im Browser unter der Adresse: <code>http://localhost:3000</code> eine Erfolgsmeldung sehen:</p>
<p><em>Welcome aboard. You’re riding the Rails!</em></p>
<div id="crp_related"><h3>Ähnliche Beiträge:</h3><ul><li><a href="http://www.christophbuente.de/2008-07-30-gepatchte-mysql-version-nuetzliche-features-schluesselfertig-eingebaut/" rel="bookmark" class="crp_title">Gepatchte MySQL Version &#8211; nützliche Features schlüsselfertig eingebaut</a></li><li><a href="http://www.christophbuente.de/2008-07-01-mysql-myisam-index-oder-nicht/" rel="bookmark" class="crp_title">MySQL MyISAM &#8211; Index oder nicht?</a></li><li><a href="http://www.christophbuente.de/2010-07-20-rails-hosting-bei-rocket-rentals-auch-mit-staging-umgebung/" rel="bookmark" class="crp_title">Rails Hosting bei Rocket Rentals &#8211; auch mit Staging Umgebung</a></li><li><a href="http://www.christophbuente.de/2007-11-24-advancing-rails-ein-workshop-mit-david-a-black/" rel="bookmark" class="crp_title">Advancing Rails &#8211; Ein Workshop mit David A. Black</a></li><li><a href="http://www.christophbuente.de/2007-10-05-frei-nehmen-um-zu-lernen/" rel="bookmark" class="crp_title">Rails tutorial &#8211;  Eine Woche lang Agile Webentwicklung</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://www.christophbuente.de/2007-11-25-ruby-on-rails-auf-mac-os-x-5-minuten-kurzanleitung/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Advancing Rails &#8211; Ein Workshop mit David A. Black</title>
		<link>http://www.christophbuente.de/2007-11-24-advancing-rails-ein-workshop-mit-david-a-black/</link>
		<comments>http://www.christophbuente.de/2007-11-24-advancing-rails-ein-workshop-mit-david-a-black/#comments</comments>
		<pubDate>Sat, 24 Nov 2007 07:49:40 +0000</pubDate>
		<dc:creator>Christoph Bünte</dc:creator>
				<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Software Entwicklung]]></category>
		<category><![CDATA[david black]]></category>
		<category><![CDATA[railsschulung]]></category>
		<category><![CDATA[selenium]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[Web Entwicklung]]></category>

		<guid isPermaLink="false">http://www.christophbuente.de/2007-11-24-advancing-rails-ein-workshop-mit-david-a-black/</guid>
		<description><![CDATA[&#8220;Wer rastet, der rostet.&#8221; &#8211; So geht es auch Software-Entwicklern. Wer sich nicht ständig nach neuem umschaut und den imaginären Werkzeugkasten mit neuen Tools füllt, wird irgendwann selbst zum alten Eisen gehören. Deswegen habe ich schon vor einiger Zeit angefangen, Ruby on Rails in meine Repertoire aufzunehmen. Diese Woche habe ich in einem 4 Tage [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.flickr.com/photos/berlinchrizzle/2056227725/" title="IMG_5150-2 by chrizzle my nizzle, on Flickr"><img src="http://farm3.static.flickr.com/2350/2056227725_6a2bff9597_o.jpg" style="padding-right: 10px; height: 180px; float: left" alt="IMG_5150-2" /></a></p>
<p><i>&#8220;Wer rastet, der rostet.&#8221;</i> &#8211; So geht es auch Software-Entwicklern. Wer sich nicht ständig nach neuem umschaut und den imaginären Werkzeugkasten mit neuen Tools füllt, wird irgendwann selbst zum alten Eisen gehören. Deswegen habe ich schon vor einiger Zeit angefangen, Ruby on Rails in meine Repertoire aufzunehmen. Diese Woche habe ich in einem <a href="http://dablog.rubypal.com/2007/10/19/advancing-with-rails-training-in-berlin-nov-19-22">4 Tage Workshop</a> mein Wissen über Ruby und Rails weiter ausbauen können.</p>
<p><span id="more-21"></span></p>
<p>[ad#vert-banner]</p>
<p><a href="http://dablog.rubypal.com/">David A. Black</a>, Autor des Buches <a href="http://rubypal.org/ruby_for_rails">&#8220;Ruby for Rails&#8221;</a> ist für diesen Workshop aus New Jersey, USA angereist. Neben einem ganzen Tag für ActiveRecord und REST, wurden auch Themen wie &#8220;routing, testing, caching, deploy, plugins, rjs und javascript behandelt. Das Ganze war gespickt mit hunderten von ad hoc Beispielen, die David in seiner ganz eigenen Art in die Konsole oder den irb hackt.</p>
<p>Derartig tiefgreifende Kenntnis über eine bestimmte Thematik ist mir bisher noch nicht begegnet. Klar, arbeit man hier und da mit Leuten zusammen, die deutlich fitter sind als man selbst. Doch David &#8220;lebt&#8221; Ruby seit Jahren.</p>
<p>Um hier nicht nur meine Freude für Ruby und Rails zum Ausdruck zu bringen, sondern vielleicht auch ein nützliches Detail zu liefern, möchte ich das <a href="http://www.openqa.org/selenium/">Test Framework Selenium</a> erwähnen. Damit ist es möglich über die hoffentlich bereits geschriebenen Unit-, Functional- und Integrationtests hinaus, die Anwendung in einem echten Browser zu testen. Damit kann der Kunde sehr schnell sehen, was geht und was nicht. Und was noch viel wichtiger ist: wie es aussieht in verschiedenen Browsern aussieht.</p>
<p>Die Installation ist schnell und einfach. Im Hauptverzeichnis des Rails Projektes installiert wird das Plugin mit Hilfe des folgenden Kommandozeilen Befehls installiert:</p>
<pre lang="bash"> ruby script/plugin install selenium</pre>
<p>Anschließend generiert man eine Testdatei im Ordner <code>test/selenium</code> mit folgendem Befehl:</p>
<pre lang="bash"> ruby script/generate selenium login.rse</pre>
<p>Der generierte Testfall muss nun noch editiert werden, um die Applikation sinnvoll zu testen. Dabei können sämtliche Benutzerinteraktionen in lesbarer Form geskriptet werden. Die <a href="http://release.openqa.org/selenium-core/0.8.0/reference.html">Kommandoreferenz</a> findet man hier. Sollte die Anwendung bisher noch nicht im Testmodus verwendet worden sein, ist es nötig, die Testdatenbank auf den neuesten Stand zu bringen:</p>
<pre lang="bash"> rake db:migrate RAILS_ENV=test</pre>
<p>Damit die Tests auch durchführbar sind, muss der Server im Testmodus gestartet werden:</p>
<pre lang="bash"> ruby script/server -e test</pre>
<p>Ruft man jetzt die Rails Anwendung in einem beliebigen Browser auf (http://localhost:3000/selenium) sieht man das Admininterface von Selenium. Hier lässt sich einstellen, welche Tests laufen sollen. Darüber hinaus lassen sich noch die Geschwindigkeit und Highlighting variieren. Ein Klick auf den &#8220;Play-Button&#8221; im oberen rechten Fenster lässt die Show beginnen. Eine <a href="http://www.openqa.org/selenium-core/usage.html">ausführliche Anleitung</a> zur Benutzung des Interface findet man ebenfalls auf der selenium Webseite. Nach belieben können weitere Tests generiert werden und erscheinen dann automatisch im linken Fenster neben dem bereits erstellen login test.</p>
<p>Abschließend möchte ich <a href="http://www.railsschulung.de/" title="railsschulungen in Berlin">Benjamin und Mathias</a> danken, dass sie David nach Berlin gebracht haben. Es war spitze!</p>
<div id="crp_related"><h3>Ähnliche Beiträge:</h3><ul><li><a href="http://www.christophbuente.de/2007-11-27-kostenloses-ruby-on-rails-buch-nur-noch-4-tage/" rel="bookmark" class="crp_title">Kostenloses Ruby on Rails Buch &#8211;  Nur noch 4 Tage</a></li><li><a href="http://www.christophbuente.de/2009-05-16-remarkable-rails-anwendungen-automatisch-testen/" rel="bookmark" class="crp_title">Remarkable &#8211; Rails Anwendungen automatisch testen</a></li><li><a href="http://www.christophbuente.de/2007-11-25-ruby-on-rails-auf-mac-os-x-5-minuten-kurzanleitung/" rel="bookmark" class="crp_title">Ruby on Rails auf Mac OS X &#8211; 5 Minuten Kurzanleitung</a></li><li><a href="http://www.christophbuente.de/2007-12-31-2008/" rel="bookmark" class="crp_title">2008</a></li><li><a href="http://www.christophbuente.de/2007-10-05-frei-nehmen-um-zu-lernen/" rel="bookmark" class="crp_title">Rails tutorial &#8211;  Eine Woche lang Agile Webentwicklung</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://www.christophbuente.de/2007-11-24-advancing-rails-ein-workshop-mit-david-a-black/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paypal subscriptions &#8211; Abonnements in Java integrieren</title>
		<link>http://www.christophbuente.de/2007-11-10-paypal-subscriptions-abonnements-in-java-integrieren/</link>
		<comments>http://www.christophbuente.de/2007-11-10-paypal-subscriptions-abonnements-in-java-integrieren/#comments</comments>
		<pubDate>Sat, 10 Nov 2007 14:48:10 +0000</pubDate>
		<dc:creator>Christoph Bünte</dc:creator>
				<category><![CDATA[Web Entwicklung]]></category>
		<category><![CDATA[abonnement]]></category>
		<category><![CDATA[anleitung]]></category>
		<category><![CDATA[buttonfactory]]></category>
		<category><![CDATA[exclude]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JEE]]></category>
		<category><![CDATA[paypal]]></category>
		<category><![CDATA[paypal subschription]]></category>
		<category><![CDATA[sandbox]]></category>
		<category><![CDATA[Software Entwicklung]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[zahlungsabwicklung]]></category>

		<guid isPermaLink="false">http://www.christophbuente.de/2007-11-10-paypal-subscriptions-abonnements-in-java-integrieren/</guid>
		<description><![CDATA[Für einen Kunden habe ich in der letzten Woche die Zahlungsabwicklung von Abonnements über Paypal integriert. Der Web Nutzer kann auf diesem einfachen Wege die Vorzüge vom Premiumdiensten nutzen. Der Vorteil hierbei ist: Der potentielle Käufer muss seine Kreditkarten- bzw. Kontodaten nicht an eine &#8220;nicht vertrauenswürdige&#8221; Seite weitergeben, sondern nutzt die bereits bei Paypal hinterlegten [...]]]></description>
			<content:encoded><![CDATA[<p>Für einen Kunden habe ich in der letzten Woche die Zahlungsabwicklung von Abonnements über <a href="http://www.paypal.de">Paypal</a> integriert. Der Web Nutzer kann auf diesem einfachen Wege die Vorzüge vom Premiumdiensten nutzen. Der Vorteil hierbei ist: Der potentielle Käufer muss seine Kreditkarten- bzw. Kontodaten nicht an eine &#8220;nicht vertrauenswürdige&#8221; Seite weitergeben, sondern nutzt die bereits bei <a href="http://www.paypal.de">Paypal</a> hinterlegten Daten. Ist der Käufer noch keine <a href="http://www.paypal.de">Paypal</a> Kunde, ist eine vorige Anmeldung allerdings notwendig.</p>
<p><span id="more-18"></span></p>
<p>Der grundlegende Mechanismus für Subscriptions ist denkbar einfach. An einer beliebigen Stelle in unserer Webapplikation wird der Benutzer per link oder submit button an die Paypal Seite geleitet. Alle relevanten Daten für die Zahlung werden entweder als GET Parameter (Link) oder POST Parameter (Button) übertragen. Dazu zählen vor allem folgende Angaben:</p>
<ul>
<li>Abonnementinterval (1 Tag, 4 Wochen oder 12 Monate)</li>
<li>Betrag pro Abonnementinterval</li>
<li>Währung</li>
<li>Paypal Id des Verkäufers</li>
<li>Rechnungsnummer</li>
<li>Automatische Verlängerung (ja/nein)</li>
<li>Beliebige zusätzliche Informationen</li>
</ul>
<p>Sollte der Benutzer noch kein Paypal Kunde sein, muss er sich bei Paypal erst registrieren. Um dies so einfach wie möglich zu gestalten können wir zusätzlich Benutzerinformationen mitsenden, die Paypal in das Registrierungsformular einfügt.</p>
<ul>
<li>Name, Nachname</li>
<li>Adress(Strasse, <abbr title="Postleitzahl">PLZ</abbr>, Stadt, Land)</li>
<li>Email Adresse</li>
</ul>
<p>Damit wir zum Testen unserer Applikation kein Vermögen hinblättern müssen, stellt Paypal eine sogenannte Sandbox zur Verfügung. Eine eigene <a href="https://developer.paypal.com">Developer Sandbox</a> kann man sich hier einrichten. Diese <a href="https://sandbox.paypal.com">Sandbox</a> stellt die kompletten Funktionen von Paypal zur Verfügung. Mit zwei Unterschieden: Es fließt kein echtes Geld und es gibt nur die Benutzer, die wir selbst angelegt haben. Um also einen Zahlungsfluss zu simulieren müssen in der Sandbox mindestens zwei Benutzer angelegt sein (Verkäufer und Käufer). Zum Erzeugen eines einfachen Bezahlknopfes  eignet sich die Paypal Button Factory, welche unter <a href="https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_merchant&amp;nav=3">Merchant Services</a> im Paypal Menü des Verkäufers erreichbar ist. Doch es gibt auch Gründe gegen einen statischen Bezahlknopf auf der Webseite. Angenommen wir wollen mehrere Abonnements verkaufen, die sich je nach Laufzeit im Preis unterscheiden. Wäre es da nicht besser ein eigenes Formular zu erstellen, welches dem Benutzer die komplette Auswahl unserer Produkte anzeigt? Nachdem sich der Benutzer für eines unserer Abonnements entschieden hat, überträgt er seine Auswahl per druck auf den Bezahlknopf. Unsere Webapplikation wertet diese Informationen aus und leitet den Benutzer per redirect auf die Paypal Seite um. Dazu muss die <acronym title="Uniform Resource Locator" lang="en">URL</acronym> mit sämtlichen Zahlungsinformationen dynamisch erzeugt werden. Hier eine Beispielimplementierung:</p>
<pre lang="java">public String getPurchaseUrl() throws EncoderException {
	StringBuffer urlBuffer = new StringBuffer("https://www.sandbox.paypal.com/cgi-bin/webscr");
	urlBuffer.append("?no_note=1"); // Abonnements unterstützen keine Notizen
	urlBuffer.append("&amp;a3=9.99"); // Der Preis des Abonnements
	urlBuffer.append("&amp;t3=M"); // Intervall Einheit = Monate
	urlBuffer.append("&amp;p3=1"); // Intervall Länge = 1 (Monat)
	urlBuffer.append("&amp;business=verkaeufer@deine.url.de"); // Die email adresse für den verkäufer account bei paypal
	urlBuffer.append("&amp;cmd=_xclick-subscriptions"); // Wir wollen eine abonnement
	urlBuffer.append("&amp;src=1"); // Abonnement verlängert sich automatisch
	urlBuffer.append("&amp;currency_code=EUR"); // Wir nehmen Zahlungen in Euro entgegen
	urlBuffer.append("&amp;invoice=Rechnungsnummer"); // Hier kann eine Rechnungsnummer vergeben werden
	urlBuffer.append("&amp;lc=DE"); // Die Zielseite soll in deutscher Sprache angezeigt werden
	urlBuffer.append("&amp;no_shipping=1"); // Wir brauchen keine Versandadresse, denn wir verkaufen virtuelle Güter oder Dienstleistungen
	URLCodec encoder = new URLCodec();
	return encoder.encode(urlBuffer.toString());
}</pre>
<p>Für das URL Encoding, welches alle Sonderzeichen in übertragbare Zeichen umwandelt, verwende ich die <a href="http://commons.apache.org/codec/">commons-codec Bibliothek</a> von <a href="http://commons.apache.org/">Apache Commons</a>.</p>
<p><!--adsense#vert-banner--></p>
<p>Die geladene Paypal Seite enthält nun alle Zahlungsinformationen. Der Potentielle Käufer muss sich lediglich mit seinem Paypal Benutzernamen anmelden und die Zahlung bestätigen. Ist die Automatische Rückleitung aktiviert, landet der Benutzer danach wieder auf unserer Seite. Die Aktivierung kann im Paypal Menu des Verkäufers unter &#8220;Profile -&gt;Website Payment Preferences&#8221; eingestellt werden. Dazu wählt man den Radiobutton &#8220;Auto Return ON&#8221; und trägt unter &#8220;Return URL&#8221; die entsprechende Url ein, an die der Benutzer weitergeleitet werden soll. Mit dieser URL überträgt Paypal eine Transaktions Id,  wenn in den Einstellungen zusätzlich noch &#8220;Payment Data Transfer&#8221; auf &#8220;On&#8221; gesetzt wurde. Die Dokumentation für <acronym title="Payment Data Transfer" lang="en">PDT</acronym> finden man auf der <a href="https://www.paypal.com/IntegrationCenter/ic_pdt.html">Paypal Developer Seite</a>. Paypal erwartet darauf hin, dass unsere Webapplikation folgende Informationen zurück sendet:</p>
<ul>
<li>TransaktionsId</li>
<li>Identitätstoken</li>
<li> Kommando</li>
</ul>
<p>Die TransaktionsId ist die eben empfangene Id. Der Identitätstoken kann an der Stelle eingesehen werden, wo &#8220;Payment Data Transfer&#8221; angeschaltet wurde. Dies stellt sicher, dass  Zahlungsinformationen nicht an unberechtigte Personen gesendet werden. Das Kommando muss den den Wert &#8220;_notify-synch&#8221; enthalten. Hier ein Beispiel, wie die Implementierung aussehen könnte. Für den Post Request zurück an Paypal habe ich die <a href="http://jakarta.apache.org/httpcomponents/httpclient-3.x/">commons-httpclient Bibliothek</a> von <a href="http://commons.apache.org/">Apache Commons</a> verwendet.</p>
<pre lang="java">/**
 * Verabeitet Paypals Payment Data Transfer
 *
 * @param txId The transactionId
 * @return
 */
public void handleCallback(long txId) {
	Map&lt;String,Object&gt; params = new HashMap&lt;String, Object&gt;();
	params.put("cmd","_notify_synch");
	params.put("tx", txId);
	params.put("at", "IDENTITY_TOKEN");
	try {
		PostMethod method = doPostRequest("https://www.sandbox.paypal.com/cgi-bin/webscr", params);
		String responseBody = method.getResponseBodyAsString();
		if(responseBody != null) {
			if(responseBody.startsWith("SUCCESS")) {
				// Super, alles hat geklappt
				// Erfolgsnachricht anzeigen
			} else if(responseBody.startsWith("FAIL")) {
				// Die Zahlung war nicht korrekt durchgeführt
				// Fehlernachricht anzeigen
			} else {
				// Irgendwas ging richtig in die Hose
				// Fehlerbehandlung
			}
		} else {
 			// Das war auch nicht korrekt
	 		// Fehlerbehandlung
 		}
	} catch (HttpException e) {
		// Error sending PDT response
	} catch (IOException e) {
		// Error sending PDT response
	}
}
/**
 * Does a POST request to the given url and the given parameter map
 *
 * @param url
 * @param params
 * @return The method containing the response
 * @throws HttpException
 * @throws IOException
 */
public PostMethod doPostRequest(String url, Map&lt;String, Object &gt; params)
 	throws HttpException, IOException {
		HttpClient client = new HttpClient();
		HttpConnectionManager conManager = client.getHttpConnectionManager();
		HttpClientParams clientParams = client.getParams();
		clientParams.setConnectionManagerTimeout(2000);
		HttpConnectionManagerParams managerParams = conManager.getParams();
		managerParams.setConnectionTimeout(5000);
		managerParams.setSoTimeout(5000);
		PostMethod method = new PostMethod();
		method.setURI(new URI(url, false));
		HttpMethodParams paramsM = method.getParams(); // Wiederholungen unterdrücken
		paramsM.setParameter(HttpMethodParams.RETRY_HANDLER,
	 		new DefaultHttpMethodRetryHandler(0, false));
	Set&lt;String&gt; keys = params.keySet();
	for (String key : keys) {
		String value = (String) params.get(key);
		method.addParameter(key, value); // Setze Url Parameter
	}
	client.executeMethod(method); // Http Post ausführen
	return method;
}</pre>
<p>Der Benutzer merkt vom Austausch der Zahlungsinformationen nichts, Es wird lediglich eine einfache Bestätigungsseite angezeigt. Auf dieser Seite wird dem Benutzer mitgeteilt wird, dass die Zahlung erfolgreich oder nicht erfolgreich war, und dass er von Paypal per Mail darüber benachrichtigt werden wird.</p>
<p>Paypal bietet zusätzlich die Möglichkeit, unsere Webapplikation asynchron über eingehende Zahlungen und/oder Kündigungen zu informieren. Die Mechanismus nennt sich <acronym title="Instant Payment Notification" lang="en">IPN</acronym>, was soviel bedeutet wie &#8220;sofortige Zahlungsavisierung&#8221;. Dieser Mechanismus ist deutlich robuster, als &#8220;Payment Data Transfer&#8221;. Doch dazu in später mehr.</p>
<div id="crp_related"><h3>Ähnliche Beiträge:</h3><ul><li><a href="http://www.christophbuente.de/2007-10-23-bots-aussperren-mit-captcha/" rel="bookmark" class="crp_title">captcha tutorial &#8211; Bots aussperren</a></li><li><a href="http://www.christophbuente.de/2009-10-29-opensocial-gadgets-apps-fur-studivz-selbst-entwickeln/" rel="bookmark" class="crp_title">Opensocial Gadgets &#8211; Apps für StudiVZ selbst entwickeln</a></li><li><a href="http://www.christophbuente.de/2008-01-24-stripes-framework-tests-fur-actionbeans-im-wizard-modus/" rel="bookmark" class="crp_title">Stripes Framework &#8211; Tests für ActionBeans im Wizard Modus</a></li><li><a href="http://www.christophbuente.de/2007-11-17-canoo-webtest-web-anwendungen-automatisch-testen/" rel="bookmark" class="crp_title">canoo webtest &#8211; Web-Anwendungen automatisch testen</a></li><li><a href="http://www.christophbuente.de/2009-09-09-500-internal-server-error-apache-config-vs-wp-super-cache/" rel="bookmark" class="crp_title">500 Internal Server Error &#8211; Apache config vs. WP-Super-Cache</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://www.christophbuente.de/2007-11-10-paypal-subscriptions-abonnements-in-java-integrieren/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>captcha tutorial &#8211; Bots aussperren</title>
		<link>http://www.christophbuente.de/2007-10-23-bots-aussperren-mit-captcha/</link>
		<comments>http://www.christophbuente.de/2007-10-23-bots-aussperren-mit-captcha/#comments</comments>
		<pubDate>Tue, 23 Oct 2007 07:30:55 +0000</pubDate>
		<dc:creator>Christoph Bünte</dc:creator>
				<category><![CDATA[Java JEE]]></category>
		<category><![CDATA[Software Entwicklung]]></category>
		<category><![CDATA[bot aussperren]]></category>
		<category><![CDATA[captcha]]></category>
		<category><![CDATA[integration]]></category>
		<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[jcaptcha]]></category>
		<category><![CDATA[JEE]]></category>
		<category><![CDATA[jsp]]></category>
		<category><![CDATA[session id]]></category>
		<category><![CDATA[Web Entwicklung]]></category>

		<guid isPermaLink="false">http://www.christophbuente.de/2007/10/23/bots-aussperren-mit-captcha/</guid>
		<description><![CDATA[Vergangene Woche habe ich für einen aktuellen Kunden eine Funktion implementiert, die es dem Nutzer einer Webseite erlaubt, anderen diese Seite per E-Mail zu empfehlen. Dies öffnete Spammern potentiell Tür und Tor. Dem Kunden war besonders daran gelegen, sogenannte Bots auszusperren, die diese Funktion in sehr hoher Frequenz ausführen können. [ad#vert-banner] Als Mittel der Wahl [...]]]></description>
			<content:encoded><![CDATA[<p>Vergangene Woche habe ich für einen aktuellen Kunden eine Funktion implementiert, die es dem Nutzer einer Webseite erlaubt, anderen diese Seite per E-Mail zu empfehlen. Dies öffnete Spammern potentiell Tür und Tor. Dem Kunden war besonders daran gelegen, sogenannte Bots auszusperren, die diese Funktion in sehr hoher Frequenz ausführen können.</p>
<p><span id="more-16"></span></p>
<p>[ad#vert-banner]</p>
<p>Als Mittel der Wahl hat sich seit Jahren das sogenannte <acronym title="Completely Automated Public Turing Test to tell Computer and Humans Apart" lang="en"><a href="http://de.wikipedia.org/wiki/Captcha">CAPTCHA</a></acronym> etabliert. Dabei werden Bilder erzeugt, die auf unebendem Hintergrund verzerrte Zeichen darstellen. Der Benutzer muss diese sogenannte Challenge lösen, in dem er die Zeichenfolge in ein Formularfeld eingibt. Stimmen die eingegebenen Zeichen und die Zeichen auf dem Bild übereinstimmen. Hier macht man sich zu nutze, dass der Mensch besser abstrahieren kann, und die Buchstaben trotz widriger Umstände erkennen kann, wo eine Maschine scheitert.</p>
<p>Da ich das Rad nicht neu erfinden will, habe ich mich einer Bibliothek bedient, die diese Funktionalität zur Verfügung stellt, und sehr einfach in das aktuelle Projekt einzubinden ist. Meine Wahl fiel auf <a href="http://jcaptcha.sourceforge.net/">JCaptcha</a>. Wie der Name vermuten läßt ist JCaptcha eine Java basierte Implementierung. Sie läßt sich in Java basierte Webprojekte sehr einfach integrieren.</p>
<p>Folgende Schritte sind notwendig:</p>
<ol>
<li>Implementierung eines eigenen Servlets, welches mit der Auslieferung der generierten Bilder beauftragt wird.</li>
<li> Eintrag des Servlets und des Mappings in die web.xml</li>
<li> Implementierung eines Singleton, welches den CaptchaService zur Verfügung stellt.</li>
<li>Erstellen einer validierungsmethode zum Überprüfen der Benutzereingabe.</li>
</ol>
<p>Der generelle Ablauf ist recht einfach. Das in der JSP mit Hilfe des &lt;img src=&#8221;img.captcha&#8221;&gt; referenzierte Bild wird von unserem Servlet bedient. Dieses erzeugt mit Hilfe des CaptchaService ein neues Bild und gibt es als jpg zurück.  Der CaptchaService speichert in einer internen Map unter dem Schlüssel der jeweiligen session id das captcha response. Die Methode validateResponseForID des CaptchaService prüft, ob der eingegebene Text mit dem Bild übereinstimmt.</p>
<p>Das Servlet wird wie folgt implementiert:</p>
<pre lang="java">
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.octo.captcha.service.CaptchaServiceException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
 * @author Christoph Bünte
 *
 * @version $Revision: 1.5 $
 *
 * @created 11.10.2007
 *
 */
public class CaptchaServlet extends HttpServlet {

/**
  * The serial id for this class
  */
 private static final long serialVersionUID = -5555311841435084305L;

@Override
 public void init(ServletConfig servletConfig) throws ServletException {

super.init(servletConfig);
}

/**
  * Generiert das Captcha image in form eines bytearrays
  *
  * @param token
  *            SessionId
  * @param loc
  *            Locale Object für die Sprache des Benutzers
  * @return
  */
 private synchronized byte[] getCaptchaChallenge(String token, Locale loc) {

byte[] captchaChallenge = null;

try {
 		// create the captcha challenge
 		BufferedImage challenge = CaptchaServiceSingleton.getInstance()
 				.getImageChallengeForID(token, loc);

		// transform image data into jpeg byte array
 		ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();

		JPEGImageEncoder jpegEncoder = JPEGCodec
 				.createJPEGEncoder(jpegOutputStream);

		jpegEncoder.encode(challenge);

		captchaChallenge = jpegOutputStream.toByteArray();

 	} catch (IOException e) {
 		//TODO: Fehlerbehandlung
 	} catch (CaptchaServiceException e) {
 		//TODO: Fehlerbehandlung
 	}

	return captchaChallenge;

 }

@Override
 protected void doGet(HttpServletRequest request,

 		HttpServletResponse response) throws ServletException, IOException {

			// Hier benutzen wir die session id und das Locale Objekt des requests
			String token = request.getSession().getId();
 			Locale loc = request.getLocale();

			// hole das CAPTCHA challenge als JPEG
			byte[] captchaAsJpeg = getCaptchaChallenge(token, loc);

			// falls nicht verfügbar, sende http code 404 (resource not available)
			if (captchaAsJpeg == null) {
				response.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
			}

			// gibt ein response Objekt mit dem jpeg bild zurück
			// (für den browser: benutze keinen cache und verwirf den inhalt sofort)
			// set header

			response.setHeader("Cache-Control", "no-store");
			response.setHeader("Pragma", "no-cache");
			response.setDateHeader("Expires", 0);
			response.setContentType("image/jpeg");

			// sende bilddaten
			ServletOutputStream responseOutputStream = response.getOutputStream();
			responseOutputStream.write(captchaAsJpeg);
			responseOutputStream.flush();
			responseOutputStream.close();
 		}

}</pre>
<p>Der Eintrag in die web.xml sieht so aus:</p>
<pre class="xml">
&lt;servlet&gt;
	&lt;servlet-name&gt;jcaptcha&lt;/servlet-name&gt;
	&lt;display-name&gt;JCaptcha&lt;/display-name&gt;
	&lt;servlet-class&gt;your.package.path.CaptchaServlet&lt;/servlet-class&gt;
	&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;</pre>
<pre class="xml">&lt;servlet-mapping&gt;
	&lt;servlet-name&gt;jcaptcha&lt;/servlet-name&gt;
	&lt;url-pattern&gt;*.captcha&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<pre class="xml"></pre>
<p>Das CaptchaService als Singleton implementiert:</p>
<pre lang="java">
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.service.captchastore.CaptchaStore;
import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;</pre>
<pre lang="java">/**
 *
 * @author Christoph Bünte
 *
 * @version $Revision: 1.6 $
 *
 * @created 11.10.2007
 *
 */
public class CaptchaServiceSingleton {

	private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();

	/**
	  * Gibt die einzige Instanz einnes CaptchaService zurück.
	  *
	  * @return
	  */
	 public static ImageCaptchaService getInstance() {

		return instance;
	 }
}</pre>
<p>Im Formular für die Eingabe der Captcha Response, wird das Bild wie folgt eingebunden:</p>
<pre class="html">
&lt;img src="img.captcha"  class="captcha"&gt;</pre>
<p>Je nachdem, welches Webframework man verwendet, differiert die Implementierung einer Validierungsmethode. Letztlich könnte sie wie folgt aussehen. Es ist ein Beispiel, wie es im <a href="http://mc4j.org/confluence/display/stripes/Home">stripes framework</a> verwendet wird.</p>
<pre lang="java">/**
 * Validating capture input
 *
 * @param errors
 */
@ValidationMethod(on = { "sendRecommendation" }, when = ValidationState.ALWAYS)

public void validateCaptcha(ValidationErrors errors) {
 try {
 	String sessionId = context.getRequest().getSession().getId();
 	if (!CaptchaServiceSingleton.getInstance().validateResponseForID(
 			sessionId, captchaResponse)) {
 		errors.add("captchaResponse", new LocalizableError(
 				"captchaResponse.invalidValue"));
 	}
 } catch (CaptchaServiceException e) {
 	// ungültige Session id, Fehlerbehandlung
 }
}</pre>
<p>Das Ergebnis ist schon ganz ansehnlich. Im Formular wird mit der Default Engine ein mehr oder weniger schönes Captcha Bild erzeugt. Jedoch fällt Ästheten sofort auf, dass das Bild überhaupt nicht in das Design der Seite passt. Um solche Anforderungen zu erfüllen, implementiert man am besten eine CaptchaEngine. Dazu müssen wir die Singleton Klasse noch etwas modifizieren, doch das schnell erledigt. Wird sind doch agil, oder?</p>
<p>Die unten gezeigte Engine erzeugt ein Captcha Bild mit hellblauem Hintergrund und grauer und schwarzer Schrift. Für die Schrift werden nur Großbuchstaben verwendet. Man hätte als Alternative auch die Informationen zum Rendern des Bildes auch direkt in der Singleton Klasse zu einer vorhandenen Engine hinzufügen können. Unangenehmer Nebeneffekt ist aber, dass es immer eine Default engine gibt. Beim Rendern der Bilder wird dann eine verfügbare Engine zufällig ausgewählt, so dass in unregelmässigen Abständen eine Bild mit grün-rot-gelb gesprenkeltem Hintergrund erscheint. Dieses, für mich anfänglich, merkwürde Verhalten hat mich zwei Stunden aufgehalten. Wer unterschiedliche Captcha Bilder erzeugen möchte implenentiert entsprechend mehr Klassen. Hier ein Beispiel:</p>
<pre lang="java">
import java.awt.Color;import com.octo.captcha.component.image.backgroundgenerator.BackgroundGenerator;
import com.octo.captcha.component.image.backgroundgenerator.UniColorBackgroundGenerator;
import com.octo.captcha.component.image.fontgenerator.DeformedRandomFontGenerator;
import com.octo.captcha.component.image.fontgenerator.FontGenerator;
import com.octo.captcha.component.image.textpaster.RandomTextPaster;
import com.octo.captcha.component.image.textpaster.TextPaster;
import com.octo.captcha.component.image.wordtoimage.ComposedWordToImage;
import com.octo.captcha.component.image.wordtoimage.WordToImage;
import com.octo.captcha.component.word.wordgenerator.RandomWordGenerator;
import com.octo.captcha.component.word.wordgenerator.WordGenerator;
import com.octo.captcha.engine.CaptchaEngine;
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.image.ImageCaptchaFactory;
import com.octo.captcha.image.gimpy.GimpyFactory;

/**
 * Das ist unsere spezielle {@link CaptchaEngine} Implementierung.
 * @author Christoph Bünte
 *
 * @version $Revision: 1.2 $
 *
 * @created 12.10.2007
 *
 */
final public class CustomCaptchaEngine extends ListImageCaptchaEngine {

@Override
 protected void buildInitialFactories() {

// RGB: #4C4E42
 	Color textColor = new Color(0x4c, 0x4e, 0x42);
 	Color textColor2 = Color.BLACK;
 	Color[] colors = new Color[2];
 	colors[0]=textColor;
 	colors[1]=textColor2;

// RGB: #F4F8FB
 	Color backgroundColor = new Color(0xf4, 0xf8, 0xfb);

// Einheitlicher Hintergrund mit der gegeben Breite, Höhe und Farbe
 	BackgroundGenerator bgGenerator = new UniColorBackgroundGenerator(250,
 			70, backgroundColor);

// Der Text hat die Min-und Maxlänge mit den gegeben Farben
 	TextPaster paster = new RandomTextPaster(6, 6, colors);

// Schriftgrößen 10 bis 30
 	FontGenerator fontGenerator = new DeformedRandomFontGenerator(
 			10, 30);

// Benutze einen einfachen Mechanismus zum Mischen von Hintergrund und Text
 	WordToImage wordToImage = new ComposedWordToImage(fontGenerator,
 			bgGenerator, paster);

// Benutze zufällige Worte aus den gegebenen Buchstaben
 	WordGenerator wordGenerator = new RandomWordGenerator(
 			"ABCDEFGHIJKLMNOPQRSTUVWXYZ");

ImageCaptchaFactory factory = new GimpyFactory(wordGenerator,
 			wordToImage);

 	this.addFactory(factory);
 }
}</pre>
<p>Und hier die neue Singleton Implementierung:</p>
<pre lang="java">
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.service.captchastore.CaptchaStore;
import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;

/**
 *
 * @author Christoph Bünte
 *
 * @version $Revision: 1.6 $
 *
 * @created 11.10.2007
 *
 */

public class CaptchaServiceSingleton {

	private static ImageCaptchaService instance = initializeService();

	/**
	  * Gibt die einzige Instanz einnes CaptchaService zurück.
	  *
	  * @return
	  */
	 public static ImageCaptchaService getInstance() {
	 	return instance;
	 }

	/**
	  * Initialisiert den {@link ImageCaptchaService}
	  *
	  * @return
	  */
	 private static ImageCaptchaService initializeService() {

	// Wir brauchen eine Instanz unser eigenen Engine
	 	ListImageCaptchaEngine engine = new CustomCaptchaEngine();

		CaptchaStore captchaStore = new FastHashMapCaptchaStore();
 		captchaStore.empty();

		ImageCaptchaService service = new DefaultManageableImageCaptchaService(
 			captchaStore, engine, 180, 100000, 75000);

		return service;

 	}

}</pre>
<p>Kommentare und Anregungen sind wie immer willkommen. Beispiele für die kreativsten Bilder nehme ich auch gerne entgegen. Viel Spass beim ausprobieren.</p>
<div id="crp_related"><h3>Ähnliche Beiträge:</h3><ul><li><a href="http://www.christophbuente.de/2008-01-24-stripes-framework-tests-fur-actionbeans-im-wizard-modus/" rel="bookmark" class="crp_title">Stripes Framework &#8211; Tests für ActionBeans im Wizard Modus</a></li><li><a href="http://www.christophbuente.de/2007-11-10-paypal-subscriptions-abonnements-in-java-integrieren/" rel="bookmark" class="crp_title">Paypal subscriptions &#8211; Abonnements in Java integrieren</a></li><li><a href="http://www.christophbuente.de/2007-12-21-voiceglue-konfiguration-server-fur-sprachanwendungen-einrichten/" rel="bookmark" class="crp_title">VoiceGlue Konfiguration &#8211; Server für Sprachanwendungen einrichten</a></li><li><a href="http://www.christophbuente.de/2008-08-04-bilder-versehentlich-geloscht-urlaubserinnerungen-ganz-leicht-zuruckholen/" rel="bookmark" class="crp_title">Bilder versehentlich gelöscht &#8211; Urlaubserinnerungen ganz leicht zurückholen</a></li><li><a href="http://www.christophbuente.de/2009-10-29-opensocial-gadgets-apps-fur-studivz-selbst-entwickeln/" rel="bookmark" class="crp_title">Opensocial Gadgets &#8211; Apps für StudiVZ selbst entwickeln</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://www.christophbuente.de/2007-10-23-bots-aussperren-mit-captcha/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using memcached (Feed is rejected)
Page Caching using memcached
Database Caching 8/33 queries in 0.055 seconds using memcached
Object Caching 1093/1149 objects using memcached

Served from: www.christophbuente.de @ 2012-02-08 09:59:28 -->
