Skip to content

Commit

Permalink
Add webhooks verify to extension paypal sdk
Browse files Browse the repository at this point in the history
Every call to the paypal webhooks endpoint must be validated to proceed
  • Loading branch information
Jecko-o committed Apr 28, 2021
1 parent 3e63f5d commit 78d7f73
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 4 deletions.
14 changes: 10 additions & 4 deletions app/controllers/spree/paypal_webhooks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ class PaypalWebhooksController < Spree::BaseController
skip_before_action :verify_authenticity_token

def create
action = SolidusPaypalMarketplace::Webhooks::Sorter.call(params.permit!)
if action[:result] == true
render json: action, status: :ok
params.permit!
verification = SolidusPaypalMarketplace::PaypalPartnerSdk.webhook_verify(request.headers, params)
if verification
action = SolidusPaypalMarketplace::Webhooks::Sorter.call(params)
if action[:result] == true
render json: action, status: :ok
else
render json: action, status: :bad_request
end
else
render json: action, status: :bad_request
render json: { verification: verification }, status: :unauthorized
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions lib/solidus_paypal_marketplace/paypal_partner_sdk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'solidus_paypal_marketplace/paypal_partner_sdk/create_partner_referral'
require 'solidus_paypal_marketplace/paypal_partner_sdk/show_seller_status'
require 'solidus_paypal_marketplace/paypal_partner_sdk/webhook_verify'

module SolidusPaypalMarketplace
module PaypalPartnerSdk
Expand All @@ -23,6 +24,18 @@ def show_seller_status(merchant_id:)
response.result
end

def webhook_verify(headers, params)
request = ::SolidusPaypalMarketplace::PaypalPartnerSdk::WebhookVerify.new(
headers: headers,
params: params
)
response = SolidusPaypalMarketplace::PaypalPartnerSdk.execute(request)
return false unless response&.status_code == '200'

data = JSON.parse(response.body)
data['verification_status'] == "SUCCESS"
end

def execute(request)
SolidusPaypalMarketplace.client.execute(request)
rescue PayPalHttp::HttpError => e
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
frozen_string_literal: true

module SolidusPaypalMarketplace
module PaypalPartnerSdk
class WebhookVerify
PATH = '/v1/notifications/verify-webhook-signature'
VERB = 'POST'
HEADERS = { "Content-Type": "application/json" }.freeze

attr_accessor :path, :verb, :headers, :body

def initialize(headers:, params:)
@path = PATH.dup
@verb = VERB.dup
@headers = { "Content-Type" => "application/json" }
@body = {
"auth_algo" => headers["PAYPAL-AUTH-ALGO"],
"cert_url" => headers["PAYPAL-CERT-URL"],
"transmission_id" => headers["PAYPAL-TRANSMISSION-ID"],
"transmission_sig" => headers["PAYPAL-TRANSMISSION-SIG"],
"transmission_time" => headers["PAYPAL-TRANSMISSION-TIME"],
"webhook_id" => SolidusPaypalMarketplace.config.paypal_webhook_id,
"webhook_event" => params
}
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe SolidusPaypalMarketplace::PaypalPartnerSdk::WebhookVerify do
subject(:verify) { described_class.new(headers: headers, params: params) }

let(:headers) {
{
"PAYPAL-AUTH-ALGO" => "string",
"PAYPAL-CERT-URL" => "string",
"PAYPAL-TRANSMISSION-ID" => "string",
"PAYPAL-TRANSMISSION-SIG" => "string",
"PAYPAL-TRANSMISSION-TIME" => "string"
}
}
let(:params) {
{ something: "string" }
}

before do
SolidusPaypalMarketplace.config.paypal_webhook_id = "string"
end

it { is_expected.to respond_to(:path) }
it { is_expected.to respond_to(:verb) }
it { is_expected.to respond_to(:headers) }
it { is_expected.to respond_to(:body) }

describe "#path" do
it do
expect(verify.path).to eq '/v1/notifications/verify-webhook-signature'
end
end

describe "#verb" do
it do
expect(verify.verb).to eq 'POST'
end
end

describe "#headers" do
it do
expect(verify.headers['Content-Type']).to eq 'application/json'
end
end

describe "#body" do
[
"auth_algo",
"cert_url",
"transmission_id",
"transmission_sig",
"transmission_time",
"webhook_id",
"webhook_event"
].each do |key|
it "includes #{key}" do
expect(verify.body[key]).to be_present
end
end
end
end

0 comments on commit 78d7f73

Please sign in to comment.