ruby on rails 支付宝支付

1. 先在支付宝下载密钥生成器

  https://docs.open.alipay.com/291/106097

2. 使用密钥生成器生成密钥,我们ruby自然用非java专用生成,使用RSA2,即2048

把密钥保存下,然后把公钥添加到支付宝开发配置里

3. 修改密钥名称,如rsa.pem(私钥), rsa.key(公钥),拷贝到项目下

4.参数设置

biz_content = {
        out_trade_no: order[:order_sn], # out_trade_no 商户订单号
        product_code: 'FAST_INSTANT_TRADE_PAY',   # 销售产品码 目前仅支持FAST_INSTANT_TRADE_PAY
        total_amount: order[:money], # 订单总金额,单位为元,精确到小数点后两位
        subject: order[:title] #订单标题
      }
      param = {
        app_id: @appid, # 支付宝分配给开发者的应用ID
        method: 'alipay.trade.page.pay', # 接口名称alipay.trade.page.pay
        format: 'JSON',
        return_url: @notify_url,
        charset: 'utf-8', 
        sign_type: 'RSA2', # 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA
        timestamp: Time.zone.now.strftime("%Y-%m-%d %H:%M:%S"), # 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
        version: '1.0', #调用的接口版本,固定为:1.0
        notify_url: @notify_url,
        biz_content: JSON.generate(biz_content)
      }

5.参数排序并拼接

'''
    参数从小到大排序
    '''
    def self.sort_param(data)
      data = data.sort{ |a,b| a.to_s <=> b.to_s }.to_h
    end

    '''
    hash转url参数格式化
    '''
    def self.to_query(param)
      format = []
      param.each_with_index do |value|
        if value[1].present? 
          format << "#{value[0]}=#{value[1]}"
        end
      end
      format.join('&')
    end

6.加密

def sign_string(data)
      key = File.read(File.dirname(__FILE__) + '/rsa.pem')
      pkey = OpenSSL::PKey::RSA.new(Base64.decode64(key))
      #signature = pkey.sign(OpenSSL::Digest::SHA256.new, data)
      signature = pkey.sign('sha256', data)
      #signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
      signature = Base64.strict_encode64(signature)
    end

7.把sign添加到参数列表

param[:sign] = sign

8.发送请求

 <% if @alipay.present? %>
    <div class="pay-type">支付宝支付</div>
    <form name="payForm" action="<%= @alipay[:url]%>" method="post">
    <% @alipay[:param].each do |key, value| %>
    <input type="hidden" name="<%= key %>" value="<%= value %>">
    <% end %>
    <input type="submit" value="立即支付">
    </form>
    <% end %>

这里一定要记住, 我们的标题一般都是中文,会遇到签名不通过问题,一定要加编码

https://openapi.alipay.com/gateway.do?charset=utf-8

charset=utf-8

charset=utf-8

charset=utf-8

在这里卡了很久,csdn还是比较可靠的,很多大侠。

9.回调验证

回调验证顺序也差不多

排除sign和sign_type

param.delete('sign')
      param.delete('sign_type')

url_dncode

param.each{ |key, val| param[key] = URI::decode(val) }

排序并拼接

验证签名

def check_sign_string(data, signature)
      signature = Base64.strict_decode64(signature)
      key = File.read(File.dirname(__FILE__) + '/rsa.key')
      pkey = OpenSSL::PKey::RSA.new(Base64.decode64(key))
      pkey.verify('sha256', signature, data)
    end

完整代码

module Payment
  class Alipay
    def initialize 
      @send_url = 'https://openapi.alipay.com/gateway.do?charset=utf-8'.freeze
      @notify_url = Rails.configuration.application['PAY_RESPOND']
      @appid = 'youappid'
    end

    '''
    支付
    '''
    def pay_code(order)
      # code 网关返回码
      # msg 网关返回码描述
      # sign 签名
      # trade_no 支付宝交易号
      # out_trade_no 商户订单号
      # seller_id 收款支付宝账号对应的支付宝唯一用户号
      # total_amount 交易金额
      param = pay_params(order)
      res = {
        url: @send_url,
        param: param
      }
      res
    end
    '''
    支付确认
    '''
    def check_pay(param)
      return '' if param['app_id'] != @appid
      sign = param['sign']
      param.delete('sign')
      param.delete('sign_type')
      param.each{ |key, val| param[key] = URI::decode(val) }
      # 排序
      param = Tools::Sign.sort_param(param)
      # 转为url
      formatstr = Tools::Sign.to_query(param)
      check_sign = check_sign_string(formatstr, sign)
      if check_sign
        res = {
          out_trade_no: param['out_trade_no'],
          pay_order_sn: param['trade_no'],
          pay_money: param['total_amount'].to_f
        }
      else   
        res = ''
      end
    end
    '''
    签名
    '''
    def sign_param(param)
      # 排序
      param = Tools::Sign.sort_param(param)
       # 转为url
      formatstr = Tools::Sign.to_query(param)
      # 签名
      signature = sign_string(formatstr).to_s.chomp
    end

    '''
    公共参数
    '''
    def pay_params(order) 
      # biz_content 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
      biz_content = {
        out_trade_no: order[:order_sn], # out_trade_no 商户订单号
        product_code: 'FAST_INSTANT_TRADE_PAY',   # 销售产品码 目前仅支持FAST_INSTANT_TRADE_PAY
        total_amount: order[:money], # 订单总金额,单位为元,精确到小数点后两位
        subject: order[:title] #订单标题
      }
      param = {
        app_id: @appid, # 支付宝分配给开发者的应用ID
        method: 'alipay.trade.page.pay', # 接口名称alipay.trade.page.pay
        format: 'JSON',
        return_url: @notify_url,
        charset: 'utf-8', 
        sign_type: 'RSA2', # 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA
        timestamp: Time.zone.now.strftime("%Y-%m-%d %H:%M:%S"), # 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
        version: '1.0', #调用的接口版本,固定为:1.0
        notify_url: @notify_url,
        biz_content: JSON.generate(biz_content)
      }
      #final_url = URI.encode(sign_param(param))
      sign = sign_param(param)
      param[:sign] = sign
      param
    end

    '''
    生成签名
    '''
    def sign_string(data)
      key = File.read(File.dirname(__FILE__) + '/rsa.pem')
      pkey = OpenSSL::PKey::RSA.new(Base64.decode64(key))
      #signature = pkey.sign(OpenSSL::Digest::SHA256.new, data)
      signature = pkey.sign('sha256', data)
      #signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
      signature = Base64.strict_encode64(signature)
    end
    '''
    验证签名
    '''
    def check_sign_string(data, signature)
      signature = Base64.strict_decode64(signature)
      key = File.read(File.dirname(__FILE__) + '/rsa.key')
      pkey = OpenSSL::PKey::RSA.new(Base64.decode64(key))
      pkey.verify('sha256', signature, data)
    end
    

  end
end
@alipay = Payment::Alipay.new.pay_code(order)

 

 <% if @alipay.present? %>
    <div class="pay-type">支付宝支付</div>
    <form name="payForm" action="<%= @alipay[:url]%>" method="post">
    <% @alipay[:param].each do |key, value| %>
    <input type="hidden" name="<%= key %>" value="<%= value %>">
    <% end %>
    <input type="submit" value="立即支付">
    </form>
    <% end %>

回调

param = param.to_unsafe_h
      if param['trade_status'].present? &&  param['trade_status'] == 'TRADE_SUCCESS'
        pay_res = Payment::Alipay.new.check_pay(param)
        if pay_res.present? 
          change_with_result(pay_res)
          return render plain: 'success'
        end
      end
return render plain: 'fail'

这里还有个问题要记住, rails5.1后,prams就不再是hash了,是一个对象

所以会报错

解决是

param = param.to_unsafe_h

我也记录了下这个问题

https://blog.csdn.net/tang05709/article/details/88744410

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值