tenpay 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ test
2
+ pkg
3
+ .DS_Store
@@ -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,57 @@
1
+ Tenpay
2
+ ======
3
+
4
+ A Ruby on Rails plugin for Tenpay(财付通).
5
+
6
+ 使用
7
+ =======
8
+
9
+ 1. 修改Gemfile:
10
+
11
+ gem "tenpay"
12
+
13
+ 2. 生成配置文件:
14
+
15
+ $ script/rails g tenpay
16
+
17
+ 这个命令会在你的应用的config目录创建一个tenpay.yml文件,默认是快钱提供的测试帐号(关于测试帐号的使用方法请参看测试章节)
18
+ 将tenpay.yml中的商户ID和密钥替换为财付通提供给你的真实ID和密钥然后进行下一步。
19
+
20
+ 3. 下面的代码会创建一笔新订单:
21
+
22
+ @request = Tenpay::Request.new(
23
+ 'test product', # 产品名称
24
+ 1, # 订单编号,必须保持唯一
25
+ Time.now, # 订单生成时间
26
+ 4500, # 订单价格,以分为单位
27
+ 'http://return', # 完成支付后的回调地址
28
+ request.remote_ip, # 用于IP验证,开发模式可忽略
29
+ 'attach data') # 自定义数据
30
+ redirect_to @request.url
31
+
32
+ 上面的代码会将用户重定向到财付通的支付页面。
33
+
34
+ 4. 在用户完成支付后,财付通会调用你在支付请求中提供的返回URL:
35
+
36
+ @response = Tenpay::Response.new(params)
37
+ if @response.successful?
38
+ # 支付成功
39
+ else
40
+ # 失败
41
+ end
42
+
43
+ 注意,快钱可能会多次调用你的返回URL,并将结果展现给用户,因此你的代码要考虑多次执行后的输出对用户的有好度。
44
+
45
+ 查询
46
+ ========
47
+
48
+ 如果需要查询订单支付状态,可以使用下面的接口:
49
+
50
+ @query = Tenpay::Query.new(1, # 订单id
51
+ Time.now) # 订单日期
52
+ @query.response.successful? # true => 已支付, false => 未支付
53
+
54
+ ----------------
55
+ 如果在使用中遇到任何的问题或者建议,欢迎和我联系: zhangyuanyi@gmail.com
56
+
57
+ Copyright (c) 2009 Yuanyi Zhang, released under the MIT license
@@ -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 tenpay 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 = "tenpay"
20
+ s.summary = "A Ruby wrapper of Tenpay(财付通) Payment API"
21
+ s.email = "zhangyuanyi@gmail.com"
22
+ s.homepage = "http://github.com/yzhang/tenpay"
23
+ s.description = "A Ruby wrapper of Tenpay(财付通) 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,9 @@
1
+ test:
2
+ spid: '1900000109'
3
+ key: '8934e7d15453e97507ef794cf7b0519d'
4
+ development:
5
+ spid: '1900000109'
6
+ key: '8934e7d15453e97507ef794cf7b0519d'
7
+ production:
8
+ spid: '1900000109'
9
+ key: '8934e7d15453e97507ef794cf7b0519d'
@@ -0,0 +1,11 @@
1
+ require 'rails/generators'
2
+
3
+ class TenpayGenerator < Rails::Generators::Base
4
+ def install_douban
5
+ copy_file("tenpay.yml", 'config/tenpay.yml')
6
+ end
7
+
8
+ def self.source_root
9
+ File.join(File.dirname(__FILE__), 'templates')
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require 'tenpay/config'
2
+ require 'tenpay/request'
3
+ require 'tenpay/response'
4
+ require 'tenpay/query'
5
+ require 'tenpay/query_response'
@@ -0,0 +1,18 @@
1
+ module Tenpay
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/tenpay.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,54 @@
1
+ require 'digest/md5'
2
+ require 'cgi'
3
+ require 'iconv'
4
+ require 'net/http'
5
+ require 'uri'
6
+ require 'hpricot'
7
+
8
+ module Tenpay
9
+ class Query
10
+ GATEWAY_URL = "http://mch.tenpay.com/cgi-bin/cfbi_query_order_v3.cgi"
11
+
12
+ def initialize(order_id, date, attach=nil, charset='UTF-8')
13
+ @cmdno = 1
14
+ @date = date.strftime("%Y%m%d")
15
+ @spid = Tenpay::Config.spid
16
+ @key = Tenpay::Config.key
17
+
18
+ @order_id = order_id.to_i
19
+ @attach = attach || 'nil'
20
+ @charset = charset
21
+ end
22
+
23
+ def response
24
+ @response ||= QueryResponse.new(Net::HTTP.get(URI.parse("#{GATEWAY_URL}?#{params}")))
25
+ end
26
+
27
+ def transaction_id
28
+ @transaction_id ||= "%s%s%010d" % [@spid, @date, @order_id]
29
+ end
30
+
31
+ def sign
32
+ @sign ||= Digest::MD5.hexdigest(sign_params).upcase
33
+ end
34
+
35
+ def sign_params
36
+ @sign_params ||= generate_sign_params
37
+ end
38
+
39
+ private
40
+ def params
41
+ @params ||= generate_params
42
+ end
43
+
44
+ def generate_params
45
+ "cmdno=2&date=#{@date}&bargainor_id=#{@spid}&transaction_id=#{transaction_id}&sp_billno=#{@order_id}&attach=#{@attach}&" +
46
+ "output_xml=1&charset=#{@charset}&sign=#{sign}"
47
+ end
48
+
49
+ def generate_sign_params
50
+ "attach=#{@attach}&bargainor_id=#{@spid}&charset=#{@charset}&cmdno=2&date=#{@date}&output_xml=1&" +
51
+ "sp_billno=#{@order_id}&transaction_id=#{transaction_id}&key=#{@key}"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,65 @@
1
+ module Tenpay
2
+ class QueryResponse
3
+ def initialize(response)
4
+ @body = response
5
+ end
6
+
7
+ def valid?
8
+ sign == Digest::MD5.hexdigest(sign_params).upcase
9
+ end
10
+
11
+ def successful?
12
+ valid? && pay_result == '0'
13
+ end
14
+
15
+ def doc
16
+ @doc ||= Hpricot(@body)
17
+ end
18
+
19
+ def attach
20
+ @attach ||= (doc / 'attach').inner_text
21
+ end
22
+
23
+ def cmdno
24
+ @cmdno ||= (doc / 'cmdno').inner_text
25
+ end
26
+
27
+ def date
28
+ @date ||= (doc / 'date').inner_text
29
+ end
30
+
31
+ def fee_type
32
+ @fee_type ||= (doc / 'fee_type').inner_text
33
+ end
34
+
35
+ def pay_info
36
+ @pay_info ||= (doc / 'pay_info').inner_text
37
+ end
38
+
39
+ def pay_result
40
+ @pay_result ||= (doc / 'pay_result').inner_text
41
+ end
42
+
43
+ def order_id
44
+ @order_id ||= (doc / 'sp_billno').inner_text
45
+ end
46
+
47
+ def total_fee
48
+ @total_fee ||= (doc / 'total_fee').inner_text
49
+ end
50
+
51
+ def transaction_id
52
+ @transaction_id ||= (doc / 'transaction_id').inner_text
53
+ end
54
+
55
+ def sign
56
+ @sign ||= (doc / 'sign').inner_text
57
+ end
58
+ private
59
+ def sign_params
60
+ "attach=#{attach}&bargainor_id=#{Tenpay::Config.spid}&cmdno=#{cmdno}&date=#{date}&fee_type=#{fee_type}" +
61
+ "&pay_info=#{pay_info}&pay_result=#{pay_result}&sp_billno=#{order_id}&total_fee=#{total_fee}&" +
62
+ "transaction_id=#{transaction_id}&key=#{Tenpay::Config.key}"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,64 @@
1
+ require 'digest/md5'
2
+ require 'cgi'
3
+ require 'iconv'
4
+
5
+ module Tenpay
6
+ class Request
7
+ GATEWAY_URL = "https://www.tenpay.com/cgi-bin/v1.0/pay_gate.cgi"
8
+
9
+ def initialize(description, order_id, created_at, total_fee,
10
+ return_url, purchaser_ip='', attach=nil)
11
+ @cmdno = 1
12
+ @date = created_at.strftime("%Y%m%d")
13
+ @bank_type = 0
14
+ @fee_type = 1
15
+
16
+ @desc = Iconv.iconv('gbk', 'utf-8', description).join
17
+ @spid = Tenpay::Config.spid
18
+ @key = Tenpay::Config.key
19
+
20
+ @order_id = order_id.to_i
21
+ @total_fee = total_fee.to_i
22
+ @return_url = return_url
23
+ @attach = attach || 'nil'
24
+
25
+ @purchaser_ip = (RAILS_ENV == 'production' ? purchaser_ip : '')
26
+ end
27
+
28
+ def transaction_id
29
+ @transaction_id ||= "%s%s%010d" % [@spid, @date, @order_id]
30
+ end
31
+
32
+ def sign
33
+ @sign ||= Digest::MD5.hexdigest(sign_params).upcase
34
+ end
35
+
36
+ def sign_params
37
+ @sign_params ||= generate_sign_params
38
+ end
39
+
40
+ def params
41
+ @params ||= generate_params
42
+ end
43
+
44
+ def url
45
+ "#{GATEWAY_URL}?#{params}&sign=#{sign}"
46
+ end
47
+
48
+ private
49
+ def generate_params
50
+ params = "cmdno=#{@cmdno}&date=#{@date}&bargainor_id=#{@spid}&transaction_id=#{transaction_id}" +
51
+ "&sp_billno=#{@order_id}&total_fee=#{@total_fee}&fee_type=#{@fee_type}" +
52
+ "&return_url=#{@return_url}&attach=#{CGI.escape(@attach)}&desc=#{CGI.escape(@desc)}"
53
+ params << "&spbill_create_ip=#{@purchaser_ip}" unless @purchaser_ip.nil? || @purchaser_ip == ''
54
+ params
55
+ end
56
+ def generate_sign_params
57
+ sign_params = "cmdno=#{@cmdno}&date=#{@date}&bargainor_id=#{@spid}&transaction_id=#{transaction_id}" +
58
+ "&sp_billno=#{@order_id}&total_fee=#{@total_fee}&fee_type=#{@fee_type}" +
59
+ "&return_url=#{@return_url}&attach=#{CGI.escape(@attach)}"
60
+ sign_params << "&spbill_create_ip=#{@purchaser_ip}" unless @purchaser_ip.nil? || @purchaser_ip == ''
61
+ sign_params << "&key=#{@key}"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,41 @@
1
+ require 'digest/md5'
2
+ require 'cgi'
3
+ require 'iconv'
4
+
5
+ module Tenpay
6
+ class Response
7
+ attr_reader :order_id, :total_fee, :attach, :pay_time
8
+
9
+ def initialize(params)
10
+ @cmdno = params[:cmdno] || ''
11
+ @date = params[:date] || ''
12
+ @fee_type = params[:fee_type] || ''
13
+ @pay_info = params[:pay_info] || ''
14
+ @pay_result = params[:pay_result] || ''
15
+ @pay_time = Time.at((params[:pay_time] || '0').to_i)
16
+ @sign = params[:sign] || ''
17
+ @order_id = (params[:sp_billno] || '').to_i
18
+ @transaction_id = params[:transaction_id] || ''
19
+ @total_fee = params[:total_fee] || ''
20
+ @attach = params[:attach] || ''
21
+
22
+ @spid = Tenpay::Config.spid
23
+ @key = Tenpay::Config.key
24
+ end
25
+
26
+ def successful?
27
+ @pay_info == 'OK' && @pay_result == '0' && valid_sign?
28
+ end
29
+
30
+ def valid_sign?
31
+ @sign == Digest::MD5.hexdigest(sign_params).upcase
32
+ end
33
+
34
+ private
35
+ def sign_params
36
+ @sign_params ||= "cmdno=#{@cmdno}&pay_result=#{@pay_result}&date=#{@date}&transaction_id=#{@transaction_id}" +
37
+ "&sp_billno=#{@order_id}&total_fee=#{@total_fee}&fee_type=#{@fee_type}" +
38
+ "&attach=#{CGI.escape(@attach)}&key=#{@key}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,80 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require 'spec'
3
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
4
+ require "tenpay"
5
+ require 'net/http'
6
+
7
+ describe Tenpay::Request do
8
+ before do
9
+ @query = Tenpay::Query.new(1, Date.new(2009, 9, 12))
10
+ @sign_params = "attach=nil&bargainor_id=1900000109&charset=UTF-8&cmdno=2&date=20090912&output_xml=1&" +
11
+ "sp_billno=1&transaction_id=1900000109200909120000000001&key=8934e7d15453e97507ef794cf7b0519d"
12
+ @sign = Digest::MD5.hexdigest(@sign_params).upcase
13
+ @params = "cmdno=2&date=20090912&bargainor_id=1900000109&transaction_id=1900000109200909120000000001" +
14
+ "&sp_billno=1&attach=nil&output_xml=1&charset=UTF-8&sign=#{@sign}"
15
+ end
16
+
17
+ it "should return a Query Response object." do
18
+ @uri = mock('uri')
19
+ URI.should_receive(:parse).with("#{Tenpay::Query::GATEWAY_URL}?#{@params}").and_return(@uri)
20
+ Net::HTTP.should_receive(:get).with(@uri)
21
+ Tenpay::QueryResponse.stub!(:new).and_return(mock('response'))
22
+ @query.response
23
+ end
24
+
25
+ it "should construct sign params properly." do
26
+ @query.sign_params.should == @sign_params
27
+ end
28
+ end
29
+
30
+ describe Tenpay::QueryResponse do
31
+ before do
32
+ @valid_response = VALID_XML
33
+ @invalid_response = ''
34
+ end
35
+
36
+ it "should be invalid with invalid response" do
37
+ @response = Tenpay::QueryResponse.new(@invalid_response)
38
+ @response.should_not be_valid
39
+ end
40
+
41
+ it "should be valid with valid response" do
42
+ @response = Tenpay::QueryResponse.new(@valid_response)
43
+ @response.attach.should == 'a'
44
+ @response.cmdno.should == '2'
45
+ @response.date.should == '20090315'
46
+ @response.fee_type.should == '1'
47
+ @response.pay_info.should == 'test'
48
+ @response.pay_result.should == '2006'
49
+ @response.order_id.should == '56212087'
50
+ @response.total_fee.should == '3000'
51
+ @response.transaction_id.should == '1900000108200903150056212087'
52
+ @response.sign.should == 'A8D50F1DC2E2E4CC83191AE39C702C27'
53
+
54
+ @response.should be_valid
55
+ end
56
+
57
+ VALID_XML = %(<?xml version="1.0" encoding="GB2312" ?>
58
+ <root>
59
+ <attach>a</attach>
60
+ <bank_type />
61
+ <bargainor_id>1900000108</bargainor_id>
62
+ <bill_no />
63
+ <charset>GB2312</charset>
64
+ <cmdno>2</cmdno>
65
+ <date>20090315</date>
66
+ <dump_html_file />
67
+ <fee_type>1</fee_type>
68
+ <output_xml>1</output_xml>
69
+ <pay_info>test</pay_info>
70
+ <pay_result>2006</pay_result>
71
+ <retcode>00</retcode>
72
+ <retmsg>交易成功</retmsg>
73
+ <return_url>http://192.168.1.129/bank/cft_return.aspx</return_url>
74
+ <sign>A8D50F1DC2E2E4CC83191AE39C702C27</sign>
75
+ <sp_billno>56212087</sp_billno>
76
+ <total_fee>3000</total_fee>
77
+ <transaction_id>1900000108200903150056212087</transaction_id>
78
+ </root>
79
+ )
80
+ end
@@ -0,0 +1,33 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require 'spec'
3
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
4
+ require "tenpay"
5
+
6
+ describe Tenpay::Request do
7
+ before do
8
+ @request = Tenpay::Request.new('test product', 1,
9
+ 4500, 'http://return', '', 'order_id=1')
10
+ today = Date.today
11
+ @date = "%d%02d%02d" % [today.year, today.month, today.day]
12
+ @params = "cmdno=1&date=#{@date}&bargainor_id=1900000109&" +
13
+ "transaction_id=1900000109#{@date}0000000001&sp_billno=1&total_fee=4500&fee_type=1" +
14
+ "&return_url=#{'http://return'}&attach=#{CGI.escape('order_id=1')}"
15
+ @sign = Digest::MD5.hexdigest("#{@params}&key=8934e7d15453e97507ef794cf7b0519d").upcase
16
+ end
17
+
18
+ it "should construct transaction correctly." do
19
+ @request.transaction_id.should == "1900000109#{@date}0000000001"
20
+ end
21
+
22
+ it "should construct params correctly." do
23
+ @request.params.should == "#{@params}&desc=test product"
24
+ end
25
+
26
+ it "should sign the params" do
27
+ @request.sign.should == @sign
28
+ end
29
+
30
+ it "should generate url for user paying" do
31
+ @request.url.should == "https://www.tenpay.com/cgi-bin/v1.0/pay_gate.cgi?#{@params}&desc=test product&sign=#{@sign}"
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require 'spec'
3
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
4
+ require "tenpay"
5
+
6
+ describe Tenpay::Response do
7
+ before do
8
+ @valid_params = {:cmdno => '1', :date => '20090818',
9
+ :fee_type => '1', :pay_info => 'OK', :pay_result => '0',
10
+ :pay_time => '1250554120',
11
+ :sign => 'D2171C4319EE7A7A8008F1A4479C8C93',
12
+ :sp_billno => '2',
13
+ :transaction_id => '1900000109200908180000000002',
14
+ :total_fee => '1',
15
+ :attach => 'nil'}
16
+ end
17
+
18
+ it "should be successful with valid params and key" do
19
+ Tenpay::Response.new(@valid_params).should be_successful
20
+ end
21
+
22
+ it "should not be successful with invalid params and key" do
23
+ @valid_params[:date] = '20100912'
24
+ Tenpay::Response.new(@valid_params).should_not be_successful
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tenpay
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-07-06 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: "A Ruby wrapper of Tenpay(\xE8\xB4\xA2\xE4\xBB\x98\xE9\x80\x9A) Payment API"
22
+ email: zhangyuanyi@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README
29
+ files:
30
+ - .gitignore
31
+ - MIT-LICENSE
32
+ - README
33
+ - Rakefile
34
+ - VERSION
35
+ - lib/generators/tenpay/templates/tenpay.yml
36
+ - lib/generators/tenpay/tenpay_generator.rb
37
+ - lib/tenpay.rb
38
+ - lib/tenpay/config.rb
39
+ - lib/tenpay/query.rb
40
+ - lib/tenpay/query_response.rb
41
+ - lib/tenpay/request.rb
42
+ - lib/tenpay/response.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/yzhang/tenpay
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --charset=UTF-8
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.3.6
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: "A Ruby wrapper of Tenpay(\xE8\xB4\xA2\xE4\xBB\x98\xE9\x80\x9A) Payment API"
73
+ test_files:
74
+ - spec/query_spec.rb
75
+ - spec/request_spec.rb
76
+ - spec/response_spec.rb