Moving Order Creation in Ecommerce

I’m one of the engineers at edX currently working on https://github.com/edx/ecommerce. We’re looking at potentially changing the order creation flow so that orders are created before payment with a pending status, and then transitioned to either a failed state or the rest of our current order pipeline once payment goes through. As this is likely a pretty big change, I wanted to get a sense of how others were using ecommerce to try and see whether or not this is something that we should make optional for openedx (if we do decide to do it). Comments, questions, and concerns are welcome

This might cause us problems here at EDUlib https://cours.edulib.org

In order to use the payment processor used by the Université de Montréal, Ecole des Hautes Etudes Commerciales and Ecole Polytechnique here in Montréal, we had to “adapt” the Cybersource code in order to make it work with the hosted payment solution at Paysafe (previously Netbanx). Our finance department would not let us use Cybersource or Paypal for “political financial reasons” :slight_smile: We needed to use the payment processor we were told to use. Since the bank with whom the University deals with uses Netbanx, we had no choice but to make it work with Netbanx (which became Paysafe later on).

I wasn’t directly involved in the modifications we made to the code but I know for sure that the code does need to include the payment as part of the order. Everything “live” for a lack of a better word. No batch transaction or offline transactions. That was my comprehension of the issues we encountered at the time which was about 3 to 3.5 years ago.

If needed, I could point you to our modifications in https://github.com/EDUlib/ecommerce/tree/edulib-ironwood.1 (most notably in https://github.com/EDUlib/ecommerce/blob/edulib-ironwood.1/ecommerce/extensions/payment/processors/paysafe.py).

There were also other modifications needed in a few other files in ecommerce but I cannot remember out of off the top of my head right now. A “git diff” would probably turn up a few of the major differences in our fork.

When we install ecommerce we also need to include the SDK from Paysafe / Netbanx. Therefore we do have an installation process that needs to include the SDK as part of the installation. Instructions available upon requests. I still haven’t found a useful way to have the SDK repository be included as part of our ecommerce fork.

We are still using this version https://github.com/EDUlib/Python_SDK-1 of the SDK. it’s a fork from https://github.com/OptimalPayments/Python_SDK. We might need to switch to a more recent version (most probably this one https://github.com/paysafegroup/paysafe_sdk_python) soon when Open edX (and ecommerce) moves to Python 3. That was already part of our plans in the near to middle future.

If you have any questions, please do not hesitate to contact us. We are really interested in any changes that might impact our ecommerce implementation through Open edX and Paysafe.

Ok that’s good info to have. We’ll try and take it into account. Do you think this old flow could eventually be deprecated, or should we be sure to have the entire thing guarded by a site configuration or something else?

Obviously we’d like to support as little as possible, but we also understand if that would cause problems

I can’t answer you definitively right now. I am exploring a few things.

For example, I have been trying for the last few days to run our ecommerce fork with Python 3 and using the Netbanx SDK we currently use in Production. Right now, I cannot even log in on the ecommerce console. Strange error messages with regards to the redirect_uri, scope and other values even after I force the values of some variables.

But if I try to login through the Django Admin login then yes I can access the ecommerce dashboard. I also can’t communicate with the payment processor or create new courses in the Commerce Administration Tool. I am still trying to figure out what is going on.

Another engineer is currently working on our python 3 upgrade process as well and I believe there are a number of issues they’ve been working through. Unfortunately I don’t have much more information on that right now

The major problem I am facing right now with Python 3 is that the redirect_uri adds

a ?redirect_state=VzOURTd1bt3fSAakkNmG2m2rp6mv4YpE (or a different value) each time I try to login to ecommerce and as a result the redirect_uri is not the same as what is expected by oauth2.

I’m sorry but I’m not familiar with that issue or the specifics of our authentication flow

Could you share some more about what it is in your order creation that relies on the payment happening first? I’d like to get some more information there because it seems to us like the order creation flow where orders are created after payment is a bit of a bug, as suggested by one of the django-oscar maintainers here: https://github.com/django-oscar/django-oscar/issues/2891#issuecomment-443398648

I was able to fix the redirect state problem after finding a message after a similar error in another Django App. It seems it needed to absolutely run under https and my ALB wasn’t expecting https for my test and neither did Nginx. That part if fixed.

I have another issue now about the system requiring credit providers. We have none and we do not expect to have any. For the time being, I cannot list the courses under /courses or create any new courses. Still trying to figure out what could have changed to force us to have credit providers for the courses we currently have.

What I really meant is that the order and the payment must go hand in hand.

We could not figure out how to do asynchronous payment with Paysafe. That’s what I meant by payment must happen first. Maybe I was explaining it wrong?

Therefore, the steps are at a 1000 feet : user buys a certificate, user is redirected to the hosted solution at Paysafe, user fills in the form at Paysafe, user submits form at Paysafe, payment goes through Paysafe, the system waits for the confirmation, confirmation from Paysafe comes back, LMS / ecommerce does it’s magic to change the user from audit to verified, the LMS / ecommerce displays the receipt to the user. That’s my understanding of the steps we see.

As I explained in a previous post, we took the cybersource.py code and adapted it for Paysafe and their SDK. Some files were changed in our ecommerce fork (https://github.com/EDUlib/ecommerce/tree/edulib-ironwood.1) as a result.

We had to make some modifications to the following files:

ecommerce/extensions/checkout/mixins.py
ecommerce/extensions/fulfillment/modules.py
ecommerce/extensions/payment/exceptions.py
ecommerce/extensions/order/utils.py
ecommerce/extensions/payment/migrations/0006_enable_payment_processors.py
ecommerce/extensions/payment/processors/paysafe.py (this is where most of the modifications went)
ecommerce/extensions/payment/urls.py
ecommerce/extensions/payment/views/paysafe.py (this is where most of the modifications went)
ecommerce/extensions/refund/models.py
ecommerce/notifications/notifications.py
ecommerce/templates/customer/email_base.html
ecommerce/templates/customer/emails/commtype_course_purchased_body.html
ecommerce/templates/customer/emails/commtype_course_purchased_body.txt
ecommerce/templates/edx/checkout/receipt.html

Some of the modifications were minimal, we had to add Paysafe here and there whenever there was code for cybersource or paypal so that ecommerce would recognize Paysafe as a new payment processor.

The major changes were in the payment processor and its related views. We had to scratch our head at first in order to make sure Paysafe was getting the right value from the basket or from the order. Our goal was to make minimal changes to the Cybersource code and adapt it to Paysafe. It finally took the contractor who worked on it a few months to get something working. Nonetheless, it takes us a few hours or days each time after we do a rebase for a new open-release to test everything in our test environment and make sure we can still do transactions.

If you need more details, let me know.

I’ll try and look around some more on this, but the team involved with publisher/discovery which works most closely with this hasn’t made any changes to credit provider.

Maybe I’m misunderstanding, but I don’t think that switching to an upfront order creation would cause issue with your flow. The new flow would be something like:

user tries to buy a certificate, order is created as pending, user is redirected to paysafe and does all the stuff there, then if payment confirmation comes back from paysafe the order switches to open and the rest of the usual flow happens. Alternatively, if the payment to paysafe fails, then the order would be moved to a failure state, in which case whatever your customer support structure is would likely take care of it.

Let me know if I’m misinterpreting or could explain that better

That sounds about right.

I am probably the one who originally misunderstood what you were trying to do. As I said, maybe I was thinking of asynchronous orders. For example, the user tries to buy a certificate, submit the payment without waiting for a confirmation from Paysafe, the user goes back to do whatever he wants in the LMS, then at one point the payment confirmation comes back from Paysafe, and then the user is set as verified because the order went through. Right now, the user has to wait for the confirmation to come back. I was afraid this would change. Sorry if I misunderstood what you are trying to achieve.

1 Like

No problem at all, it’s a bit of a tricky thing to explain. Thanks for your patience and informing me on your organization’s particular use of ecommerce. I’ll let you know if I find anything more about the issues with credit providers that you’re seeing on courses

1 Like

With regards to my credit provider issue, I wonder if it is a JWT issue.

< code from master

code from ironwood.master

1,2d0
< from future import absolute_import
<
12a11

from edx_rest_api_client.client import EdxRestApiClient
15a15
from ecommerce.core.url_utils import get_lms_url
50c50,53
< credit_api = self.request.site.siteconfiguration.credit_api_client


        credit_api = EdxRestApiClient(
            get_lms_url('/api/credit/v1/'),
            oauth_access_token=self.request.user.access_token
        )

Something definitely changed in the way the credit_api is called between ironwood.master and master.

I do have the following variables in lms.env.json under edxapp, so I do not know why it tries to retrieve the credit api

    "ENABLE_CREDIT_API": false,
    "ENABLE_CREDIT_ELIGIBILITY": false,

The check should be done before accessing the credit providers, right?

Nov 21 17:49:48 ip-10-0-0-71 [service_variant=ecommerce][ecommerce.courses.views] ERROR [ip-10-0-0-71 22625] [/edx/app/ecommerce/ecommerce/ecommerce/courses/views.py:57] - Failed to retrieve credit providers!
File “/edx/app/ecommerce/ecommerce/ecommerce/courses/views.py”, line 51, in get_credit_providers
credit _providers = credit_api.providers.get()
slumber.exceptions.HttpClientError: Client Error 401: https://test-cours.edulib.org/api/credit/v1/providers/

I have courses in the ecommerce database but they are not being displayed under /courses