cmbchina 0.0.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.
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: []