1- // Importing base modules
2- import Array "mo:base/Array" ;
3- import Blob "mo:base/Blob" ;
4- import Cycles "mo:base/ExperimentalCycles" ;
5- import Debug "mo:base/Debug" ;
6- import Nat "mo:base/Nat" ;
7- import Nat64 "mo:base/Nat64" ;
8- import Principal "mo:base/Principal" ;
9- import Text "mo:base/Text" ;
10- import Time "mo:base/Time" ;
11- import Trie "mo:base/Trie" ;
12- import Buffer "mo:base/Buffer" ;
13-
14- // Importing local modules
1+ import Array "mo:core/Array" ;
2+ import Blob "mo:core/Blob" ;
3+ import Debug "mo:core/Debug" ;
4+ import Map "mo:core/Map" ;
5+ import Nat "mo:core/Nat" ;
6+ import Nat64 "mo:core/Nat64" ;
7+ import Principal "mo:core/Principal" ;
8+ import Text "mo:core/Text" ;
9+ import Time "mo:core/Time" ;
10+
1511import MainTypes "main.types" ;
1612import CkBtcLedger "canister:icrc1_ledger" ;
1713import HttpTypes "http/http.types" ;
@@ -26,18 +22,18 @@ import HttpTypes "http/http.types";
2622*/
2723shared (actorContext) persistent actor class Main (_startBlock : Nat ) {
2824
29- private var merchantStore : Trie . Trie < Text , MainTypes . Merchant > = Trie . empty ();
25+ private let merchantStore = Map . empty < Text , MainTypes . Merchant > ();
3026 private var latestTransactionIndex : Nat = 0 ;
3127 private var courierApiKey : Text = "" ;
32- private transient var logData = Buffer . Buffer < Text > ( 0 ) ;
28+ private transient var logData : [ Text ] = [] ;
3329
3430 /**
3531 * Get the merchant's information
3632 */
3733 public query (context) func getMerchant() : async MainTypes . Response < MainTypes . Merchant > {
3834 let caller : Principal = context. caller;
3935
40- switch (Trie . get(merchantStore, merchantKey( Principal . toText(caller)), Text . equal )) {
36+ switch (merchantStore . get(caller . toText() )) {
4137 case (?merchant) {
4238 {
4339 status = 200 ;
@@ -51,7 +47,7 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
5147 status = 404 ;
5248 status_text = "Not Found" ;
5349 data = null ;
54- error_text = ?("Merchant with principal ID: " # Principal . toText(caller ) # " not found." );
50+ error_text = ?("Merchant with principal ID: " # caller . toText() # " not found." );
5551 };
5652 };
5753 };
@@ -61,14 +57,8 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
6157 * Update the merchant's information
6258 */
6359 public shared (context) func updateMerchant(merchant : MainTypes . Merchant ) : async MainTypes . Response < MainTypes . Merchant > {
64-
6560 let caller : Principal = context. caller;
66- merchantStore := Trie . replace(
67- merchantStore,
68- merchantKey(Principal . toText(caller)),
69- Text . equal,
70- ?merchant,
71- ). 0 ;
61+ let _ = merchantStore. swap(caller. toText(), merchant);
7262 {
7363 status = 200 ;
7464 status_text = "OK" ;
@@ -81,7 +71,7 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
8171 * Set the courier API key. Only the owner can set the courier API key.
8272 */
8373 public shared (context) func setCourierApiKey(apiKey : Text ) : async MainTypes . Response < Text > {
84- if (not Principal . equal( context. caller, actorContext. caller) ) {
74+ if (context. caller != actorContext. caller) {
8575 return {
8676 status = 403 ;
8777 status_text = "Forbidden" ;
@@ -102,28 +92,17 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
10292 * Get latest log items. Log output is capped at 100 items.
10393 */
10494 public query func getLogs() : async [Text ] {
105- Buffer . toArray( logData) ;
95+ logData;
10696 };
10797
10898 /**
10999 * Log a message. Log output is capped at 100 items.
110100 */
111101 private func log(text : Text ) {
112102 Debug . print(text);
113- logData. reserve(logData. size() + 1 );
114- logData. insert(0 , text);
115- // Cap the log at 100 items
116- if (logData. size() == 100 ) {
117- let _ = logData. removeLast();
118- };
119- return ;
120- };
121-
122- /**
123- * Generate a Trie key based on a merchant's principal ID
124- */
125- private func merchantKey(x : Text ) : Trie . Key < Text > {
126- return { hash = Text . hash(x); key = x };
103+ logData := Array . tabulate< Text > ((logData. size() + 1 ). min(100 ), func (i : Nat ) : Text {
104+ if (i == 0 ) text else logData[i - 1 ]
105+ });
127106 };
128107
129108 /**
@@ -150,15 +129,15 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
150129 length = 1 ;
151130 });
152131
153- if (Array . size( response. transactions) > 0 ) {
132+ if (response. transactions. size( ) > 0 ) {
154133 latestTransactionIndex := start;
155134
156135 if (response. transactions[0 ]. kind == "transfer" ) {
157136 let t = response. transactions[0 ];
158137 switch (t. transfer) {
159138 case (?transfer) {
160139 let to = transfer. to. owner;
161- switch (Trie . get(merchantStore, merchantKey( Principal . toText(to)), Text . equal )) {
140+ switch (merchantStore . get(to . toText() )) {
162141 case (?merchant) {
163142 if (merchant. email_notifications or merchant. phone_notifications) {
164143 log("Sending notification to: " # debug_show (merchant. email_address));
@@ -182,28 +161,23 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
182161 * Send a notification to a merchant about a received payment
183162 */
184163 private func sendNotification(merchant : MainTypes . Merchant , transaction : CkBtcLedger . Transaction ) : async () {
185- // Managment canister
186164 let ic : HttpTypes . IC = actor ("aaaaa-aa" );
187165
188- // Create request body
189166 var amount = "0" ;
190167 var from = "" ;
191168 switch (transaction. transfer) {
192169 case (?transfer) {
193- amount := Nat . toText( transfer. amount);
194- from := Principal . toText( transfer. from. owner);
170+ amount := transfer. amount. toText( );
171+ from := transfer. from. owner. toText( );
195172 };
196173 case null {};
197174 };
198- let idempotencyKey : Text = Text . concat( merchant. name, Nat64 . toText( transaction. timestamp) );
175+ let idempotencyKey : Text = merchant. name # transaction. timestamp. toText( );
199176 let requestBodyJson : Text = "{ \" idempotencyKey \" : \" " # idempotencyKey # " \" , \" email \" : \" " # merchant. email_address # " \" , \" phone \" : \" " # merchant. phone_number # " \" , \" amount \" : \" " # amount # " \" , \" payer \" : \" " # from # " \" }" ;
200- let requestBodyAsBlob : Blob = Text . encodeUtf8(requestBodyJson );
201- let requestBodyAsNat8 : [Nat8 ] = Blob . toArray(requestBodyAsBlob );
177+ let requestBodyAsBlob : Blob = requestBodyJson . encodeUtf8();
178+ let requestBodyAsNat8 : [Nat8 ] = requestBodyAsBlob . toArray();
202179
203- // Setup request
204180 let httpRequest : HttpTypes . HttpRequestArgs = {
205- // The notification service is hosted on Netlify and the URL is hardcoded
206- // in this example. In a real application, the URL would be configurable.
207181 url = "https://icpos-notifications.xyz/.netlify/functions/notify" ;
208182 max_response_bytes = ?Nat64 . fromNat(1000 );
209183 headers = [
@@ -217,15 +191,11 @@ shared (actorContext) persistent actor class Main(_startBlock : Nat) {
217191 // Cycle cost of sending a notification
218192 // 49.14M + 5200 * request_size + 10400 * max_response_bytes
219193 // 49.14M + (5200 * 1000) + (10400 * 1000) = 64.74M
220- Cycles . add< system > (70_000_000 );
221-
222- // Send the request
223- let httpResponse : HttpTypes . HttpResponsePayload = await ic. http_request(httpRequest);
194+ let httpResponse : HttpTypes . HttpResponsePayload = await (with cycles = 70_000_000 ) ic. http_request(httpRequest);
224195
225- // Check the response
226196 if (httpResponse. status > 299 ) {
227- let response_body : Blob = Blob . fromArray( httpResponse. body);
228- let decoded_text : Text = switch (Text . decodeUtf8(response_body )) {
197+ let response_body : Blob = httpResponse. body. fromArray( );
198+ let decoded_text : Text = switch (response_body . decodeUtf8()) {
229199 case (null ) { "No value returned" };
230200 case (?y) { y };
231201 };
0 commit comments