omnipay 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c3c13dede2e2ea788ee716eb834b225b1cae1ca9
4
- data.tar.gz: 949abeb3bde942deb0c11d8c011814b692f538f1
3
+ metadata.gz: b70c2a69c944278d12c7d4d28dba68f9a4cdc7b7
4
+ data.tar.gz: e1957fbd18a16d08f9e624fe527dcc856f52f568
5
5
  SHA512:
6
- metadata.gz: afbd5ccc734ef955631e111ded2dbf447dc63ae3658069d7b72c1c8210dbee2eb707a989f5057dc0ec112f0a7dcfa43660b6f8d0aaa6b76a80155b6883331d09
7
- data.tar.gz: d419fd51650c301b5c2a379fea3a11192921b1b585f146726af5ed2d933d0c1054afd28ac1c6d41cafb443e835e2075554048a85aa59faeddb8cb23af40934c3
6
+ metadata.gz: 1d8b70552a9695e6adadccb9b710b5c9070bbed9d86cdb8ae6bf2dbd529f8c70a6eda5ff5ab9d528085291814e67cf549d3af40d1422556258c2b13f008a8eba
7
+ data.tar.gz: 16eb68cb0f62b6b1bd5984b234a04dc9b2afdaf6d51347c77fd2ffc1112ef8ad363781616cac3e806c9e66ea4a79dfbafb00085f74fd6352cab29fb1890a78d0
@@ -1,5 +1,6 @@
1
1
  require 'singleton'
2
2
 
3
+ # The root Omnipay module. Used for defining its global configuration
3
4
  module Omnipay
4
5
 
5
6
  autoload :Gateway, 'omnipay/gateway'
@@ -9,22 +10,38 @@ module Omnipay
9
10
  autoload :AutosubmitForm, 'omnipay/autosubmit_form'
10
11
  autoload :Signer, 'omnipay/signer'
11
12
 
12
- # Error codes
13
+ # Error code for an untreatable response
13
14
  INVALID_RESPONSE = :invalid_response
15
+
16
+ # Error code for a user-initiated payment failure
14
17
  CANCELATION = :cancelation
18
+
19
+ # Error code for a valid response but a failed payment
15
20
  PAYMENT_REFUSED = :payment_refused
21
+
22
+ # Error code for a signature mismatch
16
23
  WRONG_SIGNATURE = :wrong_signature
17
24
 
18
- # Configuration
25
+
26
+ # The global Omnipay configuration singleton
19
27
  class Configuration
20
28
  include Singleton
21
29
  attr_accessor :secret_token
22
30
  end
23
31
 
32
+ # Accessor to the global configuration
33
+ # @return [Configuration]
24
34
  def self.configuration
25
35
  Configuration.instance
26
36
  end
27
37
 
38
+ # Allows to configure omnipay via a block
39
+ #
40
+ # Example use :
41
+ #
42
+ # Omnipay.configure do |config|
43
+ # config.secret_token = "a-secret-token"
44
+ # end
28
45
  def self.configure
29
46
  yield configuration
30
47
  end
@@ -1,13 +1,18 @@
1
- # Glue between the rack middleware and the adapter implementation
2
- # Responsible for initializing, configuring the adapter, and formatting
3
- # the responses for use in the middleware
4
-
5
1
  module Omnipay
6
2
 
3
+ # Wrapper around an actual adapter implementation. Responsible mainly for handling its initialization with
4
+ # a static or dynamic (block) configuration
7
5
  class Adapter
8
6
 
7
+ # The adapter's unique identifier. Will be passed to the dynamic configuration block.
9
8
  attr_reader :uid
10
9
 
10
+
11
+ # @param uid [String] The adapter's unique identifier
12
+ # @param callback_url [String] The absolute URL to be used for the user redirection after the payment
13
+ # @param config [Hash] A static adapter configuration
14
+ # @param dynamic_config [String => Hash] A dynamic config block. Takes the uid as an input and returns the adapter's configuration
15
+ # @return [Adapter]
11
16
  def initialize(uid, callback_url, config, dynamic_config)
12
17
  @uid = uid
13
18
  @callback_url = callback_url
@@ -17,14 +22,23 @@ module Omnipay
17
22
  @strategy = build_strategy
18
23
  end
19
24
 
25
+ # Is there a valid adapter configuration for the given parameters. Checks notably if the given uid is valid in case of a dyncamic configuration.
26
+ # @return [Boolean]
20
27
  def valid?
21
28
  @strategy != nil
22
29
  end
23
30
 
24
- def request_phase(amount, opts)
31
+ # Proxy to the adapter's implementation's request_phase method
32
+ # @param amount [Integer] The amount to pay, in **cents**
33
+ # @param opts [Hash] The custom GET parameters sent to the omnipay payment url
34
+ # @return [Array] The array containing the redirection method (GET or POST), its url, its get or post params, and the unique associated transaction id
35
+ def request_phase(amount, opts = {})
25
36
  @strategy.request_phase(amount, opts)
26
37
  end
27
38
 
39
+ # Proxy to the adapter's implementation's callback_phase method
40
+ # @param params [Hash] The GET/POST params sent by the payment gateway to the callback url
41
+ # @return [Hash] The omnipay response environment. Contains the response success status, the amount payed, the error message if any, ...
28
42
  def callback_hash(params)
29
43
  @strategy.callback_hash(params)
30
44
  end
@@ -0,0 +1,117 @@
1
+ module Omnipay
2
+ module Adapters
3
+
4
+ # This is a sample Omnipay adapter implementation for a fictive payment gateway. You can use it as a boilerplate to develop your own.
5
+ #
6
+ # This adapter will take two arguments in its configuration :
7
+ # * *api_key* [String - mandatory] : given by the payment gateway, used for authenticating to their API
8
+ # * *sandbox* [Boolean - optional] : whether the payment should be done in a sandbox or with the real payment page
9
+ # It may then be initialized like this in a rack application
10
+ # use Omnipay::Gateway,
11
+ # :uid => 'my-gateway',
12
+ # :adapter => Omnipay::Adapters::SampleAdapter
13
+ # :config => {
14
+ # :api_key => 'my-api-key',
15
+ # :sandbox => (ENV['RACK_ENV'] != 'production')
16
+ # }
17
+
18
+ class SampleAdapter
19
+
20
+ # The adapters initialization
21
+ # @param callback_url [String] the absolute callback URL the user should be redirected to after the payment. Will be generated by Omnipay. Example value : "http\://\www\.my-host.tld/pay/my-gateway/callback"
22
+ # @param config [Hash] the adapter's configuration as defined in the application's omnipay config file. For the example above in a dev environment, the value would be {:api_key => 'my-api-key', :sandbox => true}. It is up to you to decide which fields are mandatory, and to both check them in this initializer and specify them in your documentation. In this case :
23
+ # * *:api_key* [String] (mandatory) : the API key given for your fictive gateway's account
24
+ # * *:sandbox* [Boolean] (optional) : whether to use a sandboxed payment page, or a real one. Defaults to true
25
+ # @return [SampleAdapter]
26
+ def initialize(callback_url, config = {})
27
+ @callback_url = callback_url
28
+
29
+ @api_key = config[:api_key]
30
+ raise ArgumentError.new("Missing api_key") unless @api_key
31
+
32
+ @mode = (config[:sandbox] == false) ? 'production' : 'sandbox'
33
+ end
34
+
35
+
36
+ # Responsible for determining the redirection to the payment page for a given amount
37
+ #
38
+ # @param amount [Integer] the amount **in cents** that the user has to pay
39
+ # @param params [Hash] the GET parameters sent to the omnipay payment URL. Can be used to specify transaction-specific variables (the locale to use, the payment page's title, ...). Please use the following keys if you need to, for consistency among adapters :
40
+ # * *locale* The ISO 639-1 locale code for the payment page
41
+ # * *title* The title to display on the payment page
42
+ # * *transaction_id* The transaction id to use, if you want the user to be able to force it
43
+ # @return [Array] an array containing the 4 following values :
44
+ # * +[String]+ 'GET' or 'POST' : the HTTP method to use for redirecting to the payment page
45
+ # * +[String]+ the absolute URL of the payment page. Example : "https\://my-provider.tld/sc12fdg57df"
46
+ # * +[Hash]+ the GET or POST parameters to send to the payment page
47
+ # * +[String]+ the unique transaction_id given by the payment provider for the upcoming payment. Has to be accessible in the callback phase.
48
+
49
+ def request_phase(amount, params={})
50
+ amount_in_dollar = amount * 1.0 / 100
51
+ locale = params[:locale] || 'en'
52
+
53
+ transaction = build_new_transaction(amount_in_dollars, locale)
54
+
55
+ uri = URI(transaction.payment_url)
56
+
57
+ method = 'GET'
58
+ url = "#{uri.scheme}://#{uri.host}#{uri.path}"
59
+ get_params = Rack::Utils.parse_query(uri.query)
60
+ transaction_id = transaction.id
61
+
62
+ return [
63
+ method,
64
+ url,
65
+ get_params,
66
+ transaction_id
67
+ ]
68
+
69
+ end
70
+
71
+
72
+ # @param params [Hash] the GET/POST parameters sent by the payment gateway to the callback url
73
+ # @return [Hash] the resulting response hash which will be accessible in the application. Must contain the following values :
74
+ # * *:success* (+Boolean+) | Did the payment occur or not?
75
+ # * *:amount* (+Integer+) <i>if successful</i> | The amount <b>in cents</b> actually payed
76
+ # * *:transaction_id* (+String+) <i>if successful</i> | The id of the transaction. Must match the one returned in the request phase.
77
+ # * *:error* (+Symbol+) <i>if failed</i> | The reason why the payment was not successful. The available values are :
78
+ # * _Omnipay::CANCELED_ : The payment didn't occur because of the user.
79
+ # * _Omnipay::PAYMENT_REFUSED_ : The payment didn't occue because of an error on the gateway's side.
80
+ # * _Omnipay::INVALID_RESPONSE_ : The response from the gateway cannot be parsed. The payment may or may not have occured.
81
+ # * *:error_message* (+String+) <i>if failed</i> | A more detailed error message / stack trace, for logging purposes
82
+
83
+ def callback_hash(params)
84
+
85
+ transaction_id = params[:transaction_id]
86
+ transaction = fetch_transaction(transaction_id)
87
+
88
+ if !transaction
89
+ return { :success => false, :error => Omnipay::INVALID_RESPONSE, :error_message => "No transaction found with id #{transaction_id}"}
90
+ end
91
+
92
+ if transaction.success
93
+ { :success => true, :amount => (transaction.amount*100).to_i, :transaction_id => transaction_id }
94
+ else
95
+ if transaction.canceled
96
+ { :success => false, :error => Omnipay::CANCELATION }
97
+ else
98
+ { :success => false, :error => Omnipay::PAYMENT_REFUSED, :error_message => "Transaction #{transaction_id} was not successful : #{transaction.error}" }
99
+ end
100
+ end
101
+ end
102
+
103
+
104
+ private
105
+
106
+ def build_new_transaction(amount, locale)
107
+ # TODO
108
+ end
109
+
110
+ def fetch_transaction(transaction_id)
111
+ # TODO
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+ end
@@ -1,36 +1,52 @@
1
- # Builds an html page with an autosubmitted form, to handle POST redirections
2
1
  module Omnipay
2
+
3
+ # This class is responsible for generating an html page which will redirect its visitor to an arbitrary url,
4
+ # with arbitrary POST parameters in the request body.
5
+ #
6
+ # It does so by including an hidden form in the page, and submitting it via javascript on page load.
7
+ #
3
8
  class AutosubmitForm
4
9
 
5
10
  HEADER = <<-HEADER
6
11
  <!DOCTYPE html>
7
12
  <html>
8
13
  <head>
9
- <script type="text/javascript">
10
- window.onload=function(){
11
- document.getElementById('autosubmit-form').submit();
12
- }
13
- </script>
14
+ <title>You are being redirected</title>
14
15
  </head>
15
-
16
16
  <body>
17
17
  <h1>Redirecting...</h1>
18
18
  HEADER
19
19
 
20
20
  FOOTER = <<-FOOTER
21
+ <script type="text/javascript">
22
+ document.getElementById('autosubmit-form').submit();
23
+ </script>
21
24
  </body>
22
25
  </html>
23
26
  FOOTER
24
27
 
28
+
29
+ # Initializes the form with its action, and its inputs name/value pairs
30
+ #
31
+ # @param action [String] The url the form must be submitted to. Will be the value of its <action> attribute
32
+ # @param fields [Hash] The key/value pairs of parameters to be sent as POST to the action. Will be a set of hidden <inputs> in the form.
33
+ #
34
+ # @return [AutosubmitForm]
25
35
  def initialize(action, fields)
26
36
  @action = action
27
37
  @fields = fields.map{|name, value| {:name => name, :value => value}}
28
38
  end
29
39
 
40
+
41
+ # Returns the full html page
42
+ # @return [String]
30
43
  def html
31
44
  HEADER + form_html + FOOTER
32
45
  end
33
46
 
47
+
48
+ private
49
+
34
50
  def form_html
35
51
  " <form method=\"POST\" id=\"autosubmit-form\" action=\"#{@action}\">\n" +
36
52
  @fields.map{|field|
@@ -1,14 +1,19 @@
1
- # Class responsible for updating the request env in the callback phase
2
-
3
1
  module Omnipay
2
+
3
+ # Class responsible for the processing in a gateway's callback phase. It updates the request's environemnt with the formatted Omnipay response given by the apdater's implementation.
4
4
  class CallbackPhase
5
5
 
6
+ # @param request [Rack::Request] The request corresponding to the redirection from the payment gateway to the application.
7
+ # @param adapter [Adapter] The adapter instance of the gateway having catched this request
8
+ # @return [CallbackPhase]
6
9
  def initialize(request, adapter)
7
10
  @request = request
8
11
  @adapter = adapter
9
12
  end
10
13
 
11
14
 
15
+ # Actually set the request's environment variable 'omnipay.response' with the formatted summary of the payment transaction.
16
+ # @note Should only be called once because of the signatures lifetime
12
17
  def update_env!
13
18
 
14
19
  # The request params, keys symbolized
@@ -2,11 +2,27 @@ require 'rack'
2
2
 
3
3
  module Omnipay
4
4
 
5
- # Gateway middleware
5
+ # This is the actual Rack middleware
6
+ #
7
+ # It is associated with an adapter instance, and
8
+ # responsible for monitoring the incoming request
9
+ # and - depending on their path - forwarding them
10
+ # for processing to a RequestPhase or CallbackPhase
11
+ # instance for processing
6
12
  class Gateway
7
13
 
8
14
  BASE_PATH = '/pay'
9
15
 
16
+ # @param app [Rack application] : The rack app it should forward to if the request doens't match a monitored path
17
+ #
18
+ # @param options [Hash] : must contains the following configuration options
19
+ # * :uid : the subpath to monitor
20
+ # * :adapter : the adapter class to use
21
+ # * :config : the config hash which will be passed at the adapter for initialization
22
+ #
23
+ # The gateway may be initialized with a block returning a hash instead of a hash. In this case, the
24
+ # block's only argument is the uid, and the returned hash must contain the adapter class and its configuration
25
+
10
26
  def initialize(app, options={}, &block)
11
27
  @app = app
12
28
 
@@ -18,7 +34,8 @@ module Omnipay
18
34
  @request = nil
19
35
  end
20
36
 
21
-
37
+ # The standard rack middleware call. Will be processed by an instance of RequestPhase or CallbackPhase if the
38
+ # path matches the adapter's uid. Will forward to the app otherwise
22
39
  def call(env)
23
40
 
24
41
  # Get the current request
@@ -1,13 +1,18 @@
1
- # Class responsible for formatting the redirection in the request phase
2
-
3
1
  module Omnipay
2
+
3
+ # Class responsible for formatting the redirection in the request phase
4
4
  class RequestPhase
5
5
 
6
+ # @param request [Rack::Request] The request corresponding to the redirection from the payment gateway to the application.
7
+ # @param adapter [Adapter] The adapter instance of the gateway having catched this request
8
+ # @return [RequestPhase]
6
9
  def initialize(request, adapter)
7
10
  @request = request
8
11
  @adapter = adapter
9
12
  end
10
13
 
14
+ # Returns the rack response for redirecting the user to the payment page. Can be a 302 redirect if a GET redirection, or an AutosubmittedForm for POST redirections
15
+ # @return [Rack::Response]
11
16
  def response
12
17
  method, url, params, transaction_id = @adapter.request_phase(amount, adapter_params)
13
18
 
@@ -1,30 +1,33 @@
1
- # Class responsible for signing the outgoing payments in the request phase,
2
- # and validating them in the callback phase
3
-
4
1
  require 'openssl'
5
2
 
6
3
  module Omnipay
4
+
5
+ # Class responsible for computing a signature of a payment.
7
6
  class Signer
8
7
 
8
+ # @param transaction_id [String] the transactions's unique identifier
9
+ # @param amount [Integer] the amount **in cents** of the transaction
10
+ # @param context [Hash] the transaction's context hash
11
+ # @return [Signer]
9
12
  def initialize(transaction_id, amount, context)
10
13
  @transaction_id = transaction_id
11
14
  @amount = amount
12
15
  @context = context || {}
13
16
  end
14
17
 
18
+ # Actually computes the signature
19
+ # @return [String] The computed signature
15
20
  def signature
16
21
  to_sign = "#{secret_token}:#{@transaction_id}:#{@amount}:#{self.class.hash_to_string @context}"
17
22
  CGI.escape(Base64.encode64(OpenSSL::HMAC.digest('sha1', secret_token, to_sign)))
18
23
  end
19
24
 
25
+ private
20
26
 
21
- # Unique key : to configure globally
22
27
  def secret_token
23
28
  Omnipay.configuration.secret_token
24
29
  end
25
30
 
26
- private
27
-
28
31
  def self.hash_to_string(hash)
29
32
  # key/values appended by alphabetical key order
30
33
  hash.sort_by{|k,_|k}.flatten.join('')
@@ -1,3 +1,3 @@
1
1
  module Omnipay
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnipay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ClicRDV
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-10 00:00:00.000000000 Z
11
+ date: 2014-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -51,6 +51,7 @@ files:
51
51
  - lib/omnipay/adapters/mangopay.rb
52
52
  - lib/omnipay/adapters/mangopay/client.rb
53
53
  - lib/omnipay/adapters/oneclicpay.rb
54
+ - lib/omnipay/adapters/sample_adapter.rb
54
55
  - lib/omnipay/autosubmit_form.rb
55
56
  - lib/omnipay/callback_phase.rb
56
57
  - lib/omnipay/gateway.rb
@@ -81,3 +82,4 @@ signing_key:
81
82
  specification_version: 4
82
83
  summary: Payment gateway abstraction for rack applications.
83
84
  test_files: []
85
+ has_rdoc: