Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does retry logic work? #63

Open
matin opened this issue Oct 12, 2013 · 8 comments
Open

How does retry logic work? #63

matin opened this issue Oct 12, 2013 · 8 comments
Labels

Comments

@matin
Copy link
Member

matin commented Oct 12, 2013

One of the things that we've seen with companies that have recurring billing or delayed invoicing is a higher than normal rate of Soft Failures when charging a card. A Soft Failure is a card declination where you can retry later and possibly have the card approved.

Here are a few examples:

  1. Insufficient funds. The card holder may pay off their balance the next day, which means you can now charge the card.
  2. Blocked card. I had my card blocked as a result of making a purchase in Spain. I received an email from GitHub that my recurring payment failed. I called my bank and had the block removed explaining I was in fact in Spain. GitHub automatically retried, and the charge went through
  3. The recurring charge itself is flagged. The card holder will receive notification from their bank verifying the charge. Attempting the charge again will cause a success

How will this be handled by billy?

@matin
Copy link
Member Author

matin commented Oct 12, 2013

There's actually another example where the issuing bank's processing systems are down. It happens more than expected.

I can document some of the declination codes here.

@fangpenlin
Copy link
Contributor

For recurring billing, current transaction model works like this

Transaction state diagram

Transactions are yielded from subscription periodically, they will be processed at scheduled time. Once failed, it will go into RETRYING status, when the error count exceeds the limit, then it will go into FAILED. When the subscription is canceled, it will go into CANCELED status. If it is done, then it will be in DONE status.

And for invoicing, I am thinking about the process. Here is the state transition diagram

billy_invoice_state_diagram

Current transaction model is pretty solid, so I think I can build the invoicing based on it. When an invoice is issued, it is in INIT status. Say, at the end of month, the cloud service provider issues an invoice to John. Upon receiving the invoice, John decides to settle it via credit card, so he updates the payment method (get a tokenized credit card number and assign it to the invoice). Then the invoice goes into PROCESSING status, a corresponding InvoiceTransaction will be generated. If the credit card has no enough fund for charging, and the InvoiceTransaction fails after some attempts, then the invoice goes into PROCESS_FAILED status. John was notified for this problem, he updates his payment method then. By doing that, invoice goes back into PROCESSING status. A while later, the charging is done, then it is SETTLED now.

For some reasons, the service provider want to refund the invoice, then the invoice goes into REFUNDING status. By the time, the upstream balanced API server is down, so its refunding transaction will retry. For a while later, it is done. Then it is REFUNDED eventually.

So, there will be invoice transactions look like this

  • type=charge, status=FAILED, payment_uri=/v1/credit_card1
  • type=charge, status=DONE, payment_uri=/v1/credit_card2
  • type=refund, status=DONE

This is pretty much the story in my mind so far. It basically tries a couple of times when a payment method is set, when it fails, goes into fail status and wait actions from users. The under-layer transaction model provides a retrying mechanism, so temporary issues (server downtime or anything like that) can be avoided. If there are some corner cases are missing, please let me know.

@matin
Copy link
Member Author

matin commented Oct 14, 2013

@victorlin do you consider what the failure reason is? You shouldn't retry if there is a Hard Failure such any of the following:

  • lost or stolen card
  • invalid card
  • canceled card

@fangpenlin
Copy link
Contributor

@matin make sense. will do.

@mahmoudimus @mjallday @msherry

What kind of hard failure errors (code or message) we are expecting from Balanced API? I try to read the source code, but cannot find something useful. I think they are from under-layer API.

@mjallday
Copy link
Contributor

@victorlin I think the Balanced API should return a flag indicating if it's a hard failure or not. the current error codes are vendor specific and it shouldn't be the job of services that consume the Balanced API to interpret 3rd party error messages.

There's an open issue to address this - balanced/balanced-api#412

@fangpenlin
Copy link
Contributor

@mjallday So, how can I tell is it a hard failure or not from the response? For example, I received a response like this

{
  "status": "Conflict",
  "category_code": "bank-account-already-associated",
  "description": "Bank account has already been associated with an account. Your request id is OHMbaa8b6620a2b11e3adde026ba7c1aba6.",
  "status_code": 409,
  "extras": {},
  "category_type": "logical",
  "_uris": {},
  "request_id": "OHMbaa8b6620a2b11e3adde026ba7c1aba6",
  "additional": null
}

@fangpenlin
Copy link
Contributor

New transaction state diagram, upon encountering hard failure, it will go into FAILED status immediately.

billy_transaction_state_diagram_rev2

@mahmoudimus
Copy link
Contributor

Why would the API expose a flag telling you to retry? Just retry over intervals. Google for "dunning recurring billing" and you'll see a wealth of resources.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants