In your solution I think that the hook will be called every time you update the oder. So if you change something after the order is completed that method will be called again. If it's like this by design that could be the right solution anyway Spree suggests to directly use state machine callback to do stuff like this. For example:
Spree::Order.class_eval do
state_machine do
after_transition :to => :complete, :do => :add_user_credits
end
def add_user_credits
# do some stuff
end
end
This way the code will be executed immediately after the order goes into the complete state.