Git Product home page Git Product logo

collective.salesforce.fundraising's Introduction

Introduction
============

collective.salesforce.fundraising is an online fundraising system designed to help non-profit organizations raise money online.  

The system was initially developed by The Innocence Project, Inc. who sponsored the development with internal staff as well as consulting from Groundwire and design services from Exotic Objects (http://www.exoticobjects.com)

Additional development on personal fundraising and event ticketing was funded by the Innocence Project and jointly developed with Jazkarta (http://www.jazkarta.com).

The first production site is now live at https://secure.innocenceproject.org

In general, the system is currently designed to function as a standalone donation system.  It requires a lot of new packages from Plone and thus may be difficult to integrate into an existing Plone site.

Features
========

*  Self Hosted, PCI SAQ A - A key design requirement was to avoid the cumbersome hosting and process requirements of PCI SAQ C instead preferring the much simpler PCI SAQ A.  PCI SAQ A avoids the requirement for PCI compliant web hosting saving hundreds per month.  The system never touches credit card data, even to receive from the browser and retransmit to an API.  All donation submissions are sent directly to the payment processor.  No credit card data is ever stored or transmitted by your server.

*  Integration with Salesforce Campaigns - Campaigns can be created either in Salesforce or in Plone and are always linked back to a Campaign in Salesforce

*  Campaigns with Timeline and Goal - If a Campaign is configured with a goal (Expected Revenue in Salesforce) and a start/end date (Start/End date in Salesforce), progress indicators in the right column indicate the progress towards the goal and the time remaining to reach the goal.

*  Personal Fundraising - Allow users to create personal campaign pages off any campaign.  Users can set a goal and promote their campaign to their friends.  Personal Campaigns are created as a child Campaign of the main Fundraising Campaign in Salesforce and all donations rollup to the parent campaign in the Hierarchy.

*  Donation Products - "Sell" a selectable quantity of Donation Products in a Fundraising Campaign.  Products can be physical or virtual items or event tickets.  Each products gets its own donation form tab and all donations in Salesforce are linked to the Product.

*  Product Forms - Build a custom donation form through the web with products grouped into fieldsets, optional donation only products (i.e. additionaly donation on ticket purchase), and custom fields mapped back to Salesforce objects (custom fields coming soon).

*  Mailchimp/Mandrill Email Receipts - Use Mailchimp (http://mailchimp.com)  templates to send formatted thank you receipts to donors through the Mandrill (http://mandrill.com) transactional email service.  New templates can be created either in Mailchimp or uploaded directly through the site.  The Mailchimp template is then sync'ed with Mandrill as a template.  The configured templates are then available for selection on each Fundraising Campaign allowing highly stylized receipt emails customized to the campaign.

*  Honorary/Memorial Donations - A checkbox on the donation form allows the user to classify their donation as an Honorary or Memorial donation.  On the next page after submitting the donation, the user can enter the Honorary/Memorial information including optionally sending a notification of their donation via either email (automatically sent by the system) or mail (manually handled through Salesforce)

*  Donor Quotes - After donating, users are prompted to share the campaign or enter a Donor Quote (Testimonial).  Donor Quotes are moderated and once approved are randomly selected for display in a box in the right column

*  Social Integration via Janrain - Janrain is a social login and sharing service.  We've integrated Janrain for social login, mainly used by personal fundraisers, and social sharing to help spread the word about the campaign on multiple social networks

*  Social Share Messages - Inside a campaign, admins can add Share Messages which contain a title, description, image, and default user comment.  On the Share Campaign page, users are presented with 3 randomly selected Share Messages to choose from.  Any donations that come from clicking on a Share Message shared to social networks is tracked back to the Share Message as the Source Campaign.  This allows tracking of the effectiveness of each Share Message

*  Support for one time and recurring donations - The recommended payment processor integration is Stripe (https://stripe.com).  Stripe encodes the credit card data on the client side into a token which can then safely be passed to your server to represent the credit card data.  This approach allows the donation form to post back to your server but without any sensative credit card data in the request.  The Stripe integration is also capable of handling recurring and one time payments through one system.   Previously, one time and recurring donations were handled by Authorize.net Direct Post Method (DPM) for one time and Recurly for recurring.  However, the new integration with Stripe is the recommended payment integration as it is far smoother, free to setup, and less expensive.

*  Sensable site wide defaults with override per campaign - In order to make the process of launching a new campaign as easy and quick as possible, the system stores a site wide configuration for standard fundraising campaigns.  When creating a campaign, you can always override the site defaults for the individual campaign if needed.

*  Configurable "Campaign Seals" per Campaign - A common best practice is to include fundraising "seals" or badges on the donation form to let donors know about third party analysis and endorsements of the organization as well as display charts on the distribution of funds.  Seals can be added by administrators through the web and linked to individual campaigns or sitewide defaults.  Seals contain a compact view with a More Info link to show more details.

*  Simple and Modern UI - Much effort was put into optimizing the user experience to avoid confusing or overwhelming the user while still presenting relevant information at the right time

Installation
============

You can build out a local version of the system using the supplied buildout.cfg file in the root of the package.  Assuming virtualenv for python 2.7.x is in your path already, you should be able to checkout and have a running instance with the following on a Mac (ideally, install libjpeg libraries) or Linux system:

    git clone [email protected]:innocenceproject/collective.salesforce.fundraising.git
    virtualenv --no-site-packages collective.salesforce.fundraising
    cd collective.salesforce.fundraising
    source bin/activate
    python bootstrap.py

    # Run the buildout
    bin/buildout

    # Fire up the instance on port 8080
    bin/instance fg

With a freshly created Plone site, you will want to do the following:
- Install "Fundraising for Salesforce.com" in Add/Remove Products under Site Setup.
- In the ZMI, set the username and password for Salesforce portal_salesforcebaseconnector 
- Go to Fundraising Settings in Site Setup and fill out the form
- Go to Stripe Settings in Add/Remove Products and enter your test and live Stripe keys (https://stripe.com)

Salesforce Setup
================

The system assumes the existence of a number of custom fields in Salesforce.  For now, the following fields will need to be manually created in your Salesforce instance.  In the future, there will be an installable package for Salesforce with the needed customizations.

Opportunity (Donation)
----------------------

Honorary_City__c
Text(128)

Honorary_Contact__c
Lookup(Contact)

Honorary_Country__c
Text(128)

Honorary_Email__c
Email

Honorary_First_Name__c
Text(64)

Honorary_Last_Name__c
Text(64)

Honorary_Message__c
Long Text Area(32768)

Honorary_Notification_Type__c
Picklist

Honorary_Recipient_First_Name__c
Text(64)

Honorary_Recipient_Last_Name__c
Text(64)

Honorary_State__c
Text(128)

Honorary_Street_Address__c
Text(255)

Honorary_Type__c
Picklist

Honorary_Zip__c
Text(32)

Parent_Campaign__c
Lookup(Campaign)

Source_Campaign__c
Lookup(Campaign)

Source_URL__c
URL(255)

Success_Transaction_ID__c
Text(64) (External ID) (Unique Case Insensitive)


OpportunityProduct
------------------

Campaign__c 
*Lookup(Campaign)*

Fundraising_URL__c
*URL(255)*


Contact
-------

Email_Opt_In__c
*Checkbox*

Online_Fundraising_User__c
*Checkbox*


collective.salesforce.fundraising's People

Contributors

calvinhp avatar cdw9 avatar cewing avatar cguardia avatar claytron avatar davisagli avatar hathawsh avatar jlantz avatar jlantz-ip avatar rpatterson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

rpatterson tet3

collective.salesforce.fundraising's Issues

Portlet: Campaign Timeline

The Campaign Timeline shows where the campaign is at in comparison to its start and end dates. The element is only displayed if there is a configured Start Date for the campaign. If there is no configured end date, instead show a counter of how many days have elapsed since the start of the campaign. If the current time is before the Start Date, the element should display the number of days until the campaign starts. If the current time is after the End Date, the element should show that the campaign has now ended.

Prevent multiple Personal Campaign Pages for same campaign from same user

There are 2 ways this could happen:

  1. The Donation Fundraisers portlet currently does no checking if the user already has a personal campaign page for the campaign before showing the link.
  2. If a user created a page, then returns but does not log in, they will see the link to create a personal campaign page. Clicking it will bring them to the login page, then back to the form. Instead, once a user has logged in, they should be redirected to their campaign if one exists.

Portlet: Campaign Goal

The Campaign Goal element shows the current status of the campaign towards reaching its goal. It is only shown on a campaign with a set goal and which has raised above a configured threshold.

Recurly Integration

After getting really excited that Authorize.net's DPM would eliminate any PCI issues, I discovered that it doesn't work with ARB or CIM. Thus, there's now way with DPM to handle recurring donations.

I settled on using Recurly (http://www.recurly.com) which seems pretty reasonable option. Their recurly.js library and the python library published on pypi make a PCI compliant integration to manage recurring billing pretty easy. Since they charge 1.25% plus $.10 on top of merchant account fees, it still makes sense to use Authorize.net's DPM for one time donations. There's a $69/month minimum.

A goal of the system was to keep credit card data far enough away from the system to allow for PCI SAQ A. Using DPM and Recurly seems to accomplish that goal.

Initiating recurring donations in Recurly requires the following changes on the python side:

  • Control panel fields for subdomain, api key, and private key
  • Control panel field for plan code. * NOTE: This should probably be changed to be set per campaign for great flexibility
  • A view method to generate a signature for the javascript which is passed through the view's template. The method just needs the plan name and the recurly python library
  • A view to receive the post callback after successful subscription creation. This method receives a token via post. The recurly python library can then be used to fetch the subscription details from the Recurly API and redirect the user to the thank you page.

On the javascript side, loading the Recurly form is pretty simple. You pass some config variables and a jquery selector of where to place the form and the form is injected into the page's html. The form html is very elegantly structured with good selectors allowing for easy customization.

There is a Salesforce integration available from Recurly but it is an additional monthly charge. I still need to figure out how to integrate the payments and subscription profiles.

The recurly.js library also allows for creation of Update Subscription forms allowing users to update their billing information from your website while keeping all credit card data flowing through PCI Level 1 channels. Integrating subscription updating should be on the list as well.

Support Copying a Fundraising Campaign

Currently, doing a copy in Plone of a Fundraising Campaign creates another campaign with the same sf_object_id and doesn't create a new campaign in Salesforce. This should be handled better as it would also not be good to have 2 Fundraising Campaign objects with the same sf_object_id as much of the code assume a lookup by sf_object_id will only return one result.

Remove dependency on collective.salesforce.content

The initial design of the system was based around collective.salesforce.content with the assumption that campaigns would originate from Salesforce and be pushed to Plone. However, in practice, it has proven to be much easier to manage campaigns from the Plone side. There is no longer functionality which relies on collective.salesforce.fundraising.

Part of this implementation should include a way to sync campaign values (total donations, donation count, etc) from Salesforce to Plone.

Create Fundraising Theme

Create a simple diazo theme product optimized for running collective.salesforce.fundraising as a standalone fundraising site. The theme should use the non-proprietary portions of ip.theme.fundraising including the responsive design styles and provide a baseline for building a fundraising oriented theme.

Better Image Handling for Donor Quote

Currently, a Donor Quote has its own image field that's not really integrated with anything else. In order to increase the odds of having a good image with a quote which makes it much more effective, we should add the following:

  • When adding a quote, automatically use the user's Portrait from their profile if available allowing the user to override the portrait with a new file if they wish
  • If a new image is uploaded, store it as the user's Portrait (this is a bit subjective but seems to make sense in the context of the system)
  • Add a site default profile pic to fall back to if no image is found

Switch from beatbox to collective.simplesalesforce

The beatbox library is starting to have errors which seem related to recent changes in the Salesforce API. No one is maintaining the library. It uses SOAP. The new simple-salesforce library is much cleaner and uses the REST api. Switch to it.

Javascript error if compression enabled on fundraising.js

If the fundraising.js script is marked for compression in portal_javascripts, running in non-development mode causes a syntax error about missing }.

Easiest solution is to change installer to mark fundraising.js for no compression

Portlet: Donor Quotes

The Donor Quotes page element displays a randomly selected (possibly rotating) Donor Quote for the current campaign. If no quote exists and the campaign is a Personal Fundraising Campaign, quotes are selected from the parent Fundraising Campaign. If no quotes are found for the Fundraising Campaign, the element is not displayed.

Split out Janrain Functionality

I'm thinking it probably makes sense to create two packages from this:

  • collective.rpx.auth
  • collective.rpx.share

The share package would contain all the methods for initializing the javascript sharing widget with a custom title, description, url, image, and initial user comment (can be edited by the user). This is a much more flexible alternative to using the Facebook meta tags as you can have different messages pointing to the same page. The drawback is that the end user must grant rights to your FB app.

Donor Quotes Listing and Detail Page

Add a new view on a campaign that lists all its donor quotes in a paginated list. Create a public view of each quote with the donation form below the quote. Include a share button on the donor quote page, mainly for the donor to share their quote with friends.

personal campaigns need to get published

When you add a personal campaign, it's not obvious that you need to publish it before anonymous visitors can see it. Should we make it get published immediately?

In a related question, is there any reason for the green edit bar to appear for people who are only logged in to add personal campaigns? Should I find a way to hide it for them?

Cannot edit Donor Quote

When attempting to edit donor quote, the Title field is required but not relevant and the Description field is not relevant though not required. The Campaign Salesforce ID field is also not being populated causing an error on edit as it's required.

Use Donation contact info instead of creating a Person object

Currently, after a donation, a Person object is created in the background automatically with a random password which is never sent to the donor. The donor never sees the password or even knows they have a user account on the site in the background. This was done to make it easier to sync Contacts via add/edit event handlers on Person.

However, the new Donation object captures all the data needed to sync with Contacts making the creation of a Person object unnecessary.

For now, the Person object should remain for the purpose of personal fundraising.

Authorize.net DPM Postback loses Source Campaign and Source URL

With DPM, auth.net's servers post back the transaction info which is used to create the Salesforce data. Since the source info is cached in a cookie in the client's browser, the postback from auth.net's servers doesn't have the cookie. Thus, while the client's browser has the correct source info, it's not available at the time of creation in Salesforce.

The source url and campaign should be passed as custom fields in the DPM form and parsed out of the postback from auth.net.

Form Tabs List Not Populating Correctly

The default form tabs when creating a new Fundraising Campaign is being split as a string instead of as a list producing one letter per line as opposed to one line per line.

Thank You View

The Thank You page is shown to a donor after they have donated. They are presented with a configurable Thank You message for the campaign as well as with options to amplify their donation by taking the following actions:

  • Share with friends
  • Create a Personal Fundraising Campaign
  • Provide a Donor Quote along with an optional photo and name

Portlet: Campaign Counts

The Campaign Counts element shows counts relevant to the campaign such as the number of donations, the number of personal fundraisers, ???

Don't use inline inserted Javascript for Donation Form

Since the form embed code is inserted inline, it can block the page from loading if formstack is taking a while to respond. We could avoid this by inserting the embed code using javascript after the page has loaded.

Sync breaks if parent campaign doesn't already exist

It appears that collective.salesforce.content is syncing the personal campaigns first which results in an error if the parent Fundraising Campaign hasn't been created yet. This makes sense since personal campaigns are stored inside their parent.

Port Recurly to new Donation object process

Recurly integration was implemented with the old method of directly writing to Salesforce rather than writing donation data to Plone. That method is no longer recommended. The new method was implemented as part of Stripe integration.

It seems as though Recurly may be useful in more international use cases such as the Plone Foundation and thus should be brought forward to the new process (unlike Authorize.net DPM which should be removed soon).

Configurable Campaign Messages and Defaults

The Fundraising Campaign type should have fields allowing the admin to set messages used on views as well as default values presented to the user for personalization. The following fields are needed:

  • Thank You: The content body of the Thank You page thanking the donor. Should provide a sensible default text
  • Personal Campaign - Thank You: The default for the Thank You field when creating a personal campaign
  • Personal Campaign - Pitch: The default for the Pitch field when creating a personal campaign

Personal Campaign Pages should be created by user on sync

Currently, a Personal Campaign Page sets the creator correctly if created from Plone. However, if the campaign is setup in Salesforce, the creator will be the user running the collective.salesforce.content sync rather than the actual user. On sync, the email of the contact should be used to set up the creator on the new object in Plone.

This is a lower priority as I don't anticipate a need to create personal fundraisers from Salesforce.

Include Support for Assigning a Fund to a Campaign

Many nonprofits support multiple donation funds allocated towards different purposes. The system should support optionally assigning a donation campaign to a fund.

This change was brought about by 2 requests. First, we wanted to be able to show on the receipt if a donation was allocated to a specific fund. Second, we wanted to allow a donor to donate a recurring donation to more than one fund which was currently not possible as the Recurly integration assumes there is only 1 plan for recurring.

The concept of this feature proposal is that a nonprofit maintains a general fund with no specific allocation as well as possibly maintaining dedicated funds for donations allocated to specific uses. Since most nonprofits would prefer donations to a general fund due to the flexibility, it is assumed that most campaigns will be towards the general fund. Keeping with the philosophy of launching a normal campaign as fast as possible, if a campaign is towards the general fund, no value has to be provided for Fund when creating a campaign. A value only needs to be provided if making a non-standard campaign (i.e. a campaign designated to a specific fund).

The following changes need to happen to achieve this functionality:

  • Add new Fund__c field to Campaign in Salesforce
  • Map the Fund__c field in Salesforce to the "fund" field on Fundraising Campaign
  • Add a field to Fundraising Campaign for Recurly Plan Code. If no value is provided, use the global value in registry
  • If Recurly is enabled, create a new plan via the Recurly API whenever a new fund name is found and add the new plan's code to the Recurly Plan Code field on the campaign
  • Include Fund on the donation receipt if a fund was specified.

Authorize.net DPM Integration

Authorize.net's Direct Post Method is designed to allow developers to build custom html forms with the user experience they want for their users while still keeping credit card data from being transmitted by the developer's system and thus avoid PCI headaches. DPM requires the form contain fields with a transaction fingerprint generated by server side code. The fingerprint is a combination of the API Login ID, transaction id (generated by developer), current UTC timestamp, and amount of transaction. The form fields must also conform to the field names for the DPM service. When the form is submitted, the submissions goes straight from the user's browser to Authorize.net without ever contacting the host system. Authorize.net then calls a callback url to notify the host system of the transaction ad expects a response back with where to redirect the user. Once the response is received, Authorize.net issues a redirect to the client's browser. The client's browser should remain on the host's url and never show authorize.net in the process yet remains fully PCI compliant.

DPM also has full support for Automated Recurring Billing (ARB).

Full docs here:
http://developer.authorize.net/guides/DPM/wwhelp/wwhimpl/js/html/wwhelp.htm

Requirements:

  • A view for fundraising campaigns and personal campaign pages to render the form. The form html should come from the prototype developed for ip.theme.fundraising.
  • Register and include all css and js from ip.theme.fundraising relevant to the donation form.
  • A view to render the transaction fingerprint. DPM normally assumes a shopping cart type checkout process where items are selected, then the checkout page is called for payment details on the selected items. Since the cart is not changed on the payment page, the fingerprint can come from the server at time of rendering the form. However, in the donation form, the amount and payment details can be edited in the same place. Javascript should call this view whenever the amount field changes to get a new fingerprint and insert the fingerprint values into the appropriate fields on the form. While this ajax call is taking place, the form should not be able to be submitted to avoid submission with an incorrect fingerprint.
  • Since the form will never send back data, I don't think it makes sense to use a form framework for this. The form doesn't need to know its state. Validation can be handled for most fields via javascript so it occurs client side.
  • A callback view where Authorize.net will submit payment details (minus cc info) and expect a url in return where the user's browser should be redirected.
  • Figure out where in this process to upsert the appropriate Salesforce Contact, Opportunity, OpportunityContactRole, and CampaignMember

One open question is how to handle errors such as invalid credit card numbers. I'm sure the documentation covers this in the response sent to the callback url, but I haven't found it yet in my scanning of the docs.

Make Personal Campaign Page Inherit as much as possible from parent campaign

This needs to be researched and defined in more detail, but the general idea is to keep control at the parent level as much as possible to allow changing all personal campaign pages inside a campaign in one place where appropriate. Also, use the override pattern wherever possible rather than storing default text with each instance.

Create Fundraising Campaign from Plone

Currently, the Fundraising Campaign is designed to be created first in Salesforce and then appear in Plone after collective.salesforce.content syncs the Fundraising Campaign type.

Given that there is additional content to edit on the Plone side when setting up a campaign, it makes sense to allow for creating a campaign from the Plone side and having the corresponding Salesforce Campaign automatically created.

Port js and css from ip.theme.fundraising

Leading up to the launch of the Innocence Project's new fundraising site, a lot of css and javascript development was done to improve the user experience. However, much of that work was put into css and js files in the private ip.theme.fundraising package.

The js and css that are relevant to the open source package should be moved into the open source package.

Promote Personal Fundraising Campaign

The owner of a Personal Fundraising Campaign can access this page to help them promote their campaign to their network of people. Each Fundraising Campaign will have sharing messages configured by the administrator with a title, description, and image. Users can select from a message to send, then select where to send it (Facebook, Twitter, Email).

Upgrade to new Janrain Share Widget

The new Janrain share widget is out and has a much nicer UI as well as better functionality. Specifically, users can now select their friends from social networks and post directly to their friends wall. Also, if you have a Pro account, you can share via email in the same widget.

Goal, Raised, and Direct Total Raised error with commas

When pulling up the edit form on a Fundraising Campaign to enter the fundraising content for the campaign, the values in the Goal, Total Raised, and Direct Total Raised fields are shown with commas and a trailing .0 which causes validation errors when saving. This can be worked around by removing the commas and the .0 but it's a pain.

I verified that even after setting the values manually on the object in Plone and not running the SF sync after the change, the same problem occurs. Thus, I think it's something inside of Plone rather than a type mapping issue in collective.salesforce.content.

Post Donation View

Create the post_fundraising_payment view available on the site root. The view should accept a campaign_id and an amount.

The amount should be added to the campaign's donations_total and donations_count fields to instantly display the donation's amount added to the campaign's total in Plone. The next sync from SF will overwrite this value with a more precise value (i.e. including offline donations and refunds since the last sync). If the campaign is a child of another campaign, also add the amount to the donations_total and donations_count fields of the parent campaign (assume only a 2 level campaign hierarchy).

Then, redirect the user to the campaign's thank-you view

Personal Fundraising Campaign Donors List

This page allows a Personal Fundraiser to view a list of who has donated to their campaign. The goal is to encourage the fundraiser to send personal thank you emails to their donors. Fundraisers should be able to see who they have sent to and who they have not sent to. The status is manually changed by the fundraiser after they have sent an email.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.