Пример #1
0
    def testSimpleRequiresChoices(self):

        v = config_value("SHOP", "rc1")
        self.assertEqual(v, ["c1"])

        g = config_get_group("req2")
        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c3"])

        c = config_get("SHOP", "rc1")
        c.update(["c1", "c2"])

        g = config_get_group("req2")
        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c2", "c3"])
Пример #2
0
    def testSimpleRequiresChoices(self):

        v = config_value("SHOP", "rc1")
        self.assertEqual(v, ["c1"])

        g = config_get_group("req2")
        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c3"])

        c = config_get("SHOP", "rc1")
        c.update(["c1", "c2"])

        g = config_get_group("req2")
        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c2", "c3"])
Пример #3
0
    def testRequiresValue(self):
        v = config_value("SHOP", "valchoices")
        self.assertEqual(v, ["foo"])

        g = config_get_group("reqval")

        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c3"])

        c = config_get("SHOP", "valchoices")
        c.update(["foo", "bar"])

        g = config_get_group("reqval")
        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c2", "c3"])
Пример #4
0
    def testRequiresValue(self):
        v = config_value("SHOP", "valchoices")
        self.assertEqual(v, ["foo"])

        g = config_get_group("reqval")

        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c3"])

        c = config_get("SHOP", "valchoices")
        c.update(["foo", "bar"])

        g = config_get_group("reqval")
        keys = [cfg.key for cfg in g]
        self.assertEqual(keys, ["c1", "c2", "c3"])
Пример #5
0
def one_step(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    payment_module = config_get_group("PAYMENT_AUTOSUCCESS")

    # First verify that the customer exists
    try:
        contact = Contact.objects.from_request(request, create=False)
    except Contact.DoesNotExist:
        url = lookup_url(payment_module, "satchmo_checkout-step1")
        return HttpResponseRedirect(url)
    # Verify we still have items in the cart
    if cart.numItems == 0:
        template = lookup_template(payment_module, "checkout/empty_cart.html")
        return render(request, template)

    # Create a new order
    newOrder = Order(contact=contact)
    pay_ship_save(newOrder, cart, contact, shipping="", discount="")

    request.session["orderID"] = newOrder.id

    newOrder.add_status(status="Pending", notes="Order successfully submitted")

    record_payment(newOrder, payment_module, amount=newOrder.balance)

    success = lookup_url(payment_module, "satchmo_checkout-success")
    return HttpResponseRedirect(success)
Пример #6
0
def payment_label(value):
    """convert a payment key into its translated text"""

    payments = config_get("PAYMENT", "MODULES")
    for mod in payments.value:
        config = config_get_group(mod)
        if config.KEY.value == value:
            return translation.ugettext(str(config.LABEL))
    return value.capitalize()
Пример #7
0
def pay_ship_info(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    return payship.base_pay_ship_info(
        request,
        config_get_group("PAYMENT_PAYPAL"),
        payship.simple_pay_ship_process_form,
        "checkout/paypal/pay_ship.html",
    )
Пример #8
0
def pay_ship_info(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    return payship.base_pay_ship_info(
        request,
        config_get_group("PAYMENT_PAYPAL"),
        payship.simple_pay_ship_process_form,
        "checkout/paypal/pay_ship.html",
    )
Пример #9
0
def configure_api():
    """ Configure PayPal api """
    payment_module = config_get_group("PAYMENT_PAYPAL")
    if payment_module.LIVE.value:
        MODE = "live"
        CLIENT = payment_module.CLIENT_ID.value
        SECRET = payment_module.SECRET_KEY.value
    else:
        MODE = "sandbox"
        CLIENT = payment_module.SANDBOX_CLIENT_ID.value
        SECRET = payment_module.SANDBOX_SECRET_KEY.value

    paypalrestsdk.configure(
        {"mode": MODE, "client_id": CLIENT, "client_secret": SECRET}
    )
Пример #10
0
    def apply_to_order(self, order):
        """Apply up to the full amount of the balance of this cert to the order.

        Returns new balance.
        """
        amount = min(order.balance, self.balance)
        log.info(
            "applying %s from giftcert #%i [%s] to order #%i [%s]",
            money_format(amount, order.currency.iso_4217_code),
            self.id,
            money_format(self.balance, order.currency.iso_4217_code),
            order.id,
            money_format(order.balance, order.currency.iso_4217_code),
        )
        config = config_get_group("PAYMENT_GIFTCERTIFICATE")
        orderpayment = record_payment(order, config, amount)
        return self.use(amount, orderpayment=orderpayment)
Пример #11
0
    def apply_to_order(self, order):
        """Apply up to the full amount of the balance of this cert to the order.

        Returns new balance.
        """
        amount = min(order.balance, self.balance)
        log.info(
            "applying %s from giftcert #%i [%s] to order #%i [%s]",
            money_format(amount, order.currency.iso_4217_code),
            self.id,
            money_format(self.balance, order.currency.iso_4217_code),
            order.id,
            money_format(order.balance, order.currency.iso_4217_code),
        )
        config = config_get_group("PAYMENT_GIFTCERTIFICATE")
        orderpayment = record_payment(order, config, amount)
        return self.use(amount, orderpayment=orderpayment)
Пример #12
0
def configure_api():
    """ Configure PayPal api """
    payment_module = config_get_group("PAYMENT_PAYPAL")
    if payment_module.LIVE.value:
        MODE = "live"
        CLIENT = payment_module.CLIENT_ID.value
        SECRET = payment_module.SECRET_KEY.value
    else:
        MODE = "sandbox"
        CLIENT = payment_module.SANDBOX_CLIENT_ID.value
        SECRET = payment_module.SANDBOX_SECRET_KEY.value

    paypalrestsdk.configure({
        "mode": MODE,
        "client_id": CLIENT,
        "client_secret": SECRET
    })
Пример #13
0
def config_tax():
    TAX_MODULE = config_get("TAX", "MODULE")
    TAX_MODULE.add_choice(("satchmo.tax.modules.area", _("By Country/Area")))
    TAX_GROUP = config_get_group("TAX")

    _tax_classes = []
    ship_default = ""

    try:
        for tax in TaxClass.objects.all():
            _tax_classes.append((tax.title, tax))
            if "ship" in tax.title.lower():
                ship_default = tax.title
    except:
        log.warn(
            "Ignoring database error retrieving tax classes - OK if you are in syncdb."
        )

    if ship_default == "" and len(_tax_classes) > 0:
        ship_default = _tax_classes[0][0]

    config_register(
        BooleanValue(
            TAX_GROUP,
            "TAX_SHIPPING",
            description=_("Tax Shipping?"),
            requires=TAX_MODULE,
            requiresvalue="satchmo.tax.modules.area",
            default=False,
        )
    )

    config_register(
        StringValue(
            TAX_GROUP,
            "TAX_CLASS",
            description=_("TaxClass for shipping"),
            help_text=_("Select a TaxClass that should be applied for shipments."),
            default=ship_default,
            choices=_tax_classes,
        )
    )
Пример #14
0
def config_tax():
    TAX_MODULE = config_get("TAX", "MODULE")
    TAX_MODULE.add_choice(("satchmo.tax.modules.area", _("By Country/Area")))
    TAX_GROUP = config_get_group("TAX")

    _tax_classes = []
    ship_default = ""

    try:
        for tax in TaxClass.objects.all():
            _tax_classes.append((tax.title, tax))
            if "ship" in tax.title.lower():
                ship_default = tax.title
    except:
        log.warn(
            "Ignoring database error retrieving tax classes - OK if you are in syncdb."
        )

    if ship_default == "" and len(_tax_classes) > 0:
        ship_default = _tax_classes[0][0]

    config_register(
        BooleanValue(
            TAX_GROUP,
            "TAX_SHIPPING",
            description=_("Tax Shipping?"),
            requires=TAX_MODULE,
            requiresvalue="satchmo.tax.modules.area",
            default=False,
        ))

    config_register(
        StringValue(
            TAX_GROUP,
            "TAX_CLASS",
            description=_("TaxClass for shipping"),
            help_text=_(
                "Select a TaxClass that should be applied for shipments."),
            default=ship_default,
            choices=_tax_classes,
        ))
Пример #15
0
def confirm_info(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    payment_module = config_get_group("PAYMENT_PAYPAL")

    # Get the order,
    # if there is no order, return to checkout step 1
    try:
        order = Order.objects.from_request(request)
    except Order.DoesNotExist:
        url = lookup_url(payment_module, "satchmo_checkout-step1")
        return HttpResponseRedirect(url)

    # Check that the cart has items in it.
    if cart.numItems == 0:
        template = lookup_template(payment_module, "checkout/empty_cart.html")
        return render(request, template)

    # Check if the order is still valid
    if not order.validate(request):
        context = {"message": _("Your order is no longer valid.")}
        return render(request, "shop_404.html", context)

    # Set environment
    if payment_module.LIVE.value:
        environment = "production"
    else:
        environment = "sandbox"

    context = {
        "order": order,
        "environment": environment,
        "PAYMENT_LIVE": payment_live(payment_module),
    }
    template = lookup_template(payment_module, "checkout/paypal/confirm.html")
    return render(request, template, context)
Пример #16
0
def confirm_info(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    payment_module = config_get_group("PAYMENT_PAYPAL")

    # Get the order,
    # if there is no order, return to checkout step 1
    try:
        order = Order.objects.from_request(request)
    except Order.DoesNotExist:
        url = lookup_url(payment_module, "satchmo_checkout-step1")
        return HttpResponseRedirect(url)

    # Check that the cart has items in it.
    if cart.numItems == 0:
        template = lookup_template(payment_module, "checkout/empty_cart.html")
        return render(request, template)

    # Check if the order is still valid
    if not order.validate(request):
        context = {"message": _("Your order is no longer valid.")}
        return render(request, "shop_404.html", context)

    # Set environment
    if payment_module.LIVE.value:
        environment = "production"
    else:
        environment = "sandbox"

    context = {
        "order": order,
        "environment": environment,
        "PAYMENT_LIVE": payment_live(payment_module),
    }
    template = lookup_template(payment_module, "checkout/paypal/confirm.html")
    return render(request, template, context)
Пример #17
0
def balance_remaining(request):
    """Allow the user to pay the remaining balance."""
    order = None
    orderid = request.session.get("orderID")
    if orderid:
        try:
            order = Order.objects.get(pk=orderid)
        except Order.DoesNotExist:
            # TODO: verify user against current user
            pass

    if not order:
        url = reverse("satchmo_checkout-step1")
        return HttpResponseRedirect(url)

    if request.method == "POST":
        new_data = request.POST.copy()
        form = PaymentMethodForm(new_data, order=order)
        if form.is_valid():
            data = form.cleaned_data
            modulename = data["paymentmethod"]
            if not modulename.startswith("PAYMENT_"):
                modulename = "PAYMENT_" + modulename

            paymentmodule = config_get_group(modulename)
            url = lookup_url(paymentmodule, "satchmo_checkout-step2")
            return HttpResponseRedirect(url)

    else:
        form = PaymentMethodForm(order=order)

    ctx = {
        "form": form,
        "order": order,
        "paymentmethod_ct": len(config_value("PAYMENT", "MODULES")),
    }
    return render(request, "checkout/balance_remaining.html", ctx)
Пример #18
0
def balance_remaining(request):
    """Allow the user to pay the remaining balance."""
    order = None
    orderid = request.session.get("orderID")
    if orderid:
        try:
            order = Order.objects.get(pk=orderid)
        except Order.DoesNotExist:
            # TODO: verify user against current user
            pass

    if not order:
        url = reverse("satchmo_checkout-step1")
        return HttpResponseRedirect(url)

    if request.method == "POST":
        new_data = request.POST.copy()
        form = PaymentMethodForm(new_data, order=order)
        if form.is_valid():
            data = form.cleaned_data
            modulename = data["paymentmethod"]
            if not modulename.startswith("PAYMENT_"):
                modulename = "PAYMENT_" + modulename

            paymentmodule = config_get_group(modulename)
            url = lookup_url(paymentmodule, "satchmo_checkout-step2")
            return HttpResponseRedirect(url)

    else:
        form = PaymentMethodForm(order=order)

    ctx = {
        "form": form,
        "order": order,
        "paymentmethod_ct": len(config_value("PAYMENT", "MODULES")),
    }
    return render(request, "checkout/balance_remaining.html", ctx)
Пример #19
0
from django.utils.translation import ugettext_lazy as _
from satchmo.configuration.functions import (
    config_get,
    config_get_group,
    config_register,
)

from satchmo.configuration.values import DecimalValue, BooleanValue


TAX_MODULE = config_get("TAX", "MODULE")
TAX_MODULE.add_choice(("satchmo.tax.modules.percent", _("Percent Tax")))
TAX_GROUP = config_get_group("TAX")

config_register(
    DecimalValue(
        TAX_GROUP,
        "PERCENT",
        description=_("Percent tax"),
        requires=TAX_MODULE,
        requiresvalue="satchmo.tax.modules.percent",
    )
)

config_register(
    BooleanValue(
        TAX_GROUP,
        "TAX_SHIPPING",
        description=_("Tax Shipping?"),
        requires=TAX_MODULE,
        requiresvalue="satchmo.tax.modules.percent",
Пример #20
0
def create_payment(request, retries=0):
    """Call the /v1/payments/payment REST API with your client ID and
    secret and your payment details to create a payment ID.

    Return the payment ID to the client as JSON.
    """
    if request.method != "POST":
        raise Http404

    # Get the order, if there is no order, return to checkout step 1
    try:
        order = Order.objects.from_request(request)
    except Order.DoesNotExist:
        raise Http404

    # Contact PayPal to create the payment
    configure_api()
    payment_module = config_get_group("PAYMENT_PAYPAL")
    site = Site.objects.get_current()

    data = {
        "intent":
        "order",
        "payer": {
            "payment_method": "paypal"
        },
        "redirect_urls": {
            "return_url":
            lookup_url(payment_module,
                       "paypal:satchmo_checkout-success",
                       include_server=True),
            "cancel_url":
            lookup_url(payment_module,
                       "satchmo_checkout-step1",
                       include_server=True),
        },
        "transactions": [{
            "amount": {
                "currency": order.currency.iso_4217_code,
                "total": str(order.total),
                "details": {
                    "subtotal": str(order.sub_total - order.discount),
                    "tax": str(order.tax),
                    "shipping": str(order.shipping_cost),
                    "shipping_discount": str(order.shipping_discount),
                },
            },
            "description":
            "Your {site} order.".format(site=site.name),
            "invoice_number":
            str(order.id),
            "payment_options": {
                "allowed_payment_method": "UNRESTRICTED"
            },
            "item_list": {
                "items": [{
                    "name":
                    item.product.name,
                    "description":
                    item.product.meta,
                    "quantity":
                    item.quantity,
                    "currency":
                    order.currency.iso_4217_code,
                    "price":
                    "{price:.2f}".format(
                        price=(item.unit_price -
                               (item.discount / item.quantity))),
                    "tax":
                    str(item.unit_tax),
                    "sku":
                    item.product.sku,
                } for item in order.orderitem_set.all()],
                "shipping_address": {
                    "recipient_name": order.ship_addressee,
                    "line1": order.ship_street1,
                    "line2": order.ship_street2,
                    "city": order.ship_city,
                    "country_code": order.ship_country.iso2_code,
                    "postal_code": order.ship_postal_code,
                    "state": order.ship_state,
                },
                "shipping_method":
                order.shipping_description,
            },
        }],
    }

    # Send it to PayPal
    payment = paypalrestsdk.Payment(data)

    if payment.create():
        order.notes += _("--- Paypal Payment Created ---") + "\n"
        order.notes += str(timezone.now()) + "\n"
        order.notes += pprint.pformat(payment.to_dict()) + "\n\n"
        order.freeze()
        order.save()
        # Create a pending payment in our system
        order_payment = create_pending_payment(order, payment_module)
        order_payment.transaction_id = payment["id"]
        order_payment.save()

        # Return JSON to client
        return JsonResponse(payment.to_dict(), status=201)

    else:
        subject = "PayPal API error"
        message = "\n".join("{key}: {value}".format(key=key, value=value)
                            for key, value in payment.error.items())
        # Add the posted data
        message += "\n\n" + pprint.pformat(data) + "\n"

        mail_admins(subject, message)
        log.error(payment.error)
        log.error(pprint.pformat(data))
        if payment.error["name"] == "VALIDATION_ERROR":
            data = payment.error["details"]
        else:
            data = {
                "message":
                _("""Something went wrong with your PayPal payment.

Please try again.

If the problem persists, please contact us.""")
            }
        # Because we are returning a list of dicts, we must mark it as unsafe
        return JsonResponse(data, status=400, safe=False)
Пример #21
0
 def setUp(self):
     self.dummy = config_get_group("PAYMENT_DUMMY")
Пример #22
0
def webhook(request):
    """Handle PayPal webhooks.

    The PayPal REST APIs use webhooks for event notification. Webhooks
    are HTTP callbacks that receive notification messages for events.

    https://developer.paypal.com/docs/api/webhooks/v1/

    We only care about the following event types
    CUSTOMER.DISPUTE.CREATED A customer dispute is created.
    CUSTOMER.DISPUTE.RESOLVED A customer dispute is resolved.
    CUSTOMER.DISPUTE.UPDATED A customer dispute is updated.
    IDENTITY.AUTHORIZATION-CONSENT.REVOKED A risk dispute is created.
    PAYMENT-NETWORKS.ALTERNATIVE-PAYMENT.COMPLETED Webhook event payload to send for Alternative Payment Completion.
    PAYMENT.SALE.COMPLETED A sale completes.
    PAYMENT.SALE.DENIED The state of a sale changes from pending to denied.
    PAYMENT.SALE.PENDING The state of a sale changes to pending.
    PAYMENT.SALE.REFUNDED A merchant refunds a sale.
    PAYMENT.SALE.REVERSED PayPal reverses a sale.
    """
    if request.method != "POST":
        raise Http404

    data = json.loads(request.body.decode("utf-8"))

    payment_module = config_get_group("PAYMENT_PAYPAL")
    if payment_module.LIVE.value:
        webhook_id = payment_module.WEBHOOK_ID.value
    else:
        webhook_id = payment_module.SANDBOX_WEBHOOK_ID.value

    transmission_id = request.META["HTTP_PAYPAL_TRANSMISSION_ID"]
    timestamp = request.META["HTTP_PAYPAL_TRANSMISSION_TIME"]
    event_body = request.body.decode("utf-8")
    cert_url = request.META["HTTP_PAYPAL_CERT_URL"]
    actual_signature = request.META["HTTP_PAYPAL_TRANSMISSION_SIG"]
    auth_algo = request.META["HTTP_PAYPAL_AUTH_ALGO"]
    verified = WebhookEvent.verify(
        transmission_id,
        timestamp,
        webhook_id,
        event_body,
        cert_url,
        actual_signature,
        auth_algo,
    )

    if verified is False:
        subject = "PayPal API webhook verification error"
        message = pprint.pformat(data)
        mail_admins(subject, message)
        log.error("PayPal API webhook verification error")
        log.error(pprint.pformat(data))
        return JsonResponse({"error": "PayPal API webhook verification error"},
                            status=400)

    event_type = data.get("event_type")
    if event_type is not None:
        if event_type.startswith("CUSTOMER.DISPUTE") or event_type.startswith(
                "RISK.DISPUTE"):
            # Customer dispute
            # Add a note, but don't do anything else
            order_payment = get_object_or_404(
                OrderPayment,
                transaction_id=data["resource"]["disputed_transactions"][0]
                ["seller_transaction_id"],
            )
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Customer Dispute ---") + "\n"
            order.add_status(
                status="Customer Dispute",
                notes=data["resource"]["messages"][0]["content"],
            )

        elif event_type == "PAYMENTS.PAYMENT.CREATED":
            try:
                order_id = data["resource"]["transactions"][0][
                    "invoice_number"]
            except KeyError:
                response_data = {"status": "not found"}
                return JsonResponse(data=response_data, status=404)

            order = get_object_or_404(Order, id=order_id)
            # Set Order to Processing
            order.add_status(status="Payment Created",
                             notes=_("PayPal payment created. Thank you."))
        elif event_type.endswith("COMPLETED"):
            # Payment complete
            order = get_object_or_404(Order,
                                      id=data["resource"]["invoice_number"])
            if order.order_states.filter(
                    status__status="Processing").exists() is False:
                order.notes += "\n" + _(
                    "--- Paypal Payment Complete ---") + "\n"
                # Update order payment values
                order_payment = get_object_or_404(
                    OrderPayment,
                    transaction_id=data["resource"]["parent_payment"])
                order_payment.amount = data["resource"]["amount"]["total"]
                order_payment.transaction_id = data["id"]
                order_payment.save()

                # Set Order to Processing
                order.add_status(status="Processing",
                                 notes=_("Paid by PayPal. Thank you."))
            else:
                log.info("Already processed {payment}".format(
                    payment=data["resource"]["parent_payment"]))
        elif event_type.endswith("DENIED"):
            order_payment = get_object_or_404(
                OrderPayment,
                transaction_id=data["resource"]["parent_payment"])
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Payment Denied ---") + "\n"
            order.add_status(status="Denied",
                             notes=_("PayPal Payment Denied."))
        elif event_type.endswith("PENDING"):
            order_payment = get_object_or_404(
                OrderPayment,
                transaction_id=data["resource"]["parent_payment"])
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Payment Pending ---") + "\n"
            order.add_status(status="Pending",
                             notes=_("PayPal Payment Pending."))
        elif event_type.endswith("REFUNDED"):
            order = get_object_or_404(Order,
                                      id=data["resource"]["invoice_number"])
            order.notes += "\n" + _("--- Paypal Payment Refunded ---") + "\n"
            order.add_status(status="Refunded", notes=_("PayPal Refund"))
            OrderRefund.objects.create(
                payment="PAYPAL",
                order=order,
                amount=Decimal(data["resource"]["amount"]["total"]),
                exchange_rate=order.exchange_rate,
                transaction_id=data["id"],
            )
        elif event_type == "PAYMENT.SALE.REVERSED":
            order_payment = get_object_or_404(
                OrderPayment, transaction_id=data["resource"]["id"])
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Payment Reversed ---") + "\n"
            order.add_status(status="Reversed",
                             notes=_("PayPal Payment Reversed."))
            OrderPayment.objects.create(
                order=order,
                amount=data["resource"]["amount"]
                ["total"],  # Minus amount for reversal
                exchange_rate=order.exchange_rate,
                payment="PAYPAL",
                transaction_id=data["id"],
            )
        else:
            order_payment = get_object_or_404(
                OrderPayment,
                transaction_id=data["resource"]["parent_payment"])
            order = order_payment.order
            order.notes += ("\n" + _(
                "--- Paypal {event_type} ---".format(event_type=event_type)) +
                            "\n")

    order.notes += str(timezone.now()) + "\n"
    order.notes += pprint.pformat(data) + "\n"
    order.save()

    response_data = {"status": "success"}

    return JsonResponse(data=response_data, status=201)
Пример #23
0
def cron_rebill(request=None):
    """Rebill customers with expiring recurring subscription products
    This can either be run via a url with GET key authentication or
    directly from a shell script.
    """
    # TODO: support re-try billing for failed transactions

    if request is not None:
        if not config_value("PAYMENT", "ALLOW_URL_REBILL"):
            return bad_or_missing(request, _("Feature is not enabled."))
        if "key" not in request.GET or request.GET["key"] != config_value(
                "PAYMENT", "CRON_KEY"):
            return HttpResponse("Authentication Key Required")

    expiring_subscriptions = OrderItem.objects.filter(
        expire_date__gte=datetime.now()).order_by("order", "id", "expire_date")
    for item in expiring_subscriptions:
        if (
                item.product.is_subscription
        ):  # TODO - need to add support for products with trial but non-recurring
            if (item.product.subscriptionproduct.recurring_times
                    and item.product.subscriptionproduct.recurring_times +
                    item.product.subscriptionproduct.get_trial_terms().count()
                    == OrderItem.objects.filter(order=item.order,
                                                product=item.product).count()):
                continue
            if item.expire_date == datetime.date(
                    datetime.now()) and item.completed:
                if (item.id == OrderItem.objects.filter(
                        product=item.product,
                        order=item.order).order_by("-id")[0].id):
                    # bill => add orderitem, recalculate total, porocess card
                    new_order_item = OrderItem(
                        order=item.order,
                        product=item.product,
                        quantity=item.quantity,
                        unit_price=item.unit_price,
                        line_item_price=item.line_item_price,
                    )
                    # if product is recurring, set subscription end
                    if item.product.subscriptionproduct.recurring:
                        new_order_item.expire_date = (
                            item.product.subscriptionproduct.calc_expire_date(
                            ))
                    # check if product has 2 or more trial periods and if the last one paid was a trial or a regular payment.
                    ordercount = item.order.orderitem_set.all().count()
                    if (item.product.subscriptionproduct.get_trial_terms(
                    ).count() > 1 and item.unit_price
                            == item.product.subscriptionproduct.
                            get_trial_terms(ordercount - 1).price):
                        new_order_item.unit_price = item.product.subscriptionproduct.get_trial.terms(
                            ordercount).price
                        new_order_item.line_item_price = (
                            new_order_item.quantity *
                            new_order_item.unit_price)
                        new_order_item.expire_date = item.product.subscriptionproduct.get_trial_terms(
                            ordercount).calc_expire_date()
                    new_order_item.save()
                    item.order.recalculate_total()
                    #                    if new_order_item.product.subscriptionproduct.is_shippable == 3:
                    #                        item.order.total = item.order.total - item.order.shipping_cost
                    #                        item.order.save()
                    payments = item.order.payments.all()[0]
                    # list of ipn based payment modules.  Include processors that use 3rd party recurring billing.
                    ipn_based = ["PAYPAL"]
                    if payments.payment not in ipn_based and item.order.balance > 0:
                        # run card
                        # Do the credit card processing here & if successful, execute the success_handler
                        payment_module = config_get_group("PAYMENT_%s" %
                                                          payments.payment)
                        credit_processor = payment_module.MODULE.load_module(
                            "processor")
                        processor = credit_processor.PaymentProcessor(
                            payment_module)
                        processor.prepareData(item.order)
                        results, reason_code, msg = processor.process()

                        log.info(
                            """Processing %s recurring transaction with %s
                            Order #%i
                            Results=%s
                            Response=%s
                            Reason=%s""",
                            payment_module.LABEL.value,
                            payment_module.KEY.value,
                            item.order.id,
                            results,
                            reason_code,
                            msg,
                        )

                        if results:
                            # success handler
                            item.order.add_status(
                                status="Pending",
                                notes=
                                "Subscription Renewal Order successfully submitted",
                            )
                            new_order_item.completed = True
                            new_order_item.save()
                            orderpayment = OrderPayment(
                                order=item.order,
                                amount=item.order.balance,
                                payment=str(payment_module.KEY.value),
                            )
                            orderpayment.save()
    return HttpResponse()
Пример #24
0
from django import http
from django.shortcuts import render

from satchmo.configuration.functions import config_get_group
from satchmo.shop.models import Order
from satchmo.payment.utils import get_or_create_order
from satchmo.payment.views import confirm, payship
from satchmo.utils.dynamic import lookup_url

from .forms import GiftCertCodeForm, GiftCertPayShipForm
from .models import GiftCertificate, GIFTCODE_KEY

gc = config_get_group("PAYMENT_GIFTCERTIFICATE")


def giftcert_pay_ship_process_form(request, contact, working_cart,
                                   payment_module):
    if request.method == "POST":
        new_data = request.POST.copy()
        form = GiftCertPayShipForm(request, payment_module, new_data)
        if form.is_valid():
            data = form.cleaned_data

            # Create a new order.
            newOrder = get_or_create_order(request, working_cart, contact,
                                           data)
            newOrder.add_variable(GIFTCODE_KEY, data["giftcode"])

            request.session["orderID"] = newOrder.id

            url = None
Пример #25
0
def cron_rebill(request=None):
    """Rebill customers with expiring recurring subscription products
    This can either be run via a url with GET key authentication or
    directly from a shell script.
    """
    # TODO: support re-try billing for failed transactions

    if request is not None:
        if not config_value("PAYMENT", "ALLOW_URL_REBILL"):
            return bad_or_missing(request, _("Feature is not enabled."))
        if "key" not in request.GET or request.GET["key"] != config_value(
            "PAYMENT", "CRON_KEY"
        ):
            return HttpResponse("Authentication Key Required")

    expiring_subscriptions = OrderItem.objects.filter(
        expire_date__gte=datetime.now()
    ).order_by("order", "id", "expire_date")
    for item in expiring_subscriptions:
        if (
            item.product.is_subscription
        ):  # TODO - need to add support for products with trial but non-recurring
            if (
                item.product.subscriptionproduct.recurring_times
                and item.product.subscriptionproduct.recurring_times
                + item.product.subscriptionproduct.get_trial_terms().count()
                == OrderItem.objects.filter(
                    order=item.order, product=item.product
                ).count()
            ):
                continue
            if item.expire_date == datetime.date(datetime.now()) and item.completed:
                if (
                    item.id
                    == OrderItem.objects.filter(product=item.product, order=item.order)
                    .order_by("-id")[0]
                    .id
                ):
                    # bill => add orderitem, recalculate total, porocess card
                    new_order_item = OrderItem(
                        order=item.order,
                        product=item.product,
                        quantity=item.quantity,
                        unit_price=item.unit_price,
                        line_item_price=item.line_item_price,
                    )
                    # if product is recurring, set subscription end
                    if item.product.subscriptionproduct.recurring:
                        new_order_item.expire_date = (
                            item.product.subscriptionproduct.calc_expire_date()
                        )
                    # check if product has 2 or more trial periods and if the last one paid was a trial or a regular payment.
                    ordercount = item.order.orderitem_set.all().count()
                    if (
                        item.product.subscriptionproduct.get_trial_terms().count() > 1
                        and item.unit_price
                        == item.product.subscriptionproduct.get_trial_terms(
                            ordercount - 1
                        ).price
                    ):
                        new_order_item.unit_price = item.product.subscriptionproduct.get_trial.terms(
                            ordercount
                        ).price
                        new_order_item.line_item_price = (
                            new_order_item.quantity * new_order_item.unit_price
                        )
                        new_order_item.expire_date = item.product.subscriptionproduct.get_trial_terms(
                            ordercount
                        ).calc_expire_date()
                    new_order_item.save()
                    item.order.recalculate_total()
                    #                    if new_order_item.product.subscriptionproduct.is_shippable == 3:
                    #                        item.order.total = item.order.total - item.order.shipping_cost
                    #                        item.order.save()
                    payments = item.order.payments.all()[0]
                    # list of ipn based payment modules.  Include processors that use 3rd party recurring billing.
                    ipn_based = ["PAYPAL"]
                    if payments.payment not in ipn_based and item.order.balance > 0:
                        # run card
                        # Do the credit card processing here & if successful, execute the success_handler
                        payment_module = config_get_group(
                            "PAYMENT_%s" % payments.payment
                        )
                        credit_processor = payment_module.MODULE.load_module(
                            "processor"
                        )
                        processor = credit_processor.PaymentProcessor(payment_module)
                        processor.prepareData(item.order)
                        results, reason_code, msg = processor.process()

                        log.info(
                            """Processing %s recurring transaction with %s
                            Order #%i
                            Results=%s
                            Response=%s
                            Reason=%s""",
                            payment_module.LABEL.value,
                            payment_module.KEY.value,
                            item.order.id,
                            results,
                            reason_code,
                            msg,
                        )

                        if results:
                            # success handler
                            item.order.add_status(
                                status="Pending",
                                notes="Subscription Renewal Order successfully submitted",
                            )
                            new_order_item.completed = True
                            new_order_item.save()
                            orderpayment = OrderPayment(
                                order=item.order,
                                amount=item.order.balance,
                                payment=str(payment_module.KEY.value),
                            )
                            orderpayment.save()
    return HttpResponse()
Пример #26
0
def contact_info(request, **kwargs):
    """View which collects demographic information from customer."""

    # First verify that the cart exists and has items
    tempCart = Cart.objects.from_request(request)
    if tempCart.numItems == 0:
        return render(request, "checkout/empty_cart.html")

    init_data = {}
    shop = Config.objects.get_current()
    if request.user.email:
        init_data["email"] = request.user.email
    if request.user.first_name:
        init_data["first_name"] = request.user.first_name
    if request.user.last_name:
        init_data["last_name"] = request.user.last_name
    try:
        contact = Contact.objects.from_request(request, create=False)
    except Contact.DoesNotExist:
        contact = None

    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return http.HttpResponseRedirect(reverse("satchmo_cart"))

    if request.method == "POST":
        new_data = request.POST.copy()
        if not tempCart.is_shippable:
            new_data["copy_address"] = True
        form = PaymentContactInfoForm(
            new_data,
            shop=shop,
            contact=contact,
            shippable=tempCart.is_shippable,
            initial=init_data,
            cart=tempCart,
        )

        if form.is_valid():
            if contact is None and request.user and request.user.is_authenticated:
                contact = Contact(user=request.user)
            custID = form.save(contact=contact)
            request.session[CUSTOMER_ID] = custID
            # TODO - Create an order here and associate it with a session
            modulename = new_data["paymentmethod"]
            if not modulename.startswith("PAYMENT_"):
                modulename = "PAYMENT_" + modulename
            paymentmodule = config_get_group(modulename)
            url = lookup_url(paymentmodule, "satchmo_checkout-step2")
            return http.HttpResponseRedirect(url)
        else:
            log.debug("Form errors: %s", form.errors)
    else:
        if contact:
            # If a person has their contact info, make sure we populate it in the form
            for item in list(contact.__dict__.keys()):
                init_data[item] = getattr(contact, item)
            if contact.shipping_address:
                for item in list(contact.shipping_address.__dict__.keys()):
                    init_data["ship_" + item] = getattr(contact.shipping_address, item)
            if contact.billing_address:
                for item in list(contact.billing_address.__dict__.keys()):
                    init_data[item] = getattr(contact.billing_address, item)
            if contact.primary_phone:
                init_data["phone"] = contact.primary_phone.phone
        else:
            # Allow them to login from this page.
            request.session.set_test_cookie()

        init_data["copy_address"] = True

        form = PaymentContactInfoForm(
            shop=shop,
            contact=contact,
            shippable=tempCart.is_shippable,
            initial=init_data,
            cart=tempCart,
        )

    if shop.in_country_only:
        only_country = shop.sales_country
    else:
        only_country = None

    context = {
        "form": form,
        "country": only_country,
        "paymentmethod_ct": len(form.fields["paymentmethod"].choices),
    }
    return render(request, "checkout/form.html", context)
Пример #27
0
from django.utils.translation import ugettext_lazy as _
from satchmo.configuration.functions import (
    config_get,
    config_get_group,
    config_register_list,
)
from satchmo.configuration.values import DecimalValue, StringValue

SHIP_MODULES = config_get("SHIPPING", "MODULES")
SHIP_MODULES.add_choice(("satchmo.shipping.modules.flat", _("Flat rate")))
SHIPPING_GROUP = config_get_group("SHIPPING")

config_register_list(
    DecimalValue(
        SHIPPING_GROUP,
        "FLAT_RATE",
        description=_("Flat shipping"),
        requires=SHIP_MODULES,
        requiresvalue="satchmo.shipping.modules.flat",
        default="4.00",
    ),
    StringValue(
        SHIPPING_GROUP,
        "FLAT_SERVICE",
        description=_("Flat Shipping Service"),
        help_text=_("Shipping service used with Flat rate shipping"),
        requires=SHIP_MODULES,
        requiresvalue="satchmo.shipping.modules.flat",
        default="U.S. Mail",
    ),
    StringValue(
Пример #28
0
from django.urls import path
from satchmo.configuration.functions import config_get_group, config_get, config_value
from satchmo.configuration.values import SettingNotSet
from satchmo.payment.views import contact, balance, cron

import logging

log = logging.getLogger(__name__)

config = config_get_group("PAYMENT")

urlpatterns = [
    path("", contact.contact_info_view, {}, "satchmo_checkout-step1"),
    path(
        "custom/charge/<int:orderitem_id>/",
        balance.charge_remaining,
        {},
        "satchmo_charge_remaining",
    ),
    path(
        "custom/charge/",
        balance.charge_remaining_post,
        {},
        "satchmo_charge_remaining_post",
    ),
    path(
        "balance/<int:order_id>/",
        balance.balance_remaining_order,
        {},
        "satchmo_balance_remaining_order",
    ),
Пример #29
0
from django.utils.translation import ugettext_lazy as _

from satchmo.configuration.functions import (
    config_get,
    config_get_group,
    config_register_list,
)
from satchmo.configuration.values import DecimalValue, StringValue


SHIP_MODULES = config_get("SHIPPING", "MODULES")

# No need to add the choice, since it is in by default
# SHIP_MODULES.add_choice(('satchmo.shipping.modules.per', _('Per piece')))

SHIPPING_GROUP = config_get_group("SHIPPING")

config_register_list(
    DecimalValue(
        SHIPPING_GROUP,
        "PER_RATE",
        description=_("Per item price"),
        requires=SHIP_MODULES,
        requiresvalue="satchmo.shipping.modules.per",
        default="4.00",
    ),
    StringValue(
        SHIPPING_GROUP,
        "PER_SERVICE",
        description=_("Per Item Shipping Service"),
        help_text=_("Shipping service used with per item shipping"),
Пример #30
0
def create_payment(request, retries=0):
    """Call the /v1/payments/payment REST API with your client ID and
    secret and your payment details to create a payment ID.

    Return the payment ID to the client as JSON.
    """
    if request.method != "POST":
        raise Http404

    # Get the order, if there is no order, return to checkout step 1
    try:
        order = Order.objects.from_request(request)
    except Order.DoesNotExist:
        raise Http404

    # Contact PayPal to create the payment
    configure_api()
    payment_module = config_get_group("PAYMENT_PAYPAL")
    site = Site.objects.get_current()

    data = {
        "intent": "order",
        "payer": {"payment_method": "paypal"},
        "redirect_urls": {
            "return_url": lookup_url(
                payment_module, "paypal:satchmo_checkout-success", include_server=True
            ),
            "cancel_url": lookup_url(
                payment_module, "satchmo_checkout-step1", include_server=True
            ),
        },
        "transactions": [
            {
                "amount": {
                    "currency": order.currency.iso_4217_code,
                    "total": str(order.total),
                    "details": {
                        "subtotal": str(order.sub_total),
                        "tax": str(order.tax),
                        "shipping": str(order.shipping_cost),
                        "shipping_discount": str(order.shipping_discount),
                    },
                },
                "description": "Your {site} order.".format(site=site.name),
                "invoice_number": str(order.id),
                "payment_options": {"allowed_payment_method": "UNRESTRICTED"},
                "item_list": {
                    "items": [
                        {
                            "name": item.product.name,
                            "description": item.product.meta,
                            "quantity": item.quantity,
                            "currency": order.currency.iso_4217_code,
                            "price": str(item.unit_price),
                            "tax": str(item.unit_tax),
                            "sku": item.product.sku,
                        }
                        for item in order.orderitem_set.all()
                    ],
                    "shipping_address": {
                        "recipient_name": order.ship_addressee,
                        "line1": order.ship_street1,
                        "line2": order.ship_street2,
                        "city": order.ship_city,
                        "country_code": order.ship_country.iso2_code,
                        "postal_code": order.ship_postal_code,
                        "state": order.ship_state,
                    },
                    "shipping_method": order.shipping_description,
                },
            }
        ],
    }

    # Send it to PayPal
    payment = paypalrestsdk.Payment(data)

    if payment.create():
        order.notes += _("--- Paypal Payment Created ---") + "\n"
        order.notes += str(timezone.now()) + "\n"
        order.notes += pprint.pformat(payment.to_dict()) + "\n"
        order.freeze()
        order.save()
        # Create a pending payment in our system
        order_payment = create_pending_payment(order, payment_module)
        order_payment.transaction_id = payment["id"]
        order_payment.save()

        # Return JSON to client
        return JsonResponse(payment.to_dict(), status=201)

    else:
        subject = "PayPal API error"
        message = "\n".join(
            "{key}: {value}".format(key=key, value=value)
            for key, value in payment.error.items()
        )
        mail_admins(subject, message)
        log.error(payment.error)
        log.error(pprint.pformat(data))
        if payment.error["name"] == "VALIDATION_ERROR":
            data = payment.error["details"]
        else:
            data = {
                "message": _(
                    """Something went wrong with your PayPal payment.

Please try again.

If the problem persists, please contact us."""
                )
            }
        # Because we are returning a list of dicts, we must mark it as unsafe
        return JsonResponse(data, status=400, safe=False)
Пример #31
0
from django.urls import path
from satchmo.configuration.functions import config_get_group, config_get, config_value
from satchmo.configuration.values import SettingNotSet
from satchmo.payment.views import contact, balance, cron

import logging

log = logging.getLogger(__name__)

config = config_get_group("PAYMENT")

urlpatterns = [
    path("", contact.contact_info_view, {}, "satchmo_checkout-step1"),
    path(
        "custom/charge/<int:orderitem_id>/",
        balance.charge_remaining,
        {},
        "satchmo_charge_remaining",
    ),
    path(
        "custom/charge/",
        balance.charge_remaining_post,
        {},
        "satchmo_charge_remaining_post",
    ),
    path(
        "balance/<int:order_id>/",
        balance.balance_remaining_order,
        {},
        "satchmo_balance_remaining_order",
    ),
Пример #32
0
from django import http
from django.contrib.sites.models import Site
from django.shortcuts import render


from satchmo.configuration.functions import config_get_group
from satchmo.shop.models import Order
from satchmo.payment.utils import get_or_create_order
from satchmo.payment.views import confirm, payship
from satchmo.utils.dynamic import lookup_url

from .forms import GiftCertCodeForm, GiftCertPayShipForm
from .models import GiftCertificate, GIFTCODE_KEY

gc = config_get_group("PAYMENT_GIFTCERTIFICATE")


def giftcert_pay_ship_process_form(request, contact, working_cart, payment_module):
    if request.method == "POST":
        new_data = request.POST.copy()
        form = GiftCertPayShipForm(request, payment_module, new_data)
        if form.is_valid():
            data = form.cleaned_data

            # Create a new order.
            newOrder = get_or_create_order(request, working_cart, contact, data)
            newOrder.add_variable(GIFTCODE_KEY, data["giftcode"])

            request.session["orderID"] = newOrder.id

            url = None
Пример #33
0
def contact_info(request, **kwargs):
    """View which collects demographic information from customer."""

    # First verify that the cart exists and has items
    tempCart = Cart.objects.from_request(request)
    if tempCart.numItems == 0:
        return render(request, "checkout/empty_cart.html")

    init_data = {}
    shop = Config.objects.get_current()
    if request.user.email:
        init_data["email"] = request.user.email
    if request.user.first_name:
        init_data["first_name"] = request.user.first_name
    if request.user.last_name:
        init_data["last_name"] = request.user.last_name
    try:
        contact = Contact.objects.from_request(request, create=False)
    except Contact.DoesNotExist:
        contact = None

    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return http.HttpResponseRedirect(reverse("satchmo_cart"))

    if request.method == "POST":
        new_data = request.POST.copy()
        if not tempCart.is_shippable:
            new_data["copy_address"] = True
        form = PaymentContactInfoForm(
            new_data,
            shop=shop,
            contact=contact,
            shippable=tempCart.is_shippable,
            initial=init_data,
            cart=tempCart,
        )

        if form.is_valid():
            if contact is None and request.user and request.user.is_authenticated:
                contact = Contact(user=request.user)
            custID = form.save(contact=contact)
            request.session[CUSTOMER_ID] = custID
            # TODO - Create an order here and associate it with a session
            modulename = new_data["paymentmethod"]
            if not modulename.startswith("PAYMENT_"):
                modulename = "PAYMENT_" + modulename
            paymentmodule = config_get_group(modulename)
            url = lookup_url(paymentmodule, "satchmo_checkout-step2")
            return http.HttpResponseRedirect(url)
        else:
            log.debug("Form errors: %s", form.errors)
    else:
        if contact:
            # If a person has their contact info, make sure we populate it in the form
            for item in list(contact.__dict__.keys()):
                init_data[item] = getattr(contact, item)
            if contact.shipping_address:
                for item in list(contact.shipping_address.__dict__.keys()):
                    init_data["ship_" + item] = getattr(
                        contact.shipping_address, item)
            if contact.billing_address:
                for item in list(contact.billing_address.__dict__.keys()):
                    init_data[item] = getattr(contact.billing_address, item)
            if contact.primary_phone:
                init_data["phone"] = contact.primary_phone.phone
        else:
            # Allow them to login from this page.
            request.session.set_test_cookie()

        init_data["copy_address"] = True

        form = PaymentContactInfoForm(
            shop=shop,
            contact=contact,
            shippable=tempCart.is_shippable,
            initial=init_data,
            cart=tempCart,
        )

    if shop.in_country_only:
        only_country = shop.sales_country
    else:
        only_country = None

    context = {
        "form": form,
        "country": only_country,
        "paymentmethod_ct": len(form.fields["paymentmethod"].choices),
    }
    return render(request, "checkout/form.html", context)
Пример #34
0
from .utils import shasign

from django import forms
from satchmo.configuration.functions import config_get_group

payment_module = config_get_group("PAYMENT_INGENICO")


class IngenicoForm(forms.Form):
    PSPID = forms.CharField(required=True, widget=forms.HiddenInput())
    ORDERID = forms.IntegerField(required=True, widget=forms.HiddenInput())
    AMOUNT = forms.CharField(required=True, widget=forms.HiddenInput())
    CURRENCY = forms.CharField(required=True, widget=forms.HiddenInput())
    CN = forms.CharField(required=True, widget=forms.HiddenInput())
    EMAIL = forms.EmailField(required=True, widget=forms.HiddenInput())
    OWNERADDRESS = forms.CharField(required=False, widget=forms.HiddenInput())
    OWNERZIP = forms.CharField(required=False, widget=forms.HiddenInput())
    OWNERTOWN = forms.CharField(required=False, widget=forms.HiddenInput())
    OWNERCTY = forms.CharField(required=False, widget=forms.HiddenInput())  # Country
    OWNERTELNO = forms.CharField(required=False, widget=forms.HiddenInput())

    # Generate this field
    SHASIGN = forms.CharField(required=False, widget=forms.HiddenInput())

    def __init__(self, *args, **kwargs):
        super(IngenicoForm, self).__init__(*args, **kwargs)

        # If Alias / Tokenisation is used
        if payment_module.ALIAS.value:
            self.fields["ALIAS"] = forms.CharField(
                required=True, widget=forms.HiddenInput()
Пример #35
0
from django.contrib.sessions.backends.db import SessionStore
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt

from satchmo.configuration.functions import config_get_group, config_value
from satchmo.payment.utils import record_payment
from satchmo.payment.views import payship
from satchmo.payment.views.checkout import success as generic_success
from satchmo.shop.models import Cart, Order
from satchmo.utils import trunc_decimal
from satchmo.utils.dynamic import lookup_template

payment_module = config_get_group("PAYMENT_WORLDPAY")


def pay_ship_info(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    return payship.simple_pay_ship_info(
        request, payment_module, template="checkout/worldpay/pay_ship.html")


def confirm_info(request):
    "Create form to send to WorldPay"
    # Check that items are in stock
Пример #36
0
 def setUp(self):
     self.dummy = config_get_group("PAYMENT_DUMMY")
Пример #37
0
def webhook(request):
    """Handle PayPal webhooks.

    The PayPal REST APIs use webhooks for event notification. Webhooks
    are HTTP callbacks that receive notification messages for events.

    https://developer.paypal.com/docs/api/webhooks/v1/

    We only care about the following event types
    CUSTOMER.DISPUTE.CREATED A customer dispute is created.
    CUSTOMER.DISPUTE.RESOLVED A customer dispute is resolved.
    CUSTOMER.DISPUTE.UPDATED A customer dispute is updated.
    IDENTITY.AUTHORIZATION-CONSENT.REVOKED A risk dispute is created.
    PAYMENT-NETWORKS.ALTERNATIVE-PAYMENT.COMPLETED Webhook event payload to send for Alternative Payment Completion.
    PAYMENT.SALE.COMPLETED A sale completes.
    PAYMENT.SALE.DENIED The state of a sale changes from pending to denied.
    PAYMENT.SALE.PENDING The state of a sale changes to pending.
    PAYMENT.SALE.REFUNDED A merchant refunds a sale.
    PAYMENT.SALE.REVERSED PayPal reverses a sale.
    """
    if request.method != "POST":
        raise Http404

    data = json.loads(request.body.decode("utf-8"))

    payment_module = config_get_group("PAYMENT_PAYPAL")
    if payment_module.LIVE.value:
        webhook_id = payment_module.WEBHOOK_ID.value
    else:
        webhook_id = payment_module.SANDBOX_WEBHOOK_ID.value

    transmission_id = request.META["HTTP_PAYPAL_TRANSMISSION_ID"]
    timestamp = request.META["HTTP_PAYPAL_TRANSMISSION_TIME"]
    event_body = request.body.decode("utf-8")
    cert_url = request.META["HTTP_PAYPAL_CERT_URL"]
    actual_signature = request.META["HTTP_PAYPAL_TRANSMISSION_SIG"]
    auth_algo = request.META["HTTP_PAYPAL_AUTH_ALGO"]
    verified = WebhookEvent.verify(
        transmission_id,
        timestamp,
        webhook_id,
        event_body,
        cert_url,
        actual_signature,
        auth_algo,
    )

    if verified is False:
        subject = "PayPal API webhook verification error"
        message = pprint.pformat(data)
        mail_admins(subject, message)
        log.error("PayPal API webhook verification error")
        log.error(pprint.pformat(data))
        return JsonResponse(
            {"error": "PayPal API webhook verification error"}, status=400
        )

    event_type = data.get("event_type")
    if event_type is not None:
        if event_type.startswith("CUSTOMER.DISPUTE"):
            # Customer dispute
            # Add a note, but don't do anything else
            order_payment = get_object_or_404(
                OrderPayment,
                transaction_id=data["resource"]["disputed_transactions"][0][
                    "seller_transaction_id"
                ],
            )
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Customer Dispute ---") + "\n"
        elif event_type.endswith("COMPLETED"):
            # Payment complete
            order = get_object_or_404(Order, id=data["resource"]["invoice_number"])
            if order.order_states.filter(status__status="Processing").exists() is False:
                order.notes += "\n" + _("--- Paypal Payment Complete ---") + "\n"
                # Update order payment values
                order_payment = get_object_or_404(
                    OrderPayment, transaction_id=data["resource"]["parent_payment"]
                )
                order_payment.amount = data["resource"]["amount"]["total"]
                order_payment.transaction_id = data["id"]
                order_payment.save()

                # Set Order to Processing
                order.add_status(
                    status="Processing", notes=_("Paid by PayPal. Thank you.")
                )
            else:
                log.info(
                    "Already processed {payment}".format(
                        payment=data["resource"]["parent_payment"]
                    )
                )
        elif event_type.endswith("DENIED"):
            order_payment = get_object_or_404(
                OrderPayment, transaction_id=data["resource"]["parent_payment"]
            )
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Payment Denied ---") + "\n"
            order.add_status(status="Denied", notes=_("PayPal Payment Denied."))
        elif event_type.endswith("PENDING"):
            order_payment = get_object_or_404(
                OrderPayment, transaction_id=data["resource"]["parent_payment"]
            )
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Payment Pending ---") + "\n"
            order.add_status(status="Pending", notes=_("PayPal Payment Pending."))
        elif event_type.endswith("REFUNDED"):
            order = get_object_or_404(Order, id=data["resource"]["invoice_number"])
            order.notes += "\n" + _("--- Paypal Payment Refunded ---") + "\n"
            order.add_status(status="Refunded", notes=_("PayPal Refund"))
            OrderRefund.objects.create(
                payment="PAYPAL",
                order=order,
                amount=Decimal(data["resource"]["amount"]["total"]),
                exchange_rate=order.exchange_rate,
                transaction_id=data["id"],
            )
        elif event_type == "PAYMENT.SALE.REVERSED":
            order_payment = get_object_or_404(
                OrderPayment, transaction_id=data["resource"]["id"]
            )
            order = order_payment.order
            order.notes += "\n" + _("--- Paypal Payment Reversed ---") + "\n"
            order.add_status(status="Reversed", notes=_("PayPal Payment Reversed."))
            OrderPayment.objects.create(
                order=order,
                amount=data["resource"]["amount"]["total"],  # Minus amount for reversal
                exchange_rate=order.exchange_rate,
                payment="PAYPAL",
                transaction_id=data["id"],
            )
        else:
            order_payment = get_object_or_404(
                OrderPayment, transaction_id=data["resource"]["parent_payment"]
            )
            order = order_payment.order
            order.notes += (
                "\n"
                + _("--- Paypal {event_type} ---".format(event_type=event_type))
                + "\n"
            )

    order.notes += str(timezone.now()) + "\n"
    order.notes += pprint.pformat(data) + "\n"
    order.save()

    response_data = {"status": "success"}

    return JsonResponse(data=response_data, status=201)
Пример #38
0
# -*- coding: utf-8 -*-

from datetime import date

from django.http import Http404
from django.test import TestCase, RequestFactory

from satchmo.configuration.functions import config_get_group
from satchmo.payment.modules.ingenico.utils import shasign
from satchmo.payment.modules.ingenico.views import process
from satchmo.shop.factories import TestOrderFactory
from satchmo.shop.models import Order, OrderRefund

payment_module = config_get_group("PAYMENT_INGENICO")


class ProcessTest(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

    def test_GET(self):
        request = self.factory.get("/shop/checkout/ingenico/success/")

        response = process(request)
        # Method Not Allowed
        self.assertEqual(response.status_code, 405)

    def test_invalid_shasign(self):
        order = TestOrderFactory()

        data = {
Пример #39
0
"""Simple wrapper for standard checkout as implemented in satchmo.payment.views"""

from django.urls import reverse
from django.http import HttpResponseRedirect

from satchmo.configuration.functions import config_get_group
from satchmo.payment.views import confirm, payship
from satchmo.shop.models import Cart

dummy = config_get_group("PAYMENT_DUMMY")


def pay_ship_info(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    return payship.credit_pay_ship_info(request, dummy)


def confirm_info(request):
    # Check that items are in stock
    cart = Cart.objects.from_request(request)
    if cart.not_enough_stock():
        return HttpResponseRedirect(reverse("satchmo_cart"))

    return confirm.credit_confirm_info(request, dummy)