Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Stripe in Laravel

I'm creating a subscription-based SaaS platform in Laravel, where Laravel Cashier does not suit my needs. Therefore I need to implement the subscription-engine myself using the Stripe library.

I found it easy to implement the connection between Laravel and Stripe via hooking into the creation and deletion events of a Subscription class, and then create or cancel a Stripe subscription accordingly.

The Stripe library is unfortunately largely based on calling static methods on some predefined classes (.. like \Stripe\Charge::create()).

This makes it hard for me to test, as you normally would allow dependency injection of some custom client for mocking, but since the Stripe library is referenced statically, there is no client to inject. Is there any way of creating a Stripe client class or such, that I can mock?

like image 260
Hedam Avatar asked Feb 03 '18 14:02

Hedam


People also ask

Can I use Stripe for testing?

Testing paymentsYou can issue cards and simulate purchases using your own Stripe integration in test mode. This allows you to test your integration before you go live without having to make real purchases. You can only use these cards for testing within your Stripe account and not for external purchases.

How do you test Stripe?

Enabling Stripe Test Mode When testing your payment form, you'll need to ensure that no real transactions are made in the process. To do so, navigate to WPForms » Settings and go to the Payments tab. Here, scroll to the Stripe section and select the Test Mode checkbox.

How do you test Stripe payments on production?

Open the form you want to run a Stripe payment check for and go to its Settings. Click Payment Options and check Stripe if it's not checked already. Then put the Checkout Mode to Testmode, click Update, and test away. Don't forget to switch to Production when you need to start accepting actual payments.


1 Answers

Hello from the future!

I was just digging into this. All those classes extend from Stripe's ApiResource class, keep digging and you'll discover that when the library is about to make an HTTP request it calls $this->httpClient(). The httpClient method returns a static reference to a variable called $_httpClient. Conveniently, there is also a static method on the Stripe ApiRequestor class called setHttpClient which accepts an object which is supposed to implement the Stripe HttpClient\ClientInterface (this interface only describes a single method called request).

Soooooo, in your test you can make a call to ApiRequestor::setHttpClient passing it an instance of your own http client mock. Then whenever Stripe makes an HTTP request it will use your mock instead of its default CurlClient. Your responsibility is then have your mock return well-formed Stripe-esque responses and your application will be none the wiser.

Here is a very dumb fake that I've started using in my tests:

<?php

namespace Tests\Doubles;

use Stripe\HttpClient\ClientInterface;

class StripeHttpClientFake implements ClientInterface
{
    private $response;
    private $responseCode;
    private $headers;

    public function __construct($response, $code = 200, $headers = [])
    {
        $this->setResponse($response);
        $this->setResponseCode($code);
        $this->setHeaders($headers);
    }

    /**
     * @param string $method The HTTP method being used
     * @param string $absUrl The URL being requested, including domain and protocol
     * @param array $headers Headers to be used in the request (full strings, not KV pairs)
     * @param array $params KV pairs for parameters. Can be nested for arrays and hashes
     * @param boolean $hasFile Whether or not $params references a file (via an @ prefix or
     *                         CURLFile)
     *
     * @return array An array whose first element is raw request body, second
     *    element is HTTP status code and third array of HTTP headers.
     * @throws \Stripe\Exception\UnexpectedValueException
     * @throws \Stripe\Exception\ApiConnectionException
     */
    public function request($method, $absUrl, $headers, $params, $hasFile)
    {
        return [$this->response, $this->responseCode, $this->headers];
    }

    public function setResponseCode($code)
    {
        $this->responseCode = $code;

        return $this;
    }

    public function setHeaders($headers)
    {
        $this->headers = $headers;

        return $this;
    }

    public function setResponse($response)
    {
        $this->response = file_get_contents(base_path("tests/fixtures/stripe/{$response}.json"));

        return $this;
    }
}

Hope this helps :)

like image 55
Colin Avatar answered Oct 05 '22 23:10

Colin