omnipay 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: