Skip to content

Commit

Permalink
feat: add code skeleton from ecommerce-hyperpay (#1)
Browse files Browse the repository at this point in the history
* feat: add code skeleton from ecommerce-hyperpay

* Update README.rst

---------

Co-authored-by: Omar Al-Ithawi <[email protected]>
Co-authored-by: Salah Alomari <[email protected]>
  • Loading branch information
3 people authored Dec 2, 2023
1 parent 67934c4 commit fa98ec0
Show file tree
Hide file tree
Showing 28 changed files with 1,510 additions and 2 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: ci

on:
push:
branches: [main]
pull_request:

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
include:
- python-version: 3.8
tox-env: py38
- python-version: 3.8
tox-env: flake8

name: "Python ${{ matrix.python-version }} - ${{ matrix.tox-env }}"
steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Cache tox environments
uses: actions/cache@v3
with:
path: .tox
# Refresh the cache if the following files change
key: "tox-${{ matrix.python-version }}-${{ matrix.tox-env }}-${{ hashFiles('tox.ini', 'setup.py', 'scripts/tox_install_ecommerce_run_pytest.sh', 'requirements/ecommerce-maple.master.txt', 'payfort-test.txt') }}"

- name: Install Dependencies
run: |
pip install tox
- name: "Python ${{ matrix.python-version }} - ${{ matrix.tox-env }}"
run: "tox -e ${{ matrix.tox-env }}"
Empty file added CHANGELOG.rst
Empty file.
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include CHANGELOG.rst
include LICENSE
include README.rst
recursive-include payfort *.py *.html *.txt
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.PHONY: compile_messages download_ecommerce_requirements


compile_messages:
msgfmt--check --strict --output-file payfort/locale/ar/LC_MESSAGES/django.mo payfort/locale/ar/LC_MESSAGES/django.po

OPENEDX_RELEASE ?= maple.master
download_ecommerce_requirements:
curl -L https://github.com/openedx/ecommerce/raw/open-release/$(OPENEDX_RELEASE)/requirements/test.txt -o requirements/ecommerce-$(OPENEDX_RELEASE).txt
sed -i '1i# This file has been downloaded by "make download_ecommerce_requirements"' requirements/ecommerce-$(OPENEDX_RELEASE).txt
sed -i '2i# Every thing else below this line is a copy from the openedx/ecommerce test.txt requirements file' requirements/ecommerce-$(OPENEDX_RELEASE).txt


tests: ## Run unit and integration tests
tox -e py38

unit_tests: ## Run unit tests
tox -e py38 -- tests/unit

quality: ## Run code quality checks
tox -e flake8
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

92 changes: 92 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
Amazon Payment Services (PayFort) Payment Processor backend for Open edX ecommerce
==================================================================================

This application provides a custom `Open edX ecommerce <https://github.com/edx/ecommerce/>`
payment processor backend for the `Amazon Payment Services <https://paymentservices-reference.payfort.com/>`_.

Getting Started with PayFort Integration Development
####################################################

The API documentation for the PayFort payment services is available at:

- https://paymentservices-reference.payfort.com/docs/api/build/index.html

Scroll through the page, it's a very long single-page documentation.

There's no official Python SDK for PayFort. But we can use the
`PayFort PHP SDK <https://github.com/payfort/payfort-php-sdk>`_ to understand the API calls and redirect mechanism.

There's a 2016 unofficial Python SDK for PayFort: https://github.com/alisterionold/payfort-python

There's a 2018 blog documenting the integration process:

- `Integrating PayFort — we suffered so you don't have to <https://medium.com/@jaysadiq/integrating-payfort-we-suffered-so-you-dont-have-to-23a4dbdef556>`_

Testing Credit Cards
####################

When the testing account is used,
`PayFort test payment card numbers <https://paymentservices.amazon.com/docs/EN/12.html>`_ can be used.


Development and Testing
#######################

TBD: Add devstck and Tutor instructions.


To run tests locally in your machine, you need to install the following dependencies::

$ pip install tox

Then run the all tests::

$ make tests


Or run the unit tests only::

$ make unit_tests

To run quality quality::

$ make quality


``tox`` can be used directly to run a specific, for example::

$ tox -e py38 -- tests/unit/test_payfort_utils.py


Installation and usage
######################

* Install this repository inside the ecommerce virtualenv environment using `pip`.
* In `ecommerce.yml`, add the following settings:
::

ADDL_INSTALLED_APPS:
- payfort
ADDL_PAYMENT_PROCESSORS:
- 'ecommerce_payfort.processors.PayFort'
# many other settings
PAYMENT_PROCESSOR_CONFIG:
<partner name>:
payfort:
<TBD: integration docs is coming>

* Restart the `ecommerce` service in production and the devserver in the devstack.
* In the `ecommerce` Django admin site, create waffle switches `payment_processor_active_payfort`, ` to enable the backends.
* Verify and ensure that the `enable_client_side_checkout` waffle flag is disabled for everyone.
* Once these steps are done, the `PayFort` processor backend provided by this application will be available as payment options
during the payment flow for purchasing paid seats in courses.


Author
######

This application was developed by `ZeitLabs <https://zeitlabs.com/>`_ at the request of
`The National Learning Center <https://elc.edu.sa/>`_.

This application is released under the terms of the `AGPLv3 license <https://www.gnu.org/licenses/agpl-3.0.html>`_
and is based on the `ecommmerce-hyperpay plugin <https://github.com/open-craft/ecommerce-hyperpay>`_.
4 changes: 4 additions & 0 deletions ecommerce_payfort/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""
The PayFort payment processor pluggable application for Open edX ecommerce.
"""
default_app_config = 'ecommerce_payfort.apps.PayFortConfig'
18 changes: 18 additions & 0 deletions ecommerce_payfort/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
PayFort payment processor Django application initialization.
"""
from django.apps import AppConfig


class PayFortConfig(AppConfig):
"""
Configuration for the PayFort payment processor Django application.
"""
name = 'ecommerce_payfort'
plugin_app = {
'url_config': {
'ecommerce': {
'namespace': 'ecommerce_payfort',
}
},
}
23 changes: 23 additions & 0 deletions ecommerce_payfort/locale/ar/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
# Translators:
# Johan <johanv26>, 2023
# NELC Open edX Translation <[email protected]>, 2023
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-05-31 14:35+0000\n"
"PO-Revision-Date: 2021-04-30 13:21+0000\n"
"Last-Translator: NELC Open edX Translation <[email protected]>, 2021\n"
"Language-Team: Arabic (https://www.transifex.com/national-center-for-e-learning-and-distance-learning/teams/118545/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
5 changes: 5 additions & 0 deletions ecommerce_payfort/locale/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Configuration for i18n workflow.

locales:
- en # English - Source Language
- ar # Arabic
41 changes: 41 additions & 0 deletions ecommerce_payfort/processors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
PayFort payment processor.
"""

import logging

from django.utils.translation import ugettext_lazy as _
from oscar.apps.payment.exceptions import GatewayError

from ecommerce.extensions.payment.processors import BasePaymentProcessor

logger = logging.getLogger(__name__)


def format_price(price):
"""
Return the price in the expected format.
"""
return '{:0.2f}'.format(price)


class PayFortException(GatewayError):
"""
An umbrella exception to catch all errors from PayFort.
"""
pass # pylint: disable=unnecessary-pass


class PayFort(BasePaymentProcessor):
"""
PayFort payment processor.
For reference, see https://paymentservices-reference.payfort.com/docs/api/build/index.html
Scroll through the page, it's a very long single-page documentation.
"""

NAME = 'payfort'
CHECKOUT_TEXT = _("Checkout with credit card")

def __init__(self, site):
self.site = site
43 changes: 43 additions & 0 deletions ecommerce_payfort/templates/payment/payfort.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{% load i18n %}
{% load static %}
{% load compress %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "PayFort" %} - {{ payment_mode }}</title>
{% compress css %}
{% if main_css %}
<link rel="stylesheet" href="{{ main_css }}" type="text/x-scss">
{% else %}
<link rel="stylesheet" href="{% static 'css/base/main.css' %}" type="text/x-scss">
{% endif %}
{% endcompress %}

{% compress css %}
{# This block is separated to better support browser caching. #}
{% block stylesheets %}
{% endblock %}
{% endcompress %}
<script src="{{ payment_widget_js }}"></script>
<script src="https://code.jquery.com/jquery.js" type="text/javascript"></script>
</head>
<body>
<div id="skip-link">
<a href="#main-content" class="element-invisible element-focusable">Skip to main content</a>
</div>
{# This adds the header for the page. #}
{% include 'edx/partials/_student_navbar.html' %}
<form action="{{ payment_result_url }}" class="paymentWidgets" data-brands="{{ brands }}">
</form>
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
{% compress js %}
<script src="{% static 'bower_components/requirejs/require.js' %}"></script>
<script src="{% static 'js/config.js' %}"></script>

Note: django-compressor does not recognize the data-main attribute. Load the main script separately.
<script src="{% static 'js/common.js' %}"></script>
{% endcompress %}
</body>
</html>
Empty file.
30 changes: 30 additions & 0 deletions ecommerce_payfort/tests/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import responses
from django.conf import settings
from oscar.core.loading import get_class, get_model
from six.moves.urllib.parse import urljoin

CURRENCY = 'SAR'
Basket = get_model('basket', 'Basket')
Order = get_model('order', 'Order')
PaymentEventType = get_model('order', 'PaymentEventType')
PaymentProcessorResponse = get_model('payment', 'PaymentProcessorResponse')
SourceType = get_model('payment', 'SourceType')

post_checkout = get_class('checkout.signals', 'post_checkout')


class PayFortMixin:
"""
Mixin with helper methods for mocking PayFort API responses.
"""

def mock_api_response(self, path, body, method=responses.POST, resp=responses):
url = self._create_api_url(path=path)
resp.add(method, url, json=body)

def _create_api_url(self, path):
"""
Returns the API URL
"""
base_url = settings.PAYMENT_PROCESSOR_CONFIG['edx']['payfort']['payfort_base_api_url']
return urljoin(base_url, path)
Empty file.
12 changes: 12 additions & 0 deletions ecommerce_payfort/tests/processors/test_payfort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ddt
import pytest

from ecommerce.extensions.payment.tests.processors.mixins import PaymentProcessorTestCaseMixin
from ecommerce.tests.testcases import TestCase
from ecommerce_payfort.tests.mixins import PayFortMixin


@ddt.ddt
@pytest.mark.xfail
class PayFortTests(PayFortMixin, PaymentProcessorTestCaseMixin, TestCase):
pass
3 changes: 3 additions & 0 deletions ecommerce_payfort/tests/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Unit tests without external dependencies on ecommerce or payfort.
"""
4 changes: 4 additions & 0 deletions ecommerce_payfort/tests/unit/test_payfort_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@


def test_utils():
pass
16 changes: 16 additions & 0 deletions ecommerce_payfort/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
Defines the URL routes for the payfort app.
"""
from django.conf.urls import url

from .views import PayFortPaymentPageView, PayFortResponseView

urlpatterns = [
url(r'^payment/payfort/pay/$', PayFortPaymentPageView.as_view(), name='payment-form'),
url(r'^payment/payfort/submit/$', PayFortResponseView.as_view(), name='submit'),
url(
r'^payment/payfort/status/(?P<encrypted_resource_path>.+)/$',
PayFortResponseView.as_view(),
name='status-check'
),
]
Loading

0 comments on commit fa98ec0

Please sign in to comment.