Files
api/services/nginx/app/tests/Api/StripeApiTest.php
T

326 lines
11 KiB
PHP

<?php
declare(strict_types=1);
use classes\email;
use classes\stripe;
use classes\stripe_fake_http_client;
putenv('STRIPE_FAKE_MODE=1');
putenv('STRIPE_FAKE_STORE_PATH=' . sys_get_temp_dir() . '/truckwash-stripe-api-tests.json');
putenv('EMAIL_FAKE_MODE=1');
usesApiSuite();
beforeEach(function (): void {
stripe_fake_http_client::resetStore();
email::resetFakeDeliveries();
if (api_tests_enabled()) {
api_test_runtime()->db()->query("DELETE FROM stripe_module_orders WHERE invoice_id LIKE 'in_fake_%'");
api_test_runtime()->db()->query("DELETE FROM stripe_module_customers WHERE customer_id LIKE 'cus_fake_%' OR email LIKE 'stripe-%@example.com'");
}
});
it('returns a setup required error when department terminal readers are requested without terminal setup', function (): void {
$department = api_fixtures()->createDepartment([
'name' => 'Stripe Setup Pending Department',
]);
$session = api_fixtures()->createUserSession([
'modules_stripe_department_terminal_readers_list',
'department_access_' . $department['id'],
]);
$response = api_client()->get(
'/modules/stripe/department/terminal/readers?id=' . $department['id'],
$session['headers']
);
$response
->assertStatus(409)
->assertEnvelope()
->assertSuccess(false)
->assertMessage('Card payments are not ready for this department. Open Stripe setup and choose a terminal location.');
expect($response->data())
->toBeArray()
->toHaveKey('code', 'stripe_terminal_setup_required');
});
it('sends a Stripe invoice by email and persists the hosted invoice association', function (): void {
$department = api_fixtures()->createDepartment([
'name' => 'Stripe Email Payments Department',
]);
$customer = api_fixtures()->createUser([
'display_name' => 'Stripe Hosted Invoice Customer',
]);
$order = api_fixtures()->createOrder([
'customer_id' => $customer['customer_number'],
'department_id' => $department['id'],
'reference' => 'STRIPE-EMAIL-SEND',
'reg_1' => 'EMAIL01',
]);
$session = api_fixtures()->createUserSession([
'modules_stripe_invoice_send',
]);
$emailAddress = sprintf('stripe-email-%d@example.com', (int)$order['id']);
$response = api_client()->post('/modules/stripe/invoice', [
'email' => $emailAddress,
'order_id' => $order['id'],
], $session['headers']);
$response
->assertStatus(200)
->assertEnvelope()
->assertSuccess(true);
expect($response->data())
->toBeArray()
->toHaveKey('id')
->toHaveKey('hosted_invoice_url')
->toHaveKey('metadata');
expect($response->data()['metadata'] ?? [])
->toMatchArray([
'order_id' => (string)$order['id'],
'customer_id' => (string)$customer['customer_number'],
'department_id' => (string)$department['id'],
'reference' => 'STRIPE-EMAIL-SEND',
'reg_1' => 'EMAIL01',
])
->and(($response->data()['metadata']['stripe_customer_id'] ?? null))
->toBe((string)($response->data()['customer'] ?? ''));
$retrievedInvoice = (new stripe())->invoice->retrieve((string)$response->data()['id']);
$retrievedMetadata = json_decode(json_encode($retrievedInvoice->metadata), true);
if (!is_array($retrievedMetadata)) {
$retrievedMetadata = [];
}
expect($retrievedMetadata)
->toMatchArray([
'order_id' => (string)$order['id'],
'customer_id' => (string)$customer['customer_number'],
'department_id' => (string)$department['id'],
'reference' => 'STRIPE-EMAIL-SEND',
'reg_1' => 'EMAIL01',
])
->and(($retrievedMetadata['stripe_customer_id'] ?? null))
->toBe((string)($response->data()['customer'] ?? ''));
$stored = api_test_runtime()->queryOne(
'SELECT invoice_id, customer_id, url FROM stripe_module_orders WHERE id = ' . (int)$order['id']
);
expect($stored)
->not->toBeNull()
->and($stored['invoice_id'] ?? null)->toBe((string)$response->data()['id'])
->and($stored['customer_id'] ?? null)->not->toBe('')
->and($stored['url'] ?? null)->toBe((string)$response->data()['hosted_invoice_url']);
});
it('returns a conflict when a Stripe hosted invoice is already active for the order', function (): void {
$department = api_fixtures()->createDepartment([
'name' => 'Stripe Email Guard Department',
]);
$customer = api_fixtures()->createUser([
'display_name' => 'Stripe Duplicate Guard Customer',
]);
$order = api_fixtures()->createOrder([
'customer_id' => $customer['customer_number'],
'department_id' => $department['id'],
'reference' => 'STRIPE-EMAIL-GUARD',
'reg_1' => 'EMAIL02',
]);
$session = api_fixtures()->createUserSession([
'modules_stripe_invoice_send',
]);
$emailAddress = sprintf('stripe-duplicate-%d@example.com', (int)$order['id']);
api_client()->post('/modules/stripe/invoice', [
'email' => $emailAddress,
'order_id' => $order['id'],
], $session['headers'])->assertStatus(200);
$response = api_client()->post('/modules/stripe/invoice', [
'email' => $emailAddress,
'order_id' => $order['id'],
], $session['headers']);
$response
->assertStatus(409)
->assertEnvelope()
->assertSuccess(false)
->assertMessage('A Stripe payment link is already active for this order.');
expect($response->data())
->toBeArray()
->toHaveKey('code', 'stripe_invoice_exists');
});
it('allows sending a new Stripe hosted invoice when the existing association is already terminal', function (): void {
$department = api_fixtures()->createDepartment([
'name' => 'Stripe Email Terminal Guard Department',
]);
$customer = api_fixtures()->createUser([
'display_name' => 'Stripe Terminal Guard Customer',
]);
$order = api_fixtures()->createOrder([
'customer_id' => $customer['customer_number'],
'department_id' => $department['id'],
'reference' => 'STRIPE-EMAIL-TERMINAL',
'reg_1' => 'EMAIL05',
]);
$session = api_fixtures()->createUserSession([
'modules_stripe_invoice_send',
]);
$emailAddress = sprintf('stripe-terminal-%d@example.com', (int)$order['id']);
$firstResponse = api_client()->post('/modules/stripe/invoice', [
'email' => $emailAddress,
'order_id' => $order['id'],
], $session['headers']);
$firstInvoiceId = (string)($firstResponse->data()['id'] ?? '');
stripe_fake_http_client::setInvoiceState($firstInvoiceId, [
'status' => 'void',
'paid' => false,
]);
$secondResponse = api_client()->post('/modules/stripe/invoice', [
'email' => $emailAddress,
'order_id' => $order['id'],
], $session['headers']);
$secondResponse
->assertStatus(200)
->assertEnvelope()
->assertSuccess(true);
expect((string)($secondResponse->data()['id'] ?? ''))
->not->toBe('')
->not->toBe($firstInvoiceId);
});
it('voids an unpaid Stripe hosted invoice and clears the local order association', function (): void {
$department = api_fixtures()->createDepartment([
'name' => 'Stripe Email Cancel Department',
]);
$customer = api_fixtures()->createUser([
'display_name' => 'Stripe Cancel Customer',
]);
$order = api_fixtures()->createOrder([
'customer_id' => $customer['customer_number'],
'department_id' => $department['id'],
'reference' => 'STRIPE-EMAIL-CANCEL',
'reg_1' => 'EMAIL03',
]);
$session = api_fixtures()->createUserSession([
'modules_stripe_invoice_send',
]);
$emailAddress = sprintf('stripe-cancel-%d@example.com', (int)$order['id']);
$sendResponse = api_client()->post('/modules/stripe/invoice', [
'email' => $emailAddress,
'order_id' => $order['id'],
], $session['headers']);
$invoiceId = (string)($sendResponse->data()['id'] ?? '');
$response = api_client()->delete('/modules/stripe/invoice', [
'order_id' => $order['id'],
], $session['headers']);
$response
->assertStatus(200)
->assertEnvelope()
->assertSuccess(true);
expect(api_test_runtime()->queryOne(
'SELECT invoice_id FROM stripe_module_orders WHERE id = ' . (int)$order['id']
))->toBeNull();
expect((new stripe())->invoice->retrieve($invoiceId)->status)->toBe('void');
});
it('refuses to cancel a paid Stripe hosted invoice', function (): void {
$department = api_fixtures()->createDepartment([
'name' => 'Stripe Email Paid Department',
]);
$customer = api_fixtures()->createUser([
'display_name' => 'Stripe Paid Customer',
]);
$order = api_fixtures()->createOrder([
'customer_id' => $customer['customer_number'],
'department_id' => $department['id'],
'reference' => 'STRIPE-EMAIL-PAID',
'reg_1' => 'EMAIL04',
]);
$session = api_fixtures()->createUserSession([
'modules_stripe_invoice_send',
]);
$emailAddress = sprintf('stripe-paid-%d@example.com', (int)$order['id']);
$sendResponse = api_client()->post('/modules/stripe/invoice', [
'email' => $emailAddress,
'order_id' => $order['id'],
], $session['headers']);
$invoiceId = (string)($sendResponse->data()['id'] ?? '');
stripe_fake_http_client::setInvoiceState($invoiceId, [
'status' => 'paid',
'paid' => true,
'amount_due' => 0,
'amount_paid' => 1000,
]);
$response = api_client()->delete('/modules/stripe/invoice', [
'order_id' => $order['id'],
], $session['headers']);
$response
->assertStatus(409)
->assertEnvelope()
->assertSuccess(false)
->assertMessage('A paid Stripe payment link cannot be cancelled.');
expect(api_test_runtime()->queryOne(
'SELECT invoice_id FROM stripe_module_orders WHERE id = ' . (int)$order['id']
))->not->toBeNull();
});
it('returns a setup required error when creating a payment intent for a department without terminal setup', function (): void {
$department = api_fixtures()->createDepartment([
'name' => 'Stripe Payment Intent Pending Department',
]);
$customer = api_fixtures()->createUser([
'display_name' => 'Stripe Payment Intent Customer',
]);
$order = api_fixtures()->createOrder([
'customer_id' => $customer['customer_number'],
'department_id' => $department['id'],
'reference' => 'STRIPE-SETUP-REQUIRED',
'reg_1' => 'STRIPE01',
]);
$session = api_fixtures()->createUserSession([
'charge_order',
'department_access_' . (int)$department['id'],
]);
$response = api_client()->post('/orders/module/stripe/payment_intent', [
'id' => $order['id'],
'reader' => 'reader_pending_setup',
'tax_percentage' => 25,
], $session['headers']);
$response
->assertStatus(409)
->assertEnvelope()
->assertSuccess(false)
->assertMessage('Card payments are not ready for this department. Open Stripe setup and choose a terminal location.');
expect($response->data())
->toBeArray()
->toHaveKey('code', 'stripe_terminal_setup_required');
});