在现在的公司 做了一个银联的手机控件支付项目,因为银联的 api 不是特别全面,而且不提供 ruby 的代码只能完全按照 php 的代码来进行签名,验签一类的,在转换成 ruby 的代码看看结果能否对上。而发现之前的一些 git 上的相关 gem 包,都不是能用了,而相关信息也很少,所以现在这记录下,然后 gem 地址:https://github.com/Yohansun/unionpay_app 1:介绍下银联的具体流程吧 (1)手机端需要后台返回的 tn 号,利用这个 tn 号和银联进行交易 (2)那么在后台如何和银联交互获得这个 tn 号呢,方法在下面有,首先就是根据 union_params 进行签名#unionpay_sign,然后 post 请求给银联 会返回你类似。 “accessType=0&bizType=000201&certId=21267647932558653966460913033289351200&encoding=utf-8&merId=898111457340146&orderId=20150415122801272&respCode=00&respMsg=\xE6\x88\x90\xE5\x8A\x9F[0000000]&signMethod=01&tn=201504151339264521528&txnSubType=01&txnTime=20150415133926&txnType=01&version=5.0.0&signature=BuCXnTey9ptln9N2rRd5W3ArFkghZB1ayAimmYBqVUktN7KjAROhZw2SFwkdex5NcG71dGEliXi6boUyf13TkcHRIbIfDm+dm5LhUWNpX7DlJgeF+lJxyuFpP2DyogiWtecifSPYbao0tv+t15fyVOp9ioJ5rB5pM+7tX4VT3kIN8mzJl93M46vCXPEaFHLFqTM45B8KRpoZWyBtamckixBftGg+Jg759a+gK5oqRVoen1/AyXNjrUUipKZ+XOLjPKPDrNjtkEOJ1R4VS5JQdyw2WQmn6hoff6aCt/nX1VhLZPzvfTEi8ia9ap6mObDECZ7zkJ2n2N9l24xqi3PrGg==” 这里面就有 tn 值。你把它返回给前端。当然 tn 值过来后 你要进行验签 #unionpay_verify params 这个方法。
#银联的签名 sign
def unionpay_sign txtAmt
union_params = {
:version => "5.0.0",
:encoding => "utf-8",
:certId => Settings.union.certId, #证书号
:txnType => '01',
:txnSubType => "01",
:bizType => "000201",
:channelType => "08",
:frontUrl => Settings.union.frontUrl,
:backUrl => Settings.union.backUrl,
:accessType => "0",
:merId => Settings.union.merId, #商户号
:orderId => Time.now.strftime("%Y%m%d%H%M%S"), #商户订单号
:txnTime => Time.now.strftime("%Y%m%d%H%M%S"), #订单发送时间
:txnAmt => txtAmt, #以分为单位
:currencyCode => '156',
:signMethod => '01',
}
data = Digest::SHA1.hexdigest(union_params.sort.map{|key, value| "#{key}=#{value}" }.join('&'))
sign = Base64.encode64(OpenSSL::PKey::RSA.new(Settings.union.private_key).sign('sha1', data.force_encoding("utf-8"))).gsub("\n", "")
union_params = {trade_number: union_params[:orderId], sign: union_params.merge(signature: sign)} #订单号 和 要发送给 银联的参数信息
end
# 调用http的post方法 即可成功发送信息 并通过银联的验签
request = Typhoeus::Request.new(Settings.union.uri, method: :post, params: unionpay_sign(params[:cent].to_i)[:sign], ssl_verifypeer: false, headers: {'Content-Type' =>'application/x-www-form-urlencoded'} )
#银联支付验签
def unionpay_verify params
if unionpay_get_public_key_by_cert_id params['certId']
public_key = unionpay_get_public_key_by_cert_id params['certId']
signature_str = params['signature']
p = params.reject{|k, v| k == "signature"}.sort.map{|key, value| "#{key}=#{value}" }.join('&')
signature = Base64.decode64(signature_str)
data = Digest::SHA1.hexdigest(p)
key = OpenSSL::PKey::RSA.new public_key
digest = OpenSSL::Digest::SHA256.new
key.verify digest, signature, data
else
false
end
end
# 银联支付 根据证书id返回公钥
def unionpay_get_public_key_by_cert_id cert_id
certificate = OpenSSL::X509::Certificate.new(Settings.union.cer) #读取cer文件
certificate.serial.to_s == cert_id ? certificate.public_key.to_s : nil #php 返回的直接是cer文件 Settings.union.cer
end
#查询接口,查询支付是否成功。
def unionpay_query order_id
union_params = {
:version => '5.0.0', #版本号
:encoding => 'utf-8', #编码方式
:certId => Settings.union.certId, #证书ID
:signMethod => '01', #签名方法
:txnType => '00', #交易类型
:txnSubType => '00', #交易子类
:bizType => '000000', #业务类型
:accessType => '0', #接入类型
:channelType => '07', #渠道类型
:orderId => order_id, #请修改被查询的交易的订单号
:merId => Settings.union.merId, #商户代码,请修改为自己的商户号
:txnTime => order_id, #请修改被查询的交易的订单发送时间
}
data = Digest::SHA1.hexdigest(union_params.sort.map{|key, value| "#{key}=#{value}" }.join('&'))
sign = Base64.encode64(OpenSSL::PKey::RSA.new(Settings.union.private_key).sign('sha1', data.force_encoding("utf-8"))).gsub("\n", "")
request = Typhoeus::Request.new(Settings.union.uri, method: :post, params: union_params.merge(signature: sign), ssl_verifypeer: false, headers: {'Content-Type' =>'application/x-www-form-urlencoded'} )
request.run
if request.response.success?
code = Hash[*request.response.body.split("&").map{|a| a.gsub("==", "@@").split("=")}.flatten]['origRespCode']
CentOrder.find_by(trade_number: order_id).update_attributes!(state: 1) if code == "00"
end
end