ethikdo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 69e3c079fa419a6f528b987b6fd27532bbe32c13fc2edf835a2935b00d57e306
4
+ data.tar.gz: 36ba7a4fff07e5be5122067fda892a6dd2075c992f1f42e05ebb441e63773cb2
5
+ SHA512:
6
+ metadata.gz: 81e308089d0537a5bf6f9ee25ac3820beb314b5b9ab1b64bde9b9aa11352008ed48439c526f40216b298a5ce9616e46bb8d0d149ce176e770e24e8b2eca078cc
7
+ data.tar.gz: f3b88b0ebe7307840a433e584557f28962c6e38b7392d25542cbbdd3c8b942f06ca5515d4a985dea0a50e50658cfc4ae406900a6e0df3c8ca3d75789cab74a0a
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.0
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,110 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ethikdo (0.0.1)
5
+ httparty (~> 0.16.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionpack (6.0.2.2)
11
+ actionview (= 6.0.2.2)
12
+ activesupport (= 6.0.2.2)
13
+ rack (~> 2.0, >= 2.0.8)
14
+ rack-test (>= 0.6.3)
15
+ rails-dom-testing (~> 2.0)
16
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
17
+ actionview (6.0.2.2)
18
+ activesupport (= 6.0.2.2)
19
+ builder (~> 3.1)
20
+ erubi (~> 1.4)
21
+ rails-dom-testing (~> 2.0)
22
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
23
+ activesupport (6.0.2.2)
24
+ concurrent-ruby (~> 1.0, >= 1.0.2)
25
+ i18n (>= 0.7, < 2)
26
+ minitest (~> 5.1)
27
+ tzinfo (~> 1.1)
28
+ zeitwerk (~> 2.2)
29
+ addressable (2.7.0)
30
+ public_suffix (>= 2.0.2, < 5.0)
31
+ builder (3.2.4)
32
+ concurrent-ruby (1.1.6)
33
+ crack (0.4.3)
34
+ safe_yaml (~> 1.0.0)
35
+ crass (1.0.6)
36
+ diff-lcs (1.3)
37
+ erubi (1.9.0)
38
+ generator_spec (0.9.4)
39
+ activesupport (>= 3.0.0)
40
+ railties (>= 3.0.0)
41
+ hashdiff (1.0.0)
42
+ httparty (0.16.4)
43
+ mime-types (~> 3.0)
44
+ multi_xml (>= 0.5.2)
45
+ i18n (1.8.2)
46
+ concurrent-ruby (~> 1.0)
47
+ loofah (2.5.0)
48
+ crass (~> 1.0.2)
49
+ nokogiri (>= 1.5.9)
50
+ method_source (1.0.0)
51
+ mime-types (3.3.1)
52
+ mime-types-data (~> 3.2015)
53
+ mime-types-data (3.2019.1009)
54
+ mini_portile2 (2.4.0)
55
+ minitest (5.14.0)
56
+ multi_xml (0.6.0)
57
+ nokogiri (1.10.9)
58
+ mini_portile2 (~> 2.4.0)
59
+ public_suffix (4.0.3)
60
+ rack (2.2.2)
61
+ rack-test (1.1.0)
62
+ rack (>= 1.0, < 3)
63
+ rails-dom-testing (2.0.3)
64
+ activesupport (>= 4.2.0)
65
+ nokogiri (>= 1.6)
66
+ rails-html-sanitizer (1.3.0)
67
+ loofah (~> 2.3)
68
+ railties (6.0.2.2)
69
+ actionpack (= 6.0.2.2)
70
+ activesupport (= 6.0.2.2)
71
+ method_source
72
+ rake (>= 0.8.7)
73
+ thor (>= 0.20.3, < 2.0)
74
+ rake (12.3.3)
75
+ rspec (3.9.0)
76
+ rspec-core (~> 3.9.0)
77
+ rspec-expectations (~> 3.9.0)
78
+ rspec-mocks (~> 3.9.0)
79
+ rspec-core (3.9.1)
80
+ rspec-support (~> 3.9.1)
81
+ rspec-expectations (3.9.0)
82
+ diff-lcs (>= 1.2.0, < 2.0)
83
+ rspec-support (~> 3.9.0)
84
+ rspec-mocks (3.9.1)
85
+ diff-lcs (>= 1.2.0, < 2.0)
86
+ rspec-support (~> 3.9.0)
87
+ rspec-support (3.9.2)
88
+ safe_yaml (1.0.5)
89
+ thor (1.0.1)
90
+ thread_safe (0.3.6)
91
+ tzinfo (1.2.7)
92
+ thread_safe (~> 0.1)
93
+ webmock (3.8.2)
94
+ addressable (>= 2.3.6)
95
+ crack (>= 0.3.2)
96
+ hashdiff (>= 0.4.0, < 2.0.0)
97
+ zeitwerk (2.3.0)
98
+
99
+ PLATFORMS
100
+ ruby
101
+
102
+ DEPENDENCIES
103
+ ethikdo!
104
+ generator_spec (~> 0.9)
105
+ rake (~> 12)
106
+ rspec (~> 3.2)
107
+ webmock (~> 3)
108
+
109
+ BUNDLED WITH
110
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Benoit Baumann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,202 @@
1
+ # Ethikdo
2
+
3
+ This gem provides an easy way to manage transactions for Ethi'kdo card holders through the Ethi'kdo v1 REST API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ethikdo'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ethikdo
20
+
21
+ ## Usage
22
+
23
+ __Configuration__
24
+
25
+ The gem needs to be configured with your private API key. To do so, execute:
26
+
27
+ ```
28
+ rails generate ethikdo:install
29
+ ```
30
+
31
+ This will generate a file named `ethikdo.rb` in `config/initializers/` with the following content:
32
+
33
+ ```ruby
34
+ Ethikdo.configure do |config|
35
+ config.api_key = ENV["ETHIKDO_API_KEY"]
36
+ config.environment = Rails.env.production? ? :production : :development
37
+ end
38
+ ```
39
+
40
+ Now, set up your private key as an environment variable in your `.env` file:
41
+
42
+ ```
43
+ ETHIKDO_API_KEY = "Token XXXXXXX"
44
+ ```
45
+
46
+ The gem can be configured for two environments: _production_ and _development_. By default, the gem's environment is set to _production_ if Rails is in _production_, else it is set to _development_.
47
+
48
+ In _development_, the requests are sent to a test API provided by Ethi'kdo. The test API's url is:
49
+ ```
50
+ https://recette.ethikdo.co/api/v1/
51
+ ```
52
+
53
+ In _production_, the requests are sent to the real API:
54
+ ```
55
+ https://www.ethikdo.co/api/v1
56
+ ```
57
+
58
+
59
+ __Capture a paiement__
60
+
61
+ The first step to capture a paiement is to obtain a capture token. This is done through the `Ethikdo::Provision` class and requires the card's number (16 digits) and card's secret key (3 digits):
62
+
63
+ ```ruby
64
+ provision = Ethikdo::Provision.create(
65
+ card_number: card_number,
66
+ card_crypto: card_crypto
67
+ )
68
+ ```
69
+ If the card's credentials are correct, an `Ethikdo::Provision` object is returned:
70
+
71
+ ```ruby
72
+ # Ethikdo::Provision:0x0000555a80009698
73
+ @capture_token = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
74
+ @card_number = "1234123412341234",
75
+ @card_value = 500000,
76
+ @date_created = "2020-04-16T16:36:46.128007+02:00",
77
+ @date_used = nil,
78
+ @url = "https://recette.ethikdo.co/api/v1/provisions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/"
79
+ ```
80
+
81
+ Once you have obtained the capture token, you can now use the `Ethikdo::Transaction` class to capture the paiement:
82
+
83
+ ```ruby
84
+ transaction = Ethikdo::Transaction.create(
85
+ capture_token: capture_token,
86
+ amount_requested: order.total_price_cents,
87
+ amount_purchased: order.total_price_cents,
88
+ transaction_id: order.id,
89
+ customer_email: nil
90
+ )
91
+ ```
92
+
93
+ If the transaction succeeds, an `Ethikdo::Transaction` object is returned:
94
+
95
+ ```ruby
96
+ # Ethikdo::Transaction:0x0000555a82216f38
97
+ @amount_debited = 100000,
98
+ @amount_purchased = 100000,
99
+ @cancelled = false,
100
+ @card_number = "1234123412341234",
101
+ @date = "2020-04-16T15:25:56.451488Z",
102
+ @transaction_id = "1",
103
+ @url = "https://recette.ethikdo.co/api/v1/sales/1/" #/1 being the transaction id
104
+ ```
105
+
106
+ __Cancel a transaction__
107
+
108
+ You can cancel a transaction through the `Ethikdo::Transaction` class:
109
+
110
+ ```ruby
111
+ Ethikdo::Transaction.cancel(transaction_id: transaction_id)
112
+ ```
113
+
114
+ If the cancellation is successful, an `Ethikdo::Transaction` object is returned with a success message and the amount refunded:
115
+
116
+ ```ruby
117
+ # Ethikdo::Transaction:0x0000555a846ccb48
118
+ @message = "La vente a bien été annulée et la carte a été recréditée de 1000,00€.",
119
+ @refund_amount = 100000
120
+ ```
121
+
122
+ __List the provisions and transactions associated to your API key__
123
+
124
+ List all the provisions:
125
+ ```ruby
126
+ Ethikdo::Provision.all
127
+ ```
128
+
129
+ Example of an `Ethikdo::Provision` object returned:
130
+
131
+ ```ruby
132
+ # Ethikdo::Provision:0x0000555a82909390
133
+ @count = 1,
134
+ @next = nil,
135
+ @previous = nil,
136
+ @results =
137
+ [
138
+ {
139
+ "url"=>"https://recette.ethikdo.co/api/v1/provisions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
140
+ "capture_token"=>"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
141
+ "card_number"=>"1234123412341234",
142
+ "card_value"=>400000,
143
+ "date_created"=>"2020-04-16T16:36:46.128007+02:00",
144
+ "date_used"=>"2020-04-16T15:25:56.451488Z"
145
+ }
146
+ ]
147
+ ```
148
+
149
+ Similarly, list all the transactions:
150
+
151
+ ```ruby
152
+ Ethikdo::Transaction.all
153
+ ```
154
+
155
+ Example of an `Ethikdo::Transaction` object returned:
156
+
157
+ ```ruby
158
+ # Ethikdo::Transaction:0x0000555a835d68c0
159
+ @count = 1,
160
+ @next = nil,
161
+ @previous = nil,
162
+ @results =
163
+ [
164
+ {
165
+ "url"=>"https://recette.ethikdo.co/api/v1/sales/1/",
166
+ "transaction_id"=>"1",
167
+ "customer_email"=>nil,
168
+ "card_number"=>"1234123412341234",
169
+ "amount_purchased"=>100000,
170
+ "amount_debited"=>100000,
171
+ "cancelled"=>false,
172
+ "date"=>"2020-04-16T15:25:56.451488Z"
173
+ }
174
+ ]
175
+ ```
176
+
177
+ __Errors__
178
+
179
+ If a request returns an error (invalid card number, amount requested greater than card value, invalid capture token, etc.), an `Ethikdo::Error` is raised. This can be simply handled by a `begin/rescue` block:
180
+
181
+ ```ruby
182
+ begin
183
+ Ethikdo::transaction.cancel(transaction_id: 1)
184
+ rescue Ethikdo::Error => e
185
+ e.inspect
186
+ end
187
+ ```
188
+
189
+ ## Contributing
190
+
191
+ 1. Fork it
192
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
193
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
194
+ 4. Push to the branch (`git push origin my-new-feature`)
195
+ 5. Create new Pull Request
196
+
197
+ Don't forget to add tests and run rspec before creating a pull request :)
198
+
199
+
200
+ ## License
201
+
202
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ethikdo"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,31 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'ethikdo/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "ethikdo"
8
+ s.version = Ethikdo::VERSION
9
+ s.authors = ["Benoit Baumann", "Kevin Chavanne"]
10
+ s.email = ["baumann.benoit@gmail.com", "kevin.chavanne@gmail.com"]
11
+
12
+ s.summary = "A ruby wrapper for Ethikdo REST API "
13
+ s.description = "This gem provides an easy way to manage transactions for Ethi'kdo card holders through the Ethi'kdo REST API"
14
+ s.homepage = "https://github.com/wedressfair/ethikdo"
15
+ s.license = "MIT"
16
+ s.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
17
+
18
+ s.metadata["homepage_uri"] = s.homepage
19
+ s.metadata["source_code_uri"] = "https://github.com/wedressfair/ethikdo"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+
26
+ s.add_development_dependency "rspec", "~> 3.2"
27
+ s.add_development_dependency 'rake', '~> 12'
28
+ s.add_development_dependency 'webmock', '~> 3'
29
+ s.add_development_dependency 'generator_spec', '~> 0.9'
30
+ s.add_runtime_dependency 'httparty', '~> 0.16.2'
31
+ end
@@ -0,0 +1,22 @@
1
+ require 'ethikdo/configuration'
2
+ require 'ethikdo/provision'
3
+ require 'ethikdo/transaction'
4
+ require 'ethikdo/version'
5
+
6
+ module Ethikdo
7
+ class << self
8
+ attr_accessor :configuration
9
+ end
10
+
11
+ def self.configuration
12
+ @configuration ||= Configuration.new
13
+ end
14
+
15
+ def self.reset_configuration
16
+ @configuration = Configuration.new
17
+ end
18
+
19
+ def self.configure
20
+ yield(configuration)
21
+ end
22
+ end
@@ -0,0 +1,65 @@
1
+ require_relative 'error'
2
+ require 'httparty'
3
+
4
+ module Ethikdo
5
+ class BaseModel
6
+ def initialize(attributes = {})
7
+ attributes.each do |key, value|
8
+ m = "#{key}=".to_sym
9
+ send(m, value) if respond_to?(m)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ BASE_TEST_URL = "https://recette.ethikdo.co/api/v1"
16
+ BASE_URL = "https://www.ethikdo.co/api/v1"
17
+
18
+ def self.api_key
19
+ Ethikdo::configuration.api_key
20
+ end
21
+
22
+ def self.execute(method, path, options = {})
23
+ begin
24
+ response = request(method, path, options)
25
+ rescue *Error::NET_HTTP_ERRORS => err
26
+ raise ConnectionError.new, err.message
27
+ end
28
+
29
+ case response.code
30
+ when 200..299
31
+ response
32
+ when 400
33
+ raise BadRequestError.new(response)
34
+ when 401
35
+ raise AuthenticationError.new(response)
36
+ when 404
37
+ raise NotFoundError.new(response)
38
+ when 400..499
39
+ raise ResponseError.new(response)
40
+ when 500
41
+ raise InternalServerError.new(response)
42
+ when 500..599
43
+ raise ServerError.new(response)
44
+ end
45
+ end
46
+
47
+ def self.request(method, path, options = {})
48
+ HTTParty.send(method, base_url + path, base_options.merge(options))
49
+ end
50
+
51
+ def self.base_url
52
+ Ethikdo::configuration.environment == :production ? BASE_URL : BASE_TEST_URL
53
+ end
54
+
55
+ def self.base_options
56
+ {
57
+ format: :json,
58
+ headers: {
59
+ 'Accept' => 'application/json',
60
+ 'Authorization' => api_key
61
+ }
62
+ }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,16 @@
1
+ module Ethikdo
2
+ class Configuration
3
+ attr_accessor :api_key
4
+ attr_reader :environment
5
+
6
+ def initialize
7
+ @api_key = nil
8
+ @environment = :production
9
+ end
10
+
11
+ def environment=(env)
12
+ env = env.to_sym
13
+ @environment = [:production, :development].include?(env)? env : nil
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,84 @@
1
+ require 'net/http'
2
+
3
+ module Ethikdo
4
+ class Error < StandardError
5
+ NET_HTTP_ERRORS = [
6
+ EOFError,
7
+ Errno::ECONNABORTED,
8
+ Errno::ECONNREFUSED,
9
+ Errno::ECONNRESET,
10
+ Errno::EHOSTUNREACH,
11
+ Errno::EINVAL,
12
+ Errno::ENETUNREACH,
13
+ Net::HTTPBadResponse,
14
+ Net::HTTPHeaderSyntaxError,
15
+ Net::ProtocolError,
16
+ Net::OpenTimeout,
17
+ Net::ReadTimeout,
18
+ SocketError,
19
+ Zlib::GzipFile::Error,
20
+ OpenSSL::SSL::SSLError
21
+ ]
22
+
23
+ attr_reader :http_response
24
+
25
+ def initialize(response = nil)
26
+ @http_response = response
27
+ super(build_error_message)
28
+ end
29
+
30
+ def http_request
31
+ http_response.request
32
+ end
33
+
34
+ private
35
+
36
+ def build_error_message
37
+ return nil if http_response.nil?
38
+
39
+ message = "#{http_request_method} "
40
+ message << "#{http_request.path} : "
41
+ message << "#{http_response.code} - "
42
+ message << response_message unless response_message.nil?
43
+ message
44
+ end
45
+
46
+ def http_request_method
47
+ http_request.http_method.name.split('::').last.upcase
48
+ end
49
+
50
+ def response_message
51
+ content_type = http_response.headers['Content-Type']
52
+ if content_type && content_type.start_with?('application/json')
53
+ http_response.parsed_response['message'] || http_response.parsed_response['detail']
54
+ else
55
+ net_http_response = http_response.response
56
+ "#{net_http_response.code} #{net_http_response.message}"
57
+ end
58
+ end
59
+ end
60
+
61
+ # Raised on errors in the 400-499 range
62
+ class ResponseError < Error; end
63
+
64
+ # Raised when the API returns a 400 HTTP status code
65
+ class BadRequestError < ResponseError; end
66
+
67
+ # Raised when the API returns a 401 HTTP status code
68
+ class AuthenticationError < ResponseError; end
69
+
70
+ # Raised when the API returns a 402 HTTP status code
71
+ class PaymentRequiredError < ResponseError; end
72
+
73
+ # Raised when the API returns a 404 HTTP status code
74
+ class NotFoundError < ResponseError; end
75
+
76
+ # Raised on errors in the 500-599 range
77
+ class ServerError < Error; end
78
+
79
+ # Raised when the API returns a 500 HTTP status code
80
+ class InternalServerError < ServerError; end
81
+
82
+ # Raised when we can't establish a connection to the API or if reading the reponse times out
83
+ class ConnectionError < Error; end
84
+ end
@@ -0,0 +1,29 @@
1
+ require_relative 'base_model'
2
+
3
+ module Ethikdo
4
+ class Provision < BaseModel
5
+ attr_accessor :card_number
6
+ attr_accessor :capture_token
7
+ attr_accessor :card_value
8
+ attr_accessor :date_created
9
+ attr_accessor :date_used
10
+ attr_accessor :url
11
+ attr_accessor :status_code
12
+ attr_accessor :error_code
13
+ attr_accessor :message
14
+ attr_accessor :count
15
+ attr_accessor :next
16
+ attr_accessor :previous
17
+ attr_accessor :results
18
+
19
+ def self.create(card_number:, card_crypto:)
20
+ response = execute("post", '/provisions/', body: { card_number: card_number, card_crypto: card_crypto })
21
+ self.new(response.parsed_response)
22
+ end
23
+
24
+ def self.all
25
+ response = execute('get', '/provisions/')
26
+ self.new(response.parsed_response)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'base_model'
2
+
3
+ module Ethikdo
4
+ class Transaction < BaseModel
5
+ attr_accessor :url
6
+ attr_accessor :transaction_id
7
+ attr_accessor :card_number
8
+ attr_accessor :amount_purchased
9
+ attr_accessor :amount_debited
10
+ attr_accessor :cancelled
11
+ attr_accessor :date
12
+ attr_accessor :refund_amount
13
+ attr_accessor :message
14
+ attr_accessor :count
15
+ attr_accessor :next
16
+ attr_accessor :previous
17
+ attr_accessor :results
18
+ attr_accessor :customer_email
19
+
20
+ def self.create(capture_token:, amount_requested:, amount_purchased: 0, transaction_id:, customer_email: nil)
21
+ response = execute('post', '/sales/', body: {
22
+ capture_token: capture_token,
23
+ amount_requested: amount_requested,
24
+ amount_purchased: amount_purchased,
25
+ transaction_id: transaction_id,
26
+ customer_email: customer_email
27
+ })
28
+ self.new(response.parsed_response)
29
+ end
30
+
31
+ def self.cancel(transaction_id:)
32
+ response = execute('get', "/sales/#{transaction_id}/cancel")
33
+ self.new(response.parsed_response)
34
+ end
35
+
36
+ def self.all
37
+ response = execute('get', '/sales/')
38
+ self.new(response.parsed_response)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Ethikdo
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Ethikdo
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ desc "Creates an initializer file at config/initializers"
7
+ source_root File.expand_path('../templates', __dir__)
8
+
9
+ def create_initializer_file
10
+ copy_file "initializer.rb", "config/initializers/ethikdo.rb"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ Ethikdo.configure do |config|
2
+ config.api_key = ENV["ETHIKDO_API_KEY"]
3
+ config.environment = Rails.env.production? ? :production : :development
4
+ end
@@ -0,0 +1,34 @@
1
+ describe Ethikdo::BaseModel do
2
+ describe '.execute' do
3
+ it 'sends the request with the correct headers' do
4
+ stub_request(:get, /test/)
5
+ Ethikdo::configuration.api_key = '1234'
6
+
7
+ Ethikdo::BaseModel.execute('get', '/test/')
8
+
9
+ expect(WebMock).to have_requested(
10
+ :get,
11
+ 'https://www.ethikdo.co/api/v1/test/'
12
+ ).with(headers: {
13
+ 'Accept' => 'application/json',
14
+ 'Authorization' => '1234'
15
+ })
16
+ end
17
+
18
+ it 'raises a custom error when no api key is submitted' do
19
+ stub_request(:get, /test/).to_return(read_http_fixture('forbidden/no_token.http'))
20
+
21
+ expect do
22
+ Ethikdo::BaseModel.execute('get', '/test/')
23
+ end.to raise_error(Ethikdo::ResponseError)
24
+ end
25
+
26
+ it 'raises a custom when a wrong api key is submitted' do
27
+ stub_request(:get, /test/).to_return(read_http_fixture('forbidden/wrong_token.http'))
28
+
29
+ expect do
30
+ Ethikdo::BaseModel.execute('get', '/test/')
31
+ end.to raise_error(Ethikdo::ResponseError)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ describe Ethikdo do
2
+ after(:all) do
3
+ Ethikdo.reset_configuration
4
+ end
5
+
6
+ it "has a version number" do
7
+ expect(Ethikdo::VERSION).not_to be nil
8
+ end
9
+
10
+ describe Ethikdo::Configuration do
11
+ it 'has a configurable api key' do
12
+ Ethikdo.configuration.api_key = "42"
13
+ expect(Ethikdo.configuration.api_key).to eq("42")
14
+ end
15
+
16
+ it 'accepts only 2 values for its instance variable "environment": "production" and "development"' do
17
+ Ethikdo.configuration.environment = :development
18
+ expect(Ethikdo.configuration.environment).to eq(:development)
19
+
20
+ Ethikdo.configuration.environment = :foo
21
+ expect(Ethikdo.configuration.environment).to eq(nil)
22
+
23
+ Ethikdo.configuration.environment = :production
24
+ expect(Ethikdo.configuration.environment).to eq(:production)
25
+ end
26
+
27
+ it 'has different base url for the development and production environment' do
28
+ Ethikdo.configuration.environment = :development
29
+ expect(Ethikdo::BaseModel.base_url).to eq(Ethikdo::BaseModel::BASE_TEST_URL)
30
+
31
+ Ethikdo.configuration.environment = :production
32
+ expect(Ethikdo::BaseModel.base_url).to eq(Ethikdo::BaseModel::BASE_URL)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ HTTP 403 Forbidden
2
+ Allow: GET, POST, HEAD, OPTIONS
3
+ Content-Type: application/json
4
+ Vary: Accept
5
+
6
+ {
7
+ "detail": "Informations d'authentification non fournies."
8
+ }
@@ -0,0 +1,8 @@
1
+ HTTP 403 Forbidden
2
+ Allow: GET, POST, HEAD, OPTIONS
3
+ Content-Type: application/json
4
+ Vary: Accept
5
+
6
+ {
7
+ "detail": "Token non valide."
8
+ }
@@ -0,0 +1,11 @@
1
+ HTTP/1.1 200
2
+ Content-Type: application/json; charset=utf-8
3
+
4
+ {
5
+ "url": "https://recette.ethikdo.co/api/v1/provisions/2faac91c-ffec-4329-b851-8695020faeee/",
6
+ "capture_token": "2faac91c-ffec-4329-b851-8695020faeee",
7
+ "card_number": "1234123412341234",
8
+ "card_value": 500000,
9
+ "date_created": "2020-02-27T12:30:58.760332+01:00",
10
+ "date_used": null
11
+ }
@@ -0,0 +1,26 @@
1
+ HTTP/1.1 200
2
+ Content-Type: application/json; charset=utf-8
3
+
4
+ {
5
+ "count": 2,
6
+ "next": null,
7
+ "previous": null,
8
+ "results": [
9
+ {
10
+ "url": "https://recette.ethikdo.co/api/v1/provisions/2faac91c-ffec-4329-b851-8695020faeee/",
11
+ "capture_token": "2faac91c-ffec-4329-b851-8695020faeee",
12
+ "card_number": "1234123412341234",
13
+ "card_value": 500000,
14
+ "date_created": "2020-02-27T12:30:58.760332+01:00",
15
+ "date_used": null
16
+ },
17
+ {
18
+ "url": "https://recette.ethikdo.co/api/v1/provisions/f93f0deb-1bdf-4c40-a19f-8bacf831f529/",
19
+ "capture_token": "f93f0deb-1bdf-4c40-a19f-8bacf831f529",
20
+ "card_number": "1234123412341234",
21
+ "card_value": 496000,
22
+ "date_created": "2020-02-27T11:06:34.285773+01:00",
23
+ "date_used": null
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,7 @@
1
+ HTTP/1.1 200
2
+ Content-Type: application/json; charset=utf-8
3
+
4
+ {
5
+ "message": "La vente a bien été annulée et la carte a été recréditée de 10,00&nbsp;€.",
6
+ "refund_amount": 1000
7
+ }
@@ -0,0 +1,13 @@
1
+ HTTP/1.1 200
2
+ Content-Type: application/json; charset=utf-8
3
+
4
+ {
5
+ "url": "https://recette.ethikdo.co/api/v1/sales/1/",
6
+ "transaction_id": "1",
7
+ "customer_email": null,
8
+ "card_number": "1010101010101010",
9
+ "amount_purchased": 1000,
10
+ "amount_debited": 1000,
11
+ "cancelled": false,
12
+ "date": "2020-02-27T11:38:19.684349Z"
13
+ }
@@ -0,0 +1,40 @@
1
+ HTTP/1.1 200
2
+ Content-Type: application/json; charset=utf-8
3
+
4
+ {
5
+ "count": 3,
6
+ "next": null,
7
+ "previous": null,
8
+ "results": [
9
+ {
10
+ "url": "https://recette.ethikdo.co/api/v1/sales/113/",
11
+ "transaction_id": "3",
12
+ "customer_email": null,
13
+ "card_number": "1234123412341234",
14
+ "amount_purchased": 0,
15
+ "amount_debited": 2000,
16
+ "cancelled": false,
17
+ "date": "2020-02-27T09:47:24.711443Z"
18
+ },
19
+ {
20
+ "url": "https://recette.ethikdo.co/api/v1/sales/112/",
21
+ "transaction_id": "2",
22
+ "customer_email": null,
23
+ "card_number": "1234123412341234",
24
+ "amount_purchased": 0,
25
+ "amount_debited": 2000,
26
+ "cancelled": false,
27
+ "date": "2020-02-27T09:25:21.702039Z"
28
+ },
29
+ {
30
+ "url": "https://recette.ethikdo.co/api/v1/sales/111/",
31
+ "transaction_id": "1",
32
+ "customer_email": null,
33
+ "card_number": "1234123412341234",
34
+ "amount_purchased": 0,
35
+ "amount_debited": 2000,
36
+ "cancelled": true,
37
+ "date": "2020-02-27T09:18:40.351488Z"
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,16 @@
1
+ describe Ethikdo::Generators::InstallGenerator, type: :generator do
2
+ destination File.expand_path('../tmp', __FILE__)
3
+
4
+ before(:all) do
5
+ prepare_destination
6
+ run_generator
7
+ end
8
+
9
+ after(:all) do
10
+ FileUtils.rm_rf destination_root
11
+ end
12
+
13
+ it "creates correctly the initializer file" do
14
+ assert_file "config/initializers/ethikdo.rb"
15
+ end
16
+ end
@@ -0,0 +1,54 @@
1
+ describe Ethikdo::Provision do
2
+ describe '.all' do
3
+ before do
4
+ stub_request(:get, /provisions/).to_return(read_http_fixture('provisions/get_provisions_success.http'))
5
+ end
6
+
7
+ it 'builds the correct request' do
8
+ Ethikdo::Provision.all
9
+
10
+ expect(WebMock).to have_requested(
11
+ :get,
12
+ 'https://www.ethikdo.co/api/v1/provisions/'
13
+ )
14
+ end
15
+
16
+ it 'returns the correct response' do
17
+ provisions = Ethikdo::Provision.all
18
+
19
+ expect(provisions).to be_a(Ethikdo::Provision)
20
+ expect(provisions.count).to eq(2)
21
+ expect(provisions.results[0]['card_number']).to eq('1234123412341234')
22
+ end
23
+ end
24
+
25
+ describe '.create' do
26
+ before do
27
+ Ethikdo::configuration.api_key = '1234'
28
+ stub_request(:post, /provisions/).to_return(read_http_fixture('provisions/create_success.http'))
29
+ end
30
+
31
+ it 'builds the correct request' do
32
+ Ethikdo::Provision.create(card_number: 1234123412341234, card_crypto: 123)
33
+
34
+ expect(WebMock).to have_requested(
35
+ :post,
36
+ 'https://www.ethikdo.co/api/v1/provisions/'
37
+ ).with(headers: {
38
+ 'Accept' => 'application/json',
39
+ 'Authorization' => '1234',
40
+ }, body: {
41
+ 'card_number' => '1234123412341234',
42
+ 'card_crypto' => '123'
43
+ })
44
+ end
45
+
46
+ it 'returns the correct response' do
47
+ provision = Ethikdo::Provision.create(card_number: 1234123412341234, card_crypto: 123)
48
+
49
+ expect(provision).to be_a(Ethikdo::Provision)
50
+ expect(provision.card_number).to eq('1234123412341234')
51
+ expect(provision.card_value).to eq(500000)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ lib = File.expand_path("../../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require "bundler/setup"
5
+ require "ethikdo"
6
+ require "generator_spec"
7
+ require 'generators/ethikdo/install_generator'
8
+
9
+ RSpec.configure do |config|
10
+ config.example_status_persistence_file_path = ".rspec_status"
11
+
12
+ config.expect_with :rspec do |expectations|
13
+ expectations.syntax = :expect
14
+ end
15
+
16
+ config.mock_with :rspec do |mocks|
17
+ mocks.syntax = :expect
18
+ end
19
+ end
20
+
21
+ unless defined?(SPEC_ROOT)
22
+ SPEC_ROOT = File.expand_path('../', __FILE__)
23
+ end
24
+
25
+ Dir[File.join(SPEC_ROOT, 'support/**/*.rb')].each { |f| require f }
@@ -0,0 +1,15 @@
1
+ module RSpecSupportHelpers
2
+
3
+ def http_fixture(*names)
4
+ File.join(SPEC_ROOT, 'http_fixtures', *names)
5
+ end
6
+
7
+ def read_http_fixture(*names)
8
+ File.read(http_fixture(*names))
9
+ end
10
+
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.include RSpecSupportHelpers
15
+ end
@@ -0,0 +1,11 @@
1
+ require 'webmock/rspec'
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:suite) do
5
+ WebMock.disable_net_connect!
6
+ end
7
+
8
+ config.after(:suite) do
9
+ WebMock.allow_net_connect!
10
+ end
11
+ end
@@ -0,0 +1,84 @@
1
+ describe Ethikdo::Transaction do
2
+ describe '.all' do
3
+ before do
4
+ stub_request(:get, /sales/).to_return(read_http_fixture('transactions/get_transactions_success.http'))
5
+ end
6
+
7
+ it 'builds the correct request' do
8
+ Ethikdo::Transaction.all
9
+
10
+ expect(WebMock).to have_requested(
11
+ :get,
12
+ 'https://www.ethikdo.co/api/v1/sales/'
13
+ )
14
+ end
15
+
16
+ it 'returns the correct response' do
17
+ transactions = Ethikdo::Transaction.all
18
+
19
+ expect(transactions).to be_a(Ethikdo::Transaction)
20
+ expect(transactions.count).to eq(3)
21
+ expect(transactions.results[0]['card_number']).to eq('1234123412341234')
22
+ end
23
+ end
24
+
25
+ describe '.create' do
26
+ before do
27
+ Ethikdo::configuration.api_key = '1234'
28
+ stub_request(:post, /sales/).to_return(read_http_fixture('transactions/create_success.http'))
29
+ end
30
+
31
+ it 'builds the correct request' do
32
+ Ethikdo::Transaction.create(capture_token: 'foo', amount_requested: 10000, amount_purchased: 10000, transaction_id: 1, customer_email: 'foo@bar.com')
33
+
34
+ expect(WebMock).to have_requested(
35
+ :post,
36
+ 'https://www.ethikdo.co/api/v1/sales/'
37
+ ).with(headers: {
38
+ 'Accept' => 'application/json',
39
+ 'Authorization' => '1234',
40
+ }, body: {
41
+ 'capture_token' => 'foo',
42
+ 'amount_requested' => '10000',
43
+ 'amount_purchased' => '10000',
44
+ 'transaction_id' => '1',
45
+ 'customer_email' => 'foo@bar.com'
46
+ })
47
+ end
48
+
49
+ it 'returns the correct response' do
50
+ transaction = Ethikdo::Transaction.create(capture_token: 'foo', amount_requested: 10000, amount_purchased: 10000, transaction_id: 1)
51
+
52
+ expect(transaction).to be_a(Ethikdo::Transaction)
53
+ expect(transaction.transaction_id).to eq('1')
54
+ expect(transaction.amount_purchased).to eq(1000)
55
+ end
56
+ end
57
+
58
+ describe '.cancel' do
59
+ before do
60
+ Ethikdo::configuration.api_key = '1234'
61
+ stub_request(:get, /sales/).to_return(read_http_fixture('transactions/cancel_success.http'))
62
+ end
63
+
64
+ it 'builds the correct request' do
65
+ Ethikdo::Transaction.cancel(transaction_id: 1)
66
+
67
+ expect(WebMock).to have_requested(
68
+ :get,
69
+ 'https://www.ethikdo.co/api/v1/sales/1/cancel'
70
+ ).with(headers: {
71
+ 'Accept' => 'application/json',
72
+ 'Authorization' => '1234',
73
+ })
74
+ end
75
+
76
+ it 'returns the correct response' do
77
+ transaction = Ethikdo::Transaction.cancel(transaction_id: 1)
78
+
79
+ expect(transaction).to be_a(Ethikdo::Transaction)
80
+ expect(transaction.message).to eq('La vente a bien été annulée et la carte a été recréditée de 10,00&nbsp;€.')
81
+ expect(transaction.refund_amount).to eq(1000)
82
+ end
83
+ end
84
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ethikdo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Benoit Baumann
8
+ - Kevin Chavanne
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-04-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '3.2'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '3.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '12'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '12'
42
+ - !ruby/object:Gem::Dependency
43
+ name: webmock
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3'
56
+ - !ruby/object:Gem::Dependency
57
+ name: generator_spec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.9'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.9'
70
+ - !ruby/object:Gem::Dependency
71
+ name: httparty
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 0.16.2
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 0.16.2
84
+ description: This gem provides an easy way to manage transactions for Ethi'kdo card
85
+ holders through the Ethi'kdo REST API
86
+ email:
87
+ - baumann.benoit@gmail.com
88
+ - kevin.chavanne@gmail.com
89
+ executables:
90
+ - console
91
+ - setup
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - ".gitignore"
96
+ - ".rspec"
97
+ - ".travis.yml"
98
+ - Gemfile
99
+ - Gemfile.lock
100
+ - LICENSE.txt
101
+ - README.md
102
+ - Rakefile
103
+ - bin/console
104
+ - bin/setup
105
+ - ethikdo.gemspec
106
+ - lib/ethikdo.rb
107
+ - lib/ethikdo/base_model.rb
108
+ - lib/ethikdo/configuration.rb
109
+ - lib/ethikdo/error.rb
110
+ - lib/ethikdo/provision.rb
111
+ - lib/ethikdo/transaction.rb
112
+ - lib/ethikdo/version.rb
113
+ - lib/generators/ethikdo/install_generator.rb
114
+ - lib/generators/templates/initializer.rb
115
+ - spec/base_model_spec.rb
116
+ - spec/ethikdo_spec.rb
117
+ - spec/http_fixtures/forbidden/no_token.http
118
+ - spec/http_fixtures/forbidden/wrong_token.http
119
+ - spec/http_fixtures/provisions/create_success.http
120
+ - spec/http_fixtures/provisions/get_provisions_success.http
121
+ - spec/http_fixtures/transactions/cancel_success.http
122
+ - spec/http_fixtures/transactions/create_success.http
123
+ - spec/http_fixtures/transactions/get_transactions_success.http
124
+ - spec/install_generator_spec.rb
125
+ - spec/provision_spec.rb
126
+ - spec/spec_helper.rb
127
+ - spec/support/helpers.rb
128
+ - spec/support/webmock.rb
129
+ - spec/transaction_spec.rb
130
+ homepage: https://github.com/wedressfair/ethikdo
131
+ licenses:
132
+ - MIT
133
+ metadata:
134
+ homepage_uri: https://github.com/wedressfair/ethikdo
135
+ source_code_uri: https://github.com/wedressfair/ethikdo
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: 2.3.0
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubygems_version: 3.1.2
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: A ruby wrapper for Ethikdo REST API
155
+ test_files: []