Best Practices Using Sources

    Best practices to accept a variety of payment methods through a single integration.

    By taking into consideration the flexibility of the Sources API when designing your checkout flow, you can minimize any changes required to support additional payment methods as you add them.

    Typical flow for card payments

    In a typical checkout flow for card payments (excluding 3D Secure), your integration collects the card information and creates a source, then immediately uses it to make a charge request. As there is no additional action for the customer to take, and card payments provide synchronous confirmation, we can immediately confirm if the payment is successful and that the funds are guaranteed—the use of webhooks isn’t necessary.

    The required use of webhooks

    Other payment methods may require your customer to take additional action (e.g., a redirect) before a source becomes chargeable and is ready to be used to make a charge request (e.g., iDEAL). This transition generally happens asynchronously and may even occur after the customer has left your website. For these reasons it is essential that your integration rely on webhooks to determine when a source becomes chargeable in order to create a charge.

    The following webhook events are sent to notify you about changes to the source’s status:

    Event Description Suggested action
    source.chargeable A Source object becomes chargeable after a customer has authenticated and verified a payment. Create a Charge.
    source.failed A Source object failed to become chargeable as your customer declined to authenticate the payment. Cancel the order and optionally re-engage the customer in your payment flow.
    source.canceled A Source object expired and cannot be used to create a charge. Cancel the order and optionally re-engage the customer in your payment flow.

    Similarly, when creating a charge, certain asynchronous payment methods (e.g., SOFORT) may require days for the funds to be confirmed and the charge to succeed, requiring the use of webhooks to know when to confirm and eventually fulfill your orders.

    The following webhook events are sent to notify you about changes to a charge’s status:

    Event Description Suggested action
    charge.pending The Charge is pending (asynchronous payments only). Nothing to do.
    charge.succeeded The Charge succeeded and the payment is complete. Finalize the order and send a confirmation to the customer over email.
    charge.failed The Charge has failed and the payment could not be completed. Cancel the order and optionally re-engage the customer in your payment flow.

    Building a flexible integration

    To ensure that your checkout process is flexible and ready to support multiple payment methods, we recommend the following approach:

    Source creation

    When creating Sources, you should record the source ID on your internal order representation so that you can retrieve the order when you receive and process source.chargeable webhooks. You should make sure to index your order objects based on this source attribute for efficient lookup.

    Charge creation

    You should rely on the source.chargeable webhook delivery to charge the Source. When receiving the webhook you should retrieve your internal order representation by a look-up based on the received source ID and verify that the order is awaiting a payment.

    When making a charge request, we highly recommend that you use your internal order ID as an idempotency key to avoid any possible race condition. Additionally, if the source is reusable and you want to reuse it, make sure to attach it to a Customer before charging it. Refer to the Single-use or reusable and Sources & Customers guides to learn more about how to handle single-use and reusable Sources and how they interact with Customers.

    Similarly to source creation, you should record the charge ID on your internal order representation so that you can retrieve the order when you receive and process charge.succeeded webhooks.

    Confirmation page

    After your customer takes the required actions to authorize a payment (for example, they have followed a redirect) you should present a confirmation page that shows the state of the order. You can do this by polling the order internally.

    Because webhook delivery latency is not guaranteed, if you would like to further streamline your confirmation page, you can poll for the status of the associated Source in your client-side code. Once you detect that your Source has become chargeable, you can initiate a Charge creation using that Source without waiting for the source.chargeable webhook to arrive.

    Note that some types of Sources take minutes (or even days) to become chargeable. If you decide to poll the Source, we recommend that you time out at some point and tell the customer that their order is awaiting payment confirmation, then send them a payment confirmation email asynchronously. You can see our recommended customer-facing messaging for each Source status in the table below.

    It’s also critical to note that client-side polling will stop if the customer leaves your page. This means you must also integrate against the source.chargeable webhook to make sure you don’t lose track of your customer’s order.

    If you’re using Stripe.js, you can use stripe.retrieveSource() to implement your own polling:

    // In order-confirmation-page.js
    
    var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
    
    // After some amount of time, we should stop trying to resolve the order synchronously:
    var MAX_POLL_COUNT = 10;
    var pollCount = 0;
    
    function pollForSourceStatus() {
      stripe.retrieveSource({id: SOURCE_ID, client_secret: CLIENT_SECRET}).then(function(result) {
        var source = result.source;
        if (source.status === 'chargeable') {
          // Make a request to your server to charge the Source.
          // Depending on the Charge status, show your customer the relevant message.
        } else if (source.status === 'pending' && pollCount < MAX_POLL_COUNT) {
          // Try again in a second, if the Source is still `pending`:
          pollCount += 1;
          setTimeout(pollForSourceStatus, 1000);
        } else {
          // Depending on the Source status, show your customer the relevant message.
        }
      });
    }
    
    pollForSourceStatus();
    
    // In order-confirmation-page.js
    
    const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
    
    // After some amount of time, we should stop trying to resolve the order synchronously:
    const MAX_POLL_COUNT = 10;
    let pollCount = 0;
    
    const pollForSourceStatus = () => {
      stripe.retrieveSource({id: SOURCE_ID, client_secret: CLIENT_SECRET}).then(({source}) => {
        if (source.status === 'chargeable') {
          // Make a request to your server to charge the Source.
          // Depending on the Charge status, show your customer the relevant message.
        } else if (source.status === 'pending' && pollCount < MAX_POLL_COUNT) {
          // Try again in a second, if the Source is still `pending`:
          pollCount += 1;
          setTimeout(pollForSourceStatus, 1000);
        } else {
          // Depending on the Source status, show your customer the relevant message.
        }
      });
    };
    
    pollForSourceStatus();
    

    The table below contains recommendations for potential customer-facing messages you can show based on the Source’s status.

    Status Customer-facing messaging
    Source is chargeable Your order was received and is awaiting payment confirmation.
    Source is canceled Your payment failed and your order couldn’t be processed.
    Source is failed Your payment failed and your order couldn’t be processed.
    Source is still pending after polling for a while Your order was received and is awaiting payment confirmation.

    Once you create a Charge (and if the user is still on your confirmation page), you can show the following messages based on the status of the Charge:

    Status Customer-facing messaging
    Charge is pending Your order was received and is awaiting payment confirmation.
    Charge is failed Your payment failed and your order couldn’t be processed.
    Charge is succeeded Your payment is confirmed and your order is complete.

    Order confirmation

    Only confirm your order once you receive the charge.succeeded webhook (either instantly or after a certain amount of time). We highly recommend sending an email to the customer at this stage as the payment confirmation might have taken days for asynchronous payments.

    Cancellations and failures

    You should listen for the source.canceled and source.failed webhooks and make sure to cancel the order associated with the source concerned. If you follow the best practices above, you should never receive a source.canceled webhook for sources that were previously chargeable (as your source.chargeable handler should have created a charge immediately, preventing the source from getting canceled). You will still receive source.canceled webhooks for sources that were never chargeable and remained pending, generally an indication that your customer left your checkout flow early. It is also possible for you to receive a source.failed webhook whenever the Customer refused the payment or a technical failure happened at the payment scheme level.

    You should also listen for the charge.failed webhooks to make sure to cancel the order associated with the received charge.

    For each of these events, we recommend that you contact your customer to let them know that their order failed and maybe invite them to re-engage in your payment flow.

    Related resources

    Questions?

    We're always happy to help with code or other questions you might have! Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    Was this page helpful? Yes No

    Send

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.