Reputation: 18729
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:
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 somehowThese 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?
Upvotes: 5
Views: 2235
Reputation: 61
Regarding ORDERs , this is how I do it in v2 Paypal API.
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"
Use the Approval link to redirect the user to it.
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".
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).
Upvotes: 0
Reputation: 52473
You can't trust the fact that the user is being redirected to some page you specified to mark a payment as done. Don't do that.
The customer could just abort the transaction and visit this "success" page by manually entering it into his address-bar. Then no payment has been done.
The only proof you can - and should - trust is if you receive a PAYMENT.SALE.COMPLETED
from PayPal.
You can provide a success URL to PayPal where the customer sees "waiting for Payment to finish" until the Request to your Webhook-endpoint arrived as expected. Then you forward him to the real success page or content he wanted to buy.
Just show a timeout error after a while if you didn't receive the expected WebHook Request. Use Ajax or Websockets to accomplish this waiting loop.
Alternatively you can show a "success" page but let the customer use/unlock the content only after the Webhook Request arrived, showig a "something went wrong" message if he tries to access it before the payment confirmation arrived in form of the WebHook Request.
Keep in mind that we're talking about a few ms of time here between the user being redirected and the Webhook being sent. Usually the Webhook request is sent before the redirect occurs for the customer but you can't trust this.
Upvotes: 2