workarea-sezzle 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  3. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.gitignore +23 -0
  6. data/.rubocop.yml +405 -0
  7. data/.scss-lint.yml +204 -0
  8. data/Gemfile +17 -0
  9. data/LICENSE +7 -0
  10. data/README.md +95 -0
  11. data/Rakefile +56 -0
  12. data/Workarea +0 -0
  13. data/app/assets/images/workarea/admin/sezzle/.keep +0 -0
  14. data/app/assets/images/workarea/sezzle_logo_white.svg +1 -0
  15. data/app/assets/images/workarea/storefront/sezzle/.keep +0 -0
  16. data/app/assets/javascripts/workarea/admin/sezzle/.keep +0 -0
  17. data/app/assets/javascripts/workarea/storefront/sezzle/.keep +0 -0
  18. data/app/assets/stylesheets/workarea/admin/sezzle/.keep +0 -0
  19. data/app/assets/stylesheets/workarea/admin/sezzle/components/_payment_icon.scss +13 -0
  20. data/app/assets/stylesheets/workarea/storefront/sezzle/.keep +0 -0
  21. data/app/assets/stylesheets/workarea/storefront/sezzle/components/_payment_icon.scss +13 -0
  22. data/app/assets/stylesheets/workarea/storefront/sezzle/components/_sezzle.scss +13 -0
  23. data/app/controllers/.keep +0 -0
  24. data/app/controllers/workarea/storefront/checkout/place_order_controller.decorator +11 -0
  25. data/app/controllers/workarea/storefront/sezzle_controller.rb +109 -0
  26. data/app/helpers/.keep +0 -0
  27. data/app/mailers/.keep +0 -0
  28. data/app/models/.keep +0 -0
  29. data/app/models/workarea/payment/authorize/sezzle.rb +32 -0
  30. data/app/models/workarea/payment/capture/sezzle.rb +85 -0
  31. data/app/models/workarea/payment/purchase/sezzle.rb +30 -0
  32. data/app/models/workarea/payment/refund/sezzle.rb +47 -0
  33. data/app/models/workarea/payment/tender/sezzle.rb +15 -0
  34. data/app/models/workarea/paymentl.decorator +32 -0
  35. data/app/services/workarea/sezzle/order_builder.rb +135 -0
  36. data/app/view_models/workarea/storefront/checkout/payment_view_model.decorator +11 -0
  37. data/app/views/.keep +0 -0
  38. data/app/views/workarea/admin/orders/tenders/_sezzle.html.haml +3 -0
  39. data/app/views/workarea/storefront/carts/_sezzle_widget.html.haml +5 -0
  40. data/app/views/workarea/storefront/checkouts/_sezzle_payment.html.haml +8 -0
  41. data/app/views/workarea/storefront/order_mailer/tenders/_sezzle.html.haml +1 -0
  42. data/app/views/workarea/storefront/order_mailer/tenders/_sezzle.text.erb +1 -0
  43. data/app/views/workarea/storefront/orders/tenders/_sezzle.html.haml +4 -0
  44. data/app/views/workarea/storefront/products/_sezzle_widget.html.haml +10 -0
  45. data/app/views/workarea/storefront/sezzle/_script.html.haml +22 -0
  46. data/bin/rails +25 -0
  47. data/config/initializers/appends.rb +30 -0
  48. data/config/initializers/configurations.rb +41 -0
  49. data/config/initializers/workarea.rb +21 -0
  50. data/config/locales/en.yml +43 -0
  51. data/config/routes.rb +5 -0
  52. data/lib/workarea/sezzle.rb +53 -0
  53. data/lib/workarea/sezzle/authentication.rb +49 -0
  54. data/lib/workarea/sezzle/bogus_gateway.rb +128 -0
  55. data/lib/workarea/sezzle/engine.rb +10 -0
  56. data/lib/workarea/sezzle/gateway.rb +90 -0
  57. data/lib/workarea/sezzle/response.rb +27 -0
  58. data/lib/workarea/sezzle/version.rb +5 -0
  59. data/module +0 -0
  60. data/test/dummy/.ruby-version +1 -0
  61. data/test/dummy/Rakefile +6 -0
  62. data/test/dummy/app/assets/config/manifest.js +2 -0
  63. data/test/dummy/app/assets/images/.keep +0 -0
  64. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  65. data/test/dummy/app/controllers/application_controller.rb +2 -0
  66. data/test/dummy/app/controllers/concerns/.keep +0 -0
  67. data/test/dummy/app/helpers/application_helper.rb +2 -0
  68. data/test/dummy/app/javascript/packs/application.js +14 -0
  69. data/test/dummy/app/jobs/application_job.rb +7 -0
  70. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  71. data/test/dummy/app/models/concerns/.keep +0 -0
  72. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  73. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  74. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  75. data/test/dummy/bin/rails +4 -0
  76. data/test/dummy/bin/rake +4 -0
  77. data/test/dummy/bin/setup +25 -0
  78. data/test/dummy/config.ru +5 -0
  79. data/test/dummy/config/application.rb +34 -0
  80. data/test/dummy/config/boot.rb +5 -0
  81. data/test/dummy/config/environment.rb +5 -0
  82. data/test/dummy/config/environments/development.rb +53 -0
  83. data/test/dummy/config/environments/production.rb +101 -0
  84. data/test/dummy/config/environments/test.rb +47 -0
  85. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  86. data/test/dummy/config/initializers/assets.rb +12 -0
  87. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  88. data/test/dummy/config/initializers/content_security_policy.rb +28 -0
  89. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  90. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  91. data/test/dummy/config/initializers/inflections.rb +16 -0
  92. data/test/dummy/config/initializers/mime_types.rb +4 -0
  93. data/test/dummy/config/initializers/workarea.rb +5 -0
  94. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  95. data/test/dummy/config/locales/en.yml +33 -0
  96. data/test/dummy/config/puma.rb +38 -0
  97. data/test/dummy/config/routes.rb +5 -0
  98. data/test/dummy/config/spring.rb +6 -0
  99. data/test/dummy/db/seeds.rb +2 -0
  100. data/test/dummy/lib/assets/.keep +0 -0
  101. data/test/dummy/log/.keep +0 -0
  102. data/test/integration/workarea/storefront/sezzle_integration_test.rb +205 -0
  103. data/test/models/workarea/payment/sezzle_payment_integration_test.rb +82 -0
  104. data/test/services/workarea/sezzle/order_builder_test.rb +116 -0
  105. data/test/teaspoon_env.rb +6 -0
  106. data/test/test_helper.rb +10 -0
  107. data/workarea-sezzle.gemspec +20 -0
  108. metadata +163 -0
@@ -0,0 +1,11 @@
1
+ module Workarea
2
+ decorate Storefront::Checkout::PaymentViewModel, with: :sezzle do
3
+ decorated do
4
+ delegate :sezzle?, to: :payment
5
+ end
6
+
7
+ def using_new_card?
8
+ super && !sezzle?
9
+ end
10
+ end
11
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ %li
2
+ = inline_svg('workarea/sezzle_logo_white.svg', class: 'payment-icon payment-icon--sezzle')
3
+ = number_to_currency tender.amount
@@ -0,0 +1,5 @@
1
+ - if Workarea.config.sezzle_merchant_id.present?
2
+ #sezzle.sezzle.sezzle--cart
3
+ .sezzle__prices
4
+ %span.sezzle__price
5
+ = number_to_currency(cart.total_price)
@@ -0,0 +1,8 @@
1
+ .checkout-payment__primary-method{ class: ('checkout-payment__primary-method--selected' if @step.sezzle?) }
2
+ .button-property
3
+ = hidden_field_tag 'from_checkout', 'from_checkout', id: nil
4
+ .value= radio_button_tag 'payment', 'sezzle', step.sezzle?
5
+ = label_tag 'payment[sezzle]', nil, class: 'button-property__name' do
6
+ %span.button-property__text= inline_svg('workarea/sezzle_logo_white.svg', class: 'payment-icon payment-icon--sezzle')
7
+ %p.checkout-payment__primary-method-description
8
+ %span= t('workarea.storefront.sezzle.on_continue')
@@ -0,0 +1 @@
1
+ %p= t('workarea.storefront.sezzle.paid_with_sezzle')
@@ -0,0 +1 @@
1
+ <%= t('workarea.sezzle.tender_description') %>
@@ -0,0 +1,4 @@
1
+ .data-card
2
+ .data-card__cell
3
+ %p.data-card__line.data-card__credit-card= inline_svg('workarea/sezzle_logo_white.svg', class: 'payment-icon payment-icon--sezzle')
4
+
@@ -0,0 +1,10 @@
1
+ - if Workarea.config.sezzle_merchant_id.present?
2
+ #sezzle.sezzle.sezzle--product
3
+ .sezzle__prices
4
+ - if product.show_sell_range?
5
+ %span.sezzle__price= number_to_currency(product.sell_min_price)
6
+
7
+ %span.sezzle__price= number_to_currency(product.sell_max_price)
8
+ - else
9
+ %span.sezzle__price
10
+ = number_to_currency(product.sell_min_price)
@@ -0,0 +1,22 @@
1
+ - if Workarea.config.sezzle_merchant_id.present?
2
+ :javascript
3
+ document.sezzleConfig = {
4
+ configGroups: [
5
+ {
6
+ targetXPath: "#{Workarea.config.sezzle.product_config[:target_xpath]}",
7
+ renderToPath: "#{Workarea.config.sezzle.product_config[:render_to_path]}",
8
+ urlMatch: "#{Workarea.config.sezzle.product_config[:url_match]}",
9
+ splitPriceElementsOn: "#{Workarea.config.sezzle.product_config[:split_price_elements_on]}"
10
+ },
11
+ {
12
+ targetXPath: "#{Workarea.config.sezzle.cart_config[:target_xpath]}",
13
+ renderToPath: "#{Workarea.config.sezzle.cart_config[:render_to_path]}",
14
+ urlMatch: "#{Workarea.config.sezzle.cart_config[:url_match]}",
15
+ altVersionTemplate: {
16
+ 'en': '4 interest-free payments of %%price%% with %%logo%% %%info%%',
17
+ 'fr': '4 paiements de %%price%% sans intérêts avec %%logo%% %%info%%'
18
+ }
19
+ }
20
+ ]
21
+ }
22
+ %script{ src: "https://widget.sezzle.com/v1/javascript/price-widget?uuid=#{Workarea.config.sezzle_merchant_id}" }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/workarea/sezzle/engine', __dir__)
7
+ APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
12
+
13
+ require 'rails'
14
+ # Pick the frameworks you want:
15
+ require 'active_model/railtie'
16
+ require 'active_job/railtie'
17
+ # require "active_record/railtie"
18
+ # require "active_storage/engine"
19
+ require 'action_controller/railtie'
20
+ require 'action_mailer/railtie'
21
+ require 'action_view/railtie'
22
+ # require "action_cable/engine"
23
+ require 'sprockets/railtie'
24
+ require 'rails/test_unit/railtie'
25
+ require 'rails/engine/commands'
@@ -0,0 +1,30 @@
1
+ Workarea::Plugin.append_stylesheets(
2
+ 'admin.components',
3
+ 'workarea/storefront/sezzle/components/payment_icon'
4
+ )
5
+
6
+ Workarea::Plugin.append_stylesheets(
7
+ 'storefront.components',
8
+ 'workarea/storefront/sezzle/components/sezzle',
9
+ 'workarea/storefront/sezzle/components/payment_icon'
10
+ )
11
+
12
+ Workarea::Plugin.append_partials(
13
+ 'storefront.payment_method',
14
+ 'workarea/storefront/checkouts/sezzle_payment'
15
+ )
16
+
17
+ Workarea::Plugin.append_partials(
18
+ 'storefront.javascript',
19
+ 'workarea/storefront/sezzle/script'
20
+ )
21
+
22
+ Workarea::Plugin.append_partials(
23
+ 'storefront.product_pricing_details',
24
+ 'workarea/storefront/products/sezzle_widget'
25
+ )
26
+
27
+ Workarea::Plugin.append_partials(
28
+ 'storefront.cart_additional_information',
29
+ 'workarea/storefront/carts/sezzle_widget'
30
+ )
@@ -0,0 +1,41 @@
1
+ if Workarea::Configuration.respond_to?(:define_fields)
2
+ Workarea::Configuration.define_fields do
3
+ fieldset 'Sezzle', namespaced: true do
4
+ field 'Public Key',
5
+ type: :string,
6
+ description: 'Public Key from merchant dashboard.',
7
+ allow_blank: false,
8
+ encrypted: false
9
+
10
+ field 'Private Key',
11
+ type: :string,
12
+ description: 'Private Key from merchant dashboard.',
13
+ allow_blank: false,
14
+ encrypted: true
15
+
16
+ field 'Merchant ID',
17
+ type: :string,
18
+ default: 'TODO',
19
+ description: 'Merchant ID from merchant dashboard.',
20
+ allow_blank: false,
21
+ encrypted: false
22
+
23
+ field 'Payment Action',
24
+ type: :string,
25
+ description: 'Determines if the order payment is authorized or immediately catpured.',
26
+ allow_blank: false,
27
+ encrypted: false,
28
+ default: 'AUTH'
29
+
30
+ field 'Test Mode',
31
+ type: :boolean,
32
+ description: 'Uses the sandbox API endpoints if set to true. Contact your Sezzle representative for sandbox credentials.',
33
+ default: !Rails.env.production?
34
+ end
35
+ end
36
+ else
37
+
38
+ Workarea.config.sezzle_test_mode = !Rails.env.production?
39
+ Workarea.config.sezzle_payment_action = 'AUTH'
40
+ Workarea.config.sezzle_merchant_id = nil # Host apps are responsible for adding this configuration
41
+ end
@@ -0,0 +1,21 @@
1
+ Workarea.configure do |config|
2
+
3
+ config.tender_types.append(:sezzle)
4
+
5
+ config.sezzle = ActiveSupport::Configurable::Configuration.new
6
+ config.sezzle.api_timeout = 10
7
+ config.sezzle.open_timeout = 10
8
+
9
+ config.sezzle.product_config = {
10
+ target_xpath: '#sezzle/.sezzle__prices',
11
+ render_to_path: './',
12
+ url_match: 'products',
13
+ split_price_elements_on: '–'
14
+ }
15
+
16
+ config.sezzle.cart_config = {
17
+ target_xpath: '#sezzle/.sezzle__prices',
18
+ render_to_path: './',
19
+ url_match: 'cart'
20
+ }
21
+ end
@@ -0,0 +1,43 @@
1
+ ---
2
+ en:
3
+ workarea:
4
+ admin:
5
+ orders:
6
+ tenders:
7
+ sezzle:
8
+ title: Sezzle
9
+ storefront:
10
+ orders:
11
+ tenders:
12
+ sezzle: Sezzle
13
+ carts:
14
+ sezzle: "Pay %{installment_count} interest free payments of %{installment_price} with"
15
+ checkouts:
16
+ sezzle: Pay With Sezzle
17
+ products:
18
+ sezzle: "Or %{installment_count} payments of %{installment_price} with"
19
+ sezzle:
20
+ address_error: We can not ship to this address. Please enter a new address.
21
+ cancel_message: Your Sezzle order has been cancelled. Please select another payment method.
22
+ check_out: Check Out with sezzle
23
+ checkout_submit_text: Continue to Sezzle
24
+ on_continue: When you continue, you will be taken to the Sezzle website to complete your order.
25
+ paid_with_sezzle: Paid with Sezzle
26
+ payment_error: There was a problem processing your Sezzle payment, you have not been charged. Please try again or select
27
+ another payment method.
28
+ sezzle: Sezzle
29
+ learn_more: Learn More
30
+ sezzle:
31
+ authorize: "%{amount} has been captured"
32
+ pre_authorized_message: "Authorization happens in the Sezzle checkout process. No API response to record. %{amount} was authorized by Sezzle."
33
+ pre_captured_message: "Capture occurred in the Sezzle checkout process. No API response to record. %{amount} was captured by Sezzle."
34
+ pre_purchased_message: "Authorize and Capture occurred in the Sezzle checkout process. No API response to record. %{amount} was captured by Sezzle."
35
+ capture: "%{amount} has been captured"
36
+ purchase: "%{amount} has been purchased"
37
+ refund: "%{amount} has been refunded"
38
+ void: Sezzle Transaction has been voided
39
+ authorize_failure: Sezzle authorize has failed
40
+ capture_failure: Sezzle capture has failed
41
+ purchase_failure: Sezzle purchase has failed
42
+ refund_failure: Sezzle refund has failed
43
+
@@ -0,0 +1,5 @@
1
+ Workarea::Storefront::Engine.routes.draw do
2
+ get 'sezzle/start' => 'sezzle#start', as: :start_sezzle
3
+ get 'sezzle/complete' => 'sezzle#complete', as: :complete_sezzle
4
+ get 'sezzle/cancel' => 'sezzle#cancel', as: :cancel_sezzle
5
+ end
@@ -0,0 +1,53 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ require 'workarea/sezzle/engine'
6
+ require 'workarea/sezzle/version'
7
+
8
+ require 'workarea/sezzle/bogus_gateway'
9
+ require 'workarea/sezzle/authentication'
10
+ require 'workarea/sezzle/gateway'
11
+ require 'workarea/sezzle/response'
12
+
13
+ module Workarea
14
+ module Sezzle
15
+ def self.config
16
+ Workarea.config.sezzle
17
+ end
18
+
19
+ # used for <= 3.5, these creds come from secrets isntead of encrypted in the
20
+ # 3.5 configurations system.
21
+ def self.credentials
22
+ (Rails.application.secrets.sezzle || {}).deep_symbolize_keys
23
+ end
24
+
25
+ def self.api_public_key
26
+ Workarea.config.sezzle_public_key || credentials[:api_public_key]
27
+ end
28
+
29
+ def self.api_private_key
30
+ Workarea.config.sezzle_private_key || credentials[:api_private_key]
31
+ end
32
+
33
+ def self.api_configured?
34
+ api_public_key.present? && api_private_key.present?
35
+ end
36
+
37
+ def self.test?
38
+ Workarea.config.sezzle_test_mode || credentials[:test]
39
+ end
40
+
41
+ def self.gateway
42
+ if Rails.env.test?
43
+ Sezzle::BogusGateway.new
44
+ else
45
+ Sezzle::Gateway.new(
46
+ api_public_key: api_public_key,
47
+ api_private_key: api_private_key,
48
+ test: test?
49
+ )
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+ module Workarea
2
+ module Sezzle
3
+ module Authentication
4
+ class AuthenticationError < StandardError; end
5
+ def token
6
+ response = get_token
7
+ body = JSON.parse(response.body)
8
+
9
+ raise AuthenticationError, response.body.to_s unless response.success?
10
+ body['token']
11
+ end
12
+
13
+ private
14
+
15
+ def get_token
16
+ Rails.cache.fetch(token_cache_key, expires_in: 5.minutes) do
17
+
18
+ body = {
19
+ public_key: api_public_key,
20
+ private_key: api_private_key
21
+ }
22
+
23
+ conn = Faraday.new(url: rest_endpoint)
24
+ conn.post do |req|
25
+ req.url 'v2/authentication'
26
+ req.headers['Content-Type'] = 'application/json'
27
+ req.body = body.to_json
28
+ end
29
+ end
30
+ end
31
+
32
+ def api_public_key
33
+ options[:api_public_key]
34
+ end
35
+
36
+ def api_private_key
37
+ options[:api_private_key]
38
+ end
39
+
40
+ def test
41
+ options[:test]
42
+ end
43
+
44
+ def token_cache_key
45
+ Digest::MD5.hexdigest "#{api_public_key}#{api_private_key}#{test}"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,128 @@
1
+ module Workarea
2
+ module Sezzle
3
+ class BogusGateway
4
+ def initialize(*)
5
+ end
6
+
7
+ def create_session(order)
8
+ b = {
9
+ "uuid": 'fadbc642-05a4-4e38-9e74-80e325623af9',
10
+ "links": [
11
+ {
12
+ "href": 'https://gateway.sezzle.com/v2/session/fadbc642-05a4-4e38-9e74-80e325623af9',
13
+ "method": 'GET',
14
+ "rel": 'self'
15
+ }
16
+ ],
17
+ "order": {
18
+ "uuid": '12a34bc5-6de7-890f-g123-4hi1238jk902',
19
+ "intent": 'CAPTURE',
20
+ "checkout_url": 'https://checkout.sezzle.com/?id=12a34bc5-6de7-890f-g123-4hi1238jk902',
21
+ "links": [
22
+ {
23
+ "href": 'https://gateway.sezzle.com/v2/order/12a34bc5-6de7-890f-g123-4hi1238jk902',
24
+ "method": 'GET',
25
+ "rel": 'self'
26
+ },
27
+ {
28
+ "href": 'https://gateway.sezzle.com/v2/order/12a34bc5-6de7-890f-g123-4hi1238jk902',
29
+ "method": 'PATCH',
30
+ "rel": 'self'
31
+ },
32
+ {
33
+ "href": 'https://gateway.sezzle.com/v2/order/12a34bc5-6de7-890f-g123-4hi1238jk902/release',
34
+ "method": 'POST',
35
+ "rel": 'release'
36
+ },
37
+ {
38
+ "href": 'https://gateway.sezzle.com/v2/order/12a34bc5-6de7-890f-g123-4hi1238jk902/capture',
39
+ "method": 'POST',
40
+ "rel": 'capture'
41
+ },
42
+ {
43
+ "href": 'https://gateway.sezzle.com/v2/order/12a34bc5-6de7-890f-g123-4hi1238jk902/refund',
44
+ "method": 'POST',
45
+ "rel": 'refund'
46
+ }
47
+ ]
48
+ },
49
+ "tokenize": {
50
+ "token": '7ec98824-67cc-469c-86ab-f9e047f9cf1a',
51
+ "expiration": '2020-04-27T14:46:59Z',
52
+ "approval_url": 'https://dashboard.sezzle.com/customer/checkout-approval?merchant-request-id=3f3244fd-78ce-4994-af0c-b8c760d47794',
53
+ "links": [
54
+ {
55
+ "href": 'https://gateway.sezzle.com/v2/token/7ec98824-67cc-469c-86ab-f9e047f9cf1a/session ',
56
+ "method": 'GET',
57
+ "rel": 'token'
58
+ }
59
+ ]
60
+ }
61
+ }
62
+
63
+ Response.new(response(b))
64
+ end
65
+
66
+ def capture(id, attrs)
67
+ if id == 'error'
68
+ Response.new(response(error_response_body, 400))
69
+ else
70
+ Response.new(response(payment_response_body, 200))
71
+ end
72
+ end
73
+
74
+ def authorize(id, attrs)
75
+ if id == 'error'
76
+ Response.new(response(error_response_body, 400))
77
+ elsif id == 'timeout_token'
78
+ Response.new(response(nil, 502))
79
+ else
80
+ Response.new(response(payment_response_body, 200))
81
+ end
82
+ end
83
+
84
+ def purchase(id, attrs)
85
+ if id == 'error'
86
+ Response.new(response(error_response_body, 402))
87
+ else
88
+ Response.new(response(payment_response_body, 200))
89
+ end
90
+ end
91
+
92
+ def refund(id, attrs)
93
+ Response.new(response(payment_response_body, 200))
94
+ end
95
+
96
+ def void(payment_id)
97
+ Response.new(response(payment_response_body))
98
+ end
99
+
100
+ private
101
+
102
+ def response(body, status = 200)
103
+ response = Faraday.new do |builder|
104
+ builder.adapter :test do |stub|
105
+ stub.get('/v1/bogus') { |env| [ status, {}, body.to_json ] }
106
+ end
107
+ end
108
+ response.get('/v1/bogus')
109
+ end
110
+
111
+ def error_response_body
112
+ [
113
+ {
114
+ "code": 'invalid',
115
+ "message": 'Order must have a valid intent',
116
+ "location": 'order.intent'
117
+ }
118
+ ]
119
+ end
120
+
121
+ def payment_response_body(status: 'APPROVED')
122
+ {
123
+ "uuid": '6c9db5d4-d09a-4224-860a-b5438ac32ca8'
124
+ }
125
+ end
126
+ end
127
+ end
128
+ end