From 9366a3ec41b132a7ca437d011ad16508cbbfccaa Mon Sep 17 00:00:00 2001 From: Bennett Benedict Date: Fri, 20 Jun 2025 13:16:00 +0300 Subject: [PATCH] release(mno): Add create order endpoint --- .../selcom/controllers/selcom.controller.ts | 15 +++- .../modules/selcom/services/selcom.service.ts | 78 +++++++++++++++---- .../common/src/interfaces/shared.interface.ts | 13 ++++ 3 files changed, 86 insertions(+), 20 deletions(-) diff --git a/apps/mno/src/modules/selcom/controllers/selcom.controller.ts b/apps/mno/src/modules/selcom/controllers/selcom.controller.ts index 81552f0..7950f42 100644 --- a/apps/mno/src/modules/selcom/controllers/selcom.controller.ts +++ b/apps/mno/src/modules/selcom/controllers/selcom.controller.ts @@ -1,16 +1,25 @@ +import { CreateOrder } from '@flexpay/common'; import { Body, Controller, Post, Res } from '@nestjs/common'; import { CheckoutResponse, ErrorResponse, MnoCheckout } from 'azampay'; -import { SelcomService } from '../services/selcom.service'; import { Response } from 'express'; +import { SelcomService } from '../services/selcom.service'; @Controller('api') export class SelcomController { constructor(private service: SelcomService) {} @Post('selcomPush') - async mnoCheckout(@Res() res: Response, @Body() payload: MnoCheckout) { + async selcomPush(@Res() res: Response, @Body() payload: MnoCheckout) { + const response: CheckoutResponse | ErrorResponse = await this.service.push( + payload, + ); + return res.status(response.statusCode).send(response); + } + + @Post('selcomPush') + async selcomCreateOrder(@Res() res: Response, @Body() payload: CreateOrder) { const response: CheckoutResponse | ErrorResponse = - await this.service.selcomPush(payload); + await this.service.createOrder(payload); return res.status(response.statusCode).send(response); } } diff --git a/apps/mno/src/modules/selcom/services/selcom.service.ts b/apps/mno/src/modules/selcom/services/selcom.service.ts index d31fa16..baa1a46 100644 --- a/apps/mno/src/modules/selcom/services/selcom.service.ts +++ b/apps/mno/src/modules/selcom/services/selcom.service.ts @@ -1,4 +1,9 @@ -import { APPENV, phoneNumber, requestResource } from '@flexpay/common'; +import { + APPENV, + CreateOrder, + phoneNumber, + requestResource, +} from '@flexpay/common'; import { HttpStatus, Injectable } from '@nestjs/common'; import { CheckoutResponse, MnoCheckout, ErrorResponse } from 'azampay'; import * as crypto from 'crypto'; @@ -6,6 +11,12 @@ import * as crypto from 'crypto'; // Types type SelcomPayload = Record; type Headers = Record; +// ======= Config & Execution ======= // +const apiKey = APPENV.SELCOM_APIKEY; +const apiSecret = APPENV.SELCOM_APISECRET; +const baseUrl = APPENV.SELCOM_APIURL; +const vendor = APPENV.SELCOM_VENDOR; +const authorization = Buffer.from(apiKey, 'ascii').toString('base64'); @Injectable() export class SelcomService { @@ -57,7 +68,29 @@ export class SelcomService { return res; }; - selcomPush = async ( + /** + * + * @param payload: CreateOrder + * @returns CheckoutResponse | ErrorResponse + */ + + createOrder = async ( + payload: CreateOrder, + ): Promise => { + const url = `${baseUrl}/checkout/wallet-payment`; + const signedFields = Object.keys(payload).join(','); + const headers: Headers = this.headers(payload as any, signedFields); + const result = await this.sendSelcomRequest(url, payload as any, headers); + console.log(JSON.stringify(result)); + return result; + }; + + /** + * @param request + * @returns CheckoutResponse | ErrorResponse + */ + + push = async ( request: MnoCheckout, ): Promise => { const { valid, withCode } = phoneNumber(request.accountNumber); @@ -69,32 +102,46 @@ export class SelcomService { code: 'FAIL', } as unknown as CheckoutResponse | ErrorResponse; } - // ======= Config & Execution ======= // - const apiKey = APPENV.SELCOM_APIKEY; - const apiSecret = APPENV.SELCOM_APISECRET; - - const url = `${APPENV.SELCOM_APIURL}/wallet/pushussd`; - // const url = `${APPENV.SELCOM_APIURL}/checkout/wallet-payment`; + const order = await this.createOrder({ + order_id: request.externalId, + amount: request.amount, + buyer_email: 'benny@example.com', + buyer_name: 'Benny', + buyer_phone: withCode, + currency: 'TZS', + buyer_remarks: 'None', + merchant_remarks: 'None', + no_of_items: 1, + } as unknown as CreateOrder); + console.log(JSON.stringify(order)); + const url = `${baseUrl}/checkout/wallet-payment`; const payload: SelcomPayload = { - utilityref: APPENV.SELCOM_VENDOR, + utilityref: vendor, transid: request.externalId, amount: request.amount, - vendor: APPENV.SELCOM_VENDOR, + vendor, msisdn: withCode, + order_id: request.externalId, }; - const authorization = Buffer.from(apiKey, 'ascii').toString('base64'); - const timestamp = new Date().toISOString(); const signedFields = Object.keys(payload).join(','); + const headers: Headers = this.headers(payload, signedFields); + const result = await this.sendSelcomRequest(url, payload, headers); + console.log(JSON.stringify(result)); + return result; + }; + + headers = (payload: SelcomPayload, signedFields: string): Headers => { + const timestamp = new Date().toISOString(); + const digest = this.computeSignature( payload, signedFields, timestamp, apiSecret, ); - - const headers: Headers = { + return { 'Content-Type': 'application/json;charset=utf-8', Accept: 'application/json', 'Cache-Control': 'no-cache', @@ -104,8 +151,5 @@ export class SelcomService { Timestamp: timestamp, 'Signed-Fields': signedFields, }; - const result = await this.sendSelcomRequest(url, payload, headers); - console.log(JSON.stringify(result)); - return result; }; } diff --git a/libs/common/src/interfaces/shared.interface.ts b/libs/common/src/interfaces/shared.interface.ts index a50c185..0f115a8 100644 --- a/libs/common/src/interfaces/shared.interface.ts +++ b/libs/common/src/interfaces/shared.interface.ts @@ -81,3 +81,16 @@ export interface GetManyReqInterface { page: number; pageSize: number; } + +export interface CreateOrder { + vendor: string; + order_id: string; + buyer_email: string; + buyer_name: string; + buyer_phone: string; + amount: number; + currency: string; + buyer_remarks: string; + merchant_remarks: string; + no_of_items: number; +}