April 20, 2015

1 Year in #Stripe

Author’s note: While I say “1 year” throughout this post, the data is actually 364 days worth.

The 19th marked 1 year since my first post in Stripe’s developer support IRC room and what a year it has been. After some 140,000 messages that have been posted in that chat room, I thought it would be nice to take a look back and have some fun with Excel and ElasticSearch.

My first post in #Stripe was a question about Stripe Connect and transactions fees and I ended up answering the question myself.

[2014-04-19 13:07:26]
markin: Question about Connect transaction fee’s. Would the full amount of the application fee go to the stripe connect application’s account or the standard 2.9+30 take a chunk of that?
[2014-04-19 13:11:28] markin: nvm mind found it, was a bit hidden
[2014-04-19 13:11:28] markin: For example, if a charge amount of $10 was made (as in the example above), $1.23 will be transferred to your account, and $8.18 cents ($10 – $0.59 – $1.23) will be transferred to your user’s account.

Then in typical IRC fashion I stayed in the room and lurked, occasionally people would ask questions and since no one else would answer I’d just answer (and often do a quick Google before).

Over the course of the year, I authored 25,809 messages and was mentioned in 7320. Looking at the 25,809 that I authored, if each took on average 1 minute to compose, that’s just over 430 hours writing messages or about 11 work weeks. (Hey Stripe guys, where can I send an invoice?)

The IRC room has grown quite a bit over the last year from averaging about 100 messages per day to now 386, though recently there has been many days where the room gets over 1000 messages!

Number of Messages Per Day

Total Message Count To Date

Total Message Count To Date

Average Number of Messages Per Date

Average Number of Messages Per Date

Daily count of posts I authored

With a year worth of log files I was a bit interested to see what kind of questions were asked most, why type of tech people were using and in general where did headaches seem to come into play.

Popularity of Tech / Programming Languages

Popularity of Tech / Programming Languages

Looking back it’s not a surprise the PHP was the most popular language referenced, I think this is largely because many new / beginner web developers use PHP. This has also led to PHP being the hardest problems to solve, not necessarily because the content of the of the question was hard but many of these issues became “how do I use PHP?” as opposed to “how do I use Stripe?” questions. In fact, my favorite problems to solve over the years are the ones that really try to push Stripe’s API, the “how can I get this new business model to work with Stripe?” style questions and ironically the ones that involve minimal code. That said, digging into a some fresh unknown code and finding the small bug is always a fun scavenger hunt.

Part of PHP’s popularity in the IRC room was the massive change Stripe rolled out adding namespaces to Stripe-PHP and temporarily making it only available via Composer. Unfortunately, nearly every Stripe PHP tutorial out there used the non-namespaced version and didn’t warn their readers that their tutorial was only valid with Stripe-PHP version 1.X. (Mentions of Stripe.php can still cause me nightmares). PHP also scared me in just that I felt that newer developers were more likely to make a mistake that could jeopardize their PCI compliance and thus their customer’s credit card info. (Part of this is due to Stripe’s interpretation of the new DSS rules, read for more thoughts about that.)

The second most popular language mentioned was Ruby (and Rails), I have no doubt this was largely due to Pete Keen’s book “Mastering Modern Payments” which focuses on Stripe and Ruby on Rails.

My primary language, Node, came in fourth, while Parse technically uses Node and Express, they decided to build their own Stripe library and not update it in years so I included them separately. If anyone reading this works at Parse: Update your library or email me and I’ll do it!

Not too surprising for me was that Apple and iOS beat Android, I’m sure Apple Pay had something to do with this.

Other interesting facts:

  • Bitcoin was more popular than ACH (though ACH is still in private beta)
  • Visa was not surprisingly the most popular payment card followed by American Express.
  • 1018 messages mentioned the API docs (too many questions could be answered by reading the docs I felt).
  • 703 messages mentioned [email protected] (I’d guess either us referring them to support for account support or in the rare case of people complaining about long support times)
  • And 4612 posts were authored by guests (nicks of either Guest* or irctc*).
Percent of messages authored by user (other is < 53 messages)

Percent of messages authored by user (other is < 53 messages)

Author Number of Authored Posts Number of Days Since Joining Number of Mentions Posts Per Day Post to Mention %
markin 25809 363 7320 71 28%
koopajah 9694 150 2478 65 26%
rfunduk 7147 245 1181 29 17%
wsmoak 4433 247 1089 18 25%
dmj 1320 358 205 4 16%
zrail 1300 359 290 4 22%
henriwatson 1037 360 105 3 10%
hybrdthry911 916 230 95 4 10%
wfpkhc 818 176 110 5 13%
dqsf 633 40 194 16 31%

I want to give a special thanks to some great folks on IRC: Koopajah, rfunkduk, and dqsf – Stripe’s developer support team, they do an awesome job and spending time with them is always fun. For community members: wsmoak, zrail, henriwatson have been awesome. Wendy was nearly as crazy as me spending tons of times in the Stripe room, and Pete literally wrote the book on Stripe, and Henri has just been a great helpful guy when he’s around.

April 13, 2015

PCI DSS 3.0, Stripe, Braintree, and My Thoughts

In January, PCI DSS 3.0 (the rules that govern payment card data) came into effect and with it major changes to the PCI requirements including the development of PCI SAQ A-EP. SAQs are questionnaires for merchants to ensure their systems meet PCI standards, and the introduction of SAQ A-EP was in response to merchants who do some partial outsourcing of cardholder data. For instance, Stripe, Braintree and others allow developers to collect card data on their site and then send it directly to the payment company’s servers in order to minimize their PCI compliance (the idea being that the actual card number does not touch the merchant’s servers). PCI DSS 2 allowed developers to do this easily and fall under SAQ A, the easiest level of compliance. Under most people’s reading of the new PCI SAQ A and A-EP, collecting data on your site and sending it to a payment processor requires you to comply with PCI SAQ A-EP, which is quite a massive leap – 12 requirements instead of 2, even if the card data never touches your servers. Stripe, however, has updated its javascript library to transport card data using an iframe and has stated that this meets the necessary requirements for their users to maintain PCI SAQ A compliance. Before we dig in much deeper, let’s take a quick comparison between the eligibility requirements for SAQ A vs. A-EP:

SAQ Comparison

The same document that has that above picture clarifies it even further with:

“If any element of a payment page delivered to consumers’ browsers originates from the merchant’s website, SAQ A does not apply; however, SAQ A-EP may be applicable. Examples of e-commerce implementations addressed by SAQ A-EP include:
- Merchant website creates the payment form, and the payment data is delivered directly to the
payment processor (often referred to as “Direct Post”).
- Merchant website loads or delivers script that runs in consumers’ browsers (for example, JavaScript) and provides functionality that supports creation of the payment page and/or how the data is transmitted to the payment processor.”

The development of this SAQ to directly be targeting tools like Stripe.js and Braintree.js. By utilizing a javascript library or a direct post, a developer can create their own form, send the card data to Stripe or Braintree, and then they only have to handle the token on their server. Braintree and the Understanding PCI SAQs docs that I quoted above seem to be on the understanding that these tools require merchants to be PCI SAQ A-EP compliant, (Braintree even provides their merchants with free SecurityMetrics account) but Stripe says that their Stripe.js qualifies for PCI SAQ A. How can they claim that? Stripe’s support site provides some insight.

“For context, the part of DSS 3.0 that’s most applicable to Stripe users is the new SAQ A-EP (Self Assessment Questionnaire), specifically aimed at businesses who accept payment data via a “direct post” (where cardholder data is sent directly to their processor, completely bypassing the servers of the business).
We’ve been working with our Qualified Security Assessor and others to update Stripe.js to meet the new requirements and security constraints, while still keeping it simple to use. The new version of Stripe.js meets these criteria by performing all transmission of sensitive cardholder data within an iframe served off of a stripe.com domain controlled by Stripe. This in turn allows our customers to continue to be eligible for SAQ-A (the older questionnaire) in most cases.”

While Stripe.js transmits the card data to Stripe through an iframe tunnel, the customer still has to enter their card data into the merchant’s site. In my opinion, transmitting the data through an iframe doesn’t meet the PCI requirements for SAQ A. Looking at SAQ A, merchants must confirm that:

“- Your company has no direct control of the manner in which cardholder data is captured, processed,
transmitted, or stored;
- The entirety of all payment pages delivered to the consumer’s browser originates directly from a
third-party PCI DSS validated service provider(s).”

By creating their own form and calling the Stripe tokenization API, the merchant has direct control on which cardholder data is captured. At least I would consider the inputs that asks for the card number, expiration, and CVC, which are developed by the merchant as direct control. Furthermore, it seems like it would violate the “entirety of all payment pages originates from the service provider” provision (though allowing an iframe is a bit of a loose interpretation of this). The card input is on the merchant’s website __not__ on the payment providers.

For comparison, Stripe and Braintree both offer iframe alternatives. With the iframe, the page that prompts the user for card info is loaded from the payment provider. Thus the payment provider, not the merchant, has direct control over how the data is captured, processed and transmitted. Also, because the card data is entered directly into the iframe, the merchant’s website can’t access the card number directly at all.

From some conversations with people at Stripe, the argument they make is that javascript tokenization is just as secure as an iframe and thus the PCI requirements for them should be the same. This is a bit interesting. There is definitely the thought of “if you have malicious JS, then your whole site is compromised”. But with malicious JS they couldn’t access the card data in an iframe, but they could access the card data if inputted directly into your site. An attacker could theoretically change the API key for your tokenization iframe or change the link of the iframe, but that would likely result in lost sales as the tokens are tied to an API key. So it’s much easier for a malicious piece of javascript or something to affect Stripe.js users than Checkout users because of the iframe where card data is entered.

Stay tuned for my next post where I prove Stripe’s theory right and create a malicious iframe that would be invisible to your customers as well as provide some best practices for tokenization.

October 6, 2014

Protect your Azure Linux VM (aka: How to Avoid a $1500 Charge)

Back in July, I got a Pingdom alert that an insanely low traffic site I was working on was down, after a quick little investigation it looked like the DB layer was down. (Just for context the only people using this website were me and about 3 other people, our DB had 3 records). I tried to SSH into the machine and I couldn’t. I logged into Azure and tried to restart the machine and was unable too. I then noticed the network graph, it was massive. I’m talking like 50Gb/s massive (note this thing was getting at most 1 req/s from pingdom before, according to my bill I use 12+ terabytes of bandwidth). So to play it safe I just deleted the VM and sent an email to Azure billing alerting them of the issue and asking if I was going to be held liable. I’ve done a bit of data center colocation work our IP transit providers were normally pretty lenient for charges based on hacked machines or attacks. At about the same time I got an email from the Azure Safeguards Team saying:

The Microsoft Azure Safeguards Team has determined that a Denial of Service Attack is originating from your Azure deployment (VIP: 138.91.***.***, Name: law****-**). We have included the details of our investigation below.
Such behavior violates the Microsoft Azure Services Terms and your azure deployment has been suspended. The Microsoft Azure Acceptable Use Policy and other agreement terms can be found at http://azure.microsoft.com/en-us/support/legal/. Repeated violations of the Azure Acceptable Use Policy may result in termination of your AzureSubscription.
The Microsoft Azure Safeguards Team ensures that customers abide by the terms of use and investigates allegations of misuse. This allows Microsoft Azure to provide a safe and reputable service environment for all.
If you are surprised by this activity on your Microsoft Azure deployment, have additional questions, or believe that this suspension is not warranted, please reply to this email and let us know. You can also contact Microsoft Azure Customer Support at http://azure.microsoft.com/en-us/support/options/.

 This explains why I couldn’t SSH in or restart the machine, I replied back saying that my machine was compromised and I also deleted the machine from my deployment. 

After about a day I heard back from billing support, they respond back and say:

It goes without saying that we would refund the amount that you incurred due to this event. But before we come to the conclusion that your VM was indeed compromised, I would like to know what led you into believing that this was due to an external factor (a hack. in this case). Although unlikely, do you think this could have been caused by a spike of one (or a bunch) of the processes running on the VM itself?

Awesome, since my machine was compromised they were gonna be nice and refund the costs, they just want to verify it was compromised. Well I got an email from the Safeguards team saying it had launched a DOS attack, and I don’t think that I would be the kind of guy to purposely launch a DOS attack (I’m a 4 year Microsoft MVP). I also tell them that I hadn’t logged into the machine in like a month, the website that this db served has no weird traffic patterns, and then he asked what OS I was using, I replied “Ubuntu”. Microsoft said they’d investigate and get back to me, here is their response: 

It turns out that the Security Team cannot investigate on this, as the data transfer is happening on a Non-Windows Virtual Machine. Looking at the data transfer, it does looks like the Virtual Machine was compromised. I would suggest you to verify this from your end as our Security Team unfortunately cannot help.

So essentially I ask “well now what? We agree that I didn’t do this intentionally, and the machine has been suspended and then deleted to prevent further damage” and their disappointing reponse: 

I have an update from the escalation team on this request. Now, because the compromise was caused due to a security or a vulnerability of a non-windows OS, Microsoft will not be responsible for this. I am afraid, a refund cannot be provided.

At this point, I got the ticket escalated and the response was:

Good Morning. As you are aware we have been in touch with our Business Management team to check whether we have the approval to process a refund for the charges. Kindly be informed that we do not have the approval to give refunds for usage that comes as a result of the VM being left in an insecure state and then getting compromised because of the insecure state.  We request you to follow good security practices and keep the VM secure at all times.

Okay, I left the machine in an insecure state, sure I could have / should have set up fail2ban, or maybe disable password login and only allow SSH keys, but I was using the default configuration. I went to the Azure create a VM page, selected an Ubuntu image, set a random password (it looked something like this: Ac12sd5s.5) and that was it. So simply creating an Ubuntu VM, choosing a decent password, and using all the default settings will leave your machine in an insecure state, and you liable for $1500 work of charges before they suspend your account

Once the charge came through to American Express, I disputed it, Microsoft never responded to the dispute, so that was it.

tl;dr: As soon as you set up your Linux based Azure box, install fail2ban, setup ssh keys, and set a cost threshold in Azure billing.

September 9, 2014

Pretty Custom Integration Stripe Button

One of the annoying things with Stripe Checkout’s custom integration is that you need to style your own button. Now for many of us this isn’t too hard, especially if you’re using a toolkit like Foundation or Bootstrap, but what if you want that iconic Stripe Checkout button look and feel? Well no worries, its pretty easy to accomplish, all you need to do, is link to the Stripe Checkout Buttons CSS, and add the appropriate CSS class stripe-button-el and wrap your text in a span. Take a look:

That turns a button like:


August 11, 2014

How to get Billing and Shipping Address data from Stripe Checkout

Stripe Checkout is an awesome tool for Stripe users to collect credit card info in a beautiful and PCI compliant way.

Up until a couple of months ago, Stripe supported developers requesting both the billing and shipping address from their customers. Since then, they’ve deprecated billing and shipping address to just billing address (renaming it address), and now they have also deprecated address support opting for just zip code support. However if you still want to get access to a customer’s billing and shipping address, fear not, the ability is still there, just a bit undocumented.

NOTE: This feature is deprecated, it might be removed at any time, or may just start acting weird.


Check it out:

So here’s how to implement it:

Simple Integration (Pure HTML)

You’ll want to add two data attributes to your Stripe checkout script tag: data-billingAddress='true' and data-shippingAddress='true'

Your html should look something like this:

When the form submits to your server, you’ll see this all this info added as part of the form:

(Also, the billing address info will also get added to the card token automagically.)


Custom Integration (JS)

The custom integration route is just a simple, just add shippingAddress=true and billingAddress=true to the object passed into your Stripe handler’s open method, like below: 

The billing info will automatically be part of the card and token, the shipping info gets passed to your token callback as a second parameter (which I called args). The contents args look like this:



  • You cannot just request the shipping address, if you set shippingAddress=true and billingAddress=false the user will be prompted for a billing address.
  • Funky stuff will happen if you try to use the address functionality and the new zipCode option. If you use address, Stripe will grab the zip code that the customer entered as part of their billing address to do a zip code check.

P.S. Need help with your Stripe integration? Feel free to send me an email [email protected]