spree_vpago 0.1.0.pre.beta → 2.0.4.pre.beta1

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_publish_gem.yml +2 -0
  3. data/Gemfile.lock +25 -23
  4. data/app/assets/javascripts/vpago/vpago_payments/user_informers/firebase.js +1686 -1426
  5. data/app/controllers/spree/admin/payment_methods_controller_decorator.rb +9 -1
  6. data/app/controllers/spree/vpago_payments_controller.rb +12 -2
  7. data/app/javascripts/queue_processor.js +33 -0
  8. data/app/javascripts/queue_processor.test.js +88 -0
  9. data/app/javascripts/vpago/vpago_payments/user_informers/firebase.js +92 -13
  10. data/app/jobs/vpago/payment_capturer_job.rb +11 -0
  11. data/app/jobs/vpago/payment_processor_job.rb +3 -0
  12. data/app/models/vpago/address_decorator.rb +10 -0
  13. data/app/models/vpago/order_decorator.rb +13 -1
  14. data/app/models/vpago/payment_decorator.rb +18 -1
  15. data/app/overrides/spree/admin/payment_methods/index/payment_methods_tabs.html.erb.deface +7 -1
  16. data/app/overrides/spree/admin/payment_methods/index/tenant_body.html.erb.deface +5 -0
  17. data/app/overrides/spree/admin/payment_methods/index/tenant_header.html.erb.deface +5 -0
  18. data/app/serializers/spree/v2/storefront/payment_serializer_decorator.rb +1 -1
  19. data/app/services/vpago/payment_processable.rb +54 -0
  20. data/app/services/vpago/payment_processor.rb +58 -41
  21. data/app/services/vpago/payment_url_constructor.rb +1 -1
  22. data/app/services/vpago/user_informers/firebase.rb +21 -16
  23. data/app/views/spree/admin/shared/_payment_methods_tabs.html.erb +7 -0
  24. data/app/views/spree/vpago_payments/processing.html.erb +36 -23
  25. data/lib/spree_vpago/version.rb +1 -1
  26. data/lib/vpago/payway_v2/base.rb +8 -8
  27. data/lib/vpago/payway_v2/checkout.rb +2 -2
  28. data/node_modules/.yarn-integrity +93 -2
  29. data/package.json +6 -1
  30. data/yarn.lock +556 -2
  31. metadata +9 -2
@@ -3,7 +3,15 @@ module Spree
3
3
  module PaymentMethodsControllerDecorator
4
4
  def scope
5
5
  scope = current_store.payment_methods_including_vendor.accessible_by(current_ability, :index)
6
- scope = scope.where.not(vendor_id: nil) if params[:tab] == 'vendors'
6
+
7
+ if params[:tab] == 'vendors'
8
+ scope = scope.where.not(vendor_id: nil)
9
+ elsif params[:tab] == 'tenants'
10
+ scope = scope.joins(:vendor)
11
+ .where.not(vendor_id: nil)
12
+ .where.not(spree_vendors: { tenant_id: nil })
13
+ end
14
+
7
15
  scope
8
16
  end
9
17
 
@@ -39,7 +39,8 @@ module Spree
39
39
  def process_payment
40
40
  return render json: { status: :ok }, status: :ok if request.method != 'POST'
41
41
 
42
- @payment = Vpago::PaymentFinder.new(params.permit!.to_h).find_and_verify
42
+ return_params = sanitize_return_params
43
+ @payment = Vpago::PaymentFinder.new(return_params).find_and_verify
43
44
  return render_not_found unless @payment.present?
44
45
 
45
46
  unless @payment.order.paid?
@@ -54,7 +55,16 @@ module Spree
54
55
  render json: { status: :internal_server_error, message: 'Failed to enqueue payment processor job' }, status: :internal_server_error
55
56
  end
56
57
 
57
- def record_not_found
58
+ def sanitize_return_params
59
+ sanitized_params = params.permit!.to_h
60
+
61
+ # In ABA case, it returns params in side return params.
62
+ sanitized_params.merge!(JSON.parse(sanitized_params.delete(:return_params))) if sanitized_params[:return_params].present?
63
+
64
+ sanitized_params
65
+ end
66
+
67
+ def render_not_found
58
68
  respond_to do |format|
59
69
  format.html { render file: Rails.public_path.join('404.html'), status: :not_found, layout: false }
60
70
  format.json { render json: { status: :not_found }, status: :not_found }
@@ -0,0 +1,33 @@
1
+ export default class QueueProcessor {
2
+ constructor() {
3
+ this.queues = [];
4
+ this.processing = false;
5
+ }
6
+
7
+ queueStateChange({ callback, minDelayInMs = 1000 }) {
8
+ this.queues.push({ callback, minDelayInMs });
9
+ if (!this.processing) this.#processQueue();
10
+ }
11
+
12
+ async #processQueue() {
13
+ if (this.queues.length === 0) {
14
+ this.processing = false;
15
+ return;
16
+ }
17
+
18
+ this.processing = true;
19
+ const { callback, minDelayInMs } = this.queues.shift();
20
+ const startTime = Date.now();
21
+
22
+ await callback();
23
+
24
+ const elapsedTime = Date.now() - startTime;
25
+ if (elapsedTime < minDelayInMs) {
26
+ await new Promise((resolve) =>
27
+ setTimeout(resolve, minDelayInMs - elapsedTime)
28
+ );
29
+ }
30
+
31
+ this.#processQueue();
32
+ }
33
+ }
@@ -0,0 +1,88 @@
1
+ import { expect } from "chai";
2
+ import QueueProcessor from "./queue_processor.js";
3
+
4
+ describe("QueueProcessor", () => {
5
+ let queueProcessor;
6
+ let minDelayInMs = 100;
7
+
8
+ beforeEach(() => {
9
+ queueProcessor = new QueueProcessor();
10
+ });
11
+
12
+ it("should initially have an empty queue and not be processing", () => {
13
+ expect(queueProcessor.queues).to.have.lengthOf(0);
14
+ expect(queueProcessor.processing).to.equal(false);
15
+ });
16
+
17
+ it("should run queue 1 by 1 while keep min delay 100", async () => {
18
+ let timeToProcess = 50;
19
+
20
+ const mockCallback = () =>
21
+ new Promise((resolve) => setTimeout(resolve, timeToProcess));
22
+
23
+ queueProcessor.queueStateChange({
24
+ callback: mockCallback,
25
+ minDelayInMs: minDelayInMs,
26
+ });
27
+
28
+ queueProcessor.queueStateChange({
29
+ callback: mockCallback,
30
+ minDelayInMs: minDelayInMs,
31
+ });
32
+
33
+ queueProcessor.queueStateChange({
34
+ callback: mockCallback,
35
+ minDelayInMs: minDelayInMs,
36
+ });
37
+
38
+ expect(queueProcessor.queues).to.have.lengthOf(2);
39
+ expect(queueProcessor.processing).to.equal(true);
40
+
41
+ await new Promise((resolve) => setTimeout(resolve, minDelayInMs * 3 + 4)); // +4ms for buffer
42
+
43
+ expect(queueProcessor.queues).to.have.lengthOf(0);
44
+ expect(queueProcessor.processing).to.equal(false);
45
+ });
46
+
47
+ describe("when process take less than the delay", () => {
48
+ it("should process and wait for remaining delay", async () => {
49
+ let timeToProcess = 50;
50
+
51
+ const mockCallback = () =>
52
+ new Promise((resolve) => setTimeout(resolve, timeToProcess));
53
+
54
+ queueProcessor.queueStateChange({
55
+ callback: mockCallback,
56
+ minDelayInMs: minDelayInMs,
57
+ });
58
+
59
+ expect(queueProcessor.queues).to.have.lengthOf(0);
60
+ expect(queueProcessor.processing).to.equal(true);
61
+
62
+ await new Promise((resolve) => setTimeout(resolve, minDelayInMs + 1)); // +1ms for buffer
63
+
64
+ expect(queueProcessor.queues).to.have.lengthOf(0);
65
+ expect(queueProcessor.processing).to.equal(false);
66
+ });
67
+ });
68
+
69
+ describe("when process take longer than the delay", () => {
70
+ it("should process and not wait for delay", async () => {
71
+ let timeToProcess = 200;
72
+
73
+ const mockCallback = () =>
74
+ new Promise((resolve) => setTimeout(resolve, timeToProcess));
75
+
76
+ queueProcessor.queueStateChange({
77
+ callback: mockCallback,
78
+ minDelayInMs: minDelayInMs,
79
+ });
80
+
81
+ expect(queueProcessor.queues).to.have.lengthOf(0);
82
+ expect(queueProcessor.processing).to.equal(true);
83
+
84
+ await new Promise((resolve) => setTimeout(resolve, timeToProcess + 1)); // +1ms for buffer
85
+ expect(queueProcessor.processing).to.equal(false);
86
+ });
87
+ });
88
+ });
@@ -1,9 +1,10 @@
1
1
  import { initializeApp } from "firebase/app";
2
2
  import { getFirestore, doc, onSnapshot, setDoc } from "firebase/firestore";
3
+ import QueueProcessor from "../../../queue_processor.js";
3
4
 
4
5
  async function listenToProcessingState({
5
6
  firebaseConfigs,
6
- orderNumber,
7
+ documentReferencePath,
7
8
  onPaymentIsProcessing,
8
9
  onOrderIsProcessing,
9
10
  onOrderIsCompleted,
@@ -15,43 +16,121 @@ async function listenToProcessingState({
15
16
  const app = initializeApp(firebaseConfigs);
16
17
  const db = getFirestore(app);
17
18
 
18
- const currentDate = new Date().toISOString().split("T")[0];
19
-
20
- const documentRef = doc(db, "statuses", "cart", currentDate, orderNumber);
19
+ const documentRef = doc(db, documentReferencePath);
21
20
  await setDoc(documentRef, { listening: true }, { merge: true });
22
21
 
22
+ const queueProcessor = new QueueProcessor();
23
+
23
24
  onSnapshot(documentRef, (doc) => {
24
25
  let documentData = doc.data();
25
26
 
27
+ let messageCode = documentData["message_code"];
26
28
  let orderState = documentData["order_state"];
27
29
  let paymentState = documentData["payment_state"];
28
- let messageCode = documentData["message_code"];
29
- let logMessage = documentData["log_message"];
30
+ let processing = documentData["processing"] === true;
31
+ let reasonCode = documentData["reason_code"];
32
+ let reasonMessage = documentData["reason_message"];
30
33
 
31
34
  let orderCompleted = orderState === "complete";
32
35
  if (orderCompleted) {
33
- onCompleted(orderState, paymentState);
36
+ queueProcessor.queueStateChange({
37
+ minDelayInMs: 1500,
38
+ callback: async () => {
39
+ await onCompleted(
40
+ orderState,
41
+ paymentState,
42
+ reasonCode,
43
+ reasonMessage
44
+ );
45
+ },
46
+ });
34
47
  return;
35
48
  }
36
49
 
37
50
  switch (messageCode) {
38
51
  case "payment_is_processing":
39
- onPaymentIsProcessing(orderState, paymentState, logMessage);
52
+ queueProcessor.queueStateChange({
53
+ minDelayInMs: 1500,
54
+ callback: async () => {
55
+ await onPaymentIsProcessing(
56
+ orderState,
57
+ paymentState,
58
+ processing,
59
+ reasonCode,
60
+ reasonMessage
61
+ );
62
+ },
63
+ });
40
64
  break;
41
65
  case "order_is_processing":
42
- onOrderIsProcessing(orderState, paymentState, logMessage);
66
+ queueProcessor.queueStateChange({
67
+ minDelayInMs: 1500,
68
+ callback: async () => {
69
+ await onOrderIsProcessing(
70
+ orderState,
71
+ paymentState,
72
+ processing,
73
+ reasonCode,
74
+ reasonMessage
75
+ );
76
+ },
77
+ });
43
78
  break;
44
79
  case "order_is_completed":
45
- onOrderIsCompleted(orderState, paymentState, logMessage);
80
+ queueProcessor.queueStateChange({
81
+ minDelayInMs: 1500,
82
+ callback: async () => {
83
+ await onOrderIsCompleted(
84
+ orderState,
85
+ paymentState,
86
+ processing,
87
+ reasonCode,
88
+ reasonMessage
89
+ );
90
+ },
91
+ });
46
92
  break;
47
93
  case "order_process_failed":
48
- onOrderProcessFailed(orderState, paymentState, logMessage);
94
+ queueProcessor.queueStateChange({
95
+ minDelayInMs: 1500,
96
+ callback: async () => {
97
+ await onOrderProcessFailed(
98
+ orderState,
99
+ paymentState,
100
+ processing,
101
+ reasonCode,
102
+ reasonMessage
103
+ );
104
+ },
105
+ });
49
106
  break;
50
107
  case "payment_is_refunded":
51
- onPaymentIsRefunded(orderState, paymentState, logMessage);
108
+ queueProcessor.queueStateChange({
109
+ minDelayInMs: 1500,
110
+ callback: async () => {
111
+ await onPaymentIsRefunded(
112
+ orderState,
113
+ paymentState,
114
+ processing,
115
+ reasonCode,
116
+ reasonMessage
117
+ );
118
+ },
119
+ });
52
120
  break;
53
121
  case "payment_process_failed":
54
- onPaymentProcessFailed(orderState, paymentState, logMessage);
122
+ queueProcessor.queueStateChange({
123
+ minDelayInMs: 1500,
124
+ callback: async () => {
125
+ await onPaymentProcessFailed(
126
+ orderState,
127
+ paymentState,
128
+ processing,
129
+ reasonCode,
130
+ reasonMessage
131
+ );
132
+ },
133
+ });
55
134
  break;
56
135
  default:
57
136
  break;
@@ -0,0 +1,11 @@
1
+ # Put :payment_processing at a higher priority in your project: config/sidekiq.yml
2
+ module Vpago
3
+ class PaymentCapturerJob < ::ApplicationUniqueJob
4
+ queue_as :payment_processing
5
+
6
+ def perform(payment_id)
7
+ payment = Spree::Payment.find(payment_id)
8
+ payment.capture! if payment.pending?
9
+ end
10
+ end
11
+ end
@@ -1,5 +1,8 @@
1
+ # Put :payment_processing at a higher priority in your project: config/sidekiq.yml
1
2
  module Vpago
2
3
  class PaymentProcessorJob < ::ApplicationUniqueJob
4
+ queue_as :payment_processing
5
+
3
6
  def perform(options)
4
7
  payment = Spree::Payment.find_by(number: options[:payment_number])
5
8
  Vpago::PaymentProcessor.new(payment: payment).call
@@ -0,0 +1,10 @@
1
+ module Vpago
2
+ module AddressDecorator
3
+ # override
4
+ def require_phone?
5
+ false
6
+ end
7
+ end
8
+ end
9
+
10
+ Spree::Address.prepend(Vpago::AddressDecorator) unless Spree::Address.included_modules.include?(Vpago::AddressDecorator)
@@ -48,7 +48,9 @@ module Vpago
48
48
 
49
49
  # override
50
50
  def available_payment_methods(store = nil)
51
- payment_methods = if vendor_payment_methods.any?
51
+ payment_methods = if respond_to?(:tenant) && tenant.present?
52
+ tenant_payment_methods
53
+ elsif vendor_payment_methods.any?
52
54
  vendor_payment_methods
53
55
  else
54
56
  collect_payment_methods(store)
@@ -61,6 +63,10 @@ module Vpago
61
63
  end
62
64
  end
63
65
 
66
+ def tenant_payment_methods
67
+ tenant.tenant_payment_methods
68
+ end
69
+
64
70
  def line_items_count
65
71
  line_items.size
66
72
  end
@@ -72,6 +78,12 @@ module Vpago
72
78
  def order_adjustment_total
73
79
  adjustments.eligible.sum(:amount)
74
80
  end
81
+
82
+ # override this method if you want flexibility
83
+ # for example, host per payment method or tenant
84
+ def payment_host
85
+ ENV.fetch('DEFAULT_URL_HOST')
86
+ end
75
87
  end
76
88
  end
77
89
 
@@ -11,13 +11,30 @@ module Vpago
11
11
  to: :url_constructor
12
12
  end
13
13
 
14
+ # override:
15
+ # to give payment another chance to re-process, even if it failed.
14
16
  def process!
15
- # give payment another chance to re-process, even if it failed.
16
17
  update!(state: :checkout) if processing? || send(:has_invalid_state?)
17
18
 
18
19
  super
19
20
  end
20
21
 
22
+ # override:
23
+ # to allow capture faraday connection error. gateway_error method already write rails log for this.
24
+ def protect_from_connection_error
25
+ yield
26
+ rescue ActiveMerchant::ConnectionError => e
27
+ failure!
28
+ gateway_error(e)
29
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
30
+ failure!
31
+ gateway_error(ActiveMerchant::ConnectionError.new(e.message, e))
32
+ end
33
+
34
+ def user_informer
35
+ @user_informer ||= ::Vpago::UserInformers::Firebase.new(order)
36
+ end
37
+
21
38
  def url_constructor
22
39
  @url_constructor ||= Vpago::PaymentUrlConstructor.new(self)
23
40
  end
@@ -4,6 +4,12 @@
4
4
 
5
5
  <% if params[:tab] == 'vendors' %>
6
6
  <div class="alert alert-info mb-3">
7
- <%= svg_icon name: "info-circle.svg", classes: 'mr-2', width: '16', height: '16' %> Payment methods for each vendor. Once set, only those payment methods will be displayed to user.
7
+ <%= svg_icon name: "info-circle.svg", classes: 'mr-2', width: '16', height: '16' %>
8
+ Payment methods for each vendor. Once set, only those payment methods will be displayed to users.
9
+ </div>
10
+ <% elsif params[:tab] == 'tenants' %>
11
+ <div class="alert alert-info mb-3">
12
+ <%= svg_icon name: "info-circle.svg", classes: 'mr-2', width: '16', height: '16' %>
13
+ Payment methods for each tenant. Once set, only those payment methods will be displayed to users.
8
14
  </div>
9
15
  <% end %>
@@ -0,0 +1,5 @@
1
+ <!-- insert_before "[data-hook='admin_payment_methods_index_row_actions']" -->
2
+
3
+ <% if params[:tab] == 'tenants' %>
4
+ <td class="text-center"><%= method.vendor&.name || 'N/A' %></td>
5
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <!-- insert_before "[data-hook='admin_payment_methods_index_header_actions']" -->
2
+
3
+ <% if params[:tab] == 'tenants' %>
4
+ <th class="text-center"><%= Spree.t(:tenant) %></th>
5
+ <% end %>
@@ -3,7 +3,7 @@ module Spree
3
3
  module Storefront
4
4
  module PaymentSerializerDecorator
5
5
  def self.prepended(base)
6
- base.attributes :pre_auth_status, :checkout_url
6
+ base.attributes :pre_auth_status, :checkout_url, :process_payment_url
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,54 @@
1
+ module Vpago
2
+ module PaymentProcessable
3
+ def cancel_pre_auth(reason_code, reason_message)
4
+ log_process('cancel_pre_auth') do
5
+ @payment.void_transaction!
6
+ user_informer.payment_is_refunded(
7
+ processing: false,
8
+ reason_code: reason_code,
9
+ reason_message: reason_message
10
+ )
11
+ end
12
+ end
13
+
14
+ # Allows canceling pre-authorization if:
15
+ # 1. The payment is pending or authorized.
16
+ # 2. Pre-auth is enabled, ensuring funds can be released to user if processing fails.
17
+ # PaymentProcessor is usually called after payment is made, so canceling pre-auth typically works.
18
+ def can_cancel_pre_auth?
19
+ @payment.pending? || @payment.payment_method.enable_pre_auth?
20
+ end
21
+
22
+ def extract_completer_failure_reason_code(error)
23
+ return :some_line_items_are_out_of_stock if error.respond_to?(:to_h) && error.to_h[:base]&.include?(Spree.t(:insufficient_stock_lines_present))
24
+ return :some_variants_are_discontinued if error.respond_to?(:to_h) && error.to_h[:base]&.include?(Spree.t(:discontinued_variants_present))
25
+
26
+ :unable_to_complete_order
27
+ end
28
+
29
+ # example.
30
+ # Started Vpago::PaymentProcessor#process_payment! for payment_number: PX81YZX with args: {}
31
+ # Completed Vpago::PaymentProcessor#process_payment! for payment_number: PX81YZX in 2000ms
32
+ def log_process(method, *args)
33
+ start_time = Time.now
34
+ Rails.logger.error("Started #{self.class}##{method} for payment_number: #{@payment.number} with args: #{args}")
35
+
36
+ yield
37
+
38
+ duration_ms = (Time.now - start_time) * 1000
39
+ Rails.logger.error("Completed #{self.class}##{method} for payment_number: #{@payment.number} in #{duration_ms}ms")
40
+ end
41
+
42
+ def user_informer
43
+ @user_informer ||= ::Vpago::UserInformers::Firebase.new(@payment.order)
44
+ end
45
+
46
+ def success?
47
+ @error.nil?
48
+ end
49
+
50
+ def failure(error)
51
+ @error = error
52
+ end
53
+ end
54
+ end
@@ -1,6 +1,13 @@
1
+ # Error reason code:
2
+ # - :some_line_items_are_out_of_stock
3
+ # - :some_variants_are_discontinued
4
+ # - :unable_to_complete_order
5
+ # - :invalid_state_machine_transition
6
+ # - :unable_to_connect_to_gateway
7
+ # - :gateway_error
1
8
  module Vpago
2
9
  class PaymentProcessor
3
- attr_accessor :payment, :error
10
+ include PaymentProcessable
4
11
 
5
12
  def initialize(payment:)
6
13
  @payment = payment
@@ -8,65 +15,75 @@ module Vpago
8
15
  end
9
16
 
10
17
  def call
18
+ log_process('call!') { call! }
19
+ end
20
+
21
+ def call!
11
22
  process_payment!
12
- return process_order if payment.completed? || payment.pending?
23
+ process_order! if @payment.completed? || @payment.pending?
24
+ rescue Spree::Core::GatewayError => e
25
+ return handle_payment_failure(:unable_to_connect_to_gateway, e.message) if e.message == Spree.t(:unable_to_connect_to_gateway)
13
26
 
14
- mark_payment_process_failed
15
- rescue Spree::Core::GatewayError, StateMachines::InvalidTransition => e
16
- mark_payment_process_failed(e.message)
27
+ handle_payment_failure(:gateway_error, e.message)
28
+ rescue StateMachines::InvalidTransition => e
29
+ handle_payment_failure(:invalid_state_machine_transition, e.message)
17
30
  end
18
31
 
19
32
  private
20
33
 
34
+ # payment.process! will throw GatewayError if not success.
21
35
  def process_payment!
22
- user_informer.payment_is_processing(processing: true)
23
- payment.process!
24
- end
25
-
26
- def process_order
27
- user_informer.order_is_processing(processing: true)
28
- completer = Spree::Checkout::Complete.new.call(order: payment.order)
29
-
30
- if completer.success?
31
- mark_order_process_completed
32
- else
33
- mark_order_process_failed(completer.error.to_s)
36
+ log_process('process_payment!') do
37
+ user_informer.payment_is_processing(processing: true)
38
+ @payment.process!
34
39
  end
35
40
  end
36
41
 
37
- def mark_order_process_completed
38
- payment.capture! if payment.pending?
39
- user_informer.order_is_completed(processing: false)
42
+ def process_order!
43
+ log_process('process_order!') do
44
+ user_informer.order_is_processing(processing: true)
45
+ completer = Spree::Checkout::Complete.new.call(order: @payment.order)
46
+
47
+ if completer.success?
48
+ handle_order_process_completed
49
+ else
50
+ reason_code = extract_completer_failure_reason_code(completer.error)
51
+ handle_order_process_failure(reason_code, completer.error.to_s)
52
+ end
53
+ end
40
54
  end
41
55
 
42
- def mark_order_process_failed(message)
43
- user_informer.order_process_failed(processing: payment.pending?, log_message: message)
44
-
45
- if payment.pending?
46
- payment.void_transaction!
47
- user_informer.payment_is_refunded(processing: false)
56
+ def handle_order_process_completed
57
+ log_process('handle_order_process_completed') do
58
+ Vpago::PaymentCapturerJob.perform_now(@payment.id) if @payment.pending?
59
+ user_informer.order_is_completed(processing: false)
48
60
  end
49
-
50
- failure(message)
51
61
  end
52
62
 
53
- def mark_payment_process_failed(message = nil)
54
- message ||= Spree.t(:payment_process_failed)
55
-
56
- user_informer.payment_process_failed(processing: false, log_message: message)
57
- failure(message)
58
- end
63
+ def handle_order_process_failure(reason_code, reason_message = nil)
64
+ log_process('handle_order_process_failure', reason_code, reason_message) do
65
+ user_informer.order_process_failed(
66
+ processing: can_cancel_pre_auth?,
67
+ reason_code: reason_code,
68
+ reason_message: reason_message
69
+ )
59
70
 
60
- def user_informer
61
- @user_informer ||= ::Vpago::UserInformers::Firebase.new(payment.order)
71
+ cancel_pre_auth(reason_code, reason_message) if can_cancel_pre_auth?
72
+ failure(reason_message)
73
+ end
62
74
  end
63
75
 
64
- def success?
65
- @error.nil?
66
- end
76
+ def handle_payment_failure(reason_code, reason_message)
77
+ log_process('handle_payment_failure', reason_code, reason_message) do
78
+ user_informer.payment_process_failed(
79
+ processing: can_cancel_pre_auth?,
80
+ reason_code: reason_code,
81
+ reason_message: reason_message
82
+ )
67
83
 
68
- def failure(error)
69
- @error = error
84
+ cancel_pre_auth(reason_code, reason_message) if can_cancel_pre_auth?
85
+ failure(reason_message)
86
+ end
70
87
  end
71
88
  end
72
89
  end
@@ -21,7 +21,7 @@ module Vpago
21
21
  private
22
22
 
23
23
  def base_url
24
- ENV.fetch('DEFAULT_URL_HOST')
24
+ order.payment_host
25
25
  end
26
26
 
27
27
  def order_jwt_token