Skip to content

Commit

Permalink
Allow Cashier to send payment confirmations by email
Browse files Browse the repository at this point in the history
  • Loading branch information
driesvints committed Jun 6, 2019
1 parent 03f3dcb commit bccfedf
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"illuminate/contracts": "~5.8.0|~5.9.0",
"illuminate/database": "~5.8.0|~5.9.0",
"illuminate/http": "~5.8.0|~5.9.0",
"illuminate/mail": "~5.8.0|~5.9.0",
"illuminate/routing": "~5.8.0|~5.9.0",
"illuminate/support": "~5.8.0|~5.9.0",
"illuminate/view": "~5.8.0|~5.9.0",
Expand Down
3 changes: 3 additions & 0 deletions resources/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
{
"All rights reserved.": "All rights reserved.",
"Confirm Payment": "Confirm Payment",
"Confirm your :amount payment": "Confirm your :amount payment",
"Continue": "Continue",
"Extra confirmation is needed to process your payment. Please confirm your payment by filling out your payment details below.": "Extra confirmation is needed to process your payment. Please confirm your payment by filling out your payment details below.",
"Extra confirmation is needed to process your payment. Please continue to the payment page by clicking on the button below.": "Extra confirmation is needed to process your payment. Please continue to the payment page by clicking on the button below.",
"Name": "Name",
"Pay :amount": "Pay :amount",
"Payment Cancelled": "Payment Cancelled",
"Payment Confirmation": "Payment Confirmation",
"Payment Successful": "Payment bla",
"Please provide your name.": "Please provide your name.",
"Thanks": "Thanks",
"The payment was successful.": "The payment was successful.",
"This payment was already successfully confirmed.": "This payment was already successfully confirmed.",
"This payment was cancelled.": "This payment was cancelled."
Expand Down
12 changes: 12 additions & 0 deletions resources/views/emails/payment_action_required.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@component('mail::message')
# {{ __('Confirm your :amount payment', ['amount' => $payment->amount()]) }}

{{ __('Extra confirmation is needed to process your payment. Please continue to the payment page by clicking on the button below.') }}

@component('mail::button', ['url' => route('cashier.payment', ['id' => $payment->id()])])
{{ __('Confirm Payment') }}
@endcomponent

{{ __('Thanks') }},<br>
{{ config('app.name') }}
@endcomponent
19 changes: 19 additions & 0 deletions src/Cashier.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class Cashier
*/
protected static $formatCurrencyUsing;

/**
* Indicates if Cashier will send out emails when extra payment confirmation is needed.
*
* @var bool
*/
public static $paymentConfirmationEmails = false;

/**
* Indicates if Cashier migrations will be run.
*
Expand Down Expand Up @@ -239,6 +246,18 @@ public static function formatAmount($amount)
return static::usesCurrencySymbol().$amount;
}

/**
* Configure Cashier to send out payment confirmation emails.
*
* @return static
*/
public static function enablePaymentConfirmationEmails()
{
static::$paymentConfirmationEmails = true;

return new static;
}

/**
* Configure Cashier to not register its migrations.
*
Expand Down
24 changes: 24 additions & 0 deletions src/Http/Controllers/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Laravel\Cashier\Cashier;
use Laravel\Cashier\Payment;
use Illuminate\Support\Carbon;
use Laravel\Cashier\Subscription;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Mail;
use Symfony\Component\HttpFoundation\Response;
use Laravel\Cashier\Mail\PaymentActionRequired;
use Stripe\PaymentIntent as StripePaymentIntent;
use Laravel\Cashier\Http\Middleware\VerifyWebhookSignature;

class WebhookController extends Controller
Expand Down Expand Up @@ -172,6 +176,26 @@ protected function handleCustomerDeleted(array $payload)
return new Response('Webhook Handled', 200);
}

/**
* Handle payment action required for invoice.
*
* @param array $payload
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function handleInvoicePaymentActionRequired(array $payload)
{
if (Cashier::$paymentConfirmationEmails &&
$user = $this->getUserByStripeId($payload['data']['object']['customer'])) {
$payment = new Payment(
StripePaymentIntent::retrieve($payload['data']['object']['payment_intent'], Cashier::stripeOptions())
);

Mail::to($user)->send(new PaymentActionRequired($user, $payment));
}

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

/**
* Get the billable entity instance by Stripe ID.
*
Expand Down
45 changes: 45 additions & 0 deletions src/Mail/PaymentActionRequired.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Laravel\Cashier\Mail;

use Laravel\Cashier\Payment;
use Illuminate\Mail\Mailable;

class PaymentActionRequired extends Mailable
{
/**
* The Stripe model instance.
*
* @var \Illuminate\Database\Eloquent\Model
*/
public $owner;

/**
* @var \Laravel\Cashier\Payment
*/
public $payment;

/**
* Create a new PaymentActionRequired instance.
*
* @param \Illuminate\Database\Eloquent\Model $owner
* @param \Laravel\Cashier\Payment $payment
* @return void
*/
public function __construct($owner, Payment $payment)
{
$this->owner = $owner;
$this->payment = $payment;
}

/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->subject(__('Confirm Payment'))
->markdown('cashier::emails.payment_action_required');
}
}
35 changes: 35 additions & 0 deletions tests/Integration/WebhooksTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
use Stripe\Plan;
use Stripe\Product;
use Illuminate\Support\Str;
use Laravel\Cashier\Cashier;
use Illuminate\Support\Facades\Mail;
use Laravel\Cashier\Exceptions\ActionRequired;
use Laravel\Cashier\Mail\PaymentActionRequired;

class WebhooksTest extends IntegrationTestCase
{
Expand Down Expand Up @@ -68,4 +72,35 @@ public function test_subscription_is_marked_as_cancelled_when_deleted_in_stripe(

$this->assertTrue($user->fresh()->subscription('main')->cancelled());
}

public function test_payment_action_required_email_is_sent()
{
Cashier::enablePaymentConfirmationEmails();

$user = $this->createCustomer('payment_action_required_email_is_sent');

try {
$user->newSubscription('main', static::$planId)->create('tok_threeDSecure2Required');

$this->fail('Expected exception '.ActionRequired::class.' was not thrown.');
} catch (ActionRequired $exception) {
Mail::fake();

$this->postJson('stripe/webhook', [
'id' => 'foo',
'type' => 'invoice.payment_action_required',
'data' => [
'object' => [
'id' => 'foo',
'customer' => $user->stripe_id,
'payment_intent' => $exception->payment->id(),
],
],
])->assertOk();

Mail::assertSent(PaymentActionRequired::class, function (PaymentActionRequired $mail) use ($user) {
return $mail->hasTo($user->email);
});
}
}
}

0 comments on commit bccfedf

Please sign in to comment.