app_manager 1.7.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 939c279b78c14a7e781d74177e6df92a287a2e463d6c3af39346760e6891115b
4
- data.tar.gz: 9a3abefe3f42d4c40b34d597a0b850d128afbff33094cbe8ddf06b66dd473fb2
3
+ metadata.gz: f97b31c44449e934975e233f07a745d883e58abd3a29ccd30537ec0e1ba6bca2
4
+ data.tar.gz: 863e6b243cd1c16839c4d5c9d7f3a3a0b1b47909ce613c9ff075e21d996ce314
5
5
  SHA512:
6
- metadata.gz: 769614783e6d231a4b75303003b8da1143787d10cae09f093647bfeb6b246c28d4e604255a23530ad6332cad3d6796cec7fd99a80a3b5c46c8d74660f8dd17ee
7
- data.tar.gz: da7329819695c265587a36c8c34ef922176e5fd40a5109acd88849695bbbf1143eff43d2d699f08c044d90eaf58bb3f45ea5f5ec09fbbc9c5d34bcb2c980b6b9
6
+ metadata.gz: 31f071cf42a3cfec28b0fae59cedfc72c2b24b80a85c237932acf0d118807977a961aa199ebd5435f5ddcb4f9310a27ac47589b35db953f39e61fbdd7e6d25d6
7
+ data.tar.gz: 67769978a3d30041d329e02a22432dad4d936a5254ac711e118a03be50101e11260aa22929a2c210ddbec027c44b60fa807f0c51dc494e6aac92a16033b8b83d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- app_manager (1.7.0)
4
+ app_manager (2.2.0)
5
5
  activerecord-import (~> 1.4)
6
6
  httparty
7
7
  kaminari (>= 0.16.3)
@@ -10,35 +10,35 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- actioncable (7.1.2)
14
- actionpack (= 7.1.2)
15
- activesupport (= 7.1.2)
13
+ actioncable (7.1.3.2)
14
+ actionpack (= 7.1.3.2)
15
+ activesupport (= 7.1.3.2)
16
16
  nio4r (~> 2.0)
17
17
  websocket-driver (>= 0.6.1)
18
18
  zeitwerk (~> 2.6)
19
- actionmailbox (7.1.2)
20
- actionpack (= 7.1.2)
21
- activejob (= 7.1.2)
22
- activerecord (= 7.1.2)
23
- activestorage (= 7.1.2)
24
- activesupport (= 7.1.2)
19
+ actionmailbox (7.1.3.2)
20
+ actionpack (= 7.1.3.2)
21
+ activejob (= 7.1.3.2)
22
+ activerecord (= 7.1.3.2)
23
+ activestorage (= 7.1.3.2)
24
+ activesupport (= 7.1.3.2)
25
25
  mail (>= 2.7.1)
26
26
  net-imap
27
27
  net-pop
28
28
  net-smtp
29
- actionmailer (7.1.2)
30
- actionpack (= 7.1.2)
31
- actionview (= 7.1.2)
32
- activejob (= 7.1.2)
33
- activesupport (= 7.1.2)
29
+ actionmailer (7.1.3.2)
30
+ actionpack (= 7.1.3.2)
31
+ actionview (= 7.1.3.2)
32
+ activejob (= 7.1.3.2)
33
+ activesupport (= 7.1.3.2)
34
34
  mail (~> 2.5, >= 2.5.4)
35
35
  net-imap
36
36
  net-pop
37
37
  net-smtp
38
38
  rails-dom-testing (~> 2.2)
39
- actionpack (7.1.2)
40
- actionview (= 7.1.2)
41
- activesupport (= 7.1.2)
39
+ actionpack (7.1.3.2)
40
+ actionview (= 7.1.3.2)
41
+ activesupport (= 7.1.3.2)
42
42
  nokogiri (>= 1.8.5)
43
43
  racc
44
44
  rack (>= 2.2.4)
@@ -46,37 +46,37 @@ GEM
46
46
  rack-test (>= 0.6.3)
47
47
  rails-dom-testing (~> 2.2)
48
48
  rails-html-sanitizer (~> 1.6)
49
- actiontext (7.1.2)
50
- actionpack (= 7.1.2)
51
- activerecord (= 7.1.2)
52
- activestorage (= 7.1.2)
53
- activesupport (= 7.1.2)
49
+ actiontext (7.1.3.2)
50
+ actionpack (= 7.1.3.2)
51
+ activerecord (= 7.1.3.2)
52
+ activestorage (= 7.1.3.2)
53
+ activesupport (= 7.1.3.2)
54
54
  globalid (>= 0.6.0)
55
55
  nokogiri (>= 1.8.5)
56
- actionview (7.1.2)
57
- activesupport (= 7.1.2)
56
+ actionview (7.1.3.2)
57
+ activesupport (= 7.1.3.2)
58
58
  builder (~> 3.1)
59
59
  erubi (~> 1.11)
60
60
  rails-dom-testing (~> 2.2)
61
61
  rails-html-sanitizer (~> 1.6)
62
- activejob (7.1.2)
63
- activesupport (= 7.1.2)
62
+ activejob (7.1.3.2)
63
+ activesupport (= 7.1.3.2)
64
64
  globalid (>= 0.3.6)
65
- activemodel (7.1.2)
66
- activesupport (= 7.1.2)
67
- activerecord (7.1.2)
68
- activemodel (= 7.1.2)
69
- activesupport (= 7.1.2)
65
+ activemodel (7.1.3.2)
66
+ activesupport (= 7.1.3.2)
67
+ activerecord (7.1.3.2)
68
+ activemodel (= 7.1.3.2)
69
+ activesupport (= 7.1.3.2)
70
70
  timeout (>= 0.4.0)
71
- activerecord-import (1.5.1)
71
+ activerecord-import (1.6.0)
72
72
  activerecord (>= 4.2)
73
- activestorage (7.1.2)
74
- actionpack (= 7.1.2)
75
- activejob (= 7.1.2)
76
- activerecord (= 7.1.2)
77
- activesupport (= 7.1.2)
73
+ activestorage (7.1.3.2)
74
+ actionpack (= 7.1.3.2)
75
+ activejob (= 7.1.3.2)
76
+ activerecord (= 7.1.3.2)
77
+ activesupport (= 7.1.3.2)
78
78
  marcel (~> 1.0)
79
- activesupport (7.1.2)
79
+ activesupport (7.1.3.2)
80
80
  base64
81
81
  bigdecimal
82
82
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -89,10 +89,10 @@ GEM
89
89
  addressable (2.8.0)
90
90
  public_suffix (>= 2.0.2, < 5.0)
91
91
  base64 (0.2.0)
92
- bigdecimal (3.1.5)
92
+ bigdecimal (3.1.7)
93
93
  builder (3.2.4)
94
94
  coderay (1.1.3)
95
- concurrent-ruby (1.2.2)
95
+ concurrent-ruby (1.2.3)
96
96
  connection_pool (2.4.1)
97
97
  crack (0.4.5)
98
98
  rexml
@@ -100,8 +100,7 @@ GEM
100
100
  date (3.3.4)
101
101
  diff-lcs (1.5.0)
102
102
  dotenv (2.1.2)
103
- drb (2.2.0)
104
- ruby2_keywords
103
+ drb (2.2.1)
105
104
  erubi (1.12.0)
106
105
  globalid (1.2.1)
107
106
  activesupport (>= 6.1)
@@ -109,12 +108,12 @@ GEM
109
108
  httparty (0.21.0)
110
109
  mini_mime (>= 1.0.0)
111
110
  multi_xml (>= 0.5.2)
112
- i18n (1.14.1)
111
+ i18n (1.14.4)
113
112
  concurrent-ruby (~> 1.0)
114
- io-console (0.7.1)
115
- irb (1.11.0)
113
+ io-console (0.7.2)
114
+ irb (1.12.0)
116
115
  rdoc
117
- reline (>= 0.3.8)
116
+ reline (>= 0.4.2)
118
117
  kaminari (1.2.2)
119
118
  activesupport (>= 4.1.0)
120
119
  kaminari-actionview (= 1.2.2)
@@ -135,11 +134,11 @@ GEM
135
134
  net-imap
136
135
  net-pop
137
136
  net-smtp
138
- marcel (1.0.2)
137
+ marcel (1.0.4)
139
138
  method_source (1.0.0)
140
139
  mini_mime (1.1.5)
141
- mini_portile2 (2.8.5)
142
- minitest (5.20.0)
140
+ mini_portile2 (2.8.6)
141
+ minitest (5.22.3)
143
142
  multi_xml (0.6.0)
144
143
  mutex_m (0.2.0)
145
144
  net-imap (0.3.7)
@@ -149,10 +148,10 @@ GEM
149
148
  net-protocol
150
149
  net-protocol (0.2.2)
151
150
  timeout
152
- net-smtp (0.4.0.1)
151
+ net-smtp (0.5.0)
153
152
  net-protocol
154
- nio4r (2.7.0)
155
- nokogiri (1.15.5)
153
+ nio4r (2.7.1)
154
+ nokogiri (1.15.6)
156
155
  mini_portile2 (~> 2.8.2)
157
156
  racc (~> 1.4)
158
157
  pry (0.14.1)
@@ -162,7 +161,7 @@ GEM
162
161
  stringio
163
162
  public_suffix (4.0.6)
164
163
  racc (1.7.3)
165
- rack (3.0.8)
164
+ rack (3.0.10)
166
165
  rack-session (2.0.0)
167
166
  rack (>= 3.0.0)
168
167
  rack-test (2.1.0)
@@ -170,20 +169,20 @@ GEM
170
169
  rackup (2.1.0)
171
170
  rack (>= 3)
172
171
  webrick (~> 1.8)
173
- rails (7.1.2)
174
- actioncable (= 7.1.2)
175
- actionmailbox (= 7.1.2)
176
- actionmailer (= 7.1.2)
177
- actionpack (= 7.1.2)
178
- actiontext (= 7.1.2)
179
- actionview (= 7.1.2)
180
- activejob (= 7.1.2)
181
- activemodel (= 7.1.2)
182
- activerecord (= 7.1.2)
183
- activestorage (= 7.1.2)
184
- activesupport (= 7.1.2)
172
+ rails (7.1.3.2)
173
+ actioncable (= 7.1.3.2)
174
+ actionmailbox (= 7.1.3.2)
175
+ actionmailer (= 7.1.3.2)
176
+ actionpack (= 7.1.3.2)
177
+ actiontext (= 7.1.3.2)
178
+ actionview (= 7.1.3.2)
179
+ activejob (= 7.1.3.2)
180
+ activemodel (= 7.1.3.2)
181
+ activerecord (= 7.1.3.2)
182
+ activestorage (= 7.1.3.2)
183
+ activesupport (= 7.1.3.2)
185
184
  bundler (>= 1.15.0)
186
- railties (= 7.1.2)
185
+ railties (= 7.1.3.2)
187
186
  rails-dom-testing (2.2.0)
188
187
  activesupport (>= 5.0.0)
189
188
  minitest
@@ -191,18 +190,18 @@ GEM
191
190
  rails-html-sanitizer (1.6.0)
192
191
  loofah (~> 2.21)
193
192
  nokogiri (~> 1.14)
194
- railties (7.1.2)
195
- actionpack (= 7.1.2)
196
- activesupport (= 7.1.2)
193
+ railties (7.1.3.2)
194
+ actionpack (= 7.1.3.2)
195
+ activesupport (= 7.1.3.2)
197
196
  irb
198
197
  rackup (>= 1.0.0)
199
198
  rake (>= 12.2)
200
199
  thor (~> 1.0, >= 1.2.2)
201
200
  zeitwerk (~> 2.6)
202
201
  rake (13.0.6)
203
- rdoc (6.6.2)
202
+ rdoc (6.6.3.1)
204
203
  psych (>= 4.0.0)
205
- reline (0.4.2)
204
+ reline (0.5.1)
206
205
  io-console (~> 0.5)
207
206
  rexml (3.2.5)
208
207
  rspec (3.11.0)
@@ -220,9 +219,8 @@ GEM
220
219
  diff-lcs (>= 1.2.0, < 2.0)
221
220
  rspec-support (~> 3.11.0)
222
221
  rspec-support (3.11.0)
223
- ruby2_keywords (0.0.5)
224
222
  stringio (3.1.0)
225
- thor (1.3.0)
223
+ thor (1.3.1)
226
224
  timeout (0.4.1)
227
225
  tzinfo (2.0.6)
228
226
  concurrent-ruby (~> 1.0)
@@ -234,7 +232,7 @@ GEM
234
232
  websocket-driver (0.7.6)
235
233
  websocket-extensions (>= 0.1.0)
236
234
  websocket-extensions (0.1.5)
237
- zeitwerk (2.6.12)
235
+ zeitwerk (2.6.13)
238
236
 
239
237
  PLATFORMS
240
238
  ruby
data/README.md CHANGED
@@ -209,8 +209,43 @@ and then you can use follwing methods with your shop objects.
209
209
  @shop.active_shopfiy_charge_full #It will give shopify recurring charge graphql object with discounted price details
210
210
  ```
211
211
 
212
-
213
212
  <a name="step4"></a>
213
+ ### Discount Link Integration
214
+
215
+ * Update app manager vue package in Rails+Vue apps & for for non-vue apps, update css or js from npm package, minimum version should be 2.4.4
216
+
217
+ * Update app manager gem in your project's Gemfile
218
+
219
+ ```ruby
220
+ gem 'app_manager', '2.0.0'
221
+ ```
222
+ * In Rails project, in application.rb, add following line after require "rails/all" line
223
+
224
+ ```ruby
225
+ require 'app_manager/set_cookie'
226
+ ```
227
+
228
+ * In route.rb file, add following route with constraints
229
+ ```ruby
230
+ condition = ->(request) { request.path_info.include?('/discount/') }
231
+ constraints(condition) do
232
+ get '/discount/*any', to: AppManager::SetCookie.new(Rails.application, condition)
233
+ end
234
+ ```
235
+ * If updating gem from 1.6.1, then run these commands on rails root.
236
+
237
+ Note: In this command, Please do not overwrite app_manager.rb file so press n
238
+ ```ruby
239
+ rails g app_manager:install
240
+ ```
241
+ This will add a new migration file in your db/app_manager directory
242
+
243
+ ```ruby
244
+ rails db:migrate
245
+ ```
246
+
247
+
248
+ <a name="step5"></a>
214
249
  ### Extras
215
250
 
216
251
  * To view the app_manager ruby gem is working in your rails app you can use rails console and initialize app_manager instance like with App Manager Portal access:
@@ -41,6 +41,7 @@ module AppManager
41
41
  end
42
42
  if @shop.update(update_info)
43
43
  begin
44
+ plan_data['old_plan'] = params[:old_plan] || nil;
44
45
  AppManager::EventHandler.new('charge_created', {
45
46
  "plan" => plan_data,
46
47
  "charge" => nil,
@@ -58,9 +59,13 @@ module AppManager
58
59
  end
59
60
  end
60
61
  request_data = {'shop' => @shop.shopify_domain, 'timestamp' => Time.now.to_i, 'plan' => params[:plan_id]}
62
+ request_data.merge!('host' => params['host']) if params['host'].present?
63
+ request_data.merge!('old_plan' => params['old_plan']) if params['old_plan'].present?
64
+
61
65
  return_url = "#{app_url}#{plan_callback_path}?#{Rack::Utils.build_query(request_data)}"
62
66
  gq_obj = AppManager::GraphqlHelper.new(@shop.shopify_domain, @shop.shopify_token)
63
- data = gq_obj.recurring_charge_api_call(plan_data, return_url, @shop)
67
+ discount_local_storage = params[:discount_code].present? && !params[:discount_code].nil? ? params[:discount_code] : nil
68
+ data = gq_obj.recurring_charge_api_call(plan_data, return_url, @shop,discount_local_storage)
64
69
  if data.present? && !data["errors"].present? && (data["data"].present? && data["data"]["appSubscriptionCreate"].present? && (!data["data"]["appSubscriptionCreate"]["userErrors"].any? && data["data"]["appSubscriptionCreate"]["confirmationUrl"]))
65
70
  redirect_charge = data["data"]["appSubscriptionCreate"]["confirmationUrl"]
66
71
  render json: {'redirect_url' => redirect_charge}
@@ -85,6 +90,7 @@ module AppManager
85
90
  shopify_token = @field_names['shopify_token']
86
91
  shopify_domain = @field_names['name']
87
92
  grandfathered_field = @field_names['grandfathered']
93
+ discounted_plans = []
88
94
  if !@shop.nil?
89
95
  old_plan_id = @shop[@plan_field]
90
96
  old_plan_data = nil
@@ -131,8 +137,23 @@ module AppManager
131
137
  rescue Exception => e
132
138
  Rollbar.error("Error in APP MANAGER Charge Created Callback >>>> #{e.inspect}")
133
139
  end
140
+
141
+ begin
142
+ plan_obj = AppManager::Client.new
143
+ if params[:discount].present? && !params[:discount].nil?
144
+ discounted_plans = plan_obj.get_related_discounted_plans(params[:discount])
145
+ if discounted_plans.empty? || discounted_plans.include?(params[:plan].to_i)
146
+ plan_obj.discount_used(@shop[shopify_domain], params[:discount])
147
+ end
148
+ end
149
+ rescue
150
+ Rollbar.error("Error in APP MANAGER Discount used API call >>>> #{e.inspect}")
151
+ end
134
152
  end
135
153
  end
154
+
155
+
156
+
136
157
  embed_host = Base64.encode64(params[:shop] + "/admin")
137
158
 
138
159
  if !old_plan_id.nil?
@@ -65,8 +65,16 @@ module AppManager
65
65
  end
66
66
  end
67
67
 
68
+ promotional_discount = []
69
+ if params[:discount_code].present? && !params[:discount_code].nil? && @shop
70
+ discount_local_storage = params[:discount_code]
71
+ plan_obj = AppManager::Client.new
72
+ promotional_discount = plan_obj.get_promotional_discount(params[:shop_domain],discount_local_storage)
73
+ promotional_discount = [] if promotional_discount.class.to_s == "Hash" && promotional_discount.has_key?('status') && promotional_discount['status'] == 404
74
+ end
68
75
  response = {
69
76
  'plans' => plans,
77
+ 'promotional_discount' => promotional_discount,
70
78
  'shopify_plan' => shopify_plan,
71
79
  'plan' => plan,
72
80
  'default_plan_id' => default_plan_id,
@@ -90,12 +98,12 @@ module AppManager
90
98
  @shopify_email = AppManager.configuration.field_names['shopify_email']
91
99
  @shopify_plan_name_field = AppManager.configuration.field_names['shopify_plan']
92
100
  if params[:search]
93
- data = model.where("#{@shopify_domain} LIKE :search OR #{@shopify_email} LIKE :search", search: "%#{search}%")
101
+ data = model.where("#{@shopify_domain} LIKE :search OR #{@shopify_email} LIKE :search", search: "%#{search}%").order(sort + " " + order)
94
102
  data = data.where(@plan_field => plans) if !plans.nil?
95
103
  data = data.where(@shopify_plan_name_field => shopify_plans) if !shopify_plans.nil?
96
104
  data = data.page(params[:page]).per(items_per_page)
97
105
  else
98
- data = model
106
+ data = model.order(sort + " " + order)
99
107
  data = data.where(@plan_field => plans) if !plans.nil?
100
108
  data = data.where(@shopify_plan_name_field => shopify_plans) if !shopify_plans.nil?
101
109
  data = data.page(params[:page]).per(items_per_page)
@@ -0,0 +1,5 @@
1
+ module AppManager
2
+ class Discount < AppManagerRecord
3
+ self.table_name = "discounts"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module AppManager
2
+ class DiscountLinkPlan < AppManagerRecord
3
+ self.table_name = "discount_link_plans"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module AppManager
2
+ class DiscountShop < AppManagerRecord
3
+ self.table_name = "discount_shops"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module AppManager
2
+ class DiscountUsageLog < AppManagerRecord
3
+ self.table_name = "discounts_usage_log"
4
+ end
5
+ end
@@ -28,8 +28,19 @@ module AppManager
28
28
  response = response_from_cache_for_api(http_method, path, options)
29
29
  failsafe_or_caching_done = true
30
30
  else
31
- time_out = Rails.env.development? ? 120 : 30
32
- response = self.class.send(http_method, path, { body: options, timeout: time_out })
31
+ time_out = Rails.env.development? ? 120 : 120
32
+ begin
33
+ response = self.class.send(http_method, path, { body: options, timeout: time_out })
34
+ rescue Net::ReadTimeout => error
35
+ # Handle timeout error here
36
+ Rails.logger.info "Net::ReadTimeout: Request timed out. Please try again."
37
+ response = response_from_failsafe(path, options)
38
+ rescue StandardError => e
39
+ # Handle other errors
40
+ Rails.logger.info "Error: #{e.message}"
41
+ response = response_from_failsafe(path, options)
42
+ end
43
+
33
44
  end
34
45
  if path.include? "/get-status"
35
46
  data = response
@@ -42,7 +53,10 @@ module AppManager
42
53
  data = parse_data(response.parsed_response)
43
54
  # data = response_from_failsafe(path,options) #uncomment to test failsafe
44
55
  return data
45
- else
56
+ elsif ((response.class == Hash) && response.has_key?("message"))
57
+ data = parse_data(response)
58
+ return data
59
+ else
46
60
  if failsafe_or_caching_done == true
47
61
  data = response
48
62
  return data
@@ -60,7 +74,7 @@ module AppManager
60
74
  return fetch_static_cached_response(cache_key)
61
75
  end
62
76
  begin
63
- response = self.class.send(http_method, path, { body: options, timeout: 15 })
77
+ response = self.class.send(http_method, path, { body: options, timeout: 120 })
64
78
  if response.class.to_s == "HTTParty::Response" && (response.code.to_s.start_with?('2') or response.code.to_s.start_with?('4')) && (response.code.to_s != '429')
65
79
  Rails.cache.write(cache_key, response, expires_in: AppManager.configuration.expires_in)
66
80
  else
@@ -68,6 +82,8 @@ module AppManager
68
82
  end
69
83
  rescue Net::ReadTimeout => error
70
84
  response = response_from_failsafe(path,options)
85
+ rescue StandardError => error
86
+ response = response_from_failsafe(path, options)
71
87
  rescue Exception => e
72
88
  response = response_from_failsafe(path,options)
73
89
  end
@@ -111,6 +127,12 @@ module AppManager
111
127
  return @fs.get_local_charge(params,options)
112
128
  when 'has-plan'
113
129
  return @fs.get_local_has_plan(params, options)
130
+ when 'discount'
131
+ return @fs.get_local_discount(params, options)
132
+ when 'use-discount'
133
+ return @fs.store_discount_used(params,options)
134
+ when 'get-related-discounted-plans'
135
+ return @fs.get_local_related_discounted_plans(params,options)
114
136
  else
115
137
  return nil
116
138
  end
@@ -26,6 +26,7 @@ module AppManager
26
26
  post("/sync-charge", options)
27
27
  end
28
28
 
29
+
29
30
  def get_remaining_days(shop_domain, trial_activated_at = nil, plan_id = nil)
30
31
  get("/get-remaining-days?shop_domain=#{shop_domain}&trial_activated_at=#{trial_activated_at}&plan_id=#{plan_id}")
31
32
  end
@@ -42,6 +43,21 @@ module AppManager
42
43
  get("/has-plan?shop_domain=#{shop_domain}&plan_id=#{plan_id}&trial_activated_at=#{trial_activated_at}&grandfathered=#{grandfathered}")
43
44
  end
44
45
 
46
+ def get_promotional_discount(shop_domain = nil,code)
47
+ get("/discount?shop_domain=#{shop_domain}&code=#{code}")
48
+ end
49
+
50
+ def discount_used(shop_domain, discount_id)
51
+ post('/use-discount', {shop_domain: shop_domain, discount_id: discount_id.to_i})
52
+ end
53
+
54
+ def sync_discount_usage_log(options = {})
55
+ post("/use-discount-sync", options)
56
+ end
57
+
58
+ def get_related_discounted_plans(discount_id)
59
+ get("/get-related-discounted-plans?discount_id=#{discount_id}")
60
+ end
45
61
 
46
62
  end
47
63
  end
@@ -52,7 +52,7 @@ module AppManager
52
52
  begin
53
53
  save_api_extend_trials(params["extend_trials"])
54
54
  rescue Exception => e
55
- Rollbar.error("[Extens Trials] APP MANAGER >>>> #{e.inspect}")
55
+ Rollbar.error("[Extend Trials] APP MANAGER >>>> #{e.inspect}")
56
56
  end
57
57
 
58
58
  begin
@@ -60,6 +60,31 @@ module AppManager
60
60
  rescue Exception => e
61
61
  Rollbar.error("[Plan User] APP MANAGER >>>> #{e.inspect}")
62
62
  end
63
+
64
+ begin
65
+ save_api_promotional_discounts(params["promotional_discounts"])
66
+ rescue Exception => e
67
+ Rollbar.error("[Promotional discounts] APP MANAGER >>>> #{e.inspect}")
68
+ end
69
+
70
+ begin
71
+ save_api_promotional_discounts_shops(params["promotional_discounts_shops"])
72
+ rescue Exception => e
73
+ Rollbar.error("[Promotional discounts shops] APP MANAGER >>>> #{e.inspect}")
74
+ end
75
+
76
+ begin
77
+ save_api_promotional_discounts_link_plans(params["promotional_discounts_plans"])
78
+ rescue Exception => e
79
+ Rollbar.error("[Promotional discounts plans] APP MANAGER >>>> #{e.inspect}")
80
+ end
81
+
82
+ begin
83
+ save_api_promotional_discounts_usage_log(params["promotional_discounts_usage_log"])
84
+ rescue Exception => e
85
+ Rollbar.error("[Promotional discounts usage log] APP MANAGER >>>> #{e.inspect}")
86
+ end
87
+
63
88
  end
64
89
 
65
90
  def save_api_plans(plans)
@@ -185,7 +210,106 @@ module AppManager
185
210
  extend_plan_users << AppManager::PlanUser.new(plan_user_id: plan_user['id'],shop_domain: plan_user['shop_domain'], plan_id: plan_user['plan_id'], created_by: plan_user['created_by'], created_at: plan_user['created_at'], updated_at: plan_user['updated_at'])
186
211
  # extend_plan_users << AppManager::PlanUser.new(id: plan_user['id'], shop_domain: plan_user['shop_domain'], plan_id: plan_user['plan_id'], created_by: plan_user['created_by'], created_at: plan_user['created_at'], updated_at: plan_user['updated_at'])
187
212
  end
188
- AppManager::ExtendTrial.bulk_import extend_plan_users
213
+ AppManager::PlanUser.bulk_import extend_plan_users
214
+ end
215
+ end
216
+
217
+ def save_api_promotional_discounts(promotional_discounts)
218
+ begin
219
+ AppManager::Discount.connection.truncate(AppManager::Discount.table_name)
220
+ rescue
221
+ AppManager::Discount.delete_all
222
+ end
223
+ if promotional_discounts.any?
224
+ promotional_discounts_data = []
225
+ promotional_discounts.each do |promotional_discount|
226
+
227
+ promotional_discounts_data << AppManager::Discount.new(
228
+ discount_id: promotional_discount['id'],
229
+ name: promotional_discount['name'],
230
+ code: promotional_discount['code'],
231
+ discount_type: promotional_discount['type'] || 'amount',
232
+ value: promotional_discount['value'] || 0,
233
+ duration_intervals: promotional_discount['duration_intervals'],
234
+ max_usage: promotional_discount['max_usage'],
235
+ enabled: promotional_discount['enabled'],
236
+ valid_from: promotional_discount['valid_from'],
237
+ valid_to: promotional_discount['valid_to'],
238
+ priority: promotional_discount['priority'] || 0,
239
+ multiple_uses: promotional_discount['multiple_uses'],
240
+ multiple_apps: promotional_discount['multiple_apps'],
241
+ app_id: promotional_discount['app_id'] || 0,
242
+ created_at: promotional_discount['created_at'],
243
+ updated_at: promotional_discount['updated_at'],
244
+ deleted_at: promotional_discount['deleted_at']
245
+ )
246
+ end
247
+
248
+ AppManager::Discount.bulk_import promotional_discounts_data
249
+ end
250
+ end
251
+
252
+ def save_api_promotional_discounts_shops(promotional_discounts_shops)
253
+
254
+ begin
255
+ AppManager::DiscountShop.connection.truncate(AppManager::DiscountShop.table_name)
256
+ rescue
257
+ AppManager::DiscountShop.delete_all
258
+ end
259
+
260
+ if promotional_discounts_shops.any?
261
+ promotional_discounts_shop_data = []
262
+ promotional_discounts_shops.each do |promotional_discounts_shop|
263
+ promotional_discounts_shop_data << AppManager::DiscountShop.new(
264
+ discount_id: promotional_discounts_shop['discount_id'],
265
+ domain: promotional_discounts_shop['domain']
266
+ )
267
+ end
268
+ AppManager::DiscountShop.bulk_import promotional_discounts_shop_data
269
+ end
270
+ end
271
+
272
+ def save_api_promotional_discounts_link_plans(promotional_discounts_plans)
273
+
274
+ begin
275
+ AppManager::DiscountLinkPlan.connection.truncate(AppManager::DiscountLinkPlan.table_name)
276
+ rescue
277
+ AppManager::DiscountLinkPlan.delete_all
278
+ end
279
+
280
+ if promotional_discounts_plans.any?
281
+ discount_link_plans_data = []
282
+ promotional_discounts_plans.each do |link_plan|
283
+ discount_link_plans_data << AppManager::DiscountLinkPlan.new(
284
+ discount_id: link_plan['discount_id'],
285
+ plan_id: link_plan['plan_id']
286
+ )
287
+ end
288
+ AppManager::DiscountLinkPlan.bulk_import discount_link_plans_data
289
+ end
290
+ end
291
+
292
+ def save_api_promotional_discounts_usage_log(promotional_discounts_usage_log)
293
+ begin
294
+ AppManager::DiscountUsageLog.connection.truncate(AppManager::DiscountUsageLog.table_name)
295
+ rescue
296
+ AppManager::DiscountUsageLog.delete_all
297
+ end
298
+
299
+ if promotional_discounts_usage_log.any?
300
+ promotional_discounts_usage_log_data = []
301
+ promotional_discounts_usage_log.each do |log|
302
+ promotional_discounts_usage_log_data << AppManager::DiscountUsageLog.new(
303
+ discount_id: log['discount_id'],
304
+ app_id: log['app_id'],
305
+ domain: log['domain'],
306
+ sync: log['sync'],
307
+ process_type: log['process_type'],
308
+ created_at: log['created_at'],
309
+ updated_at: log['updated_at']
310
+ )
311
+ end
312
+ AppManager::DiscountUsageLog.bulk_import promotional_discounts_usage_log_data
189
313
  end
190
314
  end
191
315
 
@@ -481,6 +605,71 @@ module AppManager
481
605
 
482
606
  end
483
607
 
608
+ def get_local_discount(params, options)
609
+ code = [params['code']].pack('H*')
610
+ shop_domain = params['shop_domain']
611
+ now = Time.now
612
+
613
+ discount_data = AppManager::Discount.where(enabled: true)
614
+ .where('valid_from <= ?', now)
615
+ .where('valid_to IS NULL OR valid_to >= ?', now)
616
+ .where(code: code)
617
+ .first
618
+ return [] if discount_data.nil?
619
+
620
+ discount_shop = AppManager::DiscountShop.where(discount_id: discount_data.discount_id).count
621
+
622
+ discount_plan = AppManager::DiscountLinkPlan.where(discount_id: discount_data.discount_id).pluck('plan_id')
623
+
624
+ discount_usage = AppManager::DiscountUsageLog.where(discount_id: discount_data.discount_id).count
625
+
626
+ discount_usage_by_domain = AppManager::DiscountUsageLog.where(discount_id: discount_data.discount_id)
627
+ .where(domain: shop_domain)
628
+ .count
629
+ if discount_shop > 0
630
+ discount_shop_specific = AppManager::DiscountShop.where(discount_id: discount_data.discount_id)
631
+ .where(domain: shop_domain)
632
+ .first
633
+ return [] if discount_shop_specific.nil?
634
+ end
635
+
636
+ if discount_data.max_usage.present? && discount_data.max_usage != 0
637
+ return [] if discount_usage >= discount_data.max_usage
638
+ end
639
+
640
+ if discount_data.multiple_uses == false && discount_usage_by_domain >= 1
641
+ return []
642
+ end
643
+
644
+ if discount_data.multiple_apps == false
645
+ discount_usage_by_app = AppManager::DiscountUsageLog
646
+ .where(discount_id: discount_data.discount_id, domain: shop_domain)
647
+ .where.not(app_id: discount_data.app_id)
648
+ .first
649
+ return [] if discount_usage_by_app.present?
650
+ end
651
+
652
+
653
+ discount_data = discount_data.attributes.symbolize_keys
654
+ discount_data[:plan_relation] = discount_plan
655
+ final_mapped_data = {
656
+ "id" => discount_data[:discount_id],
657
+ "name" => discount_data[:name],
658
+ "type" => discount_data[:discount_type],
659
+ "value" => discount_data[:value].to_f,
660
+ "duration_intervals" => discount_data[:duration_intervals],
661
+ "plan_relation" => discount_data[:plan_relation]
662
+ } rescue {}
663
+
664
+ return JSON.parse(final_mapped_data.to_json)
665
+
666
+ end
667
+
668
+ def get_local_related_discounted_plans(params, options)
669
+ discounted_plans = DiscountLinkPlan.where(discount_id: params['discount_id']).pluck(:plan_id) rescue []
670
+ return discounted_plans
671
+ end
672
+
484
673
  def store_local_charge(params, options)
485
674
  message = {"message" => 'fail'}
486
675
  if options
@@ -491,7 +680,7 @@ module AppManager
491
680
  test_value = charge["test"]
492
681
  plan_id = charge["plan_id"].to_i
493
682
  begin
494
- AppManager::Charge.create(charge_id: charge["charge_id"], test: test_value, status: charge["status"], name: charge["name"], type: charge["type"], price: charge["price"], interval: charge["interval"], trial_days: charge["trial_days"], billing_on: charge["billing_on"], activated_on: charge["activated_on"], trial_ends_on: charge["trial_ends_on"], cancelled_on: charge["cancelled_on"], expires_on: charge["expires_on"], plan_id: plan_id, description: charge["description"], shop_domain: charge["shop_domain"], created_at: charge["created_at"], updated_at: charge["updated_at"], sync: 0)
683
+ AppManager::Charge.create(charge_id: charge["charge_id"], test: test_value, status: charge["status"], name: charge["name"], type: charge["type"], price: charge["price"], interval: charge["interval"], trial_days: charge["trial_days"], billing_on: charge["billing_on"], activated_on: charge["activated_on"], trial_ends_on: charge["trial_ends_on"], cancelled_on: charge["cancelled_on"], expires_on: charge["expires_on"], plan_id: plan_id, description: charge["description"], shop_domain: charge["shop_domain"], created_at: charge["created_at"], updated_at: charge["updated_at"], sync: 0, process_type: 'store-charge')
495
684
  message = {"message" => 'success'}
496
685
  rescue Exception => e
497
686
  Rollbar.error("Charge not saved on local DB due to #{e.inspect}")
@@ -511,12 +700,32 @@ module AppManager
511
700
  return message
512
701
  end
513
702
 
703
+ def store_discount_used(params,options)
704
+ if options && options[:shop_domain].present? && options[:discount_id].present?
705
+ app_id = AppManager::Discount.where(discount_id: options[:discount_id])
706
+ .pluck(:app_id)
707
+ .first rescue nil
708
+ data = {
709
+ 'discount_id' => options[:discount_id],
710
+ 'domain' => options[:shop_domain],
711
+ 'sync' => false,
712
+ 'process_type' => 'use-discount',
713
+ 'app_id' => app_id
714
+ }
715
+ discount_usage_log = AppManager::DiscountUsageLog.create(data)
716
+ return { 'message' => discount_usage_log ? 'success' : 'fail' }
717
+ else
718
+ return {"message" => 'fail'}
719
+ end
720
+ end
721
+
514
722
 
515
723
  def sync_app_manager
516
724
  plan_obj = AppManager::Client.new
517
725
  response = plan_obj.get_status
518
726
  if response && response.code == 200
519
727
  charges = AppManager::Charge.where(sync: false)
728
+ discounts_usage_logs = AppManager::DiscountUsageLog.where(sync: false, process_type: 'use-discount')
520
729
  charges.each do |charge|
521
730
  if charge
522
731
  if !charge["cancelled_on"].nil?
@@ -529,6 +738,17 @@ module AppManager
529
738
  end
530
739
  end
531
740
  end
741
+
742
+ if discounts_usage_logs.any?
743
+ discounts_usage_logs.each do |discount_usage_log|
744
+ discount_obj = AppManager::Client.new
745
+ discount_response = discount_obj.sync_discount_usage_log(shop_domain: discount_usage_log['domain'], discount_id: discount_usage_log['discount_id'].to_i)
746
+ if discount_response && discount_response["data"] == "Saved"
747
+ AppManager::DiscountUsageLog.find_by(discount_id: discount_usage_log['discount_id'],sync: false).update(sync: true, process_type: nil)
748
+ end
749
+ end
750
+ end
751
+
532
752
  end
533
753
  end
534
754
 
@@ -54,7 +54,7 @@ module AppManager
54
54
  end
55
55
 
56
56
 
57
- def recurring_charge_api_call(plan,return_url,shop)
57
+ def recurring_charge_api_call(plan,return_url,shop,discount_local_storage)
58
58
  plan_test = nil
59
59
  shop_plan_field = AppManager.configuration.field_names['shopify_plan'] rescue nil
60
60
  if !plan['affiliate'].nil? && plan['affiliate'].any? && !shop_plan_field.nil? && plan['affiliate'].map{|e| e['value']}.include?(shop[shop_plan_field])
@@ -67,6 +67,7 @@ module AppManager
67
67
  remaining_obj = AppManager::Client.new
68
68
  remaining = remaining_obj.get_remaining_days(shop.shopify_domain,trial_activated_at,plan_id_field)
69
69
  # trial_days = !remaining.nil? ? remaining : trial_days
70
+
70
71
  if !remaining.nil?
71
72
  if !plan_id_field.nil?
72
73
  plan_obj = AppManager::Client.new
@@ -82,31 +83,70 @@ module AppManager
82
83
  end
83
84
  end
84
85
 
85
- discount_type = plan['discount_type'] || "percentage"
86
- discount_val = discount_type == "percentage" ? (plan['discount'].to_f/ 100) : "#{plan['discount']}"
87
- if !@api_version.nil? && @api_version.to_s.include?('2024')
88
- plan_discount = if plan['discount'] && plan['cycle_count']
89
- {
90
- "discount" => { "durationLimitInIntervals" => (plan['cycle_count'].to_i),
91
- "value" => {"#{discount_type}" => discount_val}
92
- }
93
- }
94
- elsif plan['discount']
95
- {
96
- "discount" => { "value" => {"#{discount_type}" => discount_val}
86
+ promotional_discount = []
87
+ if !discount_local_storage.nil? && shop
88
+ plan_obj = AppManager::Client.new
89
+ promotional_discount = plan_obj.get_promotional_discount(shop.shopify_domain, discount_local_storage)
90
+ promotional_discount = [] if promotional_discount.class.to_s == "Hash" && promotional_discount.has_key?('status') && promotional_discount['status'] == 404
91
+ end
92
+
93
+ if !plan['discount'].nil? && plan['discount'] != 0
94
+ discount_type = plan['discount_type'] || "percentage"
95
+ discount_val = discount_type == "percentage" ? (plan['discount'].to_f/ 100) : "#{plan['discount']}"
96
+ if !@api_version.nil? && @api_version.to_s.include?('2024')
97
+ plan_discount = if plan['discount'] && plan['cycle_count']
98
+ {
99
+ "discount" => { "durationLimitInIntervals" => (plan['cycle_count'].to_i),
100
+ "value" => {"#{discount_type}" => discount_val}
101
+ }
97
102
  }
98
- }
99
- else
100
- {}
101
- end
103
+ elsif plan['discount']
104
+ {
105
+ "discount" => { "value" => {"#{discount_type}" => discount_val}
106
+ }
107
+ }
108
+ else
109
+ {}
110
+ end
111
+ else
112
+ plan_discount = plan['discount'] ? { "discount" => { "durationLimitInIntervals" => (plan['cycle_count'].to_i), "value" => {"#{discount_type}" => discount_val} } } : {}
113
+ end
102
114
  else
103
- plan_discount = plan['discount'] ? { "discount" => { "durationLimitInIntervals" => (plan['cycle_count'].to_i), "value" => {"#{discount_type}" => discount_val} } } : {}
115
+
116
+ if promotional_discount.any?
117
+
118
+ if promotional_discount['plan_relation'].any? && !promotional_discount['plan_relation'].include?(plan['id'])
119
+ plan_discount = {}
120
+ else
121
+
122
+ discount_type = promotional_discount['type'] || 'percentage'
123
+ discount_value = {
124
+ discount_type => discount_type == 'percentage' ? promotional_discount['value'].to_f / 100 : [promotional_discount['value'].to_f, plan['price']].min
125
+ }
126
+
127
+ discount = { 'value' => discount_value }
128
+
129
+ discount['durationLimitInIntervals'] = promotional_discount['duration_intervals'].to_i if promotional_discount['duration_intervals'].to_i.positive?
130
+ plan_discount = { "discount" => discount}
131
+
132
+ end
133
+
134
+ end
135
+
104
136
  end
137
+
138
+ discount_exists = !plan['discount'].nil? && plan['discount'] != 0
139
+ promotional_discount_applies = promotional_discount.present? && promotional_discount.any?
140
+ promotional_discount_id = discount_exists && promotional_discount_applies ? 0 : promotional_discount_applies ? promotional_discount['id'] : 0
141
+
142
+ return_url += "&discount=#{promotional_discount_id}"
143
+
144
+
105
145
  price_details = {
106
146
  "price": { "amount": plan['price'], "currencyCode": 'USD' },
107
147
  "interval": plan['interval']['value']
108
148
  }
109
- price_details.merge! plan_discount if plan_discount.any?
149
+ price_details.merge! plan_discount if plan_discount && plan_discount.any?
110
150
 
111
151
  plan_var = {
112
152
  "appRecurringPricingDetails": price_details,
@@ -0,0 +1,34 @@
1
+ require 'uri'
2
+
3
+ # app/middleware/set_cookie.rb
4
+ module AppManager
5
+ class SetLocalStorage
6
+ def initialize(app, condition,destination_url)
7
+ @app = app
8
+ @condition = condition
9
+ @destination_url = destination_url
10
+ end
11
+
12
+ def call(env)
13
+ request = Rack::Request.new(env)
14
+
15
+ if @condition.call(request)
16
+ url = URI.parse(request.url)
17
+ host = url.host
18
+ discount_code = request.path.split('/')[2] rescue ''
19
+ AppManager.clear_cache #clearing cache
20
+ # Set a 302 response with an external URL and a cookie
21
+ return [
22
+ 302,
23
+ {
24
+ 'Location' => "#{@destination_url}?discount_code=#{discount_code}" ,
25
+ 'Content-Type' => 'text/plain'
26
+ },
27
+ ['Redirecting to external URL with cookie']
28
+ ]
29
+ end
30
+
31
+ @app.call(env)
32
+ end
33
+ end
34
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppManager
4
- VERSION = "1.7.0"
4
+ VERSION = "2.2.0"
5
5
  end
@@ -39,6 +39,14 @@ module AppManager
39
39
  end
40
40
  end
41
41
 
42
+ def create_discount_table_migration
43
+ if self.class.migration_exists?("db/app_manager", "add_discount_tables")
44
+ say_status("skipped", "Migration add_discount_tables.rb already exists")
45
+ else
46
+ migration_template("add_discount_tables.erb", "db/app_manager/add_discount_tables.rb")
47
+ end
48
+ end
49
+
42
50
 
43
51
  private
44
52
 
@@ -0,0 +1,48 @@
1
+ class AddDiscountTables < ActiveRecord::Migration[<%= rails_migration_version %>]
2
+ def change
3
+ create_table :discounts do |t|
4
+ t.bigint :discount_id
5
+ t.string :name
6
+ t.string :code
7
+ t.string :discount_type, default: 'amount'
8
+ t.decimal :value, default: 0
9
+ t.integer :duration_intervals
10
+ t.integer :max_usage
11
+ t.boolean :enabled, default: true
12
+ t.datetime :valid_from
13
+ t.datetime :valid_to
14
+ t.integer :priority, default: 0
15
+ t.boolean :multiple_uses, default: true
16
+ t.boolean :multiple_apps, default: true
17
+ t.integer :app_id, default: 0
18
+ t.timestamps
19
+ t.datetime :deleted_at
20
+ end
21
+
22
+ create_table :discount_shops do |t|
23
+ t.bigint :discount_id, index: true, foreign_key: true
24
+ t.string :domain
25
+ end
26
+
27
+ create_table :discounts_usage_log do |t|
28
+ t.bigint :discount_id, index: true, foreign_key: true
29
+ t.bigint :app_id, index: true, foreign_key: true
30
+ t.string :domain
31
+ t.boolean :sync, default: true
32
+ t.string :process_type
33
+ t.timestamps
34
+ end
35
+
36
+ create_table :discount_link_plans do |t|
37
+ t.bigint :discount_id, index: true, foreign_key: true
38
+ t.bigint :plan_id, index: true, foreign_key: true
39
+ end
40
+ end
41
+
42
+ def down
43
+ drop_table :discounts
44
+ drop_table :discount_shops
45
+ drop_table :discounts_usage_log
46
+ drop_table :discount_link_plans
47
+ end
48
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rahul Tiwari @ Hulkapps
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-09 00:00:00.000000000 Z
11
+ date: 2024-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -89,7 +89,11 @@ files:
89
89
  - app/model/app_manager/app_manager_record.rb
90
90
  - app/model/app_manager/app_structure.rb
91
91
  - app/model/app_manager/charge.rb
92
+ - app/model/app_manager/discount.rb
93
+ - app/model/app_manager/discount_link_plan.rb
92
94
  - app/model/app_manager/discount_plan.rb
95
+ - app/model/app_manager/discount_shop.rb
96
+ - app/model/app_manager/discount_usage_log.rb
93
97
  - app/model/app_manager/extend_trial.rb
94
98
  - app/model/app_manager/plan.rb
95
99
  - app/model/app_manager/plan_user.rb
@@ -115,9 +119,11 @@ files:
115
119
  - lib/app_manager/protection.rb
116
120
  - lib/app_manager/railtie.rb
117
121
  - lib/app_manager/response_cache.rb
122
+ - lib/app_manager/set_local_storage.rb
118
123
  - lib/app_manager/tasks/sync/local_app_manager.rake
119
124
  - lib/app_manager/version.rb
120
125
  - lib/generators/app_manager/install/install_generator.rb
126
+ - lib/generators/app_manager/install/templates/add_discount_tables.erb
121
127
  - lib/generators/app_manager/install/templates/add_external_charge_field.erb
122
128
  - lib/generators/app_manager/install/templates/add_plan_trial_grandfathered_to_shops.erb
123
129
  - lib/generators/app_manager/install/templates/app_manager.rb.tt