

Design for Failure: Processing Payments with a Background Worker - zrail
https://www.petekeen.net/design-for-failure-processing-payments-with-a-background-worker

======
sehrope
> Your page will poll /transactions/<id> until the transaction ends in either
> success or failure.

If you do something like this you need to make sure to verify that the user
actually created the transaction. Blindly loading it by id and returning it
back would let any user check on any transaction. Plus it's particularly easy
if the ids are sequence numbers.

------
dools
I recently implemented something similar to this and it works fantastically
well.

In my case I use pcntl_fork() in PHP to split off a job to communicate with
the payment gateway.

The parent process returns a hash to the client.

The client then polls with this hash. The child process communicating with the
bank will put the bank's response onto a beanstalkd tube named using this
hash.

------
optimiz3
Isn't this code just adding a single retry? What if it fails after that?

EDIT: Also, is it really a good idea to poll every 500ms? Why not use long
polling?

~~~
micaeked
It does not do a retry. It runs in the background to present the user with
immediate feedback. Seeing a "processing" page with a "spinner" animation for
30 seconds is much better than the browser waiting on the server for 30
seconds.

RE EDIT: From the article: "Typically, this completes almost immediately.". A
500ms poll isn't really a problem when for most people it will return success
on the first poll.

~~~
optimiz3
So ruby isn't a primary language form me, but doesn't the tx.save in the
rescue block cause a retry?

Also the 500ms poll looks like it is coming from the client - that definitely
seems like a no-no. You'd hit the server 60 times over a 30 second back-end
timeout.

The worst time to ratchet up load on your server is when your back-end is
having issues.

~~~
micaeked
Not quite. `txn.save!` just writes the data to the database. The call to do
the actual processing happens earlier, on the line beginning with `charge =
Stripe::Charge.create(`.

RE the poll: I tend to do doubling polls for stuff like this (500ms, 1s, 2s,
4s, 8s, etc), but in this case I don't think it matters, since all the server
does is one database lookup and render as json.

Side note to OP: It seems like a bad idea to use the auto-incremented id as a
key for polling, since it would allow an attacker to get full details of all
transactions.

~~~
zrail
Yep, you're right. The book[1] has a much more fleshed out version of this
example that uses a random guid as the key, plus a whole lot more.

[1]: [https://www.petekeen.net/mastering-modern-
payments](https://www.petekeen.net/mastering-modern-payments)

