portmone 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 22c67a332abd19218c0bbb9da2cbd51656e1f94e
4
+ data.tar.gz: db22c1a1914b670f641369c4754259af5a8b297b
5
+ SHA512:
6
+ metadata.gz: 91bb922cbcdb68f136707e5a125b8dd7c3428de9f580f022f3b20f9be1f841a5cb1a80fa69ee90a997328cfd93c65fc7332d2a57b0520e565db42bcecd532eeb
7
+ data.tar.gz: 6e6952470a8fb6b6c861106224bd770143d30540f6c8627185006bbe81bacbfbd92aec7191147c5bb2ab52f3716995a9125ae0f8c3b26b9aef4646cfb7128678
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,88 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ portmone (0.0.1)
5
+ activesupport
6
+ faraday (~> 0.9)
7
+ money (~> 6.0)
8
+ multi_xml (~> 0.5)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activesupport (5.1.3)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (~> 0.7)
16
+ minitest (~> 5.1)
17
+ tzinfo (~> 1.1)
18
+ addressable (2.5.2)
19
+ public_suffix (>= 2.0.2, < 4.0)
20
+ ast (2.3.0)
21
+ coderay (1.1.2)
22
+ concurrent-ruby (1.0.5)
23
+ crack (0.4.3)
24
+ safe_yaml (~> 1.0.0)
25
+ docile (1.1.5)
26
+ faraday (0.13.1)
27
+ multipart-post (>= 1.2, < 3)
28
+ hashdiff (0.3.7)
29
+ i18n (0.9.1)
30
+ concurrent-ruby (~> 1.0)
31
+ json (2.1.0)
32
+ method_source (0.9.0)
33
+ minitest (5.11.3)
34
+ money (6.10.0)
35
+ i18n (>= 0.6.4, < 1.0)
36
+ multi_xml (0.6.0)
37
+ multipart-post (2.0.0)
38
+ parallel (1.12.0)
39
+ parser (2.4.0.0)
40
+ ast (~> 2.2)
41
+ powerpack (0.1.1)
42
+ pry (0.11.2)
43
+ coderay (~> 1.1.0)
44
+ method_source (~> 0.9.0)
45
+ public_suffix (3.0.0)
46
+ rainbow (2.2.2)
47
+ rake
48
+ rake (10.5.0)
49
+ rubocop (0.51.0)
50
+ parallel (~> 1.10)
51
+ parser (>= 2.3.3.1, < 3.0)
52
+ powerpack (~> 0.1)
53
+ rainbow (>= 2.2.2, < 3.0)
54
+ ruby-progressbar (~> 1.7)
55
+ unicode-display_width (~> 1.0, >= 1.0.1)
56
+ ruby-progressbar (1.9.0)
57
+ safe_yaml (1.0.4)
58
+ simplecov (0.15.1)
59
+ docile (~> 1.1.0)
60
+ json (>= 1.8, < 3)
61
+ simplecov-html (~> 0.10.0)
62
+ simplecov-html (0.10.2)
63
+ thread_safe (0.3.6)
64
+ tzinfo (1.2.4)
65
+ thread_safe (~> 0.1)
66
+ unicode-display_width (1.3.0)
67
+ vcr (2.9.3)
68
+ webmock (3.1.0)
69
+ addressable (>= 2.3.6)
70
+ crack (>= 0.3.2)
71
+ hashdiff
72
+
73
+ PLATFORMS
74
+ ruby
75
+
76
+ DEPENDENCIES
77
+ bundler (~> 1.14)
78
+ minitest (~> 5.0)
79
+ portmone!
80
+ pry (~> 0.10)
81
+ rake (~> 10.0)
82
+ rubocop (~> 0.51)
83
+ simplecov (~> 0.14)
84
+ vcr (~> 2.9)
85
+ webmock (~> 3.0)
86
+
87
+ BUNDLED WITH
88
+ 1.14.6
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = 'test/*_test.rb'
7
+ t.warning = false
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,122 @@
1
+ module Portmone
2
+ class Error < StandardError; end
3
+
4
+ class Client
5
+ BASE_URL = 'https://www.portmone.com.ua/gateway/'.freeze
6
+
7
+ def initialize(payee_id:,
8
+ login:,
9
+ password:,
10
+ locale:,
11
+ currency:,
12
+ timezone: 'Europe/Kiev',
13
+ logger: nil)
14
+ @payee_id = payee_id
15
+ @login = login
16
+ @password = password
17
+ @locale = locale
18
+ @currency = currency
19
+ @timezone = timezone
20
+ @logger = logger
21
+ end
22
+
23
+ def generate_url(shop_order_number:, amount:, description:, success_url:, failure_url:, authorize_only: true)
24
+
25
+ send_request(
26
+ response_class: Portmone::Responses::GenerateURL,
27
+ method: nil,
28
+ shop_order_number: shop_order_number,
29
+ bill_amount: format_amount(amount),
30
+ bill_currency: @currency,
31
+ description: description,
32
+ success_url: success_url,
33
+ failure_url: failure_url,
34
+ encoding: 'utf-8',
35
+ preauth_flag: authorize_only ? 'Y' : 'N'
36
+ )
37
+ end
38
+
39
+ def order_status(shop_order_number)
40
+ generic_report(shop_order_number: shop_order_number)
41
+ end
42
+
43
+ def void(order_id)
44
+ generic_preauth_update(order_id: order_id, action: 'reject')
45
+ end
46
+
47
+ def charge(order_id, amount)
48
+ generic_preauth_update(order_id: order_id, action: 'set_paid', amount: amount)
49
+ end
50
+
51
+ def refund(order_id, amount:)
52
+ send_signed_request(
53
+ response_class: Portmone::Responses::RefundOrderStatus,
54
+ method: 'return',
55
+ shop_bill_id: order_id,
56
+ return_amount: format_amount(amount),
57
+ )
58
+ end
59
+
60
+ private
61
+
62
+ # может возвращать как отдельный заказ по id, так и все заказы с опр. статусом или в опр. временном диапазоне
63
+ def generic_report(shop_order_number: nil, status: nil, start_date: nil, end_date: nil)
64
+ send_signed_request(
65
+ response_class: Portmone::Responses::OrderStatus,
66
+ method: 'result',
67
+ shop_order_number: shop_order_number,
68
+ status: status,
69
+ start_date: start_date,
70
+ end_date: end_date,
71
+ )
72
+ end
73
+
74
+ def generic_preauth_update(order_id:, action:, amount: nil)
75
+ attrs = {
76
+ method: 'preauth',
77
+ response_class: Portmone::Responses::OrderStatus,
78
+ shop_bill_id: order_id,
79
+ action: action,
80
+ }
81
+ attrs[:postauth_amount] = format_amount(amount) if amount
82
+ send_signed_request(attrs)
83
+ end
84
+
85
+ def format_date(date)
86
+ date.strftime("%d.%m.%Y")
87
+ end
88
+
89
+ def format_amount(amount)
90
+ raise "Wrong currency! (got #{amount.currency}, expected #{@currency})" unless amount.currency == @currency
91
+
92
+ amount.to_f
93
+ end
94
+
95
+ def send_signed_request(**data)
96
+ send_request(data.merge(
97
+ login: @login,
98
+ password: @password,
99
+ ))
100
+ end
101
+
102
+ def send_request(**data)
103
+ response_class = data.delete(:response_class)
104
+ response = http_client.post(BASE_URL) do |req|
105
+ req.body = data.merge(
106
+ payee_id: @payee_id,
107
+ lang: @locale,
108
+ ).delete_if { |_, v| v.nil? }
109
+ end
110
+
111
+ response_class.new(response, currency: @currency, timezone: @timezone)
112
+ end
113
+
114
+ def http_client
115
+ @http_client ||= Faraday.new do |builder|
116
+ builder.request :url_encoded
117
+ builder.response :logger, @logger, bodies: true if @logger
118
+ builder.adapter Faraday.default_adapter
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,15 @@
1
+ class Portmone::Responses::BaseResponse
2
+ attr_reader :response
3
+ attr_reader :xml_data
4
+
5
+ def initialize(faraday_response, currency:, timezone:)
6
+ @response = faraday_response
7
+ @xml_data = MultiXml.parse(@response.body)
8
+ @currency = currency
9
+ @timezone = timezone
10
+ end
11
+
12
+ def http_status
13
+ response.status
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ class Portmone::Responses::GenerateURL < Portmone::Responses::BaseResponse
2
+ def success?
3
+ location.present? && http_status == 302
4
+ end
5
+
6
+ def location
7
+ response.headers['location']
8
+ end
9
+ end
@@ -0,0 +1,77 @@
1
+ class Portmone::Responses::OrderStatus < Portmone::Responses::BaseResponse
2
+ #
3
+ # статусы:
4
+ # CREATED - клиент перешел на страницу 3Ds
5
+ # PREAUTH - деньги успешно заблокированы на счету
6
+ # PAYED - деньги списаны
7
+ # REJECTED - платеж не удалось провести или отмена авторизации
8
+ # RETURN - успешный возврат
9
+ #
10
+
11
+ %i(bill_date
12
+ pay_time
13
+ pay_order_time
14
+ order_id
15
+ shop_order_number
16
+ description
17
+ auth_code
18
+ status
19
+ error_code
20
+ error_message).each do |key|
21
+
22
+ define_method(key) do
23
+ order.send key
24
+ end
25
+ end
26
+
27
+ def amount
28
+ transactions.select(&:paid?)
29
+ .map { |t| t.bill_amount }
30
+ .inject(:+) || transactions.first.bill_amount
31
+ end
32
+
33
+ def reversed_amount
34
+ transactions.select(&:reversed?)
35
+ .map { |t| t.bill_amount }
36
+ .inject(:+) || 0
37
+ end
38
+
39
+ def actual_amount
40
+ amount + reversed_amount
41
+ end
42
+
43
+ # статусы "оплачено" и "авторизовано"
44
+ def paid?
45
+ transactions.any?(&:paid?)
46
+ end
47
+
48
+ def reversed?
49
+ transactions.any?(&:reversed?)
50
+ end
51
+
52
+ def success?
53
+ order.present? && error_code == '0'
54
+ end
55
+
56
+ def transactions
57
+ @transactions ||= begin
58
+ data.map do |h|
59
+ Portmone::Transaction.new(h.merge(timezone: @timezone, currency: @currency))
60
+ end
61
+ end
62
+ end
63
+
64
+ def order
65
+ @order ||= begin
66
+ data.any? && Portmone::Transaction.new(data.first.merge(data.last).merge(timezone: @timezone, currency: @currency))
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def data
73
+ data = @xml_data.dig('portmoneresult', 'orders', 'order') || @xml_data.dig('portmoneresult', 'order') || {}
74
+ data = [data] unless data.is_a?(Array) # cannot use Array() on Hash
75
+ data
76
+ end
77
+ end
@@ -0,0 +1,53 @@
1
+ module Portmone
2
+ class Transaction
3
+ attr_reader :data
4
+
5
+ def initialize(data)
6
+ raise Error, "Wrong data: Hash expected, got #{data.inspect}" unless data.is_a?(Hash)
7
+
8
+ @timezone = data.delete(:timezone)
9
+ @currency = data.delete(:currency)
10
+ @data = data
11
+ end
12
+
13
+ def bill_date
14
+ Date.parse(data['bill_date'])
15
+ end
16
+
17
+ def pay_time
18
+ ActiveSupport::TimeZone[@timezone].parse(data['pay_date'])
19
+ end
20
+
21
+ def pay_order_time
22
+ ActiveSupport::TimeZone[@timezone].parse(data['pay_order_date']) rescue nil
23
+ end
24
+
25
+ def bill_amount
26
+ Money.from_amount(data['bill_amount'].to_f, @currency)
27
+ end
28
+
29
+ def order_id
30
+ data.dig('shop_bill_id')
31
+ end
32
+
33
+ def paid?
34
+ status == 'PAYED' || status == 'PREAUTH'
35
+ end
36
+
37
+ def reversed?
38
+ status == 'RETURN'
39
+ end
40
+
41
+ %i(shop_order_number
42
+ description
43
+ auth_code
44
+ status
45
+ error_code
46
+ error_message).each do |key|
47
+
48
+ define_method(key) do
49
+ data.dig(key.to_s)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Portmone
2
+ VERSION = '0.0.1'
3
+ end
data/lib/portmone.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Portmone
2
+ module Responses
3
+ end
4
+ end
5
+ require 'active_support/time'
6
+ require 'faraday'
7
+ require 'money'
8
+ require 'multi_xml'
9
+ require 'logger'
10
+ require 'portmone/version'
11
+ require 'portmone/transaction'
12
+ require 'portmone/responses/base_response'
13
+ require 'portmone/responses/generate_url'
14
+ require 'portmone/responses/order_status'
15
+ require 'portmone/responses/refund_order_status'
16
+ require 'portmone/client'
data/portmone.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'portmone/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'portmone'
9
+ spec.version = Portmone::VERSION
10
+ spec.authors = ['Alexander Sviridov']
11
+ spec.email = ['alexander.sviridiv@gmail.com']
12
+
13
+ spec.summary = 'Ruby wrapper for Portmone payment system API'
14
+ spec.description = 'Ruby wrapper for Portmone payment system API'
15
+ spec.homepage = 'https://github.com/busfor/portmone'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency 'activesupport'
26
+ spec.add_dependency 'faraday', '~> 0.9'
27
+ spec.add_dependency 'money', '~> 6.0'
28
+ spec.add_dependency 'multi_xml', '~> 0.5'
29
+
30
+ spec.add_development_dependency 'bundler', '~> 1.14'
31
+ spec.add_development_dependency 'minitest', '~> 5.0'
32
+ spec.add_development_dependency 'pry', '~> 0.10'
33
+ spec.add_development_dependency 'rake', '~> 10.0'
34
+ spec.add_development_dependency 'rubocop', '~> 0.51'
35
+ spec.add_development_dependency 'simplecov', '~> 0.14'
36
+ spec.add_development_dependency 'vcr', '~> 2.9'
37
+ spec.add_development_dependency 'webmock', '~> 3.0'
38
+ end
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: portmone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Sviridov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-12-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: money
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '6.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '6.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multi_xml
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.5'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.14'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.10'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.10'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.51'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.51'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.14'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.14'
153
+ - !ruby/object:Gem::Dependency
154
+ name: vcr
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '2.9'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '2.9'
167
+ - !ruby/object:Gem::Dependency
168
+ name: webmock
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '3.0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '3.0'
181
+ description: Ruby wrapper for Portmone payment system API
182
+ email:
183
+ - alexander.sviridiv@gmail.com
184
+ executables: []
185
+ extensions: []
186
+ extra_rdoc_files: []
187
+ files:
188
+ - Gemfile
189
+ - Gemfile.lock
190
+ - Rakefile
191
+ - lib/portmone.rb
192
+ - lib/portmone/client.rb
193
+ - lib/portmone/responses/base_response.rb
194
+ - lib/portmone/responses/generate_url.rb
195
+ - lib/portmone/responses/order_status.rb
196
+ - lib/portmone/transaction.rb
197
+ - lib/portmone/version.rb
198
+ - portmone.gemspec
199
+ homepage: https://github.com/busfor/portmone
200
+ licenses:
201
+ - MIT
202
+ metadata: {}
203
+ post_install_message:
204
+ rdoc_options: []
205
+ require_paths:
206
+ - lib
207
+ required_ruby_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ required_rubygems_version: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '0'
217
+ requirements: []
218
+ rubyforge_project:
219
+ rubygems_version: 2.5.2
220
+ signing_key:
221
+ specification_version: 4
222
+ summary: Ruby wrapper for Portmone payment system API
223
+ test_files: []