From 28df4aac714ed6086c44f578164960e2ca3e90f9 Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Tue, 4 Jun 2024 23:45:49 -0600 Subject: [PATCH 01/10] hopefix --- .../apps/fsp/western_union/endpoints/send_money.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py index 6ea3478..9fd2cd8 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py @@ -37,7 +37,13 @@ def create_validation_payload(hope_payload): country_code = None receiver = { - "name": {"first_name": hope_payload["first_name"], "last_name": hope_payload["last_name"], "name_type": "D"}, + "name": { + # "first_name": hope_payload["first_name"], + # "last_name": hope_payload["last_name"], + "first_name": str(hope_payload["first_name"].encode("utf-8"))[2:-1], + "last_name": str(hope_payload["last_name"].encode("utf-8"))[2:-1], + "name_type": "D", + }, "contact_phone": phone_number, "mobile_phone": { "phone_number": { From 3124d93771136540857881f74e60805dd8eeba23 Mon Sep 17 00:00:00 2001 From: Domenico Date: Thu, 6 Jun 2024 12:10:48 -0600 Subject: [PATCH 02/10] rollback ascii fix 203607 (#58) --- .../apps/fsp/western_union/endpoints/send_money.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py index 9fd2cd8..a434533 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py @@ -38,10 +38,10 @@ def create_validation_payload(hope_payload): receiver = { "name": { - # "first_name": hope_payload["first_name"], - # "last_name": hope_payload["last_name"], - "first_name": str(hope_payload["first_name"].encode("utf-8"))[2:-1], - "last_name": str(hope_payload["last_name"].encode("utf-8"))[2:-1], + "first_name": hope_payload["first_name"], + "last_name": hope_payload["last_name"], + # "first_name": str(hope_payload["first_name"].encode("utf-8"))[2:-1], + # "last_name": str(hope_payload["last_name"].encode("utf-8"))[2:-1], "name_type": "D", }, "contact_phone": phone_number, From 044f4668a676cada6ae9e72908cd625f20e4b5f0 Mon Sep 17 00:00:00 2001 From: Domenico Date: Thu, 6 Jun 2024 19:55:43 -0600 Subject: [PATCH 03/10] 203608 Make Delivery Mechanism Mandatory in Configurations (#59) --- .../apps/fsp/western_union/handlers.py | 6 ++++-- ...erviceproviderconfig_delivery_mechanism.py | 21 +++++++++++++++++++ .../apps/gateway/models.py | 8 +++---- 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 src/hope_payment_gateway/apps/gateway/migrations/0018_alter_financialserviceproviderconfig_delivery_mechanism.py diff --git a/src/hope_payment_gateway/apps/fsp/western_union/handlers.py b/src/hope_payment_gateway/apps/fsp/western_union/handlers.py index 6e2a82d..690c171 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/handlers.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/handlers.py @@ -8,10 +8,12 @@ def notify(self, records): for record in records: send_money(record.get_payload()) - def get_configuration(self, config_key): + def get_configuration(self, config_key, delivery_mechanism): wu = FinancialServiceProvider.objects.get(vision_vendor_number="1900723202") try: - config = FinancialServiceProviderConfig.objects.get(key=config_key, fsp=wu).configuration + config = FinancialServiceProviderConfig.objects.get( + key=config_key, fsp=wu, delivery_mechanism__code=delivery_mechanism + ).configuration except FinancialServiceProviderConfig.DoesNotExist: config = wu.configuration return config diff --git a/src/hope_payment_gateway/apps/gateway/migrations/0018_alter_financialserviceproviderconfig_delivery_mechanism.py b/src/hope_payment_gateway/apps/gateway/migrations/0018_alter_financialserviceproviderconfig_delivery_mechanism.py new file mode 100644 index 0000000..b5ea5f3 --- /dev/null +++ b/src/hope_payment_gateway/apps/gateway/migrations/0018_alter_financialserviceproviderconfig_delivery_mechanism.py @@ -0,0 +1,21 @@ +# Generated by Django 5.0.4 on 2024-06-06 17:53 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gateway", "0017_alter_paymentrecord_payout_amount"), + ] + + operations = [ + migrations.AlterField( + model_name="financialserviceproviderconfig", + name="delivery_mechanism", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name="fsp", to="gateway.deliverymechanism" + ), + ), + ] diff --git a/src/hope_payment_gateway/apps/gateway/models.py b/src/hope_payment_gateway/apps/gateway/models.py index 9179840..42bcdc3 100644 --- a/src/hope_payment_gateway/apps/gateway/models.py +++ b/src/hope_payment_gateway/apps/gateway/models.py @@ -31,9 +31,7 @@ class FinancialServiceProviderConfig(models.Model): key = models.CharField(max_length=16, db_index=True, unique=True) label = models.CharField(max_length=16, db_index=True, null=True, blank=True) fsp = models.ForeignKey(FinancialServiceProvider, on_delete=models.CASCADE, related_name="configs") - delivery_mechanism = models.ForeignKey( - DeliveryMechanism, on_delete=models.CASCADE, related_name="fsp", null=True, blank=True - ) + delivery_mechanism = models.ForeignKey(DeliveryMechanism, on_delete=models.CASCADE, related_name="fsp") configuration = models.JSONField(default=dict, null=True, blank=True) def __str__(self): @@ -101,7 +99,9 @@ def abort(self): def get_payload(self): payload = self.payload.copy() if "config_key" in self.extra: - config_payload = self.fsp.strategy.get_configuration(self.extra["config_key"]) + config_payload = self.fsp.strategy.get_configuration( + self.extra["config_key"], self.extra.get("delivery_mechanism", "cash") # temp fix + ) payload.update(config_payload) return payload From 886bf318816d709b17e7d8fc8d427cadab775a05 Mon Sep 17 00:00:00 2001 From: Domenico Date: Thu, 6 Jun 2024 23:19:11 -0600 Subject: [PATCH 04/10] 203615 service provider code (#60) * 203615 service provider code * 203615-ServiceProviderCode --- src/hope_payment_gateway/api/urls.py | 1 + .../api/western_union/filters.py | 13 +++- .../api/western_union/serializers.py | 8 ++- .../api/western_union/views.py | 14 ++++- .../apps/fsp/western_union/admin.py | 43 +++---------- .../apps/fsp/western_union/endpoints/das.py | 61 ++++++++++++++++++- .../migrations/0003_serviceprovidercode.py | 23 +++++++ .../apps/fsp/western_union/models.py | 10 +++ .../apps/fsp/western_union/tasks.py | 14 ++++- .../service_provider_code.fixture.json | 15 +++++ ...f18ae6b758e8014a24c7fa039e91.response.json | 22 +++++++ tests/api/test_viewsets.py | 12 ++++ tests/factories.py | 7 ++- 13 files changed, 199 insertions(+), 44 deletions(-) create mode 100644 src/hope_payment_gateway/apps/fsp/western_union/migrations/0003_serviceprovidercode.py create mode 100644 tests/api/_api_checker/api.test_viewsets/service_provider_code.fixture.json create mode 100644 tests/api/_api_checker/api.test_viewsets/test_api_wu_service_provider_code/_api_rest_wu_provider_code_/get/dc937b59892604f5a86ac96936cd7ff09e25f18ae6b758e8014a24c7fa039e91.response.json diff --git a/src/hope_payment_gateway/api/urls.py b/src/hope_payment_gateway/api/urls.py index c3a0190..1a5ad21 100644 --- a/src/hope_payment_gateway/api/urls.py +++ b/src/hope_payment_gateway/api/urls.py @@ -14,6 +14,7 @@ router.register(r"config", views.ConfigurationViewSet, basename="config") router.register(r"wu/corridors", wu_views.CorridorViewSet, basename="wu-corridor") +router.register(r"wu/provider_code", wu_views.ServiceProviderCodeViewSet, basename="wu-service-provider-code") urlpatterns = router.urls diff --git a/src/hope_payment_gateway/api/western_union/filters.py b/src/hope_payment_gateway/api/western_union/filters.py index 1bfccaf..65f4e2d 100644 --- a/src/hope_payment_gateway/api/western_union/filters.py +++ b/src/hope_payment_gateway/api/western_union/filters.py @@ -1,6 +1,6 @@ from django_filters import rest_framework as filters -from hope_payment_gateway.apps.fsp.western_union.models import Corridor +from hope_payment_gateway.apps.fsp.western_union.models import Corridor, ServiceProviderCode class CorridorFilter(filters.FilterSet): @@ -12,3 +12,14 @@ class Meta: "destination_currency": ["exact"], "template_code": ["exact"], } + + +class ServiceProviderCodeFilter(filters.FilterSet): + class Meta: + model = ServiceProviderCode + fields = { + "description": ["exact", "icontains"], + "code": ["exact", "icontains"], + "country": ["exact"], + "currency": ["exact"], + } diff --git a/src/hope_payment_gateway/api/western_union/serializers.py b/src/hope_payment_gateway/api/western_union/serializers.py index bfa8825..6f0dc89 100644 --- a/src/hope_payment_gateway/api/western_union/serializers.py +++ b/src/hope_payment_gateway/api/western_union/serializers.py @@ -1,9 +1,15 @@ from rest_framework import serializers -from hope_payment_gateway.apps.fsp.western_union.models import Corridor +from hope_payment_gateway.apps.fsp.western_union.models import Corridor, ServiceProviderCode class CorridorSerializer(serializers.ModelSerializer): class Meta: model = Corridor fields = ("id", "description", "destination_country", "destination_currency", "template_code", "template") + + +class ServiceProviderCodeSerializer(serializers.ModelSerializer): + class Meta: + model = ServiceProviderCode + fields = ("description", "code", "country", "currency") diff --git a/src/hope_payment_gateway/api/western_union/views.py b/src/hope_payment_gateway/api/western_union/views.py index c72b907..1b760f8 100644 --- a/src/hope_payment_gateway/api/western_union/views.py +++ b/src/hope_payment_gateway/api/western_union/views.py @@ -1,8 +1,8 @@ from rest_framework.viewsets import ModelViewSet -from hope_payment_gateway.api.western_union.filters import CorridorFilter -from hope_payment_gateway.api.western_union.serializers import CorridorSerializer -from hope_payment_gateway.apps.fsp.western_union.models import Corridor +from hope_payment_gateway.api.western_union.filters import CorridorFilter, ServiceProviderCodeFilter +from hope_payment_gateway.api.western_union.serializers import CorridorSerializer, ServiceProviderCodeSerializer +from hope_payment_gateway.apps.fsp.western_union.models import Corridor, ServiceProviderCode class ProtectedMixin: @@ -16,3 +16,11 @@ class CorridorViewSet(ProtectedMixin, ModelViewSet): filterset_class = CorridorFilter search_fields = ["description"] + + +class ServiceProviderCodeViewSet(ProtectedMixin, ModelViewSet): + serializer_class = ServiceProviderCodeSerializer + queryset = ServiceProviderCode.objects.all() + + filterset_class = ServiceProviderCodeFilter + search_fields = ["description", "code"] diff --git a/src/hope_payment_gateway/apps/fsp/western_union/admin.py b/src/hope_payment_gateway/apps/fsp/western_union/admin.py index a99b3df..5ef469b 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/admin.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/admin.py @@ -13,7 +13,7 @@ das_origination_currencies, ) from hope_payment_gateway.apps.fsp.western_union.endpoints.request import requests_request -from hope_payment_gateway.apps.fsp.western_union.models import Corridor +from hope_payment_gateway.apps.fsp.western_union.models import Corridor, ServiceProviderCode @admin.register(Corridor) @@ -137,38 +137,11 @@ def delivery_option_template(self, request, pk) -> TemplateResponse: f"PARAM: template_code" ) context.update(das_delivery_option_template(destination_country, destination_currency, template_code)) - if "content" in context: - rows = context["content"]["MTML"]["REPLY"]["DATA_CONTEXT"]["RECORDSET"]["GETDELIVERYOPTIONTEMPLATE"] - template = {} - structure = [] - if not obj.template: - for row in rows: - t_index = row["T_INDEX"] - if t_index != "000": - first_value = row["DESCRIPTION"].split(";")[0].split(".") - - if len(first_value) == 1: - description = row["DESCRIPTION"].split(";")[2].strip() - base = template - for item in structure[:-1]: - base = base[item] - if not base[structure[-1]]: - base[structure[-1]] = description - elif isinstance(base[structure[-1]], list): - base[structure[-1]].append(description) - else: - base[structure[-1]] = [base[structure[-1]], description] - else: - base = template - structure = first_value - for i in range(len(first_value)): - field = first_value[i] - if i == len(first_value) - 1: - base[field] = None - else: - if field not in base: - base[field] = {} - base = base[field] - obj.template = template - obj.save() return TemplateResponse(request, "western_union.html", context) + + +@admin.register(ServiceProviderCode) +class ServiceProviderCodeAdmin(admin.ModelAdmin): + list_display = ("code", "description", "country", "currency") + search_fields = ("code", "description", "country", "currency") + list_filter = ("country", "currency") diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/das.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/das.py index a68a9f2..b73c0a1 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/das.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/das.py @@ -2,7 +2,7 @@ from hope_payment_gateway.apps.fsp.western_union.endpoints.client import WesternUnionClient from hope_payment_gateway.apps.fsp.western_union.endpoints.config import unicef -from hope_payment_gateway.apps.fsp.western_union.models import Corridor +from hope_payment_gateway.apps.fsp.western_union.models import Corridor, ServiceProviderCode def create_usd(): @@ -123,6 +123,7 @@ def das_delivery_services(destination_country, destination_currency, create_corr def das_delivery_option_template(destination_country, destination_currency, template_code): + wu_env = config.WESTERN_UNION_WHITELISTED_ENV payload = { "name": "GetDeliveryOptionTemplate", @@ -136,4 +137,60 @@ def das_delivery_option_template(destination_country, destination_currency, temp }, } client = WesternUnionClient("DAS_Service_H2HService.wsdl") - return client.response_context("DAS_Service", payload, "DAS_Service_H2H", f"SOAP_HTTP_Port_{wu_env}") + context = client.response_context("DAS_Service", payload, "DAS_Service_H2H", f"SOAP_HTTP_Port_{wu_env}") + + if "content" in context: + rows = context["content"]["MTML"]["REPLY"]["DATA_CONTEXT"]["RECORDSET"] + template = {} + structure = [] + service_provider_code = False + if rows: + try: + obj = Corridor.objects.get( + destination_country=destination_country, destination_currency=destination_currency + ) + except Corridor.DoesNotExist: + obj = None + if obj and not obj.template: + for row in rows["GETDELIVERYOPTIONTEMPLATE"]: + t_index = row["T_INDEX"] + if t_index != "000": + first_value = row["DESCRIPTION"].split(";")[0].split(".") + + if len(first_value) == 1: + code = row["DESCRIPTION"].split(";")[2].strip() + description = row["DESCRIPTION"].split(";")[3].strip() + base = template + for item in structure[:-1]: + base = base[item] + if not base[structure[-1]]: + base[structure[-1]] = code + elif isinstance(base[structure[-1]], list): + base[structure[-1]].append(code) + else: + base[structure[-1]] = [base[structure[-1]], code] + if service_provider_code: + sp, created = ServiceProviderCode.objects.get_or_create( + code=code, + description=description, + country=destination_country, + currency=destination_currency, + ) + else: + base = template + structure = first_value + for i in range(len(first_value)): + field = first_value[i] + if i == len(first_value) - 1: + base[field] = None + else: + if field not in base: + base[field] = {} + base = base[field] + if first_value == ["wallet_details", "service_provider_code"]: + service_provider_code = True + else: + service_provider_code = False + obj.template = template + obj.save() + return context diff --git a/src/hope_payment_gateway/apps/fsp/western_union/migrations/0003_serviceprovidercode.py b/src/hope_payment_gateway/apps/fsp/western_union/migrations/0003_serviceprovidercode.py new file mode 100644 index 0000000..1c957c6 --- /dev/null +++ b/src/hope_payment_gateway/apps/fsp/western_union/migrations/0003_serviceprovidercode.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.4 on 2024-06-06 19:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("western_union", "0002_alter_corridor_unique_together"), + ] + + operations = [ + migrations.CreateModel( + name="ServiceProviderCode", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("description", models.CharField(max_length=64)), + ("code", models.CharField(max_length=32, unique=True)), + ("country", models.CharField(max_length=2)), + ("currency", models.CharField(max_length=3)), + ], + ), + ] diff --git a/src/hope_payment_gateway/apps/fsp/western_union/models.py b/src/hope_payment_gateway/apps/fsp/western_union/models.py index 192f58b..84522cd 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/models.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/models.py @@ -13,3 +13,13 @@ def __str__(self): class Meta: unique_together = ("destination_country", "destination_currency") + + +class ServiceProviderCode(models.Model): + description = models.CharField(max_length=64) + code = models.CharField(max_length=32, unique=True) + country = models.CharField(max_length=2) + currency = models.CharField(max_length=3) + + def __str__(self): + return f"{self.description}" diff --git a/src/hope_payment_gateway/apps/fsp/western_union/tasks.py b/src/hope_payment_gateway/apps/fsp/western_union/tasks.py index fbb8fba..33aba75 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/tasks.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/tasks.py @@ -1,6 +1,10 @@ from constance import config -from hope_payment_gateway.apps.fsp.western_union.endpoints.das import das_countries_currencies +from hope_payment_gateway.apps.fsp.western_union.endpoints.das import ( + das_countries_currencies, + das_delivery_option_template, +) +from hope_payment_gateway.apps.fsp.western_union.models import Corridor from hope_payment_gateway.apps.gateway.models import FinancialServiceProvider, PaymentInstruction, PaymentRecord from hope_payment_gateway.celery import app @@ -31,3 +35,11 @@ def western_union_send_task(vision_vendor_number="1900723202", tag=None, thresho @app.task def update_corridors(): das_countries_currencies(create_corridors=True) + + +@app.task +def update_templates(): + for corridor in Corridor.objects.all(): + das_delivery_option_template( + corridor.destination_country, corridor.destination_currency, corridor.template_code + ) diff --git a/tests/api/_api_checker/api.test_viewsets/service_provider_code.fixture.json b/tests/api/_api_checker/api.test_viewsets/service_provider_code.fixture.json new file mode 100644 index 0000000..8f0ed9e --- /dev/null +++ b/tests/api/_api_checker/api.test_viewsets/service_provider_code.fixture.json @@ -0,0 +1,15 @@ +{ + "service_provider_code": { + "master": { + "model": "western_union.serviceprovidercode", + "pk": 1, + "fields": { + "description": "", + "code": "", + "country": "", + "currency": "" + } + }, + "deps": [] + } +} \ No newline at end of file diff --git a/tests/api/_api_checker/api.test_viewsets/test_api_wu_service_provider_code/_api_rest_wu_provider_code_/get/dc937b59892604f5a86ac96936cd7ff09e25f18ae6b758e8014a24c7fa039e91.response.json b/tests/api/_api_checker/api.test_viewsets/test_api_wu_service_provider_code/_api_rest_wu_provider_code_/get/dc937b59892604f5a86ac96936cd7ff09e25f18ae6b758e8014a24c7fa039e91.response.json new file mode 100644 index 0000000..191624c --- /dev/null +++ b/tests/api/_api_checker/api.test_viewsets/test_api_wu_service_provider_code/_api_rest_wu_provider_code_/get/dc937b59892604f5a86ac96936cd7ff09e25f18ae6b758e8014a24c7fa039e91.response.json @@ -0,0 +1,22 @@ +{ + "status_code": 200, + "headers": { + "Content-Type": "application/json", + "Vary": "Accept, origin", + "Allow": "GET, POST, HEAD, OPTIONS", + "X-Frame-Options": "DENY", + "Content-Length": "57", + "X-Content-Type-Options": "nosniff", + "Referrer-Policy": "same-origin", + "Cross-Origin-Opener-Policy": "same-origin" + }, + "data": [ + { + "description": "", + "code": "", + "country": "", + "currency": "" + } + ], + "content_type": "application/json" +} \ No newline at end of file diff --git a/tests/api/test_viewsets.py b/tests/api/test_viewsets.py index f456daa..a6c140f 100644 --- a/tests/api/test_viewsets.py +++ b/tests/api/test_viewsets.py @@ -9,6 +9,7 @@ FinancialServiceProviderFactory, PaymentInstructionFactory, PaymentRecordFactory, + ServiceProviderCodeFactory, ) @@ -32,6 +33,11 @@ def corridor(request, db): return CorridorFactory() +@frozenfixture() +def service_provider_code(request, db): + return ServiceProviderCodeFactory() + + @frozenfixture() def configuration(request, db, fsp): return FinancialServiceProviderConfigFactory(fsp=fsp) @@ -66,3 +72,9 @@ def test_api_config(request, django_app, configuration): @contract(LastModifiedRecorder) def test_api_wu_corridors(request, django_app, corridor): return reverse("api:wu-corridor-list") + + +@pytest.mark.django_db +@contract(LastModifiedRecorder) +def test_api_wu_service_provider_code(request, django_app, service_provider_code): + return reverse("api:wu-service-provider-code-list") diff --git a/tests/factories.py b/tests/factories.py index 0552752..ba3b2f6 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -9,7 +9,7 @@ from hope_api_auth.models import APIToken, Grant from hope_payment_gateway.apps.core.models import System -from hope_payment_gateway.apps.fsp.western_union.models import Corridor +from hope_payment_gateway.apps.fsp.western_union.models import Corridor, ServiceProviderCode from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, FinancialServiceProvider, @@ -67,6 +67,11 @@ class Meta: model = Corridor +class ServiceProviderCodeFactory(factory.django.DjangoModelFactory): + class Meta: + model = ServiceProviderCode + + class SystemFactory(factory.django.DjangoModelFactory): name = fuzzy.FuzzyText() owner = factory.SubFactory(UserFactory) From 2edfd943c380c76e775aecdc480b92fc6d23b0d5 Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Fri, 7 Jun 2024 03:41:29 -0600 Subject: [PATCH 05/10] change default --- src/hope_payment_gateway/apps/gateway/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hope_payment_gateway/apps/gateway/models.py b/src/hope_payment_gateway/apps/gateway/models.py index 42bcdc3..8142d44 100644 --- a/src/hope_payment_gateway/apps/gateway/models.py +++ b/src/hope_payment_gateway/apps/gateway/models.py @@ -100,7 +100,7 @@ def get_payload(self): payload = self.payload.copy() if "config_key" in self.extra: config_payload = self.fsp.strategy.get_configuration( - self.extra["config_key"], self.extra.get("delivery_mechanism", "cash") # temp fix + self.extra["config_key"], self.extra.get("delivery_mechanism", "cash_over_the_counter") # temp fix ) payload.update(config_payload) return payload From e2c8a39c07b9adb10aea7577c860d5e8f9818f04 Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Fri, 7 Jun 2024 04:56:26 -0600 Subject: [PATCH 06/10] remove constraint --- ...alter_financialserviceproviderconfig_key.py | 18 ++++++++++++++++++ .../apps/gateway/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/hope_payment_gateway/apps/gateway/migrations/0019_alter_financialserviceproviderconfig_key.py diff --git a/src/hope_payment_gateway/apps/gateway/migrations/0019_alter_financialserviceproviderconfig_key.py b/src/hope_payment_gateway/apps/gateway/migrations/0019_alter_financialserviceproviderconfig_key.py new file mode 100644 index 0000000..2421958 --- /dev/null +++ b/src/hope_payment_gateway/apps/gateway/migrations/0019_alter_financialserviceproviderconfig_key.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-06-07 10:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gateway", "0018_alter_financialserviceproviderconfig_delivery_mechanism"), + ] + + operations = [ + migrations.AlterField( + model_name="financialserviceproviderconfig", + name="key", + field=models.CharField(db_index=True, max_length=16), + ), + ] diff --git a/src/hope_payment_gateway/apps/gateway/models.py b/src/hope_payment_gateway/apps/gateway/models.py index 8142d44..26a6a63 100644 --- a/src/hope_payment_gateway/apps/gateway/models.py +++ b/src/hope_payment_gateway/apps/gateway/models.py @@ -28,7 +28,7 @@ def __str__(self): class FinancialServiceProviderConfig(models.Model): - key = models.CharField(max_length=16, db_index=True, unique=True) + key = models.CharField(max_length=16, db_index=True) label = models.CharField(max_length=16, db_index=True, null=True, blank=True) fsp = models.ForeignKey(FinancialServiceProvider, on_delete=models.CASCADE, related_name="configs") delivery_mechanism = models.ForeignKey(DeliveryMechanism, on_delete=models.CASCADE, related_name="fsp") From 644c3cf9ddbf218e365213d9ea819080263fbc73 Mon Sep 17 00:00:00 2001 From: Domenico Date: Mon, 10 Jun 2024 00:02:14 -0600 Subject: [PATCH 07/10] 203607-export-api (#61) --- src/hope_payment_gateway/api/fsp/filters.py | 10 ++++ .../api/fsp/serializers.py | 9 ++++ src/hope_payment_gateway/api/fsp/views.py | 30 +++++++++++ src/hope_payment_gateway/api/urls.py | 1 + .../apps/core/permissions.py | 2 - .../apps/fsp/western_union/apps.py | 6 +-- .../apps/fsp/western_union/endpoints/nis.py | 1 - .../apps/fsp/western_union/handlers.py | 6 +++ .../apps/gateway/admin.py | 7 +++ .../gateway/migrations/0019_exporttemplate.py | 51 +++++++++++++++++++ .../apps/gateway/models.py | 34 ++++++++++++- .../apps/gateway/registry.py | 7 +-- 12 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py diff --git a/src/hope_payment_gateway/api/fsp/filters.py b/src/hope_payment_gateway/api/fsp/filters.py index 540e55c..37fba61 100644 --- a/src/hope_payment_gateway/api/fsp/filters.py +++ b/src/hope_payment_gateway/api/fsp/filters.py @@ -3,6 +3,7 @@ from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -65,3 +66,12 @@ class Meta: "parent__remote_id": ["exact", "in"], "status": ["exact", "in"], } + + +class ExportTemplateFilter(filters.FilterSet): + class Meta: + model = ExportTemplate + fields = { + "fsp": ["exact"], + "config_key": ["exact"], + } diff --git a/src/hope_payment_gateway/api/fsp/serializers.py b/src/hope_payment_gateway/api/fsp/serializers.py index 4387f0c..0c108e4 100644 --- a/src/hope_payment_gateway/api/fsp/serializers.py +++ b/src/hope_payment_gateway/api/fsp/serializers.py @@ -4,6 +4,7 @@ from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -179,3 +180,11 @@ class Meta: "payload", "payout_amount", ) + + +class ExportTemplateSerializer(serializers.ModelSerializer): + fsp = serializers.PrimaryKeyRelatedField(queryset=FinancialServiceProvider.objects.all()) + + class Meta: + model = ExportTemplate + fields = ("query", "fsp", "config_key") diff --git a/src/hope_payment_gateway/api/fsp/views.py b/src/hope_payment_gateway/api/fsp/views.py index d11f6d7..27b8e6d 100644 --- a/src/hope_payment_gateway/api/fsp/views.py +++ b/src/hope_payment_gateway/api/fsp/views.py @@ -8,6 +8,7 @@ from hope_api_auth.views import LoggingAPIViewSet from hope_payment_gateway.api.fsp.filters import ( DeliveryMechanismFilter, + ExportTemplateFilter, FinancialServiceProviderConfigFilter, FinancialServiceProviderFilter, PaymentInstructionFilter, @@ -15,6 +16,7 @@ ) from hope_payment_gateway.api.fsp.serializers import ( DeliveryMechanismSerializer, + ExportTemplateSerializer, FinancialServiceProviderConfigSerializer, FinancialServiceProviderSerializer, PaymentInstructionSerializer, @@ -23,8 +25,11 @@ ) from hope_payment_gateway.apps.core.models import System from hope_payment_gateway.apps.fsp.western_union.endpoints.cancel import cancel +from hope_payment_gateway.apps.gateway.actions import TemplateExportForm, export_as_template, export_as_template_impl +from hope_payment_gateway.apps.gateway.admin import PaymentInstructionAdmin from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -131,6 +136,24 @@ def add_records(self, request, remote_id=None): } return Response({"remote_id": obj.remote_id, "errors": error_dict}, status=HTTP_400_BAD_REQUEST) + @action(detail=True) # , methods=["post"]) + def download(self, request, remote_id=None): + + obj = self.get_object() + try: + dm = DeliveryMechanism.objects.get(code=obj.extra.get("delivery_mechanism", None)) + export = ExportTemplate.objects.get( + fsp=obj.fsp, config_key=obj.extra.get("config_key", None), delivery_mechanism=dm + ) + queryset = PaymentRecord.objects.filter(parent=obj).select_related("parent__fsp") + + return export_as_template_impl( + queryset, + export.query.split("\r\n"), + ) + except ExportTemplate.DoesNotExist as exc: + return Response({"status_error": str(exc)}, status=HTTP_400_BAD_REQUEST) + class PaymentRecordViewSet(ProtectedMixin, LoggingAPIViewSet): serializer_class = PaymentRecordSerializer @@ -152,3 +175,10 @@ def cancel(self, request): return Response({"message": "cancel triggered"}) except TransitionNotAllowed as exc: return Response({"status_error": str(exc)}, status=HTTP_400_BAD_REQUEST) + + +class ExportTemplateViewSet(ProtectedMixin, LoggingAPIViewSet): + serializer_class = ExportTemplateSerializer + queryset = ExportTemplate.objects.select_related("fsp") + filterset_class = ExportTemplateFilter + search_fields = ("config_key",) diff --git a/src/hope_payment_gateway/api/urls.py b/src/hope_payment_gateway/api/urls.py index 1a5ad21..4aee38a 100644 --- a/src/hope_payment_gateway/api/urls.py +++ b/src/hope_payment_gateway/api/urls.py @@ -11,6 +11,7 @@ router.register(r"fsp", views.FinancialServiceProviderViewSet, basename="fsp") router.register(r"payment_instructions", views.PaymentInstructionViewSet, basename="payment-instruction") router.register(r"payment_records", views.PaymentRecordViewSet, basename="payment-record") +router.register(r"export_templates", views.ExportTemplateViewSet, basename="export-template") router.register(r"config", views.ConfigurationViewSet, basename="config") router.register(r"wu/corridors", wu_views.CorridorViewSet, basename="wu-corridor") diff --git a/src/hope_payment_gateway/apps/core/permissions.py b/src/hope_payment_gateway/apps/core/permissions.py index 4c67ea4..0150df8 100644 --- a/src/hope_payment_gateway/apps/core/permissions.py +++ b/src/hope_payment_gateway/apps/core/permissions.py @@ -1,5 +1,3 @@ -from django.conf import settings - from constance import config from rest_framework import permissions diff --git a/src/hope_payment_gateway/apps/fsp/western_union/apps.py b/src/hope_payment_gateway/apps/fsp/western_union/apps.py index cd67378..be32221 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/apps.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig as BaseAppConfig -from hope_payment_gateway.apps.gateway.registry import registry +from hope_payment_gateway.apps.gateway.registry import export_registry, registry class AppConfig(BaseAppConfig): @@ -8,8 +8,8 @@ class AppConfig(BaseAppConfig): verbose_name = "Western Union" def ready(self) -> None: - from .handlers import WesternUnionHandler + from .handlers import CSVExportStrategy, WesternUnionHandler registry.register(WesternUnionHandler) - + export_registry.register(CSVExportStrategy) from . import tasks # noqa diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py index 5839f1c..c2f0401 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py @@ -2,7 +2,6 @@ import sentry_sdk from django_fsm import TransitionNotAllowed -from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.status import HTTP_400_BAD_REQUEST from rest_framework.views import APIView diff --git a/src/hope_payment_gateway/apps/fsp/western_union/handlers.py b/src/hope_payment_gateway/apps/fsp/western_union/handlers.py index 690c171..133fe56 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/handlers.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/handlers.py @@ -17,3 +17,9 @@ def get_configuration(self, config_key, delivery_mechanism): except FinancialServiceProviderConfig.DoesNotExist: config = wu.configuration return config + + +class CSVExportStrategy(FSPProcessor): + + def export(self): + pass diff --git a/src/hope_payment_gateway/apps/gateway/admin.py b/src/hope_payment_gateway/apps/gateway/admin.py index af815d2..49e68d1 100644 --- a/src/hope_payment_gateway/apps/gateway/admin.py +++ b/src/hope_payment_gateway/apps/gateway/admin.py @@ -27,6 +27,7 @@ from hope_payment_gateway.apps.gateway.actions import TemplateExportForm, export_as_template, export_as_template_impl from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -262,3 +263,9 @@ class DeliveryMechanismAdmin(ExtraButtonsMixin, admin.ModelAdmin): "name", ) search_fields = ("code", "name") + + +@admin.register(ExportTemplate) +class ExportTemplateAdmin(ExtraButtonsMixin, admin.ModelAdmin): + list_display = ("fsp", "config_key", "delivery_mechanism") + search_fields = ("config_key", "delivery_mechanism__name") diff --git a/src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py b/src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py new file mode 100644 index 0000000..448a38e --- /dev/null +++ b/src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py @@ -0,0 +1,51 @@ +# Generated by Django 5.0.4 on 2024-06-10 05:47 + +import django.db.models.deletion +import strategy_field.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gateway", "0018_alter_financialserviceproviderconfig_delivery_mechanism"), + ] + + operations = [ + migrations.CreateModel( + name="ExportTemplate", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("query", models.TextField()), + ("config_key", models.CharField(max_length=32)), + ("strategy", strategy_field.fields.StrategyField()), + ("header", models.BooleanField(default=True)), + ("delimiter", models.CharField(choices=[(",", ","), (";", ";"), ("|", "|"), (":", ":")], default=",")), + ("quotechar", models.CharField(choices=[("'", "'"), ('"', '"'), ("`", "`")], default="'")), + ( + "quoting", + models.IntegerField( + choices=[(1, "All"), (0, "Minimal"), (3, "None"), (2, "Non Numeric")], default=1 + ), + ), + ("escapechar", models.CharField(blank=True, choices=[("", ""), ("\\", "\\")], default="", null=True)), + ( + "delivery_mechanism", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="template", + to="gateway.deliverymechanism", + ), + ), + ( + "fsp", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="gateway.financialserviceprovider" + ), + ), + ], + options={ + "unique_together": {("fsp", "config_key")}, + }, + ), + ] diff --git a/src/hope_payment_gateway/apps/gateway/models.py b/src/hope_payment_gateway/apps/gateway/models.py index 26a6a63..a9b8bcf 100644 --- a/src/hope_payment_gateway/apps/gateway/models.py +++ b/src/hope_payment_gateway/apps/gateway/models.py @@ -1,11 +1,15 @@ +import csv + from django.db import models +from django.utils.translation import gettext_lazy as _ +from adminactions.api import delimiters, quotes from django_fsm import FSMField, transition from model_utils.models import TimeStampedModel from strategy_field.fields import StrategyField from hope_payment_gateway.apps.core.models import System -from hope_payment_gateway.apps.gateway.registry import registry +from hope_payment_gateway.apps.gateway.registry import export_registry, registry class DeliveryMechanism(TimeStampedModel): @@ -191,3 +195,31 @@ def cancel(self): @transition(field=status, source="*", target=ERROR, permission="western_union.change_paymentrecordlog") def fail(self): pass + + +class ExportTemplate(models.Model): + query = models.TextField() + fsp = models.ForeignKey(FinancialServiceProvider, on_delete=models.CASCADE) + config_key = models.CharField(max_length=32) + delivery_mechanism = models.ForeignKey(DeliveryMechanism, on_delete=models.CASCADE, related_name="template") + strategy = StrategyField(registry=export_registry) + + header = models.BooleanField(default=True) + delimiter = models.CharField(choices=list(zip(delimiters, delimiters)), default=",") + quotechar = models.CharField(choices=list(zip(quotes, quotes)), default="'") + quoting = models.IntegerField( + choices=( + (csv.QUOTE_ALL, _("All")), + (csv.QUOTE_MINIMAL, _("Minimal")), + (csv.QUOTE_NONE, _("None")), + (csv.QUOTE_NONNUMERIC, _("Non Numeric")), + ), + default=csv.QUOTE_ALL, + ) + escapechar = models.CharField(choices=(("", ""), ("\\", "\\")), default="", null=True, blank=True) + + def __str__(self): + return f"{self.fsp} / {self.config_key}" + + class Meta: + unique_together = ("fsp", "config_key") diff --git a/src/hope_payment_gateway/apps/gateway/registry.py b/src/hope_payment_gateway/apps/gateway/registry.py index 0cb7089..e5fe782 100644 --- a/src/hope_payment_gateway/apps/gateway/registry.py +++ b/src/hope_payment_gateway/apps/gateway/registry.py @@ -12,8 +12,9 @@ def notify(self): pass -registry = Registry(FSPProcessor) - - class DefaultProcessor(FSPProcessor): pass + + +registry = Registry(FSPProcessor) +export_registry = Registry(FSPProcessor) From 403e256c5e78bbfedbe1d79619c4f4c5b9c4888d Mon Sep 17 00:00:00 2001 From: Domenico Date: Mon, 10 Jun 2024 20:22:58 -0600 Subject: [PATCH 08/10] 203936 race condition (#62) --- .../apps/fsp/western_union/endpoints/send_money.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py index a434533..f4ffb93 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py @@ -141,12 +141,13 @@ def send_money(hope_payload): pr = PaymentRecord.objects.get(record_code=record_code, status=PaymentRecord.PENDING) except PaymentRecord.DoesNotExist: return None - try: payload = create_validation_payload(hope_payload) response = send_money_validation(payload) + pr.refresh_from_db() if response["code"] != 200: pr.message = f"Validation failed: {response['error']}" + pr.success = False pr.save() return pr smv_payload = serialize_object(response["content"]) @@ -185,6 +186,7 @@ def send_money(hope_payload): payload[key] = value response = send_money_store(payload) + pr.refresh_from_db() if response["code"] == 200: pr.message, pr.success = "Send Money Store: Success", True pr.store() From a95b4e45f9be3519b729422997121d35c2c6b742 Mon Sep 17 00:00:00 2001 From: Domenico Date: Mon, 10 Jun 2024 20:23:48 -0600 Subject: [PATCH 09/10] Dev 2 staging (#64) * 203607-export-api (#61) * 203936 race condition (#62) --- src/hope_payment_gateway/api/fsp/filters.py | 10 ++++ .../api/fsp/serializers.py | 9 ++++ src/hope_payment_gateway/api/fsp/views.py | 30 +++++++++++ src/hope_payment_gateway/api/urls.py | 1 + .../apps/core/permissions.py | 2 - .../apps/fsp/western_union/apps.py | 6 +-- .../apps/fsp/western_union/endpoints/nis.py | 1 - .../fsp/western_union/endpoints/send_money.py | 4 +- .../apps/fsp/western_union/handlers.py | 6 +++ .../apps/gateway/admin.py | 7 +++ .../gateway/migrations/0019_exporttemplate.py | 51 +++++++++++++++++++ .../apps/gateway/models.py | 34 ++++++++++++- .../apps/gateway/registry.py | 7 +-- 13 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py diff --git a/src/hope_payment_gateway/api/fsp/filters.py b/src/hope_payment_gateway/api/fsp/filters.py index 540e55c..37fba61 100644 --- a/src/hope_payment_gateway/api/fsp/filters.py +++ b/src/hope_payment_gateway/api/fsp/filters.py @@ -3,6 +3,7 @@ from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -65,3 +66,12 @@ class Meta: "parent__remote_id": ["exact", "in"], "status": ["exact", "in"], } + + +class ExportTemplateFilter(filters.FilterSet): + class Meta: + model = ExportTemplate + fields = { + "fsp": ["exact"], + "config_key": ["exact"], + } diff --git a/src/hope_payment_gateway/api/fsp/serializers.py b/src/hope_payment_gateway/api/fsp/serializers.py index 4387f0c..0c108e4 100644 --- a/src/hope_payment_gateway/api/fsp/serializers.py +++ b/src/hope_payment_gateway/api/fsp/serializers.py @@ -4,6 +4,7 @@ from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -179,3 +180,11 @@ class Meta: "payload", "payout_amount", ) + + +class ExportTemplateSerializer(serializers.ModelSerializer): + fsp = serializers.PrimaryKeyRelatedField(queryset=FinancialServiceProvider.objects.all()) + + class Meta: + model = ExportTemplate + fields = ("query", "fsp", "config_key") diff --git a/src/hope_payment_gateway/api/fsp/views.py b/src/hope_payment_gateway/api/fsp/views.py index d11f6d7..27b8e6d 100644 --- a/src/hope_payment_gateway/api/fsp/views.py +++ b/src/hope_payment_gateway/api/fsp/views.py @@ -8,6 +8,7 @@ from hope_api_auth.views import LoggingAPIViewSet from hope_payment_gateway.api.fsp.filters import ( DeliveryMechanismFilter, + ExportTemplateFilter, FinancialServiceProviderConfigFilter, FinancialServiceProviderFilter, PaymentInstructionFilter, @@ -15,6 +16,7 @@ ) from hope_payment_gateway.api.fsp.serializers import ( DeliveryMechanismSerializer, + ExportTemplateSerializer, FinancialServiceProviderConfigSerializer, FinancialServiceProviderSerializer, PaymentInstructionSerializer, @@ -23,8 +25,11 @@ ) from hope_payment_gateway.apps.core.models import System from hope_payment_gateway.apps.fsp.western_union.endpoints.cancel import cancel +from hope_payment_gateway.apps.gateway.actions import TemplateExportForm, export_as_template, export_as_template_impl +from hope_payment_gateway.apps.gateway.admin import PaymentInstructionAdmin from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -131,6 +136,24 @@ def add_records(self, request, remote_id=None): } return Response({"remote_id": obj.remote_id, "errors": error_dict}, status=HTTP_400_BAD_REQUEST) + @action(detail=True) # , methods=["post"]) + def download(self, request, remote_id=None): + + obj = self.get_object() + try: + dm = DeliveryMechanism.objects.get(code=obj.extra.get("delivery_mechanism", None)) + export = ExportTemplate.objects.get( + fsp=obj.fsp, config_key=obj.extra.get("config_key", None), delivery_mechanism=dm + ) + queryset = PaymentRecord.objects.filter(parent=obj).select_related("parent__fsp") + + return export_as_template_impl( + queryset, + export.query.split("\r\n"), + ) + except ExportTemplate.DoesNotExist as exc: + return Response({"status_error": str(exc)}, status=HTTP_400_BAD_REQUEST) + class PaymentRecordViewSet(ProtectedMixin, LoggingAPIViewSet): serializer_class = PaymentRecordSerializer @@ -152,3 +175,10 @@ def cancel(self, request): return Response({"message": "cancel triggered"}) except TransitionNotAllowed as exc: return Response({"status_error": str(exc)}, status=HTTP_400_BAD_REQUEST) + + +class ExportTemplateViewSet(ProtectedMixin, LoggingAPIViewSet): + serializer_class = ExportTemplateSerializer + queryset = ExportTemplate.objects.select_related("fsp") + filterset_class = ExportTemplateFilter + search_fields = ("config_key",) diff --git a/src/hope_payment_gateway/api/urls.py b/src/hope_payment_gateway/api/urls.py index 1a5ad21..4aee38a 100644 --- a/src/hope_payment_gateway/api/urls.py +++ b/src/hope_payment_gateway/api/urls.py @@ -11,6 +11,7 @@ router.register(r"fsp", views.FinancialServiceProviderViewSet, basename="fsp") router.register(r"payment_instructions", views.PaymentInstructionViewSet, basename="payment-instruction") router.register(r"payment_records", views.PaymentRecordViewSet, basename="payment-record") +router.register(r"export_templates", views.ExportTemplateViewSet, basename="export-template") router.register(r"config", views.ConfigurationViewSet, basename="config") router.register(r"wu/corridors", wu_views.CorridorViewSet, basename="wu-corridor") diff --git a/src/hope_payment_gateway/apps/core/permissions.py b/src/hope_payment_gateway/apps/core/permissions.py index 4c67ea4..0150df8 100644 --- a/src/hope_payment_gateway/apps/core/permissions.py +++ b/src/hope_payment_gateway/apps/core/permissions.py @@ -1,5 +1,3 @@ -from django.conf import settings - from constance import config from rest_framework import permissions diff --git a/src/hope_payment_gateway/apps/fsp/western_union/apps.py b/src/hope_payment_gateway/apps/fsp/western_union/apps.py index cd67378..be32221 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/apps.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig as BaseAppConfig -from hope_payment_gateway.apps.gateway.registry import registry +from hope_payment_gateway.apps.gateway.registry import export_registry, registry class AppConfig(BaseAppConfig): @@ -8,8 +8,8 @@ class AppConfig(BaseAppConfig): verbose_name = "Western Union" def ready(self) -> None: - from .handlers import WesternUnionHandler + from .handlers import CSVExportStrategy, WesternUnionHandler registry.register(WesternUnionHandler) - + export_registry.register(CSVExportStrategy) from . import tasks # noqa diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py index 5839f1c..c2f0401 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/nis.py @@ -2,7 +2,6 @@ import sentry_sdk from django_fsm import TransitionNotAllowed -from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.status import HTTP_400_BAD_REQUEST from rest_framework.views import APIView diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py index a434533..f4ffb93 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py @@ -141,12 +141,13 @@ def send_money(hope_payload): pr = PaymentRecord.objects.get(record_code=record_code, status=PaymentRecord.PENDING) except PaymentRecord.DoesNotExist: return None - try: payload = create_validation_payload(hope_payload) response = send_money_validation(payload) + pr.refresh_from_db() if response["code"] != 200: pr.message = f"Validation failed: {response['error']}" + pr.success = False pr.save() return pr smv_payload = serialize_object(response["content"]) @@ -185,6 +186,7 @@ def send_money(hope_payload): payload[key] = value response = send_money_store(payload) + pr.refresh_from_db() if response["code"] == 200: pr.message, pr.success = "Send Money Store: Success", True pr.store() diff --git a/src/hope_payment_gateway/apps/fsp/western_union/handlers.py b/src/hope_payment_gateway/apps/fsp/western_union/handlers.py index 690c171..133fe56 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/handlers.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/handlers.py @@ -17,3 +17,9 @@ def get_configuration(self, config_key, delivery_mechanism): except FinancialServiceProviderConfig.DoesNotExist: config = wu.configuration return config + + +class CSVExportStrategy(FSPProcessor): + + def export(self): + pass diff --git a/src/hope_payment_gateway/apps/gateway/admin.py b/src/hope_payment_gateway/apps/gateway/admin.py index af815d2..49e68d1 100644 --- a/src/hope_payment_gateway/apps/gateway/admin.py +++ b/src/hope_payment_gateway/apps/gateway/admin.py @@ -27,6 +27,7 @@ from hope_payment_gateway.apps.gateway.actions import TemplateExportForm, export_as_template, export_as_template_impl from hope_payment_gateway.apps.gateway.models import ( DeliveryMechanism, + ExportTemplate, FinancialServiceProvider, FinancialServiceProviderConfig, PaymentInstruction, @@ -262,3 +263,9 @@ class DeliveryMechanismAdmin(ExtraButtonsMixin, admin.ModelAdmin): "name", ) search_fields = ("code", "name") + + +@admin.register(ExportTemplate) +class ExportTemplateAdmin(ExtraButtonsMixin, admin.ModelAdmin): + list_display = ("fsp", "config_key", "delivery_mechanism") + search_fields = ("config_key", "delivery_mechanism__name") diff --git a/src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py b/src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py new file mode 100644 index 0000000..448a38e --- /dev/null +++ b/src/hope_payment_gateway/apps/gateway/migrations/0019_exporttemplate.py @@ -0,0 +1,51 @@ +# Generated by Django 5.0.4 on 2024-06-10 05:47 + +import django.db.models.deletion +import strategy_field.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gateway", "0018_alter_financialserviceproviderconfig_delivery_mechanism"), + ] + + operations = [ + migrations.CreateModel( + name="ExportTemplate", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("query", models.TextField()), + ("config_key", models.CharField(max_length=32)), + ("strategy", strategy_field.fields.StrategyField()), + ("header", models.BooleanField(default=True)), + ("delimiter", models.CharField(choices=[(",", ","), (";", ";"), ("|", "|"), (":", ":")], default=",")), + ("quotechar", models.CharField(choices=[("'", "'"), ('"', '"'), ("`", "`")], default="'")), + ( + "quoting", + models.IntegerField( + choices=[(1, "All"), (0, "Minimal"), (3, "None"), (2, "Non Numeric")], default=1 + ), + ), + ("escapechar", models.CharField(blank=True, choices=[("", ""), ("\\", "\\")], default="", null=True)), + ( + "delivery_mechanism", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="template", + to="gateway.deliverymechanism", + ), + ), + ( + "fsp", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="gateway.financialserviceprovider" + ), + ), + ], + options={ + "unique_together": {("fsp", "config_key")}, + }, + ), + ] diff --git a/src/hope_payment_gateway/apps/gateway/models.py b/src/hope_payment_gateway/apps/gateway/models.py index 26a6a63..a9b8bcf 100644 --- a/src/hope_payment_gateway/apps/gateway/models.py +++ b/src/hope_payment_gateway/apps/gateway/models.py @@ -1,11 +1,15 @@ +import csv + from django.db import models +from django.utils.translation import gettext_lazy as _ +from adminactions.api import delimiters, quotes from django_fsm import FSMField, transition from model_utils.models import TimeStampedModel from strategy_field.fields import StrategyField from hope_payment_gateway.apps.core.models import System -from hope_payment_gateway.apps.gateway.registry import registry +from hope_payment_gateway.apps.gateway.registry import export_registry, registry class DeliveryMechanism(TimeStampedModel): @@ -191,3 +195,31 @@ def cancel(self): @transition(field=status, source="*", target=ERROR, permission="western_union.change_paymentrecordlog") def fail(self): pass + + +class ExportTemplate(models.Model): + query = models.TextField() + fsp = models.ForeignKey(FinancialServiceProvider, on_delete=models.CASCADE) + config_key = models.CharField(max_length=32) + delivery_mechanism = models.ForeignKey(DeliveryMechanism, on_delete=models.CASCADE, related_name="template") + strategy = StrategyField(registry=export_registry) + + header = models.BooleanField(default=True) + delimiter = models.CharField(choices=list(zip(delimiters, delimiters)), default=",") + quotechar = models.CharField(choices=list(zip(quotes, quotes)), default="'") + quoting = models.IntegerField( + choices=( + (csv.QUOTE_ALL, _("All")), + (csv.QUOTE_MINIMAL, _("Minimal")), + (csv.QUOTE_NONE, _("None")), + (csv.QUOTE_NONNUMERIC, _("Non Numeric")), + ), + default=csv.QUOTE_ALL, + ) + escapechar = models.CharField(choices=(("", ""), ("\\", "\\")), default="", null=True, blank=True) + + def __str__(self): + return f"{self.fsp} / {self.config_key}" + + class Meta: + unique_together = ("fsp", "config_key") diff --git a/src/hope_payment_gateway/apps/gateway/registry.py b/src/hope_payment_gateway/apps/gateway/registry.py index 0cb7089..e5fe782 100644 --- a/src/hope_payment_gateway/apps/gateway/registry.py +++ b/src/hope_payment_gateway/apps/gateway/registry.py @@ -12,8 +12,9 @@ def notify(self): pass -registry = Registry(FSPProcessor) - - class DefaultProcessor(FSPProcessor): pass + + +registry = Registry(FSPProcessor) +export_registry = Registry(FSPProcessor) From 9c0b0742faf69454c1895b9b6a0573fe15e094b7 Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Mon, 10 Jun 2024 18:51:36 -0600 Subject: [PATCH 10/10] logs --- .../apps/fsp/western_union/endpoints/cancel.py | 15 +++++++++++++++ .../fsp/western_union/endpoints/send_money.py | 16 ++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/cancel.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/cancel.py index 87640dc..00fd3a0 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/cancel.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/cancel.py @@ -71,3 +71,18 @@ def cancel(pk): pr.extra_data.update(extra_data) pr.save() return pr + + +def reset_mtcns(mtcns): + frm = {"counter_id": "US125QCUSD8P", "identifier": "WGQCUS1250P", "reference_no": "RCPT-7050-24-0.198.578"} + for mtcn in mtcns: + response = search_request(frm, mtcn) + payload = response["content"] + try: + database_key = payload["payment_transactions"]["payment_transaction"][0]["money_transfer_key"] + except TypeError: + database_key = None + print("ERROR", mtcn) + response = cancel_request(frm, mtcn, database_key) + if response["code"] != 200: + print(response["code"], mtcn) diff --git a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py index 9fd2cd8..112a8a6 100644 --- a/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py +++ b/src/hope_payment_gateway/apps/fsp/western_union/endpoints/send_money.py @@ -1,6 +1,7 @@ import random import phonenumbers +import sentry_sdk from constance import config from django_fsm import TransitionNotAllowed from phonenumbers.phonenumberutil import NumberParseException @@ -38,10 +39,10 @@ def create_validation_payload(hope_payload): receiver = { "name": { - # "first_name": hope_payload["first_name"], - # "last_name": hope_payload["last_name"], - "first_name": str(hope_payload["first_name"].encode("utf-8"))[2:-1], - "last_name": str(hope_payload["last_name"].encode("utf-8"))[2:-1], + "first_name": hope_payload["first_name"], + "last_name": hope_payload["last_name"], + # "first_name": str(hope_payload["first_name"].encode("utf-8"))[2:-1], + # "last_name": str(hope_payload["last_name"].encode("utf-8"))[2:-1], "name_type": "D", }, "contact_phone": phone_number, @@ -122,6 +123,8 @@ def create_validation_payload(hope_payload): def send_money_validation(payload): wu_env = config.WESTERN_UNION_WHITELISTED_ENV client = WesternUnionClient("SendMoneyValidation_Service_H2HService.wsdl") + sentry_sdk.capture_message("Western Union: Send Money Validation") + print(payload["foreign_remote_number"].get("reference_no", None)) return client.response_context( "sendmoneyValidation", payload, "SendmoneyValidation_Service_H2H", f"SOAP_HTTP_Port_{wu_env}" ) @@ -130,6 +133,8 @@ def send_money_validation(payload): def send_money_store(payload): wu_env = config.WESTERN_UNION_WHITELISTED_ENV client = WesternUnionClient("SendMoneyStore_Service_H2HService.wsdl") + sentry_sdk.capture_message("Western Union: Send Money Store") + print(payload.get("mtcn", None)) return client.response_context( "SendMoneyStore_H2H", payload, "SendMoneyStore_Service_H2H", f"SOAP_HTTP_Port_{wu_env}" ) @@ -145,8 +150,10 @@ def send_money(hope_payload): try: payload = create_validation_payload(hope_payload) response = send_money_validation(payload) + pr.refresh_from_db() if response["code"] != 200: pr.message = f"Validation failed: {response['error']}" + pr.success = False pr.save() return pr smv_payload = serialize_object(response["content"]) @@ -185,6 +192,7 @@ def send_money(hope_payload): payload[key] = value response = send_money_store(payload) + pr.refresh_from_db() if response["code"] == 200: pr.message, pr.success = "Send Money Store: Success", True pr.store()