def test_canceled_scheduled_payin(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') carl = self.make_participant('carl') alice.set_tip_to(bob, EUR('1.00'), renewal_mode=2) alice_card = self.upsert_route(alice, 'stripe-card', address='pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('52.00')) alice.set_tip_to(carl, EUR('1.00'), renewal_mode=2) self.make_payin_and_transfer(alice_card, carl, EUR('52.00')) scheduled_payins = self.db.all("SELECT * FROM scheduled_payins ORDER BY id") assert len(scheduled_payins) == 1 assert scheduled_payins[0].amount == EUR('104.00') assert scheduled_payins[0].automatic is True self.db.run("UPDATE tips SET renewal_mode = 0 WHERE tippee = %s", (bob.id,)) self.db.run("UPDATE tips SET renewal_mode = 1 WHERE tippee = %s", (carl.id,)) self.db.run(""" UPDATE scheduled_payins SET execution_date = (current_date + interval '14 days') , ctime = (ctime - interval '12 hours') """) send_upcoming_debit_notifications() send_donation_reminder_notifications() emails = self.get_emails() assert len(emails) == 0 scheduled_payins = self.db.all("SELECT * FROM scheduled_payins ORDER BY id") assert len(scheduled_payins) == 0
def test_scheduled_payin_requiring_authentication(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') alice.set_tip_to(bob, EUR('4.30'), renewal_mode=2) alice_card = self.attach_stripe_payment_method( alice, 'pm_card_threeDSecureRequired') self.make_payin_and_transfer(alice_card, bob, EUR('43.00')) scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY id") assert len(scheduled_payins) == 1 assert scheduled_payins[0].amount == EUR('43.00') self.db.run(""" UPDATE scheduled_payins SET execution_date = (current_date + interval '7 days') , ctime = (ctime - interval '12 hours') """) send_upcoming_debit_notifications() emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0][ 'subject'] == 'Liberapay donation renewal: upcoming debit of €43.00' send_donation_reminder_notifications() emails = self.get_emails() assert len(emails) == 0 self.db.run(""" UPDATE scheduled_payins SET execution_date = current_date , last_notif_ts = (last_notif_ts - interval '14 days') , ctime = (ctime - interval '12 hours') """) execute_scheduled_payins() payins = self.db.all("SELECT * FROM payins ORDER BY ctime") assert len(payins) == 2 assert payins[1].payer == alice.id assert payins[1].amount == EUR('43.00') assert payins[1].off_session is True assert payins[1].status == 'awaiting_payer_action' emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0][ 'subject'] == 'Liberapay donation renewal: authentication required' payin_page_path = f'/alice/giving/pay/stripe/{payins[1].id}' assert payin_page_path in emails[0]['text'] scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY id") assert len(scheduled_payins) == 1 assert scheduled_payins[0].payin == payins[1].id # Test the payin page, it should redirect to the 3DSecure page r = self.client.GET(payin_page_path, auth_as=alice, raise_immediately=False) assert r.code == 200 assert r.headers[b'Refresh'].startswith( b'0;url=https://hooks.stripe.com/')
def test_no_scheduled_payins(self): self.make_participant('alice') send_upcoming_debit_notifications() execute_scheduled_payins() send_donation_reminder_notifications() notifs = self.db.all("SELECT * FROM notifications") assert not notifs payins = self.db.all("SELECT * FROM payins") assert not payins
def test_canceled_and_impossible_transfers(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') carl = self.make_participant('carl') dana = self.make_participant('dana') alice.set_tip_to(bob, EUR('4.00'), renewal_mode=2) alice_card = self.upsert_route(alice, 'stripe-card', address='pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('16.00')) alice.set_tip_to(carl, EUR('0.02'), renewal_mode=2) self.make_payin_and_transfer(alice_card, carl, EUR('12.00')) alice.set_tip_to(dana, EUR('5.00'), renewal_mode=2) self.make_payin_and_transfer(alice_card, dana, EUR('20.00')) scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY execution_date" ) assert len(scheduled_payins) == 2 assert scheduled_payins[0].amount == EUR('36.00') assert scheduled_payins[1].amount == EUR('12.00') bob.close(None) self.db.run("UPDATE participants SET is_suspended = true WHERE username = '******'") self.db.run("UPDATE tips SET renewal_mode = 1 WHERE tippee = %s", (dana.id,)) self.db.run(""" UPDATE scheduled_payins SET execution_date = (current_date + interval '14 days') , ctime = (ctime - interval '12 hours') """) send_upcoming_debit_notifications() emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0]['subject'] == 'Liberapay donation renewal: upcoming debits totaling €28.00' send_donation_reminder_notifications() emails = self.get_emails() assert len(emails) == 0 self.db.run(""" UPDATE scheduled_payins SET execution_date = current_date , last_notif_ts = (last_notif_ts - interval '14 days') , ctime = (ctime - interval '12 hours') """) execute_scheduled_payins() payins = self.db.all("SELECT * FROM payins ORDER BY ctime") assert len(payins) == 3 emails = self.get_emails() assert len(emails) == 2 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0]['subject'] == 'Liberapay donation renewal: payment aborted' assert emails[1]['to'] == ['alice <*****@*****.**>'] assert emails[1]['subject'] == 'Liberapay donation renewal: payment aborted'
def test_one_scheduled_payin(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') alice.set_tip_to(bob, EUR('3.00'), renewal_mode=2) alice_card = self.attach_stripe_payment_method(alice, 'pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('12.00')) scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY id") assert len(scheduled_payins) == 1 assert scheduled_payins[0].amount == EUR('12.00') assert scheduled_payins[0].payin is None self.db.run(""" UPDATE scheduled_payins SET execution_date = (current_date + interval '14 days') , ctime = (ctime - interval '12 hours') """) send_upcoming_debit_notifications() emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0][ 'subject'] == 'Liberapay donation renewal: upcoming debit of €12.00' send_donation_reminder_notifications() emails = self.get_emails() assert len(emails) == 0 self.db.run(""" UPDATE scheduled_payins SET execution_date = current_date , last_notif_ts = (last_notif_ts - interval '14 days') """) execute_scheduled_payins() payins = self.db.all("SELECT * FROM payins ORDER BY ctime") assert len(payins) == 2 assert payins[0].payer == alice.id assert payins[0].amount == EUR('12.00') assert payins[0].off_session is False assert payins[0].status == 'succeeded' assert payins[1].payer == alice.id assert payins[1].amount == EUR('12.00') assert payins[1].off_session is True assert payins[1].status == 'succeeded' emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0]['subject'] == "Your payment has succeeded" scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY execution_date") assert len(scheduled_payins) == 2 assert scheduled_payins[0].payin == payins[1].id assert scheduled_payins[1].payin is None
def test_missing_route(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') alice.set_tip_to(bob, EUR('3.00'), renewal_mode=2) alice_card = self.upsert_route(alice, 'stripe-card', address='pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('30.00')) scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY id") assert len(scheduled_payins) == 1 assert scheduled_payins[0].amount == EUR('30.00') alice_card.update_status('canceled') self.db.run(""" UPDATE scheduled_payins SET execution_date = (current_date + interval '14 days') , ctime = (ctime - interval '12 hours') """) send_donation_reminder_notifications() send_upcoming_debit_notifications() emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0][ 'subject'] == 'Liberapay donation renewal: no valid payment instrument' self.db.run(""" UPDATE scheduled_payins SET execution_date = current_date , last_notif_ts = (last_notif_ts - interval '14 days') """) execute_scheduled_payins() payins = self.db.all("SELECT * FROM payins ORDER BY ctime") assert len(payins) == 1 assert payins[0].payer == alice.id assert payins[0].amount == EUR('30.00') assert payins[0].off_session is False emails = self.get_emails() assert len(emails) == 0
def test_scheduled_payin_suspended_payer(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') alice.set_tip_to(bob, EUR('4.30'), renewal_mode=2) alice_card = self.upsert_route(alice, 'stripe-card', address='pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('43.00')) scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY id") assert len(scheduled_payins) == 1 assert scheduled_payins[0].amount == EUR('43.00') self.db.run( "UPDATE participants SET is_suspended = true WHERE username = '******'" ) self.db.run(""" UPDATE scheduled_payins SET execution_date = (current_date + interval '14 days') , ctime = (ctime - interval '12 hours') """) send_donation_reminder_notifications() send_upcoming_debit_notifications() emails = self.get_emails() assert len(emails) == 0 self.db.run(""" UPDATE scheduled_payins SET execution_date = current_date , last_notif_ts = (last_notif_ts - interval '14 days') , ctime = (ctime - interval '12 hours') """) execute_scheduled_payins() payins = self.db.all("SELECT * FROM payins ORDER BY ctime") assert len(payins) == 1 assert payins[0].payer == alice.id assert payins[0].amount == EUR('43.00') assert payins[0].off_session is False emails = self.get_emails() assert len(emails) == 0
def test_early_manual_renewal_of_automatic_donations(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') carl = self.make_participant('carl') dana = self.make_participant('dana') alice.set_tip_to(bob, EUR('4.10'), renewal_mode=2) alice_card = self.attach_stripe_payment_method(alice, 'pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('16.40')) alice.set_tip_to(carl, EUR('4.20'), renewal_mode=2) self.make_payin_and_transfer(alice_card, carl, EUR('16.80')) alice.set_tip_to(dana, EUR('4.30'), renewal_mode=2) self.make_payin_and_transfer(alice_card, dana, EUR('17.20')) scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY execution_date" ) assert len(scheduled_payins) == 1 assert scheduled_payins[0].amount == EUR('50.40') assert scheduled_payins[0].automatic is True renewal_date = scheduled_payins[0].execution_date manual_renewal_1 = self.make_payin_and_transfer(alice_card, bob, EUR('16.40'))[0] manual_renewal_2 = self.make_payin_and_transfers(alice_card, EUR('34.00'), [ (carl, EUR('16.80'), {}), (dana, EUR('17.20'), {}), ])[0] scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY mtime" ) assert len(scheduled_payins) == 3 assert scheduled_payins[0].payin == manual_renewal_1.id assert scheduled_payins[1].payin == manual_renewal_2.id assert scheduled_payins[2].amount == EUR('50.40') assert scheduled_payins[2].automatic is True assert scheduled_payins[2].execution_date > renewal_date send_donation_reminder_notifications() send_upcoming_debit_notifications() emails = self.get_emails() assert len(emails) == 0
def test_multiple_scheduled_payins(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob') carl = self.make_participant('carl') dana = self.make_participant('dana') alice.set_tip_to(bob, EUR('3.00'), renewal_mode=2) alice_card = self.attach_stripe_payment_method(alice, 'pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('12.00')) alice.set_tip_to(carl, EUR('0.01'), renewal_mode=1) self.make_payin_and_transfer(alice_card, carl, EUR('6.00')) alice.set_tip_to(dana, EUR('5.00'), renewal_mode=2) self.make_payin_and_transfer(alice_card, dana, EUR('25.00')) scheduled_payins = self.db.all( "SELECT * FROM scheduled_payins ORDER BY execution_date" ) assert len(scheduled_payins) == 2 assert scheduled_payins[0].amount == EUR('37.00') assert scheduled_payins[1].amount is None manual_payin = scheduled_payins[1] self.db.run(""" UPDATE scheduled_payins SET execution_date = (current_date + interval '7 days') , ctime = (ctime - interval '12 hours') """) send_upcoming_debit_notifications() emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0]['subject'] == 'Liberapay donation renewal: upcoming debit of €37.00' send_donation_reminder_notifications() emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0]['subject'] == "It's time to renew your donation to carl on Liberapay" send_donation_reminder_notifications() emails = self.get_emails() assert len(emails) == 0 self.db.run(""" UPDATE scheduled_payins SET execution_date = current_date , last_notif_ts = (last_notif_ts - interval '14 days') """) # Restore the correct date of the manual payin to avoid triggering a # `payment_schedule_modified` notification self.db.run(""" UPDATE scheduled_payins SET execution_date = %(execution_date)s WHERE id = %(id)s """, manual_payin.__dict__) execute_scheduled_payins() payins = self.db.all("SELECT * FROM payins ORDER BY ctime") assert len(payins) == 4 assert payins[0].payer == alice.id assert payins[0].amount == EUR('12.00') assert payins[0].off_session is False assert payins[0].status == 'succeeded' assert payins[1].payer == alice.id assert payins[1].amount == EUR('6.00') assert payins[1].off_session is False assert payins[1].status == 'succeeded' assert payins[2].payer == alice.id assert payins[2].amount == EUR('25.00') assert payins[2].off_session is False assert payins[2].status == 'succeeded' assert payins[3].payer == alice.id assert payins[3].amount == EUR('37.00') assert payins[3].off_session is True assert payins[3].status == 'succeeded' emails = self.get_emails() assert len(emails) == 1 assert emails[0]['to'] == ['alice <*****@*****.**>'] assert emails[0]['subject'] == "Your payment has succeeded"