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.
I am looking for alternatives to stripe's per-user pricing subscription : I need :
to charge my user a recurring payment which depends on the number of user accounts he has
to be able to change the number of users via an API, with customer validation is ok, but ideally without changing a plan nor creating a new type of plan. Check status, cancel, etc. via the API too.
Ideally with no up-front charge = fee + percentage of transaction
I found out that stripe might be a good option (see per-user pricing here https://stripe.com/docs/subscriptions) and unfortunately Paypal does not seem to offer this kind of feature (plans can only be increased by 20% each 180 days or you need to cancel previous profile and create a new one). Or am I mistaking about Paypal ?
What alternatives would exist for such needs?
What you want are Reference Transactions, in which case you run an original authorization or sale transaction, and then in the future you'd run DoReferenceTransaction with the original transaction ID and any new amount you need to process. It will process immediately without any redirection or additional authorization required at that point.
If you use reference transactions with Payments Pro (direct credit cards) then all you need to do is save the original auth or sale transaction ID to your database so that you can pull it out for the user when you need to process a future payment using DoReferenceTransaction.
For PayPal payments you'll use Express Checkout w/ Billing Agreements, which will give you back a billing agreement ID. In that case, the billing agreement ID is what you'd pass into future calls to DoReferenceTransaction.
In either case you'll need to build your own system to lookup payments that need to be processed each day and loop through them making a call to DoReferenceTransaction for each one.
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.
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'.
I want to make payment process in 2 step , in first step paypal should collect fund from user account but not transfer to the merchant account .
when i send another request with sucess action at that type paypal should transfer fund to merchant account or if i pass fail action then paypal should refund to user.
is there any way to do this ?
i reefer following URL but cant find solution .
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_WPECIntegration#id0861K0T0WY4
Express Checkout is what you want, but there's better documentation available.
Basically, what you're after is Authorization and Capture. When you create your Express Checkout API requests you'll set the PAYMENTACTION to Authorization. Then, when you're ready to process the payment you call DoCapture and pass in the transaction ID you get back from Express Checkout.
No money is processed until the DoCapture call is processed. If you don't end up needing to process it you can simply do nothing, but that would strand the authorization on the user's account for the default period of time depending on their bank. Usually 30 days.
It's a better practice to call DoVoid at that point, which would cancel the authorization and release those funds back to the user's account immediately.
If you want to do the same thing with credit cards directly you can use Payments Pro. The process is almost identical, except there are actually fewer calls involved.
If you're using PHP I would recommend taking a look at my PHP class library for PayPal. It'll make all of this very simple for you, and I can provide 30 min of free training to help you get going if you want, too.