diff --git a/app/controllers/domains_controller.rb b/app/controllers/domains_controller.rb
index c9d6cc92..925ea168 100644
--- a/app/controllers/domains_controller.rb
+++ b/app/controllers/domains_controller.rb
@@ -104,6 +104,15 @@ def regenerate_transfer_code
redirect_to domain_path(domain_name: @response.domain[:name])
end
+ def cancel_pending_update
+ conn = ApiConnector::Domains::PendingUpdateCanceller.new(**auth_info)
+ result = conn.call_action(payload: { name: params[:domain_name] })
+ handle_response(result); return if performed?
+
+ flash.notice = @message
+ redirect_to domain_path(domain_name: @response.domain[:name])
+ end
+
def delete
authorize! :delete, 'Epp::Domain'
end
diff --git a/app/services/api_connector/domains/pending_update_canceller.rb b/app/services/api_connector/domains/pending_update_canceller.rb
new file mode 100644
index 00000000..b28954dd
--- /dev/null
+++ b/app/services/api_connector/domains/pending_update_canceller.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class ApiConnector
+ module Domains
+ class PendingUpdateCanceller < ApiConnector
+ ACTION = 'cancel_pending_update'
+ ENDPOINT = {
+ method: 'delete',
+ endpoint: '/domains',
+ }.freeze
+
+ def cancel_pending_update(payload: nil)
+ request(url: url_with_id(CGI.escape(payload[:name])), method: method)
+ end
+
+ private
+
+ def url_with_id(domain_name)
+ "#{endpoint_url}/#{domain_name}/pending_update"
+ end
+ end
+ end
+end
diff --git a/app/views/domains/show.html.erb b/app/views/domains/show.html.erb
index f772ef7b..fdb82a36 100644
--- a/app/views/domains/show.html.erb
+++ b/app/views/domains/show.html.erb
@@ -20,6 +20,15 @@
<%= link_to t(:delete), delete_domain_path(domain_name: @domain[:name]), class: 'button button--danger' %>
<% end %>
+ <% if @domain[:statuses].key?('pendingUpdate') %>
+
+ <%= link_to t('.cancel_pending_update'),
+ domain_cancel_pending_update_path(domain_name: @domain[:name]),
+ class: 'button button--danger',
+ data: { 'turbo-method': 'delete',
+ turbo_confirm: t('.cancel_pending_update_confirm') } %>
+
+ <% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7a969f68..92a9c48b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -231,7 +231,9 @@ en:
flag: "Flag"
public_key: "Public key"
regenerate: "Regenerate transfer code"
-
+ cancel_pending_update: "Cancel registrant change"
+ cancel_pending_update_confirm: "Cancel the pending registrant change of this domain?"
+
edit:
title: "Edit domain"
diff --git a/config/locales/et.yml b/config/locales/et.yml
index ba2c2863..cc880241 100644
--- a/config/locales/et.yml
+++ b/config/locales/et.yml
@@ -238,7 +238,9 @@ et:
flag: "Lipp"
public_key: "Avalik võti"
regenerate: "Regenerate transfer code"
-
+ cancel_pending_update: "Tühista registreerija vahetus"
+ cancel_pending_update_confirm: "Kas tühistada selle domeeni ootel registreerija vahetus?"
+
edit:
title: "Muuda domeeni"
diff --git a/config/locales/statuses.en.yml b/config/locales/statuses.en.yml
index 5181f7d7..fd3c2ca9 100644
--- a/config/locales/statuses.en.yml
+++ b/config/locales/statuses.en.yml
@@ -32,7 +32,7 @@ et:
pendingRenew_text: "Renew command has been processed for the domain registration, but the action has not been completed by the server."
pendingTransfer: "pendingTransfer"
pendingTransfer_text: "Transfer command has been processed for the domain registration, but the action has not been completed by the server."
- pendingUpdate": "pendingUpdate"
+ pendingUpdate: "pendingUpdate"
pendingUpdate_text: "Update command has been processed for the domain registration, but the action has not been completed by the server."
serverAdminChangeProhibited: "serverAdminChangeProhibited"
serverAdminChangeProhibited_text: "Requests to replace admin contacts are rejected."
diff --git a/config/locales/statuses.et.yml b/config/locales/statuses.et.yml
index bbd30ec5..260a2ca0 100644
--- a/config/locales/statuses.et.yml
+++ b/config/locales/statuses.et.yml
@@ -32,7 +32,7 @@ et:
pendingRenew_text: "Domeeni registreeringu pikendamine on ootel."
pendingTransfer: "Registripidaja vahetus ootel"
pendingTransfer_text: "Registripidaja vahetus on ootel."
- pendingUpdate": "Andmete muutmine ootel"
+ pendingUpdate: "Andmete muutmine ootel"
pendingUpdate_text: "Oodatakse registreerija kinnitust registreerija vahetuse päringule"
serverAdminChangeProhibited: "Halduskontakti vahetus keelatud"
serverAdminChangeProhibited_text: "Domeeni halduskontakti(de) muutmine on keelatud"
diff --git a/config/routes.rb b/config/routes.rb
index 59b162ff..4fecd41c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -31,6 +31,8 @@
post 'domains/transfer', to: 'domains#transfer', as: :domain_transfer
post 'domains/renew', to: 'domains#renew', as: :domain_renew
delete 'domains/destroy', to: 'domains#destroy', as: :destroy_domain
+ delete 'domain/cancel_pending_update', to: 'domains#cancel_pending_update',
+ as: :domain_cancel_pending_update
resources :domains, except: %i[destroy update show edit]
resources :bulk_change, only: %i[show update], controller: 'steps_controllers/bulk_change'
diff --git a/spec/controllers/domains_controller_spec.rb b/spec/controllers/domains_controller_spec.rb
index 6b95ff36..a7c779c6 100644
--- a/spec/controllers/domains_controller_spec.rb
+++ b/spec/controllers/domains_controller_spec.rb
@@ -129,6 +129,13 @@
params: {
domain_name: domain,
},
+ },
+ {
+ method: :cancel_pending_update,
+ http_method: :delete,
+ params: {
+ domain_name: domain,
+ },
}
]
diff --git a/spec/fixtures/vcr_cassettes/controllers/domains_controller/cancel_pending_update-auth-fail.yml b/spec/fixtures/vcr_cassettes/controllers/domains_controller/cancel_pending_update-auth-fail.yml
new file mode 100644
index 00000000..30510890
--- /dev/null
+++ b/spec/fixtures/vcr_cassettes/controllers/domains_controller/cancel_pending_update-auth-fail.yml
@@ -0,0 +1,53 @@
+---
+http_interactions:
+- request:
+ method: delete
+ uri: http://registry:3000/repp/v1/domains/example092022.ee/pending_update
+ body:
+ encoding: UTF-8
+ string: ''
+ headers:
+ Authorization:
+ - ""
+ User-Agent:
+ - Faraday v2.3.0
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ response:
+ status:
+ code: 401
+ message: Unauthorized
+ headers:
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - 1; mode=block
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Content-Type:
+ - application/json; charset=utf-8
+ Vary:
+ - Accept
+ Cache-Control:
+ - no-cache
+ X-Request-Id:
+ - 3c9d5b81-7a44-4e02-8f6d-95ab2c0e13da
+ X-Runtime:
+ - '0.012884'
+ Transfer-Encoding:
+ - chunked
+ body:
+ encoding: UTF-8
+ string: '{"code":2202,"message":"Invalid authorization information","data":{"username":"ullam","password":"HcH9cT6k7O79","active":null}}'
+ recorded_at: Mon, 10 Aug 2026 08:30:00 GMT
+recorded_with: VCR 6.1.0
diff --git a/spec/fixtures/vcr_cassettes/controllers/domains_controller/cancel_pending_update.yml b/spec/fixtures/vcr_cassettes/controllers/domains_controller/cancel_pending_update.yml
new file mode 100644
index 00000000..5db3519c
--- /dev/null
+++ b/spec/fixtures/vcr_cassettes/controllers/domains_controller/cancel_pending_update.yml
@@ -0,0 +1,53 @@
+---
+http_interactions:
+- request:
+ method: delete
+ uri: http://registry:3000/repp/v1/domains/example092022.ee/pending_update
+ body:
+ encoding: UTF-8
+ string: ''
+ headers:
+ Authorization:
+ - ""
+ User-Agent:
+ - Faraday v2.3.0
+ Content-Type:
+ - application/json
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ X-Frame-Options:
+ - SAMEORIGIN
+ X-Xss-Protection:
+ - 1; mode=block
+ X-Content-Type-Options:
+ - nosniff
+ X-Download-Options:
+ - noopen
+ X-Permitted-Cross-Domain-Policies:
+ - none
+ Referrer-Policy:
+ - strict-origin-when-cross-origin
+ Content-Type:
+ - application/json; charset=utf-8
+ Vary:
+ - Accept
+ Cache-Control:
+ - no-cache
+ X-Request-Id:
+ - 8f2a1c47-2d5e-4f19-9c3b-1b6e0d7a5f42
+ X-Runtime:
+ - '0.041207'
+ Transfer-Encoding:
+ - chunked
+ body:
+ encoding: UTF-8
+ string: '{"code":1000,"message":"Command completed successfully","data":{"domain":{"name":"example092022.ee"}}}'
+ recorded_at: Mon, 10 Aug 2026 08:30:00 GMT
+recorded_with: VCR 6.1.0