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 +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:
|