1+ namespace TransactionProcessor . BusinessLogic . OperatorInterfaces . PataPawaPrePay ;
2+
3+ using System ;
4+ using System . Collections . Generic ;
5+ using System . Net . Http ;
6+ using System . Threading ;
7+ using System . Threading . Tasks ;
8+ using Common ;
9+ using EstateManagement . DataTransferObjects . Responses ;
10+ using Microsoft . Extensions . Caching . Memory ;
11+ using Newtonsoft . Json ;
12+ using Shared . Logger ;
13+
14+ public class PataPawaPrePayProxy : IOperatorProxy {
15+ private readonly PataPawaPrePaidConfiguration Configuration ;
16+
17+ private readonly HttpClient HttpClient ;
18+
19+ private readonly IMemoryCache MemoryCache ;
20+
21+ public PataPawaPrePayProxy ( PataPawaPrePaidConfiguration configuration ,
22+ HttpClient httpClient ,
23+ IMemoryCache memoryCache ) {
24+ this . Configuration = configuration ;
25+ this . HttpClient = httpClient ;
26+ this . MemoryCache = memoryCache ;
27+ }
28+
29+ public async Task < OperatorResponse > ProcessLogonMessage ( String accessToken , CancellationToken cancellationToken ) {
30+ // Check if we need to do a logon with the operator
31+ OperatorResponse operatorResponse = this . MemoryCache . Get < OperatorResponse > ( "PataPawaPrePayLogon" ) ;
32+ if ( operatorResponse != null ) {
33+ return operatorResponse ;
34+ }
35+
36+ HttpRequestMessage requestMessage = new HttpRequestMessage ( HttpMethod . Post , this . Configuration . Url ) ;
37+ MultipartFormDataContent content = new MultipartFormDataContent ( ) ;
38+ content . Add ( new StringContent ( "login" ) , "request" ) ;
39+ content . Add ( new StringContent ( this . Configuration . Username ) , "username" ) ;
40+ content . Add ( new StringContent ( this . Configuration . Password ) , "password" ) ;
41+ requestMessage . Content = content ;
42+
43+ HttpResponseMessage responseMessage = await this . HttpClient . SendAsync ( requestMessage , cancellationToken ) ;
44+
45+ // Check the send was successful
46+ if ( responseMessage . IsSuccessStatusCode == false ) {
47+ throw new Exception ( $ "Error sending logon request to Patapawa. Status Code [{ responseMessage . StatusCode } ]") ;
48+ }
49+
50+ // Get the response
51+ String responseContent = await responseMessage . Content . ReadAsStringAsync ( cancellationToken ) ;
52+
53+ Logger . LogInformation ( $ "Received response message from Patapawa [{ responseContent } ]") ;
54+
55+ return this . CreateFromLogon ( responseContent ) ;
56+ }
57+
58+ public async Task < OperatorResponse > ProcessSaleMessage ( String accessToken , Guid transactionId , String operatorIdentifier , MerchantResponse merchant , DateTime transactionDateTime , String transactionReference , Dictionary < String , String > additionalTransactionMetadata , CancellationToken cancellationToken ) {
59+ // Get the logon response for the operator
60+ OperatorResponse logonResponse = this . MemoryCache . Get < OperatorResponse > ( "PataPawaPrePayLogon" ) ;
61+ if ( logonResponse == null )
62+ {
63+ throw new ArgumentNullException ( "PataPawaPrePayLogon" , "logonResponse is null" ) ;
64+ }
65+ String apiKey = logonResponse . AdditionalTransactionResponseMetadata . ExtractFieldFromMetadata < String > ( "PataPawaPrePaidAPIKey" ) ;
66+
67+ if ( String . IsNullOrEmpty ( apiKey ) )
68+ {
69+ throw new ArgumentNullException ( "PataPawaPrePayAPIKey" , "APIKey is a required field for this transaction type" ) ;
70+ }
71+
72+ // Check the meta data for the sale message type
73+ String messageType = additionalTransactionMetadata . ExtractFieldFromMetadata < String > ( "PataPawaPrePayMessageType" ) ;
74+
75+ if ( String . IsNullOrEmpty ( messageType ) )
76+ {
77+ throw new ArgumentNullException ( "PataPawaPrePayMessageType" , "Message Type is a required field for this transaction type" ) ;
78+ }
79+
80+ String meterNumber = additionalTransactionMetadata . ExtractFieldFromMetadata < String > ( "MeterNumber" ) ;
81+
82+ if ( String . IsNullOrEmpty ( meterNumber ) )
83+ {
84+ throw new ArgumentNullException ( "MeterNumber" , "Meter Number is a required field for this transaction type" ) ;
85+ }
86+
87+ return messageType switch
88+ {
89+ "meter" => await this . PerformMeterTransaction ( meterNumber , apiKey , cancellationToken ) ,
90+ "vend" => await this . PerformVendTransaction ( meterNumber , apiKey , additionalTransactionMetadata , cancellationToken ) ,
91+ _ => throw new ArgumentOutOfRangeException ( "PataPawaPrePayMessageType" , $ "Unsupported Message Type { messageType } ")
92+ } ;
93+ }
94+
95+ private async Task < OperatorResponse > PerformMeterTransaction ( String meterNumber , String apiKey , CancellationToken cancellationToken ) {
96+ HttpRequestMessage requestMessage = new HttpRequestMessage ( HttpMethod . Post , this . Configuration . Url ) ;
97+ MultipartFormDataContent content = new MultipartFormDataContent ( ) ;
98+ content . Add ( new StringContent ( "meter" ) , "request" ) ;
99+ content . Add ( new StringContent ( this . Configuration . Username ) , "username" ) ;
100+ content . Add ( new StringContent ( meterNumber ) , "meter" ) ;
101+ content . Add ( new StringContent ( apiKey ) , "key" ) ;
102+ requestMessage . Content = content ;
103+
104+ HttpResponseMessage responseMessage = await this . HttpClient . SendAsync ( requestMessage , cancellationToken ) ;
105+
106+ // Get the response
107+ String responseContent = await responseMessage . Content . ReadAsStringAsync ( cancellationToken ) ;
108+
109+ Logger . LogInformation ( $ "Received response message from Patapawa [{ responseContent } ]") ;
110+
111+ return this . CreateFromMeter ( responseContent ) ;
112+ }
113+
114+ private async Task < OperatorResponse > PerformVendTransaction ( String meterNumber , String apiKey , Dictionary < String , String > additionalTransactionMetadata , CancellationToken cancellationToken )
115+ {
116+ String customerName = additionalTransactionMetadata . ExtractFieldFromMetadata < String > ( "CustomerName" ) ;
117+
118+ if ( String . IsNullOrEmpty ( customerName ) )
119+ {
120+ throw new ArgumentNullException ( "CustomerName" , "Customer Name is a required field for this transaction type" ) ;
121+ }
122+
123+ String amount = additionalTransactionMetadata . ExtractFieldFromMetadata < String > ( "Amount" ) ;
124+
125+ if ( String . IsNullOrEmpty ( meterNumber ) )
126+ {
127+ throw new ArgumentNullException ( "Amount" , "Amount is a required field for this transaction type" ) ;
128+ }
129+
130+
131+ HttpRequestMessage requestMessage = new HttpRequestMessage ( HttpMethod . Post , this . Configuration . Url ) ;
132+ MultipartFormDataContent content = new MultipartFormDataContent ( ) ;
133+ content . Add ( new StringContent ( "vend" ) , "request" ) ;
134+ content . Add ( new StringContent ( this . Configuration . Username ) , "username" ) ;
135+ content . Add ( new StringContent ( meterNumber ) , "meter" ) ;
136+ content . Add ( new StringContent ( apiKey ) , "key" ) ;
137+ content . Add ( new StringContent ( amount ) , "amount" ) ;
138+ content . Add ( new StringContent ( customerName ) , "customerName" ) ;
139+ requestMessage . Content = content ;
140+
141+ HttpResponseMessage responseMessage = await this . HttpClient . SendAsync ( requestMessage , cancellationToken ) ;
142+
143+ // Get the response
144+ String responseContent = await responseMessage . Content . ReadAsStringAsync ( cancellationToken ) ;
145+
146+ Logger . LogInformation ( $ "Received response message from Patapawa [{ responseContent } ]") ;
147+
148+ return this . CreateFromVend ( responseContent ) ;
149+ }
150+
151+ private OperatorResponse CreateFromLogon ( String responseContent ) {
152+ LogonResponse logonResponse = JsonConvert . DeserializeObject < LogonResponse > ( responseContent ) ;
153+
154+ if ( logonResponse . Status != 0 ) {
155+
156+ return new OperatorResponse {
157+ IsSuccessful = false ,
158+ ResponseCode = "-1" ,
159+ ResponseMessage = "Error logging on with PataPawa Post Paid API" ,
160+ AdditionalTransactionResponseMetadata = new Dictionary < String , String > ( )
161+ } ;
162+ }
163+
164+ OperatorResponse operatorResponse = new OperatorResponse {
165+ IsSuccessful = true ,
166+ ResponseCode = "0000" ,
167+ ResponseMessage = logonResponse . Msg ,
168+ AdditionalTransactionResponseMetadata = new Dictionary < String , String > ( )
169+ } ;
170+
171+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "PataPawaPrePaidAPIKey" , logonResponse . Key ) ;
172+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "PataPawaprePaidBalance" , logonResponse . Balance . ToString ( ) ) ;
173+
174+ this . MemoryCache . Set ( "PataPawaPrePayLogon" , operatorResponse , this . MemoryCacheEntryOptions ) ;
175+
176+ return operatorResponse ;
177+ }
178+
179+ private OperatorResponse CreateFromMeter ( String responseContent )
180+ {
181+ MeterResponse meterResponse = JsonConvert . DeserializeObject < MeterResponse > ( responseContent ) ;
182+
183+ if ( meterResponse . Status != 0 )
184+ {
185+ return new OperatorResponse
186+ {
187+ IsSuccessful = false ,
188+ ResponseCode = "-1" ,
189+ ResponseMessage = "Error during meter transaction" ,
190+ AdditionalTransactionResponseMetadata = new Dictionary < String , String > ( )
191+ } ;
192+ }
193+
194+ OperatorResponse operatorResponse = new OperatorResponse
195+ {
196+ IsSuccessful = true ,
197+ ResponseCode = "0000" ,
198+ ResponseMessage = meterResponse . Msg ,
199+ AdditionalTransactionResponseMetadata = new Dictionary < String , String > ( )
200+ } ;
201+
202+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "PataPawaPrePaidCustomerName" , meterResponse . CustomerName ) ;
203+
204+ return operatorResponse ;
205+ }
206+
207+ private OperatorResponse CreateFromVend ( String responseContent )
208+ {
209+ VendResponse vendResponse = JsonConvert . DeserializeObject < VendResponse > ( responseContent ) ;
210+
211+ if ( vendResponse . Status != 0 )
212+ {
213+ return new OperatorResponse
214+ {
215+ IsSuccessful = false ,
216+ ResponseCode = "-1" ,
217+ ResponseMessage = "Error during vend transaction" ,
218+ AdditionalTransactionResponseMetadata = new Dictionary < String , String > ( )
219+ } ;
220+ }
221+
222+ OperatorResponse operatorResponse = new OperatorResponse
223+ {
224+ IsSuccessful = true ,
225+ ResponseCode = "0000" ,
226+ ResponseMessage = vendResponse . Msg ,
227+ AdditionalTransactionResponseMetadata = new Dictionary < String , String > ( )
228+ } ;
229+
230+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "TransactionId" , vendResponse . Transaction . TransactionId . ToString ( ) ) ;
231+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "Reference" , vendResponse . Transaction . Ref ) ;
232+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "Units" , vendResponse . Transaction . Units ) ;
233+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "Token" , vendResponse . Transaction . Token ) ;
234+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "ReceiptNumber" , vendResponse . Transaction . StdTokenRctNum ) ;
235+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "TokenAmount" , vendResponse . Transaction . StdTokenAmt . ToString ( ) ) ;
236+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "TokenTax" , vendResponse . Transaction . StdTokenTax . ToString ( ) ) ;
237+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "MeterNumber" , vendResponse . Transaction . MeterNo ) ;
238+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "TransactionDateTime" , vendResponse . Transaction . Date . ToString ( "yyyy-MM-dd HH:mm:ss" ) ) ;
239+ operatorResponse . AdditionalTransactionResponseMetadata . Add ( "CustomerName" , vendResponse . Transaction . CustomerName ) ;
240+
241+
242+ return operatorResponse ;
243+ }
244+
245+
246+ private MemoryCacheEntryOptions MemoryCacheEntryOptions =>
247+ new MemoryCacheEntryOptions ( ) . SetPriority ( CacheItemPriority . NeverRemove ) . SetSlidingExpiration ( TimeSpan . FromHours ( 1 ) )
248+ . RegisterPostEvictionCallback ( this . PostEvictionCallback ) ;
249+
250+ private void PostEvictionCallback ( Object key ,
251+ Object value ,
252+ EvictionReason reason ,
253+ Object state ) {
254+ if ( key . ToString ( ) . Contains ( "Logon" ) ) {
255+ this . ProcessLogonMessage ( String . Empty , CancellationToken . None ) . Wait ( ) ;
256+ }
257+ }
258+ }
0 commit comments