Azure Functions: Build an ecommerce processor using Braintree's API
- Published on
- Reading time
- Authors
- Name
- Simon Waight
- Mastodon
- @simonwaight
In this blog I am continuing with my series covering useful scenarios for using Azure Functions - today I'm going to cover how you can process payments by using Functions with Braintree's payment gateway services.
I'm not going to go into the details of setting up and configuring your Braintree account, but what I will say is the model you should be applying to make this scenario work is one where you Function will play the Server role as documented in Braintree's configuration guide.
My sample below is pretty basic, and for the Function to be truly useful (and secure) to use you will need to consider a few things:
- Calculate total monetary amount to charge elsewhere and pass to the Function as an argument (my preference is via a message on a Service Bus Queue or Topic). Don't do the calculation here - make the Function do precisely one thing - post a payment to Braintree and handle the response.
- The payment method token (or nonce) should also come from upstream from an end-user's authorisation to pay. This is at the core of how Braintree securely processes payments and you should understand it by reading the Braintree documentation.
- Don't, whatever you do, put your Braintree configuration details inline as plain-text. In all environments I always use Azure Key Vault for these sorts of details, coupled with my Key Vault client code*.
Note: I did get contacted by someone who advised that heavy workloads resulting in lots of calls to Key Vault will most likely result in port exhaustion on your Key Vault and your calling code receiving errors. You should consider this in your design - it's not something I have had to work around just yet and I do have some ideas to solve in a relatively secure fashion which I hope to blog about in future.
Now we have the fundamentals let's get into it!
Firstly we need to import the Braintree nuget package that brings all the goodness we'll need to create and send requests and process responses from them. Add the following entry to your project.json file and when you save it the package will be restored.
{
"frameworks": {
"net46": {
"dependencies": {
"Braintree": "3.4.0",
}
}
}
}
Once we've done this we now have the power of the API at our fingertips and can craft requests as required.
In my simplified example below I am going to process a Transaction Sale for $10 (the currency will depend on your merchant account setup) and use a hardcoded Braintree Customer Identity that maps to an existing Customer entity that I've previously created in the Braintree Vault associated with my trial Merchant account.
This is a fairly convoluted example, as in reality you'd pass the paymentMethodToken to the Function as part of any data supplied and, as I noted above, you'd not leave your Merchant details laying around in plain-text (would you now?)
using System;
using Braintree;
public static void Run(string input, TraceWriter log)
{
var braintreeEnvironment = "sandbox"; // alternative = production
// These three config items should be stored in Azure KeyVault
var merchantId = "5xxxxxxxxxxxxxx7";
var merchPubKey = "tyyyyyyyyyyyyyyh";
var merchPrivateKey = "4oooooooooooooooooooooooooooooo4";
// This is a payment nonce or customer ID as supplied by
// the Braintree.
var paymentMethodToken = "9aaaaa";
var gateway = new BraintreeGateway(braintreeEnvironment,
merchantId,
merchPubKey,
merchPrivateKey);
// Note submitting same amount for same payment token
// within 1 minute will result in rejected payment
// unless you turn off duplicate detection in Braintree
var btRequest = new TransactionRequest()
{
Amount = 10M,
PaymentMethodToken = paymentMethodToken,
Options = new TransactionOptionsRequest
{
SubmitForSettlement = true
}
};
Transaction transaction = null;
var btResponse = gateway.Transaction.Sale(btRequest);
if (btResponse.IsSuccess())
{
// SUCCESS
transaction = btResponse.Target;
log.Info($"Success!");
log.Info($"Auth code: {transaction.ProcessorAuthorizationCode}");
// Do some processing, save somewhere
}
else
{
// FAILURE
log.Info($"Failure!");
log.Info(btResponse.Message);
// btResponse at this point has no 'Target' property
transaction = btResponse.Transaction;
// Do some processing, save somewhere
}
}
That folks, is pretty much all there is to it! Bake this into a set of Microservice Functions and pass in the total you wish to settle and off you go.
The Braintree SDK has a bunch of other service endpoints you can utilise to perform actions against other objects like Customers or creating recurring subscriptions so don't limit your use solely to paying for things on checkout.
Happy days 🙂