chore: docs and added new field for tpv domain
This commit is contained in:
@@ -95,8 +95,28 @@ Puedes crear un archivo `.env` con las siguientes variables de entorno.
|
||||
| AWS_SECRET_ACCESS_KEY | | Clave secreta de S3 |
|
||||
| AWS_STORAGE_BUCKET_NAME | | Nombre del bucket de S3 |
|
||||
| AWS_S3_ENDPOINT_URL | | Endpoint de S3 |
|
||||
| REDSYS_SHARED_SECRET | sq7HjrUOBfKmC576ILgskD5srU870gJ7 | Clave privada de Redsys |
|
||||
| REDSYS_MERCHANT_CODE | 999008881 | Código de negocio de Redsys |
|
||||
| REDSYS_TERMINAL | 001 | Terminal de Redsys |
|
||||
| REDSYS_CURRENCY_CODE | 978 | Código de moneda de Redsys |
|
||||
| REDSYS_TPV_DOMAIN | | Dominio del TPV para redirección de Redsys |
|
||||
|
||||
|
||||
## Depurar pagos TPV con Redsys
|
||||
|
||||
Para poder usar el TPV en la máquina local necesitaremos instalar y configurar [`ngrok`](https://ngrok.com/),
|
||||
siguiendo los pasos que nos indican en la web al crear nuestra cuenta
|
||||
|
||||
Arrancaremos ngrok con el siguiente comando:
|
||||
|
||||
```bash
|
||||
ngrok http http://localhost:8000
|
||||
```
|
||||
|
||||
Ngrok nos dará una URL accesible desde fuera, que tendremos que guardar en los `Ajustes de la tienda`
|
||||
como `Dominio del TPV`
|
||||
|
||||
Tendremos que establecer también los siguientes ajustes de la cuenta con estos valores para desarrollo.
|
||||
|
||||
| Variable | Valor para desarrollo | Descripción |
|
||||
|------------------------|----------------------------------|------------------------------------------------------------------------------|
|
||||
| `redsys_shared_secret` | sq7HjrUOBfKmC576ILgskD5srU870gJ7 | Clave privada de Redsys |
|
||||
| `merchant_code` | 999008881 | Código de negocio de Redsys |
|
||||
| `terminal` | 001 | Terminal de Redsys |
|
||||
| `currency_code` | 978 | Código de moneda de Redsys |
|
||||
| `tpv_domain` | <variable> | Dominio del TPV para redirección de Redsys (en local lo obtenemos por ngrok) |
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.1.4 on 2025-01-13 08:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("shop", "0003_order_shipping_method_alter_payment_order"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="shopsettings",
|
||||
name="tpv_domain",
|
||||
field=models.URLField(
|
||||
blank=True, max_length=128, null=True, verbose_name="dominio del tpv"
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -631,6 +631,9 @@ class ShopSettings(SingletonModel):
|
||||
) # 978 == EURO
|
||||
terminal = models.CharField(max_length=8, verbose_name=_("Terminal"))
|
||||
shared_secret = models.CharField(max_length=100, verbose_name=_("Clave de Redsys"))
|
||||
tpv_domain = models.URLField(
|
||||
max_length=128, blank=True, null=True, verbose_name=_("dominio del tpv")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("ajustes de la tienda")
|
||||
|
||||
+11
-8
@@ -7,7 +7,7 @@ from django.conf import settings
|
||||
from django.urls import reverse
|
||||
|
||||
from shop.exceptions import RedsysPaymentException
|
||||
from shop.models import Order
|
||||
from shop.models import Order, ShopSettings
|
||||
from shop.settings import TransactionTypes
|
||||
from shop.utils import compute_signature, decode_b64_dict
|
||||
|
||||
@@ -21,6 +21,9 @@ class RedsysClient:
|
||||
)
|
||||
REST_PROD_ENVIRONMENT_URL = "https://sis.redsys.es/sis/rest/trataPeticionREST"
|
||||
|
||||
def __init__(self):
|
||||
self.settings = ShopSettings.load()
|
||||
|
||||
def get_target_url(self):
|
||||
if settings.DEBUG:
|
||||
return self.DEBUG_ENVIRONMENT_URL
|
||||
@@ -32,31 +35,31 @@ class RedsysClient:
|
||||
return self.REST_PROD_ENVIRONMENT_URL
|
||||
|
||||
def get_merchant_code(self) -> str:
|
||||
return settings.REDSYS_MERCHANT_CODE
|
||||
return self.settings.merchant_code
|
||||
|
||||
def get_terminal(self) -> str:
|
||||
return settings.REDSYS_TERMINAL
|
||||
return self.settings.terminal
|
||||
|
||||
def get_currency_code(self) -> str:
|
||||
return settings.REDSYS_CURRENCY_CODE
|
||||
return self.settings.currency_code
|
||||
|
||||
def get_merchant_url_ok_for_order(self, order: Order) -> str:
|
||||
path = reverse("web:order", kwargs={"uuid": order.uuid})
|
||||
return f"{settings.REDSYS_TPV_DOMAIN}{path}"
|
||||
return f"{self.settings.tpv_domain}{path}"
|
||||
|
||||
def to_integer(self, value: Decimal):
|
||||
return int(value * 100)
|
||||
|
||||
def get_merchant_url_ko_for_order(self, order: Order) -> str:
|
||||
path = reverse("web:order", kwargs={"uuid": order.uuid})
|
||||
return f"{settings.REDSYS_TPV_DOMAIN}{path}"
|
||||
return f"{self.settings.tpv_domain}{path}"
|
||||
|
||||
def get_webhook_url_for_order(self, order: Order) -> str:
|
||||
path = reverse("shop:webhook", kwargs={"uuid": order.uuid})
|
||||
return f"{settings.REDSYS_TPV_DOMAIN}{path}"
|
||||
return f"{self.settings.tpv_domain}{path}"
|
||||
|
||||
def get_shared_secret(self) -> str:
|
||||
return settings.REDSYS_SHARED_SECRET
|
||||
return self.settings.shared_secret
|
||||
|
||||
def get_signature(self, hash, payload: str):
|
||||
key = self.get_shared_secret()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from django.core.signals import Signal
|
||||
|
||||
|
||||
clear_cart = Signal()
|
||||
send_order_email = Signal()
|
||||
|
||||
@@ -2,14 +2,13 @@ import base64
|
||||
import json
|
||||
from decimal import Decimal
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from shop.models import CustomerAddress, Order, Tax
|
||||
from shop.models import CustomerAddress, Order, ShopSettings, Tax
|
||||
from shop.redsys import RedsysClient
|
||||
from shop.tests.mixins import CreateProductsMixin
|
||||
from shop.utils import create_order, create_order_line_for_product, validate_expiry_date
|
||||
@@ -26,7 +25,7 @@ class TestRedsysTPV(APITestCase, CreateProductsMixin):
|
||||
self.customer = get_user_model().objects.create_user(
|
||||
username="11111111H",
|
||||
first_name="Darth",
|
||||
last_name="Maull",
|
||||
last_name="Maul",
|
||||
email="darth@maul.com",
|
||||
password="dathomir",
|
||||
)
|
||||
@@ -53,6 +52,9 @@ class TestRedsysTPV(APITestCase, CreateProductsMixin):
|
||||
self.product = self.create_product()
|
||||
self.order = self.create_order()
|
||||
self.order.calculate_total_from_lines()
|
||||
self.settings = ShopSettings.load()
|
||||
self.settings.shared_secret = "sq7HjrUOBfKmC576ILgskD5srU870gJ7" # Debug secret
|
||||
self.settings.save()
|
||||
|
||||
def create_order(self):
|
||||
order = create_order(
|
||||
@@ -83,12 +85,10 @@ class TestRedsysTPV(APITestCase, CreateProductsMixin):
|
||||
assert merchant_parameters.get("DS_MERCHANT_AMOUNT") == str(
|
||||
int(amount_to_pay * 100)
|
||||
)
|
||||
assert (
|
||||
merchant_parameters.get("DS_MERCHANT_TERMINAL") == settings.REDSYS_TERMINAL
|
||||
)
|
||||
assert merchant_parameters.get("DS_MERCHANT_TERMINAL") == self.settings.terminal
|
||||
assert (
|
||||
merchant_parameters.get("DS_MERCHANT_MERCHANTCODE")
|
||||
== settings.REDSYS_MERCHANT_CODE
|
||||
== self.settings.merchant_code
|
||||
)
|
||||
|
||||
client.get_body_for_order(self.order)
|
||||
@@ -124,7 +124,7 @@ class TestRedsysTPV(APITestCase, CreateProductsMixin):
|
||||
reverse("shop:webhook", kwargs={"uuid": self.order.uuid}),
|
||||
data={
|
||||
"Ds_MerchantParameters": b64_merchant_params,
|
||||
"Ds_Signature": settings.REDSYS_SHARED_SECRET,
|
||||
"Ds_Signature": self.settings.shared_secret,
|
||||
"Ds_SignatureVersion": "HMAC_SHA256_V1",
|
||||
},
|
||||
)
|
||||
@@ -164,7 +164,7 @@ class TestRedsysTPV(APITestCase, CreateProductsMixin):
|
||||
reverse("shop:webhook", kwargs={"uuid": self.order.uuid}),
|
||||
data={
|
||||
"Ds_MerchantParameters": b64_merchant_params,
|
||||
"Ds_Signature": settings.REDSYS_SHARED_SECRET,
|
||||
"Ds_Signature": self.settings.shared_secret,
|
||||
"Ds_SignatureVersion": "HMAC_SHA256_V1",
|
||||
},
|
||||
)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ from django.shortcuts import get_object_or_404
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from shop.models import Order
|
||||
from shop.utils import validate_payment_for_order, pay_order
|
||||
from shop.utils import pay_order, validate_payment_for_order
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
{% endblock %}
|
||||
</main>
|
||||
</body>
|
||||
|
||||
<script src="{% static 'theme/js/theme.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<svg class="h-4 w-4 text-yellow-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z"/>
|
||||
<svg class="h-4 w-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#CFCA00" viewBox="0 0 24 24">
|
||||
<path d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 315 B |
@@ -0,0 +1,5 @@
|
||||
<svg class="h-4 w-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#101010" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 326 B |
@@ -80,7 +80,9 @@ class MyAccountView(TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
addresses = CustomerAddress.objects.filter(user=self.request.user)
|
||||
settings = WebSettings.load()
|
||||
return {
|
||||
"web_title": settings.web_title,
|
||||
"customer_addresses": addresses,
|
||||
}
|
||||
|
||||
|
||||
+19
-11
@@ -1,3 +1,5 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db.models import Sum
|
||||
from django.http.response import HttpResponse, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, reverse
|
||||
@@ -79,7 +81,7 @@ class CartDetail(TemplateView, CreateView):
|
||||
return {
|
||||
"cart": cart,
|
||||
"items": cart_items,
|
||||
"title": web_settings.web_title,
|
||||
"web_title": web_settings.web_title,
|
||||
"description": _("resumen del carrito"),
|
||||
}
|
||||
|
||||
@@ -148,15 +150,21 @@ class OrderDetail(TemplateView):
|
||||
items = OrderLine.objects.select_related("product").filter(
|
||||
order=order, product__is_shipping_method=False
|
||||
)
|
||||
shipping_cost = (
|
||||
OrderLine.objects.select_related("product")
|
||||
.filter(
|
||||
|
||||
shipping_cost = OrderLine.objects.select_related("product").filter(
|
||||
order=order,
|
||||
product__is_shipping_method=True,
|
||||
)
|
||||
.aggregate(amount=Sum("total"))
|
||||
.get("amount")
|
||||
)
|
||||
).aggregate(amount=Sum("total")).get("amount") or Decimal("0")
|
||||
|
||||
base_total = OrderLine.objects.select_related("product").filter(
|
||||
order=order,
|
||||
product__is_shipping_method=False,
|
||||
).aggregate(amount=Sum("base_total")).get("amount") or Decimal("0")
|
||||
|
||||
tax_total = OrderLine.objects.select_related("product").filter(
|
||||
order=order,
|
||||
product__is_shipping_method=False,
|
||||
).aggregate(amount=Sum("taxes")).get("amount") or Decimal("0")
|
||||
|
||||
return {
|
||||
"order": order,
|
||||
@@ -165,9 +173,9 @@ class OrderDetail(TemplateView):
|
||||
"merchant_parameters": parameters.get("Ds_MerchantParameters"),
|
||||
"signature": parameters.get("Ds_Signature"),
|
||||
"redsys_target_url": redsys_client.get_target_url(),
|
||||
"total": order.total,
|
||||
"base_total": order.base_total,
|
||||
"tax_total": order.total - order.base_total,
|
||||
"total": round(order.total, 2),
|
||||
"base_total": round(base_total, 2),
|
||||
"tax_total": round(tax_total, 2),
|
||||
"shipping_cost": round(shipping_cost, 2),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user