Skip to content

Commit 7586dfc

Browse files
authored
feat(payment-stripe): add PWYW minimum price validation and metadata support (#210)
* feat(payment-stripe): add PWYW minimum price validation and metadata support * feat(payment-stripe): add PWYW minimum price validation and metadata support * feat(payment-stripe): add metadata permissions for user access
1 parent c86f2f3 commit 7586dfc

File tree

6 files changed

+66
-6
lines changed

6 files changed

+66
-6
lines changed

microservices/authorization/migrations/permissions/list/models/payment-stripe.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,14 @@
18291829
"out": {
18301830
"user": "allow"
18311831
}
1832+
},
1833+
"metadata": {
1834+
"in": {
1835+
"user": "allow"
1836+
},
1837+
"out": {
1838+
"user": "allow"
1839+
}
18321840
}
18331841
},
18341842
"createdAt": "2023-05-26T13:01:39.186Z"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm';
2+
3+
export default class AddMetadataToPrice1753736525000 implements MigrationInterface {
4+
name = 'AddMetadataToPrice1753736525000';
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`
8+
ALTER TABLE "price"
9+
ADD COLUMN "metadata" jsonb DEFAULT null
10+
`);
11+
}
12+
13+
public async down(queryRunner: QueryRunner): Promise<void> {
14+
await queryRunner.query(`
15+
ALTER TABLE "price"
16+
DROP COLUMN "metadata"
17+
`);
18+
}
19+
}

microservices/payment-stripe/src/entities/price.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ class Price {
4141
@IsNumber()
4242
unitAmount: number;
4343

44+
@JSONSchema({
45+
description: 'Metadata for storing custom data like PWYW information',
46+
})
47+
@Column({ type: 'jsonb', default: null })
48+
metadata: Record<string, any> | null;
49+
4450
@IsTypeormDate()
4551
@CreateDateColumn()
4652
createdAt: Date;

microservices/payment-stripe/src/methods/stripe/create-checkout.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Endpoint, IsNullable, IsUndefinable } from '@lomray/microservice-helpers';
2-
import { IsBoolean, IsNumber, IsString, Length } from 'class-validator';
2+
import { IsBoolean, IsNumber, IsString, Length, Min } from 'class-validator';
33
import Stripe from '@services/payment-gateway/stripe';
44

55
class CreateCheckoutInput {
@@ -21,6 +21,7 @@ class CreateCheckoutInput {
2121
isAllowPromoCode?: boolean;
2222

2323
@IsNumber()
24+
@Min(0)
2425
@IsUndefinable()
2526
customAmount?: number;
2627
}

microservices/payment-stripe/src/services/payment-gateway/abstract.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,14 +245,15 @@ abstract class Abstract {
245245
* Create new price
246246
*/
247247
public async createPrice(params: IPriceParams, priceId: string = uuid()): Promise<Price> {
248-
const { productId, currency, unitAmount, userId } = params;
248+
const { productId, currency, unitAmount, userId, metadata } = params;
249249

250250
const price = this.priceRepository.create({
251251
priceId,
252252
productId,
253253
userId,
254254
currency,
255255
unitAmount,
256+
...(metadata ? { metadata } : {}),
256257
});
257258

258259
await this.priceRepository.save(price);

microservices/payment-stripe/src/services/payment-gateway/stripe.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,31 @@ class Stripe extends Abstract {
389389
productId,
390390
currency,
391391
unitAmount,
392+
metadata,
392393
},
393394
id,
394395
);
395396
}
396397

398+
/**
399+
* Validate PWYW custom amount against minimum price
400+
*/
401+
private validatePWYWAmount(customAmount: number, price: Price): void {
402+
if (price.metadata?.isPWYW === 'true') {
403+
const minimumPrice = price.metadata?.minimumPrice ? Number(price.metadata.minimumPrice) : 0;
404+
405+
if (customAmount < minimumPrice) {
406+
Log.error(
407+
`Custom amount ${customAmount} is below minimum price ${minimumPrice} for PWYW show`,
408+
);
409+
throw new BaseException({
410+
status: 400,
411+
message: `Amount must be at least $${(minimumPrice / 100).toFixed(2)}`,
412+
});
413+
}
414+
}
415+
}
416+
397417
/**
398418
* Create checkout session and return url to redirect user for payment
399419
*/
@@ -409,6 +429,11 @@ class Stripe extends Abstract {
409429
return null;
410430
}
411431

432+
// Validate PWYW minimum price if custom amount is provided
433+
if (customAmount) {
434+
this.validatePWYWAmount(customAmount, price);
435+
}
436+
412437
/* eslint-disable camelcase */
413438
const sessionParams: StripeSdk.Checkout.SessionCreateParams = {
414439
mode: 'payment',
@@ -423,9 +448,9 @@ class Stripe extends Abstract {
423448
sessionParams.line_items = [
424449
{
425450
price_data: {
426-
currency: 'usd',
451+
currency: price.currency,
427452
product: price.productId,
428-
unit_amount: customAmount * 100, // Convert to cents
453+
unit_amount: customAmount,
429454
},
430455
quantity: 1,
431456
},
@@ -446,12 +471,12 @@ class Stripe extends Abstract {
446471
await this.createTransaction(
447472
{
448473
type: TransactionType.CREDIT,
449-
amount: customAmount ? customAmount * 100 : price.unitAmount, // Store in cents
474+
amount: customAmount ? customAmount : price.unitAmount,
450475
userId,
451476
productId: price.productId,
452477
entityId: price.product.entityId,
453478
status: TransactionStatus.INITIAL,
454-
customAmount: customAmount ? customAmount * 100 : undefined, // Store custom amount in cents
479+
customAmount: customAmount ? customAmount : undefined, // Store custom amount
455480
},
456481
id,
457482
);

0 commit comments

Comments
 (0)