PayPal Here SDK

Getting started with the SDK

  1. Prerequisites
  2. Obtaining the SDK
  3. Working with the SDK
  4. SDK capabilities
  5. SDK Best Practices

Prerequisites

Before you install the SDK:

Obtaining the SDK

To obtain the SDK:

  1. Visit the GitHub repository to obtain the software for either iOS or Android.
  2. Review the SDK's Readme file in the GitHub repository and try building the SDK.

Working with the SDK

To prepare to process transactions with the SDK for the first time, an app must perform these setup operations:

  1. Initialize the SDK each time the app starts.
  2. Initialize the merchant by passing their credentials into the SDK.
  3. Find and Connect to a card reader (for card-present transactions).

Once the setup operations are complete, an app must perform these steps to process a basic card-present transaction:

  1. Create an invoice.
  2. Add items to the invoice.
  3. Take a payment using a credit card reader.
  4. Capture the customer's signature, if required for the transaction.
  5. Send a receipt.

Note: The SDK provides the UI to capture the signature and display the receipt options.

Note: These are samples only. You should review the sample apps to see how they are used in an actual application.

Initialization

  1. Initialize the SDK.
PayPalRetailSDK.initializeSDK()
[PayPalRetailSDK initializeSDK];
RetailSDK.initialize(this, new RetailSDK.AppState()
{
    /**
     * The integrating App should return an activity on which the SDK will launch it's UI elements like card reader finder, payment processing alerts
     */
    @Override
    public Activity getCurrentActivity()
    {
      // Return an activity for SDK UI
    }


    @Override
    public boolean getIsTabletMode()
    {
      // return True for tablet mode
    }
});
RetailSDK.Initialize();
  1. Initialize the merchant. The parameter passed here is a SdkCredential object, which should include the access token, refresh URL, and the environment. The Token Management guide explains these values in greater detail.

Note: For iOS, initializeMerchant also takes care of initializing the sdk if it is not already initialized. So, for iOS, initializeMerchant can be used directly without initializeSDK.

let sdkCreds = SdkCredential.init()
sdkCreds.accessToken = "access token of merchant"
sdkCreds.refreshUrl = "refresh URL to use to refresh access token after expiry"
sdkCreds.environment = "either live or sandbox"

PayPalRetailSDK.initializeMerchant(withCredentials: sdkCreds) { (error, merchant) in
    // code to handle success or failure
    // in order to continue on, this needs to be a success
}
SdkCredential *sdkCreds = [[SdkCredential alloc] init];
sdkCreds.accessToken = @"access token of merchant";
sdkCreds.refreshUrl = @"refresh URL to use to refresh access token after expiry";
sdkCreds.environment = @"either live or sandbox";
[PayPalRetailSDK initializePPHRetailMerchantwithCredentials:sdkCreds completionHandler:^(PPRetailError *error, PPRetailMerchant merchant) {
     // code to handle success or failure
     // in order to continue on, this needs to be a success
 }
// credential declarations
SdkCredential credential = new SdkCredential("environment", "access-token");
credential.setTokenRefreshCredentials("refresh URL to use to refresh access token after expiry")

RetailSDK.initializeMerchant(credential, new RetailSDK.MerchantInitializedCallback()
{
    @Override
    public void merchantInitialized(RetailSDKException error, Merchant merchant)
    {
        if (error != null) {
            // handle error situation and try to re-initialize
        } else {
            // merchant initialization success - continue on to card reader connection
        }
    }
});
var merchant = await RetailSDK.InitializeMerchant(new SdkCredentials(sdkToken));
  1. Device discovery. Once the SDK and merchant are initialized, you'll want to connect to a card reader. The following code will demonstrate the three ways of connecting, which are to look for the last known reader, search for a reader to connect to, or auto-connect to the last known reader.

Note: When using the auto connect method, there will be no SDK UI to select a reader as it will attempt to connect in the background. If the last known reader is unable to be connected to, then you'll need to enact one of the other methods.

// previous declaration of deviceManager
let deviceManager = PayPalRetailSDK.deviceManager()

// code to connect to last known reader or find another
deviceManager.connect(toLastActiveReader: { (error, paymentDevice) -> Void in
    // code to handle success or failure
    // if error, check error and retry
    // if success, and connected reader is a BT reader, check for software update
})

// code to search and connect
deviceManager.searchAndConnect({ (error, paymentDevice) -> Void in
    // code to handle success or failure
    // if error, check error and retry
    // if success, and connected reader is a BT reader, check for software update
})

// code to auto-connect to the last known reader
let lastActiveReader = deviceManager?.getLastActiveBluetoothReader()
deviceManager.scanAndAutoConnect(toBluetoothReader: lastActiveReader, callback: { (error, paymentDevice) in
    // code to handle success or failure
    // if error, check error and connect via another method
    // if success, and connected reader is a BT reader, check for software update
})
// previous declaration of deviceManager
PPRetailDeviceManager *deviceManager = [PayPalRetailSDK deviceManager];

// code to connect to last known reader or find another
[deviceManager connectToLastActiveReader:^(PPRetailError *error, PPRetailPaymentDevice *cardReader) {
    // code to handle success or failure
    // if error, check error and retry
    // if success, and connected reader is a BT reader, check for software update
}];

// code to search and connect
[deviceManager searchAndConnect:^(PPRetailError *error, PPRetailPaymentDevice *cardReader) {
    // code to handle success or failure
    // if error, check error and retry
    // if success, and connected reader is a BT reader, check for software update
}];

// code to auto-connect to the last known reader
NSString *lastActiveReader = [self.deviceManager getLastActiveBluetoothReader];
[deviceManager scanAndAutoConnectToBluetoothReader:lastActiveReader callback:^(PPRetailError *error, PPRetailPaymentDevice *cardReader) {
    // code to handle success or failure
    // if error, check error and connect via another method
    // if success, and connected reader is a BT reader, check for software update
}];
// code to connect to last known reader or find another
RetailSDK.getDeviceManager().connectToLastActiveReader(new DeviceManager.ConnectionCallback()
{
     @Override
     public void connection(RetailSDKException error, PaymentDevice cardReader)
     {
           if (error == null && cardReader != null)
           {
             // Successfully connected to last active reader
             // if connected reader is a BT reader, check for software update
           }
           else if (error != null)
           {
             // Connection to card reader failed
           }
           else
           {
             // Could not find the last card reader to connect to
           }
     }
});

// code to search and connect
RetailSDK.getDeviceManager().searchAndConnect(new DeviceManager.ConnectionCallback()
{
    @Override
    public void connection(final RetailSDKException error, final PaymentDevice cardReader)
    {
        if (error == null && cardReader != null)
           {
             // Successfully connected to card reader
             // if connected reader is a BT reader, check for software update
           }
           else if (error != null)
           {
             // Connection to card reader failed
           }
    }
});

// code to auto-connect to the last known reader
String lastReader = RetailSDK.getDeviceManager().getLastActiveBluetoothReader();
    RetailSDK.getDeviceManager().scanAndAutoConnectToBluetoothReader(lastReader, new DeviceManager.ConnectionCallback()
    {
      @Override
      public void connection(final RetailSDKException error, final PaymentDevice cardReader)
      {
        if (error == null && cardReader != null)
           {
             // Successfully connected to card reader
             // if connected reader is a BT reader, check for software update
           }
           else if (error != null)
           {
             // Connection to card reader failed
           }
        });
      }
    });
RetailSDK.DeviceDiscovered += (sender, device) =>
{
    device.Connected += (pd) =>
    {
        // Device is connected
    }

    device.ConnectionError += (pd, error) =>
    {
        // Device connection was not successful
    }


    device.Disconnected += (pd, error) =>
    {
        // Device was disconnected
    }

    device.UpdateRequired += (pd, update) =>
    {
        device.PendingUpdate.Begin((error, upgraded) =>
        {
           // Update executed
        });
    }
};
  1. Reader firmware update. If the connected card reader is a Bluetooth reader, the next thing will be to check for the firmware update and, if one is required, start that process.
// previous declaration of deviceManager
let deviceManager = PayPalRetailSDK.deviceManager()

// adds the listener to know whether a firmware update is available or not for the active reader
deviceManager.getActiveReader().addUpdateRequiredListener({ (update) -> Void in
    if(update.isRequired) {
        update.offer({ (error, deviceUpdated) in
            if(deviceUpdated) {
                // device was updated
            } else {
                // check error and action accordingly
            }
        })
    } else {
        // firmware update not required at this time
    }
})
// previous declaration of deviceManager
PPRetailDeviceManager *deviceManager = [PayPalRetailSDK deviceManager];

// adds the listener to know whether a firmware update is available or not for the active reader
[[deviceManager getActiveReader] addUpdateRequiredListener:^(PPRetailDeviceUpdate *update) {
    if (update.isRequired) {
        [update offer:^(PPRetailError *error, BOOL deviceUpdated) {
            if(deviceUpdated) {
                // firmware update was completed
            } else {
                // check error and action accordingly
            }
         }];
    } else {
        // firmware update not required at this time
    }
}];
RetailSDK.addDeviceDiscoveredObserver(discoveredObserver);

RetailSDK.DeviceDiscoveredObserver discoveredObserver = new RetailSDK.DeviceDiscoveredObserver()
{
    @Override
    public void deviceDiscovered(PaymentDevice device)
    {
      device.addUpdateRequiredObserver(updateRequiredObserver);
    }
};

PaymentDevice.UpdateRequiredObserver updateRequiredObserver = new PaymentDevice.UpdateRequiredObserver()
{
    @Override
    public void updateRequired(DeviceUpdate update)
    {
      update.offer(new DeviceUpdate.CompletedCallback()
      {
        @Override
        public void completed(RetailSDKException error, Boolean deviceUpgraded)
        {
          if (error != null)
          {
            // Update was successful
          }
        }
      });
    }
};

SDK capabilities

Payment

  1. Create an invoice. Be sure to provide a unique invoice number to protect against duplicate transactions.
var invoice: PPRetailInvoice?
invoice = PPRetailInvoice.init(currencyCode: "USD")
invoice.addItem("My Order", quantity: 1, unitPrice: 1.00, itemId: 123, detailId: nil)
invoice.number = "unique_invoice_number"
PPRetailInvoice invoice = [[PPRetailInvoice alloc] initWithCurrencyCode:@"USD"];
[invoice addItem:@"Amount" quantity:1 unitPrice:1 itemId:@"Id" detailId:nil];
[invoice number:@"unique_invoice_number"];
Invoice invoice = new Invoice(null);
invoice.addItem("Item", new BigDecimal(1), new BigDecimal(1), 1, null);
var invoice = new Invoice(null);
invoice.AddItem("Amount", decimal.One, amount, "", "");
invoice.Number("unique_invoice_number");
  1. Create a TransactionContext using the previously created invoice.
var tc: PPRetailTransactionContext?
PayPalRetailSDK.transactionManager().createTransaction(invoice, callback: { (error, context) in
    // if error, handle accordingly, else set the transactionContext to call beginPayment from in
    // the next step
    self.tc = context
})
[PayPalRetailSDK.transactionManager createTransaction:invoice callback:^(PPRetailError *error, PPRetailTransactionContext *context) {
    // if error, handle accordingly, else set the transactionContext to call beginPayment from in
    // the next step
    self.tc = context
}];
TransactionContext currentTransaction;
RetailSDK.getTransactionManager().createTransaction(invoice, new TransactionManager.TransactionCallback()
{
  @Override
  public void transaction(RetailSDKException error, TransactionContext context)
  {
    currentTransaction = context;
    }
});
var transaction = RetailSDK.CreateTransaction(invoice);
  1. Accept a transaction. This part activates the reader so that the customer can select their payment method and then also adds the listeners that will fire when the payment method is chosen.
// Listener called once the transaction is completed
tc.setCompletedHandler { (error, txnRecord) -> Void in

    // if error, handle accordingly, else pop back to view controller and handle success

    self.navigationController?.popToViewController(self, animated: false)

    // txnRecord would have any info needed to record the successful transaction

}

// Setting up the options for the transaction.
let options = PPRetailTransactionBeginOptions()
options.showPromptInCardReader = true
options.showPromptInApp = true
options.preferredFormFactors = []
options.tippingOnReaderEnabled = false
options.amountBasedTipping = false
options.isAuthCapture = false
options.quickChipEnabled = false

// Activates the reader to show the payment options
tc.beginPayment(options)

// Listener called once the transaction is completed
[self.transactionContext setCompletedHandler:^(PPRetailError *error, PPRetailTransactionRecord *txnRecord) {
    // if error, handle accordingly, else pop back to view controller and handle success

    [self.navigationController popToViewController:self animated:YES];

    // txnRecord would have any info needed to record the successful transaction
}];

// Setting up the options for the transaction.
PPRetailTransactionBeginOptions *options = [[PPRetailTransactionBeginOptions alloc] init];
options.showPromptInCardReader = YES;
options.showPromptInApp = YES;
options.preferredFormFactors = @[];
options.tippingOnReaderEnabled = NO;
options.amountBasedTipping = NO;
options.isAuthCapture = NO;
options.quickChipEnabled = NO;

// Activates the reader to show the payment options
[self.transactionContext beginPayment:options];
currentTransaction.setCompletedHandler(new TransactionContext.TransactionCompletedCallback()
{
  @Override
  public void transactionCompleted(RetailSDKException error, TransactionRecord record)
  {
    TransactionActivity.this.transactionCompleted(error, record);
  }
});

TransactionBeginOptions options = new TransactionBeginOptions();
options.setShowPromptInCardReader(true);
options.setShowPromptInApp(false);
options.setIsAuthCapture(false);
options.setAmountBasedTipping(false);
options.setQuickChipEnabled(false);
options.setTippingOnReaderEnabled(false);

currentTransaction.beginPaymentWithOptions(options);
transaction.Begin();
transaction.SetCompletedHandler((error, record) =>
{
    if (error != null)
    {
        // Error - handle accordingly
    } else {
        // Success - do something with the transaction record
    }

});
Payment Options Description
showPromptInCardReader Bool. This is referring to the prompt that tells the customer to tap/insert/swipe the card and whether that shows on the reader or not.
showPromptInApp Bool. This is referring to the prompt that tells the customer to tap/insert/swipe the card and whether that UI shows in the integrating App.
preferredFormFactors Array. This can be used to restrict accepted payment methods (contactless, swipe, chip).
tippingOnReaderEnabled Bool. This sets whether the customer is prompted for a tip on the card reader.
amountBasedTipping Bool. This determines whether the tipping is amount based or percentage based.
isAuthCapture Bool. This determines whether the transation is ran as an authorization or a sale. If this is set to true, then you also need to implement the Receipts API.
quickChipEnabled Bool. This enables quick chip processing so customers don't have to leave their card inserted the whole time the transaction is processing.

Offline Payment

This feature is currently only available to US merchants.

Important: To use offline payments, you must enable it on your PayPal account. To enable it, send your account email address and a business case to use offline payments to pph-sdk@paypal.com.

Offline mode is only available with EMV-capable card readers. Also, offline mode forces the supported card readers to only accept swipe transactions.

To use the offline payments feature, your app must check for Internet connectivity. If you have no connectivity, your app must request that the PayPal Here SDK store the transaction as offline on the mobile device. When the Internet connectivity is regained, your app must request that PayPal Here SDK process the pending offline transaction.

You are subject to the following limits on activity with the offline payments feature:

  • You can only process transactions under USD $5,000.
  • You can only process a total of USD $50,000.
  • You must reconnect to the Internet within 72 hours or the offline transactions expire.

These limits are subject to change at PayPal's sole discretion. PayPal informs you of changes by email.

As a partner, your end-user merchants assume all liability for any offline transactions, including those that are subsequently declined, expired, or disputed. Your end-user merchants cannot dispute declined offline transactions. Your end-user merchants also assume all liability for offline transactions if the device is lost, stolen, damaged, or you delete your app before re-connecting to the Internet. Any refunds are processed in the normal course after your end user merchants are re-connected to the Internet.

Before you enable the offline payments feature, you agree to communicate these terms and conditions to your end users and secure their agreement to those terms and conditions before your end users can use the feature.

  1. Offline initialization If you don't have internet connectivity to successfully initialize a merchant, you can do an offline merchant initialization.

Note: You are only allowed to do an offline merchant initialization after successfully completing at least one online merchant initialization on the device.

PayPalRetailSDK.initializeMerchantOffline { (error, merchant) in
    // code to handle success or failure
}
[PayPalRetailSDK initializeMerchantOffline:^(PPRetailError *error, PPRetailMerchant *merchant) {
    // code to handle success or failure
}];
RetailSDK.initializeMerchantOffline(new RetailSDK.MerchantInitializedCallback()
      {
        @Override
        public void merchantInitialized(RetailSDKException error, Merchant merchant)
        {
          // code to handle success or failure
        }
      });
  1. Enable offline payments to accept payments when you have no internet connection. This will start the offline processing of transactions and save them on the device.
PayPalRetailSDK.transactionManager().startOfflinePayment(callback: { (error, offlinePaymentStatusList) in
    // Status of all the processed and unprocessed offline transactions
})
[PayPalRetailSDK.transactionManager startOfflinePayment:^(PPRetailError *error, NSArray *status) {
    // Status of all the processed and unprocessed offline transactions
}];
RetailSDK.getTransactionManager().startOfflinePayment(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, List < OfflinePaymentStatus > status)
  {
    // Status of all the processed and unprocessed offline transactions
  }
});
  1. Create a TransactionContext using the previously created invoice in the same way as one would for a normal payment.

  2. Accept a transaction in the same way as a normal payment. For offline payments the reader will only accept swipe transactions at this time. Transaction options are not available when processing offline payments.

  3. Check the status of the offline payment saved on the device.

PayPalRetailSDK.transactionManager().getOfflinePaymentStatus(callback: { (error, offlinePaymentStatusList) in
    // Status of the accepted transaction
})
[PayPalRetailSDK.transactionManager getOfflinePaymentStatus:^(PPRetailError *error, NSArray *status) {
    // Status of the accepted transaction
}];
RetailSDK.getTransactionManager().getOfflinePaymentStatus(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, List < OfflinePaymentStatus > status)
  {
    // Status of the accepted transaction
  }
});
  1. Start processing the offline payments.
PayPalRetailSDK.transactionManager().startReplayOfflineTxns(callback: { (error, offlinePaymentStatusList) in
    // Status of the accepted offline transaction after attempting to process it
})
[PayPalRetailSDK.transactionManager startReplayOfflineTxns:^(PPRetailError *error, NSArray *status) {
    // Status of the accepted offline transaction after attempting to process it
}];
RetailSDK.getTransactionManager().startReplayOfflineTxns(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, List < OfflinePaymentStatus > status)
  {
    // Status of the accepted offline transaction after attempting to process it
  }
});
  1. Stop processing the offline payments at any time.
PayPalRetailSDK.transactionManager().stopReplayOfflineTxns(callback: { (error, offlinePaymentStatusList) in
    // Status of the offline transactions
})
[PayPalRetailSDK.transactionManager stopReplayOfflineTxns:^(PPRetailError *error, NSArray *status) {
    // Status of the offline transactions
}];
RetailSDK.getTransactionManager().stopReplayOfflineTxns(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, List < OfflinePaymentStatus > status)
  {
    // Status of the offline transactions
  }
});
  1. Disable offline payments.

Note: The SDK is brought back into online mode once startReplayOfflineTxns is called. When this happens, the app does not need to call stopOfflinePayment to return to online mode.

PayPalRetailSDK.transactionManager().stopOfflinePayment();
[PayPalRetailSDK.transactionManager stopOfflinePayment];
RetailSDK.getTransactionManager().stopOfflinePayment();

Refund

Refunds can either be done within your app or you can use our Refund API to incorporate refund functionality in your back-office. These steps outline how to complete a refund within your app:

  1. Create a TransactionContext for the invoice you would like to refund. The createRefundTransaction method takes in the following parameters: PayPal invoice ID, transaction ID, payment method of the transaction, and a callback handler.
PayPalRetailSDK.transactionManager().createRefundTransaction(paypalInvoiceId, transactionNumber: transactionNumber, paymentMethod: paymentMethod, callback: refundHandler)
[PayPalRetailSDK.transactionManager createRefundTransaction:paypalInvoiceId transactionNumber:transactionNumber paymentMethod:paymentMethod callback:^(PPRetailError *error, PPRetailTransactionContext *context) {
        // refund handler
    }];
RetailSDK.getTransactionManager().createRefundTransaction(transactionRecord.getInvoiceId(), transactionRecord.getTransactionNumber(), transactionRecord.getPaymentMethod(), new TransactionManager.TransactionCallback()
{
  @Override
  public void transaction(RetailSDKException error, TransactionContext refundTransaction)
  {
    // Insert beginRefund code here
  }
});
currentTransaction = RetailSDK.createTransaction(invoice);

The callback handler will possess two parameters which are an error object and a transaction context. If the error object is not nil, then handle accordingly, otherwise use the transaction context to call beginRefund.

  1. Call into beginRefund to ask whether the card is present for the refund or not, and to then process the refund. This code would be used as part of the callbacks mentioned in the createRefundTransaction method.
// Listener that gets called once the refund processes
tc.setCompletedHandler { (error, txnRecord) -> Void in
    // if error, handle accordingly, else pop back to view controller and handle success

    self.navigationController?.popToViewController(self, animated: false)

    // txnRecord would have any info needed to record the successful refund
}

// Begins refund process and asks if a card is present for the refund
tc.beginRefund(true, amount: refundAmount)
// Listener that gets called once the refund processes
[self.transactionContext setCompletedHandler:^(PPRetailError *error, PPRetailTransactionRecord *txnRecord) {
    // if error, handle accordingly, else pop back to view controller and handle success

    [self.navigationController popToViewController:self animated:YES];

    // txnRecord would have any info needed to record the successful transaction
}];

// Begins refund process and asks if a card is present for the refund
[self.transactionContext beginRefund:YES amount:refundAmount];
// Listener that gets called once the refund processes
refundTransaction.setCompletedHandler(new TransactionContext.TransactionCompletedCallback()
{
  @Override
  public void transactionCompleted(RetailSDKException error, TransactionRecord record)
  {
    RefundActivity.this.refundCompleted(error, record);
  }
});

// Begins refund process by asking if a card is present for the refund
refundTransaction.beginRefund(true, currentAmount);
if(cardPresent) {
    currentTransaction.beginRefund(true, new BigDecimal(amount));
} else {
    TransactionContext noCardTransaction =  currentTransaction.beginRefund(false, new BigDecimal(amount));
    noCardTransaction.continueWithCard(null);
}

Additional Capabilities

The SDK also supports several additional capabilities:

  • Adding a referrer code to a transaction
  • Accepting keyed-in card payments
  • Adding a unique invoice ID to a transaction

Best Practices

  • Title the REST Application that you created on developer.paypal.com appropriately with the business name so that merchants have a clear understanding of to whom they are providing permissions.
  • Do not store the client ID and client secret on the mobile device storage. These should be stored securely on your server or back-end server for security reasons.
  • Ensure the option to connect a merchant's PayPal account for PayPal Here processing is clear and concise. Once connected, show the email address of the connected account so the merchant is aware of which account they are using at all times.
  • Provide an option for the merchant to disconnect their account from processing. When a merchant chooses this option, remove all refresh and access tokens for that merchant from your system.
  • Use official images supplied by PayPal for any PayPal Here imagery.
  • Initialize the SDK and merchant immediately after the merchant logs into your app to allow for the app to be ready to transact on the checkout page right away.
  • Implement the appropriate listeners for the reader software update so that mandatory updates are handled accordingly.
  • Utilize your own invoice number as part of the invoice object. This parameter is used for duplicate transaction prevention in the event you need to retry a payment attempt.
  • For successful transactions, keep track of the PayPal invoice ID along with the transaction ID. These values can be used later on for refunds, get details calls, etc.
  • For error scenarios, keep track of the correlation ID, date/time of the error, and the error message itself. The correlation ID greatly decreases resolution time if it is able to be provided to PayPal support teams at all times.

When your integration is complete, check the going live page to ensure that you have everything ready for activation. Once that is working, you can implement other customizations that are available with the SDK into your integration.

Feedback