Expose gateway billing flows from the synced server baseline
The preserved local work adds PopiNewAPI billing checkout, gateway account binding, and matching web console/pricing surfaces on top of the current test-server baseline. During the merge, the start-end video helpers from the deployed baseline were kept alongside the actionGenerate prompt handling from the WIP.
Constraint: Current usable baseline is 7054436, already deployed on the test server.
Rejected: Commit the WIP before syncing the baseline | would have hidden conflicts with the deployed start-end-frame changes.
Confidence: medium
Scope-risk: broad
Directive: Do not deploy this commit to the test server without rechecking gateway credentials and billing checkout behavior in that environment.
Tested: go test ./...
Tested: make build
Tested: cd web && npm run build
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import {
|
||||
SESSION_COOKIE_NAME,
|
||||
type GatewayBindResponse,
|
||||
popiartFetchEnvelope,
|
||||
} from "@/lib/popiart-api";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get(SESSION_COOKIE_NAME)?.value;
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
code: "UNAUTHENTICATED",
|
||||
message: "missing local popiart session",
|
||||
},
|
||||
},
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
let body: { gateway_user_id?: number; gateway_access_token?: string } = {};
|
||||
|
||||
try {
|
||||
body = (await request.json()) as { gateway_user_id?: number; gateway_access_token?: string };
|
||||
} catch {
|
||||
body = {};
|
||||
}
|
||||
|
||||
try {
|
||||
const { response, payload } = await popiartFetchEnvelope<GatewayBindResponse>("/auth/gateway/bind", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
payload ?? {
|
||||
ok: false,
|
||||
error: {
|
||||
code: "SERVER_ERROR",
|
||||
message: "invalid response from popiartServer",
|
||||
},
|
||||
},
|
||||
{ status: response.status },
|
||||
);
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
code: "SERVER_ERROR",
|
||||
message: "failed to reach popiartServer",
|
||||
details: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
},
|
||||
{ status: 502 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import {
|
||||
SESSION_COOKIE_NAME,
|
||||
type BillingCheckoutResult,
|
||||
popiartFetchEnvelope,
|
||||
} from "@/lib/popiart-api";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get(SESSION_COOKIE_NAME)?.value;
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
code: "UNAUTHENTICATED",
|
||||
message: "missing local popiart session",
|
||||
},
|
||||
},
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
let body: {
|
||||
kind?: "subscription" | "points";
|
||||
provider?: string;
|
||||
plan_id?: number;
|
||||
package_id?: number;
|
||||
return_url?: string;
|
||||
} = {};
|
||||
|
||||
try {
|
||||
body = (await request.json()) as typeof body;
|
||||
} catch {
|
||||
body = {};
|
||||
}
|
||||
|
||||
const pathname =
|
||||
body.kind === "subscription"
|
||||
? "/billing/checkout/subscription"
|
||||
: body.kind === "points"
|
||||
? "/billing/checkout/points"
|
||||
: "";
|
||||
|
||||
if (!pathname) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
code: "VALIDATION_ERROR",
|
||||
message: "kind must be subscription or points",
|
||||
},
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const { response, payload } = await popiartFetchEnvelope<BillingCheckoutResult>(pathname, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
payload ?? {
|
||||
ok: false,
|
||||
error: {
|
||||
code: "SERVER_ERROR",
|
||||
message: "invalid response from popiartServer",
|
||||
},
|
||||
},
|
||||
{ status: response.status },
|
||||
);
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
code: "SERVER_ERROR",
|
||||
message: "failed to reach popiartServer",
|
||||
details: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
},
|
||||
{ status: 502 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get(SESSION_COOKIE_NAME)?.value;
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
code: "UNAUTHENTICATED",
|
||||
message: "missing local popiart session",
|
||||
},
|
||||
},
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
const query = new URLSearchParams(url.searchParams);
|
||||
|
||||
try {
|
||||
const { response, payload } = await popiartFetchEnvelope<{
|
||||
message?: string;
|
||||
status?: string;
|
||||
data?: Record<string, unknown>;
|
||||
}>(`/billing/checkout/status?${query.toString()}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
payload ?? {
|
||||
ok: false,
|
||||
error: {
|
||||
code: "SERVER_ERROR",
|
||||
message: "invalid response from popiartServer",
|
||||
},
|
||||
},
|
||||
{ status: response.status },
|
||||
);
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
error: {
|
||||
code: "SERVER_ERROR",
|
||||
message: "failed to reach popiartServer",
|
||||
details: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
},
|
||||
{ status: 502 },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user