I have a checkout process that 1) creates a user account in Stripe and my database, 2) creates a paymentMethod in Stripe and logs the last4 in the database, 3) creates the subscription in Stripe and logs the subscription to database, 4) sends welcome email and creates a job record in the database to build the service for the subscription.

However, during this process, a lot can go wrong. Some of the things I can think of are: 1) credit card number is invalid, card is declined, API rate limiting, database server goes down/overloaded, etc...

I'm using a try/catch in PHP. However, how can I avoid re-running parts of the try if the user simply needs to fix some input like using a different card number because the other one declined?

At this point the customer record is already created, so I don't need to re-run those parts.

try {
- 1
- Create Stripe Payment Method
// this will throw any credit card validation errors such as invalid dates, csc, etc...

- 2
- Create Stripe Customer
- Attach payment method to Customer
- Create record in database for customers table

- 3
- Create Stripe subscription
- Create record in database for subscriptions table

- 4
- Create record in job table to send out welcome email and build service for customer 

- display thank you page
} catch 
- Stripe CreditCard validation
- Stripe API Rate Limit
- Stripe API network error
- other Stripe error handling
- PDO Exceptions

Should I create a session variable for each section and use an if block of code to prevent re-running it?

For example if section 2 completes, do something like $_SESSION['customer'] = 'done'; and then do:

 if ($_SESSION['customer'] == 'done') { 
   // don't run
 } else {
   // run code

    $_SESSION['customer'] = 'done';
  • 1
    You could validate all user input before starting the actual processes. And when the credit card api rate limit is reached, consider handling edge cases like that manually; just sign up the customer and fix the errors later. The alternative is you might lose a customer. Talk with the business people about how to handle these kinds of issues.
    – Rik D
    Sep 17, 2021 at 16:36
  • @RikD I'm doing some validation ahead of time, however, there are things like card declined that I cannot know by doing validation prior to step 3 when the card is ran. Sep 17, 2021 at 16:46
  • Ok, you don't want to run the credit card again if one had been previously accepted and the user is retrying for some other reason. But did you have the user enter a credit card as part of their retry? This is not just about backend not repeating work, but goes to the user experience as well. Maybe speak to the business people to see how they want that handled. Depending on whether you want the user to be able to come back later, session state may not be enough to remember what's what.
    – Erik Eidt
    Sep 17, 2021 at 18:55
  • I'm not sure this question is a good for SoftwareEngineering.SE.
    – moonman239
    Sep 17, 2021 at 19:09
  • Consider to introduce different states of the process and persist the current state, so when prices is run again you can continue from the current one. State machine
    – Fabio
    Sep 17, 2021 at 23:57

2 Answers 2


Work out what different states the system can end up in, e.g. "Not started yet" or "Stripe customer created but no subscription".

Store the state for each user in your database.

Break down your logic into chunks so that, no matter what state you end up in, you never need to run only part of a chunk.

When you process a request, first check the current state. That will tell you what chunks of logic you need to run, and which you can skip. After running a chunk of logic, update the current state - which will be different depending on success or failure.

  • This is a good idea. I'm just trying to think how I can apply this in a checkout session for a subscription model. It may be worth simply having the user just create an 'account' first and once logged in, add payment and subscribe. Although the UX might not be as smooth. Sep 17, 2021 at 20:59

Payment processing is a touchy subject. It is very important to have a solution that prevents multiple submits, is resilient to failure, and provides auditability in case of dispute. This is not just for the benefit of your business, but also for compliance purposes, if you take CC payments.

To achieve this, your business model should include an entity which could be called "Sales Order." This entity would contain, among other things,

  1. A unique order ID
  2. Customer ID
  3. Order create date
  4. Payment date
  5. Fulfillment date
  6. Payment method ID
  7. Payment identifier, such as a confirmation code
  8. Fulfillment identifier, such as a Shipping Order ID or a delivery tracking code

When the customer places the order, create the record.

When you collect payment, update the payment date and the confirmation code.

When the item ships, update the shipping ID or tracking code.


Basically you have to update the record each and every time one of the steps in your workflow is completed.

By looking at all these fields, your system can figure out which steps have been completed and which ones have not. For example, if the payment date is set, it knows that payment has already been collected; if it is null, payment still needs to be collected.

This will allow you system to pick up where it left off when the order is resubmitted, and provides all the information your customer support department will need in case there is a dispute.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.