unionpei 1.1.0 → 1.2.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 +4 -4
- data/.gitignore +17 -0
- data/Gemfile +6 -0
- data/Rakefile +11 -0
- data/certs/acp_test_enc.cer +25 -0
- data/certs/acp_test_middle.cer +23 -0
- data/certs/acp_test_root.cer +22 -0
- data/certs/acp_test_sign.pfx +0 -0
- data/install.sh +2 -0
- data/lib/unionpei/acp_service.rb +37 -42
- data/lib/unionpei/cert_util.rb +61 -82
- data/lib/unionpei/log_util.rb +24 -30
- data/lib/unionpei/payment.rb +50 -51
- data/lib/unionpei/sdk_config.rb +47 -47
- data/lib/unionpei/sdk_util.rb +164 -186
- data/lib/unionpei/version.rb +2 -6
- data/lib/unionpei.rb +5 -2
- data/readme.txt +25 -0
- data/unionpei.gemspec +31 -0
- metadata +84 -3
data/lib/unionpei/sdk_util.rb
CHANGED
@@ -1,328 +1,309 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'singleton'
|
4
4
|
require 'logger'
|
5
5
|
require 'net/https'
|
6
6
|
require 'uri'
|
7
7
|
require 'base64'
|
8
|
-
require
|
8
|
+
require 'zlib'
|
9
9
|
require_relative 'sdk_config'
|
10
10
|
require_relative 'cert_util'
|
11
11
|
|
12
|
-
|
13
12
|
module UnionPei
|
14
13
|
class SDKUtil
|
15
|
-
|
16
|
-
|
17
|
-
LogUtil.info("post
|
18
|
-
LogUtil.info("post content:["+content+"]")
|
14
|
+
def self.post(url, content)
|
15
|
+
LogUtil.info("post url:[#{url}]")
|
16
|
+
LogUtil.info("post content:[#{content}]")
|
19
17
|
uri = URI.parse(url)
|
20
18
|
http = Net::HTTP.new(uri.host, uri.port)
|
21
|
-
http.use_ssl = true if uri.scheme ==
|
22
|
-
|
23
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
24
|
-
end
|
19
|
+
http.use_ssl = true if uri.scheme == 'https'
|
20
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless SDKConfig.instance.ifValidateRemoteCert
|
25
21
|
res = http.post(uri.path, content).body.force_encoding(SDKConfig.instance.encoding)
|
26
|
-
LogUtil.info(
|
27
|
-
|
22
|
+
LogUtil.info("resp:[#{res}]")
|
23
|
+
res
|
28
24
|
end
|
29
25
|
|
30
|
-
def
|
31
|
-
linkString =
|
26
|
+
def self.createLinkString(para, sort, encode)
|
27
|
+
linkString = ''
|
32
28
|
keys = para.keys
|
33
|
-
if sort
|
34
|
-
|
35
|
-
end
|
36
|
-
for key in keys
|
29
|
+
keys = keys.sort if sort
|
30
|
+
keys.each do |key|
|
37
31
|
value = para[key]
|
38
32
|
# print(key + ":" + value)
|
39
|
-
if encode
|
40
|
-
value = URI.encode_www_form_component(value)
|
41
|
-
end
|
33
|
+
value = URI.encode_www_form_component(value) if encode
|
42
34
|
# print(str(type(key))+":"+str(type(value))+":"+str(key)+":"+str(value))
|
43
|
-
linkString = linkString
|
35
|
+
linkString = "#{linkString}#{key}=#{value}&"
|
44
36
|
end
|
45
|
-
linkString
|
37
|
+
linkString[0, linkString.length - 1]
|
46
38
|
# print (linkString)
|
47
|
-
return linkString
|
48
39
|
end
|
49
40
|
|
50
|
-
def
|
41
|
+
def self.filterNoneValue(para)
|
51
42
|
keys = para.keys
|
52
|
-
|
43
|
+
keys.each do |key|
|
53
44
|
value = para[key]
|
54
|
-
if !value
|
55
|
-
para.delete(key)
|
56
|
-
end
|
45
|
+
para.delete(key) if !value || (value == '')
|
57
46
|
end
|
58
47
|
end
|
59
48
|
|
60
|
-
def
|
61
|
-
|
49
|
+
def self.buildSignature(req, signCertPath = SDKConfig.instance.signCertPath, signCertPwd = SDKConfig.instance.signCertPwd, secureKey = SDKConfig.instance.secureKey)
|
62
50
|
SDKUtil.filterNoneValue(req)
|
63
|
-
|
64
|
-
LogUtil.error(
|
51
|
+
unless req['signMethod']
|
52
|
+
LogUtil.error('signMethod must not null')
|
65
53
|
return nil
|
66
54
|
end
|
67
|
-
|
68
|
-
LogUtil.error(
|
55
|
+
unless req['version']
|
56
|
+
LogUtil.error('version must not null')
|
69
57
|
return nil
|
70
58
|
end
|
71
|
-
if
|
72
|
-
req[
|
73
|
-
LogUtil.info(
|
59
|
+
if req['signMethod'] == '01'
|
60
|
+
req['certId'] = CertUtil.getSignCertId(signCertPath, signCertPwd)
|
61
|
+
LogUtil.info('=== start to sign ===')
|
74
62
|
prestr = SDKUtil.createLinkString(req, true, false)
|
75
|
-
LogUtil.info("sorted: [
|
63
|
+
LogUtil.info("sorted: [#{prestr}]")
|
76
64
|
prestr = SDKUtil.sha256(prestr)
|
77
|
-
LogUtil.info("sha256: [
|
78
|
-
LogUtil.info("sign cert: [
|
65
|
+
LogUtil.info("sha256: [#{prestr}]")
|
66
|
+
LogUtil.info("sign cert: [#{signCertPath}], pwd: [#{signCertPwd}]")
|
79
67
|
key = CertUtil.getSignPriKey(signCertPath, signCertPwd)
|
80
68
|
signature = Base64.encode64(key.sign('sha256', prestr)).gsub(/\n|\r/, '')
|
81
|
-
LogUtil.info("signature: [
|
82
|
-
elsif
|
83
|
-
LogUtil.info(
|
69
|
+
LogUtil.info("signature: [#{signature}]")
|
70
|
+
elsif req['signMethod'] == '11'
|
71
|
+
LogUtil.info('=== start to sign ===')
|
84
72
|
prestr = createLinkString(req, true, false)
|
85
|
-
LogUtil.info("sorted: [
|
73
|
+
LogUtil.info("sorted: [#{prestr}]")
|
86
74
|
if secureKey.nil?
|
87
|
-
LogUtil.error(
|
75
|
+
LogUtil.error('secureKey must not null')
|
88
76
|
return nil
|
89
77
|
end
|
90
|
-
prestr = prestr
|
91
|
-
LogUtil.debug("before final sha256: [
|
78
|
+
prestr = "#{prestr}&#{sha256(secureKey)}"
|
79
|
+
LogUtil.debug("before final sha256: [#{prestr}]")
|
92
80
|
signature = SDKUtil.sha256(prestr)
|
93
|
-
LogUtil.info("signature: [
|
94
|
-
elsif
|
95
|
-
LogUtil.error(
|
81
|
+
LogUtil.info("signature: [#{signature}]")
|
82
|
+
elsif '12' == ['signMethod']
|
83
|
+
LogUtil.error('sm3算法暂未实现,请勿使用。')
|
96
84
|
return nil
|
97
85
|
else
|
98
|
-
LogUtil.info("invalid signMethod: [
|
86
|
+
LogUtil.info("invalid signMethod: [#{req['signMethod']}]")
|
99
87
|
end
|
100
|
-
LogUtil.info(
|
101
|
-
req[
|
102
|
-
|
88
|
+
LogUtil.info('=== end of sign ===')
|
89
|
+
req['signature'] = signature
|
90
|
+
signature
|
103
91
|
end
|
104
92
|
|
105
|
-
def
|
93
|
+
def self.paraFilter(para)
|
106
94
|
result = {}
|
107
|
-
|
108
|
-
if (key ==
|
95
|
+
para.each_key do |key|
|
96
|
+
if (key == 'signature') || (para[key] == '')
|
109
97
|
next
|
110
98
|
else
|
111
99
|
result[key] = para[key]
|
112
100
|
end
|
113
101
|
end
|
114
|
-
|
102
|
+
result
|
115
103
|
end
|
116
104
|
|
117
|
-
def
|
118
|
-
OpenSSL::Digest::SHA256.digest(data).
|
105
|
+
def self.sha256(data)
|
106
|
+
OpenSSL::Digest::SHA256.digest(data).unpack1('H*').downcase
|
119
107
|
end
|
120
108
|
|
121
|
-
def
|
109
|
+
def self.putKeyValueToMap(temp, isKey, key, m, decode)
|
122
110
|
if isKey
|
123
|
-
m[key.to_s] =
|
111
|
+
m[key.to_s] = ''
|
124
112
|
else
|
125
|
-
if decode
|
126
|
-
temp = URI.decode_www_form_component(temp)
|
127
|
-
end
|
113
|
+
temp = URI.decode_www_form_component(temp) if decode
|
128
114
|
m[key.to_s] = temp
|
129
115
|
end
|
130
116
|
end
|
131
117
|
|
132
|
-
def
|
118
|
+
def self.parseQString(respString, decode = false)
|
133
119
|
resp = {}
|
134
|
-
temp =
|
135
|
-
key =
|
120
|
+
temp = ''
|
121
|
+
key = ''
|
136
122
|
isKey = true
|
137
|
-
isOpen = false
|
138
|
-
openName = "\0"
|
123
|
+
isOpen = false
|
124
|
+
openName = "\0"
|
139
125
|
|
140
|
-
|
141
|
-
if
|
142
|
-
if
|
143
|
-
|
144
|
-
|
145
|
-
temp = temp + curChar
|
146
|
-
elsif (curChar == "{")
|
126
|
+
respString.split('').each do |curChar| # 遍历整个带解析的字符串
|
127
|
+
if isOpen
|
128
|
+
isOpen = false if curChar == openName
|
129
|
+
temp += curChar
|
130
|
+
elsif curChar == '{'
|
147
131
|
isOpen = true
|
148
|
-
openName =
|
149
|
-
temp
|
150
|
-
elsif
|
132
|
+
openName = '}'
|
133
|
+
temp += curChar
|
134
|
+
elsif curChar == '['
|
151
135
|
isOpen = true
|
152
|
-
openName =
|
153
|
-
temp
|
154
|
-
elsif
|
136
|
+
openName = ']'
|
137
|
+
temp += curChar
|
138
|
+
elsif isKey && (curChar == '=')
|
155
139
|
key = temp
|
156
|
-
temp =
|
140
|
+
temp = ''
|
157
141
|
isKey = false
|
158
|
-
elsif (curChar ==
|
142
|
+
elsif (curChar == '&') && !isOpen # 如果读取到&分割符
|
159
143
|
SDKUtil.putKeyValueToMap(temp, isKey, key, resp, decode)
|
160
|
-
temp =
|
144
|
+
temp = ''
|
161
145
|
isKey = true
|
162
146
|
else
|
163
|
-
temp
|
147
|
+
temp += curChar
|
164
148
|
end
|
165
149
|
end
|
166
150
|
SDKUtil.putKeyValueToMap(temp, isKey, key, resp, decode)
|
167
|
-
|
151
|
+
resp
|
168
152
|
end
|
169
153
|
|
170
|
-
def
|
171
|
-
|
172
|
-
LogUtil.error(
|
154
|
+
def self.verify(resp)
|
155
|
+
unless resp['signMethod']
|
156
|
+
LogUtil.error('signMethod must not null')
|
173
157
|
return nil
|
174
158
|
end
|
175
|
-
|
176
|
-
LogUtil.error(
|
159
|
+
unless resp['version']
|
160
|
+
LogUtil.error('version must not null')
|
177
161
|
return nil
|
178
162
|
end
|
179
|
-
|
180
|
-
LogUtil.error(
|
163
|
+
unless resp['signature']
|
164
|
+
LogUtil.error('signature must not null')
|
181
165
|
return nil
|
182
166
|
end
|
183
|
-
signMethod = resp[
|
184
|
-
version = resp[
|
167
|
+
signMethod = resp['signMethod']
|
168
|
+
version = resp['version']
|
185
169
|
result = false
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
170
|
+
case signMethod
|
171
|
+
when '01'
|
172
|
+
LogUtil.info('=== start to verify signature ===')
|
173
|
+
signature = resp.delete('signature')
|
174
|
+
LogUtil.info("signature: [#{signature}]")
|
190
175
|
prestr = SDKUtil.createLinkString(resp, true, false)
|
191
|
-
LogUtil.info("sorted: [
|
176
|
+
LogUtil.info("sorted: [#{prestr}]")
|
192
177
|
prestr = SDKUtil.sha256(prestr)
|
193
|
-
LogUtil.info("sha256: [
|
194
|
-
key = CertUtil.verifyAndGetVerifyKey(resp[
|
178
|
+
LogUtil.info("sha256: [#{prestr}]")
|
179
|
+
key = CertUtil.verifyAndGetVerifyKey(resp['signPubKeyCert'])
|
195
180
|
if !key
|
196
|
-
LogUtil.info("no cert was found by signPubKeyCert:
|
181
|
+
LogUtil.info("no cert was found by signPubKeyCert: #{resp['signPubKeyCert']}")
|
197
182
|
result = false
|
198
183
|
else
|
199
184
|
signature = Base64.decode64(signature)
|
200
|
-
result = key.verify(
|
185
|
+
result = key.verify('sha256', signature, prestr)
|
201
186
|
end
|
202
|
-
LogUtil.info("verify signature
|
203
|
-
LogUtil.info(
|
204
|
-
|
205
|
-
|
206
|
-
|
187
|
+
LogUtil.info("verify signature #{result ? 'succeed' : 'fail'}")
|
188
|
+
LogUtil.info('=== end of verify signature ===')
|
189
|
+
result
|
190
|
+
when '11', '12'
|
191
|
+
SDKUtil.verifyBySecureKey(resp, SDKConfig.instance.secureKey)
|
207
192
|
else
|
208
|
-
LogUtil.info("Error signMethod [
|
209
|
-
|
193
|
+
LogUtil.info("Error signMethod [#{signMethod}] in validate. ")
|
194
|
+
false
|
210
195
|
end
|
211
196
|
end
|
212
197
|
|
213
|
-
def
|
214
|
-
if resp[
|
215
|
-
LogUtil.error(
|
198
|
+
def self.verifyBySecureKey(resp, secureKey)
|
199
|
+
if resp['signMethod'].nil?
|
200
|
+
LogUtil.error('signMethod must not null')
|
216
201
|
return nil
|
217
202
|
end
|
218
|
-
if resp[
|
219
|
-
LogUtil.error(
|
203
|
+
if resp['signature'].nil?
|
204
|
+
LogUtil.error('signature must not null')
|
220
205
|
return nil
|
221
206
|
end
|
222
|
-
signMethod = resp[
|
207
|
+
signMethod = resp['signMethod']
|
223
208
|
result = false
|
224
|
-
LogUtil.info(
|
225
|
-
|
226
|
-
|
227
|
-
|
209
|
+
LogUtil.info('=== start to verify signature ===')
|
210
|
+
case signMethod
|
211
|
+
when '11'
|
212
|
+
signature = resp.delete('signature')
|
213
|
+
LogUtil.info("signature: [#{signature}]")
|
228
214
|
prestr = createLinkString(resp, true, false)
|
229
|
-
LogUtil.info("sorted: [
|
230
|
-
beforeSha256 = prestr
|
231
|
-
LogUtil.debug("before final sha256: [
|
215
|
+
LogUtil.info("sorted: [#{prestr}]")
|
216
|
+
beforeSha256 = "#{prestr}&#{sha256(secureKey)}"
|
217
|
+
LogUtil.debug("before final sha256: [#{beforeSha256}]")
|
232
218
|
afterSha256 = sha256(beforeSha256)
|
233
219
|
result = (afterSha256 == signature)
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
elsif "12" == signMethod
|
238
|
-
LogUtil.error("sm3算法暂未实现,请勿使用。")
|
220
|
+
LogUtil.debug("after final sha256: [#{afterSha256}]") unless result
|
221
|
+
when '12'
|
222
|
+
LogUtil.error('sm3算法暂未实现,请勿使用。')
|
239
223
|
else
|
240
|
-
LogUtil.info("Error signMethod [
|
224
|
+
LogUtil.info("Error signMethod [#{signMethod}] in validate. ")
|
241
225
|
end
|
242
|
-
LogUtil.info("verify signature
|
243
|
-
LogUtil.info(
|
244
|
-
|
226
|
+
LogUtil.info("verify signature #{result ? 'succeed' : 'fail'}")
|
227
|
+
LogUtil.info('=== end of verify signature ===')
|
228
|
+
result
|
245
229
|
end
|
246
230
|
|
247
|
-
def
|
248
|
-
result = "<html><head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset
|
249
|
-
|
231
|
+
def self.createAutoFormHtml(params, reqUrl)
|
232
|
+
result = "<html><head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=#{SDKConfig.instance.encoding}\" /></head><body onload=\"javascript:document.pay_form.submit();\"> <form id=\"pay_form\" name=\"pay_form\" action=\"#{reqUrl}\" method=\"post\">"
|
233
|
+
params.each_key do |key|
|
250
234
|
value = params[key]
|
251
|
-
result += " <input type=\"hidden\" name=\"
|
235
|
+
result += " <input type=\"hidden\" name=\"#{key}\" id=\"#{key}\" value=\"#{value}\" />\n"
|
252
236
|
end
|
253
|
-
result +=
|
254
|
-
LogUtil.info("auto post html
|
255
|
-
|
237
|
+
result += '<!-- <input type="submit" type="hidden">--> </form></body></html>'
|
238
|
+
LogUtil.info("auto post html:#{result}")
|
239
|
+
result
|
256
240
|
end
|
257
241
|
|
258
|
-
def
|
242
|
+
def self.encryptPub(data, certPath = SDKConfig.instance.encryptCertPath)
|
259
243
|
rsaKey = CertUtil.getEncryptKey(certPath)
|
260
244
|
result = rsaKey.public_encrypt(data)
|
261
|
-
|
262
|
-
return result
|
245
|
+
Base64.encode64(result).gsub(/\n|\r/, '')
|
263
246
|
end
|
264
247
|
|
265
|
-
def
|
248
|
+
def self.decryptPri(data, certPath = SDKConfig.instance.signCertPath, certPwd = SDKConfig.instance.signCertPwd)
|
266
249
|
pkey = CertUtil.getDecryptPriKey(certPath, certPwd)
|
267
250
|
data = Base64.decode64(data)
|
268
|
-
|
269
|
-
return result
|
251
|
+
pkey.private_decrypt(data)
|
270
252
|
end
|
271
253
|
|
272
|
-
def
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
LogUtil.info("文件内容为空")
|
254
|
+
def self.deCodeFileContent(params, fileDirectory)
|
255
|
+
return false unless params['fileContent']
|
256
|
+
|
257
|
+
LogUtil.info('---------处理后台报文返回的文件---------')
|
258
|
+
fileContent = params['fileContent']
|
259
|
+
unless fileContent
|
260
|
+
LogUtil.info('文件内容为空')
|
280
261
|
return false
|
281
262
|
end
|
282
263
|
fileContent = Zlib::Inflate.inflate(Base64.decode64(fileContent))
|
283
264
|
filePath = ''
|
284
|
-
if !params[
|
285
|
-
LogUtil.info(
|
286
|
-
filePath = fileDirectory
|
265
|
+
if !params['fileName']
|
266
|
+
LogUtil.info('文件名为空')
|
267
|
+
filePath = "#{fileDirectory}/#{params['merId']}_#{params['batchNo']}_#{params['txnTime']}.txt"
|
287
268
|
else
|
288
|
-
filePath = fileDirectory
|
269
|
+
filePath = "#{fileDirectory}/#{params['fileName']}"
|
289
270
|
end
|
290
271
|
output = File.new(filePath, 'w')
|
291
|
-
|
292
|
-
LogUtil.error
|
272
|
+
unless output
|
273
|
+
LogUtil.error 'Unable to open file!'
|
293
274
|
return false
|
294
275
|
end
|
295
276
|
output.syswrite(fileContent)
|
296
|
-
LogUtil.info "文件位置
|
277
|
+
LogUtil.info "文件位置 >:#{filePath}"
|
297
278
|
output.close
|
298
|
-
|
279
|
+
true
|
299
280
|
end
|
300
281
|
|
301
|
-
def
|
282
|
+
def self.enCodeFileContent(path)
|
302
283
|
fileContent = IO.binread(path)
|
303
284
|
fileContent = Base64.encode64(Zlib::Deflate.deflate(fileContent)).gsub(/\n|\r/, '')
|
304
285
|
end
|
305
286
|
|
306
|
-
def
|
307
|
-
if params['encryptPubKeyCert'].nil?
|
308
|
-
LogUtil.error(
|
287
|
+
def self.getEncryptCert(params)
|
288
|
+
if params['encryptPubKeyCert'].nil? || params['certType'].nil?
|
289
|
+
LogUtil.error('encryptPubKeyCert or certType is null')
|
309
290
|
return -1
|
310
291
|
end
|
311
292
|
strCert = params['encryptPubKeyCert']
|
312
293
|
certType = params['certType']
|
313
294
|
|
314
295
|
x509Cert = CertUtil.getX509Cert(strCert)
|
315
|
-
|
296
|
+
case certType
|
297
|
+
when '01'
|
316
298
|
# 更新敏感信息加密公钥
|
317
|
-
if x509Cert.serial.to_s == CertUtil.getEncryptCertId
|
318
|
-
|
319
|
-
end
|
299
|
+
return 0 if x509Cert.serial.to_s == CertUtil.getEncryptCertId
|
300
|
+
|
320
301
|
localCertPath = SDKConfig.instance.encryptCertPath
|
321
302
|
newLocalCertPath = SDKUtil.genBackupName(localCertPath)
|
322
303
|
# 将本地证书进行备份存储
|
323
304
|
File.rename(localCertPath, newLocalCertPath)
|
324
|
-
f = File.new(localCertPath,
|
325
|
-
|
305
|
+
f = File.new(localCertPath, 'w')
|
306
|
+
unless f
|
326
307
|
LogUtil.error 'Unable to open file!'
|
327
308
|
return -1
|
328
309
|
end
|
@@ -330,23 +311,20 @@ module UnionPei
|
|
330
311
|
f.close
|
331
312
|
LogUtil.info('save new encryptPubKeyCert success')
|
332
313
|
CertUtil.resetEncryptCertPublicKey
|
333
|
-
|
334
|
-
|
335
|
-
|
314
|
+
1
|
315
|
+
when '02'
|
316
|
+
0
|
336
317
|
else
|
337
|
-
LogUtil.error("unknown cerType
|
338
|
-
|
318
|
+
LogUtil.error("unknown cerType:#{certType}")
|
319
|
+
-1
|
339
320
|
end
|
340
321
|
end
|
341
322
|
|
342
|
-
def
|
323
|
+
def self.genBackupName(fileName)
|
343
324
|
i = fileName.rindex('.')
|
344
325
|
leftFileName = fileName[0, i]
|
345
326
|
rightFileName = fileName[i + 1, fileName.length - i]
|
346
|
-
|
347
|
-
return newFileName
|
327
|
+
"#{leftFileName}_backup.#{rightFileName}"
|
348
328
|
end
|
349
329
|
end
|
350
330
|
end
|
351
|
-
|
352
|
-
|
data/lib/unionpei/version.rb
CHANGED
data/lib/unionpei.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'unionpei/version'
|
4
4
|
require 'unionpei/sdk_config'
|
@@ -6,4 +6,7 @@ require 'unionpei/log_util'
|
|
6
6
|
require 'unionpei/cert_util'
|
7
7
|
require 'unionpei/sdk_util'
|
8
8
|
require 'unionpei/acp_service'
|
9
|
-
require 'unionpei/payment'
|
9
|
+
require 'unionpei/payment'
|
10
|
+
|
11
|
+
module UnionPei
|
12
|
+
end
|
data/readme.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
UnionPei - 非官方银联支付(UnionPay)SDK,使用MIT协议。
|
2
|
+
|
3
|
+
免责声明
|
4
|
+
|
5
|
+
本Gem对以下非官方代码进行封装和改造:
|
6
|
+
https://open.unionpay.com/tjweb/support/faq/mchlist?id=38
|
7
|
+
代码仅供参考学习,生产环境请自行封装代码。
|
8
|
+
|
9
|
+
参考文档
|
10
|
+
|
11
|
+
银联测试参数:https://open.unionpay.com/tjweb/user/mchTest/param
|
12
|
+
测试说明:https://open.unionpay.com/tjweb/support/faq/mchlist?id=516
|
13
|
+
证书说明:https://open.unionpay.com/tjweb/support/faq/mchlist?id=21
|
14
|
+
API文档:https://open.unionpay.com/tjweb/acproduct/APIList?apiservId=448&acpAPIId=754&bussType=0
|
15
|
+
SDK下载:https://open.unionpay.com/tjweb/support/faq/mchlist?id=38
|
16
|
+
测试卡信息:https://open.unionpay.com/tjweb/support/faq/mchlist?id=4
|
17
|
+
B2B:https://open.unionpay.com/tjweb/acproduct/list?apiSvcId=452&index=999
|
18
|
+
|
19
|
+
快速开始(rails)
|
20
|
+
|
21
|
+
class PaymentsController < ApplicationController
|
22
|
+
def union_pay
|
23
|
+
render html: UnionPei::Payment.b2c.html_safe
|
24
|
+
end
|
25
|
+
end
|
data/unionpei.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'unionpei/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = 'unionpei'
|
9
|
+
s.version = UnionPei::VERSION
|
10
|
+
s.summary = 'An unofficial unionpay gem'
|
11
|
+
s.description = 'An unofficial unionpay gem'
|
12
|
+
s.authors = ['memorycancel', 'tianlu1677']
|
13
|
+
s.email = 'memorycancel@gmail.com'
|
14
|
+
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
15
|
+
f.match(%r{^(test|spec|features)/})
|
16
|
+
end
|
17
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
|
20
|
+
s.homepage = 'https://rubygems.org/gems/unionpei'
|
21
|
+
s.license = 'MIT'
|
22
|
+
s.add_runtime_dependency 'iniparse'
|
23
|
+
s.add_runtime_dependency 'jruby-openssl' if RUBY_PLATFORM == 'java'
|
24
|
+
s.add_runtime_dependency 'openssl' if RUBY_PLATFORM == 'ruby'
|
25
|
+
|
26
|
+
s.add_development_dependency 'bundler'
|
27
|
+
s.add_development_dependency 'minitest'
|
28
|
+
s.add_development_dependency 'pry'
|
29
|
+
s.add_development_dependency 'rake'
|
30
|
+
s.add_development_dependency 'webmock'
|
31
|
+
end
|