Search

Hi,

I should preface this question with the confession that I'm not well versed in POST stuff - nor particularly great at PHP. It's possible that I've bitten off more than I can chew with this current project! But hopefully I can work something out.

So my client runs an event and she wants people to be able to register and pay. After looking around I decided on Paymate as the easiest option.

My planned process was: use a basic form to send GET params to Paymate - have them process the payment - get confirmation back from Paymate which populates a form (attached to an event) on my site - Form saves data (including payment confirmation) to the database for my client to look at.

Everything is fine apart from I assuemd paymate would send back GET params but instead they are sending POST params. Annoyingly appending ?debug to the url to redirect to makes the paymate system throw and error and not redirect to anything!

And I have no idea how to grab the POST params and use them? After reading around I found this statement from Makenosound in this post which looked hopeful:

You can also use the Dynamic Event Redirect extension to redirect POST values to another Symphony-friendly URL.

Which looks hopeful. I already have Dynamic Event Redirect installed for something else but I can't find any more info on how I might make this work?

I also wondered about the new RestAPI from Nick but I have no idea how to make that work.

So I guess i need to know if what I want is possible with Symphony and the best approach to grab these POST params and use them? If I can just get their values to use in my forms or something like that I'd be away!

Thanks in advance for any help.

T

My planned process was: use a basic form to send GET params to Paymate - have them process the payment - get confirmation back from Paymate which populates a form (attached to an event) on my site - Form saves data (including payment confirmation) to the database for my client to look at.

I presume you mean that Paymate would redirect back to your site with GET params in the URL, you would populate a hidden form and use JavaScript to submit it, in order to get the data into Symphony?

The fact that Paymate use POST over GET is a blessing for two reasons:

  • the data is hidden and so harder to tamper with by the casual user
  • Symphony events ? POST

To figure this out fully you need to make a list of two things: the name of the POST variables coming from Paymate, and the name of the POST variables you want to save to Symphony. Let's assume the former will be things like order_id, status and amount, and the latter will be fields[order-ref], fields[status] and fields[order-total] (Symphony fields).

If you were submitting the latter three as a normal Symphony event you'd create three fields in HTML in the form:

<input name="fields[order-ref]" ... />

And then you'd add a submit button named after your event:

<input type="submit" name="action[save-order-details]" />

This form submits a POST request to the event attached to your page. The event accepts these POST variables and creates the Symphony entry. So to get Paymate to submit the event instead, you just need to customise your "Save Order Details" event to:

  1. trigger when Paymate sends to it (i.e. rather than triggering when the POST contains action[save-order-details] it will trigger when the POST contains order_id (or any variable name from Paymate)
  2. manipulate the POST array so that it eventually resembles what a Symphony event is expecting

It's easier than it sounds. Start by creating your event as usual (I'll assume you named it Save Order Details, then open up the PHP file in a text editor (/workspace/events/event.save_order_details.php).

First things first, change the allowEditorToParse function to return false to stop you editing the event in the backend accidentally.

Secondly modify the load function so that it calls _trigger when it finds a POST variable set from Paymate. Probably something like:

if(isset($_POST['order_id'])) return $this->__trigger();

Then in the __trigger function:

protected function __trigger(){

    // cache the original POST array then reset it
    $paymate = $_POST;
    $_POST = array();

    // map Paymate POST into Symphony fields
    $_POST['fields']['order-ref'] = $paymate['order_id'];
    $_POST['fields']['status'] = $paymate['status'];
    $_POST['fields']['order-total'] = $paymate['amount'];

    // redirect user on completion
    $_POST['redirect'] = '/order-confirmation/';

    include(TOOLKIT . '/events/event.section.php');
    return $result;
}

This first caches the POST array and resets it, just for everyone's sanity. Then it rebuilds the POST array in a form that a Symphony event expects (this is the same as submitting a form element named fields[foo-bar]. It then adds in a redirect variable so that you can push the user to a different page if successful.

The assumption I have made above is that Paymate actually redirects the user (in the browser) to this page after the payment is complete. This might be incorrect, and Paymate might operate like Paypal (and most others) and perform this POST as a hidden "callback" i.e. the user's browser remains on Paymate, but their servers make the request to you. If this is the case, there's no point in the redirect, and you might want to add an exit() at the end of the _trigger function, since there's no point in following through and rendering the whole HTML page just for Paymate's servers, a blank response would be enough.

Paymate might offer a debugging mode whereby they log the response from the callback and you can view it. If they don't, then I've found the easiest way to debug these is to write the response to a text file on your server as the script runs.

Hope this is close...

Nick! That's brilliant.

I knew that Symphony events use POST but really had no idea how to hijack them.

I'll need to have a play around but I can see how that would work. You're right in saying that Paymate actually sends the user back to my site (ie fully resolves the URL) rather than using a hidden call back. So this should work fine.

The only small hitch I can see is that I want the user to be able to confirm the details they entered on Paymate and also fill out a couple of extra fields that my client wants but that Paymate doesn't accept.

Which I why I was thinking of populating a form with the data because then they could check that what they've entered is correct, add the extra details and then click 'Confirm' to submit the event with the info from Paymate in hidden fields...

I was also hoping to use Email Template Filter at this point to send them a confirmation email by way of a receipt... this is sounding muckier all the time.

Is there any way to use your method above in combination with a Dynamic Event Redirect and/or the Email Template Filter. Or perhaps use the redirect you've mentioned above to go to a page where they can edit the record created if they want? Eg $_POST['redirect'] = '/order-confirmation/$paymate['order_id']';

I have a feeling that they way I've gone about this might be all arse backwards...

Thanks again for taking the time to help.

I just remembered the other reason I was hoping to get the POST data as params - There are two packs the user can buy when signing up. They entere a number of each that they want and I'm using JS to create a total.

Paymate doesn't support individual products so I was using the reference field so store a string which I could decode later using string functions (ie if there is product E and product B my string was 1E-3B.

Obvoiusly if I could save the data a symphony before sending it to Paymate that might help but not sure if that is the best approach. Obviously i'm rather confused!

If you want to change the redirect URL then there's no need for Dynamic Event Redirect as you're already in the realms of editing the PHP yourself:

$_POST['redirect'] = '/order-confirmation/' . $paymate['order_id'];

Obviously this has security implications, that a user might modify the order ID. Do you have any authentication? This can be solved by also implementing a hash signature. Add another text field to the section named Hash. In your event fill it's value as a hash of the form data (serialising the POST array into a string first):

$_POST['fields']['hash'] = sha1(serialize($_POST));

This creates a unique hash string of the entire POST array, it'll always be unique. Pass this along with the order ID in the URL:

/order-confirmation/:id/:hash/

And your Order data source would filter both on order ID the hash to get the order back. A hacker would to figure out a working combination of both order and hash to view an order without permission. This is security by obfuscation rather than security by authentication, but it good enough in most situations. If you need even more obfuscation, add a second hash:

$_POST['fields']['hash2'] = sha1(serialize($_POST) . time());

This one also adds in a timestamp to create a new unique value:

/order-confirmation/:id/:hash/:hash2

The URL won't be as pretty, but it'll be virtually impossible for a hacker to guess...

If you need even more obfuscation, add a second hash:

The advice you give on the hash is quite good, but I feel it might be solved even easier.

For instance, this would generate a hash that is based on both the $_POST array and a unique handle generated by the uniqid function. To "guess" this, the hacker would need to know the method used, and the time in microseconds the hash was generated, which - in most cases - is good enough.

$_POST['fields']['hash'] = sha1(serialize($_POST) . uniqid());

Yep that's a good point. My point about adding a second hash was more that it massively increases the combinations required (akin to having two passwords rather than one). But one more solid, salted hash is a simpler idea!

Howdy again,

Finding this extremely educational.

Ok - so I've mapped all the Paymate fields in my modified event and now, when I run a payment through Paymate the post values are saved perfectly into the database! Amazing - well to me anyway :)

I've used @creativedutchman's hash which is saving fine too.

The only thing that isn't working is the redirect. Despite the event saving fine to the database it's remaining on the page that is processing the event rather than redirecting to my confirmation page.

Here's the relevant part of my event:

public function load(){
        if(isset($_POST['transactionID'])) return $this->__trigger();       
        }

    protected function __trigger(){

        // cache the original POST array then reset it
        $paymate = $_POST;
        $_POST = array();

        // map Paymate POST into Symphony fields
        $_POST['fields']['first-name'] = $paymate['billingFirstName'];
        $_POST['fields']['surname'] = $paymate['billingSurname'];
        $_POST['fields']['email'] = $paymate['buyerEmail'];
        $_POST['fields']['street-address'] = $paymate['billingAddress1'];
        $_POST['fields']['suburb'] = $paymate['billingAddress2'];
        $_POST['fields']['city'] = $paymate['billingCity'];
        $_POST['fields']['post-code'] = $paymate['billingPostcode'];
        $_POST['fields']['ref'] = $paymate['ref'];
        $_POST['fields']['paymate-id'] = $paymate['transactionID'];
        $_POST['fields']['payment-status'] = $paymate['responseCode'];
        $_POST['fields']['payment-amount'] = $paymate['paymentAmount'];
        $_POST['fields']['hash'] = sha1(serialize($_POST) . uniqid());  

        // redirect user on completion
        $_POST['redirect'] = 'http://btcs.makecollective.co.nz/sign-up/confirmation/';

        include(TOOLKIT . '/events/event.section.php');
        return $result;
    }

I've switched to an absolute url (in case that was the trouble) and removed any params - but it doesn't seem to make any difference - it just stays on the page that processes the event.

Any ideas?

Also (and sorry for being so thick on this) - I'm still not 100% on how actually get the params I want, namely the hash and the paymate-id/transactionID, into the url?

Thanks again for the help - this is saving my bacon.

Getting the params in the URL is a matter of building the string:

    $_POST['redirect'] = 'http://btcs.makecollective.co.nz/sign-up/confirmation/' . $_POST['fields']['hash'] . '/';

I have no idea why it wouldn't be redirecting. My guess is that Paymate is doing a callback from its servers, as opposed to sending a POST on to your site through the browser (which can only be done with submitting an HTML form, not through server side scripting).

Other than that, I'm stumped.

Hmmm - nothing I do gets that redirect to work - so you must be right about it being a callback. The paymate documentation is awful so I can't find any info.

BUT I have a solution of sorts. Because the event is saving - I'm using the event post values to populate a JS redirect to the confirmation page with the right params in the url.

This works fine if I have a link but I've run into a problem which is that the params inside my javascript are not being evaluated (ie {$root} is showing up as {$root} in the url rather than being evaluated to http://.....

I've searched the forum but can't seem to find out why...

Here's the code from my page: http://d.pr/24yS

The link is working fine - the JS isn't. It redirects but doesn't evaluate any of the params or xpath

Looks like I needed to wait until the DOM was ready and then create a variable from the 'href' of the link to use in my javascript.

Now all working perfectly! Can anyone see any bad sides to doing things this way?

Cheers for all the help - very much appreciated.

Tim, can you post the full solution for us, I'm going to be getting into this in detail soon, and I think it would be great to add to the e-comms project I want to do shortly.

Hey John - yep for sure.

There are a couple of things I should say at the outset that I'm not 100% sure if they way I did it was best or not.

The main thing I wasn't sure on was whether to try and save all the users data before it gets sent to Paymate. For the sake of simplicty for my client I decided NOT to save the data before Paymate so that all entries created in the Symphony would relate to orders that had already been paid for. But, I think you could do it with Event Redirection so that the event saves first before sending the values off to Paymate - then when you come back you simply reference the same entry and upadate it with payment details...

BUT - how I did it was:

Make a basic form on a page (not connected to an event) with all the stuff needed to send to Paymate (ie all Paymate fields). Paymate Express uses a basic URL with GET params (https://www.paymate.com/PayMate/ExpressPayment?mid=USERNAME&amt=100.00&currency=USD etc) so you can use a combination of visible inputs and hidden inputs to populate the users data and your own payment credentials.

Paymate lets you enter a 'Return URL' which is where the user will be directed once they have completed Payment - Paymate POST the values from the transaction back to that URL.

So to get these saved in the Symphony back end you need to create a Section with all the fields you need plus a few extras - namely, Hash, Payment Status, Paymate ID. I called this section Sign-Up.

Because Symphony events use POST we can hijack the response from Paymate to trigger and event.

So create an event for the section above and attach it to a page (eg http://yoursite.com/response). Make sure that the return URL you specified in your form points to this page.

I then used the method suggested by Nick above to customise the event so that instead of being triggered by a user submit, it is triggered by the presence of some POST data (from paymate).

Then the fields from Paymate need to be mapped on to your own fields, Plus, to make things more secure, a hash created so you can reference the event using URL params without someone being able to guess the URL.

In the end my fully customised event looked like this - it's commented so should make some sense.

Lastly, as noted above - for some reason the event redirect didn't work as expected (ie not at all!) probably because there is a hidden call-back from Paymate. This might not be an issue for some people (ie if you had saved the data to Symphony before heading off to Paymate) but I wanted a few more fields filled in so needed to be able to update the saved entry via an event.

Fortunately, the Symphony event data is still available in the XML after the Post values from Paymate have been saved. So I simply used JS to redirect to my final page with the URL values from the post data.

eg

<xsl:template match="/">
&lt;script type="text/javascript"   src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js">&lt;/script>



<p>If you are not automatically redirected - please click <a id="redirect" href="{$root}/sign-up/confirmation/{/data/events/confirmation/post-values/paymate-id}/{/data/events/confirmation/post-values/hash}">here</a>.</p>

&lt;script>
$(document).ready(function() {
    var $url = $('#redirect').attr('href');
    window.location.replace($url);
});

&lt;/script>

</xsl:template>

The final page has two URL params which filter a Sign-Up data source based one Paymate ID and the Has (very hard to guess) which means I can update the Entry with an event (using a hidden field with the ID of the entry). I also used the Email Tempalte Filter at this point to send a customised email to the purchaser by way of receipt.

Hope that all makes some sense - let me know if you want me to elaborate some more on anything.

I think, if I was doing it again, I would probably look to save the data to the database first as then there would be no need for the cludgy redirect at the end - but this way does work fine.

I'm pretty sure Paymate has an API too so perhaps a Dynamic Datasource could be used to tap into this?

Cheers,

T

Also - another reason to save first - Paymate doesn't return any info about what the person has bought.

In my case there were only two options so I just used Paymate reference field to store a string that represented what the user had paid for.

So There were two packs, Essentials and Booster. People could order as many of each as they liked so I just put use Javascript to create a string. Ie if they bought 2 Essential packs and 1 booster the ref string would just read 2X1 - then I used XSLT string functions to split that back out into the 2 and 1 when the event saved.

Obvoiusly if you saved the original data before sending that off to Paymate you would need to do something so cludgy and hacky!

I like this, thanks!

I was planning on going down the route of chaining events to do this kind of thing, only theoretically, and loosely at that. But I like this method.

One question, would it be possible to spam the url you've set up to accept data? Does the transaction ID that is checked for get stored somewhere to match against?

Theoretically you'd be able to spam the page I've set up to receive data. As in you could just send all the POST data that comes from Paymate to the page from any old form I guess (hadn't thought of that!). They do publish the POST params so I guess it could happen.

I think the key would be to save the data before going to Paymate and then stash the hash as the reference field which you'd check one the way back to the site to pull the same record again. If it was doing it again that's what I'd do I think.

Thinking about above, what if the page was set to admin only and you provided the auth-token when submitting the post data? not a good idea maybe, as the auth-token would be readable right?

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details