NAV Navbar
Logo

Card Reader SDKs

Payload’s mobile SDKs provide a simple and secure embedded mobile interface for accepting payments and interacting with our supported card-reader devices.

To get started, jump right to the Installation & Setup section.

Features

Supported Platforms


Android
>=21

iOS
>=10

UWP
(Coming Q3 2019)

Installation & Setup

Installation

Android

// Project build.gradle
buildscript {
    repositories {
        flatDir { dirs 'libs' }
    }
}
// App build.gradle
dependencies {
    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
}

Manual Install

To include Payload’s Android SDK in your project, follow these steps:

  1. Add the payload_android-latest.aar file to your app/libs directory.

  2. Modify the project level build.gradle file to recognize the libs directory.

  3. Modify the app level build.gradle file to include both *.jar and *.aar files.


iOS

Manual Install

To include Payload’s iOS SDK in your project, follow these steps:

  1. If it doesn’t exist yet, create a Frameworks group by selecting New Group in Xcode.

  2. Drag and drop the Payload.framework plugin into this group.

  3. Under Build Settings, navigate to Framework search paths and add the search path $(PROEJECT_DIR)/Frameworks.

  4. Navigate to General section under targets.

  5. Under Embedded Binaries use the icon to add the Payload.framework.


Setup

import com.payload.pl;
import com.payload.Payload;

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        pl.api_key = "test_client_key_3bezxdVdCLpYP9yJ5odpg";
    }
}
import Payload

class ViewController: UIViewController {
    override func viewDidLoad() {
        Payload.api_key = "test_client_key_3bezxdVdCLpYP9yJ5odpg"
    }
}
// ViewController.h
#import <Payload/Payload-Swift.h>
// ViewController.m
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    Payload.api_key = @"test_client_key_3bezxdVdCLpYP9yJ5odpg"
}

Once the SDK is installed, you can import the library and set the client key to be able to interact with your Payload account. Because these are client-side libraries, they require the client key, not the api key.

Your client key can be found from your dashboard under Settings > API Keys where you’ll find both a testing and a production key.


Options

new Device.Manager(this, new Payload.Opts(){{
       this.autoconnect = true;
       this.emv_default = "emv";
       this.offline_enabled = Payload.Opts.SWIPE_ONLY;
       this.offline_payment_limit = 100;
}});

Payload.auto_connect = true;
Payload.default_source = "emv";
Payload.emv_default_quickchip = true;
Payload.offline_enabled = true;
Payload.manual_keyed_view_dismissal = true;
Payload.auto_connect = true;
Payload.default_source = @"emv";
Payload.emv_default_quickchip = true;
Payload.offline_enabled = true;
Payload.manual_keyed_view_dismissal = true;

You can control a few global options.

Option Description Default
auto_connect Autoconnect once a device is detected false
default_source Set a default source for transactions. emv, emv_quickchip, swipe, keyed, nfc nil
emv_default_quickchip Toggle emv type between standard and quickchip modes false
offline_enabled Enable offline payments if connection is lost true
manual_keyed_view_dismissal Allow manual dismissal of the keyed view false

Making API Calls

Making Requests

You can make API calls using the the built-in asynchronous object APIs included in the SDK. Not all objects are available from the client-side libraries.

Select

Payload.all(pl.Customer.select(), new Payload.Callback<List<pl.Customer>>() {
    public void then(List<pl.Customer> custs) {
    }
});
Payload.all(Payload.Customer.select(), {(obj: Any) in
    let custs = obj as? [Payload.Customer]
    /* Do something with response */
})
[Payload all: [Customer select] :^(id obj) {
    NSArray *custs = obj;
    Customer *cust = [custs firstObject];
    /* Do something with response */
}];

Create

Payload.create(pl.Customer(){{
    set("email", "test@gmail.com");
    set("name", "Test Account");
}}, new Payload.Callback<pl.Customer>() {
    public void then(pl.Customer cust) {}
});
Payload.create(Payload.Customer([
    "email": "test@gmail.com",
    "name": "Test Account"
]), {(obj: Any) in
    let cust = obj as? Payload.Customer
    /* Do something with response */
})
[Payload create: [[Customer alloc] init:@{
    @"email": @"test@gmail.com",
    @"name": @"Test Account"
}] :^(id obj) {
    Customer *cust = obj;
}];

Update

Payload.update(cust.set("email", "test@gmail.com"),
    new Payload.Callback<pl.Customer>() {
        public void then(pl.Customer cust) {}
    });
cust.update([
    "email": "test@gmail.com"
], {(obj: Any) in
    let cust = obj as? Payload.Customer
    /* Do something with response */
})
[cust update:@{
    @"email": @"test@gmail.com"
} :^(id obj) {
    Customer *cust = obj;
    /* Do something with response */
}];

Delete

Payload.delete(cust, new Payload.Callback<pl.Customer>() {
    public void then(pl.Customer cust) {}
});
cust.delete({(obj: Any) in
    /* Do something with response */
})
[cust delete:^(id obj) {
    /* Do something with response */
}];

Connect to a Device

Payload’s Device.Manager API provides an abstracted API for connecting with card-reader devices from the host device.

The Device.Manager API also provides events related to the host platform’s connectivity.

Start the Device Manager

You need to initialize the Device.Manager in your application to begin interfacing with any card-reader. Once the device manager is running, you can use the event api to begin monitoring for available card-reader devices.

Events

new Device.Manager(this){
    @Override
    public void detected(Device device) {
        if ( Device.connected_device == null )
            device.connect();
    }

    @Override
    public void connected(Device device) { /* handle event */ }

    @Override
    public void disconnected(Device device) { /* handle event */ }

    @Override
    public void connectionError(Device device, Device.Error errorno) { /* handle event */ }

    @Override
    public void offline() { /* handle event */ }

    @Override
    public void online() { /* handle event */ }
};
import UIKit
import Payload

class ViewController: UIViewController, PayloadDeviceManagerDelegate {
    var manager:Payload.Device.Manager!;

    override func viewDidLoad() {
        super.viewDidLoad()
        self.manager = Payload.Device.Manager(self);
        self.manager.monitor()
    }

    func detected(_ device:Payload.Device){
        if ( self.manager.connectedDevice() == nil ) {
            device.connect();
        }
    }

    func connected(_ device:Payload.Device) { /* handle event */ }
    func disconnected(_ device: Payload.Device) { /* handle event */ }
    func connectionError(_ device:Payload.Device, _ error:Payload.Device.Error) { /* handle event */ }
    func offline() { /* handle event */ }
    func online() { /* handle event */ }
    func forwarded(_ payment:Payload.Payment) { /* handle event */ }

}
#import <UIKit/UIKit.h>
#import <Payload/Payload-Swift.h>

@interface ViewController : UIViewController<PayloadDeviceManagerDelegate>
@end
@interface ViewController ()
@property Manager *manager;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.manager = [[Manager alloc] init:self];
    [self.manager monitor];
}

- (void)detected:(Device * _Nonnull)device {
    if ( [self.manager connectedDevice] == nil ) {
        [device connect];
    }
}

- (void)connected:(Device * _Nonnull)device { /* handle event */ }
- (void)disconnected:(Device * _Nonnull)device { /* handle event */ }
- (void)connectionError:(Device * _Nonnull)device :(enum Error)error { /* handle event */ }
- (void)offline { /* handle event */ }
- (void)online { /* handle event */ }
- (void)forwarded:(Payment * _Nonnull)payment { /* handle event */ }

@end

Detected

The detected event is fired if a supported card-reader device is found in proximity to the mobile device running the Payload SDK. Once a device is detected, you can initiate a connection by calling the connect() method of the device instance.

Connected

If a detected device’s connect() method was called and a successful connection was made, the connected event will be triggered. Once the connected event is triggered for a card reader device, the device is ready to accept payment requests.

Disconnected

If a card-reader device is disconnected from the host device, the disconnected event will be triggered.

Connection Error

If an attempt to connect to a device fails, the connectionError event will be triggered.

Connections fail primarily due to Bluetooth connection problems. The most common solution to connection issues is either to set the bluetooth device to discover mode first or to reenter the pairing passcode correctly.

Offline

The offline event is triggered if the host device loses internet connectivity. If this is the case and the offline payment option hasn’t been disabled, the SDK will enter offline payment mode.

Online

The online event is triggered if connectivity is restored on a host device that lost connectivity. If any payments were initialized while offline, these will be automatically forwarded for processing once online.

Device Object

Methods

Method Description
device.connect() Connect to the device
device.disconnect() Disconnect from the device
device.getBatteryLevel() Get the current battery level of the device
device.beginTransaction() Initiate a request for payment
device.isTransactionStarted() Check if a payment request has been started
device.isTransactionProcessing() Check if the payment is processing
device.cancelTransaction() Cancel a payment request
device.name The name of the device

Processing Payments

Begin Transaction

device.beginTransaction(new pl.Payment(10.12){{
    set("processing_id", "3bfCMwa8OwUbYOvUQKTGi");
}}, new Device.TransactionCallback(){
    public void processing(pl.Payment pymt) { /* handle event */ }
    public void processed(pl.Payment pymt) { /* handle event */ }
    public void declined(pl.Payment pymt) { /* handle event */ }
    public void timeout(pl.Payment pymt) { /* handle event */ }
    public void stored(pl.Payment pymt) { /* handle event */ }
});
import UIKit
import Payload

class PaymentController: UIViewController, PayloadPaymentDelegate {
    var device:Payload.Device!;

    override func viewDidLoad() {
        super.viewDidLoad()
        do {
            try Payload.beginTransaction(Payload.Payment(
                amount: 10,
                processing_id: "3bfCMwa8OwUbYOvUQKTGi"
            ), delegate: self, device: manager.connected_device )
        } catch let error as Payload.Errors.TransactionAlreadyStarted {
            print(error.message)
        }
    }

    func processing(_ payment:Payload.Payment) { /* handle event */ }
    func processed(_ payment:Payload.Payment) { /* handle event */ }
    func declined(_ payment:Payload.Payment) { /* handle event */ }
    func timeout(_ payment:Payload.Payment) { /* handle event */ }
    func stored(_ payment:Payload.Payment) { /* handle event */ }
    func error(_ payment:Payload.Payment,_ error:Payload.PayloadError) { /* handle event */ }
}
#import <UIKit/UIKit.h>
#import <Payload/Payload-Swift.h>

@interface PaymentController : UIViewController<PayloadPaymentDelegate>
@end
@interface PaymentController ()
@property Device *device;
@end

@implementation PaymentController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSError *error = nil;

    [Payload beginTransaction:[[Payment alloc] init:@{
        @"amount": @10,
        @"processing_id": @"3bfCMwa8OwUbYOvUQKTGi",
    }] delegate: self device: nil error: &error];

    if(error) {
        [self console:[NSString stringWithFormat:@"Error finding Name1: %@", error]];
    }
}

- (void)processed:(Payment * _Nonnull)payment { /* handle event */ }
- (void)processing:(Payment * _Nonnull)payment { /* handle event */ }
- (void)declined:(Payment * _Nonnull)payment { /* handle event */ }
- (void)timeout:(Payment * _Nonnull)payment { /* handle event */ }
- (void)stored:(Payment * _Nonnull)payment { /* handle event */ }
- (void)error:(Payment * _Nonnull)payment :(PayloadError * _Nonnull)error { /* handle event */ }

@end

To initiate a payment request, you must call beginTransaction with a Payload Payment object. The minimum required fields for the Payment object are amount and processing_id.

Once a payment request is submitted to beginTransaction, you can track the progress of a transaction by monitoring the payment events described below.

Events

Processing

The processing event is triggered once (1) the card information has been received by the connected device or (2) the information has been keyed in and the payment request has now been submitted to Payload for processing.

Processed

The processed event is triggered if the requested payment was approved and successfully processed.

Declined

The declined event is triggered if a payment was unsuccessful. You can inspect the declined reason by reviewing the status_response_code and status_response_message attributes.

Timeout

The timeout event is triggered if beginTransaction is called but the card-reader device does not receive the card details and the request expires.

Stored

The stored event is triggered if beginTransaction is called while the host device is in offline mode and the payment meets the requirements for being temporarily stored offline.

Error

The error event is triggered if an error occurs while attempting to process the payment request.

Error Description
UnknownRequestError An unknown network error occured
LostConnectivity Connectivity was lost and offline payments is disabled
RequestError A request error occured.
Inspect error_type, error_description, and details for information.

Cancel Transaction

if ( device.isTransactionStarted() && !device.isTransactionProcessing() ) {
    device.cancelTransaction();
}
if device.isTransactionStarted() && !device.isTransactionProcessing() {
    try! device.cancelTransaction();
}
if ([device isTransactionStarted] && ![device isTransactionProcessing]) {
    [device cancelTransaction];
}

You can check the status of a transaction with isTransactionStarted and isTransactionProcessing. If a transaction has started but but hasn’t started processing you can cancel the transaction request on the device with cancelTransaction.


if ( device.isTransactionProcessing() ) {
    device.cancelTransaction(true);
}
if device.isTransactionProcessing() {
    try! device.cancelTransaction(force: true);
}
if ([device isTransactionProcessing]) {
    [device cancelTransaction: force: true];
}

If a transaction has begun processing you can still forcibly cancel a transaction by passing force=true to the cancelTransaction function.


Payment Source

You can control the desired method of card capture when calling beginTransaction by specifying the source attribute on the Payment object.

EMV

device.beginTransaction(new pl.Payment(10.12){{
    set("source", "emv");
    set("processing_id", "3bfCMwa8OwUbYOvUQKTGi");
}}, callback);
Payload.beginTransaction(Payload.Payment(
    amount: 10,
    source: "emv",
    processing_id: "3bfCMwa8OwUbYOvUQKTGi"
), device: device, delegate: self )
NSError *error = nil;

[Payload beginTransaction:[[Payment alloc] init:@{
    @"amount": @10,
    @"source": @"emv",
    @"processing_id": @"3bfCMwa8OwUbYOvUQKTGi",
}] delegate: self device: nil error: &error];

EMV Quickchip

device.beginTransaction(new pl.Payment(10.12){{
    set("source", "emv_quickchip");
    set("processing_id", "3bfCMwa8OwUbYOvUQKTGi");
}}, callback);
Payload.beginTransaction(Payload.Payment(
    amount: 10,
    source: "emv_quickchip",
    processing_id: "3bfCMwa8OwUbYOvUQKTGi"
), device: device, delegate: self )
NSError *error = nil;

[Payload beginTransaction:[[Payment alloc] init:@{
    @"amount": @10,
    @"source": @"emv_quickchip",
    @"processing_id": @"3bfCMwa8OwUbYOvUQKTGi",
}] delegate: self device: nil error: &error];

Swipe

device.beginTransaction(new pl.Payment(10.12){{
    set("source", "swipe");
    set("processing_id", "3bfCMwa8OwUbYOvUQKTGi");
}}, callback);
Payload.beginTransaction(Payload.Payment(
    amount: 10,
    source: "swipe",
    processing_id: "3bfCMwa8OwUbYOvUQKTGi"
), device: device, delegate: self )
NSError *error = nil;

[Payload beginTransaction:[[Payment alloc] init:@{
    @"amount": @10,
    @"source": @"swipe",
    @"processing_id": @"3bfCMwa8OwUbYOvUQKTGi",
}] delegate: self device: nil error: &error];

NFC

device.beginTransaction(new pl.Payment(10.12){{
    set("source", "nfc");
    set("processing_id", "3bfCMwa8OwUbYOvUQKTGi");
}}, callback);
Payload.beginTransaction(Payload.Payment(
    amount: 10,
    source: "nfc",
    processing_id: "3bfCMwa8OwUbYOvUQKTGi"
), device: device, delegate: self )
NSError *error = nil;

[Payload beginTransaction:[[Payment alloc] init:@{
    @"amount": @10,
    @"source": @"nfc",
    @"processing_id": @"3bfCMwa8OwUbYOvUQKTGi",
}] delegate: self device: nil error: &error];

Keyed

device.beginTransaction(new pl.Payment(10.12){{
    set("source", "keyed");
    set("processing_id", "3bfCMwa8OwUbYOvUQKTGi");
}}, callback);
Payload.beginTransaction(Payload.Payment(
    amount: 10,
    source: "keyed",
    processing_id: "3bfCMwa8OwUbYOvUQKTGi"
), device: device, delegate: self )
NSError *error = nil;

[Payload beginTransaction:[[Payment alloc] init:@{
    @"amount": @10,
    @"source": @"keyed",
    @"processing_id": @"3bfCMwa8OwUbYOvUQKTGi",
}] delegate: self device: nil error: &error];

Manual Keyed View Dismissal

Payload.dismissKeyedView()

By default, the keyed interface will close once a transaction is complete but you can override this default behavior and control when you want to close the keyed view.


Keyed View Themes

PayloadFormLabel.appearance().textColor = UIColor.blueColor();
PayloadCard.appearance().backgroundColor = UIColor.cyanColor();
PayloadCardContainer.appearance().backgroundColor = UIColor.lightGrayColor();
PayloadPayBtn.appearance().backgroundColor = UIColor.blueColor();
Class Name Description
PayloadFormLabel The labels above the inputs
PayloadTitle The title text on the keyed UI
PayloadCardContainer The card display container
PayloadCard The card display example
PayloadCardLabel The labels above the inputted text in the card display
PayloadCardTextField The inputted text in the card display
PayoadPayBtn The pay button at the bottom of the page

Offline Payments

device.beginTransaction(new pl.Payment(10.12){{
    set("processing_id", "3bfCMwa8OwUbYOvUQKTGi");
}}, new Device.TransactionCallback(){
    public void stored(pl.Payment pymt) { /* handle event */ }
});
class PaymentController: UIViewController, PayloadPaymentDelegate {
    var device:Payload.Device!;

    func pay() {
        Payload.beginTransaction(Payload.Payment(
            amount: 10,
            processing_id: "3bfCMwa8OwUbYOvUQKTGi"
        ), device: device, delegate: self )
    }

    func stored(_ device:Payload.Device) { /* handle event */ }
}
#import <UIKit/UIKit.h>
#import <Payload/Payload-Swift.h>

@interface PaymentController : UIViewController<PayloadPaymentDelegate>
@end
@interface PaymentController ()
@property Device *device;
@end

@implementation PaymentController

- (void)pay {
    NSError *error = nil;

    [Payload beginTransaction:[[Payment alloc] init:@{
        @"amount": @10,
        @"processing_id": @"3bfCMwa8OwUbYOvUQKTGi",
    }] delegate: self device: nil error: &error];

    if(error) {
        [self console:[NSString stringWithFormat:@"Error finding Name1: %@", error]];
    }
}

- (void)stored:(Payment * _Nonnull)payment { /* handle event */ }
@end

If offline payments are enabled and the host device loses internet connectivity, the Payload SDK will switch to offline mode. Payment requests will be stored temporarily while offline; once connectivity is restored, those requests will be forward for processing.

You can enable offline payments by setting the offline_enabled flag.

Max Payment Limit

It’s recommended to enforce a maximum payment limit for an offline transaction. This can be achieved by setting the offline_payment_limit option. Any payment requests above this limit will be declined unless a phone authorization is provided.

Keyed Phone Authorization

For payments above the offline_payment_limit, you can attempt to key in the card details with a phone authorization, assuming there is still access to a working phone. The keyed interface presents instructions for capturing a phone authorization.

Security

All supported card readers are certified on the Payload platform and our SDKs and APIs are certified & compliant with the PCI-DSS standard.

All supported devices utilize end-to-end encryption for all sensitive card information and all requests are transmitted directly from the host device to Payload’s secure PCI environment.

You can read more about the general security features of Payload in our Security & Compliance section.