|
| 1 | +import typing as t |
| 2 | +import abc |
| 3 | +import enum |
| 4 | +import functools |
| 5 | +import json |
| 6 | +import typing as t # noqa |
| 7 | + |
| 8 | +import unzer |
| 9 | +from unzer.model import PaymentType |
| 10 | +from unzer.model.base import BaseModel |
| 11 | +from unzer.model.customer import Salutation as UnzerSalutation |
| 12 | +from unzer.model.payment import PaymentState |
| 13 | +from unzer.model.webhook import Events, IP_ADDRESS |
| 14 | +from viur import toolkit |
| 15 | +from viur.core import CallDeferred, access, current, db, errors, exposed, force_post |
| 16 | +from viur.core.skeleton import SkeletonInstance |
| 17 | +from viur.shop.skeletons import OrderSkel |
| 18 | +from viur.shop.types import * |
| 19 | + |
| 20 | +from . import PaymentProviderAbstract |
| 21 | +from ..globals import SHOP_LOGGER |
| 22 | +from ..services import HOOK_SERVICE, Hook |
| 23 | +from ..types import exceptions as e |
| 24 | + |
| 25 | +import unzer |
| 26 | +from unzer.model import PaymentType |
| 27 | +from viur.core.skeleton import SkeletonInstance |
| 28 | + |
| 29 | +from .unzer_abstract import UnzerAbstract, log_unzer_error |
| 30 | +from ..globals import SHOP_LOGGER |
| 31 | + |
| 32 | +logger = SHOP_LOGGER.getChild(__name__) |
| 33 | + |
| 34 | + |
| 35 | +class UnzerPaylaterInvoice(UnzerAbstract): |
| 36 | + """ |
| 37 | + Unzer Paylater Invoice payment method integration for the ViUR Shop. |
| 38 | +
|
| 39 | + Enables customers to pay using invoice through the Unzer payment gateway. |
| 40 | + """ |
| 41 | + |
| 42 | + name: t.Final[str] = "unzer-paylater_invoice" |
| 43 | + |
| 44 | + def can_order( |
| 45 | + self, |
| 46 | + order_skel: SkeletonInstance_T[OrderSkel], |
| 47 | + ) -> list[ClientError]: |
| 48 | + order_skel = OrderSkel.refresh_billing_address(order_skel) |
| 49 | + errs = super().can_order(order_skel) |
| 50 | + if not order_skel["billing_address"] or not order_skel["billing_address"]["dest"]["birthdate"]: |
| 51 | + errs.append(ClientError("billing_address has no birthday set")) |
| 52 | + return errs |
| 53 | + |
| 54 | + @log_unzer_error |
| 55 | + def checkout( |
| 56 | + self, |
| 57 | + order_skel: SkeletonInstance, |
| 58 | + ) -> t.Any: |
| 59 | + order_skel = OrderSkel.refresh_billing_address(order_skel) |
| 60 | + if not order_skel["billing_address"]["dest"]["birthdate"]: |
| 61 | + raise errors.PreconditionFailed("Billing address has no birthdate") |
| 62 | + customer = self.customer_from_order_skel(order_skel) |
| 63 | + logger.debug(f"{customer = }") |
| 64 | + |
| 65 | + customer = self.client.createOrUpdateCustomer(customer) |
| 66 | + logger.debug(f"{customer = } [RESPONSE]") |
| 67 | + |
| 68 | + host = current.request.get().request.host_url |
| 69 | + return_url = f'{host.rstrip("/")}/{self.modulePath.strip("/")}/return_handler?order_key={order_skel["key"].to_legacy_urlsafe().decode("ASCII")}' |
| 70 | + unzer_session = current.session.get()["unzer"] = { |
| 71 | + "customer_id": customer.key, |
| 72 | + } |
| 73 | + payment = self.client.authorize( |
| 74 | + # TODO: x-CLIENTIP=<YOUR Client's IP> |
| 75 | + unzer.PaymentRequest( |
| 76 | + self.get_payment_type(order_skel), |
| 77 | + amount=order_skel["total"], |
| 78 | + returnUrl=return_url, |
| 79 | + card3ds=True, |
| 80 | + customerId=customer.key, |
| 81 | + orderId=order_skel["key"].id_or_name, |
| 82 | + invoiceId=order_skel["order_uid"], |
| 83 | + ) |
| 84 | + ) |
| 85 | + logger.debug(f"{payment=} [authorize response]") |
| 86 | + unzer_session["paymentId"] = payment.paymentId |
| 87 | + unzer_session["redirectUrl"] = payment.redirectUrl |
| 88 | + processing_data = payment.processing.asDict() |
| 89 | + |
| 90 | + payment = payment.charge( |
| 91 | + amount=order_skel["total"] |
| 92 | + ) |
| 93 | + logger.debug(f"{payment=} [charge response]") |
| 94 | + |
| 95 | + logger.debug(f"{unzer_session = }") |
| 96 | + current.session.get().markChanged() |
| 97 | + |
| 98 | + def set_payment(skel: SkeletonInstance): |
| 99 | + skel["payment"]["payments"][-1]["payment_id"] = payment.paymentId |
| 100 | + skel["payment"]["payments"][-1]["processing_data"] = processing_data |
| 101 | + |
| 102 | + order_skel = toolkit.set_status( |
| 103 | + key=order_skel["key"], |
| 104 | + values=set_payment, |
| 105 | + skel=order_skel, |
| 106 | + ) |
| 107 | + |
| 108 | + return unzer_session |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | + def get_payment_type( |
| 113 | + self, |
| 114 | + order_skel: SkeletonInstance, |
| 115 | + ) -> PaymentType: |
| 116 | + type_id = order_skel["payment"]["payments"][-1]["type_id"] |
| 117 | + return unzer.PaylaterInvoice(key=type_id) |
0 commit comments