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 Pool