func (payment *Payment) advance(targetState PaymentStatus) error { if !targetState.canAdvance { return fmt.Errorf("Won't advance payment to %v", targetState) } for payment.status.order < targetState.order { previousStatus := payment.status var err error switch previousStatus { case PaymentCreated: err = payment.advanceToPending() case PaymentPending: err = payment.advanceToCommitted() default: return services.Conflictf("Cannot advance %v payment", previousStatus) } if err != nil { return fmt.Errorf("Failed to advance %v payment: %v", previousStatus, err) } if previousStatus.order == payment.status.order { // Avoid infinite loop return fmt.Errorf("Status of %v payment did not change", previousStatus) } } return nil }
func (shipment *Shipment) doDeliver() error { return shipment.modifyItem("delivering", func(item *Item) error { if item.Reserved < shipment.Quantity { return services.Conflictf("Cannot deliver %v %v: Only %v reserved", shipment.Quantity, item.Name, item.Reserved) } item.Reserved -= shipment.Quantity item.Shipped += shipment.Quantity shipment.Status = catalogApi.ShipmentDelivered return nil }) }
func (shipment *Shipment) doCommit() error { return shipment.modifyItem("committing", func(item *Item) error { if item.Stock < shipment.Quantity { return services.Conflictf("Cannot commit %v %v: Only %v in stock", shipment.Quantity, item.Name, item.Stock) } item.Stock -= shipment.Quantity item.Reserved += shipment.Quantity shipment.Status = catalogApi.ShipmentCommitted return nil }) }
func (shipment *Shipment) Commit() error { if err := shipment.lock.Lock(); err != nil { return fmt.Errorf("Failed to lock shipment: %v", err) } defer shipment.unlock() switch shipment.Status { case catalogApi.ShipmentCreated: return shipment.doCommit() case catalogApi.ShipmentCommitted, catalogApi.ShipmentDelivered: return nil default: return services.Conflictf("Cannot commit %v shipment", shipment.Status) } }
func (shipment *Shipment) Deliver() error { if err := shipment.lock.Lock(); err != nil { return fmt.Errorf("Failed to lock shipment: %v", err) } defer shipment.unlock() switch shipment.Status { case catalogApi.ShipmentCreated: if err := shipment.doCommit(); err != nil { return err } if shipment.Status != catalogApi.ShipmentCommitted { return fmt.Errorf("Shipment did not change to %v status", catalogApi.ShipmentCommitted) } return shipment.doDeliver() case catalogApi.ShipmentCommitted: return shipment.doDeliver() case catalogApi.ShipmentDelivered: return nil default: return services.Conflictf("Cannot deliver %v shipment", shipment.Status) } }
func (payment *Payment) Cancel() error { defer payment.unlock() switch payment.status { case PaymentCreated, PaymentCancelled: // Transaction has not been created yet, or already cancelled return nil case PaymentPending, PaymentCommitted: trans, err := payment.getTransaction() if err != nil { return err } return trans.Cancel() case PaymentProcessed: trans, err := payment.getTransaction() if err != nil { return err } return trans.Revert() default: return services.Conflictf("Cannot cancel a %v payment", payment.status) } }