Halaman

Selasa, 25 Mei 2010

accept online payment for users (Part 1a: PayPal Website Payments Standard)

dah lama ga post, kali ini saia mao post artikel yg rasanya bakal lumayan panjang ;) tentang integrasi beberapa payment gateway (paypal, authorize.net, sagepay, google-checkout) buat nerima online payment untuk user (bukan untuk site owner).
jadi intinya site hanya sebagai perantara antara user dengan payment gateway.

ok mulai dari yg paling banyak digunakan: PayPal

persiapan yg dibutuhkan:
1. rails project dengan users (saia asumsikan dsini nama modelnya User)
2. developer account buat paypal (biar bisa nyoba) register di: https://developer.paypal.com/

siapkan model untuk menyimpan data paypal tiap user dan data notifikasi:

ruby script/generate model Paypal
ruby script/generate model Ipn


buat relasi user dengan paypal dan paypal dengan ipn, agar flexible saia menggunakan relasi polymorphic tapi relasi biasa juga dapat digunakan (saia anjurkan relasi paypal dengan ipn polymorphic agar nantinya kita mudah menambahkan gateway lain):

### paypal.rb ###
belongs_to :payable, :polymorphic => true
has_many :ipns, :as => :gateway, :dependent => :destroy


### ipn.rb ###
belongs_to :gateway, :polymorphic => true


### user.rb ###
include PaymentGatewayRelation


### /lib/payment_gateway_relation.rb ###
module PaymentGatewayRelation
def self.included(recipient)
recipient.class_eval do
has_one :paypal, :as => :payable, :dependent => :destroy

def payments
[self.paypal].compact
end

def payment_available?
!self.payments.empty?
end

def payment
self.payments.first
end

def payment= value
return unless value.respond_to? :payable
klass = value.class
self.payments.each{|x| x.destroy if x.class != klass}
self.reload
self.send("#{klass.to_s.underscore}=", value)
end
end
end
end

payment_gateway_relation.rb berisi relasi untuk semua gateway dan method2 untuk membantu akses.
mungkin akan lebih saia perjelas nanti saat gateway lain ditambahkan.

kemudian, tambahkan data paypal user yang dibutuhkan pada migration:

class CreatePaypals < ActiveRecord::Migration
def self.up
create_table :paypals do |t|
t.references :payable, :polymorphic => true
t.string :business, :limit => 70
t.string :return_url
t.timestamps
end
end

def self.down
drop_table :paypals
end
end


class CreateIpns < ActiveRecord::Migration
def self.up
create_table :ipns do |t|
t.references :gateway, :polymorphic => true
t.string :trans_id, :limit => 30
t.string :description, :limit => 50
t.timestamps
end
end

def self.down
drop_table :ipns
end
end

- business: paypal business account (email) dari user.
- return_url: url redirect untuk visitor ke suatu site (bisa di luar site kita) setelah pembayaran selesai
- trans_id: transaction id untuk mencegah duplikasi pembayaran
- description: optional, untuk menyimpan catatan ato suatu referensi

lanjut, di paypal.rb tambahkan attr_accessible dan beberapa validasi untuk keamanan ;)

include UriValidator

alias_attribute :account, :business

attr_accessible :business, :account, :return_url

validates_presence_of :account, :return_url
validates_uniqueness_of :business, :scope => :payable_type, :message => "account has already been used.", :case_sensitive => false
def validate
errors.add(:return_url, 'format invalid.') unless self.return_url.blank? || validate_http(self.return_url)
end

saia menggunakan alias attribute account untuk business agar lebih mudah dimengerti.

hal yang sama di ipn.rb

validates_presence_of :trans_id
validates_uniqueness_of :trans_id, :scope => :gateway_type, :allow_nil => true, :allow_blank => true

attr_accessible :trans_id, :description, :user, :gateway


buat ruby file /lib/uri_validator.rb:

module UriValidator

def validate_http uri
require 'uri'

return false if uri.blank?

uri = "http://" + uri unless uri.index("http") == 0

uri = URI.parse(uri)
if uri.class != URI::HTTP
return false
end

true
rescue URI::InvalidURIError
return false
end
end


fiuh.. urusan model2an beres..
migrate lalu selanjutnya controller dan view..
..
di post berikutnya biar kentang (sbenernya males nulis blog panjang2) =P

Tidak ada komentar:

Posting Komentar