How to Use Stripe.com API ‘Events’ to Email your Customers

By November 27, 2012Startup Technology
stripe

Introduction to Stripe.com and Recurring Billing

For my site zankme.com I needed to be able to collect recurring payments from users. I wanted a system that was easy to use and easy to understand. It turns out that Stripe.com is both of those, and specializes in subscription-based billing. After purchasing an SSL certificate and setting up the basic functionality through the Stripe API, there was a remaining step that I had trouble finding documentation on; making sure that my site could effectively communicate all the possible billing or account changes to its users. This post describes the use cases for my online business. I hope it helps you with yours. Not everyone will have the same needs, but this should be a good starting point.

A List of Conditions Requiring Alerts or Emails from Stripe.com

  1. First payment is processed
  2. Any subsequent payment is processed
  3. User changes subscription level
  4. User updates credit card information
  5. User or admin cancels account
  6. Application of a promotional voucher
  7. Credit card information is no longer valid

For each of these conditions, I would like to communicate with my users. Here’s what I did in Stripe (note: I am assuming you have webhooks working properly on your system.  Here are the relevant Stripe.com links (thanks to Michael from the Stripe.com team):

How to Respond to Appropriate Stripe.com ‘events’

1. First Payment

Listen for: customer.subscription.created
Email:

Subject Of Email: First Payment Received – Thank You
Body Of Email:

Thanks for joining [YOUR COMPANY NAME]

This is your receipt for your [PLAN LEVEL NAME] plan for [CLIENT NAME].

======================================
RECEIPT #: [RECEIPT ID]

Bill to:
[NAME ON CC]

[PLAN LEVEL NAME] – [START DATE] to [END DATE]

Total: $[SUBSCRIPTION AMOUNT]

Your next billing date will be [NEXT BILLING DATE].

If you have any questions, please reply to [CUSTOMER SERVICE EMAIL]

— YOUR COMPANY NAME


2. Any subsequent payment is processed

Listen for: charge.succeeded
Email:

Subject Of Email: Payment Received – Thank You (or ‘Online Recurring Payment’)
Body Of Email:

Thanks for using [YOUR COMPANY NAME]

This is your receipt for your [PLAN LEVEL NAME] plan for [CLIENT NAME].

======================================
RECEIPT #: [RECEIPT ID]

Bill to:
[NAME ON CC]

[PLAN LEVEL NAME] – [START DATE] to [END DATE]

Total: $[SUBSCRIPTION AMOUNT]

Your next billing date will be [NEXT BILLING DATE].

If you have any questions, please reply to [CUSTOMER SERVICE EMAIL]

— YOUR COMPANY NAME


3. User changes subscription level [UPDATED 01/MAY2013]

Listen for: customer.subscription.updated

[HERE’S THE UPDATE]

While it’s certainly true that Stripe sends a hook for customer.subscription.updated for any plan update, this hook, and here’s the catch, ALSO gets sent each month with monthly billing statements (see charge.succeeded, step 2, above).

My customers were getting the email below EVERY month. This is unacceptable at best, as it caused a lot of confusion. So,  here’s the solution that’s working for me now.

SOLUTION: Instead of using a Stripe hook to initiate the email, just code it into your platform. For my example, when a customer up/downgrades her plan, at the moment the up/downgrade is written to the database, I send the email below. The text hasn’t changed, just the antecedent, which, in this case, is the act of writing the plan up/downgrade to the database.

I no longer listen for customer.subscription.updated.

[END UPDATE]

Email:

Subject Of Email: Changes to Your Account
Body Of Email:

You recently made changes to your [YOUR BUSINESS NAME] account.

You are now using the [PLAN LEVEL NAME] plan.

======================================

You may view all your account details online at
[YOUR BUSINESS URL FOR VIEWING ACCOUNT INFO]

======================================

If you have any questions, please reply to [CUSTOMER SERVICE EMAIL]

— YOUR COMPANY NAME


4. User updates credit card information

Listen for: customer.updated
Email:

Subject Of Email: Updated Payment Information
Body Of Email:

Changes were made recently to your [YOUR BUSINESS NAME] billing information.

You can login to view this changes here [YOUR URL].

If you did not make these changes, please contact us immediately at [BILLING @ YOUR CO. EMAIL]

— YOUR COMPANY NAME


5. User or admin cancels account

Listen for: customer.subscription.deleted
Email:

Subject Of Email: Your [YOUR BUSINESS NAME] Account has Been Cancelled
Body Of Email:

Your [YOUR COMPANY NAME] account was recently cancelled.

Thanks for using [YOUR COMPANY NAME]. We’re sorry to see you go. If this was a mistake, or you just need to update your credit card info please login here and reactivate your account.

If you have any questions, please reply to [CUSTOMER SERVICE EMAIL]

— YOUR COMPANY NAME


6. Application of a promotional voucher

Listen for: customer.discount.created
Email:

Subject Of Email: Discount Activated for [YOUR COMPANY NAME] Body Of Email:

Just to let you know, we’ve applied a promotional voucher to your account. You will receive [% DISCOUNT] % off your current bill.

If you have any questions, please reply to [CUSTOMER SERVICE EMAIL]

— YOUR COMPANY NAME


7. Credit card information is no longer valid (two events here)

The first event deals with an authorization that doesn’t succeed. The second event deals with cancellation after the card has been retry multiple times.

To configure the way that Stripe.com handles unsuccessful authorizations go to this link: https://manage.stripe.com/#account/recurring and configure it any way you want. Mine looks like this:

Stripe.com Recurring Billing API

Notice, after the first failure, I wait 3 days and try again. Then 5 days, then if it’s still invalid, I cancel the account.
To send out the failure notice(s) Here’s the format I’m using:

Listen for: invoice.payment_failed
Email:

Subject Of Email: Payment to [YOUR BUSINESS NAME] Failed
Body Of Email:

Oops! Your latest payment failed to go through. Please log on to [YOUR BUSINESS NAME] and update your credit card info to keep your subscription active. The details are included below.

======================================
RECEIPT #: [RECEIPT ID]

Bill to:
[NAME ON CC]

[PLAN LEVEL NAME] – [START DATE] to [END DATE]

Total: $[SUBSCRIPTION AMOUNT]

======================================
NEED TO CANCEL:
======================================

If you don’t need [YOUR BUSINESS NAME] anymore, go to
[URL TO YOUR CANCELLATION PAGE] and cancel at any time.

OPTIONAL: We do not offer refunds for
previous payments, but once you cancel, you will not be
charged again.

If you have any questions, please reply to [CUSTOMER SERVICE EMAIL]

— YOUR COMPANY NAME

Finally, after you’ve exhausted your attempts to contact the user to update the credit card information, you may choose to cancel the account. One way to do that is by using the email alerts sent out in number five above. Notice that the customer.subscriptions.deleted is the event that triggers account Cancellation. Stripe does this for you automatically based on your settings as shown above in the screenshot.

Summary:

Stripe’s API is a great way to work with recurring billing and using web books combined with Stripe’s ‘events’, you can effectively communicate with your customer base.

I hope this helps you implement an effective solution using Stripe.com. If I’ve left anything out or there’s a better way to do some of these, please Let me know by commenting below. I really appreciate it.

(Big thanks to JP and Michael at Stripe for their help)

Join the discussion 26 Comments

  • Great tutorial. I have very similar use cases so this sample code just saved me a *TON* of time. Thanks!

  • Jimmy Chen says:

    Really nice tutorial here. I’m recently working with Stripe to my application and I found this tutorial really handy. It saves me trouble to pick what the expect and what to do with types of event. Great work.

  • Vibgy says:

    This is comprehensive and cool. Made my code much easier to design.

    • Zank says:

      Great, Vibgy. I’m glad it helped. I’m going to add an update to this post in the next few days, so stay tuned if you’re interested to see how it’s been working out.

  • Vibgy says:

    Question: will stripe give expanded objects when it calls the webhook. For example, will it expand “subscription” in subscription.updated to give customer details?

    • Zank says:

      Vibgy,
      All Stripe hooks follow a pattern: resource.event. For subscriptions, for your example, you would receive a “customer.subscription.updated” event.
      This would look like this:
      https://stripe.com/docs/api#update_subscription

      But the simple answer to your question is, yes, you will have sufficient customer details, including the customer ID (that you should be storing in your database).
      -Zank

  • Harry Green says:

    The following article explains how to view and update the current credit card information for a hosting account. Each hosting account associated with the Account Administrator will use the same credit card information. Therefore, when the Account Administrator logs in, any domain name can be selected to modify the credit card information.

  • Great article. Did you use a third party service to style and send the emails? We use both SendGrid and MailChimp for marketing and transactional(but not payment) emails. I was wondering if there was a good template out there for these types of emails.

    • Zank says:

      Great question, Taylor. Actually, for all our emails, we use plain text, and here’s why; we don’t ever want to hit the spam box with our important emails. I modeled the emails we send (for Zankme.com) from a few of the subscription services that I use, such as BeanstalkApp.com (no affiliation). I prefer getting no-nonsense emails when it’s a payment or service related email and I decided to use the same approach. Finally, I always include a ‘cancel your account’ link in all my account-related emails. There’s nothing worse than not being able to find that button when you really need it.
      I hope this helps, and thanks for the question. -Zank

      • Patrick says:

        Found the article helpful too. Thanks Zank.

        It’s still not clear to me what you are using to send the emails.

        Thanks.

        • Zank says:

          Patrick,
          For our production system, we use SwiftMailer (http://swiftmailer.org/), but sending just a few emails (using PHP) is pretty simple. Note that this won’t work on localhost using (the free version of) MAMP; I’m not sure about other local servers.
          To send an email using PHP, something like this should work:

          < ?php $to = "customerEmail@email.com"; $subject = "Engaging/Useful Subject"; $message = "Hi, USER_NAME, here's your update...."; $from = "your@company.com"; $headers = "From:" . $from; mail($to,$subject,$message,$headers); echo "Mail Sent."; ?>
          That’s it. I hope this helps. – Zank

  • Jonathan says:

    With “customer.subscription.updated” you could/should look at the “status” (which I believe is “event.data.object.status”) as it may be marked “unpaid” and your system definitely needs to do something with this.

    You can also look at “event.data.previous_attributes” the status could be different or the dates which may help determine if you need to do something with it.

    Otherwise, nice article.

    • Thanks Jonathan and Zank! In addition to the $event->data->object->status == ‘unpaid’ check mentioned, with the previous_attributes mentioned, you can compare the plan ids for changes.
      ($event->data->object->plan->id != $event->data->previous_attributes->plan->id)

      I’m currently using these main event checks in subscriptions…

      customer.subscription.updated
      Subscription updated
      Check for unpaid status or plan change, update site account, log errors

      customer.subscription.deleted
      User or admin cancels account
      Update site account, log errors

      invoice.created
      New invoice available
      Add any site invoice items, log errors

      invoice.payment_succeeded
      Invoice payment processed
      Save any charge information, email site user, log errors

      invoice.payment_failed
      Credit card information is no longer valid
      Email site user, log errors

  • Great way to iterate over exactly how to design the hook responses.

    Keep in mind that for new subscriptions, charge.succeeded and customer.subscription.created are both hit. That means that step #2 above isn’t technically reserved for ‘any other subsequent payment,’ rather it’s reserved for every successful payment, including all new subscriptions. If you are sending out emails for each event, they need to be generic enough not to confuse the customer since both events will be triggered for new customers.

    There may be a way to check whether the charge is new or recurring, but I haven’t found a way that can be 100% relied on. Thoughts?

  • Royce Haynes says:

    Thanks for sharing. I was going to see if you or anyone has used emailhooks.co?

  • Kate says:

    Thanks for the great tutorial – really helped me out!

  • Thank you for this great overview. Did you manage to find a way how to send emails when customer credit card expire?

  • dbotelho says:

    Regarding “3. User changes subscription level”, I had the same problem as you, but solved it by checking if “plan” exists in the “previous_attributes” from event data.

  • Michael Mossad says:

    Thanks forsharing

  • Tarek Koudsi says:

    Good structured information, thanks for sharing.

  • Milan Rawal says:

    Thanks for sharing. I’m a novice web developer and currently working on integrating stripe and subscription service on my app. And I’m building notification system based on subscription periods like:-
    1) USER should be notified before 15 and 5 of subscription expiration on dashboard.
    2) User should be notified on subscription expiration.
    I’m really stuck at implementing this scenario, can you please send me the schema of your app, so that i get some idea or could you please guide me on implementing this scenario with best schema design. Any help will be heartly appreciated. My email is coderubyonrails@gmail.com if you want to share your app’s schema.

  • pranish says:

    Well this is a very good plus point for stripe that they have provided these features to their customers. Payment sites like paypal are just making the developers scratch their head by not providing proper aid and support. I am integrating paypal for card payments and recurring payments (subscriptions) and there are a lot of issues with no proper support and solutions. People have been facing the same issue for years but still those issues are not solved. Hope stripe will be a good payment solution for all.

    Regards

Leave a Reply to Jimmy Chen Cancel Reply