workarea-cyber_source 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  4. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.gitignore +14 -0
  7. data/.rails-rubocop.yml +140 -0
  8. data/.rubocop.yml +8 -0
  9. data/CHANGELOG.md +39 -0
  10. data/CODE_OF_CONDUCT.md +3 -0
  11. data/CONTRIBUTING.md +3 -0
  12. data/Gemfile +11 -0
  13. data/LICENSE +52 -0
  14. data/README.md +27 -0
  15. data/Rakefile +59 -0
  16. data/app/models/workarea/payment/authorize/credit_card.decorator +45 -0
  17. data/app/models/workarea/payment/capture/credit_card.decorator +14 -0
  18. data/app/models/workarea/payment/purchase/credit_card.decorator +48 -0
  19. data/app/models/workarea/payment/refund/credit_card.decorator +14 -0
  20. data/app/models/workarea/payment/store_credit_card.decorator +13 -0
  21. data/bin/rails +20 -0
  22. data/config/initializers/gateway.rb +1 -0
  23. data/lib/active_merchant/billing/bogus_cyber_source_gateway.rb +97 -0
  24. data/lib/active_merchant/billing/cyber_source_fix.rb +34 -0
  25. data/lib/workarea/cyber_source.rb +36 -0
  26. data/lib/workarea/cyber_source/engine.rb +8 -0
  27. data/lib/workarea/cyber_source/version.rb +5 -0
  28. data/test/dummy/Rakefile +6 -0
  29. data/test/dummy/bin/bundle +3 -0
  30. data/test/dummy/bin/rails +4 -0
  31. data/test/dummy/bin/rake +4 -0
  32. data/test/dummy/bin/setup +38 -0
  33. data/test/dummy/bin/update +29 -0
  34. data/test/dummy/bin/yarn +11 -0
  35. data/test/dummy/config.ru +5 -0
  36. data/test/dummy/config/application.rb +28 -0
  37. data/test/dummy/config/boot.rb +5 -0
  38. data/test/dummy/config/cable.yml +10 -0
  39. data/test/dummy/config/environment.rb +5 -0
  40. data/test/dummy/config/environments/development.rb +56 -0
  41. data/test/dummy/config/environments/production.rb +91 -0
  42. data/test/dummy/config/environments/test.rb +44 -0
  43. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  44. data/test/dummy/config/initializers/assets.rb +14 -0
  45. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  47. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/test/dummy/config/initializers/inflections.rb +16 -0
  49. data/test/dummy/config/initializers/mime_types.rb +4 -0
  50. data/test/dummy/config/initializers/workarea.rb +5 -0
  51. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  52. data/test/dummy/config/locales/en.yml +33 -0
  53. data/test/dummy/config/puma.rb +56 -0
  54. data/test/dummy/config/routes.rb +5 -0
  55. data/test/dummy/config/secrets.yml +32 -0
  56. data/test/dummy/config/spring.rb +6 -0
  57. data/test/dummy/db/seeds.rb +2 -0
  58. data/test/dummy/log/.keep +0 -0
  59. data/test/dummy/package.json +5 -0
  60. data/test/integration/workarea/cyber_source_integration_test.rb +191 -0
  61. data/test/models/workarea/payment/authorize/credit_card_test.decorator +68 -0
  62. data/test/models/workarea/payment/capture/credit_card_test.decorator +17 -0
  63. data/test/models/workarea/payment/purchase/credit_card_test.decorator +60 -0
  64. data/test/models/workarea/payment/refund/credit_card_test.decorator +17 -0
  65. data/test/models/workarea/payment/store_credit_card_test.decorator +15 -0
  66. data/test/support/workarea/cyber_source_support_vcr_config.rb +23 -0
  67. data/test/support/workarea/workarea_3_2_backports.rb +57 -0
  68. data/test/teaspoon_env.rb +6 -0
  69. data/test/test_helper.rb +11 -0
  70. data/test/vcr_cassettes/cyber_source/auth_capture.yml +254 -0
  71. data/test/vcr_cassettes/cyber_source/auth_capture_refund.yml +323 -0
  72. data/test/vcr_cassettes/cyber_source/auth_void.yml +250 -0
  73. data/test/vcr_cassettes/cyber_source/purchase_refund.yml +251 -0
  74. data/test/vcr_cassettes/cyber_source/purchase_void.yml +247 -0
  75. data/test/vcr_cassettes/cyber_source/store_auth.yml +179 -0
  76. data/test/vcr_cassettes/cyber_source/store_purchase.yml +180 -0
  77. data/workarea-cyber_source.gemspec +19 -0
  78. metadata +133 -0
@@ -0,0 +1,45 @@
1
+ module Workarea
2
+ decorate Payment::Authorize::CreditCard, with: :cyber_source do
3
+ decorated { delegate :address, to: :tender }
4
+
5
+ def initialize(tender, transaction, options = {})
6
+ super
7
+ @options = @options.merge(
8
+ email: email,
9
+ billing_address: billing_address,
10
+ order_id: order_id
11
+ )
12
+ end
13
+
14
+ def complete!
15
+ return unless Payment::StoreCreditCard.new(tender, options).save!
16
+
17
+ transaction.response = handle_active_merchant_errors do
18
+ gateway.authorize(transaction.amount.cents, tender.token, options)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def order_id
25
+ tender.payment.id
26
+ end
27
+
28
+ def email
29
+ return unless tender.profile.present?
30
+
31
+ tender.profile.email
32
+ end
33
+
34
+ def billing_address
35
+ {
36
+ address1: address.street,
37
+ address2: address.street_2,
38
+ city: address.city,
39
+ state: address.region,
40
+ country: address.country.try(:alpha2),
41
+ zip: address.postal_code
42
+ }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ module Workarea
2
+ decorate Payment::Capture::CreditCard, with: :cyber_source do
3
+ def complete!
4
+ validate_reference!
5
+
6
+ transaction.response = handle_active_merchant_errors do
7
+ gateway.capture(
8
+ transaction.amount.cents,
9
+ transaction.reference.response.authorization
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,48 @@
1
+ module Workarea
2
+ decorate Payment::Purchase::CreditCard, with: :cyber_source do
3
+ decorated { delegate :address, to: :tender }
4
+
5
+ def initialize(tender, transaction, options = {})
6
+ super
7
+ @options = @options.merge(
8
+ email: email,
9
+ billing_address: billing_address,
10
+ order_id: order_id
11
+ )
12
+ end
13
+
14
+ def complete!
15
+ return unless Payment::StoreCreditCard.new(tender, options).save!
16
+
17
+ transaction.response = handle_active_merchant_errors do
18
+ gateway.purchase(transaction.amount.cents, tender.token, options)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def order_id
25
+ tender.payment.id
26
+ end
27
+
28
+ def email
29
+ return unless tender.profile.present?
30
+
31
+ tender.profile.email
32
+ end
33
+
34
+ def billing_address
35
+ {
36
+ name: nil,
37
+ company: nil,
38
+ address1: address.street,
39
+ address2: address.street_2,
40
+ city: address.city,
41
+ state: address.region,
42
+ country: address.country.try(:alpha2),
43
+ zip: address.postal_code,
44
+ phone: nil
45
+ }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ module Workarea
2
+ decorate Payment::Refund::CreditCard, with: :cyber_source do
3
+ def complete!
4
+ validate_reference!
5
+
6
+ transaction.response = handle_active_merchant_errors do
7
+ gateway.refund(
8
+ transaction.amount.cents,
9
+ transaction.reference.response.authorization
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Workarea
2
+ decorate Payment::StoreCreditCard, with: :cyber_source do
3
+ def perform!
4
+ return true if @credit_card.token.present?
5
+
6
+ response = handle_active_merchant_errors do
7
+ gateway.store(@credit_card.to_active_merchant, @options)
8
+ end
9
+
10
+ @credit_card.token = response.authorization
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
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("../..", __FILE__)
6
+ ENGINE_PATH = File.expand_path("../../lib/workarea/cyber_source/engine", __FILE__)
7
+ APP_PATH = File.expand_path("../../test/dummy/config/application", __FILE__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
11
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
12
+
13
+ require "action_controller/railtie"
14
+ require "action_view/railtie"
15
+ require "action_mailer/railtie"
16
+ require "rails/test_unit/railtie"
17
+ require "sprockets/railtie"
18
+ require "teaspoon-mocha"
19
+
20
+ require "rails/engine/commands"
@@ -0,0 +1 @@
1
+ Workarea::CyberSource.auto_configure_gateway
@@ -0,0 +1,97 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ class BogusCyberSourceGateway < BogusGateway
4
+ def store(paysource, options = {})
5
+ authorization = ";5118839074516426404010;Ahj/7wSTFX4pmYA85bCqKhDdq4ZOWjNklxakXpGAKXFqRekZpABykhk0kyro9JiuKBOTFX4pmYA85bCqAAAA5wQ4;store;;;5118839074516426404010"
6
+ case normalize(paysource)
7
+ when /1$/
8
+ Response.new(true, SUCCESS_MESSAGE, { billingid: "1" }, { test: true, authorization: authorization })
9
+ when /2$/
10
+ Response.new(false, FAILURE_MESSAGE, { billingid: nil, error: FAILURE_MESSAGE }, { test: true, error_code: STANDARD_ERROR_CODE[:processing_error] })
11
+ else
12
+ raise Error, error_message(paysource)
13
+ end
14
+ end
15
+
16
+ def authorize(money, credit_card_or_subscription, options = {})
17
+ case normalize(credit_card_or_subscription)
18
+ when /1$/, "5118839074516426404010"
19
+ succuessful_auth_response
20
+ when /2$/
21
+ Response.new(false, FAILURE_MESSAGE, { authorized_amount: money, error: FAILURE_MESSAGE }, { test: true, error_code: STANDARD_ERROR_CODE[:processing_error] })
22
+ else
23
+ raise Error, error_message(credit_card_or_subscription)
24
+ end
25
+ end
26
+
27
+ def purchase(money, credit_card_or_subscription, options = {})
28
+ case normalize(credit_card_or_subscription)
29
+ when /1$/, "5118839074516426404010"
30
+ succuessful_purchase_response
31
+ when /2$/
32
+ Response.new(false, FAILURE_MESSAGE, { authorized_amount: money, error: FAILURE_MESSAGE }, { test: true, error_code: STANDARD_ERROR_CODE[:processing_error] })
33
+ else
34
+ raise Error, error_message(credit_card_or_subscription)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def normalize(paysource)
41
+ if paysource.respond_to?(:account_number) && (paysource.try(:number).blank? || paysource.number.blank?)
42
+ paysource.account_number
43
+ elsif paysource.respond_to?(:number)
44
+ paysource.number.split(";")[6] || paysource.number
45
+ else
46
+ paysource.to_s.split(";")[6] || paysource.to_s
47
+ end
48
+ end
49
+
50
+ def succuessful_auth_response
51
+ authorization = "5a216efd87c68b548a1856df;5118839077736067504008;Ahj/7wSTFX4pnG3edheIKhDdq4ZOWbVqlwethj1gKXB62GPXpAn6OUkMmkmVdHpMVxQJyYq/FM427zsLxAAA6xxJ;authorize;500;;"
52
+ params = {
53
+ "merchantReferenceCode" => "5a1d848387c68b268bf205f9",
54
+ "requestID" => "5118839077736067504008",
55
+ "decision" => "ACCEPT",
56
+ "reasonCode" => "100",
57
+ "message" => "Successful transaction",
58
+ "requestToken" => "Ahj/7wSTFX4pnG3edheIKhDdq4ZOWbVqlwethj1gKXB62GPXpAn6OUkMmkmVdHpMVxQJyYq/FM427zsLxAAA6xxJ",
59
+ "currency" => "USD",
60
+ "amount" => "5.00",
61
+ "authorizationCode" => "831000",
62
+ "avsCode" => "Y",
63
+ "avsCodeRaw" => "Y",
64
+ "authorizedDateTime" => "2017-11-28T15:45:07Z",
65
+ "processorResponse" => "000",
66
+ "paymentNetworkTransactionID" => "558196000003814",
67
+ "cardCategory" => "A",
68
+ "ownerMerchantID" => "weblinc"
69
+ }
70
+ Response.new(true, SUCCESS_MESSAGE, params, test: true, authorization: authorization)
71
+ end
72
+
73
+ def succuessful_purchase_response
74
+ authorization = "5a609ec187c68b520db767eb;5161970476206134104008;Ahj//wSTF9S7HtFYm4/IESDdm1ZtGbZvKhyKjhnKtJcl3RY7YClyXdFjt6QJ+j6jDJpJl6MVzT24YE5MX1Lse0Vibj8g0UyQ;purchase;500;;"
75
+ params = {
76
+ "merchantReferenceCode" => "5a5f54b787c68be6e7c6a553",
77
+ "requestID" => "5161970476206134104008",
78
+ "decision" => "ACCEPT",
79
+ "reasonCode" => "100",
80
+ "message" => "Successful transaction",
81
+ "requestToken" => "Ahj//wSTF9S7HtFYm4/IESDdm1ZtGbZvKhyKjhnKtJcl3RY7YClyXdFjt6QJ+j6jDJpJl6MVzT24YE5MX1Lse0Vibj8g0UyQ",
82
+ "currency" => "USD",
83
+ "amount" => "5.00",
84
+ "authorizationCode" => "888888",
85
+ "avsCode" => "X",
86
+ "avsCodeRaw" => "I1",
87
+ "authorizedDateTime" => "2018-01-17T13:50:47Z",
88
+ "processorResponse" => "100",
89
+ "reconciliationID" => "73534367JCHT83JZ",
90
+ "ownerMerchantID" => "a",
91
+ "requestDateTime" => "2018-01-17T13:50:47Z"
92
+ }
93
+ Response.new(true, SUCCESS_MESSAGE, params, test: true, authorization: authorization)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,34 @@
1
+ ActiveMerchant::Billing::CyberSourceGateway.class_eval do
2
+ def add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
3
+ if payment_method_or_reference.is_a?(String)
4
+ add_address(xml, nil, options[:billing_address], options)
5
+ add_purchase_data(xml, money, true, options)
6
+ add_subscription(xml, options, payment_method_or_reference)
7
+ elsif card_brand(payment_method_or_reference) == 'check'
8
+ add_address(xml, payment_method_or_reference, options[:billing_address], options)
9
+ add_purchase_data(xml, money, true, options)
10
+ add_check(xml, payment_method_or_reference)
11
+ else
12
+ add_address(xml, payment_method_or_reference, options[:billing_address], options)
13
+ add_address(xml, payment_method_or_reference, options[:shipping_address], options, true)
14
+ add_purchase_data(xml, money, true, options)
15
+ add_creditcard(xml, payment_method_or_reference)
16
+ end
17
+ end
18
+
19
+ def build_void_request(identification, options)
20
+ order_id, request_id, request_token, action, money, currency = identification.split(";")
21
+ options[:order_id] = order_id
22
+
23
+ xml = Builder::XmlMarkup.new :indent => 2
24
+ # normal active merchant only has if capture, but purchases should be the same as captures
25
+ # a pr was submited to active merchant, remove this if it ever gets mergex / fixed upstream
26
+ if action == "capture" || action == "purchase"
27
+ add_void_service(xml, request_id, request_token)
28
+ else
29
+ add_purchase_data(xml, money, true, options.merge(:currency => currency || default_currency))
30
+ add_auth_reversal_service(xml, request_id, request_token)
31
+ end
32
+ xml.target!
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ require "workarea"
2
+ require "workarea/storefront"
3
+ require "workarea/admin"
4
+ require "active_merchant/billing/bogus_cyber_source_gateway"
5
+ require "active_merchant/billing/cyber_source_fix"
6
+
7
+ require "workarea/cyber_source/engine"
8
+ require "workarea/cyber_source/version"
9
+
10
+ module Workarea
11
+ module CyberSource
12
+ def self.auto_configure_gateway
13
+ if Rails.application.secrets.cyber_source.present?
14
+ if ENV["HTTP_PROXY"].present?
15
+ uri = URI.parse(ENV["HTTP_PROXY"])
16
+ ActiveMerchant::Billing::CyberSourceGateway.proxy_address = uri.host
17
+ ActiveMerchant::Billing::CyberSourceGateway.proxy_port = uri.port
18
+ end
19
+
20
+ self.gateway = ActiveMerchant::Billing::CyberSourceGateway.new(
21
+ Rails.application.secrets.cyber_source.deep_symbolize_keys
22
+ )
23
+ else
24
+ self.gateway = ActiveMerchant::Billing::BogusCyberSourceGateway.new
25
+ end
26
+ end
27
+
28
+ def self.gateway
29
+ Workarea.config.gateways.credit_card
30
+ end
31
+
32
+ def self.gateway=(gateway)
33
+ Workarea.config.gateways.credit_card = gateway
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ module Workarea
2
+ module CyberSource
3
+ class Engine < ::Rails::Engine
4
+ include Workarea::Plugin
5
+ isolate_namespace Workarea::CyberSource
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Workarea
2
+ module CyberSource
3
+ VERSION = "1.0.3"
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative "config/application"
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
3
+ load Gem.bin_path("bundler", "bundle")
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ require "pathname"
3
+ require "fileutils"
4
+ include FileUtils
5
+
6
+ # path to your application root.
7
+ APP_ROOT = Pathname.new File.expand_path("../../", __FILE__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ chdir APP_ROOT do
14
+ # This script is a starting point to setup your application.
15
+ # Add necessary setup steps to this file.
16
+
17
+ puts "== Installing dependencies =="
18
+ system! "gem install bundler --conservative"
19
+ system("bundle check") || system!("bundle install")
20
+
21
+ # Install JavaScript dependencies if using Yarn
22
+ # system('bin/yarn')
23
+
24
+
25
+ # puts "\n== Copying sample files =="
26
+ # unless File.exist?('config/database.yml')
27
+ # cp 'config/database.yml.sample', 'config/database.yml'
28
+ # end
29
+
30
+ puts "\n== Preparing database =="
31
+ system! "bin/rails db:setup"
32
+
33
+ puts "\n== Removing old logs and tempfiles =="
34
+ system! "bin/rails log:clear tmp:clear"
35
+
36
+ puts "\n== Restarting application server =="
37
+ system! "bin/rails restart"
38
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ require "pathname"
3
+ require "fileutils"
4
+ include FileUtils
5
+
6
+ # path to your application root.
7
+ APP_ROOT = Pathname.new File.expand_path("../../", __FILE__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ chdir APP_ROOT do
14
+ # This script is a way to update your development environment automatically.
15
+ # Add necessary update steps to this file.
16
+
17
+ puts "== Installing dependencies =="
18
+ system! "gem install bundler --conservative"
19
+ system("bundle check") || system!("bundle install")
20
+
21
+ puts "\n== Updating database =="
22
+ system! "bin/rails db:migrate"
23
+
24
+ puts "\n== Removing old logs and tempfiles =="
25
+ system! "bin/rails log:clear tmp:clear"
26
+
27
+ puts "\n== Restarting application server =="
28
+ system! "bin/rails restart"
29
+ end