yl_alipay 0.15.2

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +5 -0
  4. data/CHANGELOG.md +101 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +810 -0
  8. data/Rakefile +9 -0
  9. data/alipay.gemspec +25 -0
  10. data/lib/alipay/app/service.rb +57 -0
  11. data/lib/alipay/app/sign.rb +36 -0
  12. data/lib/alipay/mobile/service.rb +27 -0
  13. data/lib/alipay/mobile/sign.rb +9 -0
  14. data/lib/alipay/notify.rb +19 -0
  15. data/lib/alipay/service.rb +239 -0
  16. data/lib/alipay/sign/dsa.rb +13 -0
  17. data/lib/alipay/sign/md5.rb +15 -0
  18. data/lib/alipay/sign/rsa.rb +18 -0
  19. data/lib/alipay/sign/rsa2.rb +18 -0
  20. data/lib/alipay/sign.rb +54 -0
  21. data/lib/alipay/utils.rb +19 -0
  22. data/lib/alipay/version.rb +3 -0
  23. data/lib/alipay/wap/notify.rb +13 -0
  24. data/lib/alipay/wap/service.rb +84 -0
  25. data/lib/alipay/wap/sign.rb +37 -0
  26. data/lib/alipay.rb +31 -0
  27. data/lib/yl_alipay.rb +1 -0
  28. data/test/alipay/app/service_test.rb +32 -0
  29. data/test/alipay/app/sign_test.rb +37 -0
  30. data/test/alipay/mobile/service_test.rb +16 -0
  31. data/test/alipay/mobile/sign_test.rb +7 -0
  32. data/test/alipay/notify_test.rb +35 -0
  33. data/test/alipay/service_test.rb +265 -0
  34. data/test/alipay/sign/md5_test.rb +20 -0
  35. data/test/alipay/sign/rsa2_test.rb +22 -0
  36. data/test/alipay/sign/rsa_test.rb +20 -0
  37. data/test/alipay/sign_test.rb +30 -0
  38. data/test/alipay/utils_test.rb +12 -0
  39. data/test/alipay/wap/notify_test.rb +38 -0
  40. data/test/alipay/wap/service_test.rb +67 -0
  41. data/test/alipay/wap/sign_test.rb +19 -0
  42. data/test/alipay_test.rb +11 -0
  43. data/test/test_helper.rb +34 -0
  44. data/yl_alipay.gemspec +25 -0
  45. metadata +159 -0
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/*_test.rb']
7
+ end
8
+
9
+ task :default => :test
data/alipay.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'alipay/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "alipay"
8
+ spec.version = Alipay::VERSION
9
+ spec.authors = ["Rei"]
10
+ spec.email = ["chloerei@gmail.com"]
11
+ spec.description = %q{An unofficial simple alipay gem}
12
+ spec.summary = %q{An unofficial simple alipay gem}
13
+ spec.homepage = "https://github.com/chloerei/alipay"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+ spec.add_development_dependency "webmock"
25
+ end
@@ -0,0 +1,57 @@
1
+ module Alipay
2
+ module App
3
+ module Service
4
+ GATEWAY_URL = 'https://openapi.alipay.com/gateway.do'
5
+
6
+ ALIPAY_PREPARE_PARAMS_REQUIRED_PARAMS = %w( method )
7
+ def self.prepare_params(params, options = {})
8
+ params = Utils.stringify_keys(params)
9
+ Alipay::Service.check_required_params(params, ALIPAY_PREPARE_PARAMS_REQUIRED_PARAMS)
10
+ key = options[:key] || Alipay.key
11
+ sign_type = (options[:sign_type] || :rsa2).to_s.upcase
12
+ params = {
13
+ 'method' => params['method'],
14
+ 'charset' => 'utf-8',
15
+ 'version' => '1.0',
16
+ 'timestamp' => Time.now.utc.strftime('%Y-%m-%d %H:%M:%S').to_s,
17
+ 'sign_type' => sign_type
18
+ }.merge(params)
19
+
20
+ string = Alipay::App::Sign.params_to_sorted_string(params)
21
+ sign = case sign_type
22
+ when 'RSA'
23
+ ::Alipay::Sign::RSA.sign(key, string)
24
+ when 'RSA2'
25
+ ::Alipay::Sign::RSA2.sign(key, string)
26
+ else
27
+ raise ArgumentError, "invalid sign_type #{sign_type}, allow value: 'RSA', 'RSA2'"
28
+ end
29
+ params.merge('sign' => sign)
30
+ end
31
+
32
+ ALIPAY_TRADE_APP_PAY_REQUIRED_PARAMS = %w( app_id biz_content notify_url )
33
+ def self.alipay_trade_app_pay(params, options = {})
34
+ params = Utils.stringify_keys(params)
35
+ Alipay::Service.check_required_params(params, ALIPAY_TRADE_APP_PAY_REQUIRED_PARAMS)
36
+ params = params.merge('method' => 'alipay.trade.app.pay')
37
+ params = prepare_params(params, options)
38
+ Alipay::App::Sign.params_to_encoded_string params
39
+ end
40
+
41
+ ALIPAY_TRADE_REFUND_REQUIRED_PARAMS = %w( app_id biz_content )
42
+ def self.alipay_trade_refund_url(params, options = {})
43
+ params = Utils.stringify_keys(params)
44
+ Alipay::Service.check_required_params(params, ALIPAY_TRADE_REFUND_REQUIRED_PARAMS)
45
+ params = params.merge('method' => 'alipay.trade.refund')
46
+ params = prepare_params(params, options)
47
+ request_uri(params)
48
+ end
49
+
50
+ def self.request_uri(params)
51
+ uri = URI(GATEWAY_URL)
52
+ uri.query = URI.encode_www_form(params)
53
+ uri
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,36 @@
1
+ require 'erb'
2
+
3
+ module Alipay
4
+ module App
5
+ module Sign
6
+ def self.verify?(params, options = {})
7
+ params = ::Alipay::Utils.stringify_keys(params)
8
+
9
+ sign_type = params.delete('sign_type').upcase
10
+ unless options[:sign_type].to_s.upcase == sign_type
11
+ raise "sign_type not match: params: #{params[:sign_type]} options: #{options[:sign_type]}"
12
+ end
13
+
14
+ sign = params.delete('sign')
15
+ string = ::Alipay::Sign.params_to_string(params)
16
+
17
+ case sign_type
18
+ when 'RSA'
19
+ ::Alipay::Sign::RSA.verify?(options[:key], string, sign)
20
+ when 'RSA2'
21
+ ::Alipay::Sign::RSA2.verify?(options[:key], string, sign)
22
+ else
23
+ false
24
+ end
25
+ end
26
+
27
+ def self.params_to_sorted_string(params)
28
+ params.sort.map { |key, value| %Q(#{key}=#{value.to_s}) }.join('&')
29
+ end
30
+
31
+ def self.params_to_encoded_string(params)
32
+ params.sort.map { |key, value| %Q(#{key}=#{ERB::Util.url_encode(value.to_s)}) }.join('&')
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module Alipay
2
+ module Mobile
3
+ module Service
4
+ MOBILE_SECURITY_PAY_REQUIRED_PARAMS = %w( notify_url out_trade_no subject total_fee body )
5
+ def self.mobile_securitypay_pay_string(params, options = {})
6
+ params = Utils.stringify_keys(params)
7
+ Alipay::Service.check_required_params(params, MOBILE_SECURITY_PAY_REQUIRED_PARAMS)
8
+ sign_type = options[:sign_type] || Alipay.sign_type
9
+ key = options[:key] || Alipay.key
10
+ raise ArgumentError, "only support RSA sign_type" if sign_type != 'RSA'
11
+
12
+ params = {
13
+ 'service' => 'mobile.securitypay.pay',
14
+ '_input_charset' => 'utf-8',
15
+ 'partner' => options[:pid] || Alipay.pid,
16
+ 'seller_id' => options[:pid] || Alipay.pid,
17
+ 'payment_type' => '1'
18
+ }.merge(params)
19
+
20
+ string = Alipay::Mobile::Sign.params_to_string(params)
21
+ sign = CGI.escape(Alipay::Sign::RSA.sign(key, string))
22
+
23
+ %Q(#{string}&sign="#{sign}"&sign_type="RSA")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module Alipay
2
+ module Mobile
3
+ module Sign
4
+ def self.params_to_string(params)
5
+ params.map { |key, value| %Q(#{key}="#{value}") }.join('&')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module Alipay
2
+ module Notify
3
+ def self.verify?(params, options = {})
4
+ params = Utils.stringify_keys(params)
5
+ pid = options[:pid] || Alipay.pid
6
+ Sign.verify?(params, options) && verify_notify_id?(pid, params['notify_id'])
7
+ end
8
+
9
+ def self.verify_notify_id?(pid, notify_id)
10
+ uri = URI("https://mapi.alipay.com/gateway.do")
11
+ uri.query = URI.encode_www_form(
12
+ 'service' => 'notify_verify',
13
+ 'partner' => pid,
14
+ 'notify_id' => notify_id
15
+ )
16
+ Net::HTTP.get(uri) == 'true'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,239 @@
1
+ module Alipay
2
+ module Service
3
+ GATEWAY_URL = 'https://mapi.alipay.com/gateway.do'
4
+
5
+ CREATE_PARTNER_TRADE_BY_BUYER_REQUIRED_PARAMS = %w( out_trade_no subject logistics_type logistics_fee logistics_payment price quantity )
6
+ def self.create_partner_trade_by_buyer_url(params, options = {})
7
+ params = Utils.stringify_keys(params)
8
+ check_required_params(params, CREATE_PARTNER_TRADE_BY_BUYER_REQUIRED_PARAMS)
9
+
10
+ params = {
11
+ 'service' => 'create_partner_trade_by_buyer',
12
+ '_input_charset' => 'utf-8',
13
+ 'partner' => options[:pid] || Alipay.pid,
14
+ 'seller_id' => options[:pid] || Alipay.pid,
15
+ 'payment_type' => '1'
16
+ }.merge(params)
17
+
18
+ request_uri(params, options).to_s
19
+ end
20
+
21
+ TRADE_CREATE_BY_BUYER_REQUIRED_PARAMS = %w( out_trade_no subject logistics_type logistics_fee logistics_payment price quantity )
22
+ def self.trade_create_by_buyer_url(params, options = {})
23
+ params = Utils.stringify_keys(params)
24
+ check_required_params(params, TRADE_CREATE_BY_BUYER_REQUIRED_PARAMS)
25
+
26
+ params = {
27
+ 'service' => 'trade_create_by_buyer',
28
+ '_input_charset' => 'utf-8',
29
+ 'partner' => options[:pid] || Alipay.pid,
30
+ 'seller_id' => options[:pid] || Alipay.pid,
31
+ 'payment_type' => '1'
32
+ }.merge(params)
33
+
34
+ request_uri(params, options).to_s
35
+ end
36
+
37
+ CREATE_DIRECT_PAY_BY_USER_REQUIRED_PARAMS = %w( out_trade_no subject )
38
+ def self.create_direct_pay_by_user_url(params, options = {})
39
+ params = Utils.stringify_keys(params)
40
+ check_required_params(params, CREATE_DIRECT_PAY_BY_USER_REQUIRED_PARAMS)
41
+
42
+ if Alipay.debug_mode? and params['total_fee'].nil? and (params['price'].nil? || params['quantity'].nil?)
43
+ warn("Alipay Warn: total_fee or (price && quantity) must be set")
44
+ end
45
+
46
+ params = {
47
+ 'service' => 'create_direct_pay_by_user',
48
+ '_input_charset' => 'utf-8',
49
+ 'partner' => options[:pid] || Alipay.pid,
50
+ 'seller_id' => options[:pid] || Alipay.pid,
51
+ 'payment_type' => '1'
52
+ }.merge(params)
53
+
54
+ request_uri(params, options).to_s
55
+ end
56
+
57
+ CREATE_DIRECT_PAY_BY_USER_WAP_REQUIRED_PARAMS = %w( out_trade_no subject total_fee )
58
+ def self.create_direct_pay_by_user_wap_url(params, options = {})
59
+ params = Utils.stringify_keys(params)
60
+ check_required_params(params, CREATE_DIRECT_PAY_BY_USER_WAP_REQUIRED_PARAMS)
61
+
62
+ params = {
63
+ 'service' => 'alipay.wap.create.direct.pay.by.user',
64
+ '_input_charset' => 'utf-8',
65
+ 'partner' => options[:pid] || Alipay.pid,
66
+ 'seller_id' => options[:pid] || Alipay.pid,
67
+ 'payment_type' => '1'
68
+ }.merge(params)
69
+
70
+ request_uri(params, options).to_s
71
+ end
72
+
73
+ CREATE_REFUND_URL_REQUIRED_PARAMS = %w( batch_no data notify_url )
74
+ def self.refund_fastpay_by_platform_pwd_url(params, options = {})
75
+ params = Utils.stringify_keys(params)
76
+ check_required_params(params, CREATE_REFUND_URL_REQUIRED_PARAMS)
77
+
78
+ data = params.delete('data')
79
+ detail_data = data.map do|item|
80
+ item = Utils.stringify_keys(item)
81
+ "#{item['trade_no']}^#{item['amount']}^#{item['reason']}"
82
+ end.join('#')
83
+
84
+ params = {
85
+ 'service' => 'refund_fastpay_by_platform_pwd',
86
+ '_input_charset' => 'utf-8',
87
+ 'partner' => options[:pid] || Alipay.pid,
88
+ 'seller_user_id' => options[:pid] || Alipay.pid,
89
+ 'refund_date' => Time.now.strftime('%Y-%m-%d %H:%M:%S'),
90
+ 'batch_num' => data.size,
91
+ 'detail_data' => detail_data
92
+ }.merge(params)
93
+
94
+ request_uri(params, options).to_s
95
+ end
96
+
97
+ CREATE_FOREX_SINGLE_REFUND_URL_REQUIRED_PARAMS = %w( out_return_no out_trade_no return_amount currency reason )
98
+ def self.forex_refund_url(params, options = {})
99
+ params = Utils.stringify_keys(params)
100
+ check_required_params(params, CREATE_FOREX_SINGLE_REFUND_URL_REQUIRED_PARAMS)
101
+
102
+ params = {
103
+ 'service' => 'forex_refund',
104
+ 'partner' => options[:pid] || Alipay.pid,
105
+ '_input_charset' => 'utf-8',
106
+ 'gmt_return' => Time.now.getlocal('+08:00').strftime('%Y%m%d%H%M%S')
107
+ }.merge(params)
108
+
109
+ request_uri(params, options).to_s
110
+ end
111
+
112
+ SEND_GOODS_CONFIRM_BY_PLATFORM_REQUIRED_PARAMS = %w( trade_no logistics_name )
113
+ SEND_GOODS_CONFIRM_BY_PLATFORM_OPTIONAL_PARAMS = %w( transport_type create_transport_type )
114
+ def self.send_goods_confirm_by_platform(params, options = {})
115
+ params = Utils.stringify_keys(params)
116
+ check_required_params(params, SEND_GOODS_CONFIRM_BY_PLATFORM_REQUIRED_PARAMS)
117
+ check_optional_params(params, SEND_GOODS_CONFIRM_BY_PLATFORM_OPTIONAL_PARAMS)
118
+
119
+ params = {
120
+ 'service' => 'send_goods_confirm_by_platform',
121
+ 'partner' => options[:pid] || Alipay.pid,
122
+ '_input_charset' => 'utf-8'
123
+ }.merge(params)
124
+
125
+ Net::HTTP.get(request_uri(params, options))
126
+ end
127
+
128
+ CREATE_FOREX_TRADE_REQUIRED_PARAMS = %w( notify_url subject out_trade_no currency )
129
+ CREATE_FOREX_TRADE_OPTIONAL_PARAMS = %w( total_fee rmb_fee )
130
+ def self.create_forex_trade_url(params, options = {})
131
+ params = Utils.stringify_keys(params)
132
+ check_required_params(params, CREATE_FOREX_TRADE_REQUIRED_PARAMS)
133
+ check_optional_params(params, CREATE_FOREX_TRADE_OPTIONAL_PARAMS)
134
+
135
+ params = {
136
+ 'service' => 'create_forex_trade',
137
+ '_input_charset' => 'utf-8',
138
+ 'partner' => options[:pid] || Alipay.pid,
139
+ }.merge(params)
140
+
141
+ request_uri(params, options).to_s
142
+ end
143
+
144
+ CLOSE_TRADE_REQUIRED_OPTIONAL_PARAMS = %w( trade_no out_order_no )
145
+ def self.close_trade(params, options = {})
146
+ params = Utils.stringify_keys(params)
147
+ check_optional_params(params, CLOSE_TRADE_REQUIRED_OPTIONAL_PARAMS)
148
+
149
+ params = {
150
+ 'service' => 'close_trade',
151
+ '_input_charset' => 'utf-8',
152
+ 'partner' => options[:pid] || Alipay.pid
153
+ }.merge(params)
154
+
155
+ Net::HTTP.get(request_uri(params, options))
156
+ end
157
+
158
+ SINGLE_TRADE_QUERY_OPTIONAL_PARAMS = %w( trade_no out_trade_no )
159
+ def self.single_trade_query(params, options = {})
160
+ params = Utils.stringify_keys(params)
161
+ check_optional_params(params, SINGLE_TRADE_QUERY_OPTIONAL_PARAMS)
162
+
163
+ params = {
164
+ "service" => 'single_trade_query',
165
+ "_input_charset" => "utf-8",
166
+ "partner" => options[:pid] || Alipay.pid,
167
+ }.merge(params)
168
+
169
+ Net::HTTP.get(request_uri(params, options))
170
+ end
171
+
172
+ def self.account_page_query(params, options = {})
173
+ params = {
174
+ service: 'account.page.query',
175
+ _input_charset: 'utf-8',
176
+ partner: options[:pid] || Alipay.pid,
177
+ }.merge(params)
178
+
179
+ Net::HTTP.get(request_uri(params, options))
180
+ end
181
+
182
+ BATCH_TRANS_NOTIFY_REQUIRED_PARAMS = %w( notify_url account_name detail_data batch_no batch_num batch_fee email )
183
+ def self.batch_trans_notify_url(params, options = {})
184
+ params = Utils.stringify_keys(params)
185
+ check_required_params(params, BATCH_TRANS_NOTIFY_REQUIRED_PARAMS)
186
+
187
+ params = {
188
+ 'service' => 'batch_trans_notify',
189
+ '_input_charset' => 'utf-8',
190
+ 'partner' => options[:pid] || Alipay.pid,
191
+ 'pay_date' => Time.now.strftime("%Y%m%d")
192
+ }.merge(params)
193
+
194
+ request_uri(params, options).to_s
195
+ end
196
+
197
+ CREATE_FOREX_TRADE_WAP_REQUIRED_PARAMS = %w( out_trade_no subject merchant_url currency )
198
+ def self.create_forex_trade_wap_url(params, options = {})
199
+ params = Utils.stringify_keys(params)
200
+ check_required_params(params, CREATE_FOREX_TRADE_WAP_REQUIRED_PARAMS)
201
+
202
+ params = {
203
+ 'service' => 'create_forex_trade_wap',
204
+ '_input_charset' => 'utf-8',
205
+ 'partner' => options[:pid] || Alipay.pid,
206
+ 'seller_id' => options[:pid] || Alipay.pid
207
+ }.merge(params)
208
+
209
+ request_uri(params, options).to_s
210
+ end
211
+
212
+ def self.request_uri(params, options = {})
213
+ uri = URI(GATEWAY_URL)
214
+ uri.query = URI.encode_www_form(sign_params(params, options))
215
+ uri
216
+ end
217
+
218
+ def self.sign_params(params, options = {})
219
+ params.merge(
220
+ 'sign_type' => (options[:sign_type] || Alipay.sign_type),
221
+ 'sign' => Alipay::Sign.generate(params, options)
222
+ )
223
+ end
224
+
225
+ def self.check_required_params(params, names)
226
+ return if !Alipay.debug_mode?
227
+
228
+ names.each do |name|
229
+ warn("Alipay Warn: missing required option: #{name}") unless params.has_key?(name)
230
+ end
231
+ end
232
+
233
+ def self.check_optional_params(params, names)
234
+ return if !Alipay.debug_mode?
235
+
236
+ warn("Alipay Warn: must specify either #{names.join(' or ')}") if names.all? {|name| params[name].nil? }
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,13 @@
1
+ module Alipay
2
+ module Sign
3
+ class DSA
4
+ def self.sign(key, string)
5
+ raise NotImplementedError, 'DSA sign is not implemented'
6
+ end
7
+
8
+ def self.verify?(string, sign)
9
+ raise NotImplementedError, 'DSA verify is not implemented'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'digest/md5'
2
+
3
+ module Alipay
4
+ module Sign
5
+ class MD5
6
+ def self.sign(key, string)
7
+ Digest::MD5.hexdigest("#{string}#{key}")
8
+ end
9
+
10
+ def self.verify?(key, string, sign)
11
+ sign == sign(key, string)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Alipay
5
+ module Sign
6
+ class RSA
7
+ def self.sign(key, string)
8
+ rsa = OpenSSL::PKey::RSA.new(key)
9
+ Base64.strict_encode64(rsa.sign('sha1', string))
10
+ end
11
+
12
+ def self.verify?(key, string, sign)
13
+ rsa = OpenSSL::PKey::RSA.new(key)
14
+ rsa.verify('sha1', Base64.strict_decode64(sign), string)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Alipay
5
+ module Sign
6
+ class RSA2
7
+ def self.sign(key, string)
8
+ rsa = OpenSSL::PKey::RSA.new(key)
9
+ Base64.strict_encode64(rsa.sign('sha256', string))
10
+ end
11
+
12
+ def self.verify?(key, string, sign)
13
+ rsa = OpenSSL::PKey::RSA.new(key)
14
+ rsa.verify('sha256', Base64.strict_decode64(sign), string)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,54 @@
1
+ module Alipay
2
+ module Sign
3
+ def self.generate(params, options = {})
4
+ params = Utils.stringify_keys(params)
5
+ sign_type = options[:sign_type] || Alipay.sign_type
6
+ key = options[:key] || Alipay.key
7
+ string = params_to_string(params)
8
+
9
+ case sign_type
10
+ when 'MD5'
11
+ MD5.sign(key, string)
12
+ when 'RSA'
13
+ RSA.sign(key, string)
14
+ when 'DSA'
15
+ DSA.sign(key, string)
16
+ else
17
+ raise ArgumentError, "invalid sign_type #{sign_type}, allow value: 'MD5', 'RSA', 'DSA'"
18
+ end
19
+ end
20
+
21
+ ALIPAY_RSA_PUBLIC_KEY = <<-EOF
22
+ -----BEGIN PUBLIC KEY-----
23
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRA
24
+ FljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQE
25
+ B/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5Ksi
26
+ NG9zpgmLCUYuLkxpLQIDAQAB
27
+ -----END PUBLIC KEY-----
28
+ EOF
29
+
30
+ def self.verify?(params, options = {})
31
+ params = Utils.stringify_keys(params)
32
+
33
+ sign_type = params.delete('sign_type')
34
+ sign = params.delete('sign')
35
+ string = params_to_string(params)
36
+
37
+ case sign_type
38
+ when 'MD5'
39
+ key = options[:key] || Alipay.key
40
+ MD5.verify?(key, string, sign)
41
+ when 'RSA'
42
+ RSA.verify?(ALIPAY_RSA_PUBLIC_KEY, string, sign)
43
+ when 'DSA'
44
+ DSA.verify?(string, sign)
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ def self.params_to_string(params)
51
+ params.sort.map { |item| item.join('=') }.join('&')
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,19 @@
1
+ module Alipay
2
+ module Utils
3
+ def self.stringify_keys(hash)
4
+ new_hash = {}
5
+ hash.each do |key, value|
6
+ new_hash[(key.to_s rescue key) || key] = value
7
+ end
8
+ new_hash
9
+ end
10
+
11
+ # 退款批次号,支付宝通过此批次号来防止重复退款操作,所以此号生成后最好直接保存至数据库,不要在显示页面的时候生成
12
+ # 共 24 位(8 位当前日期 + 9 位纳秒 + 1 位随机数)
13
+ def self.generate_batch_no
14
+ t = Time.now
15
+ batch_no = t.strftime('%Y%m%d%H%M%S') + t.nsec.to_s
16
+ batch_no.ljust(24, rand(10).to_s)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Alipay
2
+ VERSION = "0.15.2"
3
+ end
@@ -0,0 +1,13 @@
1
+ module Alipay
2
+ module Wap
3
+ module Notify
4
+ def self.verify?(params, options = {})
5
+ params = Utils.stringify_keys(params)
6
+ pid = options[:pid] || Alipay.pid
7
+ notify_id = params['notify_data'].scan(/\<notify_id\>(.*)\<\/notify_id\>/).flatten.first
8
+
9
+ Sign.verify?(params, options) && ::Alipay::Notify.verify_notify_id?(pid, notify_id)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,84 @@
1
+ module Alipay
2
+ module Wap
3
+ module Service
4
+ GATEWAY_URL = 'https://wappaygw.alipay.com/service/rest.htm'
5
+
6
+ TRADE_CREATE_DIRECT_TOKEN_REQUIRED_PARAMS = %w( req_data )
7
+ REQ_DATA_REQUIRED_PARAMS = %w( seller_account_name subject out_trade_no total_fee call_back_url )
8
+ def self.trade_create_direct_token(params, options = {})
9
+ params = Utils.stringify_keys(params)
10
+ Alipay::Service.check_required_params(params, TRADE_CREATE_DIRECT_TOKEN_REQUIRED_PARAMS)
11
+
12
+ req_data = Utils.stringify_keys(params.delete('req_data'))
13
+ Alipay::Service.check_required_params(req_data, REQ_DATA_REQUIRED_PARAMS)
14
+
15
+ xml = req_data.map {|k, v| "<#{k}>#{v.encode(:xml => :text)}</#{k}>" }.join
16
+ req_data_xml = "<direct_trade_create_req>#{xml}</direct_trade_create_req>"
17
+
18
+ # About req_id: http://club.alipay.com/read-htm-tid-10078020-fpage-2.html
19
+ params = {
20
+ 'service' => 'alipay.wap.trade.create.direct',
21
+ 'req_data' => req_data_xml,
22
+ 'partner' => options[:pid] || Alipay.pid,
23
+ 'req_id' => Time.now.strftime('%Y%m%d%H%M%s'),
24
+ 'format' => 'xml',
25
+ 'v' => '2.0'
26
+ }.merge(params)
27
+
28
+ xml = Net::HTTP.get(request_uri(params, options))
29
+ CGI.unescape(xml).scan(/\<request_token\>(.*)\<\/request_token\>/).flatten.first
30
+ end
31
+
32
+ AUTH_AND_EXECUTE_REQUIRED_PARAMS = %w( request_token )
33
+ def self.auth_and_execute_url(params, options = {})
34
+ params = Utils.stringify_keys(params)
35
+ Alipay::Service.check_required_params(params, AUTH_AND_EXECUTE_REQUIRED_PARAMS)
36
+
37
+ req_data_xml = "<auth_and_execute_req><request_token>#{params.delete('request_token')}</request_token></auth_and_execute_req>"
38
+
39
+ params = {
40
+ 'service' => 'alipay.wap.auth.authAndExecute',
41
+ 'req_data' => req_data_xml,
42
+ 'partner' => options[:pid] || Alipay.pid,
43
+ 'format' => 'xml',
44
+ 'v' => '2.0'
45
+ }.merge(params)
46
+
47
+ request_uri(params, options).to_s
48
+ end
49
+
50
+ def self.security_risk_detect(params, options)
51
+ params = Utils.stringify_keys(params)
52
+
53
+ params = {
54
+ 'service' => 'alipay.security.risk.detect',
55
+ '_input_charset' => 'utf-8',
56
+ 'partner' => options[:pid] || Alipay.pid,
57
+ 'timestamp' => Time.now.strftime('%Y-%m-%d %H:%M:%S'),
58
+ 'scene_code' => 'PAYMENT'
59
+ }.merge(params)
60
+
61
+ sign_params(params, options)
62
+
63
+ Net::HTTP.post_form(URI(GATEWAY_URL), params)
64
+ end
65
+
66
+ def self.request_uri(params, options = {})
67
+ uri = URI(GATEWAY_URL)
68
+ uri.query = URI.encode_www_form(sign_params(params, options))
69
+ uri
70
+ end
71
+
72
+ SIGN_TYPE_TO_SEC_ID = {
73
+ 'MD5' => 'MD5',
74
+ 'RSA' => '0001'
75
+ }
76
+
77
+ def self.sign_params(params, options = {})
78
+ sign_type = (options[:sign_type] ||= Alipay.sign_type)
79
+ params = params.merge('sec_id' => SIGN_TYPE_TO_SEC_ID[sign_type])
80
+ params.merge('sign' => Alipay::Sign.generate(params, options))
81
+ end
82
+ end
83
+ end
84
+ end