flex_commerce_api 0.6.57

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +6 -0
  3. data/.gitignore +14 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +1065 -0
  6. data/.ruby-gemset +1 -0
  7. data/.ruby-version +1 -0
  8. data/.travis.yml +3 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +56 -0
  12. data/Rakefile +1 -0
  13. data/app/models/address.rb +41 -0
  14. data/app/models/asset_file.rb +26 -0
  15. data/app/models/asset_folder.rb +14 -0
  16. data/app/models/bundle.rb +20 -0
  17. data/app/models/bundle_group.rb +15 -0
  18. data/app/models/cart.rb +136 -0
  19. data/app/models/category.rb +70 -0
  20. data/app/models/category_tree.rb +11 -0
  21. data/app/models/component.rb +8 -0
  22. data/app/models/container_coupon.rb +12 -0
  23. data/app/models/country.rb +11 -0
  24. data/app/models/coupon.rb +18 -0
  25. data/app/models/customer_account.rb +96 -0
  26. data/app/models/customer_account_authentication.rb +5 -0
  27. data/app/models/customer_segment.rb +6 -0
  28. data/app/models/customer_segment_member.rb +6 -0
  29. data/app/models/data_attribute.rb +6 -0
  30. data/app/models/data_store_record.rb +9 -0
  31. data/app/models/data_store_type.rb +9 -0
  32. data/app/models/discount_summary.rb +12 -0
  33. data/app/models/email.rb +5 -0
  34. data/app/models/ewis_opt_in.rb +8 -0
  35. data/app/models/external_url.rb +6 -0
  36. data/app/models/free_shipping_promotion.rb +12 -0
  37. data/app/models/import.rb +6 -0
  38. data/app/models/import_entry.rb +6 -0
  39. data/app/models/line_item.rb +34 -0
  40. data/app/models/line_item_discount.rb +7 -0
  41. data/app/models/markdown_price.rb +11 -0
  42. data/app/models/menu.rb +36 -0
  43. data/app/models/menu_item.rb +7 -0
  44. data/app/models/menu_item_item.rb +5 -0
  45. data/app/models/note.rb +18 -0
  46. data/app/models/order.rb +38 -0
  47. data/app/models/password_recovery.rb +20 -0
  48. data/app/models/payment_address_verification.rb +13 -0
  49. data/app/models/payment_process.rb +13 -0
  50. data/app/models/payment_provider.rb +15 -0
  51. data/app/models/payment_transaction.rb +13 -0
  52. data/app/models/product.rb +99 -0
  53. data/app/models/product_asset_file.rb +12 -0
  54. data/app/models/promotion.rb +19 -0
  55. data/app/models/promotion_qualifying_product_exclusion.rb +8 -0
  56. data/app/models/redirect.rb +14 -0
  57. data/app/models/refund.rb +14 -0
  58. data/app/models/remote_address.rb +22 -0
  59. data/app/models/remote_line_item.rb +11 -0
  60. data/app/models/remote_order.rb +15 -0
  61. data/app/models/remote_shipping_method.rb +12 -0
  62. data/app/models/report.rb +18 -0
  63. data/app/models/report_invocation.rb +18 -0
  64. data/app/models/retail_store.rb +18 -0
  65. data/app/models/role.rb +6 -0
  66. data/app/models/search_suggestion.rb +17 -0
  67. data/app/models/section.rb +9 -0
  68. data/app/models/session.rb +8 -0
  69. data/app/models/shipping_method.rb +26 -0
  70. data/app/models/slug.rb +19 -0
  71. data/app/models/static_page.rb +60 -0
  72. data/app/models/static_page_folder.rb +8 -0
  73. data/app/models/stock_level.rb +21 -0
  74. data/app/models/tax_code.rb +6 -0
  75. data/app/models/taxonomy.rb +5 -0
  76. data/app/models/template.rb +9 -0
  77. data/app/models/template_component.rb +11 -0
  78. data/app/models/template_definition.rb +12 -0
  79. data/app/models/template_section.rb +12 -0
  80. data/app/models/user.rb +8 -0
  81. data/app/models/user_profile.rb +6 -0
  82. data/app/models/v2/create_order.rb +10 -0
  83. data/app/models/v2/deallocate_order.rb +10 -0
  84. data/app/models/v2/line_item.rb +9 -0
  85. data/app/models/v2/order.rb +9 -0
  86. data/app/models/v2/unallocate_order.rb +10 -0
  87. data/app/models/variant.rb +18 -0
  88. data/app/models/webhook.rb +17 -0
  89. data/app/services/param_to_shql.rb +72 -0
  90. data/app/services/surrogate_keys.rb +44 -0
  91. data/bin/console +14 -0
  92. data/bin/setup +7 -0
  93. data/config/locales/payments.en.yml +3 -0
  94. data/flex-commerce-api.gemspec +41 -0
  95. data/lib/flex_commerce.rb +95 -0
  96. data/lib/flex_commerce_api.rb +21 -0
  97. data/lib/flex_commerce_api/api_base.rb +11 -0
  98. data/lib/flex_commerce_api/base_resource.rb +250 -0
  99. data/lib/flex_commerce_api/config.rb +55 -0
  100. data/lib/flex_commerce_api/error/access_denied.rb +6 -0
  101. data/lib/flex_commerce_api/error/bad_request.rb +10 -0
  102. data/lib/flex_commerce_api/error/base.rb +12 -0
  103. data/lib/flex_commerce_api/error/client_error.rb +7 -0
  104. data/lib/flex_commerce_api/error/connection_error.rb +6 -0
  105. data/lib/flex_commerce_api/error/internal_server.rb +37 -0
  106. data/lib/flex_commerce_api/error/not_found.rb +13 -0
  107. data/lib/flex_commerce_api/error/record_invalid.rb +16 -0
  108. data/lib/flex_commerce_api/error/unexpected_status.rb +7 -0
  109. data/lib/flex_commerce_api/errors.rb +13 -0
  110. data/lib/flex_commerce_api/json_api_client_extension/builder.rb +28 -0
  111. data/lib/flex_commerce_api/json_api_client_extension/capture_surrogate_keys_middleware.rb +16 -0
  112. data/lib/flex_commerce_api/json_api_client_extension/flexible_connection.rb +59 -0
  113. data/lib/flex_commerce_api/json_api_client_extension/has_many_association_proxy.rb +60 -0
  114. data/lib/flex_commerce_api/json_api_client_extension/included_data.rb +27 -0
  115. data/lib/flex_commerce_api/json_api_client_extension/json_format_middleware.rb +20 -0
  116. data/lib/flex_commerce_api/json_api_client_extension/logging_middleware.rb +24 -0
  117. data/lib/flex_commerce_api/json_api_client_extension/paginator.rb +26 -0
  118. data/lib/flex_commerce_api/json_api_client_extension/parse_json.rb +23 -0
  119. data/lib/flex_commerce_api/json_api_client_extension/parsers/parser.rb +16 -0
  120. data/lib/flex_commerce_api/json_api_client_extension/previewed_request_middleware.rb +17 -0
  121. data/lib/flex_commerce_api/json_api_client_extension/remote_builder.rb +29 -0
  122. data/lib/flex_commerce_api/json_api_client_extension/requestor.rb +42 -0
  123. data/lib/flex_commerce_api/json_api_client_extension/save_request_body_middleware.rb +20 -0
  124. data/lib/flex_commerce_api/json_api_client_extension/status_middleware.rb +40 -0
  125. data/lib/flex_commerce_api/v2/api_base.rb +13 -0
  126. data/lib/flex_commerce_api/version.rb +3 -0
  127. data/lib/json_erb.rb +9 -0
  128. data/lib/json_struct.rb +73 -0
  129. data/lib/patches.rb +4 -0
  130. data/lib/patches/json_api_client/resource.rb +50 -0
  131. data/lib/paypal_express.rb +3 -0
  132. data/lib/paypal_express/additional_info.rb +45 -0
  133. data/lib/paypal_express/api.rb +86 -0
  134. data/lib/paypal_express/auth.rb +83 -0
  135. data/lib/paypal_express/cart_shipping_method.rb +38 -0
  136. data/lib/paypal_express/exception/access_denied.rb +10 -0
  137. data/lib/paypal_express/exception/connection_error.rb +10 -0
  138. data/lib/paypal_express/exception/not_authorized.rb +10 -0
  139. data/lib/paypal_express/exception/transaction.rb +15 -0
  140. data/lib/paypal_express/generate_summary.rb +118 -0
  141. data/lib/paypal_express/process/paypal_params.rb +123 -0
  142. data/lib/paypal_express/process/response_parser.rb +146 -0
  143. data/lib/paypal_express/setup.rb +94 -0
  144. data/lib/paypal_express/shipping_methods_for_cart.rb +46 -0
  145. data/lib/retry.rb +20 -0
  146. data/schemas/jsonapi/schema.json +370 -0
  147. data/schemas/shift/v1/documents/collection/address.json +45 -0
  148. data/schemas/shift/v1/documents/collection/asset_file.json +43 -0
  149. data/schemas/shift/v1/documents/collection/asset_folder.json +43 -0
  150. data/schemas/shift/v1/documents/collection/customer_account.json +50 -0
  151. data/schemas/shift/v1/documents/collection/markdown_price.json +43 -0
  152. data/schemas/shift/v1/documents/collection/product.json +43 -0
  153. data/schemas/shift/v1/documents/collection/variant.json +43 -0
  154. data/schemas/shift/v1/documents/member/address.json +39 -0
  155. data/schemas/shift/v1/documents/member/asset_file.json +37 -0
  156. data/schemas/shift/v1/documents/member/asset_folder.json +39 -0
  157. data/schemas/shift/v1/documents/member/customer_account.json +44 -0
  158. data/schemas/shift/v1/documents/member/markdown_price.json +37 -0
  159. data/schemas/shift/v1/documents/member/product.json +39 -0
  160. data/schemas/shift/v1/documents/member/variant.json +46 -0
  161. data/schemas/shift/v1/resources/address.json +130 -0
  162. data/schemas/shift/v1/resources/asset_file.json +146 -0
  163. data/schemas/shift/v1/resources/asset_folder.json +188 -0
  164. data/schemas/shift/v1/resources/customer_account.json +339 -0
  165. data/schemas/shift/v1/resources/markdown_price.json +52 -0
  166. data/schemas/shift/v1/resources/product.json +230 -0
  167. data/schemas/shift/v1/resources/variant.json +298 -0
  168. data/tasks/json_schema.thor +275 -0
  169. data/todo.md +8 -0
  170. metadata +470 -0
@@ -0,0 +1,29 @@
1
+ module FlexCommerceApi
2
+ module JsonApiClientExtension
3
+ class RemoteBuilder < ::JsonApiClient::Query::Builder
4
+ def initialize(klass, path: klass.path, connection: klass.connection)
5
+ super(klass)
6
+ self.connection = connection
7
+ self.path = path
8
+ end
9
+
10
+ def find(args = {})
11
+ case args
12
+ when Hash
13
+ where(args)
14
+ else
15
+ @primary_key = args
16
+ end
17
+
18
+ get_request(params)
19
+ end
20
+
21
+ private
22
+
23
+ def get_request(params)
24
+ klass.parser.parse(klass, connection.run(:get, path, params, klass.custom_headers))
25
+ end
26
+ attr_accessor :path, :connection
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ module FlexCommerceApi
2
+ module JsonApiClientExtension
3
+ class Requestor < ::JsonApiClient::Query::Requestor
4
+ # expects a record
5
+ def create(record)
6
+ request(:post, klass.path(record.attributes, record), {
7
+ data: record.as_json_api
8
+ })
9
+ end
10
+
11
+ def update(record)
12
+ request(:patch, resource_path(record.attributes, record), {
13
+ data: record.as_json_api
14
+ })
15
+ end
16
+
17
+ def get(params = {})
18
+ path = resource_path(params)
19
+ params.delete(klass.primary_key)
20
+ request(:get, path, params)
21
+ end
22
+
23
+ def destroy(record)
24
+ request(:delete, resource_path(record.attributes, record), {})
25
+ end
26
+
27
+ protected
28
+
29
+ def resource_path(parameters, record = nil)
30
+ if resource_id = parameters[klass.primary_key]
31
+ File.join(klass.path(parameters, record), encoded(resource_id))
32
+ else
33
+ klass.path(parameters, record)
34
+ end
35
+ end
36
+
37
+ def encoded(part)
38
+ Addressable::URI.encode_component(part, Addressable::URI::CharacterClasses::UNRESERVED + "\\:")
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ module FlexCommerceApi
2
+ module JsonApiClientExtension
3
+ #
4
+ #
5
+ # @!visibility private
6
+ # This class is used internally to save the request body as it
7
+ # gets replaced by the response body.
8
+ # The result can then be evaluated by an exception handler to show
9
+ # the request and response to the developer.
10
+ class SaveRequestBodyMiddleware < ::Faraday::Middleware
11
+ #
12
+ # Saves the request body in env[:request_body]
13
+ #
14
+ def call(env)
15
+ env[:request_body] = env[:body]
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ module FlexCommerceApi
2
+ module JsonApiClientExtension
3
+ class StatusMiddleware < Faraday::Middleware
4
+ def call(environment)
5
+ request_env = environment.dup
6
+ @app.call(environment).on_complete do |env|
7
+ handle_status(env[:status], env, request_env)
8
+
9
+ # look for meta[:status]
10
+ if env[:body].is_a?(Hash)
11
+ code = env[:body].fetch("meta", {}).fetch("status", 200).to_i
12
+ handle_status(code, env, request_env)
13
+ end
14
+ end
15
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError
16
+ raise ::FlexCommerceApi::Error::ConnectionError, environment
17
+ end
18
+
19
+ protected
20
+
21
+ def handle_status(code, env, request_environment)
22
+ case code
23
+ when 200..399
24
+ when 400
25
+ raise ::FlexCommerceApi::Error::BadRequest.new request_environment, env
26
+ when 403, 401
27
+ raise ::FlexCommerceApi::Error::AccessDenied.new request_environment, env
28
+ when 404
29
+ raise ::FlexCommerceApi::Error::NotFound, env[:url]
30
+ when 400..499
31
+ # some other error
32
+ when 500..599
33
+ raise ::FlexCommerceApi::Error::InternalServer.new request_environment, env
34
+ else
35
+ raise ::FlexCommerceApi::Error::UnexpectedStatus.new(code, env[:url])
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ require "flex_commerce_api/base_resource"
2
+
3
+ module FlexCommerceApi
4
+ module V2
5
+ class ApiBase < FlexCommerceApi::BaseResource
6
+ def self.endpoint_version
7
+ "v2"
8
+ end
9
+
10
+ reconfigure
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module FlexCommerceApi
2
+ VERSION = '0.6.57'
3
+ end
@@ -0,0 +1,9 @@
1
+ class JsonErb < OpenStruct
2
+ def self.render_from_hash(t, h)
3
+ new(h).render(t)
4
+ end
5
+
6
+ def render(template)
7
+ ERB.new(template).result(binding)
8
+ end
9
+ end
@@ -0,0 +1,73 @@
1
+ require 'ostruct'
2
+ require "json"
3
+
4
+ class JsonStruct < OpenStruct
5
+ def initialize(hash=nil)
6
+
7
+ @table = {}
8
+ @hash_table = {}
9
+
10
+ if hash
11
+ recurse = Proc.new do |item|
12
+ values = []
13
+
14
+ item.each do |val|
15
+ if val.is_a?(Hash)
16
+ values.push(self.class.new(val))
17
+ elsif val.is_a?(Array)
18
+ values.push(recurse.call(val))
19
+ else
20
+ values.push(val)
21
+ end
22
+ end
23
+
24
+ item.clear
25
+ item.push(*values)
26
+
27
+ item
28
+ end
29
+
30
+ hash.each do |k,v|
31
+
32
+ if v.is_a?(Array)
33
+ recurse.call(v)
34
+ end
35
+
36
+ @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
37
+ @hash_table[k.to_sym] = v
38
+ new_ostruct_member(k)
39
+
40
+ end
41
+ end
42
+ end
43
+
44
+ def to_h
45
+ op = {}
46
+
47
+ @table.each_pair do |key, value|
48
+ if value.is_a?(Array)
49
+ op[key] = value.map do |item|
50
+ if item.is_a?(self.class)
51
+ item.to_h
52
+ else
53
+ item
54
+ end
55
+ end
56
+ elsif value.is_a?(self.class)
57
+ op[key] = value.to_h
58
+ else
59
+ op[key] = value
60
+ end
61
+ end
62
+ op
63
+ end
64
+
65
+ def as_json(*args)
66
+ to_h
67
+ end
68
+
69
+ def to_json
70
+ JSON.dump(to_h)
71
+ end
72
+
73
+ end
@@ -0,0 +1,4 @@
1
+ require "json_api_client"
2
+ Dir.glob(File.expand_path("../patches/**/*.rb", __FILE__)).each do |file|
3
+ require file
4
+ end
@@ -0,0 +1,50 @@
1
+ require "json_api_client/version"
2
+ if ["1.1.1"].include?(JsonApiClient::VERSION)
3
+ require "json_api_client/resource"
4
+ module JsonApiClient
5
+ class Resource
6
+ def save
7
+ return false unless valid?
8
+
9
+ self.last_result_set = if persisted?
10
+ self.class.requestor.update(self)
11
+ else
12
+ self.class.requestor.create(self)
13
+ end
14
+
15
+ if last_result_set.has_errors?
16
+ last_result_set.errors.each do |error|
17
+ if error.source_parameter
18
+ errors.add(error.source_parameter, error.title || error.detail)
19
+ else
20
+ errors.add(:base, error.title || error.detail)
21
+ end
22
+ end
23
+ false
24
+ else
25
+ self.errors.clear if self.errors
26
+ mark_as_persisted!
27
+ if updated = last_result_set.first
28
+ self.attributes = updated.attributes
29
+ # This line has been added as part of https://github.com/chingor13/json_api_client/pull/238
30
+ self.links.attributes = updated.links.attributes
31
+ self.relationships.attributes = updated.relationships.attributes
32
+ clear_changes_information
33
+ # This line has been added as part of https://github.com/JsonApiClient/json_api_client/pull/285
34
+ self.relationships.clear_changes_information
35
+ end
36
+ true
37
+ end
38
+ end
39
+ end
40
+ end
41
+ else
42
+ raise %q(
43
+ Please check these two PRs:
44
+ * https://github.com/chingor13/json_api_client/pull/238 (This was released in version 1.5.0)
45
+ * https://github.com/JsonApiClient/json_api_client/pull/285 (This hasn't yet been released at the time of writing this)
46
+
47
+ If both have been merged into the gem version you are using, remove this file (#{__FILE__}).
48
+ If not, add the current version to the allowed array at the top of this file.
49
+ )
50
+ end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.expand_path("../paypal_express/**/*.rb", __FILE__)).each do |file|
2
+ require file
3
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'api'
2
+
3
+ # @module FlexCommerce::PaypalExpress
4
+ module FlexCommerce
5
+ module PaypalExpress
6
+ # @class AdditionalInfo
7
+ # Address verification service using paypal
8
+ class AdditionalInfo
9
+ include ::FlexCommerce::PaypalExpress::Api
10
+
11
+ def initialize(gateway_class: ::ActiveMerchant::Billing::PaypalExpressGateway, shipping_method_model: FlexCommerce::ShippingMethod, options:)
12
+ self.gateway_class = gateway_class
13
+ self.token = options[:token]
14
+ self.shipping_method_model = shipping_method_model
15
+ self.gateway_details = {}
16
+ end
17
+
18
+ # @method call
19
+ #
20
+ # Fetches Shipping Method, Billing and Shipping address
21
+ # details from Paypal
22
+ #
23
+ # @return [PaymentAdditionalInfo]
24
+ def call
25
+ PaymentAdditionalInfo.new(meta: gateway_details_for(token))
26
+ end
27
+
28
+ private
29
+
30
+ attr_accessor :gateway_class, :token, :gateway_details, :shipping_method_model
31
+
32
+ # @method gateway_details_for
33
+ #
34
+ # @param {ID} token - Paypal token
35
+ #
36
+ def gateway_details_for(token)
37
+ response = gateway_details[token] ||= gateway.details_for(token)
38
+ raise ::FlexCommerce::PaypalExpress::Exception::AccessDenied.new(response.message) unless response.success?
39
+ Process::ResponseParser.new(response: response, shipping_method_model: shipping_method_model).call
40
+ end
41
+ end
42
+
43
+ class PaymentAdditionalInfo < OpenStruct; end
44
+ end
45
+ end
@@ -0,0 +1,86 @@
1
+ require "active_support/concern"
2
+ # @module FlexCommerce::PaypalExpress::Api
3
+ module FlexCommerce
4
+ module PaypalExpress
5
+ # A concern for use in paypal express services that need access to the API
6
+ module Api
7
+ extend ActiveSupport::Concern
8
+
9
+ USER_ERRORS = {
10
+ "10410" => {gateway_response: "Invalid token"},
11
+ "10411" => {gateway_response: "Checkout session expired"},
12
+ "10415" => {gateway_response: "Duplicate transaction"},
13
+ "13113" => {gateway_response: "Paypal declined the transaction"},
14
+ "10417" => {gateway_response: "Paypal cannot process this transaction. Please use alternative payment method"},
15
+ "10419" => {gateway_response: "PayerID Missing"},
16
+ "10421" => {gateway_response: "Invalid token"},
17
+ "10422" => {gateway_response: "Invalid funding source. Please try again with a different funding source"},
18
+ "10424" => {gateway_response: "Invalid shipping address"},
19
+ "10474" => {gateway_response: "Invalid shipping country - must be the same as the paypal account"},
20
+ "10486" => {gateway_response: "Invalid funding source. Please try again with a different funding source"},
21
+ "10736" => {gateway_response: "Invalid shipping address"},
22
+ "11084" => {gateway_response: "No funding sources. Please try a different payment method"},
23
+ "13122" => {gateway_response: "This transaction cannot be completed because it violates the PayPal User Agreement"},
24
+ "10606" => {gateway_response: "Paypal cannot process this transaction using the payment method provided"},
25
+ "10626" => {gateway_response: "Paypal declined this transaction due to its risk model"}
26
+ }.freeze
27
+
28
+ private
29
+
30
+ def gateway
31
+ verify_credentials
32
+ @gateway ||= gateway_class.new(
33
+ test: test_mode,
34
+ login: paypal_login,
35
+ password: paypal_password,
36
+ signature: paypal_signature)
37
+ end
38
+
39
+ def is_user_error?(response)
40
+ (USER_ERRORS.keys & response_error_codes(response)).present?
41
+ end
42
+
43
+ def mark_transaction_with_errors!(response)
44
+ errors = []
45
+ response_error_codes(response).each do |error_code|
46
+ errors.push(USER_ERRORS[error_code])
47
+ end
48
+ errors
49
+ end
50
+
51
+ def response_error_codes(response)
52
+ response.params["error_codes"].split(",")
53
+ end
54
+
55
+ def verify_credentials
56
+ unless paypal_login.present? && paypal_password.present? && paypal_signature.present? then
57
+ raise "Please ensure all Paypal Credentails are set in your env file."
58
+ end
59
+ end
60
+
61
+ # DEFAULT value for test mode is true.
62
+ def test_mode
63
+ FlexCommerceApi.config.order_test_mode == true || FlexCommerceApi.config.order_test_mode == "true"
64
+ end
65
+
66
+ # PAYPAL CREDENTAILS
67
+
68
+ def paypal_login
69
+ FlexCommerceApi.config.paypal_login
70
+ end
71
+
72
+ def paypal_password
73
+ FlexCommerceApi.config.paypal_password
74
+ end
75
+
76
+ def paypal_signature
77
+ FlexCommerceApi.config.paypal_signature
78
+ end
79
+
80
+ def convert_amount(amount)
81
+ (amount * 100.0).round.to_i
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'api'
3
+ require 'retry'
4
+
5
+ # @module FlexCommerce::PaypalExpress
6
+ module FlexCommerce
7
+ module PaypalExpress
8
+ # @class Setup
9
+ #
10
+ # This service authorises the payment via the Paypal gateway
11
+ class Auth
12
+ include ::Retry
13
+ include ::FlexCommerce::PaypalExpress::Api
14
+
15
+ DEFAULT_CURRENCY = "GBP"
16
+
17
+ # @initialize
18
+ #
19
+ # @param {String} token - Paypal token
20
+ # @param {String} payer_id - Paypal user id
21
+ def initialize(cart:, token:, payer_id:, payment_transaction:, gateway_class: ::ActiveMerchant::Billing::PaypalExpressGateway)
22
+ self.cart = cart
23
+ self.token = token
24
+ self.payer_id = payer_id
25
+ self.payment_transaction = payment_transaction
26
+ self.gateway_class = gateway_class
27
+ end
28
+
29
+ def call
30
+ process_with_gateway
31
+ end
32
+
33
+ private
34
+
35
+ attr_accessor :cart, :token, :payer_id, :payment_transaction, :gateway_class
36
+
37
+ def process_with_gateway
38
+ # Fetch Order details from Paypal
39
+ response = do_express_checkout_payment
40
+ unless response.success?
41
+ unless is_user_error?(response)
42
+ raise ::FlexCommerce::PaypalExpress::Exception::NotAuthorized.new("Payment not authorised - #{response.message}", response: response)
43
+ end
44
+ return mark_transaction_with_errors!(response)
45
+ end
46
+
47
+ # Authorizing transaction
48
+ auth_response = do_authorization(response)
49
+ unless auth_response.success?
50
+ unless is_user_error?(auth_response)
51
+ raise ::FlexCommerce::PaypalExpress::Exception::NotAuthorized.new("Failed authorising transaction - #{auth_response.message}", response: auth_response)
52
+ end
53
+ return mark_transaction_with_errors!(auth_response)
54
+ end
55
+
56
+ payment_transaction.attributes = { gateway_response: { payer_id: payer_id, token: token, transaction_id: response.params["transaction_id"], authorization_id: auth_response.params["transaction_id"]} }
57
+ payment_transaction.save
58
+ payment_transaction
59
+ rescue ::ActiveMerchant::ConnectionError => ex
60
+ raise ::FlexCommerce::PaypalExpress::Exception::ConnectionError.new("Failed authorising transaction due to a connection error. Original message was #{ex.message}")
61
+ end
62
+
63
+ def do_express_checkout_payment
64
+ Retry.call(no_of_retries: no_of_retires, rescue_errors: ::ActiveMerchant::ConnectionError) {
65
+ ::NewRelic::Agent.increment_metric('Custom/Paypal/Do_Express_Checkout_Payment') if defined?(NewRelic::Agent)
66
+ gateway.order(convert_amount(cart.total), token: token, payer_id: payer_id, currency: DEFAULT_CURRENCY)
67
+ }
68
+ end
69
+
70
+
71
+ def do_authorization(response)
72
+ Retry.call(no_of_retries: no_of_retires, rescue_errors: ::ActiveMerchant::ConnectionError) {
73
+ ::NewRelic::Agent.increment_metric('Custom/Paypal/Do_Auhtorization') if defined?(NewRelic::Agent)
74
+ gateway.authorize_transaction(response.params["transaction_id"], convert_amount(cart.total), transaction_entity: "Order", currency: DEFAULT_CURRENCY, payer_id: payer_id)
75
+ }
76
+ end
77
+
78
+ def no_of_retires
79
+ FlexCommerceApi.config.paypal_connection_errors_no_of_retries
80
+ end
81
+ end
82
+ end
83
+ end