Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
9c88752
Update returned payment handling:
vas3a May 19, 2025
7ad1504
Merge pull request #58 from topcoder-platform/update-returned-payment…
vas3a May 19, 2025
e95b40f
PM-1206 - reset payment status
vas3a May 20, 2025
1f65854
PM-1201 - sync payments with legacy
vas3a May 20, 2025
87cd639
fix typo
vas3a May 20, 2025
86c1b99
Merge pull request #59 from topcoder-platform/PM-1206_reset-payment-s…
vas3a May 21, 2025
f4097ff
Check for wipro emails and deny withdrawal
vas3a May 21, 2025
5f5a74a
typo
vas3a May 21, 2025
1c6ff07
fix legacy sync
vas3a May 21, 2025
41b95c1
Merge pull request #60 from topcoder-platform/PM-1201_sync-payments-w…
vas3a May 21, 2025
1b38436
PM-1257 - handle wipro users payout
vas3a May 22, 2025
067aacd
PM-1263 - notify users via email about payment setup required
vas3a May 26, 2025
031731c
fix typos
vas3a May 26, 2025
24f0d33
rename env var
vas3a May 27, 2025
e7a1de3
Merge pull request #61 from topcoder-platform/PM-1257_handle-wipro-users
vas3a May 27, 2025
0b22a6b
cleanup schema
vas3a May 27, 2025
af99bf6
Merge pull request #63 from topcoder-platform/PM-1257_handle-wipro-users
vas3a May 27, 2025
48a6b7d
Merge pull request #62 from topcoder-platform/PM-1263_notify-users-pa…
vas3a May 27, 2025
4a82736
Fix mail link for payout setup notification
vas3a May 28, 2025
b593395
Merge pull request #64 from topcoder-platform/fix-mail-link
vas3a May 28, 2025
e6308fe
PM-1277 - update wallet endpoint
vas3a May 28, 2025
6b0b056
fix dto
vas3a May 28, 2025
14379bf
Merge pull request #65 from topcoder-platform/PM-1277_update-wallet-e…
vas3a May 28, 2025
2d38155
PM-1279 - collect paypal fees
vas3a May 28, 2025
129e1ea
fee calculation
vas3a May 28, 2025
73a3a1f
fix paypal fee collection
vas3a May 28, 2025
9827b18
Merge pull request #66 from topcoder-platform/PM-1279_paypal-fee-coll…
vas3a May 28, 2025
1a488ff
PM-1111 - process & store the challenge fee & markup for each payment
vas3a Jun 2, 2025
e96c144
accept challengeFee as part of payment detail
vas3a Jun 2, 2025
35899be
Merge pull request #67 from topcoder-platform/PM-1111_store-challenge…
vas3a Jun 3, 2025
6d982fe
Add request id to the app logs for easy log tracking
vas3a Jun 4, 2025
e628580
Merge pull request #68 from topcoder-platform/logger
vas3a Jun 5, 2025
6be5bd7
PM-1278 - update estimated fees for paypal
vas3a Jun 6, 2025
c49fa49
Merge pull request #70 from topcoder-platform/PM-1278_payment-fees
vas3a Jun 6, 2025
b199ca5
Enable trust module in Trolley widget
kkartunov Jun 9, 2025
1473e1a
PM-1345 - trolley trust module
vas3a Jun 16, 2025
e942094
PM-1345 - check of id verification on winnings creation & withdrawal
vas3a Jun 16, 2025
6a1d7c1
PR feedback: typos & renaming
vas3a Jun 16, 2025
c318b0a
revert query
vas3a Jun 16, 2025
983158e
Merge pull request #71 from topcoder-platform/PM-1345_trolley-trust-m…
vas3a Jun 16, 2025
7c0e199
PM-1304 - allow admin to update payment description
vas3a Jun 18, 2025
257d0e9
PM-1278 - return minWithdrawAmount in wallet details
vas3a Jun 18, 2025
b0a52dc
Merge pull request #74 from topcoder-platform/PM-1278_expose-min-with…
vas3a Jun 18, 2025
6529ecb
Merge pull request #73 from topcoder-platform/PM-1304_allow-payment-d…
vas3a Jun 18, 2025
95d291f
PM-1374 - require otp when withdrawing
vas3a Jun 19, 2025
4098635
otp - use directly verify & generate otp
vas3a Jun 19, 2025
4817c67
Merge pull request #75 from topcoder-platform/PM-1374_require-otp-whe…
vas3a Jun 19, 2025
b26e0d5
fix payment update
vas3a Jun 20, 2025
9570210
Merge pull request #76 from topcoder-platform/fix-payment-update
vas3a Jun 20, 2025
2010f0a
PM-1304 - add audit trail when description gets updated
vas3a Jun 23, 2025
bb3f230
Merge pull request #77 from topcoder-platform/PM-1304_fix-audit
vas3a Jun 23, 2025
205ee00
Update revcipientverification event handling: do not process "phone" …
vas3a Jun 23, 2025
aa1190c
Merge pull request #78 from topcoder-platform/fix-recipientverificati…
vas3a Jun 23, 2025
03fc187
Cors update for v6 testing
jmgasper Sep 25, 2025
53b095d
Better tweak to CORS for v6 testing
jmgasper Sep 25, 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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
"csv": "^6.3.11",
"csv-stringify": "^6.5.2",
"dotenv": "^16.5.0",
"json-stringify-safe": "^5.0.1",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.2.0",
"lodash": "^4.17.21",
"nanoid": "^5.1.5",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"trolleyhq": "^1.1.0",
Expand All @@ -50,6 +52,7 @@
"@swc/core": "^1.10.7",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.14",
"@types/json-stringify-safe": "^5.0.3",
"@types/lodash": "^4.17.16",
"@types/node": "^22.13.1",
"@types/supertest": "^6.0.2",
Expand Down
26 changes: 26 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@ ALTER TABLE "paypal_payment_method" ALTER COLUMN "user_payment_method_id" DROP N
UPDATE payoneer_payment_method SET user_payment_method_id = NULL;
UPDATE paypal_payment_method SET user_payment_method_id = NULL;

DELETE FROM user_default_payment_method;
DELETE FROM user_payment_methods;
DROP TABLE user_default_payment_method;

Choose a reason for hiding this comment

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

high
correctness
Dropping the user_default_payment_method table is a destructive operation. Ensure that this table is no longer needed and that any dependent systems or processes have been updated accordingly.


DELETE FROM user_payment_methods

Choose a reason for hiding this comment

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

medium
maintainability
The DELETE operation on user_payment_methods could potentially remove a large number of records. Consider wrapping this in a transaction to ensure atomicity and to handle any potential errors gracefully.

WHERE payment_method_id NOT IN (
SELECT payment_method_id
FROM payment_method
WHERE payment_method_type IN ('Wipro Payroll', 'Trolley')
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- Update all payments witha "owed" status to "on_hold" for users that don't have a trolley_id
-- and no trolley_recipient_payment_method set.

UPDATE payment
SET payment_status = 'ON_HOLD'
WHERE winnings_id IN (

Choose a reason for hiding this comment

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

medium
performance
The subquery in the WHERE clause may lead to performance issues if the winnings table is large. Consider using a JOIN instead of a subquery to potentially improve performance.

SELECT winnings.winning_id
FROM winnings
LEFT JOIN trolley_recipient ON winnings.winner_id = trolley_recipient.user_id
LEFT JOIN trolley_recipient_payment_method ON trolley_recipient.id = trolley_recipient_payment_method.trolley_recipient_id
WHERE payment.payment_status = 'OWED' AND (

Choose a reason for hiding this comment

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

high
correctness
The condition trolley_recipient.trolley_id IS NULL OR trolley_recipient_payment_method.id IS NULL might lead to unexpected results if there are multiple trolley_recipient_payment_method entries for a single trolley_recipient. Ensure this logic aligns with the intended business rules.

trolley_recipient.trolley_id IS NULL OR
trolley_recipient_payment_method.id IS NULL
)
);

-- To test & see the updated payments:
--
-- SELECT payment.payment_id,
-- payment.total_amount,
-- payment.installment_number,
-- payment.payment_method_id,
-- payment.version,
-- payment.payment_status,
-- winnings.winner_id,
-- trolley_recipient.trolley_id,
-- trolley_recipient_payment_method.recipient_account_id
-- FROM payment
-- LEFT JOIN winnings ON payment.winnings_id = winnings.winning_id
-- LEFT JOIN trolley_recipient ON winnings.winner_id = trolley_recipient.user_id
-- LEFT JOIN trolley_recipient_payment_method ON trolley_recipient.id = trolley_recipient_payment_method.trolley_recipient_id
-- WHERE payment.payment_status = 'OWED'
-- AND trolley_recipient.trolley_id IS NULL
-- AND trolley_recipient_payment_method.id IS NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "payment" ADD COLUMN "challenge_fee" DECIMAL(12,2),

Choose a reason for hiding this comment

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

medium
correctness
Consider adding NOT NULL constraints if challenge_fee and challenge_markup should always have a value. This can help prevent potential issues with null values in calculations or business logic.

ADD COLUMN "challenge_markup" DECIMAL(12,2);
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- CreateEnum
CREATE TYPE "verification_status" AS ENUM ('ACTIVE', 'INACTIVE');

-- CreateTable
CREATE TABLE "user_identity_verification_associations" (
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
"user_id" VARCHAR(80) NOT NULL,

Choose a reason for hiding this comment

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

medium
correctness
Consider using a more specific data type for user_id if possible, such as UUID, to ensure consistency and avoid potential issues with data integrity.

"verification_id" TEXT NOT NULL,

Choose a reason for hiding this comment

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

medium
correctness
Consider using a more specific data type for verification_id if possible, such as UUID, to ensure consistency and avoid potential issues with data integrity.

"date_filed" TIMESTAMP(6) WITHOUT TIME ZONE NOT NULL,

Choose a reason for hiding this comment

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

medium
performance
Ensure that the date_filed column is indexed if it will be frequently used in queries for sorting or filtering, to improve query performance.

"verification_status" "verification_status" NOT NULL,

CONSTRAINT "user_identity_verification_associations_pkey" PRIMARY KEY ("id")
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Warnings:
- You are about to drop the column `transaction_id` on the `otp` table. All the data in the column will be lost.
- You are about to drop the `transaction` table. If the table is not empty, all the data it contains will be lost.
*/
-- AlterTable
ALTER TABLE "otp" DROP COLUMN "transaction_id";

Choose a reason for hiding this comment

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

high
correctness
Dropping the transaction_id column from the otp table will result in data loss. Ensure that this data is no longer needed or has been backed up before proceeding.


-- DropTable
DROP TABLE "transaction";

Choose a reason for hiding this comment

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

high
correctness
Dropping the transaction table will result in complete data loss for that table. Confirm that this data is backed up or no longer required before executing this migration.

33 changes: 15 additions & 18 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ model otp {
email String @db.VarChar(255)
otp_hash String @db.VarChar(255)
expiration_time DateTime @default(dbgenerated("(CURRENT_TIMESTAMP + '00:05:00'::interval)")) @db.Timestamp(6)
transaction_id String @db.VarChar(255)
action_type reference_type
created_at DateTime? @default(now()) @db.Timestamp(6)
updated_at DateTime? @default(now()) @db.Timestamp(6)
Expand All @@ -55,6 +54,8 @@ model payment {
release_date DateTime? @default(dbgenerated("(CURRENT_TIMESTAMP + '15 days'::interval)")) @db.Timestamp(6)
payment_status payment_status?
billing_account String @db.VarChar(80)
challenge_markup Decimal? @db.Decimal(12, 2)

Choose a reason for hiding this comment

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

medium
correctness
Consider adding validation or constraints to ensure challenge_markup is non-negative, as negative values might not make sense in the context of a markup.

challenge_fee Decimal? @db.Decimal(12, 2)

Choose a reason for hiding this comment

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

medium
correctness
Consider adding validation or constraints to ensure challenge_fee is non-negative, as negative values might not make sense in the context of a fee.

payment_method payment_method? @relation(fields: [payment_method_id], references: [payment_method_id], onDelete: NoAction, onUpdate: NoAction)
winnings winnings @relation(fields: [winnings_id], references: [winning_id], onDelete: NoAction, onUpdate: NoAction)
payment_release_associations payment_release_associations[]
Expand All @@ -67,7 +68,6 @@ model payment_method {
description String?
payment payment[]
payment_releases payment_releases[]
user_default_payment_method user_default_payment_method[]
user_payment_methods user_payment_methods[]
}

Expand Down Expand Up @@ -128,22 +128,6 @@ model reward {
winnings winnings @relation(fields: [winnings_id], references: [winning_id], onDelete: NoAction, onUpdate: NoAction)
}

model transaction {
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
user_id String @db.VarChar(255)
reference_id String @db.Uuid
reference_type reference_type
status transaction_status @default(INITIATED)
created_at DateTime? @default(now()) @db.Timestamp(6)
updated_at DateTime? @default(now()) @db.Timestamp(6)
}

model user_default_payment_method {
user_id String @id @db.VarChar(80)
payment_method_id Int
payment_method payment_method @relation(fields: [payment_method_id], references: [payment_method_id], onDelete: Cascade, onUpdate: NoAction, map: "fk_default_payment_method")
}

model user_payment_methods {
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
user_id String @db.VarChar(80)
Expand Down Expand Up @@ -212,13 +196,26 @@ model trolley_webhook_log {
created_at DateTime? @default(now()) @db.Timestamp(6)
}

model user_identity_verification_associations {
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
user_id String @db.VarChar(80)
verification_id String @db.Text
date_filed DateTime @db.Timestamp(6)
verification_status verification_status

Choose a reason for hiding this comment

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

medium
correctness
Consider adding a foreign key constraint to verification_status to ensure referential integrity with the verification_status table or enum. This will help maintain data consistency and prevent invalid statuses from being stored.

}

model trolley_recipient_payment_method {
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
trolley_recipient_id Int
recipient_account_id String @unique @db.VarChar(80)
trolley_recipient trolley_recipient @relation(fields: [trolley_recipient_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "fk_trolley_recipient_trolley_recipient_payment_method")
}

enum verification_status {
ACTIVE
INACTIVE
}

enum action_type {
INITIATE_WITHDRAWAL
ADD_WITHDRAWAL_METHOD
Expand Down
21 changes: 13 additions & 8 deletions src/api/admin/admin.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,24 @@ export class AdminController {
@Body() body: WinningUpdateRequestDto,
@User() user: UserInfo,
): Promise<ResponseDto<string>> {
if (!body.paymentAmount && !body.releaseDate && !body.paymentStatus) {
if (

Choose a reason for hiding this comment

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

medium
correctness
The error message in the BadRequestException should match the condition being checked. Ensure that the message accurately reflects all fields being validated to avoid confusion for the API consumers.

!body.paymentAmount &&
!body.releaseDate &&
!body.paymentStatus &&
!body.description
) {
throw new BadRequestException(
'paymentStatus, releaseDate and paymentAmount cannot be null at the same time.',
'description, paymentStatus, releaseDate and paymentAmount cannot be null at the same time.',
);
}

const result = await this.adminService.updateWinnings(body, user.id);

result.status = ResponseStatusType.SUCCESS;

Choose a reason for hiding this comment

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

high
correctness
The assignment of result.status to ResponseStatusType.SUCCESS before checking for an error could lead to incorrect status reporting if result.error is true. Consider setting the status to SUCCESS only after confirming there is no error.

if (result.error) {
result.status = ResponseStatusType.ERROR;
}

result.status = ResponseStatusType.SUCCESS;

return result;
}

Expand All @@ -200,12 +205,12 @@ export class AdminController {
@Param('winningID') winningId: string,
): Promise<ResponseDto<WinningAuditDto[]>> {
const result = await this.adminService.getWinningAudit(winningId);

result.status = ResponseStatusType.SUCCESS;

Choose a reason for hiding this comment

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

high
correctness
The assignment of result.status to ResponseStatusType.SUCCESS before checking for an error may lead to incorrect status reporting. Consider setting the status to SUCCESS only if no error is present.

if (result.error) {
result.status = ResponseStatusType.ERROR;
}

result.status = ResponseStatusType.SUCCESS;

return result;
}

Expand All @@ -230,12 +235,12 @@ export class AdminController {
@Param('winningID') winningId: string,
): Promise<ResponseDto<AuditPayoutDto[]>> {
const result = await this.adminService.getWinningAuditPayout(winningId);

result.status = ResponseStatusType.SUCCESS;

Choose a reason for hiding this comment

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

high
correctness
The assignment of result.status to ResponseStatusType.SUCCESS before checking for result.error could lead to incorrect status being set if result.error is true. Consider setting the status to SUCCESS only if no error is present.

if (result.error) {
result.status = ResponseStatusType.ERROR;
}

result.status = ResponseStatusType.SUCCESS;

return result;
}
}
9 changes: 1 addition & 8 deletions src/api/admin/admin.module.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import { Module } from '@nestjs/common';
import { AdminController } from './admin.controller';
import { AdminService } from './admin.service';
import { TaxFormRepository } from '../repository/taxForm.repo';
import { PaymentMethodRepository } from '../repository/paymentMethod.repo';
import { WinningsRepository } from '../repository/winnings.repo';

Choose a reason for hiding this comment

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

high
correctness
The TaxFormRepository and PaymentMethodRepository imports have been removed. Ensure that these repositories are no longer needed in the admin.module.ts and that their removal does not affect any functionality dependent on them.

import { TopcoderModule } from 'src/shared/topcoder/topcoder.module';
import { PaymentsModule } from 'src/shared/payments';

@Module({
imports: [TopcoderModule, PaymentsModule],
controllers: [AdminController],
providers: [
AdminService,
TaxFormRepository,
PaymentMethodRepository,
WinningsRepository,
],
providers: [AdminService, WinningsRepository],

Choose a reason for hiding this comment

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

high
correctness
The removal of TaxFormRepository and PaymentMethodRepository from the providers array could potentially impact the functionality if these services are used elsewhere in the module. Ensure that these repositories are not required for any operations within the AdminModule or that their functionality is covered elsewhere.

})
export class AdminModule {}
Loading