Braintree Quick Start
Broadleaf Commerces offers an out-of-the-box Braintree solution that requires little configuration and is easily set up.
The quick start solution implements the [[v.zero (javascript + java) SDK | https://developers.braintreepayments.com/javascript+java/sdk/overview/how-it-works]] model offered by the Braintree API.
This implementation should be useful for those with a simple checkout flow.
You must have completed the Braintree Environment Setup before continuing
Adding Braintree Checkout Support
These instructions assume integration with the default Heat Clinic Demo Site provided with the framework.
- Make sure the Braintree JS library(at the time of writing this was the latest v3 js lib) is loaded on your checkout page:
<script src="https://js.braintreegateway.com/web/dropin/1.22.1/js/dropin.min.js"></script>
- Create a Payment Form on your checkout page without any fields (if using the Drop-in UI, it will be created for you by the Braintree JS lib)
<blc:form th:action="@{/checkout/braintree/complete}" method="POST">
<div id="payment-form-dropin"></div>
<input type="hidden" id="nonce" name="payment_method_nonce" />
<input type="submit" value="Complete Order" />
</blc:form>
- Insert the following Javascript into your checkout html in order to generate the client token to retrieve the payment nonce: (See https://developers.braintreepayments.com/guides/drop-in/setup-and-integration/javascript/v3 for more options)
<script type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
$( document ).ready(function() {
braintree.dropin.create({
authorization: /*[[${#braintree.generateClientToken()}]]*/null,
container: '#payment-form-dropin'
}, function (createErr, instance) {
//check for createErr if it has some errors, show some message, handle error etc
//if no errors, add some button click or form submit listener and
var form = document.querySelector('#YOUR_FORM_ID');
form.addEventListener('submit', function (event) {
event.preventDefault();
instance.requestPaymentMethod(function (err, payload) {
if (err) {
console.log('Error', err);
return;
}
// Add the nonce to the form and submit
document.querySelector('#nonce').value = payload.nonce;
form.submit();
});
});
});
});
/*]]>*/
</script>
- Create a Spring MVC Controller to handle the form you created above in order to process the payment nonce and confirm the transaction. It would look something like this:
@Controller
public class BraintreeCheckoutController extends BroadleafCheckoutController {
@Resource(name = "blAddressService")
protected AddressService addressService;
@RequestMapping(value = "/checkout/braintree/complete")
public String completeBraintreeCheckout(Model model, HttpServletRequest request, RedirectAttributes redirectAttributes, @PathVariable Map<String, String> pathVars) throws PaymentException, PricingException {
//Get Cart
Order cart = CartState.getCart();
//Get Payment Nonce From Request
String nonce = request.getParameter("payment_method_nonce");
//Create a new PAYMENT_NONCE Order Payment
OrderPayment paymentNonce = orderPaymentService.create();
paymentNonce.setType(BraintreePaymentType.PAYMENT_NONCE);
paymentNonce.setPaymentGatewayType(BraintreePaymentGatewayType.BRAINTREE);
paymentNonce.setAmount(cart.getTotalAfterAppliedPayments());
paymentNonce.setOrder(cart);
//Populate Billing Address per UI requirements
//For this example, we'll copy the address from the temporary Credit Card's Billing address and archive the payment,
// (since Heat Clinic's checkout template saves and validates the address in a previous section).
OrderPayment tempPayment = null;
for (OrderPayment payment : cart.getPayments()) {
if (PaymentGatewayType.TEMPORARY.equals(payment.getGatewayType()) &&
PaymentType.CREDIT_CARD.equals(payment.getType())) {
tempPayment = payment;
break;
}
}
if (tempPayment != null){
paymentNonce.setBillingAddress(addressService.copyAddress(tempPayment.getBillingAddress()));
orderService.removePaymentFromOrder(cart, tempPayment);
}
// Create the UNCONFIRMED transaction for the payment
PaymentTransaction transaction = orderPaymentService.createTransaction();
transaction.setAmount(cart.getTotalAfterAppliedPayments());
transaction.setRawResponse("Braintree Payment Nonce");
transaction.setSuccess(true);
transaction.setType(PaymentTransactionType.UNCONFIRMED);
transaction.getAdditionalFields().put(MessageConstants.PAYMENT_NONCE, nonce);
transaction.setOrderPayment(paymentNonce);
paymentNonce.addTransaction(transaction);
orderService.addPaymentToOrder(cart, paymentNonce, null);
orderService.save(cart, true);
return processCompleteCheckoutOrderFinalized(redirectAttributes);
}
}
When processCompleteCheckoutOrderFinalized
is called, the checkout workflow is invoked and the ValidateAndConfirmPaymentActivity
is executed to confirm the payment nonce.
Enable Braintree OMS Support
- The OMS allows for creation of Customer Payment Tokens so that a CSR can change orders and add new payment methods when necessary. To integrate Braintree with Customer Payment capability, you will need to create a Spring MVC Controller to handle the drop-in credit card form to process the payment nonce and save the token as a Customer Payment. It might look something like this:
@Controller
public class BraintreeCustomerPaymentController {
protected static final String CUSTOMER_PAYMENT_ERROR = "CUSTOMER_PAYMENT_OMS_ERROR";
protected static String customerPaymentErrorMessage = "customerPaymentOMSErrorMessage";
@Resource(name = "blBraintreeCustomerService")
protected PaymentGatewayCustomerService braintreeCustomerService;
@Resource(name = "blCustomerPaymentGatewayService")
protected CustomerPaymentGatewayService customerPaymentGatewayService;
@Resource(name = "blBraintreeConfiguration")
protected BraintreeConfiguration configuration;
@Resource
protected SystemPropertiesService propertiesSvc;
@RequestMapping(value = "/payment-method-oms/braintree/add")
public String addBraintreePaymentMethod(Model model, HttpServletRequest request, RedirectAttributes redirectAttributes, @PathVariable Map<String, String> pathVars) throws PaymentException, PricingException {
//Get Cart
Customer customer = CustomerState.getCustomer();
//Get Payment Nonce From Request
String nonce = request.getParameter("payment_method_nonce");
PaymentRequestDTO requestDTO = new PaymentRequestDTO()
.customer()
.customerId(customer.getId().toString())
.firstName(customer.getFirstName())
.lastName(customer.getLastName())
.email(customer.getEmailAddress())
.done()
.additionalField(MessageConstants.PAYMENT_NONCE, nonce);
PaymentResponseDTO responseDTO = braintreeCustomerService.createGatewayCustomer(requestDTO);
if (responseDTO.isSuccessful()) {
// Populate BillTo Address per business requirements...
// For demonstration purposes, we'll just add an address manually.
// In most cases you can POST it along with the form and get it off the request.
responseDTO.billTo()
.addressFullName("Bill Braintree")
.addressLine1("123 Broadleaf Rd")
.addressCityLocality("Austin")
.addressStateRegion("TX")
.addressCountryCode("US")
.addressPostalCode("78759");
customerPaymentGatewayService.createCustomerPaymentFromResponseDTO(responseDTO, configuration);
return OMSCsrToolsController.REDIRECT_OMS_CHECKOUT;
}
redirectAttributes.addAttribute(CUSTOMER_PAYMENT_ERROR, customerPaymentErrorMessage);
return getOMSCustomerPaymentRedirectUrl();
}
public String getOMSCustomerPaymentRedirectUrl() {
String redirect = propertiesSvc.resolveSystemProperty("oms.changeOrder.payment.gateway.customerPayment.redirectUrl");
return BLCRequestUtils.getRequestedServerPrefix() + redirect;
}
}
Done!
At this point, all the configuration should be complete and you are now ready to test your integration with Braintree. Add something to your cart and proceed with checkout.