Using Stripe and Card.io for super easy payment in iOS apps

Today I have been testing on how to implement an easy payment system in swift. The idea was the possibility to do one time payment, but also recurrent ones, and allowing the user to scan their credit card instead of having to enter the numbers manually.

To achieve this I have been using the great Stripe SDK, which gives a set of very nice tools to accepts payment in just a few lines of code, and Card.io, which provides fast, easy credit card scanning in mobile apps. As the Stripe swift demo is lacking a full integration of the built-in text form (the swift example on their github is showing mostly how to integrate applepay), I thought a step by step tutorial with Card.io integration could be useful.

Here is what the test app look like:

To get started, create an account on Stripe. Stripe makes this super easy by allowing you to skip the email/password step and get started directly with a test account. Once this is done, go click on “Your account” at the top right of the screen, then “Account Settings”, and navigate to API Keys. Grab the Test Publishable Key (starting with pk_test…), and copy paste it in your app delegate as follow:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        Stripe.setDefaultPublishableKey("pk_test_xxxxxxxxxxx")
        return true
}

Next you need to get the Stripe sdk. In my example, I am using a few libraries,  you can get from cocoapods by adding the following lines in your podfile:

pod 'Stripe'
pod 'CardIO'
pod 'SVProgressHUD'

Run pod install from the terminal, and then add those line in your bridging header:

#import <Stripe/Stripe.h>
#import <CardIO/CardIO.h>
#import <SVProgressHUD/SVProgressHUD.h>

Once this is done, we can create a view controller in storyboard with 2 simple buttons, one for scanning the card using Card.io, and one to validate the data entered in the stripe form. Then copy paste the following code in your view controller:

import UIKit
import Stripe
import SVProgressHUD

class ViewController: UIViewController, STPPaymentCardTextFieldDelegate, CardIOPaymentViewControllerDelegate {

    @IBOutlet weak var payButton: UIButton!
    var paymentTextField: STPPaymentCardTextField!
    
    override func viewDidLoad() {
        // add stripe built-in text field to fill card information in the middle of the view
        super.viewDidLoad()
        let frame1 = CGRect(x: 20, y: 150, width: self.view.frame.size.width - 40, height: 40)
        paymentTextField = STPPaymentCardTextField(frame: frame1)
        paymentTextField.center = view.center
        paymentTextField.delegate = self
        view.addSubview(paymentTextField)
        //disable payButton if there is no card information
        payButton.enabled = false

    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        CardIOUtilities.preload()
    }

    @IBAction func scanCard(sender: AnyObject) {
        //open cardIO controller to scan the card
        let cardIOVC = CardIOPaymentViewController(paymentDelegate: self)
        cardIOVC.modalPresentationStyle = .FormSheet
        presentViewController(cardIOVC, animated: true, completion: nil)
        
    }
    
    
    @IBAction func payButtonTapped(sender: AnyObject) {
        let card = paymentTextField.cardParams
        SVProgressHUD.setDefaultMaskType(SVProgressHUDMaskType.Black)
        SVProgressHUD.setDefaultStyle(SVProgressHUDStyle.Dark)
        //send card information to stripe to get back a token
        getStripeToken(card)
    }
    
    
    func getStripeToken(card:STPCardParams) {
        // get stripe token for current card
        STPAPIClient.sharedClient().createTokenWithCard(card) { token, error in
            if let token = token {
                print(token)
                SVProgressHUD.showSuccessWithStatus("Stripe token successfully received: \(token)")
                self.postStripeToken(token)
            } else {
                print(error)
                SVProgressHUD.showErrorWithStatus(error?.localizedDescription)
            }
        }
    }
    
    // charge money from backend
    func postStripeToken(token: STPToken) {
        //Set up these params as your backend require
        let params: [String: NSObject] = ["stripeToken": token.tokenId, "amount": 10]
      
        //TODO: Send params to your backend to process payment

    }
    
    func paymentCardTextFieldDidChange(textField: STPPaymentCardTextField) {
        if textField.valid{
            payButton.enabled = true
        }
    }
    
    //MARK: - CardIO Methods
    
    //Allow user to cancel card scanning
    func userDidCancelPaymentViewController(paymentViewController: CardIOPaymentViewController!) {
        print("user canceled")
        paymentViewController?.dismissViewControllerAnimated(true, completion: nil)
    }

    //Callback when card is scanned correctly
    func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
        if let info = cardInfo {
            let str = NSString(format: "Received card info.\n Number: %@\n expiry: %02lu/%lu\n cvv: %@.", info.redactedCardNumber, info.expiryMonth, info.expiryYear, info.cvv)
            print(str)

            //dismiss scanning controller
            paymentViewController?.dismissViewControllerAnimated(true, completion: nil)
            
            //create Stripe card
            let card: STPCardParams = STPCardParams()
            card.number = info.cardNumber
            card.expMonth = info.expiryMonth
            card.expYear = info.expiryYear
            card.cvc = info.cvv

            //Send to Stripe
            getStripeToken(card)
            
        }
    }

As you see it’s pretty straightforward, once your backend is setup to process the token you have a lot of freedom to charge your customer. You can, for example, set up recurring payments, use Apple Pay, or choose to charge your customer at some point in the future. Enjoy!