unionpei 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,328 +1,309 @@
1
- # coding: utf-8
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 "zlib"
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
- def SDKUtil.post(url, content)
17
- LogUtil.info("post url:["+url+"]")
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 == "https"
22
- if !SDKConfig.instance.ifValidateRemoteCert
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('resp:['+res+']')
27
- return res
22
+ LogUtil.info("resp:[#{res}]")
23
+ res
28
24
  end
29
25
 
30
- def SDKUtil.createLinkString(para, sort, encode)
31
- linkString = ""
26
+ def self.createLinkString(para, sort, encode)
27
+ linkString = ''
32
28
  keys = para.keys
33
- if sort
34
- keys = keys.sort
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 + key + "=" + value + "&"
35
+ linkString = "#{linkString}#{key}=#{value}&"
44
36
  end
45
- linkString = linkString[0, linkString.length - 1]
37
+ linkString[0, linkString.length - 1]
46
38
  # print (linkString)
47
- return linkString
48
39
  end
49
40
 
50
- def SDKUtil.filterNoneValue(para)
41
+ def self.filterNoneValue(para)
51
42
  keys = para.keys
52
- for key in keys
43
+ keys.each do |key|
53
44
  value = para[key]
54
- if !value or value == ""
55
- para.delete(key)
56
- end
45
+ para.delete(key) if !value || (value == '')
57
46
  end
58
47
  end
59
48
 
60
- def SDKUtil.buildSignature(req, signCertPath=SDKConfig.instance.signCertPath, signCertPwd=SDKConfig.instance.signCertPwd, secureKey=SDKConfig.instance.secureKey)
61
-
49
+ def self.buildSignature(req, signCertPath = SDKConfig.instance.signCertPath, signCertPwd = SDKConfig.instance.signCertPwd, secureKey = SDKConfig.instance.secureKey)
62
50
  SDKUtil.filterNoneValue(req)
63
- if !req["signMethod"]
64
- LogUtil.error("signMethod must not null")
51
+ unless req['signMethod']
52
+ LogUtil.error('signMethod must not null')
65
53
  return nil
66
54
  end
67
- if !req["version"]
68
- LogUtil.error("version must not null")
55
+ unless req['version']
56
+ LogUtil.error('version must not null')
69
57
  return nil
70
58
  end
71
- if "01" == req["signMethod"]
72
- req["certId"] = CertUtil.getSignCertId(signCertPath, signCertPwd)
73
- LogUtil.info("=== start to sign ===")
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: [" + prestr + "]")
63
+ LogUtil.info("sorted: [#{prestr}]")
76
64
  prestr = SDKUtil.sha256(prestr)
77
- LogUtil.info("sha256: [" + prestr + "]")
78
- LogUtil.info("sign cert: [" + signCertPath + "], pwd: [" + signCertPwd + "]")
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: [" + signature + "]")
82
- elsif "11" == req["signMethod"]
83
- LogUtil.info("=== start to sign ===")
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: [" + prestr + "]")
73
+ LogUtil.info("sorted: [#{prestr}]")
86
74
  if secureKey.nil?
87
- LogUtil.error("secureKey must not null")
75
+ LogUtil.error('secureKey must not null')
88
76
  return nil
89
77
  end
90
- prestr = prestr + "&" + sha256(secureKey)
91
- LogUtil.debug("before final sha256: [" + prestr + "]")
78
+ prestr = "#{prestr}&#{sha256(secureKey)}"
79
+ LogUtil.debug("before final sha256: [#{prestr}]")
92
80
  signature = SDKUtil.sha256(prestr)
93
- LogUtil.info("signature: [" + signature + "]")
94
- elsif "12" == ["signMethod"]
95
- LogUtil.error("sm3算法暂未实现,请勿使用。")
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: [" + req["signMethod"].to_s + "]")
86
+ LogUtil.info("invalid signMethod: [#{req['signMethod']}]")
99
87
  end
100
- LogUtil.info("=== end of sign ===")
101
- req["signature"] = signature
102
- return signature
88
+ LogUtil.info('=== end of sign ===')
89
+ req['signature'] = signature
90
+ signature
103
91
  end
104
92
 
105
- def SDKUtil.paraFilter(para)
93
+ def self.paraFilter(para)
106
94
  result = {}
107
- for key in para.keys
108
- if (key == "signature" or para[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
- return result
102
+ result
115
103
  end
116
104
 
117
- def SDKUtil.sha256(data)
118
- OpenSSL::Digest::SHA256.digest(data).unpack("H*")[0].downcase
105
+ def self.sha256(data)
106
+ OpenSSL::Digest::SHA256.digest(data).unpack1('H*').downcase
119
107
  end
120
108
 
121
- def SDKUtil.putKeyValueToMap(temp, isKey, key, m, decode)
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 SDKUtil.parseQString(respString, decode=false)
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
- for curChar in respString.split("") #遍历整个带解析的字符串
141
- if (isOpen)
142
- if (curChar == openName)
143
- isOpen = false
144
- end
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 = temp + curChar
150
- elsif (curChar == "[")
132
+ openName = '}'
133
+ temp += curChar
134
+ elsif curChar == '['
151
135
  isOpen = true
152
- openName = "]"
153
- temp = temp + curChar
154
- elsif (isKey and curChar == "=")
136
+ openName = ']'
137
+ temp += curChar
138
+ elsif isKey && (curChar == '=')
155
139
  key = temp
156
- temp = ""
140
+ temp = ''
157
141
  isKey = false
158
- elsif (curChar == "&" and not isOpen) # 如果读取到&分割符
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 = temp + curChar
147
+ temp += curChar
164
148
  end
165
149
  end
166
150
  SDKUtil.putKeyValueToMap(temp, isKey, key, resp, decode)
167
- return resp
151
+ resp
168
152
  end
169
153
 
170
- def SDKUtil.verify(resp)
171
- if !resp["signMethod"]
172
- LogUtil.error("signMethod must not null")
154
+ def self.verify(resp)
155
+ unless resp['signMethod']
156
+ LogUtil.error('signMethod must not null')
173
157
  return nil
174
158
  end
175
- if !resp["version"]
176
- LogUtil.error("version must not null")
159
+ unless resp['version']
160
+ LogUtil.error('version must not null')
177
161
  return nil
178
162
  end
179
- if !resp["signature"]
180
- LogUtil.error("signature must not null")
163
+ unless resp['signature']
164
+ LogUtil.error('signature must not null')
181
165
  return nil
182
166
  end
183
- signMethod = resp["signMethod"]
184
- version = resp["version"]
167
+ signMethod = resp['signMethod']
168
+ version = resp['version']
185
169
  result = false
186
- if "01" == signMethod
187
- LogUtil.info("=== start to verify signature ===")
188
- signature = resp.delete("signature")
189
- LogUtil.info("signature: [" + signature + "]")
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: [" + prestr + "]")
176
+ LogUtil.info("sorted: [#{prestr}]")
192
177
  prestr = SDKUtil.sha256(prestr)
193
- LogUtil.info("sha256: [" + prestr + "]")
194
- key = CertUtil.verifyAndGetVerifyKey(resp["signPubKeyCert"])
178
+ LogUtil.info("sha256: [#{prestr}]")
179
+ key = CertUtil.verifyAndGetVerifyKey(resp['signPubKeyCert'])
195
180
  if !key
196
- LogUtil.info("no cert was found by signPubKeyCert: " + resp["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("sha256", signature, prestr)
185
+ result = key.verify('sha256', signature, prestr)
201
186
  end
202
- LogUtil.info("verify signature " + (result ? "succeed" : "fail"))
203
- LogUtil.info("=== end of verify signature ===")
204
- return result
205
- elsif "11" == signMethod or "12" == signMethod
206
- return SDKUtil.verifyBySecureKey(resp, SDKConfig.instance.secureKey)
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 [" + signMethod + "] in validate. ")
209
- return false
193
+ LogUtil.info("Error signMethod [#{signMethod}] in validate. ")
194
+ false
210
195
  end
211
196
  end
212
197
 
213
- def SDKUtil.verifyBySecureKey(resp, secureKey)
214
- if resp["signMethod"].nil?
215
- LogUtil.error("signMethod must not null")
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["signature"].nil?
219
- LogUtil.error("signature must not null")
203
+ if resp['signature'].nil?
204
+ LogUtil.error('signature must not null')
220
205
  return nil
221
206
  end
222
- signMethod = resp["signMethod"]
207
+ signMethod = resp['signMethod']
223
208
  result = false
224
- LogUtil.info("=== start to verify signature ===")
225
- if "11" == signMethod
226
- signature = resp.delete("signature")
227
- LogUtil.info("signature: [" + signature+ "]")
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: [" + prestr + "]")
230
- beforeSha256 = prestr + "&" + sha256(secureKey)
231
- LogUtil.debug("before final sha256: [" + beforeSha256 + "]")
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
- if !result
235
- LogUtil.debug("after final sha256: [" + afterSha256 + "]")
236
- end
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 [" + signMethod.to_s + "] in validate. ")
224
+ LogUtil.info("Error signMethod [#{signMethod}] in validate. ")
241
225
  end
242
- LogUtil.info("verify signature " + (result ? "succeed" : "fail"))
243
- LogUtil.info("=== end of verify signature ===")
244
- return result
226
+ LogUtil.info("verify signature #{result ? 'succeed' : 'fail'}")
227
+ LogUtil.info('=== end of verify signature ===')
228
+ result
245
229
  end
246
230
 
247
- def SDKUtil.createAutoFormHtml(params, reqUrl)
248
- 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\">"
249
- for key in params.keys
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=\"" + key + "\" id=\"" + key +"\" value=\""+ value +"\" />\n"
235
+ result += " <input type=\"hidden\" name=\"#{key}\" id=\"#{key}\" value=\"#{value}\" />\n"
252
236
  end
253
- result += "<!-- <input type=\"submit\" type=\"hidden\">--> </form></body></html>"
254
- LogUtil.info("auto post html:" + result)
255
- return result
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 SDKUtil.encryptPub(data, certPath=SDKConfig.instance.encryptCertPath)
242
+ def self.encryptPub(data, certPath = SDKConfig.instance.encryptCertPath)
259
243
  rsaKey = CertUtil.getEncryptKey(certPath)
260
244
  result = rsaKey.public_encrypt(data)
261
- result = Base64.encode64(result).gsub(/\n|\r/, '')
262
- return result
245
+ Base64.encode64(result).gsub(/\n|\r/, '')
263
246
  end
264
247
 
265
- def SDKUtil.decryptPri(data, certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
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
- result = pkey.private_decrypt(data)
269
- return result
251
+ pkey.private_decrypt(data)
270
252
  end
271
253
 
272
- def SDKUtil.deCodeFileContent(params, fileDirectory)
273
- if !params["fileContent"]
274
- return false
275
- end
276
- LogUtil.info("---------处理后台报文返回的文件---------")
277
- fileContent = params["fileContent"]
278
- if !fileContent
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["fileName"]
285
- LogUtil.info("文件名为空")
286
- filePath = fileDirectory + "/" + params["merId"] + "_" + params["batchNo"] + "_" + params["txnTime"] + ".txt"
265
+ if !params['fileName']
266
+ LogUtil.info('文件名为空')
267
+ filePath = "#{fileDirectory}/#{params['merId']}_#{params['batchNo']}_#{params['txnTime']}.txt"
287
268
  else
288
- filePath = fileDirectory + "/" + params['fileName']
269
+ filePath = "#{fileDirectory}/#{params['fileName']}"
289
270
  end
290
271
  output = File.new(filePath, 'w')
291
- if !output
292
- LogUtil.error "Unable to open file!"
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 "文件位置 >:" + filePath
277
+ LogUtil.info "文件位置 >:#{filePath}"
297
278
  output.close
298
- return true
279
+ true
299
280
  end
300
281
 
301
- def SDKUtil.enCodeFileContent(path)
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 SDKUtil.getEncryptCert(params)
307
- if params['encryptPubKeyCert'].nil? or params['certType'].nil?
308
- LogUtil.error("encryptPubKeyCert or certType is null")
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
- if "01" == certType
296
+ case certType
297
+ when '01'
316
298
  # 更新敏感信息加密公钥
317
- if x509Cert.serial.to_s == CertUtil.getEncryptCertId
318
- return 0
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, "w")
325
- if !f
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
- return 1
334
- elsif "02" == certType
335
- return 0
314
+ 1
315
+ when '02'
316
+ 0
336
317
  else
337
- LogUtil.error("unknown cerType:"+certType)
338
- return -1
318
+ LogUtil.error("unknown cerType:#{certType}")
319
+ -1
339
320
  end
340
321
  end
341
322
 
342
- def SDKUtil.genBackupName(fileName)
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
- newFileName = leftFileName + '_backup' + '.' + rightFileName
347
- return newFileName
327
+ "#{leftFileName}_backup.#{rightFileName}"
348
328
  end
349
329
  end
350
330
  end
351
-
352
-
@@ -1,9 +1,5 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module UnionPei
4
- class Version
5
- def self.to_str
6
- "1.1.0"
7
- end
8
- end
4
+ VERSION = '1.2.0'
9
5
  end
data/lib/unionpei.rb CHANGED
@@ -1,4 +1,4 @@
1
- module UnionPei; end
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