开发前提
首先需要在支付宝开放平台创建一个应用并申请APP支付权限。
然后补全开发信息,支付宝APP支付的回调地址需要在开放平台配置。
重点:
接口加签方式 - 我选择的是普通公钥方式;
下载支付宝开放平台开发助手工具,我选择的是RSA2加密方式,一键生成 应用私钥和应用公钥,好好保存下来;
然后将生成的应用公钥填入到接口加签方式的弹出的框内,以获取支付宝公钥。
一切准备就绪,开始接入支付宝APP支付:
进入正题
在实现支付宝APP支付时,后端只需要做两步操作:
1.将发起支付用到的参数返回给APP原生,且数据库保存预支付订单信息;
2.支付回调,支付宝APP支付完成后请求一个回调地址,这个回调地址是在 第1步获取支付参数时 填写的,验证参数后,更改数据库保存的订单支付状态;
首先是第一步:获取APP调起支付宝支付用的参数
#获取APP调起支付用的参数
def get_alipay_datas
#商户订单号
out_trade_no = "#{Time.now.strftime('%Y%m%d%H%M%S')}#{rand(1...999999)}"
biz_content = {
out_trade_no: out_trade_no, # 商户订单号
product_code: 'QUICK_MSECURITY_PAY', #固定
total_amount: 0.01, # 实际支付金额,单位为元,精确到小数点后两位
subject: "商品1" #订单标题
}
datas = {
app_id: ENV['ALIPAY_APPID'], # 支付宝分配给开发者的应用ID
method: 'alipay.trade.app.pay', # 接口名称,固定
format: 'json',
charset: 'utf-8',
sign_type: 'RSA2', # 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA
timestamp: Time.now.strftime("%Y-%m-%d %H:%M:%S"), # 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
version: '1.0', #调用的接口版本,固定为:1.0
biz_content: JSON.generate(biz_content),
notify_url: ENV['ALIPAY_NOTIFY_URL'] #回调地址
}
datas.store("sign", get_alipay_sign_str(datas)) #通过以上参数生成sign
datas.delete(:app_id) #删除敏感的app_id,让前端手动拼接
datas = sort_param(datas).handle{ |key,value|
{
"#{CGI::escape(key.to_s)}".to_sym => "#{CGI::escape characet(value.to_s)}"
}
}
datas #这个结果便是APP调起支付需要的参数了
#此时便可以创建数据库预支付的订单信息了,完成内部程序逻辑
end
#获取支付宝支付sign
def get_alipay_sign_str params
sign_arr = []
sort_param(params).map{ |key,value|
sign_arr << "#{key}=#{characet(value)}" if value.present? #排除为空的值
}
sign_str = sign_arr.join('&') #参数以&符拼接成字符串
#读取生成的私钥文件
key = File.read(File.dirname(__FILE__) + '/alipay/rsa.pem') #读取应用私钥文件,即通过支付宝开放平台开发助手工具生成的应用私钥
pkey = OpenSSL::PKey::RSA.new(Base64.decode64(key))
signature = pkey.sign('sha256', sign_str)
Base64.strict_encode64(signature)
end
#hash排序
def sort_param(data)
data = data.sort{ |a,b| a.to_s <=> b.to_s }.to_h
end
然后就是第二步:支付回调
这个地址就是第一步的 获取APP调起支付宝支付用的参数传入的notify_url参数,也是访问如下代码方法的链接地址;
desc '支付宝支付回调'
post :alipay_notify_url do
ActiveRecord::Base.transaction do
$logger.info "支付宝支付回调"
params_data = params
if params_data[:trade_status].present? && (params_data[:trade_status] == 'TRADE_SUCCESS' || params_data[:trade_status] == 'TRADE_FINISHED') #支付宝回调参数正确
#支付完成
if Redis.is_pay_order_vaild? params_data[:out_trade_no] #判断redis此订单号是否存在,且状态是否为未回调 才可以进行回调操作
$logger.info "通过redis验证"
#验签过程
if check_alipay(params_data)
#验签成功
#更新此订单的redis数据
Redis.set_pay_order_invaild params_data[:out_trade_no]
$logger.info "验签成功-订单号#{params_data[:out_trade_no]}"
#判断APPID是否正确/订单号是否存在/订单未支付状态/是否为支付宝支付/判断金额是否正确
pay_order = Order.find_by_out_trade_no(params_data[:out_trade_no]) #预支付订单信息
if params_data[:app_id] == ENV['ALIPAY_APPID'] && pay_order.present? && !pay_order.is_paid? && pay_order.pay_type == 'alipay' && pay_order.spend_rmb_money.to_d == params_data[:total_amount].to_d
#回调信息通过
$logger.info "回调信息通过-订单号#{params_data[:out_trade_no]}"
#开始执行内部程序逻辑处理,如修改订单状态
xxxxxxxx
#逻辑处理已完成
$logger.info "逻辑处理已完成-订单号#{params_data[:out_trade_no]}"
else
$logger.info "回调信息未通过-订单号#{params_data[:out_trade_no]}"
end
else
$logger.info "验签失败-订单号#{params_data[:out_trade_no]}"
end
end
end
end
end
#支付宝支付回调验签
def check_alipay params
sign = params[:sign] #将传入的sign保存下来
params.delete(:sign) #删除sign参数,保存其余参数
params.delete(:sign_type) #删除sign_type参数,保存其余参数
sign_arr = []
#params = params.each{ |key, val| params[key] = CGI::unescape(val) }
sort_param(params).map{ |key,value|
sign_arr << "#{key}=#{CGI::unescape(value)}"
}
sign_str = sign_arr.join('&') #参数以&符拼接成字符串
signature = Base64.strict_decode64(sign)
key = File.read(File.dirname(__FILE__) + '/alipay/rsa.key') #注意!!!此时用到的公钥文件,不是通过支付宝开放平台开发助手工具生成的应用公钥,而是在开放平台-接口加签方式-通过应用公钥获取的支付宝公钥
pkey = OpenSSL::PKey::RSA.new(Base64.decode64(key))
pkey.verify('sha256', signature, sign_str) #完成加密
end
以上便是 ruby实现支付宝APP支付全流程啦!