Recap - Payment Processor
Part of the Object Oriented Programming section of Coddy's GO journey. Lesson 46 of 107.
Challenge
EasyLet's build a complete payment processing system that brings together all the polymorphism concepts from this chapter. You'll create different payment methods that work through a unified interface, a processor that accepts any payment method via dependency injection, and process multiple payments through a polymorphic collection.
You'll organize your code across four files:
payment.go: Define aPaymentMethodinterface that requires aProcess(amount float64) stringmethod. This is the contract that all payment types must fulfill.methods.go: Create three payment types that each implement thePaymentMethodinterface:CreditCardwithCardNumber(string) andHolderName(string) fields. ItsProcessmethod returnsCredit Card [last 4 digits of CardNumber] charged $[amount] to [HolderName]PayPalwith anEmailfield. ItsProcessmethod returnsPayPal payment of $[amount] from [Email]BankTransferwithBankNameandAccountNumberfields. ItsProcessmethod returnsBank transfer of $[amount] via [BankName] (***[last 3 digits of AccountNumber])
processor.go: Create aPaymentProcessorstruct that holds aPaymentMethod(injected dependency). Include a constructorNewPaymentProcessorthat accepts aPaymentMethod. Add a methodExecutePayment(amount float64) stringthat delegates to the injected payment method. Also create a functionProcessAll(payments []PaymentMethod, amount float64) []stringthat processes the same amount through each payment method in the slice and returns all results.main.go: Read payment details from input, create all three payment types, and demonstrate both dependency injection and polymorphic collections. First, useNewPaymentProcessorwith the credit card and execute a payment. Then collect all three payment methods into a slice and useProcessAllto process them all with a second amount. Print each result on its own line.
The following inputs will be provided:
- Line 1: Credit card number (e.g.,
4532015112830366) - Line 2: Card holder name
- Line 3: PayPal email
- Line 4: Bank name
- Line 5: Account number (e.g.,
987654321) - Line 6: First payment amount (for single processor)
- Line 7: Second payment amount (for batch processing)
Format amounts with two decimal places. For example, given 4532015112830366, John Smith, john@email.com, Chase, 987654321, 99.99, and 50, your output should be:
Credit Card 0366 charged $99.99 to John Smith
Credit Card 0366 charged $50.00 to John Smith
PayPal payment of $50.00 from john@email.com
Bank transfer of $50.00 via Chase (***321)The first line shows the single payment through the injected processor. The remaining three lines show the batch processing through the polymorphic collection: the same ProcessAll function handles all payment types uniformly, demonstrating how interfaces enable flexible, extensible payment systems.
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Read input
cardNumber, _ := reader.ReadString('\n')
cardNumber = strings.TrimSpace(cardNumber)
holderName, _ := reader.ReadString('\n')
holderName = strings.TrimSpace(holderName)
email, _ := reader.ReadString('\n')
email = strings.TrimSpace(email)
bankName, _ := reader.ReadString('\n')
bankName = strings.TrimSpace(bankName)
accountNumber, _ := reader.ReadString('\n')
accountNumber = strings.TrimSpace(accountNumber)
amount1Str, _ := reader.ReadString('\n')
amount1Str = strings.TrimSpace(amount1Str)
amount1, _ := strconv.ParseFloat(amount1Str, 64)
amount2Str, _ := reader.ReadString('\n')
amount2Str = strings.TrimSpace(amount2Str)
amount2, _ := strconv.ParseFloat(amount2Str, 64)
// TODO: Create the three payment types (CreditCard, PayPal, BankTransfer)
// TODO: Create a PaymentProcessor with the credit card using NewPaymentProcessor
// and execute a payment with amount1, then print the result
// TODO: Create a slice of PaymentMethod containing all three payment types
// TODO: Use ProcessAll to process all payments with amount2
// and print each result on its own line
}
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker PoolPractice on your own: Online Go compiler