Customizations

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

Important: Adding cards to the Braintree vault with PayPal Here is currently only supported in the US.

For Braintree-specific functionality of this feature, please visit the PayPal Here integration page 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

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. Once this has been done, the PayPal Here SDK will get the required tokens for your Braintree account for vaulting.

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

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 to vault the card against.
vaultType Enum payOnly – Set this option to take the payment only without vaulting anything.
vaultOnly – Set this option to vault the card only and not take any payment.
payAndVault – Set this option to take a payment and vault the card all in one interaction.

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:

// 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
    }
})
// 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
    }
}];
// 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);
    }
});

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:

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
}
[[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  
}
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

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

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 capture the funds from within the integrating app. Alternatively, if you plan to capture from the server-side, you can use the Capture API.

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.

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

}
[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

}];
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
  }
});

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.

PayPalRetailSDK.transactionManager().voidAuthorization(authTransactionNumber) { (error) in

    // code to handle success or failure
    // if error, check error and handle accordingly

}
[PayPalRetailSDK voidAuthorization:authorizationId completionHandler:^(PPRetailError *error) {

    // code to handle success or failure
    // if error, check error and handle accordingly   

}];
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
  }
});

Manual entry

You can also enter a customer's credit card manually if needed. While following the traditional route for processing a 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.

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
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
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);

Next: Going live