Skip to content

Commit

Permalink
Add authorize, use getAfterUrl instead of getTargetUrl and Sync a pay…
Browse files Browse the repository at this point in the history
…ment before getting it Status
  • Loading branch information
Prometee committed Mar 16, 2021
1 parent 7110fc6 commit 88d912d
Show file tree
Hide file tree
Showing 57 changed files with 2,298 additions and 525 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ composer require php-http/guzzle6-adapter
- [Stripe Checkout Session](docs/stripe-checkout-session/README.md)

Support :
- ["One-time payments"](https://stripe.com/docs/payments/checkout/one-time)
- ["Subscription"](https://stripe.com/docs/payments/checkout/subscriptions/starting)
- ["Update payment details"](https://stripe.com/docs/payments/checkout/subscriptions/updating)
- ["One-time payments"](https://stripe.com/docs/payments/checkout/one-time)
- ["Place a hold on a card" (Authorize)](https://stripe.com/docs/payments/capture-later)
- ["Subscription"](https://stripe.com/docs/payments/checkout/subscriptions/starting)
- ["Set up future payments"](https://stripe.com/docs/payments/save-and-reuse#checkout)

- [Stripe JS](docs/stripe-js/README.md)

Support :
- ["Accept a payment"](https://stripe.com/docs/payments/accept-a-payment?integration=elements)
- ["Accept a payment"](https://stripe.com/docs/payments/accept-a-payment?integration=elements)
- ["Place a hold on a card" (Authorize)](https://stripe.com/docs/payments/capture-later)

## More

Expand Down
2 changes: 1 addition & 1 deletion UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# UPGRADE FROM `v1.2.3` TO `v1.3.0`
# UPGRADE FROM `v1.2.3` TO `v2.0.0`

**BC BREAK**: The class `FluxSE\PayumStripe\CaptureAction` has been moved and split into two classes :

Expand Down
6 changes: 6 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
"name": "flux-se/payum-stripe",
"description": "Payum Stripe gateways",
"type": "library",
"keywords": [
"stripe",
"payum",
"payment",
"authorize"
],
"require": {
"payum/core": "^1.6",
"stripe/stripe-php": "^7"
Expand Down
190 changes: 4 additions & 186 deletions docs/stripe-checkout-session/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,192 +2,10 @@

See https://stripe.com/docs/payments/checkout for more information.

## Get it started

First get [your credentials](../stripe-credentials.md) from Stripe dashboard.

> The following example is the basic Payum implementation
> (see [documentation of Payum](https://github.com/Payum/Payum/blob/master/docs/get-it-started.md) for more information)
### config.php

```php
<?php

declare(strict_types=1);

$loader = require_once( __DIR__.'/vendor/autoload.php');

use Payum\Core\GatewayFactoryInterface;
use FluxSE\PayumStripe\StripeCheckoutSessionGatewayFactory;
use Payum\Core\PayumBuilder;
use Payum\Core\Payum;

/** @var Payum $payum */
$payum = (new PayumBuilder())
->addDefaultStorages()
->addGatewayFactory('stripe_checkout_session', function(array $config, GatewayFactoryInterface $coreGatewayFactory) {
return new StripeCheckoutSessionGatewayFactory($config, $coreGatewayFactory);
})
->addGateway('stripe_checkout_session', [
'factory' => 'stripe_checkout_session',
'publishable_key' => 'pk_test_abcdefghijklmnopqrstuvwx',
'secret_key' => 'sk_test_abcdefghijklmnopqrstuvwx',
'webhook_secret_keys' => [
'whsec_abcdefghijklmnopqrstuvwxyz012345'
],
])
->getPayum()
;
```

### prepare.php

```php
<?php

declare(strict_types=1);

include __DIR__.'/config.php';

use Payum\Core\Model\Payment;

$gatewayName = 'stripe_checkout_session';

$storage = $payum->getStorage(Payment::class);

/** @var Payment $payment */
$payment = $storage->create();
$payment->setNumber(uniqid());
$payment->setCurrencyCode('EUR');
$payment->setTotalAmount(123); // 1.23 EUR
$payment->setDescription('A description');
$payment->setClientId('anId');
$payment->setClientEmail('[email protected]');
$payment->setDetails([]);

$storage->update($payment);

$tokenFactory = $payum->getTokenFactory();
$captureToken = $tokenFactory->createCaptureToken($gatewayName, $payment, 'done.php');

header("Location: ".$captureToken->getTargetUrl());
```

### capture.php

```php
<?php

declare(strict_types=1);

use Payum\Core\Reply\HttpResponse;
use Payum\Core\Request\Capture;
use Payum\Core\Reply\HttpRedirect;

include __DIR__.'/config.php';

$token = $payum->getHttpRequestVerifier()->verify($_REQUEST);
$gateway = $payum->getGateway($token->getGatewayName());

if ($reply = $gateway->execute(new Capture($token), true)) {
if ($reply instanceof HttpRedirect) {
header("Location: ".$reply->getUrl());
die();
}
if ($reply instanceof HttpResponse) {
echo $reply->getContent();
die();
}

throw new \LogicException('Unsupported reply', null, $reply);
}

$payum->getHttpRequestVerifier()->invalidate($token);

header("Location: ".$token->getAfterUrl());
```

### done.php

```php
<?php

declare(strict_types=1);

use Payum\Core\Request\GetHumanStatus;

include __DIR__.'/config.php';

$token = $payum->getHttpRequestVerifier()->verify($_REQUEST);
$gateway = $payum->getGateway($token->getGatewayName());

// you can invalidate the token. The url could not be requested any more.
// $payum->getHttpRequestVerifier()->invalidate($token);

// Once you have token you can get the model from the storage directly.
//$identity = $token->getDetails();
//$payment = $payum->getStorage($identity->getClass())->find($identity);

// or Payum can fetch the model for you while executing a request (Preferred).
$gateway->execute($status = new GetHumanStatus($token));
$payment = $status->getFirstModel();

header('Content-Type: application/json');
echo json_encode([
'status' => $status->getValue(),
'order' => [
'total_amount' => $payment->getTotalAmount(),
'currency_code' => $payment->getCurrencyCode(),
'details' => $payment->getDetails(),
],
], JSON_PRETTY_PRINT);
```

## Webhooks

For generic info on Stripe webhooks read this :
https://stripe.com/docs/webhooks

### How the webhooks are handle into this gateway ?

The starting point is always an `Action` with Payum and generically you have to use `Notify*Action` to handle webhooks.

Because we have to set a static url on Stripe backend (eg: without any token variable params),
we have to use what Payum is calling a `NotifyUnsafe`, it's a `Notify` with a `null` `Token`.
You can find this action here : [`NotifyAction.php`](../../src/Action/NotifyAction.php).
If the token is null then we will try to handle a webhook, and if a token is detected
then it's a normal `Notify` so we must handle a `Sync` to refresh a payment details.

#### Resolving a webhook : `NotifyUnsafe`

The [`NotifyAction.php`](../../src/Action/NotifyAction.php) will ask for 2 other actions to :

1. Resolve the webhook event, meaning :
- retrieve the Stripe signature in the request headers.
- try to construct the webhook `Event` object, checking it with the webhook secret key.
2. Give this resolved `Event` to an `Action` able to consume this `Event`.

So if you want to consume another webhook event type, you just need to create an `Action`
extending [`FluxSE\PayumStripe\Action\Api\WebhookEvent\AbstractWebhookEventAction`](../../src/Action/Api/WebhookEvent/AbstractWebhookEventAction.php).
Examples available into the [`src/Action/Api/WebhookEvent/`](../../src/Action/Api/WebhookEvent) folder.

## Subscription handling

Payum don't have php `Interfaces` to handle subscription, that's why subscriptions should be
managed by yourself. There is maybe a composer packages which fit your need,
but you will have to build the interface between your subscription `Model` class and `Payum`.

Usually you will have to build a `ConvertPaymentAction` like this one : [ConvertPaymentAction.php](https://github.com/FLUX-SE/SyliusPayumStripePlugin/blob/master/src/Action/ConvertPaymentAction.php)
customizing the `supports` method to fit your need and provide the right `$details` array.

Example : https://stripe.com/docs/payments/checkout/subscriptions/starting#create-checkout-session (`$details` is the array given to create a `Session`)

## Subscription update payment details

Same as the [previous chapter](#subscription-handling)

Example : https://stripe.com/docs/payments/checkout/subscriptions/updating#create-checkout-session (`$details` is the array given to create a `Session`)
- [Payment](payment.md) : How to process a Stripe Checkout Session payment ?
- [Authorize](authorize.md) : How to place a hold on a card with Stripe Checkout Session ?
- [Subscription](subscription.md) : How to handle subscription with Stripe Checkout Session ?
- [Setup](setup.md) : How to set up future payments with Stripe Checkout Session ?

## More

Expand Down
96 changes: 96 additions & 0 deletions docs/stripe-checkout-session/authorize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Stripe Checkout Session : `payment` with `capture_method = manual`

Authorize flow is following this Stripe doc page :
https://stripe.com/docs/payments/capture-later

## Get it started

First get [your credentials](../stripe-credentials.md) from Stripe dashboard.

> The following example is the basic Payum implementation
> (see [documentation of Payum](https://github.com/Payum/Payum/blob/master/docs/get-it-started.md) for more information)
Starting with the configuration of a normal payment, we will have to make some modification on the
`prepare.php` file to generate an `AuthorizeToken`. Then we will trigger the `Authorize` request in an `authorize.php` file.

### prepare.php

```php
<?php

declare(strict_types=1);

include __DIR__.'/config.php';

use Payum\Core\Model\Payment;

$gatewayName = 'stripe_checkout_session';

$storage = $payum->getStorage(Payment::class);

/** @var Payment $payment */
$payment = $storage->create();
$payment->setNumber(uniqid());
$payment->setCurrencyCode('EUR');
$payment->setTotalAmount(123); // 1.23 EUR
$payment->setDescription('A description');
$payment->setClientId('anId');
$payment->setClientEmail('[email protected]');
$payment->setDetails([]);

$storage->update($payment);

$tokenFactory = $payum->getTokenFactory();
$captureToken = $tokenFactory->createAuthorizeToken($gatewayName, $payment, 'done.php');

header("Location: ".$captureToken->getTargetUrl());
```

### authorize.php

```php
<?php

declare(strict_types=1);

use Payum\Core\Reply\HttpResponse;
use Payum\Core\Request\Authorize;
use Payum\Core\Reply\HttpRedirect;

include __DIR__.'/config.php';

$token = $payum->getHttpRequestVerifier()->verify($_REQUEST);
$gateway = $payum->getGateway($token->getGatewayName());

if ($reply = $gateway->execute(new Authorize($token), true)) {
if ($reply instanceof HttpRedirect) {
header("Location: ".$reply->getUrl());
die();
}
if ($reply instanceof HttpResponse) {
echo $reply->getContent();
die();
}

throw new \LogicException('Unsupported reply', null, $reply);
}

$payum->getHttpRequestVerifier()->invalidate($token);

header("Location: ".$token->getAfterUrl());
```

## Finally, capture the authorized payment

Complete the content of [`done.php`](payment.md#donephp) with those lines at the end of the file :

```php
if ($status->getValue() === $status::STATUS_AUTHORIZED) {
$tokenFactory = $payum->getTokenFactory();
$token = $tokenFactory->createCaptureToken($gatewayName, $payment, 'done.php');
echo '<a href="'.$token->getTargetUrl().'">Capture</a>';
}
```



Loading

0 comments on commit 88d912d

Please sign in to comment.