solidus_subscriptions 1.0.0.rc1

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 (204) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +41 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.github_changelog_generator +2 -0
  6. data/.gitignore +20 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +12 -0
  9. data/.rubocop_todo.yml +86 -0
  10. data/CHANGELOG.md +191 -0
  11. data/Gemfile +33 -0
  12. data/LICENSE +26 -0
  13. data/README.md +221 -0
  14. data/Rakefile +6 -0
  15. data/app/assets/javascripts/spree/backend/solidus_subscriptions.js +1 -0
  16. data/app/assets/javascripts/spree/backend/solidus_subscriptions/edit_subscription_payment.js +32 -0
  17. data/app/controllers/solidus_subscriptions/api/v1/base_controller.rb +13 -0
  18. data/app/controllers/solidus_subscriptions/api/v1/line_items_controller.rb +48 -0
  19. data/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb +100 -0
  20. data/app/controllers/spree/admin/installments_controller.rb +25 -0
  21. data/app/controllers/spree/admin/subscription_events_controller.rb +37 -0
  22. data/app/controllers/spree/admin/subscription_orders_controller.rb +35 -0
  23. data/app/controllers/spree/admin/subscriptions_controller.rb +100 -0
  24. data/app/controllers/spree/admin/users/subscriptions_controller.rb +17 -0
  25. data/app/decorators/controllers/solidus_subscriptions/spree/api/line_items_controller/create_subscription_line_items.rb +36 -0
  26. data/app/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items.rb +35 -0
  27. data/app/decorators/models/solidus_subscriptions/spree/line_item/subscription_line_items_association.rb +26 -0
  28. data/app/decorators/models/solidus_subscriptions/spree/order/after_create.rb +19 -0
  29. data/app/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions.rb +23 -0
  30. data/app/decorators/models/solidus_subscriptions/spree/order/installment_details_association.rb +15 -0
  31. data/app/decorators/models/solidus_subscriptions/spree/order/subscription_association.rb +15 -0
  32. data/app/decorators/models/solidus_subscriptions/spree/order/subscription_line_items_association.rb +19 -0
  33. data/app/decorators/models/solidus_subscriptions/spree/product/delegate_subscribable.rb +17 -0
  34. data/app/decorators/models/solidus_subscriptions/spree/user/have_many_subscriptions.rb +30 -0
  35. data/app/decorators/models/solidus_subscriptions/spree/variant/auto_delete_from_subscriptions.rb +20 -0
  36. data/app/decorators/models/solidus_subscriptions/spree/variant/variant_pretty_name.rb +17 -0
  37. data/app/decorators/models/solidus_subscriptions/spree/wallet_payment_source/report_default_change_to_subscriptions.rb +28 -0
  38. data/app/jobs/solidus_subscriptions/process_installment_job.rb +13 -0
  39. data/app/jobs/solidus_subscriptions/process_subscription_job.rb +31 -0
  40. data/app/models/solidus_subscriptions/installment.rb +135 -0
  41. data/app/models/solidus_subscriptions/installment_detail.rb +28 -0
  42. data/app/models/solidus_subscriptions/interval.rb +26 -0
  43. data/app/models/solidus_subscriptions/line_item.rb +42 -0
  44. data/app/models/solidus_subscriptions/promotion/rules/subscription_creation_order.rb +44 -0
  45. data/app/models/solidus_subscriptions/promotion/rules/subscription_installment_order.rb +31 -0
  46. data/app/models/solidus_subscriptions/subscription.rb +392 -0
  47. data/app/models/solidus_subscriptions/subscription_event.rb +11 -0
  48. data/app/overrides/views/admin_subscribable_product_checkbox.rb +8 -0
  49. data/app/overrides/views/admin_subscribable_variant_checkbox.rb +8 -0
  50. data/app/overrides/views/admin_subscriptions_menu_link.rb +10 -0
  51. data/app/overrides/views/admin_users_subscriptions_tab.rb +8 -0
  52. data/app/overrides/views/subscription_line_item_fields.rb +8 -0
  53. data/app/subscribers/solidus_subscriptions/churn_buster_subscriber.rb +39 -0
  54. data/app/subscribers/solidus_subscriptions/event_storage_subscriber.rb +64 -0
  55. data/app/views/spree/admin/installments/_state_pill.html.erb +8 -0
  56. data/app/views/spree/admin/installments/index.html.erb +42 -0
  57. data/app/views/spree/admin/products/_subscribable_checkbox.html.erb +8 -0
  58. data/app/views/spree/admin/promotions/rules/_subscription_creation_order.html.erb +0 -0
  59. data/app/views/spree/admin/promotions/rules/_subscription_installment_order.html.erb +0 -0
  60. data/app/views/spree/admin/shared/_subscription_actions.html.erb +35 -0
  61. data/app/views/spree/admin/shared/_subscription_breadcrumbs.html.erb +4 -0
  62. data/app/views/spree/admin/shared/_subscription_sidebar.html.erb +18 -0
  63. data/app/views/spree/admin/shared/_subscription_tab.html.erb +3 -0
  64. data/app/views/spree/admin/shared/_subscription_tabs.html.erb +18 -0
  65. data/app/views/spree/admin/subscription_events/_state_pill.html.erb +8 -0
  66. data/app/views/spree/admin/subscription_events/index.html.erb +42 -0
  67. data/app/views/spree/admin/subscription_orders/index.html.erb +93 -0
  68. data/app/views/spree/admin/subscriptions/_form.html.erb +150 -0
  69. data/app/views/spree/admin/subscriptions/_processing_state_pill.html.erb +9 -0
  70. data/app/views/spree/admin/subscriptions/_state_pill.html.erb +10 -0
  71. data/app/views/spree/admin/subscriptions/edit.html.erb +10 -0
  72. data/app/views/spree/admin/subscriptions/index.html.erb +176 -0
  73. data/app/views/spree/admin/subscriptions/new.html.erb +5 -0
  74. data/app/views/spree/admin/users/_subscription_tab.html.erb +5 -0
  75. data/app/views/spree/admin/users/subscriptions/index.html.erb +44 -0
  76. data/app/views/spree/admin/variants/_subscribable_checkbox.html.erb +6 -0
  77. data/app/views/spree/frontend/products/_subscription_line_item_fields.html.erb +30 -0
  78. data/bin/console +17 -0
  79. data/bin/rails +7 -0
  80. data/bin/rails-engine +13 -0
  81. data/bin/rails-sandbox +16 -0
  82. data/bin/rake +7 -0
  83. data/bin/sandbox +86 -0
  84. data/bin/setup +8 -0
  85. data/config/initializers/permission_sets.rb +11 -0
  86. data/config/initializers/subscribers.rb +9 -0
  87. data/config/locales/en.yml +129 -0
  88. data/config/routes.rb +34 -0
  89. data/db/migrate/20160825164850_create_solidus_subscriptions_subscriptions.rb +11 -0
  90. data/db/migrate/20160825173548_create_solidus_subscriptions_line_items.rb +17 -0
  91. data/db/migrate/20160825202248_create_solidus_subscriptions_installments.rb +23 -0
  92. data/db/migrate/20160825211202_create_solidus_subscriptions_installment_details.rb +22 -0
  93. data/db/migrate/20160825214240_add_subscribable_to_spree_variants.rb +5 -0
  94. data/db/migrate/20160829201653_change_subscription_line_items_installments_to_max_installments.rb +5 -0
  95. data/db/migrate/20160902220242_remove_state_from_solidus_susbscriptions_installment_details.rb +5 -0
  96. data/db/migrate/20160902220604_add_successful_to_solidus_subscriptions_installment_details.rb +5 -0
  97. data/db/migrate/20160902221218_add_message_to_solidus_subscriptions_installment_details.rb +5 -0
  98. data/db/migrate/20160922164101_add_interval_length_and_units_to_subscription_line_items.rb +8 -0
  99. data/db/migrate/20161006191003_add_skip_count_to_solidus_subscriptions_subscriptions.rb +5 -0
  100. data/db/migrate/20161006191127_add_successive_skip_count_to_solidus_subscriptions_subscriptions.rb +5 -0
  101. data/db/migrate/20161014212649_allow_spree_line_item_id_to_be_null.rb +5 -0
  102. data/db/migrate/20161017155749_add_order_id_to_solidus_subscriptions_installment_details.rb +6 -0
  103. data/db/migrate/20161017175509_remove_order_id_from_solidus_subscriptions_installments.rb +5 -0
  104. data/db/migrate/20161017201944_add_subscription_order_to_spree_orders.rb +5 -0
  105. data/db/migrate/20161221155142_add_store_to_solidus_subscriptions_subscriptions.rb +6 -0
  106. data/db/migrate/20161223152905_add_address_id_to_solidus_subscriptions_subscriptions.rb +7 -0
  107. data/db/migrate/20170106224713_change_line_item_max_installments_to_end_date.rb +6 -0
  108. data/db/migrate/20170111224458_change_subscription_actionable_date_to_datetime.rb +5 -0
  109. data/db/migrate/20170111232801_change_inteval_actionable_date_to_datetime.rb +5 -0
  110. data/db/migrate/20170112012407_add_config_options_to_subscriptions.rb +7 -0
  111. data/db/migrate/20200617102749_add_billing_address_to_subscriptions.rb +11 -0
  112. data/db/migrate/20200617155042_add_payment_source_to_subscriptions.rb +6 -0
  113. data/db/migrate/20200618092951_add_payment_method_to_subscriptions.rb +11 -0
  114. data/db/migrate/20200730101242_create_solidus_subscriptions_subscription_events.rb +22 -0
  115. data/db/migrate/20200917072152_add_subscription_reference_to_orders.rb +11 -0
  116. data/db/migrate/20201007140032_add_guest_token_to_subscriptions.rb +6 -0
  117. data/db/migrate/20201123171026_change_actionable_date_to_date.rb +15 -0
  118. data/db/migrate/20210205140422_add_currency_to_subscription.rb +5 -0
  119. data/db/migrate/20210323165714_update_promotion_rule_names.rb +22 -0
  120. data/lib/generators/solidus_subscriptions/install/install_generator.rb +32 -0
  121. data/lib/generators/solidus_subscriptions/install/templates/initializer.rb +99 -0
  122. data/lib/solidus_subscriptions.rb +49 -0
  123. data/lib/solidus_subscriptions/checkout.rb +74 -0
  124. data/lib/solidus_subscriptions/churn_buster/client.rb +48 -0
  125. data/lib/solidus_subscriptions/churn_buster/order_serializer.rb +19 -0
  126. data/lib/solidus_subscriptions/churn_buster/serializer.rb +23 -0
  127. data/lib/solidus_subscriptions/churn_buster/subscription_customer_serializer.rb +28 -0
  128. data/lib/solidus_subscriptions/churn_buster/subscription_payment_method_serializer.rb +37 -0
  129. data/lib/solidus_subscriptions/churn_buster/subscription_serializer.rb +17 -0
  130. data/lib/solidus_subscriptions/configuration.rb +84 -0
  131. data/lib/solidus_subscriptions/dispatcher/base.rb +18 -0
  132. data/lib/solidus_subscriptions/dispatcher/failure_dispatcher.rb +13 -0
  133. data/lib/solidus_subscriptions/dispatcher/out_of_stock_dispatcher.rb +11 -0
  134. data/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher.rb +19 -0
  135. data/lib/solidus_subscriptions/dispatcher/success_dispatcher.rb +17 -0
  136. data/lib/solidus_subscriptions/engine.rb +56 -0
  137. data/lib/solidus_subscriptions/permission_sets/default_customer.rb +19 -0
  138. data/lib/solidus_subscriptions/permission_sets/subscription_management.rb +12 -0
  139. data/lib/solidus_subscriptions/permitted_attributes.rb +20 -0
  140. data/lib/solidus_subscriptions/processor.rb +17 -0
  141. data/lib/solidus_subscriptions/subscription_generator.rb +77 -0
  142. data/lib/solidus_subscriptions/subscription_line_item_builder.rb +23 -0
  143. data/lib/solidus_subscriptions/testing_support/factories/installment_detail_factory.rb +11 -0
  144. data/lib/solidus_subscriptions/testing_support/factories/installment_factory.rb +29 -0
  145. data/lib/solidus_subscriptions/testing_support/factories/line_item_factory.rb +20 -0
  146. data/lib/solidus_subscriptions/testing_support/factories/spree/line_item_factory.rb +17 -0
  147. data/lib/solidus_subscriptions/testing_support/factories/spree/order_factory.rb +17 -0
  148. data/lib/solidus_subscriptions/testing_support/factories/spree_modification_factory.rb +10 -0
  149. data/lib/solidus_subscriptions/testing_support/factories/subscription_event_factory.rb +8 -0
  150. data/lib/solidus_subscriptions/testing_support/factories/subscription_factory.rb +56 -0
  151. data/lib/solidus_subscriptions/version.rb +5 -0
  152. data/lib/tasks/process_subscriptions.rake +8 -0
  153. data/reference/solidus_subscriptions.v1.yaml +290 -0
  154. data/solidus_subscriptions.gemspec +47 -0
  155. data/spec/controllers/spree/admin/subscriptions_controller_spec.rb +202 -0
  156. data/spec/controllers/spree/api/line_items_controller_spec.rb +103 -0
  157. data/spec/controllers/spree/api/orders_controller_spec.rb +57 -0
  158. data/spec/controllers/spree/api/users_controller_spec.rb +48 -0
  159. data/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb +80 -0
  160. data/spec/decorators/models/solidus_subscriptions/spree/line_item/subscription_line_items_association_spec.rb +10 -0
  161. data/spec/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions_spec.rb +32 -0
  162. data/spec/decorators/models/solidus_subscriptions/spree/order/installment_details_association_spec.rb +9 -0
  163. data/spec/decorators/models/solidus_subscriptions/spree/order/subscription_line_items_association_spec.rb +9 -0
  164. data/spec/decorators/models/solidus_subscriptions/spree/user/have_many_subscriptions_spec.rb +22 -0
  165. data/spec/decorators/models/solidus_subscriptions/spree/variant/auto_delete_from_subscriptions_spec.rb +25 -0
  166. data/spec/features/admin/subscription_orders_spec.rb +35 -0
  167. data/spec/features/admin/subscriptions_spec.rb +63 -0
  168. data/spec/features/admin_users_subscription_tabs_spec.rb +61 -0
  169. data/spec/fixtures/cassettes/churn_buster.yml +229 -0
  170. data/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb +38 -0
  171. data/spec/jobs/solidus_subscriptions/process_subscription_job_spec.rb +83 -0
  172. data/spec/lib/solidus_subscriptions/checkout_spec.rb +125 -0
  173. data/spec/lib/solidus_subscriptions/churn_buster/client_spec.rb +59 -0
  174. data/spec/lib/solidus_subscriptions/dispatcher/failure_dispatcher_spec.rb +29 -0
  175. data/spec/lib/solidus_subscriptions/dispatcher/out_of_stock_dispatcher_spec.rb +15 -0
  176. data/spec/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher_spec.rb +44 -0
  177. data/spec/lib/solidus_subscriptions/dispatcher/success_dispatcher_spec.rb +30 -0
  178. data/spec/lib/solidus_subscriptions/permission_sets/default_customer_spec.rb +95 -0
  179. data/spec/lib/solidus_subscriptions/permission_sets/subscription_management_spec.rb +26 -0
  180. data/spec/lib/solidus_subscriptions/processor_spec.rb +34 -0
  181. data/spec/lib/solidus_subscriptions/promotion/rules/subscription_creation_order_spec.rb +57 -0
  182. data/spec/lib/solidus_subscriptions/promotion/rules/subscription_installment_order_spec.rb +39 -0
  183. data/spec/lib/solidus_subscriptions/subscription_generator_spec.rb +83 -0
  184. data/spec/lib/solidus_subscriptions_spec.rb +30 -0
  185. data/spec/models/solidus_subscriptions/installment_detail_spec.rb +23 -0
  186. data/spec/models/solidus_subscriptions/installment_spec.rb +201 -0
  187. data/spec/models/solidus_subscriptions/line_item_spec.rb +29 -0
  188. data/spec/models/solidus_subscriptions/subscription_spec.rb +814 -0
  189. data/spec/models/spree/variant_spec.rb +16 -0
  190. data/spec/models/spree/wallet_payment_source_spec.rb +20 -0
  191. data/spec/requests/api/v1/line_items_spec.rb +116 -0
  192. data/spec/requests/api/v1/subscriptions_spec.rb +255 -0
  193. data/spec/spec_helper.rb +30 -0
  194. data/spec/subscribers/solidus_subscriptions/churn_buster_subscriber_spec.rb +76 -0
  195. data/spec/support/active_model_mocks.rb +1 -0
  196. data/spec/support/cancancan.rb +1 -0
  197. data/spec/support/factories.rb +1 -0
  198. data/spec/support/helpers/checkout_infrastructure.rb +18 -0
  199. data/spec/support/helpers/config.rb +13 -0
  200. data/spec/support/shoulda.rb +7 -0
  201. data/spec/support/timecop.rb +1 -0
  202. data/spec/support/vcr.rb +10 -0
  203. data/spec/support/version_cake.rb +8 -0
  204. metadata +498 -0
data/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # solidus_subscriptions
2
+
3
+ [![CircleCI](https://circleci.com/gh/solidusio-contrib/solidus_subscriptions.svg?style=shield)](https://circleci.com/gh/solidusio-contrib/solidus_subscriptions)
4
+ [![codecov](https://codecov.io/gh/solidusio-contrib/solidus_subscriptions/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio-contrib/solidus_subscriptions)
5
+
6
+ A Solidus extension to add subscriptions to your store.
7
+
8
+ ## Installation
9
+
10
+ Add solidus_subscriptions to your Gemfile:
11
+
12
+ ```ruby
13
+ gem 'solidus_subscriptions', github: 'solidusio-contrib/solidus_subscriptions'
14
+ ```
15
+
16
+ Bundle your dependencies and run the installation generator:
17
+
18
+ ```shell
19
+ $ bundle
20
+ $ bin/rails generate solidus_subscriptions:install
21
+ ```
22
+
23
+ ### Guest checkout
24
+
25
+ Subscriptions require a user to be present to allow them to be managed after they are purchased.
26
+
27
+ Because of this, you must disable guest checkout for orders which contain `subscription_line_items`.
28
+
29
+ An example would be adding this to the registration page:
30
+
31
+ ```erb
32
+ <%# spree/checkout/registration.html.erb %>
33
+ <% if Spree::Config[:allow_guest_checkout] && current_order.subscription_line_items.empty? %>
34
+ ```
35
+
36
+ This allows guests to add subscriptions to their carts as guests, but forces them to login or create
37
+ an account before purchasing them.
38
+
39
+ ## Usage
40
+
41
+ ### Purchasing subscriptions
42
+
43
+ By default, only Spree::Variants can be subscribed to. To subscribe to a variant, it must have the
44
+ `subscribable` attribute set to true.
45
+
46
+ To subscribe to a variant, include the following parameters when posting to `/orders/populate`:
47
+
48
+ ```json5
49
+ {
50
+ // other add to cart params
51
+ subscription_line_item: {
52
+ quantity: 2, // number of units in each subscription order
53
+ subscribable_id: 1234, // which variant the subscription is for
54
+ interval_length: 1, // time between subscription activations
55
+ interval_units: "month", // plural qualifier for length (day/week/month/year)
56
+ end_date: '2011/12/13' // stop processing after this date (use null to process ad nauseam)
57
+ }
58
+ }
59
+ ```
60
+
61
+ This will associate a `SolidusSubscriptions::LineItem` to the line item being added to the cart.
62
+
63
+ The customer will not be charged for the subscription until it is processed. The subscription line
64
+ items should be shown to the user on the cart page by looping over
65
+ `Spree::Order#subscription_line_items`.
66
+
67
+ When the order is finalized, a `SolidusSubscriptions::Subscription` will be created for each group
68
+ of subscription line items which can be fulfilled by a single subscription.
69
+
70
+ #### Example
71
+
72
+ An order is finalized and has the following associated subscription line items:
73
+
74
+ 1. `{ subscribable_id: 1, interval_length: 1, interval_units: "month" }`
75
+ 2. `{ subscribable_id: 2, interval_length: 1, interval_units: "month" }`
76
+ 3. `{ subscribable_id: 1, interval_length: 2, interval_units: "month" }`
77
+
78
+ This will generate 2 subscriptions: the first related to subscription line items 1 and 2, and the
79
+ second related to subscription line item 3.
80
+
81
+ ### Processing subscriptions
82
+
83
+ To process actionable subscriptions simply run:
84
+
85
+ ```bash
86
+ $ bundle exec rake solidus_subscriptions:process
87
+ ```
88
+
89
+ The task creates ActiveJob jobs which can be fulfilled by your queue library of choice.
90
+
91
+ We suggest using the [Whenever](https://github.com/javan/whenever) gem to schedule the task.
92
+
93
+ ### Promotion rules
94
+ This extensions adds the following [Promotion rules](https://guides.solidus.io/developers/promotions/promotion-rules.html):
95
+ * `SolidusSubscriptions::Promotion::Rules::SubscriptionCreationOrder` which applies if the order is creating a subscription;
96
+ * `SolidusSubscriptions::Promotion::Rules::SubscriptionInstallmentOrder` which applies if the order is an installment of a subscription.
97
+
98
+ ### API documentation
99
+
100
+ You can find the API documentation [here](https://stoplight.io/p/docs/gh/solidusio-contrib/solidus_subscriptions?group=master).
101
+
102
+ ### Churn Buster integration
103
+
104
+ This extension optionally integrates with [Churn Buster](https://churnbuster.io) for failed payment
105
+ recovery. In order to enable the integration, simply add your Churn Buster credentials to your
106
+ configuration:
107
+
108
+ ```ruby
109
+ SolidusSubscriptions.configure do |config|
110
+ # ...
111
+
112
+ config.churn_buster_account_id = 'YOUR_CHURN_BUSTER_ACCOUNT_ID'
113
+ config.churn_buster_api_key = 'YOUR_CHURN_BUSTER_API_KEY'
114
+ end
115
+ ```
116
+
117
+ The extension will take care of reporting successful/failed payments and payment method changes
118
+ to Churn Buster.
119
+
120
+ ### Failed installments retries
121
+
122
+ The extension generates an installment for each subscription cycle, however some of them can fail
123
+ (e.g. for an expired credit card). On each processor run the extension will try to complete all past
124
+ failed installments, however this is not always the desired behaviour.
125
+
126
+ If you want to process only the latest installment in each subscription, regardless of any number of
127
+ failed installments prior to that, you can configure the extension like so:
128
+
129
+ ```ruby
130
+ SolidusSubscriptions.configure do |config|
131
+ # ...
132
+
133
+ config.clear_past_installments = true
134
+ end
135
+ ```
136
+
137
+ ### Minimum cancellation notice
138
+
139
+ The minimum cancellation notice is set to 0 days by default - users can cancel their subscription whenever they like. To change this, you can configure the extension like this:
140
+
141
+ ```ruby
142
+ SolidusSubscriptions.configure do |config|
143
+ # ...
144
+
145
+ config.minimum_cancellation_notice = 10.days
146
+ end
147
+ ```
148
+
149
+ ### Subscription product deletion
150
+ When a product is soft deleted, its subscription line items need to be deleted as well, in order to avoid error on subscription processing.
151
+ If the product class is `Spree::Variant`, this corner case is handled automatically on the variant soft deletion, otherwise it should be handled manually.
152
+
153
+ ## Development
154
+
155
+ ### Testing the extension
156
+
157
+ First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
158
+ app if it does not exist, then it will run specs. The dummy app can be regenerated by using
159
+ `bin/rake extension:test_app`.
160
+
161
+ ```shell
162
+ bin/rake
163
+ ```
164
+
165
+ To run [Rubocop](https://github.com/bbatsov/rubocop) static code analysis run
166
+
167
+ ```shell
168
+ bundle exec rubocop
169
+ ```
170
+
171
+ When testing your application's integration with this extension you may use its factories.
172
+ Simply add this require statement to your spec_helper:
173
+
174
+ ```ruby
175
+ require 'solidus_subscriptions/factories'
176
+ ```
177
+
178
+ ### Running the sandbox
179
+
180
+ To run this extension in a sandboxed Solidus application, you can run `bin/sandbox`. The path for
181
+ the sandbox app is `./sandbox` and `bin/rails` will forward any Rails commands to
182
+ `sandbox/bin/rails`.
183
+
184
+ Here's an example:
185
+
186
+ ```
187
+ $ bin/rails server
188
+ => Booting Puma
189
+ => Rails 6.0.2.1 application starting in development
190
+ * Listening on tcp://127.0.0.1:3000
191
+ Use Ctrl-C to stop
192
+ ```
193
+
194
+ ### Updating the changelog
195
+
196
+ Before and after releases the changelog should be updated to reflect the up-to-date status of
197
+ the project:
198
+
199
+ ```shell
200
+ bin/rake changelog
201
+ git add CHANGELOG.md
202
+ git commit -m "Update the changelog"
203
+ ```
204
+
205
+ ### Releasing new versions
206
+
207
+ Your new extension version can be released using `gem-release` like this:
208
+
209
+ ```shell
210
+ bundle exec gem bump -v 1.6.0
211
+ bin/rake changelog
212
+ git commit -a --amend
213
+ git push
214
+ bundle exec gem release
215
+ ```
216
+
217
+ ## License
218
+
219
+ Copyright (c) 2016 Stembolt, released under the New BSD License
220
+
221
+ Originally sponsored by [Goby](https://www.goby.co).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_dev_support/rake_tasks'
4
+ SolidusDevSupport::RakeTasks.install
5
+
6
+ task default: 'extension:specs'
@@ -0,0 +1 @@
1
+ //= require spree/backend/solidus_subscriptions/edit_subscription_payment
@@ -0,0 +1,32 @@
1
+ Spree.Views.EditSubscriptionPayment = Backbone.View.extend({
2
+ initialize: function() {
3
+ this.onSelectMethod()
4
+ },
5
+
6
+ events: {
7
+ 'change [name="subscription[payment_method_id]"]': 'onSelectMethod',
8
+ },
9
+
10
+ onSelectMethod: function(e) {
11
+ this.selectedId = parseInt(this.$('select[name="subscription[payment_method_id]"]').val())
12
+ this.render()
13
+ },
14
+
15
+ render: function() {
16
+ let selectedId = this.selectedId
17
+
18
+ this.$('select[name="subscription[payment_source_id]"] option').each(function () {
19
+ if (!$(this).data('js-payment-method-id') || parseInt($(this).data('js-payment-method-id')) === selectedId) {
20
+ $(this).prop('disabled', false)
21
+ } else {
22
+ $(this).prop('disabled', true)
23
+ }
24
+ })
25
+ },
26
+ });
27
+
28
+ Spree.ready(function() {
29
+ $(".js-edit-subscription-payment").each(function() {
30
+ new Spree.Views.EditSubscriptionPayment({ el: this })
31
+ });
32
+ });
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusSubscriptions
4
+ module Api
5
+ module V1
6
+ class BaseController < ::Spree::Api::BaseController
7
+ def subscription_guest_token
8
+ request.headers['X-Spree-Subscription-Token']
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusSubscriptions
4
+ module Api
5
+ module V1
6
+ class LineItemsController < BaseController
7
+ protect_from_forgery unless: -> { request.format.json? }
8
+
9
+ wrap_parameters :subscription_line_item
10
+
11
+ def update
12
+ load_line_item
13
+
14
+ if @line_item.update(line_item_params)
15
+ render json: @line_item.to_json
16
+ else
17
+ render json: @line_item.errors.to_json, status: :unprocessable_entity
18
+ end
19
+ end
20
+
21
+ def destroy
22
+ load_line_item
23
+
24
+ @line_item.destroy!
25
+
26
+ if @line_item.order && !@line_item.order.complete?
27
+ @line_item.order.recalculate
28
+ end
29
+
30
+ render json: @line_item.to_json
31
+ end
32
+
33
+ private
34
+
35
+ def load_line_item
36
+ @line_item = SolidusSubscriptions::LineItem.find(params[:id])
37
+ authorize! action_name.to_sym, @line_item, subscription_guest_token
38
+ end
39
+
40
+ def line_item_params
41
+ params.require(:subscription_line_item).permit(
42
+ SolidusSubscriptions::PermittedAttributes.subscription_line_item_attributes - [:subscribable_id]
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusSubscriptions
4
+ module Api
5
+ module V1
6
+ class SubscriptionsController < BaseController
7
+ protect_from_forgery unless: -> { request.format.json? }
8
+
9
+ def create
10
+ store = params[:store_id].nil? ? ::Spree::Store.default : ::Spree::Store.find(id: params[:store_id])
11
+ attributes = create_subscription_params.merge(user: current_api_user, store: store)
12
+ acceptable_payment_details = update_payment_attributes(attributes)
13
+
14
+ if acceptable_payment_details
15
+ subscription = SolidusSubscriptions::Subscription.new(attributes)
16
+
17
+ if subscription.save
18
+ render json: subscription.to_json(include: [:line_items, :shipping_address, :billing_address])
19
+ else
20
+ render json: subscription.errors.to_json, status: :unprocessable_entity
21
+ end
22
+ else
23
+ error_message = I18n.t('solidus_subscriptions.subscription.invalid_payment_details')
24
+
25
+ render json: { payment_source_type: [error_message] }.to_json, status: :unprocessable_entity
26
+ end
27
+ end
28
+
29
+ def update
30
+ load_subscription
31
+
32
+ if @subscription.update(subscription_params)
33
+ render json: @subscription.to_json(include: [:line_items, :shipping_address, :billing_address])
34
+ else
35
+ render json: @subscription.errors.to_json, status: :unprocessable_entity
36
+ end
37
+ end
38
+
39
+ def skip
40
+ load_subscription
41
+
42
+ if @subscription.skip
43
+ render json: @subscription.to_json
44
+ else
45
+ render json: @subscription.errors.to_json, status: :unprocessable_entity
46
+ end
47
+ end
48
+
49
+ def cancel
50
+ load_subscription
51
+
52
+ if @subscription.cancel
53
+ render json: @subscription.to_json
54
+ else
55
+ render json: @subscription.errors.to_json, status: :unprocessable_entity
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def load_subscription
62
+ @subscription = SolidusSubscriptions::Subscription.find(params[:id])
63
+ authorize! action_name.to_sym, @subscription, subscription_guest_token
64
+ end
65
+
66
+ def create_subscription_params
67
+ params.require(:subscription).permit(
68
+ %i[payment_method_id payment_source_id shipping_address_id billing_address_id] |
69
+ SolidusSubscriptions.configuration.subscription_attributes |
70
+ [line_items_attributes: line_item_attributes]
71
+ )
72
+ end
73
+
74
+ def subscription_params
75
+ params.require(:subscription).permit(SolidusSubscriptions.configuration.subscription_attributes | [
76
+ line_items_attributes: line_item_attributes,
77
+ ])
78
+ end
79
+
80
+ def line_item_attributes
81
+ SolidusSubscriptions.configuration.subscription_line_item_attributes - [:subscribable_id] + [:id]
82
+ end
83
+
84
+ def update_payment_attributes(attributes)
85
+ return true if attributes[:payment_method_id].blank? && attributes[:payment_source_id].blank?
86
+
87
+ payment_method = ::Spree::PaymentMethod.find_by(id: attributes[:payment_method_id])
88
+ payment_source = payment_method&.payment_source_class&.find_by(id: attributes[:payment_source_id])
89
+ if payment_method && payment_source
90
+ attributes[:payment_method] = payment_method
91
+ attributes[:payment_source] = payment_source
92
+ true
93
+ else
94
+ false
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Admin
5
+ class InstallmentsController < ResourceController
6
+ belongs_to 'subscription', model_class: SolidusSubscriptions::Subscription
7
+
8
+ skip_before_action :load_resource, only: :index
9
+
10
+ def index
11
+ @search = collection.ransack((params[:q] || {}).reverse_merge(s: 'created_at desc'))
12
+
13
+ @installments = @search.result(distinct: true).
14
+ page(params[:page]).
15
+ per(params[:per_page] || Spree::Config[:orders_per_page])
16
+ end
17
+
18
+ private
19
+
20
+ def model_class
21
+ ::SolidusSubscriptions::Installment
22
+ end
23
+ end
24
+ end
25
+ end