Lunch Card Payment System and Payment Terminal

LunchCard and PaymentTerminal

NB: Some exercises have multiple parts, and you can receive points for the different parts separately. You can submit a partially completed exercise by choosing ‘Submit Solution’ from the menu next to the button for executing tests.

In the previous part there was an exercise where you implemented the class LunchCard. The card had separate methods for eating a regular and a special lunch, along with a method for depositing money on the card.

The LunchCard class, as you were asked to implement it, has some problems, however. The card itself had knowledge of the prices of the different lunch options, and knew to subtract the right amount of money from the balance based on these. But imagine the prices changed, or there were new items introduced to the system, but several cards were already registered in the system. This would mean all existing cards would need to be replaced by versions with knowledge of the new prices.

A better solution would be to make the cards “stupid”, ignorant of the prices of different products. The purpose of the card would be to simply keep track of the available balance. All more complicated features should be contained within another class: the payment terminal.

A simpler LunchCard

Let’s first implement a simpler version of the LunchCard class. The card should contain functionality only for finding out the current balance, depositing money on the card, and subtracting from the balance. Please fill in the subtract_from_balance(amount) method in the exercise template according to the instructions in the comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
class LunchCard:
def __init__(self, balance: float):
self.balance = balance

def deposit_money(self, amount: float):
self.balance += amount

def subtract_from_balance(self, amount: float):
pass
# The amount should be subtracted from the balance only if
# there is enough money on the card.
# If the payment is successful, the method returns True.
# Otherwise it returns False.

You may use the following code to test your function:

1
2
3
4
5
6
7
8
9
if __name__ == "__main__":
card = LunchCard(10)
print("Balance", card.balance)
result = card.subtract_from_balance(8)
print("Payment successful:", result)
print("Balance", card.balance)
result = card.subtract_from_balance(4)
print("Payment successful:", result)
print("Balance", card.balance)

Sample output

1
2
3
4
5
Balance 10
Payment successful: True
Balance 2
Payment successful: False
Balance 2

The payment terminal and dealing with cash payments

In the student cafeteria it is possible to pay with either cash or a LunchCard. A payment terminal is used to handle both cash and card transactions. Let’s start with the cash transactions.

Here we have a skeleton implementation for a PaymentTerminal class. Please implement the methods as described in the comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class PaymentTerminal:
def __init__(self):
# Initially there is 1000 euros in cash available at the terminal
self.funds = 1000
self.lunches = 0
self.specials = 0

def eat_lunch(self, payment: float):
# A regular lunch costs 2.50 euros.
# Increase the value of the funds at the terminal by the
# price of the lunch, increase the number of lunches sold,
# and return the appropriate change.
# If the payment passed as an argument is not large enough to cover
# the price, the lunch is not sold, and the entire sum is returned.

def eat_special(self, payment: float):
# A special lunch costs 4.30 euros.
# Increase the value of the funds at the terminal by the
# price of the lunch, increase the number of lunches sold,
# and return the appropriate change.
# If the payment passed as an argument is not large enough to cover
# the price, the lunch is not sold, and the entire sum is returned.

You may use the following code to test your class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
exactum = PaymentTerminal()

change = exactum.eat_lunch(10)
print("The change returned was", change)

change = exactum.eat_lunch(5)
print("The change returned was", change)

change = exactum.eat_special(4.3)
print("The change returned was", change)

print("Funds available at the terminal:", exactum.funds)
print("Regular lunches sold:", exactum.lunches)
print("Special lunches sold:", exactum.specials)

Sample output

1
2
3
4
5
6
The change returned was 7.5
The change returned was 2.5
The change returned was 0.0
Funds available at the terminal: 1009.3
Regular lunches sold: 2
Special lunches sold: 1

Dealing with card transactions

Now let’s implement card transactions. We will need methods which take a LunchCard as an argument, and reduce the balance on the card by the price of the lunch. Below you will find the outlines of these functions. Please fill in the methods as described in the comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PaymentTerminal:
# ...

def eat_lunch_lunchcard(self, card: LunchCard):
# A regular lunch costs 2.50 euros.
# If there is enough money on the card,
# subtract the price of the lunch from the balance
# and return True. If not, return False.


def eat_special_lunchcard(self, card: LunchCard):
# A special lunch costs 4.30 euros.
# If there is enough money on the card,
# subtract the price of the lunch from the balance
# and return True. If not, return False.

NB: when paying with a LunchCard the cash funds available at the terminal do not change. However, the lunches are still sold whenever there is the required balance available, so remember to increase the number of lunches sold appropriately.

You may use the following code to test your class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
exactum = PaymentTerminal()

change = exactum.eat_lunch(10)
print("The change returned was", change)

card = LunchCard(7)

result = exactum.eat_special_lunchcard(card)
print("Payment successful:", result)
result = exactum.eat_special_lunchcard(card)
print("Payment successful:", result)
result = exactum.eat_lunch_lunchcard(card)
print("Payment successful:", result)

print("Funds available at the terminal:", exactum.funds)
print("Regular lunches sold:", exactum.lunches)
print("Special lunches sold:", exactum.specials)

Sample output

1
2
3
4
5
6
7
The change returned was 7.5
Payment successful: True
Payment successful: False
Payment successful: True
Funds available at the terminal: 1002.5
Regular lunches sold: 2
Special lunches sold: 1

Depositing money on the card

Finally, let’s add a method which lets you deposit money on the card. The card owner pays this by cash, so the deposited sum is added to the funds available at the terminal. Here is an outline for the method:

1
2
def deposit_money_on_card(self, card: LunchCard, amount: float):
pass

You may use the following code to test your method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
exactum = PaymentTerminal()

card = LunchCard(2)
print(f"Card balance is {card.balance} euros")

result = exactum.eat_special_lunchcard(card)
print("Payment successful:", result)

exactum.deposit_money_on_card(card, 100)
print(f"Card balance is {card.balance} euros")

result = exactum.eat_special_lunchcard(card)
print("Payment successful:", result)
print(f"Card balance is {card.balance} euros")

print("Funds available at the terminal:", exactum.funds)
print("Regular lunches sold:", exactum.lunches)
print("Special lunches sold:", exactum.specials)

Sample output

1
2
3
4
5
6
7
8
Card balance is 2 euros
Payment successful: False
Card balance is 102 euros
Payment successful: True
Card balance is 97.7 euros
Funds available at the terminal: 1100
Regular lunches sold: 0
Special lunches sold: 1

Solution

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
class LunchCard:
def __init__(self, balance: float):
self.balance = balance

def deposit_money(self, amount: float):
self.balance += amount

def subtract_from_balance(self, amount: float):
if self.balance >= amount:
self.balance -= amount
return True

return False

# The amount should be subtracted from the balance only if there is enough money on the card
# If the payment is successful, the method returns True, and otherwise it returns False

class PaymentTerminal:
def __init__(self):
# Initially there is 1000 euros in cash available at the terminal
self.funds = 1000
self.lunches = 0
self.specials = 0

def eat_lunch(self, payment: float):
# adjust the cost of lunch here
cost_of_lunch = 2.50

if payment < cost_of_lunch:
return payment
else:
self.funds += cost_of_lunch
self.lunches += 1
return payment - cost_of_lunch
# A regular lunch costs 2.50 euros.
# Increase the value of the funds at the terminal by the price of the lunch,
# increase the number of lunches sold, and return the appropriate change.
# If the payment passed as an argument is not large enough to cover the price,
# the lunch is not sold, and the entire sum is returned.

def eat_special(self, payment: float):
cost_of_special = 4.30

if payment < cost_of_special:
return payment
else:
self.funds += cost_of_special
self.specials += 1
return payment - cost_of_special
# A special lunch costs 4.30 euros.
# Increase the value of the funds at the terminal by the price of the lunch,
# increase the number of specials sold, and return the appropriate change.
# If the payment passed as an argument is not large enough to cover the price,
# the lunch is not sold, and the entire sum is returned.


def eat_lunch_lunchcard(self, card: LunchCard):
if card.subtract_from_balance(2.50):
self.lunches += 1
return True
return False

# A regular lunch costs 2.50 euros.
# If there is enough money on the card, subtract the price of the lunch from the balance
# and return True. If not, return False.


def eat_special_lunchcard(self, card: LunchCard):
if card.subtract_from_balance(4.30):
self.specials += 1
return True
return False
# A special lunch costs 4.30 euros.
# If there is enough money on the card, subtract the price of the lunch from the balance
# and return True. If not, return False.


def deposit_money_on_card(self, card: LunchCard, amount: float):
if amount > 0:
card.deposit_money(amount)
#self.funds += amount


if __name__ == "__main__":
exactum = PaymentTerminal()

change = exactum.eat_lunch(10)
print("The change returned was", change)

card = LunchCard(7)

result = exactum.eat_special_lunchcard(card)
print("Payment successful:", result)
result = exactum.eat_special_lunchcard(card)
print("Payment successful:", result)
result = exactum.eat_lunch_lunchcard(card)
print("Payment successful:", result)

print("Funds available at the terminal:", exactum.funds)
print("Regular lunches sold:", exactum.lunches)
print("Special lunches sold:", exactum.specials)