当前位置: 首页 > 工具软件 > QRCode Rails > 使用案例 >

ruby on rails 微信支付

袁建木
2023-12-01

1。设置参数

param = {
        appid: @appid, # 微信支付分配的公众账号ID
        mch_id: @mch_id, #微信支付分配的商户号
        nonce_str: Tools::GetRandString.rand_string(30), #随机字符串
        sign_type: 'HMAC-SHA256',
        body: order[:title], # 商品简单描述
        out_trade_no: order[:order_sn], # 商户系统内部订单号,要求32个字符内
        total_fee: (order[:money].to_f * 100).to_i, # 订单总金额,单位为分
        spbill_create_ip: '120.78.161.126', # 支持IPV4和IPV6两种格式的IP地址
        notify_url: @notify_url, # 异步接收微信支付结果通知的回调地址
        trade_type: 'NATIVE' # JSAPI, NATIVE, MWEB  
      }

 

2.排序并拼接参数

'''
    参数从小到大排序
    '''
    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

3.把key加入到参数

formatstr << "&key=#{@appkey}"

4.签名

'''
    生成签名
    '''
    def sign_string(data)
      #Digest::MD5.hexdigest(data).upcase
      OpenSSL::HMAC.hexdigest("SHA256", @appkey, data).upcase
    end

5.把签名加入参数

param[:sign] = sign_param(param)

6.把参数转为xml

'''
    hash转xml
    '''
    def self.to_wx_xml(hash)
      xml = "<xml>"
      hash.each_with_index do |value, key|
        xml += "<#{value[0].to_s}><![CDATA[#{value[1].to_s}]]></#{value[0].to_s}>"
      end
      xml += "</xml>"
    end

7.获取二维码地址

result = Faraday.post(@send_url, xml_param)
      result = Hash.from_xml(result.body)['xml']
      if result["code_url"].blank? 
        ''
      else 
        result["code_url"]
      end 

8.把二维码地址转为二维码

require 'rqrcode_png'
module Tools 
  class Qrcode 
    def self.url_to_base64(url, options = {})
      if options.blank?
        options = {
          w: 200,
          h: 200
        }
      end
      qrcode = RQRCode::QRCode.new(url, :size => 8, :level => :h )
      str = Base64.encode64(qrcode.to_img.resize(options[:w], options[:h]).to_s)
      str = "data:image/png;base64,#{str}"
    end

  end
end

9.使用

code_url = Payment::Wxpay.new.pay_code(order)
        if code_url.blank?
          flash[:status] = '交易失败'
          return redirect_to customer_pay_records_path
        end
        @src = Tools::Qrcode.url_to_base64(code_url)

10.回调

sign排除

param.delete('sign')

签名并验证

signature = sign_param(param)
      #验证签名
      if sign == signature
        res = {
          out_trade_no: param['out_trade_no'],
          pay_order_sn: param['transaction_id'],
          pay_money: (param['total_fee'].to_i / 1000).to_f
        }
      else   
        res = ''
      end

回调使用

result = Hash.from_xml(request.body)['xml']
      if result['result_code'].present? && result['result_code'] == 'SUCCESS'
        pay_res = Payment::Wxpay.new.check_pay(result)
        if pay_res.present? 
          change_with_result(pay_res)
          xml = "<xml>"
          xml += " <return_code><![CDATA[SUCCESS]]></return_code>"
          xml += "<return_msg><![CDATA[OK]]></return_msg>"
          xml += "</xml>"
          return render xml: xml
        end
      end

完整代码

require 'digest/md5'
module Payment
  class Wxpay
    
    def initialize 
      @send_url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'.freeze
      @notify_url = Rails.configuration.application['PAY_RESPOND']
      @appid = '绑定的公众号appid'
      @appkey = 'api 密钥'
      @mch_id = 'mchd'

    end 
    '''
    支付
    '''
    def pay_code(order) 
      # return_code 返回状态码
      # return_msg 返回信息	
      # appid 公众账号ID
      # mch_id 商户号
      # nonce_str 随机字符串 
      # sign 签名
      # result_code 业务结果 SUCCESS/FAIL
      # code_url 二维码链接 trade_type=NATIVE时有返回,此url用于生成支付二维码
      param = pay_params(order)
      xml_param = Tools::Sign.to_wx_xml(param)
      result = Faraday.post(@send_url, xml_param)
      result = Hash.from_xml(result.body)['xml']
      if result["code_url"].blank? 
        ''
      else 
        result["code_url"]
      end 
    end 
    '''
    支付验签
    '''
    def check_pay(param)
      return '' if (param['appid'] != @appid) || (param['mch_id'] != @mch_id)
      sign = param['sign']
      param.delete('sign')
      signature = sign_param(param)
      #验证签名
      if sign == signature
        res = {
          out_trade_no: param['out_trade_no'],
          pay_order_sn: param['transaction_id'],
          pay_money: (param['total_fee'].to_i / 1000).to_f
        }
      else   
        res = ''
      end
    end
    '''
    签名
    '''
    def sign_param(param)
      param = Tools::Sign.sort_param(param)
      formatstr = Tools::Sign.to_query(param)
      formatstr << "&key=#{@appkey}"
      signature = sign_string(formatstr)
    end

    '''
    参数
    '''
    def pay_params(order)
      param = {
        appid: @appid, # 微信支付分配的公众账号ID
        mch_id: @mch_id, #微信支付分配的商户号
        nonce_str: Tools::GetRandString.rand_string(30), #随机字符串
        sign_type: 'HMAC-SHA256',
        body: order[:title], # 商品简单描述
        out_trade_no: order[:order_sn], # 商户系统内部订单号,要求32个字符内
        total_fee: (order[:money].to_f * 100).to_i, # 订单总金额,单位为分
        spbill_create_ip: '120.78.161.126', # 支持IPV4和IPV6两种格式的IP地址
        notify_url: @notify_url, # 异步接收微信支付结果通知的回调地址
        trade_type: 'NATIVE' # JSAPI, NATIVE, MWEB  
      }
      #sign 通过签名算法计算得出的签名值
      param[:sign] = sign_param(param)
      param
    end
    '''
    生成签名
    '''
    def sign_string(data)
      #Digest::MD5.hexdigest(data).upcase
      OpenSSL::HMAC.hexdigest("SHA256", @appkey, data).upcase
    end

  end
end

这里也遇到过签名不正确,奇怪的是用微信的签名验证工具就可以匹配成功,但就算签名不通过,好在这个问题比较简单,网上也有解决方法,就是重置下appi密钥

code_url = Payment::Wxpay.new.pay_code(order)
        if code_url.blank?
          flash[:status] = '交易失败'
          return redirect_to customer_pay_records_path
        end
        @src = Tools::Qrcode.url_to_base64(code_url)

回调

result = Hash.from_xml(request.body)['xml']
      if result['result_code'].present? && result['result_code'] == 'SUCCESS'
        pay_res = Payment::Wxpay.new.check_pay(result)
        if pay_res.present? 
          change_with_result(pay_res)
          xml = "<xml>"
          xml += " <return_code><![CDATA[SUCCESS]]></return_code>"
          xml += "<return_msg><![CDATA[OK]]></return_msg>"
          xml += "</xml>"
          return render xml: xml
        end
      end

 

 类似资料: