func AutoCharge(w http.ResponseWriter, r *http.Request) {
	customerID := r.FormValue("customer_id")  
	amount := r.FormValue("amount")           
	invoice := r.FormValue("invoice")
	poNum := r.FormValue("po")
	idempotencyKey := strings.TrimSpace(r.FormValue("idempotecy_key"))
	apiKey := r.FormValue("api_key")
	autoCharge, _ := strconv.ParseBool(r.FormValue("auto_charge"))          
	referrer := r.FormValue("auto_charge_referrer")                         
	reason := r.FormValue("auto_charge_reason")                             
	level3Params := r.FormValue("level3_params")                            
	level3Provided, _ := strconv.ParseBool(r.FormValue("level3_provided"))  
	if customerID == "" {
		output.Error(errMissingInput, "A customer ID should have been submitted.", w)
		return
	}
	if len(amount) == 0 {
		output.Error(errMissingInput, "No amount was provided.", w)
		return
	}
	if !autoCharge {
		output.Error(errMissingInput, "The 'auto_charge' value was not provided. This is required when trying to automatically process a charge.", w)
		return
	}
	if len(referrer) == 0 {
		output.Error(errMissingInput, "There was no 'referrer' given.  This should be the app that made this auto-charge request.  This is used for logging.", w)
		return
	}
	if len(reason) == 0 {
		output.Error(errMissingInput, "There was no 'reason' given.  This should be the function of the app that made this auto-charge request.  This is used for logging.", w)
		return
	}
	if len(apiKey) == 0 {
		output.Error(errMissingAPIKey, "There was no api given. This must be given in the 'api_key' field to authenticate this request.", w)
		return
	}
	settings, err := appsettings.Get(r)
	if err != nil {
		output.Error(err, "Could not get app settings to verify api key.", w)
		return
	}
	if settings.APIKey != apiKey {
		output.Error(errInvalidAPIKey, "The api key provided in the request is not correct.", w)
		return
	}
	amountCents, err := strconv.ParseUint(amount, 10, 64)
	if err != nil {
		output.Error(err, "Could not convert amount to integer.", w)
		return
	}
	c := r.Context()
	c, cancelFunc := context.WithTimeout(c, 10*time.Second)
	defer cancelFunc()
	custData, err := FindByCustomerID(c, customerID)
	if err != nil {
		output.Error(err, "An error occured while looking up the customer's Stripe information.", w)
		return
	}
	companyInfo, err := company.Get(r)
	if err != nil {
		output.Error(err, "Could not get statement descriptor from company info.", w)
		return
	} else if len(companyInfo.StatementDescriptor) == 0 {
		output.Error(errMissingStatementDescriptor, "Your company does not have a statement descriptor set.  Please ask an admin to set one.", w)
		return
	}
	inputs := processChargeInputs{
		context:              c,
		amountCents:          amountCents,
		invoiceNum:           invoice,
		poNum:                poNum,
		companyData:          companyInfo,
		customerData:         custData,
		userProcessingCharge: "api",
		autoChargeReferrer:   referrer,
		autoChargeReason:     reason,
		authorizeOnly:        false,
		idempotencyKey:       idempotencyKey,
	}
	if level3Provided {
		var l3Params chargeLevel3ParamsJSON
		err := json.Unmarshal([]byte(level3Params), &l3Params)
		if err != nil {
			log.Println("could not unmarshal level 3 params, continuing without them", err)
		} else {
			inputs.level3Params = l3Params
			inputs.level3Provided = true
		}
	}
	out, errMsg, err := processCharge(inputs)
	if err != nil {
		output.Error(err, errMsg, w)
		return
	}
	output.Success("cardCharged", out, w)
}
AutoCharge processes a charge on a credit card automatically
this is used to charge a card without using the gui
package card
import (
	"context"
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/appsettings"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/company"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/datastoreutils"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/output"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/sessionutils"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/sqliteutils"
	"github.com/coreymgilmore/stripe-appengine-frontend/pkgs/timestamps"
	"github.com/stripe/stripe-go/v72"
)
type processChargeInputs struct {
	context              context.Context
	amountCents          uint64
	invoiceNum           string
	poNum                string
	companyData          company.Info
	customerData         CustomerDatastore
	userProcessingCharge string
	autoChargeReferrer   string
	autoChargeReason     string
	authorizeOnly        bool
	level3Params         chargeLevel3ParamsJSON
	level3Provided       bool
	idempotencyKey       string
}
type chargeLevel3ParamsJSON struct {
	CustomerReference  string                            `json:"customer_reference"`
	LineItems          []chargeLevel3LineItemsParamsJSON `json:"line_items"`
	MerchantReference  string                            `json:"merchant_reference"`
	ShippingAddressZip string                            `json:"shipping_address_zip"`
	ShippingFromZip    string                            `json:"shipping_from_zip"`
	ShippingAmount     int64                             `json:"shipping_amount"`
}
type chargeLevel3LineItemsParamsJSON struct {
	DiscountAmount     int64  `json:"discount_amount"`
	ProductCode        string `json:"product_code"`
	ProductDescription string `json:"product_description"`
	Quantity           int64  `json:"quantity"`
	TaxAmount          int64  `json:"tax_amount"`
	UnitCost           int64  `json:"unit_cost"`
}
func ManualCharge(w http.ResponseWriter, r *http.Request) {
	datastoreID, _ := strconv.ParseInt(r.FormValue("datastoreId"), 10, 64)  
	amount := r.FormValue("amount")                                         
	invoice := r.FormValue("invoice")
	poNum := r.FormValue("po")
	chargeAndRemove, _ := strconv.ParseBool(r.FormValue("chargeAndRemove"))  
	authorizeOnly, _ := strconv.ParseBool(r.FormValue("authorizeOnly"))      
	if datastoreID == 0 {
		output.Error(errMissingInput, "A customer ID should have been submitted automatically but was not. Please contact an administrator.", w)
		return
	}
	if len(amount) == 0 {
		output.Error(errMissingInput, "No amount was provided. You cannot charge a card nothing!", w)
		return
	}
	username := sessionutils.GetUsername(r)
	amountCents, err := getAmountAsIntCents(amount)
	if err != nil {
		output.Error(err, "An error occured while converting the amount to charge into cents. Please try again or contact an administrator.", w)
		return
	}
	c := r.Context()
	c, cancelFunc := context.WithTimeout(c, 10*time.Second)
	defer cancelFunc()
	custData, err := findByDatastoreID(c, datastoreID)
	if err != nil {
		output.Error(err, "An error occured while looking up the customer's Stripe information.", w)
		return
	}
	inputs := processChargeInputs{
		context:              c,
		amountCents:          amountCents,
		invoiceNum:           invoice,
		poNum:                poNum,
		customerData:         custData,
		userProcessingCharge: username,
		autoChargeReferrer:   "",
		autoChargeReason:     "",
		authorizeOnly:        authorizeOnly,
	}
	out, errMsg, err := processCharge(inputs)
	if err != nil {
		output.Error(err, errMsg, w)
		return
	}
	if chargeAndRemove {
		err := remove(c, datastoreID)
		if err != nil {
			log.Println("Error removing card after charge.", err)
		}
	}
	output.Success("cardCharged", out, w)
}