Skip to content

Commit 91ffd21

Browse files
committed
task: Use Stripe PaymentIntents API for credit history
1 parent c976054 commit 91ffd21

File tree

17 files changed

+703
-289
lines changed

17 files changed

+703
-289
lines changed

bifrost/lib/clients/jawnTypes/private.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ export interface paths {
9797
"/v1/stripe/subscription/migrate-to-pro": {
9898
post: operations["MigrateToPro"];
9999
};
100+
"/v1/stripe/payment-intents/search": {
101+
get: operations["SearchPaymentIntents"];
102+
};
100103
"/v1/stripe/subscription": {
101104
get: operations["GetSubscription"];
102105
};
@@ -16305,6 +16308,23 @@ export interface operations {
1630516308
};
1630616309
};
1630716310
};
16311+
SearchPaymentIntents: {
16312+
parameters: {
16313+
query: {
16314+
search_kind: string;
16315+
limit?: number;
16316+
page?: string;
16317+
};
16318+
};
16319+
responses: {
16320+
/** @description Ok */
16321+
200: {
16322+
content: {
16323+
"application/json": unknown;
16324+
};
16325+
};
16326+
};
16327+
};
1630816328
GetSubscription: {
1630916329
responses: {
1631016330
/** @description Ok */

bifrost/lib/clients/jawnTypes/public.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ export interface paths {
336336
"/v1/stripe/subscription/migrate-to-pro": {
337337
post: operations["MigrateToPro"];
338338
};
339+
"/v1/stripe/payment-intents/search": {
340+
get: operations["SearchPaymentIntents"];
341+
};
339342
"/v1/stripe/subscription": {
340343
get: operations["GetSubscription"];
341344
};
@@ -5742,6 +5745,23 @@ export interface operations {
57425745
};
57435746
};
57445747
};
5748+
SearchPaymentIntents: {
5749+
parameters: {
5750+
query: {
5751+
search_kind: string;
5752+
limit?: number;
5753+
page?: string;
5754+
};
5755+
};
5756+
responses: {
5757+
/** @description Ok */
5758+
200: {
5759+
content: {
5760+
"application/json": unknown;
5761+
};
5762+
};
5763+
};
5764+
};
57455765
GetSubscription: {
57465766
responses: {
57475767
/** @description Ok */

docs/swagger.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16642,6 +16642,56 @@
1664216642
"parameters": []
1664316643
}
1664416644
},
16645+
"/v1/stripe/payment-intents/search": {
16646+
"get": {
16647+
"operationId": "SearchPaymentIntents",
16648+
"responses": {
16649+
"200": {
16650+
"description": "Ok",
16651+
"content": {
16652+
"application/json": {
16653+
"schema": {}
16654+
}
16655+
}
16656+
}
16657+
},
16658+
"tags": [
16659+
"Stripe"
16660+
],
16661+
"security": [
16662+
{
16663+
"api_key": []
16664+
}
16665+
],
16666+
"parameters": [
16667+
{
16668+
"in": "query",
16669+
"name": "search_kind",
16670+
"required": true,
16671+
"schema": {
16672+
"type": "string"
16673+
}
16674+
},
16675+
{
16676+
"in": "query",
16677+
"name": "limit",
16678+
"required": false,
16679+
"schema": {
16680+
"format": "double",
16681+
"type": "number"
16682+
}
16683+
},
16684+
{
16685+
"in": "query",
16686+
"name": "page",
16687+
"required": false,
16688+
"schema": {
16689+
"type": "string"
16690+
}
16691+
}
16692+
]
16693+
}
16694+
},
1664516695
"/v1/stripe/subscription": {
1664616696
"get": {
1664716697
"operationId": "GetSubscription",

valhalla/jawn/src/controllers/public/stripeController.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type { JawnAuthenticatedRequest } from "../../types/request";
1616
import { isError } from "../../packages/common/result";
1717
import express from "express";
1818
import { checkFeatureFlag } from "../../lib/utils/featureFlags";
19+
import Stripe from "stripe";
1920

2021
export interface UpgradeToProRequest {
2122
addons?: {
@@ -36,6 +37,23 @@ export interface CreateCloudGatewayCheckoutSessionRequest {
3637
amount: number;
3738
}
3839

40+
export enum PaymentIntentSearchKind {
41+
CREDIT_PURCHASES = "credit_purchases",
42+
}
43+
44+
export interface SearchPaymentIntentsRequest {
45+
search_kind: PaymentIntentSearchKind;
46+
limit?: number;
47+
page?: string;
48+
}
49+
50+
export interface StripePaymentIntentsResponse {
51+
data: Stripe.PaymentIntent[];
52+
has_more: boolean;
53+
next_page: string | null;
54+
count: number;
55+
}
56+
3957

4058
export interface LLMUsage {
4159
model: string;
@@ -363,6 +381,35 @@ export class StripeController extends Controller {
363381
const result = await stripeManager.migrateToPro();
364382
}
365383

384+
@Get("/payment-intents/search")
385+
public async searchPaymentIntents(
386+
@Request() request: JawnAuthenticatedRequest,
387+
@Query() search_kind: string,
388+
@Query() limit?: number,
389+
@Query() page?: string
390+
): Promise<any> {
391+
// Check if search_kind is valid
392+
if (!Object.values(PaymentIntentSearchKind).includes(search_kind as PaymentIntentSearchKind)) {
393+
this.setStatus(400);
394+
throw new Error(`Invalid search_kind: ${search_kind}. Supported types: ${Object.values(PaymentIntentSearchKind).join(", ")}`);
395+
}
396+
397+
const searchKind = search_kind as PaymentIntentSearchKind;
398+
const stripeManager = new StripeManager(request.authParams);
399+
const result = await stripeManager.searchPaymentIntents(
400+
searchKind,
401+
limit ?? 10,
402+
page
403+
);
404+
405+
if (isError(result)) {
406+
this.setStatus(500);
407+
throw new Error(result.error);
408+
}
409+
410+
return result.data;
411+
}
412+
366413
@Get("/subscription")
367414
public async getSubscription(
368415
@Request() request: JawnAuthenticatedRequest

valhalla/jawn/src/managers/stripe/StripeManager.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
LLMUsage,
44
UpgradeToProRequest,
55
UpgradeToTeamBundleRequest,
6+
StripePaymentIntentsResponse,
7+
PaymentIntentSearchKind,
68
} from "../../controllers/public/stripeController";
79
import { clickhouseDb } from "../../lib/db/ClickhouseWrapper";
810
import { Database } from "../../lib/db/database.types";
@@ -1345,5 +1347,57 @@ WHERE (${builtFilter.filter})`,
13451347
return err(`Error retrieving purchased seats: ${error.message}`);
13461348
}
13471349
}
1350+
1351+
public async searchPaymentIntents(
1352+
searchKind: PaymentIntentSearchKind,
1353+
limit: number = 10,
1354+
page?: string
1355+
): Promise<Result<StripePaymentIntentsResponse, string>> {
1356+
try {
1357+
let query: string;
1358+
1359+
// Build query based on search kind
1360+
switch (searchKind) {
1361+
case PaymentIntentSearchKind.CREDIT_PURCHASES:
1362+
const productId = process.env.STRIPE_CLOUD_GATEWAY_TOKEN_USAGE_PRODUCT;
1363+
1364+
if (!productId) {
1365+
console.error(
1366+
"[Stripe API] STRIPE_CLOUD_GATEWAY_TOKEN_USAGE_PRODUCT not configured"
1367+
);
1368+
return err("Stripe product ID not configured");
1369+
}
1370+
1371+
query = `metadata['productId']:'${productId}' AND metadata['orgId']:'${this.authParams.organizationId}'`;
1372+
break;
1373+
1374+
default:
1375+
return err(`Unsupported search kind: ${searchKind}`);
1376+
}
1377+
1378+
// Search payment intents using Stripe API
1379+
const searchParams: any = {
1380+
query,
1381+
limit,
1382+
};
1383+
1384+
// Add page parameter if provided (Stripe uses page token for search pagination)
1385+
if (page) {
1386+
searchParams.page = page;
1387+
}
1388+
1389+
const paymentIntents = await this.stripe.paymentIntents.search(searchParams);
1390+
1391+
return ok({
1392+
data: paymentIntents.data,
1393+
has_more: paymentIntents.has_more,
1394+
next_page: paymentIntents.next_page || null,
1395+
count: paymentIntents.data.length,
1396+
});
1397+
} catch (error: any) {
1398+
console.error("Error searching payment intents:", error);
1399+
return err("Failed to search payment intents");
1400+
}
1401+
}
13481402
}
13491403

valhalla/jawn/src/tsoa-build/private/routes.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15352,6 +15352,40 @@ export function RegisterRoutes(app: Router) {
1535215352
}
1535315353
});
1535415354
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
15355+
const argsStripeController_searchPaymentIntents: Record<string, TsoaRoute.ParameterSchema> = {
15356+
request: {"in":"request","name":"request","required":true,"dataType":"object"},
15357+
search_kind: {"in":"query","name":"search_kind","required":true,"dataType":"string"},
15358+
limit: {"in":"query","name":"limit","dataType":"double"},
15359+
page: {"in":"query","name":"page","dataType":"string"},
15360+
};
15361+
app.get('/v1/stripe/payment-intents/search',
15362+
authenticateMiddleware([{"api_key":[]}]),
15363+
...(fetchMiddlewares<RequestHandler>(StripeController)),
15364+
...(fetchMiddlewares<RequestHandler>(StripeController.prototype.searchPaymentIntents)),
15365+
15366+
async function StripeController_searchPaymentIntents(request: ExRequest, response: ExResponse, next: any) {
15367+
15368+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
15369+
15370+
let validatedArgs: any[] = [];
15371+
try {
15372+
validatedArgs = templateService.getValidatedArgs({ args: argsStripeController_searchPaymentIntents, request, response });
15373+
15374+
const controller = new StripeController();
15375+
15376+
await templateService.apiHandler({
15377+
methodName: 'searchPaymentIntents',
15378+
controller,
15379+
response,
15380+
next,
15381+
validatedArgs,
15382+
successStatus: undefined,
15383+
});
15384+
} catch (err) {
15385+
return next(err);
15386+
}
15387+
});
15388+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
1535515389
const argsStripeController_getSubscription: Record<string, TsoaRoute.ParameterSchema> = {
1535615390
request: {"in":"request","name":"request","required":true,"dataType":"object"},
1535715391
};

valhalla/jawn/src/tsoa-build/private/swagger.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48472,6 +48472,56 @@
4847248472
"parameters": []
4847348473
}
4847448474
},
48475+
"/v1/stripe/payment-intents/search": {
48476+
"get": {
48477+
"operationId": "SearchPaymentIntents",
48478+
"responses": {
48479+
"200": {
48480+
"description": "Ok",
48481+
"content": {
48482+
"application/json": {
48483+
"schema": {}
48484+
}
48485+
}
48486+
}
48487+
},
48488+
"tags": [
48489+
"Stripe"
48490+
],
48491+
"security": [
48492+
{
48493+
"api_key": []
48494+
}
48495+
],
48496+
"parameters": [
48497+
{
48498+
"in": "query",
48499+
"name": "search_kind",
48500+
"required": true,
48501+
"schema": {
48502+
"type": "string"
48503+
}
48504+
},
48505+
{
48506+
"in": "query",
48507+
"name": "limit",
48508+
"required": false,
48509+
"schema": {
48510+
"format": "double",
48511+
"type": "number"
48512+
}
48513+
},
48514+
{
48515+
"in": "query",
48516+
"name": "page",
48517+
"required": false,
48518+
"schema": {
48519+
"type": "string"
48520+
}
48521+
}
48522+
]
48523+
}
48524+
},
4847548525
"/v1/stripe/subscription": {
4847648526
"get": {
4847748527
"operationId": "GetSubscription",

valhalla/jawn/src/tsoa-build/public/routes.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7562,6 +7562,40 @@ export function RegisterRoutes(app: Router) {
75627562
}
75637563
});
75647564
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
7565+
const argsStripeController_searchPaymentIntents: Record<string, TsoaRoute.ParameterSchema> = {
7566+
request: {"in":"request","name":"request","required":true,"dataType":"object"},
7567+
search_kind: {"in":"query","name":"search_kind","required":true,"dataType":"string"},
7568+
limit: {"in":"query","name":"limit","dataType":"double"},
7569+
page: {"in":"query","name":"page","dataType":"string"},
7570+
};
7571+
app.get('/v1/stripe/payment-intents/search',
7572+
authenticateMiddleware([{"api_key":[]}]),
7573+
...(fetchMiddlewares<RequestHandler>(StripeController)),
7574+
...(fetchMiddlewares<RequestHandler>(StripeController.prototype.searchPaymentIntents)),
7575+
7576+
async function StripeController_searchPaymentIntents(request: ExRequest, response: ExResponse, next: any) {
7577+
7578+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
7579+
7580+
let validatedArgs: any[] = [];
7581+
try {
7582+
validatedArgs = templateService.getValidatedArgs({ args: argsStripeController_searchPaymentIntents, request, response });
7583+
7584+
const controller = new StripeController();
7585+
7586+
await templateService.apiHandler({
7587+
methodName: 'searchPaymentIntents',
7588+
controller,
7589+
response,
7590+
next,
7591+
validatedArgs,
7592+
successStatus: undefined,
7593+
});
7594+
} catch (err) {
7595+
return next(err);
7596+
}
7597+
});
7598+
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
75657599
const argsStripeController_getSubscription: Record<string, TsoaRoute.ParameterSchema> = {
75667600
request: {"in":"request","name":"request","required":true,"dataType":"object"},
75677601
};

0 commit comments

Comments
 (0)