facebook

Blog

Stay updated

How to integrate secure payments management in our store with main providers, thanks to Braintree
Braintree for payments in our store
Thursday, May 23, 2019

As we were developing e-commerce for one of our clients, we needed to face the payments’ management, both in terms of which type choose and of security problems. We need then to choose if it would be better to implement different payment methods (credit/debit cards, PayPal and so on) with a diversified approach, or to opt for a third-party service, which manages the checkout process with an integrated way.

On the web, we found Braintree, a service provided by PayPal, that offers scalability, fastness, security in payments transaction, a lesser number of clicks to arrive at checkout, web and mobile optimization. As payment methods, it offers credit and debit cards, PayPal, Apple Pay, Google Pay and the main digital wallets.

Braintree provides a sandbox, environment, on which we can register to obtain free credentials and test services, as we can see in the following image:

Looking at the documentation for developers, we can display the Braintree’s flow chart:

Two types of SDK are available:

  • SDK Client: dedicated to frontend applications, it supports JavaScript, Android and IOS languages. It permits to recover users’ payment information;
  • SDK Server:  dedicated to the Backend, available for .NET, Java, Ruby, Node.js, PHP and Python. It permits to make actions on collected payment information.

In order to use the SDK client, it’s required an authorization process, which more flexible modality is surely the client Token. Furthermore, both SDKs use nonces to refer to payment information, in order to avoid replay-attacks.

Examples for all programming languages are available.

In our solution, the frontend application is an Angular6 web app, with which we used the module ngx-braintree, a wrapper of the JavaScript Client SDK, which we integrated into the project. On client side, the module creates a drop-ui, that allows the user to insert his/her data:

The module is used in the app’s main component AppComponent, this is how it presents itself:

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  paymentResponse: any;
  chargeAmount = 11.11;
 
  clientTokenURL =  AppSettings.GET_CLIENT_TOKEN_URL;
  createPurchaseURL =  AppSettings.CREATE_PURCHASE_URL;
 
  onDropinLoaded(event) {
    console.log("dropin loaded...");
  }
 
  onPaymentStatus(response): void {
    this.paymentResponse = response;
  }
}

Here we defined chargeAmount as the amount of the transaction (statistically insert, for convenience), clientTokenURL as URL for the token request and createPurchaseURL, as URL for the transaction creation

The HTML code of the component is instead:

<ngx-braintree
    [clientTokenURL]="clientTokenURL" 
    [createPurchaseURL]="createPurchaseURL" 
    [chargeAmount]="chargeAmount"
    (paymentStatus)="onPaymentStatus($event)"
    [showCardholderName]="true"
    (dropinLoaded)="onDropinLoaded($event)"
    [buttonText]="'Buy'"
    [enablePaypalCheckout] = "true"
    [currency]="'EUR'"
    [allowChoose]="true">
</ngx-braintree>

Where the module ngx-braintree is declared and the attributes for its correct functioning defined.

In detail, there are:

  • clientTokenURL: URL of the service for the token request related to the transaction (mandatory);
  • createPurchaseURL: : URL of the service for transaction creation (mandatory);
  • chargeAmount: transaction amount (mandatory);
  • paymentStatus: event that takes place when the payment process has been finished (mandatory);
  • buttonText: title of the payment button;
  • allowChoose: it defines if the user can choose a different payment method after she/he has already filled in the data;
  • showCardholderName: it allows to display a text box, where it is possible to fill in the name of the cardholder;
  • enablePaypalCheckout: it permits to pay with the PayPal account (to enable it, it is needed to link the Braintree account to the PayPal account);
  • currency: it defines the currency used to pay (the list of available currency is available here).

For a backend project, we need a Web Api controller, BraintreeController, with two API methods:

[HttpGet("getclienttoken")]
public IActionResult GetClientToken()
{
    //read Braintree configuration from appsettings.json
    var gateway = braintreeConfiguration.GetGateway();
    var clientToken = gateway.ClientToken.Generate();
    var clientTokenResponse = new ClientTokenResponse(clientToken);
    return new JsonResult(clientTokenResponse);
}

and

[HttpPost("createpurchase")]
public IActionResult CreatePurchase([FromBody]NonceRequest nonceRequest)
{
    //read Braintree configuration from appsettings.json
    var gateway = braintreeConfiguration.GetGateway();
 
    var request = new TransactionRequest
    {
        Amount = nonceRequest.ChargeAmount,
        PaymentMethodNonce = nonceRequest.Nonce,
        Options = new TransactionOptionsRequest
        {
            SubmitForSettlement = true
        }
    };
 
    Result<Transaction> result = gateway.Transaction.Sale(request);
    return new JsonResult(result);
}

GetClientToken: it returns a token, which identifies the transaction and to CreatePurchase total amount and token information would be passed.

DTO used for information exchange, ClientTokenResponse and NonceRequest, are defined below:

public class ClientTokenResponse
{
    public string Token { get; set; }
 
    public ClientTokenResponse(string token)
    {
        Token = token;
    }
}
public class NonceRequest
{
    public string Nonce { get; set; }
    public decimal ChargeAmount { get; set; }
 
    public NonceRequest(string nonce)
    {
        Nonce = nonce;
        ChargeAmount = ChargeAmount;
    }
}

For utility, we create the class BraintreeConfiguration which retrieves settings from the configuration file and generate the gateway to access to Braintree services BraintreeConfiguration looks like this:

public class BraintreeConfiguration : IBraintreeConfiguration
{
    public string Environment { get; set; }
    public string MerchantId { get; set; }
    public string PublicKey { get; set; }
    public string PrivateKey { get; set; }
    private IBraintreeGateway BraintreeGateway { get; set; }
    private readonly IConfiguration _config;
 
    public BraintreeConfiguration(IConfiguration config)
    {
        _config = config;
    }
 
    public IBraintreeGateway CreateGateway()
    {
        Environment = GetConfigurationSetting("BraintreeEnvironment");
        MerchantId = GetConfigurationSetting("BraintreeMerchantId");
        PublicKey = GetConfigurationSetting("BraintreePublicKey");
        PrivateKey = GetConfigurationSetting("BraintreePrivateKey");
        return new BraintreeGateway(Environment, MerchantId, PublicKey, PrivateKey);
    }
 
    public string GetConfigurationSetting(string setting)
    {
        return _config.GetSection("AppSettings")[setting];
    }
 
    public IBraintreeGateway GetGateway()
    {
        if (BraintreeGateway == null)
        {
            BraintreeGateway = CreateGateway();
        }
        return BraintreeGateway;
    }
}

Configuration parameters would be recovered from the appsettings.json:

{
  "AppSettings": {
    "BraintreeEnvironment": "******",
    "BraintreeMerchantId": "******",
    "BraintreePublicKey": "******",
    "BraintreePrivateKey": "******"
  }
}

Running both projects (for any kind of CORS problem on Google Chrome, it’s advised to use the extension Allow-Control-Allow-Origin:*), we obtain a first screen where we display drop-ui, where we can choose the payment method and fill in our data:

from which, with a click on one of the drop-ui entries, we can fill in the payment information:

Once clicked on Buy, the system processes the transaction and, in our situation, it returns a screen with the result:

In the detailed view, the JSON returned from the API CreatePurchase to the frontend application, is the type below:

{
  "target": {
    "id": "********",
    "addOns": [],
    "amount": 11.11,
    //[...]
    "createdAt": "2019-05-21T05:55:36Z",
    "creditCard": {
      "bin": "411111",
      "cardholderName": "FirstName LastName",
      "cardType": {},
      "customerLocation": {},
      "lastFour": "1111",
      //[...]
      "expirationMonth": "05",
      "expirationYear": "2025",
      "imageUrl": "https://assets.braintreegateway.com/payment_method_logo/visa.png?environment=sandbox",
      "verification": null,
      "expirationDate": "05/2025",
      "maskedNumber": "411111******1111"
    },
    "currencyIsoCode": "EUR",
    "cvvResponseCode": "I",
    "gatewayRejectionReason": {},
    "merchantAccountId": "prova",
    "processorAuthorizationCode": "******",
    "processorResponseType": {},
    "processorResponseCode": "1000",
    "processorResponseText": "Approved",
    "shippingAddress": {
        //[...]
    },
    "status": {},
    "statusHistory": [
      {
        "amount": 11.11,
        "status": {},
        "timestamp": "2019-05-21T05:55:36Z",
        "source": {},
        "user": ""
      },
    ],
    //[...]
    },
    "type": {},
    "updatedAt": "2019-05-21T05:55:37Z",
    //[...]
    "networkTransactionId": "********",
    "authorizationExpiresAt": "2019-05-28T05:55:36Z"
  }
}

From the Braintree administration panel, we can then display the evolution of our business and some interesting statistics.

The last step, before the passage to production, is to record as a business account to the following link, obtain new credentials and substitute them in the appsettings.json.

The costs of Braintree business service are similar to those of PayPal but, if needed, it’s possible to contact the support team to obtain personalized rates, based on your turnover.

The code mentioned in this article is available on GitHub, to below link.

See you next