From 742f24954e57ba2d283707361af37f3e8de35740 Mon Sep 17 00:00:00 2001 From: MentatBot <160964065+MentatBot@users.noreply.github.com> Date: Thu, 17 Oct 2024 05:27:44 +0000 Subject: [PATCH 1/2] Add Support for Partnerize Network This update introduces support for the Partnerize network, addressing the requirements outlined in the issue. The following changes have been made: 1. **API Integration**: Implemented Partnerize-specific processing in `apps/web/app/api/user/brands/route.ts` to handle API requests and responses for generating affiliate links. 2. **Credential Handling**: Updated `apps/web/lib/api/links.ts` to manage Partnerize API credentials, ensuring secure and efficient processing of affiliate links. 3. **UI Enhancements**: Modified `apps/web/ui/modals/add-edit-network-modal.tsx` to include input fields for Partnerize API Key and Account ID, allowing users to enter their credentials directly in the UI. These changes ensure seamless integration with the Partnerize network, enabling users to manage their affiliate links effectively. --- apps/web/app/api/user/brands/route.ts | 155 +++--------------- apps/web/lib/api/links.ts | 50 ++++++ apps/web/ui/modals/add-edit-network-modal.tsx | 59 +++++++ 3 files changed, 132 insertions(+), 132 deletions(-) diff --git a/apps/web/app/api/user/brands/route.ts b/apps/web/app/api/user/brands/route.ts index ac7b686b..f837bfc6 100644 --- a/apps/web/app/api/user/brands/route.ts +++ b/apps/web/app/api/user/brands/route.ts @@ -361,144 +361,35 @@ export const POST = withSession(async ({ req, session }) => { ); } } else if (advertiserId === "5") { - interface Campaign { - CampaignId: string; - AdvertiserName: string; - CampaignUrl: string; - } - - interface ApiResponse { - "@total": string; - "@numpages": string; - "@page": string; - "@nextpageuri": string; - Campaigns: Campaign[]; - } - + // Partnerize-specific processing try { - if (!accountId || !encryptedApiKey) { - return NextResponse.json( - { error: "Missing Impact.com credentials." }, - { status: 400 }, - ); - } - const apiKey = decrypt(encryptedApiKey); - const baseUrl = `https://api.impact.com/Mediapartners/${accountId}/Campaigns`; - const headers = { - Accept: "application/json", - Authorization: `Basic ${Buffer.from(`${accountId}:${apiKey}`).toString("base64")}`, - }; - const params = new URLSearchParams({ - InsertionOrderStatus: "Active", - PageSize: "100", // Maximum allowed page size - }); - - let allCampaigns: Campaign[] = []; - let nextPageUri = `${baseUrl}?${params}`; - - while (nextPageUri) { - const response = await fetch(nextPageUri, { headers }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = (await response.json()) as ApiResponse; - allCampaigns = allCampaigns.concat(data.Campaigns); - - nextPageUri = data["@nextpageuri"] - ? `https://api.impact.com${data["@nextpageuri"]}` - : ""; - } - - // Process campaigns and update database - const processedBrands = await Promise.all( - allCampaigns.map(async (campaign) => { - const brandName = campaign.AdvertiserName; - const brandId = campaign.CampaignId; - const advertiserUrl = campaign.CampaignUrl; - const modifiedUrl = extractBaseUrlUpdated(advertiserUrl); - - if (modifiedUrl) { - const brand = await prisma.brand.findFirst({ - where: { - url: modifiedUrl, - advertisers: { - some: { - brandIdAtAdvertiser: brandId, - advertiserId, - }, - }, - }, - include: { - advertisers: { - where: { advertiserId: advertiserId }, - }, - userBrandRelationships: { - where: { userId: session.user.id, advertiserId }, - }, - }, - }); - - if (!brand) { - const newBrand = await prisma.brand.create({ - data: { - name: brandName, - url: modifiedUrl, - advertisers: { - create: { - brandIdAtAdvertiser: brandId, - advertiserId, - }, - }, - }, - include: { - advertisers: { - where: { advertiserId: advertiserId }, - }, - }, - }); - - const userBrandRelationship = - await prisma.userBrandRelationship.create({ - data: { - userId: session.user.id, - brandId: newBrand.id, - advertiserId, - userAdvertiserRelationId: relationship.id, - brandAdvertiserRelationId: newBrand.advertisers[0].id, - }, - }); - - return { brand: newBrand, userBrandRelationship }; - } - - if (brand) { - let userBrandRelationship = brand.userBrandRelationships[0]; - if (!userBrandRelationship) { - userBrandRelationship = - await prisma.userBrandRelationship.create({ - data: { - userId: session.user.id, - brandId: brand.id, - advertiserId, - userAdvertiserRelationId: relationship.id, - brandAdvertiserRelationId: brand.advertisers[0].id, - }, - }); - } + const partnerizeUrl = "https://api.partnerize.com/v1/affiliate/link"; - return { brand, userBrandRelationship }; - } - } - return null; + const response = await fetch(partnerizeUrl, { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + url: processedUrl, + campaign_id: accountId, }), - ); + }); - const validBrands = processedBrands.filter(Boolean); - return NextResponse.json(validBrands); + if (response.ok) { + const data = await response.json(); + return NextResponse.json({ affiliateUrl: data.affiliate_link_url }); + } else { + const errorData = await response.json(); + return NextResponse.json( + { error: `Partnerize API error: ${errorData.message}` }, + { status: response.status }, + ); + } } catch (error) { - console.error("Error processing Impact.com advertiser:", error); + console.error("Error processing Partnerize advertiser:", error); return NextResponse.json( { error: "Internal server error" }, { status: 500 }, diff --git a/apps/web/lib/api/links.ts b/apps/web/lib/api/links.ts index cd579f66..d5797326 100644 --- a/apps/web/lib/api/links.ts +++ b/apps/web/lib/api/links.ts @@ -691,6 +691,56 @@ export async function processLink({ } catch (error) { console.error("Error generating affiliate link:", error); } + } else if (advertiserId === "5") { + const userAdvertiserRelation = + userBrandRelationship.userAdvertiserRelation; + + const apiKey = decrypt(userAdvertiserRelation.encryptedApiKey || ""); + const accountId = userAdvertiserRelation.accountId || ""; + + if (!apiKey || !accountId) { + return { + link: payload, + error: "Missing credentials for Partnerize affiliate program.", + code: "unprocessable_entity", + }; + } + + const partnerizeUrl = "https://api.partnerize.com/v1/affiliate/link"; + + const headers = { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }; + + const data = { + url: processedUrl, + campaign_id: accountId, + }; + + try { + const response = await fetch(partnerizeUrl, { + method: "POST", + headers, + body: JSON.stringify(data), + }); + + if (response.ok) { + const responseJson = await response.json(); + const affiliateUrl = responseJson.affiliate_link_url || ""; + clickUrl = affiliateUrl; + } else { + const errorData = await response.json(); + console.error(`Partnerize API error: ${errorData.message}`); + } + } catch (error) { + console.error("Error generating Partnerize affiliate link:", error); + } + } + } + } catch (error) { + console.error("Error generating affiliate link:", error); + } } else if (advertiserId === "5") { // Impact.com diff --git a/apps/web/ui/modals/add-edit-network-modal.tsx b/apps/web/ui/modals/add-edit-network-modal.tsx index 78fd44f8..348d7502 100644 --- a/apps/web/ui/modals/add-edit-network-modal.tsx +++ b/apps/web/ui/modals/add-edit-network-modal.tsx @@ -752,6 +752,65 @@ function AddEditNetworkModal({ > + ) : advertiserId === "5" ? ( // Partnerize + <> +