Module: Adyen::Form

Extended by:
Form
Includes:
ActionView::Helpers::TagHelper
Included in:
Form
Defined in:
lib/adyen/form.rb

Overview

The Adyen::Form module contains all functionality that is used to send payment requests to the Adyen payment system, using either a HTML form (see hidden_fields) or a HTTP redirect (see redirect_url).

Moreover, this module contains the method redirect_signature_check to check the request that is made to your website after the visitor has made his payment on the Adyen system for genuinity.

You can use different skins in Adyen to define different payment environments. You can register these skins under a custom name in the module. The other methods will automatically use this information (i.e. the skin code and the shared secret) if it is available. Otherwise, you have to provide it yourself for every method call you make. See register_skin for more information.

See Also:

  • register_skin
  • hidden_fields
  • redirect_url
  • redirect_signature_check

Constant Summary collapse

ACTION_URL =

The URL of the Adyen payment system that still requires the current Adyen enviroment to be filled in.

"https://%s.adyen.com/hpp/select.shtml"

Instance Method Summary collapse

Instance Method Details

#calculate_signature(parameters, shared_secret = nil) ⇒ String

Calculates the payment request signature for the given payment parameters.

This signature is used by Adyen to check whether the request is genuinely originating from you. The resulting signature should be included in the payment request parameters as the merchantSig parameter; the shared secret should of course not be included.

Parameters:

  • parameters (Hash)

    The payment parameters for which to calculate the payment request signature.

  • shared_secret (String) (defaults to: nil)

    The shared secret to use for this signature. It should correspond with the skin_code parameter. This parameter can be left out if the shared_secret is included as key in the parameters.

Returns:

  • (String)

    The signature of the payment request



275
276
277
278
# File 'lib/adyen/form.rb', line 275

def calculate_signature(parameters, shared_secret = nil)
  shared_secret ||= parameters.delete(:shared_secret)
  Adyen::Encoding.hmac_base64(shared_secret, calculate_signature_string(parameters))
end

#calculate_signature_string(parameters) ⇒ String

Generates the string that is used to calculate the request signature. This signature is used by Adyen to check whether the request is genuinely originating from you.

Parameters:

  • parameters (Hash)

    The parameters that will be included in the payment request.

Returns:

  • (String)

    The string for which the siganture is calculated.



251
252
253
254
255
256
257
258
259
260
# File 'lib/adyen/form.rb', line 251

def calculate_signature_string(parameters)
  merchant_sig_string = ""
  merchant_sig_string << parameters[:payment_amount].to_s    << parameters[:currency_code].to_s      <<
                         parameters[:ship_before_date].to_s  << parameters[:merchant_reference].to_s <<
                         parameters[:skin_code].to_s         << parameters[:merchant_account].to_s   <<
                         parameters[:session_validity].to_s  << parameters[:shopper_email].to_s      <<
                         parameters[:shopper_reference].to_s << parameters[:recurring_contract].to_s <<
                         parameters[:allowed_methods].to_s   << parameters[:blocked_methods].to_s    <<
                         parameters[:shopper_statement].to_s << parameters[:billing_address_type].to_s
end

#default_parametersHash

Returns the default parameters to use, unless they are overridden.

Returns:

  • (Hash)

    The hash of default parameters

See Also:

  • default_parameters


93
94
95
# File 'lib/adyen/form.rb', line 93

def default_parameters
  @default_arguments ||= {}
end

#default_parameters=(hash) ⇒ Object

Sets the default parameters to use.

Parameters:

  • hash (Hash)

    The hash of default parameters

See Also:

  • default_parameters


100
101
102
# File 'lib/adyen/form.rb', line 100

def default_parameters=(hash)
  @default_arguments = hash
end

#do_parameter_transformations!(parameters = {}) ⇒ Object

Transforms the payment parameters hash to be in the correct format. It will also include the default_parameters hash. Finally, switches the :skin parameter out for the :skin_code and :shared_secret parameter using the list of registered skins.

Parameters:

  • parameters (Hash) (defaults to: {})

    The payment parameters hash to transform



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/adyen/form.rb', line 136

def do_parameter_transformations!(parameters = {})
  raise "YENs are not yet supported!" if parameters[:currency_code] == 'JPY' # TODO: fixme

  parameters.replace(default_parameters.merge(parameters))
  parameters[:recurring_contract] = 'RECURRING' if parameters.delete(:recurring) == true
  parameters[:order_data]         = Adyen::Encoding.gzip_base64(parameters.delete(:order_data_raw)) if parameters[:order_data_raw]
  parameters[:ship_before_date]   = Adyen::Formatter::DateTime.fmt_date(parameters[:ship_before_date])
  parameters[:session_validity]   = Adyen::Formatter::DateTime.fmt_time(parameters[:session_validity])
  
  if parameters[:skin]
    skin = Adyen::Form.skin_by_name(parameters.delete(:skin))
    parameters[:skin_code]     ||= skin[:skin_code]
    parameters[:shared_secret] ||= skin[:shared_secret]
  end
end

#hidden_fields(parameters = {}) ⇒ String

Returns a HTML snippet of hidden INPUT tags with the provided payment parameters. The snippet can be included in a payment form that POSTs to the Adyen payment system.

The payment parameters that are provided to this method will be merged with the default_parameters hash. The default parameter values will be overrided if another value is provided to this method.

You do not have to provide the :merchant_sig parameter: it will be calculated automatically if you provide either a registered skin name as the :skin parameter or provide both the :skin_code and :shared_secret parameters.

Examples:

<% form_tag(Adyen::Form.url) do %>
  <%= Adyen::Form.hidden_fields(:skin => :my_skin, :currency_code => 'USD',
        :payment_amount => 1000, ...) %>
  <%= submit_tag("Pay invoice")
<% end %>

Parameters:

  • parameters (Hash) (defaults to: {})

    The payment parameters to include in the payment request.

Returns:

  • (String)

    An HTML snippet that can be included in a form that POSTs to the Adyen payment system.



233
234
235
236
237
238
239
240
241
# File 'lib/adyen/form.rb', line 233

def hidden_fields(parameters = {})
  
  # Generate a hidden input tag per parameter, join them by newlines.
  form_str = payment_parameters(parameters).map { |key, value|
    tag(:input, :type => 'hidden', :name => key.to_s.camelize(:lower), :value => value)
  }.join("\n")
  
  form_str.respond_to?(:html_safe) ? form_str.html_safe : form_str
end

#lookup_shared_secret(skin_code) ⇒ String?

Returns the shared secret belonging to a skin code.

Parameters:

  • skin_code (String)

    The skin code of the skin

Returns:

  • (String, nil)

    The shared secret for the skin, or nil if not found.



82
83
84
# File 'lib/adyen/form.rb', line 82

def lookup_shared_secret(skin_code)
  skin = skin_by_code(skin_code)[:shared_secret] rescue nil
end

#payment_parameters(parameters = {}, shared_secret = nil) ⇒ Hash

Transforms the payment parameters to be in the correct format and calculates the merchant signature parameter. It also does some basic health checks on the parameters hash.

Parameters:

  • parameters (Hash) (defaults to: {})

    The payment parameters. The parameters set in the default_parameters hash will be included automatically.

  • shared_secret (String) (defaults to: nil)

    The shared secret that should be used to calculate the payment request signature. This parameter can be left if the skin that is used is registered (see register_skin), or if the shared secret is provided as the :shared_secret parameter.

Returns:

  • (Hash)

    The payment parameters with the :merchant_signature parameter set.

Raises:

  • (StandardError)

    Thrown if some parameter health check fails.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/adyen/form.rb', line 163

def payment_parameters(parameters = {}, shared_secret = nil)
  do_parameter_transformations!(parameters)
  
  raise "Cannot generate form: :currency code attribute not found!"         unless parameters[:currency_code]
  raise "Cannot generate form: :payment_amount code attribute not found!"   unless parameters[:payment_amount]
  raise "Cannot generate form: :merchant_account attribute not found!"      unless parameters[:merchant_account]
  raise "Cannot generate form: :skin_code attribute not found!"             unless parameters[:skin_code]

  # Calculate the merchant signature using the shared secret.
  shared_secret ||= parameters.delete(:shared_secret)
  raise "Cannot calculate payment request signature without shared secret!" unless shared_secret
  parameters[:merchant_sig] = calculate_signature(parameters, shared_secret)
  
  return parameters
end

#redirect_signature(params, shared_secret = nil) ⇒ String

Computes the redirect signature using the request parameters, so that the redirect can be checked for forgery.

Parameters:

  • params (Hash)

    A hash of HTTP GET parameters for the redirect request.

  • shared_secret (String) (defaults to: nil)

    The shared secret for the Adyen skin that was used for the original payment form. You can leave this out of the skin is registered using the register_skin method.

Returns:

  • (String)

    The redirect signature



299
300
301
302
# File 'lib/adyen/form.rb', line 299

def redirect_signature(params, shared_secret = nil)
  shared_secret ||= lookup_shared_secret(params[:skinCode])
  Adyen::Encoding.hmac_base64(shared_secret, redirect_signature_string(params))
end

#redirect_signature_check(params, shared_secret = nil) ⇒ true, false

Checks the redirect signature for this request by calcultating the signature from the provided parameters, and comparing it to the signature provided in the merchantSig parameter.

If this method returns false, the request could be a forgery and should not be handled. Therefore, you should include this check in a before_filter, and raise an error of the signature check fails.

Examples:

class PaymentsController < ApplicationController
  before_filter :check_signature, :only => [:return_from_adyen]

  def return_from_adyen
    @invoice = Invoice.find(params[:merchantReference])
    @invoice.set_paid! if params[:authResult] == 'AUTHORISED'
  end

  private

  def check_signature
    raise "Forgery!" unless Adyen::Form.redirect_signature_check(params)
  end
end

Parameters:

  • params (Hash)

    params A hash of HTTP GET parameters for the redirect request. This should include the :merchantSig parameter, which contains the signature.

  • shared_secret (String) (defaults to: nil)

    The shared secret for the Adyen skin that was used for the original payment form. You can leave this out of the skin is registered using the register_skin method.

Returns:

  • (true, false)

    Returns true only if the signature in the parameters is correct.



334
335
336
# File 'lib/adyen/form.rb', line 334

def redirect_signature_check(params, shared_secret = nil)
  params[:merchantSig] == redirect_signature(params, shared_secret)
end

#redirect_signature_string(params) ⇒ String

Generates the string for which the redirect signature is calculated, using the request paramaters.

Parameters:

  • params (Hash)

    A hash of HTTP GET parameters for the redirect request.

Returns:

  • (String)

    The signature string.



287
288
289
# File 'lib/adyen/form.rb', line 287

def redirect_signature_string(params)
  params[:authResult].to_s + params[:pspReference].to_s + params[:merchantReference].to_s + params[:skinCode].to_s
end

#redirect_url(parameters = {}) ⇒ String

Returns an absolute URL to the Adyen payment system, with the payment parameters included as GET parameters in the URL. The URL also depends on the current Adyen enviroment.

The payment parameters that are provided to this method will be merged with the default_parameters hash. The default parameter values will be overrided if another value is provided to this method.

You do not have to provide the :merchant_sig parameter: it will be calculated automatically if you provide either a registered skin name as the :skin parameter or provide both the :skin_code and :shared_secret parameters.

Note that Internet Explorer has a maximum length for URLs it can handle (2083 characters). Make sure that the URL is not longer than this limit if you want your site to work in IE.

Examples:


def pay
  # Genarate a URL to redirect to Adyen's payment system.
  adyen_url = Adyen::Form.redirect_url(:skin => :my_skin, :currency_code => 'USD',
        :payment_amount => 1000, merchant_account => 'MyMerchant', ... )

  respond_to do |format|
    format.html { redirect_to(adyen_url) }
  end
end

Parameters:

  • parameters (Hash) (defaults to: {})

    The payment parameters to include in the payment request.

Returns:

  • (String)

    An absolute URL to redirect to the Adyen payment system.



207
208
209
210
# File 'lib/adyen/form.rb', line 207

def redirect_url(parameters = {})
  url + '?' + payment_parameters(parameters).map { |(k, v)| 
    "#{k.to_s.camelize(:lower)}=#{CGI.escape(v.to_s)}" }.join('&')
end

#register_skin(name, skin_code, shared_secret) ⇒ Object

Registers a skin for later use.

You can store a skin using a self defined symbol. Once the skin is registered, you can refer to it using this symbol instead of the hard-to-remember skin code. Moreover, the skin’s shared_secret will be looked up automatically for calculting signatures.

Examples:

Adyen::Form.register_skin(:my_skin, 'dsfH67PO', 'Dfs*7uUln9')

Parameters:

  • name (Symbol)

    The name of the skin.

  • skin_code (String)

    The skin code for this skin, as defined by Adyen.

  • shared_secret (String)

    The shared secret used for signature calculation.

See Also:



61
62
63
# File 'lib/adyen/form.rb', line 61

def register_skin(name, skin_code, shared_secret)
  skins[name.to_sym] = {:name => name.to_sym, :skin_code => skin_code, :shared_secret => shared_secret }
end

#skin_by_code(skin_code) ⇒ Hash?

Returns skin information given a skin code.

Parameters:

  • skin_code (String)

    The skin code of the skin

Returns:

  • (Hash, nil)

    A hash with the skin information, or nil if not found.



75
76
77
# File 'lib/adyen/form.rb', line 75

def skin_by_code(skin_code)
  skins.detect { |(name, skin)| skin[:skin_code] == skin_code }.last rescue nil
end

#skin_by_name(skin_name) ⇒ Hash?

Returns skin information given a skin name.

Parameters:

  • skin_name (Symbol)

    The name of the skin

Returns:

  • (Hash, nil)

    A hash with the skin information, or nil if not found.



68
69
70
# File 'lib/adyen/form.rb', line 68

def skin_by_name(skin_name)
  skins[skin_name.to_sym]
end

#skinsHash

Returns all registered skins and their accompanying skin code and shared secret.

Returns:

  • (Hash)

    The hash of registered skins.



33
34
35
# File 'lib/adyen/form.rb', line 33

def skins
  @skins ||= {}
end

#skins=(hash) ⇒ Object

Sets the registered skins.

Parameters:

  • hash (Hash<Symbol, Hash>)

    A hash with the skin name as key and the skin parameter hash (which should include :skin_code and :shared_secret) as value.

See Also:

  • register_skin


41
42
43
44
45
46
# File 'lib/adyen/form.rb', line 41

def skins=(hash)
  @skins = hash.inject({}) do |skins, (name, skin)|
    skins[name.to_sym] = skin.merge(:name => name.to_sym)
    skins
  end
end

#url(environment = nil) ⇒ String

Returns the URL of the Adyen payment system, adjusted for an Adyen environment.

Parameters:

  • environment (String) (defaults to: nil)

    The Adyen environment to use. This parameter can be left out, in which case the ‘current’ environment will be used.

Returns:

  • (String)

    The absolute URL of the Adyen payment system that can be used for payment forms or redirects.

See Also:

  • environment
  • redirect_url


120
121
122
123
# File 'lib/adyen/form.rb', line 120

def url(environment = nil)
  environment ||= Adyen.environment
  Adyen::Form::ACTION_URL % environment.to_s
end