Accepting Credit Card Payments in PHP With Stripe
In one of my personal projects I needed an easy way to accept credit cards. I wanted a quick solution that was easy to integrate, didn’t cost an arm and a leg and would interact with the non-profit’s existing bank account without requiring a merchant account. In previous jobs I have worked with credit card processing software and it has never been one of my favorite tasks. I looked though many different choices and found https://stripe.com/ . I was hooked by their motto: “Payments for developers” and after looking through their documentation I decided they were the way to go.
The first thing I did was use their JavaScript API and design a simple HTML page for displaying my payment form. With a bit of jQuery and their library I had a nice looking payment form that I could just drop right in my existing system. The HTML I used is below:
One major thing to note about the form is that the credit card number, CVC (validation code) and expiration date inputs do not have names. This ensures that those values will never hit my server since they will not be in the form submission. This way I don’t have to worry about any serious PCI compliance efforts and I do not have the risk of leaking any credit card information since you cannot accidently disclose what you never knew.
Jumping back up to the JavaScript the first thing I do is set the public key that was issued to me by Stripe. Every Stripe account has two sets of keys, one for test and one for production. This is another thing I really like about Stripe, it is dead simple to switch between test and live mode just by using a different set of keys. In the form the first thing I do is disable the submit button to prevent the user from clicking it multiple times. Before I transfer the user to the server side processing page I create a token using the Stripe API. I will use this token perform the charge against the credit card on the server side. Again, using the token means that my server never has to know the sensitive credit card number, CVC and expiration date. To do this I use the Stripe.createToken function and pass in the various values from the payment form. The bare minimum information that I need to make a charge is the credit card number, CVC and expiration date. I can get better acceptance rates by including name and address information, plus I need that data for later anyhow so I include it in the token. Since the createToken function is asynchronous the last argument of the function is what function to call when it completes.
When the createToken function completes it will call the stripeResponseHandler function. There I re-enable the submit button if there was an error from the Stripe API or some other issue occurred so that the user can try again. If there were no errors with the JavaScript API then I submit the form to the PHP processing page (remember the credit card number, CVC and expiration values will not be sent to the processing page).
The first thing I do on the backend is to set the private key value for the Stripe API. This is something that has to stay private and should only be placed in server side code. Then I get the form values that were posted and encode using them htmsepcialcharacters to prevent Cross-Site Scripting (XSS) attacks since I will be echoing this data back to the user.
Now in a try/catch block I try to charge the credit card, using the token that was created in the payment form. Note that the charge amount passed to the API call must be in cents and since I am storing the amount to be charged in dollars and cents I need to convert it to cents. If the call completes successfully then the charge has gone through and I can take appropriate action. The Stripe API returns JSON as the response for every call, so I deserialize the response. This will give me a set of objects that I can pull data out of to provide a confirmation screen to my user. I want to get some of the details of the actual card used, so I deserialize that data as well.
Once I have the data I can construct a response page for the user and so that they know the confirmation details. I also need a more permanent record of the transaction so I insert the details into a MySQL table. I don’t want to leave myself open to a SQL injection attack so I make sure that I use a parameterized query to perform the insert.
If the charge did not succeed the Stripe API call will throw an exception. In my catch block I let the user know what happened and display the error reported by the API.
Without much effort at all I have now built a safe and secure online payment form that let’s my users pay online and seven days later the money appears in my bank account. There is more complex functionality available from Stripe such as the ability to create “Customers” and make recurring charges to their cards, again so that you don’t have to store their credit card numbers, as well as a range of other actions you can take with cards (refunds, pre-authorization, etc.). All in all it is a very complete service that charges reasonable fees and is a breeze to integrate into an existing system. For my simple use case, it took me less than a day to go from 0 to production.
- Fall 2012–Crypto, AOP and AppSec
- CodeMash Door Decorating Competition