Skip to content

Commit

Permalink
Merge pull request #205 from commonknowledge/integrations
Browse files Browse the repository at this point in the history
Integrations
  • Loading branch information
janbaykara authored May 9, 2022
2 parents 21cf301 + 91989fe commit b6bdfdd
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 48 deletions.
12 changes: 11 additions & 1 deletion app/models/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def subscription_status(self):
return None

@property
def subscribed_product(self) -> LBCProduct:
def primary_product(self) -> LBCProduct:
try:
if self.active_subscription is not None:
product = get_primary_product_for_djstripe_subscription(
Expand All @@ -119,6 +119,16 @@ def gifts_bought(self):
.order_by("-created")
)

@property
def gift_giver(self):
try:
gift_giver_subscription = self.active_subscription.gift_giver_subscription
sub = djstripe.models.Subscription.objects.get(id=gift_giver_subscription)
user = sub.customer.subscriber
return user
except:
return None

def __str__(self) -> str:
fn = self.get_full_name()
if fn is not None and len(fn):
Expand Down
4 changes: 1 addition & 3 deletions app/models/wagtail.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def to_price_data(self, product):
"interval": self.interval,
"interval_count": self.interval_count,
},
"metadata": self.metadata,
"metadata": {**(self.metadata or {}), "primary": True},
}

def to_shipping_price_data(self, zone):
Expand Down Expand Up @@ -282,14 +282,12 @@ def to_checkout_line_items(self, zone=None, product=None):
{
"price_data": self.to_price_data(product),
"quantity": 1,
"metadata": {"primary": True},
},
# Keep the shipping fee in, even if it's 0
# so that we can upgrade/downgrade shipping prices in the future
{
"price_data": self.to_shipping_price_data(zone),
"quantity": 1,
"metadata": {"shipping": True},
},
]
return line_items
Expand Down
5 changes: 5 additions & 0 deletions app/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,8 @@

# Facebook
FACEBOOK_PIXEL = os.getenv("FACEBOOK_PIXEL", None)

# Mailchimp
MAILCHIMP_API_KEY = os.getenv("MAILCHIMP_API_KEY", None)
MAILCHIMP_SERVER_PREFIX = os.getenv("MAILCHIMP_SERVER_PREFIX", "us12")
MAILCHIMP_LIST_ID = os.getenv("MAILCHIMP_LIST_ID", "327021")
9 changes: 9 additions & 0 deletions app/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from app import analytics
from app.models.wagtail import BookPage
from app.utils.mailchimp import tag_user_in_mailchimp
from app.utils.stripe import gift_recipient_subscription_from_code


Expand All @@ -27,6 +28,9 @@ def cancel_gift_recipient_subscription(event, **kwargs):
customer = Customer.objects.filter(id=object.get("customer")).first()
if customer is not None and customer.subscriber is not None:
analytics.cancel_gift_card(customer.subscriber)
tag_user_in_mailchimp(
customer.subscriber, tags_to_enable=["CANCELLED_GIFT_GIVER"]
)
except Exception as e:
capture_exception(e)
pass
Expand All @@ -36,6 +40,11 @@ def cancel_gift_recipient_subscription(event, **kwargs):
customer = Customer.objects.filter(id=object.get("customer")).first()
if customer is not None and customer.subscriber is not None:
analytics.cancel_membership(customer.subscriber)
tag_user_in_mailchimp(
customer.subscriber,
tags_to_enable=["CANCELLED"],
tags_to_disable=["MEMBER"],
)
except Exception as e:
capture_exception(e)
pass
Expand Down
2 changes: 1 addition & 1 deletion app/templates/app/confirm_shipping.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ <h2 class="h3 fw-normal text-center">Confirm{% if gift_mode %} gift{% endif %} d
<div class="form-check max-width-card w-100">
<input required class="form-check-input" type="checkbox" value="" id='confirm_cancel_current_subscriptions' name='confirm_cancel_current_subscriptions'>
<label class="form-check-label" for="confirm_cancel_current_subscriptions">
<p>I confirm that my existing subscription(s) will be <u>immediately cancelled</u> and replaced with this new subscription: <i>{{ user.subscribed_product.name }}</i></p>
<p>I confirm that my existing subscription(s) will be <u>immediately cancelled</u> and replaced with this new subscription: <i>{{ user.primary_product.name }}</i></p>
</label>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions app/templates/app/frames/shipping_cost.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
<span>{% money_localize final_price %}<small>{{ price.humanised_interval }}</small></span>
</div>
</div>
{% if request.GET.gift_mode %}
<p class='form-text text-black mt-1'>On the next page, <u><b>please add your own shipping address</b></u>, in case we need to send you any gift card materials.</p>
<p class='form-text'>Your gift recipient will be able to enter their own address when they redeem.</p>
{% endif %}
{% bootstrap_button button_type="submit" content="Continue to payment" button_class='w-100 btn-primary my-2' %}
{% endif %}
</turbo-frame>
6 changes: 3 additions & 3 deletions app/templates/app/includes/membership_card.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
<div class='col-10'>
<div class='text-left'>
<div class='text-muted'>Membership No. #{{ user.stripe_customer.invoice_prefix }}</div>
<div class='fw-bold'>{{ user.subscribed_product.name}}</div>
<div class='fw-bold'>{{ user.primary_product.name}}</div>
{% comment %} <div>{{user.subscribed_price.human_readable_price}}</div> {% endcomment %}
{% if user.subscribed_product.gift_giver_subscription %}
<div>Gifted by {{user.subscribed_product.gift_giver_subscription.customer.subscriber}}</div>
{% if user.primary_product.gift_giver_subscription %}
<div>Gifted by {{user.gift_giver}}</div>
{% endif %}
<div class='text-muted'>Since {{ user.active_subscription.created|date:"d M Y" }}</div>
{% if user.active_subscription.cancel_at %}
Expand Down
2 changes: 1 addition & 1 deletion app/templates/app/redeem.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h1>Someone bought you a gift!</h1>
<div class="form-check max-width-card w-100">
<input required class="form-check-input" type="checkbox" value="" id='confirm_cancel_current_subscriptions' name='confirm_cancel_current_subscriptions'>
<label class="form-check-label" for="confirm_cancel_current_subscriptions">
<p>I confirm that my existing subscription(s) will be <u>immediately cancelled</u> and replaced with this new, gifted subscription: <i>{{ user.subscribed_product.name }}</i></p>
<p>I confirm that my existing subscription(s) will be <u>immediately cancelled</u> and replaced with this new, gifted subscription: <i>{{ user.primary_product.name }}</i></p>
</label>
</div>
</div>
Expand Down
57 changes: 57 additions & 0 deletions app/utils/mailchimp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import hashlib

import mailchimp_marketing as MailchimpMarketing
from django.conf import settings
from mailchimp_marketing.api_client import ApiClientError as MailchimpApiClientError

from app.models import User

mailchimp = MailchimpMarketing.Client()
MAILCHIMP_IS_ACTIVE = (
settings.MAILCHIMP_API_KEY is not None
and settings.MAILCHIMP_SERVER_PREFIX is not None
and settings.MAILCHIMP_LIST_ID is not None
)
if MAILCHIMP_IS_ACTIVE:
mailchimp.set_config(settings.MAILCHIMP_API_KEY, settings.MAILCHIMP_SERVER_PREFIX)


def email_to_hash(email):
return hashlib.md5(email.encode("utf-8").lower()).hexdigest()


def mailchimp_contact_for_user(user: User):
if not MAILCHIMP_IS_ACTIVE:
return False
return mailchimp.lists.set_list_member(
settings.MAILCHIMP_LIST_ID,
email_to_hash(user.primary_email),
{
"email_address": user.primary_email,
"merge_fields": {"FNAME": user.first_name, "LNAME": user.last_name},
"status_if_new": "subscribed"
if user.gdpr_email_consent
else "unsubscribed",
},
)


def tag_user_in_mailchimp(user: User, tags_to_enable=list(), tags_to_disable=list()):
tags = [{"name": tag, "status": "active"} for tag in tags_to_enable] + [
{"name": tag, "status": "inactive"} for tag in tags_to_disable
]
if not MAILCHIMP_IS_ACTIVE:
print("tag_user_in_mailchimp", tags)
return
contact = mailchimp_contact_for_user(user)
if contact is None:
return
try:
response = mailchimp.lists.update_list_member_tags(
settings.MAILCHIMP_LIST_ID,
email_to_hash(user.primary_email),
{"tags": tags},
)
print(f"client.lists.update_list_member_tags() response: {response}")
except MailchimpApiClientError as error:
print(f"An exception occurred: {error.text}")
94 changes: 94 additions & 0 deletions app/utils/shopify.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json

import pycountry
import shopify
from dateutil.parser import parse
from django.conf import settings
Expand Down Expand Up @@ -39,3 +40,96 @@ def parse_metafield(f):
return json.loads(f.value)
else:
return f.value


def convert_stripe_address_to_shopify(user):
shipping = user.stripe_customer.shipping
address = {
"first_name": user.first_name,
"last_name": user.last_name,
"address1": shipping.get("address").get("line1"),
"address2": shipping.get("address").get("line2"),
"city": shipping.get("address").get("city"),
"country": pycountry.countries.search_fuzzy(
shipping.get("address").get("country")
)[0].name,
"zip": shipping.get("address").get("postal_code"),
"country_code": shipping.get("address").get("country"),
}

ret = {}
for k in address:
if address.get(k, None) is not None and address.get(k, "") != "":
ret[k] = address[k]

if len(ret.keys()) == 0:
return None

return ret


def to_shopify_address(user):
if (
user.stripe_customer
and user.stripe_customer.shipping is not None
and user.stripe_customer.shipping.get("address", {}).get("line1", False)
):
return convert_stripe_address_to_shopify(user)
return None


def create_shopify_order(user, line_items=list(), email=False, tags=list()):
if not settings.SHOPIFY_DOMAIN or settings.SHOPIFY_PRIVATE_APP_PASSWORD:
with shopify.Session.temp(
settings.SHOPIFY_DOMAIN, "2021-10", settings.SHOPIFY_PRIVATE_APP_PASSWORD
):
o = shopify.Order()
o.line_items = line_items
# [
# {
# # "variant_id": variant_id,
# "title": "New Signup",
# "price": 0,
# "requiresShipping": True,
# "quantity": quantity,
# }
# ]
o.financial_status = "paid"

# Shopify customer link
# if user.shopify_customer_id is None:
# cs = shopify.Customer.search(email=o.email)
# if len(cs) > 0:
# user.shopify_customer_id = cs[0].id
# user.save()
# else:
# c = shopify.Customer()
# c.first_name = "andres"
# c.last_name = "cepeda"
# if to_shopify_address(user) is not None:
# c.addresses = [to_shopify_address(user)]
# c.default_address = to_shopify_address(user)
# c.save()
# user.shopify_customer_id = c.id
# user.save()
# if user.shopify_customer_id is None:
# raise ValueError("Couldn't create shipping order, because customer couldn't be identified")

# o.customer = { "id": user.shopify_customer_id }
if not email:
o.send_receipt = False
o.send_fulfillment_receipt = False
o.note = f"Email: {user.primary_email}. Stripe customer: {user.stripe_customer.id}."
else:
o.email = user.primary_email

o.shipping_address = to_shopify_address(user)
o.tags = tags

if not settings.STRIPE_LIVE_MODE:
tags += ["TEST"]

o.save()

if not settings.STRIPE_LIVE_MODE:
o.cancel()
7 changes: 5 additions & 2 deletions app/utils/stripe.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def get_gift_card_coupon(product: djstripe.models.Product) -> djstripe.models.Co
return coupon


def recreate_one_off_stripe_price(price: stripe.Price):
def recreate_one_off_stripe_price(price: stripe.Price, **kwargs):
return {
"unit_amount_decimal": price.unit_amount,
"currency": price.currency,
Expand All @@ -146,6 +146,7 @@ def recreate_one_off_stripe_price(price: stripe.Price):
"interval_count",
),
),
**kwargs,
}


Expand Down Expand Up @@ -255,7 +256,9 @@ def create_gift_recipient_subscription(
items.append(
{
# Membership
"price_data": recreate_one_off_stripe_price(si.price),
"price_data": recreate_one_off_stripe_price(
si.price, metadata={"primary": True}
),
"quantity": si.quantity,
}
)
Expand Down
Loading

0 comments on commit b6bdfdd

Please sign in to comment.