Skip to content

Latest commit

 

History

History
413 lines (300 loc) · 16.7 KB

GUIDE.md

File metadata and controls

413 lines (300 loc) · 16.7 KB

Guide

If you want to build mobile apps like Taxify, Afro, Okada Books and enable people to make purchases directly in your app, our iOS and Android libraries can help.

Accepting payments in your app after collecting card information can be achieved by charging the card with our SDK. Reusable authorization codes from such transaction from can be used from your backend to charge the cards directly.

Summarized flow

Once it's time to pay, and the user has provided card details on your app,

OPTION 1: Backend starts transaction (recommended)

a. App prompts backend to initialize a transaction, backend returns access_code.

b. Provide access_code and card details to our SDK's chargeCard function.

OPTION 2: App starts transaction

a. Provide transaction parameters and card params to our SDK's chargeCard function.

SDK will prompt user for PIN, OTP or Bank authentication as required

Once successful, we will send event to your webhook url and call the didTransactionSuccess callback

Getting Started

Step 0: Add Keychain Sharing entitlements to your app

Step 1: Install the library

Manual installation

We publish our SDK as a static framework that you can copy directly into your app without any additional tools:

  • Head to our releases page and download the framework that's right for you.
  • Unzip the file you downloaded.
  • In Xcode, with your project open, click on 'File' then 'Add files to "Project"...'.
  • Select Paystack.framework in the directory you just unzipped.
  • Make sure 'Copy items if needed' is checked.
  • Click 'Add'.
  • In your project settings, go to the "Build Settings" tab, and make sure -ObjC is present under "Other Linker Flags".

Using CocoaPods

We recommend using CocoaPods to install the Paystack iOS library, since it makes it easy to keep your app's dependencies up to date.

If you haven't set up Cocoapods before, their site has installation instructions. Then, add pod 'Paystack' to your Podfile, and run pod install.

(Don't forget to use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file, from here on out.)

Using Carthage

We also support installing our SDK using Carthage. You can simply add github "paystackhq/paystack-ios" to your Cartfile, and follow the Carthage installation instructions.

Step 2: Configure API keys

First, you'll want to configure Paystack with your public API key. We recommend doing this in your AppDelegate's application:didFinishLaunchingWithOptions: method so that it will be set for the entire lifecycle of your app.

// AppDelegate.swift

import Paystack

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        Paystack.setDefaultPublicKey("pk_test_xxxx")
        return true
    }
}
// AppDelegate.m

#import "AppDelegate.h"
#import <Paystack/Paystack.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [Paystack setDefaultPublicKey:@"pk_test_xxxxx"];
    return YES;
}

@end

We've placed a test public API key as the PaystackPublicKey constant in the above snippet. You'll need to swap it out with your live public key in production. You can see all your API keys in your dashboard.

Step 3: Collecting credit card information

Test Mode

When you're using your test public key, our libraries give you the ability to test your payment flow without having to charge real credit cards.

If you're building your own form or using PSTCKPaymentCardTextField, using any of:

  1. card number 4084084084084081 with CVC 408 (along with any future expiration date); or;
  2. card number 5060666666666666666 with CVC 123 and any future expiration date, PIN 1234, OTP 123456

will accomplish the same effect.

At some point in the flow of your app, you'll want to obtain payment details from the user. There are two ways to do this. You can (in increasing order of complexity):

  • Use our pre-built form component, PSTCKPaymentCardTextField, to collect new credit card details
  • Build your own credit card form from scratch

Using PSTCKPaymentCardTextField

To use our pre-built form component, we'll create a view controller called PaymentViewController and add a PSTCKPaymentCardTextField property to the view controller.

// PaymentViewController.swift

class PaymentViewController: UIViewController, PSTCKPaymentCardTextFieldDelegate {
    let paymentTextField = PSTCKPaymentCardTextField()
}
// PaymentViewController.m

#import "PaymentViewController.h"

@interface PaymentViewController ()<PSTCKPaymentCardTextFieldDelegate>
@property(nonatomic) PSTCKPaymentCardTextField *paymentTextField;
@end

Next, let's instantiate the PSTCKPaymentCardTextField, set the PaymentViewController as its PSTCKPaymentCardTextFieldDelegate, and add it to our view.

// PaymentViewController.swift

override func viewDidLoad() {
    super.viewDidLoad();
    paymentTextField.frame = CGRectMake(15, 15, CGRectGetWidth(self.view.frame) - 30, 44)
    paymentTextField.delegate = self
    view.addSubview(paymentTextField)
}
// PaymentViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.paymentTextField = [[PSTCKPaymentCardTextField alloc] initWithFrame:CGRectMake(15, 15, CGRectGetWidth(self.view.frame) - 30, 44)];
    self.paymentTextField.delegate = self;
    [self.view addSubview:self.paymentTextField];
}

This will add an PSTCKPaymentCardTextField to the controller to accept card numbers, expiration dates, and CVCs. It'll format the input, and validate it on the fly.

When the user enters text into this field, the paymentCardTextFieldDidChange: method will be called on our view controller. In this callback, we can enable a save button that allows users to submit their valid cards if the form is valid:

func paymentCardTextFieldDidChange(textField: PSTCKPaymentCardTextField) {
    // Toggle navigation, for example
    saveButton.enabled = textField.isValid
}
- (void)paymentCardTextFieldDidChange:(PSTCKPaymentCardTextField *)textField { {
    // Toggle navigation, for example
    self.saveButton.enabled = textField.isValid;
}

Building your own form

If you build your own payment form, you'll need to collect at least your customers' card numbers, CVC and expiration dates.

Step 4: Assembling Card information into PSTCKCardParams

If you're using PSTCKPaymentCardTextField, simply call its cardParams property to get the assembled card data.

let cardParams = paymentTextField.cardParams as PSTCKCardParams
PSTCKCardParams cardParams = [paymentTextField cardParams];

If you are using your own form, you can assemble the data into an PSTCKCardParams object thus:

let cardParams = PSTCKCardParams.init();

// then set parameters thus from card
cardParams.number = card.number
cardParams.cvc = card.cvc
cardParams.expYear = card.expYear
cardParams.expMonth = card.expMonth

// or directly
cardParams.number = "2963781976222"
cardParams.cvc = "289"
cardParams.expYear = 2018
cardParams.expMonth = 9
PSTCKCardParams cardParams = [[PSTCKCardParams alloc] init];

// then set parameters thus from card
cardParams.number = [card number];
cardParams.cvc = [card cvc];
cardParams.expYear = [card expYear];
cardParams.expMonth = [card expMonth];

// or directly
cardParams.number = "2963781976222";
cardParams.cvc = "289";
cardParams.expYear = 2018;
cardParams.expMonth = 9;

Step 4: Getting payments

Our libraries shoulder the burden of PCI compliance by helping you avoid the need to send card data directly to your server. Instead, our libraries send credit card data directly to our servers, where we can charge them or create authorizations which you charge on your server.

We charge cards you send using parameters provided in your PSTCKTransactionParams. Assemble Transaction parameters into PSTCKTransactionParams, and send them along with the cardParams from the previous step to get a charge.

  • CardParams - As gathered in Step 3

  • TransactionParams - This object allows you provide information about the transaction to be made. This can be used in either of 2 ways:

    • Resume an initialized transaction: If employing this flow, you would send all required parameters for the transaction from your backend to the Paystack API via the transaction/initialize call - documented here.. The response of the call includes an access_code. This can be used to charge the card by doing transactionParams.access_code = {value from backend});. Once an access code is set, others will be ignored.
    • Initiate a fresh transaction on Paystack: By setting the parameters: amount, email, currency, plan, subaccount, transactionCharge, reference, bearer. And calling the setCustomFieldValue and setMetadataValue you can set up a fresh transaction directly from the SDK. Documentation for these parameters are same as for transaction/initialize.
  • ViewController - A view controller to be used when presenting dialogs. The currently open ViewController is perfect.

You will need to specify callbacks too. Each will be called depending on how the transaction went.

  • didTransactionSuccess will be called once the charge succeeds.

  • didRequestValidation is called every time the SDK needs to request user input. This function currently only allows the app know that the SDK is requesting further user input.

  • didEndWithError is called if an error occurred during processing. Some types that you should watch include

    • PSTCKErrorCode.PSTCKExpiredAccessCodeError: This would be thrown if the access code has already been used to attempt a charge.
    • PSTCKErrorCode.PSTCKConflictError: This would be thrown if another transaction is currently being processed by the SDK
@IBAction func charge(sender: UIButton) {
    // cardParams already fetched from our view or assembled by you
    let transactionParams = PSTCKTransactionParams.init();

    // building new Paystack Transaction
    transactionParams.amount = 1390;
    let custom_filters: NSMutableDictionary = [
        "recurring": true
    ];
    let items: NSMutableArray = [
        "Bag","Glasses"
    ];
    do {
        try transactionParams.setCustomFieldValue("iOS SDK", displayedAs: "Paid Via");
        try transactionParams.setCustomFieldValue("Paystack hats", displayedAs: "To Buy");
        try transactionParams.setMetadataValue("iOS SDK", forKey: "paid_via");
        try transactionParams.setMetadataValueDict(custom_filters, forKey: "custom_filters");
        try transactionParams.setMetadataValueArray(items, forKey: "items");
    } catch {
        print(error);
    }
    transactionParams.email = "[email protected]";

    // check https://developers.paystack.co/docs/split-payments-overview for details on how these work
    // transactionParams.subaccount  = "ACCT_80d907euhish8d";
    // transactionParams.bearer  = "subaccount";
    // transactionParams.transaction_charge  = 280;

    // if a reference is not supplied, we will give one
    // transactionParams.reference = "ChargedFromiOSSDK@"

    PSTCKAPIClient.shared().chargeCard(cardParams, forTransaction: transactionParams, on: viewController,
               didEndWithError: { (error, reference) -> Void in
                handleError(error)
            }, didRequestValidation: { (reference) -> Void in
                // an OTP was requested, transaction has not yet succeeded
            }, didTransactionSuccess: { (reference) -> Void in
                // transaction may have succeeded, please verify on backend
        })
}
- (IBAction)charge:(UIButton *)sender {
    // cardParams already fetched from our view or assembled by you

    PSTCKTransactionParams transactionParams = [[PSTCKTransactionParams alloc] init];

    // resuming a transaction initialized by backend
    transactionParams.access_code = '{access code from server}';

    [[PSTCKAPIClient sharedClient] chargeCard:cardParams
                               forTransaction:transactionParams
                             onViewController: viewController,
                              didEndWithError:^(NSError *error, NSString *reference){
                                                [self handleError:error];
                                            }
                         didRequestValidation: ^(NSString *reference){
                                                // an OTP was requested, transaction has not yet succeeded
                                            }
                        didTransactionSuccess: ^(NSString *reference){
                                                // transaction may have succeeded, please verify on backend
      }];

}

Step 5: Send the reference to your backend

The blocks you gave to chargeCard will be called whenever Paystack returns with a reference (or error). You'll need to send the reference off to your backend so you can verify the transactions.

Here's how it looks:

// ViewController.swift

func verifyCharge(reference: String) {
    let url = NSURL(string: "https://example.com/verify")!
    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "POST"
    let postBody = "reference=reference"
    let postData = postBody.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
    session.uploadTaskWithRequest(request, fromData: postData, completionHandler: { data, response, error in
        let successfulResponse = (response as? NSHTTPURLResponse)?.statusCode == 200
        if successfulResponse && error == nil && data != nil{
            // All was well
            let newStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
            print(newStr) // All we did here is log it to the output window
        } else {
            if let e=error {
                print(e.description)
            } else {
                // There was no error returned though status code was not 200
                print("There was an error communicating with your payment backend.")
                // All we did here is log it to the output window
            }

        }
    }).resume()
}
// ViewController.m

- (void)verifyCharge:(String *)reference
                           {
    NSURL *url = [NSURL URLWithString:@"https://example.com/verify"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    request.HTTPMethod = @"POST";
    NSString *body     = [NSString stringWithFormat:@"reference=%@", reference];
    request.HTTPBody   = [body dataUsingEncoding:NSUTF8StringEncoding];
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
    NSURLSessionDataTask *task =
    [session dataTaskWithRequest:request
               completionHandler:^(NSData *data,
                                   NSURLResponse *response,
                                   NSError *error) {
                   if (error) {
                       ...
                   } else {
                       ...
                   }
               }];
    [task resume];
}

On the server, you just need to implement an endpoint that will accept the parameter: reference. Make sure any communication with your backend is SSL secured to prevent eavesdropping.

Step 6: Implement verification on your server

Verify a charge by calling our REST API. An active authorization_code will be returned once the card has been charged successfully. You can learn more about our API here.

Endpoint: GET: https://api.paystack.co/transaction/verify

Documentation: https://developers.paystack.co/docs/verify-transaction

Parameters:

  • reference - the transaction reference

Example

   $ curl https://api.paystack.co/transaction/verify/trx_sjdhf2987hb \
    -H "Authorization: Bearer SECRET_KEY" \
    -H "Content-Type: application/json" \
    -X GET

Charging Returning Customers

See details for charging returning customers here. Note that only reusable authorizations can be charged with this endpoint.