---
title: OAuth Callback API
description: Yetkilendirme tamamlandığında çağrılan callback API implementasyonu
---

Mağaza sahibi uygulamayı mağazasına yüklemeyi kabul ettikten sonra çağrılan ikinci API'dir.

## ikas API Helpers

Callback oluşturmadan önce `getIkas` fonksiyonunu implementasyon etmemiz gerekiyor çünkü callback API'sinde kullanılacak.

`lib/ikas-api.ts` dosyasını oluşturun:

```typescript title="File: lib/ikas-api.ts"
import { ikas, OAuthAPI, OnCheckTokenCallback } from '@ikas/api-client';
import moment from 'moment';
import { AuthToken, RedisDB } from './redis';
import { config } from '@/globals/config';

/**
 * ikas API çağrısı yapmadan önce yeni bir `ikas` objesi oluşturan helper fonksiyon
 */
export function getIkas(token: AuthToken): ikas<AuthToken> {
  return new ikas({
    clientId: config.appId,
    clientSecret: config.appSecret,
    accessToken: token.access_token,
    tokenData: token,
    onCheckToken,
  });
}

/**
 * Token süre kontrolü ve refresh işlemi için callback fonksiyon
 */
const onCheckToken: OnCheckTokenCallback<AuthToken> = async (token) => {
  if (token) {
    const now = new Date();
    const expireDate = new Date(token.expireDate);
    
    if (now.getTime() >= expireDate.getTime()) {
      const response = await OAuthAPI.refreshToken(
        {
          refresh_token: token.refresh_token,
          client_id: config.appId,
          client_secret: config.appSecret,
        },
        { storeName: 'api' }
      );
      
      if (response.data) {
        const expireDate = moment().add(response.data.expires_in, 'seconds').toDate().toISOString();
        token.access_token = response.data.access_token;
        token.refresh_token = response.data.refresh_token;
        token.token_type = response.data.token_type;
        token.expires_in = response.data.expires_in;
        token.expireDate = expireDate;
        
        await RedisDB.token.set(token.authorizedAppId, token);
        return { accessToken: token.access_token, tokenData: token };
      }
    }
  }
  return { accessToken: undefined };
};
```

## Callback API Handler

`app/api/oauth/callback/route.ts` dosyasını oluşturun:

```typescript title="File: app/api/oauth/callback/route.ts"
import { NextRequest, NextResponse } from 'next/server';
import { OAuthAPI, WebhookScope } from '@ikas/api-client';
import moment from 'moment';
import { AuthToken, RedisDB } from '@/lib/redis';
import { config } from '@/globals/config';
import { getIkas } from '@/lib/ikas-api';

export async function GET(request: NextRequest) {
  try {
    const searchParams = request.nextUrl.searchParams;
    const code = searchParams.get('code');
    const storeName = searchParams.get('storeName');
    const state = searchParams.get('state');

    // Parametreleri doğrula
    if (!code || !storeName || !state) {
      const failUrl = new URL('/authorize-store', request.url);
      failUrl.searchParams.set('storeName', storeName || '');
      failUrl.searchParams.set('status', 'fail');
      return NextResponse.redirect(failUrl);
    }

    // State doğrulaması
    const savedState = await RedisDB.state.get(state);
    if (state !== savedState) {
      const failUrl = new URL('/authorize-store', request.url);
      failUrl.searchParams.set('storeName', storeName);
      failUrl.searchParams.set('status', 'fail');
      return NextResponse.redirect(failUrl);
    }

    // Authorization code ile token al
    const codeResponse = await OAuthAPI.getTokenWithAuthorizationCode(
      {
        code,
        client_id: config.appId,
        client_secret: config.appSecret,
        redirect_uri: config.callbackUrl,
      },
      { storeName }
    );

    if (codeResponse?.status === 200 && codeResponse.data) {
      const expireDate = moment().add(codeResponse.data.expires_in, 'seconds').toDate().toISOString();
      const token: AuthToken = {
        ...codeResponse.data,
        expireDate,
        authorizedAppId: '',
      };

      const ikasClient = getIkas(token);
      const meResponse = await ikasClient.adminApi.queries.me({});

      if (meResponse.isSuccess && meResponse.data) {
        token.authorizedAppId = meResponse.data.id!;
        
        // Token'ı kaydet
        await RedisDB.token.set(token.authorizedAppId, token);

        // Webhook kaydı (opsiyonel)
        const webhookRes = await ikasClient.adminApi.mutations.saveWebhook({
          variables: {
            input: {
              endpoint: `${config.deployUrl}/api/ikas/webhook`,
              scopes: [WebhookScope.PRODUCT_CREATED],
            },
          },
        });

        if (!webhookRes.isSuccess) {
          console.error('Webhook save error', webhookRes.error || webhookRes.errors);
        }

        // ikas admin dashboard'a yönlendir
        return NextResponse.redirect(`https://${storeName}.myikas.com/admin/authorized-app/${token.authorizedAppId}`);
      }
    }

    // Hata durumunda
    const failUrl = new URL('/authorize-store', request.url);
    failUrl.searchParams.set('storeName', storeName);
    failUrl.searchParams.set('status', 'fail');
    return NextResponse.redirect(failUrl);

  } catch (error: any) {
    console.error('Callback error:', error);
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}
```

## Kod Açıklaması

### State Doğrulaması
```typescript
const savedState = await RedisDB.state.get(state);
if (state !== savedState) {
  // Hata durumunda authorize-store sayfasına yönlendir
}
```

Redis Storage'dan `state` değişkenini alıp request'ten gelen ile karşılaştırıyoruz. Eşleşmezse yetkilendirme sayfasına geri yönlendiriyoruz.

### Token Alma
```typescript
const codeResponse = await OAuthAPI.getTokenWithAuthorizationCode({
  code,
  client_id: config.appId,
  client_secret: config.appSecret,
  redirect_uri: config.callbackUrl,
}, { storeName });
```

State doğrulandıktan sonra `access_token` almak için `OAuthAPI.getTokenWithAuthorizationCode` fonksiyonunu kullanıyoruz.

### Token Kaydetme
```typescript
const expireDate = moment().add(codeResponse.data.expires_in, 'seconds').toDate().toISOString();
const token: AuthToken = { ...codeResponse.data, expireDate, authorizedAppId: '' };

const ikasClient = getIkas(token);
const meResponse = await ikasClient.adminApi.queries.me({});

if (meResponse.isSuccess && meResponse.data) {
  token.authorizedAppId = meResponse.data.id!;
  await RedisDB.token.set(token.authorizedAppId, token);
}
```

Token'ın süre bilgisini hesaplayıp `AuthToken` objesi oluşturuyoruz. Sonra `me` query'si ile token'ı test edip `authorizedAppId`'yi alıyoruz ve Redis'e kaydediyoruz.

### Webhook Kaydı (Opsiyonel)
```typescript
const webhookRes = await ikasClient.adminApi.mutations.saveWebhook({
  variables: {
    input: {
      endpoint: `${config.deployUrl}/api/ikas/webhook`,
      scopes: [WebhookScope.PRODUCT_CREATED],
    },
  },
});
```

Ürün oluşturma webhook'larına kayıt olmak için `saveWebhook` mutation'ını kullanıyoruz.

### Dashboard'a Yönlendirme
```typescript
return NextResponse.redirect(`https://${storeName}.myikas.com/admin/authorized-app/${token.authorizedAppId}`);
```

Bu uygulama ikas dashboard'da iframe içinde çalıştığı için kullanıcıyı ikas dashboard authorized app sayfasına yönlendiriyoruz.

<Callout type="info" title="OAuth Flow Tamamlandı">
Bu adımla OAuth2 Authorization Code Flow tamamlanmış olur ve uygulama mağazaya yetki ile bağlanır.
</Callout>