Changeset View
Changeset View
Standalone View
Standalone View
src/tests/Feature/Controller/PaymentsMollieTest.php
<?php | <?php | ||||
namespace Tests\Feature\Controller; | namespace Tests\Feature\Controller; | ||||
use App\Http\Controllers\API\V4\PaymentsController; | use App\Http\Controllers\API\V4\PaymentsController; | ||||
use App\Payment; | use App\Payment; | ||||
use App\Providers\PaymentProvider; | use App\Providers\PaymentProvider; | ||||
use App\Transaction; | use App\Transaction; | ||||
use App\Wallet; | use App\Wallet; | ||||
use App\WalletSetting; | use App\WalletSetting; | ||||
use App\VatRate; | |||||
use App\Utils; | use App\Utils; | ||||
use GuzzleHttp\Psr7\Response; | use GuzzleHttp\Psr7\Response; | ||||
use Illuminate\Support\Facades\Bus; | use Illuminate\Support\Facades\Bus; | ||||
use Tests\TestCase; | use Tests\TestCase; | ||||
use Tests\BrowserAddonTrait; | use Tests\BrowserAddonTrait; | ||||
use Tests\MollieMocksTrait; | use Tests\MollieMocksTrait; | ||||
class PaymentsMollieTest extends TestCase | class PaymentsMollieTest extends TestCase | ||||
{ | { | ||||
use MollieMocksTrait; | use MollieMocksTrait; | ||||
use BrowserAddonTrait; | use BrowserAddonTrait; | ||||
/** | /** | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
public function setUp(): void | public function setUp(): void | ||||
{ | { | ||||
parent::setUp(); | parent::setUp(); | ||||
// All tests in this file use Mollie | // All tests in this file use Mollie | ||||
\config(['services.payment_provider' => 'mollie']); | \config(['services.payment_provider' => 'mollie']); | ||||
\config(['app.vat.mode' => 0]); | |||||
Utils::setTestExchangeRates(['EUR' => '0.90503424978382']); | Utils::setTestExchangeRates(['EUR' => '0.90503424978382']); | ||||
$this->deleteTestUser('payment-test@' . \config('app.domain')); | |||||
$john = $this->getTestUser('john@kolab.org'); | $john = $this->getTestUser('john@kolab.org'); | ||||
$wallet = $john->wallets()->first(); | $wallet = $john->wallets()->first(); | ||||
Payment::where('wallet_id', $wallet->id)->delete(); | Payment::query()->delete(); | ||||
VatRate::query()->delete(); | |||||
Wallet::where('id', $wallet->id)->update(['balance' => 0]); | Wallet::where('id', $wallet->id)->update(['balance' => 0]); | ||||
WalletSetting::where('wallet_id', $wallet->id)->delete(); | WalletSetting::where('wallet_id', $wallet->id)->delete(); | ||||
$types = [ | $types = [ | ||||
Transaction::WALLET_CREDIT, | Transaction::WALLET_CREDIT, | ||||
Transaction::WALLET_REFUND, | Transaction::WALLET_REFUND, | ||||
Transaction::WALLET_CHARGEBACK, | Transaction::WALLET_CHARGEBACK, | ||||
]; | ]; | ||||
Transaction::where('object_id', $wallet->id)->whereIn('type', $types)->delete(); | Transaction::where('object_id', $wallet->id)->whereIn('type', $types)->delete(); | ||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
public function tearDown(): void | public function tearDown(): void | ||||
{ | { | ||||
$this->deleteTestUser('payment-test@' . \config('app.domain')); | |||||
$john = $this->getTestUser('john@kolab.org'); | $john = $this->getTestUser('john@kolab.org'); | ||||
$wallet = $john->wallets()->first(); | $wallet = $john->wallets()->first(); | ||||
Payment::where('wallet_id', $wallet->id)->delete(); | Payment::query()->delete(); | ||||
VatRate::query()->delete(); | |||||
Wallet::where('id', $wallet->id)->update(['balance' => 0]); | Wallet::where('id', $wallet->id)->update(['balance' => 0]); | ||||
WalletSetting::where('wallet_id', $wallet->id)->delete(); | WalletSetting::where('wallet_id', $wallet->id)->delete(); | ||||
$types = [ | $types = [ | ||||
Transaction::WALLET_CREDIT, | Transaction::WALLET_CREDIT, | ||||
Transaction::WALLET_REFUND, | Transaction::WALLET_REFUND, | ||||
Transaction::WALLET_CHARGEBACK, | Transaction::WALLET_CHARGEBACK, | ||||
]; | ]; | ||||
Transaction::where('object_id', $wallet->id)->whereIn('type', $types)->delete(); | Transaction::where('object_id', $wallet->id)->whereIn('type', $types)->delete(); | ||||
▲ Show 20 Lines • Show All 469 Lines • ▼ Show 20 Lines | public function testTopUp(): void | ||||
// Create a valid mandate first (balance=0, so there's no extra payment yet) | // Create a valid mandate first (balance=0, so there's no extra payment yet) | ||||
$this->createMandate($wallet, ['amount' => 20.10, 'balance' => 0]); | $this->createMandate($wallet, ['amount' => 20.10, 'balance' => 0]); | ||||
$wallet->setSetting('mandate_balance', 10); | $wallet->setSetting('mandate_balance', 10); | ||||
// Expect a recurring payment as we have a valid mandate at this point | // Expect a recurring payment as we have a valid mandate at this point | ||||
// and the balance is below the threshold | // and the balance is below the threshold | ||||
$result = PaymentsController::topUpWallet($wallet); | $this->assertTrue(PaymentsController::topUpWallet($wallet)); | ||||
$this->assertTrue($result); | |||||
// Check that the payments table contains a new record with proper amount. | // Check that the payments table contains a new record with proper amount. | ||||
// There should be two records, one for the mandate payment and another for | // There should be two records, one for the mandate payment and another for | ||||
// the top-up payment | // the top-up payment | ||||
$payments = $wallet->payments()->orderBy('amount')->get(); | $payments = $wallet->payments()->orderBy('amount')->get(); | ||||
$this->assertCount(2, $payments); | $this->assertCount(2, $payments); | ||||
$this->assertSame(0, $payments[0]->amount); | $this->assertSame(0, $payments[0]->amount); | ||||
$this->assertSame(0, $payments[0]->currency_amount); | $this->assertSame(0, $payments[0]->currency_amount); | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | public function testTopUp(): void | ||||
$job_payment = $this->getObjectProperty($job, 'payment'); | $job_payment = $this->getObjectProperty($job, 'payment'); | ||||
return $job_payment->id === $payment->id; | return $job_payment->id === $payment->id; | ||||
}); | }); | ||||
$this->unmockMollie(); | $this->unmockMollie(); | ||||
} | } | ||||
/** | /** | ||||
* Test payment/top-up with VAT_MODE=1 | |||||
* | |||||
* @group mollie | |||||
*/ | |||||
public function testPaymentsWithVatModeOne(): void | |||||
{ | |||||
\config(['app.vat.mode' => 1]); | |||||
$user = $this->getTestUser('payment-test@' . \config('app.domain')); | |||||
$user->setSetting('country', 'US'); | |||||
$wallet = $user->wallets()->first(); | |||||
$vatRate = VatRate::create([ | |||||
'country' => 'US', | |||||
'rate' => 5.0, | |||||
'start' => now()->subDay(), | |||||
]); | |||||
// Payment | |||||
$post = ['amount' => '10', 'currency' => 'CHF', 'methodId' => 'creditcard']; | |||||
$response = $this->actingAs($user)->post("api/v4/payments", $post); | |||||
$response->assertStatus(200); | |||||
// Check that the payments table contains a new record with proper amount(s) | |||||
$payment = $wallet->payments()->first(); | |||||
$this->assertSame(1000 + intval(round(1000 * $vatRate->rate / 100)), $payment->amount); | |||||
$this->assertSame(1000, $payment->credit_amount); | |||||
$this->assertSame($payment->amount, $payment->currency_amount); | |||||
$this->assertSame('CHF', $payment->currency); | |||||
$this->assertSame($vatRate->id, $payment->vat_rate_id); | |||||
$this->assertSame('open', $payment->status); | |||||
$wallet->payments()->delete(); | |||||
$wallet->balance = -1000; | |||||
$wallet->save(); | |||||
// Top-up (mandate creation) | |||||
// Create a valid mandate first (expect an extra payment) | |||||
$this->createMandate($wallet, ['amount' => 20.10, 'balance' => 0]); | |||||
// Check that the payments table contains a new record with proper amount(s) | |||||
$payment = $wallet->payments()->first(); | |||||
$this->assertSame(2010 + intval(round(2010 * $vatRate->rate / 100)), $payment->amount); | |||||
$this->assertSame(2010, $payment->credit_amount); | |||||
$this->assertSame($payment->amount, $payment->currency_amount); | |||||
$this->assertSame($vatRate->id, $payment->vat_rate_id); | |||||
$wallet->payments()->delete(); | |||||
$wallet->balance = -1000; | |||||
$wallet->save(); | |||||
// Top-up (recurring payment) | |||||
// Expect a recurring payment as we have a valid mandate at this point | |||||
// and the balance is below the threshold | |||||
$this->assertTrue(PaymentsController::topUpWallet($wallet)); | |||||
// Check that the payments table contains a new record with proper amount(s) | |||||
$payment = $wallet->payments()->first(); | |||||
$this->assertSame(2010 + intval(round(2010 * $vatRate->rate / 100)), $payment->amount); | |||||
$this->assertSame(2010, $payment->credit_amount); | |||||
$this->assertSame($payment->amount, $payment->currency_amount); | |||||
$this->assertSame($vatRate->id, $payment->vat_rate_id); | |||||
} | |||||
/** | |||||
* Test refund/chargeback handling by the webhook | * Test refund/chargeback handling by the webhook | ||||
* | * | ||||
* @group mollie | * @group mollie | ||||
*/ | */ | ||||
public function testRefundAndChargeback(): void | public function testRefundAndChargeback(): void | ||||
{ | { | ||||
Bus::fake(); | Bus::fake(); | ||||
$user = $this->getTestUser('john@kolab.org'); | $user = $this->getTestUser('john@kolab.org'); | ||||
$wallet = $user->wallets()->first(); | $wallet = $user->wallets()->first(); | ||||
$wallet->transactions()->delete(); | $wallet->transactions()->delete(); | ||||
$mollie = PaymentProvider::factory('mollie'); | $mollie = PaymentProvider::factory('mollie'); | ||||
// Create a paid payment | // Create a paid payment | ||||
$payment = Payment::create([ | $payment = Payment::create([ | ||||
'id' => 'tr_123456', | 'id' => 'tr_123456', | ||||
'status' => PaymentProvider::STATUS_PAID, | 'status' => PaymentProvider::STATUS_PAID, | ||||
'amount' => 123, | 'amount' => 123, | ||||
'credit_amount' => 123, | |||||
'currency_amount' => 123, | 'currency_amount' => 123, | ||||
'currency' => 'CHF', | 'currency' => 'CHF', | ||||
'type' => PaymentProvider::TYPE_ONEOFF, | 'type' => PaymentProvider::TYPE_ONEOFF, | ||||
'wallet_id' => $wallet->id, | 'wallet_id' => $wallet->id, | ||||
'provider' => 'mollie', | 'provider' => 'mollie', | ||||
'description' => 'test', | 'description' => 'test', | ||||
]); | ]); | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | public function testRefundAndChargebackForeignCurrency(): void | ||||
$mollie = PaymentProvider::factory('mollie'); | $mollie = PaymentProvider::factory('mollie'); | ||||
// Create a paid payment | // Create a paid payment | ||||
$payment = Payment::create([ | $payment = Payment::create([ | ||||
'id' => 'tr_123456', | 'id' => 'tr_123456', | ||||
'status' => PaymentProvider::STATUS_PAID, | 'status' => PaymentProvider::STATUS_PAID, | ||||
'amount' => 1234, | 'amount' => 1234, | ||||
'credit_amount' => 1234, | |||||
'currency_amount' => 1117, | 'currency_amount' => 1117, | ||||
'currency' => 'EUR', | 'currency' => 'EUR', | ||||
'type' => PaymentProvider::TYPE_ONEOFF, | 'type' => PaymentProvider::TYPE_ONEOFF, | ||||
'wallet_id' => $wallet->id, | 'wallet_id' => $wallet->id, | ||||
'provider' => 'mollie', | 'provider' => 'mollie', | ||||
'description' => 'test', | 'description' => 'test', | ||||
]); | ]); | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | protected function createMandate(Wallet $wallet, array $params) | ||||
$molliePage = new \Tests\Browser\Pages\PaymentMollie(); | $molliePage = new \Tests\Browser\Pages\PaymentMollie(); | ||||
$molliePage->assert($this->browser); | $molliePage->assert($this->browser); | ||||
$molliePage->submitPayment($this->browser, 'paid'); | $molliePage->submitPayment($this->browser, 'paid'); | ||||
$this->stopBrowser(); | $this->stopBrowser(); | ||||
} | } | ||||
/** | /** | ||||
* Test listing a pending payment | * Test listing a pending payment | ||||
* | * | ||||
* @group mollie | * @group mollie | ||||
*/ | */ | ||||
public function testListingPayments(): void | public function testListingPayments(): void | ||||
{ | { | ||||
Bus::fake(); | Bus::fake(); | ||||
▲ Show 20 Lines • Show All 102 Lines • Show Last 20 Lines |