
Nel corso dello sviluppo di un e-commerce per un nostro cliente, abbiamo dovuto affrontare la gestione dei pagamenti, sia in termini di tipologie che di sicurezza, per cui abbiamo dovuto scegliere se implementare in maniera diversificata le diverse modalità (carta di credito/debito, PayPal, etc.), o optare per un servizio di terze parti che gestisse in maniera integrata il processo di checkout.
Cercando sul web abbiamo trovato Braintree, un servizio fornito da PayPal che offre scalabilità, velocità e sicurezza nei pagamenti, un minore numero di click prima del checkout, ottimizzazione per siti web e mobile e che supporta, come metodi di pagamento, carte di credito e debito, PayPal, Apple Pay, Google Pay e tutti i principali digital wallets.
Braintree mette a disposizione un ambiente sandbox, su cui possiamo iscriverci e ottenere le credenziali per utilizzare gratuitamente i servizi di test, come possiamo vedere nella immagine seguente.

Consultando la documentazione per sviluppatori, possiamo visualizzare il diagramma di flusso di Braintree:

Vengono messe a disposizione due tipologie di SDK:
- SDK Client: per le applicazioni frontend che supporta i linguaggi JavaScript, Android e iOS e che consente di recuperare le informazioni di pagamento dagli utenti;
- SDK Server: per il backend, disponibile per .NET, Java, Ruby, Node.js, PHP e Python e che consente di effettuare azioni sulle informazioni di pagamento raccolte.
Per utilizzare il client SDK è naturalmente obbligatorio un processo di autorizzazione, di cui la modalità più flessibile è sicuramente il client token. Inoltre, entrambi gli SDK utilizzano i nonces per fare riferimento a informazioni di pagamento, in modo da evitare i replay-attack.
Sono disponibili, inoltre, esempi per tutti i linguaggi di programmazione.
Nella nostra soluzione, l’applicazione frontend è una web app Angular 6, su cui abbiamo utilizzato il modulo ngx-braintree, un wrapper del JavaScript Client SDK, che abbiamo integrato nel progetto. Il modulo lato client crea una drop-ui tramite cui l’utente può inserire i propri dati:

Il modulo è utilizzato nel component principale dell’app, AppComponent, che si presenta così:
@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;
}
}
dove abbiamo definito chargeAmount come importo della transazione (inserito staticamente per comodità), e clientTokenURL e createPurchaseURL, rispettivamente URL per la richiesta del token e per la creazione della transazione
Il codice html del componente è invece:
:/p>
<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>
in cui viene dichiarato il modulo ngx-braintree e definiti gli attributi per un suo corretto funzionamento.
Nel dettaglio, ci sono:
- clientTokenURL: URL del servizio per la richiesta del token relativo alla transazione (obbligatorio);
- createPurchaseURL:URL del servizio per la creazione della transazione (obbligatorio);
- chargeAmount:ammontare della transazione (obbligatorio);
- paymentStatus: evento scatenato quando il processo di pagamento si è concluso (obbligatorio);
- buttonText:titolo del bottone di pagamento;
- allowChoose: definisce se l’utente può scegliere una diversa modalità di pagamento dopo aver inserito già i dati;
- showCardholderName: consente di visualizzare una casella di testo in cui inserire il nome del possessore della carta;
- enablePaypalCheckout: consente di pagare con l’account PayPal (per abilitarlo è necessario collegare l’account Braintree a quello PayPal);
- currency: definisce la moneta con cui viene effettuato il pagamento (la lista delle monete disponibili è presente qui).
Per il progetto backend abbiamo bisogno di un controller Web Api, BraintreeController, con due metodi API:
[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);
}
e
[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 : restituisce un token che identifica la transazione e a CreatePurchase vengono passate le informazioni del token e dell’importo totale.
I DTO utilizzati per lo scambio di informazioni, ClientTokenResponse e NonceRequest, sono definiti di seguito:
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;
}
}
Per utilità, abbiamo creato la classe BraintreeConfiguration che recupera le impostazioni dal file di configurazione e genera il gateway per accedere si servizi di Braintree. BraintreeConfiguration si presenta così:
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;
}
}
I parametri di configurazione vengono recuperati dall’appsettings.json:
{
"AppSettings": {
"BraintreeEnvironment": "******",
"BraintreeMerchantId": "******",
"BraintreePublicKey": "******",
"BraintreePrivateKey": "******"
}
}
Eseguendo i due progetti (per eventuali problemi di CORS su GoogleChrome, si consiglia l’utilizzo dell’estensione Allow-Control-Allow-Origin:*), otteniamo quindi una prima schermata in cui visualizziamo una drop-ui in cui scegliere la modalità di pagamento e inserire i nostri dati:

da cui, cliccando su una delle voci della drop-ui, possiamo inserire i dati di pagamento:

Una volta cliccato su Buy, il sistema processa la transazione e, nel nostro caso, ci restituisce una schermata contenente il risultato:

Nel dettaglio, il JSON restituito dall’API CreatePurchase all’applicazione frontend è del seguente tipo:
{
"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"
}
}
Dal pannello di amministrazione di Braintree possiamo quindi visualizzare l’andamento dei nostri affari e una serie di interessanti statistiche.

L’ultimo step, prima del passaggio in produzione, è la registrazione come account business al seguente link, ottenere le nuove credenziali e sostituirle nel appsettings.json.
I costi del servizio business Braintree sono in linea con quelli di PayPal ma, all’occorrenza, è possibile contattare il team di supporto per eventuali tariffe personalizzate in base al volume d’affari.
Il codice citato in questo articolo è disponibile su GitHub al seguente link.
Alla prossima