Skip to content

Instantly share code, notes, and snippets.

@misaelbg
Created November 21, 2024 13:08
Show Gist options
  • Select an option

  • Save misaelbg/4f6e659fcf771d2d80e870e4725df241 to your computer and use it in GitHub Desktop.

Select an option

Save misaelbg/4f6e659fcf771d2d80e870e4725df241 to your computer and use it in GitHub Desktop.
Payment Challenge Code
"""
Questions:
1. Complete the `MiniVenmo.create_user()` method to allow our application to create new users.
2. Complete the `User.pay()` method to allow users to pay each other. Consider the following: if user A is paying user B, user's A balance should be used if there's enough balance to cover the whole payment, if not, user's A credit card should be charged instead.
3. Venmo has the Feed functionality, that shows the payments that users have been doing in the app. If Bobby paid Carol $5, and then Carol paid Bobby $15, it should look something like this
Bobby paid Carol $5.00 for Coffee
Carol paid Bobby $15.00 for Lunch
Implement the `User.retrieve_activity()` and `MiniVenmo.render_feed()` methods so the MiniVenmo application can render the feed.
4. Now users should be able to add friends. Implement the `User.add_friend()` method to allow users to add friends.
5. Now modify the methods involved in rendering the feed to also show when user's added each other as friends.
"""
"""
MiniVenmo! Imagine that your phone and wallet are trying to have a beautiful
baby. In order to make this happen, you must write a social payment app.
Implement a program that will feature users, credit cards, and payment feeds.
"""
import re
import unittest
import uuid
class UsernameException(Exception):
pass
class PaymentException(Exception):
pass
class CreditCardException(Exception):
pass
class Payment:
def __init__(self, amount, actor, target, note):
self.id = str(uuid.uuid4())
self.amount = float(amount)
self.actor = actor
self.target = target
self.note = note
class User:
def __init__(self, username):
self.credit_card_number = None
self.balance = 0.0
self.friends = set()
self.feed = []
if self._is_valid_username(username):
self.username = username
else:
raise UsernameException('Username not valid.')
def retrieve_feed(self):
return self.feed
def add_friend(self, new_friend):
self.friends.add(new_friend)
def add_to_balance(self, amount):
self.balance += float(amount)
def add_credit_card(self, credit_card_number):
if self.credit_card_number is not None:
raise CreditCardException('Only one credit card per user!')
if self._is_valid_credit_card(credit_card_number):
self.credit_card_number = credit_card_number
else:
raise CreditCardException('Invalid credit card number.')
def pay(self, target, amount, note):
if self.username == target.username:
raise PaymentException("User cannot pay themselves.")
if amount <= 0.0:
raise PaymentException("Amount must be greater than zero.")
if self.balance >= amount:
self.pay_with_balance(target, amount, note)
else:
self.pay_with_card(target, amount, note)
def pay_with_card(self, target, amount, note):
amount = float(amount)
if self.username == target.username:
raise PaymentException('User cannot pay themselves.')
elif amount <= 0.0:
raise PaymentException('Amount must be a non-negative number.')
elif self.credit_card_number is None:
raise PaymentException('Must have a credit card to make a payment.')
self._charge_credit_card(self.credit_card_number)
payment = Payment(amount, self, target, note)
target.add_to_balance(amount)
return payment
def pay_with_balance(self, target, amount, note):
self.balance -= amount
target.add_to_balance(mount)
self.feed.append(f"Paid ${amount:.2f} to {target.username} for {note}")
target.feed.append(f"Received ${amount:.2f} from {self.username} for {note}")
def _is_valid_credit_card(self, credit_card_number):
return credit_card_number in ["4111111111111111", "4242424242424242"]
def _is_valid_username(self, username):
return re.match('^[A-Za-z0-9_\\-]{4,15}$', username)
def _charge_credit_card(self, credit_card_number):
# magic method that charges a credit card thru the card processor
if not self._is_valid_credit_card(credit_card_number):
raise CreditCardException("Invalid credit vard number.")
print(f"Charging credit card: {credit_card_number}")
class MiniVenmo:
def __init__(self):
self.users = {}
def create_user(self, username, balance, credit_card_number):
user = User(username)
user.add_to_balance(balance)
user.add_credit_card(credit_card_number)
self.users[username] = user
return user
def render_feed(self, feed):
# Bobby paid Carol $5.00 for Coffee
# Carol paid Bobby $15.00 for Lunch
for payment in feed:
print(f"{payment.actor.username} paid {payment.target.username} ${payment.amount:.2f} for {payment.note}")
@classmethod
def run(cls):
venmo = cls()
bobby = venmo.create_user("Bobby", 5.00, "4111111111111111")
carol = venmo.create_user("Carol", 10.00, "4242424242424242")
try:
# should complete using balance
bobby.pay(carol, 5.00, "Coffee")
# should complete using card
carol.pay(bobby, 15.00, "Lunch")
except PaymentException as e:
print(e)
feed = bobby.retrieve_feed()
venmo.render_feed(feed)
bobby.add_friend(carol)
class TestUser(unittest.TestCase):
def setUp(self):
self.sut = MiniVenmo()
self.sut.create_user("Alice", 100.0, "4111111111111111")
self.sut.create_user("Bob", 50.0, "4242424242424242")
def test_this_works(self):
with self.assertRaises(UsernameException):
raise UsernameException()
def test_create_user_valid(self):
self.sut.create_user("valid_user", 100.0, "4111111111111111")
self.assertIn("valid_user", self.sut.users)
self.assertEqual(self.sut.users["valid_user"].balance, 100.0)
def test_create_user_invalid_username(self):
with self.assertRaises(UsernameException):
self.sut.create_user("Bad@Name", 50.0, "4111111111111111")
def test_pay_with_balance(self):
alice = self.sut.users["Alice"]
bob = self.sut.users["Bob"]
alice.pay(bob, 50.0, "Dinner")
# TODO: i miss test cases for payments
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment