4 ms·
I want a message queue to hold credit card authorization requests. I want to ensure that the authorization requests occur at most once, since having a customer
by stevewepay 12y ago
I want a message queue to hold credit card authorization requests. I want to ensure that the authorization requests occur at most once, since having a customer's credit card charged twice is infinitely worse than not charging the card.
If the request never goes through, then I can always get the customer to try again, but if it gets charged more than once, then it's a huge headache, and our customers get extremely angry and lose confidence that we know what we're doing.
- IgorPartola 12y agoDon't you store the result of the authorization in your persistent storage? If you do, you can check whether you need to do the authorization again.
- jaimeyap 12y agoCredit card authorization might be better suited to a synchronous request, versus going through a message queue.
- Rapzid 12y agoIt doesn't really matter either way. Unless you are using distributed transaction protocols, you pretty much MUST store-and-forward and have idempotent processes in the consumer/receiver to guarantee something happens. Usually the easiest way is to generate a transaction ID, such as a UUID, at the very start and use it end-to-end as indicated by the earlier nonce comment. Then each step along the way can either locally dedupe, or refer to a shared database/API with it.
- jaimeyap 12y agoWell one difference is being able to easily propagate failures end to end. Agreed about idempotent processes in the "consumer" processes. But I generally prefer marking an end user session in a persistent store before propagating the request for payment (effectively acting as a sticky current transaction ID), and making synchronous requests down the chain versus generating a transaction ID and doing a bunch of async message queuing. It's just easier to know when something didn't work and display the appropriate user feedback. But your point is valid.
- politician 12y agoIf you've got a worker node that proxies those requests to your payment processor and it dies, then you can be in an unrecoverable state where you won't know whether the authorization request has succeeded or whether the message was lost. An at-most-once policy doesn't save you from that, but an at-least-once policy would force you to write an idempotent handler that may be able to recover from failure (via the PP API) or safely make progress (with a bit of transactional state).
- Xixi 12y agoFor this kind of issue I stick to at-least-once, and then use a lot of band-aid: - Each worker has to hook to a database where a record keep the state of the transaction: ready, processing, success or failure. With a good old SELECT FOR UPDATE a transaction will never be done more than once. - Timestamps and alerts ensure that if the worker fails dramatically (leaving the task as "processing"), a human will check and update the task accordingly. It has proven pretty resilient so far... but at very small scales.
- ChuckMcM 12y agoThat is certainly doable but you need to separate the message semantics from the credit card semantics. So lets say your authorization includes a nonce which is a blend of the transaction id data such that it is unique to the transaction. Now you send that through the message system which delivers it one or more times, but the nonce identifies all of them as the same transaction so the credit card processor tosses the 'extra' ones away, it has already done the work. In such a system the message passing infrastructure needs to be at-least-once and the protocol involved insures that nothing bad happens if you deliver messages twice. Then you can route around damage or non-functioning nodes. The reasoning at the protocol level then goes, "If I have never seen this nonce, then the transaction has not been authorized" (very durable, easy to implement), and "A request seen multiple times will only be recorded once" (also very durable and easy to implement). But if the message infrastructure tries to guarantee at-most-once, and the user writes their protocol, "if I see this authorization I should do it, because the protocol will only send it to me one time." Then there will come a time in that protocols future where something blew up the invariants in the message infrastructure and the message arrives twice, and the wrong thing happens on the credit card because the protocol trusts it to do the right thing. Back in the RPC wars people were arguing that it was so much "simpler" and more "transparent" if a remote procedure call had the exact same semantics as a local procedure call. And that is true it would be, if you could manage that similarity, except it has been proven over and over again that network messaging systems can't completely get it correct.
- stevewepay 12y agoInteresting, thanks for the insight. We are doing something similar to the nonce that you mention, but your argument that at-least-once semantics is more appropriate is very convincing to me, as is your RPC argument. I'm old enough to have had to endure both ONC and DCE rpcs :)
- joevandyk 12y agoWhat happens with the authorization to the credit card processor times out? You don't know if it succeeded or not. You have to have a way to ask the credit card processor for "did this charge succeed?" and then process the payment if they respond with "no".