> For the complete documentation index, see [llms.txt](https://docs.gameket.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gameket.io/introduction/authentication/token-caching.md).

# Token Caching

{% tabs %}
{% tab title="Javascript" %}
{% code expandable="true" %}

```javascript
class TokenManager {
  constructor() {
    this.token = null;
    this.expiresAt = null;
  }
  
  async getToken() {
    // Use cached token if still valid
    if (this.token && Date.now() < this.expiresAt - 60000) {
      return this.token;
    }
    
    // Request new token
    const response = await this.requestToken();
    this.token = response.token;
    this.expiresAt = new Date(response.expiresAt).getTime();
    
    return this.token;
  }
  
  async requestToken() {
    const response = await fetch('https://api.gameket.io/merchant/auth/check', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        apiKey: process.env.MERCHANT_API_KEY,
        secret: process.env.MERCHANT_SECRET
      })
    });
    
    if (!response.ok) throw new Error('Token request failed');
    const { data } = await response.json();
    return data;
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code expandable="true" %}

```python
from datetime import datetime, timedelta
import requests

class TokenManager:
    def __init__(self, api_key, secret):
        self.api_key = api_key
        self.secret = secret
        self.token = None
        self.expires_at = None
    
    def get_token(self):
        """Get cached token or request a new one"""
        if self.token and datetime.now() < self.expires_at - timedelta(seconds=60):
            return self.token
        
        response = requests.post('https://api.gameket.io/merchant/auth/check', json={
            'apiKey': self.api_key,
            'secret': self.secret
        })
        
        data = response.json()['data']
        self.token = data['token']
        self.expires_at = datetime.fromisoformat(data['expiresAt'].replace('Z', '+00:00'))
        
        return self.token
```

{% endcode %}
{% endtab %}

{% tab title="Rust" %}
{% code expandable="true" %}

```rust
use tokio::sync::Mutex;
use std::sync::Arc;
use chrono::{DateTime, Utc, Duration};
use serde_json::json;

pub struct TokenCache {
    token: Arc<Mutex<Option<String>>>,
    expires_at: Arc<Mutex<Option<DateTime<Utc>>>>,
    client: reqwest::Client,
    api_key: String,
    secret: String,
}

impl TokenCache {
    pub fn new(api_key: String, secret: String) -> Self {
        Self {
            token: Arc::new(Mutex::new(None)),
            expires_at: Arc::new(Mutex::new(None)),
            client: reqwest::Client::new(),
            api_key,
            secret,
        }
    }
    
    pub async fn get_token(&self) -> Result<String, Box<dyn std::error::Error>> {
        let mut token_guard = self.token.lock().await;
        let mut expires_guard = self.expires_at.lock().await;
        
        let now = Utc::now();
        let threshold = now + Duration::seconds(60);
        
        // Return cached token if still valid
        if let (Some(t), Some(e)) = (token_guard.as_ref(), expires_guard.as_ref()) {
            if *e > threshold {
                return Ok(t.clone());
            }
        }
        
        // Request new token
        let response = self.client
            .post("https://api.gameket.io/merchant/auth/check")
            .json(&json!({
                "apiKey": self.api_key,
                "secret": self.secret
            }))
            .send()
            .await?;

        let data: serde_json::Value = response.json().await?;
        let new_token = data["data"]["token"].as_str().unwrap().to_string();
        let expires_str = data["data"]["expiresAt"].as_str().unwrap();
        let new_expires = DateTime::parse_from_rfc3339(expires_str)?
            .with_timezone(&Utc);

        *token_guard = Some(new_token.clone());
        *expires_guard = Some(new_expires);

        Ok(new_token)
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Environment Variables

Store these securely in your `.env` file:

```bash
MERCHANT_API_KEY=your_merchant_api_key_here
MERCHANT_SECRET=your_merchant_secret_here
MERCHANT_API_BASE_URL=https://api.gameket.io
```

**Never commit `.env` to version control!** Add it to `.gitignore`.

### API Credentials Dashboard

Your API credentials are available in:

1. Log in to shop.gameket.io
2. Go to **Dashboard → Merchant**
3. View or regenerate your `apiKey` and `secret`
