spree_vpago 2.1.6 → 2.1.7.pre.pre1
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 +4 -4
- data/Gemfile.lock +4 -4
- data/app/assets/javascripts/vpago/vpago_payments/payment_processing_listener.js +62 -13
- data/app/javascripts/vpago/vpago_payments/user_informers/firebase.js +9 -9
- data/app/jobs/vpago/payment_canceler_job.rb +11 -0
- data/app/jobs/vpago/payment_capturer_job.rb +1 -1
- data/app/jobs/vpago/payment_processor_job.rb +1 -1
- data/app/jobs/vpago/payment_voider_job.rb +11 -0
- data/app/models/spree/gateway/payway_v2.rb +9 -5
- data/app/models/spree/gateway/true_money.rb +6 -13
- data/app/models/spree/gateway/vattanac_mini_app.rb +5 -11
- data/app/models/spree/vpago_payment_source.rb +59 -5
- data/app/models/vpago/payment_decorator.rb +28 -3
- data/app/services/vpago/payment_processable.rb +12 -14
- data/app/services/vpago/payment_processor.rb +15 -7
- data/app/services/vpago/user_informers/firebase.rb +1 -1
- data/app/views/spree/vpago_payments/processing.html.erb +6 -6
- data/lib/spree_vpago/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87136a1f5a93bf4b0a1b465895df7235f9ad4aee4e26f5cdf17c5523609cb63e
|
|
4
|
+
data.tar.gz: 2291730a6de4b9100723fa803bb7b17c8e38d6ca70187c7de8ce5de9e7a4062d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c9f885d4cc09498c50d9d47971c4b1554c0ac8185e8ae4675ae716383c3997681776ff68c914be70060b0677a4bcbe74bb087af440a9c3951fe3ec1e5c0dcce
|
|
7
|
+
data.tar.gz: 40fa3cd8be136446101fc71183cbc464c32d87b7998f859eeb5643e6c9dca0117c797bbd79ce9be1c857b7bc3573c4413a586b431d8a2c7bba748cbde7aba3c1
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
spree_vpago (2.1.
|
|
4
|
+
spree_vpago (2.1.7.pre.pre1)
|
|
5
5
|
faraday
|
|
6
6
|
google-cloud-firestore
|
|
7
7
|
spree_api (>= 4.5)
|
|
@@ -242,7 +242,7 @@ GEM
|
|
|
242
242
|
gapic-common (~> 1.2)
|
|
243
243
|
google-cloud-errors (~> 1.0)
|
|
244
244
|
google-logging-utils (0.2.0)
|
|
245
|
-
google-protobuf (4.33.
|
|
245
|
+
google-protobuf (4.33.2)
|
|
246
246
|
bigdecimal
|
|
247
247
|
rake (>= 13)
|
|
248
248
|
googleapis-common-protos (1.9.0)
|
|
@@ -251,7 +251,7 @@ GEM
|
|
|
251
251
|
grpc (~> 1.41)
|
|
252
252
|
googleapis-common-protos-types (1.22.0)
|
|
253
253
|
google-protobuf (~> 4.26)
|
|
254
|
-
googleauth (1.
|
|
254
|
+
googleauth (1.16.0)
|
|
255
255
|
faraday (>= 1.0, < 3.a)
|
|
256
256
|
google-cloud-env (~> 2.2)
|
|
257
257
|
google-logging-utils (~> 0.1)
|
|
@@ -259,7 +259,7 @@ GEM
|
|
|
259
259
|
multi_json (~> 1.11)
|
|
260
260
|
os (>= 0.9, < 2.0)
|
|
261
261
|
signet (>= 0.16, < 2.a)
|
|
262
|
-
grpc (1.76.0
|
|
262
|
+
grpc (1.76.0)
|
|
263
263
|
google-protobuf (>= 3.25, < 5.0)
|
|
264
264
|
googleapis-common-protos-types (~> 1.0)
|
|
265
265
|
hashdiff (1.1.0)
|
|
@@ -6,7 +6,9 @@ window.initPaymentProcessingListener = async function (
|
|
|
6
6
|
function log(status, processing, reasonCode, reasonMessage) {
|
|
7
7
|
console.log(`Status: ${status}`);
|
|
8
8
|
console.log(`Reason Code: ${reasonCode}`);
|
|
9
|
-
console.log(
|
|
9
|
+
console.log(
|
|
10
|
+
`Processing: ${processing ? "Processing..." : "No more process."}`
|
|
11
|
+
);
|
|
10
12
|
console.log(`Reason Message: ${reasonMessage}`);
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -14,33 +16,80 @@ window.initPaymentProcessingListener = async function (
|
|
|
14
16
|
firebaseConfigs,
|
|
15
17
|
documentReferencePath,
|
|
16
18
|
|
|
17
|
-
onPaymentIsProcessing(
|
|
19
|
+
onPaymentIsProcessing(
|
|
20
|
+
orderState,
|
|
21
|
+
paymentState,
|
|
22
|
+
processing,
|
|
23
|
+
reasonCode,
|
|
24
|
+
reasonMessage
|
|
25
|
+
) {
|
|
18
26
|
log("Payment is processing", processing, reasonCode, reasonMessage);
|
|
19
27
|
},
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
onPaymentIsRetrying(
|
|
30
|
+
orderState,
|
|
31
|
+
paymentState,
|
|
32
|
+
processing,
|
|
33
|
+
reasonCode,
|
|
34
|
+
reasonMessage
|
|
35
|
+
) {
|
|
36
|
+
log("Payment is retrying", processing, reasonCode, reasonMessage);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
onOrderIsProcessing(
|
|
40
|
+
orderState,
|
|
41
|
+
paymentState,
|
|
42
|
+
processing,
|
|
43
|
+
reasonCode,
|
|
44
|
+
reasonMessage
|
|
45
|
+
) {
|
|
22
46
|
log("Order is processing", processing, reasonCode, reasonMessage);
|
|
23
47
|
},
|
|
24
48
|
|
|
25
|
-
onOrderIsCompleted(
|
|
49
|
+
onOrderIsCompleted(
|
|
50
|
+
orderState,
|
|
51
|
+
paymentState,
|
|
52
|
+
processing,
|
|
53
|
+
reasonCode,
|
|
54
|
+
reasonMessage
|
|
55
|
+
) {
|
|
26
56
|
log("Order is completed", processing, reasonCode, reasonMessage);
|
|
27
57
|
},
|
|
28
58
|
|
|
29
|
-
onOrderProcessFailed(
|
|
59
|
+
onOrderProcessFailed(
|
|
60
|
+
orderState,
|
|
61
|
+
paymentState,
|
|
62
|
+
processing,
|
|
63
|
+
reasonCode,
|
|
64
|
+
reasonMessage
|
|
65
|
+
) {
|
|
30
66
|
log("Order process failed", processing, reasonCode, reasonMessage);
|
|
31
67
|
},
|
|
32
68
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
69
|
+
onPaymentProcessFailed(
|
|
70
|
+
orderState,
|
|
71
|
+
paymentState,
|
|
72
|
+
processing,
|
|
73
|
+
reasonCode,
|
|
74
|
+
reasonMessage
|
|
75
|
+
) {
|
|
38
76
|
log("Payment process failed", processing, reasonCode, reasonMessage);
|
|
39
77
|
},
|
|
40
78
|
|
|
41
|
-
onCompleted(
|
|
42
|
-
|
|
79
|
+
onCompleted(
|
|
80
|
+
orderState,
|
|
81
|
+
paymentState,
|
|
82
|
+
processing,
|
|
83
|
+
reasonCode,
|
|
84
|
+
reasonMessage
|
|
85
|
+
) {
|
|
86
|
+
log(
|
|
87
|
+
"Completed — redirecting to success URL",
|
|
88
|
+
processing,
|
|
89
|
+
reasonCode,
|
|
90
|
+
reasonMessage
|
|
91
|
+
);
|
|
43
92
|
window.location.href = successUrl;
|
|
44
|
-
}
|
|
93
|
+
},
|
|
45
94
|
});
|
|
46
95
|
};
|
|
@@ -6,10 +6,10 @@ async function listenToProcessingState({
|
|
|
6
6
|
firebaseConfigs,
|
|
7
7
|
documentReferencePath,
|
|
8
8
|
onPaymentIsProcessing,
|
|
9
|
+
onPaymentIsRetrying,
|
|
9
10
|
onOrderIsProcessing,
|
|
10
11
|
onOrderIsCompleted,
|
|
11
12
|
onOrderProcessFailed,
|
|
12
|
-
onPaymentIsRefunded,
|
|
13
13
|
onPaymentProcessFailed,
|
|
14
14
|
onCompleted,
|
|
15
15
|
}) {
|
|
@@ -62,11 +62,11 @@ async function listenToProcessingState({
|
|
|
62
62
|
},
|
|
63
63
|
});
|
|
64
64
|
break;
|
|
65
|
-
case "
|
|
65
|
+
case "payment_is_retrying":
|
|
66
66
|
queueProcessor.queueStateChange({
|
|
67
67
|
minDelayInMs: 1500,
|
|
68
68
|
callback: async () => {
|
|
69
|
-
await
|
|
69
|
+
await onPaymentIsRetrying(
|
|
70
70
|
orderState,
|
|
71
71
|
paymentState,
|
|
72
72
|
processing,
|
|
@@ -76,11 +76,11 @@ async function listenToProcessingState({
|
|
|
76
76
|
},
|
|
77
77
|
});
|
|
78
78
|
break;
|
|
79
|
-
case "
|
|
79
|
+
case "order_is_processing":
|
|
80
80
|
queueProcessor.queueStateChange({
|
|
81
81
|
minDelayInMs: 1500,
|
|
82
82
|
callback: async () => {
|
|
83
|
-
await
|
|
83
|
+
await onOrderIsProcessing(
|
|
84
84
|
orderState,
|
|
85
85
|
paymentState,
|
|
86
86
|
processing,
|
|
@@ -90,11 +90,11 @@ async function listenToProcessingState({
|
|
|
90
90
|
},
|
|
91
91
|
});
|
|
92
92
|
break;
|
|
93
|
-
case "
|
|
93
|
+
case "order_is_completed":
|
|
94
94
|
queueProcessor.queueStateChange({
|
|
95
95
|
minDelayInMs: 1500,
|
|
96
96
|
callback: async () => {
|
|
97
|
-
await
|
|
97
|
+
await onOrderIsCompleted(
|
|
98
98
|
orderState,
|
|
99
99
|
paymentState,
|
|
100
100
|
processing,
|
|
@@ -104,11 +104,11 @@ async function listenToProcessingState({
|
|
|
104
104
|
},
|
|
105
105
|
});
|
|
106
106
|
break;
|
|
107
|
-
case "
|
|
107
|
+
case "order_process_failed":
|
|
108
108
|
queueProcessor.queueStateChange({
|
|
109
109
|
minDelayInMs: 1500,
|
|
110
110
|
callback: async () => {
|
|
111
|
-
await
|
|
111
|
+
await onOrderProcessFailed(
|
|
112
112
|
orderState,
|
|
113
113
|
paymentState,
|
|
114
114
|
processing,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Put :payment_processing at a higher priority in your project: config/sidekiq.yml
|
|
2
|
+
module Vpago
|
|
3
|
+
class PaymentCancelerJob < ::ApplicationUniqueJob
|
|
4
|
+
queue_as :payment_processing
|
|
5
|
+
|
|
6
|
+
def perform(payment_id)
|
|
7
|
+
payment = Spree::Payment.find(payment_id)
|
|
8
|
+
payment.cancel! if payment.actions.include?('cancel')
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -4,7 +4,7 @@ module Vpago
|
|
|
4
4
|
queue_as :payment_processing
|
|
5
5
|
|
|
6
6
|
def perform(options)
|
|
7
|
-
payment = Spree::Payment.find_by(number: options[:payment_number])
|
|
7
|
+
payment = Spree::Payment.find_by!(number: options[:payment_number])
|
|
8
8
|
Vpago::PaymentProcessor.new(payment: payment).call
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Put :payment_processing at a higher priority in your project: config/sidekiq.yml
|
|
2
|
+
module Vpago
|
|
3
|
+
class PaymentVoiderJob < ::ApplicationUniqueJob
|
|
4
|
+
queue_as :payment_processing
|
|
5
|
+
|
|
6
|
+
def perform(payment_id)
|
|
7
|
+
payment = Spree::Payment.find(payment_id)
|
|
8
|
+
payment.void_transaction! if payment.actions.include?('void')
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -58,10 +58,12 @@ module Spree
|
|
|
58
58
|
checker = check_transaction(payment)
|
|
59
59
|
payment.update(transaction_response: checker.json_response)
|
|
60
60
|
|
|
61
|
+
params = { check: { response: checker.json_response } }
|
|
62
|
+
|
|
61
63
|
if checker.success?
|
|
62
|
-
ActiveMerchant::Billing::Response.new(true, 'Payway Gateway: Authorized')
|
|
64
|
+
ActiveMerchant::Billing::Response.new(true, 'Payway Gateway: Authorized', params)
|
|
63
65
|
else
|
|
64
|
-
ActiveMerchant::Billing::Response.new(false, 'Payway Gateway: Authorization Failed')
|
|
66
|
+
ActiveMerchant::Billing::Response.new(false, 'Payway Gateway: Authorization Failed', params)
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
69
|
|
|
@@ -140,14 +142,16 @@ module Spree
|
|
|
140
142
|
canceler = Vpago::PaywayV2::PreAuthCanceler.new(payment)
|
|
141
143
|
canceler.call
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
data = { request: canceler.request_data, response: canceler.json_response }
|
|
146
|
+
[canceler.success?, data]
|
|
144
147
|
end
|
|
145
148
|
|
|
146
149
|
def complete_pre_auth(payment)
|
|
147
150
|
completer = Vpago::PaywayV2::PreAuthCompleter.new(payment)
|
|
148
|
-
|
|
149
151
|
completer.call
|
|
150
|
-
|
|
152
|
+
|
|
153
|
+
data = { request: completer.request_data, response: completer.json_response }
|
|
154
|
+
[completer.success?, data]
|
|
151
155
|
end
|
|
152
156
|
|
|
153
157
|
def confirm_payouts(payment)
|
|
@@ -40,17 +40,18 @@ module Spree
|
|
|
40
40
|
params[:payment_response] = payment.transaction_response
|
|
41
41
|
|
|
42
42
|
if success
|
|
43
|
-
ActiveMerchant::Billing::Response.new(true, '
|
|
43
|
+
ActiveMerchant::Billing::Response.new(true, 'True money Gateway: Purchased', params)
|
|
44
44
|
else
|
|
45
|
-
ActiveMerchant::Billing::Response.new(false, '
|
|
45
|
+
ActiveMerchant::Billing::Response.new(false, 'True money Gateway: Purchasing Failed', params)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# override
|
|
50
|
-
def void(_response_code,
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
def void(_response_code, _gateway_options)
|
|
51
|
+
ActiveMerchant::Billing::Response.new(true, 'True money Gateway: Payment has been voided.')
|
|
52
|
+
end
|
|
53
53
|
|
|
54
|
+
def cancel(_response_code, payment)
|
|
54
55
|
if payment.true_money_payment?
|
|
55
56
|
params = {}
|
|
56
57
|
success, params[:refund_response] = true_money_refund(payment)
|
|
@@ -72,14 +73,6 @@ module Spree
|
|
|
72
73
|
[refund_issuer.success?, refund_issuer.parsed_response]
|
|
73
74
|
end
|
|
74
75
|
|
|
75
|
-
def cancel(_response_code, _payment)
|
|
76
|
-
# we can use this to send request to payment gateway api to cancel the payment ( void )
|
|
77
|
-
# currently True money does not support to cancel the gateway
|
|
78
|
-
|
|
79
|
-
# in our case don't do anything
|
|
80
|
-
ActiveMerchant::Billing::Response.new(true, '')
|
|
81
|
-
end
|
|
82
|
-
|
|
83
76
|
private
|
|
84
77
|
|
|
85
78
|
def check_transaction(payment)
|
|
@@ -34,10 +34,12 @@ module Spree
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# override
|
|
37
|
-
def void(_response_code,
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
def void(_response_code, _gateway_options)
|
|
38
|
+
ActiveMerchant::Billing::Response.new(true, 'Vattanac order has been voided.')
|
|
39
|
+
end
|
|
40
40
|
|
|
41
|
+
# override
|
|
42
|
+
def cancel(_response_code, payment)
|
|
41
43
|
if payment.vattanac_mini_app_payment?
|
|
42
44
|
params = {}
|
|
43
45
|
success, params[:refund_response] = vattanac_mini_app_refund(payment)
|
|
@@ -58,13 +60,5 @@ module Spree
|
|
|
58
60
|
|
|
59
61
|
[refund_issuer.success?, refund_issuer.response]
|
|
60
62
|
end
|
|
61
|
-
|
|
62
|
-
def cancel(_response_code, _payment)
|
|
63
|
-
# we can use this to send request to payment gateway api to cancel the payment ( void )
|
|
64
|
-
# currently Payway does not support to cancel the gateway
|
|
65
|
-
|
|
66
|
-
# in our case don't do anything
|
|
67
|
-
ActiveMerchant::Billing::Response.new(true, 'Vattanac order has been cancelled.')
|
|
68
|
-
end
|
|
69
63
|
end
|
|
70
64
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
class Spree::VpagoPaymentSource < Spree::Base
|
|
2
|
+
# deprecated preferences from older versions kept for data integrity
|
|
2
3
|
preference :wing_response, :hash
|
|
3
4
|
preference :acleda_response, :hash
|
|
4
5
|
preference :payway_v2_response, :hash
|
|
@@ -11,11 +12,64 @@ class Spree::VpagoPaymentSource < Spree::Base
|
|
|
11
12
|
|
|
12
13
|
# validates :updated_reason, presence: true, on: :update
|
|
13
14
|
|
|
15
|
+
# Define possible available actions for vpago payments.
|
|
16
|
+
# Then Spree::Payment#actions will select only those where can_<action>? returns true.
|
|
17
|
+
#
|
|
18
|
+
# These actions are triggered via: payment.send("#{action}!")
|
|
19
|
+
# - [void] can be triggered void_transaction (the alias by spree is to avoid conflict with the transition method name)
|
|
20
|
+
# - [open_checkout] is a view action and is handled manually in gems/spree_vpago/app/overrides/spree/admin/payments/_list/actions.html.erb.deface
|
|
21
|
+
# - [process] [capture] [void] [cancel] are built-in methods in the Spree::Payment model.
|
|
22
|
+
#
|
|
23
|
+
# Usages:
|
|
24
|
+
# See fire request method in gems/spree_backend/app/controllers/spree/admin/payments_controller.rb
|
|
25
|
+
# See jobs in gems/spree_vpago/app/jobs/vpago which are triggered by processor jobs.
|
|
26
|
+
#
|
|
27
|
+
# override
|
|
14
28
|
def actions
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
%w[open_checkout process capture void cancel]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def can_open_checkout?(payment)
|
|
33
|
+
payment.checkout?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def can_process?(payment)
|
|
37
|
+
return false if payment.completed? || payment.pending?
|
|
38
|
+
|
|
39
|
+
payment.processing? || payment.checkout? || payment.send(:has_invalid_state?)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The flow is as follows: payment authorized -> order completed -> capture the amount -> end.
|
|
43
|
+
def can_capture?(payment)
|
|
44
|
+
# Capture action should be triggered when the order is completed/secured for the user.
|
|
45
|
+
# Otherwise, we don't need to capture the payment as the order is not completed.
|
|
46
|
+
return false unless payment.order.completed?
|
|
47
|
+
|
|
48
|
+
# Most likely the payment is already captured, so we don't need to capture it again.
|
|
49
|
+
return false if payment.completed?
|
|
50
|
+
|
|
51
|
+
payment.pending?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Spree has different refund logic, called credit. We use cancel as refund for now, but only for Vattanac/True Money.
|
|
55
|
+
# In the future, we can adopt Spree's credit logic instead of cancel if needed.
|
|
56
|
+
#
|
|
57
|
+
# The flow is as follows: payment paid -> order fails -> cancel (refund) -> end.
|
|
58
|
+
def can_cancel?(payment)
|
|
59
|
+
return false unless payment.vattanac_mini_app_payment? || payment.true_money_payment?
|
|
60
|
+
|
|
61
|
+
# Cancel action is triggered when an order fails to complete.
|
|
62
|
+
# So if the order somehow completes, we do not need to cancel the payment.
|
|
63
|
+
return false if payment.order.completed?
|
|
64
|
+
|
|
65
|
+
# Most likely the payment is already canceled/voided, so we don't need to cancel it again.
|
|
66
|
+
return false if payment.void?
|
|
67
|
+
|
|
68
|
+
payment.completed?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The flow is as follows: payment authorized -> order failed -> void (release held payment) -> end.
|
|
72
|
+
def can_void?(payment)
|
|
73
|
+
payment.pending?
|
|
20
74
|
end
|
|
21
75
|
end
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Note for process! & capture! method:
|
|
2
|
+
#
|
|
3
|
+
# Original process! & capture! calls started_processing! to move state
|
|
4
|
+
# [:checkout, :pending, :completed, :processing] → :processing.
|
|
5
|
+
#
|
|
6
|
+
# When gateway call fails, handle_response calls send(:failure) which transitions
|
|
7
|
+
# the payment to :failed state. This happens BEFORE the GatewayError is raised,
|
|
8
|
+
# so when Sidekiq retries the job, the payment is already in :failed state.
|
|
9
|
+
#
|
|
10
|
+
# The :started_processing event doesn't allow transition from :failed → :processing,
|
|
11
|
+
# so we need to reset the state back to :checkout first via reset_for_retry!
|
|
12
|
+
#
|
|
13
|
+
# This allows process! or capture! to be retried by Sidekiq or by admin via /fire after connection errors
|
|
14
|
+
# or other transient gateway failures.
|
|
1
15
|
module Vpago
|
|
2
16
|
module PaymentDecorator
|
|
3
17
|
def self.prepended(base)
|
|
@@ -11,12 +25,23 @@ module Vpago
|
|
|
11
25
|
:process_payment_url,
|
|
12
26
|
:success_deeplink_url,
|
|
13
27
|
to: :url_constructor
|
|
28
|
+
|
|
29
|
+
# Add state machine event for payment retry
|
|
30
|
+
base.state_machine.event :reset_for_retry do
|
|
31
|
+
transition from: %i[failed], to: :checkout
|
|
32
|
+
end
|
|
14
33
|
end
|
|
15
34
|
|
|
16
|
-
# override
|
|
17
|
-
# to give payment another chance to re-process, even if it failed.
|
|
35
|
+
# override
|
|
18
36
|
def process!
|
|
19
|
-
|
|
37
|
+
reset_for_retry! if failed?
|
|
38
|
+
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# override
|
|
43
|
+
def capture!(amount = nil)
|
|
44
|
+
reset_for_retry! if failed?
|
|
20
45
|
|
|
21
46
|
super
|
|
22
47
|
end
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
module Vpago
|
|
2
2
|
module PaymentProcessable
|
|
3
|
-
def
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
def enqueue_capture_payment_if_available!
|
|
4
|
+
Vpago::PaymentCapturerJob.perform_later(@payment.id) if available_actions.include?('capture')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def enqueue_void_or_cancel_payment_if_available!
|
|
8
|
+
if available_actions.include?('void')
|
|
9
|
+
Vpago::PaymentVoiderJob.perform_later(@payment.id)
|
|
10
|
+
elsif available_actions.include?('cancel')
|
|
11
|
+
Vpago::PaymentCancelerJob.perform_later(@payment.id)
|
|
11
12
|
end
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
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? || @payment.vattanac_mini_app_payment? || @payment.true_money_payment?
|
|
15
|
+
# To check available actions, see app/models/spree/vpago_payment_source.rb
|
|
16
|
+
def available_actions
|
|
17
|
+
@payment.actions
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
def extract_completer_failure_reason_code(error)
|
|
@@ -19,10 +19,15 @@ module Vpago
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def call!
|
|
22
|
-
process_payment!
|
|
22
|
+
process_payment! if available_actions.include?('process')
|
|
23
23
|
process_order! if @payment.completed? || @payment.pending?
|
|
24
24
|
rescue Spree::Core::GatewayError => e
|
|
25
|
-
|
|
25
|
+
if e.message == Spree.t(:unable_to_connect_to_gateway)
|
|
26
|
+
user_informer.payment_is_retrying(processing: true)
|
|
27
|
+
|
|
28
|
+
# Re-raise to trigger Sidekiq retry mechanism.
|
|
29
|
+
raise
|
|
30
|
+
end
|
|
26
31
|
|
|
27
32
|
handle_payment_failure(:gateway_error, e.message)
|
|
28
33
|
rescue StateMachines::InvalidTransition => e
|
|
@@ -39,6 +44,9 @@ module Vpago
|
|
|
39
44
|
end
|
|
40
45
|
end
|
|
41
46
|
|
|
47
|
+
# If this job retries, we run this method/completer again even if the order is already completed.
|
|
48
|
+
# This ensures flow consistency. The completer will return success & this method will call handle_order_process_completed,
|
|
49
|
+
# which will enqueue a capture payment if available.
|
|
42
50
|
def process_order!
|
|
43
51
|
log_process('process_order!') do
|
|
44
52
|
user_informer.order_is_processing(processing: true)
|
|
@@ -55,7 +63,7 @@ module Vpago
|
|
|
55
63
|
|
|
56
64
|
def handle_order_process_completed
|
|
57
65
|
log_process('handle_order_process_completed') do
|
|
58
|
-
|
|
66
|
+
enqueue_capture_payment_if_available!
|
|
59
67
|
user_informer.order_is_completed(processing: false)
|
|
60
68
|
end
|
|
61
69
|
end
|
|
@@ -63,12 +71,12 @@ module Vpago
|
|
|
63
71
|
def handle_order_process_failure(reason_code, reason_message = nil)
|
|
64
72
|
log_process('handle_order_process_failure', reason_code, reason_message) do
|
|
65
73
|
user_informer.order_process_failed(
|
|
66
|
-
processing:
|
|
74
|
+
processing: false,
|
|
67
75
|
reason_code: reason_code,
|
|
68
76
|
reason_message: reason_message
|
|
69
77
|
)
|
|
70
78
|
|
|
71
|
-
|
|
79
|
+
enqueue_void_or_cancel_payment_if_available!
|
|
72
80
|
failure(reason_message)
|
|
73
81
|
end
|
|
74
82
|
end
|
|
@@ -76,12 +84,12 @@ module Vpago
|
|
|
76
84
|
def handle_payment_failure(reason_code, reason_message)
|
|
77
85
|
log_process('handle_payment_failure', reason_code, reason_message) do
|
|
78
86
|
user_informer.payment_process_failed(
|
|
79
|
-
processing:
|
|
87
|
+
processing: false,
|
|
80
88
|
reason_code: reason_code,
|
|
81
89
|
reason_message: reason_message
|
|
82
90
|
)
|
|
83
91
|
|
|
84
|
-
|
|
92
|
+
enqueue_void_or_cancel_payment_if_available!
|
|
85
93
|
failure(reason_message)
|
|
86
94
|
end
|
|
87
95
|
end
|
|
@@ -10,10 +10,10 @@ module Vpago
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def payment_is_processing(processing:) = notify(:payment_is_processing, processing)
|
|
13
|
+
def payment_is_retrying(processing:) = notify(:payment_is_retrying, processing)
|
|
13
14
|
def order_is_processing(processing:) = notify(:order_is_processing, processing)
|
|
14
15
|
def order_is_completed(processing:) = notify(:order_is_completed, processing)
|
|
15
16
|
def order_process_failed(processing:, reason_code:, reason_message: nil) = notify(:order_process_failed, processing, reason_code, reason_message)
|
|
16
|
-
def payment_is_refunded(processing:, reason_code:, reason_message: nil) = notify(:payment_is_refunded, processing, reason_code, reason_message)
|
|
17
17
|
def payment_process_failed(processing:, reason_code:, reason_message: nil) = notify(:payment_process_failed, processing, reason_code, reason_message)
|
|
18
18
|
|
|
19
19
|
def notify(message_code, processing, reason_code = nil, reason_message = nil)
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
document.getElementById("processing").innerText = processing ? "Processing..." : "No more process.";
|
|
24
24
|
document.getElementById("reason_message").innerText = reasonMessage;
|
|
25
25
|
},
|
|
26
|
+
onPaymentIsRetrying: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
|
27
|
+
document.getElementById("status").innerText = "Payment is retrying";
|
|
28
|
+
document.getElementById("reason_code").innerText = reasonCode;
|
|
29
|
+
document.getElementById("processing").innerText = processing ? "Retrying..." : "No more process.";
|
|
30
|
+
document.getElementById("reason_message").innerText = reasonMessage;
|
|
31
|
+
},
|
|
26
32
|
onOrderIsProcessing: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
|
27
33
|
document.getElementById("status").innerText = "Order is processing";
|
|
28
34
|
document.getElementById("reason_code").innerText = reasonCode;
|
|
@@ -41,12 +47,6 @@
|
|
|
41
47
|
document.getElementById("processing").innerText = processing ? "Processing..." : "No more process.";
|
|
42
48
|
document.getElementById("reason_message").innerText = reasonMessage;
|
|
43
49
|
},
|
|
44
|
-
onPaymentIsRefunded: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
|
45
|
-
document.getElementById("status").innerText = "Payment is refunded";
|
|
46
|
-
document.getElementById("reason_code").innerText = reasonCode;
|
|
47
|
-
document.getElementById("processing").innerText = processing ? "Processing..." : "No more process.";
|
|
48
|
-
document.getElementById("reason_message").innerText = reasonMessage
|
|
49
|
-
},
|
|
50
50
|
onPaymentProcessFailed: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
|
51
51
|
document.getElementById("status").innerText = "Payment process failed";
|
|
52
52
|
document.getElementById("reason_code").innerText = reasonCode;
|
data/lib/spree_vpago/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_vpago
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.7.pre.pre1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- You
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-12-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -184,8 +184,10 @@ files:
|
|
|
184
184
|
- app/javascripts/queue_processor.test.js
|
|
185
185
|
- app/javascripts/vpago/vpago_payments/user_informers/firebase.js
|
|
186
186
|
- app/jobs/application_unique_job.rb
|
|
187
|
+
- app/jobs/vpago/payment_canceler_job.rb
|
|
187
188
|
- app/jobs/vpago/payment_capturer_job.rb
|
|
188
189
|
- app/jobs/vpago/payment_processor_job.rb
|
|
190
|
+
- app/jobs/vpago/payment_voider_job.rb
|
|
189
191
|
- app/models/.gitkeep
|
|
190
192
|
- app/models/concerns/vpago/payoutable.rb
|
|
191
193
|
- app/models/spree/gateway/acleda.rb
|
|
@@ -434,9 +436,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
434
436
|
version: 3.2.0
|
|
435
437
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
436
438
|
requirements:
|
|
437
|
-
- - "
|
|
439
|
+
- - ">"
|
|
438
440
|
- !ruby/object:Gem::Version
|
|
439
|
-
version:
|
|
441
|
+
version: 1.3.1
|
|
440
442
|
requirements:
|
|
441
443
|
- none
|
|
442
444
|
rubygems_version: 3.4.1
|