flex_commerce_api 0.6.57
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env.example +6 -0
- data/.gitignore +14 -0
- data/.rspec +4 -0
- data/.rubocop.yml +1065 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +56 -0
- data/Rakefile +1 -0
- data/app/models/address.rb +41 -0
- data/app/models/asset_file.rb +26 -0
- data/app/models/asset_folder.rb +14 -0
- data/app/models/bundle.rb +20 -0
- data/app/models/bundle_group.rb +15 -0
- data/app/models/cart.rb +136 -0
- data/app/models/category.rb +70 -0
- data/app/models/category_tree.rb +11 -0
- data/app/models/component.rb +8 -0
- data/app/models/container_coupon.rb +12 -0
- data/app/models/country.rb +11 -0
- data/app/models/coupon.rb +18 -0
- data/app/models/customer_account.rb +96 -0
- data/app/models/customer_account_authentication.rb +5 -0
- data/app/models/customer_segment.rb +6 -0
- data/app/models/customer_segment_member.rb +6 -0
- data/app/models/data_attribute.rb +6 -0
- data/app/models/data_store_record.rb +9 -0
- data/app/models/data_store_type.rb +9 -0
- data/app/models/discount_summary.rb +12 -0
- data/app/models/email.rb +5 -0
- data/app/models/ewis_opt_in.rb +8 -0
- data/app/models/external_url.rb +6 -0
- data/app/models/free_shipping_promotion.rb +12 -0
- data/app/models/import.rb +6 -0
- data/app/models/import_entry.rb +6 -0
- data/app/models/line_item.rb +34 -0
- data/app/models/line_item_discount.rb +7 -0
- data/app/models/markdown_price.rb +11 -0
- data/app/models/menu.rb +36 -0
- data/app/models/menu_item.rb +7 -0
- data/app/models/menu_item_item.rb +5 -0
- data/app/models/note.rb +18 -0
- data/app/models/order.rb +38 -0
- data/app/models/password_recovery.rb +20 -0
- data/app/models/payment_address_verification.rb +13 -0
- data/app/models/payment_process.rb +13 -0
- data/app/models/payment_provider.rb +15 -0
- data/app/models/payment_transaction.rb +13 -0
- data/app/models/product.rb +99 -0
- data/app/models/product_asset_file.rb +12 -0
- data/app/models/promotion.rb +19 -0
- data/app/models/promotion_qualifying_product_exclusion.rb +8 -0
- data/app/models/redirect.rb +14 -0
- data/app/models/refund.rb +14 -0
- data/app/models/remote_address.rb +22 -0
- data/app/models/remote_line_item.rb +11 -0
- data/app/models/remote_order.rb +15 -0
- data/app/models/remote_shipping_method.rb +12 -0
- data/app/models/report.rb +18 -0
- data/app/models/report_invocation.rb +18 -0
- data/app/models/retail_store.rb +18 -0
- data/app/models/role.rb +6 -0
- data/app/models/search_suggestion.rb +17 -0
- data/app/models/section.rb +9 -0
- data/app/models/session.rb +8 -0
- data/app/models/shipping_method.rb +26 -0
- data/app/models/slug.rb +19 -0
- data/app/models/static_page.rb +60 -0
- data/app/models/static_page_folder.rb +8 -0
- data/app/models/stock_level.rb +21 -0
- data/app/models/tax_code.rb +6 -0
- data/app/models/taxonomy.rb +5 -0
- data/app/models/template.rb +9 -0
- data/app/models/template_component.rb +11 -0
- data/app/models/template_definition.rb +12 -0
- data/app/models/template_section.rb +12 -0
- data/app/models/user.rb +8 -0
- data/app/models/user_profile.rb +6 -0
- data/app/models/v2/create_order.rb +10 -0
- data/app/models/v2/deallocate_order.rb +10 -0
- data/app/models/v2/line_item.rb +9 -0
- data/app/models/v2/order.rb +9 -0
- data/app/models/v2/unallocate_order.rb +10 -0
- data/app/models/variant.rb +18 -0
- data/app/models/webhook.rb +17 -0
- data/app/services/param_to_shql.rb +72 -0
- data/app/services/surrogate_keys.rb +44 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/config/locales/payments.en.yml +3 -0
- data/flex-commerce-api.gemspec +41 -0
- data/lib/flex_commerce.rb +95 -0
- data/lib/flex_commerce_api.rb +21 -0
- data/lib/flex_commerce_api/api_base.rb +11 -0
- data/lib/flex_commerce_api/base_resource.rb +250 -0
- data/lib/flex_commerce_api/config.rb +55 -0
- data/lib/flex_commerce_api/error/access_denied.rb +6 -0
- data/lib/flex_commerce_api/error/bad_request.rb +10 -0
- data/lib/flex_commerce_api/error/base.rb +12 -0
- data/lib/flex_commerce_api/error/client_error.rb +7 -0
- data/lib/flex_commerce_api/error/connection_error.rb +6 -0
- data/lib/flex_commerce_api/error/internal_server.rb +37 -0
- data/lib/flex_commerce_api/error/not_found.rb +13 -0
- data/lib/flex_commerce_api/error/record_invalid.rb +16 -0
- data/lib/flex_commerce_api/error/unexpected_status.rb +7 -0
- data/lib/flex_commerce_api/errors.rb +13 -0
- data/lib/flex_commerce_api/json_api_client_extension/builder.rb +28 -0
- data/lib/flex_commerce_api/json_api_client_extension/capture_surrogate_keys_middleware.rb +16 -0
- data/lib/flex_commerce_api/json_api_client_extension/flexible_connection.rb +59 -0
- data/lib/flex_commerce_api/json_api_client_extension/has_many_association_proxy.rb +60 -0
- data/lib/flex_commerce_api/json_api_client_extension/included_data.rb +27 -0
- data/lib/flex_commerce_api/json_api_client_extension/json_format_middleware.rb +20 -0
- data/lib/flex_commerce_api/json_api_client_extension/logging_middleware.rb +24 -0
- data/lib/flex_commerce_api/json_api_client_extension/paginator.rb +26 -0
- data/lib/flex_commerce_api/json_api_client_extension/parse_json.rb +23 -0
- data/lib/flex_commerce_api/json_api_client_extension/parsers/parser.rb +16 -0
- data/lib/flex_commerce_api/json_api_client_extension/previewed_request_middleware.rb +17 -0
- data/lib/flex_commerce_api/json_api_client_extension/remote_builder.rb +29 -0
- data/lib/flex_commerce_api/json_api_client_extension/requestor.rb +42 -0
- data/lib/flex_commerce_api/json_api_client_extension/save_request_body_middleware.rb +20 -0
- data/lib/flex_commerce_api/json_api_client_extension/status_middleware.rb +40 -0
- data/lib/flex_commerce_api/v2/api_base.rb +13 -0
- data/lib/flex_commerce_api/version.rb +3 -0
- data/lib/json_erb.rb +9 -0
- data/lib/json_struct.rb +73 -0
- data/lib/patches.rb +4 -0
- data/lib/patches/json_api_client/resource.rb +50 -0
- data/lib/paypal_express.rb +3 -0
- data/lib/paypal_express/additional_info.rb +45 -0
- data/lib/paypal_express/api.rb +86 -0
- data/lib/paypal_express/auth.rb +83 -0
- data/lib/paypal_express/cart_shipping_method.rb +38 -0
- data/lib/paypal_express/exception/access_denied.rb +10 -0
- data/lib/paypal_express/exception/connection_error.rb +10 -0
- data/lib/paypal_express/exception/not_authorized.rb +10 -0
- data/lib/paypal_express/exception/transaction.rb +15 -0
- data/lib/paypal_express/generate_summary.rb +118 -0
- data/lib/paypal_express/process/paypal_params.rb +123 -0
- data/lib/paypal_express/process/response_parser.rb +146 -0
- data/lib/paypal_express/setup.rb +94 -0
- data/lib/paypal_express/shipping_methods_for_cart.rb +46 -0
- data/lib/retry.rb +20 -0
- data/schemas/jsonapi/schema.json +370 -0
- data/schemas/shift/v1/documents/collection/address.json +45 -0
- data/schemas/shift/v1/documents/collection/asset_file.json +43 -0
- data/schemas/shift/v1/documents/collection/asset_folder.json +43 -0
- data/schemas/shift/v1/documents/collection/customer_account.json +50 -0
- data/schemas/shift/v1/documents/collection/markdown_price.json +43 -0
- data/schemas/shift/v1/documents/collection/product.json +43 -0
- data/schemas/shift/v1/documents/collection/variant.json +43 -0
- data/schemas/shift/v1/documents/member/address.json +39 -0
- data/schemas/shift/v1/documents/member/asset_file.json +37 -0
- data/schemas/shift/v1/documents/member/asset_folder.json +39 -0
- data/schemas/shift/v1/documents/member/customer_account.json +44 -0
- data/schemas/shift/v1/documents/member/markdown_price.json +37 -0
- data/schemas/shift/v1/documents/member/product.json +39 -0
- data/schemas/shift/v1/documents/member/variant.json +46 -0
- data/schemas/shift/v1/resources/address.json +130 -0
- data/schemas/shift/v1/resources/asset_file.json +146 -0
- data/schemas/shift/v1/resources/asset_folder.json +188 -0
- data/schemas/shift/v1/resources/customer_account.json +339 -0
- data/schemas/shift/v1/resources/markdown_price.json +52 -0
- data/schemas/shift/v1/resources/product.json +230 -0
- data/schemas/shift/v1/resources/variant.json +298 -0
- data/tasks/json_schema.thor +275 -0
- data/todo.md +8 -0
- 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
|
data/lib/json_erb.rb
ADDED
data/lib/json_struct.rb
ADDED
@@ -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
|
data/lib/patches.rb
ADDED
@@ -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,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
|