I have a problem connecting subscription payments in paypal with their respective orders.
In a nutshell, the initial payment for the subscription is made on an order.
Once the user pays, the order is approved and I get this information from the paypal API:
{"id":"2M9235603X788581X","intent":"CAPTURE","status":"APPROVED","payment_source":{"paypal":{"email_address":"email_address","account_id":"8XHXZUT5Y3CVS","name":{"given_name":"John","surname":"Doe"},"address":{"country_code":"US"}}},"purchase_units":[],"payer":{"name":{"given_name":"John","surname":"Doe"},"email_address":"email_address","payer_id":"8XHXZUT5Y3CVS","address":{"country_code":"US"}},"create_time":"2023-02-13T17:24:46Z","links":[{"href":"https:\/\/api.sandbox.paypal.com\/v2\/checkout\/orders\/2M9235603X788581X","rel":"self","method":"GET"},{"href":"https:\/\/api.sandbox.paypal.com\/v2\/checkout\/orders\/2M9235603X788581X","rel":"update","method":"PATCH"},{"href":"https:\/\/api.sandbox.paypal.com\/v2\/checkout\/orders\/2M9235603X788581X\/capture","rel":"capture","method":"POST"}]}
Right of the bat the data is incomplete, missing crucial information such as fees and transaction details. So next best thing is to get the data on the webhook.
When the payment goes through an event is triggered "PAYMENT.SALE.COMPLETED", which sends this info to my server:
{"id":"WH-6SE66006R98946535-7F814879YL577135N","event_version":"1.0","create_time":"2023-02-13T17:25:09.906Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for EUR 39.37 EUR","resource":{"billing_agreement_id":"I-0HN4N0KTWLMP","amount":{"total":"39.37","currency":"EUR","details":{"subtotal":"39.37"}},"payment_mode":"INSTANT_TRANSFER","update_time":"2023-02-13T17:25:03Z","create_time":"2023-02-13T17:25:03Z","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","transaction_fee":{"currency":"EUR","value":"1.76"},"protection_eligibility":"ELIGIBLE","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v1/payments/sale/036421861N8145017"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/036421861N8145017/refund"}],"id":"036421861N8145017","state":"completed","invoice_number":""},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-6SE66006R98946535-7F814879YL577135N","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-6SE66006R98946535-7F814879YL577135N/resend","rel":"resend","method":"POST"}]}
From the billing agreement ID (which is the subscription ID) I can get the transactions, based on a time period:
{"transactions":[{"status":"COMPLETED","id":"036421861N8145017","amount_with_breakdown":{"gross_amount":{"currency_code":"EUR","value":"39.37"},"fee_amount":{"currency_code":"EUR","value":"1.76"},"net_amount":{"currency_code":"EUR","value":"37.61"}},"payer_name":{"given_name":"John","surname":"Doe"},"payer_email":"sb-wi93p1551674#personal.example.com","time":"2023-02-13T17:25:03.000Z"}],"links":[{"href":"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-0HN4N0KTWLMP/transactions?start_time=2023-02-01T07%3A50%3A20.940Z&end_time=2023-02-28T07%3A50%3A20.940Z","rel":"SELF","method":"GET"}]}
Now why it requires start and end date even though the filter is the agreement ID, I couldnt tell you, however here I can see the fees and since the event "PAYMENT.SALE.COMPLETED" has been triggered I can be sure that the payment has went through.
All thats left is for me to connect the transaction with the order.
But how?
There is no direct connection between the order and the transaction, no ID specified and nothing shown in the approved order.
How can I connect the transaction ID with the Order ID so I can have a proper confirmation and extract the fees?
I`ve tried direct capture, pulling the data from the API and setting up webhook for all payment and subscription events, yet nothing I've seen provides the required information.
SOLUTION:
So for any future developer that stumbels on this problem here is my advice and solution.
Make sure that you conform to the Paypal API and create reference records with their Subscription ID and transaction ID.
Now the tricky part is getting the transaction right away, so the user doesnt have to wait to long for a confirmation.
The way I`ve done this is after the subscription is created and the order approved, I send the relevant data to the server and using the Subscription ID I keep sending requests to get all transactions for it using this endpoint: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_transactions
I do this request with a while(){} cycle, for 5 max attempts with 2 seconds of sleep period between requests, since the transaction is not shown for the subscription right away.
After I get it I create the reference record and if the subscription transaction has the proper status, I save it as finished.
On the webhook, when receiving the "PAYMENT.SALE.COMPLETED" event, you need to check if the transaction reference is already created and if not, create it.
This will server you for any future payments (since its a subscription)
Hope this helps
In your example, the subscription ID is I-0HN4N0KTWLMP and the transaction ID of the first payment (sale/capture) is 036421861N8145017. (I don't know where the order ID 2M9235603X788581X is coming from; PayPal subscriptions do not use Order IDs, those are for one-time payments)
With the subscription ID you can get its details with a simple GET call: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_get , no date ranges are needed.
For details on the actual payment, such as fees, either of the payments API versions may work with that ID:
V2 capture get: https://developer.paypal.com/docs/api/payments/v2/#captures_get
Older V1 sale get: https://developer.paypal.com/docs/api/payments/v1/#sale_get
Listening for the webhook event PAYMENT.SALE.COMPLETED is the correct general solution for receiving a server-side notification of both the initial and future payments.
If you need your own "user" or similar ID for reconciliation, set custom_id when creating the subscription.
My problem is as follows:
I have a business paypal account where I have a list of transactions made by my clients (buying products on my webpage). I store their orders info via paypal IPN which only sends me the transaction ID of the order. So I store this transaction ID on my database among other basic data.
Now I need 2 things:
Retrieve the transaction data via php code.
Make refunds of any transaction also via php code.
I see Paypal API v2 is the recommended as the v1 is gonna be abandoned. I'm using Checkout-PHP-SDK:
https://github.com/paypal/Checkout-PHP-SDK
But I'm facing problems only trying to get any transaction data. I read that in order to get the data i cannot use the transaction ID but the order ID instead. But how can use an order ID that I ignore?, because from paypal IPN I only receive transaction ID and that's only what I have. So how can I get the order id from this transaction id?. I know I'm probably confusing terms so sorry in advance.
When you capture an order, the immediate API response already contains all information about the transaction -- including the transaction ID at purchase_units[0].payments.captures[0].id. There is no need to use the old IPN service. Your integration should not depend on IPN in any way, that is bad design.
For refunds, use that transaction (capture) id with the v2/payments API
I have been able to create an order, get it approved and capture payment using PayPal sandbox. I would now like to test linking to the receipt for that order.
So, given that the order id assigned by PayPal is 1VC7586169694032B, based on the docs I tried to point my browser to the following locations:
https://www.paypal.com/receipt/?id=1VC7586169694032B
https://api.sandbox.paypal.com/receipt/?id=1VC7586169694032B
https://api.sandbox.paypal.com/v2/receipt/?id=1VC7586169694032B
https://www.api.sandbox.paypal.com/receipt/?id=1VC7586169694032B
https://www.api.sandbox.paypal.com/v2/receipt/?id=1VC7586169694032B
https://sandbox.paypal.com/v2/receipt/?id=1VC7586169694032B
None of these worked.
So, are receipts for sandbox transactions stored? If so, what is the correct link? If not, how can I test the feature of linking to the order receipt without creating a real live transaction?
An order id is not a PayPal transaction id. And neither one is a receipt id, much less a PayPal Here invoice id which are the docs and links in your question.
When you capture an order id, the merchant account's transaction id will be in the response at purchase_units[0].payments.captures[0].id.
That id can be looked up in PayPal.com . Perhaps you could dynamically link to the resulting details page, I haven't tested it.
Note that the merchant account and the payer account each have their own different transaction id, but it's possible to search for the "other account's" transaction id in paypal.com and still reach the details page.
Is safe to give to client payment id and transaction id from Paypal?
For example:
Payment ID : PAY-4AB012345C901234DEFABCDE
Transaction ID: 12A34567BC8901234
Transaction ID is definitely safe. The user will even receive an email with the transaction ID as a reference.
Also, I'm pretty sure Payment ID is added to the redirect URL when the payment has been made. So they are both available for the user.
Both the Transaction ID and Payment ID are simply identifiers our system uses as part of the payment process. This information cannot really be used in any way that could cause harm.
Particularly the Payment ID is used to identify a session and has no meaning to your customers so there is no harm in displaying it, though there is also no point in displaying it at the same time.
My application uses PayPal java library to make payments. Everything works fine but I can't understand one thing. What exactly is transaction code? Let's say I make a payment and then approve it. As a result I receive the JSON object which has an ID (PAY-*) and transactions->related resources->sale->ID. Both of them I store in the DB in order to show later to user (in case he wants to refund the money). But if I see these payments from PayPal customer portal, in the descriptions of payment I see non of these ID's.. Transaction code is totally different! So which one of them should I store?
Thank you in advance
PayPal generally uses a transaction ID. It's a 16 character alphanumeric string. This will appear in both your API response and the PayPal site. Assuming REST is like Classic, the Transaction ID is how you will reference the payment for any future operations (capture, refund, etc).
Please note that if you're using Express Checkout (where the customer pays via PayPal account) they will have their own transaction ID for the exact same transaction.