fix subscription from orange
This commit is contained in:
parent
6ea3ece796
commit
0af15e26fc
@ -24,19 +24,13 @@ export interface RefundResponse{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SubscriptionParams{
|
|
||||||
|
|
||||||
}
|
|
||||||
export interface SubscriptionResponse{
|
|
||||||
|
|
||||||
}
|
|
||||||
export interface IOperatorAdapter {
|
export interface IOperatorAdapter {
|
||||||
initializeAuth(params: AuthInitParams): Promise<AuthInitResponse>;
|
initializeAuth(params: AuthInitParams): Promise<AuthInitResponse>;
|
||||||
validateAuth(params: AuthValidateParams): Promise<AuthValidateResponse>;
|
validateAuth(params: AuthValidateParams): Promise<AuthValidateResponse>;
|
||||||
charge(params: ChargeParams): Promise<ChargeResponse>;
|
charge(params: ChargeParams): Promise<ChargeResponse>;
|
||||||
refund(params: RefundParams): Promise<RefundResponse>;
|
refund(params: RefundParams): Promise<RefundResponse>;
|
||||||
sendSms(params: SmsParams): Promise<SmsResponse>;
|
sendSms(params: SmsParams): Promise<SmsResponse>;
|
||||||
createSubscription?(
|
createSubscription(
|
||||||
params: SubscriptionParams,
|
params: SubscriptionParams,
|
||||||
): Promise<SubscriptionResponse>;
|
): Promise<SubscriptionResponse>;
|
||||||
cancelSubscription?(subscriptionId: string): Promise<void>;
|
cancelSubscription?(subscriptionId: string): Promise<void>;
|
||||||
@ -73,3 +67,24 @@ export interface ChargeResponse {
|
|||||||
resourceURL: string;
|
resourceURL: string;
|
||||||
currency: string;
|
currency: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface SubscriptionParams {
|
||||||
|
merchantId: any;
|
||||||
|
periodicity: any;
|
||||||
|
userToken: string;
|
||||||
|
userAlias: string;
|
||||||
|
amount: number;
|
||||||
|
currency: string;
|
||||||
|
description: string;
|
||||||
|
productId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubscriptionResponse {
|
||||||
|
subscriptionId: string;
|
||||||
|
status: 'SUCCESS' | 'FAILED' | 'PENDING';
|
||||||
|
operatorReference: string;
|
||||||
|
amount: number;
|
||||||
|
resourceURL: string;
|
||||||
|
currency: string;
|
||||||
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import {
|
|||||||
AuthInitResponse,
|
AuthInitResponse,
|
||||||
ChargeParams,
|
ChargeParams,
|
||||||
ChargeResponse,
|
ChargeResponse,
|
||||||
|
SubscriptionParams,
|
||||||
|
SubscriptionResponse,
|
||||||
} from './operator.adapter.interface';
|
} from './operator.adapter.interface';
|
||||||
import { OrangeTransformer } from '../transformers/orange.transformer';
|
import { OrangeTransformer } from '../transformers/orange.transformer';
|
||||||
import {
|
import {
|
||||||
@ -161,6 +163,117 @@ export class OrangeAdapter implements IOperatorAdapter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createSubscription(
|
||||||
|
params: SubscriptionParams,
|
||||||
|
): Promise<SubscriptionResponse> {
|
||||||
|
this.logger.debug(
|
||||||
|
`[orange adapter createSubscription]: ${JSON.stringify(params, null, 2)}`,
|
||||||
|
);
|
||||||
|
const hubRequest = {
|
||||||
|
note: {
|
||||||
|
"text": "partner data"
|
||||||
|
},
|
||||||
|
relatedPublicKey: {
|
||||||
|
"id": "PDKSUB-200-KzIxNnh4eHh4eC1TRU4tMTc1ODY1MjI5MjMwMg==",
|
||||||
|
"name": "ISE2"
|
||||||
|
},
|
||||||
|
relatedParty: [
|
||||||
|
{
|
||||||
|
"id": "{{serviceId)}}",
|
||||||
|
"name": " DIGITALAFRIQUETELECOM ",
|
||||||
|
"role": "partner"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": `${params.merchantId}`,
|
||||||
|
"name": "{{onBehalfOf)}}",
|
||||||
|
"role": "retailer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
orderItem: {
|
||||||
|
"action": "add",
|
||||||
|
"state": "Completed",
|
||||||
|
"product": {
|
||||||
|
"id": `${params.productId}}`,
|
||||||
|
"href": "antifraudId",
|
||||||
|
"productCharacteristic": [
|
||||||
|
{
|
||||||
|
"name": "taxAmount",
|
||||||
|
"value": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "amount",
|
||||||
|
"value": `${params.amount}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "currency",//ISO 4217 see Annexes
|
||||||
|
"value": `${params.currency}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "periodicity",//86400 (daily), 604800 (weekly), 0 (monthly) only those values will be accepted
|
||||||
|
"value": `${params.periodicity}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "startDate",//YYYY-MM-DD
|
||||||
|
"value": "2021-08-16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "country",
|
||||||
|
"value": "COD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "language",//ISO 639-1 see Annexes
|
||||||
|
"value": "fr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mode",
|
||||||
|
"value": "hybrid"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const token = await this.getAccessToken();
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
`[requesting subscription to]: ${this.config.baseUrl}/payment/mea/v1/digipay_sub/productOrder`,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.debug(`[requesting token]: ${token}`);
|
||||||
|
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.post(
|
||||||
|
`${this.config.baseUrl}/payment/mea/v1/digipay_sub/productOrder`,
|
||||||
|
hubRequest,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
'X-Orange-ISE2': params.userToken,
|
||||||
|
'X-Orange-MCO': 'orange',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
`[response from orange subscription]: ${JSON.stringify(response.data, null, 2)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.transformer.transformSubscriptionResponse(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelSubscription(subscriptionId: string): Promise<void> {
|
||||||
|
this.logger.debug(
|
||||||
|
`[orange adapter cancelSubscription]: ${subscriptionId}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Implémentation de l'annulation d'abonnement
|
||||||
|
// Cela dépend de l'API Orange - à adapter selon la documentation
|
||||||
|
throw new Error('Cancel subscription not implemented for Orange');
|
||||||
|
}
|
||||||
|
|
||||||
async charge(params: ChargeParams): Promise<ChargeResponse> {
|
async charge(params: ChargeParams): Promise<ChargeResponse> {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`[orange adapter charge ]: ${JSON.stringify(params, null, 2)}`,
|
`[orange adapter charge ]: ${JSON.stringify(params, null, 2)}`,
|
||||||
@ -221,6 +334,8 @@ export class OrangeAdapter implements IOperatorAdapter {
|
|||||||
throw new Error('Refund not implemented for Orange');
|
throw new Error('Refund not implemented for Orange');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async sendSms(params: any): Promise<any> {
|
async sendSms(params: any): Promise<any> {
|
||||||
const smsRequest = {
|
const smsRequest = {
|
||||||
outboundSMSMessageRequest: {
|
outboundSMSMessageRequest: {
|
||||||
|
|||||||
@ -20,8 +20,24 @@ export class OrangeTransformer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapStatus(orangeStatus: string): string {
|
|
||||||
|
transformSubscriptionResponse(orangeResponse: any): any {
|
||||||
|
return {
|
||||||
|
subscriptionId: orangeResponse.id,
|
||||||
|
status: this.mapStatus(
|
||||||
|
orangeResponse.state,
|
||||||
|
),
|
||||||
|
operatorReference: orangeResponse.amountTransaction?.serverReferenceCode,
|
||||||
|
amount: parseFloat(
|
||||||
|
orangeResponse.amountTransaction?.paymentAmount?.totalAmountCharged,
|
||||||
|
),
|
||||||
|
createdAt: new Date(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapStatus(orangeStatus: string): string {//todo make exaustifs
|
||||||
const statusMap = {
|
const statusMap = {
|
||||||
|
Completed: 'SUCCESS',
|
||||||
Charged: 'SUCCESS',
|
Charged: 'SUCCESS',
|
||||||
Failed: 'FAILED',
|
Failed: 'FAILED',
|
||||||
Pending: 'PENDING',
|
Pending: 'PENDING',
|
||||||
|
|||||||
@ -9,6 +9,14 @@ import { PaymentType, TransactionStatus } from 'generated/prisma';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentsService {
|
export class PaymentsService {
|
||||||
private readonly logger = new Logger(PaymentsService.name);
|
private readonly logger = new Logger(PaymentsService.name);
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly operatorsService: OperatorsService,
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly eventEmitter: EventEmitter2,
|
||||||
|
) {}
|
||||||
|
|
||||||
handleWebhook(arg0: {
|
handleWebhook(arg0: {
|
||||||
partnerId: any;
|
partnerId: any;
|
||||||
event: any;
|
event: any;
|
||||||
@ -76,11 +84,7 @@ export class PaymentsService {
|
|||||||
processPayment(paymentId: any): any {
|
processPayment(paymentId: any): any {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
constructor(
|
|
||||||
private readonly operatorsService: OperatorsService,
|
|
||||||
private readonly prisma: PrismaService,
|
|
||||||
private readonly eventEmitter: EventEmitter2,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async createCharge(chargeDto: ChargeDto) {
|
async createCharge(chargeDto: ChargeDto) {
|
||||||
/* Récupérer les informations de l'utilisateur
|
/* Récupérer les informations de l'utilisateur
|
||||||
|
|||||||
@ -30,12 +30,14 @@ export class SubscriptionsController {
|
|||||||
@ApiOperation({ summary: 'Create a new subscription' })
|
@ApiOperation({ summary: 'Create a new subscription' })
|
||||||
async create(
|
async create(
|
||||||
@Headers('X-Merchant-ID') merchantId: string,
|
@Headers('X-Merchant-ID') merchantId: string,
|
||||||
|
@Headers('X-COUNTRY') country: string,
|
||||||
|
@Headers('X-OPERATOR') operator: string,
|
||||||
@Request() req, @Body() dto: CreateSubscriptionDto) {
|
@Request() req, @Body() dto: CreateSubscriptionDto) {
|
||||||
this.logger.log('Merchant ID from header:'+ merchantId);
|
this.logger.log('Merchant ID from header:'+ merchantId);
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`[request to hub ]: ${JSON.stringify(dto, null, 2)}`,
|
`[request to hub ]: ${JSON.stringify(dto, null, 2)}`,
|
||||||
)
|
)
|
||||||
return this.subscriptionsService.create(merchantId, dto);
|
return this.subscriptionsService.create(merchantId, dto,country,operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/')
|
@Get('/')
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { SubscriptionProcessor } from './processors/subscription.processor';
|
|||||||
import { PrismaService } from '../../shared/services/prisma.service';
|
import { PrismaService } from '../../shared/services/prisma.service';
|
||||||
import { PaymentsModule } from '../payments/payments.module';
|
import { PaymentsModule } from '../payments/payments.module';
|
||||||
import { HttpModule } from '@nestjs/axios';
|
import { HttpModule } from '@nestjs/axios';
|
||||||
|
import { OperatorsModule } from '../operators/operators.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -18,6 +19,7 @@ import { PaymentsModule } from '../payments/payments.module';
|
|||||||
BullModule.registerQueue({
|
BullModule.registerQueue({
|
||||||
name: 'billing',
|
name: 'billing',
|
||||||
}),
|
}),
|
||||||
|
OperatorsModule
|
||||||
// PaymentsModule,
|
// PaymentsModule,
|
||||||
],
|
],
|
||||||
controllers: [SubscriptionsController],
|
controllers: [SubscriptionsController],
|
||||||
|
|||||||
@ -1,15 +1,26 @@
|
|||||||
import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
|
import { Injectable, BadRequestException, NotFoundException, Logger } from '@nestjs/common';
|
||||||
import { InjectQueue } from '@nestjs/bull';
|
import { InjectQueue } from '@nestjs/bull';
|
||||||
import bull from 'bull';
|
import bull from 'bull';
|
||||||
import { PrismaService } from '../../shared/services/prisma.service';
|
import { PrismaService } from '../../shared/services/prisma.service';
|
||||||
import { PaymentsService } from '../payments/payments.service';
|
import { PaymentsService } from '../payments/payments.service';
|
||||||
import { CreateSubscriptionDto, UpdateSubscriptionDto } from './dto/subscription.dto';
|
import { CreateSubscriptionDto, UpdateSubscriptionDto } from './dto/subscription.dto';
|
||||||
import { Subscription } from 'generated/prisma';
|
import { Subscription } from 'generated/prisma';
|
||||||
|
import { OperatorsService } from '../operators/operators.service';
|
||||||
//import { SubscriptionStatus } from '@prisma/client';
|
//import { SubscriptionStatus } from '@prisma/client';
|
||||||
//import { SubscriptionStatus, Prisma } from '@prisma/client';
|
//import { SubscriptionStatus, Prisma } from '@prisma/client';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SubscriptionsService {
|
export class SubscriptionsService {
|
||||||
|
private readonly logger = new Logger(SubscriptionsService.name);
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly operatorsService: OperatorsService,
|
||||||
|
@InjectQueue('subscriptions') private subscriptionQueue: bull.Queue,
|
||||||
|
@InjectQueue('billing') private billingQueue: bull.Queue,
|
||||||
|
) {}
|
||||||
|
|
||||||
async get(id: number):Promise<any> {
|
async get(id: number):Promise<any> {
|
||||||
const service = await this.prisma.subscription.findUnique({
|
const service = await this.prisma.subscription.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
@ -50,13 +61,9 @@ export class SubscriptionsService {
|
|||||||
getInvoices(id: string, partnerId: any) {
|
getInvoices(id: string, partnerId: any) {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
constructor(
|
|
||||||
private readonly prisma: PrismaService,
|
|
||||||
@InjectQueue('subscriptions') private subscriptionQueue: bull.Queue,
|
|
||||||
@InjectQueue('billing') private billingQueue: bull.Queue,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async create(partnerId: string, dto: CreateSubscriptionDto) {
|
async create(partnerId: string, dto: CreateSubscriptionDto, country:string,operator:string) {
|
||||||
/* todo Vérifier l'utilisateur
|
/* todo Vérifier l'utilisateur
|
||||||
|
|
||||||
const user = await this.prisma.user.findFirst({
|
const user = await this.prisma.user.findFirst({
|
||||||
@ -82,6 +89,8 @@ export class SubscriptionsService {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Vérifier s'il n'y a pas déjà une subscription active
|
// Vérifier s'il n'y a pas déjà une subscription active
|
||||||
const existingSubscription = await this.prisma.subscription.findFirst({
|
const existingSubscription = await this.prisma.subscription.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -96,12 +105,36 @@ export class SubscriptionsService {
|
|||||||
throw new BadRequestException('User already has an active subscription for this plan');
|
throw new BadRequestException('User already has an active subscription for this plan');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const adapter = this.operatorsService.getAdapter(
|
||||||
|
operator,
|
||||||
|
country,
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
const subscriptionParams = {
|
||||||
|
userToken: dto.userToken,
|
||||||
|
userAlias: dto.userToken, //todo make alias in contrat
|
||||||
|
amount: 200,//plan.amount,todo
|
||||||
|
currency: 'XOF',//plan.currency,todo
|
||||||
|
description: 'dto.description',//plan.description,todo
|
||||||
|
productId: dto.planId +'',
|
||||||
|
merchantId: partnerId,
|
||||||
|
periodicity: '86400', // todo 86400 (daily), 604800 (weekly), 0 (monthly) only those values will be accepted
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await adapter.createSubscription(subscriptionParams);
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
`result from adapter ${JSON.stringify(result, null, 2)} for subscription creation`,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Créer la subscription
|
// Créer la subscription
|
||||||
const subscription = await this.prisma.subscription.create({
|
const subscription = await this.prisma.subscription.create({
|
||||||
data: {
|
data: {
|
||||||
customerId: 1, //user.id, todo
|
customerId: 1, //user.id, todo
|
||||||
|
externalReference: result.subscriptionId,
|
||||||
merchantPartnerId: 4,// todo , parseInt(partnerId),
|
merchantPartnerId: 4,// todo , parseInt(partnerId),
|
||||||
token: dto.userToken,
|
token: dto.userToken,
|
||||||
planId: dto.planId,
|
planId: dto.planId,
|
||||||
@ -109,7 +142,11 @@ export class SubscriptionsService {
|
|||||||
periodicity: "Daily",
|
periodicity: "Daily",
|
||||||
amount: 20,
|
amount: 20,
|
||||||
currency: "XOF",
|
currency: "XOF",
|
||||||
status: 'ACTIVE',
|
status: 'ACTIVE',//todo mapping result.status 'SUCCESS' ? 'ACTIVE' : 'PENDING',
|
||||||
|
//currentPeriodStart: new Date(),
|
||||||
|
//currentPeriodEnd: new Date(), // todo À ajuster selon la périodicité
|
||||||
|
// nextBillingDate: new Date(), // todo À ajuster selon la périodicité
|
||||||
|
//renewalCount: 0,
|
||||||
startDate: new Date(),
|
startDate: new Date(),
|
||||||
failureCount: 0,
|
failureCount: 0,
|
||||||
nextPaymentDate: new Date(), // todo À ajuster selon la périodicité
|
nextPaymentDate: new Date(), // todo À ajuster selon la périodicité
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user