eventespresso / event-espresso-core

Event Espresso 4 Core for WordPress: Build an Event Ticketing Website Today!
https://eventespresso.com
GNU General Public License v3.0
121 stars 87 forks source link

handle_payment_update method is not called #377

Closed archergod closed 6 years ago

archergod commented 6 years ago

I am writing a new payment gateway which is quite similar to Paypal Express. I read through documentation on this Repo for how to write handle_payment_update and Offsite payment. But I cannot get it working. I am using return_url as My payment gateway do not use separate IPN, in fact it doesn't have IPN, it just return the User back to site and then using Transaction ID we have to make a Curl request to their server and verify if payment is done. So they send a $return_url with added parameters.

So, I was expecting to get handle_payment_update called like Paypal express and update transaction status in there, but seems since it is GET request to our server handle_payment_update is not been called.

Basically, I get payment done, but since handle_payment_update is not called on return I am not able to get status updated. I am using Mockup offsite code sample and modify it as needed. Can you suggest possible reason why handle_payment_update is not called?

archergod commented 6 years ago

I just retest the Mockup Offsite. if return URL is send back as GET and IPN separate = false, then handler_payment_update doesn't get called. Is there any workaround for it.

mnelson4 commented 6 years ago

hi @archergod, I just tried to reproduce your issue using the Mock Offsite payment method, and I think I was able to. For example, if I change pretend_offsite_page.php https://github.com/eventespresso/event-espresso-core/blob/master/tests/mocks/addons/new-payment-method/payment_methods/New_Payment_Method_Offsite/pretend_offsite_page.php so the form's method is GET, I get an error when the form is submitted, but it works if I leave the form's method as POST. I'm pretty sure that's basically your issue too, right?

If so, the reason that error is coming up is because when a browser submits a form using GET, the querystring in the form's action attribute is overwritten by the form's inputs. Eg, when I'm testing from a site at https://ee71-develop.test/, then on pretend_offsite_page.php the form's action is https://ee71-develop.test/registration-checkout/?uts=1525278485&step=payment_options&action=process_gateway_response&selected_method_of_payment=new_payment_method_offsite&spco_txn=35#checkout, and the status input is set to PAP and the gateway_txn_id is set to 991596. When the form's method is POST, the form is submitted to the form action's URL, with the status and gateway_txn_id being put into the request body. So it works fine. But if the form's method is GET, then the form's submission is actually sent to https://ee71-develop.test/registration-checkout?status=PAP&gateway_txn_id=991596/, because the form action's querystring was overwritten with the form contents. And so when EE receives that form submission, it doesn't see a bunch of needed querystring variables, like uts and spco_txn, etc. And so EE has an error.

You can resolve the error by parsing the $return_url you were provided, and instruct the payment gateway to add $return_url's querystring variables to the URL the user is returned to. Most gateways allow you to specify parameters that should be inserted into the URL the user is returned to. Does that make sense? And when you return from the gateway what URL are you taken to? Also, what gateway is it you're working with?

mnelson4 commented 6 years ago

For example, if you rewrite pretend_offsite_page.php to this, it works even when you set the form's method to GET:

<h1>Welcome to the Pretend Offsite Page.</h1>
<p>This page is actually hosted locally, just not accessed through Wordpress (it's a static php file).</p>
<p>If you are making your own offsite payment gateway integration you obviously dont need this file and should delete it.</p>
<?php
    $args = array();
    if( $_REQUEST[ 'uses_separate_IPN_request' ] ) {
    //they setup the payment method to use separate IPNs, so allow them to send an IPN and then return separately
    $form_url = $_REQUEST[ 'ipn_url'];
    $return_url = $_REQUEST[ 'return_url' ];

} else {
    //they setup the payment to NOT use separate IPN, so just provide the option to send payment data and return together
    $form_url = $return_url = $_REQUEST[ 'return_url' ];
        //<-----NEW
    $url_info = parse_url ($form_url);
    $form_url = $url_info['scheme'] . '://' . $url_info['host'] . '/' . $url_info['path'];
    parse_str($url_info['query'], $args);
}
?>
<form method="GET" action="<?php echo $form_url;?>">
    <label for='status'>Payment Status</label>
         <!-- NEW -->
    <?php foreach($args as $arg_name => $arg_value) {
        ?>
        <input type="hidden" name="<?php echo $arg_name;?>" value="<?php echo $arg_value;?>">
    <?php
    }
    ?>
    <select id='status' name='status'>
        <option value='PAP'>Approved</option>
        <option value='PPN'>Pending</option>
        <option value='PCN'>Cancelled</option>
        <option value='PDC'>Declined</option>
        <option value='PFL'>Failed</option>
    </select><br>
    <input id='gateway_txn_id' name='gateway_txn_id' type='hidden' value='<?php echo $_REQUEST['gateway_txn_id'];?>'><br>
    <?php if( $_REQUEST[ 'uses_separate_IPN_request' ] ) {?>
    <button>Send IPN Now</button> (then press back in your browser and return to this page, and then <a href='<?php echo $return_url;?>'>Return to Event Espresso to view your transaction</a>)
    <?php } else { ?>
        <button>Make payment and return to Event Espresso</button>
    <?php } ?>
</form>
archergod commented 6 years ago

Hello, thanks your solution is good. However, the payment gateway I am using (and mode I was using) do not allow to control those aspects, it has one ID that is token for payment verification. I am using Truevo Payment's Widget method. In this method they provide the JS and then it turn a FORM element to actual form (as if Onsite), but then when user fill the form it goes to their site, and then they return back to FORM action. Which is kind a Offsite structure. Now, when I use OnSite, I cannot capture call back as do_direct_payment will get called which cannot because it is offsite, and in Offsite since I cannot control GET parameter it won't work.

Luckily I get it solved by using their OnSite Server to Server method, but my client want to use Widget method which cannot be done with your API as I see it. Maybe I am wrong. If you want to experiment Truevo provide free account within their Documentation and you can experiement to write widget code for it... Which is Mix of OnSite and OffSite world.

Thanks for your help and such a great plugin again.

mnelson4 commented 6 years ago

Offsite since I cannot control GET parameter it won't work.

Ya that's the problem, Event Espresso needs some of those querystring parameters to be set in order to know which transaction the payment should be attributed to.

A workaround would be to use WordPress' URL Rewrite API to add a special redirection page that would act as a go-between Truevo and Event Espresso. Instead of having users return to the $return_url our code provides, which includes querystring variables that get ignored, you should send users to this redirection page, and that page could then forward the user onto our original $return_url.

The redirection page could have a URL structure like this: http://mysite.com/truevo_redirect/{uts}/{step}/{action}/{selected_method_of_payment}/{spco_txn}. When generating the exact URL, you'd parse the querystring from $return_url, and put each querystring variable into the correct spot in the redirection page's path. And when users arrive at the redirection page, you'd take all the path variables, combine them with the querystring variables Truepay set, and forward the user onto the $return_url's path, and set the querystring variables to be the redirection page's path variables and redirection page's querystring variables.

Here's a more step-by-step approach:

EE_Truevo_Gateway::set_redirection_info(...$return_url){
    $return_url_parsed =  parse_url ($return_url);
    $args = parse_str($return_url_parsed ['query'])
    $redirection_url = "truevo_redirection/{$args['uts']}/{$args['action']}/{$args['selected_method_of_payment']}/{$args['spco_txn']}";
    //then instead of telling Truevo to send the user to $return_url, tell them to send the user to $redirection_url
}

And user URL rewrite to rewrite a URL like http://mysite.com/truevo_redirect/{uts}/{step}/{action}/{selected_method_of_payment}/{spco_txn} into http://mysite.com/?truevo_redirect=true&uts={uts}&step={step}&action={action}&selected_method_of_payment={selected_method_of_payment}&spco_txn={spco_txn}.

And lastly, have some code that checks for isset($_GET['truevo_redirect']), and if so, redirect the user to http://mysite.com/registration-checkout/?uts={uts}&step={step}&action={action}&selected_method_of_payment={selected_method_of_payment}&spco_txn={spco_txn}.

From there, everything will be normal. Does that make sense?

webpetal commented 6 years ago

Dude, I must say I become your fan. One don't often meet a programmer how gave such a good answer to support question. I have seen great solution been left out because their team give rude answer. But you are amazing. Plus your solution completely make sense and I cannot think of it in years. Thanks.

mnelson4 commented 6 years ago

thanks @truearrowsoftware! We'd be happy to get a nice review like that on https://wordpress.org/support/plugin/event-espresso-decaf/reviews/ too

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

mnelson4 commented 6 years ago

I think this issue is resolved. Thanks stalebot, you're so on top of things.