There are some libraries that simplify integration of the payment systems, the most popular for PHP is OmniPay. We use it as a primary Payment Services integration library now.
@startuml
legend right
|<#FFDDDD> <b>E</b> | <b>Entity</b> |
|<#DDFFDD> <b>V</b> | <b>Value Object</b> |
|<#FFFFDD> <b>A</b> | <b>Auxiliary</b> |
endlegend
!define Entity(name,desc) class name as "desc" << (E,#EEEEEE) >> #FFDDDD
!define ValueObject(name,desc) class name as "desc" << (V,#FFFFFF) >> #DDFFDD
!define Auxiliary(name,desc) class name as "desc" << (A,#FFFFFF) >> #FFFFDD
!define primary_key(x) x TEXT
!define foreign_key(x, y) <i>x</i> --> <b>y</b>
!define value_object(x, y) x <b>y</b>
hide methods
hide stereotypes
package "Payments configuration" {
Entity(merchant, "Merchant") {
primary_key(id)
email TEXT
}
Entity(gateway, "Gateway") {
primary_key(id)
foreign_key(merchant, Merchant)
foreign_key(routes, Route[])
}
Entity(route, "Route") {
primary_key(id)
foreign_key(gateway, Gateway)
foreign_key(paymentService, PaymentService)
foreign_key(providerAccount, ProviderAccount)
}
Entity(providerAccount, ProviderAccount) {
primary_key(id)
foreign_key(credentials, Credentials)
foreign_key(paymentProvider, PaymentProvider)
}
Entity(credentials, Credentials) {
hashmap<string, string>
}
Entity(paymentPage, PaymentPage) {
primary_key(id)
foreign_key(gateway, Gateway)
uiOptions hashmap<string, mixed>
secretKey string
}
}
package "Reference information" {
Entity(paymentProvider, PaymentProvider) {
primary_key(code)
name TEXT
}
Entity(paymentService, PaymentService) {
primary_key(code)
foreign_key(currency, Currency)
foreign_key(paymentFlow, PaymentFlow)
foreign_key(paymentMethod, PaymentMethod)
}
ValueObject(currency, Currency) {
primary_key(code)
}
ValueObject(paymentFlow, PaymentFlow) {
flow ENUM[hpp, token]
}
ValueObject(paymentMethod, PaymentMethod) {
primary_key(id)
category TEXT
name TEXT
}
}
package "ValueObjects" {
ValueObject(money, Money) {
amount TEXT
foreign_key(currency, Currency)
}
}
package "Customers" {
Entity(customer, Customer) {
primary_key(id)
}
}
package "Transaction" {
Entity(paymentRequest, PaymentRequest) {
primary_key(id)
foreign_key(amount, Money)
foreign_key(paymentPage, PaymentPage)
state ENUM[paid, new]
creationDate DateTimeImmutable
foreign_key(customer, Customer)
callbackUrl TEXT
successUrl TEXT
errorUrl TEXT
}
Entity(paymentTransaction, PaymentTransaction) {
primary_key(id)
foreign_key(paymentRequest, PaymentRequest)
foreign_key(route, Route)
foreign_key(amount, Money)
foreign_key(commission, Money)
createdAt DateTimeImmutable
paidAt DateTimeImmutable
state ENUM[new, paid, failed]
foreign_key(confirmation, Confirmation)
}
Entity(paymentTransactionConfirmation, PaymentTransactionConfirmation) {
primary_key(id)
remoteId TEXT
time DateTimeImmutable
payer TEXT
}
Entity(paymentTransactionConfirmationNotification, PaymentTransactionConfirmationNotification) {
primary_key(id)
foreign_key(transaction, PaymentTransaction)
foreign_key(confirmation, PaymentTransactionConfirmation)
sentAt DateTimeImmutable
accepted Boolean
request TEXT
response TEXT
}
paymentRequest -> money
paymentRequest -down-> paymentPage
paymentRequest -up-> customer
paymentTransaction -> paymentRequest
paymentTransaction -down-> route
paymentTransaction -left-> paymentTransactionConfirmation
paymentTransactionConfirmationNotification -down-> paymentTransaction
paymentTransactionConfirmationNotification -down-> paymentTransactionConfirmation
}
gateway -> merchant
route -> gateway
paymentService -up-> route
paymentFlow -> paymentService
currency -up-> paymentService
paymentMethod -up-> paymentService
providerAccount <- route
providerAccount -> paymentProvider
providerAccount -left-> credentials
gateway -> paymentPage
@enduml
@startuml
actor Customer
== Initialization ==
Customer->POS: Requests payment
activate POS
POS->"Merchant API": Creates PaymentRequest
activate "Merchant API"
"Merchant API"->POS: URL with a PaymentRequest ID
destroy "Merchant API"
POS->Customer: Redirect URL
deactivate POS
Customer->Merchant: User is being redirected to Merchant UI
== Choosing a payment provider ==
Merchant->Customer: Returns UI with different payment options
Customer->"Merchant API": Starts Transaction with the specific PaymentOption
activate Customer
activate "Merchant API"
"Merchant API"->"Service Provider": Initiate a payment request
activate "Service Provider"
"Service Provider"->"Merchant API": Returns a redirect link
deactivate "Service Provider"
"Merchant API"->Customer: Returns a redirect link
deactivate "Merchant API"
deactivate Customer
== Payment handling ==
Customer->"Service Provider": Follows a redirect
"Service Provider"->"Service Provider": Handles Customer's Payment Method
"Service Provider"->"Merchant": Redirects Customer back to Merchant
"Merchant"->Customer: Provides a "Waiting for confirmation UI"
"Service Provider"->"Merchant API": Sends a payment confirmation
"Merchant API"-->>"Service Provider": Confirms payment acceptance
"Merchant API"->POS: Propagates a payment notification
Customer->"Merchant API": Customer periodically asks for the payment confirmation
Customer->POS: Once confirmed, gets redirected to the POS
@enduml