Teeps

Getting Started with Stripe - Part 2

08 June 2017 by Casey Barth

In Getting Started with Stripe - Part 1, we saw how to properly set up your API routes which will be used by your application. In part 2, we explore how to integrate stripe into your iOS application using the routes provided by your API and the Stripe SDK.

Setting up Stripe in your iOS Project

There are many ways to include the Stripe framework into your iOS project including Cocoapods, manually, and Carthage. Here at Teeps we use Carthage as our dependency manager choice for many reasons, mainly because it seems to be less invasive than other methods and integrates seamlessly with our workflow. For more information on how to include the Stripe SDK into an iOS Project, see their Getting Started Guide.

Once you have successfully added the Stripe framework into your project, you will need to set up a Stripe account. This is completely free to do, and does not require any bank account information until your application is ready for production. To do this, head over to Stripe and sign up as a new user.

After registering your account, you will need to locate your publishable key. This key is used to link your application with your stripe account and is located in your dashboard under the API tab. Make certain that while testing you are using your test key and not the production key. Usage of either key requires configuration in your AppDelegate class. In this post, I am not going into the setup of Apple Pay but this is also where you would set up your Apple Pay merchant identifier.

import UIKit
import Stripe

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    STPPaymentConfiguration.shared().publishableKey = "YOUR_KEY_HERE"
    return true
  }
}

Since the AppDelegate is usually where you would configure the appearance of your application, this is also a good spot to configure the styling of Stripe’s provided card views. You can do that by accessing the default theme and changing its values provided to you in the Stripe standard integration guide.

primaryBackgroundColor // Background color for any views in this theme.
secondaryBackgroundColor // Background color for any supplemental views inside a view (for example the cells of a table view)
primaryForegroundColor // Color for any important labels in a view
secondaryForegroundColor //Color for any supplementary labels in a view
accentColor // For any buttons and other elements on a view that are important to highlight
errorColor // For rendering any error messages or views
font // Font to be used for all views
emphasisFont // Medium-weight font to be used for all bold text in views

After completion, your AppDelegate’s didFinishLaunchingWithOptions should look something along these lines, give or take some configuration that you require.

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    STPPaymentConfiguration.shared().publishableKey = "YOUR_KEY"
    STPPaymentConfiguration.shared().requiredBillingAddressFields = .full
    STPPaymentConfiguration.shared().companyName = "Teeps"
    STPTheme.default().accentColor = UIColor.white
    return true
  }

API Adapter

The next step in this process is creating a way for your app to communicate with the API in a way that Stripe can understand. Thankfully, Stripe provides you with a protocol that defines just that. The STPBackendAPIAdapter protocol defines methods that perfectly align with the required API implementation which you saw in part 1. To set this up, we create a new class that conforms to the protocol.

import Stripe

final class StripeAdapter: NSObject, STPBackendAPIAdapter {
  func retrieveCustomer(_ completion: @escaping STPCustomerCompletionBlock) {
  }

  func attachSource(toCustomer source: STPSourceProtocol, completion: @escaping STPErrorBlock) {
  }

  func selectDefaultCustomerSource(_ source: STPSourceProtocol, completion: @escaping STPErrorBlock) {
  }
}

The first method retrieveCustomer , enables you to get the customer object from Stripe that is linked to the user account in your API. You will notice that the method has a completion named STPCustomerCompletionBlock. This block is defined as (STPCustomer?, Error?) -> Void where STPCustomer is the customer object returned by your API. Also, Stripe does make a deserializer that you can use to easily map the response to the customer object. This snippet does utilize BuckoNetworking to make the request. For more information on how to use BuckoNetworking to simplify your API calls, see our blog about it Protocol Oriented Networking in Swift.

  func retrieveCustomer(_ completion: @escaping STPCustomerCompletionBlock) {
    API.request(endpoint: StripeService.getCustomerInfo) { response in
      if response.result.isSuccess && response.result.error != nil {
        let customerDeserializer = STPCustomerDeserializer(jsonResponse: response.result.value!)
        let customer = customerDeserializer.customer
        completion(customer, nil)
      }
      else {
        completion(nil, response.result.error)
      }
    }
  }

The second method attachSource is called when attempting to attach a new card to Stripe through their view provided. Stripe will generate a token for the card if it is valid, which is accessed on the source parameter in the method. It is required that you send this to your API in the format defined in part 1. The completion here STPErrorBlock is defined as (Error?) -> Void, so in many cases you can pass your error returned from the server as the completion. The provided SDK from Stripe will handle the displaying of any errors that occur when the user passes an error in this completion.

  func attachSource(toCustomer source: STPSourceProtocol, completion: @escaping STPErrorBlock) {
    API.request(endpoint: StripeService.sendToken(token: source.stripeID)) { response in
     // Handle success or failure here
    }
  }

The final method selectDefaultCustomerSource is called when the user selects a new default card from the Stripe card view. It will relay the card token to your API which will set the logged-in user's primary payment method as that card. Similar to the previous method, you will need to pass in the card token to your API. This method also has a completion as an STPErrorBlock.

  func selectDefaultCustomerSource(_ source: STPSourceProtocol, completion: @escaping STPErrorBlock) {
    API.request(endpoint: StripeService.setDefaultCard(token: source.stripeID)) { response in
    // Handle success or failure here
    }
  }

At this point, you have completed the Stripe API Adapter class and you are ready to move on to the implementation of your checkout screen.

Stripe Delegate and Checkout Screens

Now that your app is configured to handle the sending and retrieving of card/customer information, you will need to build your checkout screen to process payments and be notified when payments have been sent or when the context has changed. Implementing a basic checkout screen should at minimum have 4 key elements. A total cost, a product description, payment method, and a way to kick off the purchase.

Here we set up a simple model that has an id, price, and description.

class Product {
  var id: Int
  var price: Int // Stripe takes everything as cents
  var description: String

  init(id: Int, price: Int, name: String, description: String) {
    self.id = id
    self.price = price
    self.description = description
  }
}

Then, we need to set up the ViewController that will handle purchases and the updating of your PaymentContext. The PaymentContext is the object that your screen will mainly revolve around. Everything from updating UI to retrieving the customer info can be done through PaymentContext. The payment context can be defined as:

var paymentContext \= STPPaymentContext(apiAdapter: StripeAdapter())

where StripeAdapter is the class from the last section that interfaces with your API. Your ViewController will also need to adopt to the STPPaymentContextDelegate. Be sure to also set your desired ViewController as the paymentContext.hostViewController Here is a basic ViewController that conforms to that delegate and sets the PaymentContext

import UIKit
import Stripe

class CheckoutViewController: UIViewController {

  @IBOutlet weak var paymentMethodButton: UIButton!
  @IBOutlet weak var productDescLabel: UILabel!
  @IBOutlet weak var productCostLabel: UILabel!

  var product: Product?
  var paymentContext = STPPaymentContext(apiAdapter: StripeAdapter())

  override func viewDidLoad() {
    super.viewDidLoad()
    configureView()
    configurePaymentContext()
  }
  private func configureView() {
     productDescLabel.text = product.description
     productCostLabel.text = product.cost
  }

  private func configurePaymentContext() {
    paymentContext.delegate = self
    paymentContext.hostViewController = self
  }

  @IBAction func paymentMethodTapped(_ sender: Any) {
    paymentContext.pushPaymentMethodsViewController()    
  }

  @IBAction func makePaymentTapped(_ sender: Any) {
    paymentContext.requestPayment()
  }
}

extension ViewController: STPPaymentContextDelegate {
  func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
    // Update your payment Context and change your UI here
    self.paymentContext = paymentContext
    paymentMethodButton.setTitle(paymentContext.selectedPaymentMethod?.label, for: .normal)
  }

  func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
    // Show an error here with failure message
  }

  func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPErrorBlock) {
    API.request(endpoint: StripeService.makePurchase(id: self.product.id)) { response in
      // Handle the response
    }
  }

  func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
   // Handle the response
  }
}

You will notice that in the paymentContextDidChange method, you can make changes to your UI to reflect the new payment context. As shown above, I change the stripe button's text to reflect the card selected.

The didCreatePaymentResult method gets called when a user makes a call to paymentContext.requestPayment(). This is where you will notify your API of the product that is being purchased. Your API should then return a response that the payment was successful or has failed.

The last important method is didFinishWith. This is where you can handle your UI that the purchase has been completed successfully or has failed.

Testing

Once you have completed your Checkout flow, you are ready to test! Stripe has made testing incredibly easy to perform. Stripe provides test cards of all different types, including: VISA, MASTERCARD, AMEX, and more. They also provide you with cards that are expected to fail with defined reasons. This is valuable for testing to see how thoroughly you have handled your errors. You can get those cards from Stripe Testing. The test payments are also viewable through Stripe's dashboard.


Need an app that would require Stripe or have a question? Fill out our Contact form. We would love to chat.

Let's build your next big thing.

Contact Us