I have created a simple extension in EE to handle PayPal payments on submission of a Channel Form, which seems to be working up to a point.

My problem is how do I get the extension to handle the response after the return from PayPal?

My send parameters need to include a 'returnURL' which means that PayPal redirects to this page on successful completion - obviously this means the function in the extension is no longer in play!

Here's the logic in the extension:

    $response = $gateway->purchase($args)->send();

if ($response->isSuccessful()) {
    // do something with the response

} elseif ($response->isRedirect()) {
         // redirect to offsite payment gateway

} else {
         // payment failed: display message to customer
         echo $response->getMessage();

The issue is that on returning from the redirect to the offsite gateway,the 'isSuccessful' branch doesn't get executed, I am just redirected to the page specified in the 'returnURL' parameter sent with the initial request.

How do I process the return from PayPal so I can send the completePurchase() request within the extension?

EDIT I've just had a thought - should I make the returnURL an '?ACT' and add an entry to the actions table to call the next function in the extension?

... I'll try it


Except you can't use actions with extensions, only modules. And you can't use hooks in modules, only extensions. As I need to hook into the form submit AND use an action to process the return, it' back to the drawing board :-(

  • Thanks - I had a feeling I should have posted it there. However, I have just resolved the issue myself anyway. It seems I needed to create both an extension and a module, then use an action id to redirect to a method in the module. I can post code if anyone is interested in the solution Commented Feb 5, 2014 at 20:34

2 Answers 2


So, the answer was, after all, to create both an extension and a module within the same add-on folder. Extension to hook into the form submission, then a module to process the paypal payment. I created an action id to refer to the method within the module, and then used that as the returnURL

$q  = ee() -> db -> select('action_id') -> from('actions') -> where(array('method' => 'complete_purchase','class' => 'Process_payment'))->get();
$action_id = $q -> row('action_id');    $args['cancelUrl'] =  ee() -> functions -> fetch_site_index(true) . 'clients/_cancel';
$args['returnUrl'] =  ee() -> functions -> fetch_site_index(true) . '?ACT='.$action_id;

etc etc

I can post more detailed information if anyone else is stuck on this

  • Hi Martyn, nice solutiom! Would be nice to post more info please.
    – Stéphane
    Commented Sep 25, 2018 at 3:51

Yes, with ExpressionEngine you often need to create both an extension and a module. The difference is rather superficial and ideally they would all be the same thing, but that's what we have to work with for now.

You are correct in assuming you need an action URL to deal with this. Make a record of the amount which was to be paid somewhere in the database (if you are using channel entries then this will already be done for you), then create an ACT URL which will allow you to identify the payment. For example:

$url = ee()->functions->fetch_site_index().'?ACT='.$action_id.'&entry_id='.$entry_id;

If you are using EE 2.7+, make sure you set the csrf_exempt database column to 1 in the exp_actions table for your action as well (not explicitly needed for Paypal, but other off-site gateways often POST to your action which triggers EE's CSRF protection.

In your module action, simply load up Omnipay and call $gateway->completePurchase($params), which will check for the correct query string parameters and confirm the transaction with PayPal.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.