Skip to content

Instantly share code, notes, and snippets.

@MindSculpt
Created July 11, 2016 17:25
Show Gist options
  • Save MindSculpt/ea9b0aa9e162fcc1d53b1254bc1db47a to your computer and use it in GitHub Desktop.
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.
<?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