<?php
use App\Models\{Asset,ChainTransaction,Invoice,InvoiceTransaction,Merchant}; use App\Application\Services\PaymentStateMachine;
it('allows exact payment lifecycle transitions',function(){ $s=app(PaymentStateMachine::class); expect($s->canTransition('waiting','confirming'))->toBeTrue()->and($s->canTransition('confirming','confirmed'))->toBeTrue()->and($s->canTransition('confirmed','finished'))->toBeTrue(); });
it('classifies partial and overpayment amounts using integer arithmetic',function(){ expect(bccomp('49999999','50000000'))->toBe(-1)->and(bccomp('50000001','50000000'))->toBe(1); });
it('deduplicates the same chain transfer identity at database level',function(){ $m=Merchant::factory()->create();$a=Asset::factory()->create();$i=Invoice::factory()->for($m)->for($a,'asset')->create();$tx=ChainTransaction::create(['network'=>'tron','tx_hash'=>'abc','asset_id'=>$a->id,'to_address'=>'TAddress','amount_atomic'=>'50000000','confirmations'=>1,'finality_status'=>'confirmed','transfer_index'=>0]);InvoiceTransaction::create(['invoice_id'=>$i->id,'chain_transaction_id'=>$tx->id,'allocated_amount_atomic'=>'50000000']);expect(fn()=>InvoiceTransaction::create(['invoice_id'=>$i->id,'chain_transaction_id'=>$tx->id,'allocated_amount_atomic'=>'50000000']))->toThrow(Throwable::class); });
