solidus_six_saferpay 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +220 -0
  4. data/Rakefile +56 -0
  5. data/app/assets/config/solidus_six_saferpay_manifest.js +2 -0
  6. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/amex.png +0 -0
  7. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/cirrus.png +0 -0
  8. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/delta.png +0 -0
  9. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/diners_club.png +0 -0
  10. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/directdebit.png +0 -0
  11. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/discover.png +0 -0
  12. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/egold.png +0 -0
  13. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/maestro.png +0 -0
  14. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/mastercard.png +0 -0
  15. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/paypal.png +0 -0
  16. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/solo.png +0 -0
  17. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/switch.png +0 -0
  18. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/twint.png +0 -0
  19. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/visa.png +0 -0
  20. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/visaelectron.png +0 -0
  21. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/westernunion.png +0 -0
  22. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/wirecard.png +0 -0
  23. data/app/assets/images/solidus_six_saferpay/credit_cards/icons/worldpay.png +0 -0
  24. data/app/assets/javascripts/solidus_six_saferpay/application.js +14 -0
  25. data/app/assets/javascripts/solidus_six_saferpay/saferpay_payment.js +120 -0
  26. data/app/assets/stylesheets/solidus_six_saferpay/application.css +15 -0
  27. data/app/assets/stylesheets/solidus_six_saferpay/loading_animation.scss +30 -0
  28. data/app/assets/stylesheets/solidus_six_saferpay/payments.scss +10 -0
  29. data/app/controllers/solidus_six_saferpay/application_controller.rb +5 -0
  30. data/app/controllers/spree/solidus_six_saferpay/checkout_controller.rb +93 -0
  31. data/app/controllers/spree/solidus_six_saferpay/payment_page/checkout_controller.rb +27 -0
  32. data/app/controllers/spree/solidus_six_saferpay/transaction/checkout_controller.rb +27 -0
  33. data/app/helpers/solidus_six_saferpay/application_helper.rb +4 -0
  34. data/app/jobs/solidus_six_saferpay/application_job.rb +4 -0
  35. data/app/mailers/solidus_six_saferpay/application_mailer.rb +6 -0
  36. data/app/models/solidus_six_saferpay/application_record.rb +5 -0
  37. data/app/models/spree/payment_method/saferpay_payment_method.rb +46 -0
  38. data/app/models/spree/payment_method/saferpay_payment_page.rb +13 -0
  39. data/app/models/spree/payment_method/saferpay_transaction.rb +12 -0
  40. data/app/models/spree/six_saferpay_payment.rb +69 -0
  41. data/app/services/spree/route_access.rb +5 -0
  42. data/app/services/spree/solidus_six_saferpay/assert_payment_page.rb +8 -0
  43. data/app/services/spree/solidus_six_saferpay/authorize_payment.rb +60 -0
  44. data/app/services/spree/solidus_six_saferpay/authorize_transaction.rb +8 -0
  45. data/app/services/spree/solidus_six_saferpay/initialize_payment.rb +57 -0
  46. data/app/services/spree/solidus_six_saferpay/initialize_payment_page.rb +15 -0
  47. data/app/services/spree/solidus_six_saferpay/initialize_transaction.rb +16 -0
  48. data/app/services/spree/solidus_six_saferpay/inquire_payment.rb +43 -0
  49. data/app/services/spree/solidus_six_saferpay/inquire_payment_page_payment.rb +7 -0
  50. data/app/services/spree/solidus_six_saferpay/inquire_transaction.rb +7 -0
  51. data/app/services/spree/solidus_six_saferpay/inquire_transaction_payment.rb +7 -0
  52. data/app/services/spree/solidus_six_saferpay/payment_validator.rb +62 -0
  53. data/app/services/spree/solidus_six_saferpay/process_authorized_payment.rb +87 -0
  54. data/app/services/spree/solidus_six_saferpay/process_payment_page_payment.rb +7 -0
  55. data/app/services/spree/solidus_six_saferpay/process_transaction_payment.rb +7 -0
  56. data/app/services/spree/solidus_six_saferpay/use_payment_page_gateway.rb +11 -0
  57. data/app/services/spree/solidus_six_saferpay/use_transaction_gateway.rb +11 -0
  58. data/app/views/spree/admin/payments/source_views/_saferpay_payment.erb +36 -0
  59. data/app/views/spree/api/payments/source_views/_saferpay_payment.json.jbuilder +1 -0
  60. data/app/views/spree/checkout/payment/_saferpay_payment.html.erb +12 -0
  61. data/app/views/spree/six_saferpay_payments/_six_saferpay_payment.html.erb +16 -0
  62. data/app/views/spree/solidus_six_saferpay/checkout/iframe_breakout_redirect.html.erb +7 -0
  63. data/config/locales/de.yml +52 -0
  64. data/config/locales/en.yml +52 -0
  65. data/config/locales/fr.yml +51 -0
  66. data/config/routes.rb +17 -0
  67. data/db/migrate/20190523075638_create_six_saferpay_payments.rb +31 -0
  68. data/lib/generators/solidus_six_saferpay/install/install_generator.rb +20 -0
  69. data/lib/solidus_six_saferpay.rb +5 -0
  70. data/lib/solidus_six_saferpay/configuration.rb +12 -0
  71. data/lib/solidus_six_saferpay/engine.rb +25 -0
  72. data/lib/solidus_six_saferpay/error_handler.rb +21 -0
  73. data/lib/solidus_six_saferpay/gateway.rb +183 -0
  74. data/lib/solidus_six_saferpay/gateway_response.rb +38 -0
  75. data/lib/solidus_six_saferpay/invalid_saferpay_payment.rb +11 -0
  76. data/lib/solidus_six_saferpay/payment_page_gateway.rb +55 -0
  77. data/lib/solidus_six_saferpay/transaction_gateway.rb +47 -0
  78. data/lib/solidus_six_saferpay/version.rb +3 -0
  79. data/lib/tasks/solidus_six_saferpay_tasks.rake +4 -0
  80. metadata +319 -0
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,30 @@
1
+ .saferpay-iframe {
2
+ width: 100%;
3
+ }
4
+
5
+ .loading-animation {
6
+ text-align: center;
7
+ background-color: #222222;
8
+
9
+ -webkit-animation: sk-scaleout 0.5s infinite ease-in-out;
10
+ animation: sk-scaleout 0.5s infinite ease-in-out;
11
+ }
12
+
13
+ @-webkit-keyframes sk-scaleout {
14
+ 0% { -webkit-transform: scale(0) }
15
+ 100% {
16
+ -webkit-transform: scale(0.2);
17
+ opacity: 0;
18
+ }
19
+ }
20
+
21
+ @keyframes sk-scaleout {
22
+ 0% {
23
+ -webkit-transform: scale(0);
24
+ transform: scale(0);
25
+ } 100% {
26
+ -webkit-transform: scale(0.2);
27
+ transform: scale(0.2);
28
+ opacity: 0;
29
+ }
30
+ }
@@ -0,0 +1,10 @@
1
+ .saferpay-payment {
2
+ &.void {
3
+
4
+ .amount {
5
+ text-decoration: line-through;
6
+ }
7
+ }
8
+ }
9
+
10
+
@@ -0,0 +1,5 @@
1
+ module SolidusSixSaferpay
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,93 @@
1
+ module Spree
2
+ module SolidusSixSaferpay
3
+ class CheckoutController < StoreController
4
+
5
+ before_action :load_order
6
+
7
+ def init
8
+ payment_method = Spree::PaymentMethod.find(params[:payment_method_id])
9
+ initialized_payment = initialize_payment(@order, payment_method)
10
+
11
+ if initialized_payment.success?
12
+ redirect_url = initialized_payment.redirect_url
13
+ render json: { redirect_url: redirect_url }
14
+ else
15
+ render json: { errors: t('.checkout_not_initialized') }, status: 422
16
+ end
17
+ end
18
+
19
+ def success
20
+ saferpay_payment = Spree::SixSaferpayPayment.where(order_id: @order.id).order(:created_at).last
21
+
22
+ if saferpay_payment.nil?
23
+ raise Spree::Core::GatewayError, t('.payment_source_not_created')
24
+ end
25
+
26
+ # NOTE: PaymentPage payments are authorized directly. Instead, we
27
+ # perform an ASSERT here to gather the necessary details.
28
+ # This might be confusing at first, but doing it this way makes sense
29
+ # (and the code a LOT more readable) IMO. Feel free to disagree and PR
30
+ # a better solution.
31
+ # NOTE: Transaction payments are authorized here so that the money is
32
+ # already allocated when the user is on the confirm page. If the user
33
+ # then chooses another payment, the authorized payment is voided
34
+ # (cancelled).
35
+ payment_authorization = authorize_payment(saferpay_payment)
36
+
37
+ if payment_authorization.success?
38
+
39
+ processed_authorization = process_authorization(saferpay_payment)
40
+ if processed_authorization.success?
41
+ @order.next! if @order.payment?
42
+ else
43
+ flash[:error] = processed_authorization.user_message
44
+ end
45
+
46
+ else
47
+ payment_inquiry = inquire_payment(saferpay_payment)
48
+ flash[:error] = payment_inquiry.user_message
49
+ end
50
+
51
+ @redirect_path = order_checkout_path(@order.state)
52
+ render :iframe_breakout_redirect, layout: false
53
+ end
54
+
55
+ def fail
56
+ saferpay_payment = Spree::SixSaferpayPayment.where(order_id: @order.id).order(:created_at).last
57
+
58
+ payment_inquiry = inquire_payment(saferpay_payment)
59
+
60
+ @redirect_path = order_checkout_path(:payment)
61
+ flash[:error] = payment_inquiry.user_message
62
+ render :iframe_breakout_redirect, layout: false
63
+ end
64
+
65
+ private
66
+
67
+ def initialize_payment(order, payment_method)
68
+ raise NotImplementedError, "Must be implemented in PaymentPageCheckoutController or TransactionCheckoutController"
69
+ end
70
+
71
+ def authorize_payment(saferpay_payment)
72
+ raise NotImplementedError, "Must be implemented in PaymentPageCheckoutController or TransactionCheckoutController"
73
+ end
74
+
75
+ def process_authorization(saferpay_payment)
76
+ raise NotImplementedError, "Must be implemented in PaymentPageCheckoutController or TransactionCheckoutController"
77
+ end
78
+
79
+ def inquire_payment(saferpay_payment)
80
+ raise NotImplementedError, "Must be implemented in PaymentPageCheckoutController or TransactionCheckoutController"
81
+ end
82
+
83
+ def load_order
84
+ @order = current_order
85
+ redirect_to(spree.cart_path) && return unless @order
86
+ end
87
+
88
+ def order_checkout_path(state)
89
+ Spree::Core::Engine.routes.url_helpers.checkout_state_path(state)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module SolidusSixSaferpay
3
+ module PaymentPage
4
+ # explicit parent must be stated, otherwise Spree::CheckoutController has precendence
5
+ class CheckoutController < SolidusSixSaferpay::CheckoutController
6
+
7
+ private
8
+
9
+ def initialize_payment(order, payment_method)
10
+ InitializePaymentPage.call(order, payment_method)
11
+ end
12
+
13
+ def authorize_payment(saferpay_payment)
14
+ AssertPaymentPage.call(saferpay_payment)
15
+ end
16
+
17
+ def process_authorization(saferpay_payment)
18
+ ProcessPaymentPagePayment.call(saferpay_payment)
19
+ end
20
+
21
+ def inquire_payment(saferpay_payment)
22
+ InquirePaymentPagePayment.call(saferpay_payment)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module SolidusSixSaferpay
3
+ # explicit parent must be stated, otherwise Spree::CheckoutController has precendence
4
+ module Transaction
5
+ class CheckoutController < SolidusSixSaferpay::CheckoutController
6
+
7
+ private
8
+
9
+ def initialize_payment(order, payment_method)
10
+ InitializeTransaction.call(order, payment_method)
11
+ end
12
+
13
+ def authorize_payment(saferpay_payment)
14
+ AuthorizeTransaction.call(saferpay_payment)
15
+ end
16
+
17
+ def process_authorization(saferpay_payment)
18
+ ProcessTransactionPayment.call(saferpay_payment)
19
+ end
20
+
21
+ def inquire_payment(saferpay_payment)
22
+ InquireTransactionPayment.call(saferpay_payment)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ module SolidusSixSaferpay
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SolidusSixSaferpay
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module SolidusSixSaferpay
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module SolidusSixSaferpay
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,46 @@
1
+ module Spree
2
+ class PaymentMethod::SaferpayPaymentMethod < PaymentMethod::CreditCard
3
+ include RouteAccess
4
+
5
+ AVAILABLE_PAYMENT_METHODS = %w(ALIPAY AMEX BANCONTACT BONUS DINERS DIRECTDEBIT EPRZELEWY EPS GIROPAY IDEAL INVOICE JCB MAESTRO MASTERCARD MYONE PAYPAL PAYDIREKT POSTCARD POSTFINANCE SAFERPAYTEST SOFORT TWINT UNIONPAY VISA VPAY)
6
+
7
+ delegate :try_void, to: :gateway
8
+
9
+ preference :as_iframe, :boolean, default: true
10
+
11
+ preference :require_liability_shift, :boolean, default: true
12
+
13
+ # Configure all available Payment Methods for the Saferpay API as
14
+ # preferences
15
+ AVAILABLE_PAYMENT_METHODS.each do |six_payment_method|
16
+ preference "payment_method_#{six_payment_method.downcase}", :boolean, default: false
17
+ end
18
+
19
+ def enabled_payment_methods
20
+ AVAILABLE_PAYMENT_METHODS.select do |six_payment_method|
21
+ public_send("preferred_payment_method_#{six_payment_method.downcase}")
22
+ end
23
+ end
24
+
25
+ def payment_source_class
26
+ Spree::SixSaferpayPayment
27
+ end
28
+
29
+ def profiles_supported?
30
+ false
31
+ end
32
+
33
+ # We want to automatically capture the payment when the order is completed
34
+ def auto_capture
35
+ true
36
+ end
37
+
38
+ def partial_name
39
+ 'saferpay_payment'
40
+ end
41
+
42
+ def init_path
43
+ raise NotImplementedError, "Must be implemented in SaferpayPaymentPage or SaferpayTransaction"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ class PaymentMethod::SaferpayPaymentPage < PaymentMethod::SaferpayPaymentMethod
3
+
4
+
5
+ def gateway_class
6
+ ::SolidusSixSaferpay::PaymentPageGateway
7
+ end
8
+
9
+ def init_path
10
+ url_helpers.solidus_six_saferpay_payment_page_init_path
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Spree
2
+ class PaymentMethod::SaferpayTransaction < PaymentMethod::SaferpayPaymentMethod
3
+
4
+ def gateway_class
5
+ ::SolidusSixSaferpay::TransactionGateway
6
+ end
7
+
8
+ def init_path
9
+ url_helpers.solidus_six_saferpay_transaction_init_path
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,69 @@
1
+ module Spree
2
+
3
+ # attributes
4
+ # * :token
5
+ # * :expiration
6
+ # * :redirect_url
7
+ # * :transaction_id
8
+ # * :transaction_status
9
+ # * :transaction_date
10
+ # * :six_transaction_reference
11
+ # * :display_text
12
+ # * :masked_number
13
+ # * :expiration_year
14
+ # * :expiration_month
15
+ # * :response_hash
16
+ class SixSaferpayPayment < PaymentSource
17
+ belongs_to :order
18
+ belongs_to :payment_method
19
+ # store this anyway for accountability reasons
20
+ serialize :response_hash, Hash
21
+
22
+ validates :token, :expiration, presence: true
23
+ validates :token, :transaction_id, :six_transaction_reference, uniqueness: true, allow_blank: true
24
+
25
+ def create_solidus_payment!
26
+ payments.create!(order: order, response_code: transaction_id, payment_method: payment_method, amount: order.total, source: self)
27
+ end
28
+
29
+ def address
30
+ @address ||= order.bill_address
31
+ end
32
+
33
+ def payment_means
34
+ @payment_means ||= ::SixSaferpay::ResponsePaymentMeans.new(response_hash[:payment_means])
35
+ end
36
+
37
+ def transaction
38
+ @transaction ||= ::SixSaferpay::Transaction.new(response_hash[:transaction])
39
+ end
40
+
41
+ def liability
42
+ @liability ||= ::SixSaferpay::Liability.new(response_hash[:liability])
43
+ end
44
+
45
+ def card
46
+ payment_means.card
47
+ end
48
+
49
+ def name
50
+ card.holder_name
51
+ end
52
+
53
+ def brand_name
54
+ payment_means.brand.name
55
+ end
56
+
57
+ def month
58
+ card.exp_month
59
+ end
60
+
61
+ def year
62
+ card.exp_year
63
+ end
64
+
65
+ def icon_name
66
+ payment_means.brand.payment_method.downcase
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ module Spree
2
+ module RouteAccess
3
+ delegate :url_helpers, to: 'Spree::Core::Engine.routes'
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ module Spree
2
+ module SolidusSixSaferpay
3
+ class AssertPaymentPage < AuthorizePayment
4
+ include UsePaymentPageGateway
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,60 @@
1
+ module Spree
2
+ module SolidusSixSaferpay
3
+ class AuthorizePayment
4
+ attr_reader :saferpay_payment, :order, :success
5
+
6
+ def self.call(saferpay_payment)
7
+ new(saferpay_payment).call
8
+ end
9
+
10
+ def initialize(saferpay_payment)
11
+ @saferpay_payment = saferpay_payment
12
+ @order = saferpay_payment.order
13
+ end
14
+
15
+ def call
16
+ authorization = gateway.authorize(order.total, saferpay_payment)
17
+
18
+ if authorization.success?
19
+ saferpay_payment.update_attributes!(saferpay_payment_attributes(authorization.api_response))
20
+ @success = true
21
+ end
22
+ self
23
+ end
24
+
25
+ def success?
26
+ @success
27
+ end
28
+
29
+
30
+ def gateway
31
+ raise NotImplementedError, "Must be implemented in AssertPaymentPage or AuthorizeTransaction with UsePaymentPageGateway or UseTransactionGateway"
32
+ end
33
+
34
+ private
35
+
36
+ def saferpay_payment_attributes(saferpay_response)
37
+ payment_means = saferpay_response.payment_means
38
+ brand = payment_means.brand
39
+ card = payment_means.card
40
+
41
+ attributes = {
42
+ transaction_id: saferpay_response.transaction.id,
43
+ transaction_status: saferpay_response.transaction.status,
44
+ transaction_date: DateTime.parse(saferpay_response.transaction.date),
45
+ six_transaction_reference: saferpay_response.transaction.six_transaction_reference,
46
+ display_text: saferpay_response.payment_means.display_text,
47
+ response_hash: saferpay_response.to_h
48
+ }
49
+
50
+ if card
51
+ attributes[:masked_number] = card.masked_number
52
+ attributes[:expiration_year] = card.exp_year
53
+ attributes[:expiration_month] = card.exp_month
54
+ end
55
+
56
+ attributes
57
+ end
58
+ end
59
+ end
60
+ end