spree_vpago 2.3.2 → 2.3.5.pre.pre.pre.1
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 +1 -1
- data/app/controllers/spree/vpago_payments_controller.rb +61 -29
- data/app/helpers/vpago/admin/base_helper_decorator.rb +7 -0
- data/app/jobs/vpago/payment_processor_job.rb +16 -1
- data/app/models/spree/gateway/acleda_v2.rb +93 -0
- data/app/models/vpago/payment_method_decorator.rb +6 -0
- data/app/services/vpago/payment_finder.rb +10 -2
- data/app/services/vpago/payment_processable.rb +12 -11
- data/app/views/spree/admin/payments/source_views/_acleda_v2.html.erb +1 -0
- data/app/views/spree/checkout/payment/_acleda_v2_checkout_form.html.erb +12 -0
- data/app/views/spree/vpago_payments/forms/spree/gateway/_acleda_v2.html.erb +47 -0
- data/lib/spree_vpago/engine.rb +1 -0
- data/lib/spree_vpago/version.rb +1 -1
- data/lib/vpago/acleda_v2/base.rb +149 -0
- data/lib/vpago/acleda_v2/checkout.rb +124 -0
- data/lib/vpago/acleda_v2/transaction_status.rb +80 -0
- data/lib/vpago_logger.rb +59 -0
- data/node_modules/.yarn-integrity +1 -1
- metadata +12 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c0262a0bad21d6ed9d53d73ba6805f307e6b9f82db837ac96fe4f3c4725ae49b
|
|
4
|
+
data.tar.gz: 71f83ae18d1c306419bbef87f291185e1835a653fde1d1f0a09e1c783cf6f54c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ffc03cf77c82129ee5cf13ca1d3abb1646a9d39878ea647e1fd2451c30c027a885d8e8cb99fe45fabef2d7308c73f70c11b654ef9043b59814ef4737a983392c
|
|
7
|
+
data.tar.gz: cde29375e88d3428cd5854b47bb597fac4697eaf60550dfb74573dd0d5edff55624ea0df78659328ba40343548c39b60d4284fe7c6bfcd495a24ba405b82fcea
|
data/Gemfile.lock
CHANGED
|
@@ -17,7 +17,7 @@ module Spree
|
|
|
17
17
|
|
|
18
18
|
@order = @payment.order
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
VpagoLogger.log(label: 'Spree::VpagoPaymentsController#checkout', data: vpago_log_context)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# GET
|
|
@@ -27,7 +27,7 @@ module Spree
|
|
|
27
27
|
|
|
28
28
|
@order = @payment.order
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
VpagoLogger.log(label: 'Spree::VpagoPaymentsController#processing', data: vpago_log_context)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# GET
|
|
@@ -38,7 +38,7 @@ module Spree
|
|
|
38
38
|
@order = @payment.order
|
|
39
39
|
raise CanCan::AccessDenied unless @order.completed?
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
VpagoLogger.log(label: 'Spree::VpagoPaymentsController#success', data: vpago_log_context)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# GET
|
|
@@ -49,20 +49,21 @@ module Spree
|
|
|
49
49
|
return render json: { status: :success }, status: :ok if @payment.completed?
|
|
50
50
|
return render json: { status: :failed }, status: :ok if @payment.failed?
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
unless @payment.payment_method.support_check_transaction_api?
|
|
53
|
+
VpagoLogger.log(label: 'Spree::VpagoPaymentsController#check_transaction unsupported', data: vpago_log_context)
|
|
54
|
+
return render json: { status: :pending }, status: :ok
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
checker = VpagoLogger.log(
|
|
58
|
+
label: 'Spree::VpagoPaymentsController#check_transaction',
|
|
59
|
+
data: vpago_log_context
|
|
60
|
+
) { @payment.payment_method.check_transaction(@payment) }
|
|
61
|
+
|
|
62
|
+
if checker.success?
|
|
63
|
+
render json: { status: :success }, status: :ok
|
|
64
|
+
elsif checker.try(:failed?) == true
|
|
65
|
+
render json: { status: :failed }, status: :ok
|
|
64
66
|
else
|
|
65
|
-
Rails.logger.info("[Vpago][#{@payment.number}] Payment method does not support check transaction API")
|
|
66
67
|
render json: { status: :pending }, status: :ok
|
|
67
68
|
end
|
|
68
69
|
end
|
|
@@ -75,30 +76,35 @@ module Spree
|
|
|
75
76
|
@payment = Vpago::PaymentFinder.new(return_params).find_and_verify
|
|
76
77
|
|
|
77
78
|
if @payment.nil?
|
|
78
|
-
|
|
79
|
+
VpagoLogger.error(
|
|
80
|
+
label: 'Spree::VpagoPaymentsController#process_payment payment_not_found',
|
|
81
|
+
data: vpago_log_context(params: return_params)
|
|
82
|
+
)
|
|
79
83
|
return render_not_found
|
|
80
84
|
end
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
VpagoLogger.log(label: 'Spree::VpagoPaymentsController#process_payment payment_found', data: vpago_log_context)
|
|
83
87
|
|
|
84
88
|
# for ABA reviewing mode, we can disable pushback from bank, and only process it from our app UI instead.
|
|
85
89
|
# This will give ABA team to know that we don't rely on just pushback and have fallback to process payment.
|
|
86
90
|
if @payment.payment_method.type_payway_v2? && @payment.payment_method.reviewing_mode? && request_from_external_server?
|
|
87
|
-
|
|
91
|
+
VpagoLogger.log(label: 'Spree::VpagoPaymentsController#process_payment skipped_reviewing_mode', data: vpago_log_context)
|
|
88
92
|
return render json: { status: :ok }, status: :ok
|
|
89
93
|
end
|
|
90
94
|
|
|
91
95
|
unless @payment.order.paid?
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
96
|
+
VpagoLogger.log(
|
|
97
|
+
label: 'Spree::VpagoPaymentsController#process_payment enqueue_payment_processor_job',
|
|
98
|
+
data: vpago_log_context
|
|
99
|
+
) { Vpago::PaymentProcessorJob.perform_later(payment_number: @payment.number) }
|
|
96
100
|
end
|
|
97
101
|
|
|
98
|
-
Rails.logger.info("[Vpago][#{@payment.number}] Successfully enqueued payment processor job for payment #{@payment.number}")
|
|
99
102
|
render json: { status: :ok }, status: :ok
|
|
100
103
|
rescue StandardError => e
|
|
101
|
-
|
|
104
|
+
VpagoLogger.error(
|
|
105
|
+
label: 'Spree::VpagoPaymentsController#process_payment failed',
|
|
106
|
+
data: vpago_log_context(error_class: e.class.name, error_message: e.message, backtrace: e.backtrace&.first(5))
|
|
107
|
+
)
|
|
102
108
|
render json: { status: :internal_server_error, message: 'Failed to enqueue payment processor job' }, status: :internal_server_error
|
|
103
109
|
end
|
|
104
110
|
|
|
@@ -107,14 +113,28 @@ module Spree
|
|
|
107
113
|
return render json: { status: { code: '000001', message: 'success' }, data: nil }, status: :ok if request.method != 'POST'
|
|
108
114
|
|
|
109
115
|
@payment = Spree::Payment.find_by(number: params.dig(:data, :external_ref_id))
|
|
110
|
-
return render_not_found unless @payment
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
if @payment.nil?
|
|
118
|
+
VpagoLogger.error(
|
|
119
|
+
label: 'Spree::VpagoPaymentsController#true_money_process_payment payment_not_found',
|
|
120
|
+
data: vpago_log_context(external_ref_id: params.dig(:data, :external_ref_id))
|
|
121
|
+
)
|
|
122
|
+
return render_not_found
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
unless @payment.order.paid?
|
|
126
|
+
VpagoLogger.log(
|
|
127
|
+
label: 'Spree::VpagoPaymentsController#true_money_process_payment enqueue_payment_processor_job',
|
|
128
|
+
data: vpago_log_context
|
|
129
|
+
) { Vpago::PaymentProcessorJob.perform_later(payment_number: @payment.number) }
|
|
130
|
+
end
|
|
114
131
|
|
|
115
132
|
render json: { status: { code: '000001', message: 'success' }, data: nil }, status: :ok
|
|
116
133
|
rescue StandardError => e
|
|
117
|
-
|
|
134
|
+
VpagoLogger.error(
|
|
135
|
+
label: 'Spree::VpagoPaymentsController#true_money_process_payment failed',
|
|
136
|
+
data: vpago_log_context(error_class: e.class.name, error_message: e.message, backtrace: e.backtrace&.first(5))
|
|
137
|
+
)
|
|
118
138
|
render json: { status: :internal_server_error, message: 'Failed to enqueue payment processor job' }, status: :internal_server_error
|
|
119
139
|
end
|
|
120
140
|
|
|
@@ -144,5 +164,17 @@ module Spree
|
|
|
144
164
|
def request_from_external_server?
|
|
145
165
|
params[:internal_client].blank? || params[:internal_client] == 'false'
|
|
146
166
|
end
|
|
167
|
+
|
|
168
|
+
def vpago_log_context(extra = {})
|
|
169
|
+
{
|
|
170
|
+
timestamp: Time.current.utc.iso8601(3),
|
|
171
|
+
remote_ip: request.remote_ip,
|
|
172
|
+
payment_number: @payment&.number,
|
|
173
|
+
order_number: @payment&.order&.number,
|
|
174
|
+
payment_method_type: @payment&.payment_method&.type,
|
|
175
|
+
payment_method_name: @payment&.payment_method&.name,
|
|
176
|
+
request_id: request.request_id
|
|
177
|
+
}.merge(extra)
|
|
178
|
+
end
|
|
147
179
|
end
|
|
148
180
|
end
|
|
@@ -12,6 +12,7 @@ module Vpago
|
|
|
12
12
|
payway_abapay
|
|
13
13
|
payway_alipay
|
|
14
14
|
payway_wechat
|
|
15
|
+
acleda_v2
|
|
15
16
|
acleda
|
|
16
17
|
acleda_khqr
|
|
17
18
|
acleda_cards
|
|
@@ -28,6 +29,10 @@ module Vpago
|
|
|
28
29
|
%w[khqr deeplink all]
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
def available_acleda_v2_modes
|
|
33
|
+
Spree::Gateway::AcledaV2::MODES
|
|
34
|
+
end
|
|
35
|
+
|
|
31
36
|
def preference_field_for(form, field, options)
|
|
32
37
|
case field
|
|
33
38
|
when 'preferred_acleda_type'
|
|
@@ -45,6 +50,8 @@ module Vpago
|
|
|
45
50
|
return form.select(:preferred_abapay_khqr_deeplink_option, Spree::Gateway::PaywayV2::ABAPAY_KHQR_DEEPLINK_OPTIONS, {}, class: 'fullwidth select2')
|
|
46
51
|
when 'preferred_vattanac_payment_option'
|
|
47
52
|
return form.select(:preferred_vattanac_payment_option, available_vattanac_payment_options, {}, class: 'fullwidth select2')
|
|
53
|
+
when 'preferred_acleda_v2_mode'
|
|
54
|
+
return form.select(:preferred_acleda_v2_mode, available_acleda_v2_modes, {}, class: 'fullwidth select2')
|
|
48
55
|
end
|
|
49
56
|
super
|
|
50
57
|
end
|
|
@@ -5,7 +5,22 @@ module Vpago
|
|
|
5
5
|
|
|
6
6
|
def perform(options)
|
|
7
7
|
payment = Spree::Payment.find_by!(number: options[:payment_number])
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
VpagoLogger.log(
|
|
10
|
+
label: 'Vpago::PaymentProcessorJob#perform',
|
|
11
|
+
data: {
|
|
12
|
+
payment_number: payment.number,
|
|
13
|
+
order_number: payment.order.number,
|
|
14
|
+
payment_method_type: payment.payment_method.type,
|
|
15
|
+
payment_method_name: payment.payment_method.name
|
|
16
|
+
}
|
|
17
|
+
) { Vpago::PaymentProcessor.new(payment: payment).call }
|
|
18
|
+
rescue StandardError => e
|
|
19
|
+
VpagoLogger.error(
|
|
20
|
+
label: 'Vpago::PaymentProcessorJob#perform failed',
|
|
21
|
+
data: { payment_number: options[:payment_number], error_class: e.class.name, error_message: e.message, backtrace: e.backtrace&.first(5) }
|
|
22
|
+
)
|
|
23
|
+
raise
|
|
9
24
|
end
|
|
10
25
|
end
|
|
11
26
|
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class Gateway::AcledaV2 < PaymentMethod
|
|
3
|
+
MODES = %w[khqr card deeplink].freeze
|
|
4
|
+
|
|
5
|
+
# BOOKMEPLUS — sent as the "merchantName" field in getTxnStatus requests
|
|
6
|
+
preference :merchant_name, :string
|
|
7
|
+
# e.g. https://epaymentuat.acledabank.com.kh/BOOKMEPLUS/XPAYConnectorServiceInterfaceImplV2/XPAYConnectorServiceInterfaceImplV2RS
|
|
8
|
+
preference :xpay_service_base_url, :string
|
|
9
|
+
# e.g. https://epaymentuat.acledabank.com.kh/BOOKMEPLUS/paymentPage.jsp
|
|
10
|
+
preference :payment_page_url, :string
|
|
11
|
+
preference :merchant_id, :string
|
|
12
|
+
preference :login_id, :string
|
|
13
|
+
preference :password, :string
|
|
14
|
+
preference :secret, :string # HMAC-SHA512 key
|
|
15
|
+
preference :payment_expiry_time_in_mn, :integer, default: 5
|
|
16
|
+
preference :acleda_v2_mode, :string, default: 'khqr'
|
|
17
|
+
|
|
18
|
+
validates :preferred_acleda_v2_mode, inclusion: { in: MODES }
|
|
19
|
+
|
|
20
|
+
def khqr?
|
|
21
|
+
preferred_acleda_v2_mode == 'khqr'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def card?
|
|
25
|
+
preferred_acleda_v2_mode == 'card'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def deeplink?
|
|
29
|
+
preferred_acleda_v2_mode == 'deeplink'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# KHQR (Scan with Bank under Bakong) = "0"; Credit/debit card = "1"
|
|
33
|
+
def payment_card_value
|
|
34
|
+
card? ? '1' : '0'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# override: partial to render in admin
|
|
38
|
+
def method_type
|
|
39
|
+
'acleda_v2'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# override
|
|
43
|
+
def payment_source_class
|
|
44
|
+
Spree::VpagoPaymentSource
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# override
|
|
48
|
+
def payment_profiles_supported?
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# override
|
|
53
|
+
def available_for_order?(_order)
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# override: process! -> purchase! immediately (no pre-auth for ACLEDA V2)
|
|
58
|
+
def auto_capture?
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# override
|
|
63
|
+
# mirror PayWay V2: gateway_options[:order_id] == "<order_number>-<payment_number>"
|
|
64
|
+
def purchase(_amount, _source, gateway_options = {})
|
|
65
|
+
_, payment_number = gateway_options[:order_id].split('-')
|
|
66
|
+
payment = Spree::Payment.find_by(number: payment_number)
|
|
67
|
+
|
|
68
|
+
checker = check_transaction(payment)
|
|
69
|
+
payment.update(transaction_response: checker.json_response)
|
|
70
|
+
|
|
71
|
+
params = { check: { response: checker.json_response } }
|
|
72
|
+
|
|
73
|
+
if checker.success?
|
|
74
|
+
ActiveMerchant::Billing::Response.new(true, 'Acleda V2: Purchased', params)
|
|
75
|
+
else
|
|
76
|
+
ActiveMerchant::Billing::Response.new(false, 'Acleda V2: Purchasing Failed', params)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Defining check_transaction makes support_check_transaction_api? true,
|
|
81
|
+
# enabling the /vpago_payments/check_transaction poller.
|
|
82
|
+
def check_transaction(payment)
|
|
83
|
+
checker = Vpago::AcledaV2::TransactionStatus.new(payment)
|
|
84
|
+
checker.call
|
|
85
|
+
checker
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# override
|
|
89
|
+
def cancel(_response_code, _payment = nil)
|
|
90
|
+
ActiveMerchant::Billing::Response.new(true, 'Acleda V2: Payment has been canceled.')
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -4,6 +4,7 @@ module Vpago
|
|
|
4
4
|
TYPE_PAYWAY_V2 = 'Spree::Gateway::PaywayV2'.freeze
|
|
5
5
|
TYPE_WINGSDK = 'Spree::Gateway::WingSdk'.freeze
|
|
6
6
|
TYPE_ACLEDA = 'Spree::Gateway::Acleda'.freeze
|
|
7
|
+
TYPE_ACLEDA_V2 = 'Spree::Gateway::AcledaV2'.freeze
|
|
7
8
|
TYPE_ACLEDA_MOBILE = 'Spree::Gateway::AcledaMobile'.freeze
|
|
8
9
|
TYPE_TRUE_MONEY = 'Spree::Gateway::TrueMoney'.freeze
|
|
9
10
|
TYPE_VATTANAC = 'Spree::Gateway::Vattanac'.freeze
|
|
@@ -21,6 +22,7 @@ module Vpago
|
|
|
21
22
|
Spree::PaymentMethod::TYPE_PAYWAY,
|
|
22
23
|
Spree::PaymentMethod::TYPE_WINGSDK,
|
|
23
24
|
Spree::PaymentMethod::TYPE_ACLEDA,
|
|
25
|
+
Spree::PaymentMethod::TYPE_ACLEDA_V2,
|
|
24
26
|
Spree::PaymentMethod::TYPE_ACLEDA_MOBILE,
|
|
25
27
|
Spree::PaymentMethod::TYPE_VATTANAC,
|
|
26
28
|
Spree::PaymentMethod::TYPE_VATTANAC_MINI_APP,
|
|
@@ -89,6 +91,10 @@ module Vpago
|
|
|
89
91
|
type == Spree::PaymentMethod::TYPE_ACLEDA
|
|
90
92
|
end
|
|
91
93
|
|
|
94
|
+
def type_acleda_v2?
|
|
95
|
+
type == Spree::PaymentMethod::TYPE_ACLEDA_V2
|
|
96
|
+
end
|
|
97
|
+
|
|
92
98
|
def type_payway?
|
|
93
99
|
type == Spree::PaymentMethod::TYPE_PAYWAY
|
|
94
100
|
end
|
|
@@ -14,11 +14,15 @@ module Vpago
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def find_and_verify!
|
|
17
|
-
if
|
|
17
|
+
if vattanac_mini_app_callback?
|
|
18
18
|
payload = Vpago::VattanacMiniAppDataHandler.new.decrypt_data(@params_hash[:data])
|
|
19
19
|
payment = Spree::Payment.find_by!(number: payload['paymentId'])
|
|
20
20
|
payment.update(transaction_response: payload)
|
|
21
21
|
payment
|
|
22
|
+
elsif acleda_v2_callback?
|
|
23
|
+
# ACLEDA V2's static portal callback carries only _paymenttokenid, which we
|
|
24
|
+
# stored on the payment source at openSessionV2. Resolve the payment by it.
|
|
25
|
+
Spree::VpagoPaymentSource.find_by!(transaction_id: params_hash[:_paymenttokenid]).payment
|
|
22
26
|
else
|
|
23
27
|
order = Spree::Order.find_by!(number: params_hash[:order_number])
|
|
24
28
|
verify_jwt!(order)
|
|
@@ -31,8 +35,12 @@ module Vpago
|
|
|
31
35
|
JWT.decode(params_hash[:order_jwt_token], order.token, 'HS256')
|
|
32
36
|
end
|
|
33
37
|
|
|
34
|
-
def
|
|
38
|
+
def vattanac_mini_app_callback?
|
|
35
39
|
params_hash[:data].present?
|
|
36
40
|
end
|
|
41
|
+
|
|
42
|
+
def acleda_v2_callback?
|
|
43
|
+
params_hash[:_paymenttokenid].present?
|
|
44
|
+
end
|
|
37
45
|
end
|
|
38
46
|
end
|
|
@@ -24,17 +24,18 @@ module Vpago
|
|
|
24
24
|
:unable_to_complete_order
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
def log_process(method, *args, &)
|
|
28
|
+
VpagoLogger.log(
|
|
29
|
+
label: "#{self.class.name}##{method}",
|
|
30
|
+
data: {
|
|
31
|
+
payment_number: @payment.number,
|
|
32
|
+
order_number: @payment.order.number,
|
|
33
|
+
payment_method_type: @payment.payment_method.type,
|
|
34
|
+
payment_method_name: @payment.payment_method.name,
|
|
35
|
+
args: args
|
|
36
|
+
},
|
|
37
|
+
&
|
|
38
|
+
)
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def user_informer
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= render 'spree/admin/payments/source_views/vpago_payment_tmpl', payment: payment %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<form method="post" action="<%= action_url %>" id="acleda_v2_checkout_form" style="display:none;">
|
|
2
|
+
<% gateway_params.each do |name, value| %>
|
|
3
|
+
<input type="hidden" name="<%= name %>" value="<%= value %>">
|
|
4
|
+
<% end %>
|
|
5
|
+
</form>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
(function () {
|
|
9
|
+
var form = document.getElementById("acleda_v2_checkout_form");
|
|
10
|
+
if (form) form.submit();
|
|
11
|
+
})();
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<%
|
|
2
|
+
opr_device = request.user_agent.to_s.downcase.match?(/iphone|ipad|ipod/) ? 'ios' : 'android'
|
|
3
|
+
checkout = ::Vpago::AcledaV2::Checkout.new(@payment, { platform: params[:platform], opr_device: opr_device })
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
checkout.call
|
|
7
|
+
rescue => e
|
|
8
|
+
Rails.logger.error("Acleda V2 checkout.call failed: #{e.message}")
|
|
9
|
+
end
|
|
10
|
+
%>
|
|
11
|
+
|
|
12
|
+
<% if checkout.failed? %>
|
|
13
|
+
<script>
|
|
14
|
+
console.error("[Vpago] Acleda V2 checkout error: <%= j checkout.error_message.to_s %>");
|
|
15
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
16
|
+
const loadingContainer = document.getElementById('vpago_loading_container');
|
|
17
|
+
if (loadingContainer) {
|
|
18
|
+
loadingContainer.textContent = "We couldn't start your payment. Please go back and try again.";
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
22
|
+
<% elsif @payment.payment_method.deeplink? %>
|
|
23
|
+
<script>
|
|
24
|
+
function showRedirectContainer(buttonLabel, onClick) {
|
|
25
|
+
const loadingContainer = document.getElementById('vpago_loading_container');
|
|
26
|
+
const redirectContainer = document.getElementById('vpago_redirect_container');
|
|
27
|
+
const redirectButton = document.getElementById('vpago_redirect_button');
|
|
28
|
+
|
|
29
|
+
redirectButton.textContent = buttonLabel;
|
|
30
|
+
redirectButton.addEventListener('click', onClick);
|
|
31
|
+
|
|
32
|
+
loadingContainer.style.display = 'none';
|
|
33
|
+
redirectContainer.style.removeProperty('display');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const deeplinkUrl = "<%= raw checkout.deeplink_url %>";
|
|
37
|
+
const redirectTo = (url) => { if (url) window.location.href = url; };
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
redirectTo(deeplinkUrl);
|
|
41
|
+
showRedirectContainer("Open ACLEDA", () => redirectTo(deeplinkUrl));
|
|
42
|
+
</script>
|
|
43
|
+
<% else %>
|
|
44
|
+
<%= render 'spree/checkout/payment/acleda_v2_checkout_form',
|
|
45
|
+
gateway_params: checkout.gateway_params,
|
|
46
|
+
action_url: checkout.action_url %>
|
|
47
|
+
<% end %>
|
data/lib/spree_vpago/engine.rb
CHANGED
data/lib/spree_vpago/version.rb
CHANGED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module Vpago
|
|
2
|
+
module AcledaV2
|
|
3
|
+
class Base
|
|
4
|
+
CONTENT_TYPE_JSON = 'application/json'.freeze
|
|
5
|
+
# ACLEDA requires purchaseDesc to be <= 16 chars, English only.
|
|
6
|
+
PURCHASE_DESC = 'Purchase goods'.freeze
|
|
7
|
+
|
|
8
|
+
def initialize(payment, options = {})
|
|
9
|
+
@options = options
|
|
10
|
+
@payment = payment
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def payment_method
|
|
14
|
+
@payment.payment_method
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def payment_number
|
|
18
|
+
@payment.number
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# PDF samples quote every xpayTransaction field as a JSON string.
|
|
22
|
+
def amount
|
|
23
|
+
format('%.2f', @payment.amount)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# txid <= 16 chars; payment.number fits.
|
|
27
|
+
alias transaction_id payment_number
|
|
28
|
+
|
|
29
|
+
def order_number
|
|
30
|
+
@payment.order.number
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def merchant_name
|
|
34
|
+
payment_method.preferences[:merchant_name]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def merchant_id
|
|
38
|
+
payment_method.preferences[:merchant_id]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def login_id
|
|
42
|
+
payment_method.preferences[:login_id]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def password
|
|
46
|
+
payment_method.preferences[:password]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def secret
|
|
50
|
+
payment_method.preferences[:secret]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# PDF samples quote every xpayTransaction field as a JSON string, even ones
|
|
54
|
+
# labeled "Numeric" in the field tables (paymentCard, quantity, expiryTime).
|
|
55
|
+
def expiry_time
|
|
56
|
+
payment_method.preferences[:payment_expiry_time_in_mn].to_s
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# PDF: "Date of Transaction in UTC Time", sent as a YYYY-MM-DD string in the
|
|
60
|
+
# samples. Explicit .utc so this stays correct even if config.time_zone is
|
|
61
|
+
# ever changed away from UTC.
|
|
62
|
+
def purchase_date
|
|
63
|
+
@payment.created_at.utc.strftime('%Y-%m-%d')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def purchase_currency
|
|
67
|
+
@payment.order.currency
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# --- Endpoints (each stored as a full URL preference) ---
|
|
71
|
+
# Strip embedded whitespace: these URLs are long and wrap across lines in
|
|
72
|
+
# the bank's PDFs, so a copy-paste into the admin preference easily carries
|
|
73
|
+
# over a stray space/line-break where the PDF wrapped.
|
|
74
|
+
|
|
75
|
+
def xpay_service_base_url
|
|
76
|
+
strip_whitespace(payment_method.preferences[:xpay_service_base_url])
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def open_session_url
|
|
80
|
+
"#{xpay_service_base_url}/openSessionV2"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def get_txn_status_url # rubocop:disable Naming/AccessorMethodName
|
|
84
|
+
"#{xpay_service_base_url}/getTxnStatus"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# KHQR/Card secure web redirect target
|
|
88
|
+
def action_url
|
|
89
|
+
strip_whitespace(payment_method.preferences[:payment_page_url])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# successUrlToReturn / errorUrl. The bank appends its own query string
|
|
93
|
+
# (_paymenttokenid, _resultCode, ...) when it redirects back.
|
|
94
|
+
def continue_success_url
|
|
95
|
+
@payment.processing_url
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def callback_url
|
|
99
|
+
if platform == 'app' && app_scheme.present?
|
|
100
|
+
uri = URI.parse(@payment.processing_url)
|
|
101
|
+
uri.scheme = app_scheme
|
|
102
|
+
uri.to_s
|
|
103
|
+
else
|
|
104
|
+
@payment.processing_url
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def app_scheme
|
|
109
|
+
payment_method.preferences[:app_scheme]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# android | ios (defaults to android when not supplied by the caller)
|
|
113
|
+
def opr_device
|
|
114
|
+
@options[:opr_device].presence || 'android'
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def platform
|
|
118
|
+
@options[:platform]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# --- HMAC-SHA512 (uppercase hex) signing ---
|
|
122
|
+
# openSessionV2 message: merchantID + loginId + password + txid
|
|
123
|
+
def open_session_hash
|
|
124
|
+
hmac512("#{merchant_id}#{login_id}#{password}#{payment_number}")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# getTxnStatus message: merchantId + loginId + password + paymentTokenid
|
|
128
|
+
def txn_status_hash(token)
|
|
129
|
+
hmac512("#{merchant_id}#{login_id}#{password}#{token}")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def hmac512(message)
|
|
133
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha512'), secret, message).upcase
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def parse_json(body)
|
|
137
|
+
JSON.parse(body)
|
|
138
|
+
rescue JSON::ParserError
|
|
139
|
+
{}
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def strip_whitespace(value)
|
|
145
|
+
value.to_s.gsub(/\s+/, '')
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module Vpago
|
|
4
|
+
module AcledaV2
|
|
5
|
+
# Single class handling all three modes (khqr, card, deeplink).
|
|
6
|
+
# Two-step ACLEDA flow: openSessionV2 (JSON, here) yields paymentTokenid +
|
|
7
|
+
# sessionid, then either an auto-POST form to paymentPage.jsp (KHQR/Card) or
|
|
8
|
+
# a JS deeplink redirect (Deep Link).
|
|
9
|
+
class Checkout < Base
|
|
10
|
+
attr_reader :error_message
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
@error_message = nil
|
|
14
|
+
@response = open_session
|
|
15
|
+
|
|
16
|
+
if @response.status != 500 && @response.status != 200
|
|
17
|
+
@error_message = 'Server Error'
|
|
18
|
+
elsif @response.status == 500 # mostly when passing a wrong parameter name
|
|
19
|
+
@error_message = @response.body
|
|
20
|
+
elsif json_response.dig('result', 'code') != 0 # invalid payload, wrong loginId, etc.
|
|
21
|
+
@error_message = json_response.dig('result', 'errorDetails') || 'Unknown Error'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
update_payment_source
|
|
25
|
+
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def failed?
|
|
30
|
+
@error_message.present?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def json_response
|
|
34
|
+
@json_response ||= parse_json(@response.body)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# KHQR/Card secure web redirect form fields (auto-POSTed to paymentPage.jsp).
|
|
38
|
+
def gateway_params
|
|
39
|
+
return {} if failed?
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
merchantID: merchant_id,
|
|
43
|
+
sessionid: json_response.dig('result', 'sessionid'),
|
|
44
|
+
paymenttokenid: payment_token_id,
|
|
45
|
+
description: Base::PURCHASE_DESC,
|
|
46
|
+
expirytime: expiry_time,
|
|
47
|
+
amount: amount,
|
|
48
|
+
quantity: '1',
|
|
49
|
+
item: '1',
|
|
50
|
+
invoiceid: order_number,
|
|
51
|
+
currencytype: purchase_currency,
|
|
52
|
+
transactionID: payment_number,
|
|
53
|
+
successUrlToReturn: continue_success_url,
|
|
54
|
+
errorUrl: continue_success_url
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Deep Link redirect target.
|
|
59
|
+
def deeplink_url
|
|
60
|
+
return nil if failed?
|
|
61
|
+
|
|
62
|
+
json_response.dig('result', 'deeplinkUrl')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def open_session
|
|
68
|
+
conn = Faraday::Connection.new
|
|
69
|
+
|
|
70
|
+
conn.post(open_session_url) do |request|
|
|
71
|
+
request.headers['Content-Type'] = Base::CONTENT_TYPE_JSON
|
|
72
|
+
request.body = open_session_payload.to_json
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def open_session_payload
|
|
77
|
+
base = {
|
|
78
|
+
password: password,
|
|
79
|
+
loginId: login_id,
|
|
80
|
+
merchantID: merchant_id,
|
|
81
|
+
hash: open_session_hash,
|
|
82
|
+
xpayTransaction: xpay_transaction_base
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if payment_method.deeplink?
|
|
86
|
+
base[:xpayTransaction].merge!(oprDevice: opr_device, callBackUrl: callback_url)
|
|
87
|
+
else
|
|
88
|
+
base[:xpayTransaction][:paymentCard] = payment_method.payment_card_value
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
base
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def xpay_transaction_base
|
|
95
|
+
{
|
|
96
|
+
txid: payment_number,
|
|
97
|
+
invoiceid: order_number,
|
|
98
|
+
purchaseAmount: amount,
|
|
99
|
+
purchaseCurrency: purchase_currency,
|
|
100
|
+
purchaseDate: purchase_date,
|
|
101
|
+
purchaseDesc: Base::PURCHASE_DESC,
|
|
102
|
+
item: '1',
|
|
103
|
+
quantity: '1',
|
|
104
|
+
expiryTime: expiry_time
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def payment_token_id
|
|
109
|
+
json_response.dig('result', 'xTran', 'paymentTokenid')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Persist the paymentTokenid so check_transaction / the bank callback can
|
|
113
|
+
# resolve the payment later. Wrap only the write in the writing role, since
|
|
114
|
+
# the checkout form partial runs on a GET request (replica connection).
|
|
115
|
+
def update_payment_source
|
|
116
|
+
return if failed?
|
|
117
|
+
|
|
118
|
+
ActiveRecord::Base.connected_to(role: :writing) do
|
|
119
|
+
@payment.source.update_column(:transaction_id, payment_token_id)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module Vpago
|
|
4
|
+
module AcledaV2
|
|
5
|
+
class TransactionStatus < Base
|
|
6
|
+
# Terminal failure indicators in result.errorDetails. The PDFs only document
|
|
7
|
+
# code 0 / "SUCCESS"; decline/expiry wording is conservative and should be
|
|
8
|
+
# confirmed against real responses during UAT. Anything not success and not
|
|
9
|
+
# terminal is treated as pending so the poller keeps trying.
|
|
10
|
+
FAILED_ERROR_DETAILS = %w[EXPIRED DECLINED CANCELLED FAILED REJECTED].freeze
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
@response = check_remote_status
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def json_response
|
|
18
|
+
@json_response ||= parse_json(@response.body)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def result
|
|
22
|
+
json_response['result'] || {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def result_code
|
|
26
|
+
result['code']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def error_message
|
|
30
|
+
result['errorDetails']
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def success?
|
|
34
|
+
http_success? && result_code == 0 && error_message == 'SUCCESS'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def failed?
|
|
38
|
+
return false unless http_success?
|
|
39
|
+
|
|
40
|
+
FAILED_ERROR_DETAILS.include?(error_message.to_s.upcase)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def pending?
|
|
44
|
+
!success? && !failed?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def http_success?
|
|
50
|
+
@response&.status == 200
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def token
|
|
54
|
+
@payment.source&.transaction_id
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def check_remote_status
|
|
58
|
+
conn = Faraday::Connection.new
|
|
59
|
+
|
|
60
|
+
conn.post(get_txn_status_url) do |request|
|
|
61
|
+
request.headers['Content-Type'] = Base::CONTENT_TYPE_JSON
|
|
62
|
+
request.body = payload.to_json
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# NOTE: getTxnStatus uses merchantId (lowercase d) and an extra merchantName
|
|
67
|
+
# field. merchantName is NOT part of the HMAC message.
|
|
68
|
+
def payload
|
|
69
|
+
{
|
|
70
|
+
loginId: login_id,
|
|
71
|
+
password: password,
|
|
72
|
+
merchantId: merchant_id,
|
|
73
|
+
merchantName: merchant_name,
|
|
74
|
+
hash: txn_status_hash(token),
|
|
75
|
+
paymentTokenid: token
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/vpago_logger.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# lib/vpago_logger.rb
|
|
2
|
+
module VpagoLogger
|
|
3
|
+
def self.log(label:, data: nil)
|
|
4
|
+
message = { label: label, data: safe_serialize(data) }
|
|
5
|
+
start_time = Time.current
|
|
6
|
+
Rails.logger.info(message.to_json)
|
|
7
|
+
|
|
8
|
+
return unless block_given?
|
|
9
|
+
|
|
10
|
+
# Capture the block's return value and return it to preserve existing behavior for callers expecting that value.
|
|
11
|
+
block_result = yield
|
|
12
|
+
|
|
13
|
+
message[:start_time] = start_time.iso8601(3)
|
|
14
|
+
message[:duration_ms] = (Time.current - start_time) * 1000
|
|
15
|
+
message[:result] = safe_serialize(block_result)
|
|
16
|
+
Rails.logger.info(message.to_json)
|
|
17
|
+
block_result
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.error(label:, data: nil)
|
|
21
|
+
message = {
|
|
22
|
+
label: label,
|
|
23
|
+
data: safe_serialize(data)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Rails.logger.error(message.to_json)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Safely serializes objects for JSON logging.
|
|
30
|
+
#
|
|
31
|
+
# @param obj [Object] The object to serialize
|
|
32
|
+
# @param depth [Integer] Internal parameter tracking recursion depth (max: 50)
|
|
33
|
+
# @return [Object] A JSON-safe representation of the input object
|
|
34
|
+
def self.safe_serialize(obj, depth: 0)
|
|
35
|
+
return '[Max Depth Exceeded]' if depth > 50
|
|
36
|
+
|
|
37
|
+
if obj.is_a?(Hash)
|
|
38
|
+
obj.each_with_object({}) do |(k, v), memo|
|
|
39
|
+
memo[safe_serialize(k, depth: depth + 1)] = safe_serialize(v, depth: depth + 1)
|
|
40
|
+
end
|
|
41
|
+
elsif obj.is_a?(Array)
|
|
42
|
+
obj.map { |item| safe_serialize(item, depth: depth + 1) }
|
|
43
|
+
elsif obj.is_a?(Date)
|
|
44
|
+
obj.iso8601
|
|
45
|
+
elsif obj.is_a?(Time) || obj.is_a?(DateTime) || obj.is_a?(ActiveSupport::TimeWithZone)
|
|
46
|
+
obj.iso8601(3)
|
|
47
|
+
elsif obj.is_a?(ActiveJob::Base)
|
|
48
|
+
{
|
|
49
|
+
job_class: obj.class.name,
|
|
50
|
+
job_id: obj.job_id,
|
|
51
|
+
arguments: safe_serialize(obj.arguments, depth: depth + 1)
|
|
52
|
+
}
|
|
53
|
+
elsif obj.respond_to?(:id)
|
|
54
|
+
{ class: obj.class.name, id: obj.id }
|
|
55
|
+
else
|
|
56
|
+
obj.to_s
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
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.3.
|
|
4
|
+
version: 2.3.5.pre.pre.pre.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- You
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -192,6 +192,7 @@ files:
|
|
|
192
192
|
- app/models/concerns/vpago/payoutable.rb
|
|
193
193
|
- app/models/spree/gateway/acleda.rb
|
|
194
194
|
- app/models/spree/gateway/acleda_mobile.rb
|
|
195
|
+
- app/models/spree/gateway/acleda_v2.rb
|
|
195
196
|
- app/models/spree/gateway/payway.rb
|
|
196
197
|
- app/models/spree/gateway/payway_v2.rb
|
|
197
198
|
- app/models/spree/gateway/true_money.rb
|
|
@@ -280,6 +281,7 @@ files:
|
|
|
280
281
|
- app/views/spree/admin/payments/source_forms/_payway_v2.html.erb
|
|
281
282
|
- app/views/spree/admin/payments/source_views/_acleda.html.erb
|
|
282
283
|
- app/views/spree/admin/payments/source_views/_acleda_mobile.html.erb
|
|
284
|
+
- app/views/spree/admin/payments/source_views/_acleda_v2.html.erb
|
|
283
285
|
- app/views/spree/admin/payments/source_views/_payment_payway.html.erb
|
|
284
286
|
- app/views/spree/admin/payments/source_views/_payway_v2.html.erb
|
|
285
287
|
- app/views/spree/admin/payments/source_views/_true_money.html.erb
|
|
@@ -309,6 +311,7 @@ files:
|
|
|
309
311
|
- app/views/spree/checkout/payment/_acleda.html.erb
|
|
310
312
|
- app/views/spree/checkout/payment/_acleda_checkout_form.html.erb
|
|
311
313
|
- app/views/spree/checkout/payment/_acleda_mobile.html.erb
|
|
314
|
+
- app/views/spree/checkout/payment/_acleda_v2_checkout_form.html.erb
|
|
312
315
|
- app/views/spree/checkout/payment/_payment_payway.html.erb
|
|
313
316
|
- app/views/spree/checkout/payment/_payway_loader.html.erb
|
|
314
317
|
- app/views/spree/checkout/payment/_payway_root.html.erb
|
|
@@ -331,6 +334,7 @@ files:
|
|
|
331
334
|
- app/views/spree/payway_v2_card_popups/show.html.erb
|
|
332
335
|
- app/views/spree/vpago_payments/_transaction_checker.html.erb
|
|
333
336
|
- app/views/spree/vpago_payments/checkout.html.erb
|
|
337
|
+
- app/views/spree/vpago_payments/forms/spree/gateway/_acleda_v2.html.erb
|
|
334
338
|
- app/views/spree/vpago_payments/forms/spree/gateway/_payway_v2.html.erb
|
|
335
339
|
- app/views/spree/vpago_payments/forms/spree/gateway/_true_money.html.erb
|
|
336
340
|
- app/views/spree/vpago_payments/forms/spree/gateway/_vattanac.html.erb
|
|
@@ -388,6 +392,9 @@ files:
|
|
|
388
392
|
- lib/vpago/acleda_mobile/payment_request_updater.rb
|
|
389
393
|
- lib/vpago/acleda_mobile/payment_retriever.rb
|
|
390
394
|
- lib/vpago/acleda_mobile/transaction_status.rb
|
|
395
|
+
- lib/vpago/acleda_v2/base.rb
|
|
396
|
+
- lib/vpago/acleda_v2/checkout.rb
|
|
397
|
+
- lib/vpago/acleda_v2/transaction_status.rb
|
|
391
398
|
- lib/vpago/payment_amount_calculator.rb
|
|
392
399
|
- lib/vpago/payment_request_updater.rb
|
|
393
400
|
- lib/vpago/payment_status_marker.rb
|
|
@@ -421,6 +428,7 @@ files:
|
|
|
421
428
|
- lib/vpago/wing_sdk/payment_retriever.rb
|
|
422
429
|
- lib/vpago/wing_sdk/transaction_status_checker.rb
|
|
423
430
|
- lib/vpago/wing_sdk/transaction_status_response.rb
|
|
431
|
+
- lib/vpago_logger.rb
|
|
424
432
|
- node_modules/.yarn-integrity
|
|
425
433
|
- package-lock.json
|
|
426
434
|
- package.json
|
|
@@ -442,9 +450,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
442
450
|
version: 3.2.0
|
|
443
451
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
444
452
|
requirements:
|
|
445
|
-
- - "
|
|
453
|
+
- - ">"
|
|
446
454
|
- !ruby/object:Gem::Version
|
|
447
|
-
version:
|
|
455
|
+
version: 1.3.1
|
|
448
456
|
requirements:
|
|
449
457
|
- none
|
|
450
458
|
rubygems_version: 3.4.1
|