thephpleague / omnipay-sagepay

Sage Pay driver for the Omnipay PHP payment processing library
MIT License
55 stars 78 forks source link

Form Authorize #146

Closed CristianEnache closed 4 years ago

CristianEnache commented 4 years ago

I'm having issues implementing the Form method.

$response = $gateway->authorize([
    ...all the normal details...
    //
    'returnUrl' => 'https://example.com/success',
    'failureUrl' => 'https://example.com/failure',
]);

Under Form Authorize, the documentation here: https://github.com/thephpleague/omnipay-sagepay#sage-pay-form-methods says:

The $response will be a POST redirect, which will take the user to the gateway. At the gateway, the user will authenticate or authorise their credit card, perform any 3D Secure actions that may be requested, then will return to the merchant site.

I can see when I'm debugging the application that the $response is an AuthorizeRequest object. I have to build the post redirect form myself and submit it, and it takes me to https://test.sagepay.com/gateway/service/vspform-register.vsp with the error

Status Detail: | 5080 : Form transaction registration failed.

This is my complete test code, for reference:

$transaction_id = self::generateRandomString(32);

        $gateway = OmniPay::create('SagePay\Form')->initialize([
            'vendor' => 'thevendorname',
            'testMode' => true,
            'encryptionKey' => 'key93de04f6b693d',
            'disableUtf8Decode' => true,
        ]);

        $card = new CreditCard([
            'name' => 'Joe Bloggs',
            'firstName' => 'Joe',
            'lastName' => 'Bloggs',
            'billingAddress1' => 'Billing Address 1',
            'billingAddress2' => 'Billing Address 2',
            'billingState' => '',
            'billingCity' => 'Billing City',
            'billingPostcode' => 'BPOSTC',
            'billingCountry' => 'GB',
            'billingPhone' => '01234 567 890',
            'email' =>  'test@example.com',
            'shippingAddress1' => '99',
            'shippingState' => 'NY',
            'shippingCity' => 'City1',
            'shippingPostcode' => 'SPOSTC',
            'shippingCountry' => 'US',
            'shippingPhone' => '01234 567 890 SS'
        ]);

        $data = [
            'CustomerEmail' => 'eni222@yahoo.com',
            'amount' => '29.91',
            'currency' => 'GBP',
            'description' => 'Mandatory description',
            'returnUrl' => 'https://mysite.co.uk/home/payment_status',
            'failureUrl' => 'https://mysite.co.uk/home/payment_status',
            'transactionId' => $transaction_id,
            'card' => $card
        ];

        $response = $gateway->authorize($data);

        $data = $response->getData();

        $payment_url = 'https://test.sagepay.com/gateway/service/vspform-register.vsp?' . urldecode(http_build_query($data));

        return response()->json(array('result' => $payment_url));

This code above is running on a microservice which is separate from the main site. I then return the $payment_url to the main site where the url is parsed and relevant parameters are extracted from it, then placed into a form and the form gets submitted:

/**
     * Redirect Payment Gateway
     * @param $url
     *
     * Renders javascript which generates a form and form fields.
     * Then submits the form.
     *
     */
    public function redirect_payment_gateway($url) { ?>

        <body></body>

        <script type="text/javascript">

            var res = '<?php echo $url; ?>';
            var resA = res.split("&");
            var urlA = resA[0].split("?");

            var url = urlA[0];
            var VPSProtocol = urlA[1].split("=")[1];
            var TxType = resA[1].split("=")[1];
            var Vendor = resA[2].split("=")[1];
            var Crypt = resA[3].split("=")[1];

            var form = document.createElement("form");

            var element1 = document.createElement("input");
            var element2 = document.createElement("input");
            var element3 = document.createElement("input");
            var element4 = document.createElement("input");

            form.method = "POST";
            form.action = url;

            element1.name = "VPSProtocol";
            element1.value = VPSProtocol;
            form.appendChild(element1);

            element2.name = "TxType";
            element2.value = TxType;
            form.appendChild(element2);

            element3.name = "Vendor";
            element3.value = Vendor;
            form.appendChild(element3);

            element4.name = "Crypt";
            element4.value = Crypt;
            form.appendChild(element4);

            document.body.appendChild(form);

            form.submit();

        </script>
        <?php
    }

This last chunk of code is part of the main site which I inherited from another developer so I'm trying to roll with what was already there. The main site was already processing payments via sagepay, only we lost access to the microservice which returned the $payment_url so all we need to do at this point is fix the first chunk of code running on the microservice so that it returns a url, and then the rest of the site should continue to work.

To recap, the issue I'm having is a Status Detail: | 5080 : Form transaction registration failed. error once we redirect, and that has left me stuck.

PS - the previous microservice was able to generate that url without the $card object being submitted. Is that still possible?

Thank you!


JDJ: Edited for formatting only.

judgej commented 4 years ago

I've been away for a few days, and only just seen this. I'll hopefully take a look through the details later today.

judgej commented 4 years ago

Okay, quick look, and you may have missed a simple step out. You do this:

$response = $gateway->authorize([...]); // wrong

And you say you get a request. That is correct. This is what you are really doing:

$request = $gateway->authorize([...]); // right

Subtle, I know :-) Now what do you do with the request that Omnipay gives you? You "send" it. That's what you do with all Omnipay requests, no matter what the driver, or what you expect to happen next.

$response = $request->send(); // the missing step

What you get back is an Omnipay RedirectResponseInterface object. It is that object which will have the redirect details in. To test it quickly, you can do a redirect like this:

$response->redirect();
exit;

That's a kludgy and messy way to redirect, but it is great while developing just to see it all working.

You should build your own redirect from the details:

$response->getRedirectUrl(); // Where to go.
$response->getRedirectMethod(); // How to go (post).
$response->getRedirectData(); // What to send; name=>value elements

The redirectData will contain all the parameters being posted, most of which will have been combined and encrypted for transport to the Sagepay site.

judgej commented 4 years ago

That inherited code is probably mostly not needed. It looks like it takes the url/method/data supplied by the server, adds a few more parameters, then POSTs it. I'm not sure why it does that. Everything you need to POST should be in the original generated url/method/data. I suspect I may be overlooking something there though.

In the end, Omnipay gives you a url, a method, and data, and you decide how to redirect the user's browser. That may be via a JavaScript POST, or could even be a form with hidden items and a single Pay Now button. Or a hidden form submitted automatically using JavaScript. They will all work fine.

alireza2281 commented 4 years ago

hello i using the code stated here to get a response to redirect to sagepage payment page but on the payment page there is a sentence How do you want to pay? but there is no payment method and just a cancel button! can you tell me what am i doing wrong? and by the way i noticed that the TxType is set to 'deferred' how can i change it to 'payment'?

judgej commented 4 years ago

@alireza2281 could you raise this on a new issue please. It's easier to keep each support request separate.

In the meantime, if you don't see any payment methods, then you will need to check you have payment methods set up in your Sage Pay account. The page should look something like this:

image

Closing this original issue. I'm assuming OP either fixed their problem or it is no longer relevant.

adriandmitroca commented 3 years ago

@alireza2281 have you found fix how to change deferred to payment maybe?

alireza2281 commented 3 years ago

@adriandmitroca i can't remember how exactly i fix it and unfortunately i don't have access to that code. but i remember something that i changed it, the way that the document said.