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.
- checksums.yaml +7 -0
- data/lib/cmbchina.rb +338 -0
- metadata +44 -0
checksums.yaml
ADDED
@@ -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
|
data/lib/cmbchina.rb
ADDED
@@ -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: []
|