'sk_test_fake']); Http::fake([ 'https://api.stripe.com/v1/checkout/sessions' => Http::response([ 'id' => 'cs_test_card', 'url' => 'https://checkout.stripe.com/c/pay_card', 'payment_intent' => 'pi_card', ], 200), ]); $intent = (new StripeGateway())->createIntent($this->invoice(), [ 'payment_id' => 'pay_123', 'payment_method' => 'card', 'customer_email' => 'applicant@example.com', 'return_url' => 'https://example.com/return', 'cancel_url' => 'https://example.com/cancel', ], 'PAY-123'); $this->assertSame('cs_test_card', $intent['gateway_reference']); $this->assertSame('pi_card', $intent['external_reference']); $this->assertSame('https://checkout.stripe.com/c/pay_card', $intent['checkout_url']); Http::assertSent(fn ($request) => $request->url() === 'https://api.stripe.com/v1/checkout/sessions' && $request['payment_method_types'][0] === 'card' && $request['client_reference_id'] === 'PAY-123' && $request['line_items'][0]['price_data']['product_data']['name'] === 'Application Fee'); } public function test_bank_transfer_checkout_session_creates_customer(): void { config([ 'payments.stripe.secret_key' => 'sk_test_fake', 'payments.stripe.bank_transfer_type' => 'us_bank_transfer', ]); Http::fake([ 'https://api.stripe.com/v1/customers' => Http::response(['id' => 'cus_bank'], 200), 'https://api.stripe.com/v1/checkout/sessions' => Http::response([ 'id' => 'cs_test_bank', 'url' => 'https://checkout.stripe.com/c/pay_bank', 'payment_intent' => null, 'customer' => 'cus_bank', ], 200), ]); $intent = (new StripeGateway())->createIntent($this->invoice(), [ 'payment_id' => 'pay_456', 'payment_method' => 'bank_transfer', 'customer_email' => 'applicant@example.com', 'return_url' => 'https://example.com/return', 'cancel_url' => 'https://example.com/cancel', ], 'PAY-456'); $this->assertSame('cs_test_bank', $intent['gateway_reference']); $this->assertSame('cus_bank', $intent['metadata']['stripe_customer_id']); Http::assertSent(fn ($request) => $request->url() === 'https://api.stripe.com/v1/customers'); Http::assertSent(fn ($request) => $request->url() === 'https://api.stripe.com/v1/checkout/sessions' && $request['payment_method_types'][0] === 'customer_balance' && $request['payment_method_options']['customer_balance']['funding_type'] === 'bank_transfer'); } public function test_webhook_signature_is_verified(): void { config(['payments.stripe.webhook_secret' => 'whsec_fake']); $payload = json_encode(['id' => 'evt_123', 'type' => 'checkout.session.completed'], JSON_THROW_ON_ERROR); $timestamp = time(); $signature = hash_hmac('sha256', $timestamp.'.'.$payload, 'whsec_fake'); $verified = (new StripeGateway())->verifyWebhook($payload, [ 'stripe-signature' => ['t='.$timestamp.',v1='.$signature], ]); $this->assertTrue($verified['verified']); $this->assertSame('evt_123', $verified['event_id']); } public function test_checkout_session_uses_invoice_fee_type_name(): void { config(['payments.stripe.secret_key' => 'sk_test_fake']); Http::fake([ 'https://api.stripe.com/v1/checkout/sessions' => Http::response([ 'id' => 'cs_test_membership', 'url' => 'https://checkout.stripe.com/c/pay_membership', 'payment_intent' => 'pi_membership', ], 200), ]); $invoice = $this->invoice(); $invoice->fee_type_code = 'ANNUAL_MEMBERSHIP_FEE'; (new StripeGateway())->createIntent($invoice, [ 'payment_id' => 'pay_789', 'payment_method' => 'card', 'customer_email' => 'applicant@example.com', 'return_url' => 'https://example.com/return', 'cancel_url' => 'https://example.com/cancel', ], 'PAY-789'); Http::assertSent(fn ($request) => $request->url() === 'https://api.stripe.com/v1/checkout/sessions' && $request['line_items'][0]['price_data']['product_data']['name'] === 'Annual Membership Fee'); } public function test_webhook_signature_accepts_multiple_v1_values(): void { config(['payments.stripe.webhook_secret' => 'whsec_fake']); $payload = json_encode(['id' => 'evt_456', 'type' => 'checkout.session.completed'], JSON_THROW_ON_ERROR); $timestamp = time(); $validSignature = hash_hmac('sha256', $timestamp.'.'.$payload, 'whsec_fake'); $verified = (new StripeGateway())->verifyWebhook($payload, [ 'stripe-signature' => ['t='.$timestamp.',v1=invalidsignature,v1='.$validSignature], ]); $this->assertTrue($verified['verified']); $this->assertSame('evt_456', $verified['event_id']); } private function invoice(): object { return (object) [ 'id' => 'inv_123', 'application_id' => 'app_123', 'fee_type_code' => 'APPLICATION_FEE', 'amount_due' => 20, 'currency' => 'USD', ]; } }