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

psd2 sca readiness of stripe gateway #804

Open
bruno-ds opened this issue Jun 6, 2019 · 26 comments
Open

psd2 sca readiness of stripe gateway #804

bruno-ds opened this issue Jun 6, 2019 · 26 comments

Comments

@bruno-ds
Copy link

bruno-ds commented Jun 6, 2019

Hello,

I think that at least Stripe gateway will require some work in order to be compatible with the upcoming PSD2 (source).

Do you plan to implement one of the new compatible gateway?
Since the time remaining is so short, would it be possible to communicate over a date?

Bonus question:
I suppose this is not the only gateway that would require some work.
Would it be possible to have a state of the art of the PSD2 readiness of your gateways?

@PyRowMan
Copy link

PyRowMan commented Jul 3, 2019

There is indeed something that can be done at least with stripe checkout;

Here is the documentation of the new stripe checkout api : https://stripe.com/docs/payments/checkout/server

It seems that it is now mandatory to provide an article to stripe. but it can be a virtual item.

I've managed to modify my working copy in order make a little proof of concept, and it seems to work without much modifications of the stripe code.

The important thing is only the requirements of "stripe/stripe-php" it would depends at least on the v6.29.0 of the package.

@bruno-ds
Copy link
Author

bruno-ds commented Jul 4, 2019

This would be astonishingly easy if it is confirmed to be supported by Stripe.
According to Stripe's "client side" documentation, the sku is mandatory, and the "virtual item" is not mentioned.

I immediately send an email to their support! Thank you!

@bruno-ds
Copy link
Author

bruno-ds commented Jul 8, 2019

According to Stripe's support team, the "Client integration" can't be used that way, only the "server integration":

Hi Bruno,
To accomplish what you are doing, use the Server integration quickstart for checkout. This will allow you to maintain a self-managed product catalog.

I then asked them to confirm that the Client integration is not compatible with self-managed product catalog:

Getting right to it, you are correct. Being that self-managed product catalogs tend to be more complex in terms of Checkout, the more technical Server integration path is properly suited for the job. The Client integration path is designed to be barebones, for simpler processing requests.

@PyRowMan : Since you linked the "server integration" I'm really curious about how you did your integration, if you're allowed to, could you please share your proof of concept?

@PyRowMan
Copy link

PyRowMan commented Jul 8, 2019

Hi @bruno-ds,

Here is the files of my Proof of concept; however you have to keep in mind that it's a proof concept, therefore, the code is not really pretty !

This is not fully functional because the return urls are not set, but it should not be that difficult to set and then catch them.
Don't forget to use the right version of strip/stripe-php it would depends at least on the v6.29.0 of the package.

// vendor/payum/payum/src/Payum/Stripe/Action/Api/ObtainTokenAction.php

use Stripe\Checkout\Session;
use Stripe\Stripe;

public function execute($request)
{
        /** @var $request ObtainToken */
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        if ($model['card']) {
            throw new LogicException('The token has already been set.');
        }

        $getHttpRequest = new GetHttpRequest();
        $this->gateway->execute($getHttpRequest);
        if ($getHttpRequest->method == 'POST' && isset($getHttpRequest->request['stripeToken'])) {
            $model['card'] = $getHttpRequest->request['stripeToken'];

            return;
        }
        Stripe::setApiKey($this->keys->getSecretKey());

        $session = Session::create([
            "success_url" => "https://example.com/success",
            "cancel_url" => "https://example.com/cancel",
            "payment_method_types" => ["card"],
            "line_items" => [[
                "name" => $model['description'],
//                "description" => $model['description'],
                "amount" => $model['amount'],
                "currency" => $model['currency'],
                "quantity" => 1
            ]]
        ]);
        $this->gateway->execute($renderTemplate = new RenderTemplate($this->templateName, array(
            'model' => $model,
            'publishable_key' => $this->keys->getPublishableKey(),
            'actionUrl' => $request->getToken() ? $request->getToken()->getTargetUrl() : null,
            "session_id" => $session->id
        )));

        throw new HttpResponse($renderTemplate->getResult());
}
{# vendor/payum/payum/src/Payum/Stripe/Resources/views/Action/obtain_checkout_token.html.twig #}
{% block payum_body %}
    {{ parent() }}
    <button id="stripe-button-el">Purchase</button>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script type="text/javascript" async=false defer=false src="https://js.stripe.com/v3/"></script>
    <script>
            $.getScript("https://js.stripe.com/v3/")
                .done(function (script, textStatus) {
                    var stripe = Stripe('{{ publishable_key }}');
                    stripe.redirectToCheckout({
                        // Make the id field from the Checkout Session creation API response
                        // available to this file, so you can provide it as parameter here
                        // instead of the CHECKOUT_SESSION_ID placeholder.
                        sessionId: '{{session_id}}'
                    }).then(function (result) {
                        // If `redirectToCheckout` fails due to a browser or network
                        // error, display the localized error message to your customer
                        // using `result.error.message`.
                    });
        });

    </script>

{% endblock %}

@bruno-ds
Copy link
Author

bruno-ds commented Jul 9, 2019

Thank you,
I've started a new gateway project : https://github.com/Combodo/CombodoPayumStripe
I should have a few days dedicated to move forward 🤞

@bruno-ds
Copy link
Author

bruno-ds commented Jul 15, 2019

please find my proposal here : https://github.com/Combodo/CombodoPayumStripe
attention, this is an early WIP, it requires two more services on the impementation side.

edit: code sample removed, because they are now maintained here : https://github.com/Combodo/CombodoPayumStripe/tree/master/doc

@bruno-ds
Copy link
Author

bruno-ds commented Jul 15, 2019

and of course, you have to create a webhook on stripe's side :
image

It should point to the route /payment/notify/unsafe/{gateway} (/payment/notify/unsafe/stripe_checkout_v3)

@trag-stripe
Copy link

Hi Payum contributors!

Chris here from Stripe checking in if you plan on supporting European customers once the SCA / PSD2 regulation comes into effect? (Strong Customer Authentication requirement)

We’ve written an overview and we can see in our system Payum plugin users are at risk of getting blocked by the EU's law starting enforcement September 14th https://stripe.com/payments/strong-customer-authentication

Please let me know your plans as customers are actively asking us for SCA-ready solutions.

Thanks,

Chris Traganos
Developer Relations @ Stripe

@bruno-ds
Copy link
Author

bruno-ds commented Aug 6, 2019

Hello @trag-stripe 🤗
As far as I know, the only gateway SCA ready is this unofficial one:
https://github.com/Combodo/CombodoPayumStripe

@PyRowMan and I are actively working on it.
If it is possible, I'd love to have your team review (or even contribute to) it!

@trag-stripe
Copy link

trag-stripe commented Aug 6, 2019 via email

@bruno-ds
Copy link
Author

bruno-ds commented Aug 6, 2019

Well,
The code has an MIT licence, so The Payum team has the liberty to merge it back, but I cannot speak for them.
@makasim : could you please state if you'd agree to do so?

PS: this is definitively not a fork, is is just a gateway (see it as an extension) that enable stripe checkout server v3.

@lolmx
Copy link

lolmx commented Aug 12, 2019

Hello, i currently have a fork i'm working on here.
Basically i just do same things but added an option to choose sca flow.
It lacks some independent tests still, but i'm already polishing the integration on my project.
If you have any suggestions, feel free to @ me or in the repo :)

Edit: i'll pr it once i'm satisfied with it :D

@arnofo
Copy link

arnofo commented Aug 26, 2019

Hello,

As the new European rules will be in effect starting from 14 September 2019.

Do you think there is any chance a new Payum release adressing this issue will be available in time ? Or should i start looking for another solution ?

Thank you very much.

@dzschille
Copy link

@arnofo you can test the fork of @lolmx over here. If it works he will open a PR here to merge it back.

Info for everyone who gets nervous if everything is ready in 10 days, here are some informations about delayed SCA enforcements made by the EU countries: https://support.stripe.com/questions/strong-customer-authentication-sca-enforcement-date

@lolmx
Copy link

lolmx commented Sep 4, 2019

I released it as of 1.6.

To easily test it, add in your composer.json

# composer.json
    // ...
    "repositories": [
        {
            "type": "git",
            "url": "https://github.com/lolmx/Stripe.git"
        }
    ],

Then you just have to composer require Payum/Stripe:"^1.6" and add sca_flow => true in your payum config, to begin using SCA payments :)

I'll pr back to this repository pretty soon, waiting for some feedback :)

Edit: don't forget to composer require stripe/stripe-php:"^6", they added new Intent apis in 6.9

@dzschille
Copy link

@lolmx that worked well in my app. I just added your steps and than i could successfully pay with Stripes regulatory test cards. Thanks!

@hschiebold
Copy link

@lolmx just tried to use your sca implementation but gettting error { ["message"]=> string(142) "A token may not be passed in as a PaymentMethod. Instead, use payment_method_data with type=card and card[token]=*********." ["param"]=> string(14) "payment_method" ["type"]=> string(21) "invalid_request_error" } } }

code snippet from my booking test action:

$storage = $this->getServiceLocator()->get('payum')->getStorage('Application\Model\PaymentDetails');
$details = $storage->create();
$details["amount"] = $total;
$details["currency"] = 'EUR';
$details["description"] = $booking->get('bid');
$storage->update($details);
$captureToken = $this->getServiceLocator()->get('payum.security.token_factory')->createCaptureToken(
'stripe', $details, 'square/booking/payment_done'
);

done action:

$token = $this->getServiceLocator()->get('payum.security.http_request_verifier')->verify($this);
$gateway = $this->getServiceLocator()->get('payum')->getGateway($token->getGatewayName());
$gateway->execute($status = new GetHumanStatus($token));

then status gives me the error

can somebody give me a hint what i'am doing wrong

regards Holger

@lolmx
Copy link

lolmx commented Sep 16, 2019

Hello @hschiebold !

Problem comes from your JS, you have to use new method stripe.createPaymentMethod

It tokenises the payment method where old createToken tokenised the card directly

@hschiebold
Copy link

@lolmx maybe my problem is that i try to use StripeCheckoutGatewayFactory only and then the referenced template obtain_checkout_token.html.twig with checkout.stripe.com/checkout.js in it is outdated? In StripeJsGatewayFactory the template obtain_js_token_for_strong_customer_authentication.html.twig is used with the newer js api js.stripe.com/v3/ The template for StripeCheckoutGatewayFactory should look like the one from @PyRowMan with the v3 api - am i right or do i missunderstood something? I'm not experienced with payum - only try to use it for a simple tennis court booking app for my tennis club. regards Holger

@Arvi89
Copy link

Arvi89 commented Sep 19, 2019

Hi!
It doesn't work for me either, I get the setPublishableKey is not a function, which makes sense as it has been removed for v3, so I don't understand how it can work for you guys ^^
https://stackoverflow.com/questions/49448364/stripe-setpublishablekey-is-not-a-function-error

@Sybio
Copy link

Sybio commented Sep 19, 2019

Same problem than @hschiebold :s

And the method used in vendor\payum\stripe\Payum\Stripe\Resources\views\Action\obtain_js_token_for_strong_customer_authentication.html.twig line 60 :

Stripe.createPaymentMethod('card', $form, stripeResponseHandler);

prepareAction :

public function stripePrepareAction(Request $request, $amount)
    {
        $amount *= 100;
        $gatewayName = 'stripe_checkout';
        $storage = $this->get('payum')->getStorage(Payment::class);

        $payment = $storage->create();
        $payment->setNumber(uniqid());
        $payment->setCurrencyCode('EUR');
        $payment->setTotalAmount($amount);
        $payment->setDescription('The product');

        $storage->update($payment);

        return $this->redirect($this->get('payum')->getTokenFactory()->createCaptureToken(
            $gatewayName,
            $payment,
            'fp.payment.stripe_execute'
        )->getTargetUrl());
    }

executeAction :

public function stripeExecuteAction(Request $request)
    {
        $token = $this->get('payum')->getHttpRequestVerifier()->verify($request);
        $gateway = $this->get('payum')->getGateway($token->getGatewayName());
        $gateway->execute($status = new GetHumanStatus($token));
        $this->get('payum')->getHttpRequestVerifier()->invalidate($token);
        $identity = $token->getDetails();
        $payumPayment = $this->get('payum')->getStorage($identity->getClass())->find($identity);

        if ($status->isCaptured() || $status->isAuthorized()) { // Success
            $this->addFlash(
                'message.success',
                'Success'
            );
        } elseif ($status->isPending()) { // Pending
            $this->addFlash(
                'message.fail',
                'Waiting approbation.'
            );
        } else { // Failed
            $this->addFlash(
                'message.fail',
                'Payment rejected.'
            );
        }

        return $this->redirectToRoute('fp.payment.ended');
    }

stripe_pay.html.twig :

<form action="{{ actionUrl|default('') }}" method="POST" class="payment-form">
                        <script
                            src="https://checkout.stripe.com/checkout.js" class="stripe-button"
                            data-key="{{ stripePublicKey }}"
                            data-image="{{ asset('img/paiement/logo-150x150.jpg') }}"
                            data-name="mywebsite.com"
                            data-description="{{ model.description|default("") }}"
                            data-amount="{{ model.amount }}"
                            data-currency="{{ model.currency|default("EUR") }}"
                            data-locale="fr"
                            data-label="Payer par carte bancaire - {{ model.amount / 100}}€"
                        >
                        </script>
                    </form>

My config :

payum:
    security:
        token_storage:
            AppBundle\Entity\PaymentToken: { doctrine: orm }
    storages:
        AppBundle\Entity\Payment: { doctrine: orm }
    gateways:
        stripe_checkout:
            factory: stripe_checkout
            publishable_key: %stripe_public_key%
            secret_key: %stripe_secret_key%
            sca_flow: true
            payum.template.obtain_token: "front/payment/stripe_pay.html.twig"

Versions used :

  • payum/core : 1.5.1
  • payum/payum-bundle : 2.2.1
  • payum/stripe : 1.6.1

@lolmx
Copy link

lolmx commented Sep 21, 2019

@hschiebold @Arvi89 @Sybio
Indeed, I forget to update the default template to use the new method stripe.createPaymentMethod, it's working for us because we override the template used with a custom one with said method.

I've just merged a fix to update the default template.
If you can try again using master branch it should be good now. If all's good, i'll release a new patch version, let me know.

If you have other problems can you open issues on the fork please? It'll be easier for me to not forgot some comments :)

@lolmx
Copy link

lolmx commented Sep 21, 2019

Here i'm back!
I would need some feedbacks about stripe version support.
I opened a new discussion, any input is greatly appreciated 👍

@francesco-laricchia
Copy link

Any update on this topic? Lolmx fork is still the most recommended one?

@bruno-ds
Copy link
Author

Any update on this topic? Lolmx fork is still the most recommended one?

Personally, I use mine in production : https://packagist.org/packages/combodo/stripe-v3
From what I know it is used by some other Sylius stores and by raw Symfony projects.

@Prometee
Copy link

Prometee commented Apr 16, 2020

For those who need a gateway using Stripe Session Checkout I made a bundle and a Sylius plugin to use it :
https://github.com/Prometee/PayumStripeCheckoutSessionBundle
https://github.com/Prometee/SyliusPayumStripeCheckoutSessionPlugin

All based on the lib : https://github.com/Prometee/PayumStripe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests