spree_vpago 0.1.0.pre.beta

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 (262) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +3 -0
  3. data/.github/workflows/test_and_publish_gem.yml +96 -0
  4. data/.gitignore +24 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +56 -0
  7. data/.ruby-version +1 -0
  8. data/.tool-versions +1 -0
  9. data/.travis.yml +45 -0
  10. data/Appraisals +20 -0
  11. data/Gemfile +27 -0
  12. data/Gemfile.lock +656 -0
  13. data/LICENSE +26 -0
  14. data/README.md +151 -0
  15. data/Rakefile +21 -0
  16. data/app/.gitkeep +0 -0
  17. data/app/assets/config/spree_vpago_manifest.js +3 -0
  18. data/app/assets/images/backend-process.svg +3 -0
  19. data/app/assets/images/vpago/payway/abapay.png +0 -0
  20. data/app/assets/images/vpago/payway/cards.png +0 -0
  21. data/app/assets/javascripts/vpago/vpago_payments/request_process_payment.js +44 -0
  22. data/app/assets/javascripts/vpago/vpago_payments/user_informers/firebase.js +15802 -0
  23. data/app/controllers/.gitkeep +0 -0
  24. data/app/controllers/spree/acleda_redirects_controller.rb +22 -0
  25. data/app/controllers/spree/admin/payment_methods_controller_decorator.rb +65 -0
  26. data/app/controllers/spree/admin/payment_payway_base_controller.rb +25 -0
  27. data/app/controllers/spree/admin/payment_payway_checkers_controller.rb +34 -0
  28. data/app/controllers/spree/admin/payment_payway_markers_controller.rb +44 -0
  29. data/app/controllers/spree/admin/payment_payway_queriers_controller.rb +39 -0
  30. data/app/controllers/spree/admin/payment_wing_sdk_base_controller.rb +21 -0
  31. data/app/controllers/spree/admin/payment_wing_sdk_checkers_controller.rb +26 -0
  32. data/app/controllers/spree/admin/payment_wing_sdk_markers_controller.rb +36 -0
  33. data/app/controllers/spree/admin/payment_wing_sdk_queriers_controller.rb +20 -0
  34. data/app/controllers/spree/admin/payments_controller_decorator.rb +19 -0
  35. data/app/controllers/spree/admin/payout_profile_products_controller.rb +21 -0
  36. data/app/controllers/spree/admin/payout_profile_shipping_methods_controller.rb +21 -0
  37. data/app/controllers/spree/admin/payout_profiles_controller.rb +91 -0
  38. data/app/controllers/spree/admin/payouts_controller.rb +16 -0
  39. data/app/controllers/spree/api/v2/storefront/checkout_controller_decorator.rb +84 -0
  40. data/app/controllers/spree/payway_card_popups_controller.rb +20 -0
  41. data/app/controllers/spree/payway_results_controller.rb +11 -0
  42. data/app/controllers/spree/payway_v2_card_popups_controller.rb +20 -0
  43. data/app/controllers/spree/vpago_payments_controller.rb +71 -0
  44. data/app/controllers/spree/webhook/acleda_mobiles_controller.rb +88 -0
  45. data/app/controllers/spree/webhook/acledas_controller.rb +80 -0
  46. data/app/controllers/spree/webhook/base_controller.rb +6 -0
  47. data/app/controllers/spree/webhook/payways_controller.rb +77 -0
  48. data/app/controllers/spree/webhook/wings_controller.rb +63 -0
  49. data/app/controllers/spree/wing/base_controller.rb +29 -0
  50. data/app/controllers/spree/wing/transactions_controller.rb +27 -0
  51. data/app/controllers/spree/wing_redirects_controller.rb +20 -0
  52. data/app/helpers/vpago/admin/base_helper_decorator.rb +40 -0
  53. data/app/helpers/vpago/vpago_payments_helper.rb +12 -0
  54. data/app/javascripts/vpago/vpago_payments/user_informers/firebase.js +62 -0
  55. data/app/jobs/application_unique_job.rb +1 -0
  56. data/app/jobs/vpago/payment_processor_job.rb +8 -0
  57. data/app/models/.gitkeep +0 -0
  58. data/app/models/concerns/vpago/payoutable.rb +11 -0
  59. data/app/models/spree/gateway/acleda.rb +63 -0
  60. data/app/models/spree/gateway/acleda_mobile.rb +46 -0
  61. data/app/models/spree/gateway/payway.rb +66 -0
  62. data/app/models/spree/gateway/payway_v2.rb +174 -0
  63. data/app/models/spree/gateway/wing_sdk.rb +51 -0
  64. data/app/models/spree/payout.rb +23 -0
  65. data/app/models/spree/payout_profile.rb +107 -0
  66. data/app/models/spree/payout_profile_product.rb +14 -0
  67. data/app/models/spree/payout_profile_shipping_method.rb +14 -0
  68. data/app/models/spree/payout_profiles/payway_v2.rb +40 -0
  69. data/app/models/spree/vpago_payment_source.rb +21 -0
  70. data/app/models/vpago/adjustment_decorator.rb +28 -0
  71. data/app/models/vpago/line_item_decorator.rb +75 -0
  72. data/app/models/vpago/order_decorator.rb +78 -0
  73. data/app/models/vpago/payment_decorator.rb +44 -0
  74. data/app/models/vpago/payment_method_decorator.rb +91 -0
  75. data/app/models/vpago/payouts_generator.rb +125 -0
  76. data/app/models/vpago/product_decorator.rb +30 -0
  77. data/app/models/vpago/promotion_action_decorator.rb +9 -0
  78. data/app/models/vpago/shipment_decorator.rb +23 -0
  79. data/app/models/vpago/shipping_method_decorator.rb +33 -0
  80. data/app/models/vpago/shipping_rate_decorator.rb +25 -0
  81. data/app/models/vpago/store_decorator.rb +17 -0
  82. data/app/models/vpago/tax_category_decorator.rb +9 -0
  83. data/app/models/vpago/variant_decorator.rb +22 -0
  84. data/app/models/vpago/vendor_decorator.rb +10 -0
  85. data/app/overrides/spree/admin/adjustments/_adjustment/handle_by_table_body.html.erb.deface +11 -0
  86. data/app/overrides/spree/admin/adjustments/_adjustments_table/handle_by_table_header.html.erb.deface +3 -0
  87. data/app/overrides/spree/admin/adjustments/_form/handle_by_field.html.erb.deface +10 -0
  88. data/app/overrides/spree/admin/orders/_shipment/edit_method.html.erb.deface +26 -0
  89. data/app/overrides/spree/admin/orders/_shipment/show_method.html.erb.deface +27 -0
  90. data/app/overrides/spree/admin/payment_methods/_form/enable_pre_auth_field.html.erb.deface +17 -0
  91. data/app/overrides/spree/admin/payment_methods/_form/vendor_field.html.erb.deface +6 -0
  92. data/app/overrides/spree/admin/payment_methods/index/list.html.erb.deface +45 -0
  93. data/app/overrides/spree/admin/payment_methods/index/payment_methods_tabs.html.erb.deface +9 -0
  94. data/app/overrides/spree/admin/payment_methods/index/vendor_body.html.erb.deface +5 -0
  95. data/app/overrides/spree/admin/payment_methods/index/vendor_header.html.erb.deface +5 -0
  96. data/app/overrides/spree/admin/payments/_list/actions.html.erb.deface +13 -0
  97. data/app/overrides/spree/admin/promotions/_promotion_action/run_by_field.html.erb.deface +13 -0
  98. data/app/overrides/spree/admin/shared/_order_tabs/payouts.html.erb.deface +8 -0
  99. data/app/overrides/spree/admin/shared/_product_tabs/payout_profile_products.html.erb.deface +8 -0
  100. data/app/overrides/spree/admin/shared/sub_menu/_integrations/payout_profiles.html.erb.deface +3 -0
  101. data/app/overrides/spree/admin/shipping_methods/_form/handle_by.html.erb.deface +12 -0
  102. data/app/overrides/spree/admin/shipping_methods/edit/shipping_method_tabs.html.erb.deface +3 -0
  103. data/app/overrides/spree/admin/tax_categories/_form/collect_by_field.html.erb.deface +10 -0
  104. data/app/overrides/spree/admin/tax_categories/index/collect_by_table_body.html.erb.deface +4 -0
  105. data/app/overrides/spree/admin/tax_categories/index/collect_by_table_header.html.erb.deface +4 -0
  106. data/app/serializers/spree/api/v2/platform/vpago_payment_source_serializer.rb +11 -0
  107. data/app/serializers/spree/v2/storefront/payment_method_serializer_decorator.rb +23 -0
  108. data/app/serializers/spree/v2/storefront/payment_redirect_serializer.rb +13 -0
  109. data/app/serializers/spree/v2/storefront/payment_serializer_decorator.rb +13 -0
  110. data/app/serializers/spree/v2/storefront/vpago_payment_source_serializer.rb +9 -0
  111. data/app/services/.gitkeep +0 -0
  112. data/app/services/vpago/payment_finder.rb +27 -0
  113. data/app/services/vpago/payment_processor.rb +72 -0
  114. data/app/services/vpago/payment_redirect_handler.rb +194 -0
  115. data/app/services/vpago/payment_url_constructor.rb +32 -0
  116. data/app/services/vpago/payments/find_or_create.rb +51 -0
  117. data/app/services/vpago/payout_profiles/payway/base_payout_profile_request.rb +38 -0
  118. data/app/services/vpago/payout_profiles/payway/open_ssl_encrypter.rb +31 -0
  119. data/app/services/vpago/payout_profiles/payway/payout_profile_request_creator.rb +50 -0
  120. data/app/services/vpago/payout_profiles/payway/payout_profile_request_params_builder.rb +58 -0
  121. data/app/services/vpago/payout_profiles/payway/payout_profile_request_updater.rb +28 -0
  122. data/app/services/vpago/payway_return_options_builder.rb +39 -0
  123. data/app/services/vpago/user_informers/firebase.rb +52 -0
  124. data/app/views/.gitkeep +0 -0
  125. data/app/views/layouts/acleda.html.erb +18 -0
  126. data/app/views/layouts/payway.html.erb +24 -0
  127. data/app/views/layouts/payway_v2.html.erb +18 -0
  128. data/app/views/layouts/vpago_payments.html.erb +18 -0
  129. data/app/views/layouts/wing.html.erb +18 -0
  130. data/app/views/spree/_payment_icon.html.erb +25 -0
  131. data/app/views/spree/acleda_redirects/show.html.erb +3 -0
  132. data/app/views/spree/admin/payments/source_forms/_payment_payway.html.erb +4 -0
  133. data/app/views/spree/admin/payments/source_forms/_payway_v2.html.erb +7 -0
  134. data/app/views/spree/admin/payments/source_views/_acleda.html.erb +1 -0
  135. data/app/views/spree/admin/payments/source_views/_acleda_mobile.html.erb +1 -0
  136. data/app/views/spree/admin/payments/source_views/_payment_payway.html.erb +98 -0
  137. data/app/views/spree/admin/payments/source_views/_payway_v2.html.erb +5 -0
  138. data/app/views/spree/admin/payments/source_views/_vpago_payment_tmpl.html.erb +113 -0
  139. data/app/views/spree/admin/payments/source_views/_wingsdk.html.erb +5 -0
  140. data/app/views/spree/admin/payout_profile_products/_form.html.erb +28 -0
  141. data/app/views/spree/admin/payout_profile_products/edit.html.erb +18 -0
  142. data/app/views/spree/admin/payout_profile_products/index.html.erb +58 -0
  143. data/app/views/spree/admin/payout_profile_products/new.html.erb +22 -0
  144. data/app/views/spree/admin/payout_profile_shipping_methods/_form.html.erb +28 -0
  145. data/app/views/spree/admin/payout_profile_shipping_methods/edit.html.erb +22 -0
  146. data/app/views/spree/admin/payout_profile_shipping_methods/index.html.erb +6 -0
  147. data/app/views/spree/admin/payout_profile_shipping_methods/new.html.erb +27 -0
  148. data/app/views/spree/admin/payout_profiles/_filter.html.erb +19 -0
  149. data/app/views/spree/admin/payout_profiles/_form.html.erb +72 -0
  150. data/app/views/spree/admin/payout_profiles/edit.html.erb +33 -0
  151. data/app/views/spree/admin/payout_profiles/index.html.erb +63 -0
  152. data/app/views/spree/admin/payout_profiles/new.html.erb +13 -0
  153. data/app/views/spree/admin/payouts/_line_item_info_with_popover.html.erb +43 -0
  154. data/app/views/spree/admin/payouts/index.html.erb +46 -0
  155. data/app/views/spree/admin/shared/_payment_methods_tabs.html.erb +15 -0
  156. data/app/views/spree/admin/shared/_shipping_method_tabs.html.erb +19 -0
  157. data/app/views/spree/admin/shared/payout_profile/_info_card.html.erb +24 -0
  158. data/app/views/spree/admin/shared/payout_profile/_status.html.erb +11 -0
  159. data/app/views/spree/checkout/payment/_acleda.html.erb +1 -0
  160. data/app/views/spree/checkout/payment/_acleda_checkout_form.html.erb +14 -0
  161. data/app/views/spree/checkout/payment/_acleda_mobile.html.erb +0 -0
  162. data/app/views/spree/checkout/payment/_payment_payway.html.erb +21 -0
  163. data/app/views/spree/checkout/payment/_payway_loader.html.erb +2 -0
  164. data/app/views/spree/checkout/payment/_payway_root.html.erb +9 -0
  165. data/app/views/spree/checkout/payment/_payway_script.html.erb +77 -0
  166. data/app/views/spree/checkout/payment/_payway_v2.html.erb +1 -0
  167. data/app/views/spree/checkout/payment/_payway_v2_loader.html.erb +2 -0
  168. data/app/views/spree/checkout/payment/_payway_v2_root.html.erb +9 -0
  169. data/app/views/spree/checkout/payment/_payway_v2_script.html.erb +79 -0
  170. data/app/views/spree/checkout/payment/_wing_checkout_form.html.erb +18 -0
  171. data/app/views/spree/checkout/payment/_wingsdk.html.erb +1 -0
  172. data/app/views/spree/checkout/payment/acleda_form.html.erb +3 -0
  173. data/app/views/spree/checkout/payment/payway_form.html.erb +10 -0
  174. data/app/views/spree/checkout/payment/payway_v2_form.html.erb +10 -0
  175. data/app/views/spree/checkout/payment/wingsdk_form.html.erb +3 -0
  176. data/app/views/spree/payway_card_popups/_form.html.erb +10 -0
  177. data/app/views/spree/payway_card_popups/show.html.erb +5 -0
  178. data/app/views/spree/payway_results/failed.html.erb +1 -0
  179. data/app/views/spree/payway_results/success.html.erb +1 -0
  180. data/app/views/spree/payway_v2_card_popups/_form.html.erb +15 -0
  181. data/app/views/spree/payway_v2_card_popups/show.html.erb +3 -0
  182. data/app/views/spree/vpago_payments/checkout.html.erb +3 -0
  183. data/app/views/spree/vpago_payments/forms/spree/gateway/_payway_v2.html.erb +13 -0
  184. data/app/views/spree/vpago_payments/processing.html.erb +49 -0
  185. data/app/views/spree/vpago_payments/success.html.erb +3 -0
  186. data/app/views/spree/wing_redirects/show.html.erb +3 -0
  187. data/bin/rails +8 -0
  188. data/build.js +9 -0
  189. data/config/initializers/assets.rb +1 -0
  190. data/config/initializers/spree_permitted_attributes.rb +5 -0
  191. data/config/locales/en.yml +28 -0
  192. data/config/locales/km.yml +24 -0
  193. data/config/routes.rb +92 -0
  194. data/db/migrate/20210128040625_create_spree_vpago_payment_sources.rb +14 -0
  195. data/db/migrate/20210430052432_add_fields_to_payment_sources.rb +7 -0
  196. data/db/migrate/20210824044737_add_preference_to_spree_vpago_payment_source.rb +5 -0
  197. data/db/migrate/20240506095140_create_spree_payout_profiles.rb +22 -0
  198. data/db/migrate/20240506095148_create_spree_payout_profile_products.rb +10 -0
  199. data/db/migrate/20240523070757_add_optional_spere_payout_profile_products.rb +5 -0
  200. data/db/migrate/20240715091228_create_spree_payouts.rb +25 -0
  201. data/db/migrate/20240718195510_create_spree_payout_profile_shipping_methods.rb +15 -0
  202. data/db/migrate/20240718201453_add_handle_by_to_spree_adjustments.rb +5 -0
  203. data/db/migrate/20240718201509_add_run_by_to_spree_promotion_actions.rb +5 -0
  204. data/db/migrate/20240718201523_add_collect_by_to_spree_tax_categories.rb +5 -0
  205. data/db/migrate/20240718201538_add_handle_by_to_spree_shipping_method.rb +5 -0
  206. data/db/migrate/20240718201554_add_handle_by_to_spree_shipping_rates.rb +5 -0
  207. data/db/migrate/20240808102853_add_vendor_reference_to_spree_payment_methods.rb +7 -0
  208. data/db/migrate/20240904030132_add_gateway_status_to_spree_payments.rb +5 -0
  209. data/db/migrate/20241029071959_add_pre_auth_response_to_spree_payment.rb +5 -0
  210. data/db/migrate/20241202104014_add_enable_pre_auth_to_spree_payment_method.rb +5 -0
  211. data/db/migrate/20250121044614_add_transaction_response_to_spree_payments.rb +5 -0
  212. data/docs/acleda.md +65 -0
  213. data/gemfiles/spree_3_7.gemfile +9 -0
  214. data/gemfiles/spree_4_0.gemfile +8 -0
  215. data/gemfiles/spree_4_1.gemfile +9 -0
  216. data/gemfiles/spree_master.gemfile +8 -0
  217. data/lib/generators/spree_vpago/install/install_generator.rb +26 -0
  218. data/lib/generators/spree_vpago/install/templates/app/assets/images/vpago/payway/acleda_merchant_logo_300x300.png +0 -0
  219. data/lib/spree_vpago/engine.rb +36 -0
  220. data/lib/spree_vpago/factories.rb +6 -0
  221. data/lib/spree_vpago/version.rb +9 -0
  222. data/lib/spree_vpago.rb +5 -0
  223. data/lib/vpago/acleda/base.rb +111 -0
  224. data/lib/vpago/acleda/checkout.rb +105 -0
  225. data/lib/vpago/acleda/deeplink_checkout.rb +74 -0
  226. data/lib/vpago/acleda/payment_request_updater.rb +38 -0
  227. data/lib/vpago/acleda/transaction_status.rb +71 -0
  228. data/lib/vpago/acleda_mobile/base.rb +28 -0
  229. data/lib/vpago/acleda_mobile/callback_validator.php +35 -0
  230. data/lib/vpago/acleda_mobile/callback_validator.rb +38 -0
  231. data/lib/vpago/acleda_mobile/checkout.rb +78 -0
  232. data/lib/vpago/acleda_mobile/payment_request_updater.rb +34 -0
  233. data/lib/vpago/acleda_mobile/payment_retriever.rb +24 -0
  234. data/lib/vpago/acleda_mobile/transaction_status.rb +35 -0
  235. data/lib/vpago/payment_amount_calculator.rb +23 -0
  236. data/lib/vpago/payment_request_updater.rb +19 -0
  237. data/lib/vpago/payment_status_marker.rb +109 -0
  238. data/lib/vpago/payway/base.rb +112 -0
  239. data/lib/vpago/payway/checkout.rb +24 -0
  240. data/lib/vpago/payway/payment_request_updater.rb +31 -0
  241. data/lib/vpago/payway/payment_status_marker.rb +84 -0
  242. data/lib/vpago/payway/transaction_status.rb +74 -0
  243. data/lib/vpago/payway.rb +13 -0
  244. data/lib/vpago/payway_v2/base.rb +153 -0
  245. data/lib/vpago/payway_v2/checkout.rb +35 -0
  246. data/lib/vpago/payway_v2/payment_request_updater.rb +65 -0
  247. data/lib/vpago/payway_v2/payouts_params_constructor.rb +47 -0
  248. data/lib/vpago/payway_v2/pre_auth_canceler.rb +43 -0
  249. data/lib/vpago/payway_v2/pre_auth_completer.rb +76 -0
  250. data/lib/vpago/payway_v2/transaction_status.rb +85 -0
  251. data/lib/vpago/wing_sdk/base.rb +58 -0
  252. data/lib/vpago/wing_sdk/checkout.rb +20 -0
  253. data/lib/vpago/wing_sdk/payment_request_updater.rb +46 -0
  254. data/lib/vpago/wing_sdk/payment_retriever.rb +34 -0
  255. data/lib/vpago/wing_sdk/transaction_status_checker.rb +64 -0
  256. data/lib/vpago/wing_sdk/transaction_status_response.rb +33 -0
  257. data/node_modules/.yarn-integrity +147 -0
  258. data/package-lock.json +1491 -0
  259. data/package.json +10 -0
  260. data/spree_vpago.gemspec +33 -0
  261. data/yarn.lock +921 -0
  262. metadata +403 -0
@@ -0,0 +1,78 @@
1
+ module Vpago
2
+ module OrderDecorator
3
+ extend Spree::DisplayMoney
4
+ money_methods :order_adjustment_total, :shipping_discount
5
+
6
+ def self.prepended(base)
7
+ base.has_many :payouts, class_name: 'Spree::Payout', through: :payments
8
+ base.has_many :vendor_payment_methods, -> { unscope(:order).distinct },
9
+ class_name: 'Spree::PaymentMethod',
10
+ through: :line_items
11
+
12
+ base.state_machine.before_transition from: :cart, do: :ensure_valid_vendor_payment_methods
13
+ base.state_machine.after_transition to: :complete, do: :generate_line_items_total_metadata
14
+ end
15
+
16
+ # override
17
+ def process_payments!
18
+ super if insufficient_processed_payment?
19
+
20
+ # Prevent from reaching :complete state if payment is insufficient
21
+ errors.add(:base, Spree.t(:insufficient_payment_amount_to_cover_the_total)) if insufficient_processed_payment?
22
+ end
23
+
24
+ def insufficient_processed_payment?
25
+ processed_payment_total < total
26
+ end
27
+
28
+ def processed_payment_total
29
+ payment_total + pending_payments.sum(:amount)
30
+ end
31
+
32
+ def ensure_valid_vendor_payment_methods
33
+ return if line_items_from_same_vendor?
34
+ return unless vendor_payment_methods.any?
35
+
36
+ errors.add(:line_items, 'some_payment_methods_cant_be_used_across_vendors')
37
+
38
+ false
39
+ end
40
+
41
+ def line_items_from_same_vendor?
42
+ line_items.joins(:variant).pluck('spree_variants.vendor_id').uniq.size == 1
43
+ end
44
+
45
+ def required_payway_payout?
46
+ line_items.any?(&:required_payway_payout?) || shipments.any?(&:required_payway_payout?)
47
+ end
48
+
49
+ # override
50
+ def available_payment_methods(store = nil)
51
+ payment_methods = if vendor_payment_methods.any?
52
+ vendor_payment_methods
53
+ else
54
+ collect_payment_methods(store)
55
+ end
56
+
57
+ @available_payment_methods ||= if required_payway_payout?
58
+ payment_methods.select(&:type_payway_v2?)
59
+ else
60
+ payment_methods
61
+ end
62
+ end
63
+
64
+ def line_items_count
65
+ line_items.size
66
+ end
67
+
68
+ def generate_line_items_total_metadata
69
+ line_items.each(&:update_total_metadata)
70
+ end
71
+
72
+ def order_adjustment_total
73
+ adjustments.eligible.sum(:amount)
74
+ end
75
+ end
76
+ end
77
+
78
+ Spree::Order.prepend(Vpago::OrderDecorator) if Spree::Order.included_modules.exclude?(Vpago::OrderDecorator)
@@ -0,0 +1,44 @@
1
+ module Vpago
2
+ module PaymentDecorator
3
+ def self.prepended(base)
4
+ base.has_many :payouts, class_name: 'Spree::Payout', inverse_of: :payment
5
+ base.after_create -> { Vpago::PayoutsGenerator.new(self).call }, if: :should_generate_payouts?
6
+
7
+ base.delegate :checkout_url,
8
+ :processing_url,
9
+ :success_url,
10
+ :process_payment_url,
11
+ to: :url_constructor
12
+ end
13
+
14
+ def process!
15
+ # give payment another chance to re-process, even if it failed.
16
+ update!(state: :checkout) if processing? || send(:has_invalid_state?)
17
+
18
+ super
19
+ end
20
+
21
+ def url_constructor
22
+ @url_constructor ||= Vpago::PaymentUrlConstructor.new(self)
23
+ end
24
+
25
+ def should_generate_payouts?
26
+ payment_method.enable_payout? && payouts.empty?
27
+ end
28
+
29
+ # COMPLETED, CANCELLED
30
+ def pre_auth_status
31
+ pre_auth_response['transaction_status']
32
+ end
33
+
34
+ def pre_auth_completed?
35
+ pre_auth_status == 'COMPLETED'
36
+ end
37
+
38
+ def pre_auth_cancelled?
39
+ pre_auth_status == 'CANCELLED'
40
+ end
41
+ end
42
+ end
43
+
44
+ Spree::Payment.prepend(Vpago::PaymentDecorator) unless Spree::Payment.included_modules.include?(Vpago::PaymentDecorator)
@@ -0,0 +1,91 @@
1
+ module Vpago
2
+ module PaymentMethodDecorator
3
+ TYPE_PAYWAY = 'Spree::Gateway::Payway'.freeze
4
+ TYPE_PAYWAY_V2 = 'Spree::Gateway::PaywayV2'.freeze
5
+ TYPE_WINGSDK = 'Spree::Gateway::WingSdk'.freeze
6
+ TYPE_ACLEDA = 'Spree::Gateway::Acleda'.freeze
7
+ TYPE_ACLEDA_MOBILE = 'Spree::Gateway::AcledaMobile'.freeze
8
+
9
+ def self.prepended(base)
10
+ base.preference :icon_name, :string, default: 'cheque'
11
+ base.belongs_to :vendor, class_name: 'Spree::Vendor', optional: true, inverse_of: :payment_methods
12
+
13
+ def base.vpago_payments
14
+ [
15
+ Spree::PaymentMethod::TYPE_PAYWAY_V2,
16
+ Spree::PaymentMethod::TYPE_PAYWAY,
17
+ Spree::PaymentMethod::TYPE_WINGSDK,
18
+ Spree::PaymentMethod::TYPE_ACLEDA,
19
+ Spree::PaymentMethod::TYPE_ACLEDA_MOBILE
20
+ ]
21
+ end
22
+ end
23
+
24
+ def enable_payout? = support_payout?
25
+ def enable_pre_auth? = support_pre_auth? && self[:enable_pre_auth]
26
+ def support_payout? = false
27
+ def support_pre_auth? = false
28
+
29
+ # TODO: we have already implement purchase for payway_v2.
30
+ # make sure to implement this on other payment method as well.
31
+ def purchase(_amount, _source, _gateway_options = {})
32
+ ActiveMerchant::Billing::Response.new(true, 'Payway Gateway: Success')
33
+ end
34
+
35
+ def default_payout_profile
36
+ nil
37
+ end
38
+
39
+ def vpago_payment?
40
+ self.class.vpago_payments.include?(type)
41
+ end
42
+
43
+ def vapgo_checkout_service
44
+ if type_payway?
45
+ ::Vpago::Payway::Checkout
46
+ elsif type_payway_v2?
47
+ ::Vpago::PaywayV2::Checkout
48
+ elsif type_wingsdk?
49
+ ::Vpago::WingSdk::Checkout
50
+ elsif type_acleda?
51
+ ::Vpago::Acleda::Checkout
52
+ end
53
+ end
54
+
55
+ def payment_request_updater
56
+ if type_payway?
57
+ ::Vpago::Payway::PaymentRequestUpdater
58
+ elsif type_payway_v2?
59
+ ::Vpago::PaywayV2::PaymentRequestUpdater
60
+ elsif type_wingsdk?
61
+ ::Vpago::WingSdk::PaymentRequestUpdater
62
+ elsif type_acleda?
63
+ ::Vpago::Acleda::PaymentRequestUpdater
64
+ elsif type_acleda_mobile?
65
+ ::Vpago::AcledaMobile::PaymentRequestUpdater
66
+ end
67
+ end
68
+
69
+ def type_acleda_mobile?
70
+ type == Spree::PaymentMethod::TYPE_ACLEDA_MOBILE
71
+ end
72
+
73
+ def type_acleda?
74
+ type == Spree::PaymentMethod::TYPE_ACLEDA
75
+ end
76
+
77
+ def type_payway?
78
+ type == Spree::PaymentMethod::TYPE_PAYWAY
79
+ end
80
+
81
+ def type_payway_v2?
82
+ type == Spree::PaymentMethod::TYPE_PAYWAY_V2
83
+ end
84
+
85
+ def type_wingsdk?
86
+ type == Spree::PaymentMethod::TYPE_WINGSDK
87
+ end
88
+ end
89
+ end
90
+
91
+ Spree::PaymentMethod.prepend(Vpago::PaymentMethodDecorator) unless Spree::PaymentMethod.included_modules.include?(Vpago::PaymentMethodDecorator)
@@ -0,0 +1,125 @@
1
+ # generate payouts for remaining amount of provided payment.
2
+ # currently only support for PaywayV2 payment.
3
+ module Vpago
4
+ class PayoutsGenerator
5
+ attr_reader :payment, :line_items, :shipments
6
+
7
+ attr_accessor :remaining_amount
8
+
9
+ def initialize(payment)
10
+ @payment = payment
11
+
12
+ @line_items = payment.order.line_items.includes(
13
+ :active_payway_payout_profiles,
14
+ :confirmed_payouts_for_vendor,
15
+ :adjustments
16
+ )
17
+
18
+ @shipments = payment.order.shipments.includes(
19
+ :confirmed_payouts_for_vendor,
20
+ :adjustments,
21
+ selected_shipping_rate: :active_payway_payout_profiles
22
+ )
23
+
24
+ self.remaining_amount = payment.amount
25
+ end
26
+
27
+ def call
28
+ payouts = vendor_payouts + vendor_shipment_payouts + store_payouts
29
+
30
+ return [] if payouts.all?(&:default?)
31
+ return [] unless payouts.all? { |payout| validated?(payout) }
32
+
33
+ ActiveRecord::Base.transaction { payouts.each(&:save!) }
34
+ end
35
+
36
+ def validated?(payout)
37
+ return false if payout.payout_profile.preferred_merchant_id != payment.payment_method.preferred_merchant_id
38
+
39
+ true
40
+ end
41
+
42
+ def vendor_payouts
43
+ line_items.each_with_object([]) do |line_item, payouts|
44
+ payout_profile = line_item.active_payway_payout_profiles.first
45
+ next unless payout_profile.present?
46
+
47
+ total_confirmed_payouts = line_item.confirmed_payouts_for_vendor.sum(:amount)
48
+ next if total_confirmed_payouts >= line_item.pre_commission_amount
49
+
50
+ amount_owed_to_vendor = line_item.pre_commission_amount - total_confirmed_payouts
51
+ payout_amount = [amount_owed_to_vendor, remaining_amount].min
52
+ outstanding_amount = [amount_owed_to_vendor - payout_amount, 0].max
53
+
54
+ payouts << Spree::Payout.new(
55
+ default: false,
56
+ state: :created,
57
+ payoutable: line_item,
58
+ payment: payment,
59
+ payout_profile: payout_profile,
60
+ amount: payout_amount,
61
+ outstanding_amount: outstanding_amount,
62
+ private_metadata: {
63
+ total_confirmed_payouts: total_confirmed_payouts,
64
+ amount_owed_to_vendor: amount_owed_to_vendor,
65
+ payoutable: line_item.latest_private_metadata
66
+ }
67
+ )
68
+
69
+ self.remaining_amount -= payout_amount
70
+ end
71
+ end
72
+
73
+ def vendor_shipment_payouts
74
+ shipments.each_with_object([]) do |shipment, payouts|
75
+ next unless shipment.selected_shipping_rate.handle_by_vendor?
76
+
77
+ payout_profile = shipment.selected_shipping_rate.active_payway_payout_profiles.first
78
+ next unless payout_profile.present?
79
+
80
+ total_confirmed_payouts = shipment.confirmed_payouts_for_vendor.sum(:amount)
81
+ next if total_confirmed_payouts >= shipment.cost_with_vendor_adjustment_total
82
+
83
+ amount_owed_to_shipping_vendor = shipment.cost_with_vendor_adjustment_total - total_confirmed_payouts
84
+ payout_amount = [amount_owed_to_shipping_vendor, remaining_amount].min
85
+ outstanding_amount = [amount_owed_to_shipping_vendor - payout_amount, 0].max
86
+
87
+ payouts << Spree::Payout.new(
88
+ default: false,
89
+ state: :created,
90
+ payoutable: shipment,
91
+ payment: payment,
92
+ payout_profile: payout_profile,
93
+ amount: payout_amount,
94
+ outstanding_amount: outstanding_amount,
95
+ private_metadata: {
96
+ total_confirmed_payouts: total_confirmed_payouts,
97
+ amount_owed_to_vendor: amount_owed_to_shipping_vendor,
98
+ payoutable: {
99
+ cost: shipment.cost,
100
+ vendor_adjustment_total: shipment.vendor_adjustment_total,
101
+ cost_with_vendor_adjustment_total: shipment.cost_with_vendor_adjustment_total
102
+ }
103
+ }
104
+ )
105
+
106
+ self.remaining_amount -= payout_amount
107
+ end
108
+ end
109
+
110
+ # generate payout for all remaining amount to store.
111
+ def store_payouts
112
+ return [] if self.remaining_amount <= 0
113
+
114
+ [
115
+ Spree::Payout.new(
116
+ default: true,
117
+ state: :created,
118
+ payment: payment,
119
+ payout_profile: Spree::PayoutProfiles::PaywayV2.default,
120
+ amount: self.remaining_amount
121
+ )
122
+ ]
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,30 @@
1
+ module Vpago
2
+ module ProductDecorator
3
+ def self.prepended(base)
4
+ base.has_many :payout_profile_products,
5
+ class_name: 'Spree::PayoutProfileProduct', inverse_of: :product
6
+
7
+ base.has_many :payout_profiles,
8
+ class_name: 'Spree::PayoutProfile', through: :payout_profile_products,
9
+ source: :payout_profile
10
+
11
+ base.has_many :active_payout_profiles, -> { verified.active },
12
+ class_name: 'Spree::PayoutProfile',
13
+ through: :payout_profile_products, source: :payout_profile
14
+
15
+ base.has_many :active_payway_payout_profiles, -> { payway.verified.active },
16
+ class_name: 'Spree::PayoutProfile',
17
+ through: :payout_profile_products, source: :payout_profile
18
+
19
+ base.has_many :required_payout_profile_products, -> { required },
20
+ class_name: 'Spree::PayoutProfileProduct',
21
+ inverse_of: :product
22
+
23
+ base.has_many :required_active_payout_profiles, -> { verified.active },
24
+ class_name: 'Spree::PayoutProfile',
25
+ through: :required_payout_profile_products, source: :payout_profile
26
+ end
27
+ end
28
+ end
29
+
30
+ Spree::Product.prepend(Vpago::ProductDecorator) unless Spree::Product.included_modules.include?(Vpago::ProductDecorator)
@@ -0,0 +1,9 @@
1
+ module Vpago
2
+ module PromotionActionDecorator
3
+ def self.prepended(base)
4
+ base.enum run_by: { store: 0, vendor: 1 }, _prefix: true
5
+ end
6
+ end
7
+ end
8
+
9
+ Spree::PromotionAction.prepend(Vpago::PromotionActionDecorator) unless Spree::PromotionAction.included_modules.include?(Vpago::PromotionActionDecorator)
@@ -0,0 +1,23 @@
1
+ module Vpago
2
+ module ShipmentDecorator
3
+ def self.prepended(base)
4
+ base.include ::Vpago::Payoutable
5
+ end
6
+
7
+ def required_payway_payout?
8
+ selected_shipping_rate.required_active_payout_profiles.payway.exists?
9
+ end
10
+
11
+ # there is no commission for shipment yet.
12
+ # so amount that vendor expected is cost + their adjustments.
13
+ def cost_with_vendor_adjustment_total
14
+ cost + vendor_adjustment_total
15
+ end
16
+
17
+ def vendor_adjustment_total
18
+ adjustments.handle_by_vendor.total
19
+ end
20
+ end
21
+ end
22
+
23
+ Spree::Shipment.prepend(Vpago::ShipmentDecorator) unless Spree::Shipment.included_modules.include?(Vpago::ShipmentDecorator)
@@ -0,0 +1,33 @@
1
+ module Vpago
2
+ module ShippingMethodDecorator
3
+ def self.prepended(base)
4
+ base.enum handle_by: { store: 0, vendor: 1 }, _prefix: true
5
+
6
+ base.has_many :payout_profile_shipping_methods,
7
+ class_name: 'Spree::PayoutProfileShippingMethod',
8
+ inverse_of: :shipping_method
9
+
10
+ base.has_many :payout_profiles,
11
+ class_name: 'Spree::PayoutProfile', through: :payout_profile_shipping_methods,
12
+ source: :payout_profile
13
+
14
+ base.has_many :active_payout_profiles, -> { verified.active },
15
+ class_name: 'Spree::PayoutProfile',
16
+ through: :payout_profile_shipping_methods, source: :payout_profile
17
+
18
+ base.has_many :active_payway_payout_profiles, -> { payway.verified.active },
19
+ class_name: 'Spree::PayoutProfile',
20
+ through: :payout_profile_shipping_methods, source: :payout_profile
21
+
22
+ base.has_many :required_payout_profile_shipping_methods, -> { required },
23
+ class_name: 'Spree::PayoutProfileShippingMethod',
24
+ inverse_of: :shipping_method
25
+
26
+ base.has_many :required_active_payout_profiles, -> { verified.active },
27
+ class_name: 'Spree::PayoutProfile',
28
+ through: :required_payout_profile_shipping_methods, source: :payout_profile
29
+ end
30
+ end
31
+ end
32
+
33
+ Spree::ShippingMethod.prepend(Vpago::ShippingMethodDecorator) unless Spree::ShippingMethod.included_modules.include?(Vpago::ShippingMethodDecorator)
@@ -0,0 +1,25 @@
1
+ module Vpago
2
+ module ShippingRateDecorator
3
+ def self.prepended(base)
4
+ base.has_many :payout_profiles, class_name: 'Spree::PayoutProfile', through: :shipping_method
5
+ base.has_many :active_payout_profiles, class_name: 'Spree::PayoutProfile', through: :shipping_method
6
+ base.has_many :active_payway_payout_profiles, class_name: 'Spree::PayoutProfile', through: :shipping_method
7
+ base.has_many :required_active_payout_profiles, class_name: 'Spree::PayoutProfile', through: :shipping_method
8
+
9
+ base.enum handle_by: { store: 0, vendor: 1 }, _prefix: true
10
+
11
+ base.scope :handle_by_vendor, -> { where(handle_by: :vendor) }
12
+ base.scope :handle_by_store, -> { where(handle_by: :store) }
13
+
14
+ base.before_save :set_handle_by
15
+ end
16
+
17
+ private
18
+
19
+ def set_handle_by
20
+ self.handle_by = shipping_method.handle_by
21
+ end
22
+ end
23
+ end
24
+
25
+ Spree::ShippingRate.prepend(Vpago::ShippingRateDecorator) unless Spree::ShippingRate.included_modules.include?(Vpago::ShippingRateDecorator)
@@ -0,0 +1,17 @@
1
+ module Vpago
2
+ module StoreDecorator
3
+ def self.prepended(base)
4
+ # override
5
+ base.has_many :payment_methods, -> { where(vendor_id: nil) },
6
+ through: :store_payment_methods,
7
+ class_name: 'Spree::PaymentMethod'
8
+
9
+ base.has_many :payment_methods_including_vendor,
10
+ through: :store_payment_methods,
11
+ class_name: 'Spree::PaymentMethod',
12
+ source: :payment_method
13
+ end
14
+ end
15
+ end
16
+
17
+ Spree::Store.prepend(Vpago::StoreDecorator) unless Spree::Store.included_modules.include?(Vpago::StoreDecorator)
@@ -0,0 +1,9 @@
1
+ module Vpago
2
+ module TaxCategoryDecorator
3
+ def self.prepended(base)
4
+ base.enum collect_by: { store: 0, vendor: 1 }, _prefix: true
5
+ end
6
+ end
7
+ end
8
+
9
+ Spree::TaxCategory.prepend(Vpago::TaxCategoryDecorator) unless Spree::TaxCategory.included_modules.include?(Vpago::TaxCategoryDecorator)
@@ -0,0 +1,22 @@
1
+ module Vpago
2
+ module VariantDecorator
3
+ def self.prepended(base)
4
+ base.belongs_to :vendor, class_name: 'Spree::Vendor'
5
+
6
+ base.has_many :vendor_payment_methods, class_name: 'Spree::PaymentMethod',
7
+ through: :vendor,
8
+ source: :payment_methods
9
+ end
10
+ end
11
+ end
12
+
13
+ if Spree::Variant.included_modules.exclude?(Vpago::VariantDecorator)
14
+ # spree_multi_vendor use :vendor method for assoication, we should use association (belongs_to :vendor) instead.
15
+ #
16
+ # problem with those methods is that it store value in memeory,
17
+ # even we call variant.reload, the method value is no being reload.
18
+ SpreeMultiVendor::Spree::VariantDecorator.remove_method :vendor
19
+ SpreeMultiVendor::Spree::VariantDecorator.remove_method :vendor_id
20
+
21
+ Spree::Variant.prepend(Vpago::VariantDecorator)
22
+ end
@@ -0,0 +1,10 @@
1
+ module Vpago
2
+ module VendorDecorator
3
+ def self.prepended(base)
4
+ base.has_many :payout_profiles, class_name: 'Spree::PayoutProfile', inverse_of: :vendor
5
+ base.has_many :payment_methods, class_name: 'Spree::PaymentMethod', inverse_of: :vendor
6
+ end
7
+ end
8
+ end
9
+
10
+ Spree::Vendor.prepend(Vpago::VendorDecorator) unless Spree::Vendor.included_modules.include?(Vpago::VendorDecorator)
@@ -0,0 +1,11 @@
1
+ <!-- insert_before ".actions" -->
2
+
3
+ <td class="text-center">
4
+ <% if adjustment.source.is_a?(Spree::PromotionAction) %>
5
+ <%= link_to adjustment.handle_by.to_s.capitalize, edit_admin_promotion_path(adjustment.source.promotion) %>
6
+ <% elsif adjustment.source.is_a?(Spree::TaxRate) %>
7
+ <%= link_to adjustment.handle_by.to_s.capitalize, edit_admin_tax_rate_path(adjustment.source) %>
8
+ <% else %>
9
+ <%= adjustment.handle_by.to_s.capitalize %>
10
+ <% end %>
11
+ </td>
@@ -0,0 +1,3 @@
1
+ <!-- insert_before ".actions" -->
2
+
3
+ <th class="text-center"><%= Spree.t(:handler) %></th>
@@ -0,0 +1,10 @@
1
+ <!-- insert_bottom "[data-hook='admin_adjustment_form_fields']" -->
2
+
3
+ <%= f.field_container :handle_by, class: ['mb-4'] do %>
4
+ <%= f.label :handle_by, raw(Spree.t(:handle_by) + required_span_tag) %>
5
+ <%= f.select :handle_by, Spree::Adjustment.handle_bies.keys.map {|item| [item.capitalize, item]}, {}, :class => "fullwidth select2" %>
6
+ <%= f.error_message_on :handle_by %>
7
+ <small class="form-text text-muted">
8
+ This information is crucial for the financial report.
9
+ </small>
10
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <!-- replace ".edit-method" -->
2
+
3
+ <tr class="edit-method is-hidden total">
4
+ <td colspan="3">
5
+ <div class="field alpha five columns">
6
+ <%= label_tag 'selected_shipping_rate_id', Spree.t(:shipping_method) %>
7
+ <%= select_tag :selected_shipping_rate_id,
8
+ options_for_select(shipment.shipping_rates.map {|sr| ["#{sr.name} #{sr.display_price}", sr.id] }, shipment.selected_shipping_rate_id),
9
+ { class: 'select2', data: {'shipment-number' => shipment.number } } %>
10
+ </div>
11
+ </td>
12
+ <td colspan="2">
13
+ <label><%= Spree.t(:handle_by) %>:</label>
14
+ <%= select_tag :handle_by,
15
+ options_for_select(Spree::ShippingRate.handle_bies.keys, shipment.selected_shipping_rate&.handle_by),
16
+ { class: 'fullwidth select2' } %>
17
+ </td>
18
+ <td class="actions">
19
+ <span class="d-flex justify-content-center">
20
+ <% if can? :update, shipment %>
21
+ <%= link_to_with_icon 'cancel.svg', Spree.t('actions.cancel'), "#", class: 'cancel-method btn btn-light btn-sm', data: {action: 'cancel'}, title: Spree.t('actions.cancel'), no_text: true %>
22
+ <%= link_to_with_icon 'save.svg', Spree.t('actions.save'), "#", class: 'save-method btn btn-success btn-sm', data: {'shipment-number' => shipment.number, action: 'save'}, title: Spree.t('actions.save'), no_text: true %>
23
+ <% end %>
24
+ </span>
25
+ </td>
26
+ </tr>
@@ -0,0 +1,27 @@
1
+ <!-- replace ".show-method" -->
2
+
3
+ <tr class="show-method total">
4
+ <% if shipment.selected_shipping_rate.present? %>
5
+ <td colspan="2">
6
+ <strong><%= shipment.selected_shipping_rate.name %></strong>
7
+ </td>
8
+ <td colspan="2" class="text-center">
9
+ <%= link_to edit_admin_shipping_method_path(shipment.shipping_method) do %>
10
+ Handled by <%= shipment.selected_shipping_rate.handle_by.capitalize %>
11
+ <% end %>
12
+ </td>
13
+ <td class="total text-center">
14
+ <%= shipment.display_cost %>
15
+ </td>
16
+ <% else %>
17
+ <td colspan='5'><%= Spree.t(:no_shipping_method_selected) %></td>
18
+ <% end %>
19
+
20
+ <td class="actions">
21
+ <span class="d-flex justify-content-center">
22
+ <% if( (can? :update, shipment) and !shipment.shipped?) %>
23
+ <%= link_to_with_icon 'edit.svg', Spree.t('edit'), "javascript:;", class: 'edit-method with-tip btn btn-sm btn-light', data: {action: 'edit'}, no_text: true %>
24
+ <% end %>
25
+ </span>
26
+ </td>
27
+ </tr>
@@ -0,0 +1,17 @@
1
+ <!-- insert_after "[data-hook='active']" -->
2
+
3
+ <% if @payment_method.support_pre_auth? %>
4
+ <div class="form-group">
5
+ <strong><%= Spree.t(:enable_pre_auth) %></strong>
6
+
7
+ <div class="form-check my-2">
8
+ <%= radio_button :payment_method, :enable_pre_auth, true, id: 'payment_method_enable_pre_auth_true', class: 'form-check-input' %>
9
+ <%= label_tag 'payment_method_enable_pre_auth_true', Spree.t(:say_yes), class: 'form-check-label' %>
10
+ </div>
11
+
12
+ <div class="form-check my-2">
13
+ <%= radio_button :payment_method, :enable_pre_auth, false, id: 'payment_method_enable_pre_auth_false', class: 'form-check-input' %>
14
+ <%= label_tag 'payment_method_enable_pre_auth_false', Spree.t(:say_no), class: 'form-check-label' %>
15
+ </div>
16
+ </div>
17
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <!-- insert_before "[data-hook='display']" -->
2
+
3
+ <div data-hook="vendor" class="form-group">
4
+ <%= label_tag :payment_method_vendor, Spree.t(:vendor) %>
5
+ <%= collection_select(:payment_method, :vendor_id, Spree::Vendor.all, :id, :name, { include_blank: Spree.t('match_choices.none') }, { class: 'select2' }) %>
6
+ </div>