Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c670fca
PM-1112 - apply challenge payments at end of challenge
vas3a Sep 8, 2025
99c18b7
code cleanup
vas3a Sep 8, 2025
9dfe7b3
PM-2087 - use Topcoder v6 APIs
vas3a Sep 29, 2025
52ef701
PM-2091 - Drop rewards table
vas3a Sep 29, 2025
3148ae5
PM-2091 - drop payoneer & paypal payment method tables
vas3a Sep 29, 2025
eb8f9f7
Update config.yml
Gunasekar-K Oct 7, 2025
e0323e4
v5 to v6 update
Gunasekar-K Oct 7, 2025
70a0cef
Merge branch 'v6' of github.com:topcoder-platform/tc-finance-api into…
vas3a Oct 8, 2025
90723b9
Merge pull request #79 from topcoder-platform/PM-1112_apply-challenge…
vas3a Oct 8, 2025
cd7df02
Merge branch 'v6' of github.com:topcoder-platform/tc-finance-api into…
vas3a Oct 8, 2025
41d75cc
update sample env
vas3a Oct 8, 2025
cc7edf3
We need to handle a challenge that's in CANCELLED_FAILED_REVIEW statu…
jmgasper Oct 9, 2025
49c4dbe
Fix challenge payments calculations
vas3a Oct 10, 2025
9a03280
Merge pull request #82 from topcoder-platform/fix-payments
vas3a Oct 10, 2025
2312fde
Update reviewer model: use coefficients
vas3a Oct 15, 2025
1f2a1fe
Merge pull request #84 from topcoder-platform/fix-payments
vas3a Oct 15, 2025
06cc6ec
Fix payment for reviewers & more logs
vas3a Oct 17, 2025
307eb89
Merge pull request #87 from topcoder-platform/PM-1112_fix-payment-for…
vas3a Oct 17, 2025
ccce086
Add challenge lock, update logic for reviewer, copilot payments
vas3a Oct 20, 2025
dfacc2c
Merge pull request #80 from topcoder-platform/PM-2091_cleanup-tables
vas3a Oct 22, 2025
686991d
Merge branch 'dev' of github.com:topcoder-platform/tc-finance-api int…
vas3a Oct 22, 2025
eca223e
Merge branch 'v6' of github.com:topcoder-platform/tc-finance-api into…
vas3a Oct 22, 2025
aecfeb8
Merge pull request #81 from topcoder-platform/PM-2087_use-v6-apis
vas3a Oct 22, 2025
c4ee0fe
Merge branch 'v6' of github.com:topcoder-platform/tc-finance-api into…
vas3a Oct 22, 2025
de47623
Merge pull request #88 from topcoder-platform/PM-1112_fix-payment-for…
vas3a Oct 22, 2025
eaabcb2
Add in challenge-payments endpoint for pulling payment details in rev…
jmgasper Oct 22, 2025
92a90df
Merge branch 'dev' of github.com:topcoder-platform/tc-finance-api int…
vas3a Oct 23, 2025
dd9b090
round up reviewers payments
vas3a Oct 23, 2025
bd3a8b2
undo mixup
vas3a Oct 23, 2025
5f2367e
Merge pull request #102 from topcoder-platform/fix-reviewers-payments
vas3a Oct 23, 2025
ab66bce
Reviewer payments: create payment per phase
vas3a Oct 23, 2025
7441a43
Account for iterrative reviews: each review phase has it's own phaseI…
vas3a Oct 23, 2025
fe52f0a
Better handling of unexpected errors when generating challenge review…
vas3a Oct 23, 2025
62b571b
Merge pull request #103 from topcoder-platform/fix-reviewers-payments
vas3a Oct 23, 2025
54f823d
Drop the db transaction when creating winnings
vas3a Oct 24, 2025
d7d3012
Properly use db transaction context
vas3a Oct 24, 2025
668d89f
Merge pull request #104 from topcoder-platform/fix-transaction
vas3a Oct 24, 2025
703b781
PM-1105 - Checkpoint winners
vas3a Oct 24, 2025
b9bfb17
Merge pull request #105 from topcoder-platform/PM-1105_checkpoint-win…
vas3a Oct 24, 2025
8669b20
make sure to return error message
vas3a Oct 27, 2025
8e5835f
PM-2561 - for challenges with prize sets other than USD, skip payment…
vas3a Oct 27, 2025
a47cee7
Merge pull request #109 from topcoder-platform/PM-2561_skip-payments-…
vas3a Oct 27, 2025
d0dd104
PM-2596 - Improved description for copilot & checkpoint winner payments
vas3a Oct 27, 2025
e97e88c
Merge pull request #111 from topcoder-platform/PM-2596_prizes-descrip…
vas3a Oct 27, 2025
8882254
Merge branch 'dev' of github.com:topcoder-platform/tc-finance-api int…
vas3a Oct 27, 2025
69a4c1b
PM-2595 - handle challenge canceled due to failed review
vas3a Oct 28, 2025
65efd48
Merge pull request #112 from topcoder-platform/PM-2595_payments-for-f…
vas3a Oct 28, 2025
41b4914
deploy v6
vas3a Oct 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
environment:
DEPLOY_ENV: 'DEV'
LOGICAL_ENV: 'dev'
APPNAME: 'tc-finance-api'
APPNAME: 'finance-api-v6'
DEPLOYMENT_ENVIRONMENT: 'dev'
steps: *build_and_deploy_steps

Expand All @@ -60,7 +60,7 @@ jobs:
environment:
DEPLOY_ENV: 'PROD'
LOGICAL_ENV: 'prod'
APPNAME: 'tc-finance-api'
APPNAME: 'finance-api-v6'
DEPLOYMENT_ENVIRONMENT: 'prod'
steps: *build_and_deploy_steps

Expand All @@ -74,10 +74,10 @@ workflows:
branches:
only:
- dev
- npm-module-updates
- v6
- 'build-prod':
context: org-global
filters:
branches:
only:
- master
- master
5 changes: 3 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TOPCODER_API_BASE_URL="https://api.topcoder-dev.com/v5"
TOPCODER_API_V5_BASE_URL="https://api.topcoder-dev.com/v5"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
Renaming TOPCODER_API_BASE_URL to TOPCODER_API_V5_BASE_URL improves clarity by specifying the API version. Ensure that all references to the old variable name are updated throughout the codebase to prevent runtime errors.

TOPCODER_API_V6_BASE_URL="https://api.topcoder-dev.com/v6"
AUTH0_CERT="-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEArAV0dmDkedFdlaQ6KQiqUv+UGshfMXx/4jJCLZ9802ynJqAvIt+Z
V7EiPqjc2J1xVfJJEvQ9ZS5A2TFWAk16NUTU4LN+TkjEnqeg+LlUPWY3Y4RXa2OU
Expand All @@ -15,4 +16,4 @@ DB_PASSWORD=randompassword
DB_HOST=127.0.0.1
DB_PORT=5434
DB_NAME=walletdb
DATABASE_URL="postgresql://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
DATABASE_URL="postgresql://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Warnings:
- You are about to drop the `reward` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "reward" DROP CONSTRAINT "reward_winnings_id_fkey";

-- DropTable
DROP TABLE "reward";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
Dropping a table without checking for dependencies or ensuring data backup can lead to data loss and application errors. Consider verifying that the table is no longer needed and ensure that any necessary data is backed up before proceeding.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Warnings:
- You are about to drop the `payoneer_payment_method` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `paypal_payment_method` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "payoneer_payment_method" DROP CONSTRAINT "fk_payoneer_user_payment_method";

-- DropForeignKey
ALTER TABLE "paypal_payment_method" DROP CONSTRAINT "fk_paypal_user_payment_method";

-- DropTable
DROP TABLE "payoneer_payment_method";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
Dropping the payoneer_payment_method table without a backup or migration strategy could lead to data loss. Ensure that any necessary data is backed up or migrated before executing this change.


-- DropTable
DROP TABLE "paypal_payment_method";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
Dropping the paypal_payment_method table without a backup or migration strategy could lead to data loss. Ensure that any necessary data is backed up or migrated before executing this change.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE "challenge_lock" (
"id" SERIAL PRIMARY KEY,
"external_id" VARCHAR(255) NOT NULL UNIQUE,
"lock_time" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"error" TEXT

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
Consider adding a default value for the error column to ensure consistent behavior when no error message is provided.

);
42 changes: 7 additions & 35 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -103,45 +103,11 @@ model payment_releases {
payment_method payment_method @relation(fields: [payment_method_id], references: [payment_method_id], onDelete: NoAction, onUpdate: NoAction)
}

model payoneer_payment_method {
id Int @id @default(autoincrement())
user_payment_method_id String? @db.Uuid
user_id String @unique @db.VarChar(80)
payee_id String @db.VarChar(50)
payoneer_id String? @db.VarChar(50)
user_payment_methods user_payment_methods? @relation(fields: [user_payment_method_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "fk_payoneer_user_payment_method")
}

model paypal_payment_method {
id Int @id @default(autoincrement())
user_payment_method_id String? @db.Uuid
user_id String @unique @db.VarChar(80)
email String? @db.VarChar(150)
payer_id String? @db.VarChar(50)
country_code String? @db.VarChar(2)
user_payment_methods user_payment_methods? @relation(fields: [user_payment_method_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "fk_paypal_user_payment_method")
}

model reward {
reward_id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
winnings_id String @db.Uuid
points Int?
title String? @db.VarChar(255)
description String?
reference Json?
attributes Json?
created_at DateTime? @default(now()) @db.Timestamp(6)
updated_at DateTime? @default(now()) @db.Timestamp(6)
winnings winnings @relation(fields: [winnings_id], references: [winning_id], onDelete: NoAction, onUpdate: NoAction)
}

model user_payment_methods {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The removal of the payoneer_payment_method and paypal_payment_method models and their corresponding relations in user_payment_methods could impact existing functionality if these models are in use elsewhere in the application. Ensure that these deletions are intentional and that any dependent code or database migrations are updated accordingly.

id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
user_id String @db.VarChar(80)
payment_method_id Int
status payment_method_status? @default(OTP_PENDING)
payoneer_payment_method payoneer_payment_method[]
paypal_payment_method paypal_payment_method[]
trolley_payment_method trolley_recipient[]
payment_method payment_method @relation(fields: [payment_method_id], references: [payment_method_id], onDelete: NoAction, onUpdate: NoAction, map: "fk_user_payment_method")

Expand Down Expand Up @@ -172,7 +138,6 @@ model winnings {
updated_at DateTime? @default(now()) @db.Timestamp(6)
audit audit[]
payment payment[]
reward reward[]
origin origin? @relation(fields: [origin_id], references: [origin_id], onDelete: NoAction, onUpdate: NoAction)

@@index([category, created_at(sort: Desc)], map: "idx_winnings_category_created_at")
Expand Down Expand Up @@ -223,6 +188,13 @@ model trolley_recipient_payment_method {
trolley_recipient trolley_recipient @relation(fields: [trolley_recipient_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "fk_trolley_recipient_trolley_recipient_payment_method")
}

model challenge_lock {
id Int @id @default(autoincrement())
external_id String @unique @db.VarChar(255)
lock_time DateTime @default(now()) @db.Timestamp(6)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
Consider using @updatedAt for lock_time if this field is intended to track the last update time automatically. This can improve maintainability by ensuring the timestamp is updated without manual intervention.

error String? @db.Text
}

enum verification_status {
ACTIVE
INACTIVE
Expand Down
29 changes: 1 addition & 28 deletions src/api/admin/admin.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ import { ResponseDto } from 'src/dto/api-response.dto';
import { PaymentStatus } from 'src/dto/payment.dto';
import { WinningAuditDto, AuditPayoutDto } from './dto/audit.dto';
import { WinningUpdateRequestDto } from './dto/winnings.dto';
import {
AdminPaymentUpdateData,
TopcoderChallengesService,
} from 'src/shared/topcoder/challenges.service';
import { TopcoderChallengesService } from 'src/shared/topcoder/challenges.service';
import { Logger } from 'src/shared/global';

function formatDate(date = new Date()) {
Expand Down Expand Up @@ -317,30 +314,6 @@ export class AdminService {
}
});

transactions.push(async () => {
const winning = await this.getWinningById(winningsId);
if (!winning) {
this.logger.error(
`Error updating legacy system for winning ${winningsId}. Winning not found!`,
);
throw new Error(
`Error updating legacy system for winning ${winningsId}. Winning not found!`,
);
}

const payoutData: AdminPaymentUpdateData = {
userId: +winning.winner_id,
status: body.paymentStatus,
amount: body.paymentAmount,
releaseDate: formatDate(new Date(body.releaseDate)),
};

await this.tcChallengesService.updateLegacyPayments(
winning.external_id as string,
payoutData,
);
});

// Run all transaction tasks in a single prisma transaction
await this.prisma.$transaction(async (tx) => {
for (const transaction of transactions) {
Expand Down
4 changes: 4 additions & 0 deletions src/api/api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { WinningsModule } from './winnings/winnings.module';
import { UserModule } from './user/user.module';
import { WalletModule } from './wallet/wallet.module';
import { WithdrawalModule } from './withdrawal/withdrawal.module';
import { ChallengesModule } from './challenges/challenges.module';
import { ChallengePaymentsModule } from './challenge-payments/challenge-payments.module';

@Module({
imports: [
Expand All @@ -26,9 +28,11 @@ import { WithdrawalModule } from './withdrawal/withdrawal.module';
WebhooksModule,
AdminModule,
WinningsModule,
ChallengePaymentsModule,
UserModule,
WalletModule,
WithdrawalModule,
ChallengesModule,
],
controllers: [HealthCheckController],
providers: [
Expand Down
43 changes: 43 additions & 0 deletions src/api/challenge-payments/challenge-payments.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
Controller,
Get,
Param,
Query,
Req,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { AllowedM2mScope, Roles, User } from 'src/core/auth/decorators';
import { M2mScope, Role } from 'src/core/auth/auth.constants';
import { UserInfo } from 'src/dto/user.type';
import { ChallengePaymentsService } from './challenge-payments.service';

@ApiTags('Payments')
@Controller('/challenge-payments')
@ApiBearerAuth()
export class ChallengePaymentsController {
constructor(
private readonly challengePaymentsService: ChallengePaymentsService,
) {}

@Get('/:challengeId')
@AllowedM2mScope(M2mScope.ReadPayments, M2mScope.CreatePayments)
@Roles(Role.PaymentAdmin, Role.PaymentEditor, Role.PaymentViewer, Role.User)
@ApiOperation({
summary:
'List payments (winnings) for a challenge with role-aware filtering',
})
async getChallengePayments(
@Param('challengeId') challengeId: string,
@Query('winnerOnly') winnerOnly: string | undefined,
@User() user: UserInfo,
@Req() req: any,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
Avoid using any for the req parameter. Instead, define a specific type or interface for the request object to improve type safety and maintainability.

) {
return this.challengePaymentsService.listChallengePayments({
challengeId,
requestUserId: user?.id,
isMachineToken: Boolean(req?.m2mTokenScope),
winnerOnly: (winnerOnly || '').toLowerCase() === 'true',
auth0User: req?.auth0User,
});
}
}
11 changes: 11 additions & 0 deletions src/api/challenge-payments/challenge-payments.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { ChallengePaymentsController } from './challenge-payments.controller';
import { ChallengePaymentsService } from './challenge-payments.service';
import { TopcoderModule } from 'src/shared/topcoder/topcoder.module';

@Module({
imports: [TopcoderModule],
controllers: [ChallengePaymentsController],
providers: [ChallengePaymentsService],
})
export class ChallengePaymentsModule {}
Loading