diff --git a/cmd/satellite/main.go b/cmd/satellite/main.go index 4f7d4e3b4..0f24f50bd 100644 --- a/cmd/satellite/main.go +++ b/cmd/satellite/main.go @@ -274,6 +274,20 @@ var ( Args: cobra.ExactArgs(1), RunE: cmdPayAllInvoices, } + failPendingInvoiceTokenPaymentCmd = &cobra.Command{ + Use: "fail-token-payment", + Short: "fail pending invoice token payment", + Long: "attempts to transition the token invoice payments that are stuck in a pending state to failed.", + Args: cobra.ExactArgs(1), + RunE: cmdFailPendingInvoiceTokenPayments, + } + completePendingInvoiceTokenPaymentCmd = &cobra.Command{ + Use: "complete-token-payment", + Short: "complete pending invoice token payment", + Long: "attempts to transition the token invoice payments that are stuck in a pending state to complete.", + Args: cobra.ExactArgs(1), + RunE: cmdCompletePendingInvoiceTokenPayments, + } stripeCustomerCmd = &cobra.Command{ Use: "ensure-stripe-customer", Short: "Ensures that we have a stripe customer for every user", @@ -414,6 +428,8 @@ func init() { billingCmd.AddCommand(finalizeCustomerInvoicesCmd) billingCmd.AddCommand(payInvoicesWithTokenCmd) billingCmd.AddCommand(payAllInvoicesCmd) + billingCmd.AddCommand(failPendingInvoiceTokenPaymentCmd) + billingCmd.AddCommand(completePendingInvoiceTokenPaymentCmd) billingCmd.AddCommand(stripeCustomerCmd) consistencyCmd.AddCommand(consistencyGECleanupCmd) process.Bind(runCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) @@ -450,6 +466,8 @@ func init() { process.Bind(finalizeCustomerInvoicesCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) process.Bind(payInvoicesWithTokenCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) process.Bind(payAllInvoicesCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) + process.Bind(failPendingInvoiceTokenPaymentCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) + process.Bind(completePendingInvoiceTokenPaymentCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) process.Bind(stripeCustomerCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) process.Bind(consistencyGECleanupCmd, &consistencyGECleanupCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) process.Bind(fixLastNetsCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir)) @@ -905,6 +923,20 @@ func cmdPayAllInvoices(cmd *cobra.Command, args []string) (err error) { }) } +func cmdFailPendingInvoiceTokenPayments(cmd *cobra.Command, args []string) (err error) { + ctx, _ := process.Ctx(cmd) + return runBillingCmd(ctx, func(ctx context.Context, payments *stripe.Service, _ satellite.DB) error { + return payments.FailPendingInvoiceTokenPayments(ctx, strings.Split(args[0], ",")) + }) +} + +func cmdCompletePendingInvoiceTokenPayments(cmd *cobra.Command, args []string) (err error) { + ctx, _ := process.Ctx(cmd) + return runBillingCmd(ctx, func(ctx context.Context, payments *stripe.Service, _ satellite.DB) error { + return payments.CompletePendingInvoiceTokenPayments(ctx, strings.Split(args[0], ",")) + }) +} + func cmdStripeCustomer(cmd *cobra.Command, args []string) (err error) { ctx, _ := process.Ctx(cmd) diff --git a/satellite/payments/stripe/service.go b/satellite/payments/stripe/service.go index 12cd839b4..7d64f3bd8 100644 --- a/satellite/payments/stripe/service.go +++ b/satellite/payments/stripe/service.go @@ -1180,6 +1180,32 @@ func (service *Service) PayInvoicesWithTokenBalance(ctx context.Context, userID }, invoices) } +// FailPendingInvoiceTokenPayments marks all specified pending invoice token payments as failed, and refunds the pending charges. +func (service *Service) FailPendingInvoiceTokenPayments(ctx context.Context, pendingPayments []string) (err error) { + defer mon.Task()(&ctx)(&err) + + txIDs := make([]int64, len(pendingPayments)) + + for i, s := range pendingPayments { + txIDs[i], _ = strconv.ParseInt(s, 10, 64) + } + + return service.billingDB.FailPendingInvoiceTokenPayments(ctx, txIDs...) +} + +// CompletePendingInvoiceTokenPayments updates the status of the pending invoice token payment to complete. +func (service *Service) CompletePendingInvoiceTokenPayments(ctx context.Context, pendingPayments []string) (err error) { + defer mon.Task()(&ctx)(&err) + + txIDs := make([]int64, len(pendingPayments)) + + for i, s := range pendingPayments { + txIDs[i], _ = strconv.ParseInt(s, 10, 64) + } + + return service.billingDB.CompletePendingInvoiceTokenPayments(ctx, txIDs...) +} + // payInvoicesWithTokenBalance attempts to transition the users open invoices to "paid" by charging the customer // token balance. func (service *Service) payInvoicesWithTokenBalance(ctx context.Context, cusID string, wallet storjscan.Wallet, invoices []stripe.Invoice) (err error) {