package service import ( "context" "errors" "strings" ) const ( batchImageHoldRequestPrefix = "batch_image_hold:" batchImageCaptureRequestPrefix = "batch_image_capture:" batchImageReleaseRequestPrefix = "batch_image_release:" ) func BatchImageHoldRequestID(batchID string) string { return batchImageHoldRequestPrefix + strings.TrimSpace(batchID) } func BatchImageCaptureRequestID(batchID string) string { return batchImageCaptureRequestPrefix + strings.TrimSpace(batchID) } func BatchImageReleaseRequestID(batchID string) string { return batchImageReleaseRequestPrefix + strings.TrimSpace(batchID) } func buildBatchImageHoldCommand(job *BatchImageJob, requestID string, actualAmount float64, payloadHash string) (*BatchImageBalanceHoldCommand, error) { if job == nil { return nil, ErrBatchImageBillingHoldFailed } if job.APIKeyID == nil || *job.APIKeyID <= 0 { return nil, ErrBatchImageSettlementMissingAPIKeyID } holdAmount := job.EstimatedCost if job.HoldAmount != nil { holdAmount = *job.HoldAmount } if holdAmount < 0 { holdAmount = 0 } if actualAmount < 0 { actualAmount = 0 } return &BatchImageBalanceHoldCommand{ RequestID: requestID, APIKeyID: *job.APIKeyID, UserID: job.UserID, BatchID: job.BatchID, HoldAmount: holdAmount, ActualAmount: actualAmount, RequestPayloadHash: strings.TrimSpace(payloadHash), }, nil } func reserveBatchImageBalanceHold(ctx context.Context, repo UsageBillingRepository, job *BatchImageJob, payloadHash string) error { if repo == nil { return ErrBatchImageBillingHoldFailed.WithCause(errors.New("batch image billing repository is not configured")) } cmd, err := buildBatchImageHoldCommand(job, BatchImageHoldRequestID(job.BatchID), 0, payloadHash) if err != nil { return err } if cmd.HoldAmount <= 0 { return nil } if _, err := repo.ReserveBatchImageBalance(ctx, cmd); err != nil { if errors.Is(err, ErrBatchImageInsufficientBalance) { return ErrBatchImageInsufficientBalance } return ErrBatchImageBillingHoldFailed.WithCause(err) } return nil } func captureBatchImageBalanceHold(ctx context.Context, repo UsageBillingRepository, job *BatchImageJob, actualAmount float64, payloadHash string) error { if repo == nil { return ErrBatchImageSettlementBillingFailed.WithCause(errors.New("batch image billing repository is not configured")) } cmd, err := buildBatchImageHoldCommand(job, BatchImageCaptureRequestID(job.BatchID), actualAmount, payloadHash) if err != nil { return err } if _, err := repo.CaptureBatchImageBalance(ctx, cmd); err != nil { return ErrBatchImageSettlementBillingFailed.WithCause(err) } return nil } func releaseBatchImageBalanceHold(ctx context.Context, repo UsageBillingRepository, job *BatchImageJob, payloadHash string) error { if repo == nil || job == nil { return nil } cmd, err := buildBatchImageHoldCommand(job, BatchImageReleaseRequestID(job.BatchID), 0, payloadHash) if err != nil { return err } if cmd.HoldAmount <= 0 { return nil } if _, err := repo.ReleaseBatchImageBalance(ctx, cmd); err != nil { return ErrBatchImageBillingHoldFailed.WithCause(err) } return nil }