diff --git a/app/controllers/waste_carriers_engine/govpay_forms_controller.rb b/app/controllers/waste_carriers_engine/govpay_forms_controller.rb
new file mode 100644
index 000000000..53d63a151
--- /dev/null
+++ b/app/controllers/waste_carriers_engine/govpay_forms_controller.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ class GovpayFormsController < ::WasteCarriersEngine::FormsController
+ include UnsubmittableForm
+
+ def new
+ super(GovpayForm, "govpay_form")
+
+ payment_info = prepare_for_payment
+
+ if payment_info == :error
+ flash[:error] = I18n.t(".waste_carriers_engine.govpay_forms.new.setup_error")
+ go_back
+ else
+ redirect_to payment_info[:url]
+ end
+ end
+
+ def payment_callback
+ find_or_initialize_transient_registration(params[:token])
+
+ if @transient_registration.finance_details.orders.first.govpay_status == "success"
+ Rails.logger.warn "Attempt to pay for an order with govpay_status already set to success"
+ respond_to_acceptable_payment(:success)
+
+ else
+ @transient_registration.with_lock do
+ payment_status = GovpayCallbackService.new(params[:uuid]).run
+
+ case payment_status
+ when :success, :pending
+ respond_to_acceptable_payment(payment_status)
+ else
+ respond_to_unsuccessful_payment(payment_status)
+ end
+ end
+ end
+ rescue ArgumentError
+ Rails.logger.warn "Govpay payment callback error: invalid payment uuid \"#{params[:uuid]}\""
+ Airbrake.notify("Govpay callback error", "Invalid payment uuid \"#{params[:uuid]}\"")
+ flash[:error] = I18n.t(".waste_carriers_engine.govpay_forms.new.internal_error")
+ go_back
+ end
+
+ private
+
+ def prepare_for_payment
+ @transient_registration.prepare_for_payment(:govpay, current_user)
+ order = @transient_registration.finance_details.orders.first
+ govpay_service = GovpayPaymentService.new(@transient_registration, order, current_user)
+ govpay_service.prepare_for_payment
+ end
+
+ def respond_to_acceptable_payment(action)
+ return unless valid_transient_registration?
+
+ if action != :error
+ log_and_send_govpay_response(true, action)
+ @transient_registration.next!
+ redirect_to_correct_form
+ else
+ log_and_send_govpay_response(false, action)
+ flash[:error] = I18n.t(".waste_carriers_engine.govpay_forms.#{action}.invalid_response")
+ go_back
+ end
+ end
+
+ def respond_to_unsuccessful_payment(action)
+ return unless valid_transient_registration?
+
+ if action != :error
+ log_and_send_govpay_response(true, action)
+ flash[:error] = I18n.t(".waste_carriers_engine.govpay_forms.#{action}.message")
+ else
+ log_and_send_govpay_response(false, action)
+ flash[:error] = I18n.t(".waste_carriers_engine.govpay_forms.#{action}.invalid_response")
+ end
+
+ go_back
+ end
+
+ def valid_transient_registration?
+ setup_checks_pass?
+ end
+
+ def log_and_send_govpay_response(is_valid, action)
+ valid_text = is_valid ? "Valid" : "Invalid"
+ title = "#{valid_text} Govpay response: #{action}"
+
+ Rails.logger.debug [title, "Params:", params.to_json].join("\n")
+ Airbrake.notify(title, error_message: params) unless is_valid && action == :success
+ end
+ end
+end
diff --git a/app/controllers/waste_carriers_engine/registration_received_pending_govpay_payment_forms_controller.rb b/app/controllers/waste_carriers_engine/registration_received_pending_govpay_payment_forms_controller.rb
new file mode 100644
index 000000000..2ef262e2a
--- /dev/null
+++ b/app/controllers/waste_carriers_engine/registration_received_pending_govpay_payment_forms_controller.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ class RegistrationReceivedPendingGovpayPaymentFormsController < ::WasteCarriersEngine::FormsController
+ include UnsubmittableForm
+ include CannotGoBackForm
+
+ def new
+ return unless super(
+ RegistrationReceivedPendingGovpayPaymentForm,
+ "registration_received_pending_govpay_payment_form"
+ )
+
+ begin
+ @registration = RegistrationCompletionService.run(@transient_registration)
+ rescue StandardError => e
+ Airbrake.notify(e, reg_identifier: @transient_registration.reg_identifier)
+ Rails.logger.error e
+ end
+ end
+ end
+end
diff --git a/app/controllers/waste_carriers_engine/renewal_received_pending_govpay_payment_forms_controller.rb b/app/controllers/waste_carriers_engine/renewal_received_pending_govpay_payment_forms_controller.rb
new file mode 100644
index 000000000..a51e838c4
--- /dev/null
+++ b/app/controllers/waste_carriers_engine/renewal_received_pending_govpay_payment_forms_controller.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ class RenewalReceivedPendingGovpayPaymentFormsController < ::WasteCarriersEngine::FormsController
+ include CannotGoBackForm
+ include UnsubmittableForm
+
+ def new
+ super(RenewalReceivedPendingGovpayPaymentForm, "renewal_received_pending_govpay_payment_form")
+ end
+ end
+end
diff --git a/app/forms/waste_carriers_engine/govpay_form.rb b/app/forms/waste_carriers_engine/govpay_form.rb
new file mode 100644
index 000000000..2ca201320
--- /dev/null
+++ b/app/forms/waste_carriers_engine/govpay_form.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ class GovpayForm < ::WasteCarriersEngine::BaseForm
+ def self.can_navigate_flexibly?
+ false
+ end
+ end
+end
diff --git a/app/forms/waste_carriers_engine/registration_received_pending_govpay_payment_form.rb b/app/forms/waste_carriers_engine/registration_received_pending_govpay_payment_form.rb
new file mode 100644
index 000000000..5fb6e1f09
--- /dev/null
+++ b/app/forms/waste_carriers_engine/registration_received_pending_govpay_payment_form.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ class RegistrationReceivedPendingGovpayPaymentForm < ::WasteCarriersEngine::BaseForm
+ include CannotSubmit
+
+ def self.can_navigate_flexibly?
+ false
+ end
+ end
+end
diff --git a/app/forms/waste_carriers_engine/renewal_received_pending_govpay_payment_form.rb b/app/forms/waste_carriers_engine/renewal_received_pending_govpay_payment_form.rb
new file mode 100644
index 000000000..90c0f3786
--- /dev/null
+++ b/app/forms/waste_carriers_engine/renewal_received_pending_govpay_payment_form.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ class RenewalReceivedPendingGovpayPaymentForm < ::WasteCarriersEngine::BaseForm
+ include CannotSubmit
+
+ def self.can_navigate_flexibly?
+ false
+ end
+ end
+end
diff --git a/app/models/concerns/waste_carriers_engine/can_have_registration_attributes.rb b/app/models/concerns/waste_carriers_engine/can_have_registration_attributes.rb
index 7d0726213..4060649c0 100644
--- a/app/models/concerns/waste_carriers_engine/can_have_registration_attributes.rb
+++ b/app/models/concerns/waste_carriers_engine/can_have_registration_attributes.rb
@@ -113,12 +113,16 @@ def ad_contact_email?
contact_email.blank? || contact_email == WasteCarriersEngine.configuration.assisted_digital_email
end
- def pending_worldpay_payment?
+ def pending_online_payment?
return false unless finance_details.present? &&
finance_details.orders.present? &&
finance_details.orders.first.present?
- WorldpayValidatorService.valid_world_pay_status?(:pending, finance_details.orders.first.world_pay_status)
+ if WasteCarriersEngine::FeatureToggle.active?(:govpay_payments)
+ GovpayValidatorService.valid_govpay_status?(:pending, finance_details.orders.first.govpay_status)
+ else
+ WorldpayValidatorService.valid_world_pay_status?(:pending, finance_details.orders.first.world_pay_status)
+ end
end
# Some business types should not have a company_no
diff --git a/app/models/concerns/waste_carriers_engine/can_use_new_registration_workflow.rb b/app/models/concerns/waste_carriers_engine/can_use_new_registration_workflow.rb
index 9ae07a4ed..8f346226a 100644
--- a/app/models/concerns/waste_carriers_engine/can_use_new_registration_workflow.rb
+++ b/app/models/concerns/waste_carriers_engine/can_use_new_registration_workflow.rb
@@ -66,12 +66,14 @@ module CanUseNewRegistrationWorkflow
state :cards_form
state :payment_summary_form
state :worldpay_form
+ state :govpay_form
state :confirm_bank_transfer_form
state :registration_completed_form
state :registration_received_pending_payment_form
state :registration_received_pending_conviction_form
state :registration_received_pending_worldpay_payment_form
+ state :registration_received_pending_govpay_payment_form
# Transitions
event :next do
@@ -264,6 +266,9 @@ module CanUseNewRegistrationWorkflow
# Payment & Completion
transitions from: :cards_form, to: :payment_summary_form
+ transitions from: :payment_summary_form, to: :govpay_form,
+ if: :paying_by_card_govpay?
+
transitions from: :payment_summary_form, to: :worldpay_form,
if: :paying_by_card?
@@ -274,13 +279,15 @@ module CanUseNewRegistrationWorkflow
# callback block, hence we went for `after`
after: :set_metadata_route
- transitions from: :worldpay_form, to: :registration_received_pending_worldpay_payment_form,
- if: :pending_worldpay_payment?,
+ transitions from: :worldpay_form,
+ to: :registration_received_pending_worldpay_payment_form,
+ if: :pending_online_payment?,
# TODO: This don't get triggered if in the `success`
# callback block, hence we went for `after`
after: :set_metadata_route
- transitions from: :worldpay_form, to: :registration_received_pending_conviction_form,
+ transitions from: :worldpay_form,
+ to: :registration_received_pending_conviction_form,
if: :conviction_check_required?,
# TODO: This don't get triggered if in the `success`
# callback block, hence we went for `after`
@@ -290,6 +297,19 @@ module CanUseNewRegistrationWorkflow
# TODO: This don't get triggered if in the `success`
# callback block, hence we went for `after`
after: :set_metadata_route
+
+ transitions from: :govpay_form,
+ to: :registration_received_pending_govpay_payment_form,
+ if: :pending_online_payment?,
+ after: :set_metadata_route
+
+ transitions from: :govpay_form,
+ to: :registration_received_pending_conviction_form,
+ if: :conviction_check_required?,
+ after: :set_metadata_route
+
+ transitions from: :govpay_form, to: :registration_completed_form,
+ after: :set_metadata_route
end
# Transitions
@@ -366,6 +386,10 @@ def paying_by_card?
temp_payment_method == "card"
end
+ def paying_by_card_govpay?
+ WasteCarriersEngine::FeatureToggle.active?(:govpay_payments) && temp_payment_method == "card"
+ end
+
def switch_to_lower_tier
update_attributes(tier: WasteCarriersEngine::NewRegistration::LOWER_TIER)
end
diff --git a/app/models/concerns/waste_carriers_engine/can_use_renewing_registration_workflow.rb b/app/models/concerns/waste_carriers_engine/can_use_renewing_registration_workflow.rb
index 3d64c2967..60640b830 100644
--- a/app/models/concerns/waste_carriers_engine/can_use_renewing_registration_workflow.rb
+++ b/app/models/concerns/waste_carriers_engine/can_use_renewing_registration_workflow.rb
@@ -52,12 +52,14 @@ module CanUseRenewingRegistrationWorkflow
state :cards_form
state :payment_summary_form
state :worldpay_form
+ state :govpay_form
state :confirm_bank_transfer_form
state :renewal_complete_form
state :renewal_received_pending_conviction_form
state :renewal_received_pending_payment_form
state :renewal_received_pending_worldpay_payment_form
+ state :renewal_received_pending_govpay_payment_form
state :cannot_renew_type_change_form
@@ -193,14 +195,17 @@ module CanUseRenewingRegistrationWorkflow
# Payment & completion
transitions from: :cards_form, to: :payment_summary_form
+ transitions from: :payment_summary_form, to: :govpay_form,
+ if: :paying_by_card_govpay?
+
transitions from: :payment_summary_form, to: :worldpay_form,
if: :paying_by_card?
transitions from: :payment_summary_form, to: :confirm_bank_transfer_form
transitions from: :worldpay_form, to: :renewal_received_pending_worldpay_payment_form,
- if: :pending_worldpay_payment?,
- success: :send_renewal_pending_worldpay_payment_email,
+ if: :pending_online_payment?,
+ success: :send_renewal_pending_online_payment_email,
# TODO: This don't get triggered if in the `success`
# callback block, hence we went for `after`
after: :set_metadata_route
@@ -217,6 +222,19 @@ module CanUseRenewingRegistrationWorkflow
# callback block, hence we went for `after`
after: :set_metadata_route
+ transitions from: :govpay_form, to: :renewal_received_pending_govpay_payment_form,
+ if: :pending_online_payment?,
+ success: :send_renewal_pending_online_payment_email,
+ after: :set_metadata_route
+
+ transitions from: :govpay_form, to: :renewal_received_pending_conviction_form,
+ if: :conviction_check_required?,
+ success: :send_renewal_pending_checks_email,
+ after: :set_metadata_route
+
+ transitions from: :govpay_form, to: :renewal_complete_form,
+ after: :set_metadata_route
+
transitions from: :confirm_bank_transfer_form, to: :renewal_received_pending_payment_form,
success: :send_renewal_received_pending_payment_email,
# TODO: This don't get triggered if in the `success`
@@ -279,6 +297,10 @@ def paying_by_card?
temp_payment_method == "card"
end
+ def paying_by_card_govpay?
+ WasteCarriersEngine::FeatureToggle.active?(:govpay_payments) && temp_payment_method == "card"
+ end
+
def use_trading_name?
temp_use_trading_name == "yes"
end
@@ -287,8 +309,8 @@ def incorrect_company_data?
temp_use_registered_company_details == "no"
end
- def send_renewal_pending_worldpay_payment_email
- WasteCarriersEngine::Notify::RenewalPendingWorldpayPaymentEmailService.run(registration: self)
+ def send_renewal_pending_online_payment_email
+ WasteCarriersEngine::Notify::RenewalPendingOnlinePaymentEmailService.run(registration: self)
rescue StandardError => e
Airbrake.notify(e, registration_no: reg_identifier) if defined?(Airbrake)
end
diff --git a/app/models/waste_carriers_engine/finance_details.rb b/app/models/waste_carriers_engine/finance_details.rb
index ac14730ae..06b461753 100644
--- a/app/models/waste_carriers_engine/finance_details.rb
+++ b/app/models/waste_carriers_engine/finance_details.rb
@@ -14,8 +14,7 @@ class FinanceDetails
field :balance, type: Integer
- validates :balance,
- presence: true
+ validates :balance, presence: true
def self.new_renewal_finance_details(transient_registration, method, current_user)
user_email = current_user&.email || transient_registration.contact_email
@@ -43,9 +42,7 @@ def zero_difference_balance
def update_balance
order_balance = orders.sum { |item| item[:total_amount] }
- # Select payments where the type is not WORLDPAY, or if it is, the status is AUTHORISED
- payment_balance = payments.any_of({ :payment_type.ne => "WORLDPAY" },
- world_pay_payment_status: "AUTHORISED").sum { |item| item[:amount] }
+ payment_balance = payments.except_online_not_authorised.sum { |item| item[:amount] }
self.balance = order_balance - payment_balance
end
end
diff --git a/app/models/waste_carriers_engine/order.rb b/app/models/waste_carriers_engine/order.rb
index bb5774da6..0b1e83499 100644
--- a/app/models/waste_carriers_engine/order.rb
+++ b/app/models/waste_carriers_engine/order.rb
@@ -13,10 +13,13 @@ class Order
field :orderCode, as: :order_code, type: String
field :paymentMethod, as: :payment_method, type: String
field :merchantId, as: :merchant_id, type: String
+ field :payment_uuid, type: String
field :totalAmount, as: :total_amount, type: Integer
field :currency, type: String
field :dateCreated, as: :date_created, type: DateTime
field :worldPayStatus, as: :world_pay_status, type: String
+ field :govpayId, as: :govpay_id, type: String
+ field :govpayStatus, as: :govpay_status, type: String
field :dateLastUpdated, as: :date_last_updated, type: DateTime
field :updatedByUser, as: :updated_by_user, type: String
field :description, type: String
@@ -78,12 +81,24 @@ def set_description
self.description = generate_description
end
- def update_after_worldpay(status)
- self.world_pay_status = status
+ def update_after_online_payment(status)
+ if WasteCarriersEngine::FeatureToggle.active?(:govpay_payments)
+ Rails.logger.debug "Updating order after online payment, status: #{status}"
+ self.govpay_status = status
+ else
+ self.world_pay_status = status
+ end
self.date_last_updated = Time.current
save!
end
+ # Generate a uuid for the payment associated with this order, on demand
+ def payment_uuid
+ update_attributes!(payment_uuid: SecureRandom.uuid) unless self[:payment_uuid]
+
+ self[:payment_uuid]
+ end
+
private
def generate_description
diff --git a/app/models/waste_carriers_engine/payment.rb b/app/models/waste_carriers_engine/payment.rb
index 86455c8c3..c4c68450e 100644
--- a/app/models/waste_carriers_engine/payment.rb
+++ b/app/models/waste_carriers_engine/payment.rb
@@ -11,6 +11,8 @@ class Payment
field :amount, type: Integer
field :currency, type: String, default: "GBP"
field :mac_code, type: String
+ field :uuid, type: String
+ field :govpay_id, type: String
field :dateReceived, as: :date_received, type: Date
field :dateEntered, as: :date_entered, type: DateTime
field :dateReceived_year, as: :date_received_year, type: Integer
@@ -18,28 +20,45 @@ class Payment
field :dateReceived_day, as: :date_received_day, type: Integer
field :registrationReference, as: :registration_reference, type: String
field :worldPayPaymentStatus, as: :world_pay_payment_status, type: String
+ field :govpayPaymentStatus, as: :govpay_payment_status, type: String
field :updatedByUser, as: :updated_by_user, type: String
field :comment, type: String
scope :refundable, -> { where(payment_type: { "$in" => RECEIVABLE_PAYMENT_TYPES }) }
scope :reversible, -> { where(payment_type: { "$in" => RECEIVABLE_PAYMENT_TYPES }) }
- def self.new_from_worldpay(order, user_email)
+ # Select payments where the type is not one of the online ones, or if it is, the status is AUTHORISED
+ scope :except_online_not_authorised, lambda {
+ where({ "$or" => [
+ { payment_type: { "$nin" => %w[WORLDPAY GOVPAY] } },
+ { world_pay_payment_status: "AUTHORISED" },
+ { govpay_payment_status: "success" }
+ ] })
+ }
+
+ def self.new_from_online_payment(order, user_email)
payment = Payment.new
payment[:order_key] = order[:order_code]
payment[:amount] = order[:total_amount]
payment[:currency] = "GBP"
- payment[:payment_type] = "WORLDPAY"
- payment[:registration_reference] = "Worldpay"
- payment[:comment] = "Paid via Worldpay"
payment[:updated_by_user] = user_email
payment.finance_details = order.finance_details
-
+ if WasteCarriersEngine::FeatureToggle.active?(:govpay_payments)
+ payment[:payment_type] = "GOVPAY"
+ payment[:registration_reference] = "Govpay"
+ payment[:comment] = "Paid via Govpay"
+ payment[:uuid] = order.payment_uuid
+ payment[:govpay_id] = order.govpay_id
+ else
+ payment[:payment_type] = "WORLDPAY"
+ payment[:registration_reference] = "Worldpay"
+ payment[:comment] = "Paid via Worldpay"
+ end
payment
end
- def self.new_from_non_worldpay(params, order)
+ def self.new_from_non_online_payment(params, order)
payment = Payment.new
payment[:amount] = params[:amount]
@@ -60,9 +79,14 @@ def self.new_from_non_worldpay(params, order)
payment
end
- def update_after_worldpay(params)
- self.world_pay_payment_status = params[:paymentStatus]
- self.mac_code = params[:mac]
+ def update_after_online_payment(params)
+ if WasteCarriersEngine::FeatureToggle.active?(:govpay_payments)
+ Rails.logger.debug "Updating payment after online payment, params: #{params}"
+ self.govpay_payment_status = params[:govpay_status]
+ else
+ self.world_pay_payment_status = params[:paymentStatus]
+ self.mac_code = params[:mac]
+ end
self.date_received = Date.current
self.date_entered = date_received
diff --git a/app/services/concerns/waste_carriers_engine/can_send_govpay_request.rb b/app/services/concerns/waste_carriers_engine/can_send_govpay_request.rb
new file mode 100644
index 000000000..b75dd7d36
--- /dev/null
+++ b/app/services/concerns/waste_carriers_engine/can_send_govpay_request.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ module CanSendGovpayRequest
+ extend ActiveSupport::Concern
+
+ # rubocop:disable Metrics/BlockLength
+ included do
+ private
+
+ def send_request(method, path, params = nil)
+ Rails.logger.debug "Sending request to Govpay, #{method}, path: #{path}, params: #{params}"
+
+ response = nil
+ begin
+ response = RestClient::Request.execute(
+ method: method,
+ url: url(path),
+ payload: params.present? ? params.to_json : nil,
+ headers: {
+ "Authorization" => "Bearer #{bearer_token}",
+ "Content-Type" => "application/json"
+ }
+ )
+
+ Rails.logger.debug "Received response from Govpay: #{response}"
+ rescue StandardError => e
+ Rails.logger.error("Error sending request to govpay: #{e}")
+ Airbrake.notify(e, message: "Error on govpay request")
+ end
+
+ response
+ end
+
+ def url(path)
+ "#{Rails.configuration.govpay_url}#{path}"
+ end
+
+ def bearer_token
+ @bearer_token ||= Rails.configuration.govpay_api_token
+ end
+ end
+ # rubocop:enable Metrics/BlockLength
+ end
+end
diff --git a/app/services/waste_carriers_engine/govpay_callback_service.rb b/app/services/waste_carriers_engine/govpay_callback_service.rb
new file mode 100644
index 000000000..6ee4ef1c7
--- /dev/null
+++ b/app/services/waste_carriers_engine/govpay_callback_service.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require "rest-client"
+
+module WasteCarriersEngine
+ class GovpayCallbackService
+
+ def initialize(payment_uuid)
+ @payment_uuid = payment_uuid
+ @transient_registration = transient_registration_by_payment_uuid
+ @order = order_by_payment_uuid
+ end
+
+ def run
+ @govpay_payment_status = GovpayPaymentDetailsService.new(@payment_uuid).govpay_payment_status
+
+ @response_type = GovpayPaymentDetailsService.response_type(@govpay_payment_status)
+ return :error unless govpay_response_validator(@govpay_payment_status).send("valid_#{@response_type}?".to_sym)
+
+ case @response_type
+ when :success, :pending
+ update_payment_data
+ else
+ unsuccessful_payment
+ end
+
+ @response_type
+ end
+
+ private
+
+ def transient_registration_by_payment_uuid
+ reg = TransientRegistration.find_by("financeDetails.orders.payment_uuid": @payment_uuid)
+ raise ArgumentError, "Transient registration not found for payment uuid #{@payment_uuid}" unless reg
+
+ reg
+ end
+
+ def order_by_payment_uuid
+ order = @transient_registration.finance_details.orders.find_by(payment_uuid: @payment_uuid)
+ raise ArgumentError, "Order not found for payment uuid #{@payment_uuid}" unless order
+
+ order
+ end
+
+ def unsuccessful_payment
+ @order.update_after_online_payment(@response_type)
+ end
+
+ def update_payment_data
+ @order.update_after_online_payment(@response_type)
+ payment = Payment.new_from_online_payment(@order, user_email)
+ payment.update_after_online_payment(govpay_status: @response_type)
+
+ @transient_registration.finance_details.update_balance
+ @transient_registration.finance_details.save!
+ end
+
+ def govpay_response_validator(govpay_status)
+ GovpayValidatorService.new(@order, @payment_uuid, govpay_status)
+ end
+
+ def user_email
+ @current_user&.email || @transient_registration.contact_email
+ end
+ end
+end
diff --git a/app/services/waste_carriers_engine/govpay_payment_details_service.rb b/app/services/waste_carriers_engine/govpay_payment_details_service.rb
new file mode 100644
index 000000000..e8c17ecdc
--- /dev/null
+++ b/app/services/waste_carriers_engine/govpay_payment_details_service.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require "rest-client"
+
+module WasteCarriersEngine
+ class GovpayPaymentDetailsService
+ include CanSendGovpayRequest
+
+ def initialize(payment_uuid)
+ # Because orders are embedded in finance_details, we can't search directly on orders so we need to:
+ # 1. find the transient_registration which contains the order with this payment_uuid
+ # 2. within that transient_registration, find which order has that payment_uuid
+ transient_registration = TransientRegistration.find_by("financeDetails.orders.payment_uuid": payment_uuid)
+ @order = transient_registration.finance_details.orders.find_by(payment_uuid: payment_uuid)
+ rescue StandardError
+ raise ArgumentError, "Order not found for payment uuid \"#{payment_uuid}\""
+ end
+
+ # Payment status in Govpay terms
+ def govpay_payment_status
+ response = send_request(:get, "/payments/#{@order.govpay_id}")
+ response_json = JSON.parse(response.body)
+
+ status = response_json&.dig("state", "status") || "error"
+
+ # Special case: If failed, check whether this was because of a cancellation
+ status = "cancelled" if status == "failed" && response_json.dig("state", "code") == "P0030"
+
+ status
+ rescue StandardError => e
+ Rails.logger.error "Failed to retrieve payment status: #{e}"
+ "error"
+ end
+
+ # Payment status in application terms
+ def self.response_type(status)
+ {
+ "created" => :pending,
+ "started" => :pending,
+ "submitted" => :pending,
+ "cancelled" => :cancel,
+ "failed" => :failure,
+ nil => :error
+ }.freeze[status] || status.to_sym
+ end
+ end
+end
diff --git a/app/services/waste_carriers_engine/govpay_payment_service.rb b/app/services/waste_carriers_engine/govpay_payment_service.rb
new file mode 100644
index 000000000..e2acdc723
--- /dev/null
+++ b/app/services/waste_carriers_engine/govpay_payment_service.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require "rest-client"
+
+module WasteCarriersEngine
+ class GovpayPaymentService
+ include CanSendGovpayRequest
+
+ def initialize(transient_registration, order, current_user)
+ @transient_registration = transient_registration
+ @order = order
+ @current_user = current_user
+ end
+
+ def prepare_for_payment
+ response = send_request(:post, "/payments", payment_params)
+ response_json = JSON.parse(response.body)
+
+ govpay_payment_id = response_json["payment_id"]
+ if govpay_payment_id.present?
+ @order.govpay_id = govpay_payment_id
+ @order.save!
+ {
+ payment: nil, # @payment,
+ url: govpay_redirect_url(response)
+ }
+ else
+ :error
+ end
+ end
+
+ def payment_callback_url
+ host = Rails.configuration.host
+ path = WasteCarriersEngine::Engine.routes.url_helpers.payment_callback_govpay_forms_path(
+ token: @transient_registration.token, uuid: @order.payment_uuid
+ )
+
+ [host, path].join
+ end
+
+ def govpay_redirect_url(response)
+ JSON.parse(response.body).dig("_links", "next_url", "href")
+ end
+
+ private
+
+ def payment_params
+ {
+ amount: @order.total_amount,
+ return_url: payment_callback_url,
+ reference: @order.order_code,
+ description: "Your Waste Carrier Registration #{@transient_registration.reg_identifier}"
+ }
+ end
+ end
+end
diff --git a/app/services/waste_carriers_engine/govpay_validator_service.rb b/app/services/waste_carriers_engine/govpay_validator_service.rb
new file mode 100644
index 000000000..6bd920433
--- /dev/null
+++ b/app/services/waste_carriers_engine/govpay_validator_service.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+module WasteCarriersEngine
+ # Validates requests to record a successful or failed Govpay payment.
+
+ # This should happen after a user attempts to make a payment on the Govpay site. They are then redirected to
+ # a callback route which checks the payment status and decides how to route them onwards. This service is to
+ # validate the payment status before we record that a payment has been made (or failed) and allow the user
+ # to continue.
+ class GovpayValidatorService
+ def initialize(order, payment_uuid, govpay_status)
+ @order = order
+ @payment_uuid = payment_uuid
+ @govpay_status = govpay_status
+ end
+
+ def valid_success?
+ valid?(:success)
+ end
+
+ def valid_failure?
+ valid?(:failure)
+ end
+
+ def valid_pending?
+ valid?(:pending)
+ end
+
+ def valid_cancel?
+ valid?(:cancel)
+ end
+
+ def valid_error?
+ valid?(:error)
+ end
+
+ def self.valid_govpay_status?(response_type, status)
+ @allowed_statuses ||= {
+ success: %w[success],
+ failure: %w[failed],
+ pending: %w[created started submitted],
+ cancel: %w[cancelled],
+ error: %w[error]
+ }.freeze
+ @allowed_statuses[response_type].include?(status)
+ end
+
+ private
+
+ def valid?(action)
+ valid_order? && valid_payment_uuid? && valid_status?(action)
+ end
+
+ def valid_order?
+ return true if @order.present?
+
+ Rails.logger.error "Invalid Govpay response: Cannot find matching order"
+ false
+ end
+
+ def valid_payment_uuid?
+ unless @payment_uuid.present?
+ Rails.logger.error "Invalid Govpay response: Missing payment uuid"
+ return false
+ end
+
+ transient_registration = TransientRegistration.find_by("financeDetails.orders.payment_uuid": @payment_uuid)
+ unless transient_registration.nil?
+ order = transient_registration.finance_details.orders.find_by(payment_uuid: @payment_uuid)
+ end
+
+ return true if order.present?
+
+ Rails.logger.error "Invalid Govpay response: Cannot find matching order for payment uuid #{@payment_uuid}"
+ false
+ end
+
+ def valid_status?(response_type)
+ return true if GovpayValidatorService.valid_govpay_status?(response_type, @govpay_status)
+
+ Rails.logger.error "Invalid Govpay response: #{@status} is not a valid payment status for #{response_type}"
+ false
+ end
+ end
+end
diff --git a/app/services/waste_carriers_engine/notify/registration_pending_worldpay_payment_email_service.rb b/app/services/waste_carriers_engine/notify/registration_pending_online_payment_email_service.rb
similarity index 85%
rename from app/services/waste_carriers_engine/notify/registration_pending_worldpay_payment_email_service.rb
rename to app/services/waste_carriers_engine/notify/registration_pending_online_payment_email_service.rb
index 748e1b55b..52b9f79d1 100644
--- a/app/services/waste_carriers_engine/notify/registration_pending_worldpay_payment_email_service.rb
+++ b/app/services/waste_carriers_engine/notify/registration_pending_online_payment_email_service.rb
@@ -2,7 +2,7 @@
module WasteCarriersEngine
module Notify
- class RegistrationPendingWorldpayPaymentEmailService < BaseSendEmailService
+ class RegistrationPendingOnlinePaymentEmailService < BaseSendEmailService
private
def notify_options
diff --git a/app/services/waste_carriers_engine/notify/renewal_pending_worldpay_payment_email_service.rb b/app/services/waste_carriers_engine/notify/renewal_pending_online_payment_email_service.rb
similarity index 85%
rename from app/services/waste_carriers_engine/notify/renewal_pending_worldpay_payment_email_service.rb
rename to app/services/waste_carriers_engine/notify/renewal_pending_online_payment_email_service.rb
index 9b0d7c419..b883f80c0 100644
--- a/app/services/waste_carriers_engine/notify/renewal_pending_worldpay_payment_email_service.rb
+++ b/app/services/waste_carriers_engine/notify/renewal_pending_online_payment_email_service.rb
@@ -2,7 +2,7 @@
module WasteCarriersEngine
module Notify
- class RenewalPendingWorldpayPaymentEmailService < BaseSendEmailService
+ class RenewalPendingOnlinePaymentEmailService < BaseSendEmailService
private
def notify_options
diff --git a/app/services/waste_carriers_engine/registration_completion_service.rb b/app/services/waste_carriers_engine/registration_completion_service.rb
index ecdb6dc7e..a3e6f8e61 100644
--- a/app/services/waste_carriers_engine/registration_completion_service.rb
+++ b/app/services/waste_carriers_engine/registration_completion_service.rb
@@ -5,6 +5,7 @@ module WasteCarriersEngine
class RegistrationCompletionService < BaseService
attr_reader :transient_registration
+ # rubocop:disable Metrics/MethodLength
def run(transient_registration)
@transient_registration = transient_registration
@@ -27,6 +28,7 @@ def run(transient_registration)
begin
RegistrationActivationService.run(registration: registration)
+ Rails.logger.info "Completed registration #{@transient_registration.reg_identifier}"
rescue StandardError => e
Airbrake.notify(e, reg_identifier: @transient_registration.reg_identifier)
Rails.logger.error e
@@ -35,6 +37,7 @@ def run(transient_registration)
registration
end
+ # rubocop:enable Metrics/MethodLength
private
@@ -78,8 +81,8 @@ def delete_transient_registration
# In the case when the registration can be completed, the registration activation email is sent from
# the RegistrationActivationService.
def send_confirmation_email
- if registration.pending_worldpay_payment?
- send_worldpay_pending_payment_email
+ if registration.pending_online_payment?
+ send_online_pending_payment_email
elsif registration.unpaid_balance?
send_pending_payment_email
elsif registration.conviction_check_required?
@@ -93,8 +96,8 @@ def send_pending_payment_email
Notify::RegistrationPendingPaymentEmailService.run(registration: registration)
end
- def send_worldpay_pending_payment_email
- Notify::RegistrationPendingWorldpayPaymentEmailService.run(registration: registration)
+ def send_online_pending_payment_email
+ Notify::RegistrationPendingOnlinePaymentEmailService.run(registration: registration)
end
def send_pending_conviction_check_email
diff --git a/app/services/waste_carriers_engine/worldpay_service.rb b/app/services/waste_carriers_engine/worldpay_service.rb
index 8bc1fb5c6..10f363e85 100644
--- a/app/services/waste_carriers_engine/worldpay_service.rb
+++ b/app/services/waste_carriers_engine/worldpay_service.rb
@@ -62,7 +62,7 @@ def valid_unsuccessful_payment?(validation_method)
worldpay_validator_service = WorldpayValidatorService.new(@order, @params)
return false unless worldpay_validator_service.public_send(validation_method)
- @order.update_after_worldpay(@params[:paymentStatus])
+ @order.update_after_online_payment(@params[:paymentStatus])
true
end
@@ -93,13 +93,13 @@ def parse_response(response)
end
def new_payment_object(order)
- Payment.new_from_worldpay(order, user_email)
+ Payment.new_from_online_payment(order, user_email)
end
def update_saved_data
- payment = Payment.new_from_worldpay(@order, user_email)
- payment.update_after_worldpay(@params)
- @order.update_after_worldpay(@params[:paymentStatus])
+ payment = Payment.new_from_online_payment(@order, user_email)
+ payment.update_after_online_payment(@params)
+ @order.update_after_online_payment(@params[:paymentStatus])
@transient_registration.finance_details.update_balance
@transient_registration.finance_details.save!
diff --git a/app/views/waste_carriers_engine/registration_received_pending_govpay_payment_forms/new.html.erb b/app/views/waste_carriers_engine/registration_received_pending_govpay_payment_forms/new.html.erb
new file mode 100644
index 000000000..7aea13819
--- /dev/null
+++ b/app/views/waste_carriers_engine/registration_received_pending_govpay_payment_forms/new.html.erb
@@ -0,0 +1,35 @@
+
+
+
+
+
+ <%= t(".heading") %>
+
+
+ <%= t(".highlight_text") %>
+ <%= @registration.reg_identifier %>
+
+
+
+
<%= t(".paragraph_1", email: @registration.contact_email) %>
+
+ <% unless WasteCarriersEngine.configuration.host_is_back_office? %>
+
<%= t(".paragraph_2", email: @registration.email_to_send_receipt) %>
+ <% end %>
+
+
<%= t(".subheading_1") %>
+
<%= t(".paragraph_3") %>
+
+
+
<%= t(".paragraph_4") %>
+
+
+ <%= render "shared/contact_environment_agency" %>
+
+ <%= render "shared/registration_checks" %>
+
+ <%= render "shared/registration_finished_button" %>
+
+ <%= render "shared/link_to_survey" %>
+
+
diff --git a/app/views/waste_carriers_engine/renewal_received_pending_govpay_payment_forms/new.html.erb b/app/views/waste_carriers_engine/renewal_received_pending_govpay_payment_forms/new.html.erb
new file mode 100644
index 000000000..56a4ea0ee
--- /dev/null
+++ b/app/views/waste_carriers_engine/renewal_received_pending_govpay_payment_forms/new.html.erb
@@ -0,0 +1,53 @@
+
+
+ <% if @renewal_received_pending_govpay_payment_form.errors.any? %>
+
+
<%= t(".error_heading") %>
+
+
+ <% @renewal_received_pending_govpay_payment_form.errors.full_messages.each do |message| %>
+ - <%= message %>
+ <% end %>
+
+
+ <% else %>
+ <%= form_for(@renewal_received_pending_govpay_payment_form) do |f| %>
+ <%= render partial: "waste_carriers_engine/shared/error_summary", locals: { f: f } %>
+
+
+
+ <%= t(".heading") %>
+
+
+ <%= t(".highlight_text") %>
+ <%= @renewal_received_pending_govpay_payment_form.reg_identifier %>
+
+
+
+
<%= t(".subheading") %>
+
+
<%= t(".paragraph_1",
+ email: @renewal_received_pending_govpay_payment_form.transient_registration.contact_email) %>
+
+
<%= t(".paragraph_2") %>
+
+ <% unless WasteCarriersEngine.configuration.host_is_back_office? %>
+
<%= t(".paragraph_3", email: @renewal_received_pending_govpay_payment_form.transient_registration.email_to_send_receipt) %>
+ <% end %>
+
+
+
<%= t(".paragraph_4") %>
+
+
+ <%= render "shared/contact_environment_agency" %>
+
+ <%= render "shared/registration_checks" %>
+
+ <%= render "shared/link_to_survey" %>
+
+ <%= render "shared/registration_finished_button" %>
+ <% end %>
+
+ <% end %>
+
+
diff --git a/config/locales/forms/registration_received_pending_govpay_payment_forms/en.yml b/config/locales/forms/registration_received_pending_govpay_payment_forms/en.yml
new file mode 100644
index 000000000..3969ad8b2
--- /dev/null
+++ b/config/locales/forms/registration_received_pending_govpay_payment_forms/en.yml
@@ -0,0 +1,12 @@
+en:
+ waste_carriers_engine:
+ registration_received_pending_govpay_payment_forms:
+ new:
+ title: We're processing your payment
+ heading: We're processing your payment
+ highlight_text: "Please use this registration number if you contact us:"
+ paragraph_1: We've sent a confirmation email to %{email}.
+ paragraph_2: A payment confirmation will be sent to %{email}.
+ subheading_1: We have received your application
+ paragraph_3: We're currently processing your payment and will let you know when it has been received.
+ paragraph_4: You're not legally entitled to operate as a waste carrier until we have confirmed your registration.
diff --git a/config/locales/forms/renewal_received_pending_govpay_payment_forms/en.yml b/config/locales/forms/renewal_received_pending_govpay_payment_forms/en.yml
new file mode 100644
index 000000000..b4fd50f7f
--- /dev/null
+++ b/config/locales/forms/renewal_received_pending_govpay_payment_forms/en.yml
@@ -0,0 +1,23 @@
+en:
+ waste_carriers_engine:
+ renewal_received_pending_govpay_payment_forms:
+ new:
+ title: Application received
+ heading: Application received
+ highlight_text: Your registration number is still
+ subheading: Processing your payment
+ paragraph_1: We have sent a confirmation email to %{email}.
+ paragraph_2: We are currently processing your payment and will let you know when it has been received.
+ paragraph_3: A payment confirmation will be sent to %{email}.
+ paragraph_4: Your registration will not be renewed until we have confirmed your registration.
+ error_heading: Something is wrong
+ next_button: Finished
+ activemodel:
+ errors:
+ models:
+ waste_carriers_engine/renewal_received_pending_worldpay_payment_form:
+ attributes:
+ reg_identifier:
+ invalid_format: The registration ID is not in a valid format
+ no_registration: There is no registration matching this ID
+ renewal_in_progress: This renewal is already in progress
diff --git a/config/routes.rb b/config/routes.rb
index ae3b59639..77833a440 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -44,6 +44,11 @@
path: "registration-received-pending-worldpay-payment",
path_names: { new: "" }
+ resources :registration_received_pending_govpay_payment_forms,
+ only: :new,
+ path: "registration-received-pending-govpay-payment",
+ path_names: { new: "" }
+
resources :registration_completed_forms,
only: :new,
path: "registration-completed",
@@ -416,6 +421,16 @@
on: :collection
end
+ resources :govpay_forms,
+ only: %i[new create],
+ path: "govpay",
+ path_names: { new: "" } do
+ get "payment_callback/:uuid",
+ to: "govpay_forms#payment_callback",
+ as: "payment_callback",
+ on: :collection
+ end
+
resources :confirm_bank_transfer_forms,
only: %i[new create],
path: "confirm-bank-transfer",
@@ -441,6 +456,11 @@
path: "renewal-received-pending-worldpay-payment",
path_names: { new: "" }
+ resources :renewal_received_pending_govpay_payment_forms,
+ only: %i[new create],
+ path: "renewal-received-pending-govpay-payment",
+ path_names: { new: "" }
+
resources :cannot_renew_type_change_forms,
only: %i[new create],
path: "cannot-renew-type-change",
diff --git a/spec/cassettes/notify_renewal_pending_worldpay_payment_sends_an_email.yml b/spec/cassettes/notify_renewal_pending_online_payment_sends_an_email.yml
similarity index 52%
rename from spec/cassettes/notify_renewal_pending_worldpay_payment_sends_an_email.yml
rename to spec/cassettes/notify_renewal_pending_online_payment_sends_an_email.yml
index 4e6cbbb7f..cdbe4a320 100644
--- a/spec/cassettes/notify_renewal_pending_worldpay_payment_sends_an_email.yml
+++ b/spec/cassettes/notify_renewal_pending_online_payment_sends_an_email.yml
@@ -5,7 +5,7 @@ http_interactions:
uri: https://api.notifications.service.gov.uk/v2/notifications/email
body:
encoding: UTF-8
- string: '{"email_address":"foo@example.com","template_id":"3da098e3-3db2-4c99-8e96-ed9d1a8ef227","personalisation":{"reg_identifier":"CBDU2","registration_type":"carrier,
+ string: '{"email_address":"foo@example.com","template_id":"3da098e3-3db2-4c99-8e96-ed9d1a8ef227","personalisation":{"reg_identifier":"CBDU1","registration_type":"carrier,
broker and dealer"}}'
headers:
User-Agent:
@@ -23,41 +23,48 @@ http_interactions:
code: 201
message: Created
headers:
+ Content-Type:
+ - application/json
+ Content-Length:
+ - '1011'
+ Connection:
+ - keep-alive
+ Date:
+ - Tue, 17 May 2022 16:12:38 GMT
Access-Control-Allow-Headers:
- Content-Type,Authorization
Access-Control-Allow-Methods:
- GET,PUT,POST,DELETE
Access-Control-Allow-Origin:
- "*"
- Content-Type:
- - application/json
- Date:
- - Fri, 30 Apr 2021 09:50:52 GMT
Server:
- - nginx
+ - gunicorn
Strict-Transport-Security:
- - max-age=31536000; includeSubdomains
+ - max-age=31536000; includeSubDomains; preload
X-B3-Spanid:
- - c465aec99043f401
+ - 30d987ef7f19714d
X-B3-Traceid:
- - c465aec99043f401
+ - 390284b76f3e756830d987ef7f19714d
X-Vcap-Request-Id:
- - 0f5e0b56-7790-4565-5e31-51fe31ea6e77
- Content-Length:
- - '1050'
- Connection:
- - keep-alive
+ - f048d391-2715-432b-5b5f-f5c9668459f3
+ X-Cache:
+ - Miss from cloudfront
+ Via:
+ - 1.1 57f9250ef620b33bc5b87625f8d36f5e.cloudfront.net (CloudFront)
+ X-Amz-Cf-Pop:
+ - LHR62-C3
+ X-Amz-Cf-Id:
+ - QLyOQdLghQ3wFGgev4XfSWwjfye2BHTAufKPiFfZtRRUmWrXKVJKaw==
body:
encoding: UTF-8
string: '{"content":{"body":"#Your application to renew your waste carrier,
broker and dealer registration has been received\r\n\r\nYour registration
- number is still\r\nCBDU2 \r\n\r\n#What happens next\r\n\r\nWe''ll check your
- details and let you know whether your renewal application has been successful.
- We aim to do this within 10 working days.\r\n\r\n*If you have enquiries please
- contact the Environment Agency helpline: 03708 506506\r\n*This is an automated
- email, please do not reply","from_email":"waste.carriers.registration.service@notifications.service.gov.uk","subject":"Your
- application to renew waste carriers registration CBDU2 has been received"},"id":"1513cc3d-9776-4bf6-9e22-463e3aa0a82c","reference":null,"scheduled_for":null,"template":{"id":"3da098e3-3db2-4c99-8e96-ed9d1a8ef227","uri":"https://api.notifications.service.gov.uk/services/25cb6b94-8ce7-485b-918a-559f3b18f69c/templates/3da098e3-3db2-4c99-8e96-ed9d1a8ef227","version":5},"uri":"https://api.notifications.service.gov.uk/v2/notifications/1513cc3d-9776-4bf6-9e22-463e3aa0a82c"}
+ number is still\r\nCBDU1 \r\n\r\nWe are currently processing your payment.
+ We''ll email you to let you know when your renewal application has been successful.\r\n\r\n*If
+ you have enquiries please contact the Environment Agency helpline: 03708 506506\r\n*This
+ is an automated email, please do not reply","from_email":"waste.carriers.registration.service@notifications.service.gov.uk","subject":"Your
+ application to renew waste carriers registration CBDU1 has been received"},"id":"5511c9ae-0ae6-4c0b-bc36-e72c8d48430d","reference":null,"scheduled_for":null,"template":{"id":"3da098e3-3db2-4c99-8e96-ed9d1a8ef227","uri":"https://api.notifications.service.gov.uk/services/25cb6b94-8ce7-485b-918a-559f3b18f69c/templates/3da098e3-3db2-4c99-8e96-ed9d1a8ef227","version":7},"uri":"https://api.notifications.service.gov.uk/v2/notifications/5511c9ae-0ae6-4c0b-bc36-e72c8d48430d"}
'
- recorded_at: Fri, 30 Apr 2021 09:50:52 GMT
-recorded_with: VCR 6.0.0
+ recorded_at: Tue, 17 May 2022 16:12:37 GMT
+recorded_with: VCR 6.1.0
diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb
index d51ea7ce3..d3ed55c93 100644
--- a/spec/dummy/config/application.rb
+++ b/spec/dummy/config/application.rb
@@ -86,6 +86,11 @@ class Application < Rails::Application
config.worldpay_password = ENV["WCRS_WORLDPAY_ECOM_PASSWORD"]
config.worldpay_macsecret = ENV["WCRS_WORLDPAY_ECOM_MACSECRET"]
+ # Govpay
+ config.govpay_url = ENV["WCRS_GOVPAY_URL"] || "https://publicapi.payments.service.gov.uk"
+ config.govpay_merchant_code = ENV["WCRS_GOVPAY_MERCHANT_CODE"]
+ config.govpay_api_token = ENV["WCRS_GOVPAY_API_TOKEN"]
+
# Emails
config.email_service_name = "Waste Carriers Registration Service"
config.email_service_email = ENV["WCRS_EMAIL_SERVICE_EMAIL"]
diff --git a/spec/factories/finance_details.rb b/spec/factories/finance_details.rb
index 346c3d7a9..39df3caa0 100644
--- a/spec/factories/finance_details.rb
+++ b/spec/factories/finance_details.rb
@@ -14,6 +14,12 @@
orders { [build(:order, :has_pending_worldpay_status)] }
end
+ trait :has_pending_govpay_order do
+ has_required_data
+
+ orders { [build(:order, :has_pending_govpay_status)] }
+ end
+
trait :has_order do
orders { [build(:order, :has_required_data)] }
end
diff --git a/spec/factories/new_registration.rb b/spec/factories/new_registration.rb
index 38f11e118..0a1deb861 100644
--- a/spec/factories/new_registration.rb
+++ b/spec/factories/new_registration.rb
@@ -31,6 +31,10 @@
finance_details { build(:finance_details, :has_pending_worldpay_order) }
end
+ trait :has_pending_govpay_status do
+ finance_details { build(:finance_details, :has_pending_govpay_order) }
+ end
+
trait :has_required_lower_tier_data do
location { "england" }
declared_convictions { "no" }
diff --git a/spec/factories/order.rb b/spec/factories/order.rb
index aa9b7d7f4..c9ee3ef5f 100644
--- a/spec/factories/order.rb
+++ b/spec/factories/order.rb
@@ -16,6 +16,12 @@
world_pay_status { "SENT_FOR_AUTHORISATION" }
end
+ trait :has_pending_govpay_status do
+ has_required_data
+
+ govpay_status { "created" }
+ end
+
trait :has_copy_cards_item do
date_created { Time.now }
diff --git a/spec/fixtures/files/govpay/create_payment_created_response.json b/spec/fixtures/files/govpay/create_payment_created_response.json
new file mode 100644
index 000000000..63d3d4fab
--- /dev/null
+++ b/spec/fixtures/files/govpay/create_payment_created_response.json
@@ -0,0 +1,24 @@
+{
+ "created_date": "2020-03-03T16:17:19.554Z",
+ "state": {
+ "status": "created",
+ "finished": false
+ },
+ "_links": {
+ "self": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/hu20sqlact5260q2nanm0q8u93",
+ "method": "GET"
+ },
+ "next_url": {
+ "href": "https://www.payments.service.gov.uk/secure/bb0a272c-8eaf-468d-b3xf-ae5e000d2231",
+ "method": "GET"
+ }
+ },
+ "amount": 14500,
+ "reference" : "12345",
+ "description": "Pay your council tax",
+ "return_url": "https://your.service.gov.uk/completed",
+ "payment_id": "hu20sqlact5260q2nanm0q8u93",
+ "payment_provider": "worldpay",
+ "provider_id": "10987654321"
+ }
\ No newline at end of file
diff --git a/spec/fixtures/files/govpay/create_payment_error_response.json b/spec/fixtures/files/govpay/create_payment_error_response.json
new file mode 100644
index 000000000..3d2f0b67c
--- /dev/null
+++ b/spec/fixtures/files/govpay/create_payment_error_response.json
@@ -0,0 +1,5 @@
+{
+ "field": "amount",
+ "code": "P0101",
+ "description": "Missing mandatory attribute: amount"
+}
\ No newline at end of file
diff --git a/spec/fixtures/files/govpay/get_payment_response_cancelled.json b/spec/fixtures/files/govpay/get_payment_response_cancelled.json
new file mode 100644
index 000000000..c38bb5451
--- /dev/null
+++ b/spec/fixtures/files/govpay/get_payment_response_cancelled.json
@@ -0,0 +1,61 @@
+{
+ "amount": 10501,
+ "description": "Waste carrier registration upper tier",
+ "reference": "12345",
+ "language": "en",
+ "metadata": {
+ "ledger_code": "AB100",
+ "an_internal_reference_number": 200
+ },
+ "email": "sherlock.holmes@example.com",
+ "state": {
+ "status": "failed",
+ "finished": true,
+ "message": "Payment was cancelled by the user",
+ "code": "P0030"
+ },
+ "payment_id": "cjgt8i57kavb8jfufhjlh129fm",
+ "payment_provider": "sandbox",
+ "created_date": "2022-05-18T11:46:54.697Z",
+ "refund_summary": {
+ "status": "unavailable",
+ "amount_available": 10501,
+ "amount_submitted": 0
+ },
+ "settlement_summary": {},
+ "card_details": {
+ "last_digits_card_number": "5100",
+ "first_digits_card_number": "510510",
+ "cardholder_name": "Sherlock Holmes",
+ "expiry_date": "01/24",
+ "billing_address": {
+ "line1": "221 Baker Street",
+ "line2": "Flat b",
+ "postcode": "NW1 6XE",
+ "city": "London",
+ "country": "GB"
+ },
+ "card_brand": "Mastercard",
+ "card_type": "debit"
+ },
+ "delayed_capture": false,
+ "moto": false,
+ "provider_id": "9fda0405-dc56-4d18-bf53-26167a91a1ac",
+ "return_url": "https://some-wcr-env.defra.gov.uk/completed",
+ "authorisation_mode": "web",
+ "card_brand": "Mastercard",
+ "_links": {
+ "self": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm",
+ "method": "GET"
+ },
+ "events": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/events",
+ "method": "GET"
+ },
+ "refunds": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/refunds",
+ "method": "GET"
+ }
+ }
+}
\ No newline at end of file
diff --git a/spec/fixtures/files/govpay/get_payment_response_created.json b/spec/fixtures/files/govpay/get_payment_response_created.json
new file mode 100644
index 000000000..db63489d1
--- /dev/null
+++ b/spec/fixtures/files/govpay/get_payment_response_created.json
@@ -0,0 +1,73 @@
+{
+ "amount": 10501,
+ "description": "Waste carrier registration upper tier",
+ "reference": "12345",
+ "language": "en",
+ "metadata": {
+ "ledger_code": "AB100",
+ "an_internal_reference_number": 200
+ },
+ "email": "sherlock.holmes@example.com",
+ "state": {
+ "status": "created",
+ "finished": false
+ },
+ "payment_id": "cjgt8i57kavb8jfufhjlh129fm",
+ "payment_provider": "sandbox",
+ "created_date": "2022-05-18T11:46:54.697Z",
+ "refund_summary": {
+ "status": "pending",
+ "amount_available": 10501,
+ "amount_submitted": 0
+ },
+ "settlement_summary": {},
+ "card_details": {
+ "last_digits_card_number": null,
+ "first_digits_card_number": null,
+ "cardholder_name": "Sherlock Holmes",
+ "expiry_date": null,
+ "billing_address": {
+ "line1": "221 Baker Street",
+ "line2": "Flat b",
+ "postcode": "NW1 6XE",
+ "city": "London",
+ "country": "GB"
+ },
+ "card_brand": "",
+ "card_type": null
+ },
+ "delayed_capture": false,
+ "moto": false,
+ "return_url": "https://some-wcr-env.defra.gov.uk/completed",
+ "authorisation_mode": "web",
+ "_links": {
+ "self": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm",
+ "method": "GET"
+ },
+ "next_url": {
+ "href": "https://www.payments.service.gov.uk/secure/c3b8908b-68df-478a-ad94-6a724e614e46",
+ "method": "GET"
+ },
+ "next_url_post": {
+ "type": "application/x-www-form-urlencoded",
+ "params": {
+ "chargeTokenId": "c3b8908b-68df-478a-ad94-6a724e614e46"
+ },
+ "href": "https://www.payments.service.gov.uk/secure",
+ "method": "POST"
+ },
+ "events": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/events",
+ "method": "GET"
+ },
+ "refunds": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/refunds",
+ "method": "GET"
+ },
+ "cancel": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/cancel",
+ "method": "POST"
+ }
+ }
+}
\ No newline at end of file
diff --git a/spec/fixtures/files/govpay/get_payment_response_failure.json b/spec/fixtures/files/govpay/get_payment_response_failure.json
new file mode 100644
index 000000000..adaf15077
--- /dev/null
+++ b/spec/fixtures/files/govpay/get_payment_response_failure.json
@@ -0,0 +1,9 @@
+{
+ "amount": 2000,
+ "state": {
+ "status": "failed",
+ "finished": true,
+ "message": "Payment expired",
+ "code": "P0020"
+ }
+ }
diff --git a/spec/fixtures/files/govpay/get_payment_response_not_found.json b/spec/fixtures/files/govpay/get_payment_response_not_found.json
new file mode 100644
index 000000000..6cf279db3
--- /dev/null
+++ b/spec/fixtures/files/govpay/get_payment_response_not_found.json
@@ -0,0 +1,4 @@
+{
+ "code": "P0200",
+ "description": "Not found"
+}
\ No newline at end of file
diff --git a/spec/fixtures/files/govpay/get_payment_response_submitted.json b/spec/fixtures/files/govpay/get_payment_response_submitted.json
new file mode 100644
index 000000000..1f5660fd9
--- /dev/null
+++ b/spec/fixtures/files/govpay/get_payment_response_submitted.json
@@ -0,0 +1,75 @@
+{
+ "amount": 10501,
+ "description": "Waste carrier registration upper tier",
+ "reference": "12345",
+ "language": "en",
+ "metadata": {
+ "ledger_code": "AB100",
+ "an_internal_reference_number": 200
+ },
+ "email": "sherlock.holmes@example.com",
+ "state": {
+ "status": "submitted",
+ "finished": false
+ },
+ "payment_id": "cjgt8i57kavb8jfufhjlh129fm",
+ "payment_provider": "sandbox",
+ "created_date": "2022-05-18T11:46:54.697Z",
+ "refund_summary": {
+ "status": "pending",
+ "amount_available": 10501,
+ "amount_submitted": 0
+ },
+ "settlement_summary": {},
+ "card_details": {
+ "last_digits_card_number": "5100",
+ "first_digits_card_number": "510510",
+ "cardholder_name": "Sherlock Holmes",
+ "expiry_date": "01/24",
+ "billing_address": {
+ "line1": "221 Baker Street",
+ "line2": "Flat b",
+ "postcode": "NW1 6XE",
+ "city": "London",
+ "country": "GB"
+ },
+ "card_brand": "Mastercard",
+ "card_type": "debit"
+ },
+ "delayed_capture": false,
+ "moto": false,
+ "provider_id": "9fda0405-dc56-4d18-bf53-26167a91a1ac",
+ "return_url": "https://some-wcr-env.defra.gov.uk/completed",
+ "authorisation_mode": "web",
+ "card_brand": "Mastercard",
+ "_links": {
+ "self": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm",
+ "method": "GET"
+ },
+ "next_url": {
+ "href": "https://www.payments.service.gov.uk/secure/18a144e1-f6d8-4d73-a11a-a45187da9138",
+ "method": "GET"
+ },
+ "next_url_post": {
+ "type": "application/x-www-form-urlencoded",
+ "params": {
+ "chargeTokenId": "18a144e1-f6d8-4d73-a11a-a45187da9138"
+ },
+ "href": "https://www.payments.service.gov.uk/secure",
+ "method": "POST"
+ },
+ "events": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/events",
+ "method": "GET"
+ },
+ "refunds": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/refunds",
+ "method": "GET"
+ },
+ "cancel": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cjgt8i57kavb8jfufhjlh129fm/cancel",
+ "method": "POST"
+ }
+ }
+}
\ No newline at end of file
diff --git a/spec/fixtures/files/govpay/get_payment_response_success.json b/spec/fixtures/files/govpay/get_payment_response_success.json
new file mode 100644
index 000000000..8beca9bd1
--- /dev/null
+++ b/spec/fixtures/files/govpay/get_payment_response_success.json
@@ -0,0 +1,62 @@
+{
+ "amount": 10501,
+ "description": "Waste carrier registration upper tier",
+ "reference": "12345",
+ "language": "en",
+ "metadata": {
+ "ledger_code": "AB100",
+ "an_internal_reference_number": 200
+ },
+ "email": "sherlock.holmes@example.com",
+ "state": {
+ "status": "success",
+ "finished": true
+ },
+ "payment_id": "cnnffa1e6s3u9a6n24u2cp527d",
+ "payment_provider": "sandbox",
+ "created_date": "2022-05-18T11:52:13.669Z",
+ "refund_summary": {
+ "status": "available",
+ "amount_available": 10501,
+ "amount_submitted": 0
+ },
+ "settlement_summary": {
+ "capture_submit_time": "2022-05-18T11:52:39.172Z",
+ "captured_date": "2022-05-18"
+ },
+ "card_details": {
+ "last_digits_card_number": "5100",
+ "first_digits_card_number": "510510",
+ "cardholder_name": "Sherlock Holmes",
+ "expiry_date": "01/24",
+ "billing_address": {
+ "line1": "221 Baker Street",
+ "line2": "Flat b",
+ "postcode": "NW1 6XE",
+ "city": "London",
+ "country": "GB"
+ },
+ "card_brand": "Mastercard",
+ "card_type": "debit"
+ },
+ "delayed_capture": false,
+ "moto": false,
+ "provider_id": "9bb0c2c1-d0c5-4a63-8945-f4240e06f8ae",
+ "return_url": "https://some-wcr-env.defra.gov.uk/completed",
+ "authorisation_mode": "web",
+ "card_brand": "Mastercard",
+ "_links": {
+ "self": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cnnffa1e6s3u9a6n24u2cp527d",
+ "method": "GET"
+ },
+ "events": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cnnffa1e6s3u9a6n24u2cp527d/events",
+ "method": "GET"
+ },
+ "refunds": {
+ "href": "https://publicapi.payments.service.gov.uk/v1/payments/cnnffa1e6s3u9a6n24u2cp527d/refunds",
+ "method": "GET"
+ }
+ }
+}
\ No newline at end of file
diff --git a/spec/fixtures/files/request_to_worldpay.xml b/spec/fixtures/files/worldpay/request_to_worldpay.xml
similarity index 100%
rename from spec/fixtures/files/request_to_worldpay.xml
rename to spec/fixtures/files/worldpay/request_to_worldpay.xml
diff --git a/spec/fixtures/files/response_from_worldpay.xml b/spec/fixtures/files/worldpay/response_from_worldpay.xml
similarity index 100%
rename from spec/fixtures/files/response_from_worldpay.xml
rename to spec/fixtures/files/worldpay/response_from_worldpay.xml
diff --git a/spec/fixtures/worldpay_initial_request.xml b/spec/fixtures/files/worldpay/worldpay_initial_request.xml
similarity index 100%
rename from spec/fixtures/worldpay_initial_request.xml
rename to spec/fixtures/files/worldpay/worldpay_initial_request.xml
diff --git a/spec/fixtures/worldpay_initial_request_invalid.xml b/spec/fixtures/files/worldpay/worldpay_initial_request_invalid.xml
similarity index 100%
rename from spec/fixtures/worldpay_initial_request_invalid.xml
rename to spec/fixtures/files/worldpay/worldpay_initial_request_invalid.xml
diff --git a/spec/fixtures/worldpay_redirect.xml b/spec/fixtures/files/worldpay/worldpay_redirect.xml
similarity index 100%
rename from spec/fixtures/worldpay_redirect.xml
rename to spec/fixtures/files/worldpay/worldpay_redirect.xml
diff --git a/spec/models/waste_carriers_engine/new_registration_workflow/govpay_form_spec.rb b/spec/models/waste_carriers_engine/new_registration_workflow/govpay_form_spec.rb
new file mode 100644
index 000000000..c3cbef6b9
--- /dev/null
+++ b/spec/models/waste_carriers_engine/new_registration_workflow/govpay_form_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe NewRegistration do
+ before { allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true) }
+
+ subject { build(:new_registration, workflow_state: "govpay_form") }
+
+ describe "#workflow_state" do
+ context ":govpay_form state transitions" do
+ context "on next" do
+ include_examples "has next transition", next_state: "registration_completed_form"
+
+ context "when there are pending convictions" do
+ subject { build(:new_registration, :requires_conviction_check, workflow_state: "govpay_form") }
+
+ include_examples "has next transition", next_state: "registration_received_pending_conviction_form"
+ end
+
+ context "when there is a pending govpay payment" do
+ subject { build(:new_registration, :has_pending_govpay_status, workflow_state: "govpay_form") }
+
+ include_examples "has next transition", next_state: "registration_received_pending_govpay_payment_form"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/waste_carriers_engine/new_registration_workflow/payment_summary_form_spec.rb b/spec/models/waste_carriers_engine/new_registration_workflow/payment_summary_form_spec.rb
index c5c4eec28..3f7b5bda5 100644
--- a/spec/models/waste_carriers_engine/new_registration_workflow/payment_summary_form_spec.rb
+++ b/spec/models/waste_carriers_engine/new_registration_workflow/payment_summary_form_spec.rb
@@ -9,10 +9,17 @@ module WasteCarriersEngine
describe "#workflow_state" do
context ":payment_summary_form state transitions" do
context "on next" do
- context "when the user choose to pay by card" do
+ context "when the user chooses to pay by card" do
subject { build(:new_registration, workflow_state: "payment_summary_form", temp_payment_method: "card") }
- include_examples "has next transition", next_state: "worldpay_form"
+ context "and Worldpay payments are enabled" do
+ include_examples "has next transition", next_state: "worldpay_form"
+ end
+
+ context "and Govpay payments are enabled" do
+ before { allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true) }
+ include_examples "has next transition", next_state: "govpay_form"
+ end
end
include_examples "has next transition", next_state: "confirm_bank_transfer_form"
diff --git a/spec/models/waste_carriers_engine/order_spec.rb b/spec/models/waste_carriers_engine/order_spec.rb
index 1fcb89f0d..a9c9a0a97 100644
--- a/spec/models/waste_carriers_engine/order_spec.rb
+++ b/spec/models/waste_carriers_engine/order_spec.rb
@@ -154,12 +154,12 @@ module WasteCarriersEngine
end
end
- describe "update_after_worldpay" do
+ describe "update_after_online_payment" do
let(:finance_details) { transient_registration.prepare_for_payment(:worldpay, current_user) }
let(:order) { finance_details.orders.first }
it "copies the worldpay status to the order" do
- order.update_after_worldpay("AUTHORISED")
+ order.update_after_online_payment("AUTHORISED")
expect(order.world_pay_status).to eq("AUTHORISED")
end
@@ -168,10 +168,30 @@ module WasteCarriersEngine
# Wipe the date first so we know the value has been added
order.update_attributes(date_last_updated: nil)
- order.update_after_worldpay("AUTHORISED")
+ order.update_after_online_payment("AUTHORISED")
expect(order.date_last_updated).to eq(Time.new(2004, 8, 15, 16, 23, 42))
end
end
end
+
+ describe "#payment_uuid" do
+ let(:transient_registration) { build(:renewing_registration, :has_required_data, :has_finance_details) }
+ let(:order) { described_class.new(finance_details: transient_registration.finance_details) }
+
+ context "with no pre-existing uuid" do
+ it "generates and saves a uuid" do
+ expect(order[:payment_uuid]).to be_nil
+ expect(order.payment_uuid).to be_present
+ expect(order[:payment_uuid]).to be_present
+ end
+ end
+
+ context "with a pre-existing uuid" do
+ it "returns the existing uuid" do
+ uuid = order.payment_uuid
+ expect(order.payment_uuid).to eq uuid
+ end
+ end
+ end
end
end
diff --git a/spec/models/waste_carriers_engine/payment_spec.rb b/spec/models/waste_carriers_engine/payment_spec.rb
index 8d298b562..e386b4e40 100644
--- a/spec/models/waste_carriers_engine/payment_spec.rb
+++ b/spec/models/waste_carriers_engine/payment_spec.rb
@@ -57,9 +57,33 @@ module WasteCarriersEngine
expect(result).to_not include(refund_payment)
end
end
+
+ describe ".except_online_not_authorised" do
+ let(:transient_registration) { build(:renewing_registration, :has_required_data, :has_finance_details) }
+ let(:cash_payment) { WasteCarriersEngine::Payment.new(payment_type: "CASH") }
+ let(:worldpay_payment_authorised) { WasteCarriersEngine::Payment.new(payment_type: "WORLDPAY", world_pay_payment_status: "AUTHORISED") }
+ let(:worldpay_payment_refused) { WasteCarriersEngine::Payment.new(payment_type: "WORLDPAY", world_pay_payment_status: "REFUSED") }
+ let(:govpay_payment_authorised) { WasteCarriersEngine::Payment.new(payment_type: "GOVPAY", govpay_payment_status: "success") }
+ let(:govpay_payment_refused) { WasteCarriersEngine::Payment.new(payment_type: "GOVPAY", govpay_payment_status: "failed") }
+
+ before do
+ transient_registration.finance_details.payments << cash_payment << worldpay_payment_authorised << govpay_payment_authorised << govpay_payment_refused
+ transient_registration.save
+ transient_registration.reload
+ end
+
+ it "returns the expected payments only" do
+ result = transient_registration.finance_details.payments.except_online_not_authorised
+ expect(result).to include(cash_payment)
+ expect(result).to include(worldpay_payment_authorised)
+ expect(result).not_to include(worldpay_payment_refused)
+ expect(result).to include(govpay_payment_authorised)
+ expect(result).not_to include(govpay_payment_refused)
+ end
+ end
end
- describe "new_from_worldpay" do
+ describe "new_from_online_payment" do
before do
Timecop.freeze(Time.new(2018, 1, 1)) do
transient_registration.prepare_for_payment(:worldpay, current_user)
@@ -67,7 +91,7 @@ module WasteCarriersEngine
end
let(:order) { transient_registration.finance_details.orders.first }
- let(:payment) { Payment.new_from_worldpay(order, current_user.email) }
+ let(:payment) { Payment.new_from_online_payment(order, current_user.email) }
it "should set the correct order_key" do
expect(payment.order_key).to eq("1514764800")
@@ -98,7 +122,7 @@ module WasteCarriersEngine
end
end
- describe "new_from_non_worldpay" do
+ describe "new_from_non_online_payment" do
before do
Timecop.freeze(Time.new(2018, 1, 1)) do
transient_registration.prepare_for_payment(:worldpay, current_user)
@@ -120,7 +144,7 @@ module WasteCarriersEngine
end
let(:order) { transient_registration.finance_details.orders.first }
- let(:payment) { Payment.new_from_non_worldpay(params, order) }
+ let(:payment) { Payment.new_from_non_online_payment(params, order) }
it "should set the correct amount" do
expect(payment.amount).to eq(params[:amount])
@@ -175,14 +199,14 @@ module WasteCarriersEngine
end
end
- describe "update_after_worldpay" do
+ describe "update_after_online_payment" do
let(:order) { transient_registration.finance_details.orders.first }
- let(:payment) { Payment.new_from_worldpay(order, current_user.email) }
+ let(:payment) { Payment.new_from_online_payment(order, current_user.email) }
before do
Timecop.freeze(Time.new(2018, 3, 4)) do
transient_registration.prepare_for_payment(:worldpay, current_user)
- payment.update_after_worldpay(paymentStatus: "AUTHORISED", mac: "foo")
+ payment.update_after_online_payment({ paymentStatus: "AUTHORISED", mac: "foo" })
end
end
diff --git a/spec/models/waste_carriers_engine/renewing_registration_spec.rb b/spec/models/waste_carriers_engine/renewing_registration_spec.rb
index d636074d8..1c877cf8b 100644
--- a/spec/models/waste_carriers_engine/renewing_registration_spec.rb
+++ b/spec/models/waste_carriers_engine/renewing_registration_spec.rb
@@ -31,7 +31,7 @@ module WasteCarriersEngine
context "when transitioning from worldpay_form to renewal_complete_form successfully" do
it "set the transient registration metadata route" do
expect(renewing_registration).to receive(:set_metadata_route).once
- expect(renewing_registration).to receive(:pending_worldpay_payment?).and_return(false)
+ expect(renewing_registration).to receive(:pending_online_payment?).and_return(false)
expect(renewing_registration).to receive(:conviction_check_required?).and_return(false)
renewing_registration.update_attributes(workflow_state: :worldpay_form)
@@ -42,7 +42,7 @@ module WasteCarriersEngine
context "when transitioning from worldpay_form to renewal_received_pending_conviction_form succesfully" do
it "set the transient registration metadata route" do
expect(renewing_registration).to receive(:set_metadata_route).once
- expect(renewing_registration).to receive(:pending_worldpay_payment?).and_return(false)
+ expect(renewing_registration).to receive(:pending_online_payment?).and_return(false)
expect(renewing_registration).to receive(:conviction_check_required?).and_return(true)
renewing_registration.update_attributes(workflow_state: :worldpay_form)
diff --git a/spec/models/waste_carriers_engine/renewing_registration_workflow/govpay_form_spec.rb b/spec/models/waste_carriers_engine/renewing_registration_workflow/govpay_form_spec.rb
new file mode 100644
index 000000000..40de4fbcf
--- /dev/null
+++ b/spec/models/waste_carriers_engine/renewing_registration_workflow/govpay_form_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe RenewingRegistration, type: :model do
+ subject do
+ build(:renewing_registration,
+ :has_required_data,
+ :has_conviction_search_result,
+ :has_key_people,
+ :has_paid_balance,
+ workflow_state: "govpay_form")
+ end
+
+ describe "#workflow_state" do
+ context ":govpay_form state transitions" do
+ context "on next" do
+ context "when a conviction check is not required" do
+ before do
+ allow(subject).to receive(:conviction_check_required?).and_return(false)
+ end
+
+ context "when there is no pending Govpay payment" do
+ before do
+ allow(subject).to receive(:pending_online_payment?).and_return(false)
+ end
+
+ include_examples "has next transition", next_state: "renewal_complete_form"
+
+ it "does not send a confirmation email after the 'next' event" do
+ expect(Notifications::Client).not_to receive(:new)
+
+ subject.next!
+ end
+ end
+
+ context "when there is a pending Govpay payment" do
+ before do
+ allow(subject).to receive(:pending_online_payment?).and_return(true)
+ end
+
+ include_examples "has next transition", next_state: "renewal_received_pending_govpay_payment_form"
+
+ it "sends a confirmation email after the 'next' event" do
+ expect(Notify::RenewalPendingOnlinePaymentEmailService)
+ .to receive(:run)
+ .with(registration: subject)
+ .once
+
+ subject.next!
+ end
+ end
+ end
+
+ context "when a conviction check is required" do
+ before do
+ allow(subject).to receive(:conviction_check_required?).and_return(true)
+ end
+
+ include_examples "has next transition", next_state: "renewal_received_pending_conviction_form"
+
+ it "sends a confirmation email after the 'next' event" do
+ expect(Notify::RenewalPendingChecksEmailService)
+ .to receive(:run)
+ .with(registration: subject)
+ .once
+
+ subject.next!
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/waste_carriers_engine/renewing_registration_workflow/payment_summary_form_spec.rb b/spec/models/waste_carriers_engine/renewing_registration_workflow/payment_summary_form_spec.rb
index b195ff23c..ce38fe474 100644
--- a/spec/models/waste_carriers_engine/renewing_registration_workflow/payment_summary_form_spec.rb
+++ b/spec/models/waste_carriers_engine/renewing_registration_workflow/payment_summary_form_spec.rb
@@ -18,7 +18,14 @@ module WasteCarriersEngine
context "when paying by card" do
let(:temp_payment_method) { "card" }
- include_examples "has next transition", next_state: "worldpay_form"
+ context "and Worldpay payments are enabled" do
+ include_examples "has next transition", next_state: "worldpay_form"
+ end
+
+ context "and Govpay payments are enabled" do
+ before { allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true) }
+ include_examples "has next transition", next_state: "govpay_form"
+ end
end
context "when paying by bank transfer" do
diff --git a/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_govpay_payment_form_spec.rb b/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_govpay_payment_form_spec.rb
new file mode 100644
index 000000000..33222d719
--- /dev/null
+++ b/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_govpay_payment_form_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe RenewalReceivedPendingGovpayPaymentForm, type: :model do
+ describe "#workflow_state" do
+ it_behaves_like "a fixed final state",
+ current_state: :renewal_received_pending_govpay_payment_form,
+ factory: :renewing_registration
+ end
+ end
+end
diff --git a/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_payment_form_spec.rb b/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_payment_form_spec.rb
index d6686797a..83e14e266 100644
--- a/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_payment_form_spec.rb
+++ b/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_payment_form_spec.rb
@@ -3,7 +3,7 @@
require "rails_helper"
module WasteCarriersEngine
- RSpec.describe RenewingRegistration, type: :model do
+ RSpec.describe RenewalReceivedPendingPaymentForm, type: :model do
describe "#workflow_state" do
it_behaves_like "a fixed final state",
current_state: :renewal_received_pending_payment_form,
diff --git a/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_worldpay_payment_form_spec.rb b/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_worldpay_payment_form_spec.rb
index b699a137a..cdf318805 100644
--- a/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_worldpay_payment_form_spec.rb
+++ b/spec/models/waste_carriers_engine/renewing_registration_workflow/renewal_received_pending_worldpay_payment_form_spec.rb
@@ -3,7 +3,7 @@
require "rails_helper"
module WasteCarriersEngine
- RSpec.describe RenewingRegistration, type: :model do
+ RSpec.describe RenewalReceivedPendingWorldpayPaymentForm, type: :model do
describe "#workflow_state" do
it_behaves_like "a fixed final state",
current_state: :renewal_received_pending_worldpay_payment_form,
diff --git a/spec/models/waste_carriers_engine/renewing_registration_workflow/worldpay_form_spec.rb b/spec/models/waste_carriers_engine/renewing_registration_workflow/worldpay_form_spec.rb
index 6d25efd06..ec08fac8f 100644
--- a/spec/models/waste_carriers_engine/renewing_registration_workflow/worldpay_form_spec.rb
+++ b/spec/models/waste_carriers_engine/renewing_registration_workflow/worldpay_form_spec.rb
@@ -23,7 +23,7 @@ module WasteCarriersEngine
context "when there is no pending WorldPay payment" do
before do
- allow(subject).to receive(:pending_worldpay_payment?).and_return(false)
+ allow(subject).to receive(:pending_online_payment?).and_return(false)
end
include_examples "has next transition", next_state: "renewal_complete_form"
@@ -41,13 +41,13 @@ module WasteCarriersEngine
context "when there is a pending WorldPay payment" do
before do
- allow(subject).to receive(:pending_worldpay_payment?).and_return(true)
+ allow(subject).to receive(:pending_online_payment?).and_return(true)
end
include_examples "has next transition", next_state: "renewal_received_pending_worldpay_payment_form"
it "sends a confirmation email after the 'next' event" do
- expect(Notify::RenewalPendingWorldpayPaymentEmailService)
+ expect(Notify::RenewalPendingOnlinePaymentEmailService)
.to receive(:run)
.with(registration: subject)
.once
diff --git a/spec/models/waste_carriers_engine/transient_registration_spec.rb b/spec/models/waste_carriers_engine/transient_registration_spec.rb
index 29a399891..065b3e4a5 100644
--- a/spec/models/waste_carriers_engine/transient_registration_spec.rb
+++ b/spec/models/waste_carriers_engine/transient_registration_spec.rb
@@ -116,7 +116,7 @@ module WasteCarriersEngine
end
end
- describe "#pending_worldpay_payment?" do
+ describe "#pending_online_payment?" do
context "when the renewal has an order" do
before do
transient_registration.finance_details = build(:finance_details, :has_order)
@@ -128,7 +128,7 @@ module WasteCarriersEngine
end
it "returns true" do
- expect(transient_registration.pending_worldpay_payment?).to eq(true)
+ expect(transient_registration.pending_online_payment?).to eq(true)
end
end
@@ -138,7 +138,7 @@ module WasteCarriersEngine
end
it "returns false" do
- expect(transient_registration.pending_worldpay_payment?).to eq(false)
+ expect(transient_registration.pending_online_payment?).to eq(false)
end
end
end
@@ -149,7 +149,7 @@ module WasteCarriersEngine
end
it "returns false" do
- expect(transient_registration.pending_worldpay_payment?).to eq(false)
+ expect(transient_registration.pending_online_payment?).to eq(false)
end
end
end
diff --git a/spec/requests/waste_carriers_engine/govpay_forms_spec.rb b/spec/requests/waste_carriers_engine/govpay_forms_spec.rb
new file mode 100644
index 000000000..6d004b66b
--- /dev/null
+++ b/spec/requests/waste_carriers_engine/govpay_forms_spec.rb
@@ -0,0 +1,276 @@
+# frozen_string_literal: true
+
+require "webmock/rspec"
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe "GovpayForms", type: :request do
+ let(:govpay_host) { "https://publicapi.payments.service.gov.uk" }
+ let(:order) { transient_registration.finance_details.orders.first }
+ let(:order_key) { "#{Rails.configuration.govpay_merchant_code}^#{order.order_code}" }
+
+ before do
+ allow(Rails.configuration).to receive(:govpay_url).and_return(govpay_host)
+ allow(Rails.configuration).to receive(:govpay_merchant_code).and_return("some_merchant_code")
+ allow(Rails.configuration).to receive(:govpay_api_token).and_return("some_token")
+ end
+
+ # TODO: Remove this when the feature flag is no longer required
+ # before { allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true) }
+
+ context "when a valid user is signed in" do
+ let(:user) { create(:user) }
+ before(:each) do
+ sign_in(user)
+ end
+
+ context "when a valid transient registration exists" do
+ let(:transient_registration) do
+ create(:renewing_registration,
+ :has_required_data,
+ :has_addresses,
+ :has_conviction_search_result,
+ :has_key_people,
+ account_email: user.email,
+ workflow_state: "govpay_form",
+ workflow_history: ["payment_summary_form"])
+ end
+ let(:order) { transient_registration.finance_details.orders.first }
+ let(:token) { transient_registration[:token] }
+
+ describe "#new" do
+
+ before do
+ stub_request(:any, /.*#{govpay_host}.*/).to_return(
+ status: 200,
+ body: File.read("./spec/fixtures/files/govpay/get_payment_response_created.json")
+ )
+ end
+
+ it "creates a new finance_details" do
+ get new_govpay_form_path(token)
+ expect(transient_registration.reload.finance_details).to be_present
+ end
+
+ it "redirects to govpay" do
+ get new_govpay_form_path(token)
+ expect(response.location).to include("https://www.payments.service.gov.uk")
+ end
+
+ it "populates govpay_id on the order" do
+ get new_govpay_form_path(token)
+ expect(transient_registration.reload.finance_details.orders[0].govpay_id).to be_present
+ end
+
+ context "when the transient_registration is a new registration" do
+ let(:transient_registration) do
+ create(:new_registration,
+ :has_addresses,
+ contact_email: user.email,
+ workflow_state: "govpay_form",
+ temp_cards: 2)
+ end
+
+ it "creates a new finance_details" do
+ get new_govpay_form_path(token)
+ expect(transient_registration.reload.finance_details).to be_present
+ end
+ end
+
+ context "when there is an error setting up the govpay url" do
+ before do
+ allow_any_instance_of(GovpayPaymentService).to receive(:prepare_for_payment).and_return(:error)
+ end
+
+ it "redirects to payment_summary_form" do
+ get new_govpay_form_path(token)
+ expect(response).to redirect_to(new_payment_summary_form_path(token))
+ end
+ end
+ end
+
+ describe "#payment_callback" do
+ let(:govpay_host) { "https://publicapi.payments.service.gov.uk/v1" }
+
+ before do
+ allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true)
+ allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:use_extended_grace_window).and_return(true)
+ allow(Rails.configuration).to receive(:govpay_url).and_return(govpay_host)
+ allow(Rails.configuration).to receive(:metadata_route).and_return("ASSISTED_DIGITAL")
+ stub_request(:any, %r{.*#{govpay_host}/payments}).to_return(
+ status: 200,
+ body: File.read("./spec/fixtures/files/govpay/get_payment_response_#{govpay_status}.json")
+ )
+ transient_registration.prepare_for_payment(:govpay, user)
+ GovpayPaymentService.new(transient_registration, order, user).prepare_for_payment
+ end
+
+ context "when govpay status is success" do
+ let(:govpay_status) { "success" }
+
+ context "when the payment_uuid is valid and the balance is paid" do
+
+ it "adds a new payment to the registration" do
+ expect { get payment_callback_govpay_forms_path(token, order.payment_uuid) }
+ .to change { transient_registration.reload.finance_details.payments.count }.from(0).to(1)
+ end
+
+ it "redirects to renewal_complete_form" do
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+
+ expect(response).to redirect_to(new_renewal_complete_form_path(token))
+ end
+
+ it "updates the metadata route" do
+ expect(transient_registration.reload.metaData.route).to be_nil
+
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+
+ expect(transient_registration.reload.metaData.route).to eq("ASSISTED_DIGITAL")
+ end
+
+ it "is idempotent" do
+ expect do
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+ transient_registration.reload
+ end.to change { transient_registration.finance_details.payments.count }.from(0).to(1)
+ end
+
+ context "when it has been flagged for conviction checks" do
+ before { transient_registration.conviction_sign_offs = [build(:conviction_sign_off)] }
+
+ it "updates the transient registration metadata attributes from application configuration" do
+ expect(transient_registration.reload.metaData.route).to be_nil
+
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+
+ expect(transient_registration.reload.metaData.route).to eq("ASSISTED_DIGITAL")
+ end
+
+ it "redirects to renewal_received_pending_conviction_form" do
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+
+ expect(response).to redirect_to(new_renewal_received_pending_conviction_form_path(token))
+ end
+
+ context "when the mailer fails" do
+ before do
+ allow(Rails.configuration.action_mailer).to receive(:raise_delivery_errors).and_return(true)
+ allow_any_instance_of(ActionMailer::MessageDelivery).to receive(:deliver_now).and_raise(StandardError)
+ end
+
+ it "does not raise an error" do
+ expect { get payment_callback_govpay_forms_path(token, order.payment_uuid) }.to_not raise_error
+ end
+ end
+ end
+ end
+
+ context "when the payment uuid is invalid" do
+ before do
+ stub_request(:any, %r{.*#{govpay_host}/payments}).to_return(
+ status: 200,
+ body: File.read("./spec/fixtures/files/govpay/get_payment_response_not_found.json")
+ )
+ end
+
+ it "does not create a payment" do
+ get payment_callback_govpay_forms_path(token, "invalid_uuid")
+ expect(transient_registration.reload.finance_details.payments.first).to be_nil
+ end
+
+ it "redirects to payment_summary_form" do
+ get payment_callback_govpay_forms_path(token, "invalid_uuid")
+ expect(response).to redirect_to(new_payment_summary_form_path(token))
+ end
+ end
+ end
+
+ context "for pending govpay statuses" do
+
+ RSpec.shared_examples "payment is pending" do
+
+ context "when the payment uuid is valid" do
+ before do
+ allow_any_instance_of(RenewingRegistration).to receive(:pending_online_payment?).and_return(true)
+ end
+
+ it "redirects to renewal_received_pending_govpay_payment_form" do
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+ expect(response).to redirect_to(new_renewal_received_pending_govpay_payment_form_path(token))
+ end
+ end
+
+ context "when the payment uuid is invalid" do
+ it "redirects to payment_summary_form" do
+ get payment_callback_govpay_forms_path(token, "invalid_payment_uuid")
+ expect(response).to redirect_to(new_payment_summary_form_path(token))
+ end
+ end
+ end
+
+ context "when govpay status is created" do
+ let(:govpay_status) { "created" }
+ it_behaves_like "payment is pending"
+ end
+
+ context "when govpay status is submitted" do
+ let(:govpay_status) { "submitted" }
+ it_behaves_like "payment is pending"
+ end
+ end
+
+ context "for unsuccessful govpay statuses" do
+
+ RSpec.shared_examples "payment is unsuccessful" do
+
+ context "when the payment uuid is valid" do
+ it "redirects to payment_summary_form" do
+ get payment_callback_govpay_forms_path(token, order.payment_uuid)
+ expect(response).to redirect_to(new_payment_summary_form_path(token))
+ end
+ end
+
+ context "when the payment uuid is invalid" do
+ it "redirects to payment_summary_form" do
+ get payment_callback_govpay_forms_path(token, "invalid_payment_uuid")
+ expect(response).to redirect_to(new_payment_summary_form_path(token))
+ end
+ end
+ end
+
+ context "when govpay status is cancel" do
+ let(:govpay_status) { "cancelled" }
+ it_behaves_like "payment is unsuccessful"
+ end
+
+ context "failure" do
+ let(:govpay_status) { "failure" }
+ it_behaves_like "payment is unsuccessful"
+ end
+
+ context "error" do
+ let(:govpay_status) { "not_found" }
+ it_behaves_like "payment is unsuccessful"
+ end
+ end
+
+ context "for an invalid success status" do
+ before { allow(GovpayValidatorService).to receive(:valid_govpay_status?).and_return(false) }
+
+ let(:govpay_status) { "success" }
+ it_behaves_like "payment is unsuccessful"
+ end
+
+ context "for an invalid failure status" do
+ before { allow(GovpayValidatorService).to receive(:valid_govpay_status?).and_return(false) }
+
+ let(:govpay_status) { "cancelled" }
+ it_behaves_like "payment is unsuccessful"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/waste_carriers_engine/registration_received_pending_govpay_payment_forms_spec.rb b/spec/requests/waste_carriers_engine/registration_received_pending_govpay_payment_forms_spec.rb
new file mode 100644
index 000000000..91e540b38
--- /dev/null
+++ b/spec/requests/waste_carriers_engine/registration_received_pending_govpay_payment_forms_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe "RegistrationReceivedPendingGovpayPaymentForm", type: :request do
+ describe "GET new_registration_received_pending_govpay_payment_form_path" do
+ context "when no new registration exists" do
+ it "redirects to the invalid page" do
+ get new_registration_received_pending_govpay_payment_form_path("wibblewobblejellyonaplate")
+
+ expect(response).to redirect_to(page_path("invalid"))
+ end
+ end
+
+ context "when a valid new registration exists" do
+ let(:transient_registration) do
+ create(
+ :new_registration,
+ :has_required_data,
+ workflow_state: "registration_received_pending_govpay_payment_form"
+ )
+ end
+
+ context "when the workflow_state is correct" do
+ it "returns a 200 status, renders the :new template, creates a new registration and deletes the transient registration" do
+ reg_identifier = transient_registration.reg_identifier
+ new_registrations_count = WasteCarriersEngine::NewRegistration.count
+
+ get new_registration_received_pending_govpay_payment_form_path(transient_registration.token)
+
+ registration = WasteCarriersEngine::Registration.find_by(reg_identifier: reg_identifier)
+
+ expect(response).to have_http_status(200)
+ expect(response).to render_template(:new)
+ expect(registration).to be_valid
+ expect(WasteCarriersEngine::NewRegistration.count).to eq(new_registrations_count - 1)
+ end
+ end
+
+ context "when the workflow_state is not correct" do
+ before do
+ transient_registration.update_attributes(workflow_state: "payment_summary_form")
+ end
+
+ it "redirects to the correct page and does not creates a new registration nor delete the transient object" do
+ new_registrations_count = WasteCarriersEngine::NewRegistration.count
+
+ get new_registration_received_pending_govpay_payment_form_path(transient_registration.token)
+
+ registration_scope = WasteCarriersEngine::Registration.where(reg_identifier: transient_registration.reg_identifier)
+
+ expect(response).to redirect_to(new_payment_summary_form_path(transient_registration.token))
+ expect(WasteCarriersEngine::NewRegistration.count).to eq(new_registrations_count)
+ expect(registration_scope).to be_empty
+ end
+ end
+
+ context "when the registration completion service fails" do
+ let(:the_error) { StandardError.new("Oops!") }
+
+ before do
+ allow(RegistrationCompletionService)
+ .to receive(:run)
+ .with(transient_registration)
+ .and_raise(the_error)
+
+ # Airbrake may receive notifications other than the specific one used in the spec below
+ allow(Airbrake).to receive(:notify)
+ end
+
+ it "logs the exception" do
+ expect(Airbrake)
+ .to receive(:notify)
+ .with(the_error, { reg_identifier: transient_registration.reg_identifier })
+
+ begin
+ get new_registration_received_pending_govpay_payment_form_path(transient_registration.token)
+ rescue ActionView::Template::Error
+ # Capture the exception raised in the view
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/waste_carriers_engine/renewal_received_pending_govpay_payment_forms_spec.rb b/spec/requests/waste_carriers_engine/renewal_received_pending_govpay_payment_forms_spec.rb
new file mode 100644
index 000000000..7c12d0e43
--- /dev/null
+++ b/spec/requests/waste_carriers_engine/renewal_received_pending_govpay_payment_forms_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe "RenewalReceivedPendingGovpayPaymentForms", type: :request do
+ describe "GET new_renewal_received_pending_govpay_payment_form_path" do
+ context "when a valid user is signed in" do
+ let(:user) { create(:user) }
+ before(:each) do
+ sign_in(user)
+ end
+
+ context "when no renewing registration exists" do
+ it "redirects to the invalid page" do
+ get new_renewal_received_pending_govpay_payment_form_path("wibblewobblejellyonaplate")
+
+ expect(response).to redirect_to(page_path("invalid"))
+ end
+ end
+
+ context "when a valid renewing registration exists" do
+ let(:transient_registration) do
+ create(
+ :renewing_registration,
+ :has_unpaid_balance,
+ workflow_state: "renewal_received_pending_govpay_payment_form",
+ account_email: user.email
+ )
+ end
+
+ context "when the workflow_state is correct" do
+ it "returns a 200 status and renders the :new template" do
+ get new_renewal_received_pending_govpay_payment_form_path(transient_registration.token)
+
+ expect(response).to have_http_status(200)
+ expect(response).to render_template(:new)
+ end
+ end
+
+ context "when the workflow_state is not correct" do
+ before do
+ transient_registration.update_attributes(workflow_state: "payment_summary_form")
+ end
+
+ it "redirects to the correct page" do
+ get new_renewal_received_pending_govpay_payment_form_path(transient_registration.token)
+ expect(response).to redirect_to(new_payment_summary_form_path(transient_registration.token))
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/waste_carriers_engine/worldpay_forms_spec.rb b/spec/requests/waste_carriers_engine/worldpay_forms_spec.rb
index 5080bf243..1ca3dbbcf 100644
--- a/spec/requests/waste_carriers_engine/worldpay_forms_spec.rb
+++ b/spec/requests/waste_carriers_engine/worldpay_forms_spec.rb
@@ -31,7 +31,7 @@ module WasteCarriersEngine
before do
stub_request(:any, /.*#{host}.*/).to_return(
status: 200,
- body: File.read("./spec/fixtures/worldpay_redirect.xml")
+ body: File.read("./spec/fixtures/files/worldpay/worldpay_redirect.xml")
)
end
@@ -213,7 +213,7 @@ module WasteCarriersEngine
context "when the params are valid" do
before do
allow_any_instance_of(WorldpayService).to receive(:valid_pending?).and_return(true)
- allow_any_instance_of(RenewingRegistration).to receive(:pending_worldpay_payment?).and_return(true)
+ allow_any_instance_of(RenewingRegistration).to receive(:pending_online_payment?).and_return(true)
end
it "redirects to renewal_received_pending_payment_form" do
diff --git a/spec/services/waste_carriers_engine/govpay_callback_service_spec.rb b/spec/services/waste_carriers_engine/govpay_callback_service_spec.rb
new file mode 100644
index 000000000..6fb131c56
--- /dev/null
+++ b/spec/services/waste_carriers_engine/govpay_callback_service_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+require "webmock/rspec"
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe GovpayCallbackService do
+ let(:govpay_host) { "https://publicapi.payments.service.gov.uk" }
+ let(:transient_registration) do
+ create(:renewing_registration,
+ :has_required_data,
+ :has_overseas_addresses,
+ :has_finance_details,
+ temp_cards: 0)
+ end
+ let(:current_user) { build(:user) }
+ let(:order) { transient_registration.finance_details.orders.first }
+
+ before do
+ allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true)
+ allow(Rails.configuration).to receive(:govpay_url).and_return(govpay_host)
+ allow(Rails.configuration).to receive(:renewal_charge).and_return(10_500)
+ transient_registration.prepare_for_payment(:govpay, current_user)
+ order.govpay_id = "a_govpay_id"
+ order.save!
+ end
+
+ let(:govpay_callback_service) { GovpayCallbackService.new(order.payment_uuid) }
+
+ describe "#run" do
+
+ RSpec.shared_examples "acceptable payment" do |response_type|
+ context "when the status is valid" do
+ before { allow_any_instance_of(GovpayValidatorService).to receive("valid_#{response_type}?".to_sym).and_return(true) }
+
+ it "returns #{response_type}" do
+ expect(govpay_callback_service.run).to eq(response_type)
+ end
+
+ it "updates the payment status" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.payments.first.govpay_payment_status).to eq(response_type.to_s)
+ end
+
+ it "updates the payment govpay_id" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.payments.first.govpay_id).not_to be_nil
+ end
+
+ it "updates the order govpay_status" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.orders.first.govpay_status).to eq(response_type.to_s)
+ end
+
+ it "updates the order govpay_id" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.orders.first.govpay_id).not_to be_nil
+ end
+ end
+
+ context "when the status is invalid" do
+ before { allow_any_instance_of(GovpayValidatorService).to receive("valid_#{response_type}?".to_sym).and_return(false) }
+
+ it "returns an error" do
+ expect(govpay_callback_service.run).to eq(:error)
+ end
+
+ it "does not update the order" do
+ unmodified_order = transient_registration.finance_details.orders.first
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.orders.first).to eq(unmodified_order)
+ end
+
+ it "does not create a payment" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.payments.count).to eq(0)
+ end
+ end
+ end
+
+ context "success" do
+ before { allow_any_instance_of(GovpayPaymentDetailsService).to receive(:govpay_payment_status).and_return("success") }
+
+ it_behaves_like "acceptable payment", :success
+
+ it "updates the balance" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.balance).to eq(0)
+ end
+ end
+
+ context "created" do
+ before { allow_any_instance_of(GovpayPaymentDetailsService).to receive(:govpay_payment_status).and_return("created") }
+ it_behaves_like "acceptable payment", :pending
+ end
+
+ context "submitted" do
+ before { allow_any_instance_of(GovpayPaymentDetailsService).to receive(:govpay_payment_status).and_return("submitted") }
+ it_behaves_like "acceptable payment", :pending
+ end
+
+ RSpec.shared_examples "unsuccessful payment" do |response_type|
+
+ context "when the status is valid" do
+ before do
+ allow_any_instance_of(GovpayValidatorService).to receive("valid_#{response_type}?".to_sym).and_return(true)
+ end
+
+ it "returns #{response_type}" do
+ expect(govpay_callback_service.run).to eq(response_type)
+ end
+
+ it "updates the order status" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.orders.first.govpay_status).to eq(response_type.to_s)
+ end
+ end
+
+ context "when the status is invalid" do
+ before do
+ allow_any_instance_of(GovpayValidatorService).to receive("valid_#{response_type}?".to_sym).and_return(false)
+ end
+
+ it "returns an error" do
+ expect(govpay_callback_service.run).to eq(:error)
+ end
+
+ it "does not update the order" do
+ unmodified_order = transient_registration.finance_details.orders.first
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.orders.first).to eq(unmodified_order)
+ end
+
+ it "does not create a payment" do
+ govpay_callback_service.run
+ expect(transient_registration.reload.finance_details.payments.count).to eq(0)
+ end
+ end
+ end
+
+ context "failed" do
+ before { allow_any_instance_of(GovpayPaymentDetailsService).to receive(:govpay_payment_status).and_return("failed") }
+ it_behaves_like "unsuccessful payment", :failure
+ end
+
+ context "cancelled" do
+ before { allow_any_instance_of(GovpayPaymentDetailsService).to receive(:govpay_payment_status).and_return("cancelled") }
+ it_behaves_like "unsuccessful payment", :cancel
+ end
+
+ context "error" do
+ before { allow_any_instance_of(GovpayPaymentDetailsService).to receive(:govpay_payment_status).and_return("error") }
+ it_behaves_like "unsuccessful payment", :error
+ end
+ end
+ end
+end
diff --git a/spec/services/waste_carriers_engine/govpay_payment_details_service_spec.rb b/spec/services/waste_carriers_engine/govpay_payment_details_service_spec.rb
new file mode 100644
index 000000000..dd5a328d9
--- /dev/null
+++ b/spec/services/waste_carriers_engine/govpay_payment_details_service_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require "webmock/rspec"
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe GovpayPaymentDetailsService do
+ let(:govpay_host) { "https://publicapi.payments.service.gov.uk" }
+ before do
+ allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true)
+ allow(Rails.configuration).to receive(:govpay_url).and_return(govpay_host)
+ end
+
+ let(:transient_registration) do
+ create(:renewing_registration,
+ :has_required_data,
+ :has_overseas_addresses,
+ :has_finance_details,
+ temp_cards: 0)
+ end
+ let(:current_user) { build(:user) }
+
+ before do
+ allow(Rails.configuration).to receive(:renewal_charge).and_return(10_500)
+
+ transient_registration.prepare_for_payment(:govpay, current_user)
+ end
+
+ subject { GovpayPaymentDetailsService.new(transient_registration.finance_details.orders.first.payment_uuid) }
+
+ describe "govpay_payment_status" do
+
+ context "with an invalid payment uuid" do
+ it "raises an exception" do
+ expect { GovpayPaymentDetailsService.new("bad_uuid") }.to raise_exception(ArgumentError)
+ end
+ end
+
+ context "with a valid payment uuid" do
+ shared_examples "expected status is returned" do |govpay_status, expected_status|
+ let(:response_fixture) { "get_payment_response_#{govpay_status}.json" }
+
+ it "returns #{expected_status}" do
+ expect(subject.govpay_payment_status).to eq expected_status
+ end
+ end
+
+ before do
+ stub_request(:get, /.*#{govpay_host}.*/).to_return(
+ status: 200,
+ body: File.read("./spec/fixtures/files/govpay/#{response_fixture}")
+ )
+ end
+
+ it_behaves_like "expected status is returned", "created", "created"
+
+ it_behaves_like "expected status is returned", "submitted", "submitted"
+
+ it_behaves_like "expected status is returned", "success", "success"
+
+ it_behaves_like "expected status is returned", "cancelled", "cancelled"
+
+ it_behaves_like "expected status is returned", "not_found", "error"
+ end
+ end
+
+ describe "payment_status" do
+
+ shared_examples "maps to the expected status" do |govpay_status, response_type|
+ it "returns the correct status" do
+ expect(GovpayPaymentDetailsService.response_type(govpay_status)).to eq response_type
+ end
+ end
+
+ it_behaves_like "maps to the expected status", "created", :pending
+
+ it_behaves_like "maps to the expected status", "submitted", :pending
+
+ it_behaves_like "maps to the expected status", "success", :success
+
+ it_behaves_like "maps to the expected status", "failed", :failure
+
+ it_behaves_like "maps to the expected status", "cancelled", :cancel
+
+ it_behaves_like "maps to the expected status", nil, :error
+ end
+ end
+end
diff --git a/spec/services/waste_carriers_engine/govpay_payment_service_spec.rb b/spec/services/waste_carriers_engine/govpay_payment_service_spec.rb
new file mode 100644
index 000000000..2130bfd52
--- /dev/null
+++ b/spec/services/waste_carriers_engine/govpay_payment_service_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require "webmock/rspec"
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe GovpayPaymentService do
+ let(:govpay_host) { "https://publicapi.payments.service.gov.uk" }
+ before do
+ allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true)
+ allow(Rails.configuration).to receive(:govpay_url).and_return(govpay_host)
+ end
+
+ let(:transient_registration) do
+ create(:renewing_registration,
+ :has_required_data,
+ :has_overseas_addresses,
+ :has_finance_details,
+ temp_cards: 0)
+ end
+ let(:current_user) { build(:user) }
+
+ before do
+ allow(Rails.configuration).to receive(:renewal_charge).and_return(10_500)
+
+ transient_registration.prepare_for_payment(:govpay, current_user)
+ end
+
+ let(:order) { transient_registration.finance_details.orders.first }
+
+ let(:govpay_service) { GovpayPaymentService.new(transient_registration, order, current_user) }
+
+ before do
+ stub_request(:any, /.*#{govpay_host}.*/).to_return(
+ status: 200,
+ body: File.read("./spec/fixtures/files/govpay/create_payment_created_response.json")
+ )
+ end
+
+ describe "prepare_for_payment" do
+ context "when the request is valid" do
+ let(:root) { Rails.configuration.wcrs_renewals_url }
+ let(:reg_id) { transient_registration.reg_identifier }
+
+ it "returns a link" do
+ url = govpay_service.prepare_for_payment[:url]
+ # expect the value from the payment response file fixture
+ expect(url).to eq("https://www.payments.service.gov.uk/secure/bb0a272c-8eaf-468d-b3xf-ae5e000d2231")
+ end
+
+ # Including this test because the Worldpay equivalent does create a new payment
+ it "does not create a new payment" do
+ expect { govpay_service.prepare_for_payment }.not_to change { transient_registration.finance_details.payments.length }
+ end
+ end
+
+ context "when the request is invalid" do
+ before do
+ stub_request(:any, /.*#{govpay_host}.*/).to_return(
+ status: 200,
+ body: File.read("./spec/fixtures/files/govpay/create_payment_error_response.json")
+ )
+ end
+
+ it "returns :error" do
+ expect(govpay_service.prepare_for_payment).to eq(:error)
+ end
+ end
+ end
+
+ describe "#payment_callback_url" do
+ let(:callback_host) { Faker::Internet.url }
+
+ before do
+ allow(Rails.configuration).to receive(:host).and_return(callback_host)
+ end
+
+ subject { govpay_service.payment_callback_url }
+
+ context "when the order does not exist" do
+
+ before { transient_registration.finance_details.orders = [] }
+
+ it "raises an exception" do
+ expect { subject }.to raise_error(StandardError)
+ end
+ end
+
+ context "when the order exists" do
+
+ it "the callback url includes the base path" do
+ expect(subject).to start_with(callback_host)
+ end
+
+ it "the callback url includes the payment uuid" do
+ expect(subject).to include(TransientRegistration.first.finance_details.orders.first.payment_uuid)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/waste_carriers_engine/govpay_validator_service_spec.rb b/spec/services/waste_carriers_engine/govpay_validator_service_spec.rb
new file mode 100644
index 000000000..be479c828
--- /dev/null
+++ b/spec/services/waste_carriers_engine/govpay_validator_service_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+module WasteCarriersEngine
+ RSpec.describe GovpayValidatorService do
+ let(:transient_registration) do
+ create(:renewing_registration,
+ :has_required_data,
+ :has_overseas_addresses,
+ :has_finance_details,
+ temp_cards: 0)
+ end
+ let(:payment) { Payment.new_from_online_payment(transient_registration.finance_details.orders.first, nil) }
+ let(:order) { transient_registration.finance_details.orders.first }
+ let(:govpay_validator_service) { GovpayValidatorService.new(order, payment.uuid, govpay_status) }
+ let(:govpay_host) { "https://publicapi.payments.service.gov.uk" }
+
+ before do
+ allow(WasteCarriersEngine::FeatureToggle).to receive(:active?).with(:govpay_payments).and_return(true)
+ allow(Rails.configuration).to receive(:govpay_url).and_return(govpay_host)
+ allow(Rails.configuration).to receive(:renewal_charge).and_return(10_500)
+ end
+
+ shared_examples "valid and invalid Govpay status" do |method, valid_status, invalid_status|
+ context "when the payment status is valid" do
+ let(:govpay_status) { valid_status }
+
+ it "returns true" do
+ expect(govpay_validator_service.public_send(method)).to eq(true)
+ end
+ end
+
+ context "when the payment status is invalid" do
+ let(:govpay_status) { invalid_status }
+
+ it "returns false" do
+ expect(govpay_validator_service.public_send(method)).to eq(false)
+ end
+ end
+ end
+
+ describe "#valid_success?" do
+ let(:govpay_status) { "success" }
+ context "when the govpay status is valid" do
+
+ it "returns true" do
+ expect(govpay_validator_service.valid_success?).to eq(true)
+ end
+ end
+
+ context "when the govpay status is not valid" do
+
+ let(:govpay_status) { "failed" }
+ it "returns false" do
+ expect(govpay_validator_service.valid_success?).to eq(false)
+ end
+ end
+
+ context "when the order is not present" do
+ let(:order) { nil }
+
+ it "returns false" do
+ expect(govpay_validator_service.valid_success?).to eq(false)
+ end
+ end
+
+ context "when the payment_uuid is not present" do
+ let(:govpay_validator_service) { GovpayValidatorService.new(order, nil, govpay_status) }
+
+ it "returns false" do
+ expect(govpay_validator_service.valid_success?).to eq(false)
+ end
+ end
+
+ context "when the payment_uuid is invalid" do
+ let(:govpay_validator_service) { GovpayValidatorService.new(order, "bad_payment_uuid", govpay_status) }
+
+ it "returns false" do
+ expect(govpay_validator_service.valid_success?).to eq(false)
+ end
+ end
+ end
+
+ describe "#valid_failure?" do
+ it_behaves_like "valid and invalid Govpay status", "valid_failure?", "failed"
+ end
+
+ describe "#valid_pending?" do
+ it_behaves_like "valid and invalid Govpay status", "valid_pending?", "created"
+ end
+
+ describe "#valid_cancel?" do
+ it_behaves_like "valid and invalid Govpay status", "valid_cancel?", "cancelled"
+ end
+
+ describe "#valid_error?" do
+ it_behaves_like "valid and invalid Govpay status", "valid_error?", "error"
+ end
+
+ describe "#valid_govpay_status?" do
+ it "returns true when the status matches the values for the response type" do
+ expect(described_class.valid_govpay_status?(:success, "success")).to eq(true)
+ end
+
+ it "returns false when the status does not match the values for the response type" do
+ expect(described_class.valid_govpay_status?(:success, "FOO")).to eq(false)
+ end
+ end
+ end
+end
diff --git a/spec/services/waste_carriers_engine/notify/registration_pending_worldpay_payment_email_service_spec.rb b/spec/services/waste_carriers_engine/notify/registration_pending_online_payment_email_service_spec.rb
similarity index 95%
rename from spec/services/waste_carriers_engine/notify/registration_pending_worldpay_payment_email_service_spec.rb
rename to spec/services/waste_carriers_engine/notify/registration_pending_online_payment_email_service_spec.rb
index f6a078cca..7797a2f34 100644
--- a/spec/services/waste_carriers_engine/notify/registration_pending_worldpay_payment_email_service_spec.rb
+++ b/spec/services/waste_carriers_engine/notify/registration_pending_online_payment_email_service_spec.rb
@@ -4,7 +4,7 @@
module WasteCarriersEngine
module Notify
- RSpec.describe RegistrationPendingWorldpayPaymentEmailService do
+ RSpec.describe RegistrationPendingOnlinePaymentEmailService do
let(:template_id) { "c4296e7b-dac6-4b59-906e-2c509271626f" }
let(:registration) { create(:registration, :has_required_data) }
diff --git a/spec/services/waste_carriers_engine/notify/renewal_pending_worldpay_payment_email_service_spec.rb b/spec/services/waste_carriers_engine/notify/renewal_pending_online_payment_email_service_spec.rb
similarity index 90%
rename from spec/services/waste_carriers_engine/notify/renewal_pending_worldpay_payment_email_service_spec.rb
rename to spec/services/waste_carriers_engine/notify/renewal_pending_online_payment_email_service_spec.rb
index 6edbd252c..2b9cf5b24 100644
--- a/spec/services/waste_carriers_engine/notify/renewal_pending_worldpay_payment_email_service_spec.rb
+++ b/spec/services/waste_carriers_engine/notify/renewal_pending_online_payment_email_service_spec.rb
@@ -4,7 +4,7 @@
module WasteCarriersEngine
module Notify
- RSpec.describe RenewalPendingWorldpayPaymentEmailService do
+ RSpec.describe RenewalPendingOnlinePaymentEmailService do
let(:template_id) { "3da098e3-3db2-4c99-8e96-ed9d1a8ef227" }
let(:registration) { create(:registration, :has_required_data) }
@@ -35,7 +35,7 @@ module Notify
end
subject do
- VCR.use_cassette("notify_renewal_pending_worldpay_payment_sends_an_email") do
+ VCR.use_cassette("notify_renewal_pending_online_payment_sends_an_email") do
described_class.run(registration: registration)
end
end
diff --git a/spec/services/waste_carriers_engine/registration_completion_service_spec.rb b/spec/services/waste_carriers_engine/registration_completion_service_spec.rb
index f375c7094..763cc2ab9 100644
--- a/spec/services/waste_carriers_engine/registration_completion_service_spec.rb
+++ b/spec/services/waste_carriers_engine/registration_completion_service_spec.rb
@@ -129,14 +129,14 @@ module WasteCarriersEngine
transient_registration.save
end
- it "sends a pending worldpay confirmation email with notify" do
- allow(Notify::RegistrationPendingWorldpayPaymentEmailService)
+ it "sends a pending online payment confirmation email with notify" do
+ allow(Notify::RegistrationPendingOnlinePaymentEmailService)
.to receive(:run)
.and_call_original
registration = described_class.run(transient_registration)
- expect(Notify::RegistrationPendingWorldpayPaymentEmailService)
+ expect(Notify::RegistrationPendingOnlinePaymentEmailService)
.to have_received(:run)
.with(registration: registration)
.once
@@ -146,7 +146,7 @@ module WasteCarriersEngine
before do
the_error = StandardError.new("Oops!")
- allow(Notify::RegistrationPendingWorldpayPaymentEmailService)
+ allow(Notify::RegistrationPendingOnlinePaymentEmailService)
.to receive(:run)
.and_raise(the_error)
diff --git a/spec/services/waste_carriers_engine/worldpay_service_spec.rb b/spec/services/waste_carriers_engine/worldpay_service_spec.rb
index db1352e36..604594bda 100644
--- a/spec/services/waste_carriers_engine/worldpay_service_spec.rb
+++ b/spec/services/waste_carriers_engine/worldpay_service_spec.rb
@@ -103,7 +103,7 @@ module WasteCarriersEngine
stub_request(:any, /.*#{host}.*/).to_return(
status: 200,
- body: File.read("./spec/fixtures/worldpay_initial_request.xml")
+ body: File.read("./spec/fixtures/files/worldpay/worldpay_initial_request.xml")
)
end
@@ -125,7 +125,7 @@ module WasteCarriersEngine
stub_request(:any, /.*#{host}.*/).to_return(
status: 200,
- body: File.read("./spec/fixtures/worldpay_initial_request_invalid.xml")
+ body: File.read("./spec/fixtures/files/worldpay/worldpay_initial_request_invalid.xml")
)
end
diff --git a/spec/services/waste_carriers_engine/worldpay_xml_service_spec.rb b/spec/services/waste_carriers_engine/worldpay_xml_service_spec.rb
index 7ec7c6df5..aefbfd9ce 100644
--- a/spec/services/waste_carriers_engine/worldpay_xml_service_spec.rb
+++ b/spec/services/waste_carriers_engine/worldpay_xml_service_spec.rb
@@ -30,7 +30,7 @@ module WasteCarriersEngine
describe "#build_xml" do
it "returns correctly-formatted XML" do
- xml = File.read("./spec/fixtures/files/request_to_worldpay.xml")
+ xml = File.read("./spec/fixtures/files/worldpay/request_to_worldpay.xml")
expect(worldpay_xml_service.build_xml).to eq(xml)
end