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