unionpei 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3c36dce06744feba796985728071df04f0ddd51e44f7ad666a9c7e4bb7ff7004
4
+ data.tar.gz: 99bcbe8926142f141ca88553d6b23c5635c380e275336f1ff452edc62e34f5c5
5
+ SHA512:
6
+ metadata.gz: b28982c26e99784beebb5331fe113fba53264666f76661ab67fd20647679c67892266c3e21f5e43fdc73ca50314833f8717b68650bb1dd0e261c405059fcf691
7
+ data.tar.gz: d77a75a98af0f0d5dbbce5bac5f2e2780fcf022019306a4ed649c7710f8ef4cdd595264e63ed9b985eeb4e348f02ceb17a7779b68e766d5f749f0871631dbed3
data/lib/unionpei.rb ADDED
@@ -0,0 +1,9 @@
1
+ module UnionPei; end
2
+
3
+ require 'unionpei/version'
4
+ require 'unionpei/sdk_config'
5
+ require 'unionpei/log_util'
6
+ require 'unionpei/cert_util'
7
+ require 'unionpei/sdk_util'
8
+ require 'unionpei/acp_service'
9
+ require 'unionpei/payment'
@@ -0,0 +1,73 @@
1
+ ;;;;;;;;;;;;;;SDK配置文件(证书方式签名);;;;;;;;;;;;;;;;
2
+ ; 说明:
3
+ ; 1. 使用时请删除后缀的“.证书”,并将此文件复制到根文件夹下替换原来的acp_sdk.ini。
4
+ ; 2. 具体配置项请根据注释修改。
5
+ ;
6
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
+
8
+ [acpsdk]
9
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;入网测试环境交易发送地址(线上测试需要使用生产环境交易请求地址);;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10
+
11
+ ;;交易请求地址
12
+ acpsdk.frontTransUrl=https://gateway.test.95516.com/gateway/api/frontTransReq.do
13
+ acpsdk.backTransUrl=https://gateway.test.95516.com/gateway/api/backTransReq.do
14
+ acpsdk.singleQueryUrl=https://gateway.test.95516.com/gateway/api/queryTrans.do
15
+ acpsdk.batchTransUrl=https://gateway.test.95516.com/gateway/api/batchTrans.do
16
+ acpsdk.fileTransUrl=https://filedownload.test.95516.com/
17
+ acpsdk.appTransUrl=https://gateway.test.95516.com/gateway/api/appTransReq.do
18
+ acpsdk.cardTransUrl=https://gateway.test.95516.com/gateway/api/cardTransReq.do
19
+
20
+ ;以下缴费产品使用,其余产品用不到
21
+ acpsdk.jfFrontTransUrl=https://gateway.test.95516.com/jiaofei/api/frontTransReq.do
22
+ acpsdk.jfBackTransUrl=https://gateway.test.95516.com/jiaofei/api/backTransReq.do
23
+ acpsdk.jfSingleQueryUrl=https://gateway.test.95516.com/jiaofei/api/queryTrans.do
24
+ acpsdk.jfCardTransUrl=https://gateway.test.95516.com/jiaofei/api/cardTransReq.do
25
+ acpsdk.jfAppTransUrl=https://gateway.test.95516.com/jiaofei/api/appTransReq.do
26
+
27
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28
+
29
+ ; 报文版本号,固定5.1.0,请勿改动
30
+ acpsdk.version=5.1.0
31
+
32
+ ; 签名方式,证书方式固定01,请勿改动
33
+ acpsdk.signMethod=01
34
+
35
+ ; 是否验证验签证书的CN,测试环境请设置false,生产环境请设置true。非false的值默认都当true处理。
36
+ acpsdk.ifValidateCNName=false
37
+
38
+ ; 是否验证https证书,测试环境请设置false,生产环境建议优先尝试true,不行再false。非true的值默认都当false处理。
39
+ acpsdk.ifValidateRemoteCert=false
40
+
41
+ ;后台通知地址,填写后台接收银联前台通知的地址
42
+ acpsdk.backUrl=http://222.222.222.222:8080/backRcvResponse
43
+
44
+ ;前台通知地址,填写后台接收银联后台通知的地址,必须外网能访问
45
+ acpsdk.frontUrl=http://localhost:8080/frontRcvResponse
46
+
47
+ ;;;;;;;;;;;;;;;;;;;;;;;;;入网测试环境签名证书配置 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
48
+ ; 多证书的情况证书路径为代码指定,可不对此块做配置。
49
+ ; 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
50
+ ; 测试环境证书位于assets/测试环境证书/文件夹下,请复制到d:/certs文件夹。生产环境证书由业务部门邮件发送。
51
+ ; windows样例:
52
+ acpsdk.signCert.path=certs/acp_test_sign.pfx
53
+ ; linux样例(注意:在linux下读取证书需要保证证书有被应用读的权限)(后续其他路径配置也同此条说明)
54
+ ;acpsdk.signCert.path=/SERVICE01/usr/ac_frnas/conf/ACPtest/ac00000000000001.pfx
55
+
56
+ ; 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败
57
+ acpsdk.signCert.pwd=000000
58
+
59
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;加密证书配置;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
60
+ ; 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
61
+ acpsdk.encryptCert.path=certs/acp_test_enc.cer
62
+
63
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;验签证书配置;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
64
+ ; 验签中级证书(证书位于assets/测试环境证书/文件夹下,请复制到d:/certs文件夹)
65
+ acpsdk.middleCert.path=certs/acp_test_middle.cer
66
+ ; 验签根证书(证书位于assets/测试环境证书/文件夹下,请复制到d:/certs文件夹)
67
+ acpsdk.rootCert.path=certs/acp_test_root.cer
68
+
69
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;日志配置;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70
+ ; 日志打印路径,linux注意要有写权限
71
+ acpsdk.log.file.path=upacp_sdk_ruby.log
72
+ ; 日志级别,debug级别会打印密钥,生产请用info或以上级别
73
+ acpsdk.log.level=DEBUG
@@ -0,0 +1,104 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require_relative 'log_util'
4
+ require_relative 'sdk_config'
5
+ require_relative 'sdk_util'
6
+
7
+ module UnionPei
8
+ class AcpService
9
+ def AcpService.sign(req, certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
10
+ SDKUtil.buildSignature(req, certPath, certPwd)
11
+ end
12
+
13
+ def AcpService.signByCertInfo(req, certPath, certPwd)
14
+ SDKUtil.buildSignature(req, certPath, certPwd)
15
+ end
16
+
17
+ def AcpService.signBySecureKey(req, secureKey)
18
+ SDKUtil.buildSignature(req, nil, nil, secureKey)
19
+ end
20
+
21
+ def AcpService.validate(resp)
22
+ SDKUtil.verify(resp)
23
+ end
24
+
25
+ def AcpService.validateBySecureKey(resp, secureKey)
26
+ SDKUtil.verifyBySecureKey(resp, secureKey)
27
+ end
28
+
29
+ def AcpService.post(params, url)
30
+ content = SDKUtil.createLinkString(params, false, true)
31
+ respString = SDKUtil.post(url, content)
32
+ resp = SDKUtil.parseQString(respString)
33
+ return resp
34
+ end
35
+
36
+ def AcpService.createAutoFormHtml(params, reqUrl)
37
+ return SDKUtil.createAutoFormHtml(params, reqUrl)
38
+ end
39
+
40
+ def AcpService.getCustomerInfo(customerInfo)
41
+ if(customerInfo == nil or customerInfo.length == 0)
42
+ return ""
43
+ end
44
+ return Base.encode64("{" + SDKUtil.createLinkString(customerInfo,false,false)+"}").gsub(/\n|\r/, '')
45
+ end
46
+
47
+ def AcpService.getCustomerInfoWithEncrypt(customerInfo)
48
+ if(customerInfo == nil or customerInfo.length == 0)
49
+ return ""
50
+ end
51
+ encryptedInfo = {}
52
+ for key in customerInfo.keys
53
+ if (key == 'phoneNo' or key == 'cvn2' or key == 'expired')
54
+ encryptedInfo[key] = customerInfo.delete(key)
55
+ end
56
+ end
57
+ if (encryptedInfo.length > 0)
58
+ encryptedInfo = SDKUtil.createLinkString(encryptedInfo, false, false)
59
+ encryptedInfo = AcpService.encryptData(encryptedInfo, SDKConfig.instance.encryptCertPath)
60
+ customerInfo['encryptedInfo'] = encryptedInfo
61
+ end
62
+ return Base64.encode64("{" + SDKUtil.createLinkString(customerInfo,false,false)+"}").gsub(/\n|\r/, '')
63
+ end
64
+
65
+ def AcpService.parseCustomerInfo(customerInfostr, certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
66
+ customerInfostr = Base64.decode64(customerInfostr)
67
+ customerInfostr = customerInfostr[1, customerInfostr.length-1]
68
+ customerInfo = SDKUtil.parseQString(customerInfostr)
69
+ if customerInfo['encryptedInfo']
70
+ encryptedInfoStr = customerInfo.delete('encryptedInfo')
71
+ encryptedInfoStr = AcpService.decryptData(encryptedInfoStr, certPath, certPwd)
72
+ encryptedInfo = SDKUtil.parseQString(encryptedInfoStr)
73
+ for key in encryptedInfo.keys
74
+ customerInfo[key] = encryptedInfo[key]
75
+ end
76
+ end
77
+ return customerInfo
78
+ end
79
+
80
+ def AcpService.getEncryptCertId
81
+ return CertUtil.getEncryptCertId
82
+ end
83
+
84
+ def AcpService.encryptData(data, certPath=SDKConfig.instance.encryptCertPath)
85
+ return SDKUtil.encryptPub(data, certPath)
86
+ end
87
+
88
+ def AcpService.decryptData(data, certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
89
+ return SDKUtil.decryptPri(data, certPath, certPwd)
90
+ end
91
+
92
+ def AcpService.deCodeFileContent(params, fileDirectory)
93
+ return SDKUtil.deCodeFileContent(params, fileDirectory)
94
+ end
95
+
96
+ def AcpService.enCodeFileContent(path)
97
+ return SDKUtil.enCodeFileContent(path)
98
+ end
99
+
100
+ def AcpService.updateEncryptCert(params)
101
+ return SDKUtil.getEncryptCert(params)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,193 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require_relative 'log_util'
4
+ require_relative 'sdk_config'
5
+
6
+
7
+ module UnionPei
8
+ UNIONPAY_CNNAME = "中国银联股份有限公司"
9
+
10
+ class Cert
11
+ attr_accessor :cert, :certId, :key
12
+ @certId
13
+ @key
14
+ @cert
15
+ end
16
+
17
+ class CertUtil
18
+
19
+ @@signCerts = {}
20
+ @@encryptCert = {}
21
+ @@verifyCerts = {} #5.0.0验签证书,key是certId
22
+ @@verifyCerts5_1_0 = {} #5.1.0验签证书,key是base64的证书内容
23
+ @@middleCert = nil
24
+ @@rootCert = nil
25
+
26
+ private
27
+ def CertUtil.initSignCert(certPath, certPwd)
28
+ if !certPath || !certPwd
29
+ LogUtil.info("signCertPath or signCertPwd is none, exit initSignCert")
30
+ return
31
+ end
32
+ LogUtil.info("读取签名证书……")
33
+ cert = Cert.new
34
+ file = IO.binread(certPath)
35
+ p12 = OpenSSL::PKCS12.new(file, certPwd)
36
+ cert.certId = p12.certificate.serial.to_s
37
+ cert.cert = p12.certificate
38
+ cert.key = p12.key
39
+ @@signCerts[certPath] = cert
40
+ LogUtil.info("签名证书读取成功,序列号:" + cert.certId)
41
+ end
42
+
43
+ def CertUtil.initEncryptCert(certPath=SDKConfig.instance.encryptCertPath)
44
+ if !certPath
45
+ LogUtil.info("encryptCertPath is none, exit initEncryptCert")
46
+ return
47
+ end
48
+ LogUtil.info("读取加密证书……")
49
+ cert = Cert.new
50
+ file = IO.binread(certPath)
51
+ x509Cert = OpenSSL::X509::Certificate.new(file)
52
+ cert.cert = x509Cert
53
+ cert.certId = x509Cert.serial.to_s
54
+ cert.key = x509Cert.public_key
55
+ @@encryptCert[certPath] = cert
56
+ LogUtil.info("加密证书读取成功,序列号:" + cert.certId)
57
+ end
58
+
59
+ def CertUtil.initRootCert()
60
+ if @@rootCert
61
+ return
62
+ end
63
+ if !SDKConfig.instance.rootCertPath
64
+ LogUtil.info("rootCertPath is none, exit initRootCert")
65
+ return
66
+ end
67
+ LogUtil.info("start initRootCert")
68
+ file = IO.binread(SDKConfig.instance.rootCertPath)
69
+ x509Cert = OpenSSL::X509::Certificate.new(file)
70
+ @@rootCert = x509Cert
71
+ LogUtil.info("initRootCert succeed")
72
+ end
73
+
74
+ def CertUtil.initMiddleCert()
75
+ if @@middleCert
76
+ return
77
+ end
78
+ if !SDKConfig.instance.middleCertPath
79
+ LogUtil.info("middleCertPath is none, exit initMiddleCert")
80
+ return
81
+ end
82
+ LogUtil.info("start initMiddleCert")
83
+ file = IO.binread(SDKConfig.instance.middleCertPath)
84
+ x509Cert = OpenSSL::X509::Certificate.new(file)
85
+ @@middleCert = x509Cert
86
+ LogUtil.info("initMiddleCert succeed")
87
+ end
88
+
89
+ public
90
+ def CertUtil.getSignPriKey(certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
91
+ if !@@signCerts[certPath]
92
+ CertUtil.initSignCert(certPath, certPwd)
93
+ end
94
+ @@signCerts[certPath].key
95
+ end
96
+
97
+ def CertUtil.getSignCertId(certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
98
+ if !@@signCerts[certPath]
99
+ CertUtil.initSignCert(certPath, certPwd)
100
+ end
101
+ @@signCerts[certPath].certId
102
+ end
103
+
104
+ def CertUtil.getEncryptKey(certPath=SDKConfig.instance.encryptCertPath)
105
+ if !@@encryptCert[certPath]
106
+ CertUtil.initEncryptCert(certPath)
107
+ end
108
+ @@encryptCert[certPath].key
109
+ end
110
+
111
+ def CertUtil.getEncryptCertId(certPath=SDKConfig.instance.encryptCertPath)
112
+ if !@@encryptCert[certPath]
113
+ CertUtil.initEncryptCert(certPath)
114
+ end
115
+ @@encryptCert[certPath].certId
116
+ end
117
+
118
+ def CertUtil.verifyAndGetVerifyKey(certBase64String)
119
+
120
+ if @@verifyCerts5_1_0[certBase64String]
121
+ return @@verifyCerts5_1_0[certBase64String].key
122
+ end
123
+ initMiddleCert
124
+ initRootCert
125
+
126
+ x509Cert = OpenSSL::X509::Certificate.new(certBase64String)
127
+
128
+ cert = Cert.new
129
+ cert.cert = x509Cert
130
+ cert.certId = x509Cert.serial.to_s
131
+ cert.key = x509Cert.public_key
132
+
133
+ store = OpenSSL::X509::Store.new
134
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
135
+ store.add_cert(x509Cert)
136
+ store.add_cert(@@middleCert)
137
+ store.add_cert(@@rootCert)
138
+ if !store.verify(x509Cert)
139
+ LogUtil.error("validate signPubKeyCert by cert chain failed, error=" + store.error + ", error string=" + store.error_string)
140
+ return nil
141
+ end
142
+
143
+ sSubject = x509Cert.subject.to_s
144
+ ss = sSubject.split("@")
145
+ if ss.length <= 2
146
+ LogUtil.error("error sSubject: " + sSubject)
147
+ return nil
148
+ end
149
+ cn = ss[2];
150
+ if SDKConfig.instance.ifValidateCNName
151
+ if UNIONPAY_CNNAME != cn
152
+ LogUtil.error("cer owner is not CUP:" + cn)
153
+ return nil
154
+ elsif UNIONPAY_CNNAME != cn and cn != "00040000:SIGN" #测试环境目前是00040000:SIGN
155
+ LogUtil.error("cer owner is not CUP:" + cn)
156
+ return nil
157
+ end
158
+ end
159
+
160
+ LogUtil.info("validate signPubKeyCert by cert succeed: " + certBase64String)
161
+ @@verifyCerts5_1_0[certBase64String] = cert;
162
+ return @@verifyCerts5_1_0[certBase64String].key
163
+
164
+ # 用bc的jar用中级证书验证可以单独验时间,然后再用中级证书验一下,但为了和谐统一,目前改store验证书链验证了。
165
+ # if Time.new<x509Cert.not_before or Time.new>x509Cert.not_after
166
+ # LogUtil..info("verifyPubKeyCert has expired")
167
+ # return nil
168
+ # end
169
+ # if x509Cert.verify(@@middleKey)
170
+ # return x509Cert.public_key
171
+ # else
172
+ # LogUtil.info("validate signPubKeyCert by rootCert failed")
173
+ # return nil
174
+ # end
175
+ end
176
+
177
+ def CertUtil.getDecryptPriKey(certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
178
+ if !@@signCerts[certPath]
179
+ CertUtil.initSignCert(certPath, certPwd)
180
+ end
181
+ @@signCerts[certPath].key
182
+ end
183
+
184
+ def CertUtil.resetEncryptCertPublicKey()
185
+ @@encryptCert = {}
186
+ CertUtil.initEncryptCert
187
+ end
188
+
189
+ def CertUtil.getX509Cert(strCert)
190
+ OpenSSL::X509::Certificate.new(strCert)
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,82 @@
1
+ # coding: utf-8
2
+
3
+ require 'singleton'
4
+ require 'logger'
5
+ require 'net/https'
6
+ require 'uri'
7
+ require_relative 'sdk_config'
8
+
9
+ module UnionPei
10
+ class LogUtil
11
+
12
+ @@logger = nil
13
+
14
+ private_class_method :new
15
+
16
+ private
17
+
18
+ def LogUtil.getLogger
19
+ if !@@logger
20
+ puts "init LogUtil"
21
+ if SDKConfig.instance.logFilePath.nil?
22
+ @@logger = Logger.new(STDOUT)
23
+ else
24
+ @@logger = Logger.new(SDKConfig.instance.logFilePath)
25
+ end
26
+ @@logger.datetime_format = '%Y-%m-%d %H:%M:%S'
27
+ @@logger.formatter = proc do |severity, datetime, progname, msg|
28
+ "#{datetime} [#{severity}] #{progname}: #{msg}\n"
29
+ end
30
+ @@logger.level = case SDKConfig.instance.logLevel.upcase
31
+ when 'INFO' then
32
+ Logger::INFO
33
+ when 'DEBUG' then
34
+ Logger::DEBUG
35
+ when 'WARN' then
36
+ Logger::WARN
37
+ when 'ERROR' then
38
+ Logger::ERROR
39
+ when 'FATAL' then
40
+ Logger::FATAL
41
+ else
42
+ Logger::UNKNOWN
43
+ end
44
+ end
45
+ p = LogUtil.parse_caller(caller(0)[2]) #可能有多线程问题?我才不管哼
46
+ @@logger.progname = p[0].to_s + ":" + p[1].to_s
47
+ @@logger
48
+ end
49
+
50
+ def LogUtil.parse_caller(at)
51
+ if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
52
+ file = $1
53
+ line = $2.to_i
54
+ method = $3
55
+ [file, line, method]
56
+ end
57
+ end
58
+
59
+ public
60
+
61
+ def LogUtil.info(msg)
62
+ LogUtil.getLogger.info(msg)
63
+ end
64
+
65
+ def LogUtil.debug(msg)
66
+ LogUtil.getLogger.debug(msg)
67
+ end
68
+
69
+ def LogUtil.warn(msg)
70
+ LogUtil.getLogger.warn(msg)
71
+ end
72
+
73
+ def LogUtil.error(msg)
74
+ LogUtil.getLogger.error(msg)
75
+ end
76
+
77
+ def LogUtil.fatal(msg)
78
+ LogUtil.getLogger.fatal(msg)
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ #encoding: utf-8
2
+ require "date"
3
+ require_relative 'sdk_config'
4
+ require_relative 'acp_service'
5
+
6
+ module UnionPei
7
+ class Payment
8
+ class << self
9
+ def B2C
10
+ req = {}
11
+
12
+ req["version"] = UnionPei::SDKConfig.instance.version
13
+ req["encoding"] = UnionPei::SDKConfig.instance.encoding
14
+ req["signMethod"] = UnionPei::SDKConfig.instance.signMethod
15
+
16
+ req["frontUrl"] = UnionPei::SDKConfig.instance.frontUrl
17
+ req["backUrl"] = UnionPei::SDKConfig.instance.backUrl
18
+
19
+ req["txnType"] = "01"
20
+ req["txnSubType"] = "01"
21
+ req["bizType"] = "000201" # 000201 是b2c / 000202 是 b2b
22
+ req["channelType"] = "07"
23
+ req["currencyCode"] = "156"
24
+ req["txnAmt"] = "881000"
25
+
26
+ req["merId"] = "777290058189920"
27
+ req["orderId"] = DateTime.parse(Time.now.to_s).strftime("%Y%m%d%H%M%S").to_s
28
+ req["txnTime"] = DateTime.parse(Time.now.to_s).strftime("%Y%m%d%H%M%S").to_s
29
+ req["accessType"] = "0"
30
+
31
+ #签名示例
32
+ UnionPei::AcpService.sign(req)
33
+ url = UnionPei::SDKConfig.instance.frontTransUrl
34
+
35
+ #前台自提交表单示例
36
+ resp = UnionPei::AcpService.createAutoFormHtml(req, url)
37
+ resp
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,76 @@
1
+ # coding: utf-8
2
+
3
+ require 'iniparse'
4
+ require 'singleton'
5
+
6
+ module UnionPei
7
+ class SDKConfig
8
+ include Singleton
9
+ attr_reader :frontTransUrl, :singleQueryUrl, :backTransUrl, :batchTransUrl, :fileTransUrl, :appTransUrl,
10
+ :cardTransUrl, :jfFrontTransUrl, :jfSingleQueryUrl, :jfBackTransUrl, :jfCardTransUrl,
11
+ :jfAppTransUrl, :qrcBackTransUrl, :qrcB2cIssBackTransUrl, :qrcB2cMerBackTransUrl,
12
+ :signMethod, :version, :ifValidateCNName, :ifValidateRemoteCert, :signCertPath, :signCertPwd,
13
+ :validateCertDir, :encryptCertPath, :rootCertPath, :middleCertPath, :frontUrl, :backUrl,
14
+ :encoding, :secureKey, :logFilePath, :logLevel
15
+
16
+ def initialize
17
+
18
+ path = File.dirname(__FILE__)
19
+ ini = IniParse.parse(File.read("#{path}/acp_sdk.ini").force_encoding("UTF-8"))
20
+ puts 'load config: ' + "#{path}/acp_sdk.ini"
21
+
22
+ @frontTransUrl = ini["acpsdk"]["acpsdk.frontTransUrl"]
23
+ @singleQueryUrl = ini["acpsdk"]["acpsdk.singleQueryUrl"]
24
+ @backTransUrl = ini["acpsdk"]["acpsdk.backTransUrl"]
25
+ @batchTransUrl = ini["acpsdk"]["acpsdk.batchTransUrl"]
26
+ @fileTransUrl = ini["acpsdk"]["acpsdk.fileTransUrl"]
27
+ @appTransUrl = ini["acpsdk"]["acpsdk.appTransUrl"]
28
+ @cardTransUrl = ini["acpsdk"]["acpsdk.cardTransUrl"]
29
+
30
+ @jfFrontTransUrl = ini["acpsdk"]["acpsdk.jfFrontTransUrl"]
31
+ @jfSingleQueryUrl = ini["acpsdk"]["acpsdk.jfSingleQueryUrl"]
32
+ @jfBackTransUrl = ini["acpsdk"]["acpsdk.jfBackTransUrl"]
33
+ @jfCardTransUrl = ini["acpsdk"]["acpsdk.jfCardTransUrl"]
34
+ @jfAppTransUrl = ini["acpsdk"]["acpsdk.jfAppTransUrl"]
35
+
36
+ @qrcBackTransUrl = ini["acpsdk"]["acpsdk.qrcBackTransUrl"]
37
+ @qrcB2cIssBackTransUrl = ini["acpsdk"]["acpsdk.qrcB2cIssBackTransUrl"]
38
+ @qrcB2cMerBackTransUrl = ini["acpsdk"]["acpsdk.qrcB2cMerBackTransUrl"]
39
+
40
+ @signMethod = ini["acpsdk"]["acpsdk.signMethod"]
41
+ @signMethod = @signMethod.to_s if !@signMethod.nil?
42
+ @version = ini["acpsdk"]["acpsdk.version"]
43
+ @version = "5.0.0" if @version.nil?
44
+
45
+ @ifValidateCNName = ini["acpsdk"]["acpsdk.ifValidateCNName"]
46
+ @ifValidateCNName = true if @ifValidateCNName.nil?
47
+ @ifValidateRemoteCert = ini["acpsdk"]["acpsdk.ifValidateRemoteCert"]
48
+ @ifValidateRemoteCert = false if @ifValidateRemoteCert.nil?
49
+
50
+ @signCertPath = ini["acpsdk"]["acpsdk.signCert.path"]
51
+ @signCertPwd = ini["acpsdk"]["acpsdk.signCert.pwd"]
52
+ @signCertPwd = @signCertPwd.to_s if !@signCertPwd.nil?
53
+
54
+ @validateCertDir = ini["acpsdk"]["acpsdk.validateCert.dir"]
55
+ @encryptCertPath = ini["acpsdk"]["acpsdk.encryptCert.path"]
56
+ @rootCertPath = ini["acpsdk"]["acpsdk.rootCert.path"]
57
+ @middleCertPath = ini["acpsdk"]["acpsdk.middleCert.path"]
58
+
59
+ @frontUrl = ini["acpsdk"]["acpsdk.frontUrl"]
60
+ @backUrl = ini["acpsdk"]["acpsdk.backUrl"]
61
+
62
+ @encoding = ini["acpsdk"]["acpsdk.encoding"]
63
+ @secureKey = ini["acpsdk"]["acpsdk.secureKey"]
64
+ @secureKey = @secureKey.to_s if !@secureKey.nil?
65
+
66
+ @logFilePath = ini["acpsdk"]["acpsdk.log.file.path"]
67
+ @logLevel = ini["acpsdk"]["acpsdk.log.level"]
68
+
69
+ @encoding = 'UTF-8'
70
+
71
+ end
72
+ end
73
+ end
74
+
75
+
76
+
@@ -0,0 +1,350 @@
1
+ require 'singleton'
2
+ require 'logger'
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'base64'
6
+ require "zlib"
7
+ require_relative 'sdk_config'
8
+ require_relative 'cert_util'
9
+
10
+
11
+ module UnionPei
12
+ class SDKUtil
13
+
14
+ def SDKUtil.post(url, content)
15
+ LogUtil.info("post url:["+url+"]")
16
+ LogUtil.info("post content:["+content+"]")
17
+ uri = URI.parse(url)
18
+ http = Net::HTTP.new(uri.host, uri.port)
19
+ http.use_ssl = true if uri.scheme == "https"
20
+ if !SDKConfig.instance.ifValidateRemoteCert
21
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
22
+ end
23
+ res = http.post(uri.path, content).body.force_encoding(SDKConfig.instance.encoding)
24
+ LogUtil.info('resp:['+res+']')
25
+ return res
26
+ end
27
+
28
+ def SDKUtil.createLinkString(para, sort, encode)
29
+ linkString = ""
30
+ keys = para.keys
31
+ if sort
32
+ keys = keys.sort
33
+ end
34
+ for key in keys
35
+ value = para[key]
36
+ # print(key + ":" + value)
37
+ if encode
38
+ value = URI.encode_www_form_component(value)
39
+ end
40
+ # print(str(type(key))+":"+str(type(value))+":"+str(key)+":"+str(value))
41
+ linkString = linkString + key + "=" + value + "&"
42
+ end
43
+ linkString = linkString[0, linkString.length - 1]
44
+ # print (linkString)
45
+ return linkString
46
+ end
47
+
48
+ def SDKUtil.filterNoneValue(para)
49
+ keys = para.keys
50
+ for key in keys
51
+ value = para[key]
52
+ if !value or value == ""
53
+ para.delete(key)
54
+ end
55
+ end
56
+ end
57
+
58
+ def SDKUtil.buildSignature(req, signCertPath=SDKConfig.instance.signCertPath, signCertPwd=SDKConfig.instance.signCertPwd, secureKey=SDKConfig.instance.secureKey)
59
+
60
+ SDKUtil.filterNoneValue(req)
61
+ if !req["signMethod"]
62
+ LogUtil.error("signMethod must not null")
63
+ return nil
64
+ end
65
+ if !req["version"]
66
+ LogUtil.error("version must not null")
67
+ return nil
68
+ end
69
+ if "01" == req["signMethod"]
70
+ req["certId"] = CertUtil.getSignCertId(signCertPath, signCertPwd)
71
+ LogUtil.info("=== start to sign ===")
72
+ prestr = SDKUtil.createLinkString(req, true, false)
73
+ LogUtil.info("sorted: [" + prestr + "]")
74
+ prestr = SDKUtil.sha256(prestr)
75
+ LogUtil.info("sha256: [" + prestr + "]")
76
+ LogUtil.info("sign cert: [" + signCertPath + "], pwd: [" + signCertPwd + "]")
77
+ key = CertUtil.getSignPriKey(signCertPath, signCertPwd)
78
+ signature = Base64.encode64(key.sign('sha256', prestr)).gsub(/\n|\r/, '')
79
+ LogUtil.info("signature: [" + signature + "]")
80
+ elsif "11" == req["signMethod"]
81
+ LogUtil.info("=== start to sign ===")
82
+ prestr = createLinkString(req, true, false)
83
+ LogUtil.info("sorted: [" + prestr + "]")
84
+ if secureKey.nil?
85
+ LogUtil.error("secureKey must not null")
86
+ return nil
87
+ end
88
+ prestr = prestr + "&" + sha256(secureKey)
89
+ LogUtil.debug("before final sha256: [" + prestr + "]")
90
+ signature = SDKUtil.sha256(prestr)
91
+ LogUtil.info("signature: [" + signature + "]")
92
+ elsif "12" == ["signMethod"]
93
+ LogUtil.error("sm3算法暂未实现,请勿使用。")
94
+ return nil
95
+ else
96
+ LogUtil.info("invalid signMethod: [" + req["signMethod"].to_s + "]")
97
+ end
98
+ LogUtil.info("=== end of sign ===")
99
+ req["signature"] = signature
100
+ return signature
101
+ end
102
+
103
+ def SDKUtil.paraFilter(para)
104
+ result = {}
105
+ for key in para.keys
106
+ if (key == "signature" or para[key] == "")
107
+ next
108
+ else
109
+ result[key] = para[key]
110
+ end
111
+ end
112
+ return result
113
+ end
114
+
115
+ def SDKUtil.sha256(data)
116
+ OpenSSL::Digest::SHA256.digest(data).unpack("H*")[0].downcase
117
+ end
118
+
119
+ def SDKUtil.putKeyValueToMap(temp, isKey, key, m, decode)
120
+ if isKey
121
+ m[key.to_s] = ""
122
+ else
123
+ if decode
124
+ temp = URI.decode_www_form_component(temp)
125
+ end
126
+ m[key.to_s] = temp
127
+ end
128
+ end
129
+
130
+ def SDKUtil.parseQString(respString, decode=false)
131
+ resp = {}
132
+ temp = ""
133
+ key = ""
134
+ isKey = true
135
+ isOpen = false;
136
+ openName = "\0";
137
+
138
+ for curChar in respString.split("") #遍历整个带解析的字符串
139
+ if (isOpen)
140
+ if (curChar == openName)
141
+ isOpen = false
142
+ end
143
+ temp = temp + curChar
144
+ elsif (curChar == "{")
145
+ isOpen = true
146
+ openName = "}"
147
+ temp = temp + curChar
148
+ elsif (curChar == "[")
149
+ isOpen = true
150
+ openName = "]"
151
+ temp = temp + curChar
152
+ elsif (isKey and curChar == "=")
153
+ key = temp
154
+ temp = ""
155
+ isKey = false
156
+ elsif (curChar == "&" and not isOpen) # 如果读取到&分割符
157
+ SDKUtil.putKeyValueToMap(temp, isKey, key, resp, decode)
158
+ temp = ""
159
+ isKey = true
160
+ else
161
+ temp = temp + curChar
162
+ end
163
+ end
164
+ SDKUtil.putKeyValueToMap(temp, isKey, key, resp, decode)
165
+ return resp
166
+ end
167
+
168
+ def SDKUtil.verify(resp)
169
+ if !resp["signMethod"]
170
+ LogUtil.error("signMethod must not null")
171
+ return nil
172
+ end
173
+ if !resp["version"]
174
+ LogUtil.error("version must not null")
175
+ return nil
176
+ end
177
+ if !resp["signature"]
178
+ LogUtil.error("signature must not null")
179
+ return nil
180
+ end
181
+ signMethod = resp["signMethod"]
182
+ version = resp["version"]
183
+ result = false
184
+ if "01" == signMethod
185
+ LogUtil.info("=== start to verify signature ===")
186
+ signature = resp.delete("signature")
187
+ LogUtil.info("signature: [" + signature + "]")
188
+ prestr = SDKUtil.createLinkString(resp, true, false)
189
+ LogUtil.info("sorted: [" + prestr + "]")
190
+ prestr = SDKUtil.sha256(prestr)
191
+ LogUtil.info("sha256: [" + prestr + "]")
192
+ key = CertUtil.verifyAndGetVerifyKey(resp["signPubKeyCert"])
193
+ if !key
194
+ LogUtil.info("no cert was found by signPubKeyCert: " + resp["signPubKeyCert"])
195
+ result = false
196
+ else
197
+ signature = Base64.decode64(signature)
198
+ result = key.verify("sha256", signature, prestr)
199
+ end
200
+ LogUtil.info("verify signature " + (result ? "succeed" : "fail"))
201
+ LogUtil.info("=== end of verify signature ===")
202
+ return result
203
+ elsif "11" == signMethod or "12" == signMethod
204
+ return SDKUtil.verifyBySecureKey(resp, SDKConfig.instance.secureKey)
205
+ else
206
+ LogUtil.info("Error signMethod [" + signMethod + "] in validate. ")
207
+ return false
208
+ end
209
+ end
210
+
211
+ def SDKUtil.verifyBySecureKey(resp, secureKey)
212
+ if resp["signMethod"].nil?
213
+ LogUtil.error("signMethod must not null")
214
+ return nil
215
+ end
216
+ if resp["signature"].nil?
217
+ LogUtil.error("signature must not null")
218
+ return nil
219
+ end
220
+ signMethod = resp["signMethod"]
221
+ result = false
222
+ LogUtil.info("=== start to verify signature ===")
223
+ if "11" == signMethod
224
+ signature = resp.delete("signature")
225
+ LogUtil.info("signature: [" + signature+ "]")
226
+ prestr = createLinkString(resp, true, false)
227
+ LogUtil.info("sorted: [" + prestr + "]")
228
+ beforeSha256 = prestr + "&" + sha256(secureKey)
229
+ LogUtil.debug("before final sha256: [" + beforeSha256 + "]")
230
+ afterSha256 = sha256(beforeSha256)
231
+ result = (afterSha256 == signature)
232
+ if !result
233
+ LogUtil.debug("after final sha256: [" + afterSha256 + "]")
234
+ end
235
+ elsif "12" == signMethod
236
+ LogUtil.error("sm3算法暂未实现,请勿使用。")
237
+ else
238
+ LogUtil.info("Error signMethod [" + signMethod.to_s + "] in validate. ")
239
+ end
240
+ LogUtil.info("verify signature " + (result ? "succeed" : "fail"))
241
+ LogUtil.info("=== end of verify signature ===")
242
+ return result
243
+ end
244
+
245
+ def SDKUtil.createAutoFormHtml(params, reqUrl)
246
+ 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\">"
247
+ for key in params.keys
248
+ value = params[key]
249
+ result += " <input type=\"hidden\" name=\"" + key + "\" id=\"" + key +"\" value=\""+ value +"\" />\n"
250
+ end
251
+ result += "<!-- <input type=\"submit\" type=\"hidden\">--> </form></body></html>"
252
+ LogUtil.info("auto post html:" + result)
253
+ return result
254
+ end
255
+
256
+ def SDKUtil.encryptPub(data, certPath=SDKConfig.instance.encryptCertPath)
257
+ rsaKey = CertUtil.getEncryptKey(certPath)
258
+ result = rsaKey.public_encrypt(data)
259
+ result = Base64.encode64(result).gsub(/\n|\r/, '')
260
+ return result
261
+ end
262
+
263
+ def SDKUtil.decryptPri(data, certPath=SDKConfig.instance.signCertPath, certPwd=SDKConfig.instance.signCertPwd)
264
+ pkey = CertUtil.getDecryptPriKey(certPath, certPwd)
265
+ data = Base64.decode64(data)
266
+ result = pkey.private_decrypt(data)
267
+ return result
268
+ end
269
+
270
+ def SDKUtil.deCodeFileContent(params, fileDirectory)
271
+ if !params["fileContent"]
272
+ return false
273
+ end
274
+ LogUtil.info("---------处理后台报文返回的文件---------")
275
+ fileContent = params["fileContent"]
276
+ if !fileContent
277
+ LogUtil.info("文件内容为空")
278
+ return false
279
+ end
280
+ fileContent = Zlib::Inflate.inflate(Base64.decode64(fileContent))
281
+ filePath = ''
282
+ if !params["fileName"]
283
+ LogUtil.info("文件名为空")
284
+ filePath = fileDirectory + "/" + params["merId"] + "_" + params["batchNo"] + "_" + params["txnTime"] + ".txt"
285
+ else
286
+ filePath = fileDirectory + "/" + params['fileName']
287
+ end
288
+ output = File.new(filePath, 'w')
289
+ if !output
290
+ LogUtil.error "Unable to open file!"
291
+ return false
292
+ end
293
+ output.syswrite(fileContent)
294
+ LogUtil.info "文件位置 >:" + filePath
295
+ output.close
296
+ return true
297
+ end
298
+
299
+ def SDKUtil.enCodeFileContent(path)
300
+ fileContent = IO.binread(path)
301
+ fileContent = Base64.encode64(Zlib::Deflate.deflate(fileContent)).gsub(/\n|\r/, '')
302
+ end
303
+
304
+ def SDKUtil.getEncryptCert(params)
305
+ if params['encryptPubKeyCert'].nil? or params['certType'].nil?
306
+ LogUtil.error("encryptPubKeyCert or certType is null")
307
+ return -1
308
+ end
309
+ strCert = params['encryptPubKeyCert']
310
+ certType = params['certType']
311
+
312
+ x509Cert = CertUtil.getX509Cert(strCert)
313
+ if "01" == certType
314
+ # 更新敏感信息加密公钥
315
+ if x509Cert.serial.to_s == CertUtil.getEncryptCertId
316
+ return 0
317
+ end
318
+ localCertPath = SDKConfig.instance.encryptCertPath
319
+ newLocalCertPath = SDKUtil.genBackupName(localCertPath)
320
+ # 将本地证书进行备份存储
321
+ File.rename(localCertPath, newLocalCertPath)
322
+ f = File.new(localCertPath, "w")
323
+ if !f
324
+ LogUtil.error 'Unable to open file!'
325
+ return -1
326
+ end
327
+ f.syswrite(strCert)
328
+ f.close
329
+ LogUtil.info('save new encryptPubKeyCert success')
330
+ CertUtil.resetEncryptCertPublicKey
331
+ return 1
332
+ elsif "02" == certType
333
+ return 0
334
+ else
335
+ LogUtil.error("unknown cerType:"+certType)
336
+ return -1
337
+ end
338
+ end
339
+
340
+ def SDKUtil.genBackupName(fileName)
341
+ i = fileName.rindex('.')
342
+ leftFileName = fileName[0, i]
343
+ rightFileName = fileName[i + 1, fileName.length - i]
344
+ newFileName = leftFileName + '_backup' + '.' + rightFileName
345
+ return newFileName
346
+ end
347
+ end
348
+ end
349
+
350
+
@@ -0,0 +1,9 @@
1
+ # coding: utf-8
2
+
3
+ module UnionPei
4
+ class Version
5
+ def self.to_string
6
+ "0.0.1"
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unionpei
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Shuang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: iniparse
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jruby-openssl
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: An unofficial unionpay gem
42
+ email: memorycancel@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/unionpei.rb
48
+ - lib/unionpei/acp_sdk.ini
49
+ - lib/unionpei/acp_service.rb
50
+ - lib/unionpei/cert_util.rb
51
+ - lib/unionpei/log_util.rb
52
+ - lib/unionpei/payment.rb
53
+ - lib/unionpei/sdk_config.rb
54
+ - lib/unionpei/sdk_util.rb
55
+ - lib/unionpei/version.rb
56
+ homepage: https://rubygems.org/gems/unionpei
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.1.4
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: An unofficial unionpay gem
79
+ test_files: []