mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 17:45:42 +00:00
feat(payments): support web and mobile platform for Telebirr initiation
This commit is contained in:
@@ -1,13 +1,19 @@
|
|||||||
import { IsString, IsEnum, IsOptional } from 'class-validator';
|
import { IsString, IsEnum, IsOptional, IsIn } from 'class-validator';
|
||||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { PaymentIntentStatus } from '@prisma/client';
|
import { PaymentIntentStatus } from '@prisma/client';
|
||||||
|
|
||||||
export enum PaymentMethodTypeEnum { TELEBIRR = 'TELEBIRR', CBE_BIRR = 'CBE_BIRR', EBIRR = 'EBIRR', CARD = 'CARD', WALLET = 'WALLET' }
|
export enum PaymentMethodTypeEnum { TELEBIRR = 'TELEBIRR', CBE_BIRR = 'CBE_BIRR', EBIRR = 'EBIRR', CARD = 'CARD', WALLET = 'WALLET' }
|
||||||
|
|
||||||
|
export type PaymentPlatformDto = 'web' | 'mobile';
|
||||||
|
|
||||||
export class InitiatePaymentDto {
|
export class InitiatePaymentDto {
|
||||||
@ApiProperty() @IsString() bookingId: string;
|
@ApiProperty() @IsString() bookingId: string;
|
||||||
@ApiProperty({ enum: PaymentMethodTypeEnum }) @IsEnum(PaymentMethodTypeEnum) method: PaymentMethodTypeEnum;
|
@ApiProperty({ enum: PaymentMethodTypeEnum }) @IsEnum(PaymentMethodTypeEnum) method: PaymentMethodTypeEnum;
|
||||||
@ApiPropertyOptional() @IsOptional() @IsString() paymentMethodId?: string;
|
@ApiPropertyOptional() @IsOptional() @IsString() paymentMethodId?: string;
|
||||||
|
@ApiPropertyOptional({ enum: ['web', 'mobile'], default: 'web' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsIn(['web', 'mobile'])
|
||||||
|
platform?: PaymentPlatformDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RefundDto {
|
export class RefundDto {
|
||||||
@@ -23,8 +29,11 @@ export class AddPaymentMethodDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ClientActionDto {
|
export class ClientActionDto {
|
||||||
@ApiProperty({ enum: ['REDIRECT'] }) type: 'REDIRECT';
|
@ApiProperty({ enum: ['REDIRECT', 'LAUNCH_APP'] }) type: 'REDIRECT' | 'LAUNCH_APP';
|
||||||
@ApiProperty() url: string;
|
@ApiPropertyOptional({ description: 'Set when type=REDIRECT (web flow)' }) url?: string;
|
||||||
|
@ApiPropertyOptional({ description: 'Set when type=LAUNCH_APP (mobile flow)' }) prepayId?: string;
|
||||||
|
@ApiPropertyOptional({ description: 'Set when type=LAUNCH_APP (mobile flow)' }) receiveCode?: string;
|
||||||
|
@ApiPropertyOptional({ description: 'Set when type=LAUNCH_APP (mobile flow)' }) shortCode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InitiateResponseDto {
|
export class InitiateResponseDto {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { TicketsService } from '../tickets/tickets.service';
|
|||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { Prisma, PaymentIntentStatus, PaymentMethodType } from '@prisma/client';
|
import { Prisma, PaymentIntentStatus, PaymentMethodType } from '@prisma/client';
|
||||||
import { InitiatePaymentDto, RefundDto, AddPaymentMethodDto, InitiateResponseDto, IntentStatusDto } from './payments.dto';
|
import { InitiatePaymentDto, RefundDto, AddPaymentMethodDto, InitiateResponseDto, IntentStatusDto } from './payments.dto';
|
||||||
import { PaymentProvider, ProviderStatus } from './payments.types';
|
import { ClientAction, PaymentProvider, ProviderStatus } from './payments.types';
|
||||||
import { TelebirrProvider } from './providers/telebirr.provider';
|
import { TelebirrProvider } from './providers/telebirr.provider';
|
||||||
import { CbeBirrProvider } from './providers/cbe-birr.provider';
|
import { CbeBirrProvider } from './providers/cbe-birr.provider';
|
||||||
import { EBirrProvider } from './providers/ebirr.provider';
|
import { EBirrProvider } from './providers/ebirr.provider';
|
||||||
@@ -66,7 +66,7 @@ export class PaymentsService {
|
|||||||
|
|
||||||
const provider = this.providers.get(method);
|
const provider = this.providers.get(method);
|
||||||
if (provider) {
|
if (provider) {
|
||||||
return this.initiateProviderPayment(booking, provider);
|
return this.initiateProviderPayment(booking, provider, dto.platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new BadRequestException(`Unsupported payment method: ${method}`);
|
throw new BadRequestException(`Unsupported payment method: ${method}`);
|
||||||
@@ -139,6 +139,7 @@ export class PaymentsService {
|
|||||||
private async initiateProviderPayment(
|
private async initiateProviderPayment(
|
||||||
booking: Prisma.BookingGetPayload<{ include: { seats: true } }>,
|
booking: Prisma.BookingGetPayload<{ include: { seats: true } }>,
|
||||||
provider: PaymentProvider,
|
provider: PaymentProvider,
|
||||||
|
platform: 'web' | 'mobile' | undefined,
|
||||||
): Promise<InitiateResponseDto> {
|
): Promise<InitiateResponseDto> {
|
||||||
const merchantOrderId = createMerchantOrderId();
|
const merchantOrderId = createMerchantOrderId();
|
||||||
const result = await provider.initiate({
|
const result = await provider.initiate({
|
||||||
@@ -146,6 +147,7 @@ export class PaymentsService {
|
|||||||
bookingRef: booking.bookingRef,
|
bookingRef: booking.bookingRef,
|
||||||
amountMinor: booking.totalMinor,
|
amountMinor: booking.totalMinor,
|
||||||
currency: booking.currency,
|
currency: booking.currency,
|
||||||
|
platform,
|
||||||
});
|
});
|
||||||
|
|
||||||
const intent = await this.prisma.paymentIntent.upsert({
|
const intent = await this.prisma.paymentIntent.upsert({
|
||||||
@@ -184,7 +186,7 @@ export class PaymentsService {
|
|||||||
): InitiateResponseDto {
|
): InitiateResponseDto {
|
||||||
const clientAction =
|
const clientAction =
|
||||||
intent.clientAction && typeof intent.clientAction === 'object'
|
intent.clientAction && typeof intent.clientAction === 'object'
|
||||||
? (intent.clientAction as unknown as { type: 'REDIRECT'; url: string })
|
? (intent.clientAction as unknown as ClientAction)
|
||||||
: undefined;
|
: undefined;
|
||||||
return {
|
return {
|
||||||
intentId: intent.id,
|
intentId: intent.id,
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import { PaymentIntentStatus, PaymentMethodType } from '@prisma/client';
|
import { PaymentIntentStatus, PaymentMethodType } from '@prisma/client';
|
||||||
|
|
||||||
export interface ClientAction {
|
export type PaymentPlatform = 'web' | 'mobile';
|
||||||
type: 'REDIRECT';
|
|
||||||
url: string;
|
export type ClientAction =
|
||||||
}
|
| { type: 'REDIRECT'; url: string }
|
||||||
|
| { type: 'LAUNCH_APP'; prepayId: string; receiveCode?: string; shortCode: string };
|
||||||
|
|
||||||
export interface ProviderInitiationInput {
|
export interface ProviderInitiationInput {
|
||||||
merchantOrderId: string;
|
merchantOrderId: string;
|
||||||
bookingRef: string;
|
bookingRef: string;
|
||||||
amountMinor: number;
|
amountMinor: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
|
platform?: PaymentPlatform;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderInitiationResult {
|
export interface ProviderInitiationResult {
|
||||||
|
|||||||
@@ -58,12 +58,21 @@ export class TelebirrProvider implements PaymentProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkoutUrl = this.buildCheckoutUrl(prepayId);
|
|
||||||
const expiresAt = this.computeExpiresAt(requestBody.biz_content.timeout_express);
|
const expiresAt = this.computeExpiresAt(requestBody.biz_content.timeout_express);
|
||||||
|
const platform = input.platform ?? 'web';
|
||||||
|
const clientAction =
|
||||||
|
platform === 'mobile'
|
||||||
|
? {
|
||||||
|
type: 'LAUNCH_APP' as const,
|
||||||
|
prepayId,
|
||||||
|
receiveCode: response.biz_content?.receiveCode,
|
||||||
|
shortCode: this.merchantCode,
|
||||||
|
}
|
||||||
|
: { type: 'REDIRECT' as const, url: this.buildCheckoutUrl(prepayId) };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
providerOrderId: prepayId,
|
providerOrderId: prepayId,
|
||||||
clientAction: { type: 'REDIRECT', url: checkoutUrl },
|
clientAction,
|
||||||
expiresAt,
|
expiresAt,
|
||||||
rawInitiation: {
|
rawInitiation: {
|
||||||
request: this.sanitize(requestBody),
|
request: this.sanitize(requestBody),
|
||||||
|
|||||||
Reference in New Issue
Block a user