We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Error Handling with Exceptions, Retries and Warnings step 3 of 20
Catch Errors by Category
A customer clicks Pay, and your code asks the payment gateway to charge their card. The charge can fail in several ways. A declined card needs a specific message, a busy gateway means "try again soon", and a bug in your own code should not look like a payment problem.
The gateway's errors are a family of classes, so you can catch them by category:
PaymentError
├── TransientPaymentError has .retry_after, in seconds
└── PermanentPaymentError
└── CardDeclinedError has .decline_code
An except clause catches its own class and every subclass of it. So
except PermanentPaymentError also catches a CardDeclinedError, and
except PaymentError catches all four.
checkout("4242") # "paid: r-4242"
checkout("expired") # "declined: expired_card"
What to write
Fill in checkout(card). Call charge(card) once, and return the message
to show the customer:
- If
chargereturns a receipt id, return"paid: <receipt>". - For a
CardDeclinedError, return"declined: <decline_code>". Read the code from the error'sdecline_codeattribute. - For a
TransientPaymentError, return"busy: try again in <retry_after>s", for example"busy: try again in 5s". - For any other
PaymentError, return"failed: <message>", where the message isstr(error). - Let every other exception reach the caller. A
ValueErrorfromchargemeans your code passed it something that is not a card number. That is a bug, so it should crash, not turn into a payment message.
Python checks except clauses from top to bottom and runs the first one that
matches. Order your clauses so that each error gets the message listed above.
The fake gateway
charge(card) decides what happens from the card string:
| Card | What charge does |
|---|---|
"expired" |
raises CardDeclinedError with code "expired_card" |
"no-funds" |
raises CardDeclinedError with code "insufficient_funds" |
"busy" |
raises TransientPaymentError with retry_after=5 |
"stolen" |
raises PermanentPaymentError("card reported stolen") |
"closed" |
raises PaymentError("merchant account closed") |
only digits, such as "4242" |
returns the receipt "r-4242" |
| anything else | raises ValueError |
What the driver reports
Keep the error classes, charge and solve as they are. solve(cards=...)
calls checkout once per card and returns results, one string per card.
If checkout lets an exception escape, the string is "crashed: " followed
by the class name, for example "crashed: ValueError".
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.