Simple Donation Request Application

Nwokolo Chibuzo - Oct 16 '21 - - Dev Community

I had a friend who needed to raise funds for a tumour surgery a couple of months ago. Obviously, the popular gofundme is the option. However, for those who have visited the gofund me page, only a few countries are supported. My friend had to look for someone living in one of the supported countries to assist set up a gofundme account.

I wondered why we cannot have our own local gofund me-like application in our own local currency. Therefore, this tutorial aims to demonstrate the design and implementation of a simple application with minimum requirements to have a funding application up and running in roughly an hour depending on your speed. That said, c’est parti!

Scope/Features

  1. Our application application would allow users to register and create a funding request with target amount
  2. Users should be able to share the link to the funding request on their Facebook
  3. The fund request link would allow donors make payments.

Tools

  • CodeSandbox. We would be building our application inside codesanbox. A quick search on google with “codesandbox” shows that it is nothing other than an online code editor and prototyping tool. Many programming languages are supported as today.

  • Next Js. This would be our progamming language/framework.

  • Cloudinary. Images that users upload would be stored on Cloudinary online database instead of housing the images locally. You need to create a Cloudinary account if you do not have one aready.

  • GetForm: Since this is a simple application, we opted not to use an explicit database. Rather we would utilise a Getform back-end for storing user details. Create your getform user account on getform site

  • Auth0. Our authentication would be delegated to a third party authentication service Auth0. Create a free account on the Auth 0 website

  • Payment Gateway Integration. There are several payment processors ranging from paypal, paystack, flutterwave etc. For this demonstration, we would use Fidelity bank Paygate integraton because of the ease of integration. Feel free to use any other option based on your choice and country.

Creation of Sandbox: This sandbox for the app is on the link Code Sandbox link. I already created an account on code sandbox and created a sandbox named naijafundme. The link on how to create a sandbox is detailed in the documentation for code sanbox . Ensure that you select the Next js template while creating your sandbox to follow along with the tutorial

Dependencies. To add a dependency, simply type the name of the dependency under Dependencies as shown in the left hand side menu search. Once the name of the dependency is displayed, click it and it gets added to your project.

sandbox image

After installing all the required dependencies, your package.json file should look like this.

{
  "name": "naijafundme",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "post-update": "yarn upgrade --latest"
  },
  "dependencies": {
    "@auth0/nextjs-auth0": "1.5.0",
    "bootstrap": "5.1.1",
    "cloudinary": "1.27.0",
    "dotenv": "10.0.0",
    "form-data": "4.0.0",
    "formidable": "1.2.2",
    "next": "11.1.2",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "license": "MIT",
  "keywords": [],
  "description": ""
}
Enter fullscreen mode Exit fullscreen mode

Navheader.js. The nav bar is common to all the pages of the application. We therefore created a component for the navbar under the components folder.

import "bootstrap/dist/css/bootstrap.css";
export default function NavHeader(pageProps) {
  return (
    <nav
      class="navbar navbar-icon-top navbar-expand-lg navbar-dark bg-primary"

    >
Enter fullscreen mode Exit fullscreen mode

Layout file. We modified the layout of the application by creating a layout.js file under the components folder.

import Head from "next/head";
import NavHeader from "./nav/navbar";
export default function Layout({ children, pageProps }) {
  return (
    <div>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
          crossorigin="anonymous"
        />
        <script
          src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js"
          integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW"
          crossorigin="anonymous"
        ></script>
      </Head>
      <NavHeader {...pageProps} />
      <div class="container">{children}</div>;
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

_App js. Modify the _app.js file by using the layout.js created above.

import React from "react";
import Layout from "../components/layout";
export default function MyApp({ Component, pageProps }) {
  return (
      <Layout {...pageProps}>
        <Component {...pageProps} />
      </Layout>
  );
}
Enter fullscreen mode Exit fullscreen mode

Configuring Auth0 Authentication

Create an account on Auth0. Then create a new application or use the default app created during your account creation. We would need the following parameters which can be gotten from the settings of the application you created. These are the Domain name, the Client Id and the Client Secret. To get to the settings, click on the application name. For instance, I would click on the NaijaFundme to go its settings.

Auth0 dashboard

Auth0 dashboard

The next thing to do is to configure the the callback URL and logout URL. The callback URL is the URL where your users are redirected to after they are successfully authenticated. While the logout URL is the page users are taken to after they are logged out.

Auth0 dashboard

Once the configurations are done, we add the @auth0/nextjs-auth0 to our codesandbox dependencies.

To Configure the SDK just added, follow the following steps.

  1. Create a .env file under the project root folder and add the following code.

    AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value'
    AUTH0_BASE_URL='The base url of your application here'
    AUTH0_ISSUER_BASE_URL='Your domain gotten from the dashboard of Auth0'
    AUTH0_CLIENT_ID='Your Client Id here'
    AUTH0_CLIENT_SECRET='Your Client Secret here'

  2. Creating the Dynamic Routes.

    Inside the directory page/api/auth create a file named auth/[...auth0].js and add the code snippet below.

     // pages/api/auth/[...auth0].js
    

    import { handleAuth } from "@auth0/nextjs-auth0";
    export default handleAuth();

This creates the following routes automatically when needed:

  • /api/auth/login: The route that performs login with Auth0.
  • /api/auth/logout: This route is used to logout a logged in user
  • /api/auth/callback: This is the route Auth0 takes the user to after login
  • /api/auth/me: This route fetches the user profile

Modification of _app.js

Auth0 uses react context to manage the state of users of the application. We can get this user state information by leveraging on the component UserProvider. To make the user detail provided by UserProvider component available to all pages of the application, we need to modify our _app.js file.
Modify the _app.js file by importing the UserProvider and wrapping the Layout with the UserProvider Component as shown below .

import { UserProvider } from "@auth0/nextjs-auth0";
import React from "react";
import Layout from "../components/layout";
export default function MyApp({ Component, pageProps }) {
  return (
    <UserProvider {...pageProps}>
      <Layout {...pageProps}>
        <Component {...pageProps} />
      </Layout>
    </UserProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Creating a login and logout components

Add the login and logout functionality by creating a SignedIn and Signout Components under the Nav folder. These components are child components of the Navbar Component.

  export default function SignInUser() {
  return (
    <a href="/api/auth/login">
      <i class="fa fa-envelope-o nav-link disabled marg">
        <span class="badge badge-warning">Sign In</span>
      </i>
    </a>
  );
}



export default function SignOutUser({ user }) {
  return (
    <div>
      <span>{user.email}</span>
      <a href="/api/auth/logout">
        <i class="fa fa-envelope-o nav-link disabled marg">
          <span class="badge badge-warning">Log Out</span>
        </i>
      </a>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Usage inside the Navbar Component

import { useUser } from "@auth0/nextjs-auth0";
import SignInUser from "./signinuser";
import SignOutUser from "./signoutuser";
import "bootstrap/dist/css/bootstrap.css";
export default function NavHeader(pageProps) {
  const { user, error, isLoading } = useUser();
  **********************Other Page tags************************
              {user ? <SignOutUser user={user} /> : <SignInUser />}
            **********************Other Page tags************************
}
Enter fullscreen mode Exit fullscreen mode

Cloudinary For Image Upload:

We would make use of Cloudinary for storing the image files that users upload when setting up a fund donation request. Create a cloudinary account and create an application on the dashboard.

The parameters cloud name, api key and api secret from the dashboard would be used to create a function to upload files to Cloudinary. create the file named uploadserverless.js inside the pages/api folder

Cloudinary Setup

///// uploadserverless.js

import cloudinary from "cloudinary";
import { useUser } from "@auth0/nextjs-auth0";
cloudinary.config({
  cloud_name: "your cloudname here",
  api_key: "your api key",
  api_secret: "your api_secret"
});
export const config = {
  api: {
    bodyParser: false
  }
};
Enter fullscreen mode Exit fullscreen mode

Getform integration…

We would use getform as our backend management platform. Create an endpoint as shown below. We would store the users, email, the message for donors, a target amount and then a cloudinary image url to view the image uploaded regarding the user’s situation

Get form dashboard

Once the endpoint is created, we would use it to create another function named uploadform.js to assist us upload the users details.

/////upload.js

import FormData from "form-data";
export default async (req, res) => {
  var formData = new FormData();
  try {
    console.log(JSON.stringify(req.body));
    Object.entries(req.body).forEach(([key, value]) => {
      formData.append(key, value);
    });
    const response = await fetch(
      "https://getform.io/f/6673e6dc-f6ac-45",
      {
        method: "post",
        body: formData
      }
    );
    return res.json(response);
  } catch (error) {
  } finally {
  }
};
Enter fullscreen mode Exit fullscreen mode

Upload Button widget.
Create a uploadbutton.js file component under the component folder. This widget for image upload would be imported into the createfund.js page

export function UploadButton({ email, setImageUrl }) {
  const onChange = async (event) => {
    event.preventDefault();
    const formData = new FormData();
    const file = event.target.files[0];
    formData.append("inputFile", file);
    formData.append("email", email);
    try {
      const response = await fetch("/api/uploadserverless", {
        method: "POST",
        body: formData
      });
      const data = await response.json();
      setImageUrl(data.secure_url);
    } catch (error) {
      console.log("Error", error);
      //return res.json(error);
    } finally {
      //
    }
  };
  return <input class="form-control" type="file" onChange={onChange} />;
}
Enter fullscreen mode Exit fullscreen mode

Create fund Page:

Pages/createfund.js

import { UploadButton } from "../components/images/uploadButton";
import React, { useEffect, useState } from "react";
import Router from "next/router";
import styles from "../styles/Home.module.css";
export default function CreateFund() {
  const formSubmit = async (e) => {
    e.preventDefault();
    const res = await fetch("/api/uploadForm", {
      body: JSON.stringify({
        email: email,
        name: name,
        message: message,
        target: target,
        url: url
      }),
      headers: {
        "Content-Type": "application/json"
      },
      method: "POST"
    });
    const { error } = await res.json();
    if (error) {
      console.log(error);
      return;
    }
    const { pathname } = Router;
    if (pathname === "/createfund") {
      Router.push({ pathname: "/sharepage", query: { email: email } });
    }
  };
  return (
      <div class="container marginTop">
      <div class="row">
        <div class="col-lg-3"></div>
        <div class="col-lg-6">
          <h3 class="colorp">Set up</h3>
          <form onSubmit={formSubmit}>
            **** name input****
              **** name email****
             **** message for donors input****
            </div>
            <div className={styles.margin}>
              <UploadButton email={email} setImageUrl={setImageUrl} />
            </div>
            <button class="btn btn-success" type="submit">
              Next
            </button>
          </form>
        </div>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Social Media Share button

Create a file named sharepage.js under the pages folder. This would allow users to share the payment link generated on Facebook

import React, { useState } from "react";
import { useUser } from "@auth0/nextjs-auth0";
import { useRouter } from "next/router";
import styles from "../styles/Home.module.css";
export default function SharePage() {
  const router = useRouter();
  const { email } = router.query;
  console.log(email);
  return (
    <div class="container marginTop">
      <div class="row">
        <div class="col-lg-6">
          <form>
            <div className={styles.margin}>
              {/* <label>Name</label> */}
              <input
                type="text"
                name="target"
                required
                readOnly
                className="form-control"
                value={encodeURI(
                  `https://0hhjm.sse.codesandbox.io/paymentpage?email=${email}`
                )}
              />
            </div>
          </form>
          <a
            class="btn btn-success"

          >
            Share on Facebook
          </a>
        </div>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Payment Page Integration

For the purpose of this implementation, we used the payment gateway option from fidelity bank in Nigeria. Create a paymentpage.js file under pages with the following content.

import Image from "next/image";
import styles from "../styles/Home.module.css";
import { useState } from "react";
export default function PaymentPage({ email, target, message, name, url }) {
  return (
    <div class="container marginTop">
      ***** Payment Details embedded in hidden inputs ********
    </div>
  );
}
// This server function gets the fund creators details using the email on the link he shared to friends
export async function getServerSideProps(context) {
  //
  const fundName = context.query;
  console.log(fundName.email);
  const res = await fetch(
    "https://api.getform.io/v1/forms/667"
  ); // Get the user details from the form
  const response = await res.json();
  var myFund = response.data.submissions.filter(
    (x) => x.email === fundName.email
  )[0];
  return {
    props: myFund
  };
}
Enter fullscreen mode Exit fullscreen mode

Demo

visit the url https://0hhjm.sse.codesandbox.io/ to demo the project. The css is currently not working properly and would be fixed

Demo image

Login

Demo image

Create a fund me request by clicking the create fund.

set up request image

Share link on Facebook. Once the set up is completed above, a user can share the link on facebook.

share page image

Donation.

Users can donate by clicking the link shared on Facebook.

payment page image

donation page screenshot

Backend Management.

The details of the fund can be seen on the getform platform.

get form dashboard image

The payment processors are capable of providing the details of every payment made on the system.

Conclusion

We have demonstrated that it is indeed possible to set up simple application to help raise funds within countries where gofundme is not currently available. Check the sandbox link and github for the full source code

Improvements

The design of the project can be greatly improved. Also a proper backend application can be created to manage users.

References
The following were used as main references for this tutorial:

Content created for the Hackmamba Jamstack Content Hackathon using Auth0 and Cloudinary

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .