@淘 开源的Open edX的Ecommerce配置及本地支付接口

本文内容由@淘 老师开发及开源,请移步http://www.heyuantao.cn看全部内容。

以下内容为转载

关于OpenEdx的Ecommerce的配置

OpenEdx项目中的ecommerce和edx-platform是两个独立的组件,两个组建部署后通过Web API接口相互调用和访问数据。ecommerce的目的是为了取代edx-platform中自带的购物车,这样使得课程购买和课程播放管理相互独立,程序更容易维护。

OpenEdx的官网有如何安装ecommerce组件的介绍,单这个仅仅适用于开发版本。在安装OpenEdx的时候ecommerce一般会直接安装上,但仍需要配置。所涉及到的配置文件有edx-platform的lms.env.json、lms.auth.json和ecommerce的ecommerce.yml(/edx/etc/ecommerce.yml)文件的配置。主体过就是配置两个组件的oauth2功能。

最近在网上找到一篇博客,详细记录了设置的过程:相关博文1

(原文附在最后)


#  Ecommerce的汉化

      当前ecommerce默认的组件为英文,看到“ecommerce/conf/locale/zh_CN/LC_MESSAGES”目录中有中文的翻译文件,但是翻译的内容比较少,估计还要做大量的工作。本工作在ginkgo.2版本上进行。
一、基础性配置
      由于对ecommerce翻译所需要执行的命令和修改的配置文件不清楚,因此先从configuration的playbook文件内容入手。如下为configuration文件的部分内容:
 #playbooks/roles/ecommerce/tasks/main.yml
– name: compile sass
  shell: “{{ ecommerce_venv_dir }}/bin/python manage.py {{ item }}”
  args:
    chdir: “{{ ecommerce_code_dir }}”
  become_user: “{{ ecommerce_user }}”
  environment: “{{ ecommerce_environment }}”
  with_items:
    – “update_assets –skip-collect”             #可能时执行翻译的关键代码
  when: not devstack
  tags:
    – assets
    – assets:gather
– name: Run r.js optimizer
  shell: “. {{ ecommerce_nodeenv_bin }}/activate && {{ ecommerce_node_bin }}/r.js -o build.js”
  args:
    chdir: “{{ ecommerce_code_dir }}”
  become_user: “{{ ecommerce_user }}”
  when: not devstack
  tags:
    – assets
    – assets:gather
– name: Run collectstatic
  shell: “{{ ecommerce_venv_dir }}/bin/python manage.py {{ item }}”
  args:
    chdir: “{{ ecommerce_code_dir }}”
  become_user: “{{ ecommerce_user }}”
  environment: “{{ ecommerce_environment }}”
  with_items:
    – “collectstatic –noinput”
    – “compress”
  when: not devstack
  tags:
    – assets
    – assets:gather
       后来发现这些命令暂时用不到,只在重新汉化的时候采用得到(编译po文件时)。
      同时对edx的ecommerce的相关文档进行阅读,文档网址为“http://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/ecommerce/internationalization.html”,文档中说明了修改的地方,同时参考ecommerce的配置文件。内容如下
#/edx/etc/ecommerce.yml
….
LANGUAGE_CODE: zh_CN                                  #这个起到了比较核心的作用,将值从en修改为zh_CN即可
LANGUAGE_COOKIE_NAME: openedx-language-preference     #这个是一个cookie,可以根据这个变量的值来显示相应的语言
#将时区一并进行修改
TIME_ZONE:Asia/Shanghai      #原始值为UTC
#/edx/app/ecommerce/ecommerce/ecommerce/settings/base.py
LANGUAGES = (
    (‘zh_CN’, _(‘Chinese’)),                              #原本没有这一行,加上这一行,并将其他行屏蔽掉
#    (‘en’, _(‘English’)),
#    (‘es’, _(‘Spanish’)),
#    (‘es-419’, _(‘Spanish (Latin American)’)),
)
       上述两个文件修改完成后,在supervisor中重启ecommerce服务,界面的部分已经出现中文。
       整个流程梳理如下,ecommerce的配置文件放在几个文件中,权重从高到低分分别为下述这几个文件,这几个文件中都有关于语言的配置部分,因此在修改时先就优先级高的文件修改,然后再是低一些的。
/edx/etc/ecommerce.yml
/edx/app/ecommerce/ecommerce/ecommerce/settings/production.py
/edx/app/ecommerce/ecommerce/ecommerce/settings/base.py
      在base.py中,有一个元组,定义了ecommerce支持的语言类型,一般为多个,为了支持中文将”(‘zh_CN’, _(‘Chinese’))”,加入其中。这时ecommerce具备了根据cookie中”openedx-language-preference“的数值来显示相应语言的功能。由于ecommerce的面通常是用户从lms转跳过来的,如果lms没有做相应的设置,”openedx-language-preference“一般都为英文。为了简化修改,作者不给该变量发挥作用的余地,直接将支持的语言修改为一种,即在”LANGUAGES”元组的中将其他语言支持屏蔽掉。
       通过上述设置,页面的部分内容已经显示中文,这是“ecommerce/conf/locale/zh_CN/LC_MESSAGES”内的文件发挥了作用。但该目录内以”po“后缀结束的文件为汉化文件,但汉化的比例较少,下一步要加大汉化的程度。
source venvs/ecommerce/bin/activate
source ecommerce_env
#下面这条命令将执行语言文件的编译
python manage.py compilemessages –settings=ecommerce.settings.production
      当要对增加翻译(汉化)时,修改相应的po文件。对于汉化的过程就是修改”ecommerce/conf/locale/zh_CN/LC_MESSAGES/django.po”文件内部的内容,修改后还需要运行上述的命令,将扩展名为po的文件翻译为扩展名为mo的文件,然后重启相应的服务。
二、对新增代码的汉化和本地化
1、对代码内容的汉化
       如果需要汉化的内容涉及到了新的代码,则需要引入翻译类:
from django.utils.translation import ugettext as _
       例如作者在对Ecommerce扩展支付宝功能时,发现需要将’alipay’翻译为’支付宝’,这个显示问题在类似于如下URL时会遇到:
      并在具体的代码处使用类似如下的内容(样例):
#ecommerce/extensions/checkout/views.py
        context.update({
            ‘payment_method’: _(self.get_payment_method(order)),     #进行翻译的部分
            ‘display_credit_messaging’: self.order_contains_credit_seat(order),
        })
#self.get_payment_method(order)实际返回的的时支付处理模块的字段,实际内容为’alipay’。加上_( )这个函数用来完成实际的翻译
2、时间显示的本地化
      对于默认的模板,很多日期的显示都采用国外通用的模式,格式为月日年。对于2018年10月5日,实际显示的为“October 05, 2018″。对代码浏览后发现,Django在系统内使用了同一种时间格式,只是在显示的时候使用了”过滤器(filter)”将相应的显示方式进行了转换。在”收据”页面中,就有类似的问题,修改方式也很简单,将过滤器的格式调整下即可完成。
#ecommerce/templates/edx/checkout/receipt.html
              <dt>{% trans “Order Date:” %}</dt>
              <dd>{{ order.date_placed|date:”E d, Y” }}</dd>                    #将其改为data:”Y-m-d”,关于Y、E等字符的定义可查看django文档
            </dl>
#修改为这种方式就会显示为2018-01-02的方式,更容易阅读

 # 如何在Ecommerce中加入支付宝模块
一、对于将Ecommerce加入支付宝模块,可以参考其内部PayPal模块的实现流程。PayPal的核心代码有三部分,描述如下(本工作在ginkgo.2版本上测试):
    1、通过JS和Python脚本生成支付链接,并跳转到PayPal网站。
    2、通过Python脚本和PayPal的SDK完成用户支付完成后的转跳和交易的处理。
    为了便于代码的跟踪和调试,将ecommerce和lms支付过程中的页面转跳和操作逻辑进行记录,以便于后期扩展支付功能。为了使得该记录在后续浏览时具有通用性和易读性,将lms的站点链接统一记录为”http://lms/“,将ecommerce的站点记录为”http://ecommerce/“。
二、显示购买按钮的核心代码(第一步)
       在LMS模块中,通过注册课程系统转跳到Ecommerce模块,此时的URL为“http://ecommerce/basket/”,这个URL对应的视图函数为:
#ecommerce/extensions/basket/views.py
class BasketSummaryView(BasketView):
    def get(self, request, *args, **kwargs):
        basket = request.basket
        ….
    def get_context_data(self, **kwargs):       #这是一个很重要的函数,用于在模板中加入其它变量
       context = super(BasketSummaryView, self).get_context_data(**kwargs)
       formset = context.get(‘formset’, [])
       lines = context.get(‘line_list’, [])
       ….
       payment_processors = site_configuration.get_payment_processors()  #用于获取所支持的支付平台。
       …..
      上述这个视图引入了另外一个模块的函数,同时get_context_data是一个很重要的函数,这个函数在模板中加入了更多的变量。这个视图函数的父类为BasketView,这个类在django-oscar中有定义,参考如下代码:
#src/oscar/apps/basket/views.py
class BasketView(ModelFormSetView):
    model = get_model(‘basket’, ‘Line’)
    basket_model = get_model(‘basket’, ‘Basket’)
    …..
    template_name = ‘basket/basket.html’  #此处为该视图的模板,这个模板在django-oscar中有定义,但被ecommerce中的模板给覆盖了
       该视图实际使用的模板为ecommerce/templates/oscar/basket/basket.html  (这一步为猜测),这个模板为框架,引入了同级目录中partials目录中的多模板文件,其中涉及到支付方式的文件为“partials/hosted_checkout_basket.html”。该文件中与支付按钮相关的内容如下:
#ecommerce/templates/oscar/basket/partials/hosted_checkout_basket.html
            <div class=”pull-right payment-buttons” data-basket-id=”{{ basket.id }}”>
                {% if free_basket %}       #如果该次支付不需要付费(使用了优惠券并且100%折扣)
                    <a href=”{% url ‘checkout:free-checkout’ %}”
                       data-track-type=”click”
                       data-track-event=”edx.bi.ecommerce.basket.free_checkout”
                       data-track-category=”checkout”
                       class=”btn btn-success checkout-button”>
                        {% trans “Place Order” %}
                    </a>
                {% else %}                #如果本次支付需要付费,则显示所有的付费按钮,paypal,cybersource等。
                    {% for processor in payment_processors %}            #payment_processors为付费的平台名称
                        <button data-track-type=”click”
                                data-track-event=”edx.bi.ecommerce.basket.payment_selected”
                                data-track-category=”checkout”
                                data-processor-name=”{{ processor.NAME|lower }}”
                                data-track-checkout-type=”hosted”
                                class=”btn payment-button”
                                id=”{{ processor.NAME|lower }}”>
                            {% if processor.NAME == ‘cybersource’ %}
                                {% trans “Checkout” %}
                            {% elif processor.NAME == ‘paypal’ %}
                                {# Translators: Do NOT translate the name PayPal. #}
                                {% trans “Checkout with PayPal” %}
                                                    {% elif processor.NAME == ‘alipay’ %}                #新加的代码,配合后续的代码修改才能在支付页面上显示支付宝
                                                           {% trans “Checkout with Alipay” %}
                            {% endif %}
                        </button>
                    {% endfor %}
                {% endif %}
            </div>
       从上述模板文件中可以看出,“payment_processors”是一个可枚举类型的数据,其包含了所对接的支付平台,而这个变量的内容来自于BasketSummaryView类中的get_context_data函数,在这个函数内将payment_processor进行了赋值。
#ecommerce/core/models.py
class SiteConfiguration(models.Model):
    def get_payment_processors(self):
        all_processors = self._all_payment_processors()
        all_processor_names = {processor.NAME for processor in all_processors}
        missing_processor_configurations = self.payment_processors_set – all_processor_names
        if missing_processor_configurations:
            processor_config_repr = “, “.join(missing_processor_configurations)
            log.warning(
                ‘Unknown payment processors [%s] are configured for site %s’, processor_config_repr, self.site.id
            )
        return [
            processor for processor in all_processors
            if processor.NAME in self.payment_processors_set and processor.is_enabled()   #确保is_enable的返回值
        ]
    def _all_payment_processors(self):
        “”” Returns all processor classes declared in settings. “””
        all_processors = [get_processor_class(path) for path in settings.PAYMENT_PROCESSORS]
        return all_processors
      对上述代码进行分析payment_processors_set变量来源于类的payment_processors,而payment_processors类型为CharField,该数值存放在数据库中,形式为’cybersource,paypal’(以逗号分开)。另外一个变量”all_processors”来源于ecommerce的配置文件,配置部分为settings.PAYMENT_PROCESSORS。因此要让页面显示出某个支付模板,要同时在这两个地方进行修改。分别为:
#/edx/etc/ecommerce.yml
PAYMENT_PROCESSOR_CONFIG:
    edx:
        paypal:
            cancel_url: http://127.0.0.1:8000/commerce/checkout/cancel/
            client_id: AYskdUzpJGCDSJx8xPhFJq4We0FPINEjOTToNH0klY1BdDKj5B-k9CkEfvgUrBknbFxKriYc0DYjsqOJ
            client_secret: EBDM5GgT2RVC8MNacNWFluqj3sx4PdL37qj-A86gypVKQEFDltVkuhTLGfiXHlc5gKn4pnwTcKyWp5sJ
            error_url: http://127.0.0.1:8000/commerce/checkout/error/
            mode: sandbox
            receipt_url: http://127.0.0.1:8000/commerce/checkout/receipt/
   alipay:
            app_id: 2016081900287513
            app_private_key_path: /edx/app/ecommerce/cert/app_private_key.pem
            alipay_public_key_path: /edx/app/ecommerce/cert/alipay_public_key.pem
            mode: sandbox
            app_notify_url: http://ecommerce/checkout/alipay/
            cancel_url: http://127.0.0.1:8000/commerce/checkout/cancel/
            error_url: http://127.0.0.1:8000/commerce/checkout/error/
            receipt_url: http://127.0.0.1:8000/commerce/checkout/receipt/
进入admin管理界面,并修改默认的站点配置。修改Payment processors(付款处理器)字段,加入alipay(以逗号分隔)
#ecommerce/settings/_oscar.py
PAYMENT_PROCESSORS = (
    ‘ecommerce.extensions.payment.processors.cybersource.Cybersource’,
    ‘ecommerce.extensions.payment.processors.paypal.Paypal’,
    ‘ecommerce.extensions.payment.processors.alipay.Alipay’, #加入这行,同时加入程序文件
)
..
PAYMENT_PROCESSOR_CONFIG = {
    ‘edx’: {
        ‘paypal’: {
            # ‘mode’ can be either ‘sandbox’ or ‘live’
            ‘mode’: None,
            ‘client_id’: None,
            ‘client_secret’: None,
            ‘receipt_path’: PAYMENT_PROCESSOR_RECEIPT_PATH,
            ‘cancel_checkout_path’: PAYMENT_PROCESSOR_CANCEL_PATH,
            ‘error_path’: PAYMENT_PROCESSOR_ERROR_PATH,
        },
        ‘alipay’: {
            # ‘mode’ can be either ‘sandbox’ or ‘live’
            ‘app_id’: None,
            ‘app_private_key_path’: None,
            ‘alipay_public_key_path’: None,
            ‘mode’: None,
            ‘client_id’: None,
            ‘client_secret’: None,
            ‘receipt_path’: PAYMENT_PROCESSOR_RECEIPT_PATH,
            ‘cancel_checkout_path’: PAYMENT_PROCESSOR_CANCEL_PATH,
            ‘error_path’: PAYMENT_PROCESSOR_ERROR_PATH,
        },
    },
      上述修改完成后,加入支付宝模块的代码,目录在”ecommerce/extensions/payment/processors/”中,该目录同时也有paypal和cybersource的支付平台对接的代码,为了减少代码量,直接复制一份,操作如下:
#cp paypal.py alipay.py     #拷贝完成后注意文件的权限
将文件内的类Paypal修改为AliPay,同时将NAME变量修改为’alipay’,修改 后的结果如下:
class Alipay(BasePaymentProcessor):     #后续功能的修改也是在本文件完成的
    NAME = ‘alipay’
    DEFAULT_PROFILE_NAME = ‘default’
    def __init__(self, site):
         ….
      修改后依然不显示,根据错误提示,发现processor.is_enabled()的返回值影响了显示,找到相关代码如下:
#ecommerce/extensions/payment/processors/__init__.py
class BasePaymentProcessor(object):
    def is_enabled(cls):
        return waffle.switch_is_active(settings.PAYMENT_PROCESSOR_SWITCH_PREFIX + cls.NAME)
      根据代码,是waffle的开关设置问题,这个开关在admin管理面板可以设置。配置方法如下,配置后重启ecommerce即可显示:
#支付开关的页面
      添加一个开关,仿照paypal和cybersource的样式,开关名字为payment_processor_active_alipay,同时勾选active状态。如果要禁用其他支付模块,可在此处把开关取消active状态。由于系统默认开启了paypal,而后续又要添加alipay,可以在此时通过开关禁用掉paypay。
三、支付时的核心代码(第二步)
      当用户点击支付时(Checkout With Alipay),JS代码会向“http://ecommerce/api/v2/checkout/”发送POST数据,内容如下:
{“basket_id”:19,”payment_processor”:”alipay”}
      这个请求的处理函数为:
#ecommerce/extensions/api/v2/views/checkout.py
class CheckoutView(APIView):
    “””
    Freezes a basket, and returns the information necessary to start the payment process.
    “””
    permission_classes = (IsAuthenticated,)
    def post(self, request):
        basket_id = request.data[‘basket_id’]
        payment_processor_name = request.data[‘payment_processor’]
        logger.info(
            ‘Checkout view called for basket [%s].’,
            basket_id
        )
        ……
        parameters = payment_processor.get_transaction_parameters(basket, request=request)      #该处为核心代码
        payment_page_url = parameters.pop(‘payment_page_url’)
        data = {
            ‘payment_form_data’: parameters,
            ‘payment_page_url’: payment_page_url,
            ‘payment_processor’: payment_processor.NAME,
        }
        serializer = CheckoutSerializer(data)
        return Response(serializer.data)
          上述代码的核心代码为 payment_processor.get_transaction_parameters(basket, request=request),而该代码实际为”ecommerce/extensions/payment/processors/alipay.py”中的代码。由于后续的代码修改要用到支付宝的第三方SDK,因此进入ecommerce的virtualenv环境,安装该软件包,过程如下:
cd /edx/app/ecommerce                 #进入软件目录
sudo -H -u ecommerce bash             #更改用户
source venvs/ecommerce/bin/active     #加载python虚拟环境和环境变量
source ecommerce_env
pip install python-alipay-sdk                         #安装第三方支付模块
     当前的代码为,该代码即可完成从ecommerce到支付宝平台的跳转
#ecommerce/ecommerce/extensions/payment/processors/alipay.py
from __future__ import unicode_literals
from __future__ import absolute_import  #work wich conflicts alipay sdk name and class name
….
from alipay import AliPay as AliPaySdk
import json
logger = logging.getLogger(__name__)
class Alipay(BasePaymentProcessor):
    NAME = ‘alipay’
    DEFAULT_PROFILE_NAME = ‘default’
    def __init__(self, site):
        super(Alipay, self).__init__(site)
        mode = self.configuration[‘mode’].decode(‘utf-8’)
        if mode ==’sandbox’:
        self.alipay_gateway = ‘https://openapi.alipaydev.com/gateway.do
        else:
        self.alipay_gateway = ‘https://openapi.alipay.com/gateway.do
        self.Debug = False if self.configuration[‘mode’] is ‘sandbox’ else True
        self.app_private_key_string = open(self.configuration[‘app_private_key_path’]).read()
        self.alipay_public_key_string = open(self.configuration[‘alipay_public_key_path’]).read()
        self.notify_url = urljoin(get_ecommerce_url(), reverse(‘alipay:notify’))
        self.return_url = urljoin(get_ecommerce_url(), reverse(‘alipay:return’))
        self.alipay = AliPaySdk(appid=self.configuration[‘app_id’] ,app_notify_url = self.notify_url ,app_private_key_string = self.app_private_key_string ,alipay_public_key_string = self.alipay_public_key_string ,sign_type = “RSA2” ,debug = self.Debug )
        # Number of times payment execution is retried after failure.
        #self.retry_attempts = PaypalProcessorConfiguration.get_solo().retry_attempts
    def get_transaction_parameters(self, basket, request=None, use_client_side_checkout=False, **kwargs):
        “””
        Create a new PayPal payment.
        Arguments:
            basket (Basket): The basket of products being purchased.
            request (Request, optional): A Request object which is used to construct PayPal’s return_url.
            use_client_side_checkout (bool, optional): This value is not used.
            **kwargs: Additional parameters; not used by this method.
        Returns:
            dict: PayPal-specific parameters required to complete a transaction. Must contain a URL
                to which users can be directed in order to approve a newly created payment.
        Raises:
            GatewayError: Indicates a general error or unexpected behavior on the part of PayPal which prevented
                a payment from being created.
        “””
    total_amount = unicode(basket.total_incl_tax)
    out_trade_no = basket.order_number #or basket.id ?
    subject = ‘Course Buy’
    alipay_buy_link = self.alipay_gateway+”?”+self.alipay.api_alipay_trade_page_pay(out_trade_no = out_trade_no, total_amount = total_amount, subject = subject, return_url = self.return_url ,notify_url = self.notify_url)
        parameters = {
            ‘payment_page_url’: alipay_buy_link,
        }
        return parameters
以支付完成后的转跳为例子(paypal),其核心代码如下:
#ecommerce/extensions/payment/views/paypal.py
def get(self, request):
        “””Handle an incoming user returned to us by PayPal after approving payment.”””
        payment_id = request.GET.get(‘paymentId’)
        payer_id = request.GET.get(‘PayerID’)
        logger.info(u”Payment [%s] approved by payer [%s]”, payment_id, payer_id)
        paypal_response = request.GET.dict()
        basket = self._get_basket(payment_id)
        if not basket:
            return redirect(self.payment_processor.error_url)
        receipt_url = get_receipt_page_url(
            order_number=basket.order_number,
            site_configuration=basket.site.siteconfiguration
        )
        …..
        #实际的支付代码
      PayPal和AliPay的交易流程有一定的不同,即AliPay有异步通知的步骤,因此当AliPay交易完成并转跳会原先的网址时,交易已经成功了。而PayPal则时在页面同步转跳后由原先网址的代码检查并确定支付的成功。
      因此对于上述代码中”receipt_url”这个地址对于支付宝的异步通知没有,这个地址只在同步转跳中发挥作用,即让支付宝在同步状态阶段来到这个页面。而生成这个页面需要的参数为”order_number”即订单编号。如下为支付宝在“异步通知”和“同步转跳”时接收到的数据:
#异步通知数据
{u’version’: u’1.0′, u’app_id’: u’2016081900287513′, u’sign’: u’bGVJbMok3RSalj+ayK0GTbqups9T0uv54tvk1x+xu7hGhzoFUqm8nvxuGpyrVMRjyv0U72K58g81b7iVKAHBpIG4Lzpaw7Rg5f4qet0E7rhnEoEj6xUKTTGGt2FkCbq97K2noprvMyxNE7RkkyMx1XieXdFhrBr0tjMAVPPlsdqZRrZ0olN+S8a419/Qrm6TCbSXtbHuNxsGRrTHH2AXtp5PQhC6uHUqlXqyRqRpGNSGphhcYfBNhl/Y65WudFJjhHO1rzGGYQ3MAKOgNGv/8QXAk4XGgDl6dFskvRXpF40K/c2JPZRsfXAu2lMnpLtONptwS5L9XJcU2FyWU9x4Vw==’, u’buyer_pay_amount’: u’0.01′, u’point_amount’: u’0.00′, u’subject’: u’\u6d4b\u8bd5\u8ba2\u5355′, u’charset’: u’utf-8′, u’gmt_create’: u’2018-08-18 00:05:36′, u’out_trade_no’: u’20161155′, u’invoice_amount’: u’0.01′, u’sign_type’: u’RSA2′, u’auth_app_id’: u’2016081900287513′, u’fund_bill_list’: u'[{“amount”:”0.01″,”fundChannel”:”ALIPAYACCOUNT”}]’, u’receipt_amount’: u’0.01′, u’trade_status’: u’TRADE_SUCCESS’, u’gmt_payment’: u’2018-08-18 00:05:46′, u’trade_no’: u’2018081821001004870200545301′, u’seller_id’: u’2088102172081509′, u’total_amount’: u’0.01′, u’notify_time’: u’2018-08-18 19:59:49′, u’notify_id’: u’eb07415964fae02dd2620c7cee2767cmpq’, u’notify_type’: u’trade_status_sync’, u’buyer_id’: u’2088102175004879′}
#同步跳转数据
{u’trade_no’: u’2018081821001004870200545301′, u’seller_id’: u’2088102172081509′, u’total_amount’: u’0.01′, u’timestamp’: u’2018-08-18 00:06:11′, u’charset’: u’utf-8′, u’app_id’: u’2016081900287513′, u’sign’: u’r7S79IgDAvO4600A8x8BZJ8tKPLG/asELyoTOmhLmHsIBZLgbaTdxt13+NbSvbi+GclrSBpJQZ9ypAN5J5UzmlvklUU4+E3oHHfG+l2A6874NtYeGUzQFgD3GVq7eDuwxixjvJWHwMTL+8/jykvfASuq+aZZZJ5FWsdpqAE5AJOU3YuCRef1Ht/rjQtmw+/dSRB9HFKj+jPJfkpzAOFWdyljfSN7NP7bAuJ4KInRZ32rBqBLSTR1jPYiUWWxELNbjWhr+pMOCDw9HDaYc9oPXvf2efDFbGU6AOh+8wyAHbmypTrEM3TrKezP6JJkP+i0mf5JfaUslzyNV125DM67nQ==’, u’out_trade_no’: u’20161155′, u’version’: u’1.0′, u’sign_type’: u’RSA2′, u’auth_app_id’: u’2016081900287513′, u’method’: u’alipay.trade.page.pay.return’}
       在这两个数据中,字段“out_trade_no”即可以用于保存”order_number”的数据,即在提交订单时,将order_number的数值赋值给out_trade_no”。
         为方便修改,将Ecommerce于支付宝对接的代码一并存放,核心代码涉及到了三个文件,分别是urls.py,processors/alipay.py,views/alipay.py三个文件,三个文件都在ecommerce/extensions/payment目录中。文件下载

# 如何配置OpenEdx的Ecommerce
         ecommerce是openedx的商品购买模块,在新的openedx版本中,ecommerce已经取代了原来的shoppingcart模块。由于ecommerce模块较为复杂,其与edx-platform通过API接口进行通信。由于这种原因其配置和调试较为困难。为了方便文档的表述,将ecommerce的域名为http://ecommerce.site,将edx-platform中lms模块的域名为http://lms.site,配置过程如下(本操作基于ginkgo.2版本):
一、初始化设置
1、登录http://lms.site/admin/oauth2/client/这个页面(使用超级用户),如果该页面无法访问,检查lms/envs/commony.py的设置是否为如下:
‘ENABLE_OAUTH2_PROVIDER’:true
     在界面上添加一个客户端设置,内容如下(供参考),为了后续记录方便”Client id”和”Client secert”使用了预定义的字符
Name:Ecommerce
Client id:sample_id                (该部分会自动生成,也可以手动修改,注意不要泄露)
Client secret:sample_secert   (该部分会自动生成,也可以手动修改,注意不要泄露)
Client type:Confidential(Web application)
2、登录http://lms.site/admin/edx_oauth2_provider/trustedclient/,在该页面添加并选定一个刚设置的客户端,内容如下:
3、配置ecommerce的基本设置,加载基本环境,使用如下命令:
cd /edx/app/ecommerce/
sudo -H -u ecommerce bash
source ./venvs/ecommerce/bin/activate
      加载基本环境后,创建初始的配置,这个配置会写在数据库中。
cd /edx/app/ecommerce/ecommerce/
manage.py create_or_update_site –site-id=1 –site-domain=ecommerce.site –partner-code=edX –partner-name=’Ginkgo’ –lms-url-root=http://lms.site –payment-processors=paypal –client-id=sample_id –client-secret=sample_secret –from-email=he_yuan_tao@163.com
       上述配置模式只是用paypal支付功能
4、启用LMS的Commerce功能
       此时LMS在支付时并不能转跳到Ecommerce界面,需要再LMS的admin的界面进行配置:
#增加一个选项并进行如下配置
Enabled:勾选
Checkout on ecommerce service:勾选
Single course checkout page: /basket/single-item/
Cache Time To Live:600    #这个数值暂不清楚功能
Recept page:/checkout/receipt/?order_number=
Enable automatic refund approval:勾选
二、配置文件修改
1、LMS和CMS部分的修改
        在lms.env.json文件中修改如下内容:
    “ECOMMERCE_API_URL”: “http://ecommerce.site/api/v2“,
    “ECOMMERCE_PUBLIC_URL_ROOT”: “http://ecommerce.site/“,
    “JWT_AUTH”: {
        “JWT_AUDIENCE”: “SET-ME-PLEASE”,
        “JWT_ISSUER”: “http://lms.site/oauth2“,
        “JWT_SECRET_KEY”: “SET-ME-PLEASE”
    },
    “JWT_ISSUER”: “http://lms.site/oauth2“,
   “LMS_ROOT_URL”: “http://lms.site“,
   “OAUTH_ENFORCE_SECURE”: false,
   “OAUTH_OIDC_ISSUER”: “http://lms.site/oauth2“,
   “FEATURES”:{
        “ENABLE_OAUTH2_PROVIDER”:true,
   }
       在lms.auth.json文件中进行如下字段的修改:
“EDX_API_KEY”: “”  #填写1.1中key的内容
“ECOMMERCE_API_SIGNING_KEY”: “”  #填写1.1中key的内容
        建议对cms.env.json和cms.auth.json的内容进行类似的修改。
2、修改ecommerce的配置文件
       在/edx/etc/ecommerce.yml文件中修改如下内容
ECOMMERCE_URL_ROOT: http://ecommerce.site/
EDX_API_KEY: sample_id
JWT_AUTH:
    JWT_ALGORITHM: HS256
    JWT_DECODE_HANDLER: ecommerce.extensions.api.handlers.jwt_decode_handler
    JWT_ISSUERS:
    – ecommerce_worker
    JWT_LEEWAY: 1
    JWT_SECRET_KEY: SET-ME-PLEASE
    JWT_SECRET_KEYS:
    – SET-ME-PLEASE
    JWT_VERIFY_EXPIRATION: true
LMS_DASHBOARD_URL: http://lms.site/dashboard
LMS_HEARTBEAT_URL: http://lms.site/heartbeat
LMS_URL_ROOT: http://lms.site
OAUTH2_PROVIDER_URL: http://lms.site/oauth2
PAYMENT_PROCESSOR_CONFIG:
        alipay:
            app_id: 2016081900287513
            app_private_key_path: /edx/app/ecommerce/cert/app_private_key.pem          #自己定义的密钥
            alipay_public_key_path: /edx/app/ecommerce/cert/alipay_public_key.pem      #从阿里云下载的公钥
            mode: sandbox
            app_notify_url: http://ecommerce.site/checkout/alipay/
            cancel_url: http://127.0.0.1:8000/commerce/checkout/cancel/
            error_url: http://127.0.0.1:8000/commerce/checkout/error/
            receipt_url: http://127.0.0.1:8000/commerce/checkout/receipt/
SECRET_KEY: sample_secert
SESSION_EXPIRE_AT_BROWSER_CLOSE: false
SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY: sample_id
#上面的这个可能用不上
SOCIAL_AUTH_EDX_OIDC_ISSUER: http://lms.site/oauth2
SOCIAL_AUTH_EDX_OIDC_KEY: sample_id
SOCIAL_AUTH_EDX_OIDC_LOGOUT_URL: http://lms.site/logout
SOCIAL_AUTH_EDX_OIDC_SECRET: sample_secert
SOCIAL_AUTH_EDX_OIDC_URL_ROOT: http://lms.site/oauth2
SOCIAL_AUTH_REDIRECT_IS_HTTPS: false
三、细节修改
        配置完成后,支付的流程已经能进行,但由于openedx在支付阶段使用了https来增强支付的安全性。课程购买过程中会发生转跳到https协议网址的情况,但如果网址没有启用https则会发生异常。需要对配置进行修改:
#ecommerce/core/models.py
class SiteConfiguration(models.Model):
    …
    def build_ecommerce_url(self, path=”):
    …
        scheme = ‘http’ if settings.DEBUG else ‘https’     #将此处修改为只支持http协议
        ecommerce_url_root = “{scheme}://{domain}”.format(scheme=scheme, domain=self.site.domain)
        return urljoin(ecommerce_url_root, path)

FROM https://blog.lawrencemcdaniel.com/open-edx-ecommerce/

Get Ecommerce working on your Open edX native build. This post is a supplement to the official documentation, “How to Install and Start the E-Commerce Service in Native Installations“, covering installation and configuration, Django configuration, Nginx considerations, and PayPal integration.

Summary

The Open edX Ecommerce module is actually a 3rd party Django project named Oscar that the Open edX team fully integrated into the platform by fusing the user lists together with Oauth. While recently setting up the Ecommerce module on a Gingko.2 installation I found myself stumbling through some the steps in the official documentation while disagreeing entirely with others. What follows is a modified procedure that I followed to get Ecommerce running on a Ginkgo.2 installation. Hopefully this will save you some time.

Setup Procedure

1. Verify that the Ecommerce module is running

If you followed the documented instructions from “Native Open edX Ubuntu 16.04 64 bit Installation” for Gingko or later then the Ecommerce module is automatically installed and should be running on your instance. You can verify this by running the following command from a terminal window connected to your Ubuntu instance.

sudo /edx/bin/supervisorctl status

If you see the “Ecommerce” module in a running state then you can ignore the first two steps of the official documentation, or at least when running these you should notice that both steps are benign. If for any reason you decide to execute the first two steps of the official documentation then be forewarned that step #2 is tantamount to upgrading your entire platform and could thus bring unintended consequences beyond the scope of the Ecommerce module setup.

Moreover, if the Ecommerce module is not currently running then you should think carefully about your next steps. If your installation is not version Ginkgo or later then you should consider upgrading your entire platform before proceeding simply to ensure that your code base doesn’t venture too far into the unknown. You can read my blog post on Upgrading Open edX for more detail on what’s involved in upgrading and how to prepare.

2. Configure edX

There are a handful of things to configure in the LMS via the eponymous configuration file /edx/app/edxapp/lms.env.json. Refer to the screen shots below for guidance on the subject matter and approximate locations of each section. You’ll need to restart the LMS for these changes to take effect:

Briefly summarizing the purpose of each section:

  • ENABLE_OAUTH2_PROVIDER – This enables the LMS as a provider of Oauth2 authentication services. The Ecommerce module will leverage Oauth2 rather than host its own user list and passwords. For example, as an end user if you’ve ever elected to “Login with Facebook” then this flag makes your LMS the “Facebook” of that proposition.
  • JWT_AUTH – This is the URL of the JSON Web Token authorizer. In our case it’s exactly the same as the issuer.
  • JWT_ISSUER – This is the URL of the JSON Web Token issuer
  • OAUTH_OIDC_ISSUER – This is the URL of the Open ID issuer (eg your LMS platform)
  • PAID_COURSE_REGISTRATION_CURRENCY – The Ecommerce module reads this value to determine which currency symbol to display along side products. This value is also passed to the payment gateway.
  • PDF_RECEIPT_ – All of the salient text for customer receipts is stored in these fields.
3. Setup Oauth between the CMS and the Ecommerce module

It wasn’t initially apparent to me but, setting configuration parameters in lms.env.json only indicates to the LMS that you want to use it as part of an Oauth authentication; you still have to setup Oauth itself. We’ll use the Django admin console for this. For the Python/Django uninitiated, Django apps like LMS and CMS come with a “back end” admin console where additional configuration parameters are available beyond what you’ll find in the four JSON files in /edx/app/edxapp/. Refer to this screen shot for the URL path and guidelines for creating an Oauth client.

4. Configure Django to use Oscar Ecommerce

This is the definitive step in “activating” the Ecommerce module. Refer to the following screen shot for parameter values and the desired outcome.

5. Configure Oscar Ecommerce

This step essentially binds together the Django configurations you created in the previous two steps 3 & 4 so that the Oscar Ecommerce module works seamlessly with our LMS.

Following is how these command line arguments map to the two Django configuration screens from the previous step.

6. Navigate to Oscar Ecommerce Dashboard

If everything is working as it should then you’ll be able to navigate to the Ecommerce dashboard where you can begin setting up your products. You can refer to the Oscar Ecommerce Official Documentation for further instructions on setting up products, payment gateways, etcetera.

Trouble Shooting

  • Firewall. You should pay close attention to the unorthodox port assignments used by the Ecommerce module. By default the Oscar Ecommerce module runs on port 8002 and the LMS “Course Discovery” API runs on port 18381. You might need to open these ports on your firewall.
  • Port Settings. Take note that ports have changed over time. The official documentation makes multiple references to ports 18020 and 18130; neither of which seem to be in use on Ginkgo.
  • Nginx. If you’re using SSL on your site then you might need to make adjustments to one or more of the Nginx virtual server configurations located in /edx/app/nginx/sites-available/.
  • Additional Work Notes. You can reference these additional work notes from my colleague Eric Mensah and myself as he was trouble-shooting a couple of details related to ports and ssl.

I hope you found this helpful. Please help me improve this article by leaving a comment below. Thank you!

Posted in Open edX.

edustack

edustack webmaster

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.