Feed on
Posts
Comments

indexubid4.jpg

Tanggal launching: 21 Juni 2007
Estimasi lama pengerjaan: 10 jam

INDEXUBID.COM, adalah sebuah direktori website dimana para webmaster akan mendaftarkan websitenya untuk melakukan promosi dan meningkatkan link popularity dalam hal SEO. Berbeda dengan web directory pada umumnya, posisi listing dalam bidding directory ditentukan oleh sistem lelang, dimana siapa yang berani membayar tertinggi, dialah yang akan mendapatkan posisi teratas.

Saya menggunakan script indexu sebagai core systemnya, lalu melakukan sedikit modifikasi untuk menambahkan fitur-fitur pendukung untuk sebuah bidding directory, antara lain:

  • Integrasi sistem pembayaran dengan menggunakan paypal, dimana webmaster dapat memasukkan besarnya penawaran yang dia lakukan.
  • Sebuah form untuk melakukan penawaran dengan nilai yang lebih tinggi, karena bisa jadi posisi yang dimiliki webmaster akan turun sejalan dengan penawaran lainnya yang lebih tinggi.
  • 10 Penawar tertinggi mendapatkan sitewide listing

Untuk lebih jelasnya, di bawah ini saya sertakan beberapa screenshot modifikasi yang telah saya lakukan.

indexubid1.jpg

Webmaster dapat memasukkan besarnya penawaran pada saat mendaftarkan websitenya (add new listing) dan memperbarui penawaran (bid higher).

indexubid2.jpg

Sidewide listing untuk 10 bidder tertinggi.

indexubid3.jpg

Tampilan untuk daftar listing baru di halaman utama.

indexubid5.jpg

Tampilan di halaman detail untuk melakukan penawaran kembali, tentunya dengan nilai yang lebih besar.

Bulan Maret 07 kemaren saya sempat mengalami kecemasan yang cukup mendalam. Pasalnya saat itu saya sedang mempertimbangkan untuk menjual usaha yang sudah saya tekuni sejak tahun 2000. Usaha yang telah begitu berarti dalam hidup saya, dimana saya telah membesarkannya mulai dari nol. Tidak terhitung lagi pengorbanan waktu dan tenaga yang telah keluar, bahkan sampai sampai saya merelakan diri untuk menghabiskan waktu 15 semester untuk bisa lulus kuliah, sedangkan saya memulai usaha ini sejak semester 5. Hasilnya memang telah melebihi dari pengorbanan yang telah saya keluarkan. Dengan omset berkisar antara $3000 hingga $5000 per bulan, usaha ini benar-benar membuat saya merasa sangat nyaman.

nicecoder.jpg

Namun dasar saya ini anak kampungan, tidak pandai pegang uang besar. Bukannya rajin menabung malah suka beli ini itu hanya untuk memuaskan keinginan saja, yang mana ujung-ujungnya hanya sebagian kecil dari penghasilan yang masuk ke tabungan. Sekarang saya sudah beristri dan memiliki seorang anak perempuan berusia 9 bulan, membuat saya lebih perpikir. Bagaimana agar keuangan keluarga aman untuk selamanya. Akhirnya terbersit pikiran untuk menjual satu-satunya usaha saya ini. Sehari dua hari saya mencoba berpikir jernih, jual tidak, jual tidak. Akhirnya dengan berbagai pertimbangan saya merasa mantap 95%, saya baru meminta pertimbangan istri dan beberapa teman dekat. Reaksi mereka seperti yang saya duga sebelumnya 100% semuanya menentangnya, meskipun pada akhirnya istri saya mendukungnya setelah saya menjelaskan dasar pertimbangan yang saya buat.

Saya berniat menjualnya seharga 50 ribu dolar, atau setara dengan satu tahun penghasilan. Saya pikir ini adalah harga yang pantas dan realistis. Dengan uang sebanyak itu rasanya saya bakal bisa memiliki banyak pilihan untuk memulai usaha lainnya. Bukankah pada awalnya motivasi saya dalam menjalankan usaha ini adalah untuk masa depan keluarga, anak, dan istri. Jadi tidak ada salahnya kalau sekarang saya menjualnya demi masa depan keluarga. Ok, kemudian saya melelangnya di website sitepoint.

sitepoint.jpg

Penawaran demi penawaran, akhirnya saya memutuskan untuk menjualnya 60 persen hak kepemilikan saja, dengan harga 20 ribu dolar dan masih memiliki 40 persen sisa kepemilikan sehingga punya hak pembagian profit 40 persen setiap bulannya. Dan yang paling penting dan sangat menguntungkan bagi saya adalah bahwa saya adalah sebagai pemilik pasif, artinya saya tidak lagi terbebani oleh beban kerja lagi. Memang sekarang saya telah kehilangan kepemilikan sesungguhnya, semua domain saya transfer, berikut aset penting lainnya. Tapi apalah artinya itu, saya kembali ke tujuan semula, yaitu semuanya demi keluarga, meskipun akan hilang rasa bangga atas kepemilikan penuh atas usaha ini. Dalam benak saya, saya berharap pemilik baru bisa meningkatkan penjualan jauh lebih bagus daripada ketika saya pegang, sehingga nantinya penghasilan 40 persen yang saya dapatkan sekarang ini tak ubahnya seperti 100 persen ketika masih saya pegang. Artinya saya berharap akan tetap mendapatkan 3000 sampe 5000 dolar per bulan seperti sebelumnya, tentunya dengan tanpa beban kerja (dan tanpa pride akan ownership, meskipun pada kenyataannya saya memiliki 40 persennya).

Sekarang sudah tiga bulan berjalan, pemilik baru benar-benar handal dan jitu dalam mengelolanya. Dalam dua bulan ini penjualan selalu tembus 5000 dolar, dan tren nya terus meningkat. Dari penjualan sebesar itu, bagian yang saya terima adalah sekitar 1500 dolar sebulan. Nilai yang sudah sangat cukup untuk kebutuhan sehari-hari. Ditambah lagi ini saya terima hampir tanpa beban kerja, kecuali cukup aktif di forum support dan membantu promosi di forum lainnya.

Robert Kyosaki bilang ini adalah pasif income. Yah pasif income, tanpa saya sadari ternyata saya telah berpindah kuadran. Semua berawal dari masalah yang ternyata menggiring saya menuju pilihan ini. Entah ini keputusan yang tepat atau tidak, tapi saya merasa yakin seyakin yakinnya bahwa inilah yang terbaik bagi saya dan keluarga, dan mungkin ini adalah keputusan yang terbaik yang telah saya lakukan setelah saya menikah dua tahun lalu.

Semoga tulisan ini bisa menjadi pelajaran dan barokah buat kita semua. Anda, seorang programmer (tidak ada alasan anda baca blog ini bila anda bukan programmer), seperti saya juga seorang programmer, dengan usaha yang keras, insyaAllah keinginan dan harapan kita bakal terwujud.

PS: Tahu kah Anda, bahwa usia pemilik baru tersebut adalah 25 tahun, dan dia tidak kuliah karena memang dia tidak suka sekolah. Dia memulai bekerja pada saat berumur 18 tahun, dengan dedikasi tinggi sekarang dia telah memiliki berbagai bisnis online dan berpenghasilan satu milyar rupiah setahun.

Saat ini semakin banyak saja website yang menjual jasa atau produk, entah itu sekedar untuk donasi (cakephp.org), unsur having fun macam download komik dan film (narutofan.com), jualan game (play-asia.com), virtual credit card (trucards.com), bahkan jasa underground sekalipun tidak mau kalah ketinggalan (rentacracker.com). Sayangnya belum banyak para pelaku bisnis internet di indonesia yang memanfaatkannya, padahal paypal indonesia sudah dibuka (meskipun masih bisa untuk pembayaran saja) dan egold pun sudah semakin merakyat.

Sebelum anda memulai tutorial ini, pastikan anda memiliki account di Paypal Developer Central. Lalu buatlah dua account paypal sandbox berbeda, satu untuk merchant yang akan menerima pembayaran dan satu untuk pembeli untuk melakukan pembayaran. Jangan lupa untuk melakukan deposit sejumlah uang ke account pembeli, biasanya saya menambahkan sebesar $10,000 (gratis).

seolinkbid5.jpg

Tutorial kali ini tetap mengambil contoh website seolinkbid. Dimana pada website ini, pengunjung dapat membeli space linkback.

Bagaimana alur transaksi terjadi

  1. Pembeli masuk website seolinkbid.com, menuju ke halaman 'Buy a Link' dan mengisi form, lalu tekan tombol submit.
  2. Seketika setelah tombol submit ditekan, pembeli akan dibawa ke website payment gateway, dalam hal ini adalah paypal.com.
  3. Pembeli mengisi data paypalnya dan melakukan pembayaran.
  4. Apabila pembayaran gagal, proses berhenti di sini atau mengulang kembali ke nomor 3.
  5. Apabila pembayaran sukses, maka pembeli akan menerima email konfirmasi pembelian, begitu juga dengan merchant juga menerima email konfirmasi pembelian.
  6. Disamping menerima email konfirmasi, merchant juga menerima konfirmasi dari paypal dalam bentuk server-to-server confirmation, dalam hal ini disebut Paypal IPN (Instant Payment Notification). Nah IPN inilah yang bertanggungjawab dalam mengotomatisasi deliveri ke pihak pembeli. Nantinya kita akan membuat script untuk menangani IPN  ini.

Mengirim data pembelian ke paypal.com

Pada saat pembeli berada di halaman 'Buy a Link', maka program kita mengumpulkan informasi item yang hendak dibeli, dalam hal ini adalah 

  1. alamat email pembeli
  2. url
  3. title
  4. description line 1
  5. line 2
  6. bid atau jumlah uang yang harus dibayarkan

Dari data-data ini tidak semuanya akan dikirimkan kepada paypal, namun sebagian saja yang HANYA dibutuhkan oleh paypal, selebihnya anda dapat menyimpannya di dalam database. Untuk memperjelas, simak potongan kode berikut (add.php)

$query  = "insert into slb_link
                 (email, title, url, desc_1, desc_2,
                 submit_date, seo_title, bid, latest_bid_date, active)
                 values
                 ('{$_POST['email']}', '{$_POST['title']}', '{$_POST['url']}',
               '{$_POST['desc_1']}', '{$_POST['desc_2']}',
               now(), '{$seo_title}', '{$_POST['bid']}', now() , 0)"
;
    $result = $db->Execute($query);
     
    $link_id = $db->Insert_ID();
    $title   = $_POST['title'];

    include('gateway/paypal.php');

Saya memisahkan antara bagian program utama dengan fungsi yang berhubungan dengan payment system, dengan membuatnya menjadi independent dan modular akan mempermudah kita nantinya untuk menambah berbagai macam payment gateway.

Pada panduan yang disediakan oleh Paypal, kita harus mengirimkan data ke server paypal dalam bentuk METHOD POST. Untuk itu saya menggunakan form dan dengan menginstruksikan browser untuk mengirim data setelah form selesai di-load. Berikut adalah kode dari paypal.php.

<?

$paypal_email  = $config['paypal_email'];
$item_name     = "[{$config['website_name']}][BID] TITLE: ". $title . ", ID:" . $link_id;
$currency_code = 'USD';
$total         = $_POST['bid'];
$custom        = $link_id . "|" . $_POST['bid'];
$notify_url    = $config['website_url'] . '/gateway/paypal-ipn.php';
$return_url    = $config['website_url'] . "/{$seo_title}.html";
$cancel_url    = $config['website_url'];

?>

<body onLoad="document.payment.submit()">
<form name="payment" action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="<?= $paypal_email; ?>">
<input type="hidden" name="rm" value="2">
<input type="hidden" name="item_name" value="<?= $item_name; ?>">
<input type="hidden" name="item_number" value="1">
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="cs" value="0">
<input type="hidden" name="currency_code" value="<?= $currency_code ?>">
<input type="hidden" name="amount" value="<?= $total; ?>">
<input type="hidden" name="invoice" value="<?= $invoice_id; ?>">
<input type="hidden" name="custom" value="<?= $custom; ?>">
<input type="hidden" name="notify_url" value="<?= $notify_url; ?>">
<input type="hidden" name="return" value="<?= $return_url; ?>">
<input type="hidden" name="cancel_return" value="<?= $cancel_url; ?>">
</form>

Kode diatas terbagi menjadi dua bagian. Yang pertama adalah bagian inisialisasi nilai yang akan dikirim ke paypal. Berikutnya adalah bagian data-data yang akan dikirimkan. Saya menggunakan cara ini sekedar untuk mempermudah pembacaan dan perubahan data semata. Perlu anda perhatikan bahwa perintah onLoad="document.payment.submit()" akan segera mengirimkan data setelah halaman selesai di-load, dan data akan dikirimkan ke https://www.sandbox.paypal.com/cgi-bin/webscr. Alamat tersebut adalah alamat paypal sandbox, yang HANYA anda gunakan selama proses development. Pada saat proses deployment, anda harus menghapus kata sandbox dan mengarahkannya ke server paypal sesungguhnya yaitu: https://www.paypal.com/cgi-bin/webscr.

Penjelasan data berikutnya kurang lebihnya seperti berikut:

<input type="hidden" name="business" value="<?= $paypal_email; ?>">

Alamat paypal anda sebagai merchant yang menerima pembayaran.

<input type="hidden" name="item_name" value="<?= $item_name; ?>">

Nama item yang dijual.

<input type="hidden" name="currency_code" value="<?= $currency_code ?>">

Kode mata uang.

<input type="hidden" name="amount" value="<?= $total; ?>">

Total nilai transaksi.

<input type="hidden" name="custom" value="<?= $custom; ?>">

Data di sini adalah data sembarang yang anda perlukan untuk aplikasi anda. Dalam hal ini, seolinkbid mengirimkan data link_id dan nilai bid. Karena tipe data yang diterima di sini adalah bentuk string, maka saya menggunakan delimiter '|' sebagai pembatas data, yang kemudian diperlukan parsing untuk membacanya kembali.

<input type="hidden" name="notify_url" value="<?= $notify_url; ?>">

Alamat dari Paypal IPN, yaitu alamat url dari program kita yang melakukan proses otomatisasi.

<input type="hidden" name="return" value="<?= $return_url; ?>">

Alamat url ketika transaksi telah berhasil, biasanya menuju halaman terima kasih atau ke client area, tergantung dari kasus yang anda kerjakan.

<input type="hidden" name="cancel_return" value="<?= $cancel_url; ?>">

Alamat url ketika transaksi gagal, biasanya menuju halaman konfirmasi transaksi gagal, atau cukup arahkan ke homepage saja.

Script Paypal IPN

Kode IPN yang anda buat HARUS mengikuti aturan yang telah ditetapkan oleh paypal, hal ini untuk menjaga keamanan transaksi. Setelah pembeli melakukan pembayaran dan menghasilkan status sukses, maka paypal akan mengirim konfirmasi ke server kita. Dalam panduan yang disediakan oleh paypal, dijelaskan bahwa data yang dikirimkan oleh paypal adalah data dalam bentuk POST. Data ini tidak serta merta langsung kita baca, tetapi kita perlu mengembalikan data ini lagi kepada paypal sebagai bentuk konfirmasi. Selanjutnya paypal mengirim data kembali beserta status dari pembayaran, antara lain VERIFIED berarti sukses, dan INVALID berarti gagal. Terdapat beberapa kode lainnya, namun saat ini cukup kita pahami dulu yang paling penting yaitu VERIFIED. Berikut adalah kode dari paypal-ipn.php.

<?

  // read the post from PayPal system and add 'cmd'
  $req = 'cmd=_notify-validate';

  foreach ($_POST as $key => $value) {
    $value = urlencode(stripslashes($value));
    $req .= "&$key=$value";
  }

  // post back to PayPal system to validate
  $header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
  $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
  $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
  $fp = fsockopen ('sandbox.paypal.com', 80, $errno, $errstr, 30);

  if ($fp) {
    fputs ($fp, $header . $req);
    while (!feof($fp)) {
      $res = fgets ($fp, 1024);
      if (strcmp ($res, "VERIFIED") == 0) {
        if ($_POST['receiver_email'] == $paypal_email) {

          //-------------------------
          //.. Put custom action here
          //-------------------------
                   
          include "../init.php";   
               
          // get the custom value
          $raw_data = explode('|',$_POST['custom']);
          $bid     = array_pop($raw_data);
          $link_id = array_pop($raw_data);

          $query = "update olb_link set active = 1 and bid = {$bid}
                    where link_id = {$link_id}"
;
          $result = $db->Execute($query);
                   
          //-----------------------
          //.. End of custom action
          //-----------------------

        }
      }
      else if (strcmp ($res, "INVALID") == 0) {
        // log for manual investigation
      }
    }
    fclose ($fp);
  }
?>

Pada kode di atas kita menempatkan kode untuk mengotomatisasi transaksi, setelah kita cek sebelumnya bahwa statusnya adalah VERIFIED. Pada kasus seolinkbid di atas, saya hanya mengupdate nilai dari field active menjadi 1 dan nilai bid menjadi nilai bid saat ini. Untuk kasus lainnya, anda dapat menambahkan kode-kode yg diperlukan di bagian ini. Perhatikan juga pada potongan kode $fp = fsockopen ('sandbox.paypal.com', 80, $errno, $errstr, 30);, anda perlu menghapus kata sandbox ketika melakukan deployment.

Pada tutorial ini anda telah mempelajari hal yang sangat mendasar dalam melakukan integrasi. Namun materi di sini sudah sangat cukup untuk menjadi bekal untuk mengembangkannya lebih lanjut, misalnya untuk pembayaran subscription, atau untuk menambah faktor keamanan lainnya. Pada tutorial berikutnya kita akan mempelajari integrasi dengan payment system lainnya, yaitu E-GOLD.COM.

Halaman detail biasanya merupakan halaman yang wajib untuk dibuat dalam bentuk search engine url. Pada website seolinkbid, saya mencoba menghilangkan nomor id (link_id) dan menggantinya dengan string yang merupakan title dari link yang bersangkutan. Halaman detail sebelumnya memiliki url dinamis dalam bentuk berikut

http://www.seolinkbid.com/detail.php?link_id=100

dan akan saya ubah dalam bentuk statis yang lebih menarik, yaitu

http://www.seolinkbid.com/free-web-based-proxy-hosting.html

Pada dasarnya strategi yang saya gunakan adalah sederhana, yaitu melakukan query detal link berdasarkan title, bukan berdasarkan link_id seperti yang biasa digunakan.

Untuk ini saya membutuhkan sebuah title yang unique untuk setiap data link. Saya menambahkan sebuah field tambahan, yaitu seo_title yang merupakan bentuk modifikasi dari data field title yang menganut konsep seo.

CREATE TABLE `slb_link` (
  `link_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `email` varchar(100) NOT NULL DEFAULT '',
  `active` char(1) NOT NULL DEFAULT '',
  `title` varchar(255) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
   `desc_1` varchar(255) DEFAULT NULL,
  `desc_2` varchar(255) DEFAULT NULL,
  `submit_date` timestamp NULL DEFAULT NULL,
  `latest_bid_date` timestamp NULL DEFAULT NULL,
  `bid` double DEFAULT NULL,
  `seo_title` varchar(255) DEFAULT NULL,
  PRIMARY KEY  (`link_id`),
  KEY `title` (`title`)
)

Setiap kali terdapat data link yang baru masuk, maka akan digenerate data untuk field seo_title, yang merupakan bentuk seo dari title. Misalnya link dengan title sebagai berikut

File konfigurasi dan inisialisasi awal

akan memiliki data seo_title

file-konfigurasi-dan-inisialisasi-awal

Contoh lainnya

Free web based proxy hosting

memiliki seo_title

free-web-based-proxy-hosting

Masalah berikutnya, apa yang terjadi apabila terdapat link dengan title yang sama. Saya menggunakan solusi termudah, yaitu dengan memberikan tambahan dua huruf random di belakang seo_title yang telah digenerate sebelumnya. Jadi misalnya, terdapat lagi data dengan title

Free web based proxy hosting

sedangkan saat ini telah ada data lain yang sama sebelumnya, maka akan digenerate ulang menjadi bentuk seperti berikut

free-web-based-proxy-hosting-93

Berikut adalah fungsi yang akan mengenerate data untuk seo_title.

function SeoUrl($s) {
    $c = array (' ','-','/','\\',',','.','#',':',';','\'','"','[',']','{',
      '}',')','(','|','`','~','!','@','%','$','^','&','*','=','?','+');

    $s = strtolower(str_replace($c, '-', $s));
    return $s;
}

Kemudian saya menambahkan potongan program berikut untuk memastikan bahwa akan didapat nilai seo_title yang unique, sebelum memasukkan data link baru ke dalam database.

if(CheckSeoUrlConflict($_POST['title'])) {
      $rand = '-'.rand(1,99);
    }
    $seo_title = SeoUrl($_POST['title']) . $rand;

Kali ini kita menggunakan kode mod-rewrite yang lebih kompleks. Kita di sini mencoba untuk menangkap semua url termasuk yang mengakibatkan pesan error 404 (file not found).

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^.* detail.php

Tiga baris pertama menyatakan sebuah kondisi untuk rule berikutnya. Bisa diartikan kira-kira seperti ini "BILA nama file dalam url tidak terdapat file sesungguhnya, atau tidak terdapat directory sesungguhnya, atau tidak terdapat symbolic link MAKA jalankan rule dibawahnya, yaitu untuk semua url redirect ke file detail.php".

Tahap berikutnya adalah memikirkan cara bagaimana mengambil nilai seo_title yang ada di dalam url. Misalnya untuk url sebagai berikut

http://www.seolinkbid.com/free-web-based-proxy-hosting.html

Cara yang saya pakai adalah memecah string url tersebut menjadi variable array berdasarkan pemisah tanda '/', karena kita tahu bahwa nantinya nama file akan SELALU berada pada array index terakhir. Berikutnya cukup menghilangkan string '.html'

$p_url     = explode('/',$_SERVER['REQUEST_URI']);
$f_url     = array_pop($p_url);
$seo_title = substr($f_url,0,strlen($f_url)-5);

Karena kita menangkap semua url yang tidak exist, maka kita harus bisa mendeteksi dan memberikan pesan error 404 file not found. Tentunya hal ini tergantung dari aplikasi yang kita buat. Untuk seolinkbid, saya cukup mengecek apakah data detail untuk link yang bersangkutan tersedia atau tidak.

$query  = "select * from slb_link where seo_title = '{$seo_title}' and active = 1";
$result = $db->SelectLimit($query,1,0);
$links  = $result->GetArray();

if($result->RecordCount()==0) {
  $tpl->display('404.html');
  exit();
}

Berikut adalah kode komplit dari detail.php, di sini saya telah menambahkan sebuah fungsi untuk untuk melakukan bidding.

<?
include_once "init.php";

//-------------------------------
//.. get seo title and bid status
//-------------------------------

$p_url     = explode('/',$_SERVER['REQUEST_URI']);
$f_url     = array_pop($p_url);
$seo_title = substr($f_url,0,strlen($f_url)-5);

if(array_pop($p_url)=='bid') {
  $bidding = 1;
}

//----------------------
//.. checking valid link
//----------------------

$query  = "select * from slb_link where seo_title = '{$seo_title}' and active = 1";
$result = $db->SelectLimit($query,1,0);
$links  = $result->GetArray();

if($result->RecordCount()==0) {
  $tpl->display('404.html');
  exit();
}

//----------
//.. routing
//----------

if ($bidding) {
  ProcessBid();
}
else {
  ShowDetail();
}

function ShowDetail() {
  global $config, $tpl, $val, $links, $seo_title;

  $min_bid = $links[0]['bid'] + $config['bid_increament'];
  $bid = $min_bid;

  //-----------
  //.. template
  //-----------

  $tpl->assign('links',$links);
  $tpl->assign('bid',$bid);
  $tpl->assign('min_bid',$min_bid);
  $tpl->assign('seo_title',$seo_title);

  $tpl->assign('e_msg_tos',$val->GetErrorMsg('tos'));
  $tpl->assign('e_msg_bid',$val->GetErrorMsg('bid'));

  $tpl->display('detail.html');
}

function ProcessBid() {
  global $config, $val, $links;

  $min_bid = $links[0]['bid'] + $config['bid_increament'];

  //------------------
  //.. form validation
  //------------------

  if(empty($_POST['bid']) or $_POST['bid']<$min_bid) {
    $val->AddError('bid', _('Your bid is too low'));
  }
  elseif(!$val->IsNumeric($_POST['bid'])) {
    $val->AddError('bid', _('Your bid value is incorrect'));
  }

  if($_POST['tos']!=1) {
    $val->AddError('tos', _('You must agree with Term of Service'));
  }

  if(!$val->GetError()) {
    $link_id = $links[0]['link_id'];
    $title   = $links[0]['title'];   
    include('gateway/paypal.php');
  }
  else {
    ShowDetail();
  }

}
?>

Sampai pada tutorial ini, anda telah mempelajari bagaimana membentuk url statis dengan menggunakan mod-rewrite. Pada tutorial pertama anda mempelajari teknik yang paling sederhana, diikuti dengan tutorial kedua dan ketiga dengan teknik yang lebih lanjut.

Kali ini kita akan mengupas tuntas cara membuat url statis untuk halaman search result. Cara yang saya gunakan di sini tidak jauh beda dengan cara membuat menu alphabar sebelumnya.

seolinkbid3.jpg

Kita mulai dengan membuat kode html untuk inputan form search. Sama seperti cara umum, saya menggunakan file action mengarah kepada search.php.

<form method="get" action="search.php">
<input name="q" type="text" />
<input type="submit"  value="search" />
</form>

Sehingga apabila seseorang melakukan pencarian, maka akan di-generate url seperti ini

search.php?q=proxy
search.php?q=free
search.php?q=hosting
dst...

Karena tujuan kita adalah untuk membuat url dinamis yang search engine friendly, dimana kita mengusahakan terdapat keyword di url, maka saya me-redirect url di atas ke bentuk sebagai berikut:

search/proxy.html
search/free.html
search/hosting.html
dst...

Hal ini cukup kita lakukan dengan cara seperti berikut

if($_GET['r']!='1') {
  header("Location: search/{$_GET['q']}.html");
}

Selanjutnya kita perlu menambahkan sebuah rule di file .htaccess yang telah kita buat sebelumnya. Tambahkan rule sebagai berikut

RewriteRule ^search/(.*).html search.php?q=$1&r=1

Maksudnya adalah, teruskan semua url yang diawali dengan search/, yang diikuti dengan sembarang karakter, dan berakhir dengan .html, KE url aslinya yaitu search.php?q=$1&r=1, dimana $1 adalah parameter pertama yang ditangkap oleh (.*) (sembarang karakter).

Perhatikan bahwa saya mempassing sebuah parameter tambahan yaitu r=1, ini digunakan untuk memberikan status bahwa url ini telah di-redirect sebelumnya. Sehingga dari contoh request sebelumnya, nantinya akan dilakukan request aslinya seperti berikut

search.php?q=proxy&r=1
search.php?q=free&r=1
search.php?q=hosting&r=1
dst...

Berikut adalah kode komplit dari search.php

<?
include_once "init.php";

//-----------------------------------------
//.. intercept and redirect to SEO friendly
//-----------------------------------------

if($_GET['r']!='1') {
  header("Location: search/{$_GET['q']}.html");
}

//--------
//.. links
//--------

$query  = "select * from slb_link where title like '%{$_GET['q']}%' and active = 1 order by bid desc";
$result = $db->SelectLimit($query,$config['max_rows_per_page'],0);
$links  = $result->GetArray();

//-----------
//.. template
//-----------

$tpl->assign('links',$links);
$tpl->display('search.html');
?>

Pada tutorial berikutnya akan kita lanjutkan teknik mod-rewrite yang lebih lanjut, dimana kita berusaha menghilangkan nomor id data yang biasanya bagi pakar SEO cukup mengganggu di url, dan menggantinya menjadi string sehingga lebih menjelaskan tentang isi dari sebuah halaman web.

Seringkali kita membuat program php yang membutuhkan passing parameter melalui url. Misalnya untuk melihat detail data, kita mempassing id nya, sehingga bentuknya seperti berikut

http://www.mydomain.com/detail.php?id=300

Secara umum, model url seperti ini kita kenal dengan sebutan url dinamik. Lain halnya dengan url statis, dimana halaman tersebut tidak lagi membutuhkan passing parameter, sehingga bentuknya menjadi seperti berikut

http://www.mydomain.com/file-konfigurasi-dan-inisialisasi-awal.html

Pakar SEO berpendapat bahwa search engine akan akan lebih mudah meng-index halaman web yang meggunakan bentuk url statis. Meskipun hal ini belum tentu 100% benar, karena sering kita melihat google dengan sukses meng-index halaman dengan bentuk url dinamis. Namun sebagai seorang programmer web, kita dituntut untuk mengikuti kehendak dari customer, dimana mereka sangat menyukai bentuk-bentuk url statis seperti ini.

Satu hal yang sangat fundamental dalam merubah url dinamis menjadi url statis, kita membutuhkan bantuan modul mod-rewrite dari web server. Untuk web server apache, modul ini sudah tersedia secara gratis, anda hanya perlu memastikan module ini aktif. Untuk IIS, tersedia juga modul mod-rewrite url, namun merupakan paket terpisah yang harus dibeli. Untuk tutorial ini saya menggunakan server apache dengan mod-rewrite telah aktif. Untuk memastikannya, buka lah file httpd.conf dan pastikan baris berikut telah tersedia (apabila anda menggunakan shared web hosting, tanyakan lah pada customer service mereka).

LoadModule rewrite_module modules/mod_rewrite.so

seolinkbid2.jpg

Sebagai bahan tutorial saya mengambil contoh website seolinkbid. Pada website ini terdapat 3 bagian yang ingin saya buat dalam bentuk url statis, yaitu:

  1. menu alphabar dari # A sampai Z
  2. halaman search result
  3. halaman detail (Place Bid)

Menu Alphabar

Menu ini menyediakan daftar link untuk memberikaan daftar link berdasarkan huruf awalan. Kode html nya adalah standar seperti biasanya dan tidak ada hal yang istimewa di sini, yaitu:

<a HREF="index.php">HOME</a>
<a HREF="0.html">#</a>
<a HREF="a.html">A</a>
<a HREF="b.html">B</a>
<a HREF="c.html">C</a>
<a HREF="d.html">D</a>
<a HREF="e.html">E</a>
<a HREF="f.html">F</a>
<a HREF="g.html">G</a>
<a HREF="h.html">H</a>
<a HREF="i.html">I</a>
<a HREF="j.html">J</a>
<a HREF="k.html">K</a>
<a HREF="l.html">L</a>
<a HREF="m.html">M</a>
<a HREF="n.html">N</a>
<a HREF="o.html">O</a>
<a HREF="p.html">P</a>
<a HREF="q.html">Q</a>
<a HREF="r.html">R</a>
<a HREF="s.html">S</a>
<a HREF="t.html">T</a>
<a HREF="u.html">U</a>
<a HREF="v.html">V</a>
<a HREF="w.html">W</a>
<a HREF="x.html">X</a>
<a HREF="y.html">Y</a>
<a HREF="z.html">Z</a>

Tentunya kita tidak perlu membuat banyak halaman seperti link di atas (mulai 0.html, a.html, b.html, dst). Cukup membuat sebuah file untuk semua halaman tersebut, yaitu browse.php. Yang perlu kita lakukan adalah mem-passing sebuah huruf ke browse.php, misalnya

browse.php?a=0
browse.php?a=a
browse.php?a=b
dst...

Kode di dalam browse.php, juga tidak ada yang istimewa, cukup lakukan hal seperti biasa, yaitu melakukan query berdasarkan parameter yang di-passing.

if($_GET['a']=='0') {
  $query  = "select * from slb_link
             where title like '0%'
             or title like '1%'
             or title like '2%'
             or title like '3%'
             or title like '4%'
             or title like '5%'
             or title like '6%'
             or title like '7%'
             or title like '8%'
             or title like '9%'
             order by bid desc"
;
}
else {
  $query  = "select * from slb_link where title like '{$_GET['a']}%' and active = 1 order by bid desc";
}

Tahap berikutnya adalah membuat sebuah file dengan nama .htaccess yang harus anda letakkan pada folder root dari aplikasi anda. Aktifkan mod-rewrite

Options -MultiViews
RewriteEngine On

Buat rule untuk mengarahkan url ke url aslinya

RewriteRule ^0.html browse.php?a=0
RewriteRule ^a.html browse.php?a=a
RewriteRule ^b.html browse.php?a=b
RewriteRule ^c.html browse.php?a=c
RewriteRule ^d.html browse.php?a=d
RewriteRule ^e.html browse.php?a=e
RewriteRule ^f.html browse.php?a=f
RewriteRule ^g.html browse.php?a=g
RewriteRule ^h.html browse.php?a=h
RewriteRule ^i.html browse.php?a=i
RewriteRule ^j.html browse.php?a=j
RewriteRule ^k.html browse.php?a=k
RewriteRule ^l.html browse.php?a=l
RewriteRule ^m.html browse.php?a=m
RewriteRule ^n.html browse.php?a=n
RewriteRule ^o.html browse.php?a=o
RewriteRule ^p.html browse.php?a=p
RewriteRule ^q.html browse.php?a=q
RewriteRule ^r.html browse.php?a=r
RewriteRule ^s.html browse.php?a=s
RewriteRule ^t.html browse.php?a=t
RewriteRule ^u.html browse.php?a=u
RewriteRule ^v.html browse.php?a=v
RewriteRule ^w.html browse.php?a=w
RewriteRule ^x.html browse.php?a=x
RewriteRule ^y.html browse.php?a=y
RewriteRule ^z.html browse.php?a=z

Maksud dari RewriteRule ^0.html browse.php?a=0 adalah bahwa setiap request url yang diawali dengan 0.html akan diteruskan ke alamat browse.php?a=0. Demikian juga dengan baris-baris berikutnya.

Sampai pada tahap ini anda sudah bisa membuat menu alphabar menjadi url statis. Pada tutorial selanjutkan akan dijelaskan bagaimana membuat halaman search result menjadi url statis.

config.php

File konfigurasi dibutuhkan untuk menyimpan status konfigurasi sistem. Contoh paling sederhana adalah untuk menyimpan data koneksi ke database. Untuk keperluan ini saya membuat sebuah file dengan nama /config.php.

<?
  $db_name = 'seolinkbid';
  $db_host = 'localhost';
  $db_user = 'root';
  $db_pwd  = '';
?>

Untuk data konfigurasi lainnya, saya lebih suka memasukkannya ke dalam database (meskipun anda bisa mamasukkannya di dalam file ini). Keuntungannya adalah untuk mempermudah dalam proses upgrade ke versi selanjutnya, dimana user cukup melakukan setting ulang saja terhadap konfigurasi koneksi database. Selain itu dari sisi programmer, akan mempermudah kita melakukan penambahan dan pengurangan data konfigurasi. Dari sisi performance pun saya merasa tidak menjadi kendala, karena secara umum dalam hal manajemen database, cost untuk melakukan operasi write adalah mahal dan untuk operasi read adalah murah. Dimana dalam data konfigurasi ini, proses akan lebih banyak pada operasi read, sehingga cost nya tidak banyak.

Selanjutnya struktur database yang saya gunakan untuk menyimpan data konfigurasi adalah

CREATE TABLE `slb_config` (
  `config_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `value` varchar(255) DEFAULT NULL,
  PRIMARY KEY  (`config_id`)
)

Prefix slb_ di atas adalah sebagai kode dari nama software kita. Dengan menggunakan prefix, kita akan memberi kesempatan kepada user yang hanya memiliki hak akses terbatas pada satu database saja, dengan demikian tidak akan berbenturan dengan tabel-tabel yang digunakan oleh software lainnya.

Untuk tabel konfigurasi ini, saya membuat sebuah fungsi dengan nama LoadConfig(), dimana fungsi ini akan memberikan return value dalam bentuk array.

function LoadConfig() {
  global $db;

  $query   = "select name, value from olb_config";
  $result  = $db->Execute($query);
  $cfg_num = $result->RecordCount();
  $raw_cfg = $result->GetArray($cfg_num);

  foreach($raw_cfg as $k => $v) {
    $cfg[$v['name']] = $v['value'];
  }

  return $cfg;
}

Fungsi ini saya masukkan dalam file /lib/functions.php dengan pertimbangan fungsi ini sering dipakai (hampir selalu di-load setiap kali membuka halaman).

init.php

Berikutnya adalah sebuah file inisialisasi, yaitu sebuah file yang bertugas melakukan inisialisasi awal yang nantinya akan di-include-kan oleh file-file php lainnya. Hal-hal yang akan dilakukan oleh file ini adalah sebagai berikut.

Mendapatkan lokasi absolute path, nilai ini digunakan untuk di proses selanjutnya untuk membaca file-file library

define('SLB_PATH', dirname(__FILE__).'/');

Membaca file /config.php.

include_once SLB_PATH . "config.php";

Membaca library yang sering digunakan.

include_once SLB_PATH . "lib/adodb_lite/adodb.inc.php";
include_once SLB_PATH . "lib/smarty/Smarty.class.php";

include_once SLB_PATH . "lib/functions.php";
include_once SLB_PATH . "lib/template.php";
include_once SLB_PATH . "lib/validation.php";

Melakukan koneksi database.

$db = ADONewConnection('mysql');
$db->createdatabase = true ;
$result = $db->Connect($db_host, $db_user, $db_pwd, $db_name);
$db->debug = true ;
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;

Membaca konfigurasi di database.

$config = LoadConfig();

Menginisialisasi class yang sering digunakan (template dan validation).

$tpl = new Template;
$tpl_admin = new TemplateAdmin;
$val = new Validation;

Kesimpulan

Pada tutorial ini anda telah membuat file:
/config.php
/init.php
/lib/functions.php

Pertama kali yang perlu kita lakukan adalah melakukan perencanaan terhadap struktur file dan directory dari aplikasi kita. File-file perlu kita organisasikan sesuai dengan tujuannya untuk mempermudah proses code maintenance di saat mendatang. Untuk hal ini, saya biasanya membuat struktur directory sebagai berikut:

/admin/
/admin/templates/
/lib/
/templates/

File-file yang berhubungan dengan administrasi (admin control panel), dimasukkan dalam directory /admin. Sedangkan file-file templatenya masuk ke dalam directory /admin/templates/.

File-file library, baik yang kita develop sendiri maupun library dari pihak ketiga, saya masukkan ke dalam directory /lib. Library pihak ketiga yang biasanya saya gunakan adalah adodblite (untuk akses database), smarty (untuk template engine), dan phpmailer (untuk mengirimkan email). Ketiganya masuk dalam directory ini.

Yang terakhir adalah directory /templates yang berisi file-file template untuk halaman yang bisa diakses oleh public.

Struktur directory seperti ini tidak lah baku, biasanya saya hanya memulainya seperti di atas, kemudian mengembangkannya seperlunya sesuai dengan kebutuhkan project kita. Misalnya saat kita menggunakan library smarty, maka kita membutuhkan sebuah directory lagi untuk menampung file-file template yang telah ter-compile. Untuk itu perlu dibuat lagi sebuah directory dengan nama:

/templates_c/

Untuk project yang membutuhkan akses ke payment system, saya membutuhkan directory tambahan lainnya seperti: /gateway berisi file-file payment gateway yang menghubungkan proses pembayaran ke paypal dan e-gold, dan directory /locale yang berisi file-file language untuk fungsi gettext() untuk mempermudah translasi ke bahasa lainnya. Berikut ini adalah contoh struktur directory yang saya pergunakan di awal project:

struktur-directory.jpg