How can I replace the snowball particles with a creeper that has a fuse of 0?

This is not going to be that easy of a thing to implement.

Summoning mobs relative to anything other than the coordinates that the command block is sitting relies on the /execute command, which executes a command relative to another entity. Particles aren't entities, so we cannot execute any command whatsoever relative to where the particles occur. It's not that it's difficult to do, it's simply not possible.

I'd suggest working relative to the snowball itself, except there's no way to tell when they hit something. They're destroyed once they hit something. However... we can do some magic with armor stands.

We're going to need a scoreboard too. So go ahead and set up a scoreboard called "AGE" (or call it whatever you want, but remember to replace it where relevant).

/scoreboard objectives add AGE dummy

This will keep track of how old our entities are. Kind of. You'll see in a minute.

Place 7 command blocks in a line. These should be set up using a repeat command block at the beginning, and chain command blocks through the rest of the line. Make sure all of them are set to unconditional/always on. This will continuously pulse them every game tick with minimal lag. We need 2 of these guys to increment the AGE score for armor stands and snowballs:

/scoreboard players add @e[type=Snowball] AGE 1
/scoreboard players add @e[type=ArmorStand] AGE 1

For brand new snowballs (snowballs with an AGE score exactly 1), we need to summon an invisible armor stand at the position of that snowball:

/execute @e[type=Snowball,score_AGE=1,score_AGE_min=1] ~ ~ ~ summon ArmorStand ~ ~ ~ {NoGravity:1,Invisible:1}

These armor stands will have their age scores incremented by the previous command we set up. From here, we need to move that armor stand to the snowball that summoned it, which in for our purposes is the snowball nearest to it. The c=C selector argument is useful here. It selects the nearest C entities that match the rest of the selector (selecting the oldest if there is a tie). So the command we'll want is as follows:

/execute @e[type=Snowball,score_AGE_min=2] ~ ~ ~ tp @e[type=ArmorStand,c=1] @e[type=Snowball,c=1]

What this command does is executes the /tp command for all snowballs that are old enough to also have a matching armor stand, which moves the armor stand along the arc of the snowball as long as it exists.

We'll be using the AGE score from the armor stand to determine where creepers need to be sent, however, we don't want to do that as long as the snowball is still in the air. I handled this by setting the AGE score of all armor stands with a corresponding snowball to 0:

/execute @e[type=Snowball,score_AGE_min=2] ~ ~ ~ scoreboard players set @e[type=ArmorStand,c=1] AGE 0

This causes each snowball to set the AGE score of their corresponding armor stands to 0. Effectively, what this means is as long as the snowball is in flight, the corresponding armor stand will not age.

Finally, the creeper. For all armor stands that have aged, we want to summon a creeper and then kill that armor stand:

/execute @e[type=ArmorStand,score_AGE=2,score_AGE_min=2] ~ ~ ~ summon Creeper ~ ~ ~ {Fuse:0,NoAI:1}
/kill @e[type=ArmorStand,score_AGE_min=3]

This is quite a bit more complicated than you probably wanted, but it works well. The only real issue with it comes if you throw multiple snowballs in rapid (read: immediate) succession. In that case, you will end up with a creeper in your face (which is less than ideal). Think of it like your explosive snowball hit the other explosive snowball (which is essentially what is happening). Throwing them with reasonable timing, or in different directions produces good results.