Catching duplicate PayPal IPN payments - php

I have a recurring payment solution set up via PayPal's IPN service, basically the user fills in a form, pays the money and via IPN my system gets a ping to grand the user access to the system.
Everything technically works fine, but occasionally on the last day of a user's cycle they forget that it is a recurring payment and complete the form again. PayPal doesn't seem to mind this and creates a second recurring payment profile.
Currently I go in, refund the money and cancel this new payment profile, but obviously this isn't ideal - is there any way I can configure PayPal to not accept new profiles from people with currently active profiles? Or will I need to catch this at my website's end and do some form of lookup before allowing the payment?

Paypal did not recognize the a transaction is a duplicate one, It tackles the transaction as a new transaction. You will have to tackle it from your system.
When implementing payment gateways, It is good approach to save the billing with a status pending or something in your system before sending to payment gateway.
Also in the form user is filling you can implement a field to uniquely identify the user like his email address and you can also save the expiry or second recurring date against the user unique field in your system. Now when the user enter the same unique field and want to submit a form again you can validate that the same user is paying again but its expiry of second billing date is not yet reached, so prompt and restrict him for paying again.
Hope it will help you.

Related

Stripe checkout event for failed payments

I have a paid membership site which takes payments via Stripe Checkout (server integration). When a user registers, they are redirected to the Stripe checkout (with their ID passed as the 'client_reference_id') where they enter their card details. For the checkout fulfillment, I have a webhook setup that is linked to the event 'checkout.session.complete'.
Everything is working fine, the webhook triggers and the endpoint processes the data and marks the corresponding account as activated, sends an email to the account holder as well as the site admin to let them both know the account has been created and a payment has been successful.
However, if a payment fails, I don't know what event to attach to the webhook to detect failed payments via Stripe Checkout. The only checkout event documented is 'checkout.session.complete', there isn't one for something like 'checkout.session.failed'. There's 'invoice.payment_failed' but this is used in subscriptions not one-off checkout payments.
We're wanting to basically notify the site admin when a payment fails so they are aware an account has been created but it still awaiting payment.
Is there a checkout event I'm missing or another way to go about finding failed checkout transactions?
To charge a credit or a debit card, you create a Charge object. You can retrieve and refund individual charges as well as list all charges. Charges are identified by a unique, random ID.
-- From the Stripe API Documentation.
You can then detect a webhook response of charge.failed to show that an attempted use of a charge object has failed.
Stripe used to be very cleanly documented, but they've lost their way a little, recently.
Stripe card payments ALWAYS use Charge objects, sometimes these are set by you (invoices, subscriptions, etc.) and sometimes these are set behind the scenes by Stripe ("Checkout Process", etc) -- but they are always set, so your webhook can always detect a charge.failed event.
Addendum
This does work, however, since the client_reference_id is not in the charge.failed data, I can't link the failed payment to a specific account anyway. But in regards to my actual question, this event does work and is the correct answer, just unfortunate that it doesn't solve my specific situation.
To solve this; either using the Stipe interface or by coding:
1) Create a Customer object and keep some sort of record of this customer Id.
2) When the Stripe Charge runs, an associated $charge->customer is set. This is available to your webhook.
3) When the webhook result appears, use this (typically) $event->data->object->customer value to cross reference the charge with the correct customer on your server.
4) As mentioned in comments, Stripe Webhook Testing does not populate the webhooks with example data such as ->customer... :-(
I know this is a bit late for a reply - but I have a working solution!!
When I create a "Session" in Stripe, I make sure to fill out the SessionCreateOptions so that the PaymentIntentData.Metadata field is setup to have my customer's ID and stuff in it. When I do that, the metadata not only shows up in payment_intent.created and payment_intent.succeeded webhooks, but it ALSO shows up in charge.succeeded/failed!
This means that when a payment fails I can look up the attempted payment that I keep track of and email them back letting them know that it failed and why, and I can also log it for myself for reference when I go look up that customer in my customer service tools.
It's not clear from the documentation and I spent a lot of time grokking how it works, so I hope this will save you some time.
The checkout session CANNOT fail (!)
A Checkout Session can have one of 3 states: "open" is an active session which can be paid, "completed" is paid ("happy path"), and "expired" if within the expiration time the session was not completed.
A session can only expire with two options:
After 24 hours if not paid (or sooner, if you set a shorter expiration time https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-expires_at)
Manually by calling https://stripe.com/docs/api/checkout/sessions/expire
A session's URL can be accessed unlimited number of times for payment attempts until it is either paid or expires. This is by design, so a user can have a couple of failed attempts to pay until she actually pays. With a session, you do not care about those attempts, and in most cases you do not need to track them.
So one option for your workflow can be the following, for example:
For creating a new order, create a new session, save it's ID in the order's record in the DB, and redirect the user to the session's URL
On the back-end, listen for "session completed" event, which is the happy path - everything is paid (watch out for the async payments, if you accept any! See herehttps://stripe.com/docs/payments/checkout/fulfill-orders#delayed-notification)
Also listen for "session expired" event. This will tell you that the user bailed and hasn't paid the order. You can send them a "failed payment, try again" email in this case (see below for Recovery workflow)
If the user returns to your website before paying for the order, you can display "NOT PAID, TRY AGAIN" message next to the unpaid order.
Attention: it is perfectly safe for the user to open a dozen of the session's URL and attempt to pay as Stripe won't allow the double payment after card information is submitted.
So you are thinking about creating expiring the old session and creating a new one for this unpaid order? Wrong! You can retrieve it by ID (you saved it in the DB, right?) and send the same URL to the user.
However, if the retrieved session is "expired", there will be no URL to send, so you will be forced to create a new session, save it in DB, and the whole cycle repeats.
Another possibility is for the repayment workflow to happen in rare state between the payment and "session is completed" webhook event (the user already paid in another tab, but you haven't received the "paid" event yet), you will get that the retrieved session state is "completed", in this case you will need the capability for the front-end not only to redirect the customer to a session's URL, but instead sometimes show a message like "nope, it's already paid".
As you can see, there is no "failed" state for the checkout session.
Now, another thing for recovering the unpaid and expired sessions is the Recovery workflow (https://stripe.com/docs/payments/checkout/abandoned-carts). The gist of it is the following:
When creating a session, you set "recovery" to true, so if the session expires, you will have access to a special URL that will "revive the same session with the same parameters" that will be active for 30 days after the original session expiration. You then send this URL to users with possibly some discount to recover their abandoned cart. Also, you can track whether the session is "original" or "revived". However, I did not use this way, and instead opted to simply creating a new session manually if the old one is expired.

Get Paypal's transaction using the REST API with only a receipt number

Okay so I've setup a website with a basic paid subscription using Paypal. Here's the general flow of things:
1) A person fills up a basic subscription form and click subscribe
2) They get forwarded to Paypal who handles the payment
3) User clicks on "back to website" button
4) Account gets activated and user is forwarded to splash page
The thing is that if the user decides to close the window instead of clicking "Back to website", then his account will remain locked even though he paid (and yes, I'm aware you can skip the "back to website" button, but my client wants to keep it).
So, what I'd like to do is basically implement a form that will query Paypal's REST api to check if the user's receipt number actually exists and if so, complete his subscription. The problem is that most of the users pay through credit card, not Paypal... so all they have is a receipt number.
I've searched through the documentation and I can't seem to find how to query Paypal's REST API using the receipt number. Can anyone point me in the right direction?
I use the IPN service provided by PayPal. With this, I pass a custom field along with the transaction containing the user's account number (you could use receipt number as long as you have it stored). I have an IPN Handler script that receives the verification from PayPal that the transaction completed successfully. This script receives the custom field, which can then be used to locate the user's account and update their subscription status.

Custom Paypal Cart (Payment ID Number)

We are working on finding a solution for a custom PayPal cart install. I'll try to explain what actions are taking place then point out where we are having issues.
Here is the issue that we are facing:
1.) Users of our site can purchase a subscription
2.) That subscription once paid for activates several actions on the user’s account.
3.) Subscriptions last for Xx amount of time.
4.) Each hour our cron page runs and checks for payment received or not.
5.) After Xx amount of time (the subscription length duration) if the payment is verified to have been received the users account automatically renews. If payment is not verified to have been received then the account we return to normal.
6.) We have it operating correctly in the scenario if the payment is made from one PayPal account for a single user subscription.
7.) However, if a user uses one PayPal account to purchase multiple subscriptions (for this example 10) and cancels X amount (for this example 9) of subscriptions from within their PayPal account, none of the accounts get cancelled from within our site. Say a office manager submits payment from one single PayPal account (because they are in charge of 10 users accounts). Then immediately cancels 9 of the subscriptions from within that sign PayPal account and leaves one active account in our current setup it still thinks a payment was received for all 10 of the users because it’s checking the account rather than individual PayPal subscription for payment. (Tricky to explain)
** What I am are trying to figure out is how to have this action record the Payment ID # that is given by PayPal for each individual subscription when a purchase is completed and when the cron page runs it would pair the user’s (our site user) account to their Payment ID # and take action accordingly. So this way if 9 of the payment ids did not receive a renewal payment those accounts would become inactive and the one payment ID subscription that did receive payment would active properly.
I hope that makes sense and I would really appreciate anyone's help on this. Thanks a ton!
You should be using Instant Payment Notification (IPN) to handle automated updates based on actions that occur on the PayPal account including new profiles, payments, failed payments, canceled profiles, etc.
Any time a transaction occurs on your account (payments, profiles, refunds, disputes, etc.) PayPal's server will POST all of the transaction data to a listener script you have sitting on your server. Within your script you can process that data however you need to, and it happens in real-time.

PayPal subscriptions PDT / IPN - PLEASE

Am having a lot of trouble getting my head around this paypal payment stuff...
How can i confirm that the user has successfully signed up for my subscription?
I know 0 about IPN but for example : If a user signs up to my website with example#e.com but uses the paypal account sample#s.com to pay then how to i match up the user.
I read that PDT do not send out a transaction ID (tx) for recurring (subscription) payments is that true ?
Just need help with it all... honest and easy :)
Thanks.
Yeah sometimes is hard to understand Paypal and all their documentation but for any kind of subscription i would recommend Paypal IPN. You set IPN URL in your paypal account for example: http://www.domain.com/ipn.php. Then you create a subscription button in paypal account where you can set the price, recurring price etc.
More about Paypal IPN you can read here:
https://www.paypal.com/ipn
When creating Paypal button you can also add some custom fields which can help you determine which customer really pays. So for example you have a database of users and there is user with userid=100, username=Nickname and registered_email=xxx#gmail.com. Now you can decide to add userid=100 as a custom field and add this to paypal button. For example CUSTOM=100.
ipn.php in this case is a script which handles all the logic. When user pay, Paypal send any status to http://www.domain.com/ipn.php (you set this in your paypal account as IPN URL). So ipn.php in this case needs to check if payment is complete and status is ok. Then retrieve a CUSTOM field ($_POST['custom']) which is in this case 100 (userid). And then update your database with users and somehow mark that this user payed of course if status is Completed.
Hope this helps a little. But in this case it's not really important from which paypal account user pays and which is registered in your database. All you need is that you somehow link it together. In this case with userid or something like that.
If you want to implement Paypal IPN and your customers don't use same email from my experience you can use a form to authenticate the user
user login on your website with xxx#example.org
user clicks on your item and pays with yyy#example.org
after he pays you can redirect him to a form where they can merge Paypal account with website account
every time that user pays then your IPN will be covered by the information he provided
you should save his payment information for later use.

Paypal Recurring Payments - How to prevent duplicates?

So I've been using paypal recurring payments for about a year now, and for the most part it works well (with IPN verification after payment goes through, etc...)
One issue I have run into is sometimes my customers will set up multiple payment profiles, which I definitely don't want them to do. Then I have to go in + refund payments and cancel additional profiles.
I was curious if anyone had any suggestions to prevent users from doing this, or WHERE I should prevent this from occurring.
After the IPN notification hits my server, users can't purchase again, but sometimes this can take 30 seconds, or an hour. So then my user goes back in thinking it didn't go through and purchases again.
My process is pretty much this:
Page 1 (user selects options)
Paypal API: SetExpressCheckout
Page 2 (user goes to paypal, logs in, accepts agreement)
Page 3 (I create the agreement)
Paypal API: GetExpressCheckoutDetails
Paypal API: CreateRecurringPaymentsProfile
I would think somewhere on Page 3 I should do a check, but I'm not 100% sure HOW I should go about it. Simply make an association w/PayerID they they tried to purchase something here then prevent it ongoing?
What do people recommend?
Thanks!
After creating the first recurring payment profile, in the response you will get a profile id and a profile status which should be 'ActiveProfile'. I would recommend that you save it, and do a check before he selects any subscription(or product) again on Page 1.

Categories