Leon's notes

Aug 11

Paypal on Rails, gotchas and tricks

With the help of Activemerchant, integrate Paypal into Rails is really easy, yet there are some gotchas you should be aware of.

  1. The original ‘_ext-enter’ for ‘cmd’ is deprecated
  2. If you are to submit multiple line items, the ‘cmd’ value must be ‘_cart’, then you can use item_name_x/quantity_x/amount_x as the line items details
  3. The built in helper in Activemerchant do not provide the multiple line mapping, I’ved add one myself, the line_items method.
    then you can use it in your view like 
    - service.line_items line_items
  4. The shipping cost to whole cart should be set in ‘handling_cart’, instead of ‘shipping’ as suggested in Paypal’s document, this is really bad.

See the comments you can understand how the customized helper works

# We changed this paypal helper provided by activemerchant because:
# 
# 1) change the initializer to meet our need, for example, 
#   to submit mulitple items, the 'cmd' value should be '_cart',
#   see here: https://www.x.com/thread/43442
#   and here: https://www.x.com/docs/DOC-1340
#   the original '_ext-enter' for 'cmd' is deprecated
# 
# 2) add the line_items method to prepare details for each line item
# 
# 3) accroding to https://www.x.com/thread/39507
#   if you want to pass shpping cost for the whole cart, 
#   you should set up the 'handling_cart' instead of 'shipping' as documented
#   mapping :shipping, 'handling_cart'

module ActiveMerchant #:nodoc:
  module Billing #:nodoc:
    module Integrations #:nodoc:
      module Paypal
        class Helper < ActiveMerchant::Billing::Integrations::Helper
          def initialize(order, account, options = {})
            super
            # indecate we are using thirdparty shopping cart
            add_field('cmd', '_cart')
            add_field('upload', '1')


            add_field('no_shipping', '1')
            add_field('no_note', '1')
            add_field('charset', 'utf-8')
            add_field('address_override', '0')
            add_field('bn', application_id.to_s.slice(0,32)) unless application_id.blank?
          end
          
          # pass the shipping cost for whole cart
          mapping :shipping, 'handling_cart'

          # mapping header image
          mapping :cpp_header_image, 'cpp_header_image'

          # add line item details, note the amount_x 
          # should be price instead of line total   
          def line_items(items = [])
            items.each_with_index do |line, index|
              add_field("item_name_#{index+1}", line.item.item_name)
              add_field("amount_#{index+1}", line.price.value_without_unit)
              add_field("quantity_#{index+1}", line.quantity)
            end
          end          

        end
      end
    end
  end
end

Also, you need to create your own payment_service_for( I named it paypal_payment_service_for) to support encrypt data before sending to paypal, because the built in payment_service_for in Activemerchant does not suppor that

require_library_or_gem 'action_pack'

module ActiveMerchant #:nodoc:
  module Billing #:nodoc:
    module Integrations #:nodoc:
      module ActionViewHelper
        def paypal_payment_service_for(order, account, options = {}, &proc)
          raise ArgumentError, "Missing block" unless block_given?

          integration_module = ActiveMerchant::Billing::Integrations.const_get(options.delete(:service).to_s.camelize)

          encrypt = options.delete(:encrypt)

          result = []
          result << form_tag(integration_module.service_url, options.delete(:html) || {})

          service_class = integration_module.const_get('Helper')
          service = service_class.new(order, account, options)

          result << capture(service, &proc)

          if encrypt
            paypal_params = { :cert_id => cert_id }
            service.form_fields.each do |field, value|
              paypal_params.merge!(field => value)
            end

            result << hidden_field_tag(:cmd, "_s_xclick")
            result << "\n"
            result << hidden_field_tag(:encrypted, encrypt_for_paypal(paypal_params))
          else
            service.form_fields.each do |field, value|
              result << hidden_field_tag(field, value)
            end
          end

          result << ''
          result= result.join("\n")

          concat(result.respond_to?(:html_safe) ? result.html_safe : result)
          nil
        end

        # encrypt values
        def encrypt_for_paypal(values)
          unless Settings.payment.paypal.test_mode
            app_cert_pem = File.read("#{Rails.root}/config/paypal/paypal-pubcert.pem")
            app_key_pem = File.read("#{Rails.root}/config/paypal/paypal-prvkey.pem")
            paypal_cert_pem = File.read("#{Rails.root}/config/paypal/paypal-cert.pem")
          else
            app_cert_pem = File.read("#{Rails.root}/config/paypal/paypal-sandbox-pubcert.pem")
            app_key_pem = File.read("#{Rails.root}/config/paypal/paypal-sandbox-prvkey.pem")
            paypal_cert_pem = File.read("#{Rails.root}/config/paypal/paypal-sandbox-cert.pem")
          end

          signed = OpenSSL::PKCS7::sign(OpenSSL::X509::Certificate.new(app_cert_pem), OpenSSL::PKey::RSA.new(app_key_pem, ''), values.map { |k, v| "#{k}=#{v}" }.join("\n"), [], OpenSSL::PKCS7::BINARY)
          OpenSSL::PKCS7::encrypt([OpenSSL::X509::Certificate.new(paypal_cert_pem)], signed.to_der, OpenSSL::Cipher::Cipher::new("DES3"), OpenSSL::PKCS7::BINARY).to_s.gsub("\n", "")
        end

        def cert_id
          Settings.payment.paypal.certificate_id
        end
      end
    end
  end
end

Aug 2
Hello Lion!
到目前为止的问题:
git:command not found,重新下载了git的安装包安装
imagemagick好像坏了,所有相关的cuke feature都失败了
打开itunes提示说itunes的lib是新版本的itunes创建的,需要下载新版本itunes才能打开,wtf?
好处:
细节比以前漂亮了很多,窗口出现、消失时的效果,窗口的圆角等等
Mail!!!当初没买sparrow是正确的,太好用了!
其他:
滚动方向基本适应
输入法有改进,不过既然QIM都免费了,这个意义就不大了

Hello Lion!

到目前为止的问题:

  • git:command not found,重新下载了git的安装包安装
  • imagemagick好像坏了,所有相关的cuke feature都失败了
  • 打开itunes提示说itunes的lib是新版本的itunes创建的,需要下载新版本itunes才能打开,wtf?

好处:

  • 细节比以前漂亮了很多,窗口出现、消失时的效果,窗口的圆角等等
  • Mail!!!当初没买sparrow是正确的,太好用了!

其他:

  • 滚动方向基本适应
  • 输入法有改进,不过既然QIM都免费了,这个意义就不大了

Jul 24

2011.07.24 珠三角技术沙龙HTML5专场, a set on Flickr.
一些感受:
Web仍然有很强的生命力。虽然最近有一波波的Web已死的言论,但是正如@DHH所说,Web是十年来沉淀下证明最有生命力的技术,虽然一再有人宣称它已死,但事实证明它的生命力超乎想象。随着HTML5为它增加更加强大的表达能力,Web应该会焕发更强的光彩。
HTML5目前还处在有限使用阶段。主要的障碍来自浏览器支持不统一、自身实现的效率等等,不过可以在考虑兼容性的情况下选择局部选用。这方面今天qqmail和网易都给出了很好的例子,尤其是网易使用HTML5来实现全站变色这个“神奇”的需求的例子,全场都响起了会心的笑声和掌声。
从上海专程该来的google市场经理(忘记名字),则从市场的角度给大家上了一课,非常的有用。跳出技术的禁锢,从产品和市场的角度来重新审视这个世界,相信大家都会有新发现。今天他提到的两个例子都非常有说服力:一个是每天新增50w下载的一个团队,本身并不是特别在意这些下载量,而是关心活跃用户数和用户反馈,把所有一星反馈逐条研读,确实存在的问题一定改进;一个是在市场已经100多款闹钟的情况下,依然选择闹钟作为产品方向,并且做到单款产品的下载量就达到“北京白领中高收入水平”。这说明只要用心打造满足用户需要、高质量的产品,就无须刻意追求市场,相信好产品自己会召唤用户。
演讲技巧。今天感觉网易邮箱的张极的演讲最好,讲解深入浅出,slide也做的非常的生动。而个别演讲人,技术水准应该是毋庸置疑的,但讲起来节奏拖沓,例子也准备的不充分,看来演讲也是一门艺术,要修炼才行,不能茶壶里装饺子,肚里有货,嘴里倒不出阿。
流水账记完,看变形金刚去咯 ;-)
ps, 更多信息请前往珠三角技术沙龙官方网站: techparty.org

P1110466P1110467P1110468P1110469

2011.07.24 珠三角技术沙龙HTML5专场, a set on Flickr.

一些感受:

  1. Web仍然有很强的生命力。虽然最近有一波波的Web已死的言论,但是正如@DHH所说,Web是十年来沉淀下证明最有生命力的技术,虽然一再有人宣称它已死,但事实证明它的生命力超乎想象。随着HTML5为它增加更加强大的表达能力,Web应该会焕发更强的光彩。
  2. HTML5目前还处在有限使用阶段。主要的障碍来自浏览器支持不统一、自身实现的效率等等,不过可以在考虑兼容性的情况下选择局部选用。这方面今天qqmail和网易都给出了很好的例子,尤其是网易使用HTML5来实现全站变色这个“神奇”的需求的例子,全场都响起了会心的笑声和掌声。
  3. 从上海专程该来的google市场经理(忘记名字),则从市场的角度给大家上了一课,非常的有用。跳出技术的禁锢,从产品和市场的角度来重新审视这个世界,相信大家都会有新发现。今天他提到的两个例子都非常有说服力:一个是每天新增50w下载的一个团队,本身并不是特别在意这些下载量,而是关心活跃用户数和用户反馈,把所有一星反馈逐条研读,确实存在的问题一定改进;一个是在市场已经100多款闹钟的情况下,依然选择闹钟作为产品方向,并且做到单款产品的下载量就达到“北京白领中高收入水平”。这说明只要用心打造满足用户需要、高质量的产品,就无须刻意追求市场,相信好产品自己会召唤用户。
  4. 演讲技巧。今天感觉网易邮箱的张极的演讲最好,讲解深入浅出,slide也做的非常的生动。而个别演讲人,技术水准应该是毋庸置疑的,但讲起来节奏拖沓,例子也准备的不充分,看来演讲也是一门艺术,要修炼才行,不能茶壶里装饺子,肚里有货,嘴里倒不出阿。

流水账记完,看变形金刚去咯 ;-)

ps, 更多信息请前往珠三角技术沙龙官方网站: techparty.org


Jul 16

Test basic auth with Cucumber

The senario

Scenario: admin dashboard
  Given I perform HTTP authentication as "admin/secret"
    And I am on "/admin"
  Then I should see "Dashboard"

The step

When /^I perform HTTP authentication as "([^\"]*)\/([^\"]*)"$/ do |name, password|
  if page.driver.respond_to?(:basic_auth)
    page.driver.basic_auth(name, password)
  elsif page.driver.respond_to?(:basic_authorize)
    page.driver.basic_authorize(name, password)
  elsif page.driver.respond_to?(:browser) && page.driver.browser.respond_to?(:basic_authorize)
    page.driver.browser.basic_authorize(name, password)
  else
    raise "I don't know how to log in!"
  end
end

from the discussion here:
http://stackoverflow.com/questions/4329985/http-basic-auth-for-capybara

When using selenium driver, according to their wiki

How do I use Selenium to login to sites that require HTTP basic authentication (where the browser makes a modal dialog asking for credentials)?

Use a username and password in the URL, as described in RFC 1738:

open  http://myusername:myuserpassword@myexample.com/blah/blah/blah

Note that on Internet Explorer this won’t work, since Microsoft has disabled usernames/passwords in URLs in IE. However, you can add that functionality back in by modifying your registry, as described in the linked KB article. Set an “iexplore.exe” DWORD to 0 in HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_HTTP_USERNAME_PASSWORD_DISABLE.

If you don’t want to modify the registry yourself, you can always just use Selenium Remote Control, which automatically sets that that registry key for you as of version 0.9.2.

Also the discussion on github:

iast opened this issue January 02, 2011

HTTP Basic Authentication for Selenium driver

No milestone

No one is assigned

Hi, I’m using Cucumber+Capybara+Selenium to test the app that relies on HTTP Basic Authentication. There is a way to use this kind of auth by calling page.driver.basic_authorize(user, pass) method.

But when I mark the Scenario with @javascript tag (enabling Selenium driver) I get an error:
undefined method `basic_authorize’ for #Capybara::Driver::Selenium:0x000000041ea880 (NoMethodError)

 jnicklas commented

January 09, 2011

You’re accessing a private API and calling through to a method which is specific to the rack-test driver. Selenium doesn’t support basic authentication other than through the URL afaik, which is why we don’t have an API for it in Capybara in the first place.

 iast commented

January 12, 2011

It’s a pity. It would be nice to have a cross-driver way of using basic auth.

But, anyway, I solved the problem in my case by using URL to identify current user and session to persist current user between requests (when Selenium is active only).

Thanks for the reply.

The only way would be something like “http://myusername:myuserpassword@myexample.com/blah/blah/blah”


Jul 7

iTerm2

 

早上在twitter看到:

robinlu iTerm2 发布1.0了,最近一直在用。我比较喜欢的功能主要是可分割窗口,窗口内容可搜索。”

下载试了一下,非常惊喜,可分割窗口和搜索功能确实很实用,感谢robinlu的推荐。

more: http://www.iterm2.com/

Update(2011-07-16): iTem2 到行首和行尾的快捷键是 fn + 左右方向键,和terminal中默认的fn + shift + 左右方向键不同。