customer(); $this->fakeSwishAcceptsWithToken('TOKEN123'); $response = $this->postJson('/api/swish/initiate', [ 'customer_id' => $customer->id, 'amount' => 250, 'give_leftover' => true, ]); $response->assertOk(); $this->assertStringStartsWith('swish://paymentrequest?token=TOKEN123', $response->json('swish_url')); $transaction = Transaction::firstWhere('payment_reference', $response->json('payment_reference')); $this->assertSame('pending', $transaction->status); $this->assertSame(250, $transaction->expected_amount); $this->assertNotEmpty($transaction->callback_identifier); } public function test_the_request_to_swish_has_the_fields_swish_handel_requires(): void { $customer = $this->customer(); $this->fakeSwishAcceptsWithToken(); $response = $this->postJson('/api/swish/initiate', [ 'customer_id' => $customer->id, 'amount' => 250, 'give_leftover' => false, ]); $body = $this->lastSwishBody(); $transaction = Transaction::firstWhere('payment_reference', $response->json('payment_reference')); $this->assertSame('1231181189', $body['payeeAlias']); $this->assertSame('SEK', $body['currency']); $this->assertSame('250.00', $body['amount']); $this->assertSame('https://swish.example.se/webhook/swish/kiosk', $body['callbackUrl']); $this->assertSame($transaction->payment_reference, $body['payeePaymentReference']); $this->assertSame($transaction->callback_identifier, $body['callbackIdentifier']); // The reference has to survive Swish's own validation $this->assertMatchesRegularExpression('/^[a-zA-Z0-9-]{1,35}$/', $body['payeePaymentReference']); $this->assertMatchesRegularExpression('/^[0-9a-zA-Z-]{32,36}$/', $body['callbackIdentifier']); $this->assertLessThanOrEqual(50, strlen($body['message'])); } public function test_the_payment_request_is_a_put_to_the_instruction_uuid(): void { $customer = $this->customer(); $this->fakeSwishAcceptsWithToken(); $response = $this->postJson('/api/swish/initiate', [ 'customer_id' => $customer->id, 'amount' => 100, 'give_leftover' => false, ]); $transaction = Transaction::firstWhere('payment_reference', $response->json('payment_reference')); $request = end($this->swishRequests)['request']; $this->assertSame('PUT', $request->getMethod()); $this->assertSame( '/swish-cpcapi/api/v2/paymentrequests/' . $transaction->instruction_uuid, $request->getUri()->getPath() ); $this->assertMatchesRegularExpression('/^[0-9A-F]{32}$/', $transaction->instruction_uuid); } public function test_it_fails_cleanly_when_swish_is_not_configured(): void { config(['app.swish_api_url' => '']); $customer = $this->customer(); $response = $this->postJson('/api/swish/initiate', [ 'customer_id' => $customer->id, 'amount' => 100, 'give_leftover' => false, ]); $response->assertStatus(502); $this->assertSame('failed', Transaction::first()->status); } public function test_it_fails_when_swish_rejects_the_payment_request(): void { $customer = $this->customer(); $this->fakeSwish(new GuzzleResponse(422, [], json_encode([ ['errorCode' => 'PA02', 'errorMessage' => 'Amount value is missing or not a valid number'], ]))); $response = $this->postJson('/api/swish/initiate', [ 'customer_id' => $customer->id, 'amount' => 100, 'give_leftover' => false, ]); $response->assertStatus(502); $this->assertSame('failed', Transaction::first()->status); } public function test_it_fails_when_swish_answers_without_a_token(): void { $customer = $this->customer(); $this->fakeSwish(new GuzzleResponse(201)); $this->postJson('/api/swish/initiate', [ 'customer_id' => $customer->id, 'amount' => 100, 'give_leftover' => false, ])->assertStatus(502); $this->assertSame('failed', Transaction::first()->status); } public function test_it_validates_its_input(): void { $customer = $this->customer(); $this->postJson('/api/swish/initiate', [ 'customer_id' => $customer->id, 'amount' => 0, 'give_leftover' => false, ])->assertStatus(422); $this->postJson('/api/swish/initiate', [ 'customer_id' => 999999, 'amount' => 100, 'give_leftover' => false, ])->assertStatus(422); $this->assertSame(0, Transaction::count()); } }