diff --git a/README.md b/README.md index 5a0b4c1..b7b42df 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,30 @@ Pingpp.api_key = "YOUR-KEY" Pingpp.private_key_path = '/path/to/your_rsa_private_key.pem' ``` -### 设置 Ping++ 公钥 -公钥请登录 [Ping++ Dashboard](https://dashboard.pingxx.com) 获取 +### 验证 Webhooks + +#### 设置 Ping++ 公钥 + +公钥请登录 [Ping++ Dashboard](https://dashboard.pingxx.com) 获取。 + +此公钥用于webhooks回调时,验证请求对象的正确性。 + 设置你的 Ping++ 公钥路径 + ``` ruby Pingpp.pub_key_path = "/path/to/pingpp_rsa_public_key.pem" ``` +#### 验证 + +支持基于Rack的web框架,如Rails,Sinatra等. +```ruby +Pingpp::Webhook.verify?(request) # 验证回调的请求对象的正确性 +# 解析回调内容 +JSON.parse(request.raw_post) # Ruby on Rails +JSON.parse(request.body.read) # Sinatra +``` + ### 支付 ``` ruby Pingpp::Charge.create( @@ -54,12 +71,6 @@ Pingpp::Charge.create( ) ``` -### 验证 Webhooks -```ruby -Pingpp::Webhook.verify?(request) # 验证回调 -JSON.parse(request.raw_post) # 解析回调内容(Ruby on Rails) -``` - ### 查询 ``` ruby Pingpp::Charge.retrieve("CHARGE_ID") diff --git a/lib/pingpp/webhook.rb b/lib/pingpp/webhook.rb index d159fda..4c7b4c2 100644 --- a/lib/pingpp/webhook.rb +++ b/lib/pingpp/webhook.rb @@ -1,34 +1,58 @@ module Pingpp module Webhook def self.verify?(request, pub_key=Pingpp.pub_key) - if !pub_key + unless pub_key + puts 'Warn: ' + 'No Public key provided. ' + + 'Set your Public key using "Pingpp.pub_key_path = ' + + 'or Pingpp.pub_key = " ' + + 'You can get Public key from the Pingpp website: ' + + 'https://dashboard.pingxx.com/settings/development_info ' return false end - raw_data = nil - if request.respond_to?('raw_post') - raw_data = request.raw_post - elsif request.respond_to?('body') - raw_data = request.body - else - return false - end + # raw_data = nil + # if request.respond_to?('raw_post') + # raw_data = request.raw_post + # elsif request.respond_to?('body') + # raw_data = request.body + # else + # return false + # end - headers = nil - if request.respond_to?('headers') - headers = request.headers - elsif request.respond_to?('header') - headers = request.header - else - return false - end + raw_data = extract_raw_data(request) + + # headers = nil + # if request.respond_to?('headers') + # headers = request.headers + # elsif request.respond_to?('header') + # headers = request.header + # else + # return false + # end + + headers = extract_headers(request) - formated_headers = Util.format_headers(headers) - return false if !formated_headers.has_key?(:x_pingplusplus_signature) + formatted_headers = Util.format_headers(headers) + return false unless formatted_headers.has_key?(:x_pingplusplus_signature) + + signature = formatted_headers[:x_pingplusplus_signature] - signature = formated_headers[:x_pingplusplus_signature] rsa_public_key = OpenSSL::PKey.read(pub_key) - return rsa_public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), raw_data) + rsa_public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), raw_data) + end + + # A dumbed down version extracted from ActionDispatch::Request#raw_post + # see actionpack-4.2.7.1/lib/action_dispatch/http/request.rb#251 + def self.extract_raw_data(request) + body = request.env['rack.input'] + raw_post_body = body.read(request.content_length.to_i) + body.rewind if body.respond_to?(:rewind) + raw_post_body + end + + # see actionpack-4.2.7.1/lib/action_dispatch/http/headers.rb#each + def self.extract_headers(request) + request.env end end end