Syndicode
Contact Us
Contact Us
4.9 on Clutch
Delivery Team, Marketing Team, Oleksandr Subbotin

Payment processing with Adyen and Ruby on Rails

99.99% of our startup clients have one question in common – how will we accept payments? In this article I’ll show how to integrate your Ruby on Rails application with Adyen payment processing. It has a solution for mobile, online and in-store transactions, their technology enables merchants to accept almost any type of payment, anywhere in the world.

First, setup Adyen account.

Login to the Adyen Customer Area and retrieve the password of your ws user that can submit API calls. Select ws@Company.YourCompany and generate a new password (make a note of your password). Make a note of your Hosted Client Encryption Token to include the encryption library in the following steps.

Set up your payment form

Make sure the form includes all the mandatory fields as shown below:

%form#adyen-encrypted-form{ action: "#handler", method: "POST"}
  %input{ data: { "encrypted-name" => "number" }, size: "20", type: "text"}
  %input{ data: { "encrypted-name" => "holderName" }, size: "20", type: "text"}
  %input{ data: { "encrypted-name" => "expiryMonth" }, maxlength: "2", size: "2", type: "text"}
  %input{ data: { "encrypted-name" => "expiryYear" }, maxlength: "4", size: "4", type: "text"}
  %input{ data: { "encrypted-name" => "cvc" }, maxlength: "4", size: "4", type: "text"}
  %input{ data: { "encrypted-name" => "generationtime" }, type: "hidden", 
          value: Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ')}
  %input{ type: "submit", value: "Pay"}

JS encryption library

Next, include the JS encryption library and use it on your payment form.
Your public encryption key and library are hosted by Adyen and you have a unique URL to use when loading the JS in your payment page.
This URL have to contain your unique Hosted Client Encryption Token (see step #1), as shown in the code below.
In the javascript file, call createEncryptedForm with your payment form thus the credit card information is encrypted before the request reaches your servers.

%script{src: "https://test.adyen.com/hpp/cse/js/.shtml", type: 'text/javascript'}
  var form = document.getElementById('adyen-encrypted-form');
  adyen.createEncryptedForm(form);

 Wrapper for Adyen API

Then let’s create a small wrapper for API calls:

class Adyen
  API_URL = 'https://pal-test.adyen.com/pal/servlet/Payment/v12/'

  def self.authorise(encrypted_data, amount_in_cents, currency)
    operation_data = {
      merchantAccount: ENV,
      additionalData: {"card.encrypted.json" => encrypted_data},
      amount: {currency: currency, value: amount_in_cents},
      reference: "your_prefix_" + "#{user.id.to_s}_" +  rand(99999999).to_s
    }
    uri = URI(API_URL + __method__.to_s)
    call_api(uri, operation_data)
  end

  private

  def self.call_api(uri, operation_data)
    res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
      http.request create_request(uri, operation_data)
    end
    JSON.parse(res.body)
  end

  def self.create_request(uri, operation_data)
    Net::HTTP::Post.new(uri, initheader = {'Content-Type' =>'application/json'}).tap |req|
      req.basic_auth ENV, ENV
      req.body = operation_data.to_json
    end
  end
end

Real business

Form submit

After the shopper has submitted your payment form, and before it reaches your server, the script automatically adds the encrypted data to the form. For testing you can use one of test credit cards. Make sure that you include this encrypted data in the server-to-server API call, together with all mandatory fields as seen in the example below. While submitting payments from your server, include your valid credentials (see ENV[]).

Authorize payment

And now you can submit payment to the Adyen test point:

auth_res = Adyen.authorise(params, (amount * 100).to_i, currency)

After submitting the payment, you will receive the response that includes a PSP reference (eg. 7457830855284661). The PSP reference identifies each payment and can be used for further operations like cancel, capture or refund the payment.

Capture your money

After the successful payment authorisation you can completely capture the money, just add one more method to our wrapper class:

class Adyen
  def self.capture(psp, amount_in_cents, currency)
    operation_data = {
      merchantAccount: ENV,
      modificationAmount: {value: amount_in_cents, currency: currency},
      originalReference: psp
    }

    uri = URI(API_URL + __method__.to_s)
    call_api(uri, operation_data)
  end
end

And voilà:

cap_res = Adyen.capture(auth_res, (amount*100).to_i, currency)

Remember this client has paid

Now we can check response on success: cap_res == ”  and see all processed payments in the Adyen Payments List.

Now it’s time for another type of integration: Hosted Payment Pages (HPP).

Why Ruby on Rails online payments with Adyen HPP

You can easily do Ruby on Rails online payments with Stripe, BrainTree, and many others. We have experience integrating these online payments to Ruby on Rails web app during just one day. However, competitive advantage of Adyen’s HPP is that in addition to PayPal and credit card, Adyen’s HPP can allow your Ruby on Rails direct bank payment, with such mathods as SEPA, SOFORT, iDEAL, etc.

HPP provides a secure, flexible and easy way for customers to purchase services and goods:

  • Customers go to your site, then they select and add the services/items to a shopping cart.
  • Next, they are redirected to the hosted payment page, where they enter the billing details to process the payment.
  • After submitting they are redirected back to your web site, where they can see a summary information displaying the result of the payment processing.
  • You can customise the look of the HPP using Adyen’s skin technology and toolset to create a seamless checkout for your customers.

Prepare account for using HPP

Login to the Adyen Customer Area, create a new skin and configure the allowed payment methods.

Generagte HMAC key to be used in your payment request’s signature and add a method to prepare your HPP URI:

def gs(i) # adyen merchant signature params escaping
  i.gsub(':', ':').gsub('', '\')
end

def hpp_uri(order)
  user = order.user
  merchant_reference = "your_prefix_" + user.id.to_s + "_" + order.id.to_s + "_" +  rand(99999999).to_s
  merchant_account = ENV
  payment_amount = (order.total*100).to_i.to_s
  currncy = order.currency
  session_validity = 1.day.from_now.strftime('%Y-%m-%dT%H:%M:%SZ')
  ship_before_date = order.shipment.date.strftime('%Y-%m-%d')

  # ---- merchant signature ----
  string = "currencyCode:merchantAccount:merchantReference:paymentAmount:recurringContract:sessionValidity:shipBeforeDate:shopperEmail:shopperLocale:shopperReference:skinCode"
  string += ':'+gs(currency)
  string += ':'+gs(merchant_account)
  string += ':'+gs(merchant_reference)
  string += ':'+gs(payment_amount)
  string += ':ONECLICK'
  string += ':'+gs(session_validity)
  string += ':'+gs(ship_before_date)
  string += ':'+gs(user.email)
  string += ':'+gs(I18n.locale.to_s)
  string += ':'+gs(user.shopper_reference)
  string += ':'+gs('YOUR_HPP_SKIN_CODE')

  hmac = Order.create_hmac(string)
  # ---- merchant signature ----

  uri = ENV # 'https://test.adyen.com/hpp/pay.shtml'
  uri += '?'
  uri += "merchantSig="+Rack::Utils.escape(hmac)
  uri += "&sessionValidity="+session_validity
  uri += "&shipBeforeDate="+ship_before_date
  uri += "&shopperLocale="+I18n.locale.to_s
  uri += "&merchantAccount="+merchant_account
  uri += "&paymentAmount="+payment_amount
  uri += "&currencyCode="+currency
  uri += "&skinCode="+'YOUR_HPP_SKIN_CODE'
  uri += "&merchantReference="+merchant_reference
  uri += "&shopperReference="+user.shopper_reference
  uri += "&recurringContract=ONECLICK"
  uri += "&shopperEmail="+user.email
end

Prepare your application to process HPP payments

Create a new endpoint in your Orders controller where user will be redirected after payment processing:

def payment_processed
  return redirect_to root_url, alert: 'Adyen request invalid' unless params

  order_id = params.split('_').to_i
  order = Order.find order_id

  if  .include?(params.downcase) ||
      Adyen.capture(params, (order.total*100).to_i, order.currency) != ''

    # process failed payment
    redirect_to order, alert: I18n.t('payment_failed')
  else
    # process success payment
    redirect_to order
  end
end

Add this endpoint to your Adyen’s HPP skin:

Снимок экрана от 2016-04-28 13:29:12

In your Orders controller #create action setup redirect to Adyen HPP:

def create
  order = Order.new(order_params)
  if order.save
    redirect_to Adyen.hpp_uri(order)
  else
    render :new
  end
end

And now you are ready for global online payments and EU direct bank payments!