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 +4 -4
- data/lib/omnipay.rb +19 -2
- data/lib/omnipay/adapter.rb +19 -5
- data/lib/omnipay/adapters/sample_adapter.rb +117 -0
- data/lib/omnipay/autosubmit_form.rb +23 -7
- data/lib/omnipay/callback_phase.rb +7 -2
- data/lib/omnipay/gateway.rb +19 -2
- data/lib/omnipay/request_phase.rb +7 -2
- data/lib/omnipay/signer.rb +9 -6
- data/lib/omnipay/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b70c2a69c944278d12c7d4d28dba68f9a4cdc7b7
|
4
|
+
data.tar.gz: e1957fbd18a16d08f9e624fe527dcc856f52f568
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d8b70552a9695e6adadccb9b710b5c9070bbed9d86cdb8ae6bf2dbd529f8c70a6eda5ff5ab9d528085291814e67cf549d3af40d1422556258c2b13f008a8eba
|
7
|
+
data.tar.gz: 16eb68cb0f62b6b1bd5984b234a04dc9b2afdaf6d51347c77fd2ffc1112ef8ad363781616cac3e806c9e66ea4a79dfbafb00085f74fd6352cab29fb1890a78d0
|
data/lib/omnipay.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
data/lib/omnipay/adapter.rb
CHANGED
@@ -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
|
-
|
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
|
-
<
|
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
|
data/lib/omnipay/gateway.rb
CHANGED
@@ -2,11 +2,27 @@ require 'rack'
|
|
2
2
|
|
3
3
|
module Omnipay
|
4
4
|
|
5
|
-
#
|
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
|
|
data/lib/omnipay/signer.rb
CHANGED
@@ -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('')
|
data/lib/omnipay/version.rb
CHANGED
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.
|
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-
|
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:
|