How to get Order ID of a subscription payment from a Transaction from paypal API - paypal

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.

Related

How to prevent users with Paypal subscription having trial to cancel and have another trial period

I have a SaaS with Paypal subscription.
In Paypal we can have a trial period as a part of the plan, which is managed by Paypal.
Is there any pattern to organize account deletion and subscription cancelation? I see some options?
Users can cancel subscription, but if they want to make a new subscription - they will get a new trial since it's a new subscription. I could get another plan without trial for this, but can't they substitute plan ID with the one with trial with some HTML knowledge?
Subscription cancelation can only be done together with account deletion. User can suspend subscription if he doesn't want to delete the account. Suspension is not exactly like cancelation, looks incomplete, like you may pay accidentally at some point.
Yes, a plan_id on the client side can be changed. You could conceivably create the subscription using the create subscription API call and fetch the ID of the created subscription instead, with a pattern similar to https://developer.paypal.com/demo/checkout/#/pattern/server
Subscriptions can always be cancelled in the payer's PayPal account.
Whatever solution you decide on, register for the webhook PAYMENT.SALE.COMPLETED and build you logic using that. For reconciliation, add a custom_id when the subscription is created; this can essentially be your own user ID or whatever other unique identifier you want.

Paypal v2 API how to get transaction data and also how to make a refund

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

Automatically get a different status for an authorized vs. captured payment?

Is there a way or an extension out there that allows a Magento order to get a different status automatically once a payment has been captured?
The reason why we're looking for this is because we connect to ShipStation (www.shipstation.com) for shipping and eCC Webgility (www.webgility.com) to post orders to our accounting software. We need a more automated way for those orders that get pulled in for us to know whether the order has had funds captured or not. The only way for us to do this now is manually change the order status since Magento's default method keeps the order in "processing" regardless of it being invoiced or not.
You'll be able to do all of that with an Instant Payment Notification (IPN) solution.
For any transaction that ever hits your PayPal account, whether it's a payment, a refund, a cleared or failed pending payment, disputes, etc. their server will POST the transaction data to a listener script on your server.
Within this script you can use that data to update your database, generate custom email notifications, hit 3rd party web service API's, etc. all automatically, in real-time.

When to stop querying paypal api's for payment status?

Using the parallel payments paypal api. If user 1 makes a payment to user 2, I make a check using the api to make sure paypal returns the payment details as COMPLETED.
How long does paypal keep that COMPLETED payment record? Do they keep it indefinitely, or do they delete it after a while? I ask because I am at the stage of development where I need to decide if I should rely on paypal keeping that record indefinitely, or if I should create a record on my server that the payment has been completed, or if I should always check if the payment has been marked as COMPLETED by querying paypal?
The only reason why I "want" to check via paypal, is if the payment is ever returned to user1 as REFUNDED or PARITALLY_REFUNDED when paypal is queried using their apis. I would want to act accordingly in such situations.
It's not too much of an issue if the status is REFUNDED or PARTIALLY_REFUNDED as apparently people can't get refunds if they don't open a case with paypal before 45 days are up. I am more concerned about the REVERSED status, which can apparently happen any time, even after 45 days which is beyond paypal's control, as it is done by the credit card companies, if the user pays by credit card...
Using the PaymentDetails request I was able to pull my oldest Adaptive Payment transaction, which was over 60 days old and had no problems pulling it up using the transactionId field. It should be able to pull up payments as long as they are listed in the PayPal account (currently forever.)
This will work using the payKey field also if you are storing that instead of transactionId, however the transactionId is displayed in your PayPal account and is sent with IPN responses.

Execute tasks after PayPal chained payment is successful

Current Status
I'm quite new to PayPal and I'm currently integrating chained adaptive payments on our website. Already successfully called the PAY API call via the .NET SDK. Money (sandbox enviroment) will be correctly transferred to each participants of this payment (sender, primary and secondary receiver).
Requirement
My goal is to execute vital tasks after the payment is successful (update DB, send mail, ...) or has been cancelled (clean up stuff, ...).
Possible solutions
1)
First approach was to create the payment with actionType set to CREATE, redirect the user to paypal.com (wait for approval), redirect user back to website and execute the payment and then perform the vital tasks. But it seems not to work, the payment will be paid and is COMPLETED before the second redirect.
2)
Another possible solution would be to get the preapproval from the user, redirect back to the website and execute the payment. Haven't tried this solution yet, don't think that this is best practice.
3)
Call PAY with actionType set to "PAY" and wait for IPN. Haven't tried that either, because it is quite difficult to test it locally (even though I've already found this question: Paypal Sandbox Test Tool IPN Simulator in Localhost).
Question(s)
Which solution is best practice? I guess the recommended solution would be to wait for an IPN?
If I'am using IPN how long is the average response time after a payment has been completed? Seconds, minutes, hours? I know it depends on the load of the PayPal webservers and that there are 15 retries over 4 days, but what are some real world numbers?
Can I store additional information (e.g. UserId) about the sender in a payment (besides the memo field) which I then get back in an IPN?
Here is how I do it.
When I'm going to initiate PayPal payment (before I send a request to obtain TOKEN), I create new transaction in my database and set it's status to PENDING. In transaction table I also have userID column, which is foreign key to user table. This way I connect transaction with user.
When transaction is created in my table, I use transactionId value, and save it to PHP $_SESSION variable.
Please note that if you want to support recurring payments, you can provide transactionId to PayPal. This you can do by setting:
"PAYMENTREQUEST_0_INVNUM"=>$transaction->id.
This value will be sent when PayPal sends you IPN request after recurring payment happens.
Go back now to the story
User is redirected to PayPal, and when user fills PayPal username and password, and when user confirms payment details, user will be redirected back to your website and you have to call DoExpressCheckoutPayment to make payment itself.
If the result of DoExpressCheckoutPayment API call is success, that means that transaction was successful and you have money. At this point, you can send email, notifications, or any other important action.
$transactionResponse=$paypal->request("DoExpressCheckoutPayment",$requestParams);//Execute transaction
if(is_array($transactionResponse) && $transactionResponse["ACK"]=="Success")//Payment was successfull
{
//Send email
//Notify user
//Do other important changes in database, for example mark this transaction as successful
Transactions()::model()->updateByPk($_SESSION['transactionId'],array('status'=>'SUCCESS');
}
IMPORTANT NOTE FOR RECURRING PAYMENTS: PayPal can/will send you several IPN requests for the same recurring transaction which means that you have to add logic which will chekc weather specific IPN request is already processed or not. Usually I do it in a way to check weather status of my transaction with transactionId is 'PENDING' or 'SUCCESSFUL'.