Forum

Thread tagged as: Question, Problem, Shop

Yes, I tried multiple times. When I add:

<?php

class PerchShopGateway_mollie extends PerchShopGateway_default
{

On the bottom of default.class.php it does overwrite, but when in the seperate file it won't.

Also Perch returns the orders as failed, even though the orders are successful (On my Mollie account they show as successful). I suppose this is because I have to overwrite some methods from the default gateway class, right?

Drew McLellan

Drew McLellan 2638 points
Perch Support

On the bottom of default.class.php it does overwrite, but when in the seperate file it won't.

That sounds like an issue with how you're loading the classes. I don't think it's a Perch issue.

Do you think that is also the reason Perch doesn't see the payment as successful?

Drew McLellan

Drew McLellan 2638 points
Perch Support

I think you need to make sure PHP is using your class before we can even speculate as to that.

Okay, I am really stuck at this point.

In /perch_shop/lib/ there is a 'vendor' map with some gateways. When installing omnipay mollie via composer, I also got a 'vendor' map in the root.

From which vendor folder do I need to call the autoload.php?

I am now using:

<?php

include($_SERVER['DOCUMENT_ROOT'].'/vendor/autoload.php');

When I include the autoload from /perch_shop/lib I get an error, so I don't think I have to use that one:

Fatal error: Uncaught Omnipay\Common\Exception\RuntimeException: Class '\Omnipay\Mollie\Gateway' not found in C:\xampp\htdocs\kevcel\perch\addons\apps\perch_shop\lib\vendor\omnipay\common\src\Omnipay\Common\GatewayFactory.php:105 Stack trace: #0 [internal function]: Omnipay\Common\GatewayFactory->create('\\Omnipay\\Mollie...') #1 C:\xampp\htdocs\kevcel\perch\addons\apps\perch_shop\lib\vendor\omnipay\common\src\Omnipay\Omnipay.php(115): call_user_func_array(Array, Array) #2 C:\xampp\htdocs\kevcel\perch\addons\apps\perch_shop\lib\gateways\PerchShopGateway_default.class.php(213): Omnipay\Omnipay::__callStatic('create', Array) #3 C:\xampp\htdocs\kevcel\perch\addons\apps\perch_shop\lib\gateways\PerchShopGateway_default.class.php(51): PerchShopGateway_default->get_omnipay_instance() #4 C:\xampp\htdocs\kevcel\perch\addons\apps\perch_shop\lib\PerchShop_Order.class.php(90): PerchShopGateway_default->take_payment(Object(PerchShop_Order), Array) #5 C:\xampp\htdocs\kevcel\perch\addons\apps\perch_shop\lib\PerchShop_Runtime.class.php(6 in C:\xampp\htdocs\kevcel\perch\addons\apps\perch_shop\lib\vendor\omnipay\common\src\Omnipay\Common\GatewayFactory.php on line 105
Drew McLellan

Drew McLellan 2638 points
Perch Support

You don't need to mess with anything in /perch_shop/lib/

If you've added your own vendor folder, make sure you include its autoloader.

My gateway class is now overwriting the default gateway class!

Autoloader root/vendor/autoload.php:

<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

include($_SERVER['DOCUMENT_ROOT'].'/perch/addons/apps/mollie/PerchShopGateway_mollie.class.php');

return ComposerAutoloaderInit3b93c4eebb3022b2c6e7fec60fbce1a6::getLoader();

Runtime root/perch/addons/apps/mollie/runtime.php:

<?php

require($_SERVER['DOCUMENT_ROOT'].'/vendor/autoload.php');

use Omnipay\Mollie\Gateway;

Gateway class Mollie root/perch/addons/apps/mollie/PerchShopGateway_mollie.class.php:

<?php

class PerchShopGateway_mollie extends PerchShopGateway_default
{
    protected $slug = 'mollie';
    public $omnipay_name = 'Mollie';

#######

Now my final and last problem (I hope) is that Perch doesn't redirect me to the cancel_url when cancelling a payment, and that Perch is not returning a payment as successful when Mollie says it is.

Mollie is using a webhook URL to check the status of a payment. For that to work I need to add:

        $payment_opts = [
                'amount'        => $Order->orderTotal(),
                'currency'      => $Order->get_currency_code(),
                'transactionId' => $Order->id(),
                'clientIp'      => PerchUtil::get_client_ip(),
                'description'   => 'Item #'.$Order->id(),
                "return_url"  => "#",
                "webhookUrl"   => "#",
            ];

But I think Perch is not seeing 'webhookUrl' as a valid URL. Just like returnUrl has to be return_url. How could I fix this? (Or am I even looking in the right direction)

I was looking in the database and found all the orders. They all have the status 'created'. When changing an order to 'paid' in the database, it is showing in the Orders app, with all the information that I need. So, my only problem now is that the status is not sent back to Perch.

Mollie docs: https://www.mollie.com/nl/docs/webhook

require_once 'Mollie/API/Autoloader.php';

$mollie = new Mollie_API_Client;
$mollie->setApiKey('test_dHar4XY7LxsDOtmnkVtjNVWXLSlXsM');

$payment    = $mollie->payments->get($_POST["id"]);

/*
 * The order ID saved in the payment can be used to load the order and update it's
 * status
 */
$order_id = $payment->metadata->order_id;

if ($payment->isPaid())
{
    /*
     * At this point you'd probably want to start the process of delivering the product
     * to the customer.
     */
}
elseif (! $payment->isOpen())
{
    /*
     * The payment isn't paid and isn't open anymore. We can assume it was aborted.
     */
}

Mollie uses a webhook to check and send back the status of the order. In this code they use get($_POST["id"]); to get the id of the order. In the database I see that this id also gets sent to Perch, as orderGatewayRef().

The webhook is requested with a webhookUrl like this: (https://www.mollie.com/nl/docs/reference/payments/create)

require_once 'Mollie/API/Autoloader.php';

$mollie = new Mollie_API_Client;
$mollie->setApiKey('test_dHar4XY7LxsDOtmnkVtjNVWXLSlXsM');

try
{
    $payment = $mollie->payments->create(
        array(
            'amount'      => 10.00,
            'description' => 'My first payment',
            'redirectUrl' => 'https://webshop.example.org/order/12345/',
            'webhookUrl'  => 'https://webshop.example.org/payments/webhook/',
            'metadata'    => array(
                'order_id' => '12345'
            )
        )
    );

    /*
     * Send the customer off to complete the payment.
     * This request should always be a GET, thus we enforce 303 http response code
     */
    header("Location: " . $payment->getPaymentUrl(), true, 303);
    exit;
}
catch (Mollie_API_Exception $e)
{
    echo "API call failed: " . htmlspecialchars($e->getMessage());
    echo " on field " . htmlspecialchars($e->getField());
}

Could you please help me with this last piece of the puzzle?

I found CompletePurchaseRequestTest.php in the omnipay mollie folder, maybe you know if it is of any help: (The 'tr_Qzin4iTWrU' code is the order id, which I can see for every order in the database (orderGatewayRef())

<?php

namespace Omnipay\Mollie\Message;

use Omnipay\Tests\TestCase;

class CompletePurchaseRequestTest extends TestCase
{
    /**
     * @var \Omnipay\Mollie\Message\CompletePurchaseRequest
     */
    protected $request;

    public function setUp()
    {
        $this->request = new CompletePurchaseRequest($this->getHttpClient(), $this->getHttpRequest());
        $this->request->initialize(array(
            'apiKey' => 'mykey',
        ));

        $this->getHttpRequest()->request->replace(array(
            'id' => 'tr_Qzin4iTWrU',
        ));
    }

    /**
     * @expectedException \Omnipay\Common\Exception\InvalidRequestException
     * @expectedExceptionMessage The transactionReference parameter is required
     */
    public function testGetDataWithoutIDParameter()
    {
        $this->getHttpRequest()->request->remove('id');

        $data = $this->request->getData();

        $this->assertEmpty($data);
    }

    public function testGetData()
    {
        $data = $this->request->getData();

        $this->assertSame("tr_Qzin4iTWrU", $data['id']);
        $this->assertCount(1, $data);
    }

    public function testSendSuccess()
    {
        $this->setMockHttpResponse('CompletePurchaseSuccess.txt');
        $response = $this->request->send();

        $this->assertInstanceOf('Omnipay\Mollie\Message\CompletePurchaseResponse', $response);
        $this->assertTrue($response->isSuccessful());
        $this->assertFalse($response->isOpen());
        $this->assertTrue($response->isPaid());
        $this->assertFalse($response->isRedirect());
        $this->assertSame('tr_Qzin4iTWrU', $response->getTransactionReference());
    }

    public function testSendExpired()
    {
        $this->setMockHttpResponse('CompletePurchaseExpired.txt');
        $response = $this->request->send();

        $this->assertInstanceOf('Omnipay\Mollie\Message\CompletePurchaseResponse', $response);
        $this->assertFalse($response->isSuccessful());
        $this->assertFalse($response->isPaid());
        $this->assertTrue($response->isExpired());
        $this->assertFalse($response->isRedirect());
        $this->assertSame('tr_Qzin4iTWrU', $response->getTransactionReference());
    }

    public function testSendFailure()
    {
        $this->setMockHttpResponse('PurchaseFailure.txt');
        $response = $this->request->send();

        $this->assertInstanceOf('Omnipay\Mollie\Message\CompletePurchaseResponse', $response);
        $this->assertFalse($response->isSuccessful());
        $this->assertFalse($response->isRedirect());
        $this->assertNull($response->getTransactionReference());
        $this->assertSame("The issuer is invalid", $response->getMessage());
    }
}
Drew McLellan

Drew McLellan 2638 points
Perch Support

What are you expecting me to do here? I'm not sure how I can help you.

I want to know how to change the status from 'created' to 'paid'

Drew McLellan

Drew McLellan 2638 points
Perch Support

Have you implemented action_payment_callback() in your gateway?

No I did not. I am now looking at the worldpay and sagepay action_payment_callback() functions, but I am not sure how to change them so it works with Mollie.