~sircmpwn/meta.sr.ht

ref: e0be9dcd8e96f952547c243e078c47571f54fca2 meta.sr.ht/metasrht/billing.py -rw-r--r-- 2.9 KiB
e0be9dcdDrew DeVault billing.py: fix leap day error 1 year, 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import stripe
from datetime import datetime, timedelta
from srht.config import cfg
from srht.database import db
from metasrht.audit import audit_log
from metasrht.types import User, UserType, PaymentInterval, Invoice
from enum import Enum

stripe.api_key = cfg("meta.sr.ht::billing", "stripe-secret-key")

class ChargeResult(Enum):
    success = "success"
    failed = "failed"
    cancelled = "cancelled"
    delinquent = "delinquent"
    account_current = "account_current"

def charge_user(user):
    if user.user_type == UserType.active_free:
        return ChargeResult.account_current, "Your account is exempt from payment."
    if user.payment_due >= datetime.utcnow():
        return ChargeResult.account_current, "Your account is current."
    desc = f"{cfg('sr.ht', 'site-name')} {user.payment_interval.value} payment"
    if user.payment_cents == 0:
        # They cancelled their payment and their current term is up
        user.user_type = UserType.active_non_paying
        return ChargeResult.cancelled, "Your paid service has been cancelled."
    try:
        amount = user.payment_cents
        if user.payment_interval == PaymentInterval.yearly:
            amount = amount * 10 # Apply yearly discount
        # TODO: Multiple currencies
        charge = stripe.Charge.create(
            amount=amount,
            currency="usd",
            customer=user.stripe_customer,
            description=desc)
        audit_log("billing",
                details="charged ${:.2f}".format(amount / 100))
    except stripe.error.CardError as e:
        details = e.json_body["error"]["message"]
        if user.user_type == UserType.active_delinquent:
            # Don't email them twice
            return ChargeResult.delinquent, "Your account payment is delinquent"
        user.user_type = UserType.active_delinquent
        return ChargeResult.failed, details
    except:
        return ChargeResult.failed, "Your payment failed. Contact support."
    invoice = Invoice()
    invoice.cents = amount
    invoice.user_id = user.id
    try:
        invoice.source = f"{charge.source.brand} ending in {charge.source.last4}"
    except:
        # Not a credit card? dunno how this works
        invoice.source = charge.source.stripe_id
    db.session.add(invoice)
    if user.payment_interval == PaymentInterval.monthly:
        invoice.valid_thru = datetime.utcnow() + timedelta(days=30)
        user.payment_due = invoice.valid_thru
    else:
        invoice.valid_thru = datetime.utcnow()
        leapday = (invoice.valid_thru.month == 2
                and invoice.valid_thru.day == 29)
        invoice.valid_thru = datetime(year=invoice.valid_thru.year + 1,
                month=invoice.valid_thru.month, day=(invoice.valid_thru.day - 1
                    if leapday else invoice.valid_thru.day))
        user.payment_due = invoice.valid_thru
    user.user_type = UserType.active_paying
    db.session.commit()
    return ChargeResult.success, "Your card was successfully charged. Thank you!"