2025-11-26 10:09:52 +00:00
|
|
|
<?php
|
|
|
|
|
|
2025-12-30 09:55:06 +00:00
|
|
|
namespace Blax\Shop\Tests\Feature\Checkout;
|
2025-11-26 10:09:52 +00:00
|
|
|
|
|
|
|
|
use Blax\Shop\Models\PaymentMethod;
|
|
|
|
|
use Blax\Shop\Models\PaymentProviderIdentity;
|
|
|
|
|
use Blax\Shop\Services\PaymentProvider\PaymentProviderService;
|
|
|
|
|
use Blax\Shop\Services\PaymentProvider\StripeService;
|
|
|
|
|
use Blax\Shop\Tests\TestCase;
|
|
|
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
|
use Mockery;
|
|
|
|
|
use Workbench\App\Models\User;
|
2025-12-24 18:40:10 +00:00
|
|
|
use PHPUnit\Framework\Attributes\Test;
|
2025-11-26 10:09:52 +00:00
|
|
|
|
|
|
|
|
class PaymentProviderTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
use RefreshDatabase;
|
|
|
|
|
|
|
|
|
|
protected StripeService $stripeService;
|
|
|
|
|
protected PaymentProviderService $paymentProviderService;
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
|
|
// Mock the Stripe service
|
|
|
|
|
$this->stripeService = Mockery::mock(StripeService::class);
|
|
|
|
|
$this->app->instance(StripeService::class, $this->stripeService);
|
|
|
|
|
|
|
|
|
|
// Create the payment provider service with the mocked stripe service
|
|
|
|
|
$this->paymentProviderService = new PaymentProviderService($this->stripeService);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_can_create_a_customer_on_stripe()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create([
|
|
|
|
|
'name' => 'John Doe',
|
|
|
|
|
'email' => 'john@example.com',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Mock Stripe customer creation - use stdClass instead of actual Stripe objects
|
|
|
|
|
$mockStripeCustomer = new \stdClass();
|
|
|
|
|
$mockStripeCustomer->id = 'cus_test123';
|
|
|
|
|
$mockStripeCustomer->email = 'john@example.com';
|
|
|
|
|
$mockStripeCustomer->created = time();
|
|
|
|
|
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('createCustomer')
|
|
|
|
|
->once()
|
|
|
|
|
->with(Mockery::on(function ($arg) use ($user) {
|
|
|
|
|
return $arg['email'] === 'john@example.com'
|
|
|
|
|
&& $arg['name'] === 'John Doe'
|
|
|
|
|
&& $arg['metadata']['customer_id'] === $user->id;
|
|
|
|
|
}))
|
|
|
|
|
->andReturn($mockStripeCustomer);
|
|
|
|
|
|
|
|
|
|
// Create customer
|
|
|
|
|
$identity = $this->paymentProviderService->createOrGetCustomer($user, 'stripe');
|
|
|
|
|
|
|
|
|
|
$this->assertInstanceOf(PaymentProviderIdentity::class, $identity);
|
|
|
|
|
$this->assertEquals('stripe', $identity->provider_name);
|
|
|
|
|
$this->assertEquals('cus_test123', $identity->customer_identification_id);
|
|
|
|
|
$this->assertEquals($user->id, $identity->customer_id);
|
|
|
|
|
$this->assertEquals(get_class($user), $identity->customer_type);
|
|
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('payment_provider_identities', [
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
'customer_identification_id' => 'cus_test123',
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_returns_existing_customer_identity_if_already_exists()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
|
|
|
|
|
$existingIdentity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
'customer_identification_id' => 'cus_existing123',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Should not call Stripe since customer already exists
|
|
|
|
|
$this->stripeService->shouldNotReceive('createCustomer');
|
|
|
|
|
|
|
|
|
|
$identity = $this->paymentProviderService->createOrGetCustomer($user, 'stripe');
|
|
|
|
|
|
|
|
|
|
$this->assertEquals($existingIdentity->id, $identity->id);
|
|
|
|
|
$this->assertEquals('cus_existing123', $identity->customer_identification_id);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_can_add_a_payment_method()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$identity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
'customer_identification_id' => 'cus_test123',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Mock Stripe payment method
|
|
|
|
|
$mockCard = new \stdClass();
|
|
|
|
|
$mockCard->last4 = '4242';
|
|
|
|
|
$mockCard->brand = 'visa';
|
|
|
|
|
$mockCard->exp_month = 12;
|
|
|
|
|
$mockCard->exp_year = 2025;
|
|
|
|
|
$mockCard->funding = 'credit';
|
|
|
|
|
$mockCard->country = 'US';
|
|
|
|
|
$mockCard->fingerprint = 'fingerprint123';
|
|
|
|
|
|
|
|
|
|
$mockStripePaymentMethod = new \stdClass();
|
|
|
|
|
$mockStripePaymentMethod->id = 'pm_test123';
|
|
|
|
|
$mockStripePaymentMethod->type = 'card';
|
|
|
|
|
$mockStripePaymentMethod->card = $mockCard;
|
|
|
|
|
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('attachPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->with('pm_test123', 'cus_test123')
|
|
|
|
|
->andReturn($mockStripePaymentMethod);
|
|
|
|
|
|
|
|
|
|
// First payment method gets set as default
|
|
|
|
|
$mockStripeCustomer = new \stdClass();
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('setDefaultPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($mockStripeCustomer);
|
|
|
|
|
|
|
|
|
|
// Add payment method
|
|
|
|
|
$paymentMethod = $this->paymentProviderService->addPaymentMethod($identity, 'pm_test123');
|
|
|
|
|
|
|
|
|
|
$this->assertInstanceOf(PaymentMethod::class, $paymentMethod);
|
|
|
|
|
$this->assertEquals('pm_test123', $paymentMethod->provider_payment_method_id);
|
|
|
|
|
$this->assertEquals('card', $paymentMethod->type);
|
|
|
|
|
$this->assertEquals('4242', $paymentMethod->last_digits);
|
|
|
|
|
$this->assertEquals('visa', $paymentMethod->brand);
|
|
|
|
|
$this->assertEquals(12, $paymentMethod->exp_month);
|
|
|
|
|
$this->assertEquals(2025, $paymentMethod->exp_year);
|
|
|
|
|
$this->assertTrue($paymentMethod->is_active);
|
|
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('payment_methods', [
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'provider_payment_method_id' => 'pm_test123',
|
|
|
|
|
'last_digits' => '4242',
|
|
|
|
|
'brand' => 'visa',
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function first_payment_method_is_automatically_set_as_default()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$identity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Mock Stripe payment method
|
|
|
|
|
$mockCard = new \stdClass();
|
|
|
|
|
$mockCard->last4 = '4242';
|
|
|
|
|
$mockCard->brand = 'visa';
|
|
|
|
|
$mockCard->exp_month = 12;
|
|
|
|
|
$mockCard->exp_year = 2025;
|
|
|
|
|
$mockCard->funding = 'credit';
|
|
|
|
|
$mockCard->country = 'US';
|
|
|
|
|
$mockCard->fingerprint = 'fingerprint123';
|
|
|
|
|
|
|
|
|
|
$mockStripePaymentMethod = new \stdClass();
|
|
|
|
|
$mockStripePaymentMethod->id = 'pm_test123';
|
|
|
|
|
$mockStripePaymentMethod->type = 'card';
|
|
|
|
|
$mockStripePaymentMethod->card = $mockCard;
|
|
|
|
|
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('attachPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($mockStripePaymentMethod);
|
|
|
|
|
|
|
|
|
|
$mockStripeCustomer = new \stdClass();
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('setDefaultPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->with($identity->customer_identification_id, 'pm_test123')
|
|
|
|
|
->andReturn($mockStripeCustomer);
|
|
|
|
|
|
|
|
|
|
// Add first payment method
|
|
|
|
|
$paymentMethod = $this->paymentProviderService->addPaymentMethod($identity, 'pm_test123');
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($paymentMethod->fresh()->is_default);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_can_list_payment_methods()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$identity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Create multiple payment methods
|
|
|
|
|
$method1 = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
$method2 = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
$method3 = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'is_active' => false, // Inactive
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// List active payment methods
|
|
|
|
|
$methods = $this->paymentProviderService->listPaymentMethods($identity, true);
|
|
|
|
|
|
|
|
|
|
$this->assertCount(2, $methods);
|
|
|
|
|
$this->assertTrue($methods->contains($method1));
|
|
|
|
|
$this->assertTrue($methods->contains($method2));
|
|
|
|
|
$this->assertFalse($methods->contains($method3));
|
|
|
|
|
|
|
|
|
|
// List all payment methods
|
|
|
|
|
$allMethods = $this->paymentProviderService->listPaymentMethods($identity, false);
|
|
|
|
|
$this->assertCount(3, $allMethods);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_can_set_a_payment_method_as_default()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$identity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$method1 = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'is_default' => true,
|
|
|
|
|
]);
|
|
|
|
|
$method2 = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'is_default' => false,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$mockStripeCustomer = new \stdClass();
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('setDefaultPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->with($identity->customer_identification_id, $method2->provider_payment_method_id)
|
|
|
|
|
->andReturn($mockStripeCustomer);
|
|
|
|
|
|
|
|
|
|
// Set method2 as default
|
|
|
|
|
$this->paymentProviderService->setDefaultPaymentMethod($method2);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($method2->fresh()->is_default);
|
|
|
|
|
$this->assertFalse($method1->fresh()->is_default);
|
|
|
|
|
|
|
|
|
|
// Verify only one default exists
|
|
|
|
|
$defaultCount = PaymentMethod::where('payment_provider_identity_id', $identity->id)
|
|
|
|
|
->where('is_default', true)
|
|
|
|
|
->count();
|
|
|
|
|
$this->assertEquals(1, $defaultCount);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_can_remove_a_payment_method()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$identity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$paymentMethod = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$mockStripePaymentMethod = new \stdClass();
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('detachPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->with(Mockery::type('string'))
|
|
|
|
|
->andReturn($mockStripePaymentMethod);
|
|
|
|
|
|
|
|
|
|
// Remove payment method
|
|
|
|
|
$result = $this->paymentProviderService->removePaymentMethod($paymentMethod, true);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($result);
|
|
|
|
|
$this->assertDatabaseMissing('payment_methods', [
|
|
|
|
|
'id' => $paymentMethod->id,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function removing_default_payment_method_sets_another_as_default()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$identity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$method1 = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'is_default' => true,
|
|
|
|
|
]);
|
|
|
|
|
$method2 = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'is_default' => false,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$mockStripePaymentMethod = new \stdClass();
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('detachPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($mockStripePaymentMethod);
|
|
|
|
|
|
|
|
|
|
$mockStripeCustomer = new \stdClass();
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('setDefaultPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->with($identity->customer_identification_id, $method2->provider_payment_method_id)
|
|
|
|
|
->andReturn($mockStripeCustomer);
|
|
|
|
|
|
|
|
|
|
// Remove default payment method
|
|
|
|
|
$this->paymentProviderService->removePaymentMethod($method1, true);
|
|
|
|
|
|
|
|
|
|
// Method2 should now be default
|
|
|
|
|
$this->assertTrue($method2->fresh()->is_default);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_can_use_trait_to_add_payment_methods()
|
|
|
|
|
{
|
|
|
|
|
// Use actual User model since it doesn't have the trait yet - test with direct service calls
|
|
|
|
|
$user = User::factory()->create([
|
|
|
|
|
'name' => 'John Doe',
|
|
|
|
|
'email' => 'john@example.com',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Mock Stripe customer creation
|
|
|
|
|
$mockStripeCustomer = new \stdClass();
|
|
|
|
|
$mockStripeCustomer->id = 'cus_test123';
|
|
|
|
|
$mockStripeCustomer->email = 'john@example.com';
|
|
|
|
|
$mockStripeCustomer->created = time();
|
|
|
|
|
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('createCustomer')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($mockStripeCustomer);
|
|
|
|
|
|
|
|
|
|
// Mock Stripe payment method
|
|
|
|
|
$mockCard = new \stdClass();
|
|
|
|
|
$mockCard->last4 = '4242';
|
|
|
|
|
$mockCard->brand = 'visa';
|
|
|
|
|
$mockCard->exp_month = 12;
|
|
|
|
|
$mockCard->exp_year = 2025;
|
|
|
|
|
$mockCard->funding = 'credit';
|
|
|
|
|
$mockCard->country = 'US';
|
|
|
|
|
$mockCard->fingerprint = 'fingerprint123';
|
|
|
|
|
|
|
|
|
|
$mockStripePaymentMethod = new \stdClass();
|
|
|
|
|
$mockStripePaymentMethod->id = 'pm_test123';
|
|
|
|
|
$mockStripePaymentMethod->type = 'card';
|
|
|
|
|
$mockStripePaymentMethod->card = $mockCard;
|
|
|
|
|
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('attachPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($mockStripePaymentMethod);
|
|
|
|
|
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('setDefaultPaymentMethod')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($mockStripeCustomer);
|
|
|
|
|
|
|
|
|
|
// Create identity and add payment method using the service
|
|
|
|
|
$identity = $this->paymentProviderService->createOrGetCustomer($user, 'stripe');
|
|
|
|
|
$paymentMethod = $this->paymentProviderService->addPaymentMethod($identity, 'pm_test123', ['name' => 'My Card']);
|
|
|
|
|
|
|
|
|
|
$this->assertInstanceOf(PaymentMethod::class, $paymentMethod);
|
|
|
|
|
$this->assertEquals('pm_test123', $paymentMethod->provider_payment_method_id);
|
|
|
|
|
$this->assertEquals('My Card', $paymentMethod->name);
|
|
|
|
|
|
|
|
|
|
// Test identity and method relationships
|
|
|
|
|
$this->assertEquals($user->id, $identity->customer_id);
|
|
|
|
|
$this->assertEquals(get_class($user), $identity->customer_type);
|
2025-12-24 18:40:10 +00:00
|
|
|
|
2025-11-26 10:09:52 +00:00
|
|
|
$methods = $this->paymentProviderService->listPaymentMethods($identity);
|
|
|
|
|
$this->assertCount(1, $methods);
|
|
|
|
|
|
|
|
|
|
$defaultMethod = $this->paymentProviderService->getDefaultPaymentMethod($identity);
|
|
|
|
|
$this->assertNotNull($defaultMethod);
|
|
|
|
|
$this->assertEquals($paymentMethod->id, $defaultMethod->id);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function it_can_sync_payment_methods_from_stripe()
|
|
|
|
|
{
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$identity = PaymentProviderIdentity::factory()->create([
|
|
|
|
|
'customer_type' => get_class($user),
|
|
|
|
|
'customer_id' => $user->id,
|
|
|
|
|
'provider_name' => 'stripe',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Create a local payment method that's not on Stripe (should be marked inactive)
|
|
|
|
|
$oldMethod = PaymentMethod::factory()->create([
|
|
|
|
|
'payment_provider_identity_id' => $identity->id,
|
|
|
|
|
'provider_payment_method_id' => 'pm_old123',
|
|
|
|
|
'is_active' => true,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Mock Stripe collection with payment methods
|
|
|
|
|
$mockCard1 = new \stdClass();
|
|
|
|
|
$mockCard1->last4 = '4242';
|
|
|
|
|
$mockCard1->brand = 'visa';
|
|
|
|
|
$mockCard1->exp_month = 12;
|
|
|
|
|
$mockCard1->exp_year = 2025;
|
|
|
|
|
|
|
|
|
|
$mockCard2 = new \stdClass();
|
|
|
|
|
$mockCard2->last4 = '5555';
|
|
|
|
|
$mockCard2->brand = 'mastercard';
|
|
|
|
|
$mockCard2->exp_month = 6;
|
|
|
|
|
$mockCard2->exp_year = 2026;
|
|
|
|
|
|
|
|
|
|
$mockMethod1 = new \stdClass();
|
|
|
|
|
$mockMethod1->id = 'pm_new123';
|
|
|
|
|
$mockMethod1->type = 'card';
|
|
|
|
|
$mockMethod1->card = $mockCard1;
|
|
|
|
|
|
|
|
|
|
$mockMethod2 = new \stdClass();
|
|
|
|
|
$mockMethod2->id = 'pm_new456';
|
|
|
|
|
$mockMethod2->type = 'card';
|
|
|
|
|
$mockMethod2->card = $mockCard2;
|
|
|
|
|
|
|
|
|
|
$mockCollection = new \stdClass();
|
|
|
|
|
$mockCollection->data = [$mockMethod1, $mockMethod2];
|
|
|
|
|
|
|
|
|
|
$this->stripeService
|
|
|
|
|
->shouldReceive('listPaymentMethods')
|
|
|
|
|
->once()
|
|
|
|
|
->with($identity->customer_identification_id)
|
|
|
|
|
->andReturn($mockCollection);
|
|
|
|
|
|
|
|
|
|
// Sync payment methods
|
|
|
|
|
$syncedMethods = $this->paymentProviderService->syncPaymentMethods($identity);
|
|
|
|
|
|
|
|
|
|
$this->assertCount(2, $syncedMethods);
|
|
|
|
|
|
|
|
|
|
// Old method should be marked inactive
|
|
|
|
|
$this->assertFalse($oldMethod->fresh()->is_active);
|
|
|
|
|
|
|
|
|
|
// New methods should exist
|
|
|
|
|
$this->assertDatabaseHas('payment_methods', [
|
|
|
|
|
'provider_payment_method_id' => 'pm_new123',
|
|
|
|
|
'last_digits' => '4242',
|
|
|
|
|
'brand' => 'visa',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('payment_methods', [
|
|
|
|
|
'provider_payment_method_id' => 'pm_new456',
|
|
|
|
|
'last_digits' => '5555',
|
|
|
|
|
'brand' => 'mastercard',
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function payment_method_can_check_if_expired()
|
|
|
|
|
{
|
|
|
|
|
$paymentMethod = PaymentMethod::factory()->create([
|
|
|
|
|
'exp_month' => now()->subMonth()->month,
|
|
|
|
|
'exp_year' => now()->subYear()->year,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($paymentMethod->isExpired());
|
|
|
|
|
|
|
|
|
|
$validMethod = PaymentMethod::factory()->create([
|
|
|
|
|
'exp_month' => 12,
|
|
|
|
|
'exp_year' => now()->addYear()->year,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertFalse($validMethod->isExpired());
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function payment_method_has_display_name_attribute()
|
|
|
|
|
{
|
|
|
|
|
$method1 = PaymentMethod::factory()->create([
|
|
|
|
|
'name' => 'My Personal Card',
|
|
|
|
|
'brand' => 'visa',
|
|
|
|
|
'last_digits' => '4242',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals('My Personal Card', $method1->display_name);
|
|
|
|
|
|
|
|
|
|
$method2 = PaymentMethod::factory()->create([
|
|
|
|
|
'name' => null,
|
|
|
|
|
'brand' => 'mastercard',
|
|
|
|
|
'last_digits' => '5555',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals('Mastercard ending in 5555', $method2->display_name);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 18:40:10 +00:00
|
|
|
#[Test]
|
2025-11-26 10:09:52 +00:00
|
|
|
public function payment_method_has_formatted_expiration()
|
|
|
|
|
{
|
|
|
|
|
$paymentMethod = PaymentMethod::factory()->create([
|
|
|
|
|
'exp_month' => 6,
|
|
|
|
|
'exp_year' => 2025,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals('06/2025', $paymentMethod->formatted_expiration);
|
|
|
|
|
|
|
|
|
|
$methodWithoutExpiration = PaymentMethod::factory()->create([
|
|
|
|
|
'exp_month' => null,
|
|
|
|
|
'exp_year' => null,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertNull($methodWithoutExpiration->formatted_expiration);
|
|
|
|
|
}
|
|
|
|
|
}
|