spree_vpago 2.2.2.pre.pre19 → 2.2.2.pre.pre21

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +0 -4
  3. data/Gemfile.lock +4 -4
  4. data/README.md +0 -8
  5. data/app/assets/javascripts/vpago/vpago_payments/check_transaction_periodically.js +59 -0
  6. data/app/controllers/spree/vpago_payments_controller.rb +22 -15
  7. data/app/helpers/vpago/vpago_payments_helper.rb +0 -11
  8. data/app/models/spree/gateway/payway_v2.rb +0 -12
  9. data/app/models/spree/gateway/true_money.rb +0 -2
  10. data/app/models/spree/gateway/vattanac.rb +0 -2
  11. data/app/models/vpago/payment_decorator.rb +2 -2
  12. data/app/models/vpago/payment_method_decorator.rb +3 -4
  13. data/app/services/vpago/payment_url_constructor.rb +1 -1
  14. data/app/views/layouts/vpago_payments.html.erb +1 -1
  15. data/app/views/spree/vpago_payments/_transaction_checker.html.erb +7 -37
  16. data/app/views/spree/vpago_payments/checkout.html.erb +13 -7
  17. data/app/views/spree/vpago_payments/forms/spree/gateway/_payway_v2.html.erb +28 -19
  18. data/app/views/spree/vpago_payments/forms/spree/gateway/_true_money.html.erb +14 -17
  19. data/app/views/spree/vpago_payments/forms/spree/gateway/_vattanac.html.erb +17 -3
  20. data/app/views/spree/vpago_payments/forms/spree/gateway/_vattanac_mini_app.html.erb +15 -17
  21. data/app/views/spree/vpago_payments/processing.html.erb +20 -0
  22. data/config/routes.rb +1 -1
  23. data/lib/spree_vpago/version.rb +1 -1
  24. data/lib/vpago/payway_v2/checkout.rb +1 -1
  25. data/lib/vpago/payway_v2/pre_auth_canceler.rb +1 -1
  26. data/lib/vpago/payway_v2/pre_auth_completer.rb +1 -1
  27. data/lib/vpago/payway_v2/transaction_status.rb +2 -2
  28. metadata +3 -3
  29. data/app/assets/javascripts/vpago/vpago_payments/payment_processing_listener.js +0 -95
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe27a3c3be7d021399478823880839edf3366fa5a8b9a3395e813f584a27cfac
4
- data.tar.gz: b6966f5ee14676a590ea06e05cebb411aa5a4500fe6c38a2f100b7c69c2cb5d4
3
+ metadata.gz: '05536639cc3731496d0e01bd7ecccb56062d2b412ca795b7a924deac49c9c9a5'
4
+ data.tar.gz: f2dbe115e30add8edc1188b3c8a8ae855235d88197d45683615cb5dbe4b17445
5
5
  SHA512:
6
- metadata.gz: 5abfe727cad505307d532928096edb04925ce2aed86469f3ace8b881db910c570f9c4a2514f10311b9d307127ae15e548fee258964066cdf1a131ec507e9ac34
7
- data.tar.gz: e397f93cd854c21f9de913b73799cdd7330d33a55fa9720106cc1ab2dcd80fe7f6d0c181e5f99036ae7ae9a110c69eda02df80831a283f85b2a459d9511457bc
6
+ metadata.gz: 43688b89eab0f758808875722761c47b35d460c2c76501b3ff0218ea25b60d2b7859183cf4733a66704ae0f10b72b4f7bc9a75a6b1e97a1139f893c08b5cf008
7
+ data.tar.gz: 5642262ac80774489724678f53fb027e09c2257dbed79ae9fa32c3cf9f806baee60315120053f97feb272779d8fda1b3497c4cdf7e0941e1c9c9dbffbd838012
data/.env.example CHANGED
@@ -1,7 +1,3 @@
1
- #pre-auth
2
- PAYWAY_V2_PRE_AUTH_COMPLETE_PATH=""
3
- PAYWAY_V2_PRE_AUTH_CANCEL_PATH=""
4
-
5
1
  #vattanac-mini-app
6
2
  VATTANAC_PUBLIC_KEY=""
7
3
  VATTANAC_AES_SECRET_KEY=""
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- spree_vpago (2.2.2.pre.pre19)
4
+ spree_vpago (2.2.2.pre.pre21)
5
5
  faraday
6
6
  google-cloud-firestore
7
7
  spree_api (>= 4.5)
@@ -188,7 +188,7 @@ GEM
188
188
  faraday-http-cache (2.5.0)
189
189
  faraday (>= 0.8)
190
190
  faraday-net_http (3.0.2)
191
- faraday-retry (2.4.0)
191
+ faraday-retry (2.3.2)
192
192
  faraday (~> 2.0)
193
193
  ffaker (2.21.0)
194
194
  ffi (1.15.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.5)
245
+ google-protobuf (4.33.4)
246
246
  bigdecimal
247
247
  rake (>= 13)
248
248
  googleapis-common-protos (1.9.0)
@@ -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.78.1)
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)
data/README.md CHANGED
@@ -95,14 +95,6 @@ ENV configuration:
95
95
 
96
96
  ```ruby
97
97
  ENV['PAYWAY_MERCHANT_PROFILE_CONTENT_TYPE'] # html, json
98
- ENV['PAYWAY_CHECKOUT_PATH']
99
- ENV['PAYWAY_CHECK_TRANSACTION_PATH']
100
- ENV['PAYWAY_RETURN_CALLBACK_URL']
101
- ENV['PAYWAY_CONTINUE_SUCCESS_CALLBACK_URL']
102
-
103
- #pre-auth
104
- ENV['PAYWAY_V2_PRE_AUTH_COMPLETE_PATH']
105
- ENV['PAYWAY_V2_PRE_AUTH_CANCEL_PATH']
106
98
  ```
107
99
 
108
100
  Payment method configuration:
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Polls the transaction status endpoint until the payment succeeds, fails, or
3
+ * the maximum polling duration is reached.
4
+ *
5
+ * @param {Object} options
6
+ * @param {string} options.checkTransactionUrl
7
+ * @param {Function} options.onSuccess
8
+ * @param {Function} options.onFailure
9
+ */
10
+ async function checkTransactionPeriodically({
11
+ checkTransactionUrl,
12
+ onSuccess,
13
+ onFailure,
14
+ }) {
15
+ var pollIntervalMs = 5000;
16
+ var maxDurationMs = 10 * 60 * 1000;
17
+ var shouldPoll = true;
18
+ var intervalId;
19
+ var startTime = Date.now();
20
+
21
+ var pollStatus = function () {
22
+ if (shouldPoll) return;
23
+
24
+ if (Date.now() - startTime >= maxDurationMs) {
25
+ if (intervalId) clearInterval(intervalId);
26
+ return;
27
+ }
28
+
29
+ fetch(checkTransactionUrl, {
30
+ method: "GET",
31
+ headers: { Accept: "application/json" },
32
+ credentials: "same-origin",
33
+ })
34
+ .then(function (response) {
35
+ return response.json().then(function (body) {
36
+ return { ok: response.ok, body: body };
37
+ });
38
+ })
39
+ .then(function (result) {
40
+ var status =
41
+ result.body && result.body.status
42
+ ? String(result.body.status)
43
+ : "pending";
44
+
45
+ if (status === "success" || status === "failed") {
46
+ shouldPoll = false;
47
+ if (intervalId) clearInterval(intervalId);
48
+
49
+ if (status === "success") onSuccess(status);
50
+ if (status === "failed") onFailure(status);
51
+ }
52
+ })
53
+ .catch(function () {});
54
+ };
55
+
56
+ intervalId = window.setInterval(pollStatus, pollIntervalMs);
57
+ }
58
+
59
+ window.checkTransactionPeriodically = checkTransactionPeriodically;
@@ -19,36 +19,43 @@ module Spree
19
19
  end
20
20
 
21
21
  # GET
22
- def check_transaction
22
+ def processing
23
23
  @payment = Vpago::PaymentFinder.new(params.permit!.to_h).find_and_verify
24
24
  raise ActiveRecord::RecordNotFound unless @payment.present?
25
25
 
26
- checker = @payment.payment_method.check_transaction(@payment)
27
-
28
- if checker.success?
29
- render json: { status: :success }, status: :ok
30
- elsif checker.failed?
31
- render json: { status: :failed }, status: :payment_required
32
- else
33
- render json: { status: :pending }, status: :accepted
34
- end
26
+ @order = @payment.order
35
27
  end
36
28
 
37
29
  # GET
38
- def processing
30
+ def success
39
31
  @payment = Vpago::PaymentFinder.new(params.permit!.to_h).find_and_verify
40
32
  raise ActiveRecord::RecordNotFound unless @payment.present?
41
33
 
42
34
  @order = @payment.order
35
+ raise CanCan::AccessDenied unless @order.completed?
43
36
  end
44
37
 
45
38
  # GET
46
- def success
39
+ def check_transaction
47
40
  @payment = Vpago::PaymentFinder.new(params.permit!.to_h).find_and_verify
48
41
  raise ActiveRecord::RecordNotFound unless @payment.present?
49
-
50
- @order = @payment.order
51
- raise CanCan::AccessDenied unless @order.completed?
42
+
43
+ return render json: { status: :success }, status: :ok if @payment.completed?
44
+ return render json: { status: :failed }, status: :ok if @payment.failed?
45
+
46
+ if @payment.payment_method.support_check_transaction_api?
47
+ checker = @payment.payment_method.check_transaction(@payment)
48
+
49
+ if checker.success?
50
+ render json: { status: :success }, status: :ok
51
+ elsif checker.try(:failed?) == true
52
+ render json: { status: :failed }, status: :ok
53
+ else
54
+ render json: { status: :pending }, status: :ok
55
+ end
56
+ else
57
+ render json: { status: :pending }, status: :ok
58
+ end
52
59
  end
53
60
 
54
61
  # POST
@@ -4,17 +4,6 @@ module Vpago
4
4
  'firebase'
5
5
  end
6
6
 
7
- # eg. forms/spree/gateway/payway_v2
8
- def render_form
9
- render partial: "spree/vpago_payments/forms/#{@payment.payment_method.class.to_s.underscore}"
10
- end
11
-
12
- def render_transaction_checker
13
- if @payment.payment_method.support_check_transaction_api?
14
- render partial: 'spree/vpago_payments/transaction_checker'
15
- end
16
- end
17
-
18
7
  # when return nil, each payment method should use their default URL.
19
8
  # eg. PayWayV2 will use the continue_success_url as deeplink back URL.
20
9
  def custom_return_deeplink_url
@@ -33,11 +33,6 @@ module Spree
33
33
  preferred_abapay_khqr_deeplink_option.in?(%w[open_both open_checkout_url_only])
34
34
  end
35
35
 
36
- # override
37
- def support_check_transaction_api?
38
- true
39
- end
40
-
41
36
  # override
42
37
  def default_payout_profile
43
38
  Spree::PayoutProfiles::PaywayV2.default
@@ -72,12 +67,6 @@ module Spree
72
67
  !enable_pre_auth?
73
68
  end
74
69
 
75
- # override
76
- def check_status(payment)
77
- checker = check_transaction(payment)
78
- checker.success?
79
- end
80
-
81
70
  # override
82
71
  # authorize is used when pre auth enabled
83
72
  def authorize(_amount, _source, gateway_options = {})
@@ -159,7 +148,6 @@ module Spree
159
148
  ActiveMerchant::Billing::Response.new(true, 'Payway Gateway: Payment has been canceled.')
160
149
  end
161
150
 
162
- # override
163
151
  def check_transaction(payment)
164
152
  checker = Vpago::PaywayV2::TransactionStatus.new(payment)
165
153
  checker.call
@@ -73,8 +73,6 @@ module Spree
73
73
  [refund_issuer.success?, refund_issuer.parsed_response]
74
74
  end
75
75
 
76
- private
77
-
78
76
  def check_transaction(payment)
79
77
  checker = Vpago::TrueMoney::TransactionStatus.new(payment)
80
78
  checker.call
@@ -51,8 +51,6 @@ module Spree
51
51
  ActiveMerchant::Billing::Response.new(true, '')
52
52
  end
53
53
 
54
- private
55
-
56
54
  def check_transaction(payment)
57
55
  checker = Vpago::Vattanac::TransactionStatus.new(payment)
58
56
  checker.call
@@ -20,11 +20,11 @@ module Vpago
20
20
 
21
21
  base.delegate :checkout_url,
22
22
  :web_checkout_url,
23
- :check_transaction_url,
24
23
  :processing_url,
25
24
  :success_url,
26
- :process_payment_url,
27
25
  :success_deeplink_url,
26
+ :check_transaction_url,
27
+ :process_payment_url,
28
28
  to: :url_constructor
29
29
 
30
30
  # Add state machine event for payment retry
@@ -35,11 +35,10 @@ module Vpago
35
35
  def support_payout? = false
36
36
  def support_pre_auth? = false
37
37
 
38
- # Must be explicitly overridden to true by each payment method.
39
- # Payment method must implement check_transaction(payment), and its response
40
- # must provide: success?, failed?, pending?.
38
+ # The payment method must implement `check_transaction(payment)`.
39
+ # The returned object should respond to `.success?`, and optionally `.failed?` and `.pending?`.
41
40
  def support_check_transaction_api?
42
- false
41
+ respond_to?(:check_transaction)
43
42
  end
44
43
 
45
44
  # TODO: we have already implement purchase for payway_v2.
@@ -12,12 +12,12 @@ module Vpago
12
12
  def checkout_url = "#{base_url}/vpago_payments/checkout?#{query}&platform=app"
13
13
  def web_checkout_url = "#{base_url}/vpago_payments/checkout?#{query}&platform=web"
14
14
 
15
- def check_transaction_url = "#{base_url}/vpago_payments/check_transaction?#{query}"
16
15
  def processing_url = "#{base_url}/vpago_payments/processing?#{query}"
17
16
 
18
17
  def success_url = "#{base_url}/vpago_payments/success?#{query}"
19
18
  def success_deeplink_url = "#{app_scheme}://#{URI(base_url).host}/book/payment?number=#{order.number}&tk=#{order.token}"
20
19
 
20
+ def check_transaction_url = "#{base_url}/vpago_payments/check_transaction?#{query}"
21
21
  def process_payment_url = "#{base_url}/vpago_payments/process_payment?#{query}"
22
22
 
23
23
  def query
@@ -8,7 +8,7 @@
8
8
  <%= csrf_meta_tags %>
9
9
  <%= csp_meta_tag %>
10
10
 
11
- <%= javascript_include_tag "vpago/vpago_payments/payment_processing_listener", 'data-turbo-track': 'reload' %>
11
+ <%= javascript_include_tag "vpago/vpago_payments/check_transaction_periodically", 'data-turbo-track': 'reload' %>
12
12
  <%= javascript_include_tag "vpago/vpago_payments/request_process_payment", 'data-turbo-track': 'reload' %>
13
13
  <%= javascript_include_tag "vpago/vpago_payments/user_informers/#{user_informer}", 'data-turbo-track': 'reload' %>
14
14
  </head>
@@ -1,40 +1,10 @@
1
- <%# This partial listens to payment status changes from bank and does not process the payment. %>
2
- <%# When it detects a status change, it automatically redirects to the processing URL for actual processing. %>
3
- <%# Designed to be used in checkout.html.erb %>
4
-
5
1
  <script>
6
2
  document.addEventListener("DOMContentLoaded", function() {
7
- var checkTransactionUrl = "<%= j @payment.check_transaction_url %>";
8
- var processingUrl = "<%= j @payment.processing_url %>";
9
- var pollIntervalMs = 5000;
10
- var hasRedirected = false;
11
- var intervalId;
12
-
13
- var pollStatus = function() {
14
- if (hasRedirected) return;
15
-
16
- fetch(checkTransactionUrl, {
17
- method: "GET",
18
- headers: { "Accept": "application/json" },
19
- credentials: "same-origin"
20
- })
21
- .then(function(response) {
22
- return response.json().then(function(body) {
23
- return { ok: response.ok, body: body };
24
- });
25
- })
26
- .then(function(result) {
27
- var status = result.body && result.body.status ? String(result.body.status) : "pending";
28
-
29
- if (status === "success" || status === "failed") {
30
- hasRedirected = true;
31
- if (intervalId) clearInterval(intervalId);
32
- window.location.replace(processingUrl);
33
- }
34
- })
35
- .catch(function() {});
36
- };
37
-
38
- intervalId = window.setInterval(pollStatus, pollIntervalMs);
3
+ var processingUrl = "<%= raw @payment.processing_url %>";
4
+ window.checkTransactionPeriodically({
5
+ checkTransactionUrl: "<%= raw @payment.check_transaction_url %>",
6
+ onFailure: () => window.location.replace(processingUrl),
7
+ onSuccess: () => window.location.replace(processingUrl),
8
+ });
39
9
  });
40
- </script>
10
+ </script>
@@ -1,9 +1,15 @@
1
- <h1>Checkout...</h1>
1
+ <div id="vpago_loading_container">
2
+ Loading...
3
+ </div>
2
4
 
3
- <%# Primary action button fixed at bottom of screen %>
4
- <%# Each payment method can customize this button (title, onclick) for their specific action. %>
5
- <%# See: app/views/spree/vpago_payments/forms/spree/gateway/_payway_v2.html.erb for example %>
6
- <button id="vpago_primary_button" style="display: none;"></button>
5
+ <div id="vpago_redirect_container" style="display: none;">
6
+ <h1>We're redirecting you to your bank</h1>
7
+ <p>You'll be returned here after confirming your <strong><%= @payment.display_amount %></strong> payment.</p>
7
8
 
8
- <%= render_transaction_checker %>
9
- <%= render_form %>
9
+ <button id="vpago_redirect_button">
10
+ <!-- Use JS to modify button label and callback -->
11
+ </button>
12
+ </div>
13
+
14
+ <%= render partial: "spree/vpago_payments/forms/#{@payment.payment_method.class.to_s.underscore}" %>
15
+ <%= render partial: 'spree/vpago_payments/transaction_checker' %>
@@ -11,38 +11,40 @@
11
11
  const isTelegram = <%= telegram_user_agent? %>;
12
12
  const isMobile = <%= mobile_user_agent? %>;
13
13
 
14
- const abapayKhqrDeeplink = "<%= @payment.payment_method.abapay_khqr_deeplink? %>";
14
+ const abapayKhqrDeeplink = <%= @payment.payment_method.abapay_khqr_deeplink? %>;
15
15
  const shouldOpenAbaPayDeeplink = isMobile && <%= @payment.payment_method.open_abapay_deeplink? %>;
16
16
  const shouldOpenCheckoutUrl = <%= @payment.payment_method.open_checkout_url? %>;
17
17
 
18
- function openDeeplink(abapay_deeplink) {
18
+ function openDeeplinkUrl(deeplinkUrl) {
19
19
  if(isTelegram){
20
- window.open(abapay_deeplink, '_blank');
20
+ window.open(deeplinkUrl, '_blank');
21
21
  } else {
22
- window.location.href = abapay_deeplink;
22
+ window.location.href = deeplinkUrl;
23
23
  }
24
24
  }
25
25
 
26
- function openAbapayDeeplink(abapay_deeplink) {
27
- document.getElementById("vpago_primary_button").style.display = "block";
28
- document.getElementById("vpago_primary_button").innerText = "Open ABA Pay";
29
- document.getElementById("vpago_primary_button").addEventListener("click", function() {
30
- openDeeplink(abapay_deeplink)
31
- });
32
-
33
- openDeeplink(abapay_deeplink)
34
- }
35
-
36
- function openCheckoutUrl(checkout_qr_url, delay = 0) {
26
+ function openUrl(url, delay = 0) {
37
27
  if(delay > 0) {
38
28
  setTimeout(function() {
39
- window.location.href = checkout_qr_url;
29
+ window.location.href = url;
40
30
  }, delay);
41
31
  } else {
42
- window.location.href = checkout_qr_url;
32
+ window.location.href = url;
43
33
  }
44
34
  }
45
35
 
36
+ function showRedirectContainer(buttonLabel, onClick) {
37
+ const loadingContainer = document.getElementById('vpago_loading_container');
38
+ const redirectContainer = document.getElementById('vpago_redirect_container');
39
+ const redirectButton = document.getElementById('vpago_redirect_button');
40
+
41
+ redirectButton.textContent = buttonLabel;
42
+ redirectButton.addEventListener('click', onClick);
43
+
44
+ loadingContainer.style.display = 'none';
45
+ redirectContainer.style.removeProperty('display');
46
+ }
47
+
46
48
  document.addEventListener("DOMContentLoaded", function () {
47
49
  if (abapayKhqrDeeplink) {
48
50
  var formData = new FormData(document.getElementById("aba_merchant_request"));
@@ -57,14 +59,21 @@
57
59
  })
58
60
  .then(function(data) {
59
61
  var shouldDelayBeforeOpenCheckoutUrl = false;
62
+ var shouldShowRedirectContainer = false;
60
63
 
61
64
  if (data.abapay_deeplink && shouldOpenAbaPayDeeplink) {
62
- openAbapayDeeplink(data.abapay_deeplink);
65
+ openDeeplinkUrl(data.abapay_deeplink);
63
66
  shouldDelayBeforeOpenCheckoutUrl = true;
67
+ shouldShowRedirectContainer = true;
64
68
  }
65
69
 
66
70
  if (data.checkout_qr_url && shouldOpenCheckoutUrl) {
67
- openCheckoutUrl(data.checkout_qr_url, shouldDelayBeforeOpenCheckoutUrl ? 500 : 0);
71
+ openUrl(data.checkout_qr_url, shouldDelayBeforeOpenCheckoutUrl ? 500 : 0);
72
+ shouldShowRedirectContainer = false;
73
+ }
74
+
75
+ if(shouldShowRedirectContainer){
76
+ showRedirectContainer("Open ABA Mobile", () => openDeeplinkUrl(data.abapay_deeplink));
68
77
  }
69
78
  })
70
79
  .catch(function(error) {
@@ -1,15 +1,22 @@
1
1
  <% @checkout = ::Vpago::TrueMoney::Checkout.new(@payment) %>
2
2
  <% redirect_urls = @checkout.generate_payment_urls(params[:platform]) %>
3
- <% @payment.user_informer.payment_is_processing(processing: true) %>
4
3
 
5
4
  <script>
5
+ function showRedirectContainer(buttonLabel, onClick) {
6
+ const loadingContainer = document.getElementById('vpago_loading_container');
7
+ const redirectContainer = document.getElementById('vpago_redirect_container');
8
+ const redirectButton = document.getElementById('vpago_redirect_button');
9
+
10
+ redirectButton.textContent = buttonLabel;
11
+ redirectButton.addEventListener('click', onClick);
12
+
13
+ loadingContainer.style.display = 'none';
14
+ redirectContainer.style.removeProperty('display');
15
+ }
16
+
6
17
  document.addEventListener("DOMContentLoaded", () => {
7
18
  const platform = "<%= params[:platform] %>";
8
19
  const redirectUrls = <%= redirect_urls.to_json.html_safe %>;
9
- const confirmButton = document.getElementById("confirm-payment-button");
10
- const firebaseConfigs = <%= Rails.application.credentials.firebase_web_config.to_json.html_safe %>;
11
- const docRefPath = "<%= @payment.user_informer.document_reference_path %>";
12
- const successUrl = "<%= @payment.success_url %>";
13
20
 
14
21
  function setupConfirmPaymentButton() {
15
22
  if (platform === "app") {
@@ -17,21 +24,11 @@
17
24
  setTimeout(() => {
18
25
  window.location.href = redirectUrls.webview;
19
26
  }, 1500);
20
- } else if (confirmButton) {
21
- confirmButton.addEventListener("click", () => {
22
- window.open(redirectUrls.webview, '_blank');
23
- });
24
- }
25
- }
26
-
27
- function setupPaymentProcessingListener() {
28
- if (window.initPaymentProcessingListener) {
29
- window.initPaymentProcessingListener(firebaseConfigs, docRefPath, successUrl);
30
27
  } else {
31
- console.warn("initPaymentProcessingListener is not defined");
28
+ window.open(redirectUrls.webview, '_blank');
32
29
  }
33
30
  }
31
+
34
32
  setupConfirmPaymentButton();
35
- setupPaymentProcessingListener();
36
33
  });
37
34
  </script>
@@ -11,6 +11,18 @@ end
11
11
  %>
12
12
 
13
13
  <script>
14
+ function showRedirectContainer(buttonLabel, onClick) {
15
+ const loadingContainer = document.getElementById('vpago_loading_container');
16
+ const redirectContainer = document.getElementById('vpago_redirect_container');
17
+ const redirectButton = document.getElementById('vpago_redirect_button');
18
+
19
+ redirectButton.textContent = buttonLabel;
20
+ redirectButton.addEventListener('click', onClick);
21
+
22
+ loadingContainer.style.display = 'none';
23
+ redirectContainer.style.removeProperty('display');
24
+ }
25
+
14
26
  const platform = "<%= params[:platform] %>";
15
27
  const deeplinkUrl = "<%= raw(result[:deeplink_url]) %>";
16
28
  const webUrl = "<%= raw(result[:web_url]) %>";
@@ -25,12 +37,14 @@ end
25
37
 
26
38
  case "deeplink":
27
39
  redirectTo(deeplinkUrl);
40
+ showRedirectContainer("Continue", () => redirectTo(deeplinkUrl));
28
41
  break;
29
42
 
30
43
  case "all":
31
- if (platform === "app") redirectTo(deeplinkUrl);
32
- else if (platform === "web") redirectTo(webUrl);
33
- else console.error("Unknown platform:", platform);
44
+ redirectTo(deeplinkUrl);
45
+ setTimeout(() => {
46
+ redirectTo(webUrl);
47
+ }, 1500);
34
48
  break;
35
49
 
36
50
  default:
@@ -1,13 +1,23 @@
1
1
  <% @checkout = ::Vpago::VattanacMiniApp::Checkout.new(@payment) %>
2
- <% @payment.user_informer.payment_is_processing(processing: true) %>
3
2
 
4
3
  <p id="unsupported-message"
5
4
  style="margin-top: 10px; font-weight: bold; color: red; display: none;">
6
5
  </p>
7
6
 
8
7
  <script>
9
- document.addEventListener("DOMContentLoaded", () => {
8
+ function showRedirectContainer(buttonLabel, onClick) {
9
+ const loadingContainer = document.getElementById('vpago_loading_container');
10
+ const redirectContainer = document.getElementById('vpago_redirect_container');
11
+ const redirectButton = document.getElementById('vpago_redirect_button');
12
+
13
+ redirectButton.textContent = buttonLabel;
14
+ redirectButton.addEventListener('click', onClick);
10
15
 
16
+ loadingContainer.style.display = 'none';
17
+ redirectContainer.style.removeProperty('display');
18
+ }
19
+
20
+ document.addEventListener("DOMContentLoaded", () => {
11
21
  const setupMiniAppPayment = () => {
12
22
  const detectMobileOS = () => {
13
23
  const ua = navigator.userAgent || navigator.vendor || window.opera;
@@ -24,8 +34,10 @@
24
34
 
25
35
  if (os === 'iOS' && window.webkit?.messageHandlers?.startPayment) {
26
36
  window.webkit.messageHandlers.startPayment.postMessage(JSON.stringify(payload));
37
+ showRedirectContainer("Continue", () => window.webkit.messageHandlers.startPayment.postMessage(JSON.stringify(payload)));
27
38
  } else if (os === 'Android' && window.AndroidInterface?.startPayment) {
28
39
  window.AndroidInterface.startPayment(JSON.stringify(payload));
40
+ showRedirectContainer("Continue", () => window.AndroidInterface.startPayment(JSON.stringify(payload)));
29
41
  } else {
30
42
  const unsupported = document.getElementById('unsupported-message');
31
43
  unsupported.style.display = 'block';
@@ -33,20 +45,6 @@
33
45
  }
34
46
  };
35
47
 
36
- const setupPaymentProcessingListener = () => {
37
- const firebaseConfigs = <%= Rails.application.credentials.firebase_web_config.to_json.html_safe %>;
38
- const documentReferencePath = "<%= @payment.user_informer.document_reference_path %>";
39
- const successUrl = "<%= @payment.success_url %>";
40
-
41
- if (window.initPaymentProcessingListener) {
42
- window.initPaymentProcessingListener(firebaseConfigs, documentReferencePath, successUrl);
43
- } else {
44
- console.log("initPaymentProcessingListener is not defined");
45
- }
46
- };
47
-
48
- setupMiniAppPayment();
49
- setupPaymentProcessingListener();
50
-
48
+ setupMiniAppPayment();
51
49
  });
52
50
  </script>
@@ -14,6 +14,26 @@
14
14
  retryDelayMs: 1000,
15
15
  })
16
16
 
17
+ // This block in /processing is purely JUST for review/audit purposes (ABA requirement).
18
+ // It checks the bank transaction status periodically without actually
19
+ // triggering payment processing again. By the time the user lands
20
+ // on this page, the transaction is normally complete or failed.
21
+ //
22
+ // What it does:
23
+ // 1. Logs or updates the UI with transaction status for review.
24
+ // 2. Provides reassurance that the bank transaction was confirmed.
25
+ // 3. Optionally, could allow a manual retry button if needed.
26
+ //
27
+ // What it does NOT do:
28
+ // - It does NOT call requestProcessPayment again (payment job is
29
+ // already unique and idempotent elsewhere).
30
+ // - It does NOT retry processing unnecessarily.
31
+ window.checkTransactionPeriodically({
32
+ checkTransactionUrl: "<%= raw @payment.check_transaction_url %>",
33
+ onFailure: () => console.log("Bank transaction failed"),
34
+ onSuccess: () => console.log("Bank transaction confirmed"),
35
+ })
36
+
17
37
  window.listenToProcessingState({
18
38
  firebaseConfigs: firebaseConfigs,
19
39
  documentReferencePath: "<%= payment.user_informer.document_reference_path %>",
data/config/routes.rb CHANGED
@@ -20,10 +20,10 @@ Spree::Core::Engine.add_routes do
20
20
  resource :vpago_payments do
21
21
  collection do
22
22
  get :checkout
23
- get :check_transaction
24
23
  get :processing
25
24
  get :success
26
25
 
26
+ match :check_transaction, via: %i[get]
27
27
  match :process_payment, via: %i[get post]
28
28
  match :true_money_process_payment, via: %i[get post]
29
29
  end
@@ -1,7 +1,7 @@
1
1
  module SpreeVpago
2
2
  module_function
3
3
 
4
- VERSION = '2.2.2-pre19'.freeze
4
+ VERSION = '2.2.2-pre21'.freeze
5
5
 
6
6
  def version
7
7
  Gem::Version.new VERSION
@@ -27,7 +27,7 @@ module Vpago
27
27
  end
28
28
 
29
29
  def checkout_url
30
- "#{host}#{ENV.fetch('PAYWAY_CHECKOUT_PATH', nil)}"
30
+ "#{host}/api/payment-gateway/v1/payments/purchase"
31
31
  end
32
32
 
33
33
  alias action_url checkout_url
@@ -36,7 +36,7 @@ module Vpago
36
36
  end
37
37
 
38
38
  def cancel_url
39
- "#{host}#{ENV.fetch('PAYWAY_V2_PRE_AUTH_CANCEL_PATH')}"
39
+ "#{host}/api/merchant-portal/merchant-access/online-transaction/pre-auth-cancellation"
40
40
  end
41
41
  end
42
42
  end
@@ -58,7 +58,7 @@ module Vpago
58
58
  end
59
59
 
60
60
  def complete_url
61
- "#{host}#{ENV.fetch('PAYWAY_V2_PRE_AUTH_COMPLETE_PATH')}"
61
+ "#{host}/api/merchant-portal/merchant-access/online-transaction/pre-auth-completion"
62
62
  end
63
63
 
64
64
  def success?
@@ -76,9 +76,9 @@ module Vpago
76
76
  # somehow php counter part are not able to decode if the \n present.
77
77
  hash.delete("\n")
78
78
  end
79
-
79
+
80
80
  def check_transaction_url
81
- "#{host}#{ENV.fetch('PAYWAY_CHECK_TRANSACTION_PATH', nil)}"
81
+ "#{host}/api/payment-gateway/v1/payments/check-transaction-2"
82
82
  end
83
83
  end
84
84
  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.2.2.pre.pre19
4
+ version: 2.2.2.pre.pre21
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-25 00:00:00.000000000 Z
11
+ date: 2026-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -146,7 +146,7 @@ files:
146
146
  - app/assets/images/payment_logos/wingpay.png
147
147
  - app/assets/images/vpago/payway/abapay.png
148
148
  - app/assets/images/vpago/payway/cards.png
149
- - app/assets/javascripts/vpago/vpago_payments/payment_processing_listener.js
149
+ - app/assets/javascripts/vpago/vpago_payments/check_transaction_periodically.js
150
150
  - app/assets/javascripts/vpago/vpago_payments/request_process_payment.js
151
151
  - app/assets/javascripts/vpago/vpago_payments/user_informers/firebase.js
152
152
  - app/controllers/.gitkeep
@@ -1,95 +0,0 @@
1
- window.initPaymentProcessingListener = async function (
2
- firebaseConfigs,
3
- documentReferencePath,
4
- successUrl
5
- ) {
6
- function log(status, processing, reasonCode, reasonMessage) {
7
- console.log(`Status: ${status}`);
8
- console.log(`Reason Code: ${reasonCode}`);
9
- console.log(
10
- `Processing: ${processing ? "Processing..." : "No more process."}`
11
- );
12
- console.log(`Reason Message: ${reasonMessage}`);
13
- }
14
-
15
- window.listenToProcessingState({
16
- firebaseConfigs,
17
- documentReferencePath,
18
-
19
- onPaymentIsProcessing(
20
- orderState,
21
- paymentState,
22
- processing,
23
- reasonCode,
24
- reasonMessage
25
- ) {
26
- log("Payment is processing", processing, reasonCode, reasonMessage);
27
- },
28
-
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
- ) {
46
- log("Order is processing", processing, reasonCode, reasonMessage);
47
- },
48
-
49
- onOrderIsCompleted(
50
- orderState,
51
- paymentState,
52
- processing,
53
- reasonCode,
54
- reasonMessage
55
- ) {
56
- log("Order is completed", processing, reasonCode, reasonMessage);
57
- },
58
-
59
- onOrderProcessFailed(
60
- orderState,
61
- paymentState,
62
- processing,
63
- reasonCode,
64
- reasonMessage
65
- ) {
66
- log("Order process failed", processing, reasonCode, reasonMessage);
67
- },
68
-
69
- onPaymentProcessFailed(
70
- orderState,
71
- paymentState,
72
- processing,
73
- reasonCode,
74
- reasonMessage
75
- ) {
76
- log("Payment process failed", processing, reasonCode, reasonMessage);
77
- },
78
-
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
- );
92
- window.location.href = successUrl;
93
- },
94
- });
95
- };