Skip to content

Commit

Permalink
Stripe Checkout
Browse files Browse the repository at this point in the history
  • Loading branch information
driesvints committed Oct 5, 2020
1 parent acc0485 commit 8e44f86
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 7 deletions.
27 changes: 27 additions & 0 deletions resources/views/checkout.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<button
id="checkout-{{ $sessionId }}"
role="link"
style="background-color:#6772E5;color:#FFF;padding:8px 12px;border:0;border-radius:4px;font-size:1em"
>
{{ $label }}
</button>

<div id="error-message"></div>

<script>
const checkoutButton = document.getElementById('checkout-{{ $sessionId }}');
checkoutButton.addEventListener('click', function () {
// When the customer clicks on the button, redirect them to Checkout.
Stripe('{{ config('cashier.key') }}').redirectToCheckout({
sessionId: '{{ $sessionId }}'
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
if (result.error) {
document.getElementById('error-message').innerText = result.error.message;
}
});
});
</script>
88 changes: 88 additions & 0 deletions src/Checkout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace Laravel\Cashier;

use Illuminate\Support\Facades\View;
use Stripe\Checkout\Session;

class Checkout
{
/**
* @var \Illuminate\Database\Eloquent\Model
*/
protected $owner;

/**
* @var \Stripe\Checkout\Session
*/
protected $session;

/**
* Create a new Checkout instance.
*
* @param \Illuminate\Database\Eloquent\Model $owner
* @param \Stripe\Checkout\Session $session
* @return void
*/
public function __construct($owner, Session $session)
{
$this->owner = $owner;
$this->session = $session;
}

/**
* Get the Checkout Session ID.
*
* @return string
*/
public function sessionId()
{
return $this->session->id;
}

/**
* Begin a new Checkout Session.
*
* @param \Illuminate\Database\Eloquent\Model $owner
* @param array $sessionOptions
* @param array $customerOptions
* @return \Laravel\Cashier\Checkout
*/
public static function create($owner, array $sessionOptions = [], array $customerOptions = [])
{
$customer = $owner->createOrGetStripeCustomer($customerOptions);

$session = Session::create(array_merge([
'customer' => $customer->id,
'mode' => 'payment',
'success_url' => route('home').'?checkout=success',
'cancel_url' => route('home').'?checkout=cancelled',
'payment_method_types' => ['card'],
], $sessionOptions), Cashier::stripeOptions());

return new static($customer, $session);
}

/**
* Get the View instance for the button.
*
* @param string $label
* @param array $options
* @return \Illuminate\Contracts\View\View
*/
public function button($label = 'Check out', array $options = [])
{
return View::make('cashier::checkout', array_merge([
'label' => $label,
'sessionId' => $this->session->id,
], $options));
}

/**
* @return \Stripe\Checkout\Session
*/
public function asStripeCheckoutSession()
{
return $this->session;
}
}
46 changes: 46 additions & 0 deletions src/Concerns/PerformsCharges.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laravel\Cashier\Concerns;

use Laravel\Cashier\Checkout;
use Laravel\Cashier\Payment;
use Stripe\PaymentIntent as StripePaymentIntent;
use Stripe\Refund as StripeRefund;
Expand Down Expand Up @@ -57,4 +58,49 @@ public function refund($paymentIntent, array $options = [])
$this->stripeOptions()
);
}

/**
* Begin a new Checkout Session.
*
* @param int $amount
* @param string $name
* @param int $quantity
* @param array $sessionOptions
* @param array $customerOptions
* @return \Laravel\Cashier\Checkout
*/
public function checkout($amount, $name, $quantity = 1, array $sessionOptions = [], array $customerOptions = [])
{
return Checkout::create($this, array_merge([
'line_items' => [[
'price_data' => [
'currency' => $this->preferredCurrency(),
'product_data' => [
'name' => $name,
],
'unit_amount' => $amount,
],
'quantity' => $quantity,
]],
], $sessionOptions), $customerOptions);
}

/**
* Begin a new Checkout Session.
*
* @param string $price
* @param int $quantity
* @param array $sessionOptions
* @param array $customerOptions
* @return \Laravel\Cashier\Checkout
*/
public function checkoutProduct($price, $quantity = 1, array $sessionOptions = [], array $customerOptions = [])
{
return Checkout::create($this, array_merge([
'line_items' => [[
'price' => $price,
'quantity' => $quantity,
]],
], $sessionOptions), $customerOptions);
}
}
43 changes: 43 additions & 0 deletions src/Http/Controllers/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,49 @@ public function handleWebhook(Request $request)
return $this->missingMethod();
}

/**
* Handle customer subscription created.
*
* @param array $payload
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function handleCustomerSubscriptionCreated(array $payload)
{
$user = $this->getUserByStripeId($payload['data']['object']['customer']);

if ($user) {
$data = $payload['data']['object'];

if (! $user->subscriptions->contains('stripe_id', $data['id'])) {
if (isset($data['trial_end'])) {
$trialEndsAt = Carbon::createFromTimestamp($data['trial_end']);
} else {
$trialEndsAt = null;
}

$subscription = $user->subscriptions()->create([
'name' => $data['metadata']['name'],
'stripe_id' => $data['id'],
'stripe_status' => $data['status'],
'stripe_plan' => $data['plan']['id'] ?? null,
'quantity' => $data['quantity'],
'trial_ends_at' => $trialEndsAt,
'ends_at' => null,
]);

foreach ($data['items']['data'] as $item) {
$subscription->items()->create([
'stripe_id' => $item['id'],
'stripe_plan' => $item['plan']['id'],
'quantity' => $item['quantity'],
]);
}
}
}

return new Response('Webhook Handled', 200);
}

/**
* Handle customer subscription updated.
*
Expand Down
6 changes: 4 additions & 2 deletions src/Subscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ class Subscription extends Model
* @var array
*/
protected $dates = [
'trial_ends_at', 'ends_at',
'created_at', 'updated_at',
'created_at',
'ends_at',
'trial_ends_at',
'updated_at',
];

/**
Expand Down
42 changes: 37 additions & 5 deletions src/SubscriptionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ class SubscriptionBuilder
/**
* The metadata to apply to the subscription.
*
* @var array|null
* @var array
*/
protected $metadata;
protected $metadata = [];

/**
* Create a new subscription builder instance.
Expand Down Expand Up @@ -99,7 +99,7 @@ public function __construct($owner, $name, $plans = null)
public function plan($plan, $quantity = 1)
{
$options = [
'plan' => $plan,
'price' => $plan,
'quantity' => $quantity,
];

Expand All @@ -126,7 +126,7 @@ public function quantity($quantity, $plan = null)
throw new InvalidArgumentException('Plan is required when creating multi-plan subscriptions.');
}

$plan = Arr::first($this->items)['plan'];
$plan = Arr::first($this->items)['price'];
}

return $this->plan($plan, $quantity);
Expand Down Expand Up @@ -208,7 +208,7 @@ public function withCoupon($coupon)
*/
public function withMetadata($metadata)
{
$this->metadata = $metadata;
$this->metadata = (array) $metadata;

return $this;
}
Expand Down Expand Up @@ -289,6 +289,38 @@ public function create($paymentMethod = null, array $customerOptions = [], array
return $subscription;
}

/**
* Begin a new Checkout Session.
*
* @param array $sessionOptions
* @param array $customerOptions
* @return \Laravel\Cashier\Checkout
*/
public function checkout(array $sessionOptions = [], array $customerOptions = [])
{
if (! $this->skipTrial && $this->trialExpires) {
// Checkout Sessions are active for 24 hours after their creation and within that time frame the customer
// can complete the payment at any time. Stripe requires the trial end at least 48 hours in the future
// so that there is still at least a one day trial if your customer pays at the end of the 24 hours.
$minimumTrialPeriod = Carbon::now()->addHours(48);

$trialEnd = $this->trialExpires->gt($minimumTrialPeriod) ? $this->trialExpires : $minimumTrialPeriod;
} else {
$trialEnd = null;
}

return Checkout::create($this->owner, array_merge([
'mode' => 'subscription',
'line_items' => collect($this->items)->values()->all(),
'default_tax_rates' => $this->getTaxRatesForPayload(),
'subscription_data' => [
'coupon' => $this->coupon,
'trial_end' => $trialEnd ? $trialEnd->getTimestamp() : null,
'metadata' => array_merge($this->metadata, ['name' => $this->name]),
],
], $sessionOptions), $customerOptions);
}

/**
* Get the Stripe customer instance for the current user and payment method.
*
Expand Down

0 comments on commit 8e44f86

Please sign in to comment.