The transaction notifications sent by PayPal will slightly change. We need to adapt our application to be able to process them. Please refer to the [investigation documentation](https://docs.google.com/document/d/1UzOKLG9mk9kWO3wRKZKGBMevnVJc3qdVYNNY9nb-u8o/edit#heading=h.qeqkx356gbyr) for more details.
**Acceptance Criteria**
- Payment notifications for recurring payments (both from legacy and API) can be processed by our application.
- Dependeing on the payment, the notification controller dispatches to the correct notification use case (donation, membership). Since memberships are not implemented, the controller should log an error instead.
**Implementation Notes**
- To get a data for "real" IPN (needed for the test cases), you can activate the API on the test server and make a PayPal donation with our test account. Paypal will send an IPN, that will be rejected (because of its unknown transaction type) and be logged in `logs/application/paypal.log` on the server.
- The transaction types (`txn_type`) for recurrring payments will be:
- `recurring_payment_profile_created` (you can ignore this, it might get relevant later when we handle memberships)
- `recurring_payment`
- The transaction type (`txn_type`) for one-time payments will be `cart` (see {T344839})
The Fundraising Application needs to be changed to handle the new transaction types:
- Create a a "lenient" authorizer implementation that always returns `true` for `systemCanModifyDonation`. Background Info: With API payments we no longer have an `updateToken`. But that's not a problem since we have other means to make the end point secure.
- Construct the `BookDonationUseCase` (for PayPal) with the new lenient authorizer
- Create a new service class (e.g. `ConfirmationUseCaseDispatcher`) for looking up donation/membership ID and type using IPN data: It should look at the transaction type to determine the name of the ID field (`recurring_payment_id` for `recurring_payment`, `txn_id` for `cart` ), use the database table `payment_paypal_identifier` to get a payment id, `payment_id` field in donation and membership tables to get to the type (donation/membership) and the entity id. Dispatch (with similar code as you can find in the `HandlePayPalPaymentNotificationController`) as follows:
- if the transaction type is neither `recurring_payment` nor `cart`, dispatch to `BookDonationUseCase` (using the existing logic in the controller)
- if payment is for membership, log a "not implemented" error
- Do the current lookup of the donation (by Id) for all other `txn_type` values, calling `BookDonationUseCase`
- Make the `ConfirmationUseCaseDispatcher` resilient: the transaction ID for a one-time donation is not found in `payment_paypal_identifier`, that might be a race condition, occuring when PayPal sends the notification after the use case from {T354963} has issued the capture API call but before it stored the modified Order with transaction ID to the database. In this case (transaction type is `cart` but `txn_id` was not found in `payment_paypal_identifier`), try to read the database again after an increasing amount of seconds (flushing query caches in between) before failing. The retry mechanism should be an interface, so in production we can have an exponential backoff (e.g. 1, 2, 4, 8, 16 and 32 seconds) but when unit-testing the route we can update the database after the first or second try
- Use `ConfirmationUseCaseDispatcher` in `HandlePayPalPaymentNotificationController`.
- Minimum suggested additional test cases in `HandlePayPalPaymentNotificationRouteTest` that check the new payment data:
- `recurring_payment` & matching `recurring_payment_id` for donations should be handled
- `recurring_payment` & non-matching `recurring_payment_id` should be dropped (and logged)
- `cart` & matching `txn_id` for donations should be handled for donations
- `cart` & non-matching `txn_id` should retry until either the donation is found or the maximum limit of retries is reach (it should log then). 2 test cases
- `cart` & non-matching `txn_id` for donations should be dropped (and logged)
- `recurring_payment` & matching `recurring_payment_id` for memberships should log an error