//Show builds an html page that display a receipt //this is a very boring, plain test, monospaced font page designed for easy printing and reading //the receipt is generated from the charge id //the data for the charge may be in memcache or will have to be retrieved from stripe func Show(w http.ResponseWriter, r *http.Request) { //get charge id from form value chargeId := r.FormValue("chg_id") //try looking up charge data in memcache var chg *stripe.Charge c := appengine.NewContext(r) _, err := memcache.Gob.Get(c, chargeId, &chg) //charge not found in memcache //look up charge data from stripe if err == memcache.ErrCacheMiss { //init stripe c := appengine.NewContext(r) stripe.SetBackend(stripe.APIBackend, nil) stripe.SetHTTPClient(urlfetch.Client(c)) //get charge data chg, err = charge.Get(chargeId, nil) if err != nil { fmt.Fprint(w, "An error occured and the receipt cannot be displayed.\n") fmt.Fprint(w, err) return } //save to memcache //just in case we want to view the receipt again memcacheutils.Save(c, chg.ID, chg) } //extract charge data d := chargeutils.ExtractData(chg) //get company info info, err := getCompanyInfo(r) name, street, suite, city, state, postal, country, phone := "", "", "", "", "", "", "", "" if err == ErrCompanyDataDoesNotExist { name = "**Company info has not been set yet.**" street = "**Please contact an administrator to fix this.**" } else { name = info.CompanyName street = info.Street suite = info.Suite city = info.City state = info.State postal = info.PostalCode country = info.Country phone = info.PhoneNum } //display receipt output := templateData{ CompanyName: name, Street: street, Suite: suite, City: city, State: state, Postal: postal, Country: country, PhoneNum: phone, Customer: d.Customer, Cardholder: d.Cardholder, CardBrand: d.CardBrand, LastFour: d.LastFour, Expiration: d.Expiration, Captured: d.CapturedStr, Timestamp: d.Timestamp, Amount: d.AmountDollars, Invoice: d.Invoice, Po: d.Po, } templates.Load(w, "receipt", output) return }
//Report gets the data for charges and refunds by the defined filters (date range and customer) and builds the reports page //the reports show up in a different page so they are easily printable and more easily inspected //date range is inclusive of start and end days func Report(w http.ResponseWriter, r *http.Request) { //get form valuess datastoreId := r.FormValue("customer-id") startString := r.FormValue("start-date") endString := r.FormValue("end-date") hoursToUTC := r.FormValue("timezone") //get report data form stripe //make sure inputs are given if len(startString) == 0 { output.Error(ErrMissingInput, "You must supply a 'start-date'.", w, r) return } if len(endString) == 0 { output.Error(ErrMissingInput, "You must supply a 'end-date'.", w, r) return } if len(hoursToUTC) == 0 { output.Error(ErrMissingInput, "You must supply a 'timezone'.", w, r) return } //get timezone offset //adjust for the local timezone the user is in so that the date range is correct //hoursToUTC is a number generated by JS (-4 for EST) tzOffset := calcTzOffset(hoursToUTC) //get datetimes from provided strings startDt, err := time.Parse("2006-01-02 -0700", startString+" "+tzOffset) if err != nil { output.Error(err, "Could not convert start date to a time.Time datetime.", w, r) return } endDt, err := time.Parse("2006-01-02 -0700", endString+" "+tzOffset) if err != nil { output.Error(err, "Could not convert end date to a time.Time datetime.", w, r) return } //get end of day datetime //need to get 23:59:59 so we include the whole day endDt = endDt.Add((24*60-1)*time.Minute + (59 * time.Second)) //get unix timestamps //stripe only accepts timestamps for filtering charges startUnix := startDt.Unix() endUnix := endDt.Unix() //init stripe c := appengine.NewContext(r) sc := createAppengineStripeClient(c) //retrieve data from stripe //date is a range inclusive of the days the user chose //limit of 100 is the max per stripe params := &stripe.ChargeListParams{} params.Filters.AddFilter("created", "gte", strconv.FormatInt(startUnix, 10)) params.Filters.AddFilter("created", "lte", strconv.FormatInt(endUnix, 10)) params.Filters.AddFilter("limit", "", "100") //check if we need to filter by a specific customer //look up stripe customer id by the datastore id if len(datastoreId) != 0 { datastoreIdInt, _ := strconv.ParseInt(datastoreId, 10, 64) custData, err := findByDatastoreId(c, datastoreIdInt) if err != nil { output.Error(err, "An error occured and this report could not be generated.", w, r) return } params.Filters.AddFilter("customer", "", custData.StripeCustomerToken) } //get results //loop through each charge and extract charge data //add up total amount of all charges charges := sc.Charges.List(params) data := make([]chargeutils.Data, 0, 10) var amountTotal uint64 = 0 var numCharges uint16 = 0 for charges.Next() { //get each charges data chg := charges.Charge() d := chargeutils.ExtractData(chg) //make sure this charge was captured //do not count charges that failed if d.Captured == false { continue } data = append(data, d) //increment totals amountTotal += d.AmountCents numCharges++ } //convert total amount to dollars amountTotalDollars := strconv.FormatFloat((float64(amountTotal) / 100), 'f', 2, 64) //retrieve refunds eventParams := &stripe.EventListParams{} eventParams.Filters.AddFilter("created", "gte", strconv.FormatInt(startUnix, 10)) eventParams.Filters.AddFilter("created", "lte", strconv.FormatInt(endUnix, 10)) eventParams.Filters.AddFilter("limit", "", "100") eventParams.Filters.AddFilter("type", "", "charge.refunded") events := sc.Events.List(eventParams) refunds := chargeutils.ExtractRefunds(events) //get logged in user's data //for determining if receipt/refund buttons need to be hidden or shown based on user's access rights session := sessionutils.Get(r) userId := session.Values["user_id"].(int64) userdata, _ := users.Find(c, userId) //store data for building template result := reportData{ UserData: userdata, StartDate: startDt, EndDate: endDt, Charges: data, Refunds: refunds, TotalAmount: amountTotalDollars, NumCharges: numCharges, } //build template to display report //separate page in gui templates.Load(w, "report", result) return }