diff --git a/composer.json b/composer.json index 4341fe9..5a3a473 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "test": "vendor/bin/phpunit" }, "require": { - "payavel/orchestration": "dev-master" + "payavel/orchestration": "^2.0" }, "require-dev": { "orchestra/testbench": "^8.0|^9.0" diff --git a/database/factories/DisputeFactory.php b/database/factories/DisputeFactory.php new file mode 100644 index 0000000..604c8aa --- /dev/null +++ b/database/factories/DisputeFactory.php @@ -0,0 +1,49 @@ + $this->faker->uuid(), + ]; + } + + /** + * Configure the model factory. + * + * @return $this + */ + public function configure() + { + return $this->afterMaking(function (Dispute $dispute) { + if (is_null($dispute->payment_id)) { + $dispute->payment_id = Payment::inRandomOrder()->firstOr( + fn () => Payment::factory()->create() + )->id; + } + + if (is_null($dispute->amount)) { + $dispute->amount = $dispute->payment->amount; + } + }); + } +} diff --git a/database/factories/PaymentFactory.php b/database/factories/PaymentFactory.php new file mode 100644 index 0000000..d934cd0 --- /dev/null +++ b/database/factories/PaymentFactory.php @@ -0,0 +1,87 @@ + $this->faker->uuid(), + 'amount' => $this->faker->numberBetween(1, 999) * 100, + 'currency' => $this->faker->currencyCode(), + ]; + } + + /** + * Configure the model factory. + * + * @return $this + */ + public function configure() + { + return $this->afterMaking(function (Payment $payment) { + if (is_null($payment->provider_id)) { + $provider = ! is_null($payment->instrument_id) + ? $payment->instrument->provider + : Provider::whereHas( + 'accounts', + fn ($query) => $query->where('accounts.id', $payment->account_id) + )->inRandomOrder()->firstOr( + fn () => Provider::factory()->create() + ); + + $payment->provider_id = $provider->id; + } + + if (is_null($payment->account_id)) { + $account = ! is_null($payment->instrument_id) + ? $payment->instrument->account + : Account::whereHas( + 'providers', + fn ($query) => $query->where('providers.id', $payment->provider_id) + )->inRandomOrder() + ->firstOr(function () use ($payment) { + $account = Account::factory()->create(['default_provider_id' => $payment->provider_id]); + + $account->providers()->attach($payment->provider_id); + + return $account; + }); + + $payment->account_id = $account->id; + } + + if (is_null($payment->rail_id)) { + $rail = ! is_null($payment->instrument_id) + ? $payment->instrument->type->rails()->inRandomOrder()->firstOrCreate( + ['parent_type_id' => $payment->instrument->type_id], + ['type_id' => $payment->instrument->type_id] + ) : PaymentRail::inRandomOrder() + ->firstOr( + fn () => PaymentRail::factory()->create() + ); + + $payment->rail_id = $rail->id; + } + }); + } +} diff --git a/database/factories/PaymentMethodFactory.php b/database/factories/PaymentInstrumentFactory.php similarity index 63% rename from database/factories/PaymentMethodFactory.php rename to database/factories/PaymentInstrumentFactory.php index e4248ee..4ae9a06 100644 --- a/database/factories/PaymentMethodFactory.php +++ b/database/factories/PaymentInstrumentFactory.php @@ -4,17 +4,17 @@ use Illuminate\Database\Eloquent\Factories\Factory; use Payavel\Checkout\Models\Wallet; -use Payavel\Checkout\Models\PaymentMethod; +use Payavel\Checkout\Models\PaymentInstrument; use Payavel\Checkout\Models\PaymentType; -class PaymentMethodFactory extends Factory +class PaymentInstrumentFactory extends Factory { /** * The name of the factory's corresponding model. * * @var string */ - protected $model = PaymentMethod::class; + protected $model = PaymentInstrument::class; /** * Define the model's default state. @@ -24,7 +24,7 @@ class PaymentMethodFactory extends Factory public function definition() { return [ - 'token' => $this->faker->uuid(), + 'reference' => $this->faker->uuid(), ]; } @@ -35,21 +35,21 @@ public function definition() */ public function configure() { - return $this->afterMaking(function (PaymentMethod $paymentMethod) { - if(is_null($paymentMethod->wallet_id)) { + return $this->afterMaking(function (PaymentInstrument $paymentInstrument) { + if (is_null($paymentInstrument->wallet_id)) { $wallet = Wallet::inRandomOrder()->firstOr( fn () => Wallet::factory()->create() ); - $paymentMethod->wallet_id = $wallet->id; + $paymentInstrument->wallet_id = $wallet->id; } - if (is_null($paymentMethod->type_id)) { + if (is_null($paymentInstrument->type_id)) { $type = PaymentType::inRandomOrder()->firstOr( fn () => PaymentType::factory()->create() ); - $paymentMethod->type_id = $type->id; + $paymentInstrument->type_id = $type->id; } }); } diff --git a/database/factories/PaymentRailFactory.php b/database/factories/PaymentRailFactory.php new file mode 100644 index 0000000..5c323ac --- /dev/null +++ b/database/factories/PaymentRailFactory.php @@ -0,0 +1,133 @@ + 'visa', + 'type_id' => 'visa', + ], + [ + 'parent_type_id' => 'mastercard', + 'type_id' => 'mastercard', + ], + [ + 'parent_type_id' => 'amex', + 'type_id' => 'amex', + ], + [ + 'parent_type_id' => 'discover', + 'type_id' => 'discover', + ], + [ + 'parent_type_id' => 'diners_club', + 'type_id' => 'diners_club', + ], + [ + 'parent_type_id' => 'jcb', + 'type_id' => 'jcb', + ], + [ + 'parent_type_id' => 'apple_pay', + 'type_id' => 'visa', + ], + [ + 'parent_type_id' => 'apple_pay', + 'type_id' => 'mastercard', + ], + [ + 'parent_type_id' => 'apple_pay', + 'type_id' => 'amex', + ], + [ + 'parent_type_id' => 'apple_pay', + 'type_id' => 'discover', + ], + [ + 'parent_type_id' => 'google_pay', + 'type_id' => 'visa', + ], + [ + 'parent_type_id' => 'google_pay', + 'type_id' => 'mastercard', + ], + [ + 'parent_type_id' => 'google_pay', + 'type_id' => 'amex', + ], + [ + 'parent_type_id' => 'google_pay', + 'type_id' => 'discover', + ], + [ + 'parent_type_id' => 'paypal', + 'type_id' => 'paypal', + ], + ]; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return []; + } + + public function real() + { + return $this->state(function () { + $existingPaymentRails = PaymentRail::all()->pluck('id'); + + $rail = collect(static::REAL) + ->filter(function ($realPaymentRail) use ($existingPaymentRails) { + $realPaymentRailId = $realPaymentRail['parent_type_id'] === $realPaymentRail['type_id'] + ? $realPaymentRail['type_id'] + : "{$realPaymentRail['parent_type_id']}:{$realPaymentRail['type_id']}"; + + return $existingPaymentRails->doesntContain($realPaymentRailId); + }) + ->first(); + + if (is_null($rail)) { + return []; + } + + return $rail; + }); + } + + /** + * Configure the model factory. + * + * @return $this + */ + public function configure() + { + return $this->afterMaking(function (PaymentRail $paymentRail) { + if (is_null($paymentRail->parent_type_id)) { + $paymentRail->parent_type_id = PaymentType::inRandomOrder()->firstOr( + fn () => PaymentType::factory()->create() + )->id; + } + + if (is_null($paymentRail->type_id)) { + $paymentRail->type_id = PaymentType::factory()->create()->id; + } + }); + } +} diff --git a/database/factories/PaymentTransactionEventFactory.php b/database/factories/PaymentTransactionEventFactory.php deleted file mode 100644 index 275b072..0000000 --- a/database/factories/PaymentTransactionEventFactory.php +++ /dev/null @@ -1,57 +0,0 @@ - $this->faker->uuid(), - 'status_code' => $this->faker->randomElement([ - CheckoutStatus::CAPTURED, - CheckoutStatus::SETTLED, - CheckoutStatus::VOIDED, - CheckoutStatus::REFUNDED, - CheckoutStatus::REFUND_SETTLED, - ]), - ]; - } - - /** - * Configure the model factory. - * - * @return $this - */ - public function configure() - { - return $this->afterMaking(function (PaymentTransactionEvent $transactionEvent) { - if (is_null($transactionEvent->transaction_id)) { - $transaction = PaymentTransaction::factory()->create(); - - $transactionEvent->transaction_id = $transaction->id; - } - - if (is_null($transactionEvent->amount)) { - $transactionEvent->amount = $transactionEvent->transaction->amount; - } - }); - } -} diff --git a/database/factories/PaymentTransactionFactory.php b/database/factories/PaymentTransactionFactory.php deleted file mode 100644 index 810d935..0000000 --- a/database/factories/PaymentTransactionFactory.php +++ /dev/null @@ -1,75 +0,0 @@ - $this->faker->uuid(), - 'amount' => $this->faker->numberBetween(1, 999) * 100, - 'currency' => $this->faker->currencyCode(), - 'status_code' => CheckoutStatus::AUTHORIZED, - ]; - } - - /** - * Configure the model factory. - * - * @return $this - */ - public function configure() - { - return $this->afterMaking(function (PaymentTransaction $transaction) { - if (is_null($transaction->provider_id)) { - $provider = ! is_null($transaction->payment_method_id) - ? $transaction->paymentMethod->provider - : Provider::whereHas( - 'accounts', - fn ($query) => $query->where('payment_accounts.id', $transaction->account_id) - )->inRandomOrder()->firstOr( - fn () => Provider::factory()->create() - ); - - $transaction->provider_id = $provider->id; - } - - if (is_null($transaction->account_id)) { - $account = ! is_null($transaction->payment_method_id) - ? $transaction->paymentMethod->account - : Account::whereHas( - 'providers', - fn ($query) => $query->where('payment_providers.id', $transaction->provider_id) - )->inRandomOrder() - ->firstOr(function () use ($transaction) { - $account = Account::factory()->create(); - - $account->providers()->attach($transaction->provider_id, ['is_default' => true]); - - return $account; - }); - - $transaction->account_id = $account->id; - } - }); - } -} diff --git a/database/factories/PaymentTypeFactory.php b/database/factories/PaymentTypeFactory.php index bac4a86..53ce9cb 100644 --- a/database/factories/PaymentTypeFactory.php +++ b/database/factories/PaymentTypeFactory.php @@ -3,6 +3,7 @@ namespace Payavel\Checkout\Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Support\Str; use Payavel\Checkout\Models\PaymentType; class PaymentTypeFactory extends Factory @@ -14,46 +15,42 @@ class PaymentTypeFactory extends Factory */ protected $model = PaymentType::class; - public const DEFAULTS = [ + public const REAL = [ [ + 'id' => 'visa', 'name' => 'VISA', - 'slug' => 'visa', ], [ + 'id' => 'mastercard', 'name' => 'MasterCard', - 'slug' => 'mastercard', ], [ + 'id' => 'amex', 'name' => 'AMEX', - 'slug' => 'amex', ], [ - 'name' => 'Alipay', - 'slug' => 'alipay', - ], - [ - 'name' => 'Apple Pay', - 'slug' => 'apple_pay', + 'id' => 'discover', + 'name' => 'Discover', ], [ - 'name' => 'Google Pay', - 'slug' => 'google_pay', + 'id' => 'diners_club', + 'name' => 'Diners Club', ], [ + 'id' => 'jcb', 'name' => 'JCB', - 'slug' => 'jcb', ], [ - 'name' => 'Diners Club', - 'slug' => 'diners_club', + 'id' => 'apple_pay', + 'name' => 'Apple Pay', ], [ - 'name' => 'Discover', - 'slug' => 'discover', + 'id' => 'google_pay', + 'name' => 'Google Pay', ], [ + 'id' => 'paypal', 'name' => 'PayPal', - 'slug' => 'paypal', ], ]; @@ -64,18 +61,18 @@ class PaymentTypeFactory extends Factory */ public function definition() { - $name = $this->faker->unique()->lexify('????'); + $name = Str::ucfirst($this->faker->unique()->lexify(Str::repeat('?', rand(4, 8)))); return [ - 'name' => ucfirst($name), - 'slug' => PaymentType::slugify($name), + 'id' => preg_replace('/[^a-z]+/i', '_', Str::lower($name)), + 'name' => $name, ]; } public function real() { return $this->state(function () { - $type = collect(static::DEFAULTS)->whereNotIn('slug', PaymentType::all()->pluck('slug'))->first(); + $type = collect(static::REAL)->whereNotIn('id', PaymentType::all()->pluck('id'))->first(); if (is_null($type)) { return []; diff --git a/database/factories/RefundFactory.php b/database/factories/RefundFactory.php new file mode 100644 index 0000000..607e677 --- /dev/null +++ b/database/factories/RefundFactory.php @@ -0,0 +1,49 @@ + $this->faker->uuid(), + ]; + } + + /** + * Configure the model factory. + * + * @return $this + */ + public function configure() + { + return $this->afterMaking(function (Refund $refund) { + if (is_null($refund->payment_id)) { + $refund->payment_id = Payment::inRandomOrder()->firstOr( + fn () => Payment::factory()->create() + )->id; + } + + if (is_null($refund->amount)) { + $refund->amount = $refund->payment->amount; + } + }); + } +} diff --git a/database/factories/TransactionEventFactory.php b/database/factories/TransactionEventFactory.php new file mode 100644 index 0000000..a4e2ad2 --- /dev/null +++ b/database/factories/TransactionEventFactory.php @@ -0,0 +1,64 @@ + $this->faker->uuid(), + ]; + } + + /** + * Configure the model factory. + * + * @return $this + */ + public function configure() + { + $this->model = ServiceConfig::get('checkout', 'models.' . $this->model, $this->model); + + return $this->afterMaking(function (TransactionEvent $transactionEvent) { + if (is_null($transactionEvent->payment_id)) { + $transactionEvent->payment_id = Payment::factory()->create()->id; + } + + if (is_null($transactionEvent->amount)) { + $transactionEvent->amount = $transactionEvent->transactionable->amount ?? $transactionEvent->payment->amount; + } + + if (is_null($transactionEvent->status_code)) { + $transactionEvent->status_code = [ + ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class) => CheckoutStatus::AUTHORIZED, + ServiceConfig::get('checkout', 'models.' . Refund::class, Refund::class) => CheckoutStatus::REFUNDED, + ServiceConfig::get('checkout', 'models.' . Dispute::class, Dispute::class) => CheckoutStatus::CHARGEBACK, + ][ + ServiceConfig::get('checkout', 'models.' . $transactionEvent->transactionable_type, $transactionEvent->transactionable_type) ?? + ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class) + ]; + } + }); + } +} diff --git a/database/factories/WalletFactory.php b/database/factories/WalletFactory.php index 50c3af6..c51f29b 100644 --- a/database/factories/WalletFactory.php +++ b/database/factories/WalletFactory.php @@ -24,7 +24,7 @@ class WalletFactory extends Factory public function definition() { return [ - 'token' => $this->faker->uuid(), + 'reference' => $this->faker->uuid(), ]; } @@ -39,7 +39,7 @@ public function configure() if (is_null($wallet->provider_id)) { $provider = Provider::whereHas( 'accounts', - fn ($query) => $query->where('payment_accounts.id', $wallet->account_id) + fn ($query) => $query->where('accounts.id', $wallet->account_id) )->inRandomOrder() ->firstOr( fn () => Provider::factory()->create() @@ -51,12 +51,12 @@ public function configure() if (is_null($wallet->account_id)) { $account = Account::whereHas( 'providers', - fn ($query) => $query->where('payment_providers.id', $wallet->provider_id) + fn ($query) => $query->where('providers.id', $wallet->provider_id) )->inRandomOrder() ->firstOr(function () use ($wallet) { - $account = Account::factory()->create(); + $account = Account::factory()->create(['default_provider_id' => $wallet->provider_id]); - $account->providers()->attach($wallet->provider_id, ['is_default' => true]); + $account->providers()->attach($wallet->provider_id); return $account; }); diff --git a/database/migrations/2024_01_01_000010_create_base_checkout_tables.php b/database/migrations/2024_01_01_000010_create_base_checkout_tables.php index 468ef94..f1b5088 100644 --- a/database/migrations/2024_01_01_000010_create_base_checkout_tables.php +++ b/database/migrations/2024_01_01_000010_create_base_checkout_tables.php @@ -16,48 +16,61 @@ public function up() $usingDatabaseDriver = ServiceConfig::get('checkout', 'defaults.driver') === 'database'; Schema::create('payment_types', function (Blueprint $table) { - $table->smallIncrements('id'); + $table->string('id')->primary(); $table->string('name'); - $table->string('slug')->unique(); + $table->string('logo')->default('checkoutshopper-live.adyen.com/checkoutshopper/images/logos/card.svg'); $table->timestamps(); }); + Schema::create('payment_rails', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('parent_type_id'); + $table->string('type_id'); + $table->timestamps(); + + $table->foreign('parent_type_id')->references('id')->on('payment_types')->onUpdate('cascade')->onDelete('cascade'); + $table->foreign('type_id')->references('id')->on('payment_types')->onUpdate('cascade')->onDelete('cascade'); + }); + Schema::create('wallets', function (Blueprint $table) use ($usingDatabaseDriver) { $table->bigIncrements('id'); - $table->unsignedBigInteger('billable_id')->nullable(); - $table->string('billable_type')->nullable(); + $table->nullableMorphs('billable'); $table->string('provider_id'); $table->string('account_id'); - $table->string('token'); + $table->string('reference'); $table->timestamps(); if ($usingDatabaseDriver) { $table->foreign('provider_id')->references('id')->on('providers')->onUpdate('cascade')->onDelete('cascade'); $table->foreign('account_id')->references('id')->on('accounts')->onUpdate('cascade')->onDelete('cascade'); } + + $table->index('reference'); }); - Schema::create('payment_methods', function (Blueprint $table) { + Schema::create('payment_instruments', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('wallet_id'); - $table->string('token'); - $table->unsignedSmallInteger('type_id'); + $table->string('type_id'); + $table->string('reference'); $table->json('details')->nullable(); $table->timestamps(); $table->foreign('wallet_id')->references('id')->on('wallets')->onDelete('cascade'); - $table->foreign('type_id')->references('id')->on('payment_types'); + $table->foreign('type_id')->references('id')->on('payment_types')->onUpdate('cascade'); + $table->index('reference'); }); - Schema::create('payment_transactions', function (Blueprint $table) use ($usingDatabaseDriver) { + Schema::create('payments', function (Blueprint $table) use ($usingDatabaseDriver) { $table->bigIncrements('id'); $table->string('provider_id'); $table->string('account_id'); $table->string('reference'); + $table->boolean('authorized')->default(true); $table->unsignedInteger('amount'); $table->char('currency', 3)->default('USD'); - $table->unsignedBigInteger('payment_method_id')->nullable(); - $table->unsignedSmallInteger('status_code'); + $table->string('rail_id'); + $table->unsignedBigInteger('instrument_id')->nullable(); $table->json('details')->nullable(); $table->timestamps(); @@ -66,19 +79,49 @@ public function up() $table->foreign('account_id')->references('id')->on('accounts')->onUpdate('cascade'); } - $table->foreign('payment_method_id')->references('id')->on('payment_methods')->onDelete('set null'); + $table->index('reference'); + $table->index('authorized'); + $table->foreign('rail_id')->references('id')->on('payment_rails')->onUpdate('cascade'); + $table->foreign('instrument_id')->references('id')->on('payment_instruments')->onDelete('set null'); }); - Schema::create('payment_transaction_events', function (Blueprint $table) { + Schema::create('refunds', function (Blueprint $table) { $table->bigIncrements('id'); - $table->unsignedBigInteger('transaction_id'); - $table->string('reference')->nullable(); - $table->unsignedBigInteger('amount'); - $table->smallInteger('status_code'); + $table->unsignedBigInteger('payment_id'); + $table->string('reference'); + $table->unsignedInteger('amount'); $table->json('details')->nullable(); $table->timestamps(); - $table->foreign('transaction_id')->references('id')->on('payment_transactions')->onDelete('cascade'); + $table->foreign('payment_id')->references('id')->on('payments')->onDelete('cascade'); + $table->index('reference'); + }); + + Schema::create('disputes', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('payment_id'); + $table->string('reference'); + $table->unsignedInteger('amount'); + $table->json('details')->nullable(); + $table->timestamps(); + + $table->foreign('payment_id')->references('id')->on('payments')->onDelete('cascade'); + $table->index('reference'); + }); + + Schema::create('transaction_events', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('payment_id'); + $table->nullableMorphs('transactionable'); + $table->string('reference'); + $table->unsignedInteger('status_code'); + $table->unsignedInteger('amount'); + $table->json('details')->nullable(); + $table->timestamps(); + + $table->foreign('payment_id')->references('id')->on('payments')->onDelete('cascade'); + $table->index('reference'); + $table->index('status_code'); }); } @@ -89,10 +132,13 @@ public function up() */ public function down() { - Schema::dropIfExists('payment_transaction_events'); - Schema::dropIfExists('payment_transactions'); - Schema::dropIfExists('payment_methods'); - Schema::dropIfExists('payment_types'); + Schema::dropIfExists('transaction_events'); + Schema::dropIfExists('disputes'); + Schema::dropIfExists('refunds'); + Schema::dropIfExists('payments'); + Schema::dropIfExists('payment_instruments'); Schema::dropIfExists('wallets'); + Schema::dropIfExists('payment_rails'); + Schema::dropIfExists('payment_types'); } }; diff --git a/database/seeders/PaymentTypeSeeder.php b/database/seeders/PaymentTypeSeeder.php deleted file mode 100644 index 65fc868..0000000 --- a/database/seeders/PaymentTypeSeeder.php +++ /dev/null @@ -1,24 +0,0 @@ - $paymentType['slug'] - ], - [ - 'name' => $paymentType['name'], - ] - ); - } - } -} \ No newline at end of file diff --git a/src/CheckoutServiceProvider.php b/src/CheckoutServiceProvider.php index 1b03d43..9daa762 100644 --- a/src/CheckoutServiceProvider.php +++ b/src/CheckoutServiceProvider.php @@ -2,9 +2,14 @@ namespace Payavel\Checkout; +use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\ServiceProvider; use Payavel\Checkout\Console\Commands\CheckoutInstall; use Payavel\Checkout\Console\Commands\CheckoutProvider; +use Payavel\Checkout\Models\Dispute; +use Payavel\Checkout\Models\Payment; +use Payavel\Checkout\Models\Refund; +use Payavel\Orchestration\Support\ServiceConfig; class CheckoutServiceProvider extends ServiceProvider { @@ -19,6 +24,12 @@ public function boot() $this->registerCommands(); $this->registerMigrations(); + + Relation::morphMap([ + Payment::class => fn () => ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class), + Refund::class => fn () => ServiceConfig::get('checkout', 'models.' . Refund::class, Refund::class), + Dispute::class => fn () => ServiceConfig::get('checkout', 'models.' . Dispute::class, Dispute::class), + ]); } public function register() @@ -42,6 +53,7 @@ protected function registerPublishableAssets() $this->publishes([ __DIR__ . '/../stubs/config-service.stub' => base_path('stubs/orchestration/checkout/config-service.stub'), + __DIR__ . '/../stubs/config-service-database.stub' => base_path('stubs/orchestration/checkout/config-service-database.stub'), __DIR__ . '/../stubs/service-requester.stub' => base_path('stubs/orchestration/checkout/service-requester.stub'), __DIR__ . '/../stubs/service-responder.stub' => base_path('stubs/orchestration/checkout/service-responder.stub'), __DIR__ . '/../stubs/service-request.stub' => base_path('stubs/orchestration/checkout/service-request.stub'), diff --git a/src/Contracts/CheckoutRequester.php b/src/Contracts/CheckoutRequester.php index 8904eb7..cef067a 100644 --- a/src/Contracts/CheckoutRequester.php +++ b/src/Contracts/CheckoutRequester.php @@ -3,8 +3,8 @@ namespace Payavel\Checkout\Contracts; use Payavel\Checkout\Contracts\Billable; -use Payavel\Checkout\Models\PaymentMethod; -use Payavel\Checkout\Models\PaymentTransaction; +use Payavel\Checkout\Models\PaymentInstrument; +use Payavel\Checkout\Models\Payment; use Payavel\Checkout\Models\Wallet; interface CheckoutRequester @@ -18,41 +18,41 @@ interface CheckoutRequester public function getWallet(Wallet $wallet); /** - * Retrieve the payment method's details from the provider. + * Retrieve the payment instrument's details from the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function getPaymentMethod(PaymentMethod $paymentMethod); + public function getPaymentInstrument(PaymentInstrument $paymentInstrument); /** - * Store the payment method details at the provider. + * Store the payment instrument details at the provider. * * @param \Payavel\Checkout\Contracts\Billable $billable * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function tokenizePaymentMethod(Billable $billable, $data); + public function tokenizePaymentInstrument(Billable $billable, $data); /** - * Update the payment method's details at the provider. + * Update the payment instrument's details at the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function updatePaymentMethod(PaymentMethod $paymentMethod, $data); + public function updatePaymentInstrument(PaymentInstrument $paymentInstrument, $data); /** - * Delete the payment method at the provider. + * Delete the payment instrument at the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function deletePaymentMethod(PaymentMethod $paymentMethod); + public function deletePaymentInstrument(PaymentInstrument $paymentInstrument); /** - * Authorize a transaction. + * Authorize a payment. * * @param array|mixed $data * @param \Payavel\Checkout\Contracts\Billable|null $billable @@ -61,37 +61,38 @@ public function deletePaymentMethod(PaymentMethod $paymentMethod); public function authorize($data, Billable $billable = null); /** - * Capture a previously authorized transaction. + * Capture an authorized payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function capture(PaymentTransaction $transaction, $data = []); + public function capture(Payment $payment, $data = []); + // ToDo: The param should be an instance of Transactionable (Payment, Refund or Dispute) /** * Retrieve the transaction details from the provider. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $transaction * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function getTransaction(PaymentTransaction $transaction); + public function getTransaction(Payment $transaction); /** - * Void a previously authorized transaction. + * Void an authorized payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function void(PaymentTransaction $transaction, $data = []); + public function void(Payment $payment, $data = []); /** - * Refund a previously captured transaction. + * Refund a payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function refund(PaymentTransaction $transaction, $data = []); + public function refund(Payment $payment, $data = []); } diff --git a/src/Contracts/CheckoutResponder.php b/src/Contracts/CheckoutResponder.php index fe6e2c6..dfe2cdc 100644 --- a/src/Contracts/CheckoutResponder.php +++ b/src/Contracts/CheckoutResponder.php @@ -12,32 +12,32 @@ interface CheckoutResponder public function getWalletResponse(); /** - * Maps details from the getPaymentMethod() response to the expected format. + * Maps details from the getPaymentInstrument() response to the expected format. * * @return array|mixed */ - public function getPaymentMethodResponse(); + public function getPaymentInstrumentResponse(); /** - * Maps details from the tokenizePaymentMethod() response to the expected format. + * Maps details from the tokenizePaymentInstrument() response to the expected format. * * @return array|mixed */ - public function tokenizePaymentMethodResponse(); + public function tokenizePaymentInstrumentResponse(); /** - * Maps details from the updatePaymentMethod() response to the expected format. + * Maps details from the updatePaymentInstrument() response to the expected format. * * @return array|mixed */ - public function updatePaymentMethodResponse(); + public function updatePaymentInstrumentResponse(); /** - * Maps details from the deletePaymentMethod() response to the expected format. + * Maps details from the deletePaymentInstrument() response to the expected format. * * @return array|mixed */ - public function deletePaymentMethodResponse(); + public function deletePaymentInstrumentResponse(); /** * Maps details from the authorize() response to the expected format. diff --git a/src/Facades/Checkout.php b/src/Facades/Checkout.php index 7eb583f..cd134e4 100644 --- a/src/Facades/Checkout.php +++ b/src/Facades/Checkout.php @@ -16,15 +16,15 @@ * @method static void reset() * @method static string|int|\Payavel\Orchestration\Contracts\Accountable getDefaultAccount() * @method static \Payavel\Checkout\CheckoutResponse getWallet(\Payavel\Checkout\Models\Wallet $wallet) - * @method static \Payavel\Checkout\CheckoutResponse getPaymentMethod(\Payavel\Checkout\Models\PaymentMethod $paymentMethod) - * @method static \Payavel\Checkout\CheckoutResponse tokenizePaymentMethod(\Payavel\Checkout\Contracts\Billable $billable, $data) - * @method static \Payavel\Checkout\CheckoutResponse updatePaymentMethod(\Payavel\Checkout\Models\PaymentMethod $paymentMethod, $data) - * @method static \Payavel\Checkout\CheckoutResponse deletePaymentMethod(\Payavel\Checkout\Models\PaymentMethod $paymentMethod) + * @method static \Payavel\Checkout\CheckoutResponse getPaymentInstrument(\Payavel\Checkout\Models\PaymentInstrument $paymentInstrument) + * @method static \Payavel\Checkout\CheckoutResponse tokenizePaymentInstrument(\Payavel\Checkout\Contracts\Billable $billable, $data) + * @method static \Payavel\Checkout\CheckoutResponse updatePaymentInstrument(\Payavel\Checkout\Models\PaymentInstrument $paymentInstrument, $data) + * @method static \Payavel\Checkout\CheckoutResponse deletePaymentInstrument(\Payavel\Checkout\Models\PaymentInstrument $paymentInstrument) * @method static \Payavel\Checkout\CheckoutResponse authorize($data, \Payavel\Checkout\Contracts\Billable $billable = null) - * @method static \Payavel\Checkout\CheckoutResponse capture(\Payavel\Checkout\Models\PaymentTransaction $transaction, $data = []) - * @method static \Payavel\Checkout\CheckoutResponse getTransaction(\Payavel\Checkout\Models\PaymentTransaction $transaction) - * @method static \Payavel\Checkout\CheckoutResponse void(\Payavel\Checkout\Models\PaymentTransaction $transaction, $data = []) - * @method static \Payavel\Checkout\CheckoutResponse refund(\Payavel\Checkout\Models\PaymentTransaction $transaction, $data = []) + * @method static \Payavel\Checkout\CheckoutResponse capture(\Payavel\Checkout\Models\Payment $payment, $data = []) + * @method static \Payavel\Checkout\CheckoutResponse getTransaction(\Payavel\Checkout\Models\Payment $transaction) + * @method static \Payavel\Checkout\CheckoutResponse void(\Payavel\Checkout\Models\Payment $payment, $data = []) + * @method static \Payavel\Checkout\CheckoutResponse refund(\Payavel\Checkout\Models\Payment $payment, $data = []) * * @see \Payavel\Checkout\CheckoutGateway */ diff --git a/src/Models/Dispute.php b/src/Models/Dispute.php new file mode 100644 index 0000000..ffd3b05 --- /dev/null +++ b/src/Models/Dispute.php @@ -0,0 +1,87 @@ + 'array', + ]; + + /** + * Custom factory namespace fallback. + * + * @return string + */ + protected static function getFactoryNamespace() + { + return 'Payavel\\Checkout\\Database\\Factories'; + } + + /** + * Get the dispute's provider. + * + * @return \Payavel\Orchestration\Models\Provider + */ + public function getProviderAttribute() + { + return $this->payment->provider; + } + + /** + * Get the dispute's account. + * + * @return \Payavel\Orchestration\Models\Account + */ + public function getAccountAttribute() + { + return $this->payment->account; + } + + /** + * Get the payment that is being disputed. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function payment() + { + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class)); + } + + /** + * Get the transaction specific events. + * + * @return \Illuminate\Database\Eloquent\Relations\MorphMany + */ + public function transactionEvents() + { + return $this->morphMany(ServiceConfig::get('checkout', 'models.' . TransactionEvent::class, TransactionEvent::class), 'transactionable'); + } +} diff --git a/src/Models/PaymentTransaction.php b/src/Models/Payment.php similarity index 67% rename from src/Models/PaymentTransaction.php rename to src/Models/Payment.php index 18cb662..f3aa25d 100644 --- a/src/Models/PaymentTransaction.php +++ b/src/Models/Payment.php @@ -9,7 +9,7 @@ use Payavel\Orchestration\Support\ServiceConfig; use Payavel\Orchestration\Traits\HasFactory; -class PaymentTransaction extends Model +class Payment extends Model { use ConfiguresCheckoutGateway; use HasFactory; @@ -50,47 +50,67 @@ protected static function getFactoryNamespace() } /** - * Get the payment method used for this transaction. + * Get the provider that processed the payment. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function paymentMethod() + public function provider() { - return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . PaymentMethod::class, PaymentMethod::class)); + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . Provider::class, Provider::class)); } /** - * Get the provider the transaction belongs to. + * Get the account the payment belongs to. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function provider() + public function account() { - return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . Provider::class, Provider::class)); + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . Account::class, Account::class)); } /** - * Get the account the transaction belongs to. + * Get the rail the payment was processed on. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function account() + public function rail() { - return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . Account::class, Account::class)); + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . PaymentRail::class, PaymentRail::class)); } /** - * Get the transaction's event history. + * Get the instrument used to process this payment. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function instrument() + { + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . PaymentInstrument::class, PaymentInstrument::class)); + } + + /** + * Get the payment event full history. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function events() { - return $this->hasMany(ServiceConfig::get('checkout', 'models.' . PaymentTransactionEvent::class, PaymentTransactionEvent::class), 'transaction_id'); + return $this->hasMany(ServiceConfig::get('checkout', 'models.' . TransactionEvent::class, TransactionEvent::class)); + } + + /** + * Get the transaction specific events. + * + * @return \Illuminate\Database\Eloquent\Relations\MorphMany + */ + public function transactionEvents() + { + return $this->morphMany(ServiceConfig::get('checkout', 'models.' . TransactionEvent::class, TransactionEvent::class), 'transactionable'); } /** - * Fetch the transaction details from the provider. + * Fetch the payment details from the provider. * * @return \Payavel\Checkout\CheckoutResponse */ @@ -100,7 +120,7 @@ public function fetch() } /** - * Request the provider to void the transaction. + * Request the provider to void the payment. * * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse @@ -111,7 +131,7 @@ public function void($data = []) } /** - * Request the provider to refund the transaction. + * Request the provider to refund the payment. * * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse diff --git a/src/Models/PaymentMethod.php b/src/Models/PaymentInstrument.php similarity index 73% rename from src/Models/PaymentMethod.php rename to src/Models/PaymentInstrument.php index 171f237..6fb95e8 100644 --- a/src/Models/PaymentMethod.php +++ b/src/Models/PaymentInstrument.php @@ -7,7 +7,7 @@ use Payavel\Orchestration\Support\ServiceConfig; use Payavel\Orchestration\Traits\HasFactory; -class PaymentMethod extends Model +class PaymentInstrument extends Model { use ConfiguresCheckoutGateway; use HasFactory; @@ -25,7 +25,7 @@ class PaymentMethod extends Model * @var array */ protected $hidden = [ - 'token', + 'reference', 'details', ]; @@ -49,7 +49,7 @@ protected static function getFactoryNamespace() } /** - * Get the payment method's provider. + * Get the payment instrument's provider. * * @return \Payavel\Orchestration\Models\Provider */ @@ -59,7 +59,7 @@ public function getProviderAttribute() } /** - * Get the payment method's account. + * Get the payment instrument's account. * * @return \Payavel\Orchestration\Models\Account */ @@ -69,7 +69,7 @@ public function getAccountAttribute() } /** - * Get the wallet the payment method belongs to. + * Get the wallet the payment instrument belongs to. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -79,7 +79,7 @@ public function wallet() } /** - * Get the payment method's type + * Get the payment instrument's type * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -89,43 +89,43 @@ public function type() } /** - * Get the transactions that this payment method has triggered. + * Get the payments that this instrument has been used for. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function transactions() + public function payments() { - return $this->hasMany(ServiceConfig::get('checkout', 'models.' . PaymentTransaction::class, PaymentTransaction::class)); + return $this->hasMany(ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class), 'instrument_id'); } /** - * Fetch the payment method details from the provider. + * Fetch the payment instrument details from the provider. * * @return \Payavel\Checkout\CheckoutResponse */ public function fetch() { - return $this->gateway->getPaymentMethod($this); + return $this->gateway->getPaymentInstrument($this); } /** - * Request the provider to update the payment method's details. + * Request the provider to update the payment instrument's details. * * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse */ public function patch($data) { - return $this->gateway->updatePaymentMethod($this, $data); + return $this->gateway->updatePaymentInstrument($this, $data); } /** - * Request the provider to remove the payment method from their system. + * Request the provider to remove the payment instrument from their system. * * @return \Payavel\Checkout\CheckoutResponse */ public function disable() { - return $this->gateway->deletePaymentMethod($this); + return $this->gateway->deletePaymentInstrument($this); } } diff --git a/src/Models/PaymentRail.php b/src/Models/PaymentRail.php new file mode 100644 index 0000000..e5e3257 --- /dev/null +++ b/src/Models/PaymentRail.php @@ -0,0 +1,79 @@ +id = $paymentRail->parent_type_id === $paymentRail->type_id + ? $paymentRail->type_id + : "{$paymentRail->parent_type_id}:{$paymentRail->type_id}"; + }); + } + + /** + * Custom factory namespace fallback. + * + * @return string + */ + protected static function getFactoryNamespace() + { + return 'Payavel\\Checkout\\Database\\Factories'; + } + + /** + * Get the parent payment type. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function parentType() + { + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . PaymentType::class, PaymentType::class)); + } + + /** + * Get the rail's payment type. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function type() + { + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . PaymentType::class, PaymentType::class)); + } + + /** + * Get the payments that were processed over this rail. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function payments() + { + return $this->hasMany(ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class), 'rail_id'); + } +} diff --git a/src/Models/PaymentType.php b/src/Models/PaymentType.php index 45d1825..e242f50 100644 --- a/src/Models/PaymentType.php +++ b/src/Models/PaymentType.php @@ -10,12 +10,19 @@ class PaymentType extends Model { use HasFactory; + /** + * Indicates if the model's ID is auto-incrementing. + * + * @var bool + */ + public $incrementing = false; + /** * The attributes that aren't mass assignable. * * @var string[]|bool */ - protected $guarded = ['id']; + protected $guarded = []; /** * Custom factory namespace fallback. @@ -28,23 +35,22 @@ protected static function getFactoryNamespace() } /** - * Get the payment methods that inherit this type. + * Get the payment rail this type could potentially use. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function paymentMethods() + public function rails() { - return $this->hasMany(ServiceConfig::get('checkout', 'models.' . PaymentMethod::class, PaymentMethod::class), 'type_id'); + return $this->hasMany(ServiceConfig::get('checkout', 'models.' . PaymentRail::class, PaymentRail::class), 'parent_type_id'); } /** - * Generate a slug based off a string that follows a set of rules to make it valid. + * Get the payment instruments that inherit this type. * - * @param string $name - * @return string + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public static function slugify($name) + public function instruments() { - return preg_replace('/[^a-z0-9]+/i', '_', trim(strtolower($name))); + return $this->hasMany(ServiceConfig::get('checkout', 'models.' . PaymentInstrument::class, PaymentInstrument::class), 'type_id'); } } diff --git a/src/Models/Refund.php b/src/Models/Refund.php new file mode 100644 index 0000000..9c67a14 --- /dev/null +++ b/src/Models/Refund.php @@ -0,0 +1,87 @@ + 'array', + ]; + + /** + * Custom factory namespace fallback. + * + * @return string + */ + protected static function getFactoryNamespace() + { + return 'Payavel\\Checkout\\Database\\Factories'; + } + + /** + * Get the refund's provider. + * + * @return \Payavel\Orchestration\Models\Provider + */ + public function getProviderAttribute() + { + return $this->payment->provider; + } + + /** + * Get the refund's account. + * + * @return \Payavel\Orchestration\Models\Account + */ + public function getAccountAttribute() + { + return $this->payment->account; + } + + /** + * Get the payment that is being refunded. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function payment() + { + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class)); + } + + /** + * Get the transaction specific events. + * + * @return \Illuminate\Database\Eloquent\Relations\MorphMany + */ + public function transactionEvents() + { + return $this->morphMany(ServiceConfig::get('checkout', 'models.' . TransactionEvent::class, TransactionEvent::class), 'transactionable'); + } +} diff --git a/src/Models/PaymentTransactionEvent.php b/src/Models/TransactionEvent.php similarity index 54% rename from src/Models/PaymentTransactionEvent.php rename to src/Models/TransactionEvent.php index 48ccf75..fafbe07 100644 --- a/src/Models/PaymentTransactionEvent.php +++ b/src/Models/TransactionEvent.php @@ -3,10 +3,12 @@ namespace Payavel\Checkout\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\Relation; +use Illuminate\Support\Arr; use Payavel\Orchestration\Support\ServiceConfig; use Payavel\Orchestration\Traits\HasFactory; -class PaymentTransactionEvent extends Model +class TransactionEvent extends Model { use HasFactory; @@ -46,12 +48,39 @@ protected static function getFactoryNamespace() } /** - * Get the event's original transaction. + * Get the event's originating payment. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function transaction() + public function payment() { - return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . PaymentTransaction::class, PaymentTransaction::class)); + return $this->belongsTo(ServiceConfig::get('checkout', 'models.' . Payment::class, Payment::class)); + } + + /** + * Get the event's originating transaction. + * + * @return \Illuminate\Database\Eloquent\Relations\MorphTo + */ + public function transactionable() + { + return $this->morphTo(); + } + + /** + * Retrieve the actual class name for a given morph class. + * + * @param string $class + * @return string + */ + public static function getActualClassNameForMorph($class) + { + $value = Arr::get(Relation::morphMap() ?: [], $class, $class); + + if (is_callable($value)) { + return $value(); + } + + return $value; } } diff --git a/src/Models/Wallet.php b/src/Models/Wallet.php index cb1d405..462421e 100644 --- a/src/Models/Wallet.php +++ b/src/Models/Wallet.php @@ -26,7 +26,7 @@ class Wallet extends Model * * @var array */ - protected $hidden = ['token']; + protected $hidden = ['reference']; /** * Custom factory namespace fallback. @@ -69,13 +69,13 @@ public function account() } /** - * Get the wallet's payment methods. + * Get the wallet's payment instruments. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function paymentMethods() + public function paymentInstruments() { - return $this->hasMany(ServiceConfig::get('checkout', 'models.' . PaymentMethod::class, PaymentMethod::class)); + return $this->hasMany(ServiceConfig::get('checkout', 'models.' . PaymentInstrument::class, PaymentInstrument::class)); } /** diff --git a/src/Traits/CheckoutRequests.php b/src/Traits/CheckoutRequests.php index 2540602..facecca 100644 --- a/src/Traits/CheckoutRequests.php +++ b/src/Traits/CheckoutRequests.php @@ -3,8 +3,8 @@ namespace Payavel\Checkout\Traits; use Payavel\Checkout\Contracts\Billable; -use Payavel\Checkout\Models\PaymentMethod; -use Payavel\Checkout\Models\PaymentTransaction; +use Payavel\Checkout\Models\PaymentInstrument; +use Payavel\Checkout\Models\Payment; use Payavel\Checkout\Models\Wallet; use Payavel\Orchestration\Traits\ThrowsRuntimeException; @@ -24,53 +24,53 @@ public function getWallet(Wallet $wallet) } /** - * Retrieve the payment method's details from the provider. + * Retrieve the payment instrument's details from the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function getPaymentMethod(PaymentMethod $paymentMethod) + public function getPaymentInstrument(PaymentInstrument $paymentInstrument) { $this->throwRuntimeException(__FUNCTION__); } /** - * Store the payment method details at the provider. + * Store the payment instrument details at the provider. * * @param \Payavel\Checkout\Contracts\Billable $billable * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function tokenizePaymentMethod(Billable $billable, $data) + public function tokenizePaymentInstrument(Billable $billable, $data) { $this->throwRuntimeException(__FUNCTION__); } /** - * Update the payment method's details at the provider. + * Update the payment instrument's details at the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function updatePaymentMethod(PaymentMethod $paymentMethod, $data) + public function updatePaymentInstrument(PaymentInstrument $paymentInstrument, $data) { $this->throwRuntimeException(__FUNCTION__); } /** - * Delete the payment method at the provider. + * Delete the payment instrument at the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function deletePaymentMethod(PaymentMethod $paymentMethod) + public function deletePaymentInstrument(PaymentInstrument $paymentInstrument) { $this->throwRuntimeException(__FUNCTION__); } /** - * Authorize a transaction. + * Authorize a payment. * * @param array|mixed $data * @param \Payavel\Checkout\Contracts\Billable|null $billable @@ -82,48 +82,49 @@ public function authorize($data, Billable $billable = null) } /** - * Capture a previously authorized transaction. + * Capture an authorized payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function capture(PaymentTransaction $transaction, $data = []) + public function capture(Payment $payment, $data = []) { $this->throwRuntimeException(__FUNCTION__); } + // ToDo: The param should be an instance of Transactionable (Payment, Refund or Dispute) /** * Retrieve the transaction details from the provider. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $transaction * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function getTransaction(PaymentTransaction $transaction) + public function getTransaction(Payment $transaction) { $this->throwRuntimeException(__FUNCTION__); } /** - * Void a previously authorized transaction. + * Void an authorized payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $paymentTransaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function void(PaymentTransaction $paymentTransaction, $data = []) + public function void(Payment $payment, $data = []) { $this->throwRuntimeException(__FUNCTION__); } /** - * Refund a previously captured transaction. + * Refund a payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $paymentTransaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function refund(PaymentTransaction $paymentTransaction, $data = []) + public function refund(Payment $payment, $data = []) { $this->throwRuntimeException(__FUNCTION__); } diff --git a/src/Traits/CheckoutResponses.php b/src/Traits/CheckoutResponses.php index 80fc9ce..f68b34b 100644 --- a/src/Traits/CheckoutResponses.php +++ b/src/Traits/CheckoutResponses.php @@ -23,49 +23,49 @@ public function getWalletResponse() } /** - * Maps details from the getPaymentMethod() response to the expected format. + * Maps details from the getPaymentInstrument() response to the expected format. * * @return array|mixed * * @throws \RuntimeException|Exception */ - public function getPaymentMethodResponse() + public function getPaymentInstrumentResponse() { return $this->genericResponse(__FUNCTION__); } /** - * Maps details from the tokenizePaymentMethod() response to the expected format. + * Maps details from the tokenizePaymentInstrument() response to the expected format. * * @return array|mixed * * @throws \RuntimeException|Exception */ - public function tokenizePaymentMethodResponse() + public function tokenizePaymentInstrumentResponse() { return $this->genericResponse(__FUNCTION__); } /** - * Maps details from the updatePaymentMethod() response to the expected format. + * Maps details from the updatePaymentInstrument() response to the expected format. * * @return array|mixed * * @throws \RuntimeException|Exception */ - public function updatePaymentMethodResponse() + public function updatePaymentInstrumentResponse() { return $this->genericResponse(__FUNCTION__); } /** - * Maps details from the deletePaymentMethod() response to the expected format. + * Maps details from the deletePaymentInstrument() response to the expected format. * * @return array|mixed * * @throws \RuntimeException|Exception */ - public function deletePaymentMethodResponse() + public function deletePaymentInstrumentResponse() { return $this->genericResponse(__FUNCTION__); } @@ -129,7 +129,7 @@ public function refundResponse() } /** - * Attempts to call the generic response method, else throws RuntimeException. + * Attempts to call the generic response Instrument, else throws RuntimeException. * * @param string $function * @return array|mixed diff --git a/src/Traits/ConfiguresCheckoutGateway.php b/src/Traits/ConfiguresCheckoutGateway.php index 1211fad..38c9c17 100644 --- a/src/Traits/ConfiguresCheckoutGateway.php +++ b/src/Traits/ConfiguresCheckoutGateway.php @@ -14,7 +14,7 @@ trait ConfiguresCheckoutGateway private $checkoutGateway; /** - * Retrieve the payment method's configured gateway. + * Retrieve the checkout model's pre-configured gateway. * * @return \Payavel\Checkout\CheckoutGateway */ diff --git a/stubs/config-service-database.stub b/stubs/config-service-database.stub new file mode 100644 index 0000000..aa6032b --- /dev/null +++ b/stubs/config-service-database.stub @@ -0,0 +1,68 @@ + 'Checkout', + + /* + |-------------------------------------------------------------------------- + | Checkout Defaults + |-------------------------------------------------------------------------- + | + | This option defines the default checkout service config + | for your application. You should define your checkout + | provider of choice along with your primary account. + | + */ + 'defaults' => [ + 'driver' => '{{ driver }}', + 'provider' => '{{ provider }}', + 'account' => '{{ account }}', + ], + + /* + |-------------------------------------------------------------------------- + | Checkout Test Mode + |-------------------------------------------------------------------------- + | + | When set to true, the provider & account will be shared with the fake checkout + | request so you can mock your responses as you wish. This is very useful for + | local & testing environments where a sandbox is limited or non-existent. + | + */ + 'test_mode' => env('CHECKOUT_TEST_MODE', false), + + /* + |-------------------------------------------------------------------------- + | Checkout Testing + |-------------------------------------------------------------------------- + | + | This option allows you to define the location of the fake checkout + | request & response classes you would like to leverage when test_mode + | is set to true. Also, feel free to add any other settings here. + | + */ + 'test_gateway' => \App\Services\Checkout\FakeCheckoutRequest::class, + + /* + |-------------------------------------------------------------------------- + | Checkout Models + |-------------------------------------------------------------------------- + | + | You may override the checkout package models by specifying a model from your + | application. The checkout package will automatically inject the overridden + | model in it's place, so be sure to have covered all of its functionality. + | + */ + // 'models' => [ + // \Payavel\Checkout\Models\PaymentType::class => \App\Models\PaymentType::class, + // \Payavel\Checkout\Models\PaymentRail::class => \App\Models\PaymentRail::class, + // \Payavel\Checkout\Models\Wallet::class => \App\Models\Wallet::class, + // \Payavel\Checkout\Models\PaymentInstrument::class => \App\Models\PaymentInstrument::class, + // \Payavel\Checkout\Models\Payment::class => \App\Models\Payment::class, + // \Payavel\Checkout\Models\Refund::class => \App\Models\Refund::class, + // \Payavel\Checkout\Models\Dispute::class => \App\Models\Dispute::class, + // \Payavel\Checkout\Models\TransactionEvent::class => \App\Models\TransactionEvent::class, + // ], + +]; diff --git a/stubs/config-service.stub b/stubs/config-service.stub index 3978dbd..a6d275a 100644 --- a/stubs/config-service.stub +++ b/stubs/config-service.stub @@ -83,11 +83,14 @@ return [ | */ // 'models' => [ - // \Payavel\Checkout\Models\PaymentMethod::class => \App\Models\PaymentMethod::class, - // \Payavel\Checkout\Models\PaymentTransaction::class => \App\Models\PaymentTransaction::class, - // \Payavel\Checkout\Models\PaymentTransactionEvent::class => \App\Models\PaymentTransactionEvent::class, // \Payavel\Checkout\Models\PaymentType::class => \App\Models\PaymentType::class, + // \Payavel\Checkout\Models\PaymentRail::class => \App\Models\PaymentRail::class, // \Payavel\Checkout\Models\Wallet::class => \App\Models\Wallet::class, + // \Payavel\Checkout\Models\PaymentInstrument::class => \App\Models\PaymentInstrument::class, + // \Payavel\Checkout\Models\Payment::class => \App\Models\Payment::class, + // \Payavel\Checkout\Models\Refund::class => \App\Models\Refund::class, + // \Payavel\Checkout\Models\Dispute::class => \App\Models\Dispute::class, + // \Payavel\Checkout\Models\TransactionEvent::class => \App\Models\TransactionEvent::class, // ], ]; diff --git a/stubs/service-request.stub b/stubs/service-request.stub index fcec122..b9d1ec7 100644 --- a/stubs/service-request.stub +++ b/stubs/service-request.stub @@ -5,8 +5,8 @@ namespace App\Services\Checkout; use App\Services\Checkout\Contracts\CheckoutRequester; use Payavel\Checkout\CheckoutRequest; use Payavel\Checkout\Contracts\Billable; -use Payavel\Checkout\Models\PaymentMethod; -use Payavel\Checkout\Models\PaymentTransaction; +use Payavel\Checkout\Models\PaymentInstrument; +use Payavel\Checkout\Models\Payment; use Payavel\Checkout\Models\Wallet; class {{ Provider }}CheckoutRequest extends CheckoutRequest implements CheckoutRequester @@ -33,53 +33,53 @@ class {{ Provider }}CheckoutRequest extends CheckoutRequest implements CheckoutR } /** - * Retrieve the payment method's details from the provider. + * Retrieve the payment instrument's details from the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function getPaymentMethod(PaymentMethod $paymentMethod) + public function getPaymentInstrument(PaymentInstrument $paymentInstrument) { // } /** - * Store the payment method details at the provider. + * Store the payment instrument details at the provider. * * @param \Payavel\Checkout\Contracts\Billable $billable * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function tokenizePaymentMethod(Billable $billable, $data) + public function tokenizePaymentInstrument(Billable $billable, $data) { // } /** - * Update the payment method's details at the provider. + * Update the payment instrument's details at the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function updatePaymentMethod(PaymentMethod $paymentMethod, $data) + public function updatePaymentInstrument(PaymentInstrument $paymentInstrument, $data) { // } /** - * Delete the payment method at the provider. + * Delete the payment instrument at the provider. * - * @param \Payavel\Checkout\Models\PaymentMethod $paymentMethod + * @param \Payavel\Checkout\Models\PaymentInstrument $paymentInstrument * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function deletePaymentMethod(PaymentMethod $paymentMethod) + public function deletePaymentInstrument(PaymentInstrument $paymentInstrument) { // } /** - * Authorize a transaction. + * Authorize a payment. * * @param array|mixed $data * @param \Payavel\Checkout\Contracts\Billable|null $billable @@ -91,13 +91,13 @@ class {{ Provider }}CheckoutRequest extends CheckoutRequest implements CheckoutR } /** - * Capture a previously authorized transaction. + * Capture any authorized payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function capture(PaymentTransaction $transaction, $data = []) + public function capture(Payment $payment, $data = []) { // } @@ -105,34 +105,34 @@ class {{ Provider }}CheckoutRequest extends CheckoutRequest implements CheckoutR /** * Retrieve the transaction details from the provider. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $transaction * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function getTransaction(PaymentTransaction $transaction) + public function getTransaction(Payment $transaction) { // } /** - * Void a previously authorized transaction. + * Void an authorized payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed $data * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function void(PaymentTransaction $transaction, $data = []) + public function void(Payment $payment, $data = []) { // } /** - * Refund a previously captured transaction. + * Refund a payment. * - * @param \Payavel\Checkout\Models\PaymentTransaction $transaction + * @param \Payavel\Checkout\Models\Payment $payment * @param array|mixed * @return \Payavel\Checkout\CheckoutResponse|mixed */ - public function refund(PaymentTransaction $transaction, $data = []) + public function refund(Payment $payment, $data = []) { // } diff --git a/stubs/service-response.stub b/stubs/service-response.stub index a5f7f26..30a78f1 100644 --- a/stubs/service-response.stub +++ b/stubs/service-response.stub @@ -38,41 +38,41 @@ class {{ Provider }}CheckoutResponse extends CheckoutResponse implements Checkou } /** - * Maps details from the getPaymentMethod() response to the expected format. + * Maps details from the getPaymentInstrument() response to the expected format. * * @return array|mixed */ - public function getPaymentMethodResponse() + public function getPaymentInstrumentResponse() { // } /** - * Maps details from the tokenizePaymentMethod() response to the expected format. + * Maps details from the tokenizePaymentInstrument() response to the expected format. * * @return array|mixed */ - public function tokenizePaymentMethodResponse() + public function tokenizePaymentInstrumentResponse() { // } /** - * Maps details from the updatePaymentMethod() response to the expected format. + * Maps details from the updatePaymentInstrument() response to the expected format. * * @return array|mixed */ - public function updatePaymentMethodResponse() + public function updatePaymentInstrumentResponse() { // } /** - * Maps details from the deletePaymentMethod() response to the expected format. + * Maps details from the deletePaymentInstrument() response to the expected format. * * @return array|mixed */ - public function deletePaymentMethodResponse() + public function deletePaymentInstrumentResponse() { // } diff --git a/tests/Models/TestAccount.php b/tests/Models/TestAccount.php new file mode 100644 index 0000000..89433c1 --- /dev/null +++ b/tests/Models/TestAccount.php @@ -0,0 +1,22 @@ +create(); + $this->assertInstanceOf(Provider::class, $disputeWithProvider->provider); + + ServiceConfig::set('checkout', 'models.' . Provider::class, TestProvider::class); + $disputeWithOverriddenProvider = Dispute::factory()->create(); + $this->assertInstanceOf(TestProvider::class, $disputeWithOverriddenProvider->provider); + } + + #[Test] + public function retrieve_dispute_account() + { + $disputeWithAccount = Dispute::factory()->create(); + $this->assertInstanceOf(Account::class, $disputeWithAccount->account); + + ServiceConfig::set('checkout', 'models.' . Account::class, TestAccount::class); + $disputeWithOverriddenAccount = Dispute::factory()->create(); + $this->assertInstanceOf(TestAccount::class, $disputeWithOverriddenAccount->account); + } + + #[Test] + public function retrieve_dispute_payment() + { + $disputeWithPayment = Dispute::factory()->create(); + $this->assertInstanceOf(Payment::class, $disputeWithPayment->payment); + + ServiceConfig::set('checkout', 'models.' . Payment::class, TestPayment::class); + $disputeWithOverriddenPayment = Dispute::factory()->create(); + $this->assertInstanceOf(TestPayment::class, $disputeWithOverriddenPayment->payment); + } + + #[Test] + public function retrieve_dispute_transaction_events() + { + $dispute = Dispute::factory()->create(); + $this->assertEmpty($dispute->transactionEvents); + + $disputeWith2TransactionEvents = Dispute::factory()->hasTransactionEvents(2)->create(); + $this->assertCount(2, $disputeWith2TransactionEvents->transactionEvents); + $this->assertContainsOnlyInstancesOf(TransactionEvent::class, $disputeWith2TransactionEvents->transactionEvents); + + ServiceConfig::set('checkout', 'models.' . TransactionEvent::class, TestTransactionEvent::class); + $disputeWith3OverriddenTransactionEvents = Dispute::factory()->hasTransactionEvents(3)->create(); + $this->assertCount(3, $disputeWith3OverriddenTransactionEvents->transactionEvents); + $this->assertContainsOnlyInstancesOf(TestTransactionEvent::class, $disputeWith3OverriddenTransactionEvents->transactionEvents); + } +} diff --git a/tests/Unit/PaymentInstrumentModelTest.php b/tests/Unit/PaymentInstrumentModelTest.php new file mode 100644 index 0000000..96a7a45 --- /dev/null +++ b/tests/Unit/PaymentInstrumentModelTest.php @@ -0,0 +1,79 @@ +create(); + $this->assertInstanceOf(Provider::class, $paymentInstrumentWithProviderViaWallet->provider); + + ServiceConfig::set('checkout', 'models.' . Wallet::class, TestWallet::class); + $paymentInstrumentWithProviderViaOverriddenWallet = PaymentInstrument::factory()->create(); + $this->assertInstanceOf(Provider::class, $paymentInstrumentWithProviderViaOverriddenWallet->provider); + } + + #[Test] + public function retrieve_payment_instrument_account() + { + $paymentInstrumentWithAccountViaWallet = PaymentInstrument::factory()->create(); + $this->assertInstanceOf(Account::class, $paymentInstrumentWithAccountViaWallet->account); + + ServiceConfig::set('checkout', 'models.' . Wallet::class, TestWallet::class); + $paymentInstrumentWithAccountViaOverriddenWallet = PaymentInstrument::factory()->create(); + $this->assertInstanceOf(Account::class, $paymentInstrumentWithAccountViaOverriddenWallet->account); + } + + #[Test] + public function retrieve_payment_instrument_wallet() + { + $paymentInstrumentWithWallet = PaymentInstrument::factory()->create(); + $this->assertInstanceOf(Wallet::class, $paymentInstrumentWithWallet->wallet); + + ServiceConfig::set('checkout', 'models.' . Wallet::class, TestWallet::class); + $paymentInstrumentWithOverriddenWallet = PaymentInstrument::factory()->create(); + $this->assertInstanceOf(TestWallet::class, $paymentInstrumentWithOverriddenWallet->wallet); + } + + #[Test] + public function retrieve_payment_instrument_type() + { + $paymentInstrumentWithType = PaymentInstrument::factory()->create(); + $this->assertInstanceOf(PaymentType::class, $paymentInstrumentWithType->type); + + ServiceConfig::set('checkout', 'models.' . PaymentType::class, TestPaymentType::class); + $paymentInstrumentWithOverriddenType = PaymentInstrument::factory()->create(); + $this->assertInstanceOf(TestPaymentType::class, $paymentInstrumentWithOverriddenType->type); + } + + #[Test] + public function retrieve_payment_instrument_payments() + { + $paymentInstrument = PaymentInstrument::factory()->create(); + $this->assertEmpty($paymentInstrument->payments); + + $paymentInstrumentWith2Payments = PaymentInstrument::factory()->hasPayments(2)->create(); + $this->assertCount(2, $paymentInstrumentWith2Payments->payments); + $this->assertContainsOnlyInstancesOf(Payment::class, $paymentInstrumentWith2Payments->payments); + + ServiceConfig::set('checkout', 'models.' . Payment::class, TestPayment::class); + $paymentInstrumentWith3OverriddenPayments = PaymentInstrument::factory()->hasPayments(3)->create(); + $this->assertCount(3, $paymentInstrumentWith3OverriddenPayments->payments); + $this->assertContainsOnlyInstancesOf(TestPayment::class, $paymentInstrumentWith3OverriddenPayments->payments); + } +} diff --git a/tests/Unit/PaymentModelTest.php b/tests/Unit/PaymentModelTest.php new file mode 100644 index 0000000..a9a8756 --- /dev/null +++ b/tests/Unit/PaymentModelTest.php @@ -0,0 +1,100 @@ +create(); + $this->assertInstanceOf(Provider::class, $paymentWithProvider->provider); + + ServiceConfig::set('checkout', 'models.' . Provider::class, TestProvider::class); + $paymentWithOverriddenProvider = Payment::factory()->create(); + $this->assertInstanceOf(TestProvider::class, $paymentWithOverriddenProvider->provider); + } + + #[Test] + public function retrieve_payment_account() + { + $paymentWithAccount = Payment::factory()->create(); + $this->assertInstanceOf(Account::class, $paymentWithAccount->account); + + ServiceConfig::set('checkout', 'models.' . Account::class, TestAccount::class); + $paymentWithOverriddenAccount = Payment::factory()->create(); + $this->assertInstanceOf(TestAccount::class, $paymentWithOverriddenAccount->account); + } + + #[Test] + public function retrieve_payment_rail() + { + $paymentWithRail = Payment::factory()->create(); + $this->assertInstanceOf(PaymentRail::class, $paymentWithRail->rail); + + ServiceConfig::set('checkout', 'models.' . PaymentRail::class, TestPaymentRail::class); + $paymentWithOverriddenRail = Payment::factory()->create(); + $this->assertInstanceOf(TestPaymentRail::class, $paymentWithOverriddenRail->rail); + } + + #[Test] + public function retrieve_payment_instrument() + { + $payment = Payment::factory()->create(); + $this->assertNull($payment->instrument); + + $paymentWithInstrument = Payment::factory()->forInstrument()->create(); + $this->assertInstanceOf(PaymentInstrument::class, $paymentWithInstrument->instrument); + + ServiceConfig::set('checkout', 'models.' . PaymentInstrument::class, TestPaymentInstrument::class); + $paymentWithOverriddenInstrument = Payment::factory()->forInstrument()->create(); + $this->assertInstanceOf(TestPaymentInstrument::class, $paymentWithOverriddenInstrument->instrument); + } + + #[Test] + public function retrieve_payment_events() + { + $payment = Payment::factory()->create(); + $this->assertEmpty($payment->events); + + $paymentWith2Events = Payment::factory()->hasEvents(2)->create(); + $this->assertCount(2, $paymentWith2Events->events); + $this->assertContainsOnlyInstancesOf(TransactionEvent::class, $paymentWith2Events->events); + + ServiceConfig::set('checkout', 'models.' . TransactionEvent::class, TestTransactionEvent::class); + $paymentWith3OverriddenEvents = Payment::factory()->hasEvents(3)->create(); + $this->assertCount(3, $paymentWith3OverriddenEvents->events); + $this->assertContainsOnlyInstancesOf(TestTransactionEvent::class, $paymentWith3OverriddenEvents->events); + } + + #[Test] + public function retrieve_payment_transaction_events() + { + $payment = Payment::factory()->create(); + $this->assertEmpty($payment->transactionEvents); + + $paymentWith2TransactionEvents = Payment::factory()->hasTransactionEvents(2)->create(); + $this->assertCount(2, $paymentWith2TransactionEvents->transactionEvents); + $this->assertContainsOnlyInstancesOf(TransactionEvent::class, $paymentWith2TransactionEvents->transactionEvents); + + ServiceConfig::set('checkout', 'models.' . TransactionEvent::class, TestTransactionEvent::class); + $paymentWith3OverriddenTransactionEvents = Payment::factory()->hasTransactionEvents(3)->create(); + $this->assertCount(3, $paymentWith3OverriddenTransactionEvents->transactionEvents); + $this->assertContainsOnlyInstancesOf(TestTransactionEvent::class, $paymentWith3OverriddenTransactionEvents->transactionEvents); + } +} diff --git a/tests/Unit/PaymentRailModelTest.php b/tests/Unit/PaymentRailModelTest.php new file mode 100644 index 0000000..fda3f2c --- /dev/null +++ b/tests/Unit/PaymentRailModelTest.php @@ -0,0 +1,80 @@ +create(); + $paymentType = PaymentType::factory()->create(); + + $paymentRail = PaymentRail::create([ + 'parent_type_id' => $parentPaymentType->id, + 'type_id' => $paymentType->id, + ]); + + $this->assertEquals("{$parentPaymentType->id}:{$paymentType->id}", $paymentRail->id); + } + + #[Test] + public function payment_rail_generates_the_same_id_before_committing_when_parent_type_is_equal_to_type() + { + $paymentType = PaymentType::factory()->create(); + + $paymentRail = PaymentRail::create([ + 'parent_type_id' => $paymentType->id, + 'type_id' => $paymentType->id, + ]); + + $this->assertEquals($paymentType->id, $paymentRail->id); + } + + #[Test] + public function retrieve_payment_rail_parent_type() + { + $paymentRail = PaymentRail::factory()->create(); + $this->assertInstanceOf(PaymentType::class, $paymentRail->parentType); + + ServiceConfig::set('checkout', 'models.' . PaymentType::class, TestPaymentType::class); + $paymentRailWithOverriddenParentType = PaymentRail::factory()->create(); + $this->assertInstanceOf(TestPaymentType::class, $paymentRailWithOverriddenParentType->parentType); + } + + #[Test] + public function retrieve_payment_rail_type() + { + $paymentRail = PaymentRail::factory()->create(); + $this->assertInstanceOf(PaymentType::class, $paymentRail->type); + + ServiceConfig::set('checkout', 'models.' . PaymentType::class, TestPaymentType::class); + $paymentRailWithOverriddenType = PaymentRail::factory()->create(); + $this->assertInstanceOf(TestPaymentType::class, $paymentRailWithOverriddenType->type); + } + + #[Test] + public function retrieve_payment_rail_payments() + { + $paymentRail = PaymentRail::factory()->create(); + $this->assertEmpty($paymentRail->payments); + + $paymentRailWith2Payments = PaymentRail::factory()->hasPayments(2)->create(); + $this->assertCount(2, $paymentRailWith2Payments->payments); + $this->assertContainsOnlyInstancesOf(Payment::class, $paymentRailWith2Payments->payments); + + ServiceConfig::set('checkout', 'models.' . Payment::class, TestPayment::class); + $paymentRailWith3OverriddenPayments = PaymentRail::factory()->hasPayments(3)->create(); + $this->assertCount(3, $paymentRailWith3OverriddenPayments->payments); + $this->assertContainsOnlyInstancesOf(TestPayment::class, $paymentRailWith3OverriddenPayments->payments); + } +} diff --git a/tests/Unit/PaymentTypeModelTest.php b/tests/Unit/PaymentTypeModelTest.php new file mode 100644 index 0000000..9efe382 --- /dev/null +++ b/tests/Unit/PaymentTypeModelTest.php @@ -0,0 +1,47 @@ +create(); + $this->assertEmpty($paymentType->rails); + + $paymentTypeWith2Rails = PaymentType::factory()->hasRails(2)->create(); + $this->assertCount(2, $paymentTypeWith2Rails->rails); + $this->assertContainsOnlyInstancesOf(PaymentRail::class, $paymentTypeWith2Rails->rails); + + ServiceConfig::set('checkout', 'models.' . PaymentRail::class, TestPaymentRail::class); + $paymentTypeWith3OverriddenRails = PaymentType::factory()->hasRails(3)->create(); + $this->assertCount(3, $paymentTypeWith3OverriddenRails->rails); + $this->assertContainsOnlyInstancesOf(TestPaymentRail::class, $paymentTypeWith3OverriddenRails->rails); + } + + #[Test] + public function retrieve_payment_type_instruments() + { + $paymentType = PaymentType::factory()->create(); + $this->assertEmpty($paymentType->instruments); + + $paymentTypeWith2Instruments = PaymentType::factory()->hasInstruments(2)->create(); + $this->assertCount(2, $paymentTypeWith2Instruments->instruments); + $this->assertContainsOnlyInstancesOf(PaymentInstrument::class, $paymentTypeWith2Instruments->instruments); + + ServiceConfig::set('checkout', 'models.' . PaymentInstrument::class, TestPaymentInstrument::class); + $paymentTypeWith3OverriddenInstruments = PaymentType::factory()->hasInstruments(3)->create(); + $this->assertCount(3, $paymentTypeWith3OverriddenInstruments->instruments); + $this->assertContainsOnlyInstancesOf(TestPaymentInstrument::class, $paymentTypeWith3OverriddenInstruments->instruments); + } +} diff --git a/tests/Unit/RefundModelTest.php b/tests/Unit/RefundModelTest.php new file mode 100644 index 0000000..6cf03de --- /dev/null +++ b/tests/Unit/RefundModelTest.php @@ -0,0 +1,68 @@ +create(); + $this->assertInstanceOf(Provider::class, $refundWithProvider->provider); + + ServiceConfig::set('checkout', 'models.' . Provider::class, TestProvider::class); + $refundWithOverriddenProvider = Refund::factory()->create(); + $this->assertInstanceOf(TestProvider::class, $refundWithOverriddenProvider->provider); + } + + #[Test] + public function retrieve_refund_account() + { + $refundWithAccount = Refund::factory()->create(); + $this->assertInstanceOf(Account::class, $refundWithAccount->account); + + ServiceConfig::set('checkout', 'models.' . Account::class, TestAccount::class); + $refundWithOverriddenAccount = Refund::factory()->create(); + $this->assertInstanceOf(TestAccount::class, $refundWithOverriddenAccount->account); + } + + #[Test] + public function retrieve_refund_payment() + { + $refundWithPayment = Refund::factory()->create(); + $this->assertInstanceOf(Payment::class, $refundWithPayment->payment); + + ServiceConfig::set('checkout', 'models.' . Payment::class, TestPayment::class); + $refundWithOverriddenPayment = Refund::factory()->create(); + $this->assertInstanceOf(TestPayment::class, $refundWithOverriddenPayment->payment); + } + + #[Test] + public function retrieve_refund_transaction_events() + { + $refund = Refund::factory()->create(); + $this->assertEmpty($refund->transactionEvents); + + $refundWith2TransactionEvents = Refund::factory()->hasTransactionEvents(2)->create(); + $this->assertCount(2, $refundWith2TransactionEvents->transactionEvents); + $this->assertContainsOnlyInstancesOf(TransactionEvent::class, $refundWith2TransactionEvents->transactionEvents); + + ServiceConfig::set('checkout', 'models.' . TransactionEvent::class, TestTransactionEvent::class); + $refundWith3OverriddenTransactionEvents = Refund::factory()->hasTransactionEvents(3)->create(); + $this->assertCount(3, $refundWith3OverriddenTransactionEvents->transactionEvents); + $this->assertContainsOnlyInstancesOf(TestTransactionEvent::class, $refundWith3OverriddenTransactionEvents->transactionEvents); + } +} diff --git a/tests/Unit/TestCheckoutGateway.php b/tests/Unit/TestCheckoutGateway.php index 332fb9f..42d8018 100644 --- a/tests/Unit/TestCheckoutGateway.php +++ b/tests/Unit/TestCheckoutGateway.php @@ -4,8 +4,8 @@ use Payavel\Checkout\Contracts\Billable; use Payavel\Checkout\Facades\Checkout; -use Payavel\Checkout\Models\PaymentMethod; -use Payavel\Checkout\Models\PaymentTransaction; +use Payavel\Checkout\Models\PaymentInstrument; +use Payavel\Checkout\Models\Payment; use Payavel\Checkout\Models\Wallet; use Payavel\Checkout\CheckoutRequest; use Payavel\Checkout\CheckoutResponse; @@ -53,72 +53,72 @@ public function get_wallet_method_returns_configured_response() } #[Test] - public function get_payment_method_method_returns_configured_response() + public function get_payment_instrument_method_returns_configured_response() { $wallet = Wallet::factory()->create([ 'provider_id' => Checkout::getProvider()->getId(), 'account_id' => Checkout::getAccount()->getId(), ]); - $paymentMethod = PaymentMethod::factory()->create([ + $paymentInstrument = PaymentInstrument::factory()->create([ 'wallet_id' => $wallet->id, ]); - $response = Checkout::getPaymentMethod($paymentMethod); + $response = Checkout::getPaymentInstrument($paymentInstrument); $this->assertResponseIsConfigured($response); - $this->assertEquals('getPaymentMethod', $response->data['requestMethod']); + $this->assertEquals('getPaymentInstrument', $response->data['requestMethod']); } #[Test] - public function tokenize_payment_method_method_returns_configured_response() + public function tokenize_payment_instrument_method_returns_configured_response() { $user = User::factory()->create(); - $response = Checkout::tokenizePaymentMethod($user, []); + $response = Checkout::tokenizePaymentInstrument($user, []); $this->assertResponseIsConfigured($response); - $this->assertEquals('tokenizePaymentMethod', $response->data['requestMethod']); + $this->assertEquals('tokenizePaymentInstrument', $response->data['requestMethod']); } #[Test] - public function update_payment_method_method_returns_configured_response() + public function update_payment_instrument_method_returns_configured_response() { $wallet = Wallet::factory()->create([ 'provider_id' => Checkout::getProvider()->getId(), 'account_id' => Checkout::getAccount()->getId(), ]); - $paymentMethod = PaymentMethod::factory()->create([ + $paymentInstrument = PaymentInstrument::factory()->create([ 'wallet_id' => $wallet->id, ]); - $response = Checkout::updatePaymentMethod($paymentMethod, []); + $response = Checkout::updatePaymentInstrument($paymentInstrument, []); $this->assertResponseIsConfigured($response); - $this->assertEquals('updatePaymentMethod', $response->data['requestMethod']); + $this->assertEquals('updatePaymentInstrument', $response->data['requestMethod']); } #[Test] - public function delete_payment_method_method_returns_configured_response() + public function delete_payment_instrument_method_returns_configured_response() { $wallet = Wallet::factory()->create([ 'provider_id' => Checkout::getProvider()->getId(), 'account_id' => Checkout::getAccount()->getId(), ]); - $paymentMethod = PaymentMethod::factory()->create([ + $paymentInstrument = PaymentInstrument::factory()->create([ 'wallet_id' => $wallet->id, ]); - $response = Checkout::deletePaymentMethod($paymentMethod); + $response = Checkout::deletePaymentInstrument($paymentInstrument); $this->assertResponseIsConfigured($response); - $this->assertEquals('deletePaymentMethod', $response->data['requestMethod']); + $this->assertEquals('deletePaymentInstrument', $response->data['requestMethod']); } #[Test] @@ -134,12 +134,12 @@ public function authorize_method_returns_configured_response() #[Test] public function capture_method_returns_configured_response() { - $transaction = PaymentTransaction::factory()->create([ + $payment = Payment::factory()->create([ 'provider_id' => Checkout::getProvider()->getId(), 'account_id' => Checkout::getAccount()->getId(), ]); - $response = Checkout::capture($transaction); + $response = Checkout::capture($payment); $this->assertResponseIsConfigured($response); @@ -149,12 +149,12 @@ public function capture_method_returns_configured_response() #[Test] public function get_transaction_method_returns_configured_response() { - $transaction = PaymentTransaction::factory()->create([ + $payment = Payment::factory()->create([ 'provider_id' => Checkout::getProvider()->getId(), 'account_id' => Checkout::getAccount()->getId(), ]); - $response = Checkout::getTransaction($transaction); + $response = Checkout::getTransaction($payment); $this->assertResponseIsConfigured($response); @@ -164,12 +164,12 @@ public function get_transaction_method_returns_configured_response() #[Test] public function void_method_returns_configured_response() { - $transaction = PaymentTransaction::factory()->create([ + $payment = Payment::factory()->create([ 'provider_id' => Checkout::getProvider()->getId(), 'account_id' => Checkout::getAccount()->getId(), ]); - $response = Checkout::void($transaction); + $response = Checkout::void($payment); $this->assertResponseIsConfigured($response); @@ -179,12 +179,12 @@ public function void_method_returns_configured_response() #[Test] public function refund_method_returns_configured_response() { - $transaction = PaymentTransaction::factory()->create([ + $payment = Payment::factory()->create([ 'provider_id' => Checkout::getProvider()->getId(), 'account_id' => Checkout::getAccount()->getId(), ]); - $response = Checkout::refund($transaction); + $response = Checkout::refund($payment); $this->assertResponseIsConfigured($response); @@ -212,22 +212,22 @@ public function getWallet(Wallet $wallet) return new TestCheckoutResponse([]); } - public function getPaymentMethod(PaymentMethod $paymentMethod) + public function getPaymentInstrument(PaymentInstrument $paymentInstrument) { return new TestCheckoutResponse([]); } - public function tokenizePaymentMethod(Billable $billable, $data) + public function tokenizePaymentInstrument(Billable $billable, $data) { return new TestCheckoutResponse([]); } - public function updatePaymentMethod(PaymentMethod $paymentMethod, $data) + public function updatePaymentInstrument(PaymentInstrument $paymentInstrument, $data) { return new TestCheckoutResponse([]); } - public function deletePaymentMethod(PaymentMethod $paymentMethod) + public function deletePaymentInstrument(PaymentInstrument $paymentInstrument) { return new TestCheckoutResponse([]); } @@ -237,22 +237,22 @@ public function authorize($data, Billable $billable = null) return new TestCheckoutResponse([]); } - public function capture(PaymentTransaction $transaction, $data = []) + public function capture(Payment $payment, $data = []) { return new TestCheckoutResponse([]); } - public function getTransaction(PaymentTransaction $transaction) + public function getTransaction(Payment $transaction) { return new TestCheckoutResponse([]); } - public function void(PaymentTransaction $paymentTransaction, $data = []) + public function void(Payment $payment, $data = []) { return new TestCheckoutResponse([]); } - public function refund(PaymentTransaction $paymentTransaction, $data = []) + public function refund(Payment $payment, $data = []) { return new TestCheckoutResponse([]); } @@ -267,28 +267,28 @@ public function getWalletResponse() ]; } - public function getPaymentMethodResponse() + public function getPaymentInstrumentResponse() { return [ 'requestMethod' => $this->requestMethod, ]; } - public function tokenizePaymentMethodResponse() + public function tokenizePaymentInstrumentResponse() { return [ 'requestMethod' => $this->requestMethod, ]; } - public function updatePaymentMethodResponse() + public function updatePaymentInstrumentResponse() { return [ 'requestMethod' => $this->requestMethod, ]; } - public function deletePaymentMethodResponse() + public function deletePaymentInstrumentResponse() { return [ 'requestMethod' => $this->requestMethod, diff --git a/tests/Unit/TransactionEventModelTest.php b/tests/Unit/TransactionEventModelTest.php new file mode 100644 index 0000000..d850512 --- /dev/null +++ b/tests/Unit/TransactionEventModelTest.php @@ -0,0 +1,50 @@ +create(); + $this->assertInstanceOf(Payment::class, $transactionEventWithPayment->payment); + + ServiceConfig::set('checkout', 'models.' . Payment::class, TestPayment::class); + $transactionEventWithOverriddenPayment = TransactionEvent::factory()->create(); + $this->assertInstanceOf(TestPayment::class, $transactionEventWithOverriddenPayment->payment); + } + + #[Test] + public function retrieve_transaction_event_transactionable() + { + $transactionEvent = TransactionEvent::factory()->create(); + $this->assertNull($transactionEvent->transactionable); + + $transactionables = [ + Payment::class => TestPayment::class, + Refund::class => TestRefund::class, + Dispute::class => TestDispute::class, + ]; + + $randomTransactionable = $this->faker->randomElement(array_keys($transactionables)); + $transactionEventWithTransactionable = TransactionEvent::factory()->for($randomTransactionable::factory(), 'transactionable')->create(); + $this->assertInstanceOf($randomTransactionable, $transactionEventWithTransactionable->transactionable); + + $randomTransactionable = $this->faker->randomElement(array_keys($transactionables)); + ServiceConfig::set('checkout', 'models.' . $randomTransactionable, $transactionables[$randomTransactionable]); + $transactionEventWithOverriddenTransactionable = TransactionEvent::factory()->for($transactionables[$randomTransactionable]::factory(), 'transactionable')->create(); + $this->assertInstanceOf($transactionables[$randomTransactionable], $transactionEventWithOverriddenTransactionable->transactionable); + } +} diff --git a/tests/Unit/WalletModelTest.php b/tests/Unit/WalletModelTest.php new file mode 100644 index 0000000..319275a --- /dev/null +++ b/tests/Unit/WalletModelTest.php @@ -0,0 +1,69 @@ +create(); + $this->assertNull($wallet->billable); + + $billable = User::factory()->create(); + $walletWithBillable = Wallet::factory()->create(); + $walletWithBillable->billable()->associate($billable); + $this->assertInstanceOf(Billable::class, $walletWithBillable->billable); + } + + #[Test] + public function retrieve_wallet_provider() + { + $walletWithProvider = Wallet::factory()->create(); + $this->assertInstanceOf(Provider::class, $walletWithProvider->provider); + + ServiceConfig::set('checkout', 'models.' . Provider::class, TestProvider::class); + $walletWithOverriddenProvider = Wallet::factory()->create(); + $this->assertInstanceOF(TestProvider::class, $walletWithOverriddenProvider->provider); + } + + #[Test] + public function retrieve_wallet_account() + { + $walletWithAccount = Wallet::factory()->create(); + $this->assertInstanceOf(Account::class, $walletWithAccount->account); + + ServiceConfig::set('checkout', 'models.' . Account::class, TestAccount::class); + $walletWithOverriddenAccount = Wallet::factory()->create(); + $this->assertInstanceOF(TestAccount::class, $walletWithOverriddenAccount->account); + } + + #[Test] + public function retrieve_wallet_payment_instruments() + { + $wallet = Wallet::factory()->create(); + $this->assertEmpty($wallet->paymentInstruments); + + $walletWith2PaymentInstruments = Wallet::factory()->hasPaymentInstruments(2)->create(); + $this->assertCount(2, $walletWith2PaymentInstruments->paymentInstruments); + $this->assertContainsOnlyInstancesOf(PaymentInstrument::class, $walletWith2PaymentInstruments->paymentInstruments); + + ServiceConfig::set('checkout', 'models.' . PaymentInstrument::class, TestPaymentInstrument::class); + $walletWith3OverriddenPaymentInstruments = Wallet::factory()->hasPaymentInstruments(3)->create(); + $this->assertCount(3, $walletWith3OverriddenPaymentInstruments->paymentInstruments); + $this->assertContainsOnlyInstancesOf(TestPaymentInstrument::class, $walletWith3OverriddenPaymentInstruments->paymentInstruments); + } +}