Skip to content

Commit

Permalink
[13.x] Support more payment method types (#1074)
Browse files Browse the repository at this point in the history
* allow payment method type as parameter

* update tests for parameter add

* fix doc types from String to string

* Adjustments

* Rename card columns

Co-authored-by: Simon Hirtreiter <s.hirtreiter@awsh-dev.de>
  • Loading branch information
driesvints and simonhir authored Feb 26, 2021
1 parent 382e53a commit 8a1ccef
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('stripe_id')->nullable()->index();
$table->string('card_brand')->nullable();
$table->string('card_last_four', 4)->nullable();
$table->string('pm_type')->nullable();
$table->string('pm_last_four', 4)->nullable();
$table->timestamp('trial_ends_at')->nullable();
});
}
Expand All @@ -31,8 +31,8 @@ public function down()
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'stripe_id',
'card_brand',
'card_last_four',
'pm_type',
'pm_last_four',
'trial_ends_at',
]);
});
Expand Down
46 changes: 26 additions & 20 deletions src/Concerns/ManagesPaymentMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,28 @@ public function createSetupIntent(array $options = [])
*/
public function hasDefaultPaymentMethod()
{
return (bool) $this->card_brand;
return (bool) $this->pm_type;
}

/**
* Determines if the customer currently has at least one payment method.
* Determines if the customer currently has at least one payment method of the given type.
*
* @param string $type
* @return bool
*/
public function hasPaymentMethod()
public function hasPaymentMethod($type = 'card')
{
return $this->paymentMethods()->isNotEmpty();
return $this->paymentMethods($type)->isNotEmpty();
}

/**
* Get a collection of the entity's payment methods.
* Get a collection of the entity's payment methods of the given type.
*
* @param string $type
* @param array $parameters
* @return \Illuminate\Support\Collection|\Laravel\Cashier\PaymentMethod[]
*/
public function paymentMethods($parameters = [])
public function paymentMethods($type = 'card', $parameters = [])
{
if (! $this->hasStripeId()) {
return collect();
Expand All @@ -61,7 +63,7 @@ public function paymentMethods($parameters = [])

// "type" is temporarily required by Stripe...
$paymentMethods = StripePaymentMethod::all(
['customer' => $this->stripe_id, 'type' => 'card'] + $parameters,
['customer' => $this->stripe_id, 'type' => $type] + $parameters,
$this->stripeOptions()
);

Expand Down Expand Up @@ -116,8 +118,8 @@ public function removePaymentMethod($paymentMethod)
// If the payment method was the default payment method, we'll remove it manually...
if ($stripePaymentMethod->id === $defaultPaymentMethod) {
$this->forceFill([
'card_brand' => null,
'card_last_four' => null,
'pm_type' => null,
'pm_last_four' => null,
])->save();
}
}
Expand Down Expand Up @@ -205,8 +207,8 @@ public function updateDefaultPaymentMethodFromStripe()
}
} else {
$this->forceFill([
'card_brand' => null,
'card_last_four' => null,
'pm_type' => null,
'pm_last_four' => null,
])->save();
}

Expand All @@ -222,8 +224,11 @@ public function updateDefaultPaymentMethodFromStripe()
protected function fillPaymentMethodDetails($paymentMethod)
{
if ($paymentMethod->type === 'card') {
$this->card_brand = $paymentMethod->card->brand;
$this->card_last_four = $paymentMethod->card->last4;
$this->pm_type = $paymentMethod->card->brand;
$this->pm_last_four = $paymentMethod->card->last4;
} else {
$this->pm_type = $type = $paymentMethod->type;
$this->pm_last_four = optional($paymentMethod)->$type->last4;
}

return $this;
Expand All @@ -240,24 +245,25 @@ protected function fillPaymentMethodDetails($paymentMethod)
protected function fillSourceDetails($source)
{
if ($source instanceof StripeCard) {
$this->card_brand = $source->brand;
$this->card_last_four = $source->last4;
$this->pm_type = $source->brand;
$this->pm_last_four = $source->last4;
} elseif ($source instanceof StripeBankAccount) {
$this->card_brand = 'Bank Account';
$this->card_last_four = $source->last4;
$this->pm_type = 'Bank Account';
$this->pm_last_four = $source->last4;
}

return $this;
}

/**
* Deletes the entity's payment methods.
* Deletes the entity's payment methods of the given type.
*
* @param string $type
* @return void
*/
public function deletePaymentMethods()
public function deletePaymentMethods($type = 'card')
{
$this->paymentMethods()->each(function (PaymentMethod $paymentMethod) {
$this->paymentMethods($type)->each(function (PaymentMethod $paymentMethod) {
$paymentMethod->delete();
});

Expand Down
4 changes: 2 additions & 2 deletions src/Http/Controllers/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ protected function handleCustomerDeleted(array $payload)
$user->forceFill([
'stripe_id' => null,
'trial_ends_at' => null,
'card_brand' => null,
'card_last_four' => null,
'pm_type' => null,
'pm_last_four' => null,
])->save();
}

Expand Down
45 changes: 39 additions & 6 deletions tests/Feature/PaymentMethodsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

namespace Laravel\Cashier\Tests\Feature;

use Laravel\Cashier\Cashier;
use Laravel\Cashier\PaymentMethod;
use Stripe\Card as StripeCard;
use Stripe\PaymentMethod as StripePaymentMethod;
use Stripe\SetupIntent as StripeSetupIntent;
use Stripe\StripeClient;

class PaymentMethodsTest extends FeatureTestCase
{
Expand All @@ -32,6 +34,35 @@ public function test_we_can_add_payment_methods()
$this->assertFalse($user->hasDefaultPaymentMethod());
}

public function test_we_can_add_default_sepa_payment_method()
{
$user = $this->createCustomer('we_can_add_default_sepa_payment_method');
$user->createAsStripeCustomer();

$stripe = new StripeClient(Cashier::stripeOptions());

$paymentMethod = $stripe->paymentMethods->create([
'type' => 'sepa_debit',
'billing_details' => [
'name' => 'John Doe',
'email' => 'john@example.com',
],
'sepa_debit' => [
'iban' => 'BE62510007547061',
],
]);

$paymentMethod = $user->updateDefaultPaymentMethod($paymentMethod);

$this->assertInstanceOf(PaymentMethod::class, $paymentMethod);
$this->assertEquals('sepa_debit', $user->pm_type);
$this->assertEquals('7061', $user->pm_last_four);
$this->assertEquals('sepa_debit', $paymentMethod->type);
$this->assertEquals('7061', $paymentMethod->sepa_debit->last4);
$this->assertTrue($user->hasPaymentMethod('sepa_debit'));
$this->assertTrue($user->hasDefaultPaymentMethod());
}

public function test_we_can_remove_payment_methods()
{
$user = $this->createCustomer('we_can_remove_payment_methods');
Expand Down Expand Up @@ -63,8 +94,8 @@ public function test_we_can_remove_the_default_payment_method()

$this->assertCount(0, $user->paymentMethods());
$this->assertNull($user->defaultPaymentMethod());
$this->assertNull($user->card_brand);
$this->assertNull($user->card_last_four);
$this->assertNull($user->pm_type);
$this->assertNull($user->pm_last_four);
$this->assertFalse($user->hasPaymentMethod());
$this->assertFalse($user->hasDefaultPaymentMethod());
}
Expand All @@ -85,7 +116,9 @@ public function test_we_can_set_a_default_payment_method()

$this->assertInstanceOf(PaymentMethod::class, $paymentMethod);
$this->assertEquals('visa', $paymentMethod->card->brand);
$this->assertEquals('visa', $user->pm_type);
$this->assertEquals('4242', $paymentMethod->card->last4);
$this->assertEquals('4242', $user->pm_last_four);
}

public function test_legacy_we_can_retrieve_an_old_default_source_as_a_default_payment_method()
Expand Down Expand Up @@ -136,13 +169,13 @@ public function test_we_can_sync_the_default_payment_method_from_stripe()

$user->refresh();

$this->assertNull($user->card_brand);
$this->assertNull($user->card_last_four);
$this->assertNull($user->pm_type);
$this->assertNull($user->pm_last_four);

$user = $user->updateDefaultPaymentMethodFromStripe();

$this->assertEquals('visa', $user->card_brand);
$this->assertEquals('4242', $user->card_last_four);
$this->assertEquals('visa', $user->pm_type);
$this->assertEquals('4242', $user->pm_last_four);
}

public function test_we_delete_all_payment_methods()
Expand Down
1 change: 0 additions & 1 deletion tests/Feature/SubscriptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,6 @@ public function test_trial_remains_when_customer_is_invoiced_immediately_on_swap
$this->assertTrue($subscription->onTrial());
}

/** @group FOO */
public function test_trial_on_swap_is_skipped_when_explicitly_asked_to()
{
$user = $this->createCustomer('trial_on_swap_is_skipped_when_explicitly_asked_to');
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/CustomerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function test_customer_can_be_put_on_a_generic_trial()
public function test_we_can_determine_if_it_has_a_payment_method()
{
$user = new User;
$user->card_brand = 'visa';
$user->pm_type = 'visa';

$this->assertTrue($user->hasDefaultPaymentMethod());

Expand Down

0 comments on commit 8a1ccef

Please sign in to comment.