Rails webhook integration development with Ngrok
Tags: rails, ruby, webdev, ngrok
Published: 2019-01-11 21:20:01

All begin when I started using [Mollie](https://www.mollie.com), it's a PSP (Payment Service Provider) like [Stripe](https://stripe.com) and it has a ruby wrapper, but if you want to test payments in a development environment (localhost) you need a valid URL so they can reach you via webhook (if you want to validate the payment, the webhook URL parameter is mandatory).

The solution was easy I just create an [ngrok](https://ngrok.com/) tunnel and I change the `default_url_options` with an environment variable depending if I was working on something that needed only my local machine or other services.

For running my app with mollie webhooks i needed to do this:

- Start ngrok
- Change `ENV` variables for the new ngrok `https` url
- Start my app
- Start Sidekiq (for background jobs)

IMO it's not to much but at some point becomes annoying so I decided to make it easier for me, now i only have:

- Start my app
- Start Sidekiq

Here it is how to did it:

- Install ngrok v2+ in your computer
- Add `ngrok-tunnel` in your Gemfile

```ruby
group :development do
  gem 'ngrok-tunnel'
  # Cool stuff for later but not really needed
  gem 'tty-box'
end
```

- In `puma.rb` we add this code at the bottom

```ruby
if ENV["IS_DEV_ENV"]
  begin
    options = {
      # App port
      addr: ENV.fetch("PORT") { 3000 },
      # ngrok system files
      config: File.join(ENV["HOME"], ".ngrok2", "ngrok.yml"),
    }

    # In case that you have a pay plan you can create
    # tunnels with custom subdomains
    options[:subdomain] = ENV["NGROK_SUBDOMAIN"] if ENV["NGROK_SUBDOMAIN"]

    # Region (since I only work in the EU is hardcoded)
    options[:region] = "eu"

    # Create tunnel
    Ngrok::Tunnel.start(options)

    # Create cool box
    box = TTY::Box.frame(width: 50, height: 10, padding: 2, title: {top_left: "<NGROK>", bottom_right: "</NGROK>"}, style: {fg: :green, bg: :black, border: {fg: :green, bg: :black}}) do
      "STATUS: #{Ngrok::Tunnel.status}\nPORT:   #{Ngrok::Tunnel.port}\nHTTP:   #{Ngrok::Tunnel.ngrok_url}\nHTTPS:  #{Ngrok::Tunnel.ngrok_url_https}\n"
    end
  rescue => error
    box = TTY::Box.frame(width: 50, height: 5, align: :center, padding: 1, title: {top_left: "<NGROK>", bottom_right: "</NGROK>"}, style: {fg: :red, bg: :black, border: {fg: :red, bg: :black}}) do
      "I couldn't create the tunnel ;("
    end
  end
  puts "\n#{box}\n"
end
```

Let's comment this code:

- `ENV["IS_DEV_ENV"]` : Since `Rails.env.development?` is not loaded (at least in Heroku) in this point of the app we are using an environment variable that is only set in your `secrets.yml` or `application.yml` only in the `development` section and it needs to be set to true.
```yml
    development:
    	IS_DEV_ENV: "true"
```
- In the `options` hash we have the app port and the home directory where the ngrok files are located.
- `options[:subdomain]` In case that you are an ngrok subscriber you can have a custom subdomain so I left the code here just in case I became a subscriber one day.
- `options[:region]` This is not in an ENV variable because we are based in the EU and we don't work outside of it, so I just hardcoded EU but you can choose between:
    - us - United States (Ohio)
    - eu - Europe (Frankfurt)
    - ap - Asia/Pacific (Singapore)
    - au - Australia (Sydney)
- WHAT'S IN THE `box` ????!!! just to make it cooler (at least for me) it's showing a nice box with the ngrok information. 

![](https://thepracticaldev.s3.amazonaws.com/i/c49od4z38jvwa59r30xh.png)

- In case ngrok cannot be connected we use the box to show that we cannot connect

![](https://thepracticaldev.s3.amazonaws.com/i/0tt8x1gahc801h6zl1h9.png)

## Change the varibles

You maybe don't need this if you just want to test Mollie payments but for me was nice to have.

To change the config dynamically I choose a way that I think it's far from okey but it works (yeah i know it's running on every request). In the `application_controller.rb` I added this method:
```ruby
before_action :set_ngrok_urls, if: "Rails.env.development?"

def set_ngrok_urls
    if Ngrok::Tunnel.running?
      # Getting current url
      url = Ngrok::Tunnel.ngrok_url_https

      # Variable hash
      default_url_options = {host: url}

      # Overwriting current variables
      Rails.application.config.action_controller.asset_host = url
      Rails.application.config.action_mailer.asset_host = url
      Rails.application.routes.default_url_options = default_url_options
      Rails.application.config.action_mailer.default_url_options = default_url_options
    end
  end
```

So what I'm doing here is in case that the environment is development I'm executing the function `set_ngrok_urls` and checks if ngrok is connected.

Then I get the current ngrok https URL and I create a hash (this hash is needed for the routes and the mailer default URL's) the two first only use the url.

That will be all, hope this works for some people and please if I did something very wrong leave a comment.

## Bibliography

- Autostart Ngrok With Rails: [https://stuff-things.net/2016/10/12/autostart-ngrok-with-rails/](https://stuff-things.net/2016/10/12/autostart-ngrok-with-rails/)