Donations API Tutorial

Looking to add donations in your app and don’t know where to start? Then look no further. The goal of this tutorial is to show you how to use the Donations API to create and retrieve donations. If you get stuck at any point you can always reach out at hello@getchange.io or the chat link on this page. All the code in this tutorial can be found here.

💡 Before you continue, please make sure to sign up if you haven’t already at https://api.getchange.io/sign_up.

To kick off this tutorial, we are going to learn how to make POST API requests to Change to create a donation. We will then learn how to make GET API requests to retrieve our created donation and to view all donations we’ve created in the past. At the end, we will go over some more advanced features in the API: the external_id and metadata fields. These can make the development process easier and unlock key insights about your app.

Setting the scene

To help visualize the process of integrating donations into your app, we will be using code examples in Javascript running on the web application framework, Express (https://expressjs.com/). If you’ve never used Express before, follow these steps to get a basic Express app running: https://expressjs.com/en/starter/installing.html. Even though this tutorial is in JavaScript, these steps can be translated to any language/framework of your choice.

You should have an express application setup with the following directory structure:

node_modules/
app.js
package-lock.json
package.json

And your app.js file should look something like this:

const express = require('express')

// put your public and secret keys here
const public_key = // public_key
const secret_key = // secret_key

// setup express
const app = express()
const port = 3000

// parses JSON payloads
app.use(express.json())

app.get('/', (req, res) => {
res.status(200).json({
message: 'hello world!'
})
})

app.listen(port, () => {
console.log(`Donation app listening on port ${port}`)
})

💡 You can find the public_key and secret_key at https://api.getchange.io/test/developers. Keep in mind that these are your test keys. Production keys can be found at https://api.getchange.io/developers or by toggling the “Viewing test data” button in the API Keys section to switch to production mode.

Change API test keys.

Running node app.js in the root directory of your application should give the following:

donation-app % node app.js
Donation app listening on port 3000

And going to http://localhost:3000 in your browser should show the following:

Hello Change JSON payload.

Once you see the above, we’re ready to start donating!

Making a donation

To create a donation using Change, we’re going to make a POST API request to Change using Javascript’s fetch API. Let’s make a new endpoint called /donate and populate the function with the following code:

app.post('/donate', (req, res) => {
fetch('https://api.getchange.io/api/v1/donations', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(public_key + ":" + secret_key).toString('base64'),
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 500,
nonprofit_id: 'n_OdoHlOZvrdJ0skpDC1AD7qL2',
funds_collected: false
})
})
.then(response => response.json())
.then(data => res.status(200).json(data))
.catch(error => res.status(500).json(error))
})

Let’s break down what’s happening in this function:

  1. We’re creating a POST API endpoint with the path /donate
  2. In the callback function which is triggered when a POST request is made to /donate we are making a POST request to Change’s donation endpoint https://api.getchange.io/api/v1/donations
  3. In the body, we are specifying the donation parameters of:
    1. amount: 500 the donation amount in cents ($5.00)
    2. nonprofit_id: n_OdoHlOZvrdJ0skpDC1AD7qL2 the unique identifier of the nonprofit in Change’s dashboard (Black Girls Code - https://api.getchange.io/nonprofits/n_OdoHlOZvrdJ0skpDC1AD7qL2)
    3. funds_collected: false whether you are collecting payment for the donation (this is for tax purposes at the end of the year - if you were making this donation on someone else’s behalf this value would be set to true
  4. After the fetch request is made, we parse the response body and return it to the client making the request

💡 To find a nonprofit_id in Change’s dashboard, use the nonprofit navigation at https://api.getchange.io/nonprofits to search for the nonprofit you’d like to donate to. The nonprofit_id can be found in the URL of the nonprofit’s profile page or on the nonprofit profile labeled “Nonprofit ID”.

Now that your app has all the code it needs, let’s run this thing. In your terminal, restart your instance using CTRL + C and node app.js . Restarting lets Express know that you’ve made changes to your app and it needs to reflect those changes. If this seems too tedious, you can look into the node package nodemon (https://github.com/remy/nodemon) which will automatically restart your server when you save files.

donation-app % node app.js
Donation app listening on port 3000
^C
donation-app % node app.js
Donation app listening on port 3000

💡 If you run into the error ReferenceError: fetch is not defined it means your Node version is too low! fetch is only available for Node versions 16 or greater. To check your Node version you can run the command node --version in your terminal. Either install Node version 16 or greater or use the node package node-fetch (https://github.com/node-fetch/node-fetch).

Once your server is up and running, we can try hitting the endpoint. Open up a new terminal window (without exiting the server) and run:

donation-app % curl -X POST http://localhost:3000/donate

If you see the response below, then congrats! You just made a donation using Change! 🥳

{
"amount":500,
"id":"d_t2mv7cRKPwMALE8NCKZIrO4I",
"live_mode":false,
"nonprofit_id":"n_OdoHlOZvrdJ0skpDC1AD7qL2",
"order_value":null,
"zip_code":null,
"external_id":null,
"metadata":{},
"currency":"USD"
}

The response JSON contains details about the donation you just made, for now the most important fields are:

  1. amount (Number) the amount of the donation in cents
  2. id (Number) the unique identifier of the donation you’ve just created
  3. live_mode (Boolean) a boolean which declares if this donation was made in our test or production environment
  4. currency (String) although not specified, donations made through the API endpoint will always be USD.

To check that your donation has gone through, you can view recent donations made on your developer page at https://api.getchange.io/test/developers.

Example donation on the Change dashboard.

To make the nonprofit and donation amount dynamic we can modify our function to use arguments from our POST request. Let’s modify our function to look like this:

app.post('/donate', (req, res) => {
const { amount, nonprofit_id, funds_collected } = req.body

fetch('https://api.getchange.io/api/v1/donations', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(public_key + ":" + secret_key).toString('base64'),
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
amount: amount,
nonprofit_id: nonprofit_id,
funds_collected: funds_collected
})
})
.then(response => response.json())
.then(data => res.status(200).json(data))
.catch(error => res.status(500).json(error))
})

And now our POST request is dynamic!

donation-app % curl -X POST http://localhost:3000/donate    \
-H "Content-Type: application/json" \
-d '{"amount": 1000, "nonprofit_id": "n_OdoHlOZvrdJ0skpDC1AD7qL2", "funds_collected": false }'

You should see the response below:

{
"amount":1000,
"id":"d_dk7HJ5MhAesMAg0mFxjm7Bry",
"live_mode":false,
"nonprofit_id":"n_OdoHlOZvrdJ0skpDC1AD7qL2",
"order_value":null,
"zip_code":null,
"external_id":null,
"metadata":{},
"currency":"USD"
}

And that’s it for making a donation! Congrats on making it this far! In the upcoming sections, we’re going to go over how to retrieve donations we’ve made via the Change API, and talk about some of the other fields in the donation JSON response object above, such as external_id and metadata.

For more formal documentation on everything our POST /donate API can do, go to: https://docs.getchange.io/api/#Donations

Retrieving donations

Next, we’re going to go over how to retrieve a donation we just created. Going back to our express file app.js, let’s create a new endpoint, GET /donation/:id:

app.get('/donation/:id', (req, res) => {
console.log(req.id)
fetch(`https://api.getchange.io/api/v1/donations/${req.params.id}`, {
method: 'GET',
headers: {
'Authorization' : 'Basic ' + Buffer.from(public_key + ":" + secret_key).toString('base64'),
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => res.status(200).json(data))
.catch(error => res.status(500).json(error))
})

In the previous endpoint we used the request body to make a request to our server. In this case, since we are making a GET request, we have to use a request parameter which is contained within the URL of the request.

Using our trusty terminal once again, we’re going to make a cURL request which hits the GET API endpoint we just created. Make sure to include the id at the end! You can get the donation ID from making a POST request in the previous section.

donation-app % curl http://localhost:3000/donation/<id>

You should see the response below:

{
"amount":1000,
"id":"d_dk7HJ5MhAesMAg0mFxjm7Bry",
"live_mode":false,
"nonprofit_id":"n_OdoHlOZvrdJ0skpDC1AD7qL2",
"order_value":null,
"zip_code":null,
"external_id":null,
"metadata":{},
"currency":"USD"
}

Amazing! Change makes it easy to retrieve details about donations you’ve made previously. This is super helpful for showing customers donation data such as how much has been donated on their behalf!

Moving quickly to the next step, let’s set up an endpoint to retrieve ALL donations we’ve made. Once again, let’s create another endpoint:

app.get('/donations', (req, res) => {
// do your business logic here

fetch(`https://api.getchange.io/api/v1/donations`, {
method: 'GET',
headers: {
'Authorization' : 'Basic ' + Buffer.from(public_key + ":" + secret_key).toString('base64'),
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => res.status(200).json(data))
.catch(error => res.status(500).json(error))
})

This time the cURL request is straightforward:

donation-app % curl http://localhost:3000/donations

You should see the response below:

{
"donations": [
{
"amount":1000,
"id":"d_dk7HJ5MhAesMAg0mFxjm7Bry",
"live_mode":false,
"nonprofit_id":"n_OdoHlOZvrdJ0skpDC1AD7qL2",
"order_value":null,
"zip_code":null,
"external_id":null,
"metadata":{},
"currency":"USD"
},
{
"amount":500,
"id":"d_t2mv7cRKPwMALE8NCKZIrO4I",
"live_mode":false,
"nonprofit_id":"n_OdoHlOZvrdJ0skpDC1AD7qL2",
"order_value":null,
"zip_code":null,
"external_id":null,
"metadata":{},
"currency":"USD"
}
],
"page": 1
}

We are cruising 🚗 through these endpoints!

With Change, you can easily retrieve all of the donations you’ve made in the past! Being able to retrieve all of your donations via API makes performing data analysis a breeze.

💡 We understand that handling data via API isn’t ideal for everyone, which is why we’ve placed an “Export” button on your donations page https://api.getchange.io/test/donations/ledger allowing you to download all of your donation data via CSV 😃

And that’s pretty much all you need to get started with Change’s APIs! Congratulations! 🎉 You’ve successfully integrated donations! For the rest of this post, we will be digging into some more advanced features of Change’s APIs: external_id and metadata.

Leveling Up Your Donation Game ⬆️

To round the final corner of this guide, we’re going to revisit the APIs we’ve already seen and go over some features which we initially glazed over. I will also be giving example scenarios as to why you might want to include these features in your application!

Both external_id and metadata are fields you can add when creating a donation and will be stored with your donation. While external_id takes a string, metadata uses an object to store as much additional information as you want on the donation. The only other difference between the two fields is that you can filter your donations via external_id when retrieving donations via API while metadata is exclusively used for additional details. We will go over some examples to solidify these topics.

Scenario

We are an online business selling different types of tea. For each bag of green tea purchased, we will donate $1 to Black Girls Code. If it’s any other kind of tea purchased, we will only donate $0.50 to Black Girls Code. Let’s design an endpoint which is hit when a customer buys tea:

app.post('/donate', (req, res) => {
const types = ['jasmine', 'green', 'black', 'oolong']
const { type, customer_id } = req.body
if (!types.includes(type)) return res.status(400).json({message: 'invalid tea type'})
// update our database with a new purchase of tea of type 'type'
fetch('https://api.getchange.io/api/v1/donations', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(public_key + ":" + secret_key).toString('base64'),
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
external_id: customer_id, // we can use this to track customers
amount: type === 'green' ? 100 : 50, // $1 if green tea, 50 cents otherwise
nonprofit_id: "n_OdoHlOZvrdJ0skpDC1AD7qL2", // Black Girls Code nonprofit ID
funds_collected: false, // we are donating this money, not the customer
metadata: {
tea_type: type // we can use this to track tea types
}
})
})
.then(response => response.json())
.then(data => res.status(200).json(data))
.catch(error => res.status(500).json(error))
})

Great, now let’s try it out! Based off the code it looks like our POST /tea request takes in two payload fields: type of value jasmine, green, black, or oolong and customer_id which will be the ID of the user in our system. In our case we will just use the ID c_123.

donation-app % curl -X POST http://localhost:3000/donate    \
-H "Content-Type: application/json" \
-d '{"type": "green", "customer_id": "cus_123" }'

You should see the response below:

{
"amount":100,
"id":"d_7RgHPINvr86wHBGC8aBGXkFV",
"live_mode":false,
"nonprofit_id":"n_OdoHlOZvrdJ0skpDC1AD7qL2",
"order_value":null,
"zip_code":null,
"external_id":"c_123",
"metadata":{
"tea_type":"green"
},
"currency":"USD"
}

As you can see, our returned donation object now has values for fields external_id and metadata! If you click on your latest donation either on https://api.getchange.io/test/developers or https://api.getchange.io/test/donations/ledger you will see the donation you just created, as well as the external_id and metadata fields you’ve supplied.

Example of donation details in the Change dashboard.

Storing this data in Change allows you to come back and retrieve it when your system is ready and allows Change to do more with your donation data. Take for example the GET /donations endpoint we were using previously. Now that you’ve supplied a donation with an external_id, you can specifically query the GET /donations endpoint for donations with that external_id! Since we supplied our customer ID cus_123 as the external_id, we can now retrieve all donations a customer has contributed to! Create a new endpoint called /tea-donations:

app.get('/tea-donations', (req, res) => {
const allowedKeys = ['nonprofit_id', 'external_id', 'start_timestamp', 'end_timestamp']
const filteredParams = {};
var querystring = require('querystring');
for (const key in req.query) {
if (allowedKeys.includes(key)) filteredParams[key] = req.query[key]
}
fetch(`https://api.getchange.io/api/v1/donations?${querystring.stringify(filteredParams)}`, {
method: 'GET',
headers: {
'Authorization' : 'Basic ' + Buffer.from(public_key + ":" + secret_key).toString('base64'),
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => res.status(200).json(data))
.catch(error => res.status(500).json(error))
})

And as always run the cURL request:

donations-app % curl -G http://localhost:3000/tea-donations --data-urlencode "external_id=cus_123"

You should see the response below:

{
"donations":[
{
"amount":100,
"id":"d_7RgHPINvr86wHBGC8aBGXkFV",
"live_mode":false,
"nonprofit_id":"n_OdoHlOZvrdJ0skpDC1AD7qL2",
"order_value":null,
"zip_code":null,
"external_id":"cus_123",
"metadata":{
"tea_type": "green"
},
"currency":"USD"
}
],
"page":1
}

The above request URL is equivalent to http://localhost:3000/tea-donations?external_id=cus_123

As you can see, only the donations associated to cus_123 were returned and none of the donations we made previously. GET /donations has other built in query parameters such as nonprofit_id, start_timestamp, and end_timestamp . Check them out at the docs here: /api/v1/donations.

And that’s it for external_id and metadata. Thanks for making it all the way here! Change is constantly iterating on top of our core APIs so stay tuned as we add more ways to utilize the external_id and metadata fields to unlock even more insights for our customers.

Last but not least, moving to production…

All these APIs are great and all but don’t serve much purpose if they’re all made in test mode. But don’t worry – we make switching to production donations easy! Simply swap out your test keys for the production keys here: https://api.getchange.io/developers. And you’re done! It’s as simple as that. Now all your API requests will be made in production instead of in test mode!

And that wraps things up! Make yourself a nice cup of green tea 🍵 (or whatever your cup of tea is) as a treat for making it to the end! Thank you for reading and happy donating! ❤️

You can find the entire repo for this post at https://github.com/get-change/donations-api-tutorial and as always, if you have any more questions, feel free to click the chat button on this page! ✌️😎

Made with ❤ in San Francisco | Changelog | Status