monri 0.1.0 → 0.2.0

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -2
  3. data/LICENCE +21 -0
  4. data/README.md +90 -1
  5. data/{bin → exe}/byebug +1 -1
  6. data/{bin → exe}/coderay +1 -1
  7. data/{bin → exe}/m +0 -0
  8. data/{bin → exe}/pry +0 -0
  9. data/{bin → exe}/rake +1 -1
  10. data/{bin → exe}/rubocop +0 -0
  11. data/{bin → exe}/ruby-parse +0 -0
  12. data/{bin → exe}/ruby-rewrite +0 -0
  13. data/extensions/x86_64-darwin-19/2.6.0/byebug-11.1.3/byebug/byebug.bundle +0 -0
  14. data/extensions/x86_64-darwin-19/2.6.0/byebug-11.1.3/gem_make.out +3 -2
  15. data/lib/monri/access_tokens/create_response.rb +37 -0
  16. data/lib/monri/access_tokens.rb +29 -12
  17. data/lib/monri/client.rb +49 -4
  18. data/lib/monri/customers/create_response.rb +97 -0
  19. data/lib/monri/customers.rb +11 -9
  20. data/lib/monri/errors.rb +7 -1
  21. data/lib/monri/http_client.rb +22 -11
  22. data/lib/monri/payment_methods.rb +2 -2
  23. data/lib/monri/payments/create_response.rb +36 -0
  24. data/lib/monri/payments/payment_result.rb +52 -0
  25. data/lib/monri/payments/status_response.rb +45 -0
  26. data/lib/monri/payments.rb +11 -8
  27. data/lib/monri/response.rb +13 -24
  28. data/lib/monri/tokens/ephemeral_card_token_response.rb +46 -0
  29. data/lib/monri/tokens.rb +68 -0
  30. data/lib/monri/transactions/secure_message.rb +30 -0
  31. data/lib/monri/transactions/transaction.rb +131 -0
  32. data/lib/monri/transactions/transaction_response.rb +59 -0
  33. data/lib/monri/transactions.rb +58 -0
  34. data/lib/monri/validate_callback.rb +47 -0
  35. data/lib/monri.rb +13 -1
  36. data/monri.gemspec +1 -2
  37. metadata +24 -47
  38. data/lib/monri/transaction.rb +0 -14
  39. data/specifications/addressable-2.8.1.gemspec +0 -40
  40. data/specifications/ast-2.4.2.gemspec +0 -50
  41. data/specifications/byebug-11.1.3.gemspec +0 -39
  42. data/specifications/coderay-1.1.3.gemspec +0 -25
  43. data/specifications/crack-0.4.5.gemspec +0 -32
  44. data/specifications/hashdiff-1.0.1.gemspec +0 -46
  45. data/specifications/jaro_winkler-1.5.4.gemspec +0 -44
  46. data/specifications/m-1.6.0.gemspec +0 -49
  47. data/specifications/metaclass-0.0.4.gemspec +0 -19
  48. data/specifications/method_source-1.0.0.gemspec +0 -35
  49. data/specifications/minitest-5.16.2.gemspec +0 -41
  50. data/specifications/mocha-0.13.3.gemspec +0 -43
  51. data/specifications/parallel-1.22.1.gemspec +0 -21
  52. data/specifications/parser-3.1.2.1.gemspec +0 -63
  53. data/specifications/power_assert-2.0.2.gemspec +0 -54
  54. data/specifications/pry-0.14.1.gemspec +0 -39
  55. data/specifications/pry-byebug-3.8.0.gemspec +0 -38
  56. data/specifications/public_suffix-5.0.0.gemspec +0 -24
  57. data/specifications/rack-3.0.0.gemspec +0 -45
  58. data/specifications/rainbow-3.1.1.gemspec +0 -33
  59. data/specifications/rake-13.0.6.gemspec +0 -26
  60. data/specifications/rexml-3.2.5.gemspec +0 -42
  61. data/specifications/rubocop-0.80.0.gemspec +0 -59
  62. data/specifications/ruby-progressbar-1.11.0.gemspec +0 -43
  63. data/specifications/test-unit-3.5.5.gemspec +0 -48
  64. data/specifications/unicode-display_width-1.6.1.gemspec +0 -39
  65. data/specifications/webmock-3.18.1.gemspec +0 -85
@@ -0,0 +1,45 @@
1
+ module Monri
2
+ class Payments
3
+ class StatusResponse < Response
4
+
5
+ # @param [Hash] params
6
+ def initialize(params)
7
+ if params.has_key?(:payment_result)
8
+ self[:payment_result] = PaymentResult.new(params.delete(:payment_result))
9
+ end
10
+ self.merge!(params)
11
+ end
12
+
13
+ # @return [String]
14
+ def status
15
+ self[:status]
16
+ end
17
+
18
+ # @return [String]
19
+ def payment_status
20
+ self[:payment_status]
21
+ end
22
+
23
+ # @return [String]
24
+ def client_secret
25
+ self[:client_secret]
26
+ end
27
+
28
+ # @return [Monri::Payments::PaymentResult]
29
+ def payment_result
30
+ self[:payment_result]
31
+ end
32
+
33
+ # @return [StatusResponse]
34
+ def self.create
35
+ raise ArgumentError, 'Provide a block' unless block_given?
36
+
37
+ begin
38
+ StatusResponse.new(yield)
39
+ rescue StandardError => e
40
+ StatusResponse.new(exception: e)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -11,10 +11,11 @@ module Monri
11
11
  attr_writer :access_tokens
12
12
 
13
13
  # @param [Hash] options
14
+ # @return [Monri::Payments::CreateResponse]
14
15
  def create(options)
15
- Response.create do
16
- access_token = @access_tokens.create(scopes: ['payments'])[:access_token]
17
- response = @http_client.post('/v2/payment/new', options, headers: { 'Authorization' => "Bearer #{access_token}" })
16
+ CreateResponse.create do
17
+ access_token = @access_tokens.create!(scopes: ['payments']).access_token
18
+ response = @http_client.post('/v2/payment/new', options, oauth: access_token)
18
19
  if response.failed?
19
20
  raise response.exception
20
21
  elsif response.success?
@@ -26,16 +27,18 @@ module Monri
26
27
  end
27
28
 
28
29
  # @param [String] id
29
- # @return [Response] id
30
+ # @return [StatusResponse] id
30
31
  def status(id)
31
- Response.create do
32
+ StatusResponse.create do
32
33
  if id.nil? || !id.is_a?(String)
33
34
  raise ArgumentError('Id should be a string')
34
35
  end
35
36
 
36
- access_token = @access_tokens.create(scopes: ['payments'])[:access_token]
37
- response = @http_client.post("/v2/payment/#{id}/status", options, headers: { 'Authorization' => "Bearer #{access_token}" })
38
- if response.success?
37
+ access_token = @access_tokens.create!(scopes: ['payments']).access_token
38
+ response = @http_client.get("/v2/payment/#{id}/status", oauth: access_token)
39
+ if response.failed?
40
+ raise response.exception
41
+ elsif response.success?
39
42
  response.body
40
43
  else
41
44
  # TODO: handle this case
@@ -1,40 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Monri
4
- class Response
5
- # @return [Object]
6
- attr_accessor :result
7
- # @return [Exception]
8
- attr_accessor :exception
4
+ class Response < Hash
9
5
 
10
- # @return [Monri::Response]
11
- # @param [Object] result
12
- def self.result(result)
13
- raise ArgumentError, 'Argument result is nil' if result.nil?
6
+ def initialize(params = {})
7
+ if params.has_key?(:exception)
8
+ self[:exception] = params[:exception]
9
+ end
10
+ self.merge!(params)
11
+ end
14
12
 
15
- rv = Response.new
16
- rv.result = result
17
- rv
13
+ # @return [Exception, NilClass]
14
+ def exception
15
+ self[:exception]
18
16
  end
19
17
 
20
- def success?
21
- exception == nil
18
+ # @param [Exception] val
19
+ def exception=(val)
20
+ self[:exception] = val
22
21
  end
23
22
 
24
23
  def failed?
25
24
  exception != nil
26
25
  end
27
26
 
28
- def self.create
29
- raise ArgumentError, 'Provide a block' unless block_given?
30
-
31
- begin
32
- result(yield)
33
- rescue StandardError => e
34
- exception(e)
35
- end
36
- end
37
-
38
27
  # @param [Exception] exception
39
28
  def self.exception(exception)
40
29
  rv = Response.new
@@ -0,0 +1,46 @@
1
+ module Monri
2
+ class Tokens
3
+ class EphemeralCardTokenResponse < Response
4
+
5
+ def approved?
6
+ status == 'approved'
7
+ end
8
+
9
+ # @return [String]
10
+ def id
11
+ self[:id]
12
+ end
13
+
14
+ # @return [String]
15
+ def status
16
+ self[:status]
17
+ end
18
+
19
+ # @return [String]
20
+ def masked_pan
21
+ self[:masked_pan]
22
+ end
23
+
24
+ # @return [String]
25
+ def cc_type
26
+ self[:cc_type]
27
+ end
28
+
29
+ # @return [String]
30
+ def cc_issuer
31
+ self[:cc_issuer]
32
+ end
33
+
34
+ # @return [EphemeralCardTokenResponse]
35
+ def self.create
36
+ raise ArgumentError, 'Provide a block' unless block_given?
37
+
38
+ begin
39
+ EphemeralCardTokenResponse.new(yield)
40
+ rescue StandardError => e
41
+ EphemeralCardTokenResponse.new(exception: e)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,68 @@
1
+ module Monri
2
+ class Tokens
3
+
4
+ # @return [Monri::Config]
5
+ attr_accessor :config
6
+
7
+ # @return [Monri::HttpClient]
8
+ attr_accessor :http_client
9
+
10
+ TEMP_TOKENIZE_REQUIRED_FIELDS = [:cvv, :type, :pan, :expiration_date, :temp_card_id, :digest, :timestamp]
11
+
12
+ # @note Create ephemeral (lasting for a very short time - 15minutes in this case) - token to replace card details.
13
+ # Required fields are: pan, expiration_date(YYMM), cvv, type (card). Optional fields are: tokenize_pan: bool
14
+ # @param [Hash] params
15
+ def create_ephemeral_card_token(params)
16
+
17
+ EphemeralCardTokenResponse.create do
18
+ ensure_configured!
19
+
20
+ # If there's no temp-card-id, create one
21
+ unless params.has_key?(:temp_card_id)
22
+ params.merge!(create_card_token)
23
+ end
24
+
25
+ missing_keys = TEMP_TOKENIZE_REQUIRED_FIELDS.reject { |k| params.has_key?(k) }
26
+
27
+ params[:authenticity_token] = config.authenticity_token
28
+
29
+ if missing_keys.length > 0
30
+ raise Monri::Errors::InvalidArgumentsError.new("Missing required keys=#{missing_keys.join(', ')}")
31
+ end
32
+
33
+ # TODO: assert if merchant has PCI-DSS certificate - request pci-dss access token
34
+ http_response = http_client.post('/v2/temp-tokenize', params)
35
+ if http_response.failed?
36
+ raise http_response.exception
37
+ elsif http_response.success?
38
+ http_response.body
39
+ else
40
+ # TODO: handle this case
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def ensure_configured!
48
+ if config == nil || http_client == nil
49
+ raise Monri::Errors::InvalidArgumentsError.new('Configuration error, config or http client not set')
50
+ end
51
+ end
52
+
53
+ # @param [Hash] params
54
+ # @return [Hash]
55
+ def create_card_token(params = {})
56
+ ensure_configured!
57
+
58
+ token = params.has_key?(:temp_card_id) ? params.delete(:temp_card_id) : SecureRandom.hex
59
+ timestamp = Time.now.iso8601
60
+ {
61
+ timestamp: timestamp,
62
+ temp_card_id: token,
63
+ digest: Digest::SHA512.hexdigest("#{config.merchant_key}#{token}#{timestamp}")
64
+ }
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,30 @@
1
+ module Monri
2
+ class Transactions
3
+ class SecureMessage < Hash
4
+ # @param [Hash] params
5
+ def initialize(params)
6
+ self.merge!(params)
7
+ end
8
+
9
+ # @return [String]
10
+ def id
11
+ self[:id]
12
+ end
13
+
14
+ # @return [String]
15
+ def acs_url
16
+ self[:acs_url]
17
+ end
18
+
19
+ # @return [String]
20
+ def pareq
21
+ self[:pareq]
22
+ end
23
+
24
+ # @return [String]
25
+ def authenticity_token
26
+ self[:authenticity_token]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,131 @@
1
+ module Monri
2
+ class Transactions
3
+ class Transaction < Hash
4
+
5
+ # @param [Hash] params
6
+ def initialize(params)
7
+ self.merge!(params)
8
+ end
9
+
10
+ # @return [String]
11
+ def id
12
+ self[:id]
13
+ end
14
+
15
+ # @return [String]
16
+ def acquirer
17
+ self[:acquirer]
18
+ end
19
+
20
+ # @return [String]
21
+ def order_number
22
+ self[:order_number]
23
+ end
24
+
25
+ # @return [String]
26
+ def amount
27
+ self[:amount]
28
+ end
29
+
30
+ # @return [String]
31
+ def currency
32
+ self[:currency]
33
+ end
34
+
35
+ # @return [String]
36
+ def outgoing_amount
37
+ self[:outgoing_amount]
38
+ end
39
+
40
+ # @return [String]
41
+ def outgoing_currency
42
+ self[:outgoing_currency]
43
+ end
44
+
45
+ # @return [String]
46
+ def approval_code
47
+ self[:approval_code]
48
+ end
49
+
50
+ # @return [String]
51
+ def response_code
52
+ self[:response_code]
53
+ end
54
+
55
+ # @return [String]
56
+ def response_message
57
+ self[:response_message]
58
+ end
59
+
60
+ # @return [String]
61
+ def reference_number
62
+ self[:reference_number]
63
+ end
64
+
65
+ # @return [String]
66
+ def systan
67
+ self[:systan]
68
+ end
69
+
70
+ # @return [String]
71
+ def eci
72
+ self[:eci]
73
+ end
74
+
75
+ # @return [String]
76
+ def xid
77
+ self[:xid]
78
+ end
79
+
80
+ # @return [String]
81
+ def acsv
82
+ self[:acsv]
83
+ end
84
+
85
+ # @return [String]
86
+ def cc_type
87
+ self[:cc_type]
88
+ end
89
+
90
+ # @return [String]
91
+ def status
92
+ self[:status]
93
+ end
94
+
95
+ # @return [String]
96
+ def created_at
97
+ self[:created_at]
98
+ end
99
+
100
+ # @return [String]
101
+ def transaction_type
102
+ self[:transaction_type]
103
+ end
104
+
105
+ # @return [String]
106
+ def enrollment
107
+ self[:enrollment]
108
+ end
109
+
110
+ # @return [String]
111
+ def authentication
112
+ self[:authentication]
113
+ end
114
+
115
+ # @return [String]
116
+ def pan_token
117
+ self[:pan_token]
118
+ end
119
+
120
+ # @return [String]
121
+ def issuer
122
+ self[:issuer]
123
+ end
124
+
125
+ # @return [String]
126
+ def three_ds_version
127
+ self[:three_ds_version]
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,59 @@
1
+ module Monri
2
+ class Transactions
3
+ class TransactionResponse < Response
4
+
5
+ # @param [Hash] params
6
+ def initialize(params)
7
+ if params.has_key?(:errors)
8
+ self[:errors] = params.delete(:errors)
9
+ end
10
+
11
+ if params.has_key?(:transaction)
12
+ self[:transaction] = Monri::Transactions::Transaction.new(params.delete(:transaction))
13
+ end
14
+
15
+ if params.has_key?(:secure_message)
16
+ self[:secure_message] = Monri::Transactions::SecureMessage.new(params.delete(:secure_message))
17
+ end
18
+ super(params)
19
+ end
20
+
21
+ # @return [TrueClass, FalseClass]
22
+ def failed?
23
+ errors != nil && errors.length > 0 || super
24
+ end
25
+
26
+ # @return [Array]
27
+ def errors
28
+ self[:errors]
29
+ end
30
+
31
+ # @return [Monri::Transactions::TransactionResponse]
32
+ def transaction
33
+ self[:transaction]
34
+ end
35
+
36
+ # @return [Monri::Transactions::SecureMessage]
37
+ def secure_message
38
+ self[:secure_message]
39
+ end
40
+
41
+ # @return [TransactionResponse]
42
+ def self.create
43
+ raise ArgumentError, 'Provide a block' unless block_given?
44
+
45
+ begin
46
+ TransactionResponse.new(yield)
47
+ rescue StandardError => e
48
+ params = { exception: e }
49
+ if e.is_a?(Monri::Errors::HttpRequestError) && e.body != nil
50
+ body = JSON.parse(e.body, symbolize_names: true) rescue {}
51
+ params.merge!(body)
52
+ end
53
+ TransactionResponse.new(params)
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ module Monri
2
+ class Transactions
3
+
4
+ # @return [Monri::Config]
5
+ attr_writer :config
6
+
7
+ # @return [Monri::HttpClient]
8
+ attr_writer :http_client
9
+
10
+ REQUIRED_FIELDS = [
11
+ :transaction_type,
12
+ :amount,
13
+ :ip,
14
+ :order_info,
15
+ :ch_address,
16
+ :ch_city,
17
+ :ch_country,
18
+ :ch_email,
19
+ :ch_full_name,
20
+ :ch_phone,
21
+ :ch_zip,
22
+ :currency,
23
+ :order_number,
24
+ :language
25
+ ].freeze
26
+
27
+ # @param [Hash] params
28
+ # @return [Monri::Transactions::TransactionResponse]
29
+ def transaction(params)
30
+ TransactionResponse.create do
31
+ unless params.is_a?(Hash)
32
+ raise Monri::Errors::InvalidArgumentsError.new('First parameter - params, should be a Hash')
33
+ end
34
+
35
+ missing_keys = REQUIRED_FIELDS.reject { |k| params.has_key?(k) }
36
+ if missing_keys.length > 0
37
+ raise Monri::Errors::InvalidArgumentsError.new("Missing required keys=#{missing_keys.join(', ')}")
38
+ end
39
+
40
+ params[:authenticity_token] = @config.authenticity_token
41
+ digest_parts = [@config.merchant_key, params[:order_number], params[:amount], params[:currency]]
42
+ params[:digest] = Digest::SHA512.hexdigest(digest_parts.join)
43
+
44
+ req = { transaction: params }
45
+
46
+ rv = @http_client.post('/v2/transaction', req)
47
+ if rv.failed?
48
+ raise rv.exception
49
+ elsif rv.success?
50
+ rv.body
51
+ else
52
+ # TODO: how to handle this case
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ module Monri
2
+ class ValidateCallback
3
+
4
+ # @return [Monri::Config]
5
+ attr_accessor :config
6
+
7
+ # @param [String] header
8
+ # @param [String] body
9
+ # @param [Hash] options
10
+ # @return [Hash{Symbol->String | TrueClass | FalseClass}]
11
+ def validate(header, body, options = {})
12
+
13
+ unless header.is_a?(String)
14
+ raise Monri::Errors::InvalidArgumentsError.new('First parameter - authorization header, should be a String')
15
+ end
16
+
17
+ unless body.is_a?(String)
18
+ raise Monri::Errors::InvalidArgumentsError.new('Second parameter - body, should be a String')
19
+ end
20
+
21
+ unless options.is_a?(Hash)
22
+ raise Monri::Errors::InvalidArgumentsError.new('Third parameter - options, should be a Hash')
23
+ end
24
+
25
+ version = options.delete(:version) || '2'
26
+
27
+ if version == 'v2'
28
+ expected_digest = Digest::SHA512.hexdigest("#{config.merchant_key}#{body}")
29
+ elsif version == '1'
30
+ unless options.has_key?(:order_number)
31
+ raise Monri::Errors::InvalidArgumentsError.new('For version=1 provide order-number')
32
+ end
33
+ order_number = options.delete(:order_number)
34
+ expected_digest = Digest::SHA1.hexdigest("#{config.merchant_key}#{order_number}")
35
+ else
36
+ raise Monri::Errors::InvalidArgumentsError.new("Version option, value='#{version}' is not supported")
37
+ end
38
+
39
+ expected_header = "WP3-callback #{expected_digest}"
40
+ {
41
+ header: header,
42
+ expected_header: expected_header,
43
+ valid: header == expected_header
44
+ }
45
+ end
46
+ end
47
+ end
data/lib/monri.rb CHANGED
@@ -11,13 +11,25 @@ require 'monri/config'
11
11
  require 'monri/client'
12
12
  require 'monri/errors'
13
13
  require 'monri/http_client'
14
- require 'monri/transaction'
15
14
  require 'monri/payments'
16
15
  require 'monri/payment_methods'
17
16
  require 'monri/access_tokens'
18
17
  require 'monri/customers'
19
18
  require 'monri/response'
20
19
  require 'monri/api_http_response'
20
+ require 'monri/validate_callback'
21
+ require 'monri/tokens'
22
+ require 'monri/transactions'
23
+ require 'monri/transactions/transaction'
24
+ require 'monri/transactions/transaction_response'
25
+ require 'monri/transactions/secure_message'
26
+ require 'monri/payments/create_response'
27
+ require 'monri/payments/status_response'
28
+ require 'monri/payments/payment_result'
29
+ require 'monri/tokens/ephemeral_card_token_response'
30
+ require 'monri/customers/create_response'
31
+ require 'monri/access_tokens/create_response'
32
+ require 'time'
21
33
 
22
34
  module Monri
23
35
  end
data/monri.gemspec CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'monri'
5
- # TODO: add version directly to the code
6
- s.version = '0.1.0'
5
+ s.version = '0.2.0'
7
6
  s.required_ruby_version = '>= 2.3.0'
8
7
  s.summary = 'Ruby bindings for the Monri API'
9
8
  s.description = 'Your voyage through payment experience starts here. Learn more about how Monri helps to power your business.' \