Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

shadrix's avatar
Level 12

Mocking Stripe - How not to call the API

I feel bad asking, but I don't know how to solve my test. I need to test if a user is being charged or not. In my unit test, I'm testing my method with the real API. But in my feature test, I don't want to use the real API again.

I have a job "ProcessPayment" and somewhere in the handle method I'm calling $this->chargeBuyer();

 protected function chargeBuyer()
 {
    $customer = $this->firstOrCreateCustomer(); //it finds the Stripe\Customer

    return $this->order
                ->buyer
                ->payment()
                ->chargeOrder($this->order, $customer); //this part should be mocked (I know it works).
 }

How do I do this? My first approach is (it's not working but just an idea)

 /** @test */
 public function it_charges_user() 
 {
    $this->orderMock = \Mockery::mock(Order::class);

$this->orderMock->shouldReceive('accepted')
               ->once()
               ->andReturn(true);

    $this->orderMock->shouldReceive('buyer->customerId')
               ->once()
               ->andReturn(true);

    $this->orderMock->shouldReceive('transaction->source_id')
               ->once()
               ->andReturn('jibberish');

    $this->orderMock->shouldReceive('buyer->customer->create')
               ->once()
               ->with(['source' => 'jibberish'])
               ->andReturn('some-customer');

    $this->orderMock->shouldReceive('buyer->payment->chargeOrder')
               ->once()
               ->with('order', 'customer')
               ->andReturn('charged');

    $payment = new ProcessPayment($this->orderMock); //Inject Order

    $payment->handle(); //Start my Job
}

You see that this is super gross, but I don't know how to handle the test...

Here is an excerpt of the job code:

class ProcessPayment implements ShouldQueue
{
   use ...;

 public $order;

/**
 * Create a new job instance.
 *
 * @return void
 */
public function __construct(Order $order)
{
    $this->order = $order;
}

/**
 * Execute the job.
 *
 * @return void
 */
public function handle()
{
    if(! $this->order->accepted) return;

    if(! $this->order->transaction->isChargable){

        return $this->notifyBuyerToPay();
    }
    
    $this->chargeBuyer();
    

}

 /**
 * Checks if Buyer is customer and 
 * is charge for the Order. 
 * @return Stripe\Charge 
 */
protected function chargeBuyer()
{
    $customer = $this->findOrCreateCustomer();

    return $this->order
                ->buyer
                ->payment()
                ->chargeOrder($this->order, $customer);
}
0 likes
4 replies
Drfraker's avatar
Drfraker
Best Answer
Level 28

Here's an overview of how I've done it based on the tdd series by Adam Wathan.

  1. Create an interface and call it PaymentGateway. Add a method to the interface called chargeOrder() that takes an order and a customer parameter. You'll also want to add other methods for getting total charges for a customer etc.
  2. Create a StripePaymentGateway and have it implement the PaymentGateway interface and all of its methods. This class is where you can import Stripe classes and actually talk to stripe. It should do some work and return whatever it returns like your current chargeOrder does.
  3. create a FakePaymentGateway and also have it implement the PaymentGateway interface. In this class you can just fake the chargeOrder method and have it return the same thing as the real implementation does when a successful transaction takes place but it won't actually talk to stripe. This class will just do something simple like store charges in an array so that you can retrieve them and ensure that they were charged properly in your test assertions.

If you go this route you will want to write contract tests in a trait to ensure that your fake and real implementation behave the same way and do not diverge. But that is more advanced and might be hard to cover in a forum post.

7 likes
shadrix's avatar
Level 12

@Drfraker thank you! Helped me a lot. Btw you should have named yourself "DrFaker" :P You know how to mock ;)

2 likes
shawnyv's avatar

+1 to @drfraker's comment! I've been going through the Wathan course again recently, and the "create your own Fake Gateways" strategy has become a core part of my toolbox for dealing with 3rd party services.

That with the "Contract Tests" has been a lifesaver, and has helped me in working with everything from Google product APIs to proprietary 3rd party services for private clients.

Please or to participate in this conversation.