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 code
s from such transaction from can be used from your backend to charge the cards directly.
Once it's time to pay, and the user has provided card details on your app,
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.
a. Provide transaction parameters and card params to our SDK's chargeCard
function.
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.)
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.
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.
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:
- card number
4084084084084081
with CVC408
(along with any future expiration date); or; - card number
5060666666666666666
with CVC123
and any future expiration date, PIN1234
, OTP123456
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
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;
}
If you build your own payment form, you'll need to collect at least your customers' card numbers, CVC and expiration dates.
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;
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 anaccess_code
. This can be used to charge the card by doingtransactionParams.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 thesetCustomFieldValue
andsetMetadataValue
you can set up a fresh transaction directly from the SDK. Documentation for these parameters are same as fortransaction/initialize
.
- 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
-
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
}];
}
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.
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
See details for charging returning customers here. Note that only reusable
authorizations can be charged with this endpoint.