Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[13.x] Add pay method #1345

Merged
merged 15 commits into from
Apr 19, 2022
77 changes: 73 additions & 4 deletions src/Concerns/PerformsCharges.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Laravel\Cashier\Checkout;
use Laravel\Cashier\Payment;
use LogicException;
use Stripe\Exception\InvalidRequestException as StripeInvalidRequestException;

trait PerformsCharges
{
Expand All @@ -27,23 +28,91 @@ public function charge($amount, $paymentMethod, array $options = [])
$options = array_merge([
'confirmation_method' => 'automatic',
'confirm' => true,
], $options);

$options['payment_method'] = $paymentMethod;

$payment = $this->createPayment($amount, $options);

$payment->validate();

return $payment;
}

/**
* Create a new PaymentIntent instance.
*
* @param int $amount
* @param array $options
* @return \Laravel\Cashier\Payment
*/
public function pay($amount, array $options = [])
{
$options['automatic_payment_methods'] = ['enabled' => true];

unset($options['payment_method_types']);
mozex marked this conversation as resolved.
Show resolved Hide resolved

return $this->createPayment($amount, $options);
}

/**
* Create a new PaymentIntent instance for the given payment method types.
*
* @param int $amount
* @param array $paymentMethods
* @param array $options
* @return \Laravel\Cashier\Payment
*/
public function payWith($amount, array $paymentMethods, array $options = [])
{
$options['payment_method_types'] = $paymentMethods;

unset($options['automatic_payment_methods']);

return $this->createPayment($amount, $options);
}

/**
* Create a new Payment instance with a Stripe PaymentIntent.
*
* @param int $amount
* @param array $options
* @return \Laravel\Cashier\Payment
*/
public function createPayment($amount, array $options = [])
{
$options = array_merge([
'currency' => $this->preferredCurrency(),
], $options);

$options['amount'] = $amount;
$options['payment_method'] = $paymentMethod;

if ($this->hasStripeId()) {
$options['customer'] = $this->stripe_id;
}

$payment = new Payment(
return new Payment(
$this->stripe()->paymentIntents->create($options)
);
}

$payment->validate();
/**
* Find a payment intent by ID.
*
* @param string $id
* @return \Laravel\Cashier\Payment|null
*/
public function findPayment($id)
{
$stripePaymentIntent = null;

return $payment;
try {
$stripePaymentIntent = $this->stripe()->paymentIntents->retrieve($id);
} catch (StripeInvalidRequestException $exception) {
//
}

return $stripePaymentIntent ? new Payment($stripePaymentIntent) : null;
}

/**
Expand Down
15 changes: 15 additions & 0 deletions src/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Traits\ForwardsCalls;
use JsonSerializable;
use Laravel\Cashier\Exceptions\IncompletePayment;
use Stripe\PaymentIntent as StripePaymentIntent;

class Payment implements Arrayable, Jsonable, JsonSerializable
{
use ForwardsCalls;

/**
* The Stripe PaymentIntent instance.
*
Expand Down Expand Up @@ -249,4 +252,16 @@ public function __get($key)
{
return $this->paymentIntent->{$key};
}

/**
* Dynamically pass missing methods to the PaymentIntent instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->forwardCallTo($this->paymentIntent, $method, $parameters);
}
}
20 changes: 20 additions & 0 deletions tests/Feature/ChargesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ public function test_non_stripe_customer_can_be_charged()
$this->assertNull($response->customer);
}

public function test_customer_can_pay()
{
$user = $this->createCustomer('customer_can_pay');
$user->createAsStripeCustomer();

$response = $user->pay(1000);

$this->assertInstanceOf(Payment::class, $response);
$this->assertEquals(1000, $response->rawAmount());
$this->assertEquals($user->stripe_id, $response->customer);
$this->assertTrue($response->requiresPaymentMethod());
$this->assertTrue($response->automatic_payment_methods->enabled);

// Payment intent can be retrieved...
$payment = $user->findPayment($response->id);

$this->assertInstanceOf(Payment::class, $payment);
$this->assertSame($response->id, $payment->id);
}

public function test_customer_can_be_charged_and_invoiced_immediately()
{
$user = $this->createCustomer('customer_can_be_charged_and_invoiced_immediately');
Expand Down
28 changes: 25 additions & 3 deletions tests/Unit/PaymentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

use Laravel\Cashier\Payment;
use PHPUnit\Framework\TestCase;
use Stripe\PaymentIntent;
use Stripe\Subscription;
use Stripe\PaymentIntent as StripePaymentIntent;
use Stripe\Subscription as StripeSubscription;

class PaymentTest extends TestCase
{
Expand All @@ -30,7 +30,7 @@ public function test_it_can_return_its_requires_action_status()
public function test_it_can_return_its_canceled_status()
{
$paymentIntent = new PaymentIntent();
$paymentIntent->status = Subscription::STATUS_CANCELED;
$paymentIntent->status = StripeSubscription::STATUS_CANCELED;
$payment = new Payment($paymentIntent);

$this->assertTrue($payment->isCanceled());
Expand All @@ -44,4 +44,26 @@ public function test_it_can_return_its_succeeded_status()

$this->assertTrue($payment->isSucceeded());
}

public function test_method_calls_are_forward_to_the_stripe_object()
{
$payment = new Payment(new PaymentIntent());

$this->assertTrue($payment->cancel()->cancelled);
}
}

class PaymentIntent extends StripePaymentIntent
{
public $cancelled = false;

/**
* @inheritDoc
*/
public function cancel($params = null, $opts = null)
{
$this->cancelled = true;

return $this;
}
}