I have integrated a smart button checkout functionality in a marketplace where anybody can sell/buy items from anybody.
I also have successfully installed a webhook listener that gets notified if a refund of payment has been issued.
But in the body of the refund event received, I am failing to find information WHICH payment/transaction has been refunded.
By reading the event with
file_get_contents('php://input');
I will get the JSON encoded event details like this:
{
"id":"WH-3WS24689NP236083V-89P84301TC0576916",
"event_version":"1.0",
"create_time":"2020-07-10T15:36:33.720Z",
"resource_type":"refund",
"resource_version":"2.0",
"event_type":"PAYMENT.CAPTURE.REFUNDED",
"summary":"A EUR 7.89 EUR capture payment was refunded",
"resource":{
"seller_payable_breakdown":{
"total_refunded_amount":{
"value":"7.89",
"currency_code":"EUR"},
"paypal_fee":{
"value":"0.15",
"currency_code":"EUR"
},
"gross_amount":{
"value":"7.89",
"currency_code":"EUR"},
"net_amount": {
"value":"7.74",
"currency_code":"EUR"
}
},
"amount":{
"value":"7.89",
"currency_code":"EUR"
},
"update_time":"2020-07-10T08:36:00-07:00",
"create_time":"2020-07-10T08:36:00-07:00",
"links":[
{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v2/payments/refunds/3TF6899507696873K"},
{"method":"GET","rel":"up","href":"https://api.sandbox.paypal.com/v2/payments/captures/5U107751JJ334642K"}
],
"id":"3TF6899507696873K",
"status":"COMPLETED"
},
"links":[{
"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-3WS24689NP236083V-89P84301TC0576916",
"rel":"self",
"method":"GET"},{
"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-3WS24689NP236083V-89P84301TC0576916/resend",
"rel":"resend",
"method":"POST"}
]
}
So I get the data. But within the data I can not find any information like a "transaction id", "order id" or similar which I could use to look up in the DB if it matches the capture id (from capture order) of a previous order.
What do I need to do to get the information I need? Or am I looking in the wrong place/for the wrong field?
In case I need to auth the request first and make another request to get more data, I would be very grateful for a FULL example (or link to an example) in PHP as I can not make sense of the Paypal documentation on webhooks.
EDIT: It seems that in one of the links (in the event above), the capture ID (which is the same as the transaction id txn) of the original payment is "hidden" in one of the links providing endpoints. In this case: 5U107751JJ334642K
I can only shake my head at Paypal's way of making things incredible difficult to achieve. How can you NOT have a dedicated field with the id of the original transaction in a refund message?
Welcome to the wonderful world of Hypermedia as the Engine of Application State (HATEOAS [emphasis added…])!
The purpose of which is to give your application the information needed to discover and navigate to / execute related resources / actions dynamically. To that end, each API response or webhook payload contains a list of links.
As you have already discovered, in the present case, those link back to the parent resource, i.e. the refunded capture, via the up relationship.
While it may be tempting to extract the capture ID straight from the href, it might be safer to actually follow that link. You will receive the original capture response from which you can either grab the capture id or continue following the up link to get to the authorization.
You can find a little more information on PayPal's HATEOAS implementation on https://developer.paypal.com/docs/api/reference/api-responses/#hateoas-links. Those rel types that are standardized are documented on https://www.iana.org/assignments/link-relations/link-relations.xhtml#link-relations-1. For example, the description of rel="up" is "refers to a parent document in a hierarchy of documents".
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last month.
Improve this question
So our platform operates on Laravel/PHP/SQL/JS/Dart/Flutter/SQL/Firebase and our payment processor has an rest api in which we need to work with to send a digital invoice out for payment. Now the processor requires information (Basics Name, Email, Amount, Phone, Ipaddress, Order Number, etc.) to be included in the api call some of which won't be available or known until after the order is placed on our system. Necessitating a spinner widget to place the order in a pending payment status to then fire the api call to collect payment after. Additionally all requests made to the api need to be converted to base64 encoding prior to being sent over the api.
According to the api documentation It says that i need to create a json object containing all the required information and encode that data using Base64 Encoding which is then sent via a put request to the api to obtain a response alongside a Payment token which will then need to be stored to be called upon later using a callback api get request. I am trying to figure out the best way to accomplish this so that our payment flow becomes:
Customer Shops Via Web / App adding items to cart
Clicks or Taps on the Confirm Order Button
Our App/Platform creates a "draft order" with a order status of pending payment & sends the necessary information to our processor to generate a digital invoice & payment token where the token is stored in our database with the rest of the order data & processor then send a text message to the customer with a link to complete DIV (Digital Identity Verification) & collect payment for the order.
Customer then follows the link & completes payment where processor indicates if the given orders payment was successful or unsuccessful in it's system while our platform sends an additional api request using the payment tokens to get the current payment status for orders sitting in the "pending payment status" so our platform can mark the order as paid and update it's status to processing.
I know i will invariably receive a response of why does your payment methodology need to be so complex just get processing and go and as much as we would love for that to be the case, it simply is just not possible at the moment in our industry and current political climate. Forcing us to use this type of payment methodology to be able to operate or be restricted to simply being cash only.
To be honest I have not yet attempted, as i am still trying to map out what this needs in order to work and am seeking advice on the best practical way to accomplish this using the frameworks/stack that we operate on. Admittedly i am feeling slightly intimidated by this project since management just kind of threw this in my lap after losing our backend server side developer. I am not super familiar with Laravel and am somewhat familiar with PHP however most of my background is in Flutter, Dart, Kotlin, Swift, Html, CSS, JS, React & management does not seem to know the difference and somehow expect me to make this work.
This is the stack we are currently using
PHP/Laravel for our Administrative Control Panel. This is where this functionality needs to be added.
SQL for database.
Flutter/Dart for Mobile app / Front End.
Firebase for OTP/Push Notifications etc.
Query needs to be made to database after order placement grabbing order info and placing it into a json object
Expected result should look like:
{
'ipAddress':'123.456.789.012',
'merchantId': '1234567890',
'invoicenumber': '1234',
'firstName':'Mary',
'lastName':'Jane',
'email':'email#email.com',
'currency':'USD',
'amount' : '1000',
'redirectURL':'example.com',
'apiKey': 'Rw309njyNnklbkf9Pn6YNx68494292EV'
}
Where the json object data needs to be encoded using Base64 Encoding
Where the output of that encoding should look like this:
ew0KDQonaXBBZGRyZXNzJzonMTIzLjQ1Ni43ODkuMDEyJywNCg0KJ21lcmNoYW50SWQnOiAnWENwUjQyOTInLA0KDQonZmlyc3ROYW1lJzoncmFqZXNoJywNCg0KJ2xhc3ROYW1lJzonZ29uZGFsaXlhJywNCg0KJ2VtYWlsJzonZW1haWxAZW1haWwuY29tJywNCg0KJ2N1cnJlbmN5JzonVVNEJywNCg0KJ2Ftb3VudCcgOiAnMTAwMCcsDQoNCidyZWRpcmVjdFVSTCc6J2V4YW1wbGUuY29tJywNCg0KJ2FwaUtleSc6ICdSdzMwOW5qeU5ua2xia2Y5UG42WU54Njg0OTQyOTJFVicNCg0KfQ==
Which will also then need to be stored temporarily as a string with the field name of RTPRequest
Where the encrypted string is then passed via a post request to the api endpoint
Where the response from the api endpoint will include a payment token in the body which will be needed to be added to the database table row for the order that was just sent to the api
API get callback request is then sent requesting an updated payment status and if successful updates the payment status to paid and the orders status from pending payment to processing taking the user to from the order processing spinner to their orders page.
Thoughts, suggestions, advice etc...
That doesn't seem so complicated. If you're confident with the listed technologies you should be able to handle the Laravel stuff pretty easily, the documentation is good.
You'll need to expose some API routes (Routing, Controllers) that read/update data on your tables (Models) and make server-to-server API calls to your payment provider (HTTP requests)
The architecture of the logical flow will be the same as any other stack really.
draft order generated
user redirect to provider
provider directs user back to yourself with some status (or a webhook)
your back end handles the returned user / webhook
I am having some trouble thinking this through the right way.
I am running Laravel as the backend for a ReactJS Frontend - separately.
It is going to be a small onlineshop only having PayPal as the Payment Gateway (using PayPal Smart Checkout Buttons) without User Registration.
React will call the Laravel API on payment initiation which will prepare the actual call to PayPal on server-side (get the price from database, in case user tampered with it, i take only the product ID and get the real product data from the database). The response will be returned to React.
As you might know PayPal works this way: create order in first API call, then capture the order in second API call ("create order" and "capture order" calls)
Laravel Endpoints that are called: "/create-paypal-transaction" and "/capture-paypal-transaction".
Currently (old version of this project was a monolithic laravel solution, no separate frontend) I store the response of the "capture order" API call in my DB as the final order / as the completed transaction. The result of the "create order" call is not stored in the DB. I do store the result of the "create order" call, with all its information (product, price, id, paypal_id etc.) in the session. Then, after capturing I load from the session to create the final order as a DB entry.
My trouble is: how do I securely realize this with a separate frontend and pass data from my first API call to my second API call?
I see only two options right now, maybe you guys can enlighten me:
Save the response from PayPal of the "create order"-call to my DB
via laravel and then, after approval & response of the "capture
order"-call, just update the stored order status from something
like "created" to "captured".
Use the response data from the first API call in React and pass it to the second API call to hit the Laravel & store the order if PayPal returns success.
Problems that I don't like with either method:
currently there is no Authentication method implemented (no JWT or Passport or any). I was going to go with Laravel default API Authentication as I have no Users - therefore Passport & JWT don't seem to fit perfectly. People checkout without any registration. Therefore:
if I go with 1. solution people could easily mess with the DB and create loads of orders ("create order" calls)
and the 2. solution is just no real solution. I will not trust client-side data.
So, please, please let me know if you got any idea how to do this the smart way. Maybe I just don't see the obvious right now, but it has been a long day and all the thought about right way to securely communicate have been making me tired...
Thanks in advance!
Both (1) and (2) are overly complex. When the client calls create-paypal-transaction, all it needs in response (in addition to anything of your own you want to provide for your own purposes) is the order id. That's it, that's all the client wants and needs to keep track of.
It will use that id when it calls capture-paypal-transaction, and your webserver can check that it is appropriate and valid (in the session) before attempting to do the capture. No database storage is necessary until you have a successful capture, although you are welcome to store in-progress orderIDs for debug purposes. They have no accounting value, and no security value outside the session.
Introduction
Hello I'm going to sell a software using serial keys. Any person can go into my webpage and click on the paypal button to buy a serial key, they don't need to register.
What I'm trying to achieve
I need to let any person use the button and receive a serial key when the payment is done.
So the workflow would be something like:
Any person (non-registered) clicks on the button.
The paypal page shows up.
The user pays and gets redirected to a page that shows something like "Congratulations, here is your serial key: {{serialKey}}" and also receives the same serialKey via email.
What I tried
I'm trying to use Paypal's Smart Checkout but I don't understand how to implement what I need.
Right now I'm able to:
Render the button.
The button performs a request to my back to get the price (so the user can't change it and trick me).
The paypal page shows up.
On success it performs a request to my back.
On failure it restarts.
What I'm missing:
How can I check that the payment did actually succeed on the back, I mean that I received the money? Is there any way that I can post to paypal using the ID and see if I that transaction was correct?
Additional question
Is this the right approach? All I need is to let anyone click the button, pay and receive a serial key ONLY if the payment was correct. Right now it seems too complex for what I need. I don't understand everything I see on the paypal docs, if you follow it line by line the examples don't work, you have to adapt a lot of the code and I'm not sure about what I'm doing.
I also read IPN's docs but using it I won't be able to redirect the user because everything would be done in the back without the user even knowing what is happening.
Final text
Any help is appreciated. I'm asking here because I saw a lot of another questions about paypal like this one that were upvoted.
The most robust approach is to combine the PayPal Checkout front-end with a v2/orders backend for payment setup and capture.
create order
capture order
This way the capture happens from your server, so you have an immediate success/failure API response -- and can immediately do whatever you need to do to handle the business logic of the digital good purchase (serial key activation/distribution)
Once you have everything working well for the happy path, don't neglect to handle funding source failures, so that if the capture fails due to e.g. the buyer's first card being declined, this is propagated back to the UI and the buyer can select a different funding source.
I need to use PHP to count the number of active subscriptions in my PayPal account (people subscribed to my service, not the counts of services I'm subscribed to).
Does anyone know how to do this or can point me in the right direction?
I can't find any documentation that allows you to see a list of your active subscriptions via the API, but I'm assuming that PayPal has this option and I'm just missing it...
This option cannot be found because it's apparently not available:
https://github.com/paypal/PayPal-REST-API-issues/issues/5
Apparently you cannot get a list of subscriptions regardless the status for the time being, as you can see here https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions there is no such feature available so let's hope Paypal adds it in the future.
However if you are storing the subscription ids in a database then you can loop them and foreach subscription id you can get the details of the subscription https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get and check its status and finally display only the ones with an active status.
https://www.paypal.com/billing/api/orchestrator/subscriptions?data=%7B%22total_required%22%3Atrue%2C%22sort_order%22%3A%22desc%22%2C%22page_size%22%3A%2220%22%2C%22page%22%3A%221%22%2C%22plan_ids%22%3A%22P-123456789ABCDE%22%2C%22statuses%22%3A%22ACTIVE%22%2C%22tab%22%3A%22all%22%7D
Just pass a query string like:
{"plan_ids":"P-123456789ABCDE","statuses":"ACTIVE"}
You can GET this, and specify a page size. It seems the max is 20, but you can change the page number too.
Change the plan_ids parameter that I have as "P-123456789ABCDE" to your plan ID and you'll get a JSON response. I'm sure this isn't advertised, but I found it digging through the PayPal site.
There doesn't appear to be a direct way to list all of your subscriptions, but you can use the Transaction Search API to retrieve a list of your transactions (one month at a time, max 500 results per page) and grab the paypal_reference_id of each transaction object, which will be a valid subscription ID if the paypal_reference_id_type is "SUB".
Here's an example of the GET URL, which requires an Oauth token:
https://api-m.sandbox.paypal.com/v1/reporting/transactions?start_date=2022-07-01T00:00:00-0700&end_date=2022-07-31T23:59:59-0700&fields=all&page_size=500&page=1
Here's a simplified example of the response data:
{
"transaction_details":
[
"transaction_info":
{
"paypal_reference_id": "I-ABCDEFGHIJKL",
"paypal_reference_id_type": "SUB",
}
]
}
Once you've retrieved all of your subscription IDs, you can use the Subscriptions API to retrieve their details (such as its status) via an authenticated GET request:
https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-ABCDEFGHIJKL
Here's a simplified example of the response data:
{
"status": "ACTIVE",
}
I have successfully posted my custom shopping cart to PayPal -- it processes the order just beautifully, and when the payment is received, it posts data back to the URL I specified in the config. The code is based on the library found here: http://www.phpfour.com/blog/2009/02/php-payment-gateway-library-for-paypal-authorizenet-and-2checkout/
So I'm successfully verifying the IPN by posting data back to PayPal -- that is all great. Here's my dilemma -- how do I know what order the IPN is confirming?
I am not making use of PayPals shopping cart, I have my own. It has it's own cart ID in my database, and when I receive an IPN for that cart, I'd like to "close" the cart and save it as an order to be looked up at a later date.
I've tried passing an additional custom field along with the redirect to PayPal that populates the cart, but that value isn't returned back to me in the IPN. The documentation on x.com is just plain lacking so I've found no help there.
Does anyone have any experience with PayPal and IPN? Doesn't necessarily have to be with PHP -- I can interpret code -- but if you have a way to send a value to PayPal with an order and then have that value returned with the IPN, that is AWESOME!
If this isn't possible with PayPal's API (which I would find hard to believe) -- any other suggestions on how to handle this?
I do not know if this is a good idea or not, but here are a couple different options:
A: Use the first set of on1 / os1 for the item 1 and add the order id to that.
B: In the custom field, I am not sure what you have in there, but you can make it something like orderidhere41|otheritems here and then just parse this out by exploding at the | to get them separated.
Paypal does limit this, and I do not know why, but both of those should work. Doing it as an on / os will put it on the paypal receipt for the user, so that is my preferred method.
If someone else has a better solution, I would be interested in it as well!
EDIT:
Clarifying on1 os1. These are "options" generally used for Size / color etc. See IPN PDT Paypal variables under option_name1 option_selection(sp) for more information on them. The name of course is the title which would be "Order ID" the os would be the actual id.
EDIT:
Looking through that documentation $my2CO->addField('cart_order_id', rand(1, 100)); is where I would put my own cart order id. That should be the correct field. Sorry for the confusion :)
EDIT:
In the end there is a custom field for the paypal IPN, called "custom" adding data to this will pass through, this will transfer the orderid for you to and from. It must be called custom on both sides.
(this may be different for the other API's).
My experience has been with the Express checkout via C#, but the process should be the same even in PHP. If you're using the Name-Value Pair (NVP) interface right before you redirect the user to PayPal you hit the PayPal site to retrieve the redirection URL. As part of their response they pass back a token to you. You save this token along with your order. When the IPN postback occurs you get this same token back which lets you look up the original order.
The process flow looks like this ("You" being your site):
User fills cart, clicks button/link to check out
Request is sent to your site
Your site receives request, sends data to PayPal
You get an initial response from PayPal which contains a token
You save this token along with this user's shopping cart.
You redirect the user to the link returned by PayPal
User is redirected to PayPal and enters payment info
Payment info is validated by PayPal
User is redirected back to your site
PayPal sends IPN response back to your site
You grab the token included in the response
You look up the token you previously saved to find the shopping cart (they are the same value)
You close out the initial order/shopping cart.
You might want to try out the PHP SDK - scroll down to the Name-Value Pair Interface.
I very much doubt this will help the original poster, but PayPal have either added, or finally documented the option "invoice" which allows you to post the OrderID, and have it posted back via the IPN.
I am currently using this and it seems to work as expected.
For further info see the Website Payments Standard Integration Guide (PDF): Appendix A, Table A.4
You can create and post a custom pass-through variable that's 255 characters long that could hold any data. More info on page 44 in the official Paypal IPN Guide.