The PayPal implementation has been implemented in C++ following the recently developed REST API.
The current implementation supports straight sales and recurring payment plans with setup fees. Recurring payments are limited to ONE recurring product and any number of optional recurring payment setup fee products. All payments are using the PayPal payment method (opposed to PayPal Pro that allows you to directly charge credit card on your website.)
The way the system works with the REST API is different from the SOAP API most of us have been used to. The main significant difference is that payments are done in realtime. This means you do not have a URL that gets hit at a later time when the payment finally went through. Instead you get the user to authorize the payment, then you execute it immediately upon return of the user.
The following shows you the process of a direct sale:
Note that the OAuth2 steps are skipped if the last OAuth2 authorization token is still valid. At time of writing, PayPal gives authorizations that last 8 hours. So it is likely that you will skip that step often throughout the day. It is very fast anyway. (i.e. the dashed arrow shows you what happens if the checkout process finds a valid OAuth2 token; these are saved in the PayPal settings page: /admin/settings/epayment/paypal -- in the secret table.)
The HTTP POST which are followed by an HTTP response are immediate. The response data includes all you need to know to move forward to the next step.
The Approve Payment step is either showing the user his account or his credit card payment information, depending on what he or she selected in the step prior. In any event, once the user says clicks on the Continue button, they are sent back to the Snap! website where it can execute the payment immediately. (This is were it changes dramatically from the old interface that would call an IPN later, possibly minutes or hours later...)
TBD -- I have not seen a way to pay with an account that requires a check to arrive. This was allowed in the old API and would justify a 2 or 3 weeks delay for the IPN to be called. I'm not too sure how that could work with this new interface.
The "Execute Payment" page has the following URL:
https://www.your-domain.com/epayment/paypal/return
It is used to actually execute the payment and then send the user to the "Thank You" page. We use a redirect to avoid users doing a Reload on the return URL which could cause problems (i.e. attempt to execute that same payment twice.)
Here PayPal made it a bit more complicated. A recurring payment requires a plan, then you can attach a user to said plan.
For this reason we have a limit in our implementation where only one single plan can be purchased at a time. In most cases that should not cause any problems, though, since you very rarely ask someone to subscribes to more than one plan anyway.
So, recurring payments proceed in two steps. The first one is to create and activate a plan. Then the second step is to create an agreement for the user to agree to. If you don't understand what I just wrote, that's quite normal. I did not get it either at first.
Since we can assign any number of users to the same plan, we create one plan per product marked as subscriptions. We use the secret table to save the PayPal data such as the plan identifier.
There is the process of creating and activating a plan (a created plan status is "CREATED" and a plan in such a state cannot be used to assign users to the product.)
Just like with a regular sale, we need the OAuth2 tokens which we gather if none are currently available.
Next we create a plan with a POST and then update it with a PATCH. The update requests PayPal to change the state parameter from CREATED to ACTIVE. It should return with an HTTP code of 200 (although the documentation says 204.)
With a plan token, we can now create an agreement. The REST API does not explain at all how to pass that parameter, luckily there is an example in the document that shows that all you have to do is pass the plan identifier (i.e. the JSON includes the following: "plan": { "id": "..." }.)
The following shows the process which is very similar to a standard sale, only the order to create the agreement and the responses are different.
Note that the Execute Agreement page is a different page than direct sales since the agreement execution is somewhat different from a sale payment execution. The plan Execute Agreement page uses the following URL:
https://www.your-domain.com/epayment/paypal/return-plan
Then the user is redirected to the Thank You page with a GET. We use a redirect to avoid users doing a Reload on the return plan URL which could cause problems (i.e. attempt to execute that same agreement twice.)
Note that the creation of an OAuth2 token and of a plan are shared between all users (although for a plan, it is specific to each subscription product) and therefore the system makes use of a QCassandraLock to make sure it is synchronized appropriately. It is really not likely to cause any downtime, unless you have a really large load of users all using PayPal simultaneously.
The current version does not redirect to a Thank You page. Instead it shows that one specific page. This means users could do a Reload which can be a problem.
Snap! Websites
An Open Source CMS System in C++