Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Pagamenti con Braintree e PayPal

E-commerce con PHP. Scopriamo come implementare i pagamenti con Braintree usando come case-study PayPal.
E-commerce con PHP. Scopriamo come implementare i pagamenti con Braintree usando come case-study PayPal.
Link copiato negli appunti

In quests capitolo affronteremo la procedura di pagamento utilizzando come gateway Braintree. Per prima cosa registriamoci sulla sandbox di Braintree per ottenere le nostre credenziali di accesso alle API, che sono:

  1. Environment
  2. Merchant ID
  3. Public key
  4. Private key

Quindi scarichiamo la libreria PHP ufficiale e la richiamiamo nel nostro controller principale.

use Braintree\Gateway as Gateway;
use Braintree\Transaction as Transaction;

Il nostro endpoint per il pagamento accetta due metodi HTTP, GET e POST. Il primo serve a mostrare il form di pagamento con il token generato dalla libreria di Braintree, il secondo elabora la transazione.

public function payment() {
      if($_SERVER['REQUEST_METHOD'] === 'GET') {
          if (Session::hasItem('cart')) {
              $sess_cart = Session::getItem('cart', true);
              $is_empty_cart = (count($sess_cart['items']) == 0);
              if ($is_empty_cart) {
                  Router::redirect(SITE_URL);
              } else {
                      $bt_gateway = new Gateway([
                          'environment' => BT_ENVIRONMENT,
                          'merchantId' => BT_MERCHANT_ID,
                          'publicKey' => BT_PUBLIC_KEY,
                          'privateKey' => BT_PRIVATE_KEY
                      ]);
                      $bt_token = $bt_gateway->ClientToken()->generate();
                      $cart_total = Templater::total($sess_cart['total']);
                      $items = Templater::cart($sess_cart['items']);
                      $vat = new Vat(VAT_VALUE);
                      $cart_total_taxes = Templater::total($vat->add($sess_cart['total']));
                      $order_id = Session::getItem('order_id');
                      $front_cart = Templater::frontCart();
                      Render::view('payment-bt', [
                          'cart_total' => $cart_total,
                          'items' => $items,
                          'cart_total_taxes' => $cart_total_taxes,
                          'order_id' => $order_id,
                          'front_cart' => $front_cart,
                          'token' => $bt_token,
                          'amount' => Templater::rawTotal($vat->add($sess_cart['total']))
                      ]);
              }
          } else {
              Router::redirect(SITE_URL);
          }
      } else if($_SERVER['REQUEST_METHOD'] === 'POST') {
          //...
          } else {
              Router::redirect(SITE_URL);
          }
      }
    }

L’unica modifica sul totale del carrello è l’aggiunta del valore dell’IVA al totale. Il token generato serve a Braintree per gestire il pagamento tramite l’SDK JavaScript che andremo ad includere nel template della view.

<div class="bt-drop-in-wrapper">
            <div id="bt-dropin"></div>
        </div>
 <p class="mt-5">
            <input type="hidden" id="amount" name="amount" value="<?= $amount; ?>">
            <input id="nonce" name="payment_method_nonce" type="hidden">
            <input type="submit" value="<?= $locale['buttons']['braintree']; ?>" class="btn btn-primary btn-lg">
        </p>
    </form>
<script src="https://js.braintreegateway.com/web/dropin/1.14.0/js/dropin.min.js"></script>
<script>
        var form = document.querySelector('#form-payment');
        var client_token = "<?= $token; ?>";
        braintree.dropin.create({
            authorization: client_token,
            selector: '#bt-dropin',
            paypal: {
                flow: 'vault'
            }
        }, function (createErr, instance) {
            if (createErr) {
                console.log('Create Error', createErr);
                return;
            }
            form.addEventListener('submit', function (event) {
                event.preventDefault();
                instance.requestPaymentMethod(function (err, payload) {
                    if (err) {
                        console.log('Request Payment Method Error', err);
                        return;
                    }
                    document.querySelector('#nonce').value = payload.nonce;
                    form.submit();
                });
            });
        });
    </script>

Il codice JavaScript di Braintree abilita il pulsante di pagamento con PayPal e effettua una preventiva richiesta HTTP con cui imposta il valore del campo nascosto nonce e quindi invia il form. A questo punto, sempre nel nostro endpoint di pagamento, possiamo gestire la richiesta in POST del form.

else if($_SERVER['REQUEST_METHOD'] === 'POST') {
		if (Session::hasItem('cart')) {
              $amount = $_POST['amount'];
              $nonce = $_POST['payment_method_nonce'];
              $bt_gateway = new Gateway([
                  'environment' => BT_ENVIRONMENT,
                  'merchantId' => BT_MERCHANT_ID,
                  'publicKey' => BT_PUBLIC_KEY,
                  'privateKey' => BT_PRIVATE_KEY
              ]);
              $result = $bt_gateway->transaction()->sale([
                  'amount' => $amount,
                  'paymentMethodNonce' => $nonce,
                  'options' => [
                      'submitForSettlement' => true
                  ]
              ]);
              if ($result->success || !is_null($result->transaction)) {
                  $transaction = $result->transaction;
                  Router::redirect(SITE_URL . '/transaction/' . $transaction->id);
              } else {
                  $errors = [];
                  foreach($result->errors->deepAll() as $error) {
                      $errors[$error->code] = $error->message;
                  }
                  Router::redirect(SITE_URL . 'payment/?' . http_build_query($errors));
              }
          } else {
              Router::redirect(SITE_URL);
          }
}

Se la transazione ha avuto successo, effettuiamo il redirect all’endpoint che gestisce la transazione con il suo ID, altrimenti creiamo una querystring con i messaggi di errore restituiti da Braintree sullo stesso URL dell’endpoint del pagamento.

A questo punto dobbiamo solo gestire l’avvenuta transazione nell’endpoint dedicato.

public function transaction($args) {
        $bt_gateway = new Gateway([
            'environment' => BT_ENVIRONMENT,
            'merchantId' => BT_MERCHANT_ID,
            'publicKey' => BT_PUBLIC_KEY,
            'privateKey' => BT_PRIVATE_KEY
        ]);
        $transactionSuccessStatuses = [
            Transaction::AUTHORIZED,
            Transaction::AUTHORIZING,
            Transaction::SETTLED,
            Transaction::SETTLING,
            Transaction::SETTLEMENT_CONFIRMED,
            Transaction::SETTLEMENT_PENDING,
            Transaction::SUBMITTED_FOR_SETTLEMENT
        ];
        $transaction = $bt_gateway->transaction()->find($args[0]);
        if (in_array($transaction->status, $transactionSuccessStatuses)) {
            Router::redirect(SITE_URL . 'thank-you/?st=completed');
        } else {
            Router::redirect(SITE_URL . 'payment/?st=' . $transaction->status);
        }
    }

Se lo status della transazione ha un codice che comprende uno dei valori delle costanti riportate nell’array dei codici di status, vuol dire che la transazione ha avuto successo e quindi possiamo reindirizzare l’utente alla pagina di completamento dell’ordine con il parametro GET st impostato sul valore completed. Viceversa lo reindirizziamo alla pagina di pagamento con un parametro che riporta il codice di stato di Braintree.

Perché PayPal?

Abbiamo scelto PayPal come metodo di pagamento perché ad oggi è il metodo che paradossalmente presenta più problemi con Braintree, pur essendo Braintree un prodotto di PayPal.

Braintree accetta molti altri metodi di pagamento (carta di credito, Apple Pay ecc.) ma PayPal è l’unico metodo in cui le impostazioni dell’account possono compromettere il risultato finale. Ad esempio noi abbiamo inviato il pagamento in saldo diretto, ma se sull’account PayPal è impostata la revisione (review) dei pagamenti, il pagamento non avverrà fino a quando il titolare dell’account non lo approverà.

Ancora: noi stiamo effettuando pagamenti in Euro perché presupponiamo che il titolare dell’account PayPal abbia l’Euro come valuta principale. Tuttavia, se la valuta differisce occorre abilitare la conversione di valuta nelle impostazioni dei pagamenti. In caso contrario il pagamento verrà rifiutato. Tuttavia, sono stati riportati casi in cui le transazioni fallivano anche dopo l’abilitazione della conversione ed il problema è stato risolto solo con l’ausilio del supporto tecnico di PayPal.


Ti consigliamo anche