I have implemented client side checkout as demo'd here https://developer.paypal.com/demo/checkout/#/pattern/client. Its working fine while testing with sandbox account. But I am concerned about how we can verify whether the payment has actually gone through from the server side?
For eg: if the user executed a payment for Order number 'X', I want to make sure that the payment has been successful. I noteiced the transaction Id which I understand is generated by paypal only if the payment has gone through and we can recieve that Id in the parameter for payment execute call back:
actions.payment.execute().then(function (payment) { // where payment.transactions[0].related_resources[0].sale.id is some value say 'xxx' });
The same Unique Id can be seen in the sandbox transaction details page (https://www.sandbox.paypal.com/webscr?cmd=_history-details-from-hub&id=xxx) as Unique Transaction IDxxx. Is there a way (a paypal endpoint) to verify this through a server side call? That is verfiy that the Transaction Id received at the server is a valid one from paypal.
Excuse my ignorance, are webhook events meant to do this?
You can use IPN/Webhooks, then PayPal will send a notification to you for every payment with the transaction id, payment details. You can use that information for verifying the payment.
Get an access token
First we need to have an oAuth token from paypal. For this we need to do a POST to https://api.sandbox.paypal.com/v1/oauth2/token (sandbox url) passing our PayPal app's client Id and Secret.
Details of the HttpClient object I used in C#:
DefaultRequestHeaders.Accept: application/json
DefaultRequestHeaders.AcceptLanguage: en_US
DefaultRequestHeaders.Authorization: AuthenticationHeaderValue of Basic scheme, parameter of Base64String of bytes encoded from (clientId:clientSecret).
The response will have the property access_token which has the access token we want to pass in the next request to get the status of the transaction. For more details on getting access token refer: https://developer.paypal.com/docs/api/overview/#get-an-access-token.
Get sale details
Now to make the request for sale/transaction details as said here: https://developer.paypal.com/docs/api/payments/v1/#sale_get.
Do a GET passing the access token as Bearer token in the Authorization header.
And the unique transaction Id as parameter for the sale id. The response will have the property "state". Having the value "COMPLETED" ensures the transaction has gone successfully.
Related
2 questions for you regarding Paypal Hosted Checkout solution and the goal of the "Identity Token" or "Token ID".
1-
I've come accross several online Paypal docs (such as for Payflow integration) that talk about providing the "Identity Token" (or "Token ID", I think they're the same do they?), but I was wondering what's the goal of passing over this token ID, is it for my own security, or Paypal's one, or something else? Does anybody know exactly what's the purpose of that token ID, what Paypal is doing with it, and/or what the vendor shall be doing with it?
Asking this because when doing the form post to redirect the user to the Paypal hosted checkout, we have to first call the paypal gateway server to obtain the "secure token" and this API call is already secured through another method right, I need to pass my account credentials. So why posting only the "secure token" is not enough and we also need to post that "token ID"? Paypal should already have associated the secure token with my account information through the first API call no?
2-
Also, at the end of the flow, once Paypal returns the customer to my vendor website, does Paypal include any of those tokens (token ID or secure token) as part of their request (perhaps by adding url parameters to my given vendor return url)? If so, does Paypal recommands any sort of validation to be made on the vendor side, such as validating that the tokens match the ones that I, the vendor, stored in the user session prior to redirecting the customer through a form post to the Paypal hosted checkout? Basically, how can I ensure that the session was not hijacked between the time I redirect the customer to Paypal hosted checkout and the time Paypal returns the customer back to my site?
Reference: https://developer.paypal.com/docs/classic/payflow/integration-guide/#hosted-checkout-pages
Thanks a lot
As the previous user states, the Token id is used basically to identify an specific transaction process during it's workflow.
About your second question, in case of Express Checkout, the workflow does not ends when PayPal returns the user to your site. This step you are describing is probably when you send the user to PayPal to AUTHORIZE a payment that you will issue later. The last step is the DoExpressCheckoutPayment, in which you just inform paypal to make the transaction, for this you just pass to PayPal the token, so PayPal knows what you are "talking" about.
Is it good practice to validate the token, I would say yes. Somebody might be listening at your connection and injecting some invalid token. In any case, if you send an invalid token you will get an error message from paypal.
the following image illustrates very good the whole process:
As I understand it (and if reading this correctly), the Secure Token is for processing transactions on your own site instead of passing the user and order to paypal for processing. The Secure Token identifies that specific transaction and ensures the continuity of the order is not broken. You require a Token ID in order to obtain a Secure Token.
I think the ReSt authorization API is broken, I don't get back the ID for the auth object when doing POST /payment with intent=authorize.
Even calling GET /v1/payments/payment/{id} I don't get an auth id.
Here's the http log: http://pastebin.com/e1bEjfBF
As you can see I get "related_resources":[{"authorization":{"create_time"... but there is no id.
The pastbin sample shows the lookup of a payment resource that has not been approved yet.
Generally, for a PayPal Payment in Rest, you will have to:
Create a PayPal payment (POST payment request to https://api.sandbox.paypal.com/v1/payments/payment)
Redirect the buyer to the returned Approval_URL (usually something like https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=)
Upon return of the buyer to the return_url you specified in step 1, execute the payment (POST the PayerID to https://api.sandbox.paypal.com/v1/payments/payment/PAY-xxxx/execute/)
When you now lookup the Pay-Key, you should see the state of the Pay-Key as "approved", included buyer details, an Authorization TXN-ID and authorization state of "Authorized".
The next step would be to call the appropriate HATEOAS links to capture, void or reauthorize the authorization.
I've uploaded a sample flow to http://pastebin.com/JWG8Gzuv
You can also refer to the developer documentation located under https://developer.paypal.com/webapps/developer/docs/integration/direct/capture-payment/
I'm using the express checkout API through the ActiveMerchant gem in a Ruby on Rails app. The whole authorize and capture flow works just fine when is done within 3 hours. But after that my token expires and I lose the transaction. Even if the authorize and capture documentation says that the authorization is valid for 3 days (at least according to https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/howto_admin_authcapture).
So, how can I capture the transaction after the token has expired?
You shouldn't have to be passing your token. You should be following the flow below.
Make your SetExpressCheckout API call and set payment action to authorization (A)
Get token back
Redirect buyer over to PayPal with to token to login and agree to payment
Buyer gets redirected back to your site with token and payer id returned
You can then execute your GetExpressCheckoutDetails API call using the token.(this step is optional)
Then you perform the DoExpressCheckoutPayment API where you pass over the token and set the payment action to authorization (A)
Then then completes the Express Checkout authorization.
Now you would go back a day or two later and perform your DoCapture API where you send over the transaction id that was returned from your DoExpressCheckoutPayment API earlier. You don't send over the token again here. Once you complete the DoCapture, the funds should then show in your account.
I'm using PayPal Payments Advanced with TEMPLATE=TEMPLATEC. I already figured out how to create an IFRAME and receive confirm/cancel/silent_post responses from PayPal. But I've found no way to validate parameters my confirm/cancel/silent_post pages receive. Is there a way to ensure that these parameters are from PayPal and not just sent by arbitrary user?
About the best option you have is to run an inquiry transaction (TRXTYPE=I) against the secure token and secure token ID you received from PayPal before displaying the iframe. If a transaction was run, that call will give you the transaction ID (PNREF) from the transaction. (And depending on your situation, the PNREF may be all you need.) If that matches the PNREF sent back to you by the buyer, then there's a good chance that the rest of the data is genuine.
For example:
Request:
USER=****&VENDOR=****&PARTNER=****&PWD=****&TRXTYPE=I&SECURETOKEN=7tGDq6ILZeEmATCwTXrSRkwjz&SECURETOKENID=76ac5819ee01475daf15b2af038da977&VERBOSITY=HIGH
Response:
RESULT=0&PNREF=E79P4ABEC9DE&TRANSSTATE=8&ORIGRESULT=0&ORIGPNREF=E19P4BFB14B2&RESPMSG=Approved&AUTHCODE=111111&AVSADDR=Y&AVSZIP=Y&CVV2MATCH=Y&ORIGPPREF=1XR06058R58346646&CORRELATIONID=bdd79cb3c7fb6&PROCAVS=X&PROCCVV2=M&SETTLE_DATE=2013-04-23 07:22:06&TRANSTIME=2013-04-23 07:22:06&LASTNAME=NotProvided&AMT=24.99&ACCT=3698&EXPDATE=1214&CARDTYPE=0&IAVS=N
ORIGRESULT is the result of the original transaction (0 is a success; anything else is a failure.)
ORIGPNREF is the PNREF from the original transaction.
You can also put a long, unique "token" parameter in your silent post URL. Something like
"http:// www.my-web-site.com/confirm-payment?token=2348349u21034ms39n899"
and match it up with the same token a server side script is expecting. Since the silent post URL is stored in your PayPal manager account, the token is confidential, and even the URL as a whole is confidential. Plus the transaction info is also passed to this silent post URL allowing you to match up info with your transaction you saved in your database at checkout. This is a good secure method to confirm payment was correctly made. The silent post will work for both "pay with paypal" and "direct credit card payment" methods on the payments advanced iframe (a.k.a. hosted checkout page). Additionally, you can also throw in one more check, to see if PayPal is the $_SERVER['HTTP_REFERER']; (which of course can't be trusted purely on its own).
I've setup a PayPal site which uses IPN and I was having trouble getting PayPal to send the GET variables to the return URL that I had specified. It was sending the user's browser to the return URL, but nothing was being passed via GET or POST.
I changed one setting in the PayPal business account: "Payment Data Transfer (optional)" to On which generated an "Identity Token" on the PayPal website.
I also got an automated email from PayPal saying:
---------- Forwarded message ----------
From: service#paypal.com <service#paypal.com>
Subject: Payment Data Transfer (PDT) Has Been Enabled
This email is to inform you that you have successfully enabled Payment Data Transfer.
PDT's primary function is to display payment transaction details to buyers when they are redirected back to your site upon payment completion. However, there are cases, such as with pending transactions, where you won't receive notification of all transactions. For this reason, PayPal strongly recommends that you also enable Instant Payment Notification (IPN).
To learn more about enabling and setting up IPN:
https://www.paypal.com/us/cgi-bin/?cmd=p/xcl/rec/ipn-intro-outside
To learn more about Payment Data Transfer, including setup instructions and a complete list of variables:
https://www.paypal.com/us/cgi-bin/?cmd=p/xcl/rec/pdt-intro-outside
Sincerely,
PayPal
Clicking on the second link and clicking on "Technical Overview" (https://www.paypal.com/us/cgi-bin/webscr?cmd=p/xcl/rec/pdt-techview-outside) shows:
Your POST should be sent to
https://www.paypal.com/cgi-bin/webscr.
You must post the transaction token
using the variable "tx" and the value
of the transaction token previously
received (e.g.
"tx=transaction_token"), and the
special identity token using the
variable at and the value of your PDT
identity token (e.g.
"at=identity_token"). You will also
need to append a variable named "cmd"
with the value "_notify-synch", for
example "cmd=_notify-synch", to the
POST string.
However, I am NOT passing the Identity Token at all, yet everything is working fine!
(a) Is this a problem?
(b) Why is it working if the documentation implies that it shouldn't?
(c) Is this a consequence of specifying an outdated API version (58.0)? What is the value I should be using?
In my opinion the identity token should be a required param since it is the only way Paypal can verify that the request you're making is valid. Otherwise, other people can simply guess a transaction id (even though it is not intended for their accounts) and get details for that transaction from Paypal.
I'm guessing this is a bug you're experiencing. Are you testing in the Paypal sandbox or in a live environment?
Realizing that the OP probably no longer needs an answer after 9 years, but others still might:
The POST of the transaction ID and identity token is purely for the purpose of verifying that the original transaction notification (relayed via the GET method to the merchant's Return URL) actually came from PayPal.
It is as if to say to PayPal, "My website just got this supposed confirmation that a customer paid. Here is the transaction ID and my seller ID again. Is this a legitimate match?"
In fact, at https://developer.paypal.com/docs/api-basics/notifications/payment-data-transfer/, when talking about setting up for testing, it only talks about getting your script ready to receive, parse and display the GET data. It doesn't mention the POSTing back to PayPal (though that is mentioned elsewhere). So, yes, the PDT function should work without doing the POST back to PayPal afterward and waiting for that response of SUCCESS or FAIL, but...
Anyone who knew what they were doing could go to a seller's URL and append a query string with the right combination of variables to fake the same kind of GET request that the PayPal PDT system would initially send, whether or not the transaction ID were a real one.