Skip to content

Commit

Permalink
Merge pull request #4731 from korycins/feature/webhooks
Browse files Browse the repository at this point in the history
Support for webhooks
  • Loading branch information
korycins committed Sep 26, 2019
2 parents b90b838 + 1cb2245 commit 7478b7c
Show file tree
Hide file tree
Showing 63 changed files with 2,317 additions and 557 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Unified MenuItemMove to other reordering mutations. It now uses relative positions instead of absolute ones (breaking change) - #4734 by @NyanKiyoshi.
- Add descriptions for queries and query arguments - #4758 by @maarcingebala
- Fixed the inability of users to set a variant's `priceOverride` and `costPrice` to `null` - #4754 by @NyanKiyoshi
- Add support for webhooks - #4731 by @korycins

## 2.8.0

Expand Down
2 changes: 2 additions & 0 deletions saleor/account/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from ..account import events as account_events
from ..account.models import User
from ..extensions.manager import get_extensions_manager
from . import emails
from .i18n import AddressMetaForm, get_address_form_class

Expand Down Expand Up @@ -109,6 +110,7 @@ def save(self, request=None, commit=True):
if commit:
user.save()
account_events.customer_account_created_event(user=user)
get_extensions_manager().customer_created(customer=user)
return user


Expand Down
8 changes: 2 additions & 6 deletions saleor/checkout/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
add_gift_card_code_to_checkout,
remove_gift_card_code_from_checkout,
)
from ..order import events
from ..order.actions import order_created
from ..order.emails import send_order_confirmation
from ..order.models import Order, OrderLine
from ..shipping.models import ShippingMethod
Expand Down Expand Up @@ -1164,11 +1164,7 @@ def create_order(*, checkout: Checkout, order_data: dict, user: User) -> Order:
# assign checkout payments to the order
checkout.payments.update(order=order)

manager = get_extensions_manager()
manager.postprocess_order_creation(order)

# Create the order placed
events.order_created_event(order=order, user=user)
order_created(order=order, user=user)

# Send the order confirmation email
send_order_confirmation.delay(order.pk, user.pk)
Expand Down
1 change: 1 addition & 0 deletions saleor/core/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"shipping.manage_shipping",
"site.manage_settings",
"site.manage_translations",
"webhook.manage_webhooks",
]


Expand Down
2 changes: 2 additions & 0 deletions saleor/dashboard/customer/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from ...account import events as account_events
from ...account.models import CustomerNote, User
from ...extensions.manager import get_extensions_manager


def get_name_placeholder(name):
Expand Down Expand Up @@ -77,6 +78,7 @@ def save(self, commit=True):
instance = super(CustomerForm, self).save(commit=commit) # type: User
if is_user_creation:
account_events.customer_account_created_event(user=instance)
get_extensions_manager().customer_created(customer=instance)
return instance

has_new_email = "email" in self.changed_data
Expand Down
21 changes: 13 additions & 8 deletions saleor/dashboard/order/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@
from ...discount.utils import decrease_voucher_usage, increase_voucher_usage
from ...extensions.manager import get_extensions_manager
from ...order import OrderStatus, events
from ...order.actions import (
cancel_fulfillment,
cancel_order,
clean_mark_order_as_paid,
fulfill_order_line,
mark_order_as_paid,
order_shipping_updated,
)
from ...order.models import Fulfillment, FulfillmentLine, Order, OrderLine
from ...order.utils import (
add_variant_to_order,
cancel_fulfillment,
cancel_order,
change_order_line_quantity,
fulfill_order_line,
recalculate_order,
)
from ...payment import ChargeStatus, CustomPaymentChoices, PaymentError, gateway
from ...payment.utils import clean_mark_order_as_paid, mark_order_as_paid
from ...product.models import Product, ProductVariant
from ...product.utils import allocate_stock, deallocate_stock
from ...shipping.models import ShippingMethod
Expand Down Expand Up @@ -209,8 +213,9 @@ def save(self, commit=True):

manager = get_extensions_manager()
self.instance.shipping_price = manager.calculate_order_shipping(self.instance)
recalculate_order(self.instance)
return super().save(commit)
instance = super().save(commit)
order_shipping_updated(instance)
return instance


class OrderRemoveShippingForm(forms.ModelForm):
Expand Down Expand Up @@ -487,7 +492,7 @@ def clean(self):
return data

def cancel_order(self, user):
cancel_order(user, self.order, self.cleaned_data.get("restock"))
cancel_order(self.order, user, self.cleaned_data.get("restock"))


class CancelFulfillmentForm(forms.Form):
Expand Down Expand Up @@ -520,7 +525,7 @@ def clean(self):
return data

def cancel_fulfillment(self, user):
cancel_fulfillment(user, self.fulfillment, self.cleaned_data.get("restock"))
cancel_fulfillment(self.fulfillment, user, self.cleaned_data.get("restock"))


class FulfillmentTrackingNumberForm(forms.ModelForm):
Expand Down
56 changes: 23 additions & 33 deletions saleor/dashboard/order/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@
from ...core.exceptions import InsufficientStock
from ...core.utils import get_paginator_items
from ...order import OrderStatus, events
from ...order.emails import (
send_fulfillment_confirmation_to_customer,
send_fulfillment_update,
send_order_confirmation,
from ...order.actions import (
fulfillment_tracking_updated,
order_address_updated,
order_captured,
order_created,
order_fulfilled,
order_refunded,
order_voided,
)
from ...order.emails import send_fulfillment_update, send_order_confirmation
from ...order.models import Fulfillment, FulfillmentLine, Order
from ...order.utils import update_order_prices, update_order_status
from ...order.utils import update_order_prices
from ...shipping.models import ShippingMethod
from ..views import staff_member_required
from .filters import OrderFilter
Expand Down Expand Up @@ -103,10 +108,9 @@ def create_order_from_draft(request, order_pk):
msg = pgettext_lazy(
"Dashboard message related to an order", "Order created from draft order"
)
order_created(order=order, user=request.user, from_draft=True)

events.order_created_event(order=order, user=request.user, from_draft=True)
messages.success(request, msg)

if form.cleaned_data.get("notify_customer"):
send_order_confirmation.delay(order.pk, request.user.pk)
return redirect("dashboard:order-details", order_pk=order.pk)
Expand Down Expand Up @@ -190,10 +194,10 @@ def capture_payment(request, order_pk, payment_pk):
msg = pgettext_lazy(
"Dashboard message related to a payment", "Captured %(amount)s"
) % {"amount": prices.amount(amount)}
events.payment_captured_event(
messages.success(request, msg)
order_captured(
order=order, user=request.user, amount=amount.amount, payment=payment
)
messages.success(request, msg)
return redirect("dashboard:order-details", order_pk=order.pk)
status = 400 if form.errors else 200
ctx = {"captured": amount, "form": form, "order": order, "payment": payment}
Expand All @@ -217,10 +221,8 @@ def refund_payment(request, order_pk, payment_pk):
msg = pgettext_lazy(
"Dashboard message related to a payment", "Refunded %(amount)s"
) % {"amount": prices.amount(payment.get_captured_amount())}
events.payment_refunded_event(
order=order, user=request.user, amount=amount, payment=payment
)
messages.success(request, msg)
order_refunded(order, request.user, amount, payment)
return redirect("dashboard:order-details", order_pk=order.pk)
status = 400 if form.errors else 200
ctx = {
Expand All @@ -243,7 +245,7 @@ def void_payment(request, order_pk, payment_pk):
form = VoidPaymentForm(request.POST or None, payment=payment)
if form.is_valid() and form.void(request.user):
msg = pgettext_lazy("Dashboard message", "Voided payment")
events.payment_voided_event(order=order, user=request.user, payment=payment)
order_voided(order=order, user=request.user, payment=payment)
messages.success(request, msg)
return redirect("dashboard:order-details", order_pk=order.pk)
status = 400 if form.errors else 200
Expand Down Expand Up @@ -371,9 +373,7 @@ def order_address(request, order_pk, address_type):
if update_prices:
update_order_prices(order, request.discounts)
if not order.is_draft():
events.order_updated_address_event(
order=order, user=request.user, address=address
)
order_address_updated(order, request.user, address)
messages.success(request, success_msg)
return redirect("dashboard:order-details", order_pk=order_pk)
ctx = {"order": order, "address_type": address_type, "form": form}
Expand Down Expand Up @@ -630,26 +630,19 @@ def fulfill_order_lines(request, order_pk):
quantity_fulfilled += quantity
quantities.append(quantity)
order_lines.append(line)
# update to refresh prefetched lines quantity_fulfilled
order = orders.get(pk=order_pk)
update_order_status(order)
msg = npgettext_lazy(
"Dashboard message related to an order",
"Fulfilled %(quantity_fulfilled)d item",
"Fulfilled %(quantity_fulfilled)d items",
number="quantity_fulfilled",
) % {"quantity_fulfilled": quantity_fulfilled}

events.fulfillment_fulfilled_items_event(
order=order,
user=request.user,
fulfillment_lines=fulfillment.lines.all(),
order_fulfilled(
fulfillment,
request.user,
fulfillment.lines.all(),
form.cleaned_data.get("send_mail"),
)

if form.cleaned_data.get("send_mail"):
send_fulfillment_confirmation_to_customer(
order, fulfillment, request.user
)
else:
msg = pgettext_lazy(
"Dashboard message related to an order", "No items fulfilled"
Expand Down Expand Up @@ -702,11 +695,8 @@ def change_fulfillment_tracking(request, order_pk, fulfillment_pk):
form = FulfillmentTrackingNumberForm(request.POST or None, instance=fulfillment)
if form.is_valid():
form.save()
events.fulfillment_tracking_updated_event(
order=order,
user=request.user,
tracking_number=request.POST.get("tracking_number"),
fulfillment=fulfillment,
fulfillment_tracking_updated(
fulfillment, request.user, request.POST.get("tracking_number")
)
if form.cleaned_data.get("send_mail"):
events.email_sent_event(
Expand Down
1 change: 1 addition & 0 deletions saleor/dashboard/product/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def product_create(request, type_pk):
variant_form.save()
msg = pgettext_lazy("Dashboard message", "Added product %s") % (product,)
messages.success(request, msg)
request.extensions.product_created(product)
return redirect("dashboard:product-details", pk=product.pk)
ctx = {
"product_form": product_form,
Expand Down
22 changes: 20 additions & 2 deletions saleor/extensions/base_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ..core.taxes import TaxType
from ..checkout.models import Checkout, CheckoutLine
from ..product.models import Product
from ..account.models import Address
from ..account.models import Address, User
from ..order.models import OrderLine, Order
from ..payment.interface import GatewayResponse, PaymentData

Expand Down Expand Up @@ -120,7 +120,7 @@ def preprocess_order_creation(
):
return NotImplemented

def postprocess_order_creation(self, order: "Order", previous_value: Any):
def order_created(self, order: "Order", previous_value: Any):
return NotImplemented

def assign_tax_code_to_object_meta(
Expand All @@ -138,6 +138,24 @@ def get_tax_rate_percentage_value(
) -> Decimal:
return NotImplemented

def customer_created(self, customer: "User", previous_value: Any) -> Any:
return NotImplemented

def product_created(self, product: "Product", previous_value: Any) -> Any:
return NotImplemented

def order_fully_paid(self, order: "Order", previous_value: Any) -> Any:
return NotImplemented

def order_updated(self, order: "Order", previous_value: Any) -> Any:
return NotImplemented

def order_cancelled(self, order: "Order", previous_value: Any) -> Any:
return NotImplemented

def order_fulfilled(self, order: "Order", previous_value: Any) -> Any:
return NotImplemented

def authorize_payment(
self, payment_information: "PaymentData", previous_value
) -> "GatewayResponse":
Expand Down
32 changes: 27 additions & 5 deletions saleor/extensions/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .base_plugin import BasePlugin
from ..checkout.models import Checkout, CheckoutLine
from ..product.models import Product
from ..account.models import Address
from ..account.models import Address, User
from ..order.models import OrderLine, Order
from ..payment.interface import PaymentData, GatewayResponse, TokenConfig

Expand Down Expand Up @@ -174,11 +174,33 @@ def preprocess_order_creation(
"preprocess_order_creation", default_value, checkout, discounts
)

def postprocess_order_creation(self, order: "Order"):
def customer_created(self, customer: "User"):
default_value = None
return self.__run_method_on_plugins(
"postprocess_order_creation", default_value, order
)
return self.__run_method_on_plugins("customer_created", default_value, customer)

def product_created(self, product: "Product"):
default_value = None
return self.__run_method_on_plugins("product_created", default_value, product)

def order_created(self, order: "Order"):
default_value = None
return self.__run_method_on_plugins("order_created", default_value, order)

def order_fully_paid(self, order: "Order"):
default_value = None
return self.__run_method_on_plugins("order_fully_paid", default_value, order)

def order_updated(self, order: "Order"):
default_value = None
return self.__run_method_on_plugins("order_updated", default_value, order)

def order_cancelled(self, order: "Order"):
default_value = None
return self.__run_method_on_plugins("order_cancelled", default_value, order)

def order_fulfilled(self, order: "Order"):
default_value = None
return self.__run_method_on_plugins("order_fulfilled", default_value, order)

def authorize_payment(
self, gateway: Gateway, payment_information: "PaymentData"
Expand Down
2 changes: 1 addition & 1 deletion saleor/extensions/plugins/avatax/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def preprocess_order_creation(
raise TaxError(customer_msg)
return previous_value

def postprocess_order_creation(self, order: "Order", previous_value: Any) -> Any:
def order_created(self, order: "Order", previous_value: Any) -> Any:
self._initialize_plugin_configuration()

if not self.active:
Expand Down
5 changes: 2 additions & 3 deletions saleor/extensions/plugins/avatax/tasks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from celery import shared_task

from ....celeryconf import app
from . import api_post_request


@shared_task
@app.task
def api_post_request_task(transaction_url, data):
api_post_request(transaction_url, data)
28 changes: 28 additions & 0 deletions saleor/extensions/plugins/webhook/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import hashlib
import hmac
from typing import Optional

from ....site.models import Site


def create_hmac_signature(body: str, secret_key: str, encoding: str):
b_body = bytes(body, encoding)
hash = hmac.new(bytes(secret_key, encoding), b_body, hashlib.sha256)
return hash.hexdigest()


def create_webhook_headers(
event_name: str,
body: Optional[str] = None,
secret_key: Optional[str] = None,
encoding: str = "utf-8",
):
signature_prefix = "sha1="
domain = Site.objects.get_current().domain
headers = {"X-Saleor-Event": event_name, "X-Saleor-Domain": domain}
if secret_key and body:
saleor_hmac_sha256 = signature_prefix + create_hmac_signature(
body, secret_key, encoding
)
headers["X-Saleor-HMAC-SHA256"] = saleor_hmac_sha256
return headers

0 comments on commit 7478b7c

Please sign in to comment.