listen up

Harnessing the Power of the Classy API to drive online donations

We humans love to see the success stories. It’s why we watch movies, award shows, and get sucked into a good book. We like to see if the hero gets the job done and wins the heart of that special someone at the same time. Seeing the win is just something that draws us in. Let’s get technical today and share how we can create a powerful call to action around your story, and drive up donations with donation management tools from Classy.org, and some custom code.
If you’re a non-profit that uses Classy.org to accept and track donations, you know there is some deep data there. Using the metrics from your campaigns and fundraisers we can show some powerful information and create clear call to action elements to increase your donations. We accomplished this beautifully for Lifewater International on their Blessed campaign by adding amounts raised and “thermometer” charts to show their donors just where they were with donations.

Let’s dive in and find out how we can access the API. You’ll need to be familiar with JavaScript and some PHP/Composer knowledge to get up and running. We are also going to assume you are working on Mac OSX/macOS for this tutorial.

Setting Up Classy Authentication

With the newest iteration of the Classy API we must now authenticate our calls with OAuth2.0. If you are familiar with OAuth2.0 you can use your preferred tool to authenticate, but for this example we will use Composer / Guzzle to get started.

1 – Install Composer and Guzzle

First of all, what is Composer? From getcomposer.org:

Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.

Navigate to your project directory and install Composer locally using:

curl -sS https://getcomposer.org/installer | php

After composer installed, we will need to navigate to our project folder and setup our Composer dependencies – which in this case – is Guzzle.
Create a file called composer.json and add Guzzle. Your file should look like this:

{
    "require": {
        "guzzlehttp/guzzle": "~6.0"
    }
}

Then execute the install process via the command line:

php composer.phar install

Now we have Composer and Guzzle all setup and ready to rock. Let’s connect it to Classy and start getting some data!

2 – Setting Up the Classy API Call, Generating a Token, and Getting Campaign Data

Now, in our project directory, lets create a file called classy.php.
The first thing we will need to do is autoload Guzzle.

<?php
/*
 * @Package: Classy
 * @Author: Eric Stout (factor1)
 * @Version: 0.0.1
 *
 */
require_once('vendor/autoload.php');

 
This will load all the functions we need to create our response. Next, we will setup our client ID and secret key (you can obtain these from classy.org) as well as the client.


(Note: Be sure to protect your Secret Key. It’s a good idea to exclude it from public repositories and from public view)


Adding to our classy.php file:

<?php
// Substitute w/ your own values.  PROTECT YOUR SECRET.  Do not put in public repo!
$clientId = 'XXXXXXXXXXXXX';
$clientSecret = 'XXXXXXXXXXXXX'; // it may be a good idea to make this a variable in a separate file
$client = new GuzzleHttp\Client();

Now we will setup the request, handle errors, and generate a token to get values with. Continue adding to classy.php:

<?php
try {
    // Use credentials to receive access token
    $response = $client->request(
        'POST',
        'https://api.classy.org/oauth2/auth',
        [
            'form_params' => [
                'grant_type' => 'client_credentials',
                'client_id' => $clientId,
                'client_secret' => $clientSecret
            ]
        ]
    );
    // return a JSON response
    $result = json_decode($response->getBody(), true);
    $accessToken = $result['access_token'];
} catch (Exception $ex) {
    // Handle Errors ...
    echo $ex->getMessage() . "\n";
}

Now that we have a token, we can grab data from their API endpoints. You can find more documentation on the Classy API here: https://developers.classy.org/api-docs/v2/index.html. In this example we will be grabbing data sets for campaigns. Again, adding to classy.php:

<?php
// Fetch a campaign
function getCampaign($accessToken, $client, $id){
  $response = $client->request(
      'GET',
      'https://api.classy.org/2.0/campaigns/'.$id,
      [
          'headers' => [
              'Authorization' => "Bearer $accessToken"
          ]
      ]
  );
  echo $response->getBody();
}

Here, we’ve created a function called getCampaign and we will use this function to actually make the call. The function takes 3 parameters, $accessToken, $client, $id. The $id parameter is the ID of the campaign you are trying to call, which can be found in Classy. The last step for our classy.php file is to call the getCampaign function. For this example we are calling a Lifewater.org campaign.

<?php
getCampaign($accessToken,$client,91466);

So to recap, your classy.php file should look similar to this at this point:

<?php
/*
 * @Package: Classy
 * @Author: Eric Stout (factor1)
 * @Version: 0.0.1
 *
 */
require_once('vendor/autoload.php');
// Substitute w/ your own values.  PROTECT YOUR SECRET.  Do not put in public repo!
$clientId = 'XXXXXXXXXXXXX';
$clientSecret = 'XXXXXXXXXXXXX'; // it may be a good idea to make this a variable in a separate file
$client = new GuzzleHttp\Client();
try {
    // Use credentials to receive access token
    $response = $client->request(
        'POST',
        'https://api.classy.org/oauth2/auth',
        [
            'form_params' => [
                'grant_type' => 'client_credentials',
                'client_id' => $clientId,
                'client_secret' => $clientSecret
            ]
        ]
    );
    // return a JSON response
    $result = json_decode($response->getBody(), true);
    $accessToken = $result['access_token'];
} catch (Exception $ex) {
    // Handle Errors ...
    echo $ex->getMessage() . "\n";
}
// Fetch a campaign
function getCampaign($accessToken, $client, $id){
  $response = $client->request(
      'GET',
      'https://api.classy.org/2.0/campaigns/'.$id,
      [
          'headers' => [
              'Authorization' => "Bearer $accessToken"
          ]
      ]
  );
  echo $response->getBody();
}
getCampaign($accessToken,$client,91466);

Great! We did it! So what does this return? A nice JSON response with lots of data to access:

{
  "address1": "533 F Street",
  "allow_ecards": false,
  "allow_fundraising_pages": true,
  "allow_duplicate_fundraisers": false,
  "category_id": 1,
  "channel_id": 2,
  "channel_keywords: `some_keyword`": "Hello, world!",
  "city": "San Diego",
  "classy_mode_appeal": "false",
  "classy_mode_checked_by_default": false,
  "classy_mode_enabled": false,
  "collect_shipping_address": false,
  "contact_email": "test@classy.org",
  "contact_phone": "619-555-1212",
  "country": "US",
  "created_at": "2016-01-01T12:00:00+0000",
  "created_with": "classyapp",
  "default_page_appeal": "Please donate to my fundraising page",
  "default_page_appeal_email": "Please donate to my fundraising page",
  "default_page_post_asset_id": 1,
  "default_page_post_body": "Lorem ipsum",
  "default_page_post_title": "Welcome to our team page!",
  "default_team_appeal": "Please donate to my fundraising team",
  "default_team_appeal_email": "Please donate to my fundraising team",
  "default_team_post_asset_id": 1,
  "default_team_post_body": "Lorem ipsum",
  "default_team_post_title": "Welcome to our team page!",
  "default_thank_you_text": "Thank you so much!",
  "designation_id": 10,
  "disable_donation_attribution": false,
  "goal": 123.45,
  "hide_anonymous_donations": false,
  "hide_contact_opt_in": false,
  "hide_dedications": false,
  "hide_donation_comments": false,
  "host_member_id": 1001,
  "name": "My Campaign",
  "offer_dedication_postal_notifications": false,
  "opt_in_checked_by_default": false,
  "organization_id": "101",
  "postal_code": "92101",
  "send_dedication_emails": false,
  "started_at": "2016-01-01T12:00:00+0000",
  "state": "CA",
  "status": "active",
  "timezone_identifier: `America/Los_Angeles`": "Hello, world!",
  "type": "crowdfunding",
  "updated_at": "2016-01-01T12:00:00+0000",
  "venue": "City Townhall"
}

We have a few key values to take a look at here in this response. We get the name of the campaign as well as the goal of the campaign and this is very useful when trying to display how much has been raised as well as whats left to go. But now what? We need to do something with this response. So, lets save it to our server so that we can reference the classy data later.
Let’s modify our getCampaign function a bit to create a JSON file.

<?php
// Original function
function getCampaign($accessToken, $client, $id){
  $response = $client->request(
      'GET',
      'https://api.classy.org/2.0/campaigns/'.$id,
      [
          'headers' => [
              'Authorization' => "Bearer $accessToken"
          ]
      ]
  );
  echo $response->getBody();
}
getCampaign($accessToken,$client,91466);
// Updated function that saves a file for us
function getCampaign($accessToken, $client, $id){
  $response = $client->request(
      'GET',
      'https://api.classy.org/2.0/campaigns/'.$id,
      [
          'headers' => [
              'Authorization' => "Bearer $accessToken"
          ]
      ]
  );
  // Name the file output, here we add the $id variable so we can have multiple files if needed
  $file = 'classy-'.$id.'.json';
  // Gets content of the file and overwrites when data changes
  $current = file_get_contents($file);
  // Add the response to the file
  $current = $response->getBody();
  // Create the file!
  file_put_contents($file, $current);
  return;
}
getCampaign($accessToken,$client,91466);

Great! Now we have a a response that will create a file with our data when it is called. There are a few ways you can handle executing this file to create updates to the data/generate a new response. For testing purposes you can just open your browser and navigate to classy.php and it will generate. You could also trigger this within your template, another php function, an ajax request, or even a cron job – whatever works best for your implementation.
You may have noticed that while we have the goal for the campaign we don’t have any other data such as how much money has been raised. Strangely, Classy stores this information within a different endpoint. So lets dive into getting more detailed information about our campaign.
Adding to our classy.php file, lets create a new function called getCampaignOverview():

<?php
function getCampaignOverview($accessToken, $client, $id){
  $response = $client->request(
    'GET',
    'https://api.classy.org/2.0/campaigns/'.$id.'/overview',
    [
      'headers' => [
          'Authorization' => "Bearer $accessToken"
      ]
    ]
  );
  $file = 'classy-'.$id.'-overview.json';
  $current = file_get_contents($file);
  $current = $response->getBody();
  file_put_contents($file, $current);
  return;
}

This function is nearly identical to our first campaign function, with the exception of the endpoint url. It also generates a file for us (classy-{id)-overview.json) for us to grab data from. A typical response from this endpoint would look similar to this:

{
  "start_time_utc": null,
  "end_time_utc": null,
  "gross_amount": "8850.00",
  "fees_amount": "66.50",
  "net_amount": "8783.50",
  "transactions_count": 39,
  "donors_count": 39,
  "registrations_amount": "0.0000",
  "donations_amount": "8850.0000",
  "total_gross_amount": "8850.00",
  "donation_net_amount": "8783.50"
}

Now that we have this data, we can use it to calculate amount left, amount raised, or how many people have donated and display it on your site which can be great information to show to potential donors and create a sense of urgency.
Your finished classy.php file should look similar to this:

<?php
/*
 * @Package: Classy
 * @Author: Eric Stout (factor1)
 * @Version: 0.0.1
 *
 */
require_once('vendor/autoload.php');
// Substitute w/ your own values.  PROTECT YOUR SECRET.  Do not put in public repo!
$clientId = 'XXXXXXXXXXXXXX';
$clientSecret = 'XXXXXXXXXXXXX'; // it may be a good idea to make this a variable in a separate file
$client = new GuzzleHttp\Client();
try {
    // Use credentials to receive access token
    $response = $client->request(
        'POST',
        'https://api.classy.org/oauth2/auth',
        [
            'form_params' => [
                'grant_type' => 'client_credentials',
                'client_id' => $clientId,
                'client_secret' => $clientSecret
            ]
        ]
    );
    // return a JSON response
    $result = json_decode($response->getBody(), true);
    $accessToken = $result['access_token'];
} catch (Exception $ex) {
    // Handle Errors ...
    echo $ex->getMessage() . "\n";
}
// Fetch a campaign
function getCampaign($accessToken, $client, $id){
  $response = $client->request(
      'GET',
      'https://api.classy.org/2.0/campaigns/'.$id,
      [
          'headers' => [
              'Authorization' => "Bearer $accessToken"
          ]
      ]
  );
  // Name the file output, here we add the $id variable so we can have multiple files if needed
  $file = 'classy-'.$id.'.json';
  // Gets content of the file and overwrites when data changes
  $current = file_get_contents($file);
  // Add the response to the file
  $current = $response->getBody();
  // Create the file!
  file_put_contents($file, $current);
  return;
}
function getCampaignOverview($accessToken, $client, $id){
  $response = $client->request(
    'GET',
    'https://api.classy.org/2.0/campaigns/'.$id.'/overview',
    [
      'headers' => [
          'Authorization' => "Bearer $accessToken"
      ]
    ]
  );
  $file = 'classy-'.$id.'-overview.json';
  $current = file_get_contents($file);
  $current = $response->getBody();
  file_put_contents($file, $current);
  return;
}
getCampaign($accessToken,$client,91466);
getCampaignOverview($accessToken,$client,91466);

Overview

You did it! Hopefully you have a functioning API call to Classy to gather information about your campaign. Since you’ve made it this far, pat yourself on the back. If you have any questions or would like to implement this on your site please reach out to us at https://factor1studios.com/contact/.