Capturing a Stripe Credit Card Charge

In this article, I'll show you the JSON objects and Angular/Node code to capture a credit card with the Stripe service. The code for this example project is on GitHub. It is a working project so the code may not be exactly like this article by the time you find it.

Introduction
Capturing credit card information for products, services, and subscriptions is easy with many tools provided by credit card processing companies. Stripe provides an html button that pops up a form to collect credit card information. It is incredibly simple and straightforward.



You can control the form to collect more data. You don't need to know how to program it, and it works. Yeah! This article doesn't cover the easy route of button and pop up because Stripe did a great job of that on their website.

If you would prefer to control the credit card experience from beginning to end, you may choose to build your own form and process the data you collect to meet your own business needs. If that is the case read on.

The Credit Card Token/Charge Experience
A credit card number should never make it to your server – where you are liable for fraud or theft. Instead, the client-side code sends the credit card information to the processor, in this case Stripe, and Stripe sends back a token to the client-side code.

The token isn't a charge to the card but more a promise to charge the card in the future. For the remainder of the customer's experience, you do not use the credit card information, only the token. The token travels with the rest of the customer information (name, address, items purchased) to the server. The server will make the actual charge and receive the resulting information from Stripe.

With stripe, all you really need is a way to make an https request to their Api server, passing JSON, then receive the JSON request. That is true for both the token request and the charge request. Stripe responses with JSON.

JSON is independent of the Technology
The rest of this article is broken into two different sections. The first section will review the JSON objects which have some requirements and a lot of optional fields. You can use any technology you want to make these, including curl. The second section will cover a specific code base of Angular on the client and Node/Express on the server. The code is very simple so there is little or no styling, validation, or error management.

The Stripe Key
Stripe needs your Publishable key to create the token. You can find this on the Stripe dashboard in your account settings.



If you are using curl, you can pass the key along with the token request. If you are passing it from code, it will need to be set before the token request. The curl example is:

curl https://api.stripe.com/v1/tokens \  -u sk_test_WqKypPUzUwdHSgZLT3zWZmBq: \  -d card[number]=4242424242424242 \  -d card[exp_month]=12 \  -d card[exp_year]=2017 \  -d card[cvc]=123

The key is the value after the –u param: sk_test_…

Notice that only the key and card information is passed in the above curl. You can and should capture and pass the billing address. This allows you to see the address in the Stripe Dashboard when the card is finally made.



Notice that you only see the last 4 digits of the credit card.

JSON to create a Stripe Token
The JSON object to request a token contains the credit card information. It should also contain the billing information for the customer.  A full list of card key/value pairs is listed in the Stripe docs.
client stripe token request =  
{   
   "number":"4242424242424242", 
   "cvc":"123", 
   "exp_month":"11", 
   "exp_year":"2016", 
   "name":"Barbara Jones", 
   "address_city":"Seattle", 
   "address_line1":"5678 Nine Street", 
   "address_line2":"Box 3", 
   "address_country":"USA", 
   "address_state":"WA", 
   "address_zip":"98105" 
}

The response will be a status code, success is 200 with a json object of data including the token.

client stripe token response.response = {   
   "id":"tok_17jylQJklCPSOV9aLkiy5879", 
   "object":"token", 
   "card":{   
      "id":"card_17jylQJklCPSOV9ahtXhPVB8", 
      "object":"card", 
      "address_city":"Seattle", 
      "address_country":"USA", 
      "address_line1":"5678 Nine Street", 
      "address_line1_check":"unchecked", 
      "address_line2":"Box 3", 
      "address_state":"WA", 
      "address_zip":"98105", 
      "address_zip_check":"unchecked", 
      "brand":"Visa", 
      "country":"US", 
      "cvc_check":"unchecked", 
      "dynamic_last4":null, 
      "exp_month":11, 
      "exp_year":2016, 
      "funding":"credit", 
      "last4":"4242", 
      "metadata":{     
      }, 
      "name":"Barbara Jones", 
      "tokenization_method":null 
   }, 
   "client_ip":"73.11.000.147", 
   "created":1456782072, 
   "livemode":false, 
   "type":"card", 
   "used":false 
}

For the rest of the credit card transaction, use the token only. You will need to pass it when you charge the customer's credit card.

JSON for a successful Stripe Charge
Now that you have the token, you can create a JSON object to represent the credit card charge.

client stripe token response.status = 200
server stripe charge request object = {    
   "amount":1000,  
   "currency":"usd",  
   "source":"tok_17jylQJklCPSOV9aLkiy5879",  
   "description":"Donation for XYZ",  
   "metadata":{    
      "ShipTo":"Bob Smith",  
      "BillTo":"Barbara Jones"  
   },  
   "receipt_email":"bob@company.com",  
   "statement_descriptor":"MyStripeStore",  
   "shipping":{    
      "address":{    
         "city":"Seattle",  
         "country":"USA",  
         "line1":"1234 Five Lane",  
         "line2":"Floor 2",  
         "postal_code":"98101",  
         "state":"WA"  
      },  
      "name":"Bob Smith",  
      "phone":""  
   }  
} 

Make sure the statement_descriptor has meaningful information to figure out the charge was valid– it shows up on the customer's bill. If you have information important to the transaction that Stripe doesn't collect, put those values in the metadata key. You can retrieve this information from Stripe to reconcile or fulfill the transaction on your end. Think of it as a backup – if your system goes down, Stripe still has enough information for you to rebuild the transaction.  The amount includes dollars and cents but no decimal. So "1000" is ten dollars, $10.00.

If you are new to Stripe, you may not be using their advanced features but if you collect the data now, converting to and seeding some of the advanced feature objects will be easy.

A successful charge response returns an null error object and a result object.

server stripe charge response.charge = {    
   "id":"ch_17jylQJklCPSOV9aHyof0XV8",  
   "object":"charge",  
   "amount":1000,  
   "amount_refunded":0,  
   "application_fee":null,  
   "balance_transaction":"txn_17jylQJklCPSOV9afi1bpfz5",  
   "captured":true,  
   "created":1456782072,  
   "currency":"usd",  
   "customer":null,  
   "description":"Donation for XYZ",  
   "destination":null,  
   "dispute":null,  
   "failure_code":null,  
   "failure_message":null,  
   "fraud_details":{    
   },  
   "invoice":null,  
   "livemode":false,  
   "metadata":{    
      "ShipTo":"Bob Smith",  
      "BillTo":"Barbara Jones"  
   },  
   "order":null,  
   "paid":true,  
   "receipt_email":"bob@company.com",  
   "receipt_number":null,  
   "refunded":false,  
   "refunds":{    
      "object":"list",  
      "data":[    
      ],  
      "has_more":false,  
      "total_count":0,  
      "url":"/v1/charges/ch_17jylQJklCPSOV9aHyof0XV8/refunds"  
   },  
   "shipping":{    
      "address":{    
         "city":"Seattle",  
         "country":"USA",  
         "line1":"1234 Five Lane",  
         "line2":"Floor 2",  
         "postal_code":"98101",  
         "state":"WA"  
      },  
      "carrier":null,  
      "name":"Bob Smith",  
      "phone":"",  
      "tracking_number":null  
   },  
   "source":{    
      "id":"card_17jylQJklCPSOV9ahtXhPVB8",  
      "object":"card",  
      "address_city":"Seattle",  
      "address_country":"USA",  
      "address_line1":"5678 Nine Street",  
      "address_line1_check":"pass",  
      "address_line2":"Box 3",  
      "address_state":"WA",  
      "address_zip":"98105",  
      "address_zip_check":"pass",  
      "brand":"Visa",  
      "country":"US",  
      "customer":null,  
      "cvc_check":"pass",  
      "dynamic_last4":null,  
      "exp_month":11,  
      "exp_year":2016,  
      "fingerprint":"uOlT1SgxEykd9grd",  
      "funding":"credit",  
      "last4":"4242",  
      "metadata":{    
      },  
      "name":"Barbara Jones",  
      "tokenization_method":null  
   },  
   "source_transfer":null,  
   "statement_descriptor":"MyStripeStore",  
   "status":"succeeded"  
} 

The two items you want to look for is status and paid.

Stripe keeps a log of your transactions as JSON objects including the request and response of both the token and the charge. You may want to store all the information on your server, but if you don't, you can get it from Stripe when you need to.

JSON for a failed Stripe Charge
If you customer's charge fails, the JSON for the charge is a bit different. You will want to present the customer with the meaningful error message to help them correct the problem on their end and complete the transaction successfully.

The charge object will be null and the status object will have the error message.

server stripe charge response.status = {    
   "type":"StripeCardError",  
   "stack":"Error: Your card was declined.\n    at Error._Error (/Users/dfberry/repos/stripe-express-angular/node_modules/stripe/lib/Error.js:12:17)\n    at Error.Constructor (/Users/dfberry/repos/stripe-express-angular/node_modules/stripe/lib/utils.js:105:13)\n    at Error.Constructor (/Users/dfberry/repos/stripe-express-angular/node_modules/stripe/lib/utils.js:105:13)\n    at Function.StripeError.generate (/Users/dfberry/repos/stripe-express-angular/node_modules/stripe/lib/Error.js:54:14)\n    at IncomingMessage.<anonymous> (/Users/dfberry/repos/stripe-express-angular/node_modules/stripe/lib/StripeResource.js:138:39)\n    at emitNone (events.js:72:20)\n    at IncomingMessage.emit (events.js:166:7)\n    at endReadableNT (_stream_readable.js:905:12)\n    at nextTickCallbackWith2Args (node.js:455:9)\n    at process._tickCallback (node.js:369:17)",  
   "rawType":"card_error",  
   "code":"card_declined",  
   "message":"Your card was declined.",  
   "raw":{    
      "message":"Your card was declined.",  
      "type":"card_error",  
      "code":"card_declined",  
      "charge":"ch_17jz4rJklCPSOV9aDPh2eooP",  
      "statusCode":402,  
      "requestId":"req_7zz28B9jlOpH1x"  
   },  
   "requestId":"req_7zz28B9jlOpH1x",  
   "statusCode":402  
} 

The message contains the information to display to the customer. While the transaction didn't complete, the log in the Stripe Dashboard will contain the same information. For a complete list of issues, look at the Stripe api for errors.

The Angular and Node/Express application
In order to use Stripe on the client, you need to pull in the Stripe javascript library.

You can get the Stripe library for the client from stripe

<script type="text/javascript" src="https://js.stripe.com/v2/"></script>

In order to use Stripe on the server, you need to install Stripe from NPM

npm install stripe –save-dev

The Donation Form
The example is a donation form. It collects the customer information and allows the customer to choose the donation amount. A shipping address is also collected, for a thank you card back to the customer.



The web page has very little styling, no validation, and only a single error message if the credit card charge is denied.

Angular Client Code
The Angular code includes a directive to display the html, a controller to collect the customer's information, and a service to post to the server. The token creation and charge are both handled in the service one call right after the other. This is definitely not ideal for a real-world situation.

The card, card, and customer information are kept in JSON objects in the models.js file. The Stripe publishable key is in config.js along with the cart item name. When the token and charge are made, the JSON object that Stripe expects for each of these is created.

As all of the Stripe work is in the service, that is the Angular code to review. The complete charge as 3 separate objects, cart, card, and customer.

exports.$myservice = function($http,$myappconfig){  
    var commit = function (completeCharge, callback){  
        var result = {};  
        // my stripe test key  
        Stripe.setPublishableKey($myappconfig.stripePublishableKey);  
        // credit card info passed   
        // billing address passed as part of charge.card  
        Stripe.card.createToken(completeCharge.card, function(status, response) {  
             if (status.error) {  
                console.log("stripe token not created");  
                result.error = status.error;  
                callback(result);  
            }   
            var chargeRequestObject = {   
                    stripeToken: response.id,   
                    cart: completeCharge.cart ,   
                    customer: completeCharge.customer  
            };  
            // token (not credit card) passed  
            // shipping address passed in charge.customer  
            $http.  
                post('/api/v1/checkout', chargeRequestObject).  
                then(function(data) { //success  
                    callback(null, data);  
                },  
                function(response){ //failure  
                    callback(response.data.error, response);  
                });  
            });  
        }  
    return {  
      commit: commit  
    };      
}

Node Server Code
The server code is a node app using Express. It only serves the html page above and has an api to process a charge -- very lean in order to drop it into any other web site.  Morgan is used for logging and Wagner is used for dependency injection.  You can start the app with npm start or node server/index.js.

Index.js
var express = require('express');  
var wagner = require('wagner-core');  
var path = require('path');  
require('./dependencies')(wagner);  
var app = express();  
app.use(require('morgan')());  
app.get(['/'], function (req, res) {res.sendFile(path.join(__dirname + '/../public/default.html'));});  
app.use('/api/v1', require('./api')(wagner));  
app.use('/public', express.static(__dirname + '/../public', { maxAge: 4 * 60 * 60 * 1000 /* 2hrs */}));  
app.listen(3000);  
console.log('Listening on port 3000!'); 

The dependencies file creates the wagner dependency injection for the config and stripe objects.

Dependencies.js
var fs = require('fs');  
var Stripe = require('stripe');  
var configFile = require('./config.json');  
module.exports = function(wagner) {  
  wagner.factory('Stripe', function(Config) {  
    return Stripe(Config.stripeKey);  
  });  
  wagner.factory('Config', function() {  
    return configFile;  
  });  
};

The config.json is simple the configuration json object:

{  
  "stripeKey": "sk_test_WqKypPUzUXXXXXXXT3zWZmBq",  
  "stripePublishableClientKey": "pk_test_ArJPMXXXXlF2Ml4m4e8ILmiP",  
  "Characters22_StoreName": "MyStripeStore"  
}

The main Stripe code of the application is in the api.js file to process the Stripe charge.
module.exports = function(wagner) {  
  var api = express.Router();  
  api.use(bodyparser.json());  
  /* Stripe Checkout API */  
  api.post('/checkout', wagner.invoke(function(Stripe) {  
    return function(req, res) {  
        // https://stripe.com/docs/api#capture_charge  
        // shipping name is in the metadata so that it is easily found on stripe's website   
        // statement_descriptor & description will show on credit card bill  
        // receipt_email is sent but stripe isn't sending receipt - you still have to do that  
        // shipping is sent only so you can pull information from stripe   
        // metadata: 20 keys, with key names up to 40 characters long and values up to 500 characters long  
        var stripeCharge = {  
            amount: req.body.cart.totalprice,  
            currency: 'usd',  
            source: req.body.stripeToken,  
            description: req.body.cart.name,  
            metadata: {'ShipTo': req.body.customer.shipping.name, 'BillTo': req.body.customer.billing.name},  
            receipt_email: req.body.customer.email,  
            statement_descriptor: config.Characters22_StoreName,  
            shipping: req.body.customer.shipping   
        };  
        console.log("server stripe charge request object = " + JSON.stringify(stripeCharge)+ "\n");  
        // Charge the card NOW  
        Stripe.charges.create(stripeCharge,function(err, charge) {  
            console.log("server stripe charge response.err = " + JSON.stringify(err) + "\n");        
            console.log("server stripe charge response.charge = " + JSON.stringify(charge) + "\n");   
            if (err) {  
                return res.  
                status(status.INTERNAL_SERVER_ERROR).  
                json({ error: err.toString(), charge: err.raw.charge, request: err.requestId, type : err.type});  
            }  
            return res.json(charge);  
        });   
     };  
  }));  
  return api;  
}; 

Summary
Captures credit cards with Stripe is simple. You can use their button and form or create your own system. If you create your own client and server, understanding the JSON objects for the token request and the charge request is important. You can pass just the minimum or all the data you have. Create the token on the client and pass the token and charge details to the server to complete the transaction.

Popular posts from this blog

Yet once more into the breech (of altered programming logic)

Simple WP7 Mango App for Background Tasks, Toast, and Tiles: Code Explanation

How to convert SVG data to a Png Image file Using InkScape