PayPal REST API: Fulfill Order/Payment on Redirect URL or on Webhook call?

Viewed 1631

I am working on integrating the PayPal REST API in my Symfony 2 web app. I am not sure what is the correct time/location to fulfill an order/payment:

Together with other parameters like the amount, customer data, etc. I transfer two URLs to the PayPal API: One URL the user is redirected to when the payment was accepted and one URL the user is redirected to when the payment is canceled.

This works without any problem and when the payment is completed I get all information I need to fullfill the order (e.g. unlock some content), when the user is redirected to my page using the URL I transferred before.

However I am not sure if this is the right way to go:

  • If redirecting the user back to my page fails for any reason, the purchase will not be completed (no content unlocked) although the payment was completed.
  • If the customer uses manual bank transfer, the payment is created as pending and will be set to completed when he transferred the money manually to his PayPal account. I have to handle this second completed message somehow
  • ...

These problems can be addressed by using Webhooks: I specify a special callback URL in my PayPal profile, and the API will notify my system on different Events using this URL.

This way I would get informed when Payment is pending and get a second notification as soon as it is completed. Sounds great. But are Webhooks also meant to fullfill instant payments? The Doks say, that Webhooks calls are executed asynchronly and there is no guarantee about their order.

Can I be sure, that my system will receive the PAYMENT.SALE.COMPLETED event though the Webhook BEFORE the user is redirected to my page? Or is it possible, that he is redirected first and I receive the event sometime later? In this case the user would be back on the page and see no result of the purchase.

So, what is the correct way/order to process the redirect URL and webhook events to fullfill a payment?

2 Answers

Regarding ORDERs , this is how I do it in v2 Paypal API.

  1. Create the order at POST : /v2/checkout/orders with intent = CAPUTRE, which if is correct formatted will reply me a JSON with 4 links. Among them there is "approval" link. In same step I create and store locally the Order/Transaction Payal ID you receive in that response. Add to it a "status=pending"

  2. Use the Approval link to redirect the user to it.

  3. In step 1 you need to tell Paypal the "return_url" and "cancel_url" for your scripts. When everything was ok , and user clicked PAY, he will be redirect to "return_url".

What does return_url script contain ?

Using the GET Parameter "token" paypal sends, I check my database and retrieve the saved Order at Step 1 . I make sure the Order was with status "pending" so it;s not completed.

Do "POST : /v2/checkout/orders/$token/capture" to capture the payment this will reply with a JSON. If the status is COMPLETED (99.9% of cases) you are done. The payment was a success etc etc and you mark the order in your local database as "status=completed" and can give the user the products.

If the CAPTURE request turns errors, it means the money were not transfered so you can easily throw an error to user and cancel the order all together so he starts/create a new one. So as you can see/tell, payment and product-exchange happens only after the user came back to your website after the paypal-redirect AND the capture was succesuful If something happens and user is not redirect, the transaction never happens.

The only odd case is if the CAPTURE returns PENDING. And this happens if you enable reviewing each order before capture in your Account/API options. In this case the transaction is not done, so the buyer shouldn't get the prodcuts. So simply set your local order "status=pending", and listen to the webhooks to enable/validate it. However, for this particular case you need to store the CAPTURE_id (you get it from the response when CAPTURING) and attach it to your local order. That's the only identification you'll get when the PAYMENT.CAPTURE.COMPLETED hook comes. So you need to use that id from the hook to fetch the products.

In the case user has not enough funds for the order, he won't be able to "approve" the order on paypal. Meaning: he won't be redirected to "return_url".

If he manually inserts the return_url, your script will fail the CAPTURE and Paypal will return error saying "CAPTURE can be performed only on APPROVED orders".

As you can see there's no need for WEBHOOKS in this scenario and the responses from paypal are instant (or at least synchronous for your payment-validation script).

Related