From b7a23c2fb01769761d7b57ee86991889ecf9f630 Mon Sep 17 00:00:00 2001 From: Nicholas Wolff Date: Mon, 29 Jul 2019 07:42:05 +0200 Subject: [PATCH] first commit --- .envrc | 1 + .gitignore | 18 + .travis.yml | 35 ++ LICENSE | 9 + README.md | 98 +++ docs/design.md | 41 ++ docs/payment-with-stripe.png | Bin 0 -> 242857 bytes example_project/__init__.py | 0 example_project/manage.py | 11 + example_project/settings.py | 123 ++++ example_project/templates/operation_list.html | 20 + .../templates/stripe/checkout.html | 25 + .../templates/stripe/elements_token.html | 144 +++++ .../templates/stripe/old_checkout_ajax.html | 43 ++ .../stripe/payment_intents_manual_flow.html | 85 +++ example_project/urls.py | 29 + example_project/views/__init__.py | 10 + example_project/views/stripe.py | 134 ++++ example_project/views/stripe_deprecated.py | 128 ++++ manage.py | 15 + payment/__init__.py | 134 ++++ payment/admin.py | 149 +++++ payment/apps.py | 7 + payment/gateways/__init__.py | 0 payment/gateways/dummy/__init__.py | 107 ++++ payment/gateways/dummy/forms.py | 40 ++ payment/gateways/stripe/__init__.py | 227 +++++++ payment/gateways/stripe/connect.py | 31 + payment/gateways/stripe/errors.py | 6 + payment/gateways/stripe/forms.py | 84 +++ payment/gateways/stripe/utils.py | 95 +++ payment/interface.py | 64 ++ payment/locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 2614 bytes payment/locale/fr/LC_MESSAGES/django.po | 208 ++++++ payment/migrations/0001_initial.py | 66 ++ .../0002_cascade_delete_transaction.py | 19 + payment/migrations/__init__.py | 0 payment/models.py | 239 +++++++ payment/utils.py | 420 +++++++++++++ setup.cfg | 24 + setup.py | 45 ++ tests/__init__.py | 0 tests/conftest.py | 68 ++ tests/gateways/__init__.py | 0 tests/gateways/test_dummy.py | 245 ++++++++ tests/gateways/test_stripe.py | 593 ++++++++++++++++++ tests/settings.py | 103 +++ tests/urls.py | 6 + tox.ini | 26 + 49 files changed, 3975 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/design.md create mode 100644 docs/payment-with-stripe.png create mode 100644 example_project/__init__.py create mode 100755 example_project/manage.py create mode 100644 example_project/settings.py create mode 100644 example_project/templates/operation_list.html create mode 100644 example_project/templates/stripe/checkout.html create mode 100644 example_project/templates/stripe/elements_token.html create mode 100644 example_project/templates/stripe/old_checkout_ajax.html create mode 100644 example_project/templates/stripe/payment_intents_manual_flow.html create mode 100644 example_project/urls.py create mode 100644 example_project/views/__init__.py create mode 100644 example_project/views/stripe.py create mode 100644 example_project/views/stripe_deprecated.py create mode 100755 manage.py create mode 100644 payment/__init__.py create mode 100644 payment/admin.py create mode 100644 payment/apps.py create mode 100644 payment/gateways/__init__.py create mode 100644 payment/gateways/dummy/__init__.py create mode 100644 payment/gateways/dummy/forms.py create mode 100644 payment/gateways/stripe/__init__.py create mode 100644 payment/gateways/stripe/connect.py create mode 100644 payment/gateways/stripe/errors.py create mode 100644 payment/gateways/stripe/forms.py create mode 100644 payment/gateways/stripe/utils.py create mode 100644 payment/interface.py create mode 100644 payment/locale/fr/LC_MESSAGES/django.mo create mode 100644 payment/locale/fr/LC_MESSAGES/django.po create mode 100644 payment/migrations/0001_initial.py create mode 100644 payment/migrations/0002_cascade_delete_transaction.py create mode 100644 payment/migrations/__init__.py create mode 100644 payment/models.py create mode 100644 payment/utils.py create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/gateways/__init__.py create mode 100644 tests/gateways/test_dummy.py create mode 100644 tests/gateways/test_stripe.py create mode 100644 tests/settings.py create mode 100644 tests/urls.py create mode 100644 tox.ini diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..94840b3 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +layout python3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1895af1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Python +.direnv +*.pyc +__pycache__ +*.egg-info +/dist/ +/build/ +.tox +.coverage +.cache +test.db +.mypy_cache +.pytest_cache/ + +# Intellij idea +.idea +*.iml + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bea8854 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,35 @@ +dist: xenial + +sudo: false + +language: python + +python: + - 3.6 + - 3.7 + +install: pip install tox-travis coveralls + +script: tox + +stages: + - test + - name: deploy + if: tag IS present + +jobs: + include: + - stage: deploy + install: skip + script: skip + deploy: + provider: pypi + user: skioo + password: + secure: "bMMhRbHn4ZPdoGAuMoHIZdpt0Op73IKc1bi0xo6kHONJXbGFli2xJXYZNz9DjiqKn2WzvQkkxnjv4yMH1jdda01XlIdqKJPxlxzEYvInMBbrbKWv7mktbneJVDYw9cX9NcXkT0sFqH8r2htoVksBg0q4nQFp2fxjELDRYmdA3QjjTEVX8nw/eNElc8sSCdC5HmVaIpPN7yIrLO3Qe+f0RFEgkV8nTvrDQ3Nsg+SbQPWf78iBj7+3+8c6HvhKvDX+Wc01NCZSsM7NPtNqpv3907SGN56mPGVdCmW7aTLNWyf3zZeD2w5TRVNoUehcx7mMahJbCW0rBSAECVHVXUGJm35cbSk4OP5NPqJNCxlEQcfoqrEpvUayyLBsVK5cAjWS0k6A2l/TqXOzJ8UPIR/94aiD6X4FKxydFhhXf1ywevyi71mz/H2nESc18sOUgmPjjCrKAIxv/7phEYJ2FPB53Tg8zWQoLfB1ixu3kh/LoZmbmhnk27NfosXflnEsii/E1pSQ+QSnyDH4SvKeQCrSbmZvIc1MWoTCdMmtizew9MgzHbI/aG7TbttOzHjYYGG+iBMZTr0yMf+M/bej3sDglK7GJksJjYurpmR86B2f/s4gKdXFZ2Dl3/xmgLpHE/O/RYVWXqbv+1zuVtSQ9TZSHTW8HDVDb3ab56Jv6HoRb60=" + distribution: sdist bdist_wheel + on: + tags: true + +after_success: + - coveralls diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7956dfa --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2017, Skioo SA and individual contributors. + + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e58b8c8 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# django-payment + +[![Build Status](https://travis-ci.org/skioo/django-payment.svg?branch=master)](https://travis-ci.org/skioo/django-payment) +[![PyPI version](https://badge.fury.io/py/django-payment.svg)](https://badge.fury.io/py/django-payment) +[![Requirements Status](https://requires.io/github/skioo/django-payment/requirements.svg?branch=master)](https://requires.io/github/skioo/django-payment/requirements/?branch=master) +[![Coverage Status](https://coveralls.io/repos/github/skioo/django-payment/badge.svg?branch=master)](https://coveralls.io/github/skioo/django-payment?branch=master) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) + + +## Requirements +* Python: 3.6 and over +* Django: 2.1 and over + + +## Usage +To use django-payments in your existing django project: + +Add payment to your `INSTALLED_APPS`: + + INSTALLED_APPS = ( + ... + 'payment.apps.PaymentConfig, + ... + ) + + +Run the migrations: + + ./manage.py migrate + + +Configure the CHECKOUT_PAYMENT_GATEWAYS and PAYMENT_GATEWAYS settings. See [example settings.py](example_project/settings.py) + + +## Payment gateways +This module provides implementations for the following payment-gateways: + +### Stripe +Implemented features: +- Simple Payment (authorization and capture at once) +- Separate authorization and capture +- Refunds +- Split Payment with stripe connect +- Adding metadata to the stripe payment, for easy sorting in stripe + + +## The example project +The source distribution includes an example project that lets one exercise +the different gateway implementations without having to write any code. + +Install the django-payment dependencies (the example project has identical dependencies): + + pip install -e . + + Create the database and admin user: + + cd example_project + ./manage.py migrate + ./manage.py createsuperuser + + Start the dev server: + + ./manage.py runserver + +Then point your browser to: + + http://127.0.0.1:8000/admin + +Create a new payment. + +Then operate on that payment with: + + http://127.0.0.1:8000/payment/ + +## Development + +To install all dependencies: + + pip install -e . + +To run unit tests: + + pip install pytest-django + pytest + +To lint, typecheck, test, and verify you didn't forget to create a migration: + + pip install tox + tox + +To install the version being developed into another django project: + + pip install -e + + +## More information + +* [The design of this application](docs/design.md) diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..067b63b --- /dev/null +++ b/docs/design.md @@ -0,0 +1,41 @@ +Rationale +--------- + +The idea is to profit from the design and implementation of the payment gateways in saleor, +without pulling in the rest of saleor. Saleor wants to separate the payment part from the rest of saleor, +but they're not done yet: https://github.com/mirumee/saleor/issues/3422 . It also seems like it's not really in progress. + +I last retrieved the saleor code on 2019-06-24, commit hash: 509cb4530 + +Changing as little as possible in the copied code so that next time I retrieve the saleor code I can easily apply the changes. + + +General architecture +-------------------- +Client code interacts with a generic interface. +In interface.py Payment, the billing and shipping addresses are for optional fraud checking. + +Payment-gateway implementors implement the + +![payment with stripe sequence diagram](payment-with-stripe.png) + + +Manifest +-------- +This package contains: +- A copy of the interfaces of sealor payments. +- A copy of the ```stripe``` and ```dummy``` saleor gateways package and tests (we're not interested in braintree or razorpay) +- A modified copy of the models (to remove references to saleor orders and to the saleor custom money) +- A modified copy of utils.py (again to remove references to saleor orders and to the saleor custom money) +- A modified copy of stripe/__init__.py (to fix a bug when we just want to authorize) + + +Our changes +----------- +- Don't depend on saleor orders from within our code +- Don't depend on postgres, so we replace jsonfield by textfield + (there is a database-independent jsonfield but it's not very useful and also unmaintained) +- Don't depend on the saleor homegrown money class. +- Remove use the DEFAULT_CURRENCY settings, the way it's used in the saleor code looks like a very bad idea. +- Use the django admin instead of a handcrafted UI. + diff --git a/docs/payment-with-stripe.png b/docs/payment-with-stripe.png new file mode 100644 index 0000000000000000000000000000000000000000..a1e1d4d973d0928bc2c85679e8753cace4a78d23 GIT binary patch literal 242857 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%0DJp9xBMasbdo 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

}1+ewpC645<`>>1acn)WmXT86%Eig*AG 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 L)IyaZEkpkYP{=f< literal 0 HcmV?d00001 diff --git a/example_project/__init__.py b/example_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/example_project/manage.py b/example_project/manage.py new file mode 100755 index 0000000..b5a2943 --- /dev/null +++ b/example_project/manage.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import os +import sys + + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/example_project/settings.py b/example_project/settings.py new file mode 100644 index 0000000..2efc2f0 --- /dev/null +++ b/example_project/settings.py @@ -0,0 +1,123 @@ +# flake8: noqa + +import os +import sys + + +def abspath(*args): + return os.path.abspath(os.path.join(*args)) + + +PROJECT_ROOT = abspath(os.path.dirname(__file__)) +PAYMENT_MODULE_PATH = abspath(PROJECT_ROOT, '..') +sys.path.insert(0, PAYMENT_MODULE_PATH) + +DEBUG = True + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'test.db' + }, +} + +SECRET_KEY = 'not_so_secret' + +USE_TZ = True + +# Use a fast hasher to speed up tests. +PASSWORD_HASHERS = [ + 'django.contrib.auth.hashers.MD5PasswordHasher', +] + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.contenttypes', + 'django.contrib.staticfiles', + 'django_fsm', + 'djmoney', + 'tests', + 'payment.apps.PaymentConfig', +] + +MIDDLEWARE = [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +] + +STATIC_URL = '/static/' + +STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static/') + +STATICFILES_DIRS = [os.path.join(PROJECT_ROOT, 'project/static')] + +MEDIA_URL = '/media/' + +ROOT_URLCONF = 'urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': ['templates'], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.debug', + 'django.template.context_processors.i18n', + 'django.template.context_processors.request', + 'django.template.context_processors.static', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +# Enable specific currencies (djmoney) +CURRENCIES = ['USD', 'EUR', 'JPY', 'GBP', 'CAD', 'CHF'] + + +DUMMY = "dummy" +STRIPE = "stripe" + +CHECKOUT_PAYMENT_GATEWAYS = { + DUMMY: "Dummy gateway", + STRIPE: "Stripe", +} + +PAYMENT_GATEWAYS = { + DUMMY: { + "module": "payment.gateways.dummy", + "config": { + "auto_capture": True, + "connection_params": {}, + "template_path": "payment/dummy.html", + }, + }, + STRIPE: { + "module": "payment.gateways.stripe", + "config": { + "auto_capture": True, + "template_path": "payment/stripe.html", + "connection_params": { + "public_key": os.environ.get("STRIPE_PUBLIC_KEY"), + "secret_key": os.environ.get("STRIPE_SECRET_KEY"), + "store_name": os.environ.get("STRIPE_STORE_NAME", "skioo shop"), + "store_image": os.environ.get("STRIPE_STORE_IMAGE", None), + "prefill": os.environ.get("STRIPE_PREFILL", True), + "remember_me": os.environ.get("STRIPE_REMEMBER_ME", False), + "locale": os.environ.get("STRIPE_LOCALE", "auto"), + "enable_billing_address": os.environ.get( + "STRIPE_ENABLE_BILLING_ADDRESS", False + ), + "enable_shipping_address": os.environ.get( + "STRIPE_ENABLE_SHIPPING_ADDRESS", False + ), + }, + }, + }, +} diff --git a/example_project/templates/operation_list.html b/example_project/templates/operation_list.html new file mode 100644 index 0000000..2d20746 --- /dev/null +++ b/example_project/templates/operation_list.html @@ -0,0 +1,20 @@ +

Payment

+
    +
  • gateway: {{ payment.gateway }}
  • +
  • id: {{ payment.id }}
  • +
  • total: {{ payment.total }}
  • +
  • charge_status: {{ payment.charge_status }}
  • +
  • authorized amount: {{ payment.get_authorized_amount }}
  • +
  • captured amount: {{ payment.captured_amount }}
  • +
+ +
See payment in admin + +

Stripe Operations

+ + diff --git a/example_project/templates/stripe/checkout.html b/example_project/templates/stripe/checkout.html new file mode 100644 index 0000000..881ba84 --- /dev/null +++ b/example_project/templates/stripe/checkout.html @@ -0,0 +1,25 @@ + + + + +

Redirecting to stripe checkout

+ + + + + + + + + \ No newline at end of file diff --git a/example_project/templates/stripe/elements_token.html b/example_project/templates/stripe/elements_token.html new file mode 100644 index 0000000..7c03983 --- /dev/null +++ b/example_project/templates/stripe/elements_token.html @@ -0,0 +1,144 @@ + + + + Stripe elements + + + + + + + +
+
+ +
+ +
+ + + +
+
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/example_project/templates/stripe/old_checkout_ajax.html b/example_project/templates/stripe/old_checkout_ajax.html new file mode 100644 index 0000000..688f19a --- /dev/null +++ b/example_project/templates/stripe/old_checkout_ajax.html @@ -0,0 +1,43 @@ + + + + + + + + + \ No newline at end of file diff --git a/example_project/templates/stripe/payment_intents_manual_flow.html b/example_project/templates/stripe/payment_intents_manual_flow.html new file mode 100644 index 0000000..1ae3792 --- /dev/null +++ b/example_project/templates/stripe/payment_intents_manual_flow.html @@ -0,0 +1,85 @@ + + + + payment intents + + + + + +

Payment intents manual flow

+ + + +
+
+ + + + + + + diff --git a/example_project/urls.py b/example_project/urls.py new file mode 100644 index 0000000..e0e74fb --- /dev/null +++ b/example_project/urls.py @@ -0,0 +1,29 @@ +from django.conf import settings +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import path, include + +from views import stripe +from views import view_payment + +urlpatterns = [ + path('admin/', admin.site.urls), + path('payment/', view_payment, name='view_payment'), +] + +stripe_urls = [ + path('/stripe/checkout', stripe.checkout, name='stripe_checkout'), + path('/stripe/elements_token', stripe.elements_token, name='stripe_elements_token'), + + path('/stripe/payment_intents_manual_flow', stripe.payment_intents_manual_flow, + name='stripe_payment_intents_manual_flow'), + path('/stripe/payment_intents_confirm_payment', stripe.payment_intents_confirm_payment, + name='stripe_payment_intents_confirm_payment'), + path('/stripe/capture', stripe.capture, name='stripe_capture'), +] + + +urlpatterns += [path('stripe', include(stripe_urls))] + + +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/example_project/views/__init__.py b/example_project/views/__init__.py new file mode 100644 index 0000000..ff58d15 --- /dev/null +++ b/example_project/views/__init__.py @@ -0,0 +1,10 @@ +from django.http import HttpRequest, HttpResponse +from django.shortcuts import get_object_or_404 +from django.template.response import TemplateResponse + +from payment.models import Payment + + +def view_payment(request: HttpRequest, payment_id: int) -> HttpResponse: + payment = get_object_or_404(Payment, id=payment_id) + return TemplateResponse(request, 'operation_list.html', {'payment': payment}) diff --git a/example_project/views/stripe.py b/example_project/views/stripe.py new file mode 100644 index 0000000..b24cd2a --- /dev/null +++ b/example_project/views/stripe.py @@ -0,0 +1,134 @@ +from django.http import HttpRequest, HttpResponse, JsonResponse +from django.shortcuts import get_object_or_404, redirect +from django.template.response import TemplateResponse +from django.urls import reverse +from django.views.decorators.csrf import csrf_exempt +from rest_framework.decorators import api_view +from rest_framework.status import HTTP_500_INTERNAL_SERVER_ERROR +from structlog import get_logger + +from payment import get_payment_gateway +from payment.gateways.stripe import get_amount_for_stripe, get_currency_for_stripe +from payment.models import Payment +from payment.utils import gateway_authorize, gateway_capture + +logger = get_logger() + + +def capture(request: HttpRequest, payment_id: int) -> HttpResponse: + payment = get_object_or_404(Payment, id=payment_id) + capture_result = gateway_capture(payment=payment) + logger.info('stripe capture', payment=payment, capture_result=capture_result) + return redirect('view_payment', payment_id=payment_id) + + +def checkout(request, payment_id: int) -> HttpResponse: + """ + Takes the user to the stripe checkout page. + This is not part of the gateway abstraction, so we implement it directly using the stripe API + """ + payment = get_object_or_404(Payment, id=payment_id) + + import stripe + stripe.api_key = 'sk_test_QWtEpnVswmgW9aUJkyKmEutp00dsgn2KAa' + + session = stripe.checkout.Session.create( + payment_method_types=['card'], + customer_email=payment.customer_email, + line_items=[{ + 'name': 'Your order', + 'amount': get_amount_for_stripe(payment.total.amount, payment.total.currency.code), + 'currency': get_currency_for_stripe(payment.total.currency.code), + 'quantity': 1, + }], + payment_intent_data={ + 'capture_method': 'manual', + }, + success_url='https://example.com/success', + cancel_url='https://example.com/cancel', + ) + return TemplateResponse(request, 'stripe/checkout.html', {'CHECKOUT_SESSION_ID': session.id}) + + +@csrf_exempt +def elements_token(request, payment_id: int) -> HttpResponse: + payment = get_object_or_404(Payment, id=payment_id) + payment_gateway, gateway_config = get_payment_gateway(payment.gateway) + connection_params = gateway_config.connection_params + + if request.method == 'GET': + return TemplateResponse( + request, + 'stripe/elements_token.html', + {'stripe_public_key': connection_params['public_key']}) + elif request.method == 'POST': + stripe_token = request.POST.get('stripeToken') + if stripe_token is None: + return HttpResponse('missing stripe token') + try: + logger.info('stripe authorize', payment=payment) + gateway_authorize(payment=payment, payment_token=stripe_token) + except Exception as exc: + logger.error('stripe authorize', exc_info=exc) + return HttpResponse('Error authorizing {}: {}'.format(payment_id, exc)) + else: + return redirect('view_payment', payment_id=payment.pk) + + +def payment_intents_manual_flow(request, payment_id: int) -> HttpResponse: + payment = get_object_or_404(Payment, id=payment_id) + payment_gateway, gateway_config = get_payment_gateway(payment.gateway) + connection_params = gateway_config.connection_params + + stripe_public_key = connection_params['public_key'] + confirm_payment_endpoint = reverse('stripe_payment_intents_confirm_payment', args=[payment_id]) + + return TemplateResponse( + request, + 'stripe/payment_intents_manual_flow.html', + { + 'stripe_public_key': stripe_public_key, + 'confirm_payment_endpoint': confirm_payment_endpoint}) + + +@api_view(['POST']) +def payment_intents_confirm_payment(request, payment_id): + # XXX: Update the payment with the info + payment = get_object_or_404(Payment, id=payment_id) + payment_gateway, gateway_config = get_payment_gateway(payment.gateway) + connection_params = gateway_config.connection_params + stripe_public_key = connection_params['public_key'] + + import stripe + stripe.api_key = stripe_public_key + + data = request.data + + try: + if 'payment_method_id' in data: + # Create the PaymentIntent + intent = stripe.PaymentIntent.create( + payment_method=data['payment_method_id'], + amount=1099, + currency='chf', + confirmation_method='manual', + confirm=True, + ) + elif 'payment_intent_id' in data: + intent = stripe.PaymentIntent.confirm(data['payment_intent_id']) + except stripe.error.CardError as e: + # Display error on client + return JsonResponse({'error': e.user_message}) + + if intent.status == 'requires_action' and intent.next_action.type == 'use_stripe_sdk': + # Tell the client to handle the action + return JsonResponse({ + 'requires_action': True, + 'payment_intent_client_secret': intent.client_secret}) + elif intent.status == 'succeeded': + # The payment didn’t need any additional actions and completed! + # Handle post-payment fulfillment + return JsonResponse({'success': True}) + else: + # Invalid status + return JsonResponse({'error': 'Invalid PaymentIntent status'}, status=HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/example_project/views/stripe_deprecated.py b/example_project/views/stripe_deprecated.py new file mode 100644 index 0000000..1b4d0db --- /dev/null +++ b/example_project/views/stripe_deprecated.py @@ -0,0 +1,128 @@ +from dataclasses import asdict +from django.http import HttpRequest, HttpResponse, JsonResponse +from django.shortcuts import get_object_or_404, redirect +from django.template.response import TemplateResponse +from django.urls import reverse +from django.views.decorators.csrf import csrf_exempt +from rest_framework.decorators import api_view +from structlog import get_logger + +from payment import get_payment_gateway +from payment.gateways.stripe import get_amount_for_stripe +from payment.models import Payment +from payment.utils import create_payment_information, gateway_authorize +from payment.utils import gateway_process_payment + +logger = get_logger() + + +def authorize_and_capture_old_checkout(request: HttpRequest, payment_id: int) -> HttpResponse: + return _pay(request, payment_id, True) + + +def authorize_old_checkout(request: HttpRequest, payment_id: int) -> HttpResponse: + return _pay(request, payment_id, False) + + +def _pay(request: HttpRequest, payment_id: int, also_capture: bool) -> HttpResponse: + payment = get_object_or_404(Payment, id=payment_id) + payment_data = create_payment_information(payment) + + logger.debug('stripe _pay payment-data', **asdict(payment_data)) + + payment_gateway, gateway_config = get_payment_gateway(payment.gateway) + + connection_params = gateway_config.connection_params + form = payment_gateway.create_form( + request.POST or None, + payment_information=payment_data, + connection_params=connection_params, + ) + if form.is_valid(): + try: + if also_capture: + logger.info('stripe gateway-process-payment', payment=payment) + gateway_process_payment(payment=payment, payment_token=form.get_payment_token()) + else: + logger.info('stripe authorize', payment=payment) + gateway_authorize(payment=payment, payment_token=form.get_payment_token()) + except Exception as exc: + form.add_error(None, str(exc)) + else: + return redirect('view_payment', payment_id=payment.pk) + + client_token = payment_gateway.get_client_token(connection_params=connection_params) + ctx = { + "form": form, + "payment": payment, + "client_token": client_token, + } + return TemplateResponse(request, gateway_config.template_path, ctx) + + +@csrf_exempt +def authorize_old_checkout_ajax(request, payment_id: int) -> HttpResponse: + if request.method == 'GET': + payment_params_endpoint = reverse('stripe_payment_params', args=[payment_id]) + return TemplateResponse( + request, + 'stripe/old_checkout_ajax.html', + {'payment_params_endpoint': payment_params_endpoint}) + elif request.method == 'POST': + payment = get_object_or_404(Payment, id=payment_id) + payment_data = create_payment_information(payment) + logger.debug('stripe payment-data', **asdict(payment_data)) + payment_gateway, gateway_config = get_payment_gateway(payment.gateway) + connection_params = gateway_config.connection_params + form = payment_gateway.create_form( + request.POST, + payment_information=payment_data, + connection_params=connection_params) + if form.is_valid(): + try: + logger.info('stripe authorize', payment=payment) + gateway_authorize(payment=payment, payment_token=form.get_payment_token()) + except Exception as exc: + form.add_error(None, str(exc)) + logger.error('stripe authorize', exc_info=exc) + return HttpResponse('Error authorizing {}: {}'.format(payment_id, exc)) + else: + return redirect('view_payment', payment_id=payment.pk) + + +@api_view(['GET']) +def payment_params(request, payment_id: int) -> HttpResponse: + """ + Returns a data representation of the parameters that are needed to initiate a stripe payment. + This is not part of the gateway abstraction, so we implement it directly using the stripe API + """ + payment = get_object_or_404(Payment, id=payment_id) + + _, gateway_config = get_payment_gateway(payment.gateway) + gateway_params = gateway_config.connection_params + + amount = payment.total.amount + currency = payment.total.currency.code + + stripe_payment_params = { + "key": gateway_params.get("public_key"), + "amount": get_amount_for_stripe(amount, currency), + "name": gateway_params.get("store_name"), + "currency": currency, + "locale": "auto", + "allow-remember-me": "false", + "billing-address": "false", + "zip-code": "false", + "email": payment.customer_email + } + + image = gateway_params.get("store_image") + if image: + payment_params["image"] = image + + result = { + "gateway": payment.gateway, + "params": stripe_payment_params, + } + + return JsonResponse(result) diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..e9e7fdd --- /dev/null +++ b/manage.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) diff --git a/payment/__init__.py b/payment/__init__.py new file mode 100644 index 0000000..b9089f1 --- /dev/null +++ b/payment/__init__.py @@ -0,0 +1,134 @@ +import importlib +from enum import Enum + +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.utils.translation import pgettext_lazy + +from .interface import GatewayConfig + + +class PaymentError(Exception): + def __init__(self, message): + super(PaymentError, self).__init__(message) + self.message = message + + +class GatewayError(IOError): + pass + + +class CustomPaymentChoices: + MANUAL = "manual" + + CHOICES = [(MANUAL, pgettext_lazy("Custom payment choice type", "Manual"))] + + +class OperationType(Enum): + PROCESS_PAYMENT = "process_payment" + AUTH = "authorize" + CAPTURE = "capture" + VOID = "void" + REFUND = "refund" + + +class TransactionError(Enum): + """Represents a transaction error.""" + + INCORRECT_NUMBER = "incorrect_number" + INVALID_NUMBER = "invalid_number" + INCORRECT_CVV = "incorrect_cvv" + INVALID_CVV = "invalid_cvv" + INCORRECT_ZIP = "incorrect_zip" + INCORRECT_ADDRESS = "incorrect_address" + INVALID_EXPIRY_DATE = "invalid_expiry_date" + EXPIRED = "expired" + PROCESSING_ERROR = "processing_error" + DECLINED = "declined" + + +class TransactionKind: + """Represents the type of a transaction. + + The following transactions types are possible: + - AUTH - an amount reserved against the customer's funding source. Money + does not change hands until the authorization is captured. + - VOID - a cancellation of a pending authorization or capture. + - CAPTURE - a transfer of the money that was reserved during the + authorization stage. + - REFUND - full or partial return of captured funds to the customer. + """ + + AUTH = "auth" + CAPTURE = "capture" + VOID = "void" + REFUND = "refund" + # FIXME we could use another status like WAITING_FOR_AUTH for transactions + # Which were authorized, but needs to be confirmed manually by staff + # eg. Braintree with "submit_for_settlement" enabled + CHOICES = [ + (AUTH, pgettext_lazy("transaction kind", "Authorization")), + (REFUND, pgettext_lazy("transaction kind", "Refund")), + (CAPTURE, pgettext_lazy("transaction kind", "Capture")), + (VOID, pgettext_lazy("transaction kind", "Void")), + ] + + +class ChargeStatus: + """Represents possible statuses of a payment. + + The following statuses are possible: + - NOT_CHARGED - no funds were take off the customer founding source yet. + - PARTIALLY_CHARGED - funds were taken off the customer's funding source, + partly covering the payment amount. + - FULLY_CHARGED - funds were taken off the customer founding source, + partly or completely covering the payment amount. + - PARTIALLY_REFUNDED - part of charged funds were returned to the customer. + - FULLY_REFUNDED - all charged funds were returned to the customer. + """ + + NOT_CHARGED = "not-charged" + PARTIALLY_CHARGED = "partially-charged" + FULLY_CHARGED = "fully-charged" + PARTIALLY_REFUNDED = "partially-refunded" + FULLY_REFUNDED = "fully-refunded" + + CHOICES = [ + (NOT_CHARGED, pgettext_lazy("payment status", "Not charged")), + (PARTIALLY_CHARGED, pgettext_lazy("payment status", "Partially charged")), + (FULLY_CHARGED, pgettext_lazy("payment status", "Fully charged")), + (PARTIALLY_REFUNDED, pgettext_lazy("payment status", "Partially refunded")), + (FULLY_REFUNDED, pgettext_lazy("payment status", "Fully refunded")), + ] + + +GATEWAYS_ENUM = Enum( # type:ignore + "GatewaysEnum", {key.upper(): key.lower() for key in settings.PAYMENT_GATEWAYS} +) + + +def get_payment_gateway(gateway_name): + if gateway_name not in settings.CHECKOUT_PAYMENT_GATEWAYS: + raise ValueError("%s is not allowed gateway" % gateway_name) + if gateway_name not in settings.PAYMENT_GATEWAYS: + raise ImproperlyConfigured( + "Payment gateway %s is not configured." % gateway_name + ) + + gateway_module = importlib.import_module( + settings.PAYMENT_GATEWAYS[gateway_name]["module"] + ) + + if "config" not in settings.PAYMENT_GATEWAYS[gateway_name]: + raise ImproperlyConfigured( + "Payment gateway %s should have own configuration" % gateway_name + ) + + gateway_config = settings.PAYMENT_GATEWAYS[gateway_name]["config"] + config = GatewayConfig( + auto_capture=gateway_config["auto_capture"], + template_path=gateway_config["template_path"], + connection_params=gateway_config["connection_params"], + ) + + return gateway_module, config diff --git a/payment/admin.py b/payment/admin.py new file mode 100644 index 0000000..fc7e532 --- /dev/null +++ b/payment/admin.py @@ -0,0 +1,149 @@ +from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ +from moneyed.localization import format_money + +from .models import Payment, Transaction + + +############################################################## +# Shared utilities + + +def amount(obj): + return format_money(obj.amount) + + +amount.admin_order_field = 'amount' # type: ignore +amount.short_description = _('amount') # type: ignore + + +def created_on(obj): + return obj.created.date() + + +created_on.admin_order_field = 'created' # type: ignore +created_on.short_description = _('created') # type: ignore + + +def modified_on(obj): + return obj.modified.date() + + +modified_on.admin_order_field = 'modified' # type: ignore +modified_on.short_description = _('modified') # type: ignore + +############################################################## +# Credit Cards + +''' +class CreditCardValidFilter(admin.SimpleListFilter): + title = _('Valid') + parameter_name = 'valid' + + def lookups(self, request, model_admin): + return ( + ('yes', _('Yes')), + ('no', _('No')), + ('all', _('All')), + ) + + def queryset(self, request, queryset): + today = datetime.now().date() + if self.value() == 'yes': + return queryset.filter(expiry_date__gte=today) + if self.value() == 'no': + return queryset.filter(expiry_date__lt=today) + + +def credit_card_expiry(obj): + return format_html('{}/{}', obj.expiry_month, obj.expiry_year) + + +credit_card_expiry.admin_order_field = 'expiry_date' # type: ignore + + +def credit_card_is_valid(obj): + return obj.is_valid() + + +credit_card_is_valid.boolean = True # type: ignore + +credit_card_is_valid.short_description = 'valid' # type: ignore + + +@admin.register(CreditCard) +class CreditCardAdmin(admin.ModelAdmin): + date_hierarchy = 'created' + list_display = ['number', created_on, 'type', 'status', credit_card_expiry, credit_card_is_valid] + search_fields = ['number'] + list_filter = ['type', 'status', CreditCardValidFilter] + ordering = ['-created'] + list_select_related = True + + readonly_fields = ['created', 'modified', 'expiry_date'] + + +class CreditCardInline(admin.TabularInline): + model = CreditCard + fields = readonly_fields = ['type', 'number', 'status', credit_card_expiry, created_on] + show_change_link = True + can_delete = False + extra = 0 + ordering = ['-created'] + +''' + + +############################################################## +# Transactions +# + +class TransactionInline(admin.TabularInline): + model = Transaction + ordering = ['-created'] + show_change_link = True + + # The gateway response is a huge field so we don't show it in the inline. + # We let the user click on the inline change link to see all the details of the transaction. + fields = readonly_fields = ['created', 'token', 'kind', amount, 'is_success', 'error'] + + def has_add_permission(self, request, obj=None): + return False + + def has_delete_permission(self, request, obj=None): + return request.user.is_superuser + + +@admin.register(Transaction) +class TransactionAdmin(admin.ModelAdmin): + + def has_module_permission(self, request): + # Prevent TransactionAdmin from appearing in the admin menu, + # to view a transaction detail users should start navigation from a Payment. + return False + + +############################################################## +# Payments + +@admin.register(Payment) +class PaymentAdmin(admin.ModelAdmin): + date_hierarchy = 'created' + ordering = ['-created'] + list_filter = ['gateway', 'is_active', 'charge_status'] + list_display = ['created', 'gateway', 'is_active', 'charge_status', 'formatted_total', 'formatted_captured_amount', + 'customer_email'] + search_fields = ['customer_email', 'token', 'total', 'id'] + + inlines = [TransactionInline] + + def formatted_total(self, obj): + return format_money(obj.total) + + formatted_total.short_description = _('total') # type: ignore + + def formatted_captured_amount(self, obj): + if obj.captured_amount is not None: + return format_money(obj.captured_amount) + + formatted_captured_amount.short_description = _('captured amount') # type: ignore diff --git a/payment/apps.py b/payment/apps.py new file mode 100644 index 0000000..89758cf --- /dev/null +++ b/payment/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class PaymentConfig(AppConfig): + name = 'payment' + verbose_name = _("Payment") diff --git a/payment/gateways/__init__.py b/payment/gateways/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/payment/gateways/dummy/__init__.py b/payment/gateways/dummy/__init__.py new file mode 100644 index 0000000..d3ba929 --- /dev/null +++ b/payment/gateways/dummy/__init__.py @@ -0,0 +1,107 @@ +import uuid + +from ... import ChargeStatus, TransactionKind +from ...interface import GatewayConfig, GatewayResponse, PaymentData +from .forms import DummyPaymentForm + + +def dummy_success(): + return True + + +def get_client_token(**_): + return str(uuid.uuid4()) + + +def create_form(data, payment_information, connection_params): + return DummyPaymentForm(data=data) + + +def authorize( + payment_information: PaymentData, config: GatewayConfig +) -> GatewayResponse: + success = dummy_success() + error = None + if not success: + error = "Unable to authorize transaction" + return GatewayResponse( + is_success=success, + kind=TransactionKind.AUTH, + amount=payment_information.amount, + currency=payment_information.currency, + transaction_id=payment_information.token, + error=error, + ) + + +def void(payment_information: PaymentData, config: GatewayConfig) -> GatewayResponse: + error = None + success = dummy_success() + if not success: + error = "Unable to void the transaction." + return GatewayResponse( + is_success=success, + kind=TransactionKind.VOID, + amount=payment_information.amount, + currency=payment_information.currency, + transaction_id=payment_information.token, + error=error, + ) + + +def capture(payment_information: PaymentData, config: GatewayConfig) -> GatewayResponse: + """Perform capture transaction.""" + error = None + success = dummy_success() + if not success: + error = "Unable to process capture" + + return GatewayResponse( + is_success=success, + kind=TransactionKind.CAPTURE, + amount=payment_information.amount, + currency=payment_information.currency, + transaction_id=payment_information.token, + error=error, + ) + + +def refund(payment_information: PaymentData, config: GatewayConfig) -> GatewayResponse: + error = None + success = dummy_success() + if not success: + error = "Unable to process refund" + return GatewayResponse( + is_success=success, + kind=TransactionKind.REFUND, + amount=payment_information.amount, + currency=payment_information.currency, + transaction_id=payment_information.token, + error=error, + ) + + +def process_payment( + payment_information: PaymentData, config: GatewayConfig +) -> GatewayResponse: + """Process the payment.""" + token = payment_information.token + + # Process payment normally if payment token is valid + if token not in dict(ChargeStatus.CHOICES): + return capture(payment_information, config) + + # Process payment by charge status which is selected in the payment form + # Note that is for testing by dummy gateway only + charge_status = token + authorize_response = authorize(payment_information, config) + if charge_status == ChargeStatus.NOT_CHARGED: + return authorize_response + + if not config.auto_capture: + return authorize_response + + capture_response = capture(payment_information, config) + if charge_status == ChargeStatus.FULLY_REFUNDED: + return refund(payment_information, config) + return capture_response diff --git a/payment/gateways/dummy/forms.py b/payment/gateways/dummy/forms.py new file mode 100644 index 0000000..4aaa438 --- /dev/null +++ b/payment/gateways/dummy/forms.py @@ -0,0 +1,40 @@ +from django import forms +from django.utils.translation import pgettext_lazy, ugettext_lazy as _ + +from ... import ChargeStatus + + +class DummyPaymentForm(forms.Form): + charge_status = forms.ChoiceField( + label=pgettext_lazy("Payment status form field", "Payment status"), + choices=ChargeStatus.CHOICES, + initial=ChargeStatus.NOT_CHARGED, + widget=forms.RadioSelect, + ) + + def clean(self): + cleaned_data = super(DummyPaymentForm, self).clean() + + # Partially refunded is not supported directly + # since only last transaction of call_gateway will be processed + charge_status = cleaned_data["charge_status"] + if charge_status in [ + ChargeStatus.PARTIALLY_CHARGED, + ChargeStatus.PARTIALLY_REFUNDED, + ]: + raise forms.ValidationError( + _( + "Setting charge status to {} directly " + "is not supported. Please use the dashboard to " + "refund partially.".format(charge_status) + ), + code="invalid_charge_status", + ) + + return cleaned_data + + def get_payment_token(self): + """Return selected charge status instead of token for testing only. + Gateways used for production should return an actual token instead.""" + charge_status = self.cleaned_data["charge_status"] + return charge_status diff --git a/payment/gateways/stripe/__init__.py b/payment/gateways/stripe/__init__.py new file mode 100644 index 0000000..a5528b7 --- /dev/null +++ b/payment/gateways/stripe/__init__.py @@ -0,0 +1,227 @@ +import stripe +from typing import Dict + +from . import connect +from .forms import StripePaymentModalForm +from .utils import ( + get_amount_for_stripe, + get_amount_from_stripe, + get_currency_for_stripe, + get_currency_from_stripe, + get_payment_billing_fullname, + shipping_to_stripe_dict, +) +from ... import TransactionKind +from ...interface import GatewayConfig, GatewayResponse, PaymentData + + +def get_client_token(**_): + """Not implemented for stripe gateway currently. The client token can be + generated by Stripe's checkout.js or stripe.js automatically. + """ + return + + +def authorize( + payment_information: PaymentData, config: GatewayConfig, should_capture: bool = False +) -> GatewayResponse: + client, error = _get_client(**config.connection_params), None + + try: + # Authorize with/without capture + response = _create_stripe_charge( + client=client, + payment_information=payment_information, + should_capture=should_capture, + ) + except stripe.error.StripeError as exc: + response = _get_error_response_from_exc(exc) + error = exc.user_message + + kind = TransactionKind.CAPTURE if should_capture else TransactionKind.AUTH + + # Create response + return _create_response( # type: ignore + payment_information=payment_information, + kind=kind, + response=response, + error=error, + ) + + +def capture(payment_information: PaymentData, config: GatewayConfig) -> GatewayResponse: + client, error = _get_client(**config.connection_params), None + + # Get amount from argument or payment, and convert to stripe's amount + amount = payment_information.amount + stripe_amount = get_amount_for_stripe(amount, payment_information.currency) + + try: + # Retrieve stripe charge and capture specific amount + stripe_charge = client.Charge.retrieve(payment_information.token) + response = stripe_charge.capture(amount=stripe_amount) + except stripe.error.StripeError as exc: + response = _get_error_response_from_exc(exc) + error = exc.user_message + + # Create response + return _create_response( # type: ignore + payment_information=payment_information, + kind=TransactionKind.CAPTURE, + response=response, + error=error + ) + + +def refund(payment_information: PaymentData, config: GatewayConfig) -> GatewayResponse: + client, error = _get_client(**config.connection_params), None + + # Get amount from payment, and convert to stripe's amount + amount = payment_information.amount + stripe_amount = get_amount_for_stripe(amount, payment_information.currency) + + try: + # Retrieve stripe charge and refund specific amount + stripe_charge = client.Charge.retrieve(payment_information.token) + response = client.Refund.create(charge=stripe_charge.id, amount=stripe_amount) + except stripe.error.StripeError as exc: + response = _get_error_response_from_exc(exc) + error = exc.user_message + + # Create response + return _create_response( # type:ignore + payment_information=payment_information, + kind=TransactionKind.REFUND, + response=response, + error=error, + ) + + +def void(payment_information: PaymentData, config: GatewayConfig) -> GatewayResponse: + client, error = _get_client(**config.connection_params), None + + try: + # Retrieve stripe charge and refund all + stripe_charge = client.Charge.retrieve(payment_information.token) + response = client.Refund.create(charge=stripe_charge.id) + except stripe.error.StripeError as exc: + response = _get_error_response_from_exc(exc) + error = exc.user_message + + # Create response + return _create_response( # type:ignore + payment_information=payment_information, + kind=TransactionKind.VOID, + response=response, + error=error, + ) + + +def process_payment( + payment_information: PaymentData, config: GatewayConfig +) -> GatewayResponse: + # Stripe supports capture during authorize process. No need to run other steps. + return authorize(payment_information=payment_information, + config=config, + should_capture=True) + + +def create_form( + data: Dict, payment_information: PaymentData, connection_params: Dict +) -> StripePaymentModalForm: + return StripePaymentModalForm( + data=data, + payment_information=payment_information, + gateway_params=connection_params, + ) + + +def _get_client(**connection_params): + stripe.api_key = connection_params.get("secret_key") + return stripe + + +def _get_stripe_charge_payload( + payment_information: PaymentData, should_capture: bool +) -> Dict: + shipping = payment_information.shipping + + # Get currency + currency = get_currency_for_stripe(payment_information.currency) + + # Get appropriate amount for stripe + stripe_amount = get_amount_for_stripe(payment_information.amount, currency) + + # Get billing name from payment + name = get_payment_billing_fullname(payment_information) + + # Construct the charge payload from the data + charge_payload = { + "capture": should_capture, + "amount": stripe_amount, + "currency": currency, + "source": payment_information.token, + "description": name, + "metadata": payment_information.metadata, + } + + if shipping: + # Update shipping address to prevent fraud in Stripe + charge_payload["shipping"] = { + "name": name, + "address": shipping_to_stripe_dict(shipping), + } + + return charge_payload + + +def _create_stripe_charge(client, payment_information, should_capture: bool): + """Create a charge with specific amount, ignoring payment's total.""" + charge_payload = _get_stripe_charge_payload(payment_information, should_capture) + connect.maybe_add_transfer_data(charge_payload) + return client.Charge.create(**charge_payload) + + +def _create_response( + payment_information: PaymentData, kind: str, response: Dict, error: str +) -> GatewayResponse: + # Get currency from response or payment + currency = get_currency_from_stripe( + response.get("currency", payment_information.currency) + ) + + amount = payment_information.amount + # Get amount from response or payment + if "amount" in response: + stripe_amount = response.get("amount") + if "amount_refunded" in response: + # This happens for partial catpure which will refund the left + # Then the actual amount should minus refunded amount + stripe_amount -= response.get("amount_refunded") # type:ignore + amount = get_amount_from_stripe(stripe_amount, currency) + + # Get token from response or use provided one + token = response.get("id", payment_information.token) + + # Check if the response's status is flagged as succeeded + is_success = response.get("status") == "succeeded" + return GatewayResponse( + is_success=is_success, + transaction_id=token, + kind=kind, + amount=amount, + currency=currency, + error=error, + raw_response=response, + ) + + +def _get_error_response_from_exc(exc): + response = exc.json_body + + # Some errors from stripe don't json_body as None + # such as stripe.error.InvalidRequestError + if response is None: + response = dict() + + return response diff --git a/payment/gateways/stripe/connect.py b/payment/gateways/stripe/connect.py new file mode 100644 index 0000000..b8e3b2d --- /dev/null +++ b/payment/gateways/stripe/connect.py @@ -0,0 +1,31 @@ +from django.conf import settings +from structlog import get_logger + +logger = get_logger() + + +def add_transfer_data(charge_payload, destination, percent): + full_amount = charge_payload['amount'] + transfer_amount = int(full_amount * percent / 100) + charge_payload['transfer_data'] = { + 'destination': destination, + 'amount': transfer_amount + } + logger.debug('stripe_connect_maybe_add_transfer_data', + full_amount=full_amount, + transfer_amount=transfer_amount, + destination=destination) + + +def maybe_add_transfer_data(charge_payload): + if hasattr(settings, 'STRIPE_CONNECT'): + _connect_settings = settings.STRIPE_CONNECT + _transfer_destination = _connect_settings['transfer_destination'] + _transfer_percent_string = _connect_settings['transfer_percent'] + try: + _transfer_percent = int(_transfer_percent_string) + except ValueError: + raise Exception("STRIPE_TRANSFER_PERCENT should be an int") + if _transfer_percent < 0 or _transfer_percent > 100: + raise Exception("STRIPE_TRANSFER_PERCENT should be between 0 and 100") + add_transfer_data(charge_payload=charge_payload, destination=_transfer_destination, percent=_transfer_percent) diff --git a/payment/gateways/stripe/errors.py b/payment/gateways/stripe/errors.py new file mode 100644 index 0000000..90d803b --- /dev/null +++ b/payment/gateways/stripe/errors.py @@ -0,0 +1,6 @@ +from django.utils.translation import pgettext_lazy + +ORDER_NOT_AUTHORIZED = pgettext_lazy( + "Stripe payment error", "Order was not authorized." +) +ORDER_NOT_CHARGED = pgettext_lazy("Stripe payment error", "Order was not charged.") diff --git a/payment/gateways/stripe/forms.py b/payment/gateways/stripe/forms.py new file mode 100644 index 0000000..e629213 --- /dev/null +++ b/payment/gateways/stripe/forms.py @@ -0,0 +1,84 @@ +from typing import Dict + +from django import forms +from django.forms.utils import flatatt +from django.forms.widgets import HiddenInput +from django.utils.html import format_html +from django.utils.translation import pgettext_lazy + +from ...interface import PaymentData +from .utils import get_amount_for_stripe + +CHECKOUT_SCRIPT_URL = "https://checkout.stripe.com/checkout.js" +CHECKOUT_DESCRIPTION = pgettext_lazy( + "Stripe payment gateway description", "Total payment" +) + + +class StripeCheckoutWidget(HiddenInput): + def __init__( + self, payment_information: PaymentData, gateway_params: Dict, *args, **kwargs + ): + attrs = kwargs.get("attrs", {}) + kwargs["attrs"] = { + "class": "stripe-button", + "src": CHECKOUT_SCRIPT_URL, + "data-key": gateway_params.get("public_key"), + "data-amount": get_amount_for_stripe( + payment_information.amount, payment_information.currency + ), + "data-name": gateway_params.get("store_name"), + "data-image": gateway_params.get("store_image"), + "data-description": CHECKOUT_DESCRIPTION, + "data-currency": payment_information.currency, + "data-locale": gateway_params.get("locale"), + "data-allow-remember-me": "true" + if gateway_params.get("remember_me") + else "false", + "data-billing-address": "true" + if gateway_params.get("enable_billing_address") + else "false", + "data-zip-code": "true" + if gateway_params.get("enable_billing_address") + else "false", + "data-shipping-address": "true" + if gateway_params.get("enable_shipping_address") + else "false", + } + + if gateway_params.get("prefill"): + kwargs["attrs"].update({"data-email": payment_information.customer_email}) + + kwargs["attrs"].update(attrs) + super(StripeCheckoutWidget, self).__init__(*args, **kwargs) + + def render(self, name=None, value=None, attrs=None, renderer=None): + attrs = attrs or {} + attrs.update(self.attrs) + attrs.pop("id", None) + return format_html("", flatatt(attrs)) + + +class StripePaymentModalForm(forms.Form): + """ + At this moment partial-payment is not supported, but there is no need to + validate amount, which may be manually adjusted in the template, + since checkout.js can do that automatically. + """ + + stripeToken = forms.CharField(required=True, widget=HiddenInput) + + def __init__( + self, payment_information: PaymentData, gateway_params: Dict, *args, **kwargs + ): + super().__init__(*args, **kwargs) + + self.fields["stripe"] = forms.CharField( + widget=StripeCheckoutWidget( + payment_information=payment_information, gateway_params=gateway_params + ), + required=False, + ) + + def get_payment_token(self): + return self.cleaned_data["stripeToken"] diff --git a/payment/gateways/stripe/utils.py b/payment/gateways/stripe/utils.py new file mode 100644 index 0000000..6aa6691 --- /dev/null +++ b/payment/gateways/stripe/utils.py @@ -0,0 +1,95 @@ +from decimal import Decimal +from typing import Dict + +from django_countries import countries + +# List of zero-decimal currencies +# Since there is no public API in Stripe backend or helper function +# in Stripe's Python library, this list is straight out of Stripe's docs +# https://stripe.com/docs/currencies#zero-decimal +from ...interface import AddressData, PaymentData + +ZERO_DECIMAL_CURRENCIES = [ + "BIF", + "CLP", + "DJF", + "GNF", + "JPY", + "KMF", + "KRW", + "MGA", + "PYG", + "RWF", + "UGX", + "VND", + "VUV", + "XAF", + "XOF", + "XPF", +] + + +def get_amount_for_stripe(amount, currency): + """Get appropriate amount for stripe. + Stripe is using currency's smallest unit such as cents for USD and + stripe requires integer instead of decimal, so multiplying by 100 + and converting to integer is required. But for zero-decimal currencies, + multiplying by 100 is not needed. + """ + # Multiply by 100 for non-zero-decimal currencies + if currency.upper() not in ZERO_DECIMAL_CURRENCIES: + amount *= 100 + + # Using int(Decimal) directly may yield wrong result + # such as int(Decimal(24.24)*100) will equal to 2423 + return int(amount.to_integral_value()) + + +def get_amount_from_stripe(amount, currency): + """Get appropriate amount from stripe.""" + amount = Decimal(amount) + + # Divide by 100 for non-zero-decimal currencies + if currency.upper() not in ZERO_DECIMAL_CURRENCIES: + # Using Decimal(amount / 100.0) will convert to decimal from float + # where precision may be lost + amount /= Decimal(100) + + return amount + + +def get_currency_for_stripe(currency): + """Convert Saleor's currency format to Stripe's currency format. + Stripe's currency is using lowercase while Saleor is using uppercase. + """ + return currency.lower() + + +def get_currency_from_stripe(currency): + """Convert Stripe's currency format to Saleor's currency format. + Stripe's currency is using lowercase while Saleor is using uppercase. + """ + return currency.upper() + + +def get_payment_billing_fullname(payment_information: PaymentData) -> str: + # Get billing name from payment + # NWO + return payment_information.customer_email + """ + return "%s %s" % ( + payment_information.billing.last_name, + payment_information.billing.first_name, + ) + """ + + +def shipping_to_stripe_dict(shipping: AddressData) -> Dict: + return { + "line1": shipping.street_address_1, + "line2": shipping.street_address_2, + "city": shipping.city, + "state": shipping.country_area, + "postal_code": shipping.postal_code, + "country": dict(countries).get(shipping.country, ""), + } diff --git a/payment/interface.py b/payment/interface.py new file mode 100644 index 0000000..4f7ae6c --- /dev/null +++ b/payment/interface.py @@ -0,0 +1,64 @@ +from decimal import Decimal +from typing import Any, Dict, Optional + +from dataclasses import dataclass + + +@dataclass +class GatewayResponse: + """Dataclass for storing gateway response. Used for unifying the + representation of gateway response. It is required to communicate between + Saleor and given payment gateway.""" + + is_success: bool + kind: str + amount: Decimal + currency: str + transaction_id: str + error: Optional[str] + raw_response: Optional[Dict[str, str]] = None + + +@dataclass +class AddressData: + first_name: str + last_name: str + company_name: str + street_address_1: str + street_address_2: str + city: str + city_area: str + postal_code: str + country: str + country_area: str + phone: str + + +@dataclass +class PaymentData: + """Dataclass for storing all payment information. Used for unifying the + representation of data. It is required to communicate between Saleor and + given payment gateway.""" + + token: str + amount: Decimal + currency: str + billing: Optional[AddressData] + shipping: Optional[AddressData] + order_id: Optional[int] + customer_ip_address: str + customer_email: str + metadata: Dict[str, str] + + +@dataclass +class GatewayConfig: + """Dataclass for storing gateway config data. Used for unifying the + representation of config data. It is required to communicate between + Saleor and given payment gateway.""" + + auto_capture: bool + template_path: str + # Each gateway has different connection data so we are not able to create + # a unified structure + connection_params: Dict[str, Any] diff --git a/payment/locale/fr/LC_MESSAGES/django.mo b/payment/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..a5427556fcb11ca83b48bb360948a2793c4d95fa GIT binary patch literal 2614 zcma)-O^g&p6vqo41qVM6QSl23FAI_$XV(y5V1adA$m;GgS!P!~sHyF)nXRR}s;R05 z7Nb!QCVC+uAt5m_F=BLL;>ClB!Ng<^T;*cIMGqztPsTGB|F5TeXW7;0WTt;zRj*#v zd#~z0zih2sX81gZ-_!WT8>q$DwYvt~h1Va@-U$8*?gnpx5xC`U#%f>&?gg)cb?_(f zUT{N&v4_A-;3jZ8ct5xc+zi%=@do%H+Ao6-fUkqJUZ?0c;3H_i3*G}>0;}K^knBDO zw}4+4{nx;DYqaex%>E$|tz0;f-c&w?Br1u5<~L7Mj-h%fu7;6;%3 ze;M2Xeg;sVE{1~M8J^{CZUx1H+-xuv01%C(0|80=g-Hb`JpRM4-;7*YGhruVn10dyf z7NmT?0b(jU1CreZ5GiBt7yTa=ya*yxb_JyQpMvB;4YH5jNtOX=OXp5q4?45^=m-W2d}Tsa)B&wAR?lNgGMa&xyWok@-bq z)de{JKj!`IuD9(7FVBf(9!VEsO|Oiuw2T*Vxi}Mv$+TyoNPT8y#LITb7p%|`3qvl? zr#vy*FP4pEDJ;FWm31Cz=_v209g9`X)d^&wmDmZqs=mjfcD2aT!)MzIe%HXmvq)E!XB|@*xN_JXd(}{@`${VrbXp}7KSvj7e4n>Vu zjP4xlS8d>;9GyYoi)^p;+AJ&l-v(>FJLb)4sqsW@7(&;M;xXtMymN}JGFF@n@z2BK z{X(vvH=7^B_5JfkMXP%qYg)5}gC0yq!MwB%yJ+y{*!U}BCnos(#O$fblQY#>Nk>1J zb{!Q3FJ>JVv`m9nno5#vaUj!jTGEl!I7!V;g*7i1WiDN|Zh(&x<6DWi2$xks(;>P9{1sXi1SY z_|(|Ui8RX-59Q@_ z(vQxyjt2(@cC@Ruw84Z9ao%*N!4E7bUv0*j6>)GJ8si#Vr+L$jjvV6s*U{aYAK}A$ z4pm=UiA+YpoXcHAONhe+)?h&|c)fP8_Cl4BoJo>1B}wWa3Av7?WqUH_dPw5-rO=Pm zIHwk3$dKd-yjaQ6D5Oselq`2uvjKS9Jdi;Wf0ItI_#t@0wERTeIZaOl$OSooxavDQ*XA&^y)Kr`m5GP3qCAUS zlO%^e6+MtAnOPQ@d~H&oG>v;*s*1q>gM%nIC#{TQ$>1E{nmE~!b?SY, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-07-29 07:50-0500\n" +"PO-Revision-Date: 2019-07-29 12:51+0000\n" +"Last-Translator: b' '\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Translated-Using: django-rosetta 0.9.3\n" + +#: __init__.py:24 +msgctxt "Custom payment choice type" +msgid "Manual" +msgstr "Manuel" + +#: __init__.py:70 +msgctxt "transaction kind" +msgid "Authorization" +msgstr "Autorisation" + +#: __init__.py:71 +msgctxt "transaction kind" +msgid "Refund" +msgstr "Remboursement" + +#: __init__.py:72 +msgctxt "transaction kind" +msgid "Capture" +msgstr "Capture" + +#: __init__.py:73 +msgctxt "transaction kind" +msgid "Void" +msgstr "Annulation" + +#: __init__.py:97 +msgctxt "payment status" +msgid "Not charged" +msgstr "Non payé" + +#: __init__.py:98 +msgctxt "payment status" +msgid "Partially charged" +msgstr "Payé en partie" + +#: __init__.py:99 +msgctxt "payment status" +msgid "Fully charged" +msgstr "Payé" + +#: __init__.py:100 +msgctxt "payment status" +msgid "Partially refunded" +msgstr "Remboursé en partie " + +#: __init__.py:101 +msgctxt "payment status" +msgid "Fully refunded" +msgstr "Remboursé" + +#: admin.py:17 models.py:228 +msgid "amount" +msgstr "montant" + +#: admin.py:25 models.py:97 models.py:222 +msgid "created" +msgstr "créé" + +#: admin.py:33 models.py:98 +msgid "modified" +msgstr "modifié" + +#: admin.py:143 models.py:102 +msgid "total" +msgstr "total" + +#: admin.py:149 models.py:103 +msgid "captured amount" +msgstr "montant capturé" + +#: apps.py:7 +msgid "Payment" +msgstr "Paiement" + +#: gateways/dummy/forms.py:9 +msgctxt "Payment status form field" +msgid "Payment status" +msgstr "Status paiement" + +#: gateways/dummy/forms.py:27 +msgid "" +"Setting charge status to {} directly is not supported. Please use the " +"dashboard to refund partially." +msgstr "" +"Il n'est pas possible de mettre le status a {} directement. \n" +"Utilisez la console pour un remboursement partiel." + +#: gateways/stripe/errors.py:4 +msgctxt "Stripe payment error" +msgid "Order was not authorized." +msgstr "L'ordre n'a pas été autorisé" + +#: gateways/stripe/errors.py:6 +msgctxt "Stripe payment error" +msgid "Order was not charged." +msgstr "L'ordre n'a pas été payé" + +#: gateways/stripe/forms.py:14 +msgctxt "Stripe payment gateway description" +msgid "Total payment" +msgstr "Paiement total" + +#: models.py:95 +msgid "gateway" +msgstr "passerelle" + +#: models.py:96 +msgid "is_active" +msgstr "actif?" + +#: models.py:99 +msgid "charge status" +msgstr "Status paiement" + +#: models.py:101 models.py:225 +msgid "token" +msgstr "jeton" + +#: models.py:105 +msgid "cc first digits" +msgstr "cc premiers chiffres" + +#: models.py:106 +msgid "cc last digits" +msgstr "cc derniers chiffres" + +#: models.py:107 +msgid "cc brand" +msgstr "cc marque" + +#: models.py:108 +msgid "cc exp month" +msgstr "cc mois d'expiration" + +#: models.py:111 +msgid "cc exp year" +msgstr "cc année d'expiration" + +#: models.py:114 +msgid "customer email" +msgstr "email client" + +#: models.py:116 +msgid "customer ip address" +msgstr "adresse ip client" + +#: models.py:117 +msgid "extra data" +msgstr "données supplémentaires" + +#: models.py:120 models.py:224 +msgid "payment" +msgstr "paiement" + +#: models.py:121 +msgid "payments" +msgstr "paiements" + +#: models.py:125 +#, fuzzy +#| msgid "Payment %s (%s)" +msgid "Payment {} ({})" +msgstr "Paiement {} ({})" + +#: models.py:226 +msgid "kind" +msgstr "type" + +#: models.py:227 +msgid "is success" +msgstr "succès?" + +#: models.py:229 +msgid "error" +msgstr "erreur" + +#: models.py:230 +msgid "gateway response" +msgstr "réponse passerelle" + +#: models.py:233 +msgid "transaction" +msgstr "transaction" + +#: models.py:234 +msgid "transactions" +msgstr "transactions" diff --git a/payment/migrations/0001_initial.py b/payment/migrations/0001_initial.py new file mode 100644 index 0000000..27da7bf --- /dev/null +++ b/payment/migrations/0001_initial.py @@ -0,0 +1,66 @@ +# Generated by Django 2.2.3 on 2019-07-29 06:00 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import djmoney.models.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Payment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('gateway', models.CharField(max_length=255, verbose_name='gateway')), + ('is_active', models.BooleanField(default=True, verbose_name='is_active')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('charge_status', models.CharField(choices=[('not-charged', 'Not charged'), ('partially-charged', 'Partially charged'), ('fully-charged', 'Fully charged'), ('partially-refunded', 'Partially refunded'), ('fully-refunded', 'Fully refunded')], default='not-charged', max_length=20, verbose_name='charge status')), + ('token', models.CharField(blank=True, default='', max_length=128, verbose_name='token')), + ('total_currency', djmoney.models.fields.CurrencyField(choices=[('XUA', 'ADB Unit of Account'), ('AFN', 'Afghani'), ('DZD', 'Algerian Dinar'), ('ARS', 'Argentine Peso'), ('AMD', 'Armenian Dram'), ('AWG', 'Aruban Guilder'), ('AUD', 'Australian Dollar'), ('AZN', 'Azerbaijanian Manat'), ('BSD', 'Bahamian Dollar'), ('BHD', 'Bahraini Dinar'), ('THB', 'Baht'), ('PAB', 'Balboa'), ('BBD', 'Barbados Dollar'), ('BYN', 'Belarussian Ruble'), ('BYR', 'Belarussian Ruble'), ('BZD', 'Belize Dollar'), ('BMD', 'Bermudian Dollar (customarily known as Bermuda Dollar)'), ('BTN', 'Bhutanese ngultrum'), ('VEF', 'Bolivar Fuerte'), ('BOB', 'Boliviano'), ('XBA', 'Bond Markets Units European Composite Unit (EURCO)'), ('BRL', 'Brazilian Real'), ('BND', 'Brunei Dollar'), ('BGN', 'Bulgarian Lev'), ('BIF', 'Burundi Franc'), ('XOF', 'CFA Franc BCEAO'), ('XAF', 'CFA franc BEAC'), ('XPF', 'CFP Franc'), ('CAD', 'Canadian Dollar'), ('CVE', 'Cape Verde Escudo'), ('KYD', 'Cayman Islands Dollar'), ('CLP', 'Chilean peso'), ('XTS', 'Codes specifically reserved for testing purposes'), ('COP', 'Colombian peso'), ('KMF', 'Comoro Franc'), ('CDF', 'Congolese franc'), ('BAM', 'Convertible Marks'), ('NIO', 'Cordoba Oro'), ('CRC', 'Costa Rican Colon'), ('HRK', 'Croatian Kuna'), ('CUP', 'Cuban Peso'), ('CUC', 'Cuban convertible peso'), ('CZK', 'Czech Koruna'), ('GMD', 'Dalasi'), ('DKK', 'Danish Krone'), ('MKD', 'Denar'), ('DJF', 'Djibouti Franc'), ('STD', 'Dobra'), ('DOP', 'Dominican Peso'), ('VND', 'Dong'), ('XCD', 'East Caribbean Dollar'), ('EGP', 'Egyptian Pound'), ('SVC', 'El Salvador Colon'), ('ETB', 'Ethiopian Birr'), ('EUR', 'Euro'), ('XBB', 'European Monetary Unit (E.M.U.-6)'), ('XBD', 'European Unit of Account 17(E.U.A.-17)'), ('XBC', 'European Unit of Account 9(E.U.A.-9)'), ('FKP', 'Falkland Islands Pound'), ('FJD', 'Fiji Dollar'), ('HUF', 'Forint'), ('GHS', 'Ghana Cedi'), ('GIP', 'Gibraltar Pound'), ('XAU', 'Gold'), ('XFO', 'Gold-Franc'), ('PYG', 'Guarani'), ('GNF', 'Guinea Franc'), ('GYD', 'Guyana Dollar'), ('HTG', 'Haitian gourde'), ('HKD', 'Hong Kong Dollar'), ('UAH', 'Hryvnia'), ('ISK', 'Iceland Krona'), ('INR', 'Indian Rupee'), ('IRR', 'Iranian Rial'), ('IQD', 'Iraqi Dinar'), ('IMP', 'Isle of Man Pound'), ('JMD', 'Jamaican Dollar'), ('JOD', 'Jordanian Dinar'), ('KES', 'Kenyan Shilling'), ('PGK', 'Kina'), ('LAK', 'Kip'), ('KWD', 'Kuwaiti Dinar'), ('AOA', 'Kwanza'), ('MMK', 'Kyat'), ('GEL', 'Lari'), ('LVL', 'Latvian Lats'), ('LBP', 'Lebanese Pound'), ('ALL', 'Lek'), ('HNL', 'Lempira'), ('SLL', 'Leone'), ('LSL', 'Lesotho loti'), ('LRD', 'Liberian Dollar'), ('LYD', 'Libyan Dinar'), ('SZL', 'Lilangeni'), ('LTL', 'Lithuanian Litas'), ('MGA', 'Malagasy Ariary'), ('MWK', 'Malawian Kwacha'), ('MYR', 'Malaysian Ringgit'), ('TMM', 'Manat'), ('MUR', 'Mauritius Rupee'), ('MZN', 'Metical'), ('MXV', 'Mexican Unidad de Inversion (UDI)'), ('MXN', 'Mexican peso'), ('MDL', 'Moldovan Leu'), ('MAD', 'Moroccan Dirham'), ('BOV', 'Mvdol'), ('NGN', 'Naira'), ('ERN', 'Nakfa'), ('NAD', 'Namibian Dollar'), ('NPR', 'Nepalese Rupee'), ('ANG', 'Netherlands Antillian Guilder'), ('ILS', 'New Israeli Sheqel'), ('RON', 'New Leu'), ('TWD', 'New Taiwan Dollar'), ('NZD', 'New Zealand Dollar'), ('KPW', 'North Korean Won'), ('NOK', 'Norwegian Krone'), ('PEN', 'Nuevo Sol'), ('MRO', 'Ouguiya'), ('TOP', 'Paanga'), ('PKR', 'Pakistan Rupee'), ('XPD', 'Palladium'), ('MOP', 'Pataca'), ('PHP', 'Philippine Peso'), ('XPT', 'Platinum'), ('GBP', 'Pound Sterling'), ('BWP', 'Pula'), ('QAR', 'Qatari Rial'), ('GTQ', 'Quetzal'), ('ZAR', 'Rand'), ('OMR', 'Rial Omani'), ('KHR', 'Riel'), ('MVR', 'Rufiyaa'), ('IDR', 'Rupiah'), ('RUB', 'Russian Ruble'), ('RWF', 'Rwanda Franc'), ('XDR', 'SDR'), ('SHP', 'Saint Helena Pound'), ('SAR', 'Saudi Riyal'), ('RSD', 'Serbian Dinar'), ('SCR', 'Seychelles Rupee'), ('XAG', 'Silver'), ('SGD', 'Singapore Dollar'), ('SBD', 'Solomon Islands Dollar'), ('KGS', 'Som'), ('SOS', 'Somali Shilling'), ('TJS', 'Somoni'), ('SSP', 'South Sudanese Pound'), ('LKR', 'Sri Lanka Rupee'), ('XSU', 'Sucre'), ('SDG', 'Sudanese Pound'), ('SRD', 'Surinam Dollar'), ('SEK', 'Swedish Krona'), ('CHF', 'Swiss Franc'), ('SYP', 'Syrian Pound'), ('BDT', 'Taka'), ('WST', 'Tala'), ('TZS', 'Tanzanian Shilling'), ('KZT', 'Tenge'), ('XXX', 'The codes assigned for transactions where no currency is involved'), ('TTD', 'Trinidad and Tobago Dollar'), ('MNT', 'Tugrik'), ('TND', 'Tunisian Dinar'), ('TRY', 'Turkish Lira'), ('TMT', 'Turkmenistan New Manat'), ('TVD', 'Tuvalu dollar'), ('AED', 'UAE Dirham'), ('XFU', 'UIC-Franc'), ('USD', 'US Dollar'), ('USN', 'US Dollar (Next day)'), ('UGX', 'Uganda Shilling'), ('CLF', 'Unidad de Fomento'), ('COU', 'Unidad de Valor Real'), ('UYI', 'Uruguay Peso en Unidades Indexadas (URUIURUI)'), ('UYU', 'Uruguayan peso'), ('UZS', 'Uzbekistan Sum'), ('VUV', 'Vatu'), ('CHE', 'WIR Euro'), ('CHW', 'WIR Franc'), ('KRW', 'Won'), ('YER', 'Yemeni Rial'), ('JPY', 'Yen'), ('CNY', 'Yuan Renminbi'), ('ZMK', 'Zambian Kwacha'), ('ZMW', 'Zambian Kwacha'), ('ZWD', 'Zimbabwe Dollar A/06'), ('ZWN', 'Zimbabwe dollar A/08'), ('ZWL', 'Zimbabwe dollar A/09'), ('PLN', 'Zloty')], default='XYZ', editable=False, max_length=3)), + ('total', djmoney.models.fields.MoneyField(decimal_places=2, max_digits=12, verbose_name='total')), + ('captured_amount_currency', djmoney.models.fields.CurrencyField(choices=[('XUA', 'ADB Unit of Account'), ('AFN', 'Afghani'), ('DZD', 'Algerian Dinar'), ('ARS', 'Argentine Peso'), ('AMD', 'Armenian Dram'), ('AWG', 'Aruban Guilder'), ('AUD', 'Australian Dollar'), ('AZN', 'Azerbaijanian Manat'), ('BSD', 'Bahamian Dollar'), ('BHD', 'Bahraini Dinar'), ('THB', 'Baht'), ('PAB', 'Balboa'), ('BBD', 'Barbados Dollar'), ('BYN', 'Belarussian Ruble'), ('BYR', 'Belarussian Ruble'), ('BZD', 'Belize Dollar'), ('BMD', 'Bermudian Dollar (customarily known as Bermuda Dollar)'), ('BTN', 'Bhutanese ngultrum'), ('VEF', 'Bolivar Fuerte'), ('BOB', 'Boliviano'), ('XBA', 'Bond Markets Units European Composite Unit (EURCO)'), ('BRL', 'Brazilian Real'), ('BND', 'Brunei Dollar'), ('BGN', 'Bulgarian Lev'), ('BIF', 'Burundi Franc'), ('XOF', 'CFA Franc BCEAO'), ('XAF', 'CFA franc BEAC'), ('XPF', 'CFP Franc'), ('CAD', 'Canadian Dollar'), ('CVE', 'Cape Verde Escudo'), ('KYD', 'Cayman Islands Dollar'), ('CLP', 'Chilean peso'), ('XTS', 'Codes specifically reserved for testing purposes'), ('COP', 'Colombian peso'), ('KMF', 'Comoro Franc'), ('CDF', 'Congolese franc'), ('BAM', 'Convertible Marks'), ('NIO', 'Cordoba Oro'), ('CRC', 'Costa Rican Colon'), ('HRK', 'Croatian Kuna'), ('CUP', 'Cuban Peso'), ('CUC', 'Cuban convertible peso'), ('CZK', 'Czech Koruna'), ('GMD', 'Dalasi'), ('DKK', 'Danish Krone'), ('MKD', 'Denar'), ('DJF', 'Djibouti Franc'), ('STD', 'Dobra'), ('DOP', 'Dominican Peso'), ('VND', 'Dong'), ('XCD', 'East Caribbean Dollar'), ('EGP', 'Egyptian Pound'), ('SVC', 'El Salvador Colon'), ('ETB', 'Ethiopian Birr'), ('EUR', 'Euro'), ('XBB', 'European Monetary Unit (E.M.U.-6)'), ('XBD', 'European Unit of Account 17(E.U.A.-17)'), ('XBC', 'European Unit of Account 9(E.U.A.-9)'), ('FKP', 'Falkland Islands Pound'), ('FJD', 'Fiji Dollar'), ('HUF', 'Forint'), ('GHS', 'Ghana Cedi'), ('GIP', 'Gibraltar Pound'), ('XAU', 'Gold'), ('XFO', 'Gold-Franc'), ('PYG', 'Guarani'), ('GNF', 'Guinea Franc'), ('GYD', 'Guyana Dollar'), ('HTG', 'Haitian gourde'), ('HKD', 'Hong Kong Dollar'), ('UAH', 'Hryvnia'), ('ISK', 'Iceland Krona'), ('INR', 'Indian Rupee'), ('IRR', 'Iranian Rial'), ('IQD', 'Iraqi Dinar'), ('IMP', 'Isle of Man Pound'), ('JMD', 'Jamaican Dollar'), ('JOD', 'Jordanian Dinar'), ('KES', 'Kenyan Shilling'), ('PGK', 'Kina'), ('LAK', 'Kip'), ('KWD', 'Kuwaiti Dinar'), ('AOA', 'Kwanza'), ('MMK', 'Kyat'), ('GEL', 'Lari'), ('LVL', 'Latvian Lats'), ('LBP', 'Lebanese Pound'), ('ALL', 'Lek'), ('HNL', 'Lempira'), ('SLL', 'Leone'), ('LSL', 'Lesotho loti'), ('LRD', 'Liberian Dollar'), ('LYD', 'Libyan Dinar'), ('SZL', 'Lilangeni'), ('LTL', 'Lithuanian Litas'), ('MGA', 'Malagasy Ariary'), ('MWK', 'Malawian Kwacha'), ('MYR', 'Malaysian Ringgit'), ('TMM', 'Manat'), ('MUR', 'Mauritius Rupee'), ('MZN', 'Metical'), ('MXV', 'Mexican Unidad de Inversion (UDI)'), ('MXN', 'Mexican peso'), ('MDL', 'Moldovan Leu'), ('MAD', 'Moroccan Dirham'), ('BOV', 'Mvdol'), ('NGN', 'Naira'), ('ERN', 'Nakfa'), ('NAD', 'Namibian Dollar'), ('NPR', 'Nepalese Rupee'), ('ANG', 'Netherlands Antillian Guilder'), ('ILS', 'New Israeli Sheqel'), ('RON', 'New Leu'), ('TWD', 'New Taiwan Dollar'), ('NZD', 'New Zealand Dollar'), ('KPW', 'North Korean Won'), ('NOK', 'Norwegian Krone'), ('PEN', 'Nuevo Sol'), ('MRO', 'Ouguiya'), ('TOP', 'Paanga'), ('PKR', 'Pakistan Rupee'), ('XPD', 'Palladium'), ('MOP', 'Pataca'), ('PHP', 'Philippine Peso'), ('XPT', 'Platinum'), ('GBP', 'Pound Sterling'), ('BWP', 'Pula'), ('QAR', 'Qatari Rial'), ('GTQ', 'Quetzal'), ('ZAR', 'Rand'), ('OMR', 'Rial Omani'), ('KHR', 'Riel'), ('MVR', 'Rufiyaa'), ('IDR', 'Rupiah'), ('RUB', 'Russian Ruble'), ('RWF', 'Rwanda Franc'), ('XDR', 'SDR'), ('SHP', 'Saint Helena Pound'), ('SAR', 'Saudi Riyal'), ('RSD', 'Serbian Dinar'), ('SCR', 'Seychelles Rupee'), ('XAG', 'Silver'), ('SGD', 'Singapore Dollar'), ('SBD', 'Solomon Islands Dollar'), ('KGS', 'Som'), ('SOS', 'Somali Shilling'), ('TJS', 'Somoni'), ('SSP', 'South Sudanese Pound'), ('LKR', 'Sri Lanka Rupee'), ('XSU', 'Sucre'), ('SDG', 'Sudanese Pound'), ('SRD', 'Surinam Dollar'), ('SEK', 'Swedish Krona'), ('CHF', 'Swiss Franc'), ('SYP', 'Syrian Pound'), ('BDT', 'Taka'), ('WST', 'Tala'), ('TZS', 'Tanzanian Shilling'), ('KZT', 'Tenge'), ('XXX', 'The codes assigned for transactions where no currency is involved'), ('TTD', 'Trinidad and Tobago Dollar'), ('MNT', 'Tugrik'), ('TND', 'Tunisian Dinar'), ('TRY', 'Turkish Lira'), ('TMT', 'Turkmenistan New Manat'), ('TVD', 'Tuvalu dollar'), ('AED', 'UAE Dirham'), ('XFU', 'UIC-Franc'), ('USD', 'US Dollar'), ('USN', 'US Dollar (Next day)'), ('UGX', 'Uganda Shilling'), ('CLF', 'Unidad de Fomento'), ('COU', 'Unidad de Valor Real'), ('UYI', 'Uruguay Peso en Unidades Indexadas (URUIURUI)'), ('UYU', 'Uruguayan peso'), ('UZS', 'Uzbekistan Sum'), ('VUV', 'Vatu'), ('CHE', 'WIR Euro'), ('CHW', 'WIR Franc'), ('KRW', 'Won'), ('YER', 'Yemeni Rial'), ('JPY', 'Yen'), ('CNY', 'Yuan Renminbi'), ('ZMK', 'Zambian Kwacha'), ('ZMW', 'Zambian Kwacha'), ('ZWD', 'Zimbabwe Dollar A/06'), ('ZWN', 'Zimbabwe dollar A/08'), ('ZWL', 'Zimbabwe dollar A/09'), ('PLN', 'Zloty')], default='XYZ', editable=False, max_length=3)), + ('captured_amount', djmoney.models.fields.MoneyField(decimal_places=2, max_digits=12, verbose_name='captured amount')), + ('cc_first_digits', models.CharField(blank=True, default='', max_length=6, verbose_name='cc first digits')), + ('cc_last_digits', models.CharField(blank=True, default='', max_length=4, verbose_name='cc last digits')), + ('cc_brand', models.CharField(blank=True, default='', max_length=40, verbose_name='cc brand')), + ('cc_exp_month', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(12)], verbose_name='cc exp month')), + ('cc_exp_year', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1000)], verbose_name='cc exp year')), + ('customer_email', models.EmailField(max_length=254, verbose_name='customer email')), + ('customer_ip_address', models.GenericIPAddressField(blank=True, null=True, verbose_name='customer ip address')), + ('extra_data', models.TextField(blank=True, default='', verbose_name='extra data')), + ], + options={ + 'verbose_name': 'payment', + 'verbose_name_plural': 'payments', + 'ordering': ('pk',), + }, + ), + migrations.CreateModel( + name='Transaction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('token', models.CharField(blank=True, default='', max_length=128, verbose_name='token')), + ('kind', models.CharField(choices=[('auth', 'Authorization'), ('refund', 'Refund'), ('capture', 'Capture'), ('void', 'Void')], max_length=10, verbose_name='kind')), + ('is_success', models.BooleanField(default=False, verbose_name='is success')), + ('amount_currency', djmoney.models.fields.CurrencyField(choices=[('XUA', 'ADB Unit of Account'), ('AFN', 'Afghani'), ('DZD', 'Algerian Dinar'), ('ARS', 'Argentine Peso'), ('AMD', 'Armenian Dram'), ('AWG', 'Aruban Guilder'), ('AUD', 'Australian Dollar'), ('AZN', 'Azerbaijanian Manat'), ('BSD', 'Bahamian Dollar'), ('BHD', 'Bahraini Dinar'), ('THB', 'Baht'), ('PAB', 'Balboa'), ('BBD', 'Barbados Dollar'), ('BYN', 'Belarussian Ruble'), ('BYR', 'Belarussian Ruble'), ('BZD', 'Belize Dollar'), ('BMD', 'Bermudian Dollar (customarily known as Bermuda Dollar)'), ('BTN', 'Bhutanese ngultrum'), ('VEF', 'Bolivar Fuerte'), ('BOB', 'Boliviano'), ('XBA', 'Bond Markets Units European Composite Unit (EURCO)'), ('BRL', 'Brazilian Real'), ('BND', 'Brunei Dollar'), ('BGN', 'Bulgarian Lev'), ('BIF', 'Burundi Franc'), ('XOF', 'CFA Franc BCEAO'), ('XAF', 'CFA franc BEAC'), ('XPF', 'CFP Franc'), ('CAD', 'Canadian Dollar'), ('CVE', 'Cape Verde Escudo'), ('KYD', 'Cayman Islands Dollar'), ('CLP', 'Chilean peso'), ('XTS', 'Codes specifically reserved for testing purposes'), ('COP', 'Colombian peso'), ('KMF', 'Comoro Franc'), ('CDF', 'Congolese franc'), ('BAM', 'Convertible Marks'), ('NIO', 'Cordoba Oro'), ('CRC', 'Costa Rican Colon'), ('HRK', 'Croatian Kuna'), ('CUP', 'Cuban Peso'), ('CUC', 'Cuban convertible peso'), ('CZK', 'Czech Koruna'), ('GMD', 'Dalasi'), ('DKK', 'Danish Krone'), ('MKD', 'Denar'), ('DJF', 'Djibouti Franc'), ('STD', 'Dobra'), ('DOP', 'Dominican Peso'), ('VND', 'Dong'), ('XCD', 'East Caribbean Dollar'), ('EGP', 'Egyptian Pound'), ('SVC', 'El Salvador Colon'), ('ETB', 'Ethiopian Birr'), ('EUR', 'Euro'), ('XBB', 'European Monetary Unit (E.M.U.-6)'), ('XBD', 'European Unit of Account 17(E.U.A.-17)'), ('XBC', 'European Unit of Account 9(E.U.A.-9)'), ('FKP', 'Falkland Islands Pound'), ('FJD', 'Fiji Dollar'), ('HUF', 'Forint'), ('GHS', 'Ghana Cedi'), ('GIP', 'Gibraltar Pound'), ('XAU', 'Gold'), ('XFO', 'Gold-Franc'), ('PYG', 'Guarani'), ('GNF', 'Guinea Franc'), ('GYD', 'Guyana Dollar'), ('HTG', 'Haitian gourde'), ('HKD', 'Hong Kong Dollar'), ('UAH', 'Hryvnia'), ('ISK', 'Iceland Krona'), ('INR', 'Indian Rupee'), ('IRR', 'Iranian Rial'), ('IQD', 'Iraqi Dinar'), ('IMP', 'Isle of Man Pound'), ('JMD', 'Jamaican Dollar'), ('JOD', 'Jordanian Dinar'), ('KES', 'Kenyan Shilling'), ('PGK', 'Kina'), ('LAK', 'Kip'), ('KWD', 'Kuwaiti Dinar'), ('AOA', 'Kwanza'), ('MMK', 'Kyat'), ('GEL', 'Lari'), ('LVL', 'Latvian Lats'), ('LBP', 'Lebanese Pound'), ('ALL', 'Lek'), ('HNL', 'Lempira'), ('SLL', 'Leone'), ('LSL', 'Lesotho loti'), ('LRD', 'Liberian Dollar'), ('LYD', 'Libyan Dinar'), ('SZL', 'Lilangeni'), ('LTL', 'Lithuanian Litas'), ('MGA', 'Malagasy Ariary'), ('MWK', 'Malawian Kwacha'), ('MYR', 'Malaysian Ringgit'), ('TMM', 'Manat'), ('MUR', 'Mauritius Rupee'), ('MZN', 'Metical'), ('MXV', 'Mexican Unidad de Inversion (UDI)'), ('MXN', 'Mexican peso'), ('MDL', 'Moldovan Leu'), ('MAD', 'Moroccan Dirham'), ('BOV', 'Mvdol'), ('NGN', 'Naira'), ('ERN', 'Nakfa'), ('NAD', 'Namibian Dollar'), ('NPR', 'Nepalese Rupee'), ('ANG', 'Netherlands Antillian Guilder'), ('ILS', 'New Israeli Sheqel'), ('RON', 'New Leu'), ('TWD', 'New Taiwan Dollar'), ('NZD', 'New Zealand Dollar'), ('KPW', 'North Korean Won'), ('NOK', 'Norwegian Krone'), ('PEN', 'Nuevo Sol'), ('MRO', 'Ouguiya'), ('TOP', 'Paanga'), ('PKR', 'Pakistan Rupee'), ('XPD', 'Palladium'), ('MOP', 'Pataca'), ('PHP', 'Philippine Peso'), ('XPT', 'Platinum'), ('GBP', 'Pound Sterling'), ('BWP', 'Pula'), ('QAR', 'Qatari Rial'), ('GTQ', 'Quetzal'), ('ZAR', 'Rand'), ('OMR', 'Rial Omani'), ('KHR', 'Riel'), ('MVR', 'Rufiyaa'), ('IDR', 'Rupiah'), ('RUB', 'Russian Ruble'), ('RWF', 'Rwanda Franc'), ('XDR', 'SDR'), ('SHP', 'Saint Helena Pound'), ('SAR', 'Saudi Riyal'), ('RSD', 'Serbian Dinar'), ('SCR', 'Seychelles Rupee'), ('XAG', 'Silver'), ('SGD', 'Singapore Dollar'), ('SBD', 'Solomon Islands Dollar'), ('KGS', 'Som'), ('SOS', 'Somali Shilling'), ('TJS', 'Somoni'), ('SSP', 'South Sudanese Pound'), ('LKR', 'Sri Lanka Rupee'), ('XSU', 'Sucre'), ('SDG', 'Sudanese Pound'), ('SRD', 'Surinam Dollar'), ('SEK', 'Swedish Krona'), ('CHF', 'Swiss Franc'), ('SYP', 'Syrian Pound'), ('BDT', 'Taka'), ('WST', 'Tala'), ('TZS', 'Tanzanian Shilling'), ('KZT', 'Tenge'), ('XXX', 'The codes assigned for transactions where no currency is involved'), ('TTD', 'Trinidad and Tobago Dollar'), ('MNT', 'Tugrik'), ('TND', 'Tunisian Dinar'), ('TRY', 'Turkish Lira'), ('TMT', 'Turkmenistan New Manat'), ('TVD', 'Tuvalu dollar'), ('AED', 'UAE Dirham'), ('XFU', 'UIC-Franc'), ('USD', 'US Dollar'), ('USN', 'US Dollar (Next day)'), ('UGX', 'Uganda Shilling'), ('CLF', 'Unidad de Fomento'), ('COU', 'Unidad de Valor Real'), ('UYI', 'Uruguay Peso en Unidades Indexadas (URUIURUI)'), ('UYU', 'Uruguayan peso'), ('UZS', 'Uzbekistan Sum'), ('VUV', 'Vatu'), ('CHE', 'WIR Euro'), ('CHW', 'WIR Franc'), ('KRW', 'Won'), ('YER', 'Yemeni Rial'), ('JPY', 'Yen'), ('CNY', 'Yuan Renminbi'), ('ZMK', 'Zambian Kwacha'), ('ZMW', 'Zambian Kwacha'), ('ZWD', 'Zimbabwe Dollar A/06'), ('ZWN', 'Zimbabwe dollar A/08'), ('ZWL', 'Zimbabwe dollar A/09'), ('PLN', 'Zloty')], default='XYZ', editable=False, max_length=3)), + ('amount', djmoney.models.fields.MoneyField(decimal_places=2, max_digits=12, verbose_name='amount')), + ('error', models.CharField(blank=True, max_length=256, null=True, verbose_name='error')), + ('gateway_response', models.TextField(verbose_name='gateway response')), + ('payment', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to='payment.Payment', verbose_name='payment')), + ], + options={ + 'verbose_name': 'transaction', + 'verbose_name_plural': 'transactions', + 'ordering': ('pk',), + }, + ), + ] diff --git a/payment/migrations/0002_cascade_delete_transaction.py b/payment/migrations/0002_cascade_delete_transaction.py new file mode 100644 index 0000000..425e1c9 --- /dev/null +++ b/payment/migrations/0002_cascade_delete_transaction.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.3 on 2019-07-29 09:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='transaction', + name='payment', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transactions', to='payment.Payment', verbose_name='payment'), + ), + ] diff --git a/payment/migrations/__init__.py b/payment/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/payment/models.py b/payment/models.py new file mode 100644 index 0000000..661d9d6 --- /dev/null +++ b/payment/models.py @@ -0,0 +1,239 @@ +import json +from operator import attrgetter + +from django.core.validators import MaxValueValidator, MinValueValidator +from django.db import models +from django.db.models import CASCADE +from django.utils.translation import ugettext_lazy as _ +from djmoney.models.fields import MoneyField +from moneyed import Money +from typing import Optional, Dict + +from . import ( + ChargeStatus, + CustomPaymentChoices, + TransactionKind, + get_payment_gateway, +) + +######################################################################################################## +# Credit Cards + +''' +def compute_expiry_date(two_digit_year: int, month: int) -> date: + year = 2000 + two_digit_year + _, last_day_of_month = calendar.monthrange(year, month) + return date(year=year, month=month, day=last_day_of_month) + + +class CreditCardQuerySet(models.QuerySet): + def valid(self, as_of: date = None): + if as_of is None: + as_of = date.today() + return self.filter(expiry_date__gte=as_of) + + +class CreditCard(models.Model): + ACTIVE = 'ACTIVE' + INACTIVE = 'INACTIVE' + STATUS_CHOICES = ( + (ACTIVE, pgettext_lazy('credit card', 'Active')), + (INACTIVE, pgettext_lazy('credit card', 'Inactive')), + ) + + created = models.DateTimeField(auto_now_add=True, db_index=True) + modified = models.DateTimeField(auto_now=True) + type = models.CharField(db_index=True, max_length=3) + number = models.CharField(max_length=255) + expiry_month = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(12)]) + expiry_year = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(99)]) + expiry_date = models.DateField() # A field in the database so we can search for expired cards + + status = FSMField(max_length=20, choices=STATUS_CHOICES, default=ACTIVE, db_index=True) + + objects = CreditCardQuerySet.as_manager() + + @transition(field=status, source=ACTIVE, target=INACTIVE) + def deactivate(self): + pass + + @transition(field=status, source=INACTIVE, target=ACTIVE) + def reactivate(self): + pass + + def is_valid(self, as_of: date = None): + if as_of is None: + as_of = datetime.now().date() + return self.expiry_date >= as_of + + def save(self, *args, **kwargs): + if self.expiry_year is not None and self.expiry_month is not None: + self.expiry_date = compute_expiry_date(two_digit_year=self.expiry_year, month=self.expiry_month) + super().save(*args, **kwargs) +''' + + +######################################################################################################## +# Payments + + +class Payment(models.Model): + """A model that represents a single payment. + + This might be a transactable payment information such as credit card + details, gift card information or a customer's authorization to charge + their PayPal account. + + All payment process related pieces of information are stored + at the gateway level, we are operating on the reusable token + which is a unique identifier of the customer for given gateway. + + Several payment methods can be used within a single order. Each payment + method may consist of multiple transactions. + """ + + gateway = models.CharField(_('gateway'), max_length=255) + is_active = models.BooleanField(_('is_active'), default=True) + created = models.DateTimeField(_('created'), auto_now_add=True) + modified = models.DateTimeField(_('modified'), auto_now=True) + charge_status = models.CharField(_('charge status'), max_length=20, choices=ChargeStatus.CHOICES, + default=ChargeStatus.NOT_CHARGED) + token = models.CharField(_('token'), max_length=128, blank=True, default="") + total = MoneyField(_('total'), max_digits=12, decimal_places=2) + captured_amount = MoneyField(_('captured amount'), max_digits=12, decimal_places=2) + + cc_first_digits = models.CharField(_('cc first digits'), max_length=6, blank=True, default="") + cc_last_digits = models.CharField(_('cc last digits'), max_length=4, blank=True, default="") + cc_brand = models.CharField(_('cc brand'), max_length=40, blank=True, default="") + cc_exp_month = models.PositiveIntegerField(_('cc exp month'), + validators=[MinValueValidator(1), MaxValueValidator(12)], null=True, + blank=True) + cc_exp_year = models.PositiveIntegerField(_('cc exp year'), validators=[MinValueValidator(1000)], null=True, + blank=True) + + customer_email = models.EmailField(_('customer email'), ) + + customer_ip_address = models.GenericIPAddressField(_('customer ip address'), blank=True, null=True) + extra_data = models.TextField(_('extra data'), blank=True, default="") + + class Meta: + verbose_name = _('payment') + verbose_name_plural = _('payments') + ordering = ("pk",) + + def __str__(self): + return _('Payment {} ({})').format(self.id, self.get_charge_status_display()) + + def __repr__(self): + return "Payment(gateway=%s, is_active=%s, created=%s, charge_status=%s)" % \ + (self.gateway, self.is_active, self.created, self.charge_status) + + def clean(self): + if self.captured_amount is None: + self.captured_amount = Money(0, self.total.currency) + + def get_last_transaction(self): + return max(self.transactions.all(), default=None, key=attrgetter("pk")) + + def get_authorized_amount(self): + money = Money(0, self.total.currency) + + # Query all the transactions which should be prefetched + # to optimize db queries + transactions = self.transactions.all() + + # There is no authorized amount anymore when capture is succeeded + # since capture can only be made once, even it is a partial capture + if any([txn.kind == TransactionKind.CAPTURE and txn.is_success for txn in transactions]): + return money + + # Filter the succeeded auth transactions + authorized_txns = [txn for txn in transactions if txn.kind == TransactionKind.AUTH and txn.is_success] + + for txn in authorized_txns: + money += txn.amount + + # If multiple partial capture is supported later though it's unlikely, + # the authorized amount should exclude the already captured amount here + return money + + def get_charge_amount(self): + """Retrieve the maximum capture possible.""" + return self.total - self.captured_amount + + @property + def is_authorized(self): + return any([txn.kind == TransactionKind.AUTH and txn.is_success for txn in self.transactions.all()]) + + @property + def not_charged(self): + return self.charge_status == ChargeStatus.NOT_CHARGED + + def can_authorize(self): + return self.is_active and self.not_charged + + def can_capture(self): + if not (self.is_active and self.not_charged): + return False + + _, gateway_config = get_payment_gateway(self.gateway) + if gateway_config.auto_capture: + return self.is_authorized + + return True + + def can_void(self): + return self.is_active and self.not_charged and self.is_authorized + + def can_refund(self): + can_refund_charge_status = ( + ChargeStatus.PARTIALLY_CHARGED, + ChargeStatus.FULLY_CHARGED, + ChargeStatus.PARTIALLY_REFUNDED, + ) + return ( + self.is_active + and self.charge_status in can_refund_charge_status + and self.gateway != CustomPaymentChoices.MANUAL + ) + + @property + def metadata(self) -> Dict[str, str]: + if self.extra_data == '': + return {} + else: + return json.loads(self.extra_data) + + @metadata.setter + def metadata(self, d: Optional[Dict[str, str]]): + if d == {}: + self.extra_data = '' + else: # Could so some assertions on the types of keys and values + self.extra_data = json.dumps(d) + + +class Transaction(models.Model): + """Represents a single payment operation. + + Transaction is an attempt to transfer money between your store + and your customers, with a chosen payment method. + """ + + created = models.DateTimeField(_('created'), auto_now_add=True, editable=False) + payment = models.ForeignKey(Payment, related_name="transactions", on_delete=CASCADE, + verbose_name=_('payment')) + token = models.CharField(_('token'), max_length=128, blank=True, default="") + kind = models.CharField(_('kind'), max_length=10, choices=TransactionKind.CHOICES) + is_success = models.BooleanField(_('is success'), default=False) + amount = MoneyField(_('amount'), max_digits=12, decimal_places=2) + error = models.CharField(_('error'), max_length=256, blank=True, null=True) + gateway_response = models.TextField(_('gateway response'), ) # JSON or XML + + class Meta: + verbose_name = _('transaction') + verbose_name_plural = _('transactions') + ordering = ("pk",) + + def __repr__(self): + return "Transaction(type=%s, is_success=%s, created=%s)" % \ + (self.kind, self.is_success, self.created) diff --git a/payment/utils.py b/payment/utils.py new file mode 100644 index 0000000..6e0e224 --- /dev/null +++ b/payment/utils.py @@ -0,0 +1,420 @@ +import json +import logging +from functools import wraps + +from django.core.serializers.json import DjangoJSONEncoder +from django.db import transaction +from moneyed import Money +from typing import Optional + +from . import ( + ChargeStatus, + GatewayError, + OperationType, + PaymentError, + TransactionKind, + get_payment_gateway, +) +from .interface import GatewayResponse, PaymentData, AddressData +from .models import Payment, Transaction + +logger = logging.getLogger(__name__) + +GENERIC_TRANSACTION_ERROR = "Transaction was unsuccessful" +REQUIRED_GATEWAY_KEYS = { + "transaction_id", + "is_success", + "kind", + "error", + "amount", + "currency", +} + +ALLOWED_GATEWAY_KINDS = {choices[0] for choices in TransactionKind.CHOICES} + + +def get_gateway_operation_func(gateway, operation_type): + """Return gateway method based on the operation type to be performed.""" + if operation_type == OperationType.PROCESS_PAYMENT: + return gateway.process_payment + if operation_type == OperationType.AUTH: + return gateway.authorize + if operation_type == OperationType.CAPTURE: + return gateway.capture + if operation_type == OperationType.VOID: + return gateway.void + if operation_type == OperationType.REFUND: + return gateway.refund + + +def create_payment_information( + payment: Payment, + payment_token: Optional[str] = None, + amount: Money = None, + billing_address: AddressData = None, + shipping_address: AddressData = None, +) -> PaymentData: + """Extracts order information along with payment details. + + Returns information required to process payment and additional + billing/shipping addresses for optional fraud-prevention mechanisms. + """ + + # PATCH: order_id = payment.order.pk if payment.order else None + order_id = None + + if amount is None: + amount = payment.total + + return PaymentData( # type:ignore + token=payment_token, # The contract is not clear, is this optional or not? + amount=amount.amount, + currency=amount.currency.code, + billing=billing_address, + shipping=shipping_address, + order_id=order_id, + customer_ip_address=payment.customer_ip_address, + customer_email=payment.customer_email, + metadata=payment.metadata, + ) + + +def require_active_payment(view): + """Require an active payment instance. + + Decorate a view to check if payment is authorized, so any actions + can be performed on it. + """ + + @wraps(view) + def func(payment: Payment, *args, **kwargs): + if not payment.is_active: + raise PaymentError("This payment is no longer active.") + return view(payment, *args, **kwargs) + + return func + + +''' +def create_payment( + gateway: str, + total: Decimal, + currency: str, + email: str, + billing_address: Address, + customer_ip_address: str = "", + payment_token: str = "", + extra_data: Dict = None, + checkout: Checkout = None, + order: Order = None, +) -> Payment: + """Create a payment instance. + + This method is responsible for creating payment instances that works for + both Django views and GraphQL mutations. + """ + defaults = { + "billing_email": email, + "billing_first_name": billing_address.first_name, + "billing_last_name": billing_address.last_name, + "billing_company_name": billing_address.company_name, + "billing_address_1": billing_address.street_address_1, + "billing_address_2": billing_address.street_address_2, + "billing_city": billing_address.city, + "billing_postal_code": billing_address.postal_code, + "billing_country_code": billing_address.country.code, + "billing_country_area": billing_address.country_area, + "currency": currency, + "gateway": gateway, + "total": total, + } + + if extra_data is None: + extra_data = {} + + data = { + "is_active": True, + "customer_ip_address": customer_ip_address, + "extra_data": extra_data, + "token": payment_token, + } + + if order is not None: + data["order"] = order + if checkout is not None: + data["checkout"] = checkout + + payment, _ = Payment.objects.get_or_create(defaults=defaults, **data) + return payment +''' + + +def create_transaction( + payment: Payment, + kind: str, + payment_information: PaymentData, + gateway_response: GatewayResponse = None, + error_msg=None, +) -> Transaction: + """Create a transaction based on transaction kind and gateway response.""" + + # Default values for token, amount, currency are only used in cases where + # response from gateway was invalid or an exception occured + if not gateway_response: + gateway_response = GatewayResponse( + kind=kind, + transaction_id=payment_information.token, + is_success=False, + amount=payment_information.amount, + currency=payment_information.currency, + error=error_msg, + raw_response={}, + ) + + return Transaction.objects.create( + payment=payment, + kind=gateway_response.kind, + token=gateway_response.transaction_id, + is_success=gateway_response.is_success, + amount=Money(gateway_response.amount, gateway_response.currency), + error=gateway_response.error, + gateway_response=gateway_response.raw_response or {}, + ) + + +def gateway_get_client_token(gateway_name: str): + """Gets client token, that will be used as a customer's identificator for + client-side tokenization of the chosen payment method. + """ + gateway, gateway_config = get_payment_gateway(gateway_name) + return gateway.get_client_token(config=gateway_config) + + +def clean_capture(payment: Payment, amount: Money): + """Check if payment can be captured.""" + if amount.amount <= 0: + raise PaymentError("Amount should be a positive number.") + if not payment.can_capture(): + raise PaymentError("This payment cannot be captured.") + if amount > payment.total or amount > (payment.total - payment.captured_amount): + raise PaymentError("Unable to charge more than un-captured amount.") + + +def clean_authorize(payment: Payment): + """Check if payment can be authorized.""" + if not payment.can_authorize(): + raise PaymentError("Charged transactions cannot be authorized again.") + + +def call_gateway(operation_type, payment, payment_token, **extra_params): + """Helper that calls the passed gateway function and handles exceptions. + + Additionally does validation of the returned gateway response. + """ + gateway, gateway_config = get_payment_gateway(payment.gateway) + gateway_response = None + error_msg = None + + payment_information = create_payment_information( + payment, payment_token, **extra_params + ) + + try: + func = get_gateway_operation_func(gateway, operation_type) + except AttributeError: + error_msg = "Gateway doesn't implement {} operation".format(operation_type.name) + logger.exception(error_msg) + raise PaymentError(error_msg) + + # The transaction kind is provided as a default value + # for creating transactions when gateway has invalid response + # The PROCESS_PAYMENT operation has CAPTURE as default transaction kind + # For other operations, the transaction kind is same wtih operation type + default_transaction_kind = TransactionKind.CAPTURE + if operation_type != OperationType.PROCESS_PAYMENT: + default_transaction_kind = getattr( + TransactionKind, OperationType(operation_type).name + ) + + # Validate the default transaction kind + if default_transaction_kind not in dict(TransactionKind.CHOICES): + error_msg = "The default transaction kind is invalid" + logger.exception(error_msg) + raise PaymentError(error_msg) + + try: + gateway_response = func( + payment_information=payment_information, config=gateway_config + ) + validate_gateway_response(gateway_response) + except GatewayError: + error_msg = "Gateway response validation failed" + logger.exception(error_msg) + gateway_response = None # Set response empty as the validation failed + except Exception as e: + error_msg = 'Gateway encountered an error {}'.format(e) + logger.exception(error_msg) + finally: + payment_transaction = create_transaction( + payment=payment, + kind=default_transaction_kind, + payment_information=payment_information, + error_msg=error_msg, + gateway_response=gateway_response, + ) + + if not payment_transaction.is_success: + # Attempt to get errors from response, if none raise a generic one + raise PaymentError(payment_transaction.error or GENERIC_TRANSACTION_ERROR) + + return payment_transaction + + +def validate_gateway_response(response: GatewayResponse): + """Validates response to be a correct format for Us to process.""" + + if not isinstance(response, GatewayResponse): + raise GatewayError("Gateway needs to return a GatewayResponse obj") + + if response.kind not in ALLOWED_GATEWAY_KINDS: + raise GatewayError("Gateway response kind must be one of {}".format(sorted(ALLOWED_GATEWAY_KINDS))) + + try: + json.dumps(response.raw_response, cls=DjangoJSONEncoder) + except (TypeError, ValueError): + raise GatewayError("Gateway response needs to be json serializable") + + +@transaction.atomic +def _gateway_postprocess(transaction, payment): + transaction_kind = transaction.kind + + if transaction_kind == TransactionKind.CAPTURE: + if payment.captured_amount is not None: + payment.captured_amount += transaction.amount + else: + payment.captured_amount = transaction.amount + + if payment.get_charge_amount().amount <= 0: + payment.charge_status = ChargeStatus.FULLY_CHARGED + else: + payment.charge_status = ChargeStatus.PARTIALLY_CHARGED + + payment.save() + + elif transaction_kind == TransactionKind.VOID: + payment.is_active = False + payment.save() + + elif transaction_kind == TransactionKind.REFUND: + payment.captured_amount -= transaction.amount + payment.charge_status = ChargeStatus.PARTIALLY_REFUNDED + if payment.captured_amount.amount <= 0: + payment.charge_status = ChargeStatus.FULLY_REFUNDED + payment.is_active = False + payment.save() + + +@require_active_payment +def gateway_process_payment(payment: Payment, payment_token: str, **extras) -> Transaction: + """Performs whole payment process on a gateway.""" + transaction = call_gateway( + operation_type=OperationType.PROCESS_PAYMENT, + payment=payment, + payment_token=payment_token, + **extras, + ) + + _gateway_postprocess(transaction, payment) + return transaction + + +@require_active_payment +def gateway_authorize(payment: Payment, payment_token: str) -> Transaction: + """Authorizes the payment and creates relevant transaction. + + Args: + - payment_token: One-time-use reference to payment information. + """ + clean_authorize(payment) + return call_gateway(operation_type=OperationType.AUTH, payment=payment, payment_token=payment_token) + + +@require_active_payment +def gateway_capture(payment: Payment, amount: Money = None) -> Transaction: + """Captures the money that was reserved during the authorization stage.""" + if amount is None: + amount = payment.get_charge_amount() + clean_capture(payment, amount) + + auth_transaction = payment.transactions.filter( + kind=TransactionKind.AUTH, is_success=True + ).first() + if auth_transaction is None: + raise PaymentError("Cannot capture unauthorized transaction") + payment_token = auth_transaction.token + + transaction = call_gateway( + operation_type=OperationType.CAPTURE, + payment=payment, + payment_token=payment_token, + amount=amount, + ) + + _gateway_postprocess(transaction, payment) + return transaction + + +@require_active_payment +def gateway_void(payment) -> Transaction: + if not payment.can_void(): + raise PaymentError("Only pre-authorized transactions can be voided.") + + auth_transaction = payment.transactions.filter( + kind=TransactionKind.AUTH, is_success=True + ).first() + if auth_transaction is None: + raise PaymentError("Cannot void unauthorized transaction") + payment_token = auth_transaction.token + + transaction = call_gateway( + operation_type=OperationType.VOID, payment=payment, payment_token=payment_token + ) + + _gateway_postprocess(transaction, payment) + return transaction + + +@require_active_payment +def gateway_refund(payment, amount: Money = None) -> Transaction: + """Refunds the charged funds back to the customer. + Refunds can be total or partial. + """ + if amount is None: + # If no amount is specified, refund the maximum possible + amount = payment.captured_amount + + if not payment.can_refund(): + raise PaymentError("This payment cannot be refunded.") + + if amount.amount <= 0: + raise PaymentError("Amount should be a positive number.") + if amount > payment.captured_amount: + raise PaymentError("Cannot refund more than captured") + + transaction = payment.transactions.filter( + kind=TransactionKind.CAPTURE, is_success=True + ).first() + if transaction is None: + raise PaymentError("Cannot refund uncaptured transaction") + payment_token = transaction.token + + transaction = call_gateway( + operation_type=OperationType.REFUND, + payment=payment, + payment_token=payment_token, + amount=amount, + ) + + _gateway_postprocess(transaction, payment) + return transaction diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..be7361a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,24 @@ +[flake8] +exclude = .git, .tox, .direnv, */migrations/* +max-line-length = 119 + +[tool:pytest] +DJANGO_SETTINGS_MODULE = tests.settings +markers = + integration + +[mypy] +ignore_missing_imports = True + +[mypy-payment.migrations.*] +ignore_errors = True + +[coverage:run] +branch = 1 +omit = + */migrations/* + +[coverage:report] +omit = + */management/* + */admin.py \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..174b450 --- /dev/null +++ b/setup.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +from setuptools import setup + +setup( + name='django-payment', + version='0.5', + description='', + long_description='', + author='Nicholas Wolff', + author_email='nwolff@gmail.com', + url='https://github.com/skioo/django-payment', + download_url='https://pypi.python.org/pypi/django-payment', + packages=[ + 'payment', + 'payment.gateways', + 'payment.gateways.dummy', + 'payment.gateways.stripe', + 'payment.migrations', + ], + package_data={ + 'payment': ['locale/*/LC_MESSAGES/*.*', ]}, + install_requires=[ + 'Django>=2.2,<2.3', + 'django-money', + 'django-fsm', + 'structlog', + 'typing', + 'stripe', + 'django-countries', + 'dataclasses', + ], + license='MIT', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'Framework :: Django :: 2.1', + 'Framework :: Django :: 2.2', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + ], +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..461063a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,68 @@ +import pytest +from moneyed import Money + +from payment import TransactionKind, ChargeStatus +from payment.models import Payment + + +@pytest.fixture +def payment_dummy(db, settings): + return Payment.objects.create( + gateway=settings.DUMMY, + total=Money(80, 'USD'), + captured_amount=Money(0, 'USD'), + customer_email='test@example.com', + ) + + +@pytest.fixture +def payment_txn_preauth(payment_dummy): + payment = payment_dummy + payment.save() + + payment.transactions.create( + amount=payment.total, + kind=TransactionKind.AUTH, + gateway_response={}, + is_success=True, + ) + return payment + + +@pytest.fixture +def payment_txn_captured(payment_dummy): + payment = payment_dummy + payment.charge_status = ChargeStatus.FULLY_CHARGED + payment.captured_amount = payment.total + payment.save() + + payment.transactions.create( + amount=payment.total, + kind=TransactionKind.CAPTURE, + gateway_response={}, + is_success=True, + ) + return payment + + +@pytest.fixture +def payment_txn_refunded(payment_dummy): + payment = payment_dummy + payment.charge_status = ChargeStatus.FULLY_REFUNDED + payment.is_active = False + payment.save() + + payment.transactions.create( + amount=payment.total, + kind=TransactionKind.REFUND, + gateway_response={}, + is_success=True, + ) + return payment + + +@pytest.fixture +def payment_not_authorized(payment_dummy): + payment_dummy.is_active = False + payment_dummy.save() + return payment_dummy diff --git a/tests/gateways/__init__.py b/tests/gateways/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/gateways/test_dummy.py b/tests/gateways/test_dummy.py new file mode 100644 index 0000000..dd0d512 --- /dev/null +++ b/tests/gateways/test_dummy.py @@ -0,0 +1,245 @@ +import pytest +from moneyed import Money + +from payment import ( + ChargeStatus, + PaymentError, + TransactionKind, + get_payment_gateway, +) +from payment.utils import ( + create_payment_information, + gateway_authorize, + gateway_capture, + gateway_process_payment, + gateway_refund, + gateway_void, +) + + +def test_authorize_success(payment_dummy): + txn = gateway_authorize(payment=payment_dummy, payment_token="Fake") + assert txn.is_success + assert txn.kind == TransactionKind.AUTH + assert txn.payment == payment_dummy + payment_dummy.refresh_from_db() + assert payment_dummy.is_active + + +@pytest.mark.parametrize( + "is_active, charge_status", + [ + (False, ChargeStatus.NOT_CHARGED), + (False, ChargeStatus.PARTIALLY_CHARGED), + (False, ChargeStatus.FULLY_CHARGED), + (False, ChargeStatus.PARTIALLY_REFUNDED), + (False, ChargeStatus.FULLY_REFUNDED), + (True, ChargeStatus.PARTIALLY_CHARGED), + (True, ChargeStatus.FULLY_CHARGED), + (True, ChargeStatus.PARTIALLY_REFUNDED), + (True, ChargeStatus.FULLY_REFUNDED), + ], +) +def test_authorize_failed(is_active, charge_status, payment_dummy): + payment = payment_dummy + payment.is_active = is_active + payment.charge_status = charge_status + payment.save() + with pytest.raises(PaymentError): + txn = gateway_authorize(payment=payment, payment_token="Fake") + assert txn is None + + +def test_authorize_gateway_error(payment_dummy, monkeypatch): + monkeypatch.setattr("payment.gateways.dummy.dummy_success", lambda: False) + with pytest.raises(PaymentError): + txn = gateway_authorize(payment=payment_dummy, payment_token="Fake") + assert txn.kind == TransactionKind.AUTH + assert not txn.is_success + assert txn.payment == payment_dummy + + +def test_void_success(payment_txn_preauth): + assert payment_txn_preauth.is_active + assert payment_txn_preauth.charge_status == ChargeStatus.NOT_CHARGED + txn = gateway_void(payment=payment_txn_preauth) + assert txn.is_success + assert txn.kind == TransactionKind.VOID + assert txn.payment == payment_txn_preauth + payment_txn_preauth.refresh_from_db() + assert not payment_txn_preauth.is_active + assert payment_txn_preauth.charge_status == ChargeStatus.NOT_CHARGED + + +@pytest.mark.parametrize( + "is_active, charge_status", + [ + (False, ChargeStatus.NOT_CHARGED), + (False, ChargeStatus.PARTIALLY_CHARGED), + (False, ChargeStatus.FULLY_CHARGED), + (False, ChargeStatus.PARTIALLY_REFUNDED), + (False, ChargeStatus.FULLY_REFUNDED), + (True, ChargeStatus.PARTIALLY_CHARGED), + (True, ChargeStatus.FULLY_CHARGED), + (True, ChargeStatus.PARTIALLY_REFUNDED), + (True, ChargeStatus.FULLY_REFUNDED), + ], +) +def test_void_failed(is_active, charge_status, payment_dummy): + payment = payment_dummy + payment.is_active = is_active + payment.charge_status = charge_status + payment.save() + with pytest.raises(PaymentError): + txn = gateway_void(payment=payment) + assert txn is None + + +def test_void_gateway_error(payment_txn_preauth, monkeypatch): + monkeypatch.setattr("payment.gateways.dummy.dummy_success", lambda: False) + with pytest.raises(PaymentError): + txn = gateway_void(payment=payment_txn_preauth) + assert txn.kind == TransactionKind.VOID + assert not txn.is_success + assert txn.payment == payment_txn_preauth + + +@pytest.mark.parametrize( + "amount, charge_status", + [(Money(80, 'USD'), ChargeStatus.FULLY_CHARGED), (Money(70, 'USD'), ChargeStatus.PARTIALLY_CHARGED)], +) +def test_capture_success(amount, charge_status, payment_txn_preauth): + txn = gateway_capture(payment=payment_txn_preauth, amount=amount) + assert txn.is_success + assert txn.payment == payment_txn_preauth + payment_txn_preauth.refresh_from_db() + assert payment_txn_preauth.charge_status == charge_status + assert payment_txn_preauth.is_active + + +@pytest.mark.parametrize( + "amount, captured_amount, charge_status, is_active", + [ + (Money(80, 'USD'), Money(0, 'USD'), ChargeStatus.NOT_CHARGED, False), + (Money(120, 'USD'), Money(0, 'USD'), ChargeStatus.NOT_CHARGED, True), + (Money(80, 'USD'), Money(20, 'USD'), ChargeStatus.PARTIALLY_CHARGED, True), + (Money(80, 'USD'), Money(80, 'USD'), ChargeStatus.FULLY_CHARGED, True), + (Money(80, 'USD'), Money(0, 'USD'), ChargeStatus.FULLY_REFUNDED, True), + ], +) +def test_capture_failed( + amount, captured_amount, charge_status, is_active, payment_dummy +): + payment = payment_dummy + payment.is_active = is_active + payment.captured_amount = captured_amount + payment.charge_status = charge_status + payment.save() + with pytest.raises(PaymentError): + txn = gateway_capture(payment=payment, amount=amount) + assert txn is None + + +def test_capture_gateway_error(payment_txn_preauth, monkeypatch): + monkeypatch.setattr("payment.gateways.dummy.dummy_success", lambda: False) + with pytest.raises(PaymentError): + txn = gateway_capture(payment=payment_txn_preauth, amount=Money(80, 'USD')) + assert txn.kind == TransactionKind.CAPTURE + assert not txn.is_success + assert txn.payment == payment_txn_preauth + + +@pytest.mark.parametrize( + ( + "initial_captured_amount, refund_amount, final_captured_amount, " + "final_charge_status, active_after" + ), + [ + (Money(80, 'USD'), Money(80, 'USD'), Money(0, 'USD'), ChargeStatus.FULLY_REFUNDED, False), + (Money(80, 'USD'), Money(10, 'USD'), Money(70, 'USD'), ChargeStatus.PARTIALLY_REFUNDED, True), + ], +) +def test_refund_success( + initial_captured_amount, + refund_amount, + final_captured_amount, + final_charge_status, + active_after, + payment_txn_captured, +): + payment = payment_txn_captured + payment.charge_status = ChargeStatus.FULLY_CHARGED + payment.captured_amount = initial_captured_amount + payment.save() + txn = gateway_refund(payment=payment, amount=refund_amount) + assert txn.kind == TransactionKind.REFUND + assert txn.is_success + assert txn.payment == payment + assert payment.charge_status == final_charge_status + assert payment.captured_amount == final_captured_amount + assert payment.is_active == active_after + + +@pytest.mark.parametrize( + "initial_captured_amount, refund_amount, initial_charge_status", + [ + (Money(0, 'USD'), Money(10, 'USD'), ChargeStatus.NOT_CHARGED), + (Money(10, 'USD'), Money(20, 'USD'), ChargeStatus.PARTIALLY_CHARGED), + (Money(10, 'USD'), Money(20, 'USD'), ChargeStatus.FULLY_CHARGED), + (Money(10, 'USD'), Money(20, 'USD'), ChargeStatus.PARTIALLY_REFUNDED), + (Money(80, 'USD'), Money(0, 'USD'), ChargeStatus.FULLY_REFUNDED), + ], +) +def test_refund_failed( + initial_captured_amount, refund_amount, initial_charge_status, payment_dummy +): + payment = payment_dummy + payment.charge_status = initial_charge_status + payment.captured_amount = initial_captured_amount + payment.save() + with pytest.raises(PaymentError): + txn = gateway_refund(payment=payment, amount=refund_amount) + assert txn is None + + +def test_refund_gateway_error(payment_txn_captured, monkeypatch): + monkeypatch.setattr("payment.gateways.dummy.dummy_success", lambda: False) + payment = payment_txn_captured + payment.charge_status = ChargeStatus.FULLY_CHARGED + payment.captured_amount = Money(80, 'USD') + payment.save() + with pytest.raises(PaymentError): + gateway_refund(payment=payment, amount=Money(80, 'USD')) + + payment.refresh_from_db() + txn = payment.transactions.last() + assert txn.kind == TransactionKind.REFUND + assert not txn.is_success + assert txn.payment == payment + assert payment.charge_status == ChargeStatus.FULLY_CHARGED + assert payment.captured_amount == Money(80, 'USD') + + +@pytest.mark.parametrize( + "kind, charge_status", + ( + (TransactionKind.AUTH, ChargeStatus.NOT_CHARGED), + (TransactionKind.CAPTURE, ChargeStatus.FULLY_CHARGED), + (TransactionKind.REFUND, ChargeStatus.FULLY_REFUNDED), + ), +) +def test_dummy_payment_form(kind, charge_status, payment_dummy): + payment = payment_dummy + data = {"charge_status": charge_status} + payment_gateway, gateway_config = get_payment_gateway(payment.gateway) + payment_info = create_payment_information(payment) + + form = payment_gateway.create_form( + data=data, + payment_information=payment_info, + connection_params=gateway_config.connection_params, + ) + assert form.is_valid() + gateway_process_payment(payment=payment, payment_token=form.get_payment_token()) + payment.refresh_from_db() + assert payment.transactions.last().kind == kind diff --git a/tests/gateways/test_stripe.py b/tests/gateways/test_stripe.py new file mode 100644 index 0000000..fa32e2c --- /dev/null +++ b/tests/gateways/test_stripe.py @@ -0,0 +1,593 @@ +from decimal import Decimal + +import pytest +import stripe +from math import isclose +from moneyed import Money +from unittest.mock import Mock, patch + +from payment import ChargeStatus +from payment.gateways.stripe import ( + TransactionKind, + _create_response, + _get_client, + _get_error_response_from_exc, + _get_stripe_charge_payload, + authorize, + capture, + create_form, + get_amount_for_stripe, + get_amount_from_stripe, + get_client_token, + get_currency_for_stripe, + get_currency_from_stripe, + refund, + void, +) +from payment.gateways.stripe.forms import ( + StripeCheckoutWidget, + StripePaymentModalForm, +) +from payment.gateways.stripe.utils import ( + get_payment_billing_fullname, +) +from payment.interface import GatewayConfig +from payment.utils import create_payment_information + +TRANSACTION_AMOUNT = Decimal(42.42) +TRANSACTION_REFUND_AMOUNT = Decimal(24.24) +TRANSACTION_CURRENCY = "USD" +TRANSACTION_TOKEN = "fake-stripe-id" +FAKE_TOKEN = "fake-token" +ERROR_MESSAGE = "error-message" + + +@pytest.fixture() +def gateway_config(): + return GatewayConfig( + auto_capture=False, + template_path="template.html", + connection_params={ + "public_key": "public", + "secret_key": "secret", + "store_name": "Saleor", + "store_image": "image.gif", + "prefill": True, + "remember_me": True, + "locale": "auto", + "enable_billing_address": False, + "enable_shipping_address": False, + }, + ) + + +@pytest.fixture() +def client_token(): + return FAKE_TOKEN + + +@pytest.fixture() +def stripe_payment(payment_dummy): + payment_dummy.total = TRANSACTION_AMOUNT + payment_dummy.currency = TRANSACTION_CURRENCY + return payment_dummy + + +@pytest.fixture() +def stripe_authorized_payment(stripe_payment): + stripe_payment.charge_status = ChargeStatus.NOT_CHARGED + stripe_payment.save(update_fields=["charge_status"]) + + return stripe_payment + + +@pytest.fixture() +def stripe_captured_payment(stripe_payment): + stripe_payment.captured_amount = stripe_payment.total + stripe_payment.charge_status = ChargeStatus.FULLY_CHARGED + stripe_payment.save(update_fields=["captured_amount", "charge_status"]) + stripe_payment.transactions.create( + amount=stripe_payment.total, + kind=TransactionKind.CAPTURE, + gateway_response={}, + is_success=True, + ) + return stripe_payment + + +@pytest.fixture() +def stripe_charge_success_response(): + return { + "id": TRANSACTION_TOKEN, + "amount": get_amount_for_stripe(TRANSACTION_AMOUNT, TRANSACTION_CURRENCY), + "amount_refunded": 0, + "currency": get_currency_for_stripe(TRANSACTION_CURRENCY), + "status": "succeeded", + } + + +@pytest.fixture() +def stripe_partial_charge_success_response(stripe_charge_success_response): + response = stripe_charge_success_response.copy() + response["amount_refunded"] = get_amount_for_stripe( + TRANSACTION_REFUND_AMOUNT, TRANSACTION_CURRENCY + ) + return response + + +@pytest.fixture() +def stripe_refund_success_response(stripe_charge_success_response): + response = stripe_charge_success_response.copy() + response.pop("amount_refunded") + response["amount"] = get_amount_for_stripe( + TRANSACTION_REFUND_AMOUNT, TRANSACTION_CURRENCY + ) + return response + + +def test_get_amount_for_stripe(): + assert get_amount_for_stripe(Decimal(1), "USD") == 100 + assert get_amount_for_stripe(Decimal(1), "usd") == 100 + + assert get_amount_for_stripe(Decimal(0.01), "USD") == 1 + assert get_amount_for_stripe(Decimal(24.24), "USD") == 2424 + assert get_amount_for_stripe(Decimal(42.42), "USD") == 4242 + + assert get_amount_for_stripe(Decimal(1), "JPY") == 1 + assert get_amount_for_stripe(Decimal(1), "jpy") == 1 + + +def test_get_amount_from_stripe(): + assert get_amount_from_stripe(100, "USD") == Decimal(1) + assert get_amount_from_stripe(100, "usd") == Decimal(1) + + assert isclose(get_amount_from_stripe(1, "USD"), Decimal(0.01)) + assert isclose(get_amount_from_stripe(2424, "USD"), Decimal(24.24)) + assert isclose(get_amount_from_stripe(4242, "USD"), Decimal(42.42)) + + assert get_amount_from_stripe(1, "JPY") == Decimal(1) + assert get_amount_from_stripe(1, "jpy") == Decimal(1) + + +def test_get_currency_for_stripe(): + assert get_currency_for_stripe("USD") == "usd" + assert get_currency_for_stripe("usd") == "usd" + assert get_currency_for_stripe("uSd") == "usd" + + +def test_get_currency_from_stripe(): + assert get_currency_from_stripe("USD") == "USD" + assert get_currency_from_stripe("usd") == "USD" + assert get_currency_from_stripe("uSd") == "USD" + + +def test_widget_with_additional_attr(stripe_payment, gateway_config): + payment_info = create_payment_information(stripe_payment) + + widget = StripeCheckoutWidget( + payment_info, + gateway_config.connection_params, + attrs={"data-custom": "custom-data"}, + ) + assert 'data-custom="custom-data"' in widget.render() + + +def test_widget_with_prefill_option(stripe_payment, gateway_config): + payment_info = create_payment_information(stripe_payment) + connection_params = gateway_config.connection_params + connection_params["prefill"] = True + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-email="test@example.com"' in widget.render() + + connection_params["prefill"] = False + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-email="test@example.com"' not in widget.render() + + +def test_widget_with_remember_me_option(stripe_payment, gateway_config): + payment_info = create_payment_information(stripe_payment) + connection_params = gateway_config.connection_params + + connection_params["remember_me"] = True + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-allow-remember-me="true"' in widget.render() + + connection_params["remember_me"] = False + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-allow-remember-me="false"' in widget.render() + + +def test_widget_with_enable_billing_address_option(stripe_payment, gateway_config): + payment_info = create_payment_information(stripe_payment, FAKE_TOKEN) + connection_params = gateway_config.connection_params + + connection_params["enable_billing_address"] = True + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-billing-address="true"' in widget.render() + assert 'data-zip-code="true"' in widget.render() + + connection_params["enable_billing_address"] = False + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-billing-address="false"' in widget.render() + assert 'data-zip-code="false"' in widget.render() + + +def test_widget_with_enable_shipping_address_option(stripe_payment, gateway_config): + payment_info = create_payment_information(stripe_payment, FAKE_TOKEN) + connection_params = gateway_config.connection_params + + connection_params["enable_shipping_address"] = True + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-shipping-address="true"' in widget.render() + + connection_params["enable_shipping_address"] = False + widget = StripeCheckoutWidget(payment_info, connection_params) + assert 'data-shipping-address="false"' in widget.render() + + +def test_stripe_payment_form(stripe_payment, gateway_config): + payment_info = create_payment_information(stripe_payment, FAKE_TOKEN) + form = create_form( + None, + payment_information=payment_info, + connection_params=gateway_config.connection_params, + ) + assert isinstance(form, StripePaymentModalForm) + assert not form.is_valid() + + form = create_form( + data={"stripeToken": FAKE_TOKEN}, + payment_information=payment_info, + connection_params=gateway_config.connection_params, + ) + assert isinstance(form, StripePaymentModalForm) + assert form.is_valid() + + +def test_get_client(gateway_config): + assert _get_client(**gateway_config.connection_params).api_key == "secret" + + +def test_get_client_token(): + assert get_client_token() is None + + +def test_get_error_response_from_exc(): + stripe_error = stripe.error.StripeError(json_body={"message": ERROR_MESSAGE}) + invalid_request_error = stripe.error.InvalidRequestError( + message=ERROR_MESSAGE, param=None + ) + + assert _get_error_response_from_exc(stripe_error) == {"message": ERROR_MESSAGE} + assert _get_error_response_from_exc(invalid_request_error) == {} + + +def test_get_stripe_charge_payload_without_shipping(stripe_payment): + payment_info = create_payment_information(stripe_payment, FAKE_TOKEN) + billing_name = get_payment_billing_fullname(payment_info) + expected_payload = { + "capture": True, + "amount": get_amount_for_stripe(TRANSACTION_AMOUNT, TRANSACTION_CURRENCY), + "currency": get_currency_for_stripe(TRANSACTION_CURRENCY), + "source": FAKE_TOKEN, + "description": billing_name, + "metadata": {}, + } + + charge_payload = _get_stripe_charge_payload(payment_info, True) + + assert charge_payload == expected_payload + + +def test_create_transaction_with_charge_success_response( + stripe_payment, stripe_charge_success_response +): + payment_info = create_payment_information(stripe_payment) + + response = _create_response( + payment_information=payment_info, + kind="ANYKIND", + response=stripe_charge_success_response, + error=None, + ) + + assert response.transaction_id == TRANSACTION_TOKEN + assert response.is_success is True + assert isclose(response.amount, TRANSACTION_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + + +def test_create_transaction_with_partial_charge_success_response( + stripe_payment, stripe_partial_charge_success_response +): + payment_info = create_payment_information(stripe_payment) + + response = _create_response( + payment_information=payment_info, + kind="ANYKIND", + response=stripe_partial_charge_success_response, + error=None, + ) + + assert response.transaction_id == TRANSACTION_TOKEN + assert response.is_success is True + assert isclose(response.amount, TRANSACTION_AMOUNT - TRANSACTION_REFUND_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + + +def test_create_transaction_with_refund_success_response( + stripe_payment, stripe_refund_success_response +): + payment_info = create_payment_information(stripe_payment) + + response = _create_response( + payment_information=payment_info, + kind="ANYKIND", + response=stripe_refund_success_response, + error=None, + ) + + assert response.transaction_id == TRANSACTION_TOKEN + assert response.is_success is True + assert isclose(response.amount, TRANSACTION_REFUND_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + + +def test_create_response_with_error_response(stripe_payment): + payment = stripe_payment + payment_info = create_payment_information(payment, FAKE_TOKEN) + stripe_error_response = {} + + response = _create_response( + payment_information=payment_info, + kind="ANYKIND", + response=stripe_error_response, + error=None, + ) + + assert response.transaction_id == FAKE_TOKEN + assert response.is_success is False + assert response.amount == payment.total.amount + assert response.currency == payment.total.currency.code + + +@pytest.mark.integration +@patch("stripe.Charge.create") +def test_authorize( + mock_charge_create, stripe_payment, gateway_config, stripe_charge_success_response +): + payment = stripe_payment + payment_info = create_payment_information(payment, FAKE_TOKEN) + response = stripe_charge_success_response + mock_charge_create.return_value = response + + response = authorize(payment_info, gateway_config) + + assert not response.error + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.AUTH + assert response.is_success + assert isclose(response.amount, TRANSACTION_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == stripe_charge_success_response + + +@pytest.mark.integration +@patch("stripe.Charge.create") +def test_authorize_error_response(mock_charge_create, stripe_payment, gateway_config): + payment = stripe_payment + payment_info = create_payment_information(payment, FAKE_TOKEN) + stripe_error = stripe.error.InvalidRequestError(message=ERROR_MESSAGE, param=None) + mock_charge_create.side_effect = stripe_error + + response = authorize(payment_info, gateway_config) + + assert response.error == ERROR_MESSAGE + assert response.transaction_id == FAKE_TOKEN + assert response.kind == TransactionKind.AUTH + assert not response.is_success + assert response.amount == payment.total.amount + assert response.currency == payment.total.currency.code + assert response.raw_response == _get_error_response_from_exc(stripe_error) + + +@pytest.mark.integration +@patch("stripe.Charge.retrieve") +def test_capture( + mock_charge_retrieve, + stripe_authorized_payment, + gateway_config, + stripe_charge_success_response, +): + payment = stripe_authorized_payment + payment_info = create_payment_information(payment, amount=Money(TRANSACTION_AMOUNT, TRANSACTION_CURRENCY)) + response = stripe_charge_success_response + mock_charge_retrieve.return_value = Mock(capture=Mock(return_value=response)) + + response = capture(payment_info, gateway_config) + + assert not response.error + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.CAPTURE + assert response.is_success + assert isclose(response.amount, TRANSACTION_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == stripe_charge_success_response + + +@pytest.mark.integration +@patch("stripe.Charge.retrieve") +def test_partial_captureummy( + mock_charge_retrieve, + stripe_authorized_payment, + gateway_config, + stripe_partial_charge_success_response, +): + payment = stripe_authorized_payment + payment_info = create_payment_information(payment, amount=Money(TRANSACTION_AMOUNT, TRANSACTION_CURRENCY)) + response = stripe_partial_charge_success_response + mock_charge_retrieve.return_value = Mock(capture=Mock(return_value=response)) + + response = capture(payment_info, gateway_config) + + assert not response.error + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.CAPTURE + assert response.is_success + assert isclose(response.amount, TRANSACTION_AMOUNT - TRANSACTION_REFUND_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == stripe_partial_charge_success_response + + +@pytest.mark.integration +@patch("stripe.Charge.retrieve") +def test_capture_error_response( + mock_charge_retrieve, stripe_authorized_payment, gateway_config +): + payment = stripe_authorized_payment + payment_info = create_payment_information( + payment, TRANSACTION_TOKEN, amount=Money(TRANSACTION_AMOUNT, TRANSACTION_CURRENCY) + ) + stripe_error = stripe.error.InvalidRequestError(message=ERROR_MESSAGE, param=None) + mock_charge_retrieve.side_effect = stripe_error + + response = capture(payment_info, gateway_config) + + assert response.error == ERROR_MESSAGE + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.CAPTURE + assert not response.is_success + assert response.amount == payment.total.amount + assert response.currency == payment.total.currency.code + assert response.raw_response == _get_error_response_from_exc(stripe_error) + + +@pytest.mark.integration +@patch("stripe.Refund.create") +@patch("stripe.Charge.retrieve") +def test_refund_charged( + mock_charge_retrieve, + mock_refund_create, + stripe_captured_payment, + gateway_config, + stripe_refund_success_response, +): + payment = stripe_captured_payment + payment_info = create_payment_information( + payment, TRANSACTION_TOKEN, amount=Money(TRANSACTION_AMOUNT, TRANSACTION_CURRENCY) + ) + response = stripe_refund_success_response + mock_charge_retrieve.return_value = Mock(id="") + mock_refund_create.return_value = response + + response = refund(payment_info, gateway_config) + + assert not response.error + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.REFUND + assert response.is_success + assert isclose(response.amount, TRANSACTION_REFUND_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == stripe_refund_success_response + + +@pytest.mark.integration +@patch("stripe.Refund.create") +@patch("stripe.Charge.retrieve") +def test_refund_captured( + mock_charge_retrieve, + mock_refund_create, + stripe_captured_payment, + gateway_config, + stripe_refund_success_response, +): + payment = stripe_captured_payment + payment_info = create_payment_information(payment, amount=Money(TRANSACTION_AMOUNT, 'USD')) + response = stripe_refund_success_response + mock_charge_retrieve.return_value = Mock(id="") + mock_refund_create.return_value = response + + response = refund(payment_info, gateway_config) + + assert not response.error + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.REFUND + assert response.is_success + assert isclose(response.amount, TRANSACTION_REFUND_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == stripe_refund_success_response + + +@pytest.mark.integration +@patch("stripe.Refund.create") +@patch("stripe.Charge.retrieve") +def test_refund_error_response( + mock_charge_retrieve, mock_refund_create, stripe_captured_payment, gateway_config +): + payment = stripe_captured_payment + payment_info = create_payment_information( + payment, TRANSACTION_TOKEN, amount=Money(TRANSACTION_AMOUNT, TRANSACTION_CURRENCY) + ) + mock_charge_retrieve.return_value = Mock(id="") + stripe_error = stripe.error.InvalidRequestError(message=ERROR_MESSAGE, param=None) + mock_refund_create.side_effect = stripe_error + + response = refund(payment_info, gateway_config) + + assert response.error == ERROR_MESSAGE + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.REFUND + assert not response.is_success + assert response.amount == payment.total.amount + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == _get_error_response_from_exc(stripe_error) + + +@pytest.mark.integration +@patch("stripe.Refund.create") +@patch("stripe.Charge.retrieve") +def test_void( + mock_charge_retrieve, + mock_refund_create, + stripe_authorized_payment, + gateway_config, + stripe_refund_success_response, +): + payment = stripe_authorized_payment + payment_info = create_payment_information(payment, TRANSACTION_TOKEN) + response = stripe_refund_success_response + mock_charge_retrieve.return_value = Mock(id="") + mock_refund_create.return_value = response + + response = void(payment_info, gateway_config) + + assert not response.error + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.VOID + assert response.is_success + assert isclose(response.amount, TRANSACTION_REFUND_AMOUNT) + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == stripe_refund_success_response + + +@pytest.mark.integration +@patch("stripe.Refund.create") +@patch("stripe.Charge.retrieve") +def test_void_error_response( + mock_charge_retrieve, mock_refund_create, stripe_authorized_payment, gateway_config +): + payment = stripe_authorized_payment + payment_info = create_payment_information(payment, TRANSACTION_TOKEN) + mock_charge_retrieve.return_value = Mock(id="") + stripe_error = stripe.error.InvalidRequestError(message=ERROR_MESSAGE, param=None) + mock_refund_create.side_effect = stripe_error + + response = void(payment_info, gateway_config) + + assert response.error == ERROR_MESSAGE + assert response.transaction_id == TRANSACTION_TOKEN + assert response.kind == TransactionKind.VOID + assert not response.is_success + assert response.amount == payment.total.amount + assert response.currency == TRANSACTION_CURRENCY + assert response.raw_response == {} diff --git a/tests/settings.py b/tests/settings.py new file mode 100644 index 0000000..b91e108 --- /dev/null +++ b/tests/settings.py @@ -0,0 +1,103 @@ +# flake8: noqa + +import os + +from django.utils.translation import pgettext_lazy + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'test.db' + }, +} + +SECRET_KEY = 'not_so_secret' + +USE_TZ = True + +# Use a fast hasher to speed up tests. +PASSWORD_HASHERS = [ + 'django.contrib.auth.hashers.MD5PasswordHasher', +] + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.messages', + 'django.contrib.contenttypes', + 'django_fsm', + 'djmoney', + 'tests', + 'payment.apps.PaymentConfig', +] + +MIDDLEWARE = [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +] + +STATIC_URL = '/static/' + +ROOT_URLCONF = 'tests.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.debug', + 'django.template.context_processors.i18n', + 'django.template.context_processors.request', + 'django.template.context_processors.static', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +DUMMY = "dummy" +STRIPE = "stripe" + +CHECKOUT_PAYMENT_GATEWAYS = { + DUMMY: pgettext_lazy("Payment method name", "Dummy gateway"), + STRIPE: pgettext_lazy("Payment method name", "Stripe"), +} + +PAYMENT_GATEWAYS = { + DUMMY: { + "module": "payment.gateways.dummy", + "config": { + "auto_capture": True, + "connection_params": {}, + "template_path": "payment/dummy.html", + }, + }, + STRIPE: { + "module": "payment.gateways.stripe", + "config": { + "auto_capture": True, + "template_path": "payment/stripe.html", + "connection_params": { + "public_key": os.environ.get("STRIPE_PUBLIC_KEY"), + "secret_key": os.environ.get("STRIPE_SECRET_KEY"), + "store_name": os.environ.get("STRIPE_STORE_NAME", "skioo shop"), + "store_image": os.environ.get("STRIPE_STORE_IMAGE", None), + "prefill": os.environ.get("STRIPE_PREFILL", True), + "remember_me": os.environ.get("STRIPE_REMEMBER_ME", False), + "locale": os.environ.get("STRIPE_LOCALE", "auto"), + "enable_billing_address": os.environ.get( + "STRIPE_ENABLE_BILLING_ADDRESS", False + ), + "enable_shipping_address": os.environ.get( + "STRIPE_ENABLE_SHIPPING_ADDRESS", False + ), + }, + }, + }, +} diff --git a/tests/urls.py b/tests/urls.py new file mode 100644 index 0000000..ede2ec9 --- /dev/null +++ b/tests/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls import url +from django.contrib import admin + +urlpatterns = [ + url(r'^admin/', admin.site.urls), +] diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..bd0aaba --- /dev/null +++ b/tox.ini @@ -0,0 +1,26 @@ +[tox] +envlist = + {py36,py37}-{django21,django22}-{test-with-coverage} + py36-django22-{checkmigrations,flake,mypy} + +[testenv] +basepython = + py36: python3.6 + py37: python3.7 +commands = + test-with-coverage: py.test tests --cov=payment + checkmigrations: ./manage.py makemigrations --check --dry-run + flake: flake8 + mypy: mypy . +deps = + django21: Django>=2.1,<2.2 + django22: Django>=2.2,<2.3 + django-money + django-fsm + structlog + typing + pytest-django + pytest-cov + flake8 + mypy +