prodamus 1.0.0

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
+ SHA256:
3
+ metadata.gz: '08f4d1e8a6e80f4d32aca52776e9e798ade8acc9def89e98e957a32b65414522'
4
+ data.tar.gz: b0d257bf1f9904ead2472a6337ed5adb7fa6a64e7a7e4d837eb4a3764245827e
5
+ SHA512:
6
+ metadata.gz: 5d5215fca8b35e60c4937f6e6f9c94fc45f96ba769e7f3203fc737b25d2f1e71a6cf5c6f912b0b8001ab97a5c23eb04a112b8d4fb120d43967228c9b27774833
7
+ data.tar.gz: 73fb2ea1c256fcb6b5aba066ac0e25da371d48086b1ddeae67a88a0e2e34687d73a35820871bef5ce10633f8bb724996832249e3d370afd89af4866179bd2b9b
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # Purchase integration with https://prodamus.ru/
2
+
3
+ ## Client library for ruby apps
4
+
5
+ Prodamus docs https://help.prodamus.ru/
6
+
7
+ # Example usage:
8
+
9
+ * configure connection
10
+
11
+ Prodamus.config do |conf|
12
+ conf.main_payment_form_url = <payment form provided by prodamus>
13
+ conf.secret_key = <key from main form settings>
14
+ end
15
+
16
+ or
17
+
18
+ Prodamus.main_payment_form_url = <payment form provided by prodamus>
19
+ Prodamus.secret_key = <key from main form settings>
20
+
21
+ * configure request wich be sent for get purchase link
22
+
23
+ Prodamus.link_config do |conf|
24
+ conf.order_id = 12
25
+ conf.currency = 'rub'
26
+ ...
27
+ end
28
+ To see more parameters https://help.prodamus.ru/payform/integracii/rest-api/instrukcii-dlya-samostoyatelnaya-integracii-servisov#obshie-neobyazatelnye-parametry
29
+
30
+ * add a product or products to be purchased (must have at least one)
31
+
32
+ Prodamus.link_config.add_product(
33
+ name: <name>, price: <price>,
34
+ quantity: 1, sku: <id in your system>
35
+ )
36
+
37
+ Description product params https://help.prodamus.ru/payform/integracii/rest-api/instrukcii-dlya-samostoyatelnaya-integracii-servisov#parametry-massiva-products
38
+
39
+ * set link expired datetime
40
+
41
+ Prodamus.link_config.form_access_duration = 1.hour
42
+
43
+ * get payment link
44
+
45
+ Prodamus.link
46
+
47
+ ## Submit callback
48
+
49
+ Your callback from Prodamus server can be look like this:
50
+
51
+ data = {
52
+ date: '2022-12-08T10:42:10+03:00',
53
+ order_id: <id in prodamus system>,
54
+ order_num: <id in your system>,
55
+ domain: <your main form>,
56
+ sum: '770.00',
57
+ currency: 'rub',
58
+ customer_phone: '+78005553535',
59
+ customer_extra: '',
60
+ payment_type: 'Оплата картой, выпущенной в РФ',
61
+ commission: '100',
62
+ commission_sum: '770.00',
63
+ attempt: '2',
64
+ callbackType: 'json',
65
+ link_expired: '2022-12-08 11:38',
66
+ products: [
67
+ {
68
+ name: <product name>,
69
+ price: '770.00',
70
+ quantity: '1',
71
+ sum: '770.00'
72
+ }
73
+ ],
74
+ payment_status: payment_status,
75
+ payment_status_description: 'Успешная оплата',
76
+ payment_init: 'manual',
77
+ submit: {
78
+ date: '2022-12-08T10:42:10+03:00',
79
+ order_id: <id in prodamus system>,
80
+ order_num: <id in your system>,
81
+ domain: <your main form>,
82
+ sum: '770.00',
83
+ currency: 'rub',
84
+ customer_phone: '+78005553535',
85
+ customer_extra: '',
86
+ payment_type: 'Оплата картой, выпущенной в РФ',
87
+ commission: '100',
88
+ commission_sum: '770.00',
89
+ attempt: '2',
90
+ callbackType: 'json',
91
+ link_expired: '2022-12-08 11:38',
92
+ products: [{ name: <product name>, price: '770.00', quantity: '1', sum: '770.00' }],
93
+ payment_status: 'success',
94
+ payment_status_description: 'Успешная оплата',
95
+ payment_init: 'manual'
96
+ }
97
+ }
98
+
99
+ Need get signature from header 'Sign' and verify this data:
100
+
101
+ sign = request.headers['Sign']
102
+ Prodamus.verify(data[:submit], sign)
103
+
104
+ This method returns 'true' or 'false'.
105
+
106
+ You must perform some actions in your system to complete purchase if this method returns'true'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../prodamus'
4
+
5
+ module Prodamus
6
+ # Some product to be sent to prodamus
7
+ class Product
8
+ attr_reader :name, :sku, :quantity, :price
9
+
10
+ def initialize(name:, price:, quantity: 1, sku: nil)
11
+ @sku = sku.to_s
12
+ @name = name
13
+ @price = price.to_s
14
+ @quantity = quantity.to_s
15
+ end
16
+ end
17
+ end
data/lib/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prodamus
4
+ NON_INSTALLMENTS_PAYMENT_METHODS = %w[
5
+ AC ACkz ACkztjp ACf ACeuruk ACusduk ACEURNMBX
6
+ ACUSDNMBX SBP PC QW GP sbol invoice
7
+ ].freeze
8
+
9
+ INSTALLMENTS_PAYMENT_METHODS = %w[
10
+ installment installment_5_21 installment_6_28
11
+ installment_10_28 installment_12_28 installment_0_0_3
12
+ installment_0_0_4 installment_0_0_6 installment_0_0_10
13
+ installment_0_0_12 installment_0_0_24 credit
14
+ vsegdada_installment_0_0_4 vsegdada_installment_0_0_6 vsegdada_installment_0_0_10
15
+ vsegdada_installment_0_0_12 vsegdada_installment_0_0_24
16
+ ].freeze
17
+
18
+ ALL_AVAIBLE_PAYMENT_METHODS = NON_INSTALLMENTS_PAYMENT_METHODS + INSTALLMENTS_PAYMENT_METHODS
19
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prodamus
4
+ VERSION = '1.0.0'
5
+ end
data/lib/prodamus.rb ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require_relative 'helper'
5
+ require_relative 'entities/product'
6
+ require_relative 'services/verifier'
7
+ require_relative 'services/link_config'
8
+
9
+ # Main settings
10
+ module Prodamus
11
+ class << self
12
+ # payment form provided by prodamus
13
+ attr_accessor :main_payment_form_url
14
+
15
+ # key from main form settings
16
+ attr_accessor :secret_key
17
+
18
+ def config
19
+ yield self if block_given?
20
+ end
21
+
22
+ # verify signatured data
23
+ def verify(data, sign)
24
+ verifier(data).verify(sign)
25
+ end
26
+
27
+ # get link to payment form
28
+ def link
29
+ response = Faraday.new(
30
+ url: @main_payment_form_url,
31
+ params: @link_config.format_result,
32
+ headers: { 'Content-Type' => 'text/plain', 'charset' => 'utf-8' },
33
+ request: { timeout: 5 }
34
+ ).get
35
+
36
+ raise response.body.force_encoding('utf-8') if response.status != 200
37
+
38
+ response.body
39
+ end
40
+
41
+ # configure the payload to receive the payment form
42
+ def link_config(&block)
43
+ @link_config ||= LinkConfig.new(&block)
44
+ end
45
+
46
+ def verifier(data, algorithm = 'sha256')
47
+ raise 'Missing secret_key.' if @secret_key.nil?
48
+
49
+ @verifier ||= Verifier.new(data, @secret_key, algorithm)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/time_with_zone'
5
+ require_relative '../prodamus'
6
+
7
+ # Main settings
8
+ module Prodamus
9
+ # Configure request wich be sent for get purchase link
10
+ class LinkConfig
11
+ DEFAULT_PAYMENTS_LIMIT = 1
12
+
13
+ # rubocop:disable Naming/MethodName
14
+ attr_accessor :order_id, :sys, :customer_phone, :customer_email,
15
+ :customer_extra, :payment_method,
16
+ :urlReturn, :urlSuccess, :urlNotification,
17
+ :currency, :payments_limit
18
+ # rubocop:enable Naming/MethodName
19
+
20
+ attr_writer :form_access_duration, :timezone
21
+ attr_reader :products, :available_payment_methods
22
+
23
+ def initialize
24
+ @products = []
25
+ yield self if block_given?
26
+ end
27
+
28
+ # Transform payload to be sent
29
+ # rubocop:disable Metrics/MethodLength
30
+ def format_result
31
+ raise 'Product must be exists' if @products.empty?
32
+
33
+ result = {
34
+ 'do' => 'link',
35
+ 'callbackType' => 'json',
36
+ 'sys' => @sys,
37
+ 'order_id' => @order_id,
38
+ 'customer_phone' => @customer_phone,
39
+ 'customer_email' => @customer_email,
40
+ 'customer_extra' => @customer_extra,
41
+ 'payment_method' => @payment_method,
42
+ 'available_payment_methods' => (@available_payment_methods || NON_INSTALLMENTS_PAYMENT_METHODS).join('|'),
43
+ 'urlReturn' => @urlReturn,
44
+ 'urlSuccess' => @urlSuccess,
45
+ 'urlNotification' => @urlNotification,
46
+ 'installments_disabled' => (@installments_disabled || 1).to_s,
47
+ 'currency' => @currency,
48
+ 'payments_limit' => (@payments_limit || DEFAULT_PAYMENTS_LIMIT).to_s,
49
+ 'link_expired' => link_expired
50
+ }.compact!
51
+
52
+ @products.each_with_index do |product, index|
53
+ result.merge!({
54
+ "products[#{index}][name]" => product.name,
55
+ "products[#{index}][sku]" => product.sku,
56
+ "products[#{index}][price]" => product.price,
57
+ "products[#{index}][quantity]" => product.quantity
58
+ })
59
+ end
60
+
61
+ result
62
+ end
63
+ # rubocop:enable Metrics/MethodLength
64
+
65
+ def add_product(name:, price:, quantity: 1, sku: nil)
66
+ @products << Product.new(name: name, price: price, quantity: quantity, sku: sku)
67
+ end
68
+
69
+ def add_available_payment_methods(*payment_methods)
70
+ payment_methods.flatten!
71
+
72
+ raise 'payment_method is already defined' if @payment
73
+ raise ArgumentError, 'Not avaible payment methods.' unless (payment_methods - ALL_AVAIBLE_PAYMENT_METHODS).empty?
74
+
75
+ if @installments_disabled&.positive? && !(payment_methods - NON_INSTALLMENTS_PAYMENT_METHODS).empty?
76
+ raise ArgumentError, 'Installments payment methods is not avaible when installments disabled.'
77
+ end
78
+
79
+ @available_payment_methods = [] if @available_payment_methods.nil?
80
+ @available_payment_methods += payment_methods
81
+ end
82
+
83
+ def installments_disabled=(disabled)
84
+ @installments_disabled = disabled ? 1 : 0
85
+ end
86
+
87
+ def installments_disabled
88
+ !@installments_disabled.zero?
89
+ end
90
+
91
+ def link_expired
92
+ tz = ActiveSupport::TimeZone.new(timezone)
93
+ date = tz.now + form_access_duration.to_i.seconds
94
+ date.strftime('%Y-%m-%d %H:%M')
95
+ end
96
+
97
+ def form_access_duration
98
+ @form_access_duration || 1.day
99
+ end
100
+
101
+ def timezone
102
+ @timezone || 'Moscow'
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../prodamus'
4
+
5
+ module Prodamus
6
+ # HMAC encoding and verify signature
7
+ class Verifier
8
+ attr_accessor :data, :key, :algorithm
9
+
10
+ def initialize(data, key, algorithm = 'sha256')
11
+ @data = data
12
+ @key = key
13
+ @algorithm = algorithm
14
+ end
15
+
16
+ def verify(sign)
17
+ encoded_data = encode
18
+ encoded_data && (encoded_data == sign)
19
+ end
20
+
21
+ def encode
22
+ data = sort(@data).to_json
23
+ digest = OpenSSL::Digest.new(@algorithm)
24
+
25
+ OpenSSL::HMAC.hexdigest(digest, @key, data)
26
+ rescue NoMethodError
27
+ raise ArgumentError, 'Expected a Hash with array of hashes.'
28
+ end
29
+
30
+ private
31
+
32
+ def sort(data)
33
+ data.sort.to_h.transform_values do |value|
34
+ value.is_a?(Array) ? value.map { |product| product.sort.to_h.transform_values(&:to_s) } : value.to_s
35
+ end
36
+ end
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,207 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prodamus
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nikita Kondrashov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-03-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '7.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '7.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.6'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.47'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.47'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-performance
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.16'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.16'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.19'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.19'
139
+ - !ruby/object:Gem::Dependency
140
+ name: vcr
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '6.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '6.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: webmock
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '3.14'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '3.14'
167
+ description: This gem allows to integrate Prodamus purchases service in your Rails
168
+ app or simple Ruby app.
169
+ email:
170
+ - hasenberg41@mail.ru
171
+ executables: []
172
+ extensions: []
173
+ extra_rdoc_files:
174
+ - README.md
175
+ files:
176
+ - README.md
177
+ - lib/entities/product.rb
178
+ - lib/helper.rb
179
+ - lib/prodamus.rb
180
+ - lib/prodamus/version.rb
181
+ - lib/services/link_config.rb
182
+ - lib/services/verifier.rb
183
+ homepage: https://github.com/hasenberg41/prodamus
184
+ licenses:
185
+ - MIT
186
+ metadata:
187
+ rubygems_mfa_required: 'true'
188
+ post_install_message:
189
+ rdoc_options: []
190
+ require_paths:
191
+ - lib
192
+ required_ruby_version: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: 2.7.0
197
+ required_rubygems_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ requirements: []
203
+ rubygems_version: 3.4.7
204
+ signing_key:
205
+ specification_version: 4
206
+ summary: Prodamus purchases integration for Ruby on Rails
207
+ test_files: []