diff --git a/database/migrations/2019_05_03_000001_create_customer_columns.php b/database/migrations/2019_05_03_000001_create_customer_columns.php index c7be66cc..5d6afde4 100644 --- a/database/migrations/2019_05_03_000001_create_customer_columns.php +++ b/database/migrations/2019_05_03_000001_create_customer_columns.php @@ -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(); }); } @@ -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', ]); }); diff --git a/src/Concerns/ManagesPaymentMethods.php b/src/Concerns/ManagesPaymentMethods.php index cc023b79..a7b660d9 100644 --- a/src/Concerns/ManagesPaymentMethods.php +++ b/src/Concerns/ManagesPaymentMethods.php @@ -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(); @@ -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() ); @@ -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(); } } @@ -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(); } @@ -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; @@ -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(); }); diff --git a/src/Http/Controllers/WebhookController.php b/src/Http/Controllers/WebhookController.php index fb30f904..9778e24b 100644 --- a/src/Http/Controllers/WebhookController.php +++ b/src/Http/Controllers/WebhookController.php @@ -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(); } diff --git a/tests/Feature/PaymentMethodsTest.php b/tests/Feature/PaymentMethodsTest.php index 5fa20315..6735685c 100644 --- a/tests/Feature/PaymentMethodsTest.php +++ b/tests/Feature/PaymentMethodsTest.php @@ -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 { @@ -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'); @@ -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()); } @@ -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() @@ -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() diff --git a/tests/Feature/SubscriptionsTest.php b/tests/Feature/SubscriptionsTest.php index c200d42d..cd670163 100644 --- a/tests/Feature/SubscriptionsTest.php +++ b/tests/Feature/SubscriptionsTest.php @@ -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'); diff --git a/tests/Unit/CustomerTest.php b/tests/Unit/CustomerTest.php index 07a760e8..70054778 100644 --- a/tests/Unit/CustomerTest.php +++ b/tests/Unit/CustomerTest.php @@ -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());