customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100]); $this->postCallback($this->paidPayload())->assertNoContent(); $customer->refresh(); $this->assertSame(800, $customer->deposit); $this->assertSame(800, $customer->amount_left); $this->assertSame('PAID', Transaction::first()->status); $this->assertSame(1, Deposit::where('customer_id', $customer->id)->count()); } public function test_it_rejects_a_callback_without_the_identifier(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id]); $this->postCallback($this->paidPayload(), null)->assertStatus(403); $this->assertSame(700, $customer->refresh()->deposit); $this->assertSame('pending', Transaction::first()->status); } public function test_it_rejects_a_callback_with_the_wrong_identifier(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id]); $this->postCallback($this->paidPayload(), 'ffffffff-ffff-ffff-ffff-ffffffffffff') ->assertStatus(403); $this->assertSame(700, $customer->refresh()->deposit); $this->assertSame('pending', Transaction::first()->status); } public function test_it_rejects_a_callback_for_a_transaction_that_was_never_registered(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id, 'callback_identifier' => null]); $this->postCallback($this->paidPayload())->assertStatus(403); $this->assertSame(700, $customer->refresh()->deposit); } public function test_a_repeated_callback_only_credits_once(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100]); $this->postCallback($this->paidPayload())->assertNoContent(); $this->postCallback($this->paidPayload())->assertNoContent(); $this->postCallback($this->paidPayload())->assertNoContent(); $this->assertSame(800, $customer->refresh()->deposit); $this->assertSame(1, Deposit::count()); } public function test_it_refuses_an_amount_that_differs_from_the_payment_request(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100]); $this->postCallback($this->paidPayload(['amount' => 5000]))->assertNoContent(); $this->assertSame(700, $customer->refresh()->deposit); $this->assertSame('amount_mismatch', Transaction::first()->status); $this->assertSame(0, Deposit::count()); } public function test_it_does_not_round_an_amount_with_ore(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100]); $this->postCallback($this->paidPayload(['amount' => 99.50]))->assertNoContent(); $this->assertSame(700, $customer->refresh()->deposit); $this->assertSame('amount_mismatch', Transaction::first()->status); } public function test_a_declined_payment_is_recorded_but_not_credited(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id]); $this->postCallback($this->paidPayload([ 'status' => 'DECLINED', 'errorCode' => 'BANKIDCL', ]))->assertNoContent(); $this->assertSame(700, $customer->refresh()->deposit); $this->assertSame('DECLINED', Transaction::first()->status); } public function test_an_unknown_reference_changes_nothing(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->postCallback($this->paidPayload(['payeePaymentReference' => 'LAN-NOSUCHREF'])) ->assertNoContent(); $this->assertSame(700, $customer->refresh()->deposit); $this->assertSame(0, Deposit::count()); } public function test_a_group_member_payment_lands_on_the_group_account(): void { $group = $this->customer([ 'lan_id' => 10, 'name' => 'Klanen', 'deposit' => 500, 'amount_left' => 500, 'is_in_group' => 0, 'customer_group_id' => 7, ]); $member = $this->customer([ 'lan_id' => 11, 'name' => 'Anna', 'deposit' => 0, 'amount_left' => 0, 'is_in_group' => 1, 'customer_group_id' => 7, ]); $this->transaction(['customer_id' => $member->id, 'expected_amount' => 100]); $this->postCallback($this->paidPayload())->assertNoContent(); $this->assertSame(600, $group->refresh()->deposit); $this->assertSame(600, $group->amount_left); $this->assertSame(0, $member->refresh()->deposit); } public function test_a_payment_to_a_customer_of_their_own_lands_on_themselves(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700, 'is_in_group' => 0]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100]); $this->postCallback($this->paidPayload())->assertNoContent(); $this->assertSame(800, $customer->refresh()->deposit); } public function test_a_late_callback_on_an_expired_transaction_is_still_booked(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100, 'status' => 'expired']); $this->postCallback($this->paidPayload())->assertNoContent(); $this->assertSame(800, $customer->refresh()->deposit); $this->assertSame('PAID', Transaction::first()->status); } public function test_the_leftover_choice_from_the_payment_follows_along(): void { $customer = $this->customer(['deposit' => 0, 'amount_left' => 0, 'give_leftover' => 0]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100, 'give_leftover' => 1]); $this->postCallback($this->paidPayload())->assertNoContent(); $this->assertSame(1, (int) $customer->refresh()->give_leftover); } public function test_it_still_understands_the_old_reference_in_the_message_field(): void { $customer = $this->customer(['deposit' => 700, 'amount_left' => 700]); $this->transaction(['customer_id' => $customer->id, 'expected_amount' => 100]); $payload = $this->paidPayload(['message' => 'kiosk|LAN-TEST0001']); unset($payload['payeePaymentReference']); $this->postCallback($payload)->assertNoContent(); $this->assertSame(800, $customer->refresh()->deposit); } }