The error messages are returned to the template as flash messages.
Handling those is documented here:
https://craftcms.com/docs/templating/craft.session
One thing to watch out for is that flash messages are only available once, and are by default 'consumed' when you retrieve them:
{{ craft.session.getFlashes() }}
...some other template code here....
{{ craft.session.getFlashes() }}
...if there is a message, the first tag will output it, but the second one won't as it has been consumed. So best to set the message to a variable if you want to save it, or you can call craft.session.getFlashes(false)
to stop the message from being consumed if you'll want to use it later in your template.
During development it is worth having some code to always display the flash messages at the top or bottom of your main layout template, so you can see them as they are occurring, and thus anticipate what you might need to handle. I use something like this:
{% if craft.config.devMode and (craft.session.hasFlash('notice') or craft.session.hasFlash('error')) %}
{% set noticeFlash = craft.session.getFlash('notice')|join('|') %}
{% set errorFlash = craft.session.getFlash('error')|join('|') %}
Notice: [{{ noticeFlash }}]
Error: [{{ errorFlash }}]
{% endif %}
Now, if you're doing your payment operations via ajax, things are a little different...that case you'll get back results in json. If there is an error, it will look something like:
{"error":["Credit Card Invalid"]}
So in your js you would check for the lack of response.success
and then look for this error message.