flex_commerce_api 0.6.57

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 (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