mollie-api-php icon indicating copy to clipboard operation
mollie-api-php copied to clipboard

No way to set a cancel URL when creating a payment

Open robertotremonti opened this issue 8 years ago • 46 comments

When you create a payment, you need to set a redirectUrl parameter. Customer is redirected to that URL when purchase is completed. The same URL is used for Back to website button if you want to cancel payment process and go back to store. "Success" and "Cancel" URLs are different most of the times. How do I set a cancelUrl? If it's not possible I ask to add this functionality in next releases.

Thanks.

screen shot 2015-12-11 at 14 57 55

robertotremonti avatar Dec 11 '15 14:12 robertotremonti

Hello Roberto,

When a customer returns to your website (when the customer clicked "Back to the website"), your webhook gets called to inform your system that the customer has aborted the transaction.

In your webhook (or this example) you can specify what should happen when this happens:

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

Ricardo, Mollie

ghost avatar Dec 15 '15 15:12 ghost

Thanks, Ricardo, for your reply. I don't use a webhook. I think that a simple "CancelURL" could be introduced in next updates, like PayPal and other payment methods offer.

robertotremonti avatar Dec 15 '15 15:12 robertotremonti

Hello Roberto,

We are currently discussing this internally. Thank you for your report.

Ricardo, Mollie

ghost avatar Dec 15 '15 15:12 ghost

What's the status on this? It makes sense to have success and cancel URL's, all the payment gateways I've used have them. Since the webhook is called in the background, we're never 100% sure the payment status is updated when the user is redirected, or am I missing something?

veloxy avatar Apr 26 '16 14:04 veloxy

You can check the status of the payment with mollie on your return url (as well as the webhook) and redirect users accordingly to succes or failure pages. Payment processed isn't guaranteed on the return url. So you do to give need proper feedback in case payment is still pending, but in my experience it is usually already processed. As a design decision I understand it, return the customer asap instead of waiting for payment to fail/succeed and only then returning. It avoids delays and otherwise why use a webhook (which is pretty useful for the event driven / asynchronuous)?

whvandervelde avatar Apr 26 '16 20:04 whvandervelde

What's the status on this? I'm getting this problem of my customer, Hope you can update API soon

quannv avatar Jun 02 '16 05:06 quannv

Hello everybody,

We did not change anything to our API regarding this. To read what has changed, see the changelog here.

My suggestion is to both use a returnUrl and a webhookUrl to get the most out of our API. The function of a returnUrl is whenever the user returns to your website, the status can be checked (If you developed that into your returnUrl). For payments that later change (eg. refund, chargeback) or delayed payments, I'd really suggest using the webhookUrl to get a correct status for a payment.

More about the status in our documentation. Also take a look at the webhook documentation.

I hope this helps.

Ricardo, Mollie

ghost avatar Jun 02 '16 07:06 ghost

This makes it impossible to use only the metadata feature when creating the payment: You'd have to retain the payment id to show a different page depending on whether the payment was cancelled.

1) The open amount for your purchase is €100, click to pay.
2) Customer returns to redirectUrl
3) Determine to show 'Thanks!' or 'Try again' => We'll need a paymentID

leongersen avatar Oct 05 '16 16:10 leongersen

@leongersen It's not impossible without the webhook.

A payment is usually linked to a parent model (or object), which has its own identifier.

You can save the combination of the new payment object's id and the parent's identifier in a payments logging table and use its identifier. (eg. PaymentAttempts {'unique_id', 'model', 'model_id', 'payment_id', 'created', 'updated'})

This would need 2 queries:

  1. Before requesting a new payment:
    • create a payment log
    • grabbing its identifier (unique_id)
    • appending/including the identifier to the redirectUrl
  2. After the payment request:
    • saving the payment id to the previously generated log identifier
    • redirecting to the paymentUrl

When a page hit occurs on the dynamic page defined in the redirectUrl, after grabbing the log's identifier from the request, you can identify the payment being made. Which now results in:

3) Determine to show 'Thanks!' or 'Try again' by:
- grabbing the payment identifier from url/request (or fail in some way, I'd suggest 418 Teapot, jk..)
- grabbing the logged payment data we added before.
- collecting payment data from Mollie
- (updating a status or some sort on the model itself)
- (track a backend conversion or two)
- redirecting to a success or fail page (or wherever and send a flash message along) depending on the payment's status
- (track some front-end conversion pixels etc)

There's simpler ways, but this would be very effective.

The webhook isn't all that bad though: using the log's identifier inside metadata could be great for future updates on the same object. I personally combine the two.

Think forward.

xewl avatar Oct 20 '16 00:10 xewl

@xewl: Yes, that was my point. You can't implement this functionality without storing the paymentId on the application side. This renders the metadata feature almost entirely useless.

leongersen avatar Oct 20 '16 09:10 leongersen

You can add custom values to the return URL . I would opt for encryption. I have seen this in some documentation.

Return URL would be /mollie-return.php?hash=abcdef&val=12345 check if val + key match hash and you can then use that to access a locally stored record of the transaction ID . Or go for a 256bit? encryption of the transaction ID and pass that in the parameter.

Parcye avatar Oct 20 '16 09:10 Parcye

@Parcye The identifier on the log table can be any "encrypted" code you'd want, but is as unique as it can get for an attempt itself, whatever value you tend to push in here: be it a primary key, or a random generated key.

It's just a matter of identifying it afterwards.

@leongersen Well... without the paymentId you aren't able to identify it this way, and thus you'd have to do it anyway. It's a better practice to log as even the webhook only receives that value. The rest is for you to grab and collect.

Long story short: If you don't log your paymentId at all, anywhere.., you dun goofed (:

xewl avatar Oct 20 '16 11:10 xewl

Hey guys, just wanted to chime in to see what we can do at Mollie to clear things up.

As stated earlier, we use a webhook system to send you status updates about your payments. We explicitly do NOT support a cancelUrl or any other type of status dependent URL, because it introduces various security issues that our webhook system has solved.

Just set up your payment with a webhook URL where we can push the status to. Send your consumer off to the gateway URL we provide. Then we will send your consumer back to the redirect URL you've provided once they either cancel or complete the payment. In both events, we will generally have called your webhook URL right before the consumer returns to your website, allowing you to show a proper status message to your consumers.

In short: we will never implement a cancelUrl because it goes entirely against our API design. Please feel free to ask us more about our webhook system if you still have questions.

mollierobbert avatar Oct 20 '16 11:10 mollierobbert

It's true that the webhook call is mostly on time for that action, and can be processed on time. However when it isn't you're bound to wait or fail.

@mollierobbert Does the webhook have any connection (in time) to the payment gateway? Is it called during the payment process, is there a timeout? Does it also call when a user cancels via the button on the bottom?

I guess their concern is that they're not able to process the webhook on time to show a correct message, or work this out without the webhook at all.

I'd say that the only option to implement it this way, would be to always show the fail/cancel-page, unless a webhook has been called before to validate Payment->isPaid(), and thus show the Success-page. That does leave some space for error though, not much, but the chances are for real.

xewl avatar Oct 20 '16 11:10 xewl

It is in everyone's best interest for us to send you a status report via webhook before the consumer is sent back to your website. We try our best to do so.

However, sometimes things go wrong. The iDEAL issuer may experience a minor hiccup, for example. We don't want to delay the consumer too long, so while we wait for the bank's final status, we'll just send the consumer back to you.

You won't have received a webhook yet, so you'll just have to tell your consumer that the payment status is still unknown, but you're expecting to receive the payment soon.

This is the absolute best we (you as merchant and Mollie as payment service provider) can do. We cannot tell the consumer something we don't know.

We should definitely not hold the consumer hostage while we wait for the bank's status report, which may take minutes. The consumer may have already closed the browser, thinking the payment failed entirely. A status based URL system tends to introduce these types of problems to your payment flow.

Does the wehook have any connection (in time) to the payment gateway?

As stated above, we do our best to send the webhook before we redirect the consumer back to you.

Is it called during the payment process, is there a timeout?

Yes, we usually receive the payment status from our issuer directly, call your webhook, then issue the redirect. There is no timeout, because we don't need one. Your server will usually have processed the payment request in milliseconds, while the consumer's browser has probably not even received the redirect response yet.

Does it also call when a user cancels via the button on the bottom?

Yes. Actually, this is the only real way we know a consumer cancelled a payment. We can't measure a browser closing. We can only detect the consumer pressing the cancel button to return to your website, in which case you get a webhook for status=cancelled right before the consumer returns to your website.

I guess their concern is that they're not able to process the webhook on time to show a correct message, or work this out without the webhook at all.

Our API relies heavily on the webhook system. We highly advise you to use it.

We also highly advise storing the Mollie payment ID in your system for future reference and to be able to process the webhook reports easily.

I'd say that the only option to implement it this way, would be to always show the fail/cancel-page, unless a webhook has been called before to validate Payment->isPaid(), and thus show the Success-page.

Please just implement a 'pending' page. You can't always tell the consumer the payment failed or cancelled immediately. This is especially true with bank transfer. You will always want to design your system as such, that it confirms any payment status change via email.

Your consumers are used to receiving a final status via email. Going against that will be counterintuitive and somewhat suspicious to your consumers.

mollierobbert avatar Oct 20 '16 12:10 mollierobbert

@mollierobbert What your are saying is entirely true, and I wasn't attempting to debate this.

We're only updating our payment status on the webhook, never on the customer visit. This is an asynchronous process, and it doesn't really matter if this happens withing seconds, minutes or hours after a payment is made. Depending on the webhook to be invoked before the customer returns would only create race conditions.

In the case that the customer clicks cancel, however, this doesn't have anything to do with timeouts or latency in the connection with the bank. The customer just wants to return to the original site. I'd like to be able to say: "You've cancelled your payment. Click here to try again." As I see it, clicking cancel isn't a payment state, it is a guarantee that no payment was made. If the customer closes the browser before making a payment, they themselves know that no payment was made. They can return to the payment process by the same flow they used to get to it the first time.

What I'm trying to address is that it doesn't matter to our application that the payment was cancelled, we don't store or handle this state. We'd just like to show the customer a page that specifically acknowledges that they cancelled.

leongersen avatar Oct 20 '16 12:10 leongersen

@mollierobbert That was the most beautiful piece of text I've read regarding this issue and now I have to go & add e-mail templates to my processing-queue. (Oops)

I fully understand the pending message. Thus. When the webhook has been called, act accordingly, or show the pending status. Should work 💯

xewl avatar Oct 20 '16 12:10 xewl

@xewl Glad to hear I could be of help :-).

@leongersen You're making a fair point I hadn't considered above. However, since the webhook system is already in place, we would still prefer to communicate the cancel status via webhook, since all payment status processing logic would already be present there in your system. Introducing a cancel URL just for this scenario, next to our regular webhook system, would undoubtedly confuse our less technical merchants.

Also, consider the consumer may return via the browser's back button, in which case the payment will remain in an open state. There's nothing we can do about that. (This is not a valid argument against the cancelUrl, just another example of why you shouldn't care too much about non-complete payment states.)

mollierobbert avatar Oct 20 '16 13:10 mollierobbert

@mollierobbert Thank you for your response. The argument it might confuse our less technical merchants seems like a very valid reason not to implement this feature. I hadn't really considered that viewpoint.

As an example for others, I've implemented the return page as something like:

Welcome back!

If you completed your payment successfully, you'll receive a confirmation email from us in a couple minutes. You can also -start the payment again-.

leongersen avatar Oct 20 '16 13:10 leongersen

On a sidenote, do you have data on what percentage of payments is explicitly cancelled?

leongersen avatar Oct 20 '16 13:10 leongersen

@leongersen Thanks for your suggestion. That would indeed be a proper message to show consumers if you did not receive a webhook call from us in the meantime.

Do you have data on what percentage of payments is explicitly cancelled?

In recent stats, only about 2% of payments are explicitly cancelled via the cancel button.

mollierobbert avatar Oct 20 '16 17:10 mollierobbert

@Ricknox we are having the same problem.

I agree with @robertotremonti - a simple CancelURL could be introduced.

raphaelsrodrigues avatar Dec 06 '16 16:12 raphaelsrodrigues

@raph8888 Please read the full thread above for a detailed explanation on why we will not support a cancelUrl parameter.

mollierobbert avatar Dec 06 '16 16:12 mollierobbert

@mollierobbert I understand your reasoning, but we do have a problem to get cancelling working well with Drupal Commerce. The Commerce Payments API expects that payment providers do have a configuration option for a 'cancel url'. This has been chosen based on a broad analysis of popular Payment Provider API's.

This is a cross-link to the Drupal issue: https://www.drupal.org/node/2908280

ndf avatar Sep 21 '17 10:09 ndf

Sad to hear the Commerce Payments API is pushing for a URL based feedback system, Niels. As stated above, Mollie won't introduce a cancel URL. It may solve your particular issue, but will result in a vague API that Mollie will have to live with for years to come.

I'd advise to push the Commerce Payments API developers to mark the cancel URL as an optional feature.

If all else fails, since you're stuck between two APIs with different workflows, a last resort could be for you to introduce a layer inbetween Mollie's API and the Commerce Payments API. The middleware would then catch all webhooks and would redirect consumers to the appropriate status URL based on the async webhook status received from Mollie.

mollierobbert avatar Sep 21 '17 10:09 mollierobbert

I know this is an old / closed issue, but just thought I'd express my desire for a cancelUrl also.

As an ecommerce platform developer our product is able to integrate with a number of payment gateways for which we aim to provide a consistent checkout flow across them all. This can be fairly difficult based on differences of those payment gateways, but the gateways that integrated the best, and a pattern we are seeing a lot is when they use 3 URLs.

  • redirectUrl being the location you are sent to on successful checkout (usually a confirmation page)
  • cancelUrl being the location you are sent to on cancel (usually the cart page)
  • webhookUrl being the location status information is sent to

Now I do understand the importance of using a webhook Url for status updates and you should NEVER use the redirect Url or cancel Url to update an order status, but from an actual user flow perspective having a cancel Url property is really useful in being able to direct visitors to the most relevant location.

I've read the suggestion of having an interim controller to try and figure out the location to send someone, but this seems like a really hacky way of doing it as it has a high potential to fail if the webhook hasn't hit yet, and also wastes a lot of requests having to fetch the order to check it's status and then perform another redirect when a simple straight redirect from the gateway could do that in one.

I do hope you might re-consider this decision in the future as it would make integration a whole lot simpler.

mattbrailsford avatar Feb 05 '20 10:02 mattbrailsford

Just wanted to support mattbrailsford: The integration of Mollie in our own php-shop-system really went fast and smooth until now, when I came over the problem to distinguish between "user was redirected, because he completed the payment-process" (although I know this doesn't mean the payment completed sucessfully) and "user was redirected because he clicked the button 'back to webpage'". It's just a point to see from the users view: Did he want to complete the payment or not?

Out customer for who we do the implementation is now thinking about cancelling the subscription to Mollie, because we can't guarantee that a user which wanted to completed the payment might be redirected to the wrong page, because the webhook might not be called at the time the redirection happens.

Cangoo avatar Jul 20 '21 12:07 Cangoo

Thanks for chiming in, Matt and Cangoo.

Since the request keeps coming up, I'll bring up the discussion internally again as well.

We do of course want to help with payment gateway standardization efforts, and improve the consumer experience. The question remains to what extent the parameter would complicate the ease of use of our API.

To be continued.

mollierobbert avatar Jul 20 '21 12:07 mollierobbert

Big fan of Molly in regards to ease of use for clients and their customers. And looking forward to implementing Molly in a Umbraco site using Vendr by @mattbrailsford so please reconsider implementing the cancelUrl

dailyenergy avatar Jul 29 '21 12:07 dailyenergy

@mollierobbert did this get raised in an internal discussion? Were there any new updates on this? We've had a handful of customers asking us to integrate which we'd love to do but so far we are just having to say it's dependent on the outcome of this issue.

mattbrailsford avatar Aug 10 '21 14:08 mattbrailsford