DEV Community

Cover image for Building a serverless donate form
monty5811
monty5811

Posted on • Originally published at deanmontgomery.com

Building a serverless donate form

Learn how to put a donation form on a website - using Netlify & Stripe - fully SCA compliant and no servers to manage!

We will:

tl;dr jump straight to the code here:

GitHub logo monty5811 / donate-form

example severless donate form with stripe & netlify

donate-form

flow chart

This repo is an example of how to build a donation form with Stripe & Netlify.

There are two main components:

  1. A super simple form with some JS to handle the redirect flow
  2. A netlify function to talk to the Stripe API



Step 1: Setup

First of all we need a form where the user can choose how much to donate:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Serverless Donate Form</title>
    <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
    <style>
        html {
            font-family: 'Lucida Grande', Verdana, sans-serif;
        }
    </style>
</head>

<body class="h-screen flex flex-col justify-center">
    <form class="antialiased max-w-xs mx-auto" id="payment-form">
        <input class="block w-64 py-2 px-3 mx-auto mb-4 border-gray-300 border rounded-md" type="number" min="0" placeholder="$50" id="giving-amount" />
        <button class="bg-green-500 hover:bg-green-600 text-white py-2 px-4 rounded-full mx-auto block" id="giving-button">
        Donate
      </button>
    </form>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

Which looks something like this (we've used Tailwind for styling):
Alt Text

Step 2: Adding Stripe to the form

Now we need some javascript to handle the interaction with Stripe. We do a few different things to hook our form up to Stripe:

  1. Listen for the user submitting the form
  2. Update the form to a "waiting" state
  3. Get a Stripe Checkout Session ID from our lambda function
  4. Redirect to Stripe's hosted checkout with the Session ID
  5. Handle any errors

Required changes:

    <!-- rest of content as above -->
    <!-- add jquery & stripe -->
    <script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
    <script src="https://js.stripe.com/v3/"></script>
    <script type="text/javascript">
        var errorText = "Failed. You have not been charged.";

        // look out for submit events on the form
        document.addEventListener("DOMContentLoaded", function(event) {
            var submitButton = document.getElementById("giving-button");

            var stripe = Stripe("<YOUR_STRIPE_PUBLISHABLE_KEY_HERE>");

            var form = document.getElementById("payment-form");
            form.addEventListener("submit", function(event) {
                event.preventDefault();
                const buttonText = submitButton.innerText;
                submitButton.innerText = "Working...";

                var data = {
                    amount: document.getElementById("giving-amount").valueAsNumber * 100,
                };

                // create a stripe session by talking to our netlify function
                $.ajax({
                    type: "POST",
                    url: "/.netlify/functions/get_checkout_session/",
                    data: JSON.stringify(data),
                    success: function(data) {
                        // we got a response from our netlify function:
                        switch (data.status) {
                            case "session-created":
                                // it worked - send the user to checkout:
                                stripe
                                    .redirectToCheckout({
                                        sessionId: data.sessionId
                                    })
                                    .then(function(result) {
                                        submitButton.innerText = result.error.message;
                                    });
                                break;
                            default:
                                submitButton.innerText = errorText;
                        }
                    },
                    dataType: "json"
                });
            });
        });
    </script>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

Step 3: Add our lambda function

Now we have a form that will take the donation amount & redirect to Stripe's hosted checkout. However, in order to use a custom "price" with Stripe Checkout we need a server-side component.1

Setting up a whole server seems like overkill for this - a serverless function is ideal.

The serverless function simply takes the amount and gets a Session ID from Stripe. This Session ID is then sent back to the browser where the user is redirected to complete their donation.

const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); // get from ENV

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

exports.handler = function(event, context, callback) {
  // some error checking:
  if (event.httpMethod !== "POST" || !event.body) {
    callback(null, {
      statusCode: 400,
      headers,
      body: JSON.stringify({ status: "bad-payload" })
    });
  }
  // Parse the body contents into an object.
  const data = JSON.parse(event.body);

  // Make sure we have all required data. Otherwise, escape.
  if (!data.amount) {
    console.error("Required information is missing.");

    callback(null, {
      statusCode: 400,
      headers,
      body: JSON.stringify({ status: "missing-information" })
    });

    return;
  }
  // actually create the session with Stripe
  // we need to provide a couple of redirect urls:
  stripe.checkout.sessions.create(
    {
      success_url: "https://donate-form-example.netlify.com/success",
      cancel_url: "https://donate-form-example.netlify.com/cancel",
      payment_method_types: ["card"],
      billing_address_collection: "required",
      payment_method_types: ["card"],
      submit_type: "donate",
      line_items: [
        {
          name: "Donation!",
          amount: data.amount,
          currency: "usd",
          quantity: 1
        }
      ]
    },
    function(err, session) {
      // asynchronously called
      if (err !== null) {
        console.log(err);
        callback(null, {
          statusCode: 200,
          headers,
          body: JSON.stringify({ status: "session-create-failed" })
        });
      }
      // woohoo! it worked, send the session id back to the browser:
      callback(null, {
        statusCode: 200,
        headers,
        body: JSON.stringify({
          status: "session-created",
          sessionId: session.id
        })
      });
    }
  );
};
Enter fullscreen mode Exit fullscreen mode

You can see how this is hooked up to Netlify in the full repo:

GitHub logo monty5811 / donate-form

example severless donate form with stripe & netlify

donate-form

flow chart

This repo is an example of how to build a donation form with Stripe & Netlify.

There are two main components:

  1. A super simple form with some JS to handle the redirect flow
  2. A netlify function to talk to the Stripe API





Conclusions

That's it! We have built a donation form where a user can choose how much they would like to donate and we have done it without ever having to worry about running our own server.

You can just as easily do this for a non-static site - you just need to replace the serverless function with a route on your site that will create a Stripe Session & return the id to the frontend.


  1. If you have fixed prices, or fixed donation amounts, then you don't need any server side components. You can do everything client side. See the Stripe docs for more info ↩

Top comments (0)