cmbchina 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cmbchina.rb +338 -0
  3. metadata +44 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c19d1a6047132f9412dceccfad7ed5facabdfb23
4
+ data.tar.gz: c63710544c0b2ccfa2b179f9719fc694787d4401
5
+ SHA512:
6
+ metadata.gz: 0ac992af18249d1ba2b104fe974f49fbbeafe8da68af72f444690541f92aec4cc9a515e7c59380a993c72b905ba8c30c7b78c932f4dfab5e38658c313bec45ea
7
+ data.tar.gz: e7192724021a355ae9862a38afb43d110f456c7384017d9f112cd73a031aa90c170f7afded1f773b4ffbc18b83bce44eb35ebc1cfb234dd4782d99aeaa744acf
@@ -0,0 +1,338 @@
1
+ # encoding: utf-8
2
+ require 'openssl'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+ require "httparty"
6
+ require 'nokogiri'
7
+ require 'digest/sha1'
8
+
9
+ module Cmbchina
10
+
11
+ URI = {
12
+ #支付网关
13
+ gateway: {
14
+ api: 'https://payment.ebank.cmbchina.com/NetPayment/BaseHttp.dll',
15
+ live: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayC2',
16
+ test: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayC2'
17
+ }
18
+ }
19
+
20
+ @@gateway_url = URI[:gateway][:live]
21
+
22
+
23
+ class << self
24
+ attr_accessor :branch_id #开户分行号
25
+ attr_accessor :co_no #商户号
26
+ attr_accessor :public_key #招行官方公钥(从der版本转换为pem版本)
27
+ attr_accessor :secret #商户秘钥
28
+ attr_accessor :password #登录密码
29
+ attr_accessor :env
30
+
31
+ def initialize
32
+ @env = :test
33
+ end
34
+
35
+ def config(options)
36
+ options.each do |key, value|
37
+ send "#{key}=", value
38
+ end
39
+ end
40
+
41
+ def url
42
+ URI[:gateway][env]
43
+ end
44
+
45
+ def api_url
46
+ URI[:gateway][:api]
47
+ end
48
+
49
+ end
50
+
51
+ module Sign
52
+ class << self
53
+ #返回消息的明文
54
+ def plain_text(param_string)
55
+ params = Rack::Utils.parse_nested_query(param_string)
56
+ params.delete("Signature")
57
+ Rack::Utils.build_query(params)
58
+ end
59
+
60
+ #返回消息的数字签名
61
+ def signature(param_string)
62
+ sign = Rack::Utils.parse_nested_query(param_string).delete("Signature")
63
+ sign.split('|').map{|ascii_code| ascii_code.to_i.chr }.join('')
64
+ end
65
+
66
+ #验证数字签名
67
+ def verify(param_string)
68
+ pub = OpenSSL::PKey::RSA.new(Cmbchina.public_key)
69
+ pub.verify('sha1', signature(param_string), plain_text(param_string))
70
+ end
71
+ end
72
+ end
73
+
74
+ module Api
75
+ include HTTParty
76
+ class << self
77
+ def post_command(command, data = {})
78
+ data = data.merge({ co_no: Cmbchina.co_no })
79
+ data = data.inject({}) do | r, (k, v) |
80
+ r[k.to_s.camelize()] = v
81
+ r
82
+ end
83
+
84
+ data['BranchID'] = Cmbchina.branch_id
85
+ url = "#{Cmbchina.api_url}?#{command}"
86
+
87
+ #puts ""
88
+ #puts "***" * 10
89
+ #puts "#{url}&#{data.to_query}"
90
+ respone = post(url, { body: data })
91
+ #p respone
92
+ #puts "***" * 10
93
+ #puts ""
94
+
95
+ respone.body.encode('utf-8', 'gb2312').split("\n")
96
+ end
97
+
98
+ def get_session
99
+ Cmbchina::SessionObject.new(post_command('DirectEnter'))
100
+ end
101
+
102
+ def login(pwd = nil)
103
+ session = get_session
104
+ session.login(pwd)
105
+ session
106
+ end
107
+
108
+ end
109
+ end
110
+
111
+ class SessionObject
112
+ include HTTParty
113
+
114
+ attr_accessor :client_no #会话号码
115
+ attr_accessor :serial_no #序列号
116
+
117
+ def initialize(message = nil)
118
+ if message.present? and message[0]=='Y'
119
+ @client_no = message[1]
120
+ @serial_no = message[2]
121
+ else
122
+ raise "could not connect the cmbchina api url"
123
+ end
124
+ end
125
+
126
+ def login(pwd = nil)
127
+ pwd = Cmbchina.password unless pwd
128
+ message = post_command('DirectLogonC', { pwd: pwd })
129
+
130
+ if message.present? and message[0]=='Y'
131
+ @client_no = message[1] if message[1]
132
+ @serial_no = message[2] if message[2]
133
+ else
134
+ raise "login fails"
135
+ end
136
+ message
137
+ end
138
+
139
+ def logout
140
+ post_command('DirectExit')
141
+ end
142
+
143
+ def query_settled_orders(options)
144
+ message_array = post_command('DirectQuerySettledOrderByPage', query_order_options(options))
145
+ explan_order_list( message_array )
146
+ end
147
+
148
+ def query_refund_orders(options)
149
+ message_array = post_command('DirectQueryRefundByDate', query_order_options(options))
150
+ explan_order_list( message_array )
151
+ end
152
+
153
+ def get_order( bill_no, bill_date )
154
+ options = {
155
+ version: 1,
156
+ co_no: Cmbchina.co_no,
157
+ date: bill_date,
158
+ bill_no: bill_no
159
+ }
160
+ message_order = post_command('DirectQuerySingleOrder', options)
161
+ if message_order.shift == 'Y' and message_order.size > 2
162
+ message_order.push bill_no
163
+ Cmbchina::Order.new( message_order )
164
+ end
165
+ end
166
+
167
+ def refund( options )
168
+ refund_options = {
169
+ date: options[:date],
170
+ bill_no: options[:bill_no],
171
+ amount: options[:amount],
172
+ desc: options[:desc],
173
+ vcode: get_vcode(options)
174
+ }
175
+ post_command('DirectRefund', refund_options )
176
+ end
177
+
178
+ private
179
+ def explan_order_list(message_array)
180
+ success = message_array.shift
181
+ pos = message_array.shift
182
+ has_next = message_array.shift
183
+ orders = []
184
+ if success =='Y' and has_next =='N' and message_array.size > 2
185
+ message_orders = message_array.in_groups_of(9)
186
+ orders = message_orders.map do | message_order |
187
+ data = [ message_order[0], message_order[1], message_order[4], message_order[2], message_array[5], message_array[6], nil, message_array[7], message_array[8], message_array[3] ]
188
+ Cmbchina::Order.new( data )
189
+ end
190
+ end
191
+ { success?: (success =='Y'), pos: pos, has_next?: (has_next =='Y'), orders: orders }
192
+ end
193
+
194
+ def post_command(command, data = {})
195
+ data = data.merge({ client_no: self.client_no, serial_no: self.serial_no })
196
+ Cmbchina::Api.post_command(command, data)
197
+ end
198
+
199
+ def query_order_options(options)
200
+ params = {
201
+ begin_date: options[:begin_date],
202
+ end_date: options[:end_date],
203
+ type: 0,
204
+ version: 1,
205
+ count: options[:count] || 200,
206
+ pos: options[:pos] || 0
207
+ }
208
+ params
209
+ end
210
+
211
+ def get_vcode(options)
212
+ code = "#{self.serial_no}#{Cmbchina.secret}#{self.client_no}#{Cmbchina.branch_id}#{Cmbchina.co_no}#{options[:date]}#{options[:bill_no]}#{options[:amount]}#{options[:desc]}"
213
+ Digest::SHA1.hexdigest(code)
214
+ end
215
+
216
+ end
217
+
218
+ class Message
219
+ include HTTParty
220
+
221
+ attr_accessor :branch_id #分行号
222
+ attr_accessor :co_no #商户号
223
+ attr_accessor :bill_no #订单号
224
+ attr_accessor :date #订单下单日期
225
+ attr_accessor :bank_serial_no #银行流水号
226
+ attr_accessor :bank_date #银行主机交易日期
227
+ attr_accessor :succeed #消息成功失败,成功为'Y',失败为'N'
228
+ attr_accessor :amount #实际支付金额
229
+ attr_accessor :msg #银行通知用户支付结构消息
230
+ attr_accessor :signature #通知命令签名
231
+ attr_accessor :merchant_para #商户自定义传递参数
232
+ attr_accessor :merchant_url #商户回调url
233
+
234
+ attr_reader :query_string #原始的query_string
235
+
236
+ def initialize(query_string)
237
+ query_string = Rack::Utils.build_query(query_string) if query_string.is_a? Hash
238
+ @query_string = query_string
239
+ params = Rack::Utils.parse_nested_query(query_string)
240
+
241
+ # 银行通知用户的支付结果消息。信息的前38个字符格式为:4位分行号+6位商户号+8位银行接受交易的日期+20位银行流水号;可以利用交易日期+银行流水号对该定单进行结帐处理;
242
+ @bill_no = params["BillNo"]
243
+ @date = params["Date"]
244
+ @succeed = params["Succeed"]
245
+ @amount = params["Amount"].to_f
246
+ @msg = params["Msg"]
247
+ @signature = params["Signature"]
248
+ @merchant_para = params["MerchantPara"]
249
+ @merchant_url = params["MerchantUrl"]
250
+
251
+ msg = params["Msg"][0..37]
252
+ @branch_id = msg[0..3]
253
+ @co_no = msg[4..9]
254
+ @bank_date = msg[10..17]
255
+ @bank_serial_no = msg[18..37]
256
+ end
257
+
258
+ def verify?
259
+ Cmbchina::Sign.verify(query_string)
260
+ end
261
+
262
+ def succeed?
263
+ succeed == 'Y'
264
+ end
265
+
266
+ def order_uuid
267
+ merchant_para
268
+ end
269
+
270
+ def trade_no
271
+ bill_no
272
+ end
273
+
274
+ def amount_cents
275
+ (amount * 100).to_i
276
+ end
277
+
278
+ def payment_date
279
+ Date.strptime(bank_date, "%Y%m%d")
280
+ end
281
+
282
+ end
283
+
284
+ class Order
285
+
286
+ attr_reader :bill_no
287
+ attr_reader :order_date
288
+ attr_reader :bill_date
289
+ attr_reader :amount
290
+ attr_reader :fee
291
+ attr_reader :merchant_para
292
+ attr_reader :bank_date
293
+ attr_reader :bank_time
294
+
295
+ def initialize( message_array )
296
+ @order_date, @bill_date, @bill_status_code, @amount, @card_type_code, @fee, @merchant_para, @bank_date, @bank_time, @bill_no = message_array
297
+ end
298
+
299
+ def trade_no
300
+ @bill_no
301
+ end
302
+
303
+ def bill_status
304
+ status_map = { '0'=>'已结账', '1'=>'已撤销', '2'=>'部分结账', '3'=>'退款记录', '5'=>'无效状态', '6'=>'未知状态' }
305
+ status_map[@bill_status_code]
306
+ end
307
+
308
+ def card_type
309
+ type_map = { '02'=>'招行一卡通', '03'=>'招行信用卡', '04'=>'其他银行卡' }
310
+ type_map[@card_type_code]
311
+ end
312
+
313
+ end
314
+
315
+ module Service
316
+
317
+ #根据订单信息生成跳转的支付链接,option为订单相关信息hash
318
+ def self.generate_link(options)
319
+ query_options = {
320
+ 'BranchID' => options[:branch_id] || Cmbchina.branch_id,
321
+ 'CoNo' => options[:co_no] || Cmbchina.co_no,
322
+ 'Amount' => options[:amount],
323
+ 'BillNo' => options[:bill_no],
324
+ 'Date' => options[:date],
325
+ 'MerchantUrl' => options[:merchant_url],
326
+ 'MerchantPara' => options[:merchant_para],
327
+ 'MerchantReturnURL' => options[:merchant_return_url]
328
+ }
329
+ { url: "#{Cmbchina.url}", options: query_options, url_and_options: "#{Cmbchina.url}?#{query_options.to_query}" }
330
+ end
331
+
332
+ #处理支付成功后返回的信息
333
+ def self.get_pay_message(query_string)
334
+ Cmbchina::Message.new query_string
335
+ end
336
+ end
337
+
338
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cmbchina
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - yellong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-25 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: cmbchina web pay gateway, 中国招商银行的支付网关简易包装
14
+ email: yellongweek@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/cmbchina.rb
20
+ homepage: http://rubygems.org/gems/cmbchina
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.2.2
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: cmbchina web pay gateway ~
44
+ test_files: []