# Customizations (/archive/paypal-here/sdk-dev/customizations)



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

There are some additional options for handling payments within the PPH SDK. This page, outlines the various options along with the relevant code samples.

## Card vaulting via Braintree [#card-vaulting-via-braintree]

For Braintree-specific functionality of this feature, please visit the PayPal Here [integration page](https://developer.paypal.com/braintree/articles/guides/paypal-here) on the Braintree developer portal. On the PayPal Here side, there are essentially two steps needed to enable the Braintree functionality within the PayPal Here SDK:

1. Authenticate with Braintree
2. Vault a card

The steps are outlined below. Specific code samples are available for reference in the iOS and Android sample apps.

### Authenticating with Braintree [#authenticating-with-braintree]

To authenticate, you go into your Braintree console's Processing Options page and toggle on the option to use PayPal Here. More detailed instructions for that are located in the [Braintree documentation](https://developer.paypal.com/braintree/articles/guides/paypal-here). Once this has been done, the PayPal Here SDK will get the required tokens for your Braintree account for vaulting.

> **Info:** **Note:** As noted in the Braintree documentation, when you link your PayPal and Braintree accounts ensure that you're using the client ID and client secret that is on the REST App that is created on your behalf, by Braintree. You need these credentials to generate the access token for the PayPal Here SDK so that the SDK can find your connected Braintree account.

#### Vault a card [#vault-a-card]

To use the Braintree vault, there are some additional options you must set on the `PPRetailTransactionBeginOptions` that is used to create a payment. Keep in mind that these are in addition to any other options that are set for your normal SDK integration. There are three options that must be set:

| Payment option    | Type   | Description                                                                                                                                                                                                                                                           |
| ----------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vaultProvider`   | Enum   | The only option available for this currently is `braintree`.                                                                                                                                                                                                          |
| `vaultCustomerId` | String | This represents the [Braintree customer](https://developer.paypal.com/braintree/docs/reference/response/customer/ruby#id) to vault the card against.                                                                                                                  |
| `vaultType`       | Enum   | `payOnly` – Set this option to take the payment only without vaulting anything.<br />`vaultOnly` – Set this option to vault the card only and not take any payment.<br />`payAndVault` – Set this option to take a payment and vault the card all in one interaction. |

Similarly to the iOS and Android SDKs, you must also set the vault provider, customer ID, and type when wanting to vault a payment method. These are added to your payment configuration and examples for pay and vault or vault only can be found in the samples below.

> **Info:** **Note:** To associate a transaction with a particular customer ID without vaulting the payment method, simply provide the `vaultCustomerId` and set the `vaultType` to `payOnly`.

When vaulting a card, register a separate `vaultCompletedHandler` to interrogate the result for success or failure:

Choose your platform:

#### iOS (Swift)

```text lineNumbers
// Set the vault completion handler before calling beginPayment to receive vault record with id
tc.setVaultCompletedHandler({ (error, vaultRecord) in
    if error != nil {
        // handle error accordingly
    } else {
        // handle success with vaultRecord
    }
})
```

#### iOS (Obj-C)

```text lineNumbers
// Set the vault completion handler before calling beginPayment to receive vault record with id
[tc setVaultCompletedHandler:^(PPRetailError *error, PPRetailVaultRecord *vaultRecord) {
    if (error) {
        // handle error accordingly
    } else {
        // handle success with vaultRecord
    }
}];
```

#### Android

```text lineNumbers
// Set the vault completion handler before calling beginPayment to receive vault record with id
currentTransaction.setVaultCompletedHandler(new TransactionContext.VaultCompletedCallback()
{
  @Override
  public void vaultCompleted(RetailSDKException error, VaultRecord record)
  {
      // handle the error or success accordingly
      ChargeActivity.this.vaultCompleted(error, record);
  }
});
```

#### Javascript

```text lineNumbers
//There are success and failure events for vaulting that you can subscribe to with your payment configuration
payment_config.subscribe
  .onVaultSuccess(function (vaultRecord) {
    // vaultRecord contains the vaultId and card object
  })
  .onVaultFailure(function (err) {
    // handle vault error accordingly
  });
```

It is important to note that when simultaneously accepting a payment and vaulting the card, an error when vaulting does not necessarily mean there was an error accepting payment. If there is an error with the `vaultRecord`, the payment may have succeeded and should be verified separately.

When vaulting a card without a payment, there is an extra helper method `createVaultTransaction` on the `TransactionManager`. This enables you to skip the standard PayPal Here transaction flow and streamline collection of vaulted payment methods:

#### iOS (Swift)

```text lineNumbers
func doVaultOnly() {
  PayPalRetailSDK.transactionManager()?.createVaultTransaction(acceptVaultOnlyTransaction(error:tc:))
}
func acceptVaultOnlyTransaction(error: PPRetailError?, tc: PPRetailTransactionContext?) {
  self.transactionContext = tc
  // set vaultCompletionHandler, transaction options, and beginPayment as outlined above
}
```

#### iOS (Obj-C)

```text lineNumbers
[[PayPalRetailSDK transactionManager] createVaultTransaction:^(PPRetailError *error, PPRetailTransactionContext *context) {
    if (error) {
        vaultModel.errorMsg = [self vaultTransactionErrorMsg];
        completion(vaultModel);
        return;
    }
    // MARK: 2. Accept transaction by setting completion handler and calling beginPayment(options)
    [self acceptVaultTransaction:context options:vaultModel.options completion:^(PPRetailVaultRecord *vaultRecord, ErrorMsg *errorMsg) {
        if (errorMsg) {
            // handle error accordingly
            return;
        }
        // Successful vault!
    }];
}];
+ (void)acceptVaultTransaction:(PPRetailTransactionContext *)tc  options:(PPRetailTransactionBeginOptions *)options completion:(void (^)(PPRetailVaultRecord *vaultRecord, ErrorMsg *errorMsg))completion { // set vaultCompletionHandler, transaction options, and beginPayment as outlined above
}
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().createVaultTransaction(new TransactionManager.TransactionCallback()
{
    @Override
    public void transaction(RetailSDKException e, TransactionContext context)
    {
        if (e != null) {
            // handle error accordingly
        }else{
            currentTransaction = context;
        }
    }
});
// set vaultCompletedHandler, transaction options, and beginPayment as outlined above
```

#### Javascript

```text lineNumbers
// For PAYANDVAULT you'll set the appropriate vault options on the payment configuration and then call either .sale() or .authorize() as normal.
payment_config.vault(pphwebsdk.Types.VaultProvider.BRAINTREE)
  .customerId(customerId)
  .type(pphwebsdk.Types.VaultType.PAYANDVAULT)
  .done();
var payment = pphwebsdk.Payment.create(identity, payment_config);
payment.for(order).as(pphwebsdk.Types.PaymentMethod.CARD).sale();
// For VAULTONLY you will set the appropriate vault options on the payment configuration and then call the .vault() method of your payment
payment_config.vault(pphwebsdk.Types.VaultProvider.BRAINTREE)
  .customerId(customerId)
  .type(pphwebsdk.Types.VaultType.VAULTONLY)
  .done();
var payment = pphwebsdk.Payment.create(identity, payment_config);
payment.for(order).as(pphwebsdk.Types.PaymentMethod.CARD).vault();
```

> **Info:** **Note:** Keep in mind the following vaulting restrictions:
>
> 1. **Offline or manual mode** — Currently vaulting cannot be used with offline mode or manual entry.
> 2. **Sandbox** — The way vaulting works in the sandbox is that there is a predetermined set of card numbers that are set on the Braintree sandbox. Since these are the only numbers that can be vaulted, the PayPal Here SDK will automatically convert whichever card you process to one of those numbers. Therefore, the card you use with the card reader to vault may not be the same card number that you see in the Braintree vault.
> 3. **Payment method storage** — The vault does not support NFC wallet payment methods (Apple Pay, Google Pay, Samsung Pay, etc.) at this time.

## Capturing authorizations [#capturing-authorizations]

During the payment, if you set the `isAuthCapture` payment option to `true` (Android & iOS) or use the `.authorize()` command (Web) to run the payment as an authorization, then you may need the option to capture the funds from within the integrating app. Alternatively, if you plan to capture from the server-side, you can use the [Capture API](/api/deprecated/payments/v1/authorization-capture).

In order to capture an authorization in-app, you need the following parameters:

* authorization ID
* PayPal invoice ID
* total amount
* a gratuity amount
* currency

The total amount is the total amount that's needed to be captured, including any gratuity. The gratuity amount is separate for reporting purposes. Therefore, if you have an authorization for `$10`, and want to capture `$12` total after a tip, then you'd submit the total amount as `$12` and the gratuity amount as `$2` accordingly. Also note that the capture currency has to match the currency of the original authorization.

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().captureAuthorization(authId, invoiceId: invoice.payPalId, totalAmount: amountToCapture, gratuityAmount: 0, currency: invoice.currency) { (error, captureId) in
    // code to handle success or failure
    // if error, check error and handle accordingly
    // if success, record the capture ID for future reference
}
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK captureAuthorizedTransaction:authorizationId invoiceId:paypalInvId totalAmount:amountToCapture gratuityAmount:0 currency:invoiceCurrency completionHandler:^(PPRetailError *error, PPRetailCaptureResponse *response) {
    if (error == NULL) {
    // code to handle success or failure
    // if error, check error and handle accordingly
    // if success, record the capture ID for future reference
}];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().captureAuthorization(authId, transactionRecord.getInvoiceId(), amountToCapture, gratuityAmount, invoice.getCurrency, new TransactionManager.CaptureAuthorizedTransactionCallback()
{
  @Override
  public void captureAuthorizedTransaction(RetailSDKException error, String captureId)
  {
    // code to handle success or failure
    // if error, check error and handle accordingly
    // if success, record the capture ID for future reference
  }
});
```

#### Javascript

```text lineNumbers
var captureTxn = pphwebsdk.AuthCapture.create(identity, payment_config);
captureTxn.captureTransaction(authTxnId)
  .orderId(ppInvoiceId)
  .currency("USD")
  .totalAmount(totalAmount)
  .gratuityAmount(0)
  .capture();
```

## Voiding authorizations [#voiding-authorizations]

During the payment, if you set the `isAuthCapture` payment option to `true` to run the payment as an authorization, then you may need the option to void the auth from within the integrating App. Alternatively, if you are voiding from the server-side, you can use the [Void API](/api/deprecated/payments/v1/authorization-void).

#### iOS (Swift)

```text lineNumbers
PayPalRetailSDK.transactionManager().voidAuthorization(authTransactionNumber) { (error) in
    // code to handle success or failure
    // if error, check error and handle accordingly
}
```

#### iOS (Obj-C)

```text lineNumbers
[PayPalRetailSDK voidAuthorization:authorizationId completionHandler:^(PPRetailError *error) {
    // code to handle success or failure
    // if error, check error and handle accordingly
}];
```

#### Android

```text lineNumbers
RetailSDK.getTransactionManager().voidAuthorization(authId, new TransactionManager.VoidAuthorizationCallback()
{
  @Override
  public void voidAuthorization(RetailSDKException error)
  {
    // code to handle success or failure
    // if error, check error and handle accordingly
  }
});
```

#### Javascript

```text lineNumbers
var voidTxn = pphwebsdk.AuthCapture.create(identity, payment_config);
voidTxn.voidTransaction(txnId);
```

## Manual entry [#manual-entry]

You can also enter a customer's credit card manually if needed. While following the traditional route for [processing a payment](/archive/paypal-here/sdk-dev/native#payment), simply build a `PPRetailManuallyEnteredCard` instance and then add two extra options to your `PPRetailTransactionBeginOptions` prior to calling `beginPayment(options)`. Those options are `paymentType` and `manualCard`.

For the Web SDK, you need to add the card object to your payment configuration similar to vaulting and then utilize the `KEYIN` payment method with your payment call.

#### iOS (Swift)

```text lineNumbers
let cardInfo = PPRetailManuallyEnteredCard.init()
cardInfo.setCardNumber("1234123412341234")
cardInfo.setExpiration("MMYYYY")
cardInfo.setCVV("123")
cardInfo.setPostalCode("12345")
// Be sure to add the following options before calling tc.beginPayment(options)
options.paymentType = PPRetailTransactionBeginOptionsPaymentTypes.typeskeyIn
options.manualCard = cardInfo
```

#### iOS (Obj-C)

```text lineNumbers
PPRetailManuallyEnteredCard *cardInfo = [[PPRetailManuallyEnteredCard alloc] init];
cardInfo.setCardNumber = @"1234123412341234";
cardInfo.setExpiration = @"MMYYYY";
cardInfo.setCVV = @"123";
cardInfo.setPostalCode = @"12345";
// Be sure to add the following options before calling [tc beginPayment:options]
options.paymentType = PPRetailTransactionBeginOptionsPaymentTypeskeyIn
options.manualCard = cardInfo
```

#### Android

```text lineNumbers
ManuallyEnteredCard card = new ManuallyEnteredCard();
card.setCardNumber("1234123412341234");
card.setCVV("123");
card.setExpiration("MMYYYY");
card.setPostalCode("12345");
// Be sure to add the following options before calling currentTransaction.beginPayment(options)
options.setPaymentType(TransactionBeginOptionsPaymentTypes.keyIn);
options.setManualCard(card);
```

#### Javascript

```text lineNumbers
payment_config.card()
  .number("cardNumber")
  .cvv("cardCVV")
  .expiry("MMYYYY")
  .done();
var keyPayment = pphwebsdk.Payment.create(identity, payment_config);
keyPayment.for(order).as(pphwebsdk.Types.PaymentMethod.KEYIN).sale();
```

[Next: Going live](/archive/paypal-here/sdk-dev/going-live)
