Concurrent executions with identical values for (IdUser,IdCoupon)
seem like the only plausible explanation.
The question says it's almost impossible but multiple executions can't always be foresighted. For example, in the context of a web application, a double-post of a form with the exact same data may occur.
To avoid the error at the plpgsql level, you can use an exception block:
BEGIN
IF (EXISTS...) THEN
UPDATE the unique corresponding row
ELSE
INSERT new row
END IF;
EXCEPTION WHEN unique_violation THEN
UPDATE the unique row
END;
Also you may want to add some log in the exception block about the context and the time of the event in the hope of understanding why the concurrent executions happen.