@sdeering There is already a tutorial for that. ;)
Stripe with Cashier and L5
Hi Jeff, Have you got any plans to make any tutorials for setting up Stripe with Cashier and L5? Sam
I believe you want a tutorial for Laravel 5, but since there is not much changed in Cashier you should be fine using the tutorial @Ozan gave you ;)
Hi guys,
Thanks for the link.
I watched this tutorial and it was good, helped understand how it works BUT it was using Laravel 4 and this process doesn't work with L5. There's a few steps missing I think.
- The default stripe script POST's to /charge
- L5 will throw a token mismatch exception when you receive this POST request.
Also I feel there is a huge demand for a series on creating the payment system using L5 cashier. A bit like how you have it setup for laracasts in listing payments and update card etc...
Thanks, Sam
This can be used to bypass the CSRF for the charge route.
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier {
protected $excludeRoutes = [
'charge',
];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
foreach($this->excludeRoutes as $route)
{
if($request->is($route)) return $next($request);
}
return parent::handle($request, $next);
}
}
@sdeering - do you mean without using cashier you get that error? my site isn't live yet, but I have no issue making posts with cashier for charges etc.
@Kryptonit3 Fetching the stripeToken (referred to as the $creditCardToken in the docs) is no problem that's just the front-end requesting it from stripe. The second step is passing this token to the back-end to process the payment (charge/ subscription) using Cashier. I was just pointing out that the Laracast Tutorial for Stripe was out of date and anyone trying to follow it will get stuck. When you copy and paste the stripe script ("Pay with Card" button) it's default code is to post to /charge and it needs to bypass the Laravel 5 CSRF token which is not included in that POST request. Once it does that you can process payments and subscriptions as you please using Cashier/Billing model.
Correct me if i'm wrong, from this point you will need to painfully write all the code which displays payments, invoices, upgrade plan, update credit card, cancel plan etc... It's a shame their isn't a decent package available. Well none that I could find.
@sdeering - I haven't watched that series. I just implemented Cashier with L5. No issues so far, I create my own form and insert the CSRF token into the form that posts to the route for managing the changes (payments/plan changes/etc). I will say I have had an issue trying to get the Cashier Invoices (and probably "payments made") to paginate, still working on that.
@Kryptonit3 Ok cool, how'd you go with modifying the default Invoice? I see Laracast uses a HTML page for the Invoices. At the moment I'm looping the invoice array with $user->downloadInvoice() and saving that file in a folder with the invoice id as the filename. Would be nice to have a logo for the Invoice and change the currency symbol. I'm working on that now.
Also I see that L5.1 will include excludes for webhooks in the verifyCSRF middleware.
@sdeering I made a copy of \vendor\laravel\cashier\src\views\receipt.blade.php and put it in my views folder. I then created a similar page to how Laracasts has their invoice page ( screenshot )
For the HTML link I have the following
public function getInvoice($invoice)
{
$invoice = $this->auth->user()->findInvoiceOrFail($invoice);
$vendor = 'CableWork.co';
$billable = $this->auth->user()->company_name;
$product = 'Subscription Dues';
return view('settings.company.receipt', compact('invoice','vendor','billable','product'));
}
and for the PDF (download)
public function downloadInvoice($invoice)
{
$invoice = $this->auth->user()->findInvoiceOrFail($invoice);
$vendor = 'CableWork.co';
$product = 'Subscription Dues';
return $this->auth->user()->downloadInvoice($invoice->id, [
'vendor' => $vendor,
'product' => $product,
]);
}
I have yet to work on customizing the look and feel, but that will be next. On Laracasts it is coded as one link, it loads the view while prompting the download. I decided on giving my users an option.
@Kryptonit3 Good idea! I like giving the users the option. Clients always want a custom Invoice so I think HTML is the way to go through a blade template.
I've tweaked it a little and extended the user model to help with the output and override some Billing methods. I guess we could extend this and read the values from a config file.
/**
* Fixes Cashier bug. https://laracasts.com/discuss/channels/general-discussion/stripe-cashier-error-invalid-decimal-must-contain-at-maximum-two-decimal-places
*/
public function getTaxPercent()
{
return 0;
}
/**
* Get the Stripe supported currency used by the entity.
*
* @return string
*/
public function getCurrency()
{
return 'gbp';
}
/**
* Get the locale for the currency used by the entity.
*
* @return string
*/
public function getCurrencyLocale()
{
return 'en_GB';
}
/**
* Add the currency symbol to a given amount.
*
* @param string $amount
* @return string
*/
public function addCurrencySymbol($amount)
{
return '£'.number_format($amount/100, 2);
}
Also if you decide to save the PDF for the invoice this function may help. Then you could attach to an email or such.
/*
* Creates and saves a users invoice into the /invoices folder and returns the relative path.
*/
private function createInvoicePDF($user, $invoice) {
$pdf = $user->downloadInvoice($invoice->id, [ 'vendor' => 'Your Company', 'product' => 'Your Product', ]);
//save invoice
$relativeFilepath = '/invoices/' . $invoice->id . '.pdf';
$fullFilepath = public_path() . $relativeFilepath;
if ( !file_exists($fullFilepath) ) {
$myfile = fopen($fullFilepath, "w") or die("Unable to open file!");
fwrite($myfile, $pdf);
fclose($myfile);
}
return $relativeFilepath;
}
@Kryptonit3 Also this line is good to get the subscription info on the invoice.
<td>{{ $subscription->plan->statement_descriptor }}</td>
Please or to participate in this conversation.