Ruby webhooks 验证公钥失败

kevin_he · 2016年03月23日 · 最后由 twairball 回复于 2016年07月12日 · 2923 次阅读

下面是我用的验证公钥的方法,但是在服务器上面运行的时候出现错误

def notify
     status = 400
     #判断请求是否有ping++的签名信息
     if request.headers['x-pingplusplus-signature'].blank?
        status = 401
        logger.debug '【报哪家】:======付款回调请求来源错误!!!!!'
        return
     end 
     #获取签名信息
     raw_data = request.body.read
     if request.headers['x-pingplusplus-signature'].is_a?(Array)
        signature = request.headers['x-pingplusplus-signature'][0].to_s
     else
        signature = request.headers['x-pingplusplus-signature'].to_s
     end
     # 获取「Webhooks 验证公钥」
     pub_key_path ="#{Rails.root}/config/rsa_public_key.pem"
     if verify_signature(raw_data, signature, pub_key_path)
            #处理接收的结果
          event = JSON.parse(raw_data) 
          #付款成功
          if event["type"] == 'charge.succeeded'
           # 开发者在此处加入对支付异步通知的处理代码
             order_no = event['data']['object']['order_no']
             order = Order.where(order_no: order_no).first
             order_from = order.status 
             if order.present?
                 #更新字段
                 order.paid = event['data']['object']['paid']    
                 if order.save
                      status = 200
                 else
                     status = 500
                 end
             else
                    logger.debug '数据库没有该条记录!'
             end
           #退款成功
         elsif event['type'] == 'refund.succeeded'
               # 开发者在此处加入对退款异步通知的处理代码
             order_no = event['data']['object']['order_no']
             order = Order.where(order_no: order_no).first
             if order.present?
                 #更新字段
                 order.time_refunded = Time.at(event['data']['object']['time_succeed'])
                 if order.save
                     status = 200
                 else
                     status = 500
                 end
             else
                   logger.debug '数据库没有该条记录!'
             end
         else
             logger.debug '付款回调返回未知操作!'
         end
       else
          logger.debug '付款回调请求来源错误!'
          status = 403
       end
       render :nothing => true, :status => status
 end

提示错误如下:

NoMethodError (undefined method `verify_signature' for #<PaymentsController:0x007fcf547c6f70>):
  app/controllers/payments_controller.rb:32:in `notify'
  lib/middleware/security.rb:11:in `call'

求大神帮忙解答

verify_signature 没定义?

#1 楼 @embbnux verify_signature 这个方式是用来验证 webhooks 中的签名是否正确的,我使用的是 RubyMine 编辑器,可以找到这个方法,但是不能调用。

以下是我包装的一个 concern (private key 故意没有填入)

# app/controllers/concerns/ping_plus_plus_concern.rb

require "base64"
require "openssl"

module PingPlusPlusConcern
  extend ActiveSupport::Concern

  PUBLIC_KEY_PATH = File.join(Rails.root, 'config', 'pingpp_rsa_public_key.pem')

  included do 
    before_action :set_public_key
    before_action :set_private_key
  end

  def verify_signature(signature, token)
    verify_hash = OpenSSL::Digest::SHA256.new
    pub = OpenSSL::PKey::RSA.new(@public_key)
    pub.verify(verify_hash, Base64.urlsafe_decode64(signature), token)
  end 

  def generate_signature(token)
    sign_hash = OpenSSL::Digest::SHA256.new
    priv = OpenSSL::PKey::RSA.new(@private_key)
    signature = Base64.urlsafe_encode64(priv.sign(sign_hash, token))
    signature.gsub!(/\n/, '')
  end

  def set_public_key
    @public_key = File.read(PUBLIC_KEY_PATH)
  end

  def set_private_key
    @private_key = "" # TODO
  end

  ##
  ## check request header and verify from pingpp
  ## get header at "x-pingplusplus-signature" and verify against json payload
  ##
  def verify_signature?
    # x-pingplusplus-signature  RSA-SHA256 private key?
    sig = request.headers["x-pingplusplus-signature"].presence
    logger.info "[Charges verify_signature] signature=#{sig}"

    # json payload from request body
    token = request.body.read
    logger.info "[Charges verify_signature] token=#{token}"

    # verify
    verify = verify_signature(sig, token)
    logger.info "[Charges verify_signature] verify=#{verify}"
    return verify
  end
end

然后 controller 里头可以这样使用:

# app/controllers/charges_controller.rb

class ChargesController < ApplicationController
  include PingPlusPlusConcern

  # skip CSRF, for callback
  skip_before_action :verify_authenticity_token, only: [:callback]

  # json response
  respond_to :json

  ...

  # POST /charges/callback
  # for pingplusplus webhook
  def callback
    unless Rails.env.test?
        head :no_content, status: :unauthorized and return unless verify_signature?
    end

    if webhook_params[:type] == "charge.succeeded"
        # handle webhook
    end
  end

end
需要 登录 后方可回复, 如果你还没有账号请 注册新账号