actingAs($customer, 'client') ->withHeaders(['x-access-token' => $this->buildClientAccessToken()]); } private function prepare(Customer $customer, int $amount = 500000): array { return $this->actingAsCustomer($customer) ->postJson('/v1/transaction/manual-deposit/prepare', ['amount' => $amount]) ->assertOk() ->json('data'); } public function test_prepare_returns_expiry_and_show_restores_draft(): void { $customer = Customer::factory()->create(['total_pending' => 0]); $draft = $this->prepare($customer); $this->assertSame(500000, $draft['amount']); $this->assertEqualsWithDelta(now()->addMinutes(30)->timestamp, strtotime($draft['expires_at']), 5); $this->actingAsCustomer($customer) ->getJson("/v1/transaction/manual-deposit/{$draft['uuid']}") ->assertOk() ->assertJsonPath('data.public_code', $draft['public_code']) ->assertJsonPath('data.amount', 500000); } public function test_show_hides_draft_of_another_customer(): void { $owner = Customer::factory()->create(); $other = Customer::factory()->create(); $draft = $this->prepare($owner); $this->actingAsCustomer($other) ->getJson("/v1/transaction/manual-deposit/{$draft['uuid']}") ->assertNotFound(); } public function test_expired_draft_is_discarded_on_show_and_rejected_on_confirm(): void { Storage::fake('local'); $customer = Customer::factory()->create(['total_pending' => 0]); $first = $this->prepare($customer); $this->travel(31)->minutes(); $this->actingAsCustomer($customer) ->getJson("/v1/transaction/manual-deposit/{$first['uuid']}") ->assertStatus(410); $this->assertNull(Transaction::withTrashed()->where('uuid', $first['uuid'])->first()); $this->assertSame(0, (int) $customer->fresh()->total_pending); $second = $this->prepare($customer); $this->travel(31)->minutes(); $path = UploadedFile::fake()->image('proof.jpg')->store('deposit-proofs/' . $customer->id, 'local'); $this->actingAsCustomer($customer) ->postJson('/v1/transaction/manual-deposit/confirm', ['uuid' => $second['uuid'], 'proof_image' => $path]) ->assertStatus(422); $this->assertNull(Transaction::where('uuid', $second['uuid'])->value('proof_image')); } public function test_purge_command_removes_only_expired_drafts(): void { $customer = Customer::factory()->create(['total_pending' => 0]); $expired = $this->prepare($customer, 300000); $this->travel(31)->minutes(); $other = Customer::factory()->create(['total_pending' => 0]); $fresh = $this->prepare($other, 200000); $submitted = Transaction::create([ 'amount' => 100000, 'customer_id' => $customer->id, 'type' => 'manual', 'status' => TransactionStatus::PENDING->value, 'proof_image' => 'deposit-proofs/x.jpg', 'trans' => 'MANTEST', ]); $this->artisan('deposits:purge-expired-drafts')->assertSuccessful(); $this->assertNull(Transaction::withTrashed()->where('uuid', $expired['uuid'])->first()); $this->assertNotNull(Transaction::where('uuid', $fresh['uuid'])->first()); $this->assertNotNull($submitted->fresh()); $this->assertSame(0, (int) $customer->fresh()->total_pending); $this->assertSame(0, (int) $other->fresh()->total_pending); } public function test_total_pending_only_counts_amount_after_proof_is_submitted(): void { Storage::fake('local'); $customer = Customer::factory()->create(['total_pending' => 0]); $draft = $this->prepare($customer, 700000); $this->assertSame(0, (int) $customer->fresh()->total_pending); $path = $this->actingAsCustomer($customer) ->post('/v1/transaction/manual-deposit/upload-proof', ['file' => UploadedFile::fake()->image('proof.jpg')]) ->assertOk() ->json('path'); $this->assertSame(0, (int) $customer->fresh()->total_pending); $this->actingAsCustomer($customer) ->postJson('/v1/transaction/manual-deposit/confirm', ['uuid' => $draft['uuid'], 'proof_image' => $path]) ->assertOk(); $this->assertSame(700000, (int) $customer->fresh()->total_pending); } }