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,84 @@
1
+ module Vpago
2
+ module Payway
3
+ class PaymentStatusMarker
4
+ attr_accessor :payment, :error_message
5
+
6
+ # :status, :description, :updated_by_user_id, updated_reason
7
+ def initialize(payment, options = {})
8
+ @payment = payment
9
+ @options = options
10
+
11
+ @options[:status] = @options[:status] || false
12
+ end
13
+
14
+ def call
15
+ ActiveRecord::Base.transaction do
16
+ update_payment_source
17
+ update_payment_and_order
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def update_payment_source
24
+ source = @payment.source
25
+
26
+ payment_status = @options[:status] ? 'success' : 'failed'
27
+ source.payment_status = payment_status
28
+ source.payment_description = @options[:description]
29
+
30
+ if @options[:status]
31
+ source.updated_by_user_id = @options[:updated_by_user_id]
32
+ source.updated_reason = @options[:updated_reason]
33
+ source.updated_by_user_at = Time.zone.now
34
+ end
35
+
36
+ return if source.save
37
+
38
+ @error_message = source.errors.full_messages.join('\n')
39
+ end
40
+
41
+ def update_payment_and_order
42
+ if @options[:status]
43
+ transition_to_paid!
44
+ else
45
+ transition_to_failed!
46
+ end
47
+
48
+ order_updater
49
+ end
50
+
51
+ def transition_to_paid!
52
+ complete_payment!
53
+ complete_order!
54
+ end
55
+
56
+ def transition_to_failed!
57
+ @payment.failure! unless @payment.failed?
58
+ @payment.order.update(state: 'payment')
59
+
60
+ notify_failed_payment
61
+ end
62
+
63
+ def order_updater
64
+ @payment.order.update_with_updater!
65
+ end
66
+
67
+ def complete_payment!
68
+ @payment.complete! unless @payment.completed?
69
+ end
70
+
71
+ def complete_order!
72
+ return if @payment.order.completed?
73
+
74
+ order = @payment.order
75
+ order.finalize!
76
+ order.update(state: 'complete', completed_at: Time.zone.now)
77
+ end
78
+
79
+ def notify_failed_payment
80
+ Rails.logger.info("Gateway error check with: #{@options[:description]} and will be marked as failed")
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,74 @@
1
+ require 'faraday'
2
+
3
+ module Vpago
4
+ module Payway
5
+ class TransactionStatus < Base
6
+ attr_accessor :error_message, :result
7
+
8
+ def call
9
+ prepare
10
+ process
11
+ end
12
+
13
+ def prepare
14
+ @error_message = nil
15
+ @result = nil
16
+ end
17
+
18
+ def checker_hmac
19
+ data = "#{merchant_id}#{transaction_id}"
20
+ hash = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), api_key, data))
21
+
22
+ # somehow php counter part are not able to decode if the \n present.
23
+ hash.delete("\n")
24
+ end
25
+
26
+ def check_remote_status
27
+ conn = Faraday::Connection.new do |faraday|
28
+ faraday.request :url_encoded
29
+ end
30
+
31
+ data = {
32
+ tran_id: transaction_id,
33
+ hash: checker_hmac
34
+ }
35
+
36
+ conn.post(check_transaction_url, data)
37
+ end
38
+
39
+ def process
40
+ @response = check_remote_status
41
+
42
+ if @response.status == 200
43
+ if json_response['status'].zero?
44
+ @result = json_response
45
+ else
46
+ fail!(json_response['description'])
47
+ end
48
+ else
49
+ fail!(@response.body)
50
+ end
51
+ end
52
+
53
+ def json_response
54
+ @json_response ||= JSON.parse(@response.body)
55
+ end
56
+
57
+ def fail!(message)
58
+ @error_message = message
59
+ end
60
+
61
+ def success?
62
+ @error_message.nil?
63
+ end
64
+
65
+ def check_transaction_url
66
+ if endpoint[-1] == '/'
67
+ "#{host}#{endpoint}check/transaction/"
68
+ else
69
+ "#{host}#{endpoint}/check/transaction/"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,13 @@
1
+ module Vpago
2
+ module Payway
3
+ CARD_TYPE_ABAPAY = 'abapay'.freeze
4
+ CARD_TYPE_ABAPAY_KHQR = 'abapay_khqr'.freeze
5
+ CARD_TYPE_ABAPAY_KHQR_DEEPLINK = 'abapay_khqr_deeplink'.freeze
6
+ CARD_TYPE_CARDS = 'cards'.freeze
7
+ CARD_TYPE_WECHAT = 'wechat'.freeze
8
+ CARD_TYPE_ALIPAY = 'alipay'.freeze
9
+
10
+ CARD_TYPES = [CARD_TYPE_ABAPAY, CARD_TYPE_ABAPAY_KHQR, CARD_TYPE_ABAPAY_KHQR_DEEPLINK, CARD_TYPE_CARDS,
11
+ CARD_TYPE_WECHAT, CARD_TYPE_ALIPAY].freeze
12
+ end
13
+ end
@@ -0,0 +1,153 @@
1
+ module Vpago
2
+ module PaywayV2
3
+ class Base
4
+ def initialize(payment, options = {})
5
+ @options = options
6
+ @payment = payment
7
+ end
8
+
9
+ def req_time
10
+ @payment.created_at.strftime('%Y%m%d%H%M%S')
11
+ end
12
+
13
+ def type
14
+ @payment.payment_method.enable_pre_auth ? 'pre-auth' : 'purchase'
15
+ end
16
+
17
+ def host
18
+ @payment.payment_method.preferences[:host]
19
+ end
20
+
21
+ def public_key
22
+ @payment.payment_method.preferences[:public_key]
23
+ end
24
+
25
+ def amount
26
+ format('%.2f', (@payment.amount + transaction_fee))
27
+ end
28
+
29
+ def transaction_fee_fix
30
+ @payment.payment_method.preferences[:transaction_fee_fix].to_f
31
+ end
32
+
33
+ def transaction_fee_percentage
34
+ @payment.payment_method.preferences[:transaction_fee_percentage].to_f
35
+ end
36
+
37
+ def transaction_fee
38
+ transaction_fee_fix + ((@payment.amount * transaction_fee_percentage) / 100)
39
+ end
40
+
41
+ def merchant_id
42
+ @payment.payment_method.preferences[:merchant_id]
43
+ end
44
+
45
+ def transaction_id
46
+ @payment.number
47
+ end
48
+
49
+ def email
50
+ @payment.order.email.presence || ENV.fetch('DEFAULT_EMAIL_FOR_PAYMENT', nil)
51
+ end
52
+
53
+ def first_name
54
+ @payment.order.billing_address.first_name.strip
55
+ end
56
+
57
+ def last_name
58
+ @payment.order.billing_address.last_name.strip
59
+ end
60
+
61
+ def return_url
62
+ uri = URI.parse(@payment.process_payment_url)
63
+ uri.query = nil
64
+
65
+ Base64.encode64(uri.to_s).delete("\n")
66
+ end
67
+
68
+ def continue_success_url
69
+ @payment.processing_url
70
+ end
71
+
72
+ # null, hosted_view, checkout, qr
73
+ def view_type
74
+ 'hosted_view'
75
+ end
76
+
77
+ def payment_option
78
+ card_option = @payment.payment_method.preferences[:payment_option]
79
+ Vpago::Payway::CARD_TYPES.index(card_option).nil? ? Vpago::Payway::CARD_TYPE_ABAPAY : card_option
80
+ end
81
+
82
+ def phone_country_code
83
+ '+855'
84
+ end
85
+
86
+ def phone
87
+ @payment.order.billing_address.phone
88
+ end
89
+
90
+ def api_key
91
+ @payment.payment_method.preferences[:api_key]
92
+ end
93
+
94
+ def return_params
95
+ uri = URI.parse(@payment.process_payment_url)
96
+ query_params = Rack::Utils.parse_nested_query(uri.query)
97
+ query_params.to_json
98
+ end
99
+
100
+ def payout
101
+ @payout ||= begin
102
+ payouts = Vpago::PaywayV2::PayoutsParamsConstructor.new(@payment).call
103
+ payouts.empty? ? nil : Base64.strict_encode64(payouts.to_json)
104
+ end
105
+ end
106
+
107
+ # redirect to continue URL, let it handle redirecting to app.
108
+ # allowed override to specific app eg. from tg://t.me
109
+ def return_deeplink_url
110
+ return @options[:override_return_deeplink_url] if @options[:override_return_deeplink_url].present?
111
+
112
+ continue_success_url
113
+ end
114
+
115
+ def return_deeplink
116
+ return nil unless return_deeplink_url.present?
117
+
118
+ Base64.strict_encode64({ android_scheme: return_deeplink_url, ios_scheme: return_deeplink_url }.to_json)
119
+ end
120
+
121
+ def hash_hmac
122
+ hash = Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), api_key, hash_data))
123
+
124
+ log_hash_data = "Hash hmac: #{hash}"
125
+ Rails.logger.info(log_hash_data)
126
+
127
+ hash
128
+ end
129
+
130
+ def hash_data
131
+ result = "#{req_time}#{merchant_id}#{transaction_id}#{amount}#{first_name}#{last_name}#{email}#{phone}"
132
+
133
+ result += type if type.present?
134
+ result += payment_option if payment_option.present?
135
+ result += return_url if return_url.present?
136
+ result += continue_success_url if continue_success_url.present?
137
+ result += return_deeplink if return_deeplink.present?
138
+ result += return_params if return_params.present?
139
+ result += payout if payout.present?
140
+
141
+ log_hash_data = "Hash data: #{result}"
142
+ Rails.logger.info(log_hash_data)
143
+
144
+ result
145
+ end
146
+
147
+ def order_jwt_token
148
+ payload = { order_number: @payment.order.number, order_id: @payment.order.id }
149
+ JWT.encode(payload, @payment.order.token, 'HS256')
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,35 @@
1
+ module Vpago
2
+ module PaywayV2
3
+ class Checkout < Base
4
+ def gateway_params
5
+ result = {
6
+ req_time: req_time,
7
+ merchant_id: merchant_id,
8
+ tran_id: transaction_id,
9
+ amount: amount,
10
+ type: type,
11
+ firstname: first_name,
12
+ lastname: last_name,
13
+ email: email,
14
+ phone: phone,
15
+ payment_option: payment_option,
16
+ return_url: return_url,
17
+ continue_success_url: continue_success_url,
18
+ return_params: return_params,
19
+ hash: hash_hmac
20
+ }
21
+
22
+ result[:return_deeplink] = return_deeplink unless return_deeplink.nil?
23
+ result[:view_type] = view_type unless view_type.nil?
24
+ result[:payout] = payout unless payout.nil?
25
+ result
26
+ end
27
+
28
+ def checkout_url
29
+ "#{host}#{ENV.fetch('PAYWAY_CHECKOUT_PATH', nil)}"
30
+ end
31
+
32
+ alias action_url checkout_url
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,65 @@
1
+ module Vpago
2
+ module PaywayV2
3
+ class PaymentRequestUpdater < ::Vpago::PaymentRequestUpdater
4
+ def call
5
+ return if @payment.order.paid?
6
+
7
+ if items_eligible?
8
+ process_payment_status
9
+ else
10
+ mark_items_as_ineligible
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def mark_items_as_ineligible
17
+ @error_message = 'Items are not eligible due to insufficient stock'
18
+ marker_options = @options.merge(status: false, description: @error_message)
19
+ marker = ::Vpago::PaymentStatusMarker.new(@payment, marker_options)
20
+ marker.call
21
+ end
22
+
23
+ def process_payment_status
24
+ checker = check_payway_status
25
+
26
+ if checker.success?
27
+ mark_payment_as_success(checker)
28
+ elsif checker.failed?
29
+ mark_payment_as_failed(checker.error_message)
30
+ end
31
+ end
32
+
33
+ def mark_payment_as_success(checker)
34
+ @error_message = nil
35
+ checker_result = {
36
+ status: true,
37
+ description: nil,
38
+ payway_v2_response: checker.json_response,
39
+ payout_total: checker.payout_total
40
+ }
41
+ marker_options = @options.merge(checker_result)
42
+ marker = ::Vpago::PaymentStatusMarker.new(@payment, marker_options)
43
+ marker.call
44
+ end
45
+
46
+ def mark_payment_as_failed(error_message)
47
+ @error_message = error_message
48
+ marker_options = @options.merge(status: false, description: @error_message)
49
+
50
+ marker = ::Vpago::PaymentStatusMarker.new(@payment, marker_options)
51
+ marker.call
52
+ end
53
+
54
+ def check_payway_status
55
+ trans_status = Vpago::PaywayV2::TransactionStatus.new(@payment)
56
+ trans_status.call
57
+ trans_status
58
+ end
59
+
60
+ def items_eligible?
61
+ @payment&.order&.line_items&.all?(&:sufficient_stock?) == true
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,47 @@
1
+ module Vpago
2
+ module PaywayV2
3
+ class PayoutsParamsConstructor
4
+ attr_reader :payment, :payouts
5
+
6
+ def initialize(payment)
7
+ @payment = payment
8
+ @payouts = payment.payouts.includes(:payout_profile)
9
+ end
10
+
11
+ def call
12
+ payouts = build_payouts_from_payment
13
+ payouts = group_payouts(payouts)
14
+
15
+ format_payouts(payouts)
16
+ end
17
+
18
+ def build_payouts_from_payment
19
+ payouts.each_with_object([]) do |payout, payouts_hash|
20
+ payouts_hash << {
21
+ acc: payout.payout_profile.bank_account_number,
22
+ amt: payout.amount
23
+ }
24
+ end
25
+ end
26
+
27
+ # group & sum amount by acc number.
28
+ def group_payouts(payouts)
29
+ payouts.group_by { |item| item[:acc] }.map do |acc, items|
30
+ {
31
+ acc: acc,
32
+ amt: items.sum { |item| item[:amt] }
33
+ }
34
+ end
35
+ end
36
+
37
+ def format_payouts(payouts)
38
+ payouts.map do |payout|
39
+ {
40
+ acc: payout[:acc],
41
+ amt: format('%.2f', payout[:amt])
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,43 @@
1
+ require 'faraday'
2
+
3
+ module Vpago
4
+ module PaywayV2
5
+ class PreAuthCanceler < PreAuthCompleter
6
+ # override
7
+ def pre_auth_response
8
+ conn = Faraday::Connection.new do |faraday|
9
+ faraday.request :url_encoded
10
+ end
11
+
12
+ conn.post(cancel_url, request_data)
13
+ end
14
+
15
+ def request_data
16
+ {
17
+ request_time: req_time,
18
+ merchant_id: merchant_id,
19
+ merchant_auth: merchant_auth_encryption,
20
+ hash: hash_data
21
+ }
22
+ end
23
+
24
+ # override
25
+ def hash_data
26
+ data = "#{merchant_id}#{merchant_auth_encryption}#{req_time}"
27
+ Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), api_key, data))
28
+ end
29
+
30
+ # override
31
+ def merchant_auth
32
+ {
33
+ 'mc_id' => merchant_id,
34
+ 'tran_id' => transaction_id
35
+ }.to_json
36
+ end
37
+
38
+ def cancel_url
39
+ "#{host}#{ENV.fetch('PAYWAY_V2_PRE_AUTH_CANCEL_PATH')}"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,76 @@
1
+ require 'faraday'
2
+
3
+ module Vpago
4
+ module PaywayV2
5
+ class PreAuthCompleter < Base
6
+ def call
7
+ return if completed? || cancelled?
8
+
9
+ @response = pre_auth_response
10
+ save_response if success?
11
+ end
12
+
13
+ def completed? = @payment.pre_auth_response['transaction_status'] == 'COMPLETED'
14
+ def cancelled? = @payment.pre_auth_response['transaction_status'] == 'CANCELLED'
15
+
16
+ def save_response
17
+ @payment.update(pre_auth_response: json_response)
18
+ end
19
+
20
+ def pre_auth_response
21
+ conn = Faraday::Connection.new do |faraday|
22
+ faraday.request :url_encoded
23
+ end
24
+
25
+ conn.post(complete_url, request_data)
26
+ end
27
+
28
+ def request_data
29
+ {
30
+ request_time: req_time,
31
+ merchant_id: merchant_id,
32
+ merchant_auth: merchant_auth_encryption,
33
+ hash: hash_data
34
+ }
35
+ end
36
+
37
+ def hash_data
38
+ data = "#{merchant_auth_encryption}#{req_time}#{merchant_id}"
39
+ Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), api_key, data))
40
+ end
41
+
42
+ def merchant_auth
43
+ {
44
+ 'mc_id' => merchant_id,
45
+ 'tran_id' => transaction_id,
46
+ 'complete_amount' => amount.to_s
47
+ }.to_json
48
+ end
49
+
50
+ def merchant_auth_encryption
51
+ @merchant_auth_encryption ||= Vpago::PayoutProfiles::Payway::OpenSslEncrypter.new(
52
+ content: merchant_auth,
53
+ rsa_public_key: public_key
54
+ ).call
55
+ end
56
+
57
+ def complete_url
58
+ "#{host}#{ENV.fetch('PAYWAY_V2_PRE_AUTH_COMPLETE_PATH')}"
59
+ end
60
+
61
+ def success?
62
+ json_response['status']['code'] == '00'
63
+ end
64
+
65
+ def json_response
66
+ return @payment.pre_auth_response if @payment.pre_auth_response.present?
67
+
68
+ @json_response ||= begin
69
+ JSON.parse(@response.body)
70
+ rescue JSON::ParserError
71
+ {}
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,85 @@
1
+ require 'faraday'
2
+
3
+ module Vpago
4
+ module PaywayV2
5
+ class TransactionStatus < Base
6
+ # status:
7
+ # 0 – Approved, PRE_AUTH, PREAUTH_APPROVED
8
+ # 1 – Created
9
+ # 2 – Pending
10
+ # 3 – Declined
11
+ # 4 – Refunded
12
+ # 5 – Wrong Hash
13
+ # 7 - Cancelled
14
+ # 11 – Other Server-side Error
15
+ def call
16
+ @response = check_remote_status
17
+ end
18
+
19
+ def status
20
+ json_response['status']&.to_s
21
+ end
22
+
23
+ def success?
24
+ status == '0'
25
+ end
26
+
27
+ # request failed does not mean payment failed.
28
+ # but it failed when status is failed.
29
+ def pending?
30
+ %w[1 2].include?(status)
31
+ end
32
+
33
+ def failed?
34
+ %w[3 4 5 7].include?(status)
35
+ end
36
+
37
+ def check_remote_status
38
+ conn = Faraday::Connection.new do |faraday|
39
+ faraday.request :url_encoded
40
+ end
41
+
42
+ data = {
43
+ req_time: req_time,
44
+ merchant_id: merchant_id,
45
+ tran_id: transaction_id,
46
+ hash: checker_hmac
47
+ }
48
+
49
+ conn.post(check_transaction_url, data)
50
+ end
51
+
52
+ def payout_total
53
+ payouts_response = json_response['payout']
54
+
55
+ return nil if payouts_response.nil? || !payouts_response.is_a?(Array) || payouts_response.empty?
56
+
57
+ payouts_response.map { |payout| payout['amt'].to_f || 0 }.sum
58
+ end
59
+
60
+ def error_message
61
+ json_response['description'].presence
62
+ end
63
+
64
+ def json_response
65
+ @json_response ||= begin
66
+ JSON.parse(@response.body)
67
+ rescue JSON::ParserError
68
+ {}
69
+ end
70
+ end
71
+
72
+ def checker_hmac
73
+ data = "#{req_time}#{merchant_id}#{transaction_id}"
74
+ hash = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), api_key, data))
75
+
76
+ # somehow php counter part are not able to decode if the \n present.
77
+ hash.delete("\n")
78
+ end
79
+
80
+ def check_transaction_url
81
+ "#{host}#{ENV.fetch('PAYWAY_CHECK_TRANSACTION_PATH', nil)}"
82
+ end
83
+ end
84
+ end
85
+ end