Adyen package for django-oscar

Overview

Adyen package for django-oscar

Latest Version on PyPI Supported Python versions TravisCI status

This package provides integration with the Adyen payment gateway. It is designed to work with the e-commerce framework django-oscar. This extension supports Django 1.8+, Python 3.6+ and Oscar 1.4+.

Documentation

https://django-oscar-adyen.readthedocs.io/en/latest/

Installation

Get it from PyPi:

$ pip install django-oscar-adyen

Add 'adyen' to INSTALLED_APPS and run:

$ django-admin migrate adyen

to create the appropriate database tables.

Configuration

You have two approaches to configure django-oscar-adyen.

Settings-based configuration

For simple deployments, setting the required values in the settings will suffice.

Edit your settings.py to set the following settings:

  • ADYEN_IDENTIFIER - The identifier of your Adyen account.
  • ADYEN_SKIN_CODE - The code for your Adyen skin.
  • ADYEN_SECRET_KEY - The secret key defined in your Adyen skin.
  • ADYEN_ACTION_URL - The URL towards which the Adyen form should be POSTed to initiate the payment process (e.g. 'https://test.adyen.com/hpp/select.shtml').
  • ADYEN_IP_ADDRESS_HTTP_HEADER - Optional. The header in META to inspect to determine the IP address of the request. Defaults to REMOTE_ADDR.

You will likely need to specify different settings in your test environment as opposed to your production environment.

Class-based configuration

In more complex deployments, you will want to e.g. alter the Adyen identifier based on the request. That is not easily implemented with Django settings, so you can alternatively set ADYEN_CONFIG_CLASS to a config class of your own. See adyen.settings_config.FromSettingsConfig for an example.

Changes

0.9.0 - unreleased

  • Upgrade to Oscar 2.0.

0.8.0 - unreleased

  • Add support for OpenInvoice (Klarna, AfterPay)

0.7.1 - released April 19th, 2016

  • Sanitize payment request form fields from newlines

0.7.0 - released April 18th, 2016

  • Add adyen.signers module to handle signature algorithm
  • Refactor how the merchantSig is generated, using the new adyen.signers module.
  • Splits constants and exceptions into their own module
  • Handle shopper, billing and delivery fields (with signatures for SHA-1)
  • Handle merchantSig with SHA-256 algorithm
  • Improve test coverage and other minor changes

This version is backwrd compatible with version 0.6.0.

Note that plugin users need to implement method get_signer_backend if they uses their own config class from the abstract config class.

Warning

The implementation of the signature with SHA-256 algorithm has not been tested in a real-life case. Plugin users may use it carefuly, and they are invited to report any issues they may encounter.

0.6.0 - released March 1st, 2016

  • Allow plugin user to extend it with get_class,
  • Split several methods in order to override specific parts of the plugin,
  • Expose more methods as public methods to allow plugin user to override more specific parts of the plugin,
  • Add deprecation note on handle_payment_feedback and add two separates methods to handle payment return case and payment notification case.
  • Add allowedMethods to the payment request form (unused by default).
  • Start a sphinx documentation for the project.

This version is backward compatible with version 0.5.0.

Note that plugin users need to implement method get_allowed_methods if they uses their own config class from the abstract config class.

0.5.0 - released October 7th, 2015

0.4.2 - released September 29, 2015

0.4.1 - released September 24, 2015

0.4.0 - released July 14th, 2015

0.3.0 - released July 8th, 2015

License

django-oscar-adyen is released under the BSD license, like Django itself.

Comments
  • Fix IP address detection bug when behind nginx

    Fix IP address detection bug when behind nginx

    Kind of a trivial fix. Nginx forwards the origin IP address in the X-Forwarded-For HTTP header, so we need to use this one when behind it.

    @twidi, care to review? :smile:

    opened by metamatik 4
  • Skip Adyen's test notifications

    Skip Adyen's test notifications

    Adyen has a notification test that one can trigger from their control panel. It's useful for debugging connection problems between our servers and theirs.

    This commit adds support for them, in the sense that it doesn't try to process them. The commit also moves payment notification tests from the PaymentResponseTestCase, and splits them into individual tests.

    opened by maiksprenger 3
  • Allow altering config values based on request

    Allow altering config values based on request

    The goal of this PR is to allow altering Adyen config values based on the request. That is needed to e.g. switch the identifier if a shop serves different domains. I couldn't help myself and cleaned up a bit along the way.

    This PR does mean interface changes for the Scaffold. Given that suspect this extension gets close to no outside users, I have not made any arrangements for backwards-compatibility. The changes should be easy to implement anyway and are documented.

    I accidentally based my PR against master. I'm not sure if it makes much of a difference.

    opened by maiksprenger 3
  • Usable for new sites?

    Usable for new sites?

    I was looking for a nice Oscar-Adyen integration, and now I'm deciding between this one and https://github.com/machtfit/adyen Sadly I'm not able to see through this code where the integration from handle_payment() should start, and where it actually calls the Adyen webservices. Could you clarify that for me? Thanks in advance!

    opened by vdboor 3
  • Fix origin IP address retrieval

    Fix origin IP address retrieval

    This PR allows one to configure the name of the HTTP header in which the IP address from which a payment originated can be found.

    The canonical default of 'REMOTE_ADDR' is assumed in case there is no ADYEN_IP_ADDRESS_HTTP_HEADER setting specified in the Django project — which should be the case if any component of the HTTP stack transfers this information in another HTTP header (often, but not always 'HTTP_X_FORWARDED_FOR').

    enhancement 
    opened by metamatik 3
  • Feature/initial

    Feature/initial

    This is the first iteration of the Adyen payment backend for Oscar. It still needs documentation, which I am going to improve during the day, but I figure it's worth starting the review ASAP.

    As for the other backends, it follows the scaffold —> facade —> gateway pattern.

    Some of it might seem overengineered right now but this is to prepare a future, fuller implementation.

    opened by metamatik 3
  • Problem handling notifications

    Problem handling notifications

    I'm trying to implement notification handling using the Adyen server-to-server communication (i.e. not the redirect feedback from the browser at the time of payment - that's working fine). handle_payment_notification is throwing an exception about a missing currency field for every test notification I post from the Adyen CA.

    Here're some examples of the notification data being POSTed from Adyen:

    {"live":"false","notificationItems":[{"NotificationRequestItem":{"amount":{"currency":"GBP","value":35323},"eventCode":"MANUAL_REVIEW_REJECT","eventDate":"2015-12-12T07:51:56+01:00","merchantAccountCode":"XYZ","merchantReference":"itIDu6_-U0J6xTaK","pspReference":"jJAGY9CPs01Pl","success":"false"}}]}
    
    {"live":"false","notificationItems":[{"NotificationRequestItem":{"additionalData":{"cardSummary":"7777","shopperIP":"127.0.0.1","totalFraudScore":"10","expiryDate":"12\/2012","billingAddress.street":"Nieuwezijds Voorburgwal","cardBin":"976543","extraCostsValue":"101","billingAddress.city":"Amsterdam","threeDAuthenticated":"false","alias":"H934380689410347","shopperInteraction":null,"paymentMethodVariant":"visa","billingAddress.country":"NL","fraudCheck-6-ShopperIpUsage":"10"," NAME1 ":"VALUE1","authCode":"1234","cardHolderName":"J. De Tester","threeDOffered":"false","billingAddress.houseNumberOrName":"21 - 5","threeDOfferedResponse":"N\/A","NAME2":"  VALUE2  ","billingAddress.postalCode":"1012RC","issuerCountry":"unknown","threeDAuthenticatedResponse":"N\/A","aliasType":"Default","extraCostsCurrency":"EUR"},"amount":{"currency":"EUR","value":10100},"eventCode":"AUTHORISATION","eventDate":"2015-12-15T08:48:56+01:00","merchantAccountCode":"XYZ","merchantReference":"8313842560770001","operations":["CANCEL","CAPTURE","REFUND"],"paymentMethod":"visa","pspReference":"test_AUTHORISATION_1","reason":"1234:7777:12\/2012","success":"true"}}]}
    
    {"live":"false","notificationItems":[{"NotificationRequestItem":{"amount":{"currency":"EUR","value":1100},"eventCode":"PENDING","eventDate":"2015-12-15T09:18:00+01:00","merchantAccountCode":"XYZ","merchantReference":"testMerchantRef1","paymentMethod":"paypal","pspReference":"8313842560770001","success":"true"}}]}
    

    Am I missing something, or is django-oscar-adyen not meant to be handling these notifications? The code mentions something about client code having to handle exceptions about missing fields and so on, but I'm not certain what I'm expected to do at this point...

    opened by willharris 2
  • Cleanup tests, add support for error and pending payment statuses

    Cleanup tests, add support for error and pending payment statuses

    We ran into a bug where a payment status of ERROR was not handled by django-oscar-adyen, and we realized that a payment status of PENDING also isn't supported. To properly test for this, I went on a bit of a rampage to first clean up tests and a bit of actual code. The only "real" changes should be the two "Handle XX status notifications from Adyen" commits.

    Reviewing commit by commit is recommended.

    opened by maiksprenger 2
  • Ignore new data coming from Adyen

    Ignore new data coming from Adyen

    When we migrated our Adyen account to the new 'systems communications' interface, we started getting new data, which caused 500s. As all of it comes in the additionalData.KEY = VALUE format and we don't need it, we just blanket ignore it for now.

    opened by maiksprenger 2
  • Switch badges from 'pypip.in' to 'shields.io'

    Switch badges from 'pypip.in' to 'shields.io'

    'pypip.in' remains unavailable for several months now:

    • https://github.com/badges/pypipins/issues/37
    • https://github.com/badges/pypipins/issues/38
    • https://github.com/badges/pypipins/issues/39

    Too bad… Let's use 'shields.io' instead (which has now badges like python versions and current version on PyPI).

    opened by wo0dyn 2
  • Bump django from 1.8.4 to 1.11.28 in /docs

    Bump django from 1.8.4 to 1.11.28 in /docs

    Bumps django from 1.8.4 to 1.11.28.

    Commits
    • e09f09b [1.11.x] Bumped version for 1.11.28 release.
    • 001b063 [1.11.x] Fixed CVE-2020-7471 -- Properly escaped StringAgg(delimiter) parameter.
    • 7fd1ca3 [1.11.x] Fixed timezones tests for PyYAML 5.3+.
    • 121115d [1.11.x] Added CVE-2019-19844 to the security archive.
    • 2c4fb9a [1.11.x] Post-release version bump.
    • 358973a [1.11.x] Bumped version for 1.11.27 release.
    • f4cff43 [1.11.x] Fixed CVE-2019-19844 -- Used verified user email for password reset ...
    • a235574 [1.11.x] Refs #31073 -- Added release notes for 02eff7ef60466da108b1a33f1e4dc...
    • e8fdf00 [1.11.x] Fixed #31073 -- Prevented CheckboxInput.get_context() from mutating ...
    • 4f15016 [1.11.x] Post-release version bump.
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • docs: Fix a few typos

    docs: Fix a few typos

    There are small typos in:

    • README.rst
    • adyen/signers.py
    • tests/test_config.py

    Fixes:

    • Should read implemented rather than implementd.
    • Should read config rather than confic.
    • Should read carefully rather than carefuly.
    • Should read backward rather than backwrd.
    • Should read another rather than anoter.

    Semi-automated pull request generated by https://github.com/timgates42/meticulous/blob/master/docs/NOTE.md

    opened by timgates42 0
  • Update use.rst

    Update use.rst

    get_form_action() and get_form_fields() does not contain any parameter to pass when we initialise the class we have to pass the order_data in contractor

    opened by shehrozkapoor 0
  • Can't find oscar app to import from

    Can't find oscar app to import from

    Hey all -- not sure if this is the right spot for this.

    I'm looking into django-oscar, and getting set up with adyen. Both look very promising, but I'm spinning my wheels a bit. I have followed the installation steps for django-oscar (I think), as well as django-oscar-adyen. Currently getting the following error when I try to run manage.py makemigrations (or any manage.py command, I suppose):

    Feb 12 03:40:21 myproject gunicorn[12456]:     "Couldn't find an Oscar app to import %s from" % module_label)
    Feb 12 03:40:21 myproject gunicorn[12456]: oscar.core.exceptions.AppNotFoundError: Couldn't find an Oscar app to import adyen.gateway from
    

    There's a strong possibility that I"m doing things wrong, but thought I'd reach out. Thanks!

    opened by mtastafford 2
  • Plugin custom `get_class` or more `build_FOO` methods

    Plugin custom `get_class` or more `build_FOO` methods

    After using get_class a bit into the plugin, I think something doesn't work well: get_class, from Django Oscar, look at oscar.apps.* package first. If plugin users try to extend adyen in their project (say in myproject.apps.adyen), it does not work well:

    • Creating only myproject.apps.adyen.scaffold is not enough, because calls to get_class will raise import error because there is no such thing as oscar.apps.adyen,
    • Plugin users will need to also create myproject.apps.adyen.facade and so on. If new sub-module are added to adyen, plugin user's project will be broken with each new update.

    Solutions

    Plan A: write custom get_class

    The first option would be to write a custom get_class, say in adyen.utils.get_class. I'm sure we can reuse parts of oscar.core.loadings functions, but that still a lot of works (more tests, more possible bugs...).

    On the other hand, that would be quite convenient since get_class is a widely used in Django Oscar. This might be more convenient for Django Oscar users, since they would be used to it already.

    Plan B: write build_FOO_BAR methods on Scaffold, Facade and Gateway

    Something I already tried to do in recent commit such as 4c28ea62685fba3cc7f2078e711508bec646fe21 to allow one to build the PaymentReturn and PaymentNotification handlers without overriding more than 1 or 2 methods.

    It would mean to add at least Scaffold.build_facade and Facade.build_gateway, to make sure one can simply override few classes. That might be enough for most case.

    What to do now?

    I can not take a decision right now, I need to think a bit more about that first. Both solution are interesting, but both come with costs.

    enhancement 
    opened by Exirel 0
  • How to handle `merchantReturnData` and how to refactor the plugin

    How to handle `merchantReturnData` and how to refactor the plugin

    Working on this plugin leads me to realize that we may not be using this field properly. As pointed out by @willharris in #25 the plugin initially use this field to store the order's amount, as a way to get this amount back at the Payment Return URL (after customers paid for their order on the HPP).

    What Adyen say about this field

    This field value is appended as-is to the return URL when the shopper completes, or abandons, the payment process and is redirected to your web shop.

    Typically, this field is used to hold and transmit a session ID.

    Maximum allowed character length: 128 characters.

    And, the warning note added for this field is rather important:

    If by including merchantReturnData in a request causes it to exceed the allowed maximum size, the payment can fail.

    (emphasis is mine)

    What we do in the code

    • The amount is needed to record the transaction as an AdyenTransaction object. It has no other purpose inside the plugin.
    • Outside the plugin, ie. inside the plugin user's project, the amount is easy to get by looking at the Order object.
    • The amount is not given by the Payment Return URL,
    • The amount is given by the Payment Notification.
    • To handle merchantReturnData both the Scaffold and the Facade need to be modified. This is not ideal since it requires one to override both to add anything new.

    Something that plugin users can not be aware of is that originally, this project is used by Oscaro internally, so some decision are related to Oscaro own projects. It does not mean it is a good idea to force Oscaro needs into the plugin this way. More importantly, Oscaro projects do not need the amount in the merchantReturnData any more.

    These lead me to the conclusion that:

    • Using the amount in the merchantReturnData was an arbitrary decision,
    • This decision seems a bit outdated now,
    • From this decision we have a backward compatibility issue if we want to make any modification,
    • Today, I don't see why we need to record AdyenTransaction outside a Payment Notification.
    • Until now, the plugin did not expose a way to handle separately Payment Return and Payment Notification. This is modified by recent commit for 0.6.0, so this might be a way to handle things more easily.

    What to do now?

    First, I would like to address the "where to handle merchantReturnData" problem: can the AdyenConfig object handle that? Or provide an handler class for it? Like, a MerchantReturnDataParser or something? I'm not sure about that yet. Maybe just CustomDataWriter and CustomerDataReader.

    In #25 @willharris suggest that the plugin should handle a plugin-specific format. I'm not really happy with that, because I don't see why one would need to pass more than an identifier here (like a session, or a tracking ID, or else). The amount stored here sound like a bad idea to me.

    I'm a bit worried that one may not be aware of the max of 128 characters length, and add arbitrary data without the plugin complaining. Or even worse, if, say, plugin users provide exactly 128 characters, and because we still keep the amount, this explode the limit and make payment failed.

    Second, I want to keep the backward compatibility at least for 0.6.0, then maybe in 0.7.0, and drop it eventually in 0.8.0. I think there is more to do with how the plugin handle Payment Return and Payment Notification.

    Another idea: set a flag on the AdyenConfig to say "let me handle merchantReturnData myself", or something like that. Somehow, this would be the same as using a settings to get a custom handler, with a default handler provided directly by the plugin.

    Right now, I can not take a decision, but writing this down help me a bit, and I'm sure other developers will have ideas/comments to add. Feel free to do so!

    enhancement 
    opened by Exirel 0
Releases(0.7.0)
  • 0.7.0(Apr 18, 2016)

    • Add adyen.signers module to handle signature algorithm
    • Refactor how the merchantSig is generated, using the new adyen.signers module.
    • Splits constants and exceptions into their own module
    • Handle shopper, billing and delivery fields (with signatures for SHA-1)
    • Handle merchantSig with SHA-256 algorithm
    • Improve test coverage and other minor changes

    This version is backward compatible with version 0.6.0.

    Note that plugin users need to implement method get_signer_backend if they uses their own config class from the abstract config class.

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Mar 1, 2016)

    This new version (released March 1st, 2016) is mostly a "code quality" version, with small feature added to extends the plugin and to handle a new Adyen HPP field.

    Also, there is a documentation now!

    • Allow plugin user to extend it with get_class,
    • Split several methods in order to override specific parts of the plugin,
    • Expose more methods as public methods to allow plugin user to override more specific parts of the plugin,
    • Add deprecation note on handle_payment_feedback and add two separates methods to handle payment return case and payment notification case.
    • Add allowedMethods to the payment request form (unused by default).
    • Start a sphinx documentation for the project.

    This version is backward compatible with version 0.5.0.

    Note that plugin users need to implement method get_allowed_methods if they uses their own config class from the abstract config class.

    Source code(tar.gz)
    Source code(zip)
Owner
Oscar
Flexible e-commerce for Django
Oscar
Django + Stripe Made Easy

dj-stripe Stripe Models for Django. Introduction dj-stripe implements all of the Stripe models, for Django. Set up your webhook endpoint and start rec

dj-stripe 1.3k Dec 28, 2022
A Django app to accept payments from various payment processors via Pluggable backends.

Django-Merchant Django-Merchant is a django application that enables you to use multiple payment processors from a single API. Gateways Following gate

Agiliq 997 Dec 24, 2022
A pluggable Django application for integrating PayPal Payments Standard or Payments Pro

Django PayPal Django PayPal is a pluggable application that integrates with PayPal Payments Standard and Payments Pro. See https://django-paypal.readt

Luke Plant 672 Dec 22, 2022
Django library to simplify payment processing with pin

Maintainer Wanted I no longer have any side projects that use django-pinpayments and I don't have the time or headspace to maintain an important proje

Ross Poulton 25 May 25, 2022
Forms, widgets, template tags and examples that make Stripe + Django easier.

Overview Zebra is a library that makes using Stripe with Django even easier. It's made of: zebra, the core library, with forms, webhook handlers, abst

GoodCloud 189 Jan 1, 2023
payu payment gateway integration for django projects

Django-PayU This package provides integration between Django and PayU Payment Gateway. Quick start Install 'django-payu' using the following command:

MicroPyramid 37 Nov 9, 2022
PayPal integration for django-oscar. Can be used without Oscar too.

PayPal package for django-oscar This package provides integration between django-oscar and both PayPal REST API, PayPal Express (NVP) and PayPal Payfl

Oscar 146 Nov 25, 2022
Api for getting bin info and getting encrypted card details for adyen.

Bin Info And Adyen Cse Enc Python api for getting bin info and getting encrypted

Roldex Stark 8 Dec 30, 2022
One version package to rule them all, One version package to find them, One version package to bring them all, and in the darkness bind them.

AwesomeVersion One version package to rule them all, One version package to find them, One version package to bring them all, and in the darkness bind

Joakim Sørensen 39 Dec 31, 2022
Meta package to combine turbo-django and stimulus-django

Hotwire + Django This repository aims to help you integrate Hotwire with Django ?? Inspiration might be taken from @hotwired/hotwire-rails. We are sti

Hotwire for Django 31 Aug 9, 2022
DRF_commands is a Django package that helps you to create django rest framework endpoints faster using manage.py.

DRF_commands is a Django package that helps you to create django rest framework endpoints faster using manage.py.

Mokrani Yacine 2 Sep 28, 2022
Django Persistent Filters is a Python package which provide a django middleware that take care to persist the querystring in the browser cookies.

Django Persistent Filters Django Persistent Filters is a Python package which provide a django middleware that take care to persist the querystring in

Lorenzo Prodon 2 Aug 5, 2022
Django module to easily send emails/sms/tts/push using django templates stored on database and managed through the Django Admin

Django-Db-Mailer Documentation available at Read the Docs. What's that Django module to easily send emails/push/sms/tts using django templates stored

LPgenerator 250 Dec 21, 2022
Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project

Django URL Shortener Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project Install this package to your Dja

Rishav Sinha 4 Nov 18, 2021
:package: :fire: Python project management. Manage packages: convert between formats, lock, install, resolve, isolate, test, build graph, show outdated, audit. Manage venvs, build package, bump version.

THE PROJECT IS ARCHIVED Forks: https://github.com/orsinium/forks DepHell -- project management for Python. Why it is better than all other tools: Form

DepHell 1.7k Dec 30, 2022
Python Package For MTN Zambia Momo API. This package can also be used by MTN momo in other countries.

MTN MoMo API Lite Python Client Power your apps with Lite-Python MTN MoMo API Usage Installation Add the latest version of the library to your project

Mathews Musukuma 7 Jan 1, 2023
A test repository to build a python package and publish the package to Artifact Registry using GCB

A test repository to build a python package and publish the package to Artifact Registry using GCB. Then have the package be a dependency in a GCF function.

null 1 Feb 9, 2022
This package helps you to directly download an APK from Google Play by providing the package id of the app

Apk Downloader About | Features | Technologies | Requirements | Starting | License | Author ?? About This package helps you to directly download an AP

Daniel Agyapong 9 Dec 11, 2022
The Django Leaflet Admin List package provides an admin list view featured by the map and bounding box filter for the geo-based data of the GeoDjango.

The Django Leaflet Admin List package provides an admin list view featured by the map and bounding box filter for the geo-based data of the GeoDjango. It requires a django-leaflet package.

Vsevolod Novikov 33 Nov 11, 2022
Django package to log request values such as device, IP address, user CPU time, system CPU time, No of queries, SQL time, no of cache calls, missing, setting data cache calls for a particular URL with a basic UI.

django-web-profiler's documentation: Introduction: django-web-profiler is a django profiling tool which logs, stores debug toolbar statistics and also

MicroPyramid 77 Oct 29, 2022