Changeset View
Changeset View
Standalone View
Standalone View
src/tests/Feature/Controller/PaymentsMollieTest.php
Show First 20 Lines • Show All 368 Lines • ▼ Show 20 Lines | public function testStoreAndWebhook(): void | ||||
$this->assertRegExp('|^https://www.mollie.com|', $json['redirectUrl']); | $this->assertRegExp('|^https://www.mollie.com|', $json['redirectUrl']); | ||||
$wallet = $user->wallets()->first(); | $wallet = $user->wallets()->first(); | ||||
$payments = Payment::where('wallet_id', $wallet->id)->get(); | $payments = Payment::where('wallet_id', $wallet->id)->get(); | ||||
$this->assertCount(1, $payments); | $this->assertCount(1, $payments); | ||||
$payment = $payments[0]; | $payment = $payments[0]; | ||||
$this->assertSame(1234, $payment->amount); | $this->assertSame(1234, $payment->amount); | ||||
$this->assertSame(1234, $payment->currency_amount); | |||||
$this->assertSame('CHF', $payment->currency); | |||||
$this->assertSame(\config('app.name') . ' Payment', $payment->description); | $this->assertSame(\config('app.name') . ' Payment', $payment->description); | ||||
$this->assertSame('open', $payment->status); | $this->assertSame('open', $payment->status); | ||||
$this->assertEquals(0, $wallet->balance); | $this->assertEquals(0, $wallet->balance); | ||||
// Test the webhook | // Test the webhook | ||||
// Note: Webhook end-point does not require authentication | // Note: Webhook end-point does not require authentication | ||||
$mollie_response = [ | $mollie_response = [ | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | public function testStoreAndWebhook(): void | ||||
$this->assertEquals(1234, $wallet->fresh()->balance); | $this->assertEquals(1234, $wallet->fresh()->balance); | ||||
// Assert that email notification job wasn't dispatched, | // Assert that email notification job wasn't dispatched, | ||||
// it is expected only for recurring payments | // it is expected only for recurring payments | ||||
Bus::assertDispatchedTimes(\App\Jobs\PaymentEmail::class, 0); | Bus::assertDispatchedTimes(\App\Jobs\PaymentEmail::class, 0); | ||||
} | } | ||||
/** | /** | ||||
* Test creating a payment and receiving a status via webhook using a foreign currency | |||||
* | |||||
* @group mollie | |||||
*/ | |||||
public function testStoreAndWebhookForeignCurrency(): void | |||||
{ | |||||
Bus::fake(); | |||||
$user = $this->getTestUser('john@kolab.org'); | |||||
$wallet = $user->wallets()->first(); | |||||
// Successful payment in EUR | |||||
$post = ['amount' => '12.34', 'currency' => 'EUR', 'methodId' => 'banktransfer']; | |||||
$response = $this->actingAs($user)->post("api/v4/payments", $post); | |||||
$response->assertStatus(200); | |||||
$payment = $wallet->payments() | |||||
->where('currency', 'EUR')->get()->last(); | |||||
$this->assertSame(1234, $payment->amount); | |||||
$this->assertSame(1117, $payment->currency_amount); | |||||
$this->assertSame('EUR', $payment->currency); | |||||
$this->assertEquals(0, $wallet->balance); | |||||
$mollie_response = [ | |||||
"resource" => "payment", | |||||
"id" => $payment->id, | |||||
"status" => "paid", | |||||
// Status is not enough, paidAt is used to distinguish the state | |||||
"paidAt" => date('c'), | |||||
"mode" => "test", | |||||
]; | |||||
$responseStack = $this->mockMollie(); | |||||
$responseStack->append(new Response(200, [], json_encode($mollie_response))); | |||||
$post = ['id' => $payment->id]; | |||||
$response = $this->post("api/webhooks/payment/mollie", $post); | |||||
$response->assertStatus(200); | |||||
$this->assertSame(PaymentProvider::STATUS_PAID, $payment->fresh()->status); | |||||
$this->assertEquals(1234, $wallet->fresh()->balance); | |||||
} | |||||
/** | |||||
* Test automatic payment charges | * Test automatic payment charges | ||||
* | * | ||||
* @group mollie | * @group mollie | ||||
*/ | */ | ||||
public function testTopUp(): void | public function testTopUp(): void | ||||
{ | { | ||||
Bus::fake(); | Bus::fake(); | ||||
Show All 11 Lines | public function testTopUp(): void | ||||
$this->assertTrue($result); | $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(2010, $payments[1]->amount); | $this->assertSame(2010, $payments[1]->amount); | ||||
$this->assertSame(2010, $payments[1]->currency_amount); | |||||
$payment = $payments[1]; | $payment = $payments[1]; | ||||
// In mollie we don't have to wait for a webhook, the response to | // In mollie we don't have to wait for a webhook, the response to | ||||
// PaymentIntent already sets the status to 'paid', so we can test | // PaymentIntent already sets the status to 'paid', so we can test | ||||
// immediately the balance update | // immediately the balance update | ||||
// Assert that email notification job has been dispatched | // Assert that email notification job has been dispatched | ||||
$this->assertSame(PaymentProvider::STATUS_PAID, $payment->status); | $this->assertSame(PaymentProvider::STATUS_PAID, $payment->status); | ||||
$this->assertEquals(2010, $wallet->fresh()->balance); | $this->assertEquals(2010, $wallet->fresh()->balance); | ||||
▲ Show 20 Lines • Show All 221 Lines • ▼ Show 20 Lines | public function testRefundAndChargeback(): void | ||||
$this->assertSame(-101, $transactions[0]->amount); | $this->assertSame(-101, $transactions[0]->amount); | ||||
$this->assertSame(Transaction::WALLET_REFUND, $transactions[0]->type); | $this->assertSame(Transaction::WALLET_REFUND, $transactions[0]->type); | ||||
$this->assertSame("refund desc", $transactions[0]->description); | $this->assertSame("refund desc", $transactions[0]->description); | ||||
$payments = $wallet->payments()->where('id', 're_123456')->get(); | $payments = $wallet->payments()->where('id', 're_123456')->get(); | ||||
$this->assertCount(1, $payments); | $this->assertCount(1, $payments); | ||||
$this->assertSame(-101, $payments[0]->amount); | $this->assertSame(-101, $payments[0]->amount); | ||||
$this->assertSame(-101, $payments[0]->currency_amount); | |||||
$this->assertSame(PaymentProvider::STATUS_PAID, $payments[0]->status); | $this->assertSame(PaymentProvider::STATUS_PAID, $payments[0]->status); | ||||
$this->assertSame(PaymentProvider::TYPE_REFUND, $payments[0]->type); | $this->assertSame(PaymentProvider::TYPE_REFUND, $payments[0]->type); | ||||
$this->assertSame("mollie", $payments[0]->provider); | $this->assertSame("mollie", $payments[0]->provider); | ||||
$this->assertSame("refund desc", $payments[0]->description); | $this->assertSame("refund desc", $payments[0]->description); | ||||
// Test handling a chargeback by the webhook | // Test handling a chargeback by the webhook | ||||
$mollie_response1["_links"] = [ | $mollie_response1["_links"] = [ | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | public function testRefundAndChargeback(): void | ||||
$this->assertSame('', $payments[0]->description); | $this->assertSame('', $payments[0]->description); | ||||
Bus::assertNotDispatched(\App\Jobs\PaymentEmail::class); | Bus::assertNotDispatched(\App\Jobs\PaymentEmail::class); | ||||
$this->unmockMollie(); | $this->unmockMollie(); | ||||
} | } | ||||
/** | /** | ||||
* Test refund/chargeback handling by the webhook in a foreign currency | |||||
* | |||||
* @group mollie | |||||
*/ | |||||
public function testRefundAndChargebackForeignCurrency(): void | |||||
{ | |||||
Bus::fake(); | |||||
$user = $this->getTestUser('john@kolab.org'); | |||||
$wallet = $user->wallets()->first(); | |||||
$wallet->transactions()->delete(); | |||||
$mollie = PaymentProvider::factory('mollie'); | |||||
// Create a paid payment | |||||
$payment = Payment::create([ | |||||
'id' => 'tr_123456', | |||||
'status' => PaymentProvider::STATUS_PAID, | |||||
'amount' => 1234, | |||||
'currency_amount' => 1117, | |||||
'currency' => 'EUR', | |||||
'type' => PaymentProvider::TYPE_ONEOFF, | |||||
'wallet_id' => $wallet->id, | |||||
'provider' => 'mollie', | |||||
'description' => 'test', | |||||
]); | |||||
// Test handling a refund by the webhook | |||||
$mollie_response1 = [ | |||||
"resource" => "payment", | |||||
"id" => $payment->id, | |||||
"status" => "paid", | |||||
// Status is not enough, paidAt is used to distinguish the state | |||||
"paidAt" => date('c'), | |||||
"mode" => "test", | |||||
"_links" => [ | |||||
"refunds" => [ | |||||
"href" => "https://api.mollie.com/v2/payments/{$payment->id}/refunds", | |||||
"type" => "application/hal+json" | |||||
] | |||||
] | |||||
]; | |||||
$mollie_response2 = [ | |||||
"count" => 1, | |||||
"_links" => [], | |||||
"_embedded" => [ | |||||
"refunds" => [ | |||||
[ | |||||
"resource" => "refund", | |||||
"id" => "re_123456", | |||||
"status" => \Mollie\Api\Types\RefundStatus::STATUS_REFUNDED, | |||||
"paymentId" => $payment->id, | |||||
"description" => "refund desc", | |||||
"amount" => [ | |||||
"currency" => "EUR", | |||||
"value" => "1.01", | |||||
], | |||||
] | |||||
] | |||||
] | |||||
]; | |||||
// We'll trigger the webhook with payment id and use mocking for | |||||
// requests to the Mollie payments API. | |||||
$responseStack = $this->mockMollie(); | |||||
$responseStack->append(new Response(200, [], json_encode($mollie_response1))); | |||||
$responseStack->append(new Response(200, [], json_encode($mollie_response2))); | |||||
$post = ['id' => $payment->id]; | |||||
$response = $this->post("api/webhooks/payment/mollie", $post); | |||||
$response->assertStatus(200); | |||||
$wallet->refresh(); | |||||
$this->assertEquals(-112, $wallet->balance); | |||||
$payments = $wallet->payments()->where('id', 're_123456')->get(); | |||||
$this->assertCount(1, $payments); | |||||
$this->assertSame(-112, $payments[0]->amount); | |||||
$this->assertSame(-101, $payments[0]->currency_amount); | |||||
$this->assertSame('EUR', $payments[0]->currency); | |||||
$this->unmockMollie(); | |||||
} | |||||
/** | |||||
* Create Mollie's auto-payment mandate using our API and Chrome browser | * Create Mollie's auto-payment mandate using our API and Chrome browser | ||||
*/ | */ | ||||
protected function createMandate(Wallet $wallet, array $params) | protected function createMandate(Wallet $wallet, array $params) | ||||
{ | { | ||||
// Use the API to create a first payment with a mandate | // Use the API to create a first payment with a mandate | ||||
$response = $this->actingAs($wallet->owner)->post("api/v4/payments/mandate", $params); | $response = $this->actingAs($wallet->owner)->post("api/v4/payments/mandate", $params); | ||||
$response->assertStatus(200); | $response->assertStatus(200); | ||||
$json = $response->json(); | $json = $response->json(); | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | public function testListingPaymentMethods(): void | ||||
Bus::fake(); | Bus::fake(); | ||||
$user = $this->getTestUser('john@kolab.org'); | $user = $this->getTestUser('john@kolab.org'); | ||||
$response = $this->actingAs($user)->get('api/v4/payments/methods?type=' . PaymentProvider::TYPE_ONEOFF); | $response = $this->actingAs($user)->get('api/v4/payments/methods?type=' . PaymentProvider::TYPE_ONEOFF); | ||||
$response->assertStatus(200); | $response->assertStatus(200); | ||||
$json = $response->json(); | $json = $response->json(); | ||||
$this->assertCount(2, $json); | $this->assertCount(3, $json); | ||||
$this->assertSame('creditcard', $json[0]['id']); | $this->assertSame('creditcard', $json[0]['id']); | ||||
$this->assertSame('paypal', $json[1]['id']); | $this->assertSame('paypal', $json[1]['id']); | ||||
$this->assertSame('banktransfer', $json[2]['id']); | |||||
$response = $this->actingAs($user)->get('api/v4/payments/methods?type=' . PaymentProvider::TYPE_RECURRING); | $response = $this->actingAs($user)->get('api/v4/payments/methods?type=' . PaymentProvider::TYPE_RECURRING); | ||||
$response->assertStatus(200); | $response->assertStatus(200); | ||||
$json = $response->json(); | $json = $response->json(); | ||||
$this->assertCount(1, $json); | $this->assertCount(1, $json); | ||||
$this->assertSame('creditcard', $json[0]['id']); | $this->assertSame('creditcard', $json[0]['id']); | ||||
} | } | ||||
} | } |