Skip to content

3. HTML Webseite für PaymentRequest mit Lieferadresse

tolja edited this page May 28, 2018 · 15 revisions

Um ein PaymentRequest mit Lieferadresse und Versandoptionen zu bauen, muss der bestehende Code um einige weitere Funktionen und Variablen ergänzt werden. Dazu den Code aus der payment_basic.html in die payment_shippingAddress.html übernehmen.

Unterhalb der <h1>-Überschrift sollte zunächst einmal die Beschreibung der Seite ersetzt werden von

<p> Teil 1: Einfacher Payment Request </p>

auf:

<p> Teil 2: Payment Request mit Lieferadresse </p>

Dann wird der CSS-Code um ein neue Element erweitert. Dies dient später der Anzeige, ob der Expressversand bei dem Produkt gewählt wurde oder nicht. Den folgenden Code in den Bereich zwischen <style> und </style> an eine beliebige Stelle einfügen.

#express_selected {
      display:none;
    }

Nun das <p>-Element in dem div produkt_bought

<p>Sie haben das Beispielprodukt erfolgreich für 49,99 € gekauft.</p>

um folgenden Code erweitern:

<p>Sie haben das Beispielprodukt erfolgreich für 49,99 € <span id="express_selected">(+ 14,99 € Expressversand)</span> gekauft.</p>

Die Variable paymentDetails im Javascript-Code wird jetzt um die Versandoptionen erweitert. Dazu den bisherigen Code

var paymentDetails = {
      total: {label: 'Beispielprodukt', amount: {currency: 'EUR', value: '49.99'}}
      };

durch folgenden Code ersetzen:

var paymentDetails = {
   total: {label: 'Gesamtsumme', amount: {currency: 'EUR', value: '49.99'}},
   displayItems: [
     {
       label: 'Beispielprodukt',
       amount: {currency: 'EUR', value: '49.99'},
     },
     {
        label: 'Standardversand',
        amount: {currency: 'EUR', value: '0.00'},
      }

   ],
   shippingOptions: [
     {
       id: 'standard',
       label: 'Standardversand',
       amount: {currency: 'EUR', value: '0.00'},
       selected: true
     },
     {
       id: 'express',
       label: 'Expressversand',
       amount: {currency: 'EUR', value: '14.99'},
     },
   ],
 };

Wichtig:

  • Die Reihenfolge der Elemente in der Variable paymentDetails diktiert auch ihre spätere Reihenfolge im PaymentRequest-Fenster.

  • Der PaymentRequest selbst führt KEINE Berechnungen durch und die richtige Angabe von Werten ist dem Entwickler überlassen.

  • Die Angabe des Parameters total ist erforderlich. Der Wert von total darf nicht negativ sein.

  • Wenn man ein Element in displayItems einfügt, muss dieses immer label, amount, currency und value besitzen. Die currency muss immer aus drei beliebigen Großbuchstaben bestehen.

Zusätzlich wird die Variable options unterhalb der paymentDetails definiert mit dem Wert von requestShipping auf true, um die Abfrage einer Lieferadresse im PaymentRequest-Fenster zu aktivieren. Übrigens können hier auch weitere Informationen des Benutzers abgefragt werden, etwa der Name, die E-Mail Adresse und die Telefonnummer. Da wir diese Daten bereits bei der Eingabe einer Kreditkarte erhalten, ist die doppelte Eingabe nicht notwendig.

  var options = {
    requestShipping: true
  };

Durch die neu hinzugekommene option muss auch die Instanz des PaymentRequest entsprechend erweitert werden von:

var paymentRequest = new PaymentRequest(supportedPaymentMethods, paymentDetails);

auf:

var paymentRequest = new PaymentRequest(supportedPaymentMethods, paymentDetails, options);

Nun wird direkt unter der Definition der Instanz eines PaymentRequest ein EventListener gesetzt, der überwacht ob sich die ausgewählte Lieferadresse ändert (Event: shippingoptionchange). Sollte dies der Fall sein, wird durch die updateWith()-Funktion ein Promise definiert, der durch die updateDetails()-Funktion aufgelöst wird.

  paymentRequest.addEventListener('shippingoptionchange', function(evt) {
        evt.updateWith(new Promise(function(resolve, reject) {
          updateDetails(paymentDetails, paymentRequest.shippingOption, resolve, reject);
        }));
      });

Direkt darunter wird nun die updateDetails()-Funktion definiert. In dieser wird geschaut, welche Versandoption der Benutzer ausgewählt hat. Bei kostenlosem Standardversand ändert sich der Endbetrag nicht, bei Auswahl von Expressversand müssen 14,99 Euro dazugerechnet werden. Im Fehlerfall soll der Promise abgelehnt werden (Unbekannte Versandart...). Wenn der Benutzer eine neue Versandoption auswählt, muss dies auch im PaymentRequest-Fenster abgebildet werden. Dazu werden die Versandoptionen mit der Funktion details.displayItems.splice(1, 2, selectedShippingOption); ausgetauscht, da immer nur eine Versandoption aktiv sein kann. Am Ende wird der Promise resolved und das Ergebnis an die updateWith()-Funktion übergeben.

function updateDetails(details, shippingOption, resolve, reject) {
  if (shippingOption === 'standard') {
    selectedShippingOption = details.shippingOptions[0];
    otherShippingOption = details.shippingOptions[1];
    details.total.amount.value = '49.99';
  } else if (shippingOption === 'express') {
    selectedShippingOption = details.shippingOptions[1];
    otherShippingOption = details.shippingOptions[0];
    details.total.amount.value = '64.98';

  } else {
    reject('Unbekannte Versandart \'' + shippingOption + '\'');
    return;
  }
  selectedShippingOption.selected = true;
  otherShippingOption.selected = false;
  details.displayItems.splice(1, 2, selectedShippingOption);
  resolve(details);
}

Nun wird noch die showPaymentResponse()-Funktion aktualisiert, um im Fall der ausgewählten Express-Versandoption den zusätzlichen Preis anzuzeigen. Dazu folgende Zeile einfügen:

  if(data.shippingOption == "express") {
          document.getElementById("express_selected").style.display = "inline-block";
      }

Die ganze Funktion sieht dann so aus:

  function showPaymentResponse(data) {

      document.getElementById("produkt_button").innerHTML = 'Gekauft';
      document.getElementById("produkt_button").style.backgroundColor = "#4CAF50";

      document.getElementById("produkt_button").removeEventListener('click', handleProduktClick);

      document.getElementById("produkt_bought").style.display = "block";

      if(data.shippingOption == "express") {
          document.getElementById("express_selected").style.display = "inline-block";
      }

      document.getElementById("data_list").innerHTML = JSON.stringify(data);

    }

Damit sind alle Änderungen abgeschlossen und der PaymentRequest sollte jetzt die Auswahl einer Lieferadresse und einer Versandoption unterstützen. Nachfolgend nochmal der gesamte Code mit den Änderungen:

<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <title>W3C Payment Request API Tutorial</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <style>
    * {
      margin: 0;
    }

    html {
      font-family: sans-serif;
      line-height: 1.4;
      padding: 2em;
    }

    body> :not(:first-child) {
      margin-top: 1.5em;
    }

    h1,
    h2 {
      line-height: 1.15;
    }

    #express_selected {
      display: none;
    }

    .produkt {
      max-width: 13em;
      padding: 1em;
      background: #fff;
    }

    .produkt> :not(:first-child) {
      margin-top: 0.85em;
    }

    .produkt_bought {
      padding: 1em;
      background: #fff;
    }

    .data_list {
      max-width: 100%;
    }

    .produkt_bought> :not(:first-child) {
      margin-top: 0.85em;
    }

    .produkt_bought {
      display: none;
    }

    .produkt_shadow {
      position: relative;
      -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    }

    .produkt_shadow:after {
      content: "";
      position: absolute;
      z-index: -1;
      -webkit-box-shadow: 0 0 40px rgba(0, 0, 0, 0.8);
      box-shadow: 0 0 40px rgba(0, 0, 0, 0.8);
      bottom: 0px;
      left: 10%;
      right: 10%;
      width: 80%;
      height: 50%;
      -moz-border-radius: 100%;
      border-radius: 100%;
    }

    .produkt_meta {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .produkt_button {
      background-color: #008CBA;
      color: #fff;
      padding: 0.25em 1em;
      text-decoration: none;
    }

    ul.nav {
    margin:0;
    padding:0;
    list-style:none;
    height:36px;
    margin-top:20px;

    font-family:Arial, Helvetica, sans-serif;
    font-size:13px;
    }

    ul.nav li {
    float:left;
    }

    ul.nav a {
    display:block;
    padding:0 28px;
    color:#000;
    text-decoration:none;
    }
  </style>
</head>

<body>
  <h1>W3C Payment Request API Tutorial</h1>

  <ul class="nav">
  <li><a href="/">1. Basic</a></li>
  <li><a href="/2">2. shippingAddress</a></li>
  <li><a href="/3">3. Sale</a></li>
  <li><a href="/4">4. reject Creditcards</a></li>
  <li><a href="/5">5. Tax</a></li>
  <li><a href="/6">6. reject shippingAddress</a></li>
  </ul>

  <p> Teil 2: Payment Request mit Lieferadresse </p>
  <div class="produkt produkt_shadow" id="produkt">
    <h2>Beispielprodukt</h2>
    <p>Ich bin ein ganz tolles Produkt! Kauf mich!</p>
    <div class="produkt_meta">
      <strong class="produkt_preis">nur 49.99 €</strong>
      <a href="#" id="produkt_button" class="produkt_button">Kaufen</a>
    </div>
  </div>

  <div class="produkt_bought" id="produkt_bought">
    <h2>Beispielprodukt gekauft!</h2>
    <p>Sie haben das Beispielprodukt erfolgreich für 49,99 € <span id="express_selected">(+ 14,99 € Expressversand)</span> gekauft.</p>
    <p>Ihre Daten:</p>
    <div id="data_list"></div>

  </div>

  <script>
    function handleProduktClick(e) {
      e.preventDefault();

      var creditCardPaymentMethod = {
        supportedMethods: 'basic-card',
      };

      var supportedPaymentMethods = [creditCardPaymentMethod];

      var paymentDetails = {
        total: {
          label: 'Gesamtsumme',
          amount: {
            currency: 'EUR',
            value: '49.99'
          }
        },
        displayItems: [{
            label: 'Beispielprodukt',
            amount: {
              currency: 'EUR',
              value: '49.99'
            },
          },
          {
            label: 'Standardversand',
            amount: {
              currency: 'EUR',
              value: '0.00'
            },
          }

        ],
        shippingOptions: [{
            id: 'standard',
            label: 'Standardversand',
            amount: {
              currency: 'EUR',
              value: '0.00'
            },
            selected: true
          },
          {
            id: 'express',
            label: 'Expressversand',
            amount: {
              currency: 'EUR',
              value: '14.99'
            },
          },
        ],
      };

      var options = {
        requestShipping: true
      };

      var paymentRequest = new PaymentRequest(supportedPaymentMethods, paymentDetails, options);


      paymentRequest.addEventListener('shippingoptionchange', function(evt) {
        evt.updateWith(new Promise(function(resolve, reject) {
          updateDetails(paymentDetails, paymentRequest.shippingOption, resolve, reject);
        }));
      });

      function updateDetails(details, shippingOption, resolve, reject) {
        if (shippingOption === 'standard') {
          selectedShippingOption = details.shippingOptions[0];
          otherShippingOption = details.shippingOptions[1];
          details.total.amount.value = '49.99';
        } else if (shippingOption === 'express') {
          selectedShippingOption = details.shippingOptions[1];
          otherShippingOption = details.shippingOptions[0];
          details.total.amount.value = '64.98';

        } else {
          reject('Unbekannte Versandart \'' + shippingOption + '\'');
          return;
        }
        selectedShippingOption.selected = true;
        otherShippingOption.selected = false;
        details.displayItems.splice(1, 2, selectedShippingOption);
        resolve(details);
      }

      paymentRequest
        .show()
        .then(paymentResponse => {
          return verifyPaymentWithBackend(paymentResponse).then((success) => {
            if (success) {

              showPaymentResponse(JSON.parse(success));
              return paymentResponse.complete('success');

            } else {
              return paymentResponse.complete('fail');
            }
          });
        })
        .catch(err => {
          console.log('Error:', err);
        });

    };

    function showPaymentResponse(data) {

      document.getElementById("produkt_button").innerHTML = 'Gekauft';
      document.getElementById("produkt_button").style.backgroundColor = "#4CAF50";

      document.getElementById("produkt_button").removeEventListener('click', handleProduktClick);

      document.getElementById("produkt_bought").style.display = "block";

      if (data.shippingOption == "express") {
        document.getElementById("express_selected").style.display = "inline-block";
      }

      document.getElementById("data_list").innerHTML = JSON.stringify(data);

    }

    function verifyPaymentWithBackend(data) {
      return new Promise((resolve, reject) => {

        var xmlhttp = new XMLHttpRequest();

        xmlhttp.onload = function() {

          if (xmlhttp.status == 201) {

            resolve(xmlhttp.responseText);
          } else {
            resolve(false)
          }
        };


        xmlhttp.open("POST", "/payment");
        xmlhttp.setRequestHeader("Content-Type", "application/json");

        xmlhttp.send(JSON.stringify(data));

      });
    }

    document.getElementById("produkt_button").addEventListener('click', handleProduktClick)
  </script>
</body>

</html>

Clone this wiki locally