spree_vpago 2.3.14.pre.pre1 → 2.4.0.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/config/spree_vpago_manifest.js +0 -2
  4. data/app/controllers/spree/admin/payment_payway_queriers_controller.rb +0 -7
  5. data/app/controllers/spree/{mini_app → miniapp}/acledas_controller.rb +6 -12
  6. data/app/controllers/spree/vpago_payments_controller.rb +3 -15
  7. data/app/helpers/vpago/vpago_payments_helper.rb +0 -6
  8. data/app/models/spree/gateway/acleda_mini_app.rb +6 -4
  9. data/app/models/spree/payment_method/cash_on.rb +0 -7
  10. data/app/models/vpago/order_decorator.rb +1 -1
  11. data/app/models/vpago/payment_decorator.rb +0 -4
  12. data/app/models/vpago/payment_method_decorator.rb +0 -14
  13. data/app/serializers/spree/v2/storefront/payment_method_serializer_decorator.rb +0 -6
  14. data/app/services/vpago/acleda_mini_app/session/create.rb +23 -45
  15. data/app/services/vpago/payment_finder.rb +0 -11
  16. data/app/services/vpago/payment_redirect_handler.rb +6 -6
  17. data/app/views/spree/admin/payments/source_views/_acleda_v2.html.erb +2 -2
  18. data/app/views/spree/admin/payments/source_views/_vattanac.html.erb +3 -3
  19. data/app/views/spree/vpago_payments/forms/spree/gateway/_acleda_mini_app.html.erb +79 -80
  20. data/app/views/spree/vpago_payments/success.html.erb +0 -2
  21. data/config/locales/en.yml +0 -1
  22. data/config/locales/km.yml +0 -1
  23. data/config/routes.rb +4 -17
  24. data/lib/spree_vpago/engine.rb +0 -1
  25. data/lib/spree_vpago/version.rb +1 -1
  26. data/lib/vpago/acleda_mini_app/checkout.rb +9 -5
  27. data/lib/vpago/payway_v2/payment_request_updater.rb +0 -5
  28. data/lib/vpago/payway_v2/transaction_status.rb +0 -2
  29. data/lib/vpago/payway_v2/transaction_status_v2.rb +0 -2
  30. data/lib/vpago/true_money/base.rb +1 -1
  31. data/lib/vpago/true_money/transaction_status.rb +1 -1
  32. data/lib/vpago/vattanac/base.rb +2 -3
  33. data/lib/vpago/vattanac/transaction_status.rb +1 -17
  34. data/lib/vpago/wing_sdk/base.rb +1 -1
  35. metadata +3 -27
  36. data/app/assets/javascripts/vpago/wing_mini_app/login.js +0 -75
  37. data/app/assets/javascripts/vpago/wing_mini_app/web_jsbridge.bundle.js +0 -1507
  38. data/app/controllers/concerns/spree/mini_app_concern.rb +0 -25
  39. data/app/controllers/spree/admin/payment_acleda_v2_base_controller.rb +0 -21
  40. data/app/controllers/spree/admin/payment_acleda_v2_checkers_controller.rb +0 -26
  41. data/app/controllers/spree/admin/payment_acleda_v2_queriers_controller.rb +0 -38
  42. data/app/controllers/spree/admin/payment_vattanac_base_controller.rb +0 -21
  43. data/app/controllers/spree/admin/payment_vattanac_checkers_controller.rb +0 -26
  44. data/app/controllers/spree/admin/payment_vattanac_queriers_controller.rb +0 -38
  45. data/app/controllers/spree/mini_app/wings_controller.rb +0 -58
  46. data/app/models/spree/gateway/wing_mini_app.rb +0 -58
  47. data/app/services/vpago/wing_mini_app/session/create.rb +0 -55
  48. data/app/views/layouts/wing_mini_app.html.erb +0 -20
  49. data/app/views/spree/admin/payments/source_views/_wing_mini_app.html.erb +0 -6
  50. data/app/views/spree/mini_app/wings/show.html.erb +0 -44
  51. data/app/views/spree/vpago_payments/_success_script.html.erb +0 -7
  52. data/app/views/spree/vpago_payments/forms/spree/gateway/_wing_mini_app.html.erb +0 -88
  53. data/app/views/spree/vpago_payments/processing_scripts/spree/gateway/_wing_mini_app.html.erb +0 -15
  54. data/lib/vpago/acleda_mini_app/session_hash_validator.rb +0 -35
  55. data/lib/vpago/acleda_v2/payment_request_updater.rb +0 -54
  56. data/lib/vpago/http_timeouts.rb +0 -6
  57. data/lib/vpago/vattanac/payment_request_updater.rb +0 -54
  58. data/lib/vpago/wing_mini_app/checkout.rb +0 -60
  59. data/lib/vpago/wing_mini_app/webhook_verifier.rb +0 -78
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c9bf6d8bf472a0e5c04edafc4a8b12713b2c4576a814d75a88fbbe3da8d807e
4
- data.tar.gz: 27e3a3394b762dc160e77f37740722e6142e3c61d64faae197c3320de77092dc
3
+ metadata.gz: 2b281c3ae420620dbd3d1da80ed2bc713c9283292484c1812cd1e9c979e6bf52
4
+ data.tar.gz: 70b7603d37c2c5e8f434e1ab333e622619296fd1ced75642ea9c0512a7591eb1
5
5
  SHA512:
6
- metadata.gz: 78302ef6b401c653e4923a343d5d1883d9708cbed5996a899aa62302e0109cc04bce03f8a1f915347cf6d437d2b8d282af8600006c3398bc83e722b6cc0320e3
7
- data.tar.gz: 3569b2e226550873e45c449ad72a3cd70d1889a9665a9bebb0f2632f55d4f4a4824239b9a9ad25fedb1bc11bc70882315d23ee7400cff77aea4b008218bc17b8
6
+ metadata.gz: 718d7eb47ff0a91b7cbcd43d13bd28f4808e008bed9ebee489c913af2d18d5069b5bdc4f06cca8e57693559e96da51a7af8c6e8843e5e01952898c7853c919e1
7
+ data.tar.gz: dd5f68ec20858da961d36f88a488bd3906d0623baa9f88a3811bbe10957ebd93bf891d047df9b2b8123a549d6881ef8ef32e1c7674144e6dd7901c779e79ae8b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- spree_vpago (2.3.14.pre.pre1)
4
+ spree_vpago (2.4.0.pre.pre1)
5
5
  faraday
6
6
  google-cloud-firestore
7
7
  spree_api (>= 4.5)
@@ -2,5 +2,3 @@
2
2
  //= link vpago/vpago_payments/check_transaction_periodically.js
3
3
  //= link vpago/vpago_payments/request_process_payment.js
4
4
  //= link vpago/vpago_payments/user_informers/firebase.js
5
- //= link vpago/wing_mini_app/web_jsbridge.bundle.js
6
- //= link vpago/wing_mini_app/login.js
@@ -16,13 +16,6 @@ module Spree
16
16
  flash[:error] = Spree.t('vpago.payments.payment_not_found_with_error', error: tran_status.error_message)
17
17
  end
18
18
 
19
- redirect_to admin_order_payment_path(order_id: @payment.order.number, id: @payment.number)
20
- rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e
21
- VpagoLogger.error(
22
- label: 'Spree::Admin::PaymentPaywayQueriersController#show gateway_timeout',
23
- data: { payment_number: @payment.number, error_class: e.class.name, error_message: e.message }
24
- )
25
- flash[:error] = Spree.t('vpago.payments.gateway_timeout')
26
19
  redirect_to admin_order_payment_path(order_id: @payment.order.number, id: @payment.number)
27
20
  end
28
21
 
@@ -1,27 +1,21 @@
1
1
  module Spree
2
- module MiniApp
2
+ module Miniapp
3
3
  # Partner Session Initialization API (ACLEDA Mini App Integration Spec, Step 01).
4
- # ACLEDA POSTs { phone, first_name, last_name } signed with X-Acleda-Signature, and
5
- # gets back a session-based miniAppUrl to open in the WebView.
4
+ # ACLEDA (server-to-server) POSTs { phone, first_name, last_name }; we return a
5
+ # session-based miniAppUrl for ACLEDA to open in the WebView.
6
6
  class AcledasController < Spree::Api::V2::BaseController
7
- include Spree::MiniAppConcern
8
-
9
- PROVIDER = 'acleda_bank'.freeze
10
-
11
- # POST /mini_app/acleda
7
+ # POST /miniapp/acleda
12
8
  def create
13
9
  result = ::Vpago::AcledaMiniApp::Session::Create.call(
14
10
  phone: session_params[:phone],
15
11
  first_name: session_params[:first_name],
16
- last_name: session_params[:last_name],
17
- signature: request.headers['X-Acleda-Signature'],
18
- gate: session_gate(PROVIDER)
12
+ last_name: session_params[:last_name]
19
13
  )
20
14
 
21
15
  if result.success?
22
16
  render json: { message: 'SUCCESS', miniAppUrl: result.value[:mini_app_url] }
23
17
  else
24
- render json: { message: 'FAILED', error: result.error.to_s }, status: failure_status(result)
18
+ render json: { message: 'FAILED', error: result.error.to_s }, status: :unprocessable_entity
25
19
  end
26
20
  end
27
21
 
@@ -24,12 +24,6 @@ module Spree
24
24
  def processing
25
25
  @order = @payment.order
26
26
 
27
- # Common, not an edge case: the gateway callback or the checkout page's transaction
28
- # checker often completes the order before the browser lands here. Skipping this page
29
- # also skips its Firebase listener, so success must post order_is_completed itself
30
- # (render_success_script) or an embedding iframe modal never closes.
31
- return redirect_to @payment.success_url, allow_other_host: true if @order.completed?
32
-
33
27
  VpagoLogger.log(label: 'Spree::VpagoPaymentsController#processing', data: vpago_log_context)
34
28
  end
35
29
 
@@ -66,12 +60,6 @@ module Spree
66
60
  else
67
61
  render json: { status: :pending }, status: :ok
68
62
  end
69
- rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e
70
- VpagoLogger.error(
71
- label: 'Spree::VpagoPaymentsController#check_transaction gateway_timeout',
72
- data: vpago_log_context(error_class: e.class.name, error_message: e.message)
73
- )
74
- render json: { status: :pending }, status: :ok
75
63
  end
76
64
 
77
65
  # POST
@@ -158,9 +146,9 @@ module Spree
158
146
  raise ActiveRecord::RecordNotFound unless @payment.present?
159
147
  end
160
148
 
161
- # These pages are embedded in iframes on many partner sites we don't
162
- # enumerate, so framing is left unrestricted: no frame-ancestors policy, and
163
- # Rails' default SAMEORIGIN header is dropped for the iframe actions.
149
+ # frame-ancestors (set above) supersedes X-Frame-Options in modern
150
+ # browsers, but the default SAMEORIGIN header would still block legacy
151
+ # browsers from embedding these pages, so drop it for the iframe actions.
164
152
  def allow_iframe_embedding
165
153
  response.headers.delete('X-Frame-Options')
166
154
  end
@@ -13,12 +13,6 @@ module Vpago
13
13
  render partial: 'spree/vpago_payments/transaction_checker'
14
14
  end
15
15
 
16
- # Host apps override success.html.erb; call this from every override so an
17
- # embedding iframe modal still learns the order completed.
18
- def render_success_script
19
- render partial: 'spree/vpago_payments/success_script'
20
- end
21
-
22
16
  # Each payment method may have their own additional processing script,
23
17
  # so we will look for the partial based on the payment method class name.
24
18
  #
@@ -1,16 +1,18 @@
1
1
  module Spree
2
2
  # ACLEDA Mini App payment method.
3
3
  #
4
- # Reuses the ACLEDA V2 integration in deeplink mode: openSessionV2 returns a deeplink the
5
- # payment page opens as a top-frame navigation, which ACLEDA's own WebView turns into an app
6
- # launch. Server-side verification reuses AcledaV2's #check_transaction (getTxnStatus).
4
+ # Reuses the ACLEDA V2 integration in deeplink mode: the deeplink produced by
5
+ # ACLEDA's openSessionV2 API is handed to the ACLEDA super-app through the
6
+ # `acledaJSBridge` (startPayment) instead of redirecting the page. Server-side
7
+ # verification reuses AcledaV2's #check_transaction (getTxnStatus).
7
8
  class Gateway::AcledaMiniApp < Gateway::AcledaV2
8
9
  # override: partial to render in admin / checkout form lookup
9
10
  def method_type
10
11
  'acleda_mini_app'
11
12
  end
12
13
 
13
- # The mini app always pays by deeplink, so admins don't need to set the ACLEDA V2 mode.
14
+ # The mini app always uses the deeplink + JS bridge, so admins don't need to
15
+ # set the ACLEDA V2 mode.
14
16
  def deeplink?
15
17
  true
16
18
  end
@@ -11,12 +11,5 @@ module Spree
11
11
  def auto_capture?
12
12
  true
13
13
  end
14
-
15
- # override: Spree::Payment#cancel! calls payment_method.cancel(response_code, payment).
16
- # Cash has no gateway to void against, so acknowledge success and let the payment
17
- # transition to `void`.
18
- def cancel(_response_code, _payment = nil)
19
- ActiveMerchant::Billing::Response.new(true, 'Cash On: Payment has been canceled.')
20
- end
21
14
  end
22
15
  end
@@ -102,7 +102,7 @@ module Vpago
102
102
  # override this method if you want flexibility
103
103
  # for example, host per payment method or tenant
104
104
  def payment_host
105
- ENV.fetch('PAYMENT_BASE_URL')
105
+ ENV.fetch('DEFAULT_URL_HOST')
106
106
  end
107
107
  end
108
108
  end
@@ -113,10 +113,6 @@ module Vpago
113
113
  payment_method.type_acleda_mini_app?
114
114
  end
115
115
 
116
- def wing_mini_app_payment?
117
- payment_method.type_wing_mini_app?
118
- end
119
-
120
116
  def check_payment?
121
117
  payment_method.type_check?
122
118
  end
@@ -10,7 +10,6 @@ module Vpago
10
10
  TYPE_TRUE_MONEY = 'Spree::Gateway::TrueMoney'.freeze
11
11
  TYPE_VATTANAC = 'Spree::Gateway::Vattanac'.freeze
12
12
  TYPE_VATTANAC_MINI_APP = 'Spree::Gateway::VattanacMiniApp'.freeze
13
- TYPE_WING_MINI_APP = 'Spree::Gateway::WingMiniApp'.freeze
14
13
  TYPE_CASH_ON = 'Spree::PaymentMethod::CashOn'.freeze
15
14
 
16
15
  def self.prepended(base)
@@ -29,7 +28,6 @@ module Vpago
29
28
  Spree::PaymentMethod::TYPE_ACLEDA_MINI_APP,
30
29
  Spree::PaymentMethod::TYPE_VATTANAC,
31
30
  Spree::PaymentMethod::TYPE_VATTANAC_MINI_APP,
32
- Spree::PaymentMethod::TYPE_WING_MINI_APP,
33
31
  Spree::PaymentMethod::TYPE_TRUE_MONEY,
34
32
  Spree::PaymentMethod::TYPE_CASH_ON
35
33
  ]
@@ -84,10 +82,6 @@ module Vpago
84
82
  ::Vpago::Acleda::PaymentRequestUpdater
85
83
  elsif type_acleda_mobile?
86
84
  ::Vpago::AcledaMobile::PaymentRequestUpdater
87
- elsif type_acleda_v2?
88
- ::Vpago::AcledaV2::PaymentRequestUpdater
89
- elsif type_vattanac?
90
- ::Vpago::Vattanac::PaymentRequestUpdater
91
85
  end
92
86
  end
93
87
 
@@ -119,18 +113,10 @@ module Vpago
119
113
  type == Spree::PaymentMethod::TYPE_WINGSDK
120
114
  end
121
115
 
122
- def type_vattanac?
123
- type == Spree::PaymentMethod::TYPE_VATTANAC
124
- end
125
-
126
116
  def type_vattanac_mini_app?
127
117
  type == Spree::PaymentMethod::TYPE_VATTANAC_MINI_APP
128
118
  end
129
119
 
130
- def type_wing_mini_app?
131
- type == Spree::PaymentMethod::TYPE_WING_MINI_APP
132
- end
133
-
134
120
  def type_true_money?
135
121
  type == Spree::PaymentMethod::TYPE_TRUE_MONEY
136
122
  end
@@ -14,12 +14,6 @@ module Spree
14
14
 
15
15
  pref.blank? || pref[:payment_option].blank? ? payment_method.method_type : pref[:payment_option]
16
16
  end
17
-
18
- # Overried the payment method type to show all payment methods as one list on App.
19
- # Controlled by ENV["PAYMENT_METHOD_VIEW"] = "split" | "join" (default: "join").
20
- base.attribute :type do |payment_method|
21
- ENV.fetch('PAYMENT_METHOD_VIEW', 'join') == 'split' ? payment_method.type : 'payment_method'
22
- end
23
17
  end
24
18
  end
25
19
  end
@@ -3,32 +3,20 @@ module Vpago
3
3
  module Session
4
4
  # Partner Session Initialization (ACLEDA Mini App Integration Spec, Step 01).
5
5
  #
6
- # ACLEDA POSTs { phone, first_name, last_name } with an X-Acleda-Signature HMAC
7
- # (see SessionHashValidator), checked before we touch the user so nobody can mint
8
- # a session for an arbitrary phone. We then find or create the customer, tag them
9
- # with the acleda_bank identity, and hand back a miniAppUrl carrying a session JWT.
6
+ # ACLEDA calls POST /miniapp/acleda with { phone, first_name, last_name }.
7
+ # We find or create the customer (tagged with the acleda_bank identity),
8
+ # mint a session JWT, and return a session-based miniAppUrl for ACLEDA to
9
+ # open in the WebView. The generic session_id OAuth grant validates the JWT
10
+ # when the WebView later authenticates.
10
11
  class Create
11
12
  prepend Spree::ServiceModule::Base
12
13
 
13
- # `signature`, not `hash`: an attr_reader named `hash` overrides Object#hash and
14
- # silently breaks this object as a Hash key.
15
- #
16
- # `gate` answers who is calling, from where and how often. The controller builds it:
17
- # the key header and the address exist only on the request.
18
- def call(gate:, phone: nil, first_name: nil, last_name: nil, signature: nil)
14
+ def call(phone: nil, first_name: nil, last_name: nil)
19
15
  return failure(nil, 'phone is required') if phone.blank?
20
16
 
21
17
  @phone = phone
22
18
  @first_name = first_name
23
19
  @last_name = last_name
24
- @signature = signature
25
- @gate = gate
26
-
27
- return failure({ status: :unauthorized }, 'missing or unknown api key') unless gate.api_key_accepted?
28
- return failure({ status: :forbidden }, 'address not allowed') unless gate.address_allowed?
29
- return failure(nil, 'invalid signature') unless valid_signature?
30
- return failure(nil, 'phone is invalid') if intel_phone.blank?
31
- return failure({ status: :too_many_requests }, 'too many requests') if gate.session_exceeded?(phone: intel_phone)
32
20
 
33
21
  user = find_or_create_user
34
22
  return failure(user, 'user creation failed') if user.nil? || !user.persisted?
@@ -38,20 +26,7 @@ module Vpago
38
26
 
39
27
  private
40
28
 
41
- attr_reader :phone, :first_name, :last_name, :signature, :gate
42
-
43
- # Only asked where this call would mint an account — the damage a leaked key does.
44
- # Returning customers never spend that quota.
45
- def new_user_throttled?
46
- !acleda_identity.persisted? && find_existing_user.nil? && gate.new_user_exceeded?(phone: intel_phone)
47
- end
48
-
49
- def valid_signature?
50
- ::Vpago::AcledaMiniApp::SessionHashValidator.new(
51
- phone: phone, first_name: first_name, last_name: last_name, hash: signature,
52
- secret_key: gate.mini_app&.preferred_secret_key
53
- ).call
54
- end
29
+ attr_reader :phone, :first_name, :last_name
55
30
 
56
31
  def find_or_create_user
57
32
  identity = acleda_identity
@@ -59,10 +34,9 @@ module Vpago
59
34
  # Returning customer: the ACLEDA identity already points at a user.
60
35
  return identity.user if identity.persisted?
61
36
 
62
- return nil if new_user_throttled?
63
-
64
- # New to ACLEDA: reuse a customer matched by phone, otherwise build one, then
65
- # tag them so the lookup above hits next time.
37
+ # New to ACLEDA: reuse a customer matched by phone, otherwise build one,
38
+ # then tag them with the acleda_bank identity so the lookup above hits
39
+ # next time.
66
40
  user = find_existing_user || build_user
67
41
  identity.name = full_name
68
42
  user.user_identity_providers << identity
@@ -71,17 +45,14 @@ module Vpago
71
45
  user
72
46
  end
73
47
 
74
- # Memoized: #new_user_throttled? asks the same questions, and this row is the one
75
- # that gets tagged onto the user.
76
48
  def acleda_identity
77
- @acleda_identity ||= SpreeCmCommissioner::UserIdentityProvider.acleda_bank.find_or_initialize_by(sub: phone)
49
+ SpreeCmCommissioner::UserIdentityProvider.acleda_bank.find_or_initialize_by(sub: phone)
78
50
  end
79
51
 
80
52
  def find_existing_user
81
- return @find_existing_user if defined?(@find_existing_user)
82
- return @find_existing_user = nil if intel_phone.blank?
53
+ return if intel_phone.blank?
83
54
 
84
- @find_existing_user = Spree::User.by_non_tenant.find_by(intel_phone_number: intel_phone)
55
+ Spree::User.by_non_tenant.find_by(intel_phone_number: intel_phone)
85
56
  end
86
57
 
87
58
  def build_user
@@ -96,14 +67,15 @@ module Vpago
96
67
  end
97
68
 
98
69
  def mini_app_url(user)
99
- # A JWT signed with the user's secure_token. `session_key`, not `session_id`, so
100
- # the OAuth grant routes it to the ACLEDA authenticator.
70
+ # A JWT signed with the user's secure_token. Passed as `session_key` (not
71
+ # `session_id`) so the OAuth grant routes it to the ACLEDA authenticator,
72
+ # which enforces the signature.
101
73
  session_key = SpreeCmCommissioner::UserSessionJwtToken.encode(
102
74
  { user_id: user.id },
103
75
  user.reload.secure_token
104
76
  )
105
77
 
106
- "#{Spree::Store.default.formatted_url}/mini_app/acleda?session_key=#{session_key}"
78
+ "#{Spree::Store.default.formatted_url}/mini_app/acleda?session_key=#{session_key}&ds=#{CGI.escape(disable_services)}"
107
79
  end
108
80
 
109
81
  def full_name
@@ -115,6 +87,12 @@ module Vpago
115
87
 
116
88
  @intel_phone = SpreeCmCommissioner::PhoneNumberParser.call(phone_number: phone).intel_phone_number
117
89
  end
90
+
91
+ # Services to disable in the mini app storefront. Configurable per ACLEDA's
92
+ # requirement; defaults to none.
93
+ def disable_services
94
+ ENV.fetch('ACLEDA_MINI_APP_DISABLE_SERVICES', '')
95
+ end
118
96
  end
119
97
  end
120
98
  end
@@ -19,13 +19,6 @@ module Vpago
19
19
  payment = Spree::Payment.find_by!(number: payload['paymentId'])
20
20
  payment.update(transaction_response: payload)
21
21
  payment
22
- elsif wing_mini_app_callback?
23
- payment = Spree::Payment.find_by!(number: params_hash[:orderRef])
24
- verifier = Vpago::WingMiniApp::WebhookVerifier.new(params_hash, payment)
25
- raise "Wing Mini App webhook: verification failed for payment #{payment.number}" unless verifier.valid?
26
-
27
- payment.update(transaction_response: params_hash)
28
- payment
29
22
  elsif acleda_v2_callback?
30
23
  # ACLEDA V2's static portal callback echoes back _transactionid, which is
31
24
  # the payment number we sent as transactionID/txid at openSessionV2.
@@ -49,9 +42,5 @@ module Vpago
49
42
  def acleda_v2_callback?
50
43
  params_hash[:_paymenttokenid].present?
51
44
  end
52
-
53
- def wing_mini_app_callback?
54
- params_hash[:orderRef].present? && params_hash[:secretKey].present?
55
- end
56
45
  end
57
46
  end
@@ -50,7 +50,7 @@ module Vpago
50
50
 
51
51
  def process_acleda_gateway
52
52
  data = {
53
- href: "#{ENV.fetch('PAYMENT_BASE_URL', nil)}/acleda_redirects?payment_number=#{@payment.number}"
53
+ href: "#{ENV.fetch('DEFAULT_URL_HOST', nil)}/acleda_redirects?payment_number=#{@payment.number}"
54
54
  }
55
55
 
56
56
  @redirect_options = data
@@ -81,7 +81,7 @@ module Vpago
81
81
  def process_payway_v2_card
82
82
  ## TO DO: generate redirect url
83
83
  data = {
84
- href: "#{ENV.fetch('PAYMENT_BASE_URL', nil)}/payway_v2_card_popups?payment_number=#{@payment.number}"
84
+ href: "#{ENV.fetch('DEFAULT_URL_HOST', nil)}/payway_v2_card_popups?payment_number=#{@payment.number}"
85
85
  }
86
86
 
87
87
  @redirect_options = data
@@ -115,7 +115,7 @@ module Vpago
115
115
  end
116
116
 
117
117
  conn.post(abapay_payment.checkout_url, gateway_params) do |request|
118
- request.headers['Referer'] = ENV.fetch('PAYMENT_BASE_URL', nil)
118
+ request.headers['Referer'] = ENV.fetch('DEFAULT_URL_HOST', nil)
119
119
  end
120
120
  end
121
121
 
@@ -127,7 +127,7 @@ module Vpago
127
127
  def process_payway_card
128
128
  ## TO DO: generate redirect url
129
129
  data = {
130
- href: "#{ENV.fetch('PAYMENT_BASE_URL', nil)}/payway_card_popups?payment_number=#{@payment.number}"
130
+ href: "#{ENV.fetch('DEFAULT_URL_HOST', nil)}/payway_card_popups?payment_number=#{@payment.number}"
131
131
  }
132
132
 
133
133
  @redirect_options = data
@@ -135,7 +135,7 @@ module Vpago
135
135
 
136
136
  def process_wing_gateway
137
137
  data = {
138
- href: "#{ENV.fetch('PAYMENT_BASE_URL', nil)}/wing_redirects?payment_number=#{@payment.number}"
138
+ href: "#{ENV.fetch('DEFAULT_URL_HOST', nil)}/wing_redirects?payment_number=#{@payment.number}"
139
139
  }
140
140
 
141
141
  @redirect_options = data
@@ -169,7 +169,7 @@ module Vpago
169
169
  end
170
170
 
171
171
  @response = conn.post(abapay_payment.action_url, gateway_params) do |request|
172
- request.headers['Referer'] = ENV.fetch('PAYMENT_BASE_URL', nil)
172
+ request.headers['Referer'] = ENV.fetch('DEFAULT_URL_HOST', nil)
173
173
  end
174
174
  end
175
175
 
@@ -1,5 +1,5 @@
1
1
  <%= render 'spree/admin/payments/source_views/vpago_payment_tmpl',
2
2
  payment: payment,
3
- check_status_url: admin_payment_acleda_v2_querier_path(payment),
4
- heal_payment_url: admin_payment_acleda_v2_checker_path(payment),
3
+ check_status_url: '',
4
+ heal_payment_url: '',
5
5
  manual_update_payment_url: '' %>
@@ -1,6 +1,6 @@
1
1
  <%= render 'spree/admin/payments/source_views/vpago_payment_tmpl',
2
- payment: payment,
3
- check_status_url: admin_payment_vattanac_querier_path(payment),
4
- heal_payment_url: admin_payment_vattanac_checker_path(payment),
2
+ payment: payment,
3
+ check_status_url: '',
4
+ heal_payment_url: '',
5
5
  manual_update_payment_url: ''
6
6
  %>
@@ -2,93 +2,92 @@
2
2
  opr_device = request.user_agent.to_s.downcase.match?(/iphone|ipad|ipod/) ? 'ios' : 'android'
3
3
  checkout = ::Vpago::AcledaMiniApp::Checkout.new(@payment, { platform: 'app', opr_device: opr_device })
4
4
 
5
- # openSessionV2 runs here and persists paymentTokenid for the later getTxnStatus check.
6
- # Rejected session or transport error both end with no deeplink — the one failure to render.
7
- acleda_deeplink =
8
- begin
9
- checkout.acleda_deeplink
10
- rescue StandardError => e
11
- Rails.logger.error("[Acleda] openSessionV2 raised: #{e.message}")
12
- nil
13
- end
14
-
15
- Rails.logger.error("[Acleda] openSessionV2 failed: #{checkout.error_message}") if checkout.failed?
5
+ begin
6
+ checkout.call
7
+ rescue => e
8
+ Rails.logger.error("Acleda Mini App checkout.call failed: #{e.message}")
9
+ end
16
10
  %>
17
11
 
18
- <p id="acleda-message"
12
+ <p id="unsupported-message"
19
13
  style="margin-top: 10px; font-weight: bold; color: red; display: none;">
20
14
  </p>
21
15
 
22
- <script>
23
- function showRedirectContainer(buttonLabel, onClick) {
24
- const loadingContainer = document.getElementById('vpago_loading_container');
25
- const redirectContainer = document.getElementById('vpago_redirect_container');
26
- const redirectButton = document.getElementById('vpago_redirect_button');
27
-
28
- redirectButton.textContent = buttonLabel;
29
- redirectButton.addEventListener('click', onClick);
30
-
31
- loadingContainer.style.display = 'none';
32
- redirectContainer.style.removeProperty('display');
33
- }
34
-
35
- function showError(detail) {
36
- console.error('[Acleda] ' + detail);
37
-
38
- const message = document.getElementById('acleda-message');
39
- message.innerText = "We couldn't start your ACLEDA payment.";
40
- message.style.display = 'block';
41
- }
42
-
43
- document.addEventListener('DOMContentLoaded', () => {
44
- const acledaDeeplink = "<%= raw acleda_deeplink %>";
45
-
46
- if (!acledaDeeplink) {
47
- showError('openSessionV2 returned no deeplinkUrl');
48
- return;
49
- }
50
-
51
- const framed = window.top !== window.self;
52
- const android = <%= (opr_device == 'android').to_json %>;
53
-
54
- // The spec's `acledaJSBridge` does not exist in ACLEDA's Mini App (`window.webkit` is
55
- // undefined), so they proposed this deeplink instead. getTxnStatus still confirms the
56
- // payment, already polled by check_transaction_periodically.js.
57
-
58
- // The three below open the wallet for the customer, so they tap Pay once and not twice.
59
- // Two rules shape them: ACLEDA's WebView only launches the app for a navigation on the
60
- // *top* frame, and a cross-origin iframe may navigate the top only while a tap's
61
- // activation lasts — which the button has and the automatic run does not.
62
- const openWallet = () => {
63
- window.top.location.href = acledaDeeplink;
64
- };
65
-
66
- // So ask our web app, the page embedding this one, which *is* the top frame. It obeys
67
- // only inside a bank mini app: anywhere else nothing intercepts the link and the checkout
68
- // would really travel to ACLEDA's Play Store listing. Silence is fine — the button still works.
69
- const askWebAppToOpen = () => {
70
- window.parent.postMessage({ code: 'open_deeplink', url: acledaDeeplink }, '*');
16
+ <% if checkout.failed? %>
17
+ <script>
18
+ console.error("[Vpago] Acleda Mini App checkout error: <%= j checkout.error_message.to_s %>");
19
+ document.addEventListener("DOMContentLoaded", () => {
20
+ const loadingContainer = document.getElementById('vpago_loading_container');
21
+ if (loadingContainer) {
22
+ loadingContainer.textContent = "We couldn't start your payment. Please go back and try again.";
23
+ }
24
+ });
25
+ </script>
26
+ <% else %>
27
+ <script>
28
+ // ACLEDA JS bridge (unified iOS + Android). Defined before use so it is
29
+ // available when the native layer injects its handlers.
30
+ window.acledaJSBridge = window.acledaJSBridge || {
31
+ call: function (action, data = {}) {
32
+ const payload = { action, data };
33
+
34
+ // iOS WKWebView
35
+ if (
36
+ window.webkit &&
37
+ window.webkit.messageHandlers &&
38
+ window.webkit.messageHandlers.acledaIOSJSBridge
39
+ ) {
40
+ window.webkit.messageHandlers.acledaIOSJSBridge.postMessage(payload);
41
+ return true;
42
+ }
43
+
44
+ // Android WebView
45
+ if (
46
+ window.acledaAndroidJSBridge &&
47
+ typeof window.acledaAndroidJSBridge.postMessage === "function"
48
+ ) {
49
+ window.acledaAndroidJSBridge.postMessage(JSON.stringify(payload));
50
+ return true;
51
+ }
52
+
53
+ console.warn("[acledaJSBridge] Native bridge not found");
54
+ return false;
55
+ }
71
56
  };
72
57
 
73
- // iOS resolves the universal link before the request is made, so a throwaway frame opens
74
- // it. Android cannot: ACLEDA serves `frame-ancestors` on the link, so Chromium refuses to
75
- // frame it at all.
76
- const openThroughHiddenFrame = () => {
77
- const frame = document.createElement('iframe');
78
-
79
- frame.style.display = 'none';
80
- frame.src = acledaDeeplink;
58
+ function showRedirectContainer(buttonLabel, onClick) {
59
+ const loadingContainer = document.getElementById('vpago_loading_container');
60
+ const redirectContainer = document.getElementById('vpago_redirect_container');
61
+ const redirectButton = document.getElementById('vpago_redirect_button');
81
62
 
82
- document.body.appendChild(frame);
83
- setTimeout(() => frame.remove(), 1000);
84
- };
63
+ redirectButton.textContent = buttonLabel;
64
+ redirectButton.addEventListener('click', onClick);
85
65
 
86
- if (!framed) openWallet();
87
- else if (android) askWebAppToOpen();
88
- else openThroughHiddenFrame();
66
+ loadingContainer.style.display = 'none';
67
+ redirectContainer.style.removeProperty('display');
68
+ }
89
69
 
90
- // Straight away, not on re-show: opening the bank app only backgrounds this page, and
91
- // waiting for it to come back left the customer stuck on "Loading..." with no way out.
92
- showRedirectContainer('Pay with ACLEDA', openWallet);
93
- });
94
- </script>
70
+ document.addEventListener("DOMContentLoaded", () => {
71
+ const acledaDeeplink = "<%= raw checkout.acleda_deeplink %>";
72
+ const processingUrl = "<%= raw @payment.processing_url %>";
73
+
74
+ // Native -> JS after payment completes. Primary completion signal; the
75
+ // transaction checker polling is the fallback.
76
+ window.onPaymentResult = function (result) {
77
+ console.log("[acledaJSBridge] Payment result:", result);
78
+ // result.batchId, result.status = "success" | "failed" | "cancelled"
79
+ window.location.href = processingUrl;
80
+ };
81
+
82
+ const startPayment = () => window.acledaJSBridge.call("startPayment", { acleda_deeplink: acledaDeeplink });
83
+
84
+ if (startPayment()) {
85
+ showRedirectContainer("Continue", startPayment);
86
+ } else {
87
+ const unsupported = document.getElementById('unsupported-message');
88
+ unsupported.style.display = 'block';
89
+ unsupported.innerText = 'Unsupported environment';
90
+ }
91
+ });
92
+ </script>
93
+ <% end %>