kuaiqian 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,58 @@
1
+ 快钱
2
+ ======
3
+
4
+ 用于支持快钱(http://99bill.com/)支付网关的Rails插件。
5
+ 演示站点:http://kqdemo.zaituu.com
6
+ 演示代码:http://github.com/yzhang/kuaiqian_demo
7
+
8
+ 安装
9
+ =======
10
+
11
+ $ script/plugin install git://github.com/yzhang/kuaiqian.git
12
+
13
+ 集成
14
+ =======
15
+
16
+ 1. 安装完成后,插件会自动在你的应用的config目录创建一个kuaiqian.yml文件,默认是快钱提供的测试帐号(关于测试帐号的使用方法请参看测试章节)
17
+ 将kuaiqian.yml中的商户ID和密钥替换为快钱提供给你的真实ID和密钥然后进行下一步。
18
+
19
+ 2. 下面的代码会创建一笔新订单:
20
+
21
+ @request = Kuaiqian::Request.new('产品名称', # 产品名称
22
+ 1, # 订单ID,必须全局唯一
23
+ Time.now.strftime("%Y%m%d%H%M%S"), # 订单生成时间,格式为20091104174132
24
+ 4500, # 订单金额,以分为单位
25
+ 'http://return', # 通知地址,用户支付成功后快钱会通过此地址通知商户支付结果
26
+ '00', # 支付类型,00显示所有方式,10只显示银行卡方式,11只显示电话银行方式,12只显示快钱帐户支付方式,13只显示线下方式
27
+ 'attach') #自定义数据,会在返回URL中原样返回
28
+ redirect_to @request.url
29
+
30
+ 上面的代码会将用户重定向到快钱的支付页面。
31
+
32
+ 3. 在用户完成支付后,快钱会调用你在支付请求中提供的返回URL:
33
+
34
+ @response = Kuaiqian::Response.new(params)
35
+ if @response.successful?
36
+ # 支付成功
37
+ else
38
+ # 支付失败
39
+ end
40
+
41
+ 注意,快钱可能会多次调用你的返回URL,并将结果展现给用户,因此你的代码要考虑多次执行后的输出对用户的有好度。
42
+
43
+ 测试
44
+ =======
45
+
46
+ 要使用快钱提供的测试ID,需要修改你的hosts文件,将快钱的域名指向测试服务器:
47
+
48
+ 218.242.247.5 www.99bill.com
49
+
50
+ 然后使用下面的帐号进行支付:
51
+
52
+ 用户名:kquser02@sina.com
53
+ 密码:99bill
54
+
55
+ ----------------
56
+ 如果在使用中遇到任何的问题或者建议,欢迎和我联系: zhangyuanyi@gmail.com
57
+
58
+ Copyright (c) 2009 Yuanyi Zhang, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc 'Generate documentation for the kuaiqian plugin.'
8
+ Rake::RDocTask.new(:rdoc) do |rdoc|
9
+ rdoc.rdoc_dir = 'rdoc'
10
+ rdoc.title = 'Tenpay'
11
+ rdoc.options << '--line-numbers' << '--inline-source'
12
+ rdoc.rdoc_files.include('README')
13
+ rdoc.rdoc_files.include('lib/**/*.rb')
14
+ end
15
+
16
+ begin
17
+ require 'jeweler'
18
+ Jeweler::Tasks.new do |s|
19
+ s.name = "kuaiqian"
20
+ s.summary = "A Ruby wrapper of Kauqian(快钱) Payment API"
21
+ s.email = "zhangyuanyi@gmail.com"
22
+ s.homepage = "http://github.com/yzhang/kuaiqian"
23
+ s.description = "A Ruby wrapper of Kauqian(快钱) Payment API"
24
+ s.authors = ["Yuanyi Zhang"]
25
+ s.files = FileList["[A-Z]*", "{lib}/**/*", '.gitignore']
26
+ end
27
+ rescue LoadError
28
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
29
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,11 @@
1
+ require 'rails/generators'
2
+
3
+ class KuaiqianGenerator < Rails::Generators::Base
4
+ def install_douban
5
+ copy_file("kuaiqian.yml", 'config/kuaiqian.yml')
6
+ end
7
+
8
+ def self.source_root
9
+ File.join(File.dirname(__FILE__), 'templates')
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ test:
2
+ spid: '1001153656201'
3
+ key: 'ZUZNJB8MF63GA83J'
4
+ development:
5
+ spid: '1001153656201'
6
+ key: 'ZUZNJB8MF63GA83J'
7
+ production:
8
+ spid: '1001153656201'
9
+ key: 'ZUZNJB8MF63GA83J'
data/lib/kuaiqian.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'kuaiqian/config'
2
+ require 'kuaiqian/request'
3
+ require 'kuaiqian/response'
@@ -0,0 +1,18 @@
1
+ module Kuaiqian
2
+ class Config
3
+ class << self
4
+ def spid; config['spid']; end
5
+ def key; config['key']; end
6
+
7
+ def config
8
+ @@config ||= lambda do
9
+ require 'yaml'
10
+ filename = "#{Rails.root}/config/kuaiqian.yml"
11
+ file = File.open(filename)
12
+ yaml = YAML.load(file)
13
+ return yaml[Rails.env]
14
+ end.call
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,73 @@
1
+ require 'digest/md5'
2
+ require 'cgi'
3
+ require 'iconv'
4
+
5
+ module Kuaiqian
6
+ class Request
7
+ GATEWAY_URL = "https://www.99bill.com/gateway/recvMerchantInfoAction.htm"
8
+ PARAMS = %w(inputCharset bgUrl version language signType merchantAcctId payerName payerContactType payerContact orderId orderAmount orderTime productName productNum productId productDesc ext1 ext2 payType redoFlag pid)
9
+
10
+ def initialize(product_name, order_id, order_time, total_fee,
11
+ return_url, pay_type='00', attach=nil, payer_name='用户')
12
+ @bank_type = 0
13
+ @fee_type = 1
14
+
15
+ @order_id = order_id.to_s
16
+ @total_fee = total_fee.to_i.to_s
17
+ @order_time = order_time.strftime("%Y%m%d%H%M%S")
18
+ @product_name = product_name
19
+
20
+ @pay_type = pay_type
21
+ @return_url = return_url
22
+ @ext1 = attach || ''
23
+
24
+ @payer_name = payer_name
25
+ end
26
+
27
+ def url
28
+ "#{GATEWAY_URL}?#{html_params}&signMsg=#{sign_msg}"
29
+ end
30
+
31
+ def input_charset; '1'; end
32
+ def bg_url; @return_url; end
33
+ def page_url; ''; end
34
+ def version; 'v2.0'; end
35
+ def language; '1'; end
36
+ def sign_type; '1'; end
37
+ def merchant_acct_id; Kuaiqian::Config.spid; end
38
+ def payer_name; @payer_name; end
39
+ def payer_contact_type; '1'; end
40
+ def payer_contact; ''; end
41
+ def key; Kuaiqian::Config.key; end
42
+ def order_id; @order_id; end
43
+ def order_amount; @total_fee; end
44
+ def order_time; @order_time; end
45
+ def product_name; @product_name; end
46
+ def product_num; '1'; end
47
+ def product_id; ''; end
48
+ def product_desc; ''; end
49
+ def ext1; @ext1; end
50
+ def ext2; ''; end
51
+ def pay_type; @pay_type; end
52
+ def redo_flag; "0"; end
53
+ def pid; ''; end
54
+
55
+ def sign_params
56
+ (PARAMS + ['key']).map do |param|
57
+ value = send(param.underscore)
58
+ value == '' ? nil : "#{param}=#{value}"
59
+ end.compact.join('&')
60
+ end
61
+
62
+ def sign_msg
63
+ Digest::MD5.hexdigest(sign_params).upcase
64
+ end
65
+
66
+ def html_params
67
+ PARAMS.map do |param|
68
+ value = send(param.underscore)
69
+ value == '' ? nil : "#{param}=#{CGI.escape(value)}"
70
+ end.compact.join('&')
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,61 @@
1
+ require 'digest/md5'
2
+ require 'cgi'
3
+ require 'iconv'
4
+
5
+ module Kuaiqian
6
+ class Response
7
+ attr_reader :order_id, :total_fee, :attach, :pay_time
8
+
9
+ BANK_NAMES = {'ICBC' => '中国工商银行', 'CMB' => '招商银行', 'CCB' => '中国建设银行',
10
+ 'ABC' => '中国农业银行', 'BOC_SH' => '中国银行(上海)', 'BOC_GZ' => '中国银行(广州)',
11
+ 'SPDB' => '上海浦东发展银行', 'BCOM' => '交通银行', 'CMBC' => '中国民生银行',
12
+ 'SDB' => '深圳发展银行', 'GDB' => '广东发展银行', 'CITIC' => '中信银行',
13
+ 'HXB' => '华夏银行', 'CIB' => '兴业银行', 'GZRCC' => '广州市农村信用合作社',
14
+ 'GZCB' => '广州市商业银行', 'SHRCC' => '上海农村商业银行', 'CPSRB' => '中国邮政储蓄',
15
+ 'CEB' => '中国光大银行', 'BOB' => '北京银行', 'CBHB' => '渤海银行',
16
+ 'BJRCB' => '北京农村商业银行', 'CNPY' => '中国银联'}
17
+
18
+ SIGN_PARAMS = %w(merchantAcctId version language signType payType bankId orderId orderTime orderAmount dealId bankDealId dealTime payAmount fee ext1 ext2 payResult errCode key)
19
+ def initialize(params)
20
+ @params = params
21
+ end
22
+
23
+ def successful?
24
+ pay_result == '10' && valid_sign?
25
+ end
26
+
27
+ def valid_sign?
28
+ sign_msg == Digest::MD5.hexdigest(sign_params).upcase
29
+ end
30
+
31
+ def sign_params
32
+ SIGN_PARAMS.map do |param|
33
+ value = send(param.underscore)
34
+ value == '' ? nil : "#{param}=#{value}"
35
+ end.compact.join('&')
36
+ end
37
+
38
+ def version; @params[:version]; end
39
+ def language; @params[:language]; end
40
+ def sign_type; @params[:signType]; end
41
+ def merchant_acct_id; @params[:merchantAcctId]; end
42
+ def key; Kuaiqian::Config.key; end
43
+ def order_id; @params[:orderId]; end
44
+ def order_amount; @params[:orderAmount]; end
45
+ def order_time; @params[:orderTime]; end
46
+ def deal_id; @params[:dealId]; end
47
+ def bank_deal_id; @params[:bankDealId]; end
48
+ def deal_time; @params[:dealTime]; end
49
+ def pay_amount; @params[:payAmount]; end
50
+ def fee; @params[:fee]; end
51
+ def attach; @params[:ext1]; end
52
+ def ext1; @params[:ext1]; end
53
+ def ext2; @params[:ext1]; end
54
+ def pay_result; @params[:payResult]; end
55
+ def err_code; @params[:errCode]; end
56
+ def sign_msg; @params[:signMsg]; end
57
+ def pay_type; @params[:payType]; end
58
+ def bank_id; @params[:bankId]; end
59
+ def bank_name; BANK_NAMES[bank_id]; end
60
+ end
61
+ end
@@ -0,0 +1,105 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
3
+ require 'spec'
4
+ require "kuaiqian"
5
+
6
+ describe Kuaiqian::Request do
7
+ before do
8
+ Kuaiqian::Config.stub!(:spid).and_return('1001153656201')
9
+ Kuaiqian::Config.stub!(:key).and_return('ZUZNJB8MF63GA83J')
10
+
11
+ time = Time.local(2009, 10, 28, 12, 03, 45)
12
+ @request = Kuaiqian::Request.new('productName', '20091028120345', time,
13
+ 2, 'http://return', '00')
14
+ @order_time = "20091028120345"
15
+
16
+ @valid_params = "inputCharset=1&bgUrl=http%3A%2F%2Freturn&version=v2.0&language=1&signType=1&merchantAcctId=1001153656201&payerName=payerName&payerContactType=1&orderId=20091028120345&orderAmount=2&orderTime=20091028120345&productName=productName&productNum=1&payType=00&redoFlag=0"
17
+ @valid_sign_params = "inputCharset=1&bgUrl=http://return&version=v2.0&language=1&signType=1&merchantAcctId=1001153656201&payerName=payerName&payerContactType=1&orderId=20091028120345&orderAmount=2&orderTime=20091028120345&productName=productName&productNum=1&payType=00&redoFlag=0&key=ZUZNJB8MF63GA83J"
18
+ end
19
+
20
+ describe "url generation" do
21
+ it "should generate sign_params correctly" do
22
+ @request.sign_params.should == @valid_sign_params
23
+ end
24
+
25
+ it "should construct url params correctly" do
26
+ @request.params.should == @valid_params
27
+ end
28
+ end
29
+
30
+ describe "basic params" do
31
+ it "should set input charset to 1" do
32
+ @request.input_charset.should == '1'
33
+ end
34
+
35
+ it "should set page url empty" do
36
+ @request.page_url.should == ''
37
+ end
38
+
39
+ it "should set bg_url properly" do
40
+ @request.bg_url.should == 'http://return'
41
+ end
42
+
43
+ it "should set version to v2.0" do
44
+ @request.version.should == 'v2.0'
45
+ end
46
+
47
+ it "should set language to 1" do
48
+ @request.language.should == '1'
49
+ end
50
+
51
+ it "should set signType to '1'" do
52
+ @request.sign_type.should == '1'
53
+ end
54
+
55
+ it "should set merchantAcctId properly" do
56
+ @request.merchant_acct_id.should == '1001153656201'
57
+ end
58
+
59
+ it "should set payer info empty" do
60
+ @request.payer_name.should == 'payerName'
61
+ @request.payer_contact_type.should == '1'
62
+ @request.payer_contact.should == ''
63
+ end
64
+
65
+ it "should set key properly" do
66
+ @request.key.should == 'ZUZNJB8MF63GA83J'
67
+ end
68
+
69
+ it "should set order id properly" do
70
+ @request.order_id.should == '20091028120345'
71
+ end
72
+
73
+ it "should set order amount properly" do
74
+ @request.order_amount.should == '2'
75
+ end
76
+
77
+ it "should set order time properly" do
78
+ @request.order_time.should == '20091028120345'
79
+ end
80
+
81
+ it "should set product info properly" do
82
+ @request.product_name.should == 'productName'
83
+ @request.product_num.should == '1'
84
+ @request.product_id.should == ''
85
+ @request.product_desc.should == ''
86
+ end
87
+
88
+ it "should set ext properly" do
89
+ @request.ext1.should == ''
90
+ @request.ext2.should == ''
91
+ end
92
+
93
+ it "should set payType properly" do
94
+ @request.pay_type.should == '00'
95
+ end
96
+
97
+ it "should set redo flag to zero" do
98
+ @request.redo_flag.should == '0'
99
+ end
100
+
101
+ it "should set pid empty" do
102
+ @request.pid.should == ''
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,75 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
3
+ require 'spec'
4
+ require "kuaiqian"
5
+
6
+ describe Kuaiqian::Response do
7
+ before(:each) do
8
+ Kuaiqian::Config.stub!(:spid).and_return('1001153656201')
9
+ Kuaiqian::Config.stub!(:key).and_return('ZUZNJB8MF63GA83J')
10
+ end
11
+
12
+ it "should be a valid response with valid params" do
13
+ response = Kuaiqian::Response.new(params)
14
+ response.should be_successful
15
+ end
16
+
17
+ it "should be an invalid response with wrong pay result" do
18
+ response = Kuaiqian::Response.new(params(:payResult => '00'))
19
+ response.should_not be_successful
20
+ end
21
+
22
+ it "should be an invalid response with wrong sign msg" do
23
+ response = Kuaiqian::Response.new(params(:signMsg => 'wrong'))
24
+ response.should_not be_successful
25
+ end
26
+
27
+ def params(options={})
28
+ {
29
+ :merchantAcctId => "1001153656201",
30
+ :payAmount =>"100",
31
+ :bankId => "",
32
+ :language => "1",
33
+ :orderId =>"20091103164410",
34
+ :id => "1",
35
+ :signType =>"1",
36
+ :orderTime =>"20091103164410",
37
+ :payResult =>"10",
38
+ :version =>"v2.0",
39
+ :payType =>"12",
40
+ :fee =>"1",
41
+ :dealId =>"5696344",
42
+ :bankDealId =>"",
43
+ :dealTime =>"20091104004452",
44
+ :errCode =>"",
45
+ :signMsg =>"361A184B9157BBEAA3627B1B0D6412D6",
46
+ :ext1 =>"",
47
+ :orderAmount =>"100",
48
+ :ext2 =>""
49
+ }.merge(options)
50
+ end
51
+
52
+ # def params(options={})
53
+ # {:merchantAcctId => 'testid',
54
+ # :version => 'v2.0',
55
+ # :language => '1',
56
+ # :signType => '1',
57
+ # :payType => '00',
58
+ # :bankId => 'ICBC',
59
+ # :orderId => '1',
60
+ # :orderTime => '20091021123200',
61
+ # :orderAmount => '4500',
62
+ # :dealId => 'testdealid',
63
+ # :bankDealId => 'testbankdeal',
64
+ # :dealTime => '20091021123203',
65
+ # :payAmount => '4500',
66
+ # :fee => '100',
67
+ # :ext1 => 'nil',
68
+ # :ext2 => '',
69
+ # :payResult => '10',
70
+ # :errCode => '',
71
+ # :key => '',
72
+ # :signMsg => '1D060818424C1335C157B1FA5A9A7551'
73
+ # }.merge(options)
74
+ # end
75
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kuaiqian
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Yuanyi Zhang
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-04 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: "A Ruby wrapper of Kauqian(\xE5\xBF\xAB\xE9\x92\xB1) Payment API"
22
+ email: zhangyuanyi@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README
29
+ files:
30
+ - MIT-LICENSE
31
+ - README
32
+ - Rakefile
33
+ - VERSION
34
+ - lib/generators/kuaiqian/kuaiqian_generator.rb
35
+ - lib/generators/kuaiqian/templates/kuaiqian.yml
36
+ - lib/kuaiqian.rb
37
+ - lib/kuaiqian/config.rb
38
+ - lib/kuaiqian/request.rb
39
+ - lib/kuaiqian/response.rb
40
+ has_rdoc: true
41
+ homepage: http://github.com/yzhang/kuaiqian
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --charset=UTF-8
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.6
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: "A Ruby wrapper of Kauqian(\xE5\xBF\xAB\xE9\x92\xB1) Payment API"
70
+ test_files:
71
+ - spec/request_spec.rb
72
+ - spec/response_spec.rb