(mode, shopPath string)
| 137 | } |
| 138 | |
| 139 | func (s *ShopAPI) createCheckoutHandler(mode, shopPath string) http.HandlerFunc { |
| 140 | return func(w http.ResponseWriter, r *http.Request) { |
| 141 | if !s.checkoutLimiter.Allow(clientIP(r)) { |
| 142 | writeJSONError(w, http.StatusTooManyRequests, "too many requests, please try again later") |
| 143 | return |
| 144 | } |
| 145 | |
| 146 | secretKey, ok := ResolveSecretKeyByMode(s.Settings, mode, s.EnvSecretKey) |
| 147 | if !ok { |
| 148 | writeJSONError(w, http.StatusServiceUnavailable, "payment not configured") |
| 149 | return |
| 150 | } |
| 151 | |
| 152 | var planID, email, subToken string |
| 153 | |
| 154 | ct := r.Header.Get("Content-Type") |
| 155 | if strings.Contains(ct, "application/json") { |
| 156 | var body struct { |
| 157 | PlanID string `json:"plan_id"` |
| 158 | Email string `json:"email"` |
| 159 | SubToken string `json:"sub_token"` |
| 160 | } |
| 161 | if err := json.NewDecoder(r.Body).Decode(&body); err != nil { |
| 162 | writeJSONError(w, http.StatusBadRequest, "invalid json") |
| 163 | return |
| 164 | } |
| 165 | planID = body.PlanID |
| 166 | email = body.Email |
| 167 | subToken = body.SubToken |
| 168 | } else { |
| 169 | if err := r.ParseForm(); err != nil { |
| 170 | writeJSONError(w, http.StatusBadRequest, "invalid form data") |
| 171 | return |
| 172 | } |
| 173 | planID = r.FormValue("plan_id") |
| 174 | email = r.FormValue("email") |
| 175 | subToken = r.FormValue("sub_token") |
| 176 | } |
| 177 | |
| 178 | if planID == "" || email == "" { |
| 179 | writeJSONError(w, http.StatusBadRequest, "plan_id and email are required") |
| 180 | return |
| 181 | } |
| 182 | if !isValidEmail(email) { |
| 183 | writeJSONError(w, http.StatusBadRequest, "invalid email format") |
| 184 | return |
| 185 | } |
| 186 | |
| 187 | plan, err := s.PlanStore.GetPlan(planID) |
| 188 | if err != nil { |
| 189 | writeJSONError(w, http.StatusNotFound, "plan not found") |
| 190 | return |
| 191 | } |
| 192 | if !plan.Enabled { |
| 193 | writeJSONError(w, http.StatusBadRequest, "plan is not available") |
| 194 | return |
| 195 | } |
| 196 | if plan.StockLimit != -1 && plan.StockSold >= plan.StockLimit { |
no test coverage detected