# 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