# app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authenticate_webhook
def payment_status
event = JSON.parse(request.body.read)
process_event(event)
head :ok
end
private
def authenticate_webhook
provided_signature = request.headers['Webhook-Signature']
payload = request.raw_post
secret = ENV['WEBHOOK_SECRET']
expected_signature = OpenSSL::HMAC.hexdigest("SHA256", secret, payload)
head :unauthorized unless ActiveSupport::SecurityUtils.secure_compare(provided_signature, expected_signature)
end
def process_event(event)
case event["type"]
when "payment.success"
handle_success(event)
when "payment.failed"
handle_failure(event)
end
end
def handle_success(event)
user = User.find_by(id: event["data"]["user_id"])
if user
user.subscription.update(status: 'active')
# Record payment details as needed
end
end
def handle_failure(event)
user = User.find_by(id: event["data"]["user_id"])
if user
user.subscription.update(status: 'failed')
# Additional failure handling logic
end
end
end
# config/routes.rb post 'webhooks/payment_status', to: 'webhooks#payment_status'
WEBHOOK_SECRET="your_webhook_secret"
# config/environments/production.rb config.force_ssl = true
# app/services/payment_gateway_service.rb def validate_payment(response) # Ensure response contains expected fields return false unless response["status"] == "success" && response["amount"] == @amount true end
# app/models/payment.rb
class Payment < ApplicationRecord
belongs_to :user
validates :status, presence: true
validates :amount, numericality: { greater_than: 0 }
end
# app/controllers/subscriptions_controller.rb
class SubscriptionsController < ApplicationController
def create
plan = SubscriptionPlan.find(params[:plan_id])
service = PaymentGatewayService.new(current_user, plan.price)
result = service.create_payment
if result["status"] == "pending"
# Only mark subscription active when webhook confirms payment
Subscription.create!(user: current_user, subscription_plan: plan, status: 'pending')
redirect_to dashboard_path, notice: "Payment initiated. Awaiting confirmation."
else
redirect_to new_subscription_path, alert: "Payment failed: #{result['error_message']}"
end
end
end
# config/initializers/rack_attack.rb
Rack::Attack.throttle("webhooks/ip", limit: 5, period: 60) do |req|
req.ip if req.path == "/webhooks/payment_status"
end
# app/models/payment.rb class Payment < ApplicationRecord encrypts :card_number, :cvv, :expiry_date, deterministic: true end
ngrok http 3000
Sign up for free and be the first to get notified about new posts.
No comment