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)
	}
}