Created
July 11, 2016 17:25
-
-
Save MindSculpt/ea9b0aa9e162fcc1d53b1254bc1db47a to your computer and use it in GitHub Desktop.
Stripe webhook class example for use with Fat-Free Framework. Basic approach highlighted; add/edit to fit your project needs.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Stripe webhook class | |
* This is not intended to be plug-and-play; it's intended to be a general outline for how to handle stripe subscription hooks. | |
* You'll need to change variables, database field names, and specific stripe behavior to match your project needs | |
* | |
* @author Michael Becker <michael.becker@mindsculpt.com> | |
*/ | |
namespace Controllers; | |
use \Models\User; // change this to your Model's name | |
include('app/Plugins/stripe/init.php'); // include stripe plugin | |
class StripeController { | |
protected $keys = array( | |
// test keys | |
"secret_key" => "your_secret_test_key", | |
"publishable_key" => "your_secret_publishable_key" | |
// live keys | |
/*"secret_key" => "your_secret_live_key", | |
"publishable_key" => "your_secret_live_key"*/ | |
); | |
function __construct() { | |
\Stripe\Stripe::setApiKey($this->keys['secret_key']); | |
} | |
/** | |
* cancel | |
* | |
* Handles user-initiated subscription cancel, usually from their profile dashboard | |
* This route is called from your server after successful form submission | |
* | |
*/ | |
public function cancel($f3, $params) { | |
// Check that user is logged in before proceeding, if not, reroute them | |
// check to see if any errors exist, if so, clear them first | |
if ($f3->exists('errors')) { | |
$f3->clear('error'); | |
} | |
// set up new arrays | |
$messages = array(); | |
$errors = array(); | |
try { | |
$error = NULL; | |
// get the stripe id of the current user so we know who's sub to cancel | |
// swap out 'current_user' to match your global settings | |
// e.g. $current_user = $f3->get('current_user'); | |
// set references to user information | |
$current_user_stripe_id = $current_user->stripe_id; | |
$current_user_subscription_id = $current_user->stripe_subscription_id; | |
// look up user stripe info... | |
$customer = \Stripe\Customer::retrieve($current_user_stripe_id); | |
$subscription = $customer->subscriptions->retrieve($current_user_subscription_id); | |
// ...and cancel | |
$subscription->cancel(array('at_period_end' => true)); | |
} catch (Exception $e) { | |
//$error = $e->getMessage(); | |
$errors[] = 'There was an error with your subscription cancellation. Please get in touch with us so we can further assist you.'; | |
$f3->set('errors', $errors); | |
// re-route to your error page | |
//$f3->reroute('/cancellation-failed'); // redirect to your error page | |
} | |
if ($error == NULL) { | |
//$f3->reroute('/upgrade-canceled'); // redirect to your confirmation page | |
} | |
} | |
/** | |
* charge | |
* | |
* Charge your customer | |
* This route is called from your server after successful form submission | |
* | |
*/ | |
public function charge($f3, $params) { | |
// Check that user is logged in before proceeding, if not, reroute them | |
// check to see if any errors exist, if so, clear them first | |
if ($f3->exists('errors')) { | |
$f3->clear('error'); | |
} | |
// set up new arrays | |
$messages = array(); | |
$errors = array(); | |
$result = ''; // set a reference to submission results, updated after our try/catch | |
// create new user object | |
$current_user = new \Models\User(); | |
// Get the credit card details submitted by your form | |
// example: | |
// $token = $f3->get('POST.stripeToken'); | |
// $description = $f3->get('POST.description'); | |
// $email = $f3->get('POST.stripeEmail'); | |
// $plan = $f3->get('POST.plan'); | |
// Create the charge on Stripe's servers - this will charge the user's card | |
try { | |
// check if this user already has a stripe id | |
if ($current_user->stripe_id != NULL) { | |
// update the subscription and prorate it | |
$customer = \Stripe\Customer::retrieve($current_user->stripe_id); | |
$current_user->save(); | |
$subscription = $customer->subscriptions->retrieve($current_user->stripe_subscription_id); | |
$subscription->plan = $plan; | |
$subscription->prorate = true; | |
$subscription->save(); | |
} else { | |
// create new stripe customer based on your requirements | |
$customer = \Stripe\Customer::create(array( | |
"source" => $token, | |
"plan" => $plan, | |
"email" => $email, | |
"tax_percent" => 6.0 | |
)); | |
// immediately access the customer id so we can save it in your db | |
$customer_id = $customer->id; | |
// do whatever other checks you need to make to this user in your db, like saving the stripe id and plan name | |
$current_user->stripe_id = $customer->id; | |
$current_user->stripe_plan = $plan; | |
$current_user->save(); | |
} | |
// save session info and reroute to success page | |
$messages[] = 'Congrats! You\'ve successfully upgraded your profile.'; | |
$result = array('success' => true, 'message' => $messages); | |
$f3->set('errors', $messages); | |
$f3->reroute('/upgrade-success'); | |
} catch(Exception $e) { | |
// The card has been declined | |
$error = $e->getMessage(); | |
// save session info and reroute to error page | |
$errors[] = 'Your card has been declined ('. $error. '). Please try again.'; | |
$result = array('success' => false, 'message' => $e); | |
$f3->set('errors', $errors); | |
$f3->reroute('/upgrade-error'); | |
} | |
} | |
/** | |
* Hook methods called by stripe | |
* | |
* Handles stripe events; basic events shown here. Add/modify as needed. | |
* | |
*/ | |
public function hook($f3, $params) { | |
$postdata = file_get_contents("php://input"); | |
$event = json_decode($postdata); | |
// -------------------------------------------------------------------------------------------------------- | |
// customer.created | |
// -------------------------------------------------------------------------------------------------------- | |
if ($event->type == 'customer.created') { | |
// get some information from the stripe obj | |
$customer_id = $event->data->object->id; // stripe id | |
$current_user = new User(); | |
$current_user->findByStripeId($customer_id); | |
// test values show in response of webhook in stripe settings panel | |
//echo 'customer.created for '.$current_user->first_name . ' ' . $current_user->last_name . ' successfully saved as ' . $customer_id; | |
// -------------------------------------------------------------------------------------------------------- | |
// invoice.payment_succeeded | |
// no emails sent here, sent in charge.succeeded instead so we can show user which card they used | |
// -------------------------------------------------------------------------------------------------------- | |
} else if ($event->type == 'invoice.payment_succeeded') { | |
// get some information from the stripe obj | |
$customer_id = $event->data->object->customer; // stripe id | |
$plan = $event->data->object->lines->data[0]->plan->name; | |
$subscription_id = $event->data->object->lines->data[0]->id; // plan id | |
$amount = $event->data->object->total; // amount of the plan | |
// test values show in response of webhook in stripe settings panel | |
//echo 'invoice.payment_succeeded for customer id: ' . $customer_id; | |
// -------------------------------------------------------------------------------------------------------- | |
// customer.subscription.created | |
// -------------------------------------------------------------------------------------------------------- | |
} else if ($event->type == 'customer.subscription.created') { | |
// initial subscription event | |
// get some information from the stripe obj | |
$customer_id = $event->data->object->customer; // stripe id | |
$plan = $event->data->object->plan->name; // plan name | |
$subscription_id = $event->data->object->id; // plan id | |
$amount = $event->data->object->plan->amount; // amount of the plan | |
//$trial_period = $event->data->object->plan->trial_period_days; // duration of trial period | |
$current_period_start = $event->data->object->current_period_start; // subscription start date | |
$current_period_end = $event->data->object->current_period_end; // subscription start date | |
$status = $event->data->object->status; // status of current subscription | |
// convert epoch time to datetime | |
$epoch_start = (int)$current_period_start; | |
$dt_start = new \DateTime("@$epoch_start"); // convert UNIX timestamp to PHP DateTime | |
$epoch_end = (int)$current_period_end; | |
$dt_end = new \DateTime("@$epoch_end"); // convert UNIX timestamp to PHP DateTime | |
$stripe_start_date = $dt_start->format('Y-m-d H:i:s'); // output = 2012-08-15 00:00:00 | |
$stripe_end_date = $dt_end->format('Y-m-d H:i:s'); // output = 2012-08-15 00:00:00 | |
$renewal_date = date("m/d/y", strtotime($stripe_end_date)); | |
// convert money format for html email | |
$amount = money_format('$%i', $amount/100); | |
// test values show in response of webhook in stripe settings panel | |
//echo $customer_id . ' ' . $plan . ' ' . $amount . ' ' . $trial_period . ' ' . $stripe_start_date . ' ' . $amount; | |
// -------------------------------------------------------------------------- | |
// create a new instance of the current user | |
// -------------------------------------------------------------------------- | |
// create new user object | |
// $current_user = new \Models\User(); | |
// grab the user by stripe id | |
// $current_user->findByStripeId($customer_id);// live | |
// -------------------------------------------------------------------------- | |
// save user information | |
// -------------------------------------------------------------------------- | |
//$current_user->stripe_start_date = $stripe_start_date; | |
//$current_user->stripe_plan_active = 1; | |
//$current_user->subscription_id = $subscription_id; | |
//$current_user->stripe_plan_status = $status; | |
//$current_user->save(); | |
// -------------------------------------------------------------------------- | |
// send your customer confirmation email here | |
// -------------------------------------------------------------------------- | |
// -------------------------------------------------------------------------------------------------------- | |
// customer.subscription.updated | |
// email should never be sent here since the event gets hit in multiple scenarios | |
// use only for user database updating | |
// -------------------------------------------------------------------------------------------------------- | |
} else if ($event->type == 'customer.subscription.updated') { | |
// get some information from the stripe obj | |
$customer_id = $event->data->object->customer; // stripe id | |
$plan = $event->data->object->plan->name; // plan name | |
$plan_id = $event->data->object->plan->id; // id of plan | |
$amount = $event->data->object->plan->amount; // amount of the plan | |
$canceled_at = $event->data->object->canceled_at; // paid status of current subscription | |
$cancel_at_period_end = $event->data->object->cancel_at_period_end; | |
$current_period_start = $event->data->object->current_period_start; | |
$current_period_end = $event->data->object->current_period_end; | |
// convert money format for html email | |
//$amount = money_format('$%i', $amount/100); | |
// create new user object | |
$current_user = new \Models\User(); | |
$current_user->findByStripeId($customer_id); | |
$current_user->stripe_plan = $plan_id; | |
// convert epoch time to datetime | |
$epoch_start = (int)$current_period_start; | |
$dt_start = new \DateTime("@$epoch_start"); // convert UNIX timestamp to PHP DateTime | |
$epoch_end = (int)$current_period_end; | |
$dt_end = new \DateTime("@$epoch_end"); // convert UNIX timestamp to PHP DateTime | |
$stripe_start_date = $dt_start->format('Y-m-d H:i:s'); // output = 2012-08-15 00:00:00 | |
$stripe_end_date = $dt_end->format('Y-m-d H:i:s'); // output = 2012-08-15 00:00:00 | |
//$renewal_date = date("m/d/y", strtotime($stripe_end_date)); | |
// set new start/end dates | |
$current_user->stripe_plan = $plan_id; | |
$current_user->stripe_start_date = $stripe_start_date; | |
$current_user->stripe_end_date = $stripe_end_date; | |
$current_user->save(); | |
//echo 'customer.subscription.updated -> ends: ' . $stripe_end_date. ', canceled? ' . $canceled_at; | |
// -------------------------------------------------------------------------------------------------------- | |
// charge.succeeded | |
// -------------------------------------------------------------------------------------------------------- | |
} else if ($event->type == 'charge.succeeded') { | |
// hit when initial subscription is successful, AND when any subsequent payment is processed | |
// email sent here is generic and not specific to any type of transaction other than a receipt notification | |
// this event is occasionally delayed, sent 1 hour later | |
// -------------------------------------------------------------------------------------------------------- | |
// get some information from the stripe obj | |
$customer_id = $event->data->object->customer; // stripe id | |
$amount = $event->data->object->amount; // amount of the plan | |
$status = $event->data->object->status; // paid status of current subscription | |
$card_type = $event->data->object->source->brand; // card type | |
$last_four = $event->data->object->source->last4; // last four of card for email receipt | |
// convert money format for html email | |
$amount = money_format('$%i', $amount/100); | |
// test values show in response of webhook in stripe settings panel | |
//echo $customer_id . ' '. $amount; | |
// create new user object | |
$current_user = new \Models\User(); | |
// grab the user by stripe id | |
$current_user->findByStripeId($customer_id); | |
// update plan status - manually update based on whether $event->data->object->paid is true or not (status not sent by stripe in this event) | |
$current_user->stripe_plan_status = 'active'; | |
$current_user->save(); | |
// get saved plan from the database since it's not sent in the stripe object for this event | |
// -------------------------------------------------------------------------- | |
// send your customer confirmation email here, use the values above get them | |
// from your database to personalize | |
// -------------------------------------------------------------------------- | |
// -------------------------------------------------------------------------------------------------------- | |
// invoice.payment_failed | |
// -------------------------------------------------------------------------------------------------------- | |
} else if ($event->type == 'invoice.payment_failed') { | |
// send notice to user that their payment has failed | |
// get some information from the stripe obj | |
$customer_id = $event->data->object->customer; // stripe id | |
$amount = $event->data->object->lines->data[0]->plan->amount; // amount of the plan | |
$plan = $event->data->object->lines->data[0]->plan->name; // name of the plan | |
$status = $event->data->object->paid; // paid status of current subscription | |
// convert money format for html email | |
$amount = money_format('$%i', $amount/100); | |
// test values show in response of webhook in stripe settings panel | |
//echo $customer_id . ' '. $amount . ' ' . $plan; | |
// create new user object | |
$current_user = new \Models\User(); | |
// grab the user by stripe id | |
$current_user->findByStripeId($customer_id);// live | |
// update plan status - manually update based on whether $event->data->object->paid is true or not (status not sent by stripe in this event) | |
$current_user->stripe_plan_status = 'unpaid'; | |
$current_user->save(); | |
// get customer name from the db since stripe doesn't pass it in this event | |
$billed_to = $current_user->first_name . ' ' . $current_user->last_name; | |
// -------------------------------------------------------------------------- | |
// send your customer confirmation email here, use the values above get them | |
// from your database to personalize | |
// -------------------------------------------------------------------------- | |
// -------------------------------------------------------------------------------------------------------- | |
// customer.subscription.deleted | |
// -------------------------------------------------------------------------------------------------------- | |
} else if ($event->type == 'customer.subscription.deleted') { | |
// event: customer.subscription.deleted -> when stripe cancels a subscription | |
$todays_date = date('Y-m-d H:i:s'); | |
// get some information from the stripe obj | |
$customer_id = $event->data->object->customer; // stripe id | |
$current_period_end = $todays_date; // today's date | |
$plan = $event->data->object->plan->name; // plan name | |
$amount = $event->data->object->plan->amount; // amount of the plan | |
$status = $event->data->object->status; // status of current subscription | |
// convert money format for html email | |
$amount = money_format('$%i', $amount/100); | |
// test values show in response of webhook in stripe settings panel | |
//echo $customer_id . ' '. $current_period_end . ' '. $amount. ' '. $plan; | |
// create new user object | |
$current_user = new \Models\User(); | |
// grab the user by stripe id | |
$current_user->findByStripeId($customer_id);// live | |
// get customer name from the db since stripe doesn't pass it in this event | |
$billed_to = $current_user->first_name . ' ' . $current_user->last_name; | |
// update the user's info in the db | |
$current_user->stripe_end_date = $current_period_end; // update the period end date | |
$current_user->stripe_plan_active = 0; // set plan inactive | |
$current_user->stripe_plan_status = $status; // update plan status | |
$current_user->save(); | |
// -------------------------------------------------------------------------- | |
// send your customer confirmation email here, use the values above get them | |
// from your database to personalize | |
// -------------------------------------------------------------------------- | |
} | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment