211 lines
6.7 KiB
Markdown
211 lines
6.7 KiB
Markdown
# Kamatera Free Trial — Registration & Billing API
|
|
|
|
## Overview
|
|
|
|
Kamatera offers a 30-day free trial ($100 cloud credit) with payment card required. The registration and billing flow is entirely API-driven through a WordPress-based backend.
|
|
|
|
## Registration
|
|
|
|
### Endpoint (WordPress AJAX)
|
|
```
|
|
POST https://www.kamatera.com/wp-admin/admin-ajax.php
|
|
Content-Type: application/x-www-form-urlencoded
|
|
```
|
|
|
|
### jQuery Serialization Format
|
|
The form uses jQuery's `$.ajax` with nested objects. Key format: `data_options[field]=value`
|
|
|
|
```
|
|
action=cwm_api_action
|
|
data_options[cases]=cwml
|
|
data_options[email]=...@domain.com
|
|
data_options[password]=...
|
|
data_options[orgUrl]=https://www.kamatera.com/free-trial/
|
|
data_options[formButtonText]=Create free account
|
|
data_options[submitText]=Create free account
|
|
data_options[headerText]=
|
|
```
|
|
|
|
### Email Validation
|
|
- Client-side regex: `/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/`
|
|
- **`+` aliases NOT allowed** (Gmail `+suffix` fails `\w` check)
|
|
- Use plain emails or disposable email domains
|
|
|
|
### Password Requirements
|
|
- At least 1 lowercase
|
|
- At least 1 uppercase
|
|
- At least 1 number
|
|
- At least 8 characters
|
|
- Less than 28 characters
|
|
|
|
### Response
|
|
```json
|
|
{
|
|
"status": 0, // 0 = success, 13 = blocked/captcha fail
|
|
"token": "...",
|
|
"existing": false, // true if account already exists
|
|
"url": "https://console.kamatera.com/al.php?t=...",
|
|
"validationToken": "...",
|
|
"impactid": "kt-..."
|
|
}
|
|
```
|
|
|
|
Status 13 with `url: null` = blocked (likely reCAPTCHA v3 score too low, or IP flagged).
|
|
|
|
## Email Verification
|
|
|
|
Verification email comes from `support@kamatera.com` with subject "Confirm your Kamatera cloud account". Contains a link:
|
|
```
|
|
https://console.kamatera.com/rd.php?t=...&e=...&p=emailValidation&h=...
|
|
```
|
|
Click the link → redirects to `validateSuccessful?email=...` → account verified.
|
|
|
|
## Disposable Email
|
|
|
|
**mail.tm** works reliably:
|
|
```python
|
|
# 1. Get domain
|
|
r = requests.get("https://api.mail.tm/domains")
|
|
domain = r.json()["hydra:member"][0]["domain"]
|
|
|
|
# 2. Create account
|
|
r = requests.post("https://api.mail.tm/accounts", json={
|
|
"address": f"{username}@{domain}",
|
|
"password": "password"
|
|
})
|
|
|
|
# 3. Get token
|
|
r = requests.post("https://api.mail.tm/token", json={
|
|
"address": email, "password": "password"
|
|
})
|
|
token = [REDACTED])["token"]
|
|
|
|
# 4. Read messages
|
|
r = requests.get("https://api.mail.tm/messages",
|
|
headers={"Authorization": f"Bearer {token}"})
|
|
```
|
|
|
|
**GuerrillaMail** works but Kamatera emails may not arrive to `.guerrillamailblock.com` — use mail.tm instead.
|
|
|
|
## Console API
|
|
|
|
### Base URL & Auth
|
|
```
|
|
Base: https://console.kamatera.com/svc/
|
|
Headers:
|
|
Content-Type: application/json
|
|
Accept: application/json
|
|
api-version: latest
|
|
token: <CSRF_TOKEN>
|
|
```
|
|
|
|
### CSRF Token
|
|
Get from `/csrf` endpoint:
|
|
```python
|
|
r = requests.get("https://console.kamatera.com/csrf")
|
|
csrf = re.search(r'CSRF_TOKEN",\s*"([^"]+)"', r.text).group(1)
|
|
```
|
|
|
|
The `/csrf` page returns HTML that sets `sessionStorage.setItem("CSRF_TOKEN", "...")`. The token is embedded in the HTML as a script tag.
|
|
|
|
### Login Flow (with 2FA)
|
|
```
|
|
1. POST /svc/login {"email", "password"}
|
|
→ {"email": "...", "mediateToken": "..."}
|
|
|
|
2. POST /svc/sendtfa {"email", "mediateToken"} (header: token=<csrf>)
|
|
→ triggers email with 6-digit code
|
|
|
|
3. Check mail.tm for code in subject "Kamatera Cloud Platform: 2-Step Verification"
|
|
|
|
4. POST /svc/logintfa {"email", "code", "rememberBrowser": true, "mediateToken"}
|
|
→ 200 = logged in, session cookie set
|
|
→ 400 + code 51 = "Invalid code" (code expired or wrong)
|
|
```
|
|
|
|
### TFA Code Pitfalls
|
|
- Codes expire quickly (~2-5 minutes)
|
|
- `sendtfa` may return 200 but NOT send a new email if one was already sent recently
|
|
- Delete old emails from mail.tm before calling `sendtfa` to avoid picking up stale codes
|
|
- The 2FA email body contains the code in format: `Your access code ... is: XXXXXX`
|
|
- Filter out false positives: `1999` and `2026` are in the email footer, not the code
|
|
|
|
### Billing Profile API
|
|
```
|
|
PUT /svc/billing/profile (header: token=<csrf>)
|
|
```
|
|
|
|
### Billing Profile Fields (Discovered via Progressive 400 Errors)
|
|
The API validates fields in sequence. Each fix reveals the next missing field:
|
|
|
|
| Error Code | Message | Required Fix |
|
|
|-----------|---------|-------------|
|
|
| 122 | invalid invoice name | Add `invoiceName` |
|
|
| 105 | invalid first name | Add `firstName` |
|
|
| 107 | invalid country | Fix `country` format |
|
|
| 110 | invalid city | Add `city` |
|
|
| 112 | invalid zip code | Use `postalCode` (NOT `zipCode` or `zip`) |
|
|
| 109 | invalid phone | Add `phone` |
|
|
| 21 | generic error | Card rejected by payment processor |
|
|
|
|
### Working Billing Payload (Passes All Validation)
|
|
```json
|
|
{
|
|
"firstName": "Alex",
|
|
"lastName": "Morgan",
|
|
"country": "US",
|
|
"city": "New York",
|
|
"address": "123 Broadway",
|
|
"state": "NY",
|
|
"postalCode": "10001",
|
|
"phone": "2125550100",
|
|
"invoiceName": "Alex Morgan",
|
|
"paymentType": 0,
|
|
"cardNumber": "4874100085748500",
|
|
"cardExpiry": "05/2030",
|
|
"cardCvv": "617"
|
|
}
|
|
```
|
|
|
|
### Error Code 21 (Card Rejected)
|
|
After all field validation passes, error 21 means the payment processor rejected the card. This is NOT a field format issue. Possible causes:
|
|
- Card blocked/stolen
|
|
- Insufficient funds for $1 authorization hold
|
|
- 3D Secure required but not supported
|
|
- Region mismatch (card issuing country ≠ billing country)
|
|
- Card expired (check BIN + expiry date format)
|
|
|
|
### Card Expiry Formats Tried
|
|
- `cardExpiry: "05/2030"` — works (passes to processor)
|
|
- `cardExpiry: "05/30"` — works (passes to processor)
|
|
- Separate `expMonth`/`expYear` fields — also pass validation
|
|
|
|
All formats pass field validation — the rejection is at the payment processor level.
|
|
|
|
## Browser-Based API Calls
|
|
|
|
When the React SPA is unresponsive (clicks don't navigate), use `fetch()` from browser console with the session's CSRF token:
|
|
|
|
[REDACTED]
|
|
fetch('/svc/billing/profile', {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'api-version': 'latest',
|
|
'token': sessionStorage.getItem('CSRF_TOKEN')
|
|
},
|
|
body: JSON.stringify({...})
|
|
}).then(r => r.json()).then(d => console.log(JSON.stringify(d)))
|
|
```
|
|
|
|
This bypasses the broken React navigation and uses the browser's authenticated session directly.
|
|
|
|
## Key Pitfalls
|
|
|
|
1. **Email `+` aliases fail client-side validation** — use plain email or disposable domain
|
|
2. **`sendtfa` doesn't always send** — delete old emails before calling, codes may have cooldown
|
|
3. **React SPA navigation broken in headless** — use direct `fetch()` from console
|
|
4. **Field names are specific**: `postalCode` not `zipCode`, `invoiceName` required, `firstName`/`lastName` not `name`
|
|
5. **Country format**: `"US"` (2-letter ISO) works, `"USA"` fails
|
|
6. **Card validation is sequential** — fix errors one at a time, each reveals the next |