def handle_refund(self, serializer):
        # dans le cas d'un remboursement, l'order_id est l'id de la transaction d'origine
        original_sp_transaction = serializer.get_transaction_by_order_id()
        payment = original_sp_transaction.payment

        if payment is None:
            raise serializers.ValidationError(
                "pas de paiement associé à la transaction d'origine",
                code="missing_payment",
            )

        if payment.mode != self.mode_id:
            raise serializers.ValidationError(
                "le mode du paiement ne correspond pas à celui pour lequel le webhook est défini",
                code="wrong_mode",
            )

        self.check_refund_transaction_match_payment(payment)

        sp_transaction = SystemPayTransaction.objects.get_or_create(
            uuid=serializer.validated_data["vads_trans_uuid"],
            defaults={
                "payment": payment,
                "is_refund": True
            },
        )

        self.save_transaction(sp_transaction, serializer)

        update_payment_from_transaction(payment, sp_transaction)
        notify_status_change(payment)

        return self.successful_response()
    def handle_payment(self, serializer):
        subscription_id = serializer.validated_data.get("subscription")

        if subscription_id:
            # il s'agit d'un paiement automatique lié à une souscription
            # on devrait avoir la trace de cette souscription dans notre
            # base de données
            sp_subscription = serializer.get_sp_subscription()
            subscription = sp_subscription.subscription

            if subscription.mode != self.mode_id:
                raise serializers.ValidationError(
                    "le mode du paiement ne correspond pas à celui pour lequel le webhook est défini",
                    code="wrong_mode",
                )

            if self.update_alias_from_transaction(serializer, sp_subscription):
                sp_subscription.save()

            self.check_payment_transaction_match_subscription(
                serializer=serializer, subscription=subscription)

            payment = create_payment(
                person=subscription.person,
                type=subscription.type,
                price=serializer.validated_data["amount"],
                mode=self.mode_id,
                subscription=subscription,
            )

            sp_transaction = SystemPayTransaction(payment=payment,
                                                  alias=sp_subscription.alias,
                                                  is_refund=False)

        else:
            # dans ce cas il s'agit d'un paiement via le formulaire
            sp_transaction = serializer.get_transaction_by_order_id()
            payment = sp_transaction.payment

            if payment is None:
                raise serializers.ValidationError(
                    "pas de paiement associé à la transaction",
                    code="missing_payment")

            if payment.mode != self.mode_id:
                raise serializers.ValidationError(
                    "le mode du paiement ne correspond pas à celui pour lequel le webhook est défini",
                    code="wrong_mode",
                )

        self.save_transaction(sp_transaction, serializer)
        update_payment_from_transaction(payment, sp_transaction)
        notify_status_change(payment)

        return self.successful_response()
Example #3
0
    def test_allocation_createdmeta_on_payment(self):
        payment = create_payment(
            person=self.p1,
            type=DonsConfig.PAYMENT_TYPE,
            price=10000,
            mode=SystemPayPaymentMode.id,
            meta={"allocations": json.dumps({str(self.group.pk): 10000})},
        )

        complete_payment(payment)
        notify_status_change(payment)

        self.assertTrue(Operation.objects.exists())
        operation = Operation.objects.get()

        self.assertEqual(operation.amount, 10000)
        self.assertEqual(operation.group, self.group)
    def save_form(self, request, form, change):
        with transaction.atomic():
            if "_changemode" in request.POST:
                if request.POST["_changemode"] not in PAYMENT_MODES:
                    raise SuspiciousOperation()

                mode = request.POST["_changemode"]
                payment = form.instance
                now = timezone.now().astimezone(timezone.utc).isoformat()

                payment.mode = mode
                payment.events.append({
                    "change_mode":
                    mode,
                    "date":
                    now,
                    "origin":
                    self.opts.app_label + "_admin_change_mode",
                })

            if "_changestatus" in request.POST:
                if int(request.POST["_changestatus"]) not in [
                        Payment.STATUS_COMPLETED,
                        Payment.STATUS_REFUSED,
                        Payment.STATUS_CANCELED,
                ]:
                    raise SuspiciousOperation()

                status = request.POST["_changestatus"]
                payment = form.instance
                now = timezone.now().astimezone(timezone.utc).isoformat()

                change_payment_status(payment, int(status))
                payment.events.append({
                    "change_status":
                    status,
                    "date":
                    now,
                    "origin":
                    self.opts.app_label + "_admin_change_button",
                })
                notify_status_change(payment)

            return super().save_form(request, form, change)
    def action(modeladmin, request, queryset):
        try:
            with transaction.atomic():
                now = timezone.now().astimezone(timezone.utc).isoformat()

                for payment in queryset.filter(
                        status=CheckPayment.STATUS_WAITING):
                    if not PAYMENT_MODES[payment.mode].can_admin:
                        raise PaymentException(
                            "Paiement n°{} ne peut pas être changé manuellement"
                            .format(payment.id))

                    change_payment_status(payment, status)
                    payment.events.append({
                        "change_status": status,
                        "date": now,
                        "origin": "payment_admin_action",
                    })
                    payment.save()
                    notify_status_change(payment)
        except PaymentException as exception:
            modeladmin.message_user(request, exception, level=messages.WARNING)
def notify_status_action(model_admin, request, queryset):
    for p in queryset:
        notify_status_change(p)
    def validate_check_view(self, request):
        get_form = CheckPaymentSearchForm(data=request.GET)
        return_response = HttpResponseRedirect(
            reverse("admin:checks_checkpayment_search"))

        if not get_form.is_valid():
            return return_response

        amount, numbers = (
            get_form.cleaned_data["amount"],
            get_form.cleaned_data["numbers"],
        )
        payments = CheckPayment.objects.filter(pk__in=numbers)

        if not len(payments) == len(numbers):
            messages.add_message(
                request,
                messages.ERROR,
                _("Erreur avec un des paiements : veuillez rééssayer"),
            )

        total_price = sum(p.price for p in payments)

        can_validate = (total_price == amount) and all(
            c.status == CheckPayment.STATUS_WAITING for c in payments)

        if can_validate and request.method == "POST":
            now = timezone.now().astimezone(timezone.utc).isoformat()

            with transaction.atomic():
                for p in payments:
                    p.status = CheckPayment.STATUS_COMPLETED
                    p.events.append({
                        "change_status": CheckPayment.STATUS_COMPLETED,
                        "date": now,
                        "origin": "check_payment_admin_validation",
                    })
                    p.save()

            # notifier en dehors de la transaction, pour être sûr que ça ait été committé
            for p in payments:
                notify_status_change(p)

            messages.add_message(
                request,
                messages.SUCCESS,
                ngettext(
                    "Chèque %(numbers)s validé",
                    "Chèques %(numbers)s validés",
                    len(numbers),
                ) % {"numbers": ", ".join(str(n) for n in numbers)},
            )
            return return_response

        return TemplateResponse(
            request,
            "admin/checks/checkpayment/validate_check.html",
            context={
                "checks": payments,
                "can_validate": can_validate,
                "total_price": total_price,
                "check_amount": amount,
                "opts": CheckPayment._meta,
            },
        )