def add_blast_subscription(form=None, customer=None): """ Adds a Blast subscription. Blast subscriptions are always recurring. They have two email addresses: one for billing and one for the newsletter subscription. """ form = clean(form) first_name = form["first_name"] last_name = form["last_name"] email = form["subscriber_email"] logging.info("----Getting contact...") contact = Contact.get_or_create(email=email, first_name=first_name, last_name=last_name) logging.info(contact) rdo = RDO(contact=contact) rdo.stripe_customer = customer["id"] rdo.campaign_id = form["campaign_id"] rdo.referral_id = form["referral_id"] rdo.lead_source = "Stripe" rdo.amount = form.get("amount", 0) rdo.agreed_to_pay_fees = form["pay_fees_value"] # Blast specific: rdo.installments = 0 rdo.description = "Blast Subscription" rdo.open_ended_status = "Open" if int(float(rdo.amount)) == 40: rdo.installment_period = "monthly" else: rdo.installment_period = "yearly" now = datetime.now(tz=ZONE).strftime("%Y-%m-%d %I:%M:%S %p %Z") rdo.name = f"{first_name} {last_name} - {now} - The Blast" rdo.type = "The Blast" rdo.billing_email = form["stripeEmail"] rdo.blast_subscription_email = form["subscriber_email"] logging.info("----Saving RDO....") apply_card_details(rdo=rdo, customer=customer) rdo.save() logging.info(rdo) # get opportunities opportunities = rdo.opportunities() today = datetime.now(tz=ZONE).strftime("%Y-%m-%d") opp = [ opportunity for opportunity in opportunities if opportunity.expected_giving_date == today ][0] try: charge(opp) except ChargeException: # TODO should we alert slack? Did not because we had no notifications here before. pass return True
def charge_cards(): lock = Lock(key="charge-cards-lock") lock.acquire() log = Log() log.it("---Starting batch card job...") three_days_ago = (datetime.now(tz=zone) - timedelta(days=14)).strftime("%Y-%m-%d") today = datetime.now(tz=zone).strftime("%Y-%m-%d") opportunities = Opportunity.list(begin=three_days_ago, end=today) log.it("---Processing charges...") log.it(f"Found {len(opportunities)} opportunities available to process.") for opportunity in opportunities: if not opportunity.stripe_customer_id: continue amount = amount_to_charge(opportunity) log.it( f"---- Charging ${amount} to {opportunity.stripe_customer_id} ({opportunity.name})" ) try: charge(opportunity) except ChargeException as e: logging.info("Batch charge error") e.send_slack_notification() log.send() lock.release()
def update_ach_charges(): lock = Lock(key='update-ach-charges-lock') lock.acquire() log = Log() log.it('---Starting batch ach job...') log.it('---Checking for status changes on ACH charges...') three_days_ago = (datetime.now(tz=zone) - timedelta(days=3)).strftime("%Y-%m-%d") today = datetime.now(tz=zone).strftime("%Y-%m-%d") opportunities = Opportunity.list(begin=three_days_ago, end=today, stage_name="ACH Pending") for opportunity in opportunities: if not opportunity.stripe_customer_id: continue amount = amount_to_charge(opportunity) log.it( f"---- ACH Charging ${amount} to {opportunity.stripe_customer_id} ({opportunity.name})" ) try: charge(opportunity) except ChargeException as e: logging.info("ACH batch charge error") e.send_slack_notification() log.send() lock.release()
def charge_cards(): lock = Lock(key="charge-cards-lock") lock.acquire() log = Log() log.it("---Starting batch job...") three_days_ago = (datetime.now(tz=zone) - timedelta(days=3)).strftime("%Y-%m-%d") today = datetime.now(tz=zone).strftime("%Y-%m-%d") opportunities = Opportunity.list(begin=three_days_ago, end=today) log.it("---Processing charges...") log.it(f"Found {len(opportunities)} opportunities available to process.") for opportunity in opportunities: if not opportunity.stripe_customer: continue amount = amount_to_charge(opportunity) log.it( f"---- Charging ${amount} to {opportunity.stripe_customer} ({opportunity.name})" ) charge(opportunity) log.send() lock.release()
def charge_cards(): lock = Lock(key="charge-cards-lock") lock.acquire() log = Log() log.it("---Starting batch job...") three_days_ago = (datetime.now(tz=zone) - timedelta(days=14)).strftime("%Y-%m-%d") today = datetime.now(tz=zone).strftime("%Y-%m-%d") opportunities = Opportunity.list(begin=three_days_ago, end=today) log.it("---Processing charges...") processing_msg = f"Found {len(opportunities)} opportunities available to process." log.it(processing_msg) send_slack_message( { "channel": "#stripe", "text": processing_msg, "icon_emoji": ":moneybag:", } ) for opportunity in opportunities: if not opportunity.stripe_customer: continue amount = amount_to_charge(opportunity) try: entry_name = opportunity.name # replaces non-ascii characters with "?" - See PR #851 encoded_name = entry_name.encode("ascii", "replace") decoded_name = encoded_name.decode("ascii") log.it( f"---- Charging ${amount} to {opportunity.stripe_customer} ({decoded_name})" ) except: log.it( f"---- Charging ${amount} to {opportunity.stripe_customer} ({opportunity.name})" ) logging.warn(f"Could not encode {opportunity.name}") try: charge(opportunity) except ChargeException as e: logging.info("Batch charge error") e.send_slack_notification() except QuarantinedException: logging.info( "Failed to charge because Opportunity %s is quarantined", opportunity ) log.send() lock.release()
def add_donation(form=None, customer=None, donation_type=None, bad_actor_request=None): """ Add a contact and their donation into SF. This is done in the background because there are a lot of API calls and there's no point in making the payer wait for them. It sends a notification about the donation to Slack (if configured). """ bad_actor_response = BadActor(bad_actor_request=bad_actor_request) quarantine = bad_actor_response.quarantine form = clean(form) first_name = form["first_name"] last_name = form["last_name"] period = form["installment_period"] email = form["stripeEmail"] zipcode = form["zipcode"] logging.info("----Getting contact....") contact = Contact.get_or_create(email=email, first_name=first_name, last_name=last_name, zipcode=zipcode) logging.info(contact) if contact.first_name == "Subscriber" and contact.last_name == "Subscriber": logging.info(f"Changing name of contact to {first_name} {last_name}") contact.first_name = first_name contact.last_name = last_name contact.mailing_postal_code = zipcode contact.save() if contact.first_name != first_name or contact.last_name != last_name: logging.info( f"Contact name doesn't match: {contact.first_name} {contact.last_name}" ) if zipcode and not contact.created and contact.mailing_postal_code != zipcode: contact.mailing_postal_code = zipcode contact.save() if contact.duplicate_found: send_multiple_account_warning(contact) if period is None: logging.info("----Creating one time payment...") opportunity = add_opportunity(contact=contact, form=form, customer=customer, quarantine=quarantine) try: charge(opportunity) logging.info(opportunity) notify_slack(contact=contact, opportunity=opportunity) except ChargeException as e: e.send_slack_notification() except QuarantinedException: bad_actor_response.notify_bad_actor(transaction_type="Opportunity", transaction=opportunity) return True elif donation_type == "circle": logging.info("----Creating circle payment...") rdo = add_circle_membership(contact=contact, form=form, customer=customer, quarantine=False) else: logging.info("----Creating recurring payment...") rdo = add_recurring_donation(contact=contact, form=form, customer=customer, quarantine=quarantine) # get opportunities opportunities = rdo.opportunities() today = datetime.now(tz=ZONE).strftime("%Y-%m-%d") opp = [ opportunity for opportunity in opportunities if opportunity.expected_giving_date == today ][0] try: charge(opp) logging.info(rdo) notify_slack(contact=contact, rdo=rdo) except ChargeException as e: e.send_slack_notification() except QuarantinedException: bad_actor_response.notify_bad_actor(transaction_type="RDO", transaction=rdo) return True
def add_business_membership( form=None, customer=None, donation_type="business_membership", bad_actor_request=None, ): """ Adds a business membership. Both single and recurring. It will look for a matching Contact (or create one). Then it will look for a matching Account (or create one). Then it will add the single or recurring donation to the Account. Then it will add an Affiliation to link the Contact with the Account. It sends a notification to Slack (if configured). It will send email notification about the new membership. """ form = clean(form) first_name = form["first_name"] last_name = form["last_name"] email = form["stripeEmail"] website = form["website"] business_name = form["business_name"] shipping_city = form["shipping_city"] shipping_street = form["shipping_street"] shipping_state = form["shipping_state"] shipping_postalcode = form["shipping_postalcode"] bad_actor_response = BadActor(bad_actor_request=bad_actor_request) quarantine = bad_actor_response.quarantine logging.info("----Getting contact....") contact = Contact.get_or_create(email=email, first_name=first_name, last_name=last_name) if contact.work_email is None: contact.work_email = email contact.save() logging.info(contact) if contact.first_name == "Subscriber" and contact.last_name == "Subscriber": logging.info(f"Changing name of contact to {first_name} {last_name}") contact.first_name = first_name contact.last_name = last_name contact.save() if contact.first_name != first_name or contact.last_name != last_name: logging.info( f"Contact name doesn't match: {contact.first_name} {contact.last_name}" ) logging.info("----Getting account....") account = Account.get_or_create( record_type_name="Organization", website=website, name=business_name, shipping_street=shipping_street, shipping_city=shipping_city, shipping_state=shipping_state, shipping_postalcode=shipping_postalcode, ) logging.info(account) if form["installment_period"] not in ["yearly", "monthly"]: raise Exception("Business membership must be either yearly or monthly") logging.info("----Creating recurring business membership...") rdo = add_business_rdo(account=account, form=form, customer=customer, quarantine=False) logging.info(rdo) logging.info("----Getting affiliation...") affiliation = Affiliation.get_or_create(account=account, contact=contact, role="Business Member Donor") logging.info(affiliation) send_email_new_business_membership(account=account, contact=contact) # get opportunities opportunities = rdo.opportunities() today = datetime.now(tz=ZONE).strftime("%Y-%m-%d") opp = [ opportunity for opportunity in opportunities if opportunity.expected_giving_date == today ][0] try: charge(opp) notify_slack(account=account, contact=contact, rdo=rdo) except ChargeException as e: e.send_slack_notification() except QuarantinedException: bad_actor_response.notify_bad_actor(transaction_type="RDO", transaction=rdo) if contact.duplicate_found: send_multiple_account_warning(contact) return True
def add_donation(form=None, customer=None): """ Add a contact and their donation into SF. This is done in the background because there are a lot of API calls and there's no point in making the payer wait for them. It sends a notification about the donation to Slack (if configured). """ form = clean(form) first_name = form["first_name"] last_name = form["last_name"] period = form["installment_period"] email = form["stripeEmail"] zipcode = form["zipcode"] logging.info("----Getting contact....") contact = Contact.get_or_create(email=email, first_name=first_name, last_name=last_name, zipcode=zipcode) logging.info(contact) if contact.first_name == "Subscriber" and contact.last_name == "Subscriber": logging.info(f"Changing name of contact to {first_name} {last_name}") contact.first_name = first_name contact.last_name = last_name contact.mailing_postal_code = zipcode contact.save() if contact.first_name != first_name or contact.last_name != last_name: logging.info( f"Contact name doesn't match: {contact.first_name} {contact.last_name}" ) if zipcode and not contact.created and contact.mailing_postal_code != zipcode: contact.mailing_postal_code = zipcode contact.save() if period is None: logging.info("----Creating one time payment...") opportunity = add_opportunity(contact=contact, form=form, customer=customer) charge(opportunity) logging.info(opportunity) notify_slack(contact=contact, opportunity=opportunity) else: logging.info("----Creating recurring payment...") rdo = add_recurring_donation(contact=contact, form=form, customer=customer) # get opportunities opportunities = rdo.opportunities() today = datetime.now(tz=ZONE).strftime("%Y-%m-%d") opp = [ opportunity for opportunity in opportunities if opportunity.expected_giving_date == today ][0] charge(opp) logging.info(rdo) notify_slack(contact=contact, rdo=rdo) if contact.duplicate_found: send_multiple_account_warning(contact) return True