This aim of this tutorial is to create an iOS app that can make peer-to-peer
payments to anyone nearby using PayPal and Chirp.

Chirp uses sound to transfer data over the air, which creates a seamless
interaction between unpaired devices. Combining this with one of the largest
payment providers in the world allows money to be transferred securely to
anyone in your vicinity. The unique affordances of data-over-sound also mean
that a payment request could be broadcast to many people simultaneously.

Since PayPal acquired Braintree in 2013, developers are now guided towards
their SDKs to create transactions. Although the Braintree SDKs are great to
use, they are more catered for merchant transactions and not peer-to-peer
payments. However this code snippet from PayPal Engineering explains how
P2P payments can be made using the PayPal REST API, and this will form the
basis for this tutorial.

You can view the source code for this app here. To go straight to the API client code, go here. A getting started guide for Chirp with Swift can be found here.

A PayPal payment is completed in three steps.

  1. The requestor first creates a payment with the payee’s email address and the requested amount.
  2. This payment ID is sent to the payer using Chirp, who authorises the payment using PayPal’s OAuth web flow, generating a unique payer ID.
  3. The payer ID is then sent back to the requestor using Chirp to execute the payment.

First of all though, we need to generate an access token to authorise our API
requests. This requires a client ID and secret which can be generated from the
PayPal developer console.


This is just for authenticating API requests, the payment authorisation all
happens in the web flow.

let clientId = "PAYPAL_CLIENT_ID"  
let clientSecret = "PAYPAL_CLIENT_SECRET"


let apiClient = PayPalAPIClient()  
apiClient.send(PayPalAuthenticate(clientId: clientId, clientSecret: clientSecret)) { response in  
    switch response {  
    case .success(let data):  
        guard let data = data as? [String:Any] else {return}  
        guard let accessToken = data["access_token"] as? String else {return}  
    }  
}

Now the requestor can use this access token to create a payment.

apiClient.send(PayPalPayment(  
    accessToken: accessToken,  
    payeeEmail: "payee@email.com",  
    amount: "10",   
    currency: "GBP",  
    returnUrl: "https://mysite.com/process",  
    cancelUrl: "http://mysite.com/cancel",  
    description: "P2P Payment"  
)) { response in  
    switch response {  
    case .success(let data):  
        guard let data = data as? [String:Any] else {return}  
        guard let paymentId = data["id"] as? String else {return}  
        guard let links = data["links"] as? [[String: String]] else {return}  
        for link in links {  
            if link["method"] == "REDIRECT" {  
                guard let href = link["href"] else {return}  
                let authUrl = URL(string: href)  
            }  
        }  
    }  
}

Once the payment has been created, the payment ID can be saved for later, as
the requestor will also be executing the payment. Inside the response there is
also a redirect URL that the payer should visit to authorise the payment.

Since most of this URL is constant, we just extract the token from the URL and
send this to the payer to keep the transmission as short as possible. To send
the entire URL with Chirp’s standard protocol would take roughly 10secs,
whereas the token alone is more like 2secs.

After the payer has completed the payment authorisation, the web browser is
redirected to the returnUrl that was specified in the initial payment
request, that contains the generated Payer ID as a query parameter. This can
be extracted from the URL and sent back to the requestor to execute.

apiClient.send(PayPalExecute(  
    accessToken: accessToken,  
    paymentId: paymentId,  
    payerId: payerId  
)) { response in  
    switch response {  
    case .success(let data):  
        print("payment successful")  
    }  
}

Like most apps, we need to create different view controllers for each view in
our app, and these controllers will likely need to access one single instance
of something, ie. Chirp Connect. Rather than creating a global Chirp instance
it is considered best practice to create a service which interfaces the SDK,
and delegate control of this to the designated view controller. To do this we
need to create a protocol to manage the data flow in and out of the service.

protocol ChirpDelegate {  
    func onReceived(data: Data?)  
}


class ChirpService {  
    var delegate: ChirpDelegate?  
    var sdk: ChirpConnect?  
      
    public init() {  
        sdk = ChirpConnect(appKey: APP_KEY, andSecret: APP_SECRET)  
        if let sdk = sdk {  
            sdk.setConfig(APP_CONFIG)  
            sdk.start()  
              
            sdk.receivedBlock = {  
                (data: Data?) -> () in  
                if let delegate = self.delegate {  
                    delegate.onReceived(data: data)  
                }  
                return;  
            }  
        }  
    }  
}

This service is now simply instantiated once in the app delegate, and control
is passed on to the relevant view controller when necessary.

So to send a payment ID to a receiving device, we can just use the connect
instance directly from the app delegate, converting the string data to the
required NSData type.

let appDelegate = UIApplication.shared.delegate as! AppDelegate  
if let connect = appDelegate.connect {  
    let payload = paymentId.data(using: .utf8, allowLossyConversion: false)  
    connect.send(payload: payload!)  
}

On the receiving side, we can delegate control of the service to the relevant
view controller to process the received payer ID.

class ReceiveController: UIViewController, ChirpDelegate {  
      
    override func viewDidLoad() {  
        if let connect = appDelegate.connect {  
            connect.delegate = self  
        }  
    }


    func onReceived(data: Data?) {  
        if let data = data {  
            let payerId = String(data: data, encoding: .ascii)!  
            print(String(format: "Received Payer ID: %@", payerId))  
        } else {  
            print("Decode failed");  
        }  
    }  
}

Please note that these code snippets serve as reference material, for a full
working example app, head over to the Chirp iOS examples
repository.


Chirp have now released a new pricing model which grants free usage to personal and commercial projects with less than 10k monthly active users. So to get started developing your own app with Chirp, sign up to download SDKs for iOS/Android/Python and find further documentation at the developer hub.

Made something cool with Chirp SDK’s? Tweet it to @Chirp.

joe@chirp.io