# iOS/Android SDK (/archive/paypal-here/sdk-dev/native)



> **Warning:** **Important:** PayPal Here is [deprecated](https://www.paypal.com/us/cshelp/article/paypal-here-deprecation-help299). PayPal doesn't accept new integrations but continues to support existing integrations.

## Work with the SDK [#work-with-the-sdk]

To prepare to process transactions with the SDK for the first time, an app must complete 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).

After setup is complete, an app must complete 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.

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

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

### Initialize the SDK, merchant, and device [#initialize-the-sdk-merchant-and-device]

1. **Initialize the SDK**.

Choose your platform:

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.initializeSDK()
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK initializeSDK];]
```

#### Android

```text lineNumbers
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
  }
});
```

#### Windows

```text lineNumbers
RetailSDK.Initialize();
```

2. **Initialize the merchant.** You pass an `SdkCredential` object, which includes the access token, refresh URL, and the environment. For details, see [Token Management](/archive/paypal-here/merchant-onboarding/permissions#manual-token-management).

   > **Note:** **Note:** For iOS, `initializeMerchant` also initializes the SDK if it is not already initialized. For iOS, you can call `initializeMerchant` directly without calling `initializeSDK`.

#### iOS (Swift)

```text lineNumbers
let sdkCreds = SdkCredential.init(accessToken: "access token of merchant", refreshUrl: "refresh URL to use to refresh access token after expiry", environment: "either live or sandbox")
PayPalRetailSDK.initializeMerchant(withCredentials: sdkCreds) { (error, merchant) in
    // Code to handle success or failure
    // To continue, this must succeed
}
```

#### iOS (Obj-C)

```text lineNumbers
SdkCredential *sdkCreds = [[SdkCredential alloc] initWithAccessToken:@"access token of merchant" refreshUrl:@"refresh URL to use to refresh access token after expiry" environment:@"either live or sandbox"];
[PayPalRetailSDK initializePPHRetailMerchantwithCredentials:sdkCreds completionHandler:^(PPRetailError *error, PPRetailMerchant merchant) {
     // Code to handle success or failure
     // To continue, this must succeed
}
```

#### Android

```text lineNumbers
// 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
    }
  }
});
```

#### Windows

```text lineNumbers
var merchant = await RetailSDK.InitializeMerchant(new SdkCredentials(sdkToken));
```

3. **Device discovery.** After you initialize the SDK and merchant, connect to a card reader. The following code demonstrates how to connect:

   * You can look for the last known reader.
   * You can search for a reader.
   * You can auto-connect to the last known reader.

   > **Note:** **Note:** When you use the auto connect method, no SDK UI appears to select a reader because it tries to connect in the background. If you cannot connect to the last known reader, you must use one of the other methods.

#### iOS (Swift)

```text lineNumbers
// 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
    // On error, check the error and retry
    // On success for a Bluetooth reader, check for software update
})
// code to search and connect
deviceManager.searchAndConnect({ (error, paymentDevice) -> Void in
    // Code to handle success or failure
    // On error, check the error and retry
    // On success for a Bluetooth 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
    // On error, check error and connect by another method
    // On success for a Bluetooth reader, check for software update
})
```

#### iOS (Obj-C)

```text lineNumbers
// 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
    // On error, check the error and retry
    // On success for a Bluetooth reader, check for software update
}];
// code to search and connect
[deviceManager searchAndConnect:^(PPRetailError *error, PPRetailPaymentDevice *cardReader) {
    // Code to handle success or failure
    // On error, check the error and retry
    // On success for a Bluetooth 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
    // On error, check error and connect by another method
    // On success for a Bluetooth reader, check for software update
}];
```

#### Android

```text lineNumbers
// 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
       // For a connected Bluetooth reader, check for software update
     }
     else if (error != null)
     {
       // Card reader connection 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
       // For a connected Bluetooth reader, check for software update
     }
     else if (error != null)
     {
       // Card reader connection 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
         // For a connected Bluetooth reader, check for software update
       }
       else if (error != null)
       {
         // Card reader connection failed
       }
      });
    }
  });
```

#### Windows

```text lineNumbers
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
        });
    }
};
```

4. **Reader firmware update.** If the connected card reader is a Bluetooth reader, you must check for and start the firmware update.

#### iOS (Swift)

```text lineNumbers
reader.pendingUpdate.offer({ (error, updateComplete) in
    // Code to handle success or failure
    // On error, check the error and retry
    // On success, continue with the payment flow
})
```

#### iOS (Obj-C)

```text lineNumbers
[[reader pendingUpdate] offer:^(PPRetailError *error, BOOL updateComplete) {
    // Code to handle success or failure
    // On error, check the error and retry
    // On success, continue with the payment flow
}];
```

#### Android

```text lineNumbers
DeviceUpdate deviceUpdate = activeDevice.getPendingUpdate();
deviceUpdate.offer(new DeviceUpdate.CompletedCallback() {
    @Override
    public void completed(RetailSDKException error, Boolean updateComplete) {
      // Code to handle success or failure
      // On error, check the error and retry
      // On success, continue with the payment flow
    }
});
```

## SDK capabilities [#sdk-capabilities]

### Payment [#payment]

1. **Create an invoice.** Be sure to provide a unique invoice number to protect against duplicate transactions.

#### iOS (Swift)

```text lineNumbers
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"
```

#### iOS (Obj-C)

```text lineNumbers
PPRetailInvoice invoice = [[PPRetailInvoice alloc] initWithCurrencyCode:@"USD"];
[invoice addItem:@"Amount" quantity:1 unitPrice:1 itemId:@"Id" detailId:nil];
[invoice number:@"unique_invoice_number"];
```

#### Android

```text lineNumbers
Invoice invoice = new Invoice("USD");
invoice.addItem("Item", new BigDecimal(1), new BigDecimal(1), 1, null);
```

#### Windows

```text lineNumbers
var invoice = new Invoice(null);
invoice.AddItem("Amount", decimal.One, amount, "", "");
invoice.Number("unique_invoice_number");
```

2. **Create a `TransactionContext` using the previously created invoice.**

#### iOS (Swift)

```text lineNumbers
var tc: PPRetailTransactionContext?
PayPalRetailSDK.transactionManager().createTransaction(invoice, callback: { (error, context) in
    // On error, handle error. Else, set transaction context to call beginPayment in next step
    self.tc = context
})
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK.transactionManager createTransaction:invoice callback:^(PPRetailError *error, PPRetailTransactionContext *context) {
    // On error, handle error. Else, set transaction context to call beginPayment in next step
    self.tc = context
}];]
```

#### Android

```text lineNumbers
TransactionContext currentTransaction;
RetailSDK.getTransactionManager().createTransaction(invoice, new TransactionManager.TransactionCallback()
{
  @Override
  public void transaction(RetailSDKException error, TransactionContext context)
  {
    // On error, handle error. Else, set transaction context to call beginPayment in next step
    currentTransaction = context;
  }
});
```

#### Windows

```text lineNumbers
var transaction = RetailSDK.CreateTransaction(invoice);
```

3. **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.

#### iOS (Swift)

```text lineNumbers
// 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)
```

#### iOS (Obj-C)

```text lineNumbers
// 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];
```

#### Android

```text lineNumbers
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.beginPayment(options);
```

#### Windows

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

| Payment option           | Type  | Description                                                                                                                                                       |
| ------------------------ | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `showPromptInCardReader` | Bool  | Prompts the customer to tap/insert/swipe the card and whether that appears on the reader.                                                                         |
| `showPromptInApp`        | Bool  | Prompts the customer to tap/insert/swipe the card and whether that UI shows in the integrating app.                                                               |
| `preferredFormFactors`   | Array | Restricts accepted payment methods (contactless, swipe, chip).                                                                                                    |
| `tippingOnReaderEnabled` | Bool  | Defines whether the customer is prompted for a tip on the card reader.                                                                                            |
| `amountBasedTipping`     | Bool  | Defines whether the tipping is amount based or percentage based.                                                                                                  |
| `isAuthCapture`          | Bool  | Defines whether the transaction runs as an authorization or a sale. If `true`, you also must implement the [Receipts API](/archive/paypal-here/sdk-dev/receipts). |
| `quickChipEnabled`       | Bool  | Enables quick chip processing so customers don't have to leave their card inserted the whole time the transaction is processing.                                  |

### Offline payment [#offline-payment]

This feature is currently only available to US merchants.

> **Note:** **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](mailto: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 24 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. **Merchant initialization** If you have an Internet connection and can call `initializeMerchant`successfully, you can proceed to step 2 to enable offline mode. You do not need to call `initializeMerchantOffline` if you've already completed a successful online merchant initialization. However, if you do not have an Internet connection to call `initializeMerchant`, you can call `intializeMerchantOffline` to successfully initialize the merchant before processing offline transactions.

> **Note:** **Notes:**
>
> * Call `initializeMerchantOffline` only if you cannot initially call `initializeMerchant`.
> * You can complete an offline merchant initialization only after successfully completing at least one online merchant initialization on the device.
> * If you cannot initially call `initializeMerchant` and instead called `initializeMerchantOffline`, you must call `initializeMerchant` before replaying offline transactions. Before you can process online transactions, online merchant initialization must succeed.

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.initializeMerchantOffline { (error, merchant) in
    // Code to handle success or failure
}
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK initializeMerchantOffline:^(PPRetailError *error, PPRetailMerchant *merchant) {
    // Code to handle success or failure
}];]
```

#### Android

```text lineNumbers
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.

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().startOfflinePayment(callback: { (error, offlinePaymentInfo) in
    // Code to handle success or failure
})
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK.transactionManager startOfflinePayment:^(PPRetailError *error, PPRetailOfflinePaymentInfo *info) {
    // Code to handle success or failure
}];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().startOfflinePayment(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, OfflinePaymentInfo info)
  {
    // Code to handle success or failure
  }
});
```

2. **Create a `TransactionContext`** using the previously created invoice in the same way as one would for a normal payment.
3. **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 and also there is a separate completed handler for offline payments.

#### iOS (Swift)

```text lineNumbers
tc.setOfflineTransactionAdditionHandler({ (error, txnRecord) 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 offline saved transaction
})
```

#### iOS (Obj-C)

```text lineNumbers
[self.transactionContext setOfflineTransactionAdditionHandler:^(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 offline saved transaction
}];
```

#### Android

```text lineNumbers
currentTransaction.setOfflineTransactionAdditionHandler(new TransactionContext.OfflineTransactionAddedCallback()
{
  @Override
  public void offlineTransactionCompleted(RetailSDKException error, TransactionRecord record)
  {
    TransactionActivity.this.offlineTransactionCompleted(error, record);
  }
});
```

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

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().getOfflinePaymentStatus(callback: { (error, offlinePaymentInfo) in
    // Code to handle success or failure
})
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK.transactionManager getOfflinePaymentStatus:^(PPRetailError *error, PPRetailOfflinePaymentInfo *info) {
    // Code to handle success or failure
}];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().getOfflinePaymentStatus(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, OfflinePaymentInfo info)
  {
    // Code to handle success or failure
    TransactionActivity.this.offlinePaymentStatus(error, info);
  }
});
```

4. **Start processing the offline payments.**

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().startReplayOfflineTxns(callback: { (error, offlinePaymentInfo) in
    // Code to handle success or failure of the offline replay
})
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK.transactionManager startReplayOfflineTxns:^(PPRetailError *error, PPRetailOfflinePaymentInfo *info) {
    // Code to handle success or failure of the offline replay
}];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().startReplayOfflineTxns(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, OfflinePaymentInfo info)
  {
    // Code to handle success or failure of the offline replay
    TransactionActivity.this.offlinePaymentCompleted(error, info);
  }
});
```

7. **Stop processing the offline payments** at any time.

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().stopReplayOfflineTxns(callback: { (error, info) in
    // Status of the remaining offline transactions
})
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK.transactionManager stopReplayOfflineTxns:^(PPRetailError *error, PPRetailOfflinePaymentInfo *info) {
    // Status of the remaining offline transactions
}];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().stopReplayOfflineTxns(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, OfflinePaymentInfo info)
  {
    // Status of the remaining offline transactions
    TransactionActivity.this.offlinePaymentStatus(error, info);
  }
});
```

8. **Disable offline payments.**

> **Note:** **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.

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().stopOfflinePayment({ (error, info) in
  // Status of the remaining offline transactions
})
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK.transactionManager stopOfflinePayment:^(PPRetailError *error, PPRetailOfflinePaymentInfo *info) {
    // Status of the remaining offline transactions
}];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().stopOfflinePayment(new TransactionManager.OfflinePaymentStatusCallback()
{
  @Override
  public void offlinePaymentStatus(RetailSDKException error, OfflinePaymentInfo info)
  {
    // Status of the remaining offline transactions
    TransactionActivity.this.offlinePaymentStatus(error, info);
  }
});
```

### Refund [#refund]

Refunds can either be done within your app or you can use our [Refund API](/api/deprecated/payments/v1/sale-refund) 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`.

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().createRefundTransaction(paypalInvoiceId, transactionNumber: transactionNumber, paymentMethod: paymentMethod, callback: refundHandler)
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK.transactionManager createRefundTransaction:paypalInvoiceId transactionNumber:transactionNumber paymentMethod:paymentMethod callback:^(PPRetailError *error, PPRetailTransactionContext *context) {
        // refund handler
    }];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().createRefundTransaction(transactionRecord.getInvoiceId(), transactionRecord.getTransactionNumber(), transactionRecord.getPaymentMethod(), new TransactionManager.TransactionCallback()
{
  @Override
  public void transaction(RetailSDKException error, TransactionContext refundTransaction)
  {
    // Insert beginRefund code here
  }
});
```

#### Windows

```text lineNumbers
currentTransaction = RetailSDK.createTransaction(invoice);
```

The callback handler accepts an error object and transaction context. If the error object is not nil, then handle accordingly. Otherwise, use the transaction context to call `beginRefund`.

2. **Call into `beginRefund`** to determine whether the card is present for the refund, and to process the refund. This code would be used as part of the callbacks mentioned in the `createRefundTransaction` method.

#### iOS (Swift)

```text lineNumbers
// 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)
```

#### iOS (Obj-C)

```text lineNumbers
// 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];
```

#### Android

```text lineNumbers
// 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);
```

#### Windows

```text lineNumbers
if(cardPresent) {
    currentTransaction.beginRefund(true, new BigDecimal(amount));
} else {
    TransactionContext noCardTransaction =  currentTransaction.beginRefund(false, new BigDecimal(amount));
    noCardTransaction.continueWithCard(null);
}
```

### Additional capabilities [#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

When your integration is complete, check the [going live](/archive/paypal-here/sdk-dev/going-live) page to ensure that you have everything ready for activation. Once that is working, you can implement other [customizations](/archive/paypal-here/sdk-dev/customizations) that are available with the SDK into your integration.
