diff --git a/stripe_checkout/stripe_checkout/utils.py b/stripe_checkout/stripe_checkout/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd58c721426a2f67f4f9fde3e9a31adfb06d0508
--- /dev/null
+++ b/stripe_checkout/stripe_checkout/utils.py
@@ -0,0 +1,39 @@
+import functools
+
+from django.conf import settings
+from django.http import HttpResponseForbidden
+
+ALLOW_ALL = "*"
+
+
+def whitelist_ips(func=None, by_setting=None):
+    if not func:
+        return functools.partial(whitelist_ips, by_setting=by_setting)
+
+    @functools.wraps(func)
+    def _whitelisted_view(request):
+        allowed_ips = _read_whitelist_setting(by_setting, [ALLOW_ALL])
+        if not allowed_ips & {ALLOW_ALL, get_client_ip(request)}:
+
+            return HttpResponseForbidden("forbidden")
+        return func(request)
+
+    return _whitelisted_view
+
+
+def _read_whitelist_setting(setting: str, default=None) -> set[str]:
+    result = getattr(settings, setting, default)
+    assert isinstance(result, list) and all(
+        isinstance(s, str) for s in result
+    ), f"{setting} must be a list of strings"
+    return set(result)
+
+
+def get_client_ip(request):
+    # cf. https://stackoverflow.com/a/4581997
+    x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
+    if x_forwarded_for:
+        ip = x_forwarded_for.split(",")[0]
+    else:
+        ip = request.META.get("REMOTE_ADDR")
+    return ip