Lass uns einen JAMstack E-Commerce Store mit Netlify Functions bauen

Avatar of Sarah Drasner
Sarah Drasner am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Viele Leute sind verwirrt darüber, was JAMstack ist. Das Akronym steht für JavaScript, APIs und Markup, aber JAMstack muss nicht alle drei beinhalten. Was JAMstack definiert, ist, dass es ohne Webserver ausgeliefert wird. Wenn man die Geschichte der Computertechnik betrachtet, ist diese Art von Abstraktion nicht unnatürlich; vielmehr ist es die unvermeidliche Entwicklung, auf die sich die Branche zubewegt.

Also, wenn JAMstack per Definition eher statisch ist, kann es keine dynamische Funktionalität, serverseitige Ereignisse haben oder ein JavaScript-Framework verwenden, richtig? Zum Glück nicht. In diesem Tutorial richten wir eine JAMstack-E-Commerce-App ein und fügen einige serverlose Funktionalitäten mit Netlify Functions hinzu (die AWS Lambda abstrahieren und meiner Meinung nach super dope sind).

Ich werde im Detail zeigen, wie der Nuxt/Vue-Teil eingerichtet wurde, in einem Folgebeitrag, aber für jetzt konzentrieren wir uns auf die Stripe-serverlose Funktion. Ich werde zeigen, wie ich diese eingerichtet habe, und wir werden sogar darüber sprechen, wie man sich mit anderen statischen Website-Generatoren wie Gatsby verbindet. Vollständige Offenlegung, ich arbeite für Netlify und nutze deren Tools dafür, es ist möglich, sich mit Stripe über andere Dienste zu verbinden. Ich habe mich zum Teil für Netlify entschieden, weil ich einige der schönen Abstraktionen genieße, die ihre Dienste bieten.

Diese Seite und das Repository sollten Ihnen den Einstieg erleichtern, wenn Sie etwas Ähnliches selbst einrichten möchten.

App-Gerüst erstellen

Der allererste Schritt ist die Einrichtung unserer App. Diese hier ist mit Nuxt erstellt, um eine Vue-App zu erstellen, aber Sie können diese Befehle durch Ihren bevorzugten Tech-Stack ersetzen.

yarn create nuxt-app

hub create

git add -A
git commit -m “initial commit”

git push -u origin master

Ich verwende yarn, hub (was mir erlaubt, Repos von der Kommandozeile aus zu erstellen) und Nuxt. Möglicherweise müssen Sie diese Tools lokal oder global installieren, bevor Sie fortfahren.

Mit diesen wenigen Befehlen und der Befolgung der Anweisungen können wir ein brandneues Nuxt-Projekt sowie das Repository einrichten.

Wenn wir uns bei Netlify anmelden und authentifizieren, werden wir aufgefordert, ein Repository auszuwählen.

choose a repo in netlify

Ich werde yarn generate verwenden, um das Projekt zu erstellen. Damit kann ich die Website-Einstellungen für Nuxt im dist-Verzeichnis hinzufügen und auf "deploy" klicken! Das ist alles, was nötig ist, um CI/CD einzurichten und die Website zu deployen! Jetzt wird bei jedem Push auf den master-Branch nicht nur deployed, sondern ich erhalte auch einen eindeutigen Link für diesen speziellen Deploy. So toll.

Eine grundlegende serverlose Funktion mit Netlify

Hier ist der spannende Teil, denn die Einrichtung für diese Art von Funktionalität ist so schnell! Wenn Sie mit Serverless nicht vertraut sind, können Sie es sich wie dieselben JavaScript-Funktionen vorstellen, die Sie kennen und lieben, aber auf dem Server ausgeführt werden. Serverlose Funktionen sind ereignisgesteuerte Logik und ihre Preise sind extrem niedrig (nicht nur bei Netlify, sondern branchenweit) und skalieren mit Ihrer Nutzung. Und ja, wir müssen hier die Einschränkung hinzufügen: Serverless verwendet immer noch Server, aber das "Babysitten" dieser ist nicht mehr Ihre Aufgabe. Lassen Sie uns beginnen.

Unsere sehr grundlegende Funktion sieht so aus. Ich habe meine in einem Ordner namens `functions` gespeichert und ihn einfach index.js genannt. Sie können den Ordner und die Funktion wirklich nennen, wie Sie möchten.

// functions/index.js
exports.handler = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: "Hi there Tacos",
      event
    })
  }
}

Wir müssen auch eine Datei namens netlify.toml im Stammverzeichnis des Projekts erstellen und sie wissen lassen, in welchem Verzeichnis sie die Funktion finden soll, nämlich "functions".

// netlify.toml
[build]
  functions = "functions"

Wenn wir nach master pushen und in das Dashboard gehen, können Sie sehen, wie es die Funktion erkennt!

netlify function in the dashboard

Wenn Sie sich den oben genannten Endpunkt ansehen, ist er hier gespeichert
https://ecommerce-netlify.netlify.com/.netlify/functions/index

Wirklich, für jede Website, die Sie ihm geben, folgt die URL diesem Muster
https:/<IhreSiteURLHier>/.netlify/functions/<FunktionsName>

Wenn wir diesen Endpunkt aufrufen, erhalten wir die von uns übergebene Nachricht sowie alle Protokollierungsdaten des Ereignisses.

the function event in the browser

Ich liebe, wie wenige Schritte das sind! Dieses kleine Stück Code gibt uns unendliche Macht und Möglichkeiten für reichhaltige, dynamische Funktionalität auf unseren Websites.

Stripe einbinden

Die Anbindung an Stripe macht extrem viel Spaß, weil es einfach zu bedienen ist, anspruchsvoll, tolle Dokumentation hat und gut mit serverlosen Funktionen funktioniert. Ich habe andere Tutorials, in denen ich Stripe verwendet habe, weil ich seinen Service so sehr genieße.

Hier ist ein Überblick über die App, die wir bauen werden.

Zuerst gehen wir zum Stripe-Dashboard und holen unsere Schlüssel. Für alle, die jetzt total schockiert sind, es ist in Ordnung, das sind Testschlüssel. Sie können sie auch verwenden, aber Sie werden mehr lernen, wenn Sie sie selbst einrichten. (Es sind zwei Klicks und ich verspreche, es ist nicht schwer, von hier aus zu folgen.)

testing keys in the stripe dashboard

Wir werden ein Paket namens dotenv installieren, das uns hilft, unseren Schlüssel zu speichern und ihn lokal zu testen. Dann speichern wir unseren Stripe-Geheimschlüssel in einer Stripe-Variable. (Sie können ihn beliebig nennen, aber hier habe ich ihn STRIPE_SECRET_KEY genannt, und das ist ziemlich branchenüblich.)

yarn add dotenv
require("dotenv").config()

const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY)

Im Netlify-Dashboard gehen wir zu "Build & deploy" und dann zu "Environment", um Umgebungsvariablen hinzuzufügen, wobei STRIPE_SECRET_KEY der Schlüssel ist und der Wert der Schlüssel ist, der mit sk beginnt.

Wir werden auch einige Header hinzufügen, damit wir keine CORS-Probleme bekommen. Diese Header werden wir in der Funktion verwenden, die wir erstellen werden.

const headers = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "Content-Type"
}

Nun erstellen wir die Funktionalität für die Kommunikation mit Stripe. Wir stellen sicher, dass wir die Fälle behandeln, in denen es sich nicht um die erwartete HTTP-Methode handelt, und dass wir auch die erwarteten Informationen erhalten.

Man kann hier schon sehen, welche Daten wir an Stripe senden müssen, anhand dessen, wonach wir suchen. Wir benötigen den Token, den Gesamtbetrag und einen **Idempotenzschlüssel**.

Wenn Sie mit Idempotenzschlüsseln nicht vertraut sind, handelt es sich um eindeutige Werte, die von einem Client generiert und zusammen mit einer Anfrage an eine API gesendet werden, falls die Verbindung unterbrochen wird. Wenn der Server einen Aufruf erhält, der als Duplikat erkannt wird, ignoriert er die Anfrage und antwortet mit einem erfolgreichen Statuscode. Oh, und es ist ein unmögliches Wort zum Aussprechen.

exports.handler = async (event, context) => {
  if (!event.body || event.httpMethod !== "POST") {
    return {
      statusCode: 400,
      headers,
      body: JSON.stringify({
        status: "invalid http method"
      })
    }
  }

  const data = JSON.parse(event.body)

  if (!data.stripeToken || !data.stripeAmt || !data.stripeIdempotency) {
    console.error("Required information is missing.")

    return {
      statusCode: 400,
      headers,
      body: JSON.stringify({
        status: "missing information"
      })
    }
  }

Jetzt starten wir die Stripe-Zahlungsabwicklung! Wir erstellen einen Stripe-Kunden mit der E-Mail und dem Token, protokollieren ein wenig und erstellen dann die Stripe-Zahlung. Wir geben die Währung, den Betrag, die E-Mail, die Kunden-ID an und geben eine Beschreibung hinzu. Schließlich stellen wir den Idempotenzschlüssel (ausgesprochen eye-dem-po-ten-see) zur Verfügung und protokollieren, dass er erfolgreich war.

(Obwohl hier nicht gezeigt, werden wir auch einige Fehlerbehandlungen durchführen.)

// stripe payment processing begins here
try {
  await stripe.customers
    .create({
      email: data.stripeEmail,
      source: data.stripeToken
    })
    .then(customer => {
      console.log(
        `starting the charges, amt: ${data.stripeAmt}, email: ${data.stripeEmail}`
      )
      return stripe.charges
        .create(
          {
            currency: "usd",
            amount: data.stripeAmt,
            receipt_email: data.stripeEmail,
            customer: customer.id,
            description: "Sample Charge"
          },
          {
            idempotency_key: data.stripeIdempotency
          }
        )
        .then(result => {
          console.log(`Charge created: ${result}`)
        })
    })

Mit Nuxt verbinden

Wenn wir uns unsere Anwendung ansehen, sehen wir, dass wir Seiten und Komponenten haben, die sich innerhalb der `pages` befinden. Der Vuex Store ist wie das Gehirn unserer Anwendung. Er speichert den Zustand der App und kommuniziert mit Stripe. Wir müssen jedoch weiterhin über den Client mit unserem Benutzer kommunizieren. Wir sammeln die Kreditkartendaten in einer Komponente namens AppCard.vue, die sich auf der Warenkorbseite befindet.

Zuerst installieren wir ein Paket namens vue-stripe-elements-plus, das uns einige Stripe-Formularelemente zur Verfügung stellt, mit denen wir Kreditkartendaten erfassen können, und uns sogar mit einer pay-Methode einrichtet, die uns die Erstellung von Tokens für die Stripe-Zahlungsabwicklung ermöglicht. Wir fügen auch eine Bibliothek namens uuid hinzu, die uns die Generierung eindeutiger Schlüssel ermöglicht, die wir für den Idempotenzschlüssel verwenden werden.

yarn add vue-stripe-elements-plus uuid

Die Standardeinrichtung, die sie uns zur Verfügung stellen, um mit vue-stripe-elements-plus zu arbeiten, sieht so aus.

<template>
  <div id='app'>
    <h1>Please give us your payment details:</h1>
    <card class='stripe-card'
      :class='{ complete }'
      stripe='pk_test_XXXXXXXXXXXXXXXXXXXXXXXX'
      :options='stripeOptions'
      @change='complete = $event.complete'
    />
    <button class='pay-with-stripe' @click='pay' :disabled='!complete'>Pay with credit card</button>
  </div>
</template>
<script>
import { stripeKey, stripeOptions } from './stripeConfig.json'
import { Card, createToken } from 'vue-stripe-elements-plus'

export default {
  data () {
    return {
      complete: false,
      stripeOptions: {
        // see https://stripe.com/docs/stripe.js#element-options for details
      }
    }
  },

  components: { Card },

  methods: {
    pay () {
      // createToken returns a Promise which resolves in a result object with
      // either a token or an error key.
      // See https://stripe.com/docs/api#tokens for the token object.
      // See https://stripe.com/docs/api#errors for the error object.
      // More general https://stripe.com/docs/stripe.js#stripe-create-token.
      createToken().then(data => console.log(data.token))
    }
  }
}
</script>

Hier ist also, was wir tun werden. Wir werden das Formular aktualisieren, um die Kunden-E-Mail zu speichern, und die pay-Methode aktualisieren, um diese und den Token oder den Fehlercode an den Vuex Store zu senden. Wir werden eine Aktion versenden, um dies zu tun.

data() {
    return {
      ...
      stripeEmail: ""
    };
  },
  methods: {
    pay() {
      createToken().then(data => {
        const stripeData = { data, stripeEmail: this.stripeEmail };
        this.$store.dispatch("postStripeFunction", stripeData);
      });
    },
 ...

Diese versendete `postStripeFunction`-Aktion sieht so aus.

// Vuex store
export const actions = {
  async postStripeFunction({ getters, commit }, payload) {
    commit("updateCartUI", "loading")

    try {
      await axios
        .post(
          "https://ecommerce-netlify.netlify.com/.netlify/functions/index",
          {
            stripeEmail: payload.stripeEmail,
            stripeAmt: Math.floor(getters.cartTotal * 100), //it expects the price in cents                        
            stripeToken: "tok_visa", //testing token, later we would use payload.data.token
            stripeIdempotency: uuidv1() //we use this library to create a unique id
          },
          {
            headers: {
              "Content-Type": "application/json"
            }
          }
        )
        .then(res => {
          if (res.status === 200) {
            commit("updateCartUI", "success")
            setTimeout(() => commit("clearCart"), 3000)
            …

Wir werden den UI-Status auf "loading" aktualisieren, während wir verarbeiten. Dann werden wir axios verwenden, um an unseren Funktionsendpunkt zu posten (die URL, die Sie weiter oben im Beitrag gesehen haben, als wir unsere Funktion eingerichtet haben). Wir werden die E-Mail, den Betrag, den Token und den eindeutigen Schlüssel senden, den die Funktion erwartet.

Wenn es erfolgreich war, werden wir den UI-Status entsprechend aktualisieren.

Ein letzter Hinweis, den ich geben kann, ist, dass ich den UI-Status in einem String speichere, anstatt in einem Boolean. Ich beginne ihn normalerweise mit etwas wie "idle" und in diesem Fall werde ich auch "loading", "success" und "failure" haben. Ich verwende keine Booleschen Zustände, weil ich selten eine Situation angetroffen habe, in der der UI-Status nur zwei Zustände hat. Wenn Sie Boolesche Werte für diesen Zweck verwenden, neigen Sie dazu, sie in immer mehr Zustände aufzuteilen, und die Überprüfung aller davon kann immer komplizierter werden.

Wie es jetzt steht, kann ich Änderungen in der Benutzeroberfläche auf der Warenkorbseite mit lesbaren Bedingungen wie folgt widerspiegeln.

<section v-if="cartUIStatus === 'idle'">
  <app-cart-display />
</section>

<section v-else-if="cartUIStatus === 'loading'" class="loader">
  <app-loader />
</section>

<section v-else-if="cartUIStatus === 'success'" class="success">
  <h2>Success!</h2>
  <p>Thank you for your purchase. You'll be receiving your items in 4 business days.</p>
  <p>Forgot something?</p>

  <button class="pay-with-stripe">
    <nuxt-link exact to="/">Back to Home</nuxt-link>
  </button>
</section>

<section v-else-if="cartUIStatus === 'failure'">
  <p>Oops, something went wrong. Redirecting you to your cart to try again.</p>
</section>

Und da haben Sie es! Wir sind bereit, Zahlungen mit Stripe auf einer Nuxt, Vue-Website mit einer Netlify-Funktion zu akzeptieren, und es war nicht einmal kompliziert einzurichten!

Gatsby-Anwendungen

Wir haben in diesem Fall Nuxt verwendet, aber wenn Sie die gleiche Art von Funktionalität mit etwas wie React wie Gatsby einrichten möchten, gibt es ein Plugin dafür. (Bei Gatsby ist alles ein Plugin. ☺️)

Sie würden es mit diesem Befehl installieren.

yarn add gatsby-plugin-netlify-functions

…und das Plugin wie folgt zu Ihrer Anwendung hinzufügen.

plugins: [
  {
    resolve: `gatsby-plugin-netlify-functions`,
    options: {
      functionsSrc: `${__dirname}/src/functions`,
      functionsOutput: `${__dirname}/functions`,
    },
  },
]

Die in dieser Demo verwendete serverlose Funktion ist reines JavaScript, daher ist sie auch auf React-Anwendungen portierbar. Es gibt ein Plugin zum Hinzufügen des Stripe-Skripts zu Ihrer Gatsby-App (nochmal, alles ist ein Plugin). Zur Warnung: Dies fügt das Skript zu jeder Seite der Website hinzu. Um die Kreditkarteninformationen auf dem Client zu erfassen, würden Sie React Stripe Elements verwenden, was dem von uns oben verwendeten Vue-Element ähnelt.

Stellen Sie nur sicher, dass Sie vom Client erfassen und alle Informationen übergeben, die die Funktion erwartet.

  • Die E-Mail des Benutzers.
  • Der Gesamtbetrag in Cent.
  • Der Token.
  • Der Idempotenzschlüssel.

Mit solch einer geringen Einstiegshürde können Sie sehen, wie Sie wirklich dynamische Erlebnisse mit JAMstack-Anwendungen schaffen können. Es ist erstaunlich, wie viel Sie ohne Wartungskosten für Server erreichen können. Stripe und Netlify Functions machen die Einrichtung der Zahlungsabwicklung in einer statischen Anwendung zu einer reibungslosen Entwicklererfahrung!