unionpei 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 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: []