braintree / braintree_java

Braintree Java library
https://developer.paypal.com/braintree/docs/start/overview
MIT License
158 stars 99 forks source link

RFE: Create OR Use an Existing Customer when making a Transaction.sale() with a nonce #105

Closed jmsjr closed 2 years ago

jmsjr commented 2 years ago

This is not an issue but a Request for Enhancement

General information

Issue description

We have been using Braintree's Java SDK for about two years now successfully for credit card payments.

As a lead developer, I want to reduce the network calls to Braintree from the Java SDK when making a credit card payment that also needs to be vaulted, so that the overall transaction time is reduced.

No issue for credit card payments where the the customer / card details are not be vaulted and no issue for credit card payments using a vaulted payment method token. ( e.g. out-of-scope ).

We capture a payment method nonce from the UI via the Hosted Fields from Braintree Javascript SDK. However, when the customer and card details are to be vaulted, we need an extra call to the SDK's Customer.find() API ( https://developer.paypal.com/braintree/docs/reference/request/customer/find ) prior to making a call to the SDK's Transaction.sale() API.

This is born out by the following :

Thus, we end up something like this ( in psuedo-code ) :

      TransactionRequest request = new TransactionRequest().amount(amount).paymentMethodNonce(nonce).merchantAccountId(mid);
      ...

      try {
        // Check if the customer exists
        gateway.customer().find(customerId);

        // Existing customer found in Braintree. Use its id into the TransactionRequest so that the
        // created payment method token belongs to this existing customer.
        request.customerId(customerId);
      }
      catch (NotFoundException notFoundEx) {
        // Existing customer not found in Braintree. Make sure we ask Braintree to create a new customer for us.
        // This will create a customer with just an ID.
        request.customer().id(customerId).done();
      }
      request.options().storeInVault(true);
      ...
      gateway.transaction().sale(request);

This call to Customer.find() adds an extra 250ms ( sometimes 1 to 2 seconds ) to the overall experience for the user. Add in the time for Transaction.sale() which on average is about 3 seconds, the overall time / round trip is about 3.5 to 5 seconds when a credit card payment is requested and the nonce is to be vaulted as a payment method token within a new or existing customer.

The RFE is to request that, given a customerId ( using a different method or something else ? ), that the Braintree gateway itself ( not the Braintree Java SDK as that would simply mean that the SDK will still do the API call to the Braintree gateway ), would then :

... and then vault the nonce into a payment method token in that new or existing customer.

The psuedo-code would then end-up something like this :

TransactionRequest request = new TransactionRequest().amount(amount).paymentMethodNonce(nonce).merchantAccountId(mid);
      ...
     request.options().createOrUseCustomerId(customerId);
     request.options().storeInVault(true);
     ...
     gateway.transaction().sale(request);     
DPoplin commented 2 years ago

Hey @jmsjr thanks for reaching out. We're forwarding this onto our engineering team for review.

In the meantime, you may be able to accomplish this by breaking out your payment flow a bit differently. I would recommend checking for customer records ahead of your clientToken().generate() request. This will allow you to pass the customer ID (if found) during the client token generation as well as the transaction().sale() call and therefore you will be able to decrease the response times during the transaction itself.