Stripe Strong Customer Authentication & Rails 
Tags: rails, ruby, stripe, sca
Published: 2019-10-19 11:40:19

---
title: Stripe Strong Customer Authentication & Rails 
published: true
description: How to migrate stripe checkout to SCA
tags: rails,ruby,stripe,sca
---

You maybe heard about the migration of the **Stripe CB payment** to `SCA Strong Customer Authentication`
All payment from the European countries are concerned.

For more information, [The stripe sca doc](https://stripe.com/fr/guides/strong-customer-authentication)


# The customer experience through the SCA

For your customer, after he puts the Credit card number, the bank can ask for a confirmation by `SMS` or a push notification in bank app in his smartphone.

Stripe now handle this and for better experience, they need to redirect the user to hosted page by stripe and will handle all the complexity.
When succeed or failed, the user is redirected to your webapp.

This is the flow that explain how this works

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/r6l1s90mypcvdrezrhkt.png)


The main steps handled by the rails app are two
1. The preparation of the checkout session [generation of the key]
2. The validation of the payment [Verify the key]

# 1. The session preparation
This will prepare `Stripe session` for the checkout of the product and the redirection when the payment succeed or not.

So we will prepare
1. The name of the product
2. The price
3. The redirection when succeed or not

```ruby
    secret = gen_secret(@product)
    Stripe::Checkout::Session.create(
      {
        payment_method_types: ['card'],
        line_items: [{
                       name: @product.name,
                       description: @product.description,
                       amount: @product.price,
                       currency: 'eur',
                       quantity: 1,
                     }],
        success_url: confirm_product_payment_url(@product, secret: secret),
        cancel_url: cancel_product_payment_url(@product)
      })


```

The interesting part here is the `gen_secret`

We need to generate this `secret` to be able to be sure and verify that the payment at `Stripe` succeed and the callback is not faked

We can generate this key like that in rails.
Thanks to rails to provide everything inside the framework and no need to add foreign dependency.


```ruby
def gen_secret(product)
  key = Rails.application.secrets.secret_key_base
  @crypt = ActiveSupport::MessageEncryptor.new(key[0,32], cipher: 'aes-256-gcm')

  @crypt.encrypt_and_sign(product.token)
end    
```

As I explained in the previous `UML flow`, the callbacks containing this secret will be stored at `stripe` using a secure connection from your server.

## The Payment process
The payment process is classic, the customer will enter his credit card numbers and may use a strong authentication if needed (SMS code verification, push notification ... ect)

## When the payment succeed
At this step, the stripe servers will send to the browser the redirection to the right callback url.
This callback is containing our `secret` generated before.
Time to validation !

# 2. The Validation process
Now, our rails app should verify this callback, in ruby it's easy
For this example I will use a simple verification.

I will only verify if decrypted token is the real token of the product.


```ruby
  # in the product controller
  def confirm
    @product = current_product

    if PaymentService.new(product: @product)
                     .confirm(secret: params[:secret])
      flash[:success] = "Payment succeed"
    else
      flash[:error] = "Oups Error !"
    end

    redirect_to product_url(@product)
  end

  # inside the PaymentService
    ...
    def confirm(secret)
    if @crypt.decrypt_and_verify(secret) == @product.token
      # handle the success of the payment
      # notifications, emails ...ect

      return true
    else
      return false
    end
  end
```

# The Front-end
 
Thanks to `Stripe` the front-end is easy !
So, when the customer want to pay a product, he will click on the `payment button`.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/wonb06fcg9r94l8bc1q2.gif)

This will call our `rails app` to prepare the session, get back the redirection and follow it.

```ruby
    <%= button_to product_pay_path(@product),
     method: :post,
     id: "pay_sca",
     data: { disable_with: '<i class="fa fa-spinner fa-spin"></i>'.html_safe },
     class: "btn btn-success btn-lg btn-block",
     remote: true do  %>
      <i style="float:left;color:white" id="notes-exists" class="fas fa-lock"></i>
      Payer
    <% end %>
```

man that's concise !, this will call our controller by ajax, show a cool animation when waiting for the response of the server and finaly follow the redirection to `Stripe`.


```ruby
  # controller
  def create
    @company_travel = current_travel.company
    @session_id = PaymentService.new(product: current_product)
                                .create_session.id
  end

```



As you have maybe notice, we are using a CRUD resource, when a customer need to pay we `create` a `charging resource`.

The view part is simple also

```ruby
# views/.../create.js.erb

const stripe = Stripe('<%= Rails.application.secrets.stripe_public_key %>');


stripe.redirectToCheckout({
  // We put here our session ID
  sessionId: '<%= @session_id %>'
}).then((result) => {
  // If `redirectToCheckout` fails due to a browser or network
  // error, display the localized error message to your customer
  // using `result.error.message`.
});

```

Easy yay !

I think now we are good to go.

Testing this will be in an other blog post with `system tests`.

As a gift, The complete service can look like this

```ruby
class PaymentService
  include Rails.application.routes.url_helpers

  def initialize(product:)
    @product = product

    key = Rails.application.secrets.secret_key_base
    @cryptor = ActiveSupport::MessageEncryptor.new(key[0,32], cipher: 'aes-256-gcm')
  end

  def create_session
    secret = gen_secret

    Stripe::Checkout::Session.create(
      {
        payment_method_types: ['card'],
        line_items: [{
                       name: @product.name
                       description: @product.description,
                       amount: @product.price,
                       currency: 'eur',
                       quantity: 1,
                     }],
        success_url: confirm_product_payment_url(@product, secret: secret),
        cancel_url: cancel_product_payment_url(@product )
      })
  end

  def confirm(secret)
    if @cryptor.decrypt_and_verify(secret) == @product.token
      # handle a succeed payment
      # send notifications, invoices ... ect
      return true
    else
      return false
    end
  end

  private

  def gen_secret
    @cryptor.encrypt_and_sign(@product.token)
  end
end


```